mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-19 19:01:58 +00:00
Compare commits
765 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ac607483aa | ||
|
3ef46a59e9 | ||
|
5529fa7124 | ||
|
7a30bc4ff6 | ||
|
8a9f6b1896 | ||
|
da77bcde04 | ||
|
c4979bdd27 | ||
|
0c9a25a073 | ||
|
e24c2491a6 | ||
|
72a2718a93 | ||
|
54d92f5b39 | ||
|
86600d4fcf | ||
|
5cfe02edf6 | ||
|
22cb9dfbe0 | ||
|
3782388e45 | ||
|
5821256590 | ||
|
14e06bfedd | ||
|
886475740a | ||
|
32e0a5bd80 | ||
|
26d19fab32 | ||
|
4271dc1638 | ||
|
a56cefda7d | ||
|
4d4d91bf84 | ||
|
54dfb35007 | ||
|
3893f59773 | ||
|
7d8786a620 | ||
|
f48bc8d299 | ||
|
d33c87c565 | ||
|
5a28479788 | ||
|
fabd7b1bfd | ||
|
e57cd13145 | ||
|
515ee0a503 | ||
|
cacbef4f75 | ||
|
2772275e62 | ||
|
34fd66c835 | ||
|
45f5a38512 | ||
|
6874d6469d | ||
|
979c1ad305 | ||
|
80de6c78dc | ||
|
5009cb5d55 | ||
|
0c90171a49 | ||
|
d282cb4e34 | ||
|
976f79b5a6 | ||
|
6693a5166f | ||
|
7b9906d493 | ||
|
f927e61b37 | ||
|
430b498caf | ||
|
983508e291 | ||
|
104a2379f3 | ||
|
e337bcf8bd | ||
|
029774687c | ||
|
be58545999 | ||
|
d774b4e2e3 | ||
|
cb65999124 | ||
|
77e7af75dc | ||
|
714184867c | ||
|
fad2331d80 | ||
|
bb5de8bf7e | ||
|
6f78c96cee | ||
|
883342b2c8 | ||
|
6232858b85 | ||
|
4e4b44d41d | ||
|
daf74e11ed | ||
|
3e2c2e6395 | ||
|
bc5b2085b9 | ||
|
ebcd751145 | ||
|
cdfde3dca8 | ||
|
4e3378451c | ||
|
415fb7294c | ||
|
1e35f0e7e3 | ||
|
b83d06294d | ||
|
7945220825 | ||
|
3e8a4d55ef | ||
|
e1fbdca5c1 | ||
|
1b31d20877 | ||
|
aaaab03995 | ||
|
affa8a317a | ||
|
5a03f3395c | ||
|
8da6ec3f5b | ||
|
a0402ac742 | ||
|
654e0fc74f | ||
|
d3bd1f4124 | ||
|
2aa6067437 | ||
|
356c87da49 | ||
|
0d76fcd564 | ||
|
963c3b2a68 | ||
|
7617fe3510 | ||
|
88724be3aa | ||
|
f845c97955 | ||
|
2c69a2eda2 | ||
|
dcce747e92 | ||
|
c251ca5daf | ||
|
bcb9794315 | ||
|
1a043e35c2 | ||
|
4fef316ddd | ||
|
7196ac3ec9 | ||
|
1c74db30ed | ||
|
00440f282b | ||
|
dfe099f9c6 | ||
|
0dbee47182 | ||
|
46796a8c10 | ||
|
c381067364 | ||
|
19573de5df | ||
|
77359fbc55 | ||
|
45eb758583 | ||
|
1a154a8d45 | ||
|
c40f6f2864 | ||
|
c036c5a2bc | ||
|
5b29e78c4b | ||
|
b54ef9f5e5 | ||
|
b7f48a19e8 | ||
|
c8f1e4bbd7 | ||
|
5cc1369191 | ||
|
16a511cf79 | ||
|
7c78708865 | ||
|
0449430ec3 | ||
|
dd5a179ba1 | ||
|
7c1139e42b | ||
|
caddf3d1c6 | ||
|
54d5778bf3 | ||
|
b1732d0de8 | ||
|
48b5f749a1 | ||
|
a63b8322db | ||
|
2130eef971 | ||
|
5b829b514f | ||
|
1bac3258da | ||
|
d7181100ee | ||
|
ccc82858ad | ||
|
5dd6b23b09 | ||
|
6a08f52fa5 | ||
|
18172b7fdb | ||
|
3eccc56c7a | ||
|
f7545af17a | ||
|
aa4da1e2e1 | ||
|
b4aafefc2b | ||
|
8304ee21f3 | ||
|
ca6479ede6 | ||
|
471cdefcff | ||
|
16b0307b0a | ||
|
2f63090e7c | ||
|
8643034945 | ||
|
6cc4d14fcb | ||
|
389ffa820f | ||
|
60fa0d7244 | ||
|
28d6885178 | ||
|
00f579909e | ||
|
a35bccb940 | ||
|
1cba62aa42 | ||
|
0974591a8f | ||
|
326bef66ee | ||
|
67ba8cee09 | ||
|
08107f7103 | ||
|
f5c075936f | ||
|
d493820cc8 | ||
|
af4a006192 | ||
|
946e3fb540 | ||
|
b19338e2f3 | ||
|
d4a44e6089 | ||
|
c90c181785 | ||
|
b75178a184 | ||
|
a77187135f | ||
|
61e24a41a2 | ||
|
07c7bf1d49 | ||
|
d4cb72cb77 | ||
|
948cdf7e5e | ||
|
e93292d98c | ||
|
7105d34fb3 | ||
|
29847c9711 | ||
|
3db778a2b1 | ||
|
361f78542a | ||
|
15d0e04431 | ||
|
b968ef416a | ||
|
6191362fe0 | ||
|
6d8fd79922 | ||
|
0227879a69 | ||
|
e4d2b121c1 | ||
|
f8941ab507 | ||
|
f3ac8a5888 | ||
|
64ac3927ec | ||
|
a558613189 | ||
|
7535ca9a26 | ||
|
03670e2dfe | ||
|
be797f5353 | ||
|
da38df6e4b | ||
|
2d53eb300a | ||
|
523ea78166 | ||
|
3b33730e48 | ||
|
351272cc27 | ||
|
a293827739 | ||
|
ead2cf92d3 | ||
|
dea9e21382 | ||
|
69725eb8c3 | ||
|
d31bb1f9b6 | ||
|
b64979e45a | ||
|
3dd3d2cabb | ||
|
94e7c3527f | ||
|
1a71d22146 | ||
|
fdc3b80f1f | ||
|
b978de78e4 | ||
|
8b6bd296e1 | ||
|
4d075feed6 | ||
|
e0e16489a1 | ||
|
deeb4933fc | ||
|
be8a0ddb3e | ||
|
49b1a6d844 | ||
|
3c1361e377 | ||
|
a7ab5b7504 | ||
|
0c8229d689 | ||
|
aebf45697f | ||
|
d5d5530a8e | ||
|
8aa7776072 | ||
|
f427267f5b | ||
|
3195dd0db0 | ||
|
e67c62e6cd | ||
|
d319a21b01 | ||
|
f8963179c3 | ||
|
d3cd657501 | ||
|
6c6bb2cd1e | ||
|
1771b3964e | ||
|
351c0ee2d7 | ||
|
1c9c380c8c | ||
|
24455bf980 | ||
|
e21b40b46c | ||
|
29eabf9c8a | ||
|
60e20ceeb1 | ||
|
37b35661be | ||
|
b54bb20617 | ||
|
19ce6b71e2 | ||
|
b68663c977 | ||
|
ac385e2647 | ||
|
bc70174f44 | ||
|
b8668b44a0 | ||
|
86a87cc951 | ||
|
be58b1d2be | ||
|
61733f6553 | ||
|
15e772d9dc | ||
|
ae7b81bf86 | ||
|
2c9ac0982a | ||
|
2df390065b | ||
|
247c9aebf3 | ||
|
0783500eaa | ||
|
c2e542004d | ||
|
806ad918f0 | ||
|
84406a74c3 | ||
|
09990acaa2 | ||
|
a73247ec8c | ||
|
e98d43dd65 | ||
|
dbd68cedc9 | ||
|
e6e8200912 | ||
|
7b1380366b | ||
|
1eda806c17 | ||
|
782ecca6a9 | ||
|
e6338705a7 | ||
|
09226e6f12 | ||
|
5ff1991cdc | ||
|
4ebb6de520 | ||
|
1ba7d65582 | ||
|
4e6063a4f8 | ||
|
85476f3549 | ||
|
ef9714b7e0 | ||
|
8d20029557 | ||
|
0f04b44ca1 | ||
|
44ed45502e | ||
|
5bcafe1311 | ||
|
5a771ccc5f | ||
|
c6145b4a3b | ||
|
b20aeca849 | ||
|
793918f2f3 | ||
|
5d410e577e | ||
|
9ec786ec3a | ||
|
e532b4d4fc | ||
|
58585d03c6 | ||
|
a4f66b3d86 | ||
|
c847621874 | ||
|
86f14885eb | ||
|
173e196bc8 | ||
|
7505db871f | ||
|
946dde8957 | ||
|
9a52cfbfbe | ||
|
b248bd6d0c | ||
|
3fff9ad0a2 | ||
|
a695a1bba2 | ||
|
91e384aae8 | ||
|
1ac95b6fa7 | ||
|
6757b6211d | ||
|
4dfb78837e | ||
|
af50ad3db4 | ||
|
01cb94aabc | ||
|
5ffc5060b9 | ||
|
43d7c956a4 | ||
|
863b07951a | ||
|
2101717edb | ||
|
e7ac1476c5 | ||
|
5d24c1fee1 | ||
|
53eb863f9d | ||
|
3c3ba637b5 | ||
|
be8286b15c | ||
|
6f6087995d | ||
|
2ff8a35171 | ||
|
7f8ed7abb6 | ||
|
2ff0c37c12 | ||
|
c593936b32 | ||
|
c54204ede9 | ||
|
d9132bbee3 | ||
|
c1be98762e | ||
|
c2733e2a8f | ||
|
722cf9b4fe | ||
|
01e31e73e0 | ||
|
dba7d05296 | ||
|
a8709a4a45 | ||
|
49b1435cba | ||
|
6a25b41952 | ||
|
c561f99de6 | ||
|
9d9053d828 | ||
|
c3c9a2f3c0 | ||
|
28465142e9 | ||
|
5473a618c9 | ||
|
ac1a8d8053 | ||
|
15ae9203b6 | ||
|
9dfc2ae20b | ||
|
230de7cbdd | ||
|
feaa003a52 | ||
|
295d01dc16 | ||
|
cbf1fde45e | ||
|
6cc47287d3 | ||
|
4bbe728376 | ||
|
427a90ec85 | ||
|
55ddb26dac | ||
|
891777f079 | ||
|
1655286b67 | ||
|
dd81636bf2 | ||
|
56a43a707d | ||
|
c2f92c6e45 | ||
|
61bd2dc840 | ||
|
f6a675f2e2 | ||
|
85b43055a7 | ||
|
e63f7bcc70 | ||
|
384dd37430 | ||
|
c6b336171c | ||
|
d2f4399a1a | ||
|
02d1bc093c | ||
|
420e493987 | ||
|
0a15479bff | ||
|
a9b76a3679 | ||
|
d1a3cd9044 | ||
|
81735d59f8 | ||
|
1d8da7f9f0 | ||
|
c5f0684030 | ||
|
25867adcb9 | ||
|
d55694cd68 | ||
|
05f069d61e | ||
|
5f6e7ad138 | ||
|
61bc38921e | ||
|
94c660545d | ||
|
4d3907948d | ||
|
e6442dd8af | ||
|
7905e0bd70 | ||
|
f4b1da352d | ||
|
0d33348941 | ||
|
c7c875e95f | ||
|
19d24b3e2a | ||
|
8fed6b6657 | ||
|
b5eafa1910 | ||
|
c15501937f | ||
|
4c743bd5b0 | ||
|
44289cbd95 | ||
|
b2f1642cfe | ||
|
341ef0220c | ||
|
9df88115bc | ||
|
c7273edb5e | ||
|
c398aa2b69 | ||
|
aa786eaaf3 | ||
|
e58a5e12d6 | ||
|
12b3575c5c | ||
|
3ca186dc8f | ||
|
1535f596f6 | ||
|
2cc326caa1 | ||
|
43436ae942 | ||
|
fbfd8475de | ||
|
405752f353 | ||
|
2af98b259a | ||
|
015242a666 | ||
|
54933fda2e | ||
|
852d057a47 | ||
|
1778f0b4f3 | ||
|
6daf083b3f | ||
|
4a7d9b130a | ||
|
4163efba55 | ||
|
db5847b49b | ||
|
6829003f5e | ||
|
047927718e | ||
|
91deb22a3f | ||
|
1629ca0739 | ||
|
8b87204f10 | ||
|
f920d90e3d | ||
|
1cb91282af | ||
|
a6ce294131 | ||
|
34ceb69776 | ||
|
eed68b5d95 | ||
|
79374c11ee | ||
|
404f9df6d3 | ||
|
4b716e35b9 | ||
|
166fc7a3e2 | ||
|
06afbc7a0a | ||
|
49a98de63a | ||
|
5b572c0da2 | ||
|
59014b1a87 | ||
|
4aec1f06e0 | ||
|
136af9625a | ||
|
edac26f757 | ||
|
ac54dd05bf | ||
|
7948058364 | ||
|
f13a6f7bf9 | ||
|
e1a5d143c5 | ||
|
f98011cd0d | ||
|
e7c10dec5c | ||
|
ae2b28fdee | ||
|
8d3fc18ca6 | ||
|
61521cf478 | ||
|
06c5e4df2c | ||
|
9e4b7f8bb4 | ||
|
144bc29eb3 | ||
|
2c0d8b9cb3 | ||
|
511b1258ba | ||
|
8cfe0af502 | ||
|
54e3c7d729 | ||
|
628b493128 | ||
|
8c552b8fa8 | ||
|
c864d904b0 | ||
|
ecb61676ab | ||
|
ee5a4caaab | ||
|
84bdd47109 | ||
|
5445752588 | ||
|
bcfbdcf3f0 | ||
|
7e6cd77203 | ||
|
5e3d00ecde | ||
|
4dea3a5f55 | ||
|
7303d8b899 | ||
|
fa0caa6518 | ||
|
26be90eff1 | ||
|
db3c9fbd28 | ||
|
87b62e2e72 | ||
|
1e6224f041 | ||
|
d6797b8428 | ||
|
76c89a8efa | ||
|
1797880488 | ||
|
cdebde5a2b | ||
|
5ee1f322d0 | ||
|
87273e232a | ||
|
53b77e6bf9 | ||
|
b8696bf8b5 | ||
|
8436a86536 | ||
|
c598151c68 | ||
|
53938f3c12 | ||
|
0813464fe0 | ||
|
4436c9939e | ||
|
5e323b958f | ||
|
367f225f38 | ||
|
a97d7058ff | ||
|
9d7fb0efe1 | ||
|
1e9596bba0 | ||
|
c6d17e9bd7 | ||
|
f76585dc9b | ||
|
cf4f76f211 | ||
|
790e29f15e | ||
|
2107406d8b | ||
|
b91a21a081 | ||
|
170e35aecc | ||
|
5701597e20 | ||
|
d16389748f | ||
|
ee844e970f | ||
|
62757812ff | ||
|
c15b72b6b0 | ||
|
11e3834235 | ||
|
affc198b03 | ||
|
d5b315e3c8 | ||
|
eb31ff4ebb | ||
|
9ab36d4598 | ||
|
bf06f8eac3 | ||
|
fa9befba11 | ||
|
3b0c061ec7 | ||
|
d021be6a25 | ||
|
69ee691497 | ||
|
5a7152ceec | ||
|
7b216543fa | ||
|
cd3f3fd781 | ||
|
4b0e46af2b | ||
|
470ab6340c | ||
|
9e54ebe6a9 | ||
|
7eb2451e3d | ||
|
6dba44ba71 | ||
|
64683647bd | ||
|
1f919b6d0e | ||
|
417a0d0f83 | ||
|
9b53576fc2 | ||
|
2a46756838 | ||
|
cdf055065f | ||
|
2cee008e4a | ||
|
d889e39aad | ||
|
72b9ecf07e | ||
|
14f4351920 | ||
|
12e81364a0 | ||
|
d489244c00 | ||
|
aceaf5f891 | ||
|
6129b9d25c | ||
|
75afe35e98 | ||
|
3246b7eb9f | ||
|
c412ffc195 | ||
|
7c3b5fe714 | ||
|
ae967c027b | ||
|
3e74fce885 | ||
|
2c7891bfb2 | ||
|
712ba00c9e | ||
|
74683b18f2 | ||
|
d0d129d965 | ||
|
87d459c5ff | ||
|
5b591ff744 | ||
|
63f00cbb7b | ||
|
c90b26c181 | ||
|
c136c60eae | ||
|
ba4c1d95a7 | ||
|
9a25d6a741 | ||
|
91c067ac9f | ||
|
24129ab69c | ||
|
2b6c3fd743 | ||
|
bd2f064eeb | ||
|
b0c9fc0792 | ||
|
e02e747f1b | ||
|
64462812fc | ||
|
46382b0d21 | ||
|
a7fffa5868 | ||
|
4334928fbd | ||
|
a478d1b544 | ||
|
08b69dfbf6 | ||
|
b631adbbf2 | ||
|
fbd88d03c5 | ||
|
4982f6f919 | ||
|
6a6bf102b6 | ||
|
524f85b9c1 | ||
|
328c960950 | ||
|
208bece7ea | ||
|
16f918a294 | ||
|
9d90beb790 | ||
|
d7e953d38c | ||
|
6967bb003e | ||
|
f63e51fea2 | ||
|
33c73701d8 | ||
|
50b710b4f6 | ||
|
d1325ffbd8 | ||
|
bfc6a70c9f | ||
|
a00f46faa9 | ||
|
c475f05652 | ||
|
d42b9ee017 | ||
|
cccaae49a5 | ||
|
1dd3018cb2 | ||
|
7ea32046af | ||
|
9bb2f1cfd3 | ||
|
fdffed636f | ||
|
bde0732135 | ||
|
d1d11ae717 | ||
|
309633069c | ||
|
2ece754927 | ||
|
6f98fc0dff | ||
|
cd65971f5e | ||
|
81d7b7b6a1 | ||
|
92ba56ae2b | ||
|
e1bcfb9ac9 | ||
|
b398924706 | ||
|
d6f46e23c5 | ||
|
8b4066a981 | ||
|
f8a695d7e1 | ||
|
630b24716d | ||
|
be41049e72 | ||
|
b726e7d106 | ||
|
1058bcd31d | ||
|
8724ba05ca | ||
|
5145707b94 | ||
|
3746cb8c71 | ||
|
624f2d0bfd | ||
|
1c397ad0ad | ||
|
a2c0f0666f | ||
|
72a4e86e2f | ||
|
5e8d94d16a | ||
|
d92b741088 | ||
|
2a4107940f | ||
|
16d5282929 | ||
|
f6ecb143fe | ||
|
f375934b41 | ||
|
9f8bf6d495 | ||
|
9e9b55da8e | ||
|
c1654f1d04 | ||
|
c526c4ae1a | ||
|
dcf1609c61 | ||
|
dd84c7f966 | ||
|
bccde86c0c | ||
|
2f17c45b3f | ||
|
b6bab003cd | ||
|
27d7eb4832 | ||
|
774f7d88c2 | ||
|
086e4d5880 | ||
|
50f4bf568b | ||
|
c218c70b1f | ||
|
a939a5ba30 | ||
|
25be550e6d | ||
|
9331f8985a | ||
|
41e6c8f73e | ||
|
a29f8d5849 | ||
|
9817c0807a | ||
|
4818baee39 | ||
|
379916ee08 | ||
|
60dbd51259 | ||
|
0927b0c8ae | ||
|
40cc510057 | ||
|
d580bf8f43 | ||
|
4434ea40ee | ||
|
2ba6ffccbc | ||
|
ec335ae88c | ||
|
542c64154c | ||
|
e4fa437f78 | ||
|
61f3f995ad | ||
|
8182cf1efa | ||
|
10d578503c | ||
|
75ee331f36 | ||
|
a32c484fd5 | ||
|
62d74e05f2 | ||
|
23ec5cbaa7 | ||
|
21fcb6f853 | ||
|
9b0a890531 | ||
|
af503597e6 | ||
|
e0c99fa80c | ||
|
a486d65893 | ||
|
08a6e22556 | ||
|
2d5c4c8101 | ||
|
4b7460c1cf | ||
|
8c8a2a1596 | ||
|
ee7731b1a1 | ||
|
36e4462957 | ||
|
e71a4a5595 | ||
|
287aa76d8d | ||
|
a9738fc2a9 | ||
|
bc485363ca | ||
|
f53e9d91c0 | ||
|
99fd90ee49 | ||
|
fb77210f2c | ||
|
6c163ebef3 | ||
|
1d8f34275a | ||
|
1fcc2f6b39 | ||
|
2eaa31163c | ||
|
3de4441bcd | ||
|
cbc43b9dc1 | ||
|
d018c57b17 | ||
|
a5d0658241 | ||
|
ce29beaf55 | ||
|
b11bcc1fbe | ||
|
ab0e77a2e8 | ||
|
80f0637c77 | ||
|
a280319a0b | ||
|
eb3a55d6e0 | ||
|
64a6661ea4 | ||
|
7f46cf805c | ||
|
d0d5dea5ce | ||
|
018ca29184 | ||
|
4b4f78b9eb | ||
|
f084e742c9 | ||
|
ab4de3fdd6 | ||
|
9f7d5b8e88 | ||
|
4958f6e4a6 | ||
|
b3e3a17add | ||
|
0b25fdd043 | ||
|
cfc48ccc26 | ||
|
3810085ea6 | ||
|
0088886915 | ||
|
c427404e5d | ||
|
e8bf53ad11 | ||
|
5f7f4328c1 | ||
|
1f1829e3b5 | ||
|
8079fab472 | ||
|
19a428bf2d | ||
|
39cdbc461e | ||
|
ccc80dd437 | ||
|
f1539c4cba | ||
|
796188603b | ||
|
b7210e296f | ||
|
6df03e2537 | ||
|
7843e781da | ||
|
bf2d1c223b | ||
|
400c0ccaca | ||
|
db05839b87 | ||
|
50d4774907 | ||
|
5361fbb76f | ||
|
a389cbe521 | ||
|
08b85cfa1a | ||
|
7e354b9c30 | ||
|
4cdc2ade01 | ||
|
dee6c0e1e4 | ||
|
72491250b7 | ||
|
88e2d8cd55 | ||
|
fd04a38359 | ||
|
529cb3d387 | ||
|
8d806e6a1d | ||
|
2a38c9b4ef | ||
|
2e3e18d836 | ||
|
156c51798d | ||
|
8fb72fe697 | ||
|
ccb1f56573 | ||
|
c8d2053500 | ||
|
4b5b47a078 | ||
|
930695b188 | ||
|
d51e5798ef | ||
|
edaa2c168a | ||
|
4dc24939ba | ||
|
4cd642b8da | ||
|
b77b3f0c21 | ||
|
0acc2c5727 | ||
|
5aae8b7743 | ||
|
e81ce3317c | ||
|
cebc0d7568 | ||
|
d5cfc12bf3 | ||
|
ac931698d3 | ||
|
0c13ac2e93 | ||
|
685107e950 | ||
|
ae57be74e0 | ||
|
58c0a69737 | ||
|
ab73322c58 | ||
|
c2e7e00cdd | ||
|
554a702c0a | ||
|
2a3775968c | ||
|
3cb01a9b50 | ||
|
e54a011e0c | ||
|
fc81833b50 | ||
|
067246be79 | ||
|
11997f0f97 | ||
|
635ef0de77 | ||
|
7f3522339c | ||
|
f99f166623 | ||
|
fb574cb173 | ||
|
cf4adae604 | ||
|
3f6719dc70 | ||
|
7cdfbc48a9 | ||
|
28b7bd4d71 | ||
|
d056f0d221 | ||
|
3771cc3b75 | ||
|
c7f25c5486 | ||
|
b697b71e59 | ||
|
a6fab50c20 | ||
|
43232c208f | ||
|
1c502667b1 | ||
|
d28bf1f8a4 | ||
|
091cd05527 | ||
|
08b9cfa221 | ||
|
42f585f630 | ||
|
67fb906855 | ||
|
6655d2e3d0 | ||
|
3711cbd9d7 | ||
|
17c8be37f5 | ||
|
90b3bce361 | ||
|
51aca7f415 | ||
|
a381dc1cfe | ||
|
bf07859718 | ||
|
996fc4dc7c | ||
|
f598c39c6e | ||
|
f4fd9e5a15 | ||
|
3adc43938e | ||
|
3f7891f4e8 | ||
|
68d53a2066 |
@@ -15,3 +15,5 @@ el_GR
|
||||
sv_SE
|
||||
zh-hans_CN
|
||||
zh-hant_CN
|
||||
fi_FI
|
||||
vi_VN
|
||||
|
102
.env.example
102
.env.example
@@ -10,33 +10,42 @@ APP_DEBUG=false
|
||||
SITE_OWNER=mail@example.com
|
||||
|
||||
# The encryption key for your sessions. Keep this very secure.
|
||||
# If you generate a new one existing data must be considered LOST.
|
||||
# If you generate a new one all existing attachments must be considered LOST.
|
||||
# Change it to a string of exactly 32 chars or use something like `php artisan key:generate` to generate it.
|
||||
# If you use Docker or similar, you can set this variable from a file by using APP_KEY_FILE
|
||||
APP_KEY=SomeRandomStringOf32CharsExactly
|
||||
|
||||
#
|
||||
# Firefly III will launch using this language (for new users and unauthenticated visitors)
|
||||
# For a list of available languages: https://github.com/firefly-iii/firefly-iii/tree/main/resources/lang
|
||||
#
|
||||
# If text is still in English, remember that not everything may have been translated.
|
||||
DEFAULT_LANGUAGE=en_US
|
||||
|
||||
# The locale defines how numbers are formatted.
|
||||
# by default this value is the same as whatever the language is.
|
||||
DEFAULT_LOCALE=equal
|
||||
|
||||
# Change this value to your preferred time zone.
|
||||
# Example: Europe/Amsterdam
|
||||
# For a list of supported time zones, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
TZ=Europe/Amsterdam
|
||||
|
||||
# This variable must match your installation's external address but keep in mind that
|
||||
# it's only used on the command line as a fallback value.
|
||||
APP_URL=http://localhost
|
||||
|
||||
# TRUSTED_PROXIES is a useful variable when using Docker and/or a reverse proxy.
|
||||
# Set it to ** and reverse proxies work just fine.
|
||||
TRUSTED_PROXIES=
|
||||
|
||||
# The log channel defines where your log entries go to.
|
||||
# - Docker + versions <= 4.8.1.8 and before: use "stdout"
|
||||
# - Docker + versions > 4.8.1.8: use "docker_out"
|
||||
# - For everything else, use 'daily'
|
||||
|
||||
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
|
||||
# Also available are 'syslog', 'errorlog' and 'stdout' which will log to the system itself.
|
||||
# A rotating log option is 'daily', creates 5 files that (surprise) rotate.
|
||||
# Default setting 'stack' will log to 'daily' and to 'stdout' at the same time.
|
||||
|
||||
# - Docker + versions <= 4.8.1.8 and before: use "stdout"
|
||||
# - Docker + versions > 4.8.1.8 : use "docker_out"
|
||||
# - Docker + versions >= 5.1.1 : use "stack"
|
||||
# - For everything else (als not Docker) : use 'stack'
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
|
||||
# Log level. You can set this from least severe to most severe:
|
||||
@@ -48,13 +57,27 @@ APP_LOG_LEVEL=notice
|
||||
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
|
||||
# For other database types, please see the FAQ: https://docs.firefly-iii.org/support/faq
|
||||
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
|
||||
DB_CONNECTION=pgsql
|
||||
DB_HOST=firefly_iii_db
|
||||
DB_PORT=5432
|
||||
# Use "pgsql" for PostgreSQL
|
||||
# Use "mysql" for MySQL and MariaDB.
|
||||
# Use "sqlite" for SQLite.
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=fireflyiiidb
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=firefly
|
||||
DB_USERNAME=firefly
|
||||
DB_PASSWORD=secret_firefly_password
|
||||
|
||||
# MySQL supports SSL. You can configure it here.
|
||||
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
|
||||
MYSQL_USE_SSL=false
|
||||
MYSQL_SSL_VERIFY_SERVER_CERT=true
|
||||
# You need to set at least of these options
|
||||
MYSQL_SSL_CAPATH=/etc/ssl/certs/
|
||||
MYSQL_SSL_CA=
|
||||
MYSQL_SSL_CERT=
|
||||
MYSQL_SSL_KEY=
|
||||
MYSQL_SSL_CIPHER=
|
||||
|
||||
# PostgreSQL supports SSL. You can configure it here.
|
||||
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
|
||||
PGSQL_SSL_MODE=prefer
|
||||
@@ -73,7 +96,8 @@ SESSION_DRIVER=file
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
REDIS_DB="0" # always use quotes
|
||||
# always use quotes and make sure redis db "0" and "1" exists. Otherwise change accordingly.
|
||||
REDIS_DB="0"
|
||||
REDIS_CACHE_DB="1"
|
||||
|
||||
# Cookie settings. Should not be necessary to change these.
|
||||
@@ -86,8 +110,8 @@ COOKIE_SECURE=false
|
||||
# If you want Firefly III to mail you, update these settings
|
||||
# For instructions, see: https://docs.firefly-iii.org/advanced-installation/email
|
||||
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
|
||||
MAIL_DRIVER=log
|
||||
MAIL_HOST=smtp.mailtrap.io
|
||||
MAIL_MAILER=log
|
||||
MAIL_HOST=null
|
||||
MAIL_PORT=2525
|
||||
MAIL_FROM=changeme@example.com
|
||||
MAIL_USERNAME=null
|
||||
@@ -145,6 +169,23 @@ FIXER_API_KEY=
|
||||
# If you use Docker or similar, you can set this variable from a file by appending it with _FILE
|
||||
LOGIN_PROVIDER=eloquent
|
||||
|
||||
#
|
||||
# It's also possible to change the way users are authenticated. You could use Authelia for example.
|
||||
# Authentication via the REMOTE_USER header is supported. Change the value below to "remote_user_guard".
|
||||
#
|
||||
# If you do this please read the documentation for instructions and warnings:
|
||||
# https://docs.firefly-iii.org/advanced-installation/authentication
|
||||
#
|
||||
# This function is available in Firefly III v5.3.0 and higher.
|
||||
AUTHENTICATION_GUARD=web
|
||||
|
||||
#
|
||||
# Likewise, it's impossible to log out users who's authentication is handled by an external system.
|
||||
# Enter a custom URL here that will force a logout (your authentication provider can tell you).
|
||||
# Setting this variable only works when AUTHENTICATION_GUARD != web
|
||||
#
|
||||
CUSTOM_LOGOUT_URI=
|
||||
|
||||
# LDAP connection configuration
|
||||
# OpenLDAP, FreeIPA or ActiveDirectory
|
||||
# # If you use Docker or similar, you can set this variable from a file by appending it with _FILE
|
||||
@@ -159,8 +200,16 @@ ADLDAP_PORT=389
|
||||
ADLDAP_TIMEOUT=5
|
||||
ADLDAP_BASEDN=""
|
||||
ADLDAP_FOLLOW_REFFERALS=false
|
||||
|
||||
# SSL/TLS settings
|
||||
ADLDAP_USE_SSL=false
|
||||
ADLDAP_USE_TLS=false
|
||||
ADLDAP_SSL_CACERTDIR=
|
||||
ADLDAP_SSL_CACERTFILE=
|
||||
ADLDAP_SSL_CERTFILE=
|
||||
ADLDAP_SSL_KEYFILE=
|
||||
ADLDAP_SSL_CIPHER_SUITE=
|
||||
ADLDAP_SSL_REQUIRE_CERT=
|
||||
|
||||
# You can set the following variables from a file by appending them with _FILE:
|
||||
ADLDAP_ADMIN_USERNAME=
|
||||
@@ -180,6 +229,7 @@ ADLDAP_AUTH_FIELD=distinguishedname
|
||||
|
||||
# Will allow SSO if your server provides an AUTH_USER field.
|
||||
# You can set the following variables from a file by appending them with _FILE:
|
||||
WINDOWS_SSO_ENABLED=false
|
||||
WINDOWS_SSO_DISCOVER=samaccountname
|
||||
WINDOWS_SSO_KEY=AUTH_USER
|
||||
|
||||
@@ -206,6 +256,12 @@ DISABLE_CSP_HEADER=false
|
||||
TRACKER_SITE_ID=
|
||||
TRACKER_URL=
|
||||
|
||||
#
|
||||
# Firefly III can collect telemetry on how you use Firefly III. This is opt-in.
|
||||
# In order to allow this, change the following variable to true.
|
||||
# To read more about this feature, go to this page: https://docs.firefly-iii.org/support/telemetry
|
||||
SEND_TELEMETRY=false
|
||||
|
||||
# You can fine tune the start-up of a Docker container by editing these environment variables.
|
||||
# Use this at your own risk. Disabling certain checks and features may result in lost of inconsistent data.
|
||||
# However if you know what you're doing you can significantly speed up container start times.
|
||||
@@ -250,7 +306,17 @@ PUSHER_ID=
|
||||
DEMO_USERNAME=
|
||||
DEMO_PASSWORD=
|
||||
USE_ENCRYPTION=false
|
||||
IS_SANDSTORM=false
|
||||
IS_DOCKER=false
|
||||
IS_HEROKU=false
|
||||
BUNQ_USE_SANDBOX=false
|
||||
FIREFLY_III_LAYOUT=v1
|
||||
|
||||
#
|
||||
# If you have trouble configuring your Firefly III installation, DON'T BOTHER setting this variable.
|
||||
# It won't work. It doesn't do ANYTHING. Don't believe the lies you read online. I'm not joking.
|
||||
# This configuration value WILL NOT HELP.
|
||||
#
|
||||
# This variable is ONLY used in some of the emails Firefly III sends around. Nowhere else.
|
||||
# So when configuring anything WEB related this variable doesn't do anything. Nothing
|
||||
#
|
||||
# If you're stuck I understand you get desperate but look SOMEWHERE ELSE.
|
||||
#
|
||||
APP_URL=http://localhost
|
||||
|
12
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
12
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
@@ -16,10 +16,10 @@ I am running Firefly III version x.x.x, and my problem is:
|
||||
<!-- Please add extra info here, such as OS, browser, and the output from the /debug page of your Firefly III installation (click the version at the bottom). -->
|
||||
|
||||
**Bonus points**
|
||||
<!-- Earn bonus points by checking the boxes -->
|
||||
<!-- Before you submit, verify the following please: -->
|
||||
|
||||
- [ ] Nobody reported this bug before
|
||||
- [ ] I have added a stack trace from my log files.
|
||||
- [ ] I have added a screenshot.
|
||||
- [ ] I was able to replicate it on the demo site https://demo.firefly-iii.org/
|
||||
<!-- - [ ] I donated money (this is a joke :wink:)-->
|
||||
- I searched and nobody reported this bug before
|
||||
- I have added a stack trace from my log files <!-- (see https://bit.ly/FF3-get-debug-info) -->
|
||||
- I have added a screenshot.
|
||||
- I was able to replicate it on the demo site https://demo.firefly-iii.org/
|
||||
<!-- - I donated money (this is a joke ;)-->
|
||||
|
10
.github/ISSUE_TEMPLATE/Custom.md
vendored
10
.github/ISSUE_TEMPLATE/Custom.md
vendored
@@ -16,8 +16,8 @@ I am running Firefly III version x.x.x.
|
||||
|
||||
<!-- Complete the following checklist for bonus points -->
|
||||
|
||||
- [ ] I have read the FAQ at https://bit.ly/FF3-FAQ
|
||||
- [ ] I added a screenshot
|
||||
- [ ] I added log files (see https://bit.ly/FF3-get-logs)
|
||||
- [ ] I was able to replicate the issue on the demo site.
|
||||
<!-- - [ ] I donated money (this is a joke :wink:)-->
|
||||
- I have read the FAQ at https://bit.ly/FF3-FAQ
|
||||
- I added a screenshot
|
||||
- I added log files <!-- (see https://bit.ly/FF3-get-debug-info) -->
|
||||
- I was able to replicate the issue on the demo site.
|
||||
<!-- - I donated money (this is a joke :wink:)-->
|
||||
|
2
.github/code_of_conduct.md
vendored
2
.github/code_of_conduct.md
vendored
@@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at thegrumpydictator@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at james@firefly-iii.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
|
4
.github/contributing.md
vendored
4
.github/contributing.md
vendored
@@ -4,11 +4,11 @@
|
||||
|
||||
## Feature requests
|
||||
|
||||
I am always interested in expanding Firefly III's many features. Just open a ticket or [drop me a line](mailto:thegrumpydictator@gmail.com).
|
||||
I am always interested in expanding Firefly III's many features. Just open a ticket or [drop me a line](mailto:james@firefly-iii.org).
|
||||
|
||||
## Pull requests
|
||||
|
||||
When contributing to Firefly III, please first discuss the change you wish to make via issue, email, or any other method. I can only accept pull requests against the `develop` branch, never the `master` branch.
|
||||
When contributing to Firefly III, please first discuss the change you wish to make via issue, email, or any other method. I can only accept pull requests against the `develop` branch, never the `main` branch.
|
||||
|
||||
## Translations :us: :fr: :de:
|
||||
|
||||
|
5
.github/pull_request_template.md
vendored
5
.github/pull_request_template.md
vendored
@@ -1,8 +1,9 @@
|
||||
<!--
|
||||
Before you create a new PR, please consider the following two considerations.
|
||||
Before you create a new PR, please consider:
|
||||
|
||||
1) Pull request for the MASTER branch will be closed.
|
||||
1) Pull requests for the MAIN branch will be closed.
|
||||
2) We cannot accept pull requests to add new currencies.
|
||||
3) DO NOT include translations in your PR. Only English US sentences.
|
||||
|
||||
Thanks.
|
||||
-->
|
||||
|
50
.github/security.md
vendored
50
.github/security.md
vendored
@@ -1,12 +1,54 @@
|
||||
# Security Policy
|
||||
|
||||
Firefly III is an application to manage your personal finances. As such, the developer has adopted this security disclosure and response policy to ensure that critical issues are responsibly handled.
|
||||
|
||||
## Supported Versions
|
||||
Only the latest Firefly III release is maintained. Applicable fixes, including security fixes, will not backported to older release branches. Please refer to [releases.md](https://github.com/firefly-iii/firefly-iii/blob/main/releases.md) for details.
|
||||
|
||||
Only the latest version of Firefly III is supported. If you're not running the latest version of Firefly III, please upgrade at your earliest convenience.
|
||||
## Reporting a Vulnerability - Private Disclosure Process
|
||||
Security is of the highest importance and all security vulnerabilities or suspected security vulnerabilities should be reported to Firefly III privately, to minimize attacks against current users of Firefly III before they are fixed. Vulnerabilities will be investigated and patched on the next patch (or minor) release as soon as possible. This information could be kept entirely internal to the project.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
If you know of a publicly disclosed security vulnerability for Firefly III, please **IMMEDIATELY** contact james@firefly-iii.org to inform the Firefly III developer. You can use my [GPG key](https://keybase.io/jc5) for extra security.
|
||||
|
||||
If you find something that compromises the security of Firefly III, you should [send me a message](mailto:thegrumpydictator@gmail.com) as soon as possible. These issues will be fixed immediately. You can also open an issue, but if you feel the issue is sensitive, please drop me a message instead.
|
||||
**IMPORTANT: Do not file public issues on GitHub for security vulnerabilities**
|
||||
|
||||
You can use my [GPG key](https://keybase.io/jc5) for extra security. My [GitHub commits](https://github.com/firefly-iii/firefly-iii/commits/master) are almost always signed with this key.
|
||||
To report a vulnerability or a security-related issue, please email the private address james@firefly-iii.org with the details of the vulnerability. The email will be received by the developer of Firefly III. Emails will be addressed within 3 business days, including a detailed plan to investigate the issue and any potential workarounds to perform in the meantime. Do not report non-security-impacting bugs through this channel. Use [GitHub issues](https://github.com/firefly-iii/firefly-iii/issues/new/choose) instead.
|
||||
|
||||
### Proposed Email Content
|
||||
Provide a descriptive subject line and in the body of the email include the following information:
|
||||
* Basic identity information, such as your name and your affiliation or company.
|
||||
* Detailed steps to reproduce the vulnerability (POC scripts, screenshots, and compressed packet captures are all helpful to us).
|
||||
* Description of the effects of the vulnerability on Firefly III and the related hardware and software configurations, so that the developer can reproduce it.
|
||||
* How the vulnerability affects Firefly III usage and an estimation of the attack surface, if there is one.
|
||||
* List other projects or dependencies that were used in conjunction with Firefly III to produce the vulnerability.
|
||||
|
||||
## When to report a vulnerability
|
||||
* When you think Firefly III has a potential security vulnerability.
|
||||
* When you suspect a potential vulnerability but you are unsure that it impacts Firefly III.
|
||||
* When you know of or suspect a potential vulnerability on another project that is used by Firefly III. For example Firefly III has a dependency on Docker, MySQL, etc.
|
||||
|
||||
## Patch, Release, and Disclosure
|
||||
The Firefly III developer will respond to vulnerability reports as follows:
|
||||
|
||||
1. The developer will investigate the vulnerability and determine its effects and criticality.
|
||||
2. If the issue is not deemed to be a vulnerability, the developer will follow up with a detailed reason for rejection.
|
||||
3. The developer will initiate a conversation with the reporter within 3 business days.
|
||||
4. If a vulnerability is acknowledged and the timeline for a fix is determined, the developer will work on a plan to communicate with the appropriate community, including identifying mitigating steps that affected users can take to protect themselves until the fix is rolled out.
|
||||
5. The developer will also create a [CVSS](https://www.first.org/cvss/specification-document) using the [CVSS Calculator](https://www.first.org/cvss/calculator/3.0). The developer makes the final call on the calculated CVSS; it is better to move quickly than making the CVSS perfect. Issues may also be reported to [Mitre](https://cve.mitre.org/) using this [scoring calculator](https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator). The CVE will initially be set to private.
|
||||
6. The developer will work on fixing the vulnerability and perform internal testing before preparing to roll out the fix.
|
||||
7. A public disclosure date is negotiated by the Firefly III developer and the bug submitter. We prefer to fully disclose the bug as soon as possible once a user mitigation or patch is available. It is reasonable to delay disclosure when the bug or the fix is not yet fully understood, the solution is not well-tested, or for distributor coordination. The timeframe for disclosure is from immediate (especially if it’s already publicly known) to a few weeks. For a critical vulnerability with a straightforward mitigation, we expect report date to public disclosure date to be on the order of 14 business days. The Firefly III developer holds the final say when setting a public disclosure date.
|
||||
9. Once the fix is confirmed, the developer will patch the vulnerability in the next patch or minor release. Upon release of the patched version of Firefly III, we will follow the **Public Disclosure Process**.
|
||||
|
||||
### Public Disclosure Process
|
||||
The developer publishes a public [advisory](https://github.com/firefly-iii/firefly-iii/security/advisories) to the Firefly III community via GitHub. In most cases, additional communication via Twitter, reddit and other channels will assist in educating Firefly III users and rolling out the patched release to affected users.
|
||||
|
||||
The develop will also publish any mitigating steps users can take until the fix can be applied to their Firefly III instances.
|
||||
|
||||
## Confidentiality, integrity and availability
|
||||
We consider vulnerabilities leading to the compromise of data confidentiality, elevation of privilege, or integrity to be our highest priority concerns. Availability, in particular in areas relating to DoS and resource exhaustion, is also a serious security concern. The Firefly III developer takes all vulnerabilities, potential vulnerabilities, and suspected vulnerabilities seriously and will investigate them in an urgent and expeditious manner.
|
||||
|
||||
Note that we do not currently consider the default settings for Firefly III to be secure-by-default. It is necessary for operators to explicitly configure settings, role based access control, and other resource related features in Firefly III to provide a hardened Firefly III environment. We will not act on any security disclosure that relates to a lack of safe defaults. Over time, we will work towards improved safe-by-default configuration, taking into account backwards compatibility.
|
||||
|
||||
## Credits
|
||||
|
||||
This security policy is based on [Harbor](https://github.com/goharbor/harbor)'s security policy.
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
/node_modules
|
||||
/frontend/node_modules
|
||||
/frontend/fonts
|
||||
/public/hot
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
|
6
app.json
6
app.json
@@ -3,7 +3,7 @@
|
||||
"description": "A free and open source personal finances manager",
|
||||
"repository": "https://github.com/firefly-iii/firefly-iii",
|
||||
"website": "https://firefly-iii.org/",
|
||||
"logo": "https://raw.githubusercontent.com/firefly-iii/firefly-iii/master/public/mstile-150x150.png",
|
||||
"logo": "https://raw.githubusercontent.com/firefly-iii/firefly-iii/main/public/mstile-150x150.png",
|
||||
"keywords": [
|
||||
"finance",
|
||||
"finances",
|
||||
@@ -41,7 +41,7 @@
|
||||
{
|
||||
"plan": "heroku-postgresql",
|
||||
"options": {
|
||||
"version": "9.5"
|
||||
"version": "12"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -58,7 +58,7 @@
|
||||
],
|
||||
"env": {
|
||||
"APP_KEY": {
|
||||
"description": "This key is used to encrypt your data.",
|
||||
"description": "This key is used to create app cookies en secure attachments.",
|
||||
"value": "base64:If1gJN4pyycXTq+WS5TjneDympKuu+8SKvTl6RZnhJg="
|
||||
}
|
||||
}
|
||||
|
@@ -31,6 +31,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
use FireflyIII\Support\Http\Api\TransactionFilter;
|
||||
use FireflyIII\Transformers\AccountTransformer;
|
||||
use FireflyIII\Transformers\AttachmentTransformer;
|
||||
use FireflyIII\Transformers\PiggyBankTransformer;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
use FireflyIII\User;
|
||||
@@ -49,8 +50,9 @@ use League\Fractal\Resource\Item;
|
||||
class AccountController extends Controller
|
||||
{
|
||||
use AccountFilter, TransactionFilter;
|
||||
/** @var AccountRepositoryInterface The account repository */
|
||||
private $repository;
|
||||
|
||||
private AccountRepositoryInterface $repository;
|
||||
public const RESOURCE_KEY = 'accounts';
|
||||
|
||||
/**
|
||||
* AccountController constructor.
|
||||
@@ -73,6 +75,35 @@ class AccountController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function attachments(Account $account): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||
$collection = $this->repository->getAttachments($account);
|
||||
|
||||
$count = $collection->count();
|
||||
$attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.accounts.attachments', [$account->id]) . $this->buildParams());
|
||||
|
||||
/** @var AttachmentTransformer $transformer */
|
||||
$transformer = app(AttachmentTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($attachments, $transformer, 'attachments');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
@@ -120,7 +151,7 @@ class AccountController extends Controller
|
||||
$transformer = app(AccountTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($accounts, $transformer, 'accounts');
|
||||
$resource = new FractalCollection($accounts, $transformer, self::RESOURCE_KEY);
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
@@ -178,7 +209,7 @@ class AccountController extends Controller
|
||||
/** @var AccountTransformer $transformer */
|
||||
$transformer = app(AccountTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
$resource = new Item($account, $transformer, 'accounts');
|
||||
$resource = new Item($account, $transformer, self::RESOURCE_KEY);
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
@@ -200,7 +231,7 @@ class AccountController extends Controller
|
||||
$transformer = app(AccountTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new Item($account, $transformer, 'accounts');
|
||||
$resource = new Item($account, $transformer, self::RESOURCE_KEY);
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
@@ -227,10 +258,8 @@ class AccountController extends Controller
|
||||
if (null !== $limit && $limit > 0) {
|
||||
$pageSize = $limit;
|
||||
}
|
||||
|
||||
$types = $this->mapTransactionTypes($this->parameters->get('type'));
|
||||
$manager = $this->getManager();
|
||||
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
|
||||
@@ -240,7 +269,6 @@ class AccountController extends Controller
|
||||
$collector->setUser($admin)->setAccounts(new Collection([$account]))
|
||||
->withAPIInformation()->setLimit($pageSize)->setPage($this->parameters->get('page'))->setTypes($types);
|
||||
|
||||
// set range if necessary:
|
||||
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
|
||||
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
|
||||
}
|
||||
@@ -277,7 +305,7 @@ class AccountController extends Controller
|
||||
/** @var AccountTransformer $transformer */
|
||||
$transformer = app(AccountTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
$resource = new Item($account, $transformer, 'accounts');
|
||||
$resource = new Item($account, $transformer, self::RESOURCE_KEY);
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
@@ -23,10 +23,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers;
|
||||
|
||||
use FireflyIII\Api\V1\Middleware\ApiDemoUser;
|
||||
use FireflyIII\Api\V1\Requests\AttachmentStoreRequest;
|
||||
use FireflyIII\Api\V1\Requests\AttachmentUpdateRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
||||
use FireflyIII\Http\Middleware\IsDemoUser;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
||||
use FireflyIII\Transformers\AttachmentTransformer;
|
||||
@@ -58,6 +60,7 @@ class AttachmentController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(ApiDemoUser::class)->except(['delete', 'download', 'show', 'index']);
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
/** @var User $user */
|
||||
@@ -65,6 +68,7 @@ class AttachmentController extends Controller
|
||||
$this->repository = app(AttachmentRepositoryInterface::class);
|
||||
$this->repository->setUser($user);
|
||||
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
@@ -92,8 +96,8 @@ class AttachmentController extends Controller
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @return LaravelResponse
|
||||
* @throws FireflyException
|
||||
* @return LaravelResponse
|
||||
*/
|
||||
public function download(Attachment $attachment): LaravelResponse
|
||||
{
|
||||
@@ -184,8 +188,8 @@ class AttachmentController extends Controller
|
||||
*
|
||||
* @param AttachmentStoreRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(AttachmentStoreRequest $request): JsonResponse
|
||||
{
|
||||
|
@@ -206,8 +206,8 @@ class BillController extends Controller
|
||||
*
|
||||
* @param BillRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(BillRequest $request): JsonResponse
|
||||
{
|
||||
|
@@ -25,13 +25,15 @@ namespace FireflyIII\Api\V1\Controllers;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Api\V1\Requests\BudgetLimitRequest;
|
||||
use FireflyIII\Api\V1\Requests\BudgetRequest;
|
||||
use FireflyIII\Api\V1\Requests\BudgetStoreRequest;
|
||||
use FireflyIII\Api\V1\Requests\BudgetUpdateRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Api\TransactionFilter;
|
||||
use FireflyIII\Transformers\AttachmentTransformer;
|
||||
use FireflyIII\Transformers\BudgetLimitTransformer;
|
||||
use FireflyIII\Transformers\BudgetTransformer;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
@@ -108,6 +110,35 @@ class BudgetController extends Controller
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function attachments(Budget $budget): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||
$collection = $this->repository->getAttachments($budget);
|
||||
|
||||
$count = $collection->count();
|
||||
$attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.budgets.attachments', [$budget->id]) . $this->buildParams());
|
||||
|
||||
/** @var AttachmentTransformer $transformer */
|
||||
$transformer = app(AttachmentTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($attachments, $transformer, 'attachments');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
@@ -179,13 +210,13 @@ class BudgetController extends Controller
|
||||
/**
|
||||
* Store a budget.
|
||||
*
|
||||
* @param BudgetRequest $request
|
||||
* @param BudgetStoreRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(BudgetRequest $request): JsonResponse
|
||||
public function store(BudgetStoreRequest $request): JsonResponse
|
||||
{
|
||||
$budget = $this->repository->store($request->getAll());
|
||||
$manager = $this->getManager();
|
||||
@@ -205,8 +236,8 @@ class BudgetController extends Controller
|
||||
* @param BudgetLimitRequest $request
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function storeBudgetLimit(BudgetLimitRequest $request, Budget $budget): JsonResponse
|
||||
{
|
||||
@@ -291,12 +322,12 @@ class BudgetController extends Controller
|
||||
/**
|
||||
* Update a budget.
|
||||
*
|
||||
* @param BudgetRequest $request
|
||||
* @param BudgetUpdateRequest $request
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function update(BudgetRequest $request, Budget $budget): JsonResponse
|
||||
public function update(BudgetUpdateRequest $request, Budget $budget): JsonResponse
|
||||
{
|
||||
$data = $request->getAll();
|
||||
$budget = $this->repository->update($budget, $data);
|
||||
|
@@ -156,16 +156,16 @@ class BudgetLimitController extends Controller
|
||||
*
|
||||
* @param BudgetLimitRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(BudgetLimitRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->getAll();
|
||||
$budget = $this->repository->findNull($data['budget_id']);
|
||||
if (null === $budget) {
|
||||
throw new FireflyException('200004: Budget does not exist.');
|
||||
throw new FireflyException('200004: Budget does not exist.'); // @codeCoverageIgnore
|
||||
}
|
||||
$data['budget'] = $budget;
|
||||
$budgetLimit = $this->blRepository->storeBudgetLimit($data);
|
||||
|
@@ -29,6 +29,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Api\TransactionFilter;
|
||||
use FireflyIII\Transformers\AttachmentTransformer;
|
||||
use FireflyIII\Transformers\CategoryTransformer;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
use FireflyIII\User;
|
||||
@@ -71,6 +72,35 @@ class CategoryController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function attachments(Category $category): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||
$collection = $this->repository->getAttachments($category);
|
||||
|
||||
$count = $collection->count();
|
||||
$attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.categories.attachments', [$category->id]) . $this->buildParams());
|
||||
|
||||
/** @var AttachmentTransformer $transformer */
|
||||
$transformer = app(AttachmentTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($attachments, $transformer, 'attachments');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
@@ -146,8 +176,8 @@ class CategoryController extends Controller
|
||||
*
|
||||
* @param CategoryRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(CategoryRequest $request): JsonResponse
|
||||
{
|
||||
|
@@ -42,10 +42,8 @@ use Illuminate\Http\JsonResponse;
|
||||
class AccountController extends Controller
|
||||
{
|
||||
use ApiSupport;
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepository;
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $repository;
|
||||
private CurrencyRepositoryInterface $currencyRepository;
|
||||
private AccountRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
* AccountController constructor.
|
||||
@@ -196,6 +194,8 @@ class AccountController extends Controller
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
'start_date' => $start->format('Y-m-d'),
|
||||
'end_date' => $end->format('Y-m-d'),
|
||||
'type' => 'line', // line, area or bar
|
||||
'yAxisID' => 0, // 0, 1, 2
|
||||
'entries' => [],
|
||||
|
@@ -85,7 +85,7 @@ class AvailableBudgetController extends Controller
|
||||
}
|
||||
$left = bcadd($availableBudget->amount, (string) $spent);
|
||||
// left less than zero? Set to zero.
|
||||
if (bccomp($left, '0') === -1) {
|
||||
if (-1 === bccomp($left, '0')) {
|
||||
$left = '0';
|
||||
}
|
||||
|
||||
|
298
app/Api/V1/Controllers/Chart/BudgetController.php
Normal file
298
app/Api/V1/Controllers/Chart/BudgetController.php
Normal file
@@ -0,0 +1,298 @@
|
||||
<?php
|
||||
/**
|
||||
* BudgetController.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\DateRequest;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class BudgetController
|
||||
*/
|
||||
class BudgetController extends Controller
|
||||
{
|
||||
private BudgetLimitRepositoryInterface $blRepository;
|
||||
private OperationsRepositoryInterface $opsRepository;
|
||||
private BudgetRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
* BudgetController constructor.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
//$this->generator = app(GeneratorInterface::class);
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
$this->opsRepository = app(OperationsRepositoryInterface::class);
|
||||
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
|
||||
|
||||
//$this->nbRepository = app(NoBudgetRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* [
|
||||
* 'label' => 'label for entire set'
|
||||
* 'currency_id' => 0,
|
||||
* 'currency_code' => 'EUR',
|
||||
* 'currency_symbol' => '$',
|
||||
* 'currency_decimal_places' => 2,
|
||||
* 'type' => 'bar', // line, area or bar
|
||||
* 'yAxisID' => 0, // 0, 1, 2
|
||||
* 'entries' => ['a' => 1, 'b' => 4],
|
||||
* ],
|
||||
*
|
||||
* @param DateRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function overview(DateRequest $request): JsonResponse
|
||||
{
|
||||
$dates = $request->getAll();
|
||||
$budgets = $this->repository->getActiveBudgets();
|
||||
$budgetNames = [];
|
||||
$currencyNames = [];
|
||||
$sets = [];
|
||||
/** @var Budget $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
$expenses = $this->getExpenses($budget, $dates['start'], $dates['end']);
|
||||
$expenses = $this->filterNulls($expenses);
|
||||
foreach ($expenses as $set) {
|
||||
$budgetNames[] = $set['budget_name'];
|
||||
$currencyNames[] = $set['currency_name'];
|
||||
$sets[] = $set;
|
||||
}
|
||||
}
|
||||
$budgetNames = array_unique($budgetNames);
|
||||
$currencyNames = array_unique($currencyNames);
|
||||
$basic = $this->createSets($budgetNames, $currencyNames);
|
||||
$filled = $this->fillSets($basic, $sets);
|
||||
$keys = array_values($filled);
|
||||
|
||||
return response()->json($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $limits
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getExpenses(Budget $budget, Carbon $start, Carbon $end): array
|
||||
{
|
||||
$limits = $this->blRepository->getBudgetLimits($budget, $start, $end);
|
||||
if (0 === $limits->count()) {
|
||||
return $this->getExpenseInRange($budget, $start, $end);
|
||||
}
|
||||
$arr = [];
|
||||
/** @var BudgetLimit $limit */
|
||||
foreach ($limits as $limit) {
|
||||
$arr[] = $this->getExpensesForLimit($limit);
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $budgetNames
|
||||
* @param array $currencyNames
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function createSets(array $budgetNames, array $currencyNames): array
|
||||
{
|
||||
$return = [];
|
||||
foreach ($currencyNames as $currencyName) {
|
||||
$entries = [];
|
||||
foreach ($budgetNames as $budgetName) {
|
||||
$label = sprintf('%s (%s)', $budgetName, $currencyName);
|
||||
$entries[$label] = '0';
|
||||
}
|
||||
|
||||
// left
|
||||
$return['left'] = [
|
||||
'label' => sprintf('%s (%s)', trans('firefly.left'), $currencyName),
|
||||
'data_type' => 'left',
|
||||
'currency_name' => $currencyName,
|
||||
'type' => 'bar',
|
||||
'yAxisID' => 0, // 0, 1, 2
|
||||
'entries' => $entries,
|
||||
];
|
||||
|
||||
// spent_capped
|
||||
$return['spent_capped'] = [
|
||||
'label' => sprintf('%s (%s)', trans('firefly.spent'), $currencyName),
|
||||
'data_type' => 'spent_capped',
|
||||
'currency_name' => $currencyName,
|
||||
'type' => 'bar',
|
||||
'yAxisID' => 0, // 0, 1, 2
|
||||
'entries' => $entries,
|
||||
];
|
||||
|
||||
// overspent
|
||||
$return['overspent'] = [
|
||||
'label' => sprintf('%s (%s)', trans('firefly.overspent'), $currencyName),
|
||||
'data_type' => 'overspent',
|
||||
'currency_name' => $currencyName,
|
||||
'type' => 'bar',
|
||||
'yAxisID' => 0, // 0, 1, 2
|
||||
'entries' => $entries,
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $basic
|
||||
* @param array $sets
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function fillSets(array $basic, array $sets): array
|
||||
{
|
||||
foreach ($sets as $set) {
|
||||
$label = $set['label'];
|
||||
//$basic['spent']['entries'][$label] = $set['entries']['spent'];
|
||||
$basic['spent_capped']['entries'][$label] = $set['entries']['spent_capped'];
|
||||
$basic['left']['entries'][$label] = $set['entries']['left'];
|
||||
$basic['overspent']['entries'][$label] = $set['entries']['overspent'];
|
||||
}
|
||||
|
||||
return $basic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $expenses
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function filterNulls(array $expenses): array
|
||||
{
|
||||
$return = [];
|
||||
/** @var array|null $arr */
|
||||
foreach ($expenses as $arr) {
|
||||
if (null !== $arr) {
|
||||
$return[] = $arr;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getExpenseInRange(Budget $budget, Carbon $start, Carbon $end): array
|
||||
{
|
||||
$spent = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$budget]), null);
|
||||
$return = [];
|
||||
/** @var array $set */
|
||||
foreach ($spent as $set) {
|
||||
$current = [
|
||||
'label' => sprintf('%s (%s)', $budget->name, $set['currency_name']),
|
||||
'budget_name' => $budget->name,
|
||||
'start_date' => $start->format('Y-m-d'),
|
||||
'end_date' => $end->format('Y-m-d'),
|
||||
'currency_id' => (int) $set['currency_id'],
|
||||
'currency_code' => $set['currency_code'],
|
||||
'currency_name' => $set['currency_name'],
|
||||
'currency_symbol' => $set['currency_symbol'],
|
||||
'currency_decimal_places' => (int) $set['currency_decimal_places'],
|
||||
'type' => 'bar', // line, area or bar,
|
||||
'entries' => [],
|
||||
];
|
||||
$sumSpent = bcmul($set['sum'], '-1'); // spent
|
||||
$current['entries']['spent'] = $sumSpent;
|
||||
$current['entries']['amount'] = '0';
|
||||
$current['entries']['spent_capped'] = $sumSpent;
|
||||
$current['entries']['left'] = '0';
|
||||
$current['entries']['overspent'] = '0';
|
||||
$return[] = $current;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetLimit $limit
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private function getExpensesForLimit(BudgetLimit $limit): ?array
|
||||
{
|
||||
$budget = $limit->budget;
|
||||
$spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $limit->transactionCurrency);
|
||||
$currency = $limit->transactionCurrency;
|
||||
// when limited to a currency, the count is always one. Or it's empty.
|
||||
$set = array_shift($spent);
|
||||
if (null === $set) {
|
||||
return null;
|
||||
}
|
||||
$return = [
|
||||
'label' => sprintf('%s (%s)', $budget->name, $set['currency_name']),
|
||||
'budget_name' => $budget->name,
|
||||
'start_date' => $limit->start_date->format('Y-m-d'),
|
||||
'end_date' => $limit->end_date->format('Y-m-d'),
|
||||
'currency_id' => (int) $currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_name' => $currency->name,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => (int) $currency->decimal_places,
|
||||
'type' => 'bar', // line, area or bar,
|
||||
'entries' => [],
|
||||
];
|
||||
$sumSpent = bcmul($set['sum'], '-1'); // spent
|
||||
$return['entries']['spent'] = $sumSpent;
|
||||
$return['entries']['amount'] = $limit->amount;
|
||||
$return['entries']['spent_capped'] = 1 === bccomp($sumSpent, $limit->amount) ? $limit->amount : $sumSpent;
|
||||
$return['entries']['left'] = 1 === bccomp($limit->amount, $sumSpent) ? bcadd($set['sum'], $limit->amount) : '0'; // left
|
||||
$return['entries']['overspent'] = 1 === bccomp($limit->amount, $sumSpent) ? '0' : bcmul(bcadd($set['sum'], $limit->amount), '-1'); // overspent
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
}
|
@@ -74,8 +74,6 @@ class CategoryController extends Controller
|
||||
* @param DateRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
*
|
||||
* TODO after 4.8,0, simplify
|
||||
*/
|
||||
public function overview(DateRequest $request): JsonResponse
|
||||
{
|
||||
@@ -89,32 +87,15 @@ class CategoryController extends Controller
|
||||
|
||||
$tempData = [];
|
||||
$spentWith = $this->opsRepository->listExpenses($start, $end);
|
||||
$earnedWith = $this->opsRepository->listIncome($start, $end);
|
||||
$spentWithout = $this->noCatRepository->listExpenses($start, $end);
|
||||
$earnedWithout = $this->noCatRepository->listIncome($start, $end);
|
||||
$categories = [];
|
||||
|
||||
|
||||
foreach ([$spentWith, $earnedWith] as $set) {
|
||||
foreach ([$spentWith, $spentWithout,] as $set) {
|
||||
foreach ($set as $currency) {
|
||||
foreach ($currency['categories'] as $category) {
|
||||
$categories[] = $category['name'];
|
||||
$inKey = sprintf('%d-i', $currency['currency_id']);
|
||||
$outKey = sprintf('%d-e', $currency['currency_id']);
|
||||
// make data arrays if not yet present.
|
||||
$tempData[$inKey] = $tempData[$inKey] ?? [
|
||||
'currency_id' => $currency['currency_id'],
|
||||
'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]),
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_decimal_places' => $currency['currency_decimal_places'],
|
||||
'type' => 'bar', // line, area or bar
|
||||
'yAxisID' => 0, // 0, 1, 2
|
||||
'entries' => [
|
||||
// per category:
|
||||
// "category" => 5,
|
||||
],
|
||||
];
|
||||
$tempData[$outKey] = $tempData[$outKey] ?? [
|
||||
'currency_id' => $currency['currency_id'],
|
||||
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]),
|
||||
@@ -123,16 +104,12 @@ class CategoryController extends Controller
|
||||
'currency_decimal_places' => $currency['currency_decimal_places'],
|
||||
'type' => 'bar', // line, area or bar
|
||||
'yAxisID' => 0, // 0, 1, 2
|
||||
'entries' => [
|
||||
// per category:
|
||||
// "category" => 5,
|
||||
],
|
||||
'entries' => [],
|
||||
];
|
||||
|
||||
foreach ($category['transaction_journals'] as $journal) {
|
||||
// is it expense or income?
|
||||
$letter = -1 === bccomp($journal['amount'], '0') ? 'e' : 'i';
|
||||
$currentKey = sprintf('%d-%s', $currency['currency_id'], $letter);
|
||||
$currentKey = sprintf('%d-%s', $currency['currency_id'], 'e');
|
||||
$name = $category['name'];
|
||||
$tempData[$currentKey]['entries'][$name] = $tempData[$currentKey]['entries'][$name] ?? '0';
|
||||
$tempData[$currentKey]['entries'][$name] = bcadd($tempData[$currentKey]['entries'][$name], $journal['amount']);
|
||||
@@ -141,49 +118,6 @@ class CategoryController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
foreach ([$spentWithout, $earnedWithout] as $set) {
|
||||
foreach ($set as $currency) {
|
||||
$inKey = sprintf('%d-i', $currency['currency_id']);
|
||||
$outKey = sprintf('%d-e', $currency['currency_id']);
|
||||
$categories[] = (string)trans('firefly.no_category');
|
||||
// make data arrays if not yet present.
|
||||
$tempData[$inKey] = $tempData[$inKey] ?? [
|
||||
'currency_id' => $currency['currency_id'],
|
||||
'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]),
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_decimal_places' => $currency['currency_decimal_places'],
|
||||
'type' => 'bar', // line, area or bar
|
||||
'yAxisID' => 0, // 0, 1, 2
|
||||
'entries' => [
|
||||
// per category:
|
||||
// "category" => 5,
|
||||
],
|
||||
];
|
||||
$tempData[$outKey] = $tempData[$outKey] ?? [
|
||||
'currency_id' => $currency['currency_id'],
|
||||
'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]),
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_decimal_places' => $currency['currency_decimal_places'],
|
||||
'type' => 'bar', // line, area or bar
|
||||
'yAxisID' => 0, // 0, 1, 2
|
||||
'entries' => [
|
||||
// per category:
|
||||
// "category" => 5,
|
||||
],
|
||||
];
|
||||
foreach ($currency['transaction_journals'] as $journal) {
|
||||
// is it expense or income?
|
||||
$letter = -1 === bccomp($journal['amount'], '0') ? 'e' : 'i';
|
||||
$currentKey = sprintf('%d-%s', $currency['currency_id'], $letter);
|
||||
$name = (string)trans('firefly.no_category');
|
||||
$tempData[$currentKey]['entries'][$name] = $tempData[$currentKey]['entries'][$name] ?? '0';
|
||||
$tempData[$currentKey]['entries'][$name] = bcadd($tempData[$currentKey]['entries'][$name], $journal['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// re-sort every spent array and add 0 for missing entries.
|
||||
foreach ($tempData as $index => $set) {
|
||||
$oldSet = $set['entries'];
|
||||
|
@@ -51,7 +51,6 @@ class ConfigurationController extends Controller
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
/** @noinspection UnusedConstructorDependenciesInspection */
|
||||
$this->repository = app(UserRepositoryInterface::class);
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
@@ -109,13 +108,12 @@ class ConfigurationController extends Controller
|
||||
$lastCheck = app('fireflyconfig')->get('last_update_check');
|
||||
/** @var Configuration $singleUser */
|
||||
$singleUser = app('fireflyconfig')->get('single_user_mode');
|
||||
$data = [
|
||||
|
||||
return [
|
||||
'is_demo_site' => null === $isDemoSite ? null : $isDemoSite->data,
|
||||
'permission_update_check' => null === $updateCheck ? null : (int) $updateCheck->data,
|
||||
'last_update_check' => null === $lastCheck ? null : (int) $lastCheck->data,
|
||||
'single_user_mode' => null === $singleUser ? null : $singleUser->data,
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@@ -302,9 +302,9 @@ class CurrencyController extends Controller
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @codeCoverageIgnore
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function delete(TransactionCurrency $currency): JsonResponse
|
||||
{
|
||||
@@ -318,6 +318,10 @@ class CurrencyController extends Controller
|
||||
if ($this->repository->currencyInUse($currency)) {
|
||||
throw new FireflyException('200006: Currency in use.'); // @codeCoverageIgnore
|
||||
}
|
||||
if ($this->repository->isFallbackCurrency($currency)) {
|
||||
throw new FireflyException('200026: Currency is fallback.'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$this->repository->destroy($currency);
|
||||
|
||||
return response()->json([], 204);
|
||||
@@ -563,13 +567,34 @@ class CurrencyController extends Controller
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a currency.
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function showDefault(): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
$currency = app('amount')->getDefaultCurrencyByUser(auth()->user());
|
||||
$this->parameters->set('defaultCurrency', $currency);
|
||||
|
||||
/** @var CurrencyTransformer $transformer */
|
||||
$transformer = app(CurrencyTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new Item($currency, $transformer, 'currencies');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store new currency.
|
||||
*
|
||||
* @param CurrencyRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(CurrencyRequest $request): JsonResponse
|
||||
{
|
||||
|
@@ -69,8 +69,8 @@ class CurrencyExchangeRateController extends Controller
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
|
278
app/Api/V1/Controllers/Data/DestroyController.php
Normal file
278
app/Api/V1/Controllers/Data/DestroyController.php
Normal file
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
/**
|
||||
* DestroyController.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Data;
|
||||
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\DataDestroyRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Destroy\AccountDestroyService;
|
||||
use FireflyIII\Services\Internal\Destroy\JournalDestroyService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
/**
|
||||
* Class DestroyController
|
||||
*/
|
||||
class DestroyController extends Controller
|
||||
{
|
||||
/**
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function destroy(DataDestroyRequest $request): JsonResponse
|
||||
{
|
||||
$objects = $request->getObjects();
|
||||
|
||||
switch ($objects) {
|
||||
default:
|
||||
throw new FireflyException(sprintf('This endpoint can\'t handle object "%s"', $objects));
|
||||
case 'budgets':
|
||||
$this->destroyBudgets();
|
||||
break;
|
||||
case 'bills':
|
||||
$this->destroyBills();
|
||||
break;
|
||||
case 'piggy_banks':
|
||||
$this->destroyPiggyBanks();
|
||||
break;
|
||||
case 'rules':
|
||||
$this->destroyRules();
|
||||
break;
|
||||
case 'recurring':
|
||||
$this->destroyRecurringTransactions();
|
||||
break;
|
||||
case 'categories':
|
||||
$this->destroyCategories();
|
||||
break;
|
||||
case 'tags':
|
||||
$this->destroyTags();
|
||||
break;
|
||||
case 'object_groups':
|
||||
$this->destroyObjectGroups();
|
||||
break;
|
||||
case 'accounts':
|
||||
$this->destroyAccounts(
|
||||
[
|
||||
AccountType::ASSET, AccountType::DEFAULT,
|
||||
AccountType::BENEFICIARY, AccountType::EXPENSE,
|
||||
AccountType::REVENUE, AccountType::INITIAL_BALANCE,
|
||||
AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'asset_accounts':
|
||||
$this->destroyAccounts(
|
||||
[
|
||||
AccountType::ASSET, AccountType::DEFAULT,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'expense_accounts':
|
||||
$this->destroyAccounts(
|
||||
[
|
||||
AccountType::BENEFICIARY, AccountType::EXPENSE,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'revenue_accounts':
|
||||
$this->destroyAccounts(
|
||||
[
|
||||
AccountType::REVENUE,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'liabilities':
|
||||
$this->destroyAccounts(
|
||||
[
|
||||
AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'transactions':
|
||||
$this->destroyTransactions(
|
||||
[
|
||||
TransactionType::WITHDRAWAL,
|
||||
TransactionType::DEPOSIT,
|
||||
TransactionType::TRANSFER,
|
||||
TransactionType::RECONCILIATION,
|
||||
TransactionType::OPENING_BALANCE,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'withdrawals':
|
||||
$this->destroyTransactions(
|
||||
[
|
||||
TransactionType::WITHDRAWAL,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'deposits':
|
||||
$this->destroyTransactions(
|
||||
[
|
||||
TransactionType::DEPOSIT,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'transfers':
|
||||
$this->destroyTransactions(
|
||||
[
|
||||
TransactionType::TRANSFER,
|
||||
]
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
*/
|
||||
private function destroyAccounts(array $types): void
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$collection = $repository->getAccountsByType($types);
|
||||
$service = app(AccountDestroyService::class);
|
||||
/** @var Account $account */
|
||||
foreach ($collection as $account) {
|
||||
$service->destroy($account, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyBills(): void
|
||||
{
|
||||
/** @var BillRepositoryInterface $repository */
|
||||
$repository = app(BillRepositoryInterface::class);
|
||||
$repository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyBudgets(): void
|
||||
{
|
||||
/** @var AvailableBudgetRepositoryInterface $abRepository */
|
||||
$abRepository = app(AvailableBudgetRepositoryInterface::class);
|
||||
$abRepository->destroyAll();
|
||||
|
||||
/** @var BudgetLimitRepositoryInterface $blRepository */
|
||||
$blRepository = app(BudgetLimitRepositoryInterface::class);
|
||||
$blRepository->destroyAll();
|
||||
|
||||
/** @var BudgetRepositoryInterface $budgetRepository */
|
||||
$budgetRepository = app(BudgetRepositoryInterface::class);
|
||||
$budgetRepository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyCategories(): void
|
||||
{
|
||||
/** @var CategoryRepositoryInterface $categoryRepos */
|
||||
$categoryRepos = app(CategoryRepositoryInterface::class);
|
||||
$categoryRepos->destroyAll();
|
||||
}
|
||||
|
||||
private function destroyObjectGroups(): void
|
||||
{
|
||||
/** @var ObjectGroupRepositoryInterface $repository */
|
||||
$repository = app(ObjectGroupRepositoryInterface::class);
|
||||
$repository->deleteAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyPiggyBanks(): void
|
||||
{
|
||||
/** @var PiggyBankRepositoryInterface $repository */
|
||||
$repository = app(PiggyBankRepositoryInterface::class);
|
||||
$repository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyRecurringTransactions(): void
|
||||
{
|
||||
/** @var RecurringRepositoryInterface $repository */
|
||||
$repository = app(RecurringRepositoryInterface::class);
|
||||
$repository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyRules(): void
|
||||
{
|
||||
/** @var RuleGroupRepositoryInterface $repository */
|
||||
$repository = app(RuleGroupRepositoryInterface::class);
|
||||
$repository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyTags(): void
|
||||
{
|
||||
/** @var TagRepositoryInterface $tagRepository */
|
||||
$tagRepository = app(TagRepositoryInterface::class);
|
||||
$tagRepository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
*/
|
||||
private function destroyTransactions(array $types): void
|
||||
{
|
||||
/** @var JournalRepositoryInterface $repository */
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
$journals = $repository->findByType($types);
|
||||
$service = app(JournalDestroyService::class);
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach($journals as $journal) {
|
||||
$service->destroy($journal);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,178 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ImportController.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers;
|
||||
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Api\TransactionFilter;
|
||||
use FireflyIII\Transformers\ImportJobTransformer;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
use League\Fractal\Resource\Item;
|
||||
|
||||
/**
|
||||
* Class ImportController
|
||||
*/
|
||||
class ImportController extends Controller
|
||||
{
|
||||
use TransactionFilter;
|
||||
/** @var ImportJobRepositoryInterface Import job repository. */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* ImportController constructor.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$this->repository = app(ImportJobRepositoryInterface::class);
|
||||
$this->repository->setUser($user);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JsonResponse
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function listAll(): JsonResponse
|
||||
{
|
||||
// create some objects:
|
||||
$manager = $this->getManager();
|
||||
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||
|
||||
// get list of accounts. Count it and split it.
|
||||
$collection = $this->repository->get();
|
||||
$count = $collection->count();
|
||||
$importJobs = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($importJobs, $count, $pageSize, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.import.list') . $this->buildParams());
|
||||
|
||||
/** @var ImportJobTransformer $transformer */
|
||||
$transformer = app(ImportJobTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($importJobs, $transformer, 'import_jobs');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ImportJob $importJob
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function show(ImportJob $importJob): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
/** @var ImportJobTransformer $transformer */
|
||||
$transformer = app(ImportJobTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new Item($importJob, $transformer, 'import_jobs');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show all transactions
|
||||
*
|
||||
* @param Request $request
|
||||
* @param ImportJob $importJob
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function transactions(Request $request, ImportJob $importJob): JsonResponse
|
||||
{
|
||||
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||
$type = $request->get('type') ?? 'default';
|
||||
$this->parameters->set('type', $type);
|
||||
|
||||
$types = $this->mapTransactionTypes($this->parameters->get('type'));
|
||||
$manager = $this->getManager();
|
||||
|
||||
$tag = $importJob->tag;
|
||||
$transactions = new Collection();
|
||||
$paginator = new LengthAwarePaginator($transactions, 0, $pageSize);
|
||||
$paginator->setPath(route('api.v1.import.transactions', [$importJob->key]) . $this->buildParams());
|
||||
|
||||
if (null !== $tag) {
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
|
||||
// use new group collector:
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector
|
||||
->setUser($admin)
|
||||
// filter on tag.
|
||||
->setTag($tag)
|
||||
// all info needed for the API:
|
||||
->withAPIInformation()
|
||||
// set page size:
|
||||
->setLimit($pageSize)
|
||||
// set page to retrieve
|
||||
->setPage($this->parameters->get('page'))
|
||||
// set types of transactions to return.
|
||||
->setTypes($types);
|
||||
|
||||
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
|
||||
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
|
||||
}
|
||||
$paginator = $collector->getPaginatedGroups();
|
||||
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
|
||||
$transactions = $paginator->getCollection();
|
||||
}
|
||||
|
||||
/** @var TransactionGroupTransformer $transformer */
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($transactions, $transformer, 'transactions');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
}
|
@@ -79,9 +79,9 @@ class LinkTypeController extends Controller
|
||||
*
|
||||
* @param LinkType $linkType
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @codeCoverageIgnore
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function delete(LinkType $linkType): JsonResponse
|
||||
{
|
||||
@@ -151,8 +151,8 @@ class LinkTypeController extends Controller
|
||||
*
|
||||
* @param LinkTypeRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(LinkTypeRequest $request): JsonResponse
|
||||
{
|
||||
@@ -241,8 +241,8 @@ class LinkTypeController extends Controller
|
||||
* @param LinkTypeRequest $request
|
||||
* @param LinkType $linkType
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function update(LinkTypeRequest $request, LinkType $linkType): JsonResponse
|
||||
{
|
||||
|
196
app/Api/V1/Controllers/ObjectGroupController.php
Normal file
196
app/Api/V1/Controllers/ObjectGroupController.php
Normal file
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
/**
|
||||
* GroupController.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\ObjectGroupUpdateRequest;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\ObjectGroup;
|
||||
use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface;
|
||||
use FireflyIII\Transformers\ObjectGroupTransformer;
|
||||
use FireflyIII\Transformers\PiggyBankTransformer;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
use League\Fractal\Resource\Item;
|
||||
|
||||
/**
|
||||
* Class GroupController.
|
||||
*
|
||||
*/
|
||||
class ObjectGroupController extends Controller
|
||||
{
|
||||
private ObjectGroupRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
* ObjectGroupController constructor.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$this->repository = app(ObjectGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($user);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param ObjectGroup $objectGroup
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function delete(ObjectGroup $objectGroup): JsonResponse
|
||||
{
|
||||
$this->repository->destroy($objectGroup);
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
|
||||
// types to get, page size:
|
||||
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||
|
||||
// get list of accounts. Count it and split it.
|
||||
$collection = $this->repository->get();
|
||||
$count = $collection->count();
|
||||
$objectGroups = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($objectGroups, $count, $pageSize, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.object-groups.index') . $this->buildParams());
|
||||
|
||||
/** @var ObjectGroupTransformer $transformer */
|
||||
$transformer = app(ObjectGroupTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($objectGroups, $transformer, 'object_groups');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List all piggies under the object group.
|
||||
*
|
||||
* @param ObjectGroup $objectGroup
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
*/
|
||||
public function piggyBanks(ObjectGroup $objectGroup): JsonResponse
|
||||
{
|
||||
// create some objects:
|
||||
$manager = $this->getManager();
|
||||
|
||||
// types to get, page size:
|
||||
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||
|
||||
// get list of piggy banks. Count it and split it.
|
||||
$collection = $this->repository->getPiggyBanks($objectGroup);
|
||||
$count = $collection->count();
|
||||
$piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.object-groups.piggy_banks', [$objectGroup->id]) . $this->buildParams());
|
||||
|
||||
/** @var PiggyBankTransformer $transformer */
|
||||
$transformer = app(PiggyBankTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($piggyBanks, $transformer, 'piggy_banks');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Show single instance.
|
||||
*
|
||||
* @param ObjectGroup $objectGroup
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function show(ObjectGroup $objectGroup): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
|
||||
/** @var ObjectGroupTransformer $transformer */
|
||||
$transformer = app(ObjectGroupTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
$resource = new Item($objectGroup, $transformer, 'object_groups');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update object.
|
||||
*
|
||||
* @param ObjectGroupUpdateRequest $request
|
||||
* @param Account $account
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function update(ObjectGroupUpdateRequest $request, ObjectGroup $objectGroup): JsonResponse
|
||||
{
|
||||
$data = $request->getUpdateData();
|
||||
$this->repository->update($objectGroup, $data);
|
||||
$this->repository->sort();
|
||||
$manager = $this->getManager();
|
||||
|
||||
/** @var ObjectGroupTransformer $transformer */
|
||||
$transformer = app(ObjectGroupTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
$resource = new Item($objectGroup, $transformer, 'object_groups');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
}
|
@@ -24,9 +24,11 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Api\V1\Controllers;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\PiggyBankRequest;
|
||||
use FireflyIII\Api\V1\Requests\PiggyBankStoreRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\Transformers\AttachmentTransformer;
|
||||
use FireflyIII\Transformers\PiggyBankEventTransformer;
|
||||
use FireflyIII\Transformers\PiggyBankTransformer;
|
||||
use FireflyIII\User;
|
||||
@@ -83,6 +85,36 @@ class PiggyBankController extends Controller
|
||||
return response()->json([], 204);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param PiggyBank $piggyBank
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function attachments(PiggyBank $piggyBank): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||
$collection = $this->repository->getAttachments($piggyBank);
|
||||
|
||||
$count = $collection->count();
|
||||
$attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.piggy_banks.attachments', [$piggyBank->id]) . $this->buildParams());
|
||||
|
||||
/** @var AttachmentTransformer $transformer */
|
||||
$transformer = app(AttachmentTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($attachments, $transformer, 'attachments');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* List all of them.
|
||||
*
|
||||
@@ -173,12 +205,12 @@ class PiggyBankController extends Controller
|
||||
/**
|
||||
* Store new object.
|
||||
*
|
||||
* @param PiggyBankRequest $request
|
||||
* @param PiggyBankStoreRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(PiggyBankRequest $request): JsonResponse
|
||||
public function store(PiggyBankStoreRequest $request): JsonResponse
|
||||
{
|
||||
$piggyBank = $this->repository->store($request->getAll());
|
||||
$manager = $this->getManager();
|
||||
@@ -209,7 +241,6 @@ class PiggyBankController extends Controller
|
||||
$this->repository->setCurrentAmount($piggyBank, $data['current_amount']);
|
||||
}
|
||||
|
||||
|
||||
$manager = $this->getManager();
|
||||
/** @var PiggyBankTransformer $transformer */
|
||||
$transformer = app(PiggyBankTransformer::class);
|
||||
|
@@ -149,8 +149,8 @@ class RecurrenceController extends Controller
|
||||
*
|
||||
* @param RecurrenceStoreRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(RecurrenceStoreRequest $request): JsonResponse
|
||||
{
|
||||
@@ -224,9 +224,9 @@ class RecurrenceController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @codeCoverageIgnore
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function trigger(): JsonResponse
|
||||
{
|
||||
|
@@ -214,8 +214,8 @@ class RuleController extends Controller
|
||||
* @param RuleTestRequest $request
|
||||
* @param Rule $rule
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function testRule(RuleTestRequest $request, Rule $rule): JsonResponse
|
||||
{
|
||||
|
@@ -246,9 +246,9 @@ class RuleGroupController extends Controller
|
||||
* @param RuleGroupTestRequest $request
|
||||
* @param RuleGroup $group
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function testGroup(RuleGroupTestRequest $request, RuleGroup $group): JsonResponse
|
||||
{
|
||||
@@ -305,8 +305,8 @@ class RuleGroupController extends Controller
|
||||
* @param RuleGroupTriggerRequest $request
|
||||
* @param RuleGroup $group
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function triggerGroup(RuleGroupTriggerRequest $request, RuleGroup $group): JsonResponse
|
||||
{
|
||||
|
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* AccountController.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -19,8 +20,9 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Search;
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Search;
|
||||
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
@@ -32,6 +34,7 @@ use Illuminate\Http\Response;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class AccountController
|
||||
@@ -40,8 +43,7 @@ class AccountController extends Controller
|
||||
{
|
||||
use AccountFilter;
|
||||
|
||||
/** @var array */
|
||||
private $validFields;
|
||||
private array $validFields;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -62,6 +64,7 @@ class AccountController extends Controller
|
||||
*/
|
||||
public function search(Request $request)
|
||||
{
|
||||
Log::debug('Now in account search()');
|
||||
$manager = $this->getManager();
|
||||
$query = $request->get('query');
|
||||
$field = $request->get('field');
|
||||
@@ -70,6 +73,8 @@ class AccountController extends Controller
|
||||
return response(null, 422);
|
||||
}
|
||||
$types = $this->mapAccountTypes($type);
|
||||
Log::debug(sprintf('Going to search for "%s" in types', $query), $types);
|
||||
|
||||
/** @var AccountSearch $search */
|
||||
$search = app(AccountSearch::class);
|
||||
$search->setUser(auth()->user());
|
||||
@@ -79,6 +84,8 @@ class AccountController extends Controller
|
||||
|
||||
$accounts = $search->search();
|
||||
|
||||
Log::debug(sprintf('Found %d accounts', $accounts->count()));
|
||||
|
||||
/** @var AccountTransformer $transformer */
|
||||
$transformer = app(AccountTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
@@ -91,5 +98,4 @@ class AccountController extends Controller
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
}
|
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* TransactionController.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -19,51 +20,51 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Search;
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Search;
|
||||
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use FireflyIII\Support\Search\TransactionSearch;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection;
|
||||
|
||||
/**
|
||||
* Class TransactionController
|
||||
*/
|
||||
class TransactionController extends Controller
|
||||
{
|
||||
/** @var string */
|
||||
const SEARCH_ALL = 'all';
|
||||
/** @var string */
|
||||
const SEARCH_DESCRIPTION = 'description';
|
||||
/** @var string */
|
||||
const SEARCH_NOTES = 'notes';
|
||||
/** @var string */
|
||||
const SEARCH_ACCOUNTS = 'accounts';
|
||||
/** @var array */
|
||||
private $validFields;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->validFields = [
|
||||
self::SEARCH_ALL,
|
||||
self::SEARCH_DESCRIPTION,
|
||||
self::SEARCH_NOTES,
|
||||
self::SEARCH_ACCOUNTS,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param SearchInterface $searcher
|
||||
*
|
||||
* @return JsonResponse|Response
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function search(Request $request)
|
||||
public function search(Request $request, SearchInterface $searcher): JsonResponse
|
||||
{
|
||||
die('the route is present but nobody\'s home.');
|
||||
}
|
||||
$manager = $this->getManager();
|
||||
$fullQuery = (string) $request->get('query');
|
||||
$page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page');
|
||||
|
||||
$searcher->parseQuery($fullQuery);
|
||||
$searcher->setPage($page);
|
||||
$searcher->setLimit((int) config('firefly.search_result_limit'));
|
||||
$groups = $searcher->searchTransactions();
|
||||
$parameters = ['search' => $fullQuery];
|
||||
$url = route('api.v1.search.transactions') . '?' . http_build_query($parameters);
|
||||
$groups->setPath($url);
|
||||
$transactions = $groups->getCollection();
|
||||
|
||||
/** @var TransactionGroupTransformer $transformer */
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new Collection($transactions, $transformer, 'transactions');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($groups));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
}
|
@@ -1,116 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* TransferController.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Search;
|
||||
|
||||
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Search\TransferRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Support\Search\TransferSearch;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
|
||||
/**
|
||||
* Class TransferController
|
||||
*/
|
||||
class TransferController extends Controller
|
||||
{
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JsonResponse|Response
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function search(TransferRequest $request)
|
||||
{
|
||||
// configure transfer search to search for a > b
|
||||
$search = app(TransferSearch::class);
|
||||
$search->setSource($request->get('source'));
|
||||
$search->setDestination($request->get('destination'));
|
||||
$search->setAmount($request->get('amount'));
|
||||
$search->setDescription($request->get('description'));
|
||||
$search->setDate($request->get('date'));
|
||||
|
||||
$left = $search->search();
|
||||
|
||||
// configure transfer search to search for b > a
|
||||
$search->setSource($request->get('destination'));
|
||||
$search->setDestination($request->get('source'));
|
||||
$search->setAmount($request->get('amount'));
|
||||
$search->setDescription($request->get('description'));
|
||||
$search->setDate($request->get('date'));
|
||||
|
||||
$right = $search->search();
|
||||
|
||||
// add parameters to URL:
|
||||
$this->parameters->set('source', $request->get('source'));
|
||||
$this->parameters->set('destination', $request->get('destination'));
|
||||
$this->parameters->set('amount', $request->get('amount'));
|
||||
$this->parameters->set('description', $request->get('description'));
|
||||
$this->parameters->set('date', $request->get('date'));
|
||||
|
||||
// get all journal ID's.
|
||||
$total = $left->merge($right)->unique('id')->pluck('id')->toArray();
|
||||
if (0 === count($total)) {
|
||||
// forces search to be empty.
|
||||
$total = [-1];
|
||||
}
|
||||
|
||||
// collector to return results.
|
||||
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||
$manager = $this->getManager();
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
|
||||
// use new group collector:
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector
|
||||
->setUser($admin)
|
||||
// all info needed for the API:
|
||||
->withAPIInformation()
|
||||
// set page size:
|
||||
->setLimit($pageSize)
|
||||
// set page to retrieve
|
||||
->setPage(1)
|
||||
->setJournalIds($total);
|
||||
|
||||
$paginator = $collector->getPaginatedGroups();
|
||||
$paginator->setPath(route('api.v1.search.transfers') . $this->buildParams());
|
||||
$transactions = $paginator->getCollection();
|
||||
|
||||
/** @var TransactionGroupTransformer $transformer */
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($transactions, $transformer, 'transactions');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
}
|
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Api\V1\Requests\DateRequest;
|
||||
@@ -42,7 +41,6 @@ use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class SummaryController
|
||||
@@ -98,8 +96,8 @@ class SummaryController extends Controller
|
||||
/**
|
||||
* @param DateRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function basic(DateRequest $request): JsonResponse
|
||||
{
|
||||
@@ -125,7 +123,6 @@ class SummaryController extends Controller
|
||||
}
|
||||
|
||||
return response()->json($return);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,25 +149,6 @@ class SummaryController extends Controller
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will scroll through the results of the spentInPeriodMc() array and return the correct info.
|
||||
*
|
||||
* @param array $spentInfo
|
||||
* @param TransactionCurrency $currency
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function findInSpentArray(array $spentInfo, TransactionCurrency $currency): string
|
||||
{
|
||||
foreach ($spentInfo as $array) {
|
||||
if ($array['currency_id'] === $currency->id) {
|
||||
return (string)$array['amount'];
|
||||
}
|
||||
}
|
||||
|
||||
return '0'; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
@@ -198,7 +176,6 @@ class SummaryController extends Controller
|
||||
$set = $collector->getExtractedJournals();
|
||||
/** @var array $transactionJournal */
|
||||
foreach ($set as $transactionJournal) {
|
||||
|
||||
$currencyId = (int) $transactionJournal['currency_id'];
|
||||
$incomes[$currencyId] = $incomes[$currencyId] ?? '0';
|
||||
$incomes[$currencyId] = bcadd($incomes[$currencyId], bcmul($transactionJournal['amount'], '-1'));
|
||||
@@ -340,8 +317,8 @@ class SummaryController extends Controller
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
* @throws Exception
|
||||
* @return array
|
||||
*/
|
||||
private function getLeftToSpendInfo(Carbon $start, Carbon $end): array
|
||||
{
|
||||
@@ -374,13 +351,17 @@ class SummaryController extends Controller
|
||||
'value_parsed' => app('amount')->formatFlat($row['currency_symbol'], $row['currency_decimal_places'], $leftToSpend, false),
|
||||
'local_icon' => 'money',
|
||||
'sub_title' => (string) trans(
|
||||
'firefly.box_spend_per_day', ['amount' => app('amount')->formatFlat(
|
||||
$row['currency_symbol'], $row['currency_decimal_places'], $perDay, false
|
||||
'firefly.box_spend_per_day',
|
||||
['amount' => app('amount')->formatFlat(
|
||||
$row['currency_symbol'],
|
||||
$row['currency_decimal_places'],
|
||||
$perDay,
|
||||
false
|
||||
)]
|
||||
),
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
@@ -443,5 +424,4 @@ class SummaryController extends Controller
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -25,12 +25,13 @@ namespace FireflyIII\Api\V1\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Api\V1\Requests\DateRequest;
|
||||
use FireflyIII\Api\V1\Requests\TagUpdateRequest;
|
||||
use FireflyIII\Api\V1\Requests\TagStoreRequest;
|
||||
use FireflyIII\Api\V1\Requests\TagUpdateRequest;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Api\TransactionFilter;
|
||||
use FireflyIII\Transformers\AttachmentTransformer;
|
||||
use FireflyIII\Transformers\TagTransformer;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
use FireflyIII\User;
|
||||
@@ -138,6 +139,36 @@ class TagController extends Controller
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Tag $tag
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function attachments(Tag $tag): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||
$collection = $this->repository->getAttachments($tag);
|
||||
|
||||
$count = $collection->count();
|
||||
$attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.tags.attachments', [$tag->id]) . $this->buildParams());
|
||||
|
||||
/** @var AttachmentTransformer $transformer */
|
||||
$transformer = app(AttachmentTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($attachments, $transformer, 'attachments');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* List single resource.
|
||||
*
|
||||
|
@@ -29,6 +29,7 @@ use FireflyIII\Api\V1\Requests\TransactionUpdateRequest;
|
||||
use FireflyIII\Events\StoredTransactionGroup;
|
||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||
use FireflyIII\Exceptions\DuplicateTransactionException;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
@@ -109,7 +110,6 @@ class TransactionController extends Controller
|
||||
$resource = new FractalCollection($attachments, $transformer, 'attachments');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,7 +214,6 @@ class TransactionController extends Controller
|
||||
$resource = new FractalCollection($events, $transformer, 'piggy_bank_events');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,10 +269,12 @@ class TransactionController extends Controller
|
||||
*
|
||||
* @param TransactionStoreRequest $request
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(TransactionStoreRequest $request): JsonResponse
|
||||
{
|
||||
Log::debug('Now in API TransactionController::store()');
|
||||
$data = $request->getAll();
|
||||
$data['user'] = auth()->user()->id;
|
||||
|
||||
@@ -283,6 +284,7 @@ class TransactionController extends Controller
|
||||
try {
|
||||
$transactionGroup = $this->groupRepository->store($data);
|
||||
} catch (DuplicateTransactionException $e) {
|
||||
Log::warning('Caught a duplicate transaction. Return error message.');
|
||||
// return bad validation message.
|
||||
// TODO use Laravel's internal validation thing to do this.
|
||||
$response = [
|
||||
@@ -293,9 +295,22 @@ class TransactionController extends Controller
|
||||
];
|
||||
|
||||
return response()->json($response, 422);
|
||||
}
|
||||
} catch (FireflyException $e) {
|
||||
Log::warning('Caught an exception. Return error message.');
|
||||
Log::error($e->getMessage());
|
||||
// return bad validation message.
|
||||
// TODO use Laravel's internal validation thing to do this.
|
||||
$response = [
|
||||
'message' => 'The given data was invalid.',
|
||||
'errors' => [
|
||||
'transactions.0.description' => [sprintf('Internal exception: %s', $e->getMessage())],
|
||||
],
|
||||
];
|
||||
|
||||
event(new StoredTransactionGroup($transactionGroup));
|
||||
return response()->json($response, 422);
|
||||
}
|
||||
app('preferences')->mark();
|
||||
event(new StoredTransactionGroup($transactionGroup, $data['apply_rules'] ?? true));
|
||||
|
||||
$manager = $this->getManager();
|
||||
/** @var User $admin */
|
||||
@@ -312,7 +327,7 @@ class TransactionController extends Controller
|
||||
|
||||
$selectedGroup = $collector->getGroups()->first();
|
||||
if (null === $selectedGroup) {
|
||||
throw new NotFoundHttpException(); // @codeCoverageIgnore
|
||||
throw new FireflyException('Cannot find transaction. Possibly, a rule deleted this transaction after its creation.');
|
||||
}
|
||||
/** @var TransactionGroupTransformer $transformer */
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
@@ -338,7 +353,8 @@ class TransactionController extends Controller
|
||||
$transactionGroup = $this->groupRepository->update($transactionGroup, $data);
|
||||
$manager = $this->getManager();
|
||||
|
||||
event(new UpdatedTransactionGroup($transactionGroup));
|
||||
app('preferences')->mark();
|
||||
event(new UpdatedTransactionGroup($transactionGroup, $data['apply_rules'] ?? true));
|
||||
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
@@ -362,6 +378,5 @@ class TransactionController extends Controller
|
||||
$resource = new Item($selectedGroup, $transformer, 'transactions');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -155,8 +155,8 @@ class TransactionLinkController extends Controller
|
||||
*
|
||||
* @param TransactionLinkRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(TransactionLinkRequest $request): JsonResponse
|
||||
{
|
||||
@@ -186,8 +186,8 @@ class TransactionLinkController extends Controller
|
||||
* @param TransactionLinkRequest $request
|
||||
* @param TransactionJournalLink $journalLink
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function update(TransactionLinkRequest $request, TransactionJournalLink $journalLink): JsonResponse
|
||||
{
|
||||
|
@@ -70,9 +70,9 @@ class UserController extends Controller
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @codeCoverageIgnore
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function delete(User $user): JsonResponse
|
||||
{
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* IsSandStormUser.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
* ApiDemoUser.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -20,38 +20,40 @@
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Middleware;
|
||||
namespace FireflyIII\Api\V1\Middleware;
|
||||
|
||||
use Closure;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
|
||||
/**
|
||||
* Class IsSandStormUser.
|
||||
* Class ApiDemoUser.
|
||||
*/
|
||||
class IsSandStormUser
|
||||
class ApiDemoUser
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request. May not be a limited user (ie. Sandstorm env. or demo user).
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null $guard
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, $guard = null)
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (Auth::guard($guard)->guest()) {
|
||||
// don't care when not logged in, usual stuff applies:
|
||||
/** @var User $user */
|
||||
$user = $request->user();
|
||||
|
||||
if (null === $user) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if (1 === (int)getenv('SANDSTORM')) {
|
||||
app('session')->flash('warning', (string)trans('firefly.sandstorm_not_available'));
|
||||
/** @var UserRepositoryInterface $repository */
|
||||
$repository = app(UserRepositoryInterface::class);
|
||||
|
||||
return response()->redirectTo(route('index'));
|
||||
if ($repository->hasRole($user, 'demo')) {
|
||||
return response('', 403);
|
||||
}
|
||||
|
||||
return $next($request);
|
@@ -23,9 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Rules\IsValidAttachmentModel;
|
||||
|
||||
/**
|
||||
@@ -57,8 +54,8 @@ class AttachmentStoreRequest extends Request
|
||||
'filename' => $this->string('filename'),
|
||||
'title' => $this->string('title'),
|
||||
'notes' => $this->nlString('notes'),
|
||||
'model' => $this->string('model'),
|
||||
'model_id' => $this->integer('model_id'),
|
||||
'model' => $this->string('attachable_type'),
|
||||
'model_id' => $this->integer('attachable_id'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -69,22 +66,22 @@ class AttachmentStoreRequest extends Request
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$models = implode(
|
||||
',',
|
||||
[
|
||||
str_replace('FireflyIII\\Models\\', '', Bill::class),
|
||||
str_replace('FireflyIII\\Models\\', '', ImportJob::class),
|
||||
str_replace('FireflyIII\\Models\\', '', TransactionJournal::class),
|
||||
]
|
||||
$models = config('firefly.valid_attachment_models');
|
||||
$models = array_map(
|
||||
|
||||
static function (string $className) {
|
||||
return str_replace('FireflyIII\\Models\\', '', $className);
|
||||
}, $models
|
||||
);
|
||||
$model = $this->string('model');
|
||||
$models = implode(',', $models);
|
||||
$model = $this->string('attachable_type');
|
||||
|
||||
return [
|
||||
'filename' => 'required|between:1,255',
|
||||
'title' => 'between:1,255',
|
||||
'notes' => 'between:1,65000',
|
||||
'model' => sprintf('required|in:%s', $models),
|
||||
'model_id' => ['required', 'numeric', new IsValidAttachmentModel($model)],
|
||||
'attachable_type' => sprintf('required|in:%s', $models),
|
||||
'attachable_id' => ['required', 'numeric', new IsValidAttachmentModel($model)],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -52,8 +52,8 @@ class AttachmentUpdateRequest extends Request
|
||||
'filename' => $this->string('filename'),
|
||||
'title' => $this->string('title'),
|
||||
'notes' => $this->nlString('notes'),
|
||||
'model' => $this->string('model'),
|
||||
'model_id' => $this->integer('model_id'),
|
||||
'model' => $this->string('attachable_type'),
|
||||
'model_id' => $this->integer('attachable_id'),
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -64,15 +64,13 @@ class AvailableBudgetRequest extends Request
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
return [
|
||||
'currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
|
||||
'amount' => 'required|numeric|more:0',
|
||||
'amount' => 'required|numeric|gt:0',
|
||||
'start' => 'required|date|before:end',
|
||||
'end' => 'required|date|after:start',
|
||||
];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -60,7 +60,7 @@ class BillRequest extends Request
|
||||
$active = $this->boolean('active');
|
||||
}
|
||||
|
||||
$data = [
|
||||
return [
|
||||
'name' => $this->string('name'),
|
||||
'amount_min' => $this->string('amount_min'),
|
||||
'amount_max' => $this->string('amount_max'),
|
||||
@@ -70,10 +70,9 @@ class BillRequest extends Request
|
||||
'repeat_freq' => $this->string('repeat_freq'),
|
||||
'skip' => $this->integer('skip'),
|
||||
'active' => $active,
|
||||
'order' => $this->integer('order'),
|
||||
'notes' => $this->nlString('notes'),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,13 +84,13 @@ class BillRequest extends Request
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => 'required|between:1,255|uniqueObjectForUser:bills,name',
|
||||
'amount_min' => 'required|numeric|more:0',
|
||||
'amount_max' => 'required|numeric|more:0',
|
||||
'name' => 'between:1,255|uniqueObjectForUser:bills,name',
|
||||
'amount_min' => 'numeric|gt:0',
|
||||
'amount_max' => 'numeric|gt:0',
|
||||
'currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
|
||||
'date' => 'required|date',
|
||||
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
|
||||
'date' => 'date',
|
||||
'repeat_freq' => 'in:weekly,monthly,quarterly,half-year,yearly',
|
||||
'skip' => 'between:0,31',
|
||||
'active' => [new IsBoolean],
|
||||
'notes' => 'between:1,65536',
|
||||
|
@@ -70,7 +70,7 @@ class BudgetLimitRequest extends Request
|
||||
'budget_id' => 'required|exists:budgets,id|belongsToUser:budgets,id',
|
||||
'start' => 'required|before:end|date',
|
||||
'end' => 'required|after:start|date',
|
||||
'amount' => 'required|more:0',
|
||||
'amount' => 'required|gt:0',
|
||||
'currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
|
||||
];
|
||||
|
106
app/Api/V1/Requests/BudgetStoreRequest.php
Normal file
106
app/Api/V1/Requests/BudgetStoreRequest.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* BudgetStoreRequest.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
/**
|
||||
* Class BudgetStoreRequest
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class BudgetStoreRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
// Only allow authenticated users
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data from the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
$active = true;
|
||||
if (null !== $this->get('active')) {
|
||||
$active = $this->boolean('active');
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $this->string('name'),
|
||||
'active' => $active,
|
||||
'order' => 0,
|
||||
'auto_budget_type' => $this->string('auto_budget_type'),
|
||||
'transaction_currency_id' => $this->integer('auto_budget_currency_id'),
|
||||
'transaction_currency_code' => $this->string('auto_budget_currency_code'),
|
||||
'auto_budget_amount' => $this->string('auto_budget_amount'),
|
||||
'auto_budget_period' => $this->string('auto_budget_period'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name',
|
||||
'active' => [new IsBoolean],
|
||||
'auto_budget_type' => 'in:reset,rollover,none',
|
||||
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
|
||||
'auto_budget_currency_code' => 'exists:transaction_currencies,code',
|
||||
'auto_budget_amount' => 'min:0|max:1000000000',
|
||||
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure the validator instance with special rules for after the basic validation rules.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator) {
|
||||
// validate all account info
|
||||
$this->validateAutoBudgetAmount($validator);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
107
app/Api/V1/Requests/BudgetUpdateRequest.php
Normal file
107
app/Api/V1/Requests/BudgetUpdateRequest.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* BudgetUpdateRequest.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
/**
|
||||
* Class BudgetUpdateRequest
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class BudgetUpdateRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
// Only allow authenticated users
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data from the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
$active = true;
|
||||
if (null !== $this->get('active')) {
|
||||
$active = $this->boolean('active');
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $this->string('name'),
|
||||
'active' => $active,
|
||||
'order' => 0,
|
||||
'auto_budget_type' => $this->string('auto_budget_type'),
|
||||
'transaction_currency_id' => $this->integer('auto_budget_currency_id'),
|
||||
'transaction_currency_code' => $this->string('auto_budget_currency_code'),
|
||||
'auto_budget_amount' => $this->string('auto_budget_amount'),
|
||||
'auto_budget_period' => $this->string('auto_budget_period'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$budget = $this->route()->parameter('budget');
|
||||
|
||||
return [
|
||||
'name' => sprintf('required|between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id),
|
||||
'active' => [new IsBoolean],
|
||||
'auto_budget_type' => 'in:reset,rollover,none',
|
||||
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
|
||||
'auto_budget_currency_code' => 'exists:transaction_currencies,code',
|
||||
'auto_budget_amount' => 'min:0|max:1000000000',
|
||||
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance with special rules for after the basic validation rules.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator) {
|
||||
// validate all account info
|
||||
$this->validateAutoBudgetAmount($validator);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* RabobankDescription.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
* DataDestroyRequest.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -18,52 +18,49 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Import\Specifics;
|
||||
namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
/**
|
||||
* Class RabobankDescription.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @deprecated
|
||||
* Class DataDestroyRequest
|
||||
*/
|
||||
class RabobankDescription implements SpecificInterface
|
||||
class DataDestroyRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Description of this specific.
|
||||
* Authorize logged in users.
|
||||
*
|
||||
* @return string
|
||||
* @codeCoverageIgnore
|
||||
* @return bool
|
||||
*/
|
||||
public static function getDescription(): string
|
||||
public function authorize(): bool
|
||||
{
|
||||
return 'import.specific_rabo_descr';
|
||||
// Only allow authenticated users
|
||||
return auth()->check() && !auth()->user()->hasRole('demo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of this specific.
|
||||
* Get all data from the request.
|
||||
*
|
||||
* @return string
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function getName(): string
|
||||
public function getObjects(): string
|
||||
{
|
||||
return 'import.specific_rabo_name';
|
||||
return $this->get('objects') ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the specific.
|
||||
*
|
||||
* @param array $row
|
||||
* The rules that the incoming request must be matched against.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function run(array $row): array
|
||||
public function rules(): array
|
||||
{
|
||||
$row = array_values($row);
|
||||
$valid = 'budgets,bills,piggy_banks,rules,recurring,categories,tags,object_groups' .
|
||||
',accounts,asset_accounts,expense_accounts,revenue_accounts,liabilities,transactions,withdrawals,deposits,transfers';
|
||||
|
||||
return $row;
|
||||
return [
|
||||
'objects' => sprintf('min:1|string|in:%s', $valid),
|
||||
];
|
||||
}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* BudgetRequest.php
|
||||
* ObjectGroupUpdateRequest.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
@@ -23,17 +24,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
|
||||
/**
|
||||
* Class BudgetRequest
|
||||
* Class AccountObjectGroupUpdateRequestUpdateRequest
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* TODO AFTER 4.8,0: split this into two request classes.
|
||||
*/
|
||||
class BudgetRequest extends Request
|
||||
class ObjectGroupUpdateRequest extends Request
|
||||
{
|
||||
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
*
|
||||
@@ -46,21 +44,13 @@ class BudgetRequest extends Request
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data from the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAll(): array
|
||||
public function getUpdateData(): array
|
||||
{
|
||||
$active = true;
|
||||
if (null !== $this->get('active')) {
|
||||
$active = $this->boolean('active');
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $this->string('name'),
|
||||
'active' => $active,
|
||||
'order' => 0,
|
||||
'title' => $this->string('title'),
|
||||
'order' => $this->integer('order'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -71,21 +61,11 @@ class BudgetRequest extends Request
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name',
|
||||
'active' => [new IsBoolean],
|
||||
];
|
||||
switch ($this->method()) {
|
||||
default:
|
||||
break;
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
/** @var Budget $budget */
|
||||
$budget = $this->route()->parameter('budget');
|
||||
$rules['name'] = sprintf('required|between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id);
|
||||
break;
|
||||
}
|
||||
$objectGroup = $this->route()->parameter('objectGroup');
|
||||
|
||||
return $rules;
|
||||
return [
|
||||
'title' => sprintf('min:1|uniqueObjectGroup:%d', $objectGroup->id),
|
||||
'order' => 'numeric',
|
||||
];
|
||||
}
|
||||
}
|
@@ -63,6 +63,7 @@ class PiggyBankRequest extends Request
|
||||
'startdate' => $this->date('start_date'),
|
||||
'targetdate' => $this->date('target_date'),
|
||||
'notes' => $this->nlString('notes'),
|
||||
'order' => $this->integer('order'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -90,7 +91,7 @@ class PiggyBankRequest extends Request
|
||||
$piggyBank = $this->route()->parameter('piggyBank');
|
||||
$rules['name'] = 'between:1,255|uniquePiggyBankForUser:' . $piggyBank->id;
|
||||
$rules['account_id'] = ['belongsToUser:accounts', new IsAssetAccountId];
|
||||
$rules['target_amount'] = 'numeric|more:0';
|
||||
$rules['target_amount'] = 'numeric|gt:0';
|
||||
$rules['current_amount'] = ['numeric', new ZeroOrMore, new LessThanPiggyTarget];
|
||||
break;
|
||||
}
|
||||
|
86
app/Api/V1/Requests/PiggyBankStoreRequest.php
Normal file
86
app/Api/V1/Requests/PiggyBankStoreRequest.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* PiggyBankStoreRequest.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
use FireflyIII\Rules\ZeroOrMore;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class PiggyBankStoreRequest
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class PiggyBankStoreRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
// Only allow authenticated users
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data from the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->string('name'),
|
||||
'account_id' => $this->integer('account_id'),
|
||||
'targetamount' => $this->string('target_amount'),
|
||||
'current_amount' => $this->string('current_amount'),
|
||||
'startdate' => $this->date('start_date'),
|
||||
'targetdate' => $this->date('target_date'),
|
||||
'notes' => $this->nlString('notes'),
|
||||
'object_group_id' => $this->integer('object_group_id'),
|
||||
'object_group' => $this->string('object_group_name'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|between:1,255|uniquePiggyBankForUser',
|
||||
'current_amount' => ['numeric', new ZeroOrMore, 'lte:target_amount'],
|
||||
'account_id' => 'required|numeric|belongsToUser:accounts,id',
|
||||
'object_group_id' => 'numeric|belongsToUser:object_groups,id',
|
||||
'target_amount' => ['numeric', new ZeroOrMore, 'lte:target_amount', 'required'],
|
||||
'start_date' => 'date|nullable',
|
||||
'target_date' => 'date|nullable|after:start_date',
|
||||
'notes' => 'max:65000',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Rules\BelongsUser;
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use FireflyIII\Validation\CurrencyValidation;
|
||||
use FireflyIII\Validation\RecurrenceValidation;
|
||||
use FireflyIII\Validation\TransactionValidation;
|
||||
use Illuminate\Validation\Validator;
|
||||
@@ -35,7 +36,7 @@ use Illuminate\Validation\Validator;
|
||||
*/
|
||||
class RecurrenceStoreRequest extends Request
|
||||
{
|
||||
use RecurrenceValidation, TransactionValidation;
|
||||
use RecurrenceValidation, TransactionValidation, CurrencyValidation;
|
||||
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
@@ -63,7 +64,8 @@ class RecurrenceStoreRequest extends Request
|
||||
if (null !== $this->get('apply_rules')) {
|
||||
$applyRules = $this->boolean('apply_rules');
|
||||
}
|
||||
$return = [
|
||||
|
||||
return [
|
||||
'recurrence' => [
|
||||
'type' => $this->string('type'),
|
||||
'title' => $this->string('title'),
|
||||
@@ -77,8 +79,6 @@ class RecurrenceStoreRequest extends Request
|
||||
'transactions' => $this->getTransactionData(),
|
||||
'repetitions' => $this->getRepetitionData(),
|
||||
];
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,8 +104,8 @@ class RecurrenceStoreRequest extends Request
|
||||
'repetitions.*.skip' => 'required|numeric|between:0,31',
|
||||
'repetitions.*.weekend' => 'required|numeric|min:1|max:4',
|
||||
'transactions.*.description' => 'required|between:1,255',
|
||||
'transactions.*.amount' => 'required|numeric|more:0',
|
||||
'transactions.*.foreign_amount' => 'numeric|more:0',
|
||||
'transactions.*.amount' => 'required|numeric|gt:0',
|
||||
'transactions.*.foreign_amount' => 'numeric|gt:0',
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
|
||||
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
@@ -193,29 +193,7 @@ class RecurrenceStoreRequest extends Request
|
||||
}
|
||||
/** @var array $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$return[] = [
|
||||
'amount' => $transaction['amount'],
|
||||
'currency_id' => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null,
|
||||
'currency_code' => $transaction['currency_code'] ?? null,
|
||||
'foreign_amount' => $transaction['foreign_amount'] ?? null,
|
||||
'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null,
|
||||
'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null,
|
||||
'source_id' => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null,
|
||||
'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null,
|
||||
'destination_id' => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null,
|
||||
'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null,
|
||||
'description' => $transaction['description'],
|
||||
'type' => $this->string('type'),
|
||||
|
||||
// new and updated fields:
|
||||
'piggy_bank_id' => isset($transaction['piggy_bank_id']) ? (int)$transaction['piggy_bank_id'] : null,
|
||||
'piggy_bank_name' => $transaction['piggy_bank_name'] ?? null,
|
||||
'tags' => $transaction['tags'] ?? [],
|
||||
'budget_id' => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null,
|
||||
'budget_name' => $transaction['budget_name'] ?? null,
|
||||
'category_id' => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null,
|
||||
'category_name' => $transaction['category_name'] ?? null,
|
||||
];
|
||||
$return[] = $this->getSingleRecurrenceData($transaction);
|
||||
}
|
||||
|
||||
return $return;
|
||||
|
@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests;
|
||||
use FireflyIII\Models\Recurrence;
|
||||
use FireflyIII\Rules\BelongsUser;
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use FireflyIII\Validation\CurrencyValidation;
|
||||
use FireflyIII\Validation\RecurrenceValidation;
|
||||
use FireflyIII\Validation\TransactionValidation;
|
||||
use Illuminate\Validation\Validator;
|
||||
@@ -35,7 +36,7 @@ use Illuminate\Validation\Validator;
|
||||
*/
|
||||
class RecurrenceUpdateRequest extends Request
|
||||
{
|
||||
use RecurrenceValidation, TransactionValidation;
|
||||
use RecurrenceValidation, TransactionValidation, CurrencyValidation;
|
||||
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
@@ -63,7 +64,8 @@ class RecurrenceUpdateRequest extends Request
|
||||
if (null !== $this->get('apply_rules')) {
|
||||
$applyRules = $this->boolean('apply_rules');
|
||||
}
|
||||
$return = [
|
||||
|
||||
return [
|
||||
'recurrence' => [
|
||||
'type' => $this->nullableString('type'),
|
||||
'title' => $this->nullableString('title'),
|
||||
@@ -78,8 +80,6 @@ class RecurrenceUpdateRequest extends Request
|
||||
'transactions' => $this->getTransactionData(),
|
||||
'repetitions' => $this->getRepetitionData(),
|
||||
];
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,8 +107,8 @@ class RecurrenceUpdateRequest extends Request
|
||||
'repetitions.*.weekend' => 'required|numeric|min:1|max:4',
|
||||
|
||||
'transactions.*.description' => 'required|between:1,255',
|
||||
'transactions.*.amount' => 'required|numeric|more:0',
|
||||
'transactions.*.foreign_amount' => 'numeric|more:0',
|
||||
'transactions.*.amount' => 'required|numeric|gt:0',
|
||||
'transactions.*.foreign_amount' => 'numeric|gt:0',
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
|
||||
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
@@ -141,7 +141,7 @@ class RecurrenceUpdateRequest extends Request
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator) {
|
||||
$this->validateOneRecurrenceTransactionUpdate($validator);
|
||||
$this->validateOneRecurrenceTransaction($validator);
|
||||
$this->validateOneRepetitionUpdate($validator);
|
||||
$this->validateRecurrenceRepetition($validator);
|
||||
$this->validateRepetitionMoment($validator);
|
||||
@@ -195,29 +195,7 @@ class RecurrenceUpdateRequest extends Request
|
||||
}
|
||||
/** @var array $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$return[] = [
|
||||
'amount' => $transaction['amount'],
|
||||
'currency_id' => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null,
|
||||
'currency_code' => $transaction['currency_code'] ?? null,
|
||||
'foreign_amount' => $transaction['foreign_amount'] ?? null,
|
||||
'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null,
|
||||
'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null,
|
||||
'source_id' => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null,
|
||||
'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null,
|
||||
'destination_id' => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null,
|
||||
'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null,
|
||||
'description' => $transaction['description'],
|
||||
'type' => $this->string('type'),
|
||||
|
||||
// new and updated fields:
|
||||
'piggy_bank_id' => isset($transaction['piggy_bank_id']) ? (int)$transaction['piggy_bank_id'] : null,
|
||||
'piggy_bank_name' => $transaction['piggy_bank_name'] ?? null,
|
||||
'tags' => $transaction['tags'] ?? [],
|
||||
'budget_id' => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null,
|
||||
'budget_name' => $transaction['budget_name'] ?? null,
|
||||
'category_id' => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null,
|
||||
'category_name' => $transaction['category_name'] ?? null,
|
||||
];
|
||||
$return[] = $this->getSingleRecurrenceData($transaction);
|
||||
}
|
||||
|
||||
return $return;
|
||||
|
@@ -34,4 +34,36 @@ use FireflyIII\Http\Requests\Request as FireflyIIIRequest;
|
||||
*/
|
||||
class Request extends FireflyIIIRequest
|
||||
{
|
||||
/**
|
||||
* @param array $transaction
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getSingleRecurrenceData(array $transaction): array
|
||||
{
|
||||
return [
|
||||
'amount' => $transaction['amount'],
|
||||
'currency_id' => isset($transaction['currency_id']) ? (int) $transaction['currency_id'] : null,
|
||||
'currency_code' => $transaction['currency_code'] ?? null,
|
||||
'foreign_amount' => $transaction['foreign_amount'] ?? null,
|
||||
'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int) $transaction['foreign_currency_id'] : null,
|
||||
'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null,
|
||||
'source_id' => isset($transaction['source_id']) ? (int) $transaction['source_id'] : null,
|
||||
'source_name' => isset($transaction['source_name']) ? (string) $transaction['source_name'] : null,
|
||||
'destination_id' => isset($transaction['destination_id']) ? (int) $transaction['destination_id'] : null,
|
||||
'destination_name' => isset($transaction['destination_name']) ? (string) $transaction['destination_name'] : null,
|
||||
'description' => $transaction['description'],
|
||||
'type' => $this->string('type'),
|
||||
|
||||
// new and updated fields:
|
||||
'piggy_bank_id' => isset($transaction['piggy_bank_id']) ? (int) $transaction['piggy_bank_id'] : null,
|
||||
'piggy_bank_name' => $transaction['piggy_bank_name'] ?? null,
|
||||
'tags' => $transaction['tags'] ?? [],
|
||||
'budget_id' => isset($transaction['budget_id']) ? (int) $transaction['budget_id'] : null,
|
||||
'budget_name' => $transaction['budget_name'] ?? null,
|
||||
'category_id' => isset($transaction['category_id']) ? (int) $transaction['category_id'] : null,
|
||||
'category_name' => $transaction['category_name'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -54,7 +54,7 @@ class RuleGroupTestRequest extends Request
|
||||
*/
|
||||
public function getTestParameters(): array
|
||||
{
|
||||
$return = [
|
||||
return [
|
||||
'page' => $this->getPage(),
|
||||
'start_date' => $this->getDate('start_date'),
|
||||
'end_date' => $this->getDate('end_date'),
|
||||
@@ -62,9 +62,6 @@ class RuleGroupTestRequest extends Request
|
||||
'trigger_limit' => $this->getTriggerLimit(),
|
||||
'accounts' => $this->getAccounts(),
|
||||
];
|
||||
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -54,14 +54,11 @@ class RuleGroupTriggerRequest extends Request
|
||||
*/
|
||||
public function getTriggerParameters(): array
|
||||
{
|
||||
$return = [
|
||||
return [
|
||||
'start_date' => $this->getDate('start_date'),
|
||||
'end_date' => $this->getDate('end_date'),
|
||||
'accounts' => $this->getAccounts(),
|
||||
];
|
||||
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -65,7 +65,7 @@ class RuleStoreRequest extends Request
|
||||
$stopProcessing = $this->boolean('stop_processing');
|
||||
}
|
||||
|
||||
$data = [
|
||||
return [
|
||||
'title' => $this->string('title'),
|
||||
'description' => $this->string('description'),
|
||||
'rule_group_id' => $this->integer('rule_group_id'),
|
||||
@@ -77,8 +77,6 @@ class RuleStoreRequest extends Request
|
||||
'triggers' => $this->getRuleTriggers(),
|
||||
'actions' => $this->getRuleActions(),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,7 +92,8 @@ class RuleStoreRequest extends Request
|
||||
// some triggers and actions require text:
|
||||
$contextTriggers = implode(',', config('firefly.context-rule-triggers'));
|
||||
$contextActions = implode(',', config('firefly.context-rule-actions'));
|
||||
$rules = [
|
||||
|
||||
return [
|
||||
'title' => 'required|between:1,100|uniqueObjectForUser:rules,title',
|
||||
'description' => 'between:1,5000|nullable',
|
||||
'rule_group_id' => 'required|belongsToUser:rule_groups|required_without:rule_group_title',
|
||||
@@ -112,8 +111,6 @@ class RuleStoreRequest extends Request
|
||||
'stop_processing' => [new IsBoolean],
|
||||
'active' => [new IsBoolean],
|
||||
];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -54,7 +54,7 @@ class RuleTestRequest extends Request
|
||||
*/
|
||||
public function getTestParameters(): array
|
||||
{
|
||||
$return = [
|
||||
return [
|
||||
'page' => $this->getPage(),
|
||||
'start_date' => $this->getDate('start_date'),
|
||||
'end_date' => $this->getDate('end_date'),
|
||||
@@ -62,9 +62,6 @@ class RuleTestRequest extends Request
|
||||
'trigger_limit' => $this->getTriggerLimit(),
|
||||
'accounts' => $this->getAccounts(),
|
||||
];
|
||||
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -53,14 +53,11 @@ class RuleTriggerRequest extends Request
|
||||
*/
|
||||
public function getTriggerParameters(): array
|
||||
{
|
||||
$return = [
|
||||
return [
|
||||
'start_date' => $this->getDate('start_date'),
|
||||
'end_date' => $this->getDate('end_date'),
|
||||
'accounts' => $this->getAccounts(),
|
||||
];
|
||||
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -65,7 +65,7 @@ class RuleUpdateRequest extends Request
|
||||
$stopProcessing = $this->boolean('stop_processing');
|
||||
}
|
||||
|
||||
$data = [
|
||||
return [
|
||||
'title' => $this->nullableString('title'),
|
||||
'description' => $this->nullableString('description'),
|
||||
'rule_group_id' => $this->nullableInteger('rule_group_id'),
|
||||
@@ -77,8 +77,6 @@ class RuleUpdateRequest extends Request
|
||||
'triggers' => $this->getRuleTriggers(),
|
||||
'actions' => $this->getRuleActions(),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +93,8 @@ class RuleUpdateRequest extends Request
|
||||
// some triggers and actions require text:
|
||||
$contextTriggers = implode(',', config('firefly.context-rule-triggers'));
|
||||
$contextActions = implode(',', config('firefly.context-rule-actions'));
|
||||
$rules = [
|
||||
|
||||
return [
|
||||
'title' => sprintf('between:1,100|uniqueObjectForUser:rules,title,%d', $rule->id),
|
||||
'description' => 'between:1,5000|nullable',
|
||||
'rule_group_id' => 'belongsToUser:rule_groups',
|
||||
@@ -113,8 +112,6 @@ class RuleUpdateRequest extends Request
|
||||
'stop_processing' => [new IsBoolean],
|
||||
'active' => [new IsBoolean],
|
||||
];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* TransferRequest.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -19,8 +20,9 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests\Search;
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests\Search;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\Request;
|
||||
use FireflyIII\Rules\IsTransferAccount;
|
||||
@@ -49,10 +51,9 @@ class TransferRequest extends Request
|
||||
return [
|
||||
'source' => ['required', new IsTransferAccount],
|
||||
'destination' => ['required', new IsTransferAccount],
|
||||
'amount' => 'required|numeric|more:0',
|
||||
'amount' => 'required|numeric|gt:0',
|
||||
'description' => 'required|min:1',
|
||||
'date' => 'required|date',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@@ -28,16 +28,18 @@ use FireflyIII\Rules\BelongsUser;
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use FireflyIII\Rules\IsDateOrTime;
|
||||
use FireflyIII\Support\NullArrayObject;
|
||||
use FireflyIII\Validation\CurrencyValidation;
|
||||
use FireflyIII\Validation\GroupValidation;
|
||||
use FireflyIII\Validation\TransactionValidation;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class TransactionStoreRequest
|
||||
*/
|
||||
class TransactionStoreRequest extends Request
|
||||
{
|
||||
use TransactionValidation;
|
||||
use TransactionValidation, GroupValidation, CurrencyValidation;
|
||||
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
@@ -46,6 +48,8 @@ class TransactionStoreRequest extends Request
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
Log::debug('Authorize TransactionStoreRequest');
|
||||
|
||||
// Only allow authenticated users
|
||||
return auth()->check();
|
||||
}
|
||||
@@ -57,13 +61,14 @@ class TransactionStoreRequest extends Request
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
$data = [
|
||||
Log::debug('get all data in TransactionStoreRequest');
|
||||
|
||||
return [
|
||||
'group_title' => $this->string('group_title'),
|
||||
'error_if_duplicate_hash' => $this->boolean('error_if_duplicate_hash'),
|
||||
'apply_rules' => $this->boolean('apply_rules', true),
|
||||
'transactions' => $this->getTransactionData(),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,10 +78,13 @@ class TransactionStoreRequest extends Request
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
Log::debug('Collect rules of TransactionStoreRequest');
|
||||
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'between:1,1000|nullable',
|
||||
'error_if_duplicate_hash' => [new IsBoolean],
|
||||
'apply_rules' => [new IsBoolean],
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
@@ -90,7 +98,7 @@ class TransactionStoreRequest extends Request
|
||||
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code|nullable',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => 'required|numeric|more:0',
|
||||
'transactions.*.amount' => 'required|numeric|gt:0',
|
||||
'transactions.*.foreign_amount' => 'numeric',
|
||||
|
||||
// description
|
||||
@@ -150,8 +158,6 @@ class TransactionStoreRequest extends Request
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
];
|
||||
|
||||
return $rules;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -215,7 +221,7 @@ class TransactionStoreRequest extends Request
|
||||
|
||||
// foreign currency info:
|
||||
'foreign_currency_id' => $this->integerFromValue((string) $object['foreign_currency_id']),
|
||||
'foreign_currency_code' => $this->stringFromValue($object['foreign_currency_code']),
|
||||
'foreign_currency_code' => $this->stringFromValue((string) $object['foreign_currency_code']),
|
||||
|
||||
// amount and foreign amount. Cannot be 0.
|
||||
'amount' => $this->stringFromValue((string) $object['amount']),
|
||||
@@ -226,45 +232,45 @@ class TransactionStoreRequest extends Request
|
||||
|
||||
// source of transaction. If everything is null, assume cash account.
|
||||
'source_id' => $this->integerFromValue((string) $object['source_id']),
|
||||
'source_name' => $this->stringFromValue($object['source_name']),
|
||||
'source_iban' => $this->stringFromValue($object['source_iban']),
|
||||
'source_number' => $this->stringFromValue($object['source_number']),
|
||||
'source_bic' => $this->stringFromValue($object['source_bic']),
|
||||
'source_name' => $this->stringFromValue((string) $object['source_name']),
|
||||
'source_iban' => $this->stringFromValue((string) $object['source_iban']),
|
||||
'source_number' => $this->stringFromValue((string) $object['source_number']),
|
||||
'source_bic' => $this->stringFromValue((string) $object['source_bic']),
|
||||
|
||||
// destination of transaction. If everything is null, assume cash account.
|
||||
'destination_id' => $this->integerFromValue((string) $object['destination_id']),
|
||||
'destination_name' => $this->stringFromValue($object['destination_name']),
|
||||
'destination_iban' => $this->stringFromValue($object['destination_iban']),
|
||||
'destination_number' => $this->stringFromValue($object['destination_number']),
|
||||
'destination_bic' => $this->stringFromValue($object['destination_bic']),
|
||||
'destination_name' => $this->stringFromValue((string) $object['destination_name']),
|
||||
'destination_iban' => $this->stringFromValue((string) $object['destination_iban']),
|
||||
'destination_number' => $this->stringFromValue((string) $object['destination_number']),
|
||||
'destination_bic' => $this->stringFromValue((string) $object['destination_bic']),
|
||||
|
||||
// budget info
|
||||
'budget_id' => $this->integerFromValue((string) $object['budget_id']),
|
||||
'budget_name' => $this->stringFromValue($object['budget_name']),
|
||||
'budget_name' => $this->stringFromValue((string) $object['budget_name']),
|
||||
|
||||
// category info
|
||||
'category_id' => $this->integerFromValue((string) $object['category_id']),
|
||||
'category_name' => $this->stringFromValue($object['category_name']),
|
||||
'category_name' => $this->stringFromValue((string) $object['category_name']),
|
||||
|
||||
// journal bill reference. Optional. Will only work for withdrawals
|
||||
'bill_id' => $this->integerFromValue((string) $object['bill_id']),
|
||||
'bill_name' => $this->stringFromValue($object['bill_name']),
|
||||
'bill_name' => $this->stringFromValue((string) $object['bill_name']),
|
||||
|
||||
// piggy bank reference. Optional. Will only work for transfers
|
||||
'piggy_bank_id' => $this->integerFromValue((string) $object['piggy_bank_id']),
|
||||
'piggy_bank_name' => $this->stringFromValue($object['piggy_bank_name']),
|
||||
'piggy_bank_name' => $this->stringFromValue((string) $object['piggy_bank_name']),
|
||||
|
||||
// some other interesting properties
|
||||
'reconciled' => $this->convertBoolean((string) $object['reconciled']),
|
||||
'notes' => $this->nlStringFromValue($object['notes']),
|
||||
'notes' => $this->nlStringFromValue((string) $object['notes']),
|
||||
'tags' => $this->arrayFromValue($object['tags']),
|
||||
|
||||
// all custom fields:
|
||||
'internal_reference' => $this->stringFromValue($object['internal_reference']),
|
||||
'external_id' => $this->stringFromValue($object['external_id']),
|
||||
'internal_reference' => $this->stringFromValue((string) $object['internal_reference']),
|
||||
'external_id' => $this->stringFromValue((string) $object['external_id']),
|
||||
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
|
||||
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
|
||||
'bunq_payment_id' => $this->stringFromValue($object['bunq_payment_id']),
|
||||
'bunq_payment_id' => $this->stringFromValue((string) $object['bunq_payment_id']),
|
||||
|
||||
'sepa_cc' => $this->stringFromValue($object['sepa_cc']),
|
||||
'sepa_ct_op' => $this->stringFromValue($object['sepa_ct_op']),
|
||||
|
@@ -28,6 +28,7 @@ use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Rules\BelongsUser;
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use FireflyIII\Rules\IsDateOrTime;
|
||||
use FireflyIII\Validation\GroupValidation;
|
||||
use FireflyIII\Validation\TransactionValidation;
|
||||
use Illuminate\Validation\Validator;
|
||||
use Log;
|
||||
@@ -37,7 +38,7 @@ use Log;
|
||||
*/
|
||||
class TransactionUpdateRequest extends Request
|
||||
{
|
||||
use TransactionValidation;
|
||||
use TransactionValidation, GroupValidation;
|
||||
|
||||
/** @var array Array values. */
|
||||
private $arrayFields;
|
||||
@@ -138,6 +139,7 @@ class TransactionUpdateRequest extends Request
|
||||
|
||||
$data = [
|
||||
'transactions' => $this->getTransactionData(),
|
||||
'apply_rules' => $this->boolean('apply_rules', true),
|
||||
];
|
||||
if ($this->has('group_title')) {
|
||||
$data['group_title'] = $this->string('group_title');
|
||||
@@ -153,9 +155,10 @@ class TransactionUpdateRequest extends Request
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'between:1,1000',
|
||||
'apply_rules' => [new IsBoolean],
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
@@ -169,7 +172,7 @@ class TransactionUpdateRequest extends Request
|
||||
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => 'numeric|more:0|max:100000000000',
|
||||
'transactions.*.amount' => 'numeric|gt:0|max:100000000000',
|
||||
'transactions.*.foreign_amount' => 'numeric|gte:0',
|
||||
|
||||
// description
|
||||
@@ -220,8 +223,6 @@ class TransactionUpdateRequest extends Request
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,21 +263,122 @@ class TransactionUpdateRequest extends Request
|
||||
// TODO if the transaction_journal_id is empty, some fields are mandatory, like the amount!
|
||||
|
||||
// all journals must have a description
|
||||
//$this->validateDescriptions($validator);
|
||||
|
||||
// // validate foreign currency info
|
||||
// $this->validateForeignCurrencyInformation($validator);
|
||||
//
|
||||
|
||||
//
|
||||
// // make sure all splits have valid source + dest info
|
||||
// $this->validateSplitAccounts($validator);
|
||||
// the group must have a description if > 1 journal.
|
||||
// $this->validateGroupDescription($validator);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $current
|
||||
* @param array $transaction
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getArrayData(array $current, array $transaction): array
|
||||
{
|
||||
foreach ($this->arrayFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->arrayFromValue($transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $current
|
||||
* @param array $transaction
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getBooleanData(array $current, array $transaction): array
|
||||
{
|
||||
foreach ($this->booleanFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->convertBoolean((string) $transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $current
|
||||
* @param array $transaction
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getDateData(array $current, array $transaction): array
|
||||
{
|
||||
foreach ($this->dateFields as $fieldName) {
|
||||
Log::debug(sprintf('Now at date field %s', $fieldName));
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]);
|
||||
Log::debug(sprintf('New value: "%s"', (string) $transaction[$fieldName]));
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* For each field, add it to the array if a reference is present in the request:
|
||||
*
|
||||
* @param array $current
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getIntegerData(array $current, array $transaction): array
|
||||
{
|
||||
foreach ($this->integerFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->integerFromValue((string) $transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $current
|
||||
* @param array $transaction
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getNlStringData(array $current, array $transaction): array
|
||||
{
|
||||
foreach ($this->textareaFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->nlStringFromValue((string) $transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $current
|
||||
* @param array $transaction
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getStringData(array $current, array $transaction): array
|
||||
{
|
||||
foreach ($this->stringFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->stringFromValue((string) $transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction data.
|
||||
*
|
||||
@@ -290,48 +392,15 @@ class TransactionUpdateRequest extends Request
|
||||
* @var int $index
|
||||
* @var array $transaction
|
||||
*/
|
||||
foreach ($this->get('transactions') as $index => $transaction) {
|
||||
foreach ($this->get('transactions') as $transaction) {
|
||||
// default response is to update nothing in the transaction:
|
||||
$current = [];
|
||||
|
||||
// for each field, add it to the array if a reference is present in the request:
|
||||
foreach ($this->integerFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->integerFromValue((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->stringFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->stringFromValue((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->textareaFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->nlStringFromValue((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->dateFields as $fieldName) {
|
||||
Log::debug(sprintf('Now at date field %s', $fieldName));
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->dateFromValue((string)$transaction[$fieldName]);
|
||||
Log::debug(sprintf('New value: "%s"', (string)$transaction[$fieldName]));
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->booleanFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->convertBoolean((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->arrayFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->arrayFromValue($transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
$current = $this->getIntegerData($current, $transaction);
|
||||
$current = $this->getStringData($current, $transaction);
|
||||
$current = $this->getNlStringData($current, $transaction);
|
||||
$current = $this->getDateData($current, $transaction);
|
||||
$current = $this->getBooleanData($current, $transaction);
|
||||
$current = $this->getArrayData($current, $transaction);
|
||||
$return[] = $current;
|
||||
}
|
||||
|
||||
|
@@ -69,14 +69,13 @@ class UserStoreRequest extends Request
|
||||
if (null !== $this->get('blocked')) {
|
||||
$blocked = $this->boolean('blocked');
|
||||
}
|
||||
$data = [
|
||||
|
||||
return [
|
||||
'email' => $this->string('email'),
|
||||
'blocked' => $blocked,
|
||||
'blocked_code' => $this->string('blocked_code'),
|
||||
'role' => $this->string('role'),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -69,14 +69,13 @@ class UserUpdateRequest extends Request
|
||||
if (null !== $this->get('blocked')) {
|
||||
$blocked = $this->boolean('blocked');
|
||||
}
|
||||
$data = [
|
||||
|
||||
return [
|
||||
'email' => $this->string('email'),
|
||||
'blocked' => $blocked,
|
||||
'blocked_code' => $this->string('blocked_code'),
|
||||
'role' => $this->string('role'),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,14 +86,13 @@ class UserUpdateRequest extends Request
|
||||
public function rules(): array
|
||||
{
|
||||
$user = $this->route()->parameter('user');
|
||||
$rules = [
|
||||
|
||||
return [
|
||||
'email' => sprintf('email|unique:users,email,%d', $user->id),
|
||||
'blocked' => [new IsBoolean],
|
||||
'blocked_code' => 'in:email_changed',
|
||||
'role' => 'in:owner,demo,',
|
||||
];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ use Schema;
|
||||
|
||||
/**
|
||||
* Class CorrectDatabase
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class CorrectDatabase extends Command
|
||||
@@ -73,7 +74,7 @@ class CorrectDatabase extends Command
|
||||
'firefly-iii:fix-ob-currencies',
|
||||
'firefly-iii:fix-long-descriptions',
|
||||
'firefly-iii:fix-recurring-transactions',
|
||||
'firefly-iii:restore-oauth-keys'
|
||||
'firefly-iii:restore-oauth-keys',
|
||||
];
|
||||
foreach ($commands as $command) {
|
||||
$this->line(sprintf('Now executing %s', $command));
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CorrectOpeningBalanceCurrencies.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
@@ -19,6 +20,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
@@ -111,17 +114,14 @@ class CorrectOpeningBalanceCurrencies extends Command
|
||||
*/
|
||||
private function getAccount(TransactionJournal $journal): ?Account
|
||||
{
|
||||
$excluded = [];
|
||||
$transactions = $journal->transactions()->with(['account', 'account.accountType'])->get();
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$account = $transaction->account;
|
||||
if (null !== $account) {
|
||||
if (AccountType::INITIAL_BALANCE !== $account->accountType->type) {
|
||||
if ((null !== $account) && AccountType::INITIAL_BALANCE !== $account->accountType->type) {
|
||||
return $account;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@@ -50,8 +50,8 @@ class CreateAccessTokens extends Command
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @throws Exception
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
|
@@ -47,7 +47,7 @@ class CreateLinkTypes extends Command
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
|
@@ -25,7 +25,6 @@ namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
use Log;
|
||||
|
||||
@@ -50,15 +49,16 @@ class DeleteEmptyGroups extends Command
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws Exception;
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
$start = microtime(true);
|
||||
$groupIds =
|
||||
TransactionGroup
|
||||
$groupIds
|
||||
= TransactionGroup
|
||||
::leftJoin('transaction_journals', 'transaction_groups.id', '=', 'transaction_journals.transaction_group_id')
|
||||
->whereNull('transaction_journals.id')->get(['transaction_groups.id'])->pluck('id')->toArray();
|
||||
|
||||
|
@@ -58,7 +58,6 @@ class DeleteEmptyJournals extends Command
|
||||
$this->deleteUnevenJournals();
|
||||
$this->deleteEmptyJournals();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -51,8 +51,8 @@ class DeleteOrphanedTransactions extends Command
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @throws Exception
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
@@ -91,8 +91,11 @@ class DeleteOrphanedTransactions extends Command
|
||||
}
|
||||
Transaction::where('transaction_journal_id', (int) $transaction->transaction_journal_id)->delete();
|
||||
$this->line(
|
||||
sprintf('Deleted transaction journal #%d because account #%d was already deleted.',
|
||||
$transaction->transaction_journal_id, $transaction->account_id)
|
||||
sprintf(
|
||||
'Deleted transaction journal #%d because account #%d was already deleted.',
|
||||
$transaction->transaction_journal_id,
|
||||
$transaction->account_id
|
||||
)
|
||||
);
|
||||
$count++;
|
||||
}
|
||||
@@ -134,6 +137,5 @@ class DeleteOrphanedTransactions extends Command
|
||||
if (0 === $count) {
|
||||
$this->info('No orphaned transactions.');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -49,6 +49,7 @@ class DeleteZeroAmount extends Command
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
|
@@ -52,7 +52,7 @@ class EnableCurrencies extends Command
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
|
@@ -49,20 +49,20 @@ class FixAccountTypes extends Command
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly-iii:fix-account-types';
|
||||
/** @var int */
|
||||
private $count;
|
||||
/** @var array */
|
||||
private $expected;
|
||||
/** @var AccountFactory */
|
||||
private $factory;
|
||||
/** @var array */
|
||||
private $fixable;
|
||||
/** @var int */
|
||||
private $count;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @throws FireflyException
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
@@ -109,23 +109,12 @@ class FixAccountTypes extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $type
|
||||
* @param Transaction $source
|
||||
* @param Transaction $dest
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function fixJournal(TransactionJournal $journal, string $type, Transaction $source, Transaction $dest): void
|
||||
@@ -167,9 +156,12 @@ class FixAccountTypes extends Command
|
||||
$dest->save();
|
||||
$this->info(
|
||||
sprintf(
|
||||
'Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").', $journal->id,
|
||||
$oldDest->id, $oldDest->name,
|
||||
$result->id, $result->name
|
||||
'Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").',
|
||||
$journal->id,
|
||||
$oldDest->id,
|
||||
$oldDest->name,
|
||||
$result->id,
|
||||
$result->name
|
||||
)
|
||||
);
|
||||
$this->inspectJournal($journal);
|
||||
@@ -184,9 +176,12 @@ class FixAccountTypes extends Command
|
||||
$source->save();
|
||||
$this->info(
|
||||
sprintf(
|
||||
'Transaction journal #%d, source account changed from #%d ("%s") to #%d ("%s").', $journal->id,
|
||||
$oldSource->id, $oldSource->name,
|
||||
$result->id, $result->name
|
||||
'Transaction journal #%d, source account changed from #%d ("%s") to #%d ("%s").',
|
||||
$journal->id,
|
||||
$oldSource->id,
|
||||
$oldSource->name,
|
||||
$result->id,
|
||||
$result->name
|
||||
)
|
||||
);
|
||||
$this->inspectJournal($journal);
|
||||
@@ -198,7 +193,6 @@ class FixAccountTypes extends Command
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,4 +268,15 @@ class FixAccountTypes extends Command
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
}
|
||||
}
|
||||
|
81
app/Console/Commands/Correction/FixGroupAccounts.php
Normal file
81
app/Console/Commands/Correction/FixGroupAccounts.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* FixGroupAccounts.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use DB;
|
||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||
use FireflyIII\Handlers\Events\UpdatedGroupEventHandler;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class FixGroupAccounts
|
||||
*/
|
||||
class FixGroupAccounts extends Command
|
||||
{
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Unify the source / destination accounts of split groups.';
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly-iii:unify-group-accounts';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
// select transaction_group_id, count(transaction_group_id) as the_count from transaction_journals group by transaction_group_id having the_count > 1
|
||||
$groups = [];
|
||||
$res = TransactionJournal
|
||||
::groupBy('transaction_group_id')
|
||||
->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]);
|
||||
foreach ($res as $journal) {
|
||||
if ((int) $journal->the_count > 1) {
|
||||
$groups[] = (int) $journal->transaction_group_id;
|
||||
}
|
||||
}
|
||||
$handler = new UpdatedGroupEventHandler;
|
||||
foreach($groups as $groupId) {
|
||||
$group = TransactionGroup::find($groupId);
|
||||
$event = new UpdatedTransactionGroup($group);
|
||||
$handler->unifyAccounts($event);
|
||||
}
|
||||
|
||||
$this->line('Updated inconsistent transaction groups.');
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* FixLongDescriptions.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
@@ -19,6 +20,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
@@ -65,7 +68,7 @@ class FixLongDescriptions extends Command
|
||||
$groups = TransactionGroup::get(['id', 'title']);
|
||||
/** @var TransactionGroup $group */
|
||||
foreach ($groups as $group) {
|
||||
if (strlen($group->title) > self::MAX_LENGTH) {
|
||||
if (strlen((string)$group->title) > self::MAX_LENGTH) {
|
||||
$group->title = substr($group->title, 0, self::MAX_LENGTH);
|
||||
$group->save();
|
||||
$this->line(sprintf('Truncated description of transaction group #%d', $group->id));
|
||||
@@ -73,6 +76,7 @@ class FixLongDescriptions extends Command
|
||||
}
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified all transaction group and journal title lengths in %s seconds.', $end));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@@ -54,7 +54,7 @@ class FixPiggies extends Command
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* FixRecurringTransactions.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
@@ -19,6 +20,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Models\Recurrence;
|
||||
@@ -51,22 +54,12 @@ class FixRecurringTransactions extends Command
|
||||
/** @var UserRepositoryInterface */
|
||||
private $userRepos;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
$this->stupidLaravel();
|
||||
|
@@ -90,11 +90,42 @@ class FixUnevenAmount extends Command
|
||||
}
|
||||
/** @var Transaction $source */
|
||||
$source = $journal->transactions()->where('amount', '<', 0)->first();
|
||||
|
||||
if (null === $source) {
|
||||
$this->error(
|
||||
sprintf(
|
||||
'Journal #%d ("%s") has no source transaction. It will be deleted to maintain database consistency.',
|
||||
$journal->id ?? 0,
|
||||
$journal->description ?? ''
|
||||
)
|
||||
);
|
||||
Transaction::where('transaction_journal_id', $journal->id ?? 0)->forceDelete();
|
||||
TransactionJournal::where('id', $journal->description ?? 0)->forceDelete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$amount = bcmul('-1', (string) $source->amount);
|
||||
|
||||
// fix amount of destination:
|
||||
/** @var Transaction $destination */
|
||||
$destination = $journal->transactions()->where('amount', '>', 0)->first();
|
||||
|
||||
if (null === $destination) {
|
||||
$this->error(
|
||||
sprintf(
|
||||
'Journal #%d ("%s") has no destination transaction. It will be deleted to maintain database consistency.',
|
||||
$journal->id ?? 0,
|
||||
$journal->description ?? ''
|
||||
)
|
||||
);
|
||||
|
||||
Transaction::where('transaction_journal_id', $journal->id ?? 0)->forceDelete();
|
||||
TransactionJournal::where('id', $journal->description ?? 0)->forceDelete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$destination->amount = $amount;
|
||||
$destination->save();
|
||||
|
||||
|
@@ -48,7 +48,7 @@ class RemoveBills extends Command
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
|
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* CreateDatabase.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
@@ -21,6 +20,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
@@ -49,16 +50,17 @@ class CreateDatabase extends Command
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
public function handle(): int
|
||||
{
|
||||
if ('mysql' !== env('DB_CONNECTION')) {
|
||||
$this->info(sprintf('CreateDB does not apply to "%s", skipped.', env('DB_CONNECTION')));
|
||||
|
||||
return 0;
|
||||
}
|
||||
// try to set up a raw connection:
|
||||
$dsn = sprintf('mysql:host=%s;charset=utf8mb4', env('DB_HOST'));
|
||||
$dsn = sprintf('mysql:host=%s;port=%d;charset=utf8mb4', env('DB_HOST', 'localhost'), env('DB_PORT', '3306'));
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
|
@@ -30,6 +30,7 @@ use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Preference;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use JsonException;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@@ -54,8 +55,8 @@ class DecryptDatabase extends Command
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @throws FireflyException
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
@@ -92,7 +93,11 @@ class DecryptDatabase extends Command
|
||||
// A separate routine for preferences:
|
||||
if ('preferences' === $table) {
|
||||
// try to json_decrypt the value.
|
||||
$value = json_decode($value, true) ?? $value;
|
||||
try {
|
||||
$value = json_decode($value, true, 512, JSON_THROW_ON_ERROR) ?? $value;
|
||||
} catch(JsonException $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
Log::debug(sprintf('Decrypted field "%s" "%s" to "%s" in table "%s" (row #%d)', $field, $original, print_r($value, true), $table, $id));
|
||||
|
||||
/** @var Preference $object */
|
||||
@@ -142,8 +147,9 @@ class DecryptDatabase extends Command
|
||||
* Tries to decrypt data. Will only throw an exception when the MAC is invalid.
|
||||
*
|
||||
* @param $value
|
||||
* @return string
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @return string
|
||||
*/
|
||||
private function tryDecrypt($value)
|
||||
{
|
||||
@@ -153,7 +159,6 @@ class DecryptDatabase extends Command
|
||||
if ('The MAC is invalid.' === $e->getMessage()) {
|
||||
throw new FireflyException($e->getMessage()); // @codeCoverageIgnore
|
||||
}
|
||||
Log::debug(sprintf('Could not decrypt. %s', $e->getMessage()));
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ExportData.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
@@ -19,9 +20,12 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Export;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Console\Commands\VerifiesAccessToken;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\AccountType;
|
||||
@@ -32,6 +36,7 @@ use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use InvalidArgumentException;
|
||||
use League\Csv\CannotInsertRecord;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@@ -76,21 +81,12 @@ class ExportData extends Command
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @throws FireflyException
|
||||
* @throws CannotInsertRecord
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
@@ -142,8 +138,12 @@ class ExportData extends Command
|
||||
$this->exportData($options, $data);
|
||||
} catch (FireflyException $e) {
|
||||
$this->error(sprintf('Could not store data: %s', $e->getMessage()));
|
||||
|
||||
app('telemetry')->feature('system.command.errored', $this->signature);
|
||||
return 1;
|
||||
}
|
||||
|
||||
app('telemetry')->feature('system.command.executed', $this->signature);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -171,8 +171,8 @@ class ExportData extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
* @throws FireflyException
|
||||
* @return Collection
|
||||
*/
|
||||
private function getAccountsParameter(): Collection
|
||||
{
|
||||
@@ -204,8 +204,9 @@ class ExportData extends Command
|
||||
/**
|
||||
* @param string $field
|
||||
*
|
||||
* @return Carbon
|
||||
* @throws FireflyException
|
||||
* @throws Exception
|
||||
* @return Carbon
|
||||
*/
|
||||
private function getDateParameter(string $field): Carbon
|
||||
{
|
||||
@@ -239,12 +240,13 @@ class ExportData extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws FireflyException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getExportDirectory(): string
|
||||
{
|
||||
$directory = $this->option('export_directory');
|
||||
$directory = (string) $this->option('export_directory');
|
||||
if (null === $directory) {
|
||||
$directory = './';
|
||||
}
|
||||
@@ -256,8 +258,8 @@ class ExportData extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
* @return array
|
||||
*/
|
||||
private function parseOptions(): array
|
||||
{
|
||||
|
@@ -1,332 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* CreateCSVImport.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @noinspection MultipleReturnStatementsInspection */
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Import;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Console\Commands\VerifiesAccessToken;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Import\Routine\RoutineInterface;
|
||||
use FireflyIII\Import\Storage\ImportArrayStorage;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class CreateCSVImport.
|
||||
*/
|
||||
class CreateCSVImport extends Command
|
||||
{
|
||||
use VerifiesAccessToken;
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Use this command to create a new CSV file import.';
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature
|
||||
= 'firefly-iii:csv-import
|
||||
{file? : The CSV file to import.}
|
||||
{configuration? : The configuration file to use for the import.}
|
||||
{--user=1 : The user ID that the import should import for.}
|
||||
{--token= : The user\'s access token.}';
|
||||
/** @var UserRepositoryInterface */
|
||||
private $userRepository;
|
||||
/** @var ImportJobRepositoryInterface */
|
||||
private $importRepository;
|
||||
/** @var ImportJob */
|
||||
private $importJob;
|
||||
|
||||
/**
|
||||
* Run the command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$this->stupidLaravel();
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!$this->verifyAccessToken()) {
|
||||
$this->errorLine('Invalid access token.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!$this->validArguments()) {
|
||||
$this->errorLine('Invalid arguments.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
/** @var User $user */
|
||||
$user = $this->userRepository->findNull((int)$this->option('user'));
|
||||
$file = (string)$this->argument('file');
|
||||
$configuration = (string)$this->argument('configuration');
|
||||
|
||||
$this->importRepository->setUser($user);
|
||||
|
||||
$configurationData = json_decode(file_get_contents($configuration), true);
|
||||
$this->importJob = $this->importRepository->create('file');
|
||||
|
||||
|
||||
// inform user (and log it)
|
||||
$this->infoLine(sprintf('Import file : %s', $file));
|
||||
$this->infoLine(sprintf('Configuration file : %s', $configuration));
|
||||
$this->infoLine(sprintf('User : #%d (%s)', $user->id, $user->email));
|
||||
$this->infoLine(sprintf('Job : %s', $this->importJob->key));
|
||||
|
||||
try {
|
||||
$this->storeFile($file);
|
||||
} catch (FireflyException $e) {
|
||||
$this->errorLine($e->getMessage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// job is ready to go
|
||||
$this->importRepository->setConfiguration($this->importJob, $configurationData);
|
||||
$this->importRepository->setStatus($this->importJob, 'ready_to_run');
|
||||
|
||||
$this->infoLine('The import routine has started. The process is not visible. Please wait.');
|
||||
Log::debug('Go for import!');
|
||||
|
||||
|
||||
// keep repeating this call until job lands on "provider_finished"
|
||||
try {
|
||||
$this->processFile();
|
||||
} catch (FireflyException $e) {
|
||||
$this->errorLine($e->getMessage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// then store data:
|
||||
try {
|
||||
$this->storeData();
|
||||
} catch (FireflyException $e) {
|
||||
$this->errorLine($e->getMessage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// give feedback:
|
||||
$this->giveFeedback();
|
||||
|
||||
// clear cache for user:
|
||||
app('preferences')->setForUser($user, 'lastActivity', microtime());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->userRepository = app(UserRepositoryInterface::class);
|
||||
$this->importRepository = app(ImportJobRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array|null $data
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function errorLine(string $message, array $data = null): void
|
||||
{
|
||||
Log::error($message, $data ?? []);
|
||||
$this->error($message);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $data
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function infoLine(string $message, array $data = null): void
|
||||
{
|
||||
Log::info($message, $data ?? []);
|
||||
$this->line($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify user inserts correct arguments.
|
||||
*
|
||||
* @noinspection MultipleReturnStatementsInspection
|
||||
* @return bool
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function validArguments(): bool
|
||||
{
|
||||
$file = (string)$this->argument('file');
|
||||
$configuration = (string)$this->argument('configuration');
|
||||
$cwd = getcwd();
|
||||
$enabled = (bool)config('import.enabled.file');
|
||||
|
||||
if (false === $enabled) {
|
||||
$this->errorLine('CSV Provider is not enabled.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_exists($file)) {
|
||||
$this->errorLine(sprintf('Firefly III cannot find file "%s" (working directory: "%s").', $file, $cwd));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_exists($configuration)) {
|
||||
$this->errorLine(sprintf('Firefly III cannot find configuration file "%s" (working directory: "%s").', $configuration, $cwd));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$configurationData = json_decode(file_get_contents($configuration), true);
|
||||
if (null === $configurationData) {
|
||||
$this->errorLine(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the supplied file as an attachment to this job.
|
||||
*
|
||||
* @param string $file
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function storeFile(string $file): void
|
||||
{
|
||||
// store file as attachment.
|
||||
if ('' !== $file) {
|
||||
$messages = $this->importRepository->storeCLIUpload($this->importJob, 'import_file', $file);
|
||||
if ($messages->count() > 0) {
|
||||
throw new FireflyException($messages->first());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep repeating import call until job lands on "provider_finished".
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function processFile(): void
|
||||
{
|
||||
$className = config('import.routine.file');
|
||||
$valid = ['provider_finished'];
|
||||
$count = 0;
|
||||
|
||||
while (!in_array($this->importJob->status, $valid, true) && $count < 6) {
|
||||
Log::debug(sprintf('Now in loop #%d.', $count + 1));
|
||||
/** @var RoutineInterface $routine */
|
||||
$routine = app($className);
|
||||
$routine->setImportJob($this->importJob);
|
||||
try {
|
||||
$routine->run();
|
||||
} catch (FireflyException|Exception $e) {
|
||||
$message = 'The import routine crashed: ' . $e->getMessage();
|
||||
Log::error($message);
|
||||
Log::error($e->getTraceAsString());
|
||||
|
||||
// set job errored out:
|
||||
$this->importRepository->setStatus($this->importJob, 'error');
|
||||
throw new FireflyException($message);
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
$this->importRepository->setStatus($this->importJob, 'provider_finished');
|
||||
$this->importJob->status = 'provider_finished';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function storeData(): void
|
||||
{
|
||||
if ('provider_finished' === $this->importJob->status) {
|
||||
$this->infoLine('Import has finished. Please wait for storage of data.');
|
||||
// set job to be storing data:
|
||||
$this->importRepository->setStatus($this->importJob, 'storing_data');
|
||||
|
||||
/** @var ImportArrayStorage $storage */
|
||||
$storage = app(ImportArrayStorage::class);
|
||||
$storage->setImportJob($this->importJob);
|
||||
|
||||
try {
|
||||
$storage->store();
|
||||
} catch (FireflyException|Exception $e) {
|
||||
$message = 'The import routine crashed: ' . $e->getMessage();
|
||||
Log::error($message);
|
||||
Log::error($e->getTraceAsString());
|
||||
|
||||
// set job errored out:
|
||||
$this->importRepository->setStatus($this->importJob, 'error');
|
||||
throw new FireflyException($message);
|
||||
|
||||
}
|
||||
// set storage to be finished:
|
||||
$this->importRepository->setStatus($this->importJob, 'storage_finished');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function giveFeedback(): void
|
||||
{
|
||||
$this->infoLine('Job has finished.');
|
||||
|
||||
|
||||
if (null !== $this->importJob->tag) {
|
||||
$this->infoLine(sprintf('%d transaction(s) have been imported.', $this->importJob->tag->transactionJournals->count()));
|
||||
$this->infoLine(sprintf('You can find your transactions under tag "%s"', $this->importJob->tag->tag));
|
||||
}
|
||||
|
||||
if (null === $this->importJob->tag) {
|
||||
$this->errorLine('No transactions have been imported :(.');
|
||||
}
|
||||
if (count($this->importJob->errors) > 0) {
|
||||
$this->infoLine(sprintf('%d error(s) occurred:', count($this->importJob->errors)));
|
||||
foreach ($this->importJob->errors as $err) {
|
||||
$this->errorLine('- ' . $err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -30,6 +30,7 @@ use Schema;
|
||||
|
||||
/**
|
||||
* Class ReportIntegrity
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class ReportIntegrity extends Command
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RestoreOAuthKeys.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
@@ -19,12 +20,13 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Integrity;
|
||||
|
||||
use Artisan;
|
||||
use Crypt;
|
||||
use FireflyIII\Support\System\OAuthKeys;
|
||||
use Illuminate\Console\Command;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class RestoreOAuthKeys
|
||||
@@ -93,7 +95,9 @@ class RestoreOAuthKeys extends Command
|
||||
*/
|
||||
private function restoreOAuthKeys(): void
|
||||
{
|
||||
Log::debug('Going to restoreOAuthKeys()');
|
||||
if (!$this->keysInDatabase() && !$this->keysOnDrive()) {
|
||||
Log::debug('Keys are not in DB and keys are not on the drive.');
|
||||
$this->generateKeys();
|
||||
$this->storeKeysInDB();
|
||||
$this->line('Generated and stored new keys.');
|
||||
@@ -101,12 +105,14 @@ class RestoreOAuthKeys extends Command
|
||||
return;
|
||||
}
|
||||
if ($this->keysInDatabase() && !$this->keysOnDrive()) {
|
||||
Log::debug('Keys are in DB and keys are not on the drive. Restore.');
|
||||
$this->restoreKeysFromDB();
|
||||
$this->line('Restored OAuth keys from database.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (!$this->keysInDatabase() && $this->keysOnDrive()) {
|
||||
Log::debug('Keys are not in DB and keys are on the drive. Save in DB.');
|
||||
$this->storeKeysInDB();
|
||||
$this->line('Stored OAuth keys in database.');
|
||||
|
||||
|
@@ -64,7 +64,6 @@ class ScanAttachments extends Command
|
||||
/** @var Attachment $attachment */
|
||||
foreach ($attachments as $attachment) {
|
||||
$fileName = $attachment->fileName();
|
||||
$decryptedContent = '';
|
||||
try {
|
||||
$encryptedContent = $disk->get($fileName);
|
||||
} catch (FileNotFoundException $e) {
|
||||
@@ -87,6 +86,7 @@ class ScanAttachments extends Command
|
||||
$this->line(sprintf('Fixed attachment #%d', $attachment->id));
|
||||
}
|
||||
|
||||
app('telemetry')->feature('system.command.executed', $this->signature);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* SetLatestVersion.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
@@ -21,10 +20,15 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class SetLatestVersion
|
||||
*/
|
||||
class SetLatestVersion extends Command
|
||||
{
|
||||
/**
|
||||
@@ -32,7 +36,7 @@ class SetLatestVersion extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
protected $description = 'Set latest version in DB.';
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
@@ -40,32 +44,24 @@ class SetLatestVersion extends Command
|
||||
*/
|
||||
protected $signature = 'firefly-iii:set-latest-version {--james-is-cool}';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
public function handle(): int
|
||||
{
|
||||
if (!$this->option('james-is-cool')) {
|
||||
$this->error('Am too!');
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
app('fireflyconfig')->set('db_version', config('firefly.db_version'));
|
||||
app('fireflyconfig')->set('ff3_version', config('firefly.version'));
|
||||
$this->line('Updated version.');
|
||||
|
||||
app('telemetry')->feature('system.command.executed', $this->signature);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@@ -60,7 +60,7 @@ class ApplyRules extends Command
|
||||
*/
|
||||
protected $signature
|
||||
= 'firefly-iii:apply-rules
|
||||
{--user=1 : The user ID that the import should import for.}
|
||||
{--user=1 : The user ID.}
|
||||
{--token= : The user\'s access token.}
|
||||
{--accounts= : A comma-separated list of asset accounts or liabilities to apply your rules to.}
|
||||
{--rule_groups= : A comma-separated list of rule groups to apply. Take the ID\'s of these rule groups from the Firefly III interface.}
|
||||
@@ -68,35 +68,32 @@ class ApplyRules extends Command
|
||||
{--all_rules : If set, will overrule both settings and simply apply ALL of your rules.}
|
||||
{--start_date= : The date of the earliest transaction to be included (inclusive). If omitted, will be your very first transaction ever. Format: YYYY-MM-DD}
|
||||
{--end_date= : The date of the latest transaction to be included (inclusive). If omitted, will be your latest transaction ever. Format: YYYY-MM-DD}';
|
||||
|
||||
/** @var Collection */
|
||||
private $accounts;
|
||||
/** @var array */
|
||||
private $acceptedAccounts;
|
||||
/** @var Collection */
|
||||
private $accounts;
|
||||
/** @var bool */
|
||||
private $allRules;
|
||||
/** @var Carbon */
|
||||
private $endDate;
|
||||
/** @var Collection */
|
||||
private $groups;
|
||||
/** @var RuleGroupRepositoryInterface */
|
||||
private $ruleGroupRepository;
|
||||
/** @var array */
|
||||
private $ruleGroupSelection;
|
||||
/** @var RuleRepositoryInterface */
|
||||
private $ruleRepository;
|
||||
/** @var array */
|
||||
private $ruleSelection;
|
||||
/** @var Carbon */
|
||||
private $startDate;
|
||||
/** @var Collection */
|
||||
private $groups;
|
||||
/** @var bool */
|
||||
private $allRules;
|
||||
|
||||
/** @var RuleRepositoryInterface */
|
||||
private $ruleRepository;
|
||||
|
||||
/** @var RuleGroupRepositoryInterface */
|
||||
private $ruleGroupRepository;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @throws FireflyException
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
@@ -115,6 +112,7 @@ class ApplyRules extends Command
|
||||
|
||||
$result = $this->verifyInput();
|
||||
if (false === $result) {
|
||||
app('telemetry')->feature('system.command.errored', $this->signature);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -132,6 +130,9 @@ class ApplyRules extends Command
|
||||
$this->warn(' --rules=1,2,...');
|
||||
$this->warn(' --rule_groups=1,2,...');
|
||||
$this->warn(' --all_rules');
|
||||
|
||||
app('telemetry')->feature('system.command.errored', $this->signature);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
@@ -166,9 +167,53 @@ class ApplyRules extends Command
|
||||
$this->line('');
|
||||
$this->line('Done!');
|
||||
|
||||
app('telemetry')->feature('system.command.executed', $this->signature);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getRulesToApply(): array
|
||||
{
|
||||
$rulesToApply = [];
|
||||
/** @var RuleGroup $group */
|
||||
foreach ($this->groups as $group) {
|
||||
$rules = $this->ruleGroupRepository->getActiveStoreRules($group);
|
||||
/** @var Rule $rule */
|
||||
foreach ($rules as $rule) {
|
||||
// if in rule selection, or group in selection or all rules, it's included.
|
||||
$test = $this->includeRule($rule, $group);
|
||||
if (true === $test) {
|
||||
Log::debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title));
|
||||
$rulesToApply[] = $rule->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rulesToApply;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
private function grabAllRules(): void
|
||||
{
|
||||
$this->groups = $this->ruleGroupRepository->getActiveGroups();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Rule $rule
|
||||
* @param RuleGroup $group
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function includeRule(Rule $rule, RuleGroup $group): bool
|
||||
{
|
||||
return in_array($group->id, $this->ruleGroupSelection, true)
|
||||
|| in_array($rule->id, $this->ruleSelection, true)
|
||||
|| $this->allRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
@@ -189,8 +234,8 @@ class ApplyRules extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws FireflyException
|
||||
* @return bool
|
||||
*/
|
||||
private function verifyInput(): bool
|
||||
{
|
||||
@@ -212,8 +257,8 @@ class ApplyRules extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws FireflyException
|
||||
* @return bool
|
||||
*/
|
||||
private function verifyInputAccounts(): bool
|
||||
{
|
||||
@@ -258,6 +303,42 @@ class ApplyRules extends Command
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function verifyInputDates(): void
|
||||
{
|
||||
// parse start date.
|
||||
$startDate = Carbon::now()->startOfMonth();
|
||||
$startString = $this->option('start_date');
|
||||
if (null === $startString) {
|
||||
/** @var JournalRepositoryInterface $repository */
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
$repository->setUser($this->getUser());
|
||||
$first = $repository->firstNull();
|
||||
if (null !== $first) {
|
||||
$startDate = $first->date;
|
||||
}
|
||||
}
|
||||
if (null !== $startString && '' !== $startString) {
|
||||
$startDate = Carbon::createFromFormat('Y-m-d', $startString);
|
||||
}
|
||||
|
||||
// parse end date
|
||||
$endDate = Carbon::now();
|
||||
$endString = $this->option('end_date');
|
||||
if (null !== $endString && '' !== $endString) {
|
||||
$endDate = Carbon::createFromFormat('Y-m-d', $endString);
|
||||
}
|
||||
|
||||
if ($startDate > $endDate) {
|
||||
[$endDate, $startDate] = [$startDate, $endDate];
|
||||
}
|
||||
|
||||
$this->startDate = $startDate;
|
||||
$this->endDate = $endDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@@ -317,82 +398,4 @@ class ApplyRules extends Command
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function verifyInputDates(): void
|
||||
{
|
||||
// parse start date.
|
||||
$startDate = Carbon::now()->startOfMonth();
|
||||
$startString = $this->option('start_date');
|
||||
if (null === $startString) {
|
||||
/** @var JournalRepositoryInterface $repository */
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
$repository->setUser($this->getUser());
|
||||
$first = $repository->firstNull();
|
||||
if (null !== $first) {
|
||||
$startDate = $first->date;
|
||||
}
|
||||
}
|
||||
if (null !== $startString && '' !== $startString) {
|
||||
$startDate = Carbon::createFromFormat('Y-m-d', $startString);
|
||||
}
|
||||
|
||||
// parse end date
|
||||
$endDate = Carbon::now();
|
||||
$endString = $this->option('end_date');
|
||||
if (null !== $endString && '' !== $endString) {
|
||||
$endDate = Carbon::createFromFormat('Y-m-d', $endString);
|
||||
}
|
||||
|
||||
if ($startDate > $endDate) {
|
||||
[$endDate, $startDate] = [$startDate, $endDate];
|
||||
}
|
||||
|
||||
$this->startDate = $startDate;
|
||||
$this->endDate = $endDate;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
private function grabAllRules(): void
|
||||
{
|
||||
$this->groups = $this->ruleGroupRepository->getActiveGroups();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Rule $rule
|
||||
* @param RuleGroup $group
|
||||
* @return bool
|
||||
*/
|
||||
private function includeRule(Rule $rule, RuleGroup $group): bool
|
||||
{
|
||||
return in_array($group->id, $this->ruleGroupSelection, true) ||
|
||||
in_array($rule->id, $this->ruleSelection, true) ||
|
||||
$this->allRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getRulesToApply(): array
|
||||
{
|
||||
$rulesToApply = [];
|
||||
/** @var RuleGroup $group */
|
||||
foreach ($this->groups as $group) {
|
||||
$rules = $this->ruleGroupRepository->getActiveStoreRules($group);
|
||||
/** @var Rule $rule */
|
||||
foreach ($rules as $rule) {
|
||||
// if in rule selection, or group in selection or all rules, it's included.
|
||||
$test = $this->includeRule($rule, $group);
|
||||
if (true === $test) {
|
||||
Log::debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title));
|
||||
$rulesToApply[] = $rule->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rulesToApply;
|
||||
}
|
||||
}
|
||||
|
@@ -27,9 +27,12 @@ namespace FireflyIII\Console\Commands\Tools;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Support\Cronjobs\AutoBudgetCronjob;
|
||||
use FireflyIII\Support\Cronjobs\RecurringCronjob;
|
||||
use FireflyIII\Support\Cronjobs\TelemetryCronjob;
|
||||
use Illuminate\Console\Command;
|
||||
use InvalidArgumentException;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class Cron
|
||||
@@ -56,45 +59,137 @@ class Cron extends Command
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @throws Exception
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$date = null;
|
||||
try {
|
||||
$date = new Carbon($this->option('date'));
|
||||
} catch (InvalidArgumentException $e) {
|
||||
} catch (InvalidArgumentException|Exception $e) {
|
||||
$this->error(sprintf('"%s" is not a valid date', $this->option('date')));
|
||||
$e->getMessage();
|
||||
}
|
||||
$force = (bool) $this->option('force');
|
||||
|
||||
/*
|
||||
* Fire recurring transaction cron job.
|
||||
*/
|
||||
try {
|
||||
$this->recurringCronJob($force, $date);
|
||||
} catch (FireflyException $e) {
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
/*
|
||||
* Fire auto-budget cron job:
|
||||
*/
|
||||
try {
|
||||
$this->autoBudgetCronJob($force, $date);
|
||||
} catch (FireflyException $e) {
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
/*
|
||||
* Fire telemetry cron job
|
||||
*/
|
||||
try {
|
||||
$this->telemetryCronJob($force, $date);
|
||||
} catch (FireflyException $e) {
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
$this->info('More feedback on the cron jobs can be found in the log files.');
|
||||
|
||||
app('telemetry')->feature('system.command.executed', $this->signature);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @throws Exception
|
||||
*/
|
||||
private function autoBudgetCronJob(bool $force, ?Carbon $date): void
|
||||
{
|
||||
$autoBudget = new AutoBudgetCronjob;
|
||||
$autoBudget->setForce($force);
|
||||
// set date in cron job:
|
||||
if (null !== $date) {
|
||||
$autoBudget->setDate($date);
|
||||
}
|
||||
|
||||
$result = $autoBudget->fire();
|
||||
|
||||
if (false === $result) {
|
||||
$this->line('The auto budget cron job did not fire.');
|
||||
}
|
||||
if (true === $result) {
|
||||
$this->line('The auto budget cron job fired successfully.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function recurringCronJob(bool $force, ?Carbon $date): void
|
||||
{
|
||||
$recurring = new RecurringCronjob;
|
||||
$recurring->setForce($this->option('force'));
|
||||
$recurring->setForce($force);
|
||||
|
||||
// set date in cron job:
|
||||
if (null !== $date) {
|
||||
$recurring->setDate($date);
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $recurring->fire();
|
||||
} catch (FireflyException $e) {
|
||||
$this->error($e->getMessage());
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (false === $result) {
|
||||
$this->line('The recurring transaction cron job did not fire.');
|
||||
}
|
||||
if (true === $result) {
|
||||
$this->line('The recurring transaction cron job fired successfully.');
|
||||
}
|
||||
|
||||
$this->info('More feedback on the cron jobs can be found in the log files.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
*/
|
||||
private function telemetryCronJob(bool $force, ?Carbon $date): void
|
||||
{
|
||||
if (false === config('firefly.send_telemetry') || false === config('firefly.feature_flags.telemetry')) {
|
||||
// if not configured to do anything with telemetry, do nothing.
|
||||
return;
|
||||
}
|
||||
$telemetry = new TelemetryCronJob;
|
||||
$telemetry->setForce($force);
|
||||
|
||||
// set date in cron job:
|
||||
if (null !== $date) {
|
||||
$telemetry->setDate($date);
|
||||
}
|
||||
|
||||
$result = $telemetry->fire();
|
||||
|
||||
if (false === $result) {
|
||||
$this->line('The telemetry cron job did not fire.');
|
||||
}
|
||||
if (true === $result) {
|
||||
$this->line('The telemetry cron job fired successfully.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -54,10 +54,10 @@ class AccountCurrencies extends Command
|
||||
protected $signature = 'firefly-iii:account-currencies {--F|force : Force the execution of this command.}';
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $accountRepos;
|
||||
/** @var UserRepositoryInterface */
|
||||
private $userRepos;
|
||||
/** @var int */
|
||||
private $count;
|
||||
/** @var UserRepositoryInterface */
|
||||
private $userRepos;
|
||||
|
||||
/**
|
||||
* Each (asset) account must have a reference to a preferred currency. If the account does not have one, it's forced upon the account.
|
||||
@@ -87,21 +87,8 @@ class AccountCurrencies extends Command
|
||||
$this->info(sprintf('Verified and fixed account currencies in %s seconds.', $end));
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->userRepos = app(UserRepositoryInterface::class);
|
||||
$this->count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,7 +104,6 @@ class AccountCurrencies extends Command
|
||||
return false; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -126,6 +112,20 @@ class AccountCurrencies extends Command
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->userRepos = app(UserRepositoryInterface::class);
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param TransactionCurrency $currency
|
||||
@@ -178,7 +178,8 @@ class AccountCurrencies extends Command
|
||||
static function (Transaction $transaction) use ($accountCurrency) {
|
||||
$transaction->transaction_currency_id = $accountCurrency;
|
||||
$transaction->save();
|
||||
});
|
||||
}
|
||||
);
|
||||
$this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name));
|
||||
$this->count++;
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* MigrateRecurrenceMeta.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
@@ -19,6 +20,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Models\RecurrenceMeta;
|
||||
@@ -115,7 +118,7 @@ class MigrateRecurrenceMeta extends Command
|
||||
|
||||
if ('tags' === $meta->name) {
|
||||
$array = explode(',', $meta->value);
|
||||
$value = json_encode($array);
|
||||
$value = json_encode($array, JSON_THROW_ON_ERROR, 512);
|
||||
}
|
||||
|
||||
RecurrenceTransactionMeta::create(
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* MigrateTagLocations.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
@@ -19,6 +20,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Models\Location;
|
||||
|
@@ -59,21 +59,21 @@ class MigrateToGroups extends Command
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly-iii:migrate-to-groups {--F|force : Force the migration, even if it fired before.}';
|
||||
/** @var JournalCLIRepositoryInterface */
|
||||
private $cliRepository;
|
||||
private $count;
|
||||
/** @var TransactionGroupFactory */
|
||||
private $groupFactory;
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepository;
|
||||
/** @var JournalCLIRepositoryInterface */
|
||||
private $cliRepository;
|
||||
/** @var JournalDestroyService */
|
||||
private $service;
|
||||
private $count;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @throws Exception
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
@@ -112,26 +112,9 @@ class MigrateToGroups extends Command
|
||||
|
||||
$this->markAsMigrated();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
$this->journalRepository = app(JournalRepositoryInterface::class);
|
||||
$this->service = app(JournalDestroyService::class);
|
||||
$this->groupFactory = app(TransactionGroupFactory::class);
|
||||
$this->cliRepository = app(JournalCLIRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Transaction $transaction
|
||||
@@ -168,6 +151,72 @@ class MigrateToGroups extends Command
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $left
|
||||
* @param Transaction $right
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function getTransactionBudget(Transaction $left, Transaction $right): ?int
|
||||
{
|
||||
Log::debug('Now in getTransactionBudget()');
|
||||
|
||||
// try to get a budget ID from the left transaction:
|
||||
/** @var Budget $budget */
|
||||
$budget = $left->budgets()->first();
|
||||
if (null !== $budget) {
|
||||
Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id));
|
||||
|
||||
return (int) $budget->id;
|
||||
}
|
||||
|
||||
// try to get a budget ID from the right transaction:
|
||||
/** @var Budget $budget */
|
||||
$budget = $right->budgets()->first();
|
||||
if (null !== $budget) {
|
||||
Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id));
|
||||
|
||||
return (int) $budget->id;
|
||||
}
|
||||
Log::debug('Neither left or right have a budget, return NULL');
|
||||
|
||||
// if all fails, return NULL.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $left
|
||||
* @param Transaction $right
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function getTransactionCategory(Transaction $left, Transaction $right): ?int
|
||||
{
|
||||
Log::debug('Now in getTransactionCategory()');
|
||||
|
||||
// try to get a category ID from the left transaction:
|
||||
/** @var Category $category */
|
||||
$category = $left->categories()->first();
|
||||
if (null !== $category) {
|
||||
Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id));
|
||||
|
||||
return (int) $category->id;
|
||||
}
|
||||
|
||||
// try to get a category ID from the left transaction:
|
||||
/** @var Category $category */
|
||||
$category = $right->categories()->first();
|
||||
if (null !== $category) {
|
||||
Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id));
|
||||
|
||||
return (int) $category->id;
|
||||
}
|
||||
Log::debug('Neither left or right have a category, return NULL');
|
||||
|
||||
// if all fails, return NULL.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*/
|
||||
@@ -302,7 +351,8 @@ class MigrateToGroups extends Command
|
||||
$this->error(
|
||||
sprintf(
|
||||
'Journal #%d has no opposing transaction for transaction #%d. Cannot upgrade this entry.',
|
||||
$journal->id, $transaction->id
|
||||
$journal->id,
|
||||
$transaction->id
|
||||
)
|
||||
);
|
||||
continue;
|
||||
@@ -365,81 +415,23 @@ class MigrateToGroups extends Command
|
||||
|
||||
// report on result:
|
||||
Log::debug(
|
||||
sprintf('Migrated journal #%d into group #%d with these journals: #%s',
|
||||
$journal->id, $group->id, implode(', #', $group->transactionJournals->pluck('id')->toArray()))
|
||||
sprintf(
|
||||
'Migrated journal #%d into group #%d with these journals: #%s',
|
||||
$journal->id,
|
||||
$group->id,
|
||||
implode(', #', $group->transactionJournals->pluck('id')->toArray())
|
||||
)
|
||||
);
|
||||
$this->line(
|
||||
sprintf('Migrated journal #%d into group #%d with these journals: #%s',
|
||||
$journal->id, $group->id, implode(', #', $group->transactionJournals->pluck('id')->toArray()))
|
||||
sprintf(
|
||||
'Migrated journal #%d into group #%d with these journals: #%s',
|
||||
$journal->id,
|
||||
$group->id,
|
||||
implode(', #', $group->transactionJournals->pluck('id')->toArray())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $left
|
||||
* @param Transaction $right
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function getTransactionBudget(Transaction $left, Transaction $right): ?int
|
||||
{
|
||||
Log::debug('Now in getTransactionBudget()');
|
||||
|
||||
// try to get a budget ID from the left transaction:
|
||||
/** @var Budget $budget */
|
||||
$budget = $left->budgets()->first();
|
||||
if (null !== $budget) {
|
||||
Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id));
|
||||
|
||||
return (int)$budget->id;
|
||||
}
|
||||
|
||||
// try to get a budget ID from the right transaction:
|
||||
/** @var Budget $budget */
|
||||
$budget = $right->budgets()->first();
|
||||
if (null !== $budget) {
|
||||
Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id));
|
||||
|
||||
return (int)$budget->id;
|
||||
}
|
||||
Log::debug('Neither left or right have a budget, return NULL');
|
||||
|
||||
// if all fails, return NULL.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $left
|
||||
* @param Transaction $right
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function getTransactionCategory(Transaction $left, Transaction $right): ?int
|
||||
{
|
||||
Log::debug('Now in getTransactionCategory()');
|
||||
|
||||
// try to get a category ID from the left transaction:
|
||||
/** @var Category $category */
|
||||
$category = $left->categories()->first();
|
||||
if (null !== $category) {
|
||||
Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id));
|
||||
|
||||
return (int)$category->id;
|
||||
}
|
||||
|
||||
// try to get a category ID from the left transaction:
|
||||
/** @var Category $category */
|
||||
$category = $right->categories()->first();
|
||||
if (null !== $category) {
|
||||
Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id));
|
||||
|
||||
return (int)$category->id;
|
||||
}
|
||||
Log::debug('Neither left or right have a category, return NULL');
|
||||
|
||||
// if all fails, return NULL.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -448,4 +440,19 @@ class MigrateToGroups extends Command
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
$this->journalRepository = app(JournalRepositoryInterface::class);
|
||||
$this->service = app(JournalDestroyService::class);
|
||||
$this->groupFactory = app(TransactionGroupFactory::class);
|
||||
$this->cliRepository = app(JournalCLIRepositoryInterface::class);
|
||||
}
|
||||
}
|
||||
|
@@ -53,22 +53,21 @@ class MigrateToRules extends Command
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly-iii:bills-to-rules {--F|force : Force the execution of this command.}';
|
||||
|
||||
/** @var UserRepositoryInterface */
|
||||
private $userRepository;
|
||||
/** @var RuleGroupRepositoryInterface */
|
||||
private $ruleGroupRepository;
|
||||
/** @var BillRepositoryInterface */
|
||||
private $billRepository;
|
||||
private $count;
|
||||
/** @var RuleGroupRepositoryInterface */
|
||||
private $ruleGroupRepository;
|
||||
/** @var RuleRepositoryInterface */
|
||||
private $ruleRepository;
|
||||
private $count;
|
||||
/** @var UserRepositoryInterface */
|
||||
private $userRepository;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @throws FireflyException
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
@@ -103,22 +102,6 @@ class MigrateToRules extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
$this->userRepository = app(UserRepositoryInterface::class);
|
||||
$this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
||||
$this->billRepository = app(BillRepositoryInterface::class);
|
||||
$this->ruleRepository = app(RuleRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@@ -140,46 +123,10 @@ class MigrateToRules extends Command
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate bills to new rule structure for a specific user.
|
||||
*
|
||||
* @param User $user
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function migrateUser(User $user): void
|
||||
{
|
||||
$this->ruleGroupRepository->setUser($user);
|
||||
$this->billRepository->setUser($user);
|
||||
$this->ruleRepository->setUser($user);
|
||||
|
||||
/** @var Preference $lang */
|
||||
$lang = app('preferences')->getForUser($user, 'language', 'en_US');
|
||||
$groupTitle = (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data);
|
||||
$ruleGroup = $this->ruleGroupRepository->findByTitle($groupTitle);
|
||||
//$currency = $this->getCurrency($user);
|
||||
|
||||
if (null === $ruleGroup) {
|
||||
$ruleGroup = $this->ruleGroupRepository->store(
|
||||
[
|
||||
'title' => (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data),
|
||||
'description' => (string)trans('firefly.rulegroup_for_bills_description', [], $lang->data),
|
||||
'active' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
$bills = $this->billRepository->getBills();
|
||||
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
$this->migrateBill($ruleGroup, $bill, $lang);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RuleGroup $ruleGroup
|
||||
* @param Bill $bill
|
||||
* @throws FireflyException
|
||||
* @param Preference $language
|
||||
*/
|
||||
private function migrateBill(RuleGroup $ruleGroup, Bill $bill, Preference $language): void
|
||||
{
|
||||
@@ -246,4 +193,56 @@ class MigrateToRules extends Command
|
||||
$this->billRepository->update($bill, $newBillData);
|
||||
$this->count++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate bills to new rule structure for a specific user.
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function migrateUser(User $user): void
|
||||
{
|
||||
$this->ruleGroupRepository->setUser($user);
|
||||
$this->billRepository->setUser($user);
|
||||
$this->ruleRepository->setUser($user);
|
||||
|
||||
/** @var Preference $lang */
|
||||
$lang = app('preferences')->getForUser($user, 'language', 'en_US');
|
||||
$groupTitle = (string) trans('firefly.rulegroup_for_bills_title', [], $lang->data);
|
||||
$ruleGroup = $this->ruleGroupRepository->findByTitle($groupTitle);
|
||||
|
||||
if (null === $ruleGroup) {
|
||||
$ruleGroup = $this->ruleGroupRepository->store(
|
||||
[
|
||||
'title' => (string) trans('firefly.rulegroup_for_bills_title', [], $lang->data),
|
||||
'description' => (string) trans('firefly.rulegroup_for_bills_description', [], $lang->data),
|
||||
'active' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
$bills = $this->billRepository->getBills();
|
||||
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
$this->migrateBill($ruleGroup, $bill, $lang);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
$this->userRepository = app(UserRepositoryInterface::class);
|
||||
$this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
||||
$this->billRepository = app(BillRepositoryInterface::class);
|
||||
$this->ruleRepository = app(RuleRepositoryInterface::class);
|
||||
}
|
||||
}
|
||||
|
@@ -40,7 +40,6 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class OtherCurrenciesCorrections extends Command
|
||||
{
|
||||
|
||||
public const CONFIG_NAME = '480_other_currencies';
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -58,14 +57,14 @@ class OtherCurrenciesCorrections extends Command
|
||||
private $accountCurrencies;
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $accountRepos;
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepos;
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepos;
|
||||
/** @var JournalCLIRepositoryInterface */
|
||||
private $cliRepos;
|
||||
/** @var int */
|
||||
private $count;
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepos;
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepos;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
@@ -94,23 +93,6 @@ class OtherCurrenciesCorrections extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
$this->accountCurrencies = [];
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->journalRepos = app(JournalRepositoryInterface::class);
|
||||
$this->cliRepos = app(JournalCLIRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
@@ -136,110 +118,6 @@ class OtherCurrenciesCorrections extends Command
|
||||
$this->accountCurrencies[$accountId] = $currency;
|
||||
|
||||
return $currency;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
private function updateJournalCurrency(TransactionJournal $journal): void
|
||||
{
|
||||
$this->accountRepos->setUser($journal->user);
|
||||
$this->journalRepos->setUser($journal->user);
|
||||
$this->currencyRepos->setUser($journal->user);
|
||||
$this->cliRepos->setUser($journal->user);
|
||||
|
||||
$leadTransaction = $this->getLeadTransaction($journal);
|
||||
|
||||
if (null === $leadTransaction) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->error(sprintf('Could not reliably determine which transaction is in the lead for transaction journal #%d.', $journal->id));
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/** @var Account $account */
|
||||
$account = $leadTransaction->account;
|
||||
$currency = $this->getCurrency($account);
|
||||
if (null === $currency) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->error(sprintf('Account #%d ("%s") has no currency preference, so transaction journal #%d can\'t be corrected',
|
||||
$account->id, $account->name, $journal->id));
|
||||
$this->count++;
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
// fix each transaction:
|
||||
$journal->transactions->each(
|
||||
static function (Transaction $transaction) use ($currency) {
|
||||
if (null === $transaction->transaction_currency_id) {
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
}
|
||||
|
||||
// when mismatch in transaction:
|
||||
if (!((int)$transaction->transaction_currency_id === (int)$currency->id)) {
|
||||
$transaction->foreign_currency_id = (int)$transaction->transaction_currency_id;
|
||||
$transaction->foreign_amount = $transaction->amount;
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
}
|
||||
}
|
||||
);
|
||||
// also update the journal, of course:
|
||||
$journal->transaction_currency_id = $currency->id;
|
||||
$this->count++;
|
||||
$journal->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for
|
||||
* the accounts they are linked to.
|
||||
*
|
||||
* Both source and destination must match the respective currency preference of the related asset account.
|
||||
* So FF3 must verify all transactions.
|
||||
*
|
||||
*/
|
||||
private function updateOtherJournalsCurrencies(): void
|
||||
{
|
||||
$set =
|
||||
$this->cliRepos->getAllJournals(
|
||||
[
|
||||
TransactionType::WITHDRAWAL,
|
||||
TransactionType::DEPOSIT,
|
||||
TransactionType::OPENING_BALANCE,
|
||||
TransactionType::RECONCILIATION,
|
||||
]
|
||||
);
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
$this->updateJournalCurrency($journal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,4 +159,129 @@ class OtherCurrenciesCorrections extends Command
|
||||
|
||||
return $lead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool) $configVar->data;
|
||||
}
|
||||
|
||||
return false; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
$this->accountCurrencies = [];
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->journalRepos = app(JournalRepositoryInterface::class);
|
||||
$this->cliRepos = app(JournalCLIRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
private function updateJournalCurrency(TransactionJournal $journal): void
|
||||
{
|
||||
$this->accountRepos->setUser($journal->user);
|
||||
$this->journalRepos->setUser($journal->user);
|
||||
$this->currencyRepos->setUser($journal->user);
|
||||
$this->cliRepos->setUser($journal->user);
|
||||
|
||||
$leadTransaction = $this->getLeadTransaction($journal);
|
||||
|
||||
if (null === $leadTransaction) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->error(sprintf('Could not reliably determine which transaction is in the lead for transaction journal #%d.', $journal->id));
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/** @var Account $account */
|
||||
$account = $leadTransaction->account;
|
||||
$currency = $this->getCurrency($account);
|
||||
if (null === $currency) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->error(
|
||||
sprintf(
|
||||
'Account #%d ("%s") has no currency preference, so transaction journal #%d can\'t be corrected',
|
||||
$account->id,
|
||||
$account->name,
|
||||
$journal->id
|
||||
)
|
||||
);
|
||||
$this->count++;
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
// fix each transaction:
|
||||
$journal->transactions->each(
|
||||
static function (Transaction $transaction) use ($currency) {
|
||||
if (null === $transaction->transaction_currency_id) {
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
}
|
||||
|
||||
// when mismatch in transaction:
|
||||
if (!((int) $transaction->transaction_currency_id === (int) $currency->id)) {
|
||||
$transaction->foreign_currency_id = (int) $transaction->transaction_currency_id;
|
||||
$transaction->foreign_amount = $transaction->amount;
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
}
|
||||
}
|
||||
);
|
||||
// also update the journal, of course:
|
||||
$journal->transaction_currency_id = $currency->id;
|
||||
$this->count++;
|
||||
$journal->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for
|
||||
* the accounts they are linked to.
|
||||
*
|
||||
* Both source and destination must match the respective currency preference of the related asset account.
|
||||
* So FF3 must verify all transactions.
|
||||
*
|
||||
*/
|
||||
private function updateOtherJournalsCurrencies(): void
|
||||
{
|
||||
$set
|
||||
= $this->cliRepos->getAllJournals(
|
||||
[
|
||||
TransactionType::WITHDRAWAL,
|
||||
TransactionType::DEPOSIT,
|
||||
TransactionType::OPENING_BALANCE,
|
||||
TransactionType::RECONCILIATION,
|
||||
]
|
||||
);
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
$this->updateJournalCurrency($journal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -50,15 +50,12 @@ class TransactionIdentifier extends Command
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly-iii:transaction-identifiers {--F|force : Force the execution of this command.}';
|
||||
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepository;
|
||||
|
||||
/** @var JournalCLIRepositoryInterface */
|
||||
private $cliRepository;
|
||||
|
||||
/** @var int */
|
||||
private $count;
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepository;
|
||||
|
||||
/**
|
||||
* This method gives all transactions which are part of a split journal (so more than 2) a sort of "order" so they are easier
|
||||
@@ -108,17 +105,36 @@ class TransactionIdentifier extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
* @param Transaction $transaction
|
||||
* @param array $exclude
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @return Transaction|null
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
private function findOpposing(Transaction $transaction, array $exclude): ?Transaction
|
||||
{
|
||||
$this->journalRepository = app(JournalRepositoryInterface::class);
|
||||
$this->cliRepository = app(JournalCLIRepositoryInterface::class);
|
||||
$this->count = 0;
|
||||
// find opposing:
|
||||
$amount = bcmul((string) $transaction->amount, '-1');
|
||||
|
||||
try {
|
||||
/** @var Transaction $opposing */
|
||||
$opposing = Transaction::where('transaction_journal_id', $transaction->transaction_journal_id)
|
||||
->where('amount', $amount)->where('identifier', '=', 0)
|
||||
->whereNotIn('id', $exclude)
|
||||
->first();
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (QueryException $e) {
|
||||
Log::error($e->getMessage());
|
||||
$this->error('Firefly III could not find the "identifier" field in the "transactions" table.');
|
||||
$this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version')));
|
||||
$this->error('Please run "php artisan migrate" to add this field to the table.');
|
||||
$this->info('Then, run "php artisan firefly:upgrade-database" to try again.');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return $opposing;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,6 +158,20 @@ class TransactionIdentifier extends Command
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->journalRepository = app(JournalRepositoryInterface::class);
|
||||
$this->cliRepository = app(JournalCLIRepositoryInterface::class);
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab all positive transactions 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.
|
||||
@@ -171,36 +201,4 @@ class TransactionIdentifier extends Command
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
* @param array $exclude
|
||||
* @return Transaction|null
|
||||
*/
|
||||
private function findOpposing(Transaction $transaction, array $exclude): ?Transaction
|
||||
{
|
||||
// find opposing:
|
||||
$amount = bcmul((string)$transaction->amount, '-1');
|
||||
|
||||
try {
|
||||
/** @var Transaction $opposing */
|
||||
$opposing = Transaction::where('transaction_journal_id', $transaction->transaction_journal_id)
|
||||
->where('amount', $amount)->where('identifier', '=', 0)
|
||||
->whereNotIn('id', $exclude)
|
||||
->first();
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (QueryException $e) {
|
||||
Log::error($e->getMessage());
|
||||
$this->error('Firefly III could not find the "identifier" field in the "transactions" table.');
|
||||
$this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version')));
|
||||
$this->error('Please run "php artisan migrate" to add this field to the table.');
|
||||
$this->info('Then, run "php artisan firefly:upgrade-database" to try again.');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return $opposing;
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
@@ -41,7 +40,6 @@ use Log;
|
||||
*/
|
||||
class TransferCurrenciesCorrections extends Command
|
||||
{
|
||||
|
||||
public const CONFIG_NAME = '480_transfer_currencies';
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -59,27 +57,26 @@ class TransferCurrenciesCorrections extends Command
|
||||
private $accountCurrencies;
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $accountRepos;
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepos;
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepos;
|
||||
/** @var JournalCLIRepositoryInterface */
|
||||
private $cliRepos;
|
||||
/** @var int */
|
||||
private $count;
|
||||
|
||||
/** @var Transaction The source transaction of the current journal. */
|
||||
private $sourceTransaction;
|
||||
/** @var Account The source account of the current journal. */
|
||||
private $sourceAccount;
|
||||
/** @var TransactionCurrency The currency preference of the source account of the current journal. */
|
||||
private $sourceCurrency;
|
||||
/** @var Transaction The destination transaction of the current journal. */
|
||||
private $destinationTransaction;
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepos;
|
||||
/** @var Account The destination account of the current journal. */
|
||||
private $destinationAccount;
|
||||
/** @var TransactionCurrency The currency preference of the destination account of the current journal. */
|
||||
private $destinationCurrency;
|
||||
/** @var Transaction The destination transaction of the current journal. */
|
||||
private $destinationTransaction;
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepos;
|
||||
/** @var Account The source account of the current journal. */
|
||||
private $sourceAccount;
|
||||
/** @var TransactionCurrency The currency preference of the source account of the current journal. */
|
||||
private $sourceCurrency;
|
||||
/** @var Transaction The source transaction of the current journal. */
|
||||
private $sourceTransaction;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
@@ -118,95 +115,192 @@ class TransferCurrenciesCorrections extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* The destination transaction must have a currency. If not, it will be added by
|
||||
* taking it from the destination account's preference.
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
private function fixDestNoCurrency(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->journalRepos = app(JournalRepositoryInterface::class);
|
||||
$this->cliRepos = app(JournalCLIRepositoryInterface::class);
|
||||
$this->accountCurrencies = [];
|
||||
$this->resetInformation();
|
||||
if (null === $this->destinationTransaction->transaction_currency_id && null !== $this->destinationCurrency) {
|
||||
$this->destinationTransaction
|
||||
->transaction_currency_id
|
||||
= (int) $this->destinationCurrency->id;
|
||||
$message = sprintf(
|
||||
'Transaction #%d has no currency setting, now set to %s.',
|
||||
$this->destinationTransaction->id,
|
||||
$this->destinationCurrency->code
|
||||
);
|
||||
Log::debug($message);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$this->destinationTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return TransactionCurrency|null
|
||||
* If the foreign amount of the destination transaction is null, but that of the other isn't, use this piece of code
|
||||
* to restore it.
|
||||
*/
|
||||
private function getCurrency(Account $account): ?TransactionCurrency
|
||||
private function fixDestNullForeignAmount(): void
|
||||
{
|
||||
$accountId = $account->id;
|
||||
if (isset($this->accountCurrencies[$accountId]) && 0 === $this->accountCurrencies[$accountId]) {
|
||||
return null; // @codeCoverageIgnore
|
||||
if (null === $this->destinationTransaction->foreign_amount && null !== $this->sourceTransaction->foreign_amount) {
|
||||
$this->destinationTransaction->foreign_amount = bcmul((string) $this->sourceTransaction->foreign_amount, '-1');
|
||||
$this->destinationTransaction->save();
|
||||
$this->count++;
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Restored foreign amount of destination transaction #%d to %s',
|
||||
$this->destinationTransaction->id,
|
||||
$this->destinationTransaction->foreign_amount
|
||||
)
|
||||
);
|
||||
}
|
||||
if (isset($this->accountCurrencies[$accountId]) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) {
|
||||
return $this->accountCurrencies[$accountId]; // @codeCoverageIgnore
|
||||
}
|
||||
// TODO we can use getAccountCurrency() instead
|
||||
$currencyId = (int)$this->accountRepos->getMetaValue($account, 'currency_id');
|
||||
$result = $this->currencyRepos->findNull($currencyId);
|
||||
if (null === $result) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->accountCurrencies[$accountId] = 0;
|
||||
|
||||
return null;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
$this->accountCurrencies[$accountId] = $result;
|
||||
|
||||
return $result;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $transfer
|
||||
*
|
||||
* @return Transaction|null
|
||||
* @codeCoverageIgnore
|
||||
* The destination transaction must have the correct currency. If not, it will be set by
|
||||
* taking it from the destination account's preference.
|
||||
*/
|
||||
private function getDestinationTransaction(TransactionJournal $transfer): ?Transaction
|
||||
private function fixDestinationUnmatchedCurrency(): void
|
||||
{
|
||||
return $transfer->transactions()->where('amount', '>', 0)->first();
|
||||
if (null !== $this->destinationCurrency
|
||||
&& null === $this->destinationTransaction->foreign_amount
|
||||
&& (int) $this->destinationTransaction->transaction_currency_id !== (int) $this->destinationCurrency->id
|
||||
) {
|
||||
$message = sprintf(
|
||||
'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.',
|
||||
$this->destinationTransaction->id,
|
||||
$this->destinationTransaction->transaction_currency_id,
|
||||
$this->destinationAccount->id,
|
||||
$this->destinationTransaction->amount
|
||||
);
|
||||
Log::debug($message);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$this->destinationTransaction->transaction_currency_id = (int) $this->destinationCurrency->id;
|
||||
$this->destinationTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $transfer
|
||||
* If the destination account currency is the same as the source currency,
|
||||
* both foreign_amount and foreign_currency_id fields must be NULL
|
||||
* for both transactions (because foreign currency info would not make sense)
|
||||
*
|
||||
* @return Transaction|null
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function getSourceTransaction(TransactionJournal $transfer): ?Transaction
|
||||
private function fixInvalidForeignCurrency(): void
|
||||
{
|
||||
return $transfer->transactions()->where('amount', '<', 0)->first();
|
||||
if ((int) $this->destinationCurrency->id === (int) $this->sourceCurrency->id) {
|
||||
// update both transactions to match:
|
||||
$this->sourceTransaction->foreign_amount = null;
|
||||
$this->sourceTransaction->foreign_currency_id = null;
|
||||
|
||||
$this->destinationTransaction->foreign_amount = null;
|
||||
$this->destinationTransaction->foreign_currency_id = null;
|
||||
|
||||
$this->sourceTransaction->save();
|
||||
$this->destinationTransaction->save();
|
||||
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Currency for account "%s" is %s, and currency for account "%s" is also
|
||||
%s, so transactions #%d and #%d has been verified to be to %s exclusively.',
|
||||
$this->destinationAccount->name,
|
||||
$this->destinationCurrency->code,
|
||||
$this->sourceAccount->name,
|
||||
$this->sourceCurrency->code,
|
||||
$this->sourceTransaction->id,
|
||||
$this->destinationTransaction->id,
|
||||
$this->sourceCurrency->code
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* If destination account currency is different from source account currency,
|
||||
* then both transactions must get the source account's currency as normal currency
|
||||
* and the opposing account's currency as foreign currency.
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
private function fixMismatchedForeignCurrency(): void
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
if ((int) $this->sourceCurrency->id !== (int) $this->destinationCurrency->id) {
|
||||
$this->sourceTransaction->transaction_currency_id = $this->sourceCurrency->id;
|
||||
$this->sourceTransaction->foreign_currency_id = $this->destinationCurrency->id;
|
||||
$this->destinationTransaction->transaction_currency_id = $this->sourceCurrency->id;
|
||||
$this->destinationTransaction->foreign_currency_id = $this->destinationCurrency->id;
|
||||
|
||||
return false; // @codeCoverageIgnore
|
||||
$this->sourceTransaction->save();
|
||||
$this->destinationTransaction->save();
|
||||
$this->count++;
|
||||
Log::debug(sprintf('Verified foreign currency ID of transaction #%d and #%d', $this->sourceTransaction->id, $this->destinationTransaction->id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* The source transaction must have a currency. If not, it will be added by
|
||||
* taking it from the source account's preference.
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
private function fixSourceNoCurrency(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
if (null === $this->sourceTransaction->transaction_currency_id && null !== $this->sourceCurrency) {
|
||||
$this->sourceTransaction
|
||||
->transaction_currency_id
|
||||
= (int) $this->sourceCurrency->id;
|
||||
$message = sprintf(
|
||||
'Transaction #%d has no currency setting, now set to %s.',
|
||||
$this->sourceTransaction->id,
|
||||
$this->sourceCurrency->code
|
||||
);
|
||||
Log::debug($message);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$this->sourceTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the foreign amount of the source transaction is null, but that of the other isn't, use this piece of code
|
||||
* to restore it.
|
||||
*/
|
||||
private function fixSourceNullForeignAmount(): void
|
||||
{
|
||||
if (null === $this->sourceTransaction->foreign_amount && null !== $this->destinationTransaction->foreign_amount) {
|
||||
$this->sourceTransaction->foreign_amount = bcmul((string) $this->destinationTransaction->foreign_amount, '-1');
|
||||
$this->sourceTransaction->save();
|
||||
$this->count++;
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Restored foreign amount of source transaction #%d to %s',
|
||||
$this->sourceTransaction->id,
|
||||
$this->sourceTransaction->foreign_amount
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The source transaction must have the correct currency. If not, it will be set by
|
||||
* taking it from the source account's preference.
|
||||
*/
|
||||
private function fixSourceUnmatchedCurrency(): void
|
||||
{
|
||||
if (null !== $this->sourceCurrency
|
||||
&& null === $this->sourceTransaction->foreign_amount
|
||||
&& (int) $this->sourceTransaction->transaction_currency_id !== (int) $this->sourceCurrency->id
|
||||
) {
|
||||
$message = sprintf(
|
||||
'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.',
|
||||
$this->sourceTransaction->id,
|
||||
$this->sourceTransaction->transaction_currency_id,
|
||||
$this->sourceAccount->id,
|
||||
$this->sourceTransaction->amount
|
||||
);
|
||||
Log::debug($message);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$this->sourceTransaction->transaction_currency_id = (int) $this->sourceCurrency->id;
|
||||
$this->sourceTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,6 +327,176 @@ class TransferCurrenciesCorrections extends Command
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return TransactionCurrency|null
|
||||
*/
|
||||
private function getCurrency(Account $account): ?TransactionCurrency
|
||||
{
|
||||
$accountId = $account->id;
|
||||
if (isset($this->accountCurrencies[$accountId]) && 0 === $this->accountCurrencies[$accountId]) {
|
||||
return null; // @codeCoverageIgnore
|
||||
}
|
||||
if (isset($this->accountCurrencies[$accountId]) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) {
|
||||
return $this->accountCurrencies[$accountId]; // @codeCoverageIgnore
|
||||
}
|
||||
$currency = $this->accountRepos->getAccountCurrency($account);
|
||||
if (null === $currency) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->accountCurrencies[$accountId] = 0;
|
||||
|
||||
return null;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
$this->accountCurrencies[$accountId] = $currency;
|
||||
|
||||
return $currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract destination transaction, destination account + destination account currency from the journal.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function getDestinationInformation(TransactionJournal $journal): void
|
||||
{
|
||||
$this->destinationTransaction = $this->getDestinationTransaction($journal);
|
||||
$this->destinationAccount = null === $this->destinationTransaction ? null : $this->destinationTransaction->account;
|
||||
$this->destinationCurrency = null === $this->destinationAccount ? null : $this->getCurrency($this->destinationAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $transfer
|
||||
*
|
||||
* @return Transaction|null
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function getDestinationTransaction(TransactionJournal $transfer): ?Transaction
|
||||
{
|
||||
return $transfer->transactions()->where('amount', '>', 0)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract source transaction, source account + source account currency from the journal.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function getSourceInformation(TransactionJournal $journal): void
|
||||
{
|
||||
$this->sourceTransaction = $this->getSourceTransaction($journal);
|
||||
$this->sourceAccount = null === $this->sourceTransaction ? null : $this->sourceTransaction->account;
|
||||
$this->sourceCurrency = null === $this->sourceAccount ? null : $this->getCurrency($this->sourceAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $transfer
|
||||
*
|
||||
* @return Transaction|null
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function getSourceTransaction(TransactionJournal $transfer): ?Transaction
|
||||
{
|
||||
return $transfer->transactions()->where('amount', '<', 0)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is either the source or destination transaction NULL?
|
||||
*
|
||||
* @return bool
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function isEmptyTransactions(): bool
|
||||
{
|
||||
return null === $this->sourceTransaction || null === $this->destinationTransaction
|
||||
|| null === $this->sourceAccount
|
||||
|| null === $this->destinationAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool) $configVar->data;
|
||||
}
|
||||
|
||||
return false; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function isNoCurrencyPresent(): bool
|
||||
{
|
||||
// source account must have a currency preference.
|
||||
if (null === $this->sourceCurrency) {
|
||||
$message = sprintf('Account #%d ("%s") must have currency preference but has none.', $this->sourceAccount->id, $this->sourceAccount->name);
|
||||
Log::error($message);
|
||||
$this->error($message);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// destination account must have a currency preference.
|
||||
if (null === $this->destinationCurrency) {
|
||||
$message = sprintf(
|
||||
'Account #%d ("%s") must have currency preference but has none.',
|
||||
$this->destinationAccount->id,
|
||||
$this->destinationAccount->name
|
||||
);
|
||||
Log::error($message);
|
||||
$this->error($message);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a split transaction journal?
|
||||
*
|
||||
* @param TransactionJournal $transfer
|
||||
*
|
||||
* @return bool
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function isSplitJournal(TransactionJournal $transfer): bool
|
||||
{
|
||||
return $transfer->transactions->count() > 2;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the class fields for the current transfer.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function resetInformation(): void
|
||||
{
|
||||
$this->sourceTransaction = null;
|
||||
$this->sourceAccount = null;
|
||||
$this->sourceCurrency = null;
|
||||
$this->destinationTransaction = null;
|
||||
$this->destinationAccount = null;
|
||||
$this->destinationCurrency = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine verifies that transfers have the correct currency settings for the accounts they are linked to.
|
||||
* For transfers, this is can be a destructive routine since we FORCE them into a currency setting whether they
|
||||
@@ -253,43 +517,21 @@ class TransferCurrenciesCorrections extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the class fields for the current transfer.
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function resetInformation(): void
|
||||
{
|
||||
$this->sourceTransaction = null;
|
||||
$this->sourceAccount = null;
|
||||
$this->sourceCurrency = null;
|
||||
$this->destinationTransaction = null;
|
||||
$this->destinationAccount = null;
|
||||
$this->destinationCurrency = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract source transaction, source account + source account currency from the journal.
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function getSourceInformation(TransactionJournal $journal): void
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->sourceTransaction = $this->getSourceTransaction($journal);
|
||||
$this->sourceAccount = null === $this->sourceTransaction ? null : $this->sourceTransaction->account;
|
||||
$this->sourceCurrency = null === $this->sourceAccount ? null : $this->getCurrency($this->sourceAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract destination transaction, destination account + destination account currency from the journal.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function getDestinationInformation(TransactionJournal $journal): void
|
||||
{
|
||||
$this->destinationTransaction = $this->getDestinationTransaction($journal);
|
||||
$this->destinationAccount = null === $this->destinationTransaction ? null : $this->destinationTransaction->account;
|
||||
$this->destinationCurrency = null === $this->destinationAccount ? null : $this->getCurrency($this->destinationAccount);
|
||||
$this->count = 0;
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->journalRepos = app(JournalRepositoryInterface::class);
|
||||
$this->cliRepos = app(JournalCLIRepositoryInterface::class);
|
||||
$this->accountCurrencies = [];
|
||||
$this->resetInformation();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,7 +539,6 @@ class TransferCurrenciesCorrections extends Command
|
||||
*/
|
||||
private function updateTransferCurrency(TransactionJournal $transfer): void
|
||||
{
|
||||
|
||||
$this->resetInformation();
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
@@ -325,7 +566,8 @@ class TransferCurrenciesCorrections extends Command
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->isNoCurrencyPresent()) {
|
||||
$this->error(
|
||||
sprintf('Source or destination accounts for transaction journal #%d have no currency information. Cannot fix this one.', $transfer->id));
|
||||
sprintf('Source or destination accounts for transaction journal #%d have no currency information. Cannot fix this one.', $transfer->id)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -355,225 +597,5 @@ class TransferCurrenciesCorrections extends Command
|
||||
|
||||
// fix journal itself:
|
||||
$this->fixTransactionJournalCurrency($transfer);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The source transaction must have a currency. If not, it will be added by
|
||||
* taking it from the source account's preference.
|
||||
*/
|
||||
private function fixSourceNoCurrency(): void
|
||||
{
|
||||
if (null === $this->sourceTransaction->transaction_currency_id && null !== $this->sourceCurrency) {
|
||||
$this->sourceTransaction
|
||||
->transaction_currency_id = (int)$this->sourceCurrency->id;
|
||||
$message = sprintf('Transaction #%d has no currency setting, now set to %s.',
|
||||
$this->sourceTransaction->id, $this->sourceCurrency->code);
|
||||
Log::debug($message);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$this->sourceTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The destination transaction must have a currency. If not, it will be added by
|
||||
* taking it from the destination account's preference.
|
||||
*/
|
||||
private function fixDestNoCurrency(): void
|
||||
{
|
||||
if (null === $this->destinationTransaction->transaction_currency_id && null !== $this->destinationCurrency) {
|
||||
$this->destinationTransaction
|
||||
->transaction_currency_id = (int)$this->destinationCurrency->id;
|
||||
$message = sprintf('Transaction #%d has no currency setting, now set to %s.',
|
||||
$this->destinationTransaction->id, $this->destinationCurrency->code);
|
||||
Log::debug($message);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$this->destinationTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The source transaction must have the correct currency. If not, it will be set by
|
||||
* taking it from the source account's preference.
|
||||
*/
|
||||
private function fixSourceUnmatchedCurrency(): void
|
||||
{
|
||||
if (null !== $this->sourceCurrency &&
|
||||
null === $this->sourceTransaction->foreign_amount &&
|
||||
(int)$this->sourceTransaction->transaction_currency_id !== (int)$this->sourceCurrency->id
|
||||
) {
|
||||
$message = sprintf(
|
||||
'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.',
|
||||
$this->sourceTransaction->id,
|
||||
$this->sourceTransaction->transaction_currency_id,
|
||||
$this->sourceAccount->id,
|
||||
$this->sourceTransaction->amount
|
||||
);
|
||||
Log::debug($message);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$this->sourceTransaction->transaction_currency_id = (int)$this->sourceCurrency->id;
|
||||
$this->sourceTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The destination transaction must have the correct currency. If not, it will be set by
|
||||
* taking it from the destination account's preference.
|
||||
*/
|
||||
private function fixDestinationUnmatchedCurrency(): void
|
||||
{
|
||||
if (null !== $this->destinationCurrency &&
|
||||
null === $this->destinationTransaction->foreign_amount &&
|
||||
(int)$this->destinationTransaction->transaction_currency_id !== (int)$this->destinationCurrency->id
|
||||
) {
|
||||
$message = sprintf(
|
||||
'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.',
|
||||
$this->destinationTransaction->id,
|
||||
$this->destinationTransaction->transaction_currency_id,
|
||||
$this->destinationAccount->id,
|
||||
$this->destinationTransaction->amount
|
||||
);
|
||||
Log::debug($message);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$this->destinationTransaction->transaction_currency_id = (int)$this->destinationCurrency->id;
|
||||
$this->destinationTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a split transaction journal?
|
||||
*
|
||||
* @param TransactionJournal $transfer
|
||||
* @return bool
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function isSplitJournal(TransactionJournal $transfer): bool
|
||||
{
|
||||
return $transfer->transactions->count() > 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is either the source or destination transaction NULL?
|
||||
* @return bool
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function isEmptyTransactions(): bool
|
||||
{
|
||||
return null === $this->sourceTransaction || null === $this->destinationTransaction ||
|
||||
null === $this->sourceAccount || null === $this->destinationAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the destination account currency is the same as the source currency,
|
||||
* both foreign_amount and foreign_currency_id fields must be NULL
|
||||
* for both transactions (because foreign currency info would not make sense)
|
||||
*
|
||||
*/
|
||||
private function fixInvalidForeignCurrency(): void
|
||||
{
|
||||
if ((int)$this->destinationCurrency->id === (int)$this->sourceCurrency->id) {
|
||||
// update both transactions to match:
|
||||
$this->sourceTransaction->foreign_amount = null;
|
||||
$this->sourceTransaction->foreign_currency_id = null;
|
||||
|
||||
$this->destinationTransaction->foreign_amount = null;
|
||||
$this->destinationTransaction->foreign_currency_id = null;
|
||||
|
||||
$this->sourceTransaction->save();
|
||||
$this->destinationTransaction->save();
|
||||
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Currency for account "%s" is %s, and currency for account "%s" is also
|
||||
%s, so transactions #%d and #%d has been verified to be to %s exclusively.',
|
||||
$this->destinationAccount->name, $this->destinationCurrency->code,
|
||||
$this->sourceAccount->name, $this->sourceCurrency->code,
|
||||
$this->sourceTransaction->id, $this->destinationTransaction->id, $this->sourceCurrency->code
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If destination account currency is different from source account currency,
|
||||
* then both transactions must get the source account's currency as normal currency
|
||||
* and the opposing account's currency as foreign currency.
|
||||
*/
|
||||
private function fixMismatchedForeignCurrency(): void
|
||||
{
|
||||
if ((int)$this->sourceCurrency->id !== (int)$this->destinationCurrency->id) {
|
||||
$this->sourceTransaction->transaction_currency_id = $this->sourceCurrency->id;
|
||||
$this->sourceTransaction->foreign_currency_id = $this->destinationCurrency->id;
|
||||
$this->destinationTransaction->transaction_currency_id = $this->sourceCurrency->id;
|
||||
$this->destinationTransaction->foreign_currency_id = $this->destinationCurrency->id;
|
||||
|
||||
$this->sourceTransaction->save();
|
||||
$this->destinationTransaction->save();
|
||||
$this->count++;
|
||||
Log::debug(sprintf('Verified foreign currency ID of transaction #%d and #%d', $this->sourceTransaction->id, $this->destinationTransaction->id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the foreign amount of the source transaction is null, but that of the other isn't, use this piece of code
|
||||
* to restore it.
|
||||
*/
|
||||
private function fixSourceNullForeignAmount(): void
|
||||
{
|
||||
if (null === $this->sourceTransaction->foreign_amount && null !== $this->destinationTransaction->foreign_amount) {
|
||||
$this->sourceTransaction->foreign_amount = bcmul((string)$this->destinationTransaction->foreign_amount, '-1');
|
||||
$this->sourceTransaction->save();
|
||||
$this->count++;
|
||||
Log::debug(sprintf('Restored foreign amount of source transaction #%d to %s',
|
||||
$this->sourceTransaction->id, $this->sourceTransaction->foreign_amount));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the foreign amount of the destination transaction is null, but that of the other isn't, use this piece of code
|
||||
* to restore it.
|
||||
*/
|
||||
private function fixDestNullForeignAmount(): void
|
||||
{
|
||||
if (null === $this->destinationTransaction->foreign_amount && null !== $this->sourceTransaction->foreign_amount) {
|
||||
$this->destinationTransaction->foreign_amount = bcmul((string)$this->sourceTransaction->foreign_amount, '-1');
|
||||
$this->destinationTransaction->save();
|
||||
$this->count++;
|
||||
Log::debug(sprintf('Restored foreign amount of destination transaction #%d to %s',
|
||||
$this->destinationTransaction->id, $this->destinationTransaction->foreign_amount));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function isNoCurrencyPresent(): bool
|
||||
{
|
||||
// source account must have a currency preference.
|
||||
if (null === $this->sourceCurrency) {
|
||||
$message = sprintf('Account #%d ("%s") must have currency preference but has none.', $this->sourceAccount->id, $this->sourceAccount->name);
|
||||
Log::error($message);
|
||||
$this->error($message);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// destination account must have a currency preference.
|
||||
if (null === $this->destinationCurrency) {
|
||||
$message = sprintf('Account #%d ("%s") must have currency preference but has none.',
|
||||
$this->destinationAccount->id, $this->destinationAccount->name);
|
||||
Log::error($message);
|
||||
$this->error($message);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class UpgradeDatabase
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class UpgradeDatabase extends Command
|
||||
@@ -75,7 +76,7 @@ class UpgradeDatabase extends Command
|
||||
'firefly-iii:migrate-recurrence-meta',
|
||||
'firefly-iii:migrate-tag-locations',
|
||||
|
||||
// there are 16 verify commands.
|
||||
// there are 15 verify commands.
|
||||
'firefly-iii:fix-piggies',
|
||||
'firefly-iii:create-link-types',
|
||||
'firefly-iii:create-access-tokens',
|
||||
@@ -92,6 +93,7 @@ class UpgradeDatabase extends Command
|
||||
'firefly-iii:fix-ob-currencies',
|
||||
'firefly-iii:fix-long-descriptions',
|
||||
'firefly-iii:fix-recurring-transactions',
|
||||
'firefly-iii:unify-group-accounts',
|
||||
|
||||
// two report commands
|
||||
'firefly-iii:report-empty-objects',
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user