mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 18:54:58 +00:00 
			
		
		
		
	Compare commits
	
		
			674 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 5f299b895b | ||
|  | 4e1bb5fbac | ||
|  | 47ccc513ad | ||
|  | cce1a01936 | ||
|  | 6f2b1a6a76 | ||
|  | 8526907f50 | ||
|  | bc192a8e54 | ||
|  | 9ff6f8fc52 | ||
|  | 6573bd6b4b | ||
|  | 9dc3f614af | ||
|  | 3888b8cceb | ||
|  | 294df4a2b3 | ||
|  | 265dd37212 | ||
|  | eb7c79ad27 | ||
|  | de111c7100 | ||
|  | e892c9a824 | ||
|  | 5eb0e18cae | ||
|  | 27cabb398e | ||
|  | 64dbb14241 | ||
|  | bb4e2be9eb | ||
|  | 7d1de0da17 | ||
|  | bf16c9a42b | ||
|  | 1a7b1ce499 | ||
|  | efc9bc71a7 | ||
|  | fc5b315af0 | ||
|  | 7a4a78628d | ||
|  | d16fb30a62 | ||
|  | 2d177e660e | ||
|  | 2f131dc170 | ||
|  | 94810e371a | ||
|  | 59731878f6 | ||
|  | 54ede8aa18 | ||
|  | b415b6b043 | ||
|  | 70c922cdc5 | ||
|  | 068fc32cb2 | ||
|  | 3dcdacc3b8 | ||
|  | a6594358d8 | ||
|  | f98921da46 | ||
|  | 25747fbcf2 | ||
|  | aac5c2b13c | ||
|  | cc810a5b6f | ||
|  | 1b3592d959 | ||
|  | d75614e9a7 | ||
|  | 08703e282f | ||
|  | 2904baf44e | ||
|  | f99e46bf75 | ||
|  | 9f87890ead | ||
|  | 638184cf66 | ||
|  | 03babfe75c | ||
|  | 238ed3c788 | ||
|  | 6a9d931ba3 | ||
|  | a3d2a9e00b | ||
|  | 39b88e8207 | ||
|  | 449c6dfde5 | ||
|  | 7cc47ca0b1 | ||
|  | 95f4a83f41 | ||
|  | 35154dc7a3 | ||
|  | 0fd0d7d080 | ||
|  | 658265c938 | ||
|  | 38fe9e7e1c | ||
|  | 77056dcf8d | ||
|  | 026683a8e1 | ||
|  | 6ab6dd6ac3 | ||
|  | 83de3482ce | ||
|  | 919a35aed3 | ||
|  | ad3defb071 | ||
|  | 9c929ecd1b | ||
|  | f79c9f7cf1 | ||
|  | 8e75c345d9 | ||
|  | 44886d9aad | ||
|  | c2d444347d | ||
|  | 5cb497596d | ||
|  | 1857469d2f | ||
|  | ea71b4843d | ||
|  | 97727e2e3d | ||
|  | f81e7da8bb | ||
|  | 8e827bf83b | ||
|  | 9e1fa284ca | ||
|  | 3bf800be6e | ||
|  | 635b9f9dba | ||
|  | 52a0d7cf7b | ||
|  | a34516932b | ||
|  | 929a2a30a2 | ||
|  | ffa88eeb08 | ||
|  | 51b45b4ed4 | ||
|  | f263844793 | ||
|  | 18c46df9aa | ||
|  | 15846e157b | ||
|  | bc59f2db0d | ||
|  | cd2be8c1a4 | ||
|  | f958115c50 | ||
|  | e7d677bfb6 | ||
|  | 3e80ffc52b | ||
|  | d0c7a5c076 | ||
|  | f3f4e6b354 | ||
|  | 5a45b25614 | ||
|  | 0b5ee1edfc | ||
|  | da3dc599f9 | ||
|  | f013b435ab | ||
|  | 5f6975a113 | ||
|  | c5dee29e4b | ||
|  | 633ee02f13 | ||
|  | 6b750c909a | ||
|  | 5f8b6640a9 | ||
|  | dd42d8437c | ||
|  | 67a178591d | ||
|  | f5e5659c1f | ||
|  | 8b0f0fb615 | ||
|  | 209116e766 | ||
|  | 79392ab656 | ||
|  | 3ca1207231 | ||
|  | cec1b147f2 | ||
|  | 46cfcfa3e7 | ||
|  | b833e8dfa2 | ||
|  | 77b843efd8 | ||
|  | db72ad7c60 | ||
|  | eadc630fcb | ||
|  | 170c1793cc | ||
|  | 9f7c6c2d0c | ||
|  | 72d054c55c | ||
|  | 524edfe7c2 | ||
|  | c25c5623d2 | ||
|  | 4f38b77ef6 | ||
|  | 5862803434 | ||
|  | 5b3beded39 | ||
|  | c61fb7a598 | ||
|  | 33d9148029 | ||
|  | 63969f5a33 | ||
|  | edde18aeef | ||
|  | 657116d361 | ||
|  | e16269daa8 | ||
|  | c07591ff5c | ||
|  | 75a478ad54 | ||
|  | 8dae8b1a7f | ||
|  | 15fd8cf486 | ||
|  | 55333156ac | ||
|  | 8cdcba3231 | ||
|  | 8bab9e84e2 | ||
|  | 2faae83912 | ||
|  | 5a61a11a61 | ||
|  | a6d71988f2 | ||
|  | 7069e242ae | ||
|  | 56ee830558 | ||
|  | 6dd12729e6 | ||
|  | 14a48303cb | ||
|  | 72cf6c9c0f | ||
|  | 144ee6b8ca | ||
|  | 8967d86da6 | ||
|  | 18c6edbb5d | ||
|  | 53de3c4717 | ||
|  | ad577e4e81 | ||
|  | 44811a3e7c | ||
|  | 1ab3f05b3a | ||
|  | 5e76488ae7 | ||
|  | 32771fe7e1 | ||
|  | 9b40cc6881 | ||
|  | 2e35260bbb | ||
|  | a067704277 | ||
|  | de281818ac | ||
|  | c49bfad38d | ||
|  | c1ba591b26 | ||
|  | 719af38a61 | ||
|  | ac61dfae6b | ||
|  | 813fb679a7 | ||
|  | e7562781f7 | ||
|  | 56d36b7f53 | ||
|  | 53b3f7f821 | ||
|  | 08a53156bd | ||
|  | 8985cd6309 | ||
|  | 3833da7410 | ||
|  | 4210cd10db | ||
|  | a7bd1c6892 | ||
|  | 52b0111afa | ||
|  | 7921d128e4 | ||
|  | d7e838701a | ||
|  | 289bcb22aa | ||
|  | 3fe57b7983 | ||
|  | 32e92c2a16 | ||
|  | 1b3d208540 | ||
|  | 6a8bf0aa62 | ||
|  | 56715556ed | ||
|  | 838330b909 | ||
|  | 69553b138b | ||
|  | 36d7a02994 | ||
|  | 301528e2d2 | ||
|  | 0303b45707 | ||
|  | ba722e8ed5 | ||
|  | 289e5a5442 | ||
|  | fdad96e2bc | ||
|  | af994e4dae | ||
|  | 006d68e279 | ||
|  | 29dc122ad3 | ||
|  | cf4a8c6204 | ||
|  | 3c73fe92bf | ||
|  | 6637590797 | ||
|  | b8bab11acd | ||
|  | a2f600feac | ||
|  | 80dd62ef0a | ||
|  | 827b1c9cd8 | ||
|  | 2e4fcf803d | ||
|  | d00d95fc6f | ||
|  | 3e3ab9bd25 | ||
|  | 6eecc7722d | ||
|  | ada4aaf69a | ||
|  | 93244c1f78 | ||
|  | be056cea6b | ||
|  | 659ca8be14 | ||
|  | ea9af8366d | ||
|  | 80edd47d36 | ||
|  | d7746b3649 | ||
|  | c4c4fbc34c | ||
|  | 59f57c96e9 | ||
|  | a2f852fecf | ||
|  | ad114ed329 | ||
|  | c4c3d0f07f | ||
|  | 6cf8102de5 | ||
|  | e7e4aa2218 | ||
|  | 6d84f4b6c1 | ||
|  | ce3e9ffd11 | ||
|  | 3ada260e0e | ||
|  | 8fdd0cb795 | ||
|  | 913e05a2e6 | ||
|  | fa1f703ef6 | ||
|  | 4004c53e1b | ||
|  | 4838670649 | ||
|  | a985e09282 | ||
|  | 9bd1503cb4 | ||
|  | a2ccbf7844 | ||
|  | 61bbe8a905 | ||
|  | 59bc5d22d1 | ||
|  | 1423d5b314 | ||
|  | 152d0eb1d0 | ||
|  | 6426d1df06 | ||
|  | 9284eb3fe9 | ||
|  | afdae8bc1e | ||
|  | 2a7085e593 | ||
|  | 2408fb3ed4 | ||
|  | 8316afb176 | ||
|  | e59fd098a3 | ||
|  | e044199693 | ||
|  | 8f8e29fc22 | ||
|  | 8de5384158 | ||
|  | 216c659335 | ||
|  | 041ca8a5d3 | ||
|  | fe4f1b306d | ||
|  | a0972d99fb | ||
|  | e332bfef7c | ||
|  | cba5e226d8 | ||
|  | 5aff0c4943 | ||
|  | cb49c00f4d | ||
|  | e26d797d57 | ||
|  | 938581527e | ||
|  | c38ae09735 | ||
|  | 28c3cfe084 | ||
|  | 4a2823bcba | ||
|  | 18eba02026 | ||
|  | d4690ce580 | ||
|  | a785c450b1 | ||
|  | 7480dc4a19 | ||
|  | ad01891a67 | ||
|  | 67fe35d564 | ||
|  | 7f19b6957a | ||
|  | 0a54caf202 | ||
|  | 4b4c1c7f8f | ||
|  | d071f3947e | ||
|  | b3d99cd210 | ||
|  | 90e696f82c | ||
|  | 958fcd1cfa | ||
|  | 8f57c7dcb3 | ||
|  | 77262f52a4 | ||
|  | 16bfbc8a12 | ||
|  | 1fd375b875 | ||
|  | 46131ad39d | ||
|  | 0b5c5b2ae9 | ||
|  | 55be174037 | ||
|  | a17b7025f1 | ||
|  | 170cf7fd77 | ||
|  | 23cdb4d326 | ||
|  | cbbe529572 | ||
|  | 0b382426e9 | ||
|  | 1cbbf9baa4 | ||
|  | 8d41ff7b79 | ||
|  | e3b6057bf8 | ||
|  | 66a4042cad | ||
|  | 56c08d8302 | ||
|  | d4e759754d | ||
|  | a96e171cbf | ||
|  | bd4a8c8397 | ||
|  | 04f71b3b43 | ||
|  | d124de51db | ||
|  | d87d12a0f5 | ||
|  | f2b08346d0 | ||
|  | d3682a6727 | ||
|  | 371bbd9508 | ||
|  | a8a28f442f | ||
|  | 65ddd8a736 | ||
|  | 8bb27de233 | ||
|  | 37e2f097ba | ||
|  | 1966d87ce6 | ||
|  | 7b8c86e1e3 | ||
|  | de634da513 | ||
|  | 96836e2d6c | ||
|  | 8a9d576f61 | ||
|  | 791d12fbb4 | ||
|  | d1329be2fa | ||
|  | 3ed6561702 | ||
|  | 7a0587f433 | ||
|  | 0fe682bfe6 | ||
|  | 0f685e8789 | ||
|  | 420771c233 | ||
|  | 5e3e9271ca | ||
|  | 1e603c0833 | ||
|  | 03e1673e92 | ||
|  | 9f992f003d | ||
|  | f50244a41f | ||
|  | 8b9607f9b5 | ||
|  | af107ad5e8 | ||
|  | 8926d62165 | ||
|  | 45344ee347 | ||
|  | baf9ebab15 | ||
|  | b7dab817f2 | ||
|  | fb2fa54480 | ||
|  | 2c966a1234 | ||
|  | 143ea69c0d | ||
|  | 8a02ead013 | ||
|  | 930d5ab941 | ||
|  | e54a56d3a8 | ||
|  | f5216c0d85 | ||
|  | ebc77540b9 | ||
|  | 28d2583c10 | ||
|  | b0988a7b00 | ||
|  | 2c4920db2d | ||
|  | 8321663815 | ||
|  | ac0280d460 | ||
|  | ba6f4268f0 | ||
|  | 0bd18f94ac | ||
|  | 66fb63661f | ||
|  | d9b16beb0a | ||
|  | 6a01a9bdfd | ||
|  | b81b34a706 | ||
|  | 3750a00b5f | ||
|  | 05ddcc169d | ||
|  | f571a5f1bd | ||
|  | 48b786adea | ||
|  | d691fa9b4d | ||
|  | da50f9e419 | ||
|  | 103de5e18a | ||
|  | dac6efd98b | ||
|  | 46aa7f81b2 | ||
|  | 0683c7cd67 | ||
|  | 50d7aa7b6a | ||
|  | b781215d0a | ||
|  | 866bc2f3bd | ||
|  | 49d4705014 | ||
|  | 9163fcfccb | ||
|  | 7e10641461 | ||
|  | cdc0e3cfd8 | ||
|  | 466e81d56a | ||
|  | 5953f691d1 | ||
|  | e2790ca6c1 | ||
|  | 66dbd48b76 | ||
|  | 73cfbbd2ba | ||
|  | 38bc38bf26 | ||
|  | e12d13c838 | ||
|  | fcc3af6136 | ||
|  | 491298e1cb | ||
|  | 72b5895217 | ||
|  | 0a8f4017bd | ||
|  | cb985f5897 | ||
|  | 968ec0853f | ||
|  | 0bde72d3df | ||
|  | 45293fbd42 | ||
|  | 72997065f0 | ||
|  | a838dc163d | ||
|  | 3d15a4ca6d | ||
|  | 51c7d4fb1b | ||
|  | fa586dba7e | ||
|  | 8ad40389f2 | ||
|  | b3333cc2d3 | ||
|  | 3699a7ba9a | ||
|  | 204e521ba4 | ||
|  | c9bab3e5c3 | ||
|  | 3d00e20238 | ||
|  | c3958ed3c4 | ||
|  | e5b88be5fa | ||
|  | 425552988a | ||
|  | a81dd8abe5 | ||
|  | bac8154a5b | ||
|  | 737d15fa0e | ||
|  | 5f2317af7f | ||
|  | 2bd1f783e5 | ||
|  | d6c0c9f963 | ||
|  | 21b6ad7a41 | ||
|  | a65d609fdc | ||
|  | 04e676b936 | ||
|  | be8eaaffdf | ||
|  | 28c753523f | ||
|  | 7fbd0b2ffc | ||
|  | 7ffb48a87a | ||
|  | d485270e1f | ||
|  | 5fd688b266 | ||
|  | 3716668e0c | ||
|  | ae7fd18c34 | ||
|  | 4f59b1d32f | ||
|  | 90cb3279df | ||
|  | cf0c7ef6b2 | ||
|  | 47c23781d9 | ||
|  | e258c050f7 | ||
|  | 57801b2f34 | ||
|  | 710e9c9423 | ||
|  | deefef83bd | ||
|  | 51e30aed66 | ||
|  | 8d109a3cfe | ||
|  | 3424e019b5 | ||
|  | c6b4bceb67 | ||
|  | afb4155015 | ||
|  | 8d99baf38a | ||
|  | b91cb60328 | ||
|  | c0d62237fc | ||
|  | 223ea80860 | ||
|  | 5a77bef494 | ||
|  | 80c0efe821 | ||
|  | 8044d89557 | ||
|  | 4f0ed97410 | ||
|  | af7952f204 | ||
|  | d8dcae856b | ||
|  | 7296796ed9 | ||
|  | a2c2bb4948 | ||
|  | 72ebfdc20e | ||
|  | 16b95ea78a | ||
|  | c04f08dfd8 | ||
|  | a30793e818 | ||
|  | e39e1eaf21 | ||
|  | ab22d2cbaa | ||
|  | 96ddbe7227 | ||
|  | 4d09235aef | ||
|  | 136b8975e3 | ||
|  | e21b1eca17 | ||
|  | 244b90b1d4 | ||
|  | b318f3f940 | ||
|  | e211c9812e | ||
|  | eef28d96f4 | ||
|  | c8227e09ee | ||
|  | 3e05fd91d9 | ||
|  | 450baba56a | ||
|  | 17a8c4918c | ||
|  | 0e2419d61a | ||
|  | 79b1a2ca6d | ||
|  | 2213c68155 | ||
|  | 2492b1fa96 | ||
|  | 6c6598dac5 | ||
|  | a137112e66 | ||
|  | 8642ae8180 | ||
|  | 894c4dc5a7 | ||
|  | d96063ea6e | ||
|  | c3dc193f3e | ||
|  | 3c2952009e | ||
|  | f4ade470df | ||
|  | 2e33b43389 | ||
|  | 92799699bc | ||
|  | 7ab0508167 | ||
|  | 3c65c28936 | ||
|  | 43892da07e | ||
|  | 7c436920a4 | ||
|  | 89d565e63b | ||
|  | 150b6fe5b6 | ||
|  | 0e77574c26 | ||
|  | df23863443 | ||
|  | 581bf11b21 | ||
|  | d602d4b429 | ||
|  | d1d4a52934 | ||
|  | 375d113769 | ||
|  | 9b83974bff | ||
|  | 3c68c99bd5 | ||
|  | ec4b37c596 | ||
|  | ba9601d21c | ||
|  | 50c13fd469 | ||
|  | 7af072b8fc | ||
|  | faa128d41e | ||
|  | 868fe46932 | ||
|  | e9e4307ce5 | ||
|  | 774d4844a9 | ||
|  | 586c53e670 | ||
|  | 68e073fbff | ||
|  | 8101dc37b1 | ||
|  | 63f16c458d | ||
|  | 821e007e95 | ||
|  | 1656a2f11a | ||
|  | 4dbc135dce | ||
|  | fc886f6bc1 | ||
|  | f93e480466 | ||
|  | fe807e23f8 | ||
|  | ecf61c31f1 | ||
|  | 4feff18af5 | ||
|  | a07c52e0d8 | ||
|  | 7bb07d7f55 | ||
|  | f12dfc8a14 | ||
|  | be030f15c4 | ||
|  | f5fb6c063b | ||
|  | fb722f06b9 | ||
|  | c0ea19e15e | ||
|  | cdeb1ad87c | ||
|  | 0dbe4e94fa | ||
|  | b5e2e8aa1d | ||
|  | 9502010248 | ||
|  | fea0557b47 | ||
|  | ed4fcc9011 | ||
|  | ed12ea7cfb | ||
|  | 73e526645e | ||
|  | 72aeafb2b5 | ||
|  | cc1af60cb4 | ||
|  | 359fab315f | ||
|  | 6a9574bab9 | ||
|  | 83d6158483 | ||
|  | 63ef89b6cc | ||
|  | b0beab4cd3 | ||
|  | a34782575f | ||
|  | 142bdc9430 | ||
|  | 14b79cb0a4 | ||
|  | ce5beeaf2c | ||
|  | 31114a2ca5 | ||
|  | 32528094ad | ||
|  | 0a2a01c44c | ||
|  | c1888dc3ac | ||
|  | 4d76afbe01 | ||
|  | 76d7a97f93 | ||
|  | 8b1366b20a | ||
|  | e0f9685578 | ||
|  | 5235657954 | ||
|  | a15fbc8094 | ||
|  | 546f1d9c50 | ||
|  | 74231f552a | ||
|  | b250a10e3c | ||
|  | a9f1b31dd6 | ||
|  | 7fe393acaf | ||
|  | 04faba4db5 | ||
|  | 91bba40c20 | ||
|  | 79e39f7de8 | ||
|  | 9c09353559 | ||
|  | 50752a5bfe | ||
|  | d59879db7d | ||
|  | aab125da27 | ||
|  | 74fc731f96 | ||
|  | bd0050fec2 | ||
|  | aa5e313b92 | ||
|  | e89d613b7e | ||
|  | 8757929ead | ||
|  | e0a9b19802 | ||
|  | 308da6dc6e | ||
|  | b6960fb0e5 | ||
|  | 137208c3fd | ||
|  | d7a9a62a1d | ||
|  | 075315bdaa | ||
|  | 3948fcd614 | ||
|  | 8e61e129ab | ||
|  | 20cffd0502 | ||
|  | 5df09dab09 | ||
|  | 7446b911e5 | ||
|  | f15267c1ab | ||
|  | 9c9fc2b5dc | ||
|  | 28f601b54b | ||
|  | 18b8a05014 | ||
|  | 910c995ed8 | ||
|  | 498468aa2c | ||
|  | 637aebcb34 | ||
|  | 9afd5cb277 | ||
|  | bc525e7272 | ||
|  | a80180780d | ||
|  | 0d73086c37 | ||
|  | 02ae39238d | ||
|  | 43d6b51d42 | ||
|  | 84566310de | ||
|  | 6a6ec9fbe4 | ||
|  | 84a7f825d7 | ||
|  | 0372c1aaf1 | ||
|  | c9fff197f7 | ||
|  | 6900392e43 | ||
|  | c00bcd78cc | ||
|  | b2b82124e6 | ||
|  | 3de57c668f | ||
|  | 43669648ce | ||
|  | 3b73b416d5 | ||
|  | 5153591c8f | ||
|  | 3172bc90da | ||
|  | 76a1b2cd51 | ||
|  | bdf7eee72f | ||
|  | 2d4b148b2c | ||
|  | d67db74ca2 | ||
|  | 516725456f | ||
|  | 001d72a484 | ||
|  | c555e28988 | ||
|  | af13d1943f | ||
|  | 52df2edc8f | ||
|  | cd08484a13 | ||
|  | f38d38f139 | ||
|  | 93b6c68938 | ||
|  | a4cc25175a | ||
|  | 3fb14b4708 | ||
|  | 6bdb6db330 | ||
|  | d05c165ace | ||
|  | ab53cdb896 | ||
|  | c1f142af78 | ||
|  | 6e261abb73 | ||
|  | 39af9e4414 | ||
|  | 5b50abb2c7 | ||
|  | 13bda0a264 | ||
|  | 1658c666ab | ||
|  | 170aebfe54 | ||
|  | c4ef379d0e | ||
|  | 18b038d8ff | ||
|  | 12ee5da872 | ||
|  | 601f9f86bb | ||
|  | d7329a5915 | ||
|  | 9e7b730002 | ||
|  | 2d59d845bc | ||
|  | c2645894e0 | ||
|  | 3751106317 | ||
|  | 60bb639351 | ||
|  | 74c50930bd | ||
|  | 9105104303 | ||
|  | 540dde135e | ||
|  | f8936210cf | ||
|  | 1dc6d8de40 | ||
|  | 1069db3c13 | ||
|  | 65122f0144 | ||
|  | d2c018f7da | ||
|  | 46493c2af6 | ||
|  | c303e03f76 | ||
|  | d8b65f62e7 | ||
|  | 854368a8f3 | ||
|  | 7653a34aea | ||
|  | ee50b58e00 | ||
|  | 1eb60ab100 | ||
|  | 4a20eef351 | ||
|  | 26c9b2c353 | ||
|  | 16374bce9b | ||
|  | 86011d4ea2 | ||
|  | fcb8b02da9 | ||
|  | 571165c2bb | ||
|  | c842113610 | ||
|  | 974a8b3b70 | ||
|  | 2e20c99ada | ||
|  | aa88ff6f2c | ||
|  | 5e6aa63d03 | ||
|  | ad6700c114 | ||
|  | f08c6efb00 | ||
|  | cc807ec132 | ||
|  | 24e7c68243 | ||
|  | ab25edd37a | ||
|  | be47fde6c2 | ||
|  | 1ffa8c5e72 | ||
|  | d855ccb8a7 | ||
|  | d88919474b | ||
|  | e139664301 | ||
|  | 5adf5f6e3f | ||
|  | aef2075c8e | ||
|  | 922b2962a3 | ||
|  | b4a401700e | ||
|  | e9601bb9c1 | ||
|  | 58af3dc6ea | ||
|  | 074295df61 | ||
|  | ec349b31c7 | ||
|  | 5ae236e016 | ||
|  | d7c5897aba | ||
|  | 5252e7efe7 | ||
|  | fc7d65629a | ||
|  | f28fdf8252 | ||
|  | 5d07c4a949 | ||
|  | fdd9eaab4b | ||
|  | e0d863a46f | ||
|  | 3aacb6f5f3 | ||
|  | 428e331b3e | ||
|  | 847e05e9a7 | ||
|  | 087eb5dbe6 | 
| @@ -1,3 +1 @@ | ||||
| src_dir: . | ||||
| coverage_clover: build/logs/clover.xml | ||||
| json_path: build/logs/coveralls-upload.json | ||||
|   | ||||
							
								
								
									
										13
									
								
								.env.example
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								.env.example
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| APP_ENV=production | ||||
| APP_DEBUG=false | ||||
| APP_KEY=SomeRandomString | ||||
| APP_KEY=SomeRandomStringOf32CharsExactly | ||||
|  | ||||
|  | ||||
| DB_CONNECTION=mysql | ||||
| DB_HOST=localhost | ||||
| @@ -11,11 +12,19 @@ DB_PASSWORD=secret | ||||
| CACHE_DRIVER=file | ||||
| SESSION_DRIVER=file | ||||
|  | ||||
| DEFAULT_CURRENCY=EUR | ||||
| DEFAULT_LANGUAGE=en_US | ||||
|  | ||||
| EMAIL_SMTP= | ||||
| EMAIL_DRIVER=smtp | ||||
| EMAIL_USERNAME= | ||||
| EMAIL_PASSWORD= | ||||
| ANALYTICS_ID= | ||||
| EMAIL_PRETEND=false | ||||
|  | ||||
| SHOW_INCOMPLETE_TRANSLATIONS=false | ||||
|  | ||||
| ANALYTICS_ID= | ||||
| RUNCLEANUP=true | ||||
| SITE_OWNER=mail@example.com | ||||
|  | ||||
| BLOCKED_DOMAINS= | ||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -5,7 +5,7 @@ Thumbs.db | ||||
| .idea/ | ||||
| tests/_output/* | ||||
| _ide_helper.php | ||||
| /build/logs/clover.xml | ||||
| /build/logs | ||||
| index.html* | ||||
| app/storage/firefly-export* | ||||
| .vagrant | ||||
| @@ -31,3 +31,6 @@ addNewLines.php | ||||
| .phpstorm.meta.php | ||||
| .env.backup | ||||
| .env.local | ||||
|  | ||||
| tests/_output/* | ||||
| tests/_output/* | ||||
|   | ||||
| @@ -1,5 +1,3 @@ | ||||
| # .scrutinizer.yml | ||||
| tools: | ||||
|   external_code_coverage: | ||||
|     timeout: 1800    # Timeout in seconds. | ||||
|     runs: 2 | ||||
|   external_code_coverage: false | ||||
|   | ||||
							
								
								
									
										10
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -10,13 +10,9 @@ install: | ||||
|   - composer update | ||||
|   - php artisan env | ||||
|   - mv -v .env.testing .env | ||||
|   - touch storage/database/testing.db | ||||
|   - php artisan migrate --env=testing | ||||
|   - php artisan migrate --seed --env=testing | ||||
|  | ||||
| script: | ||||
|   - phpunit | ||||
|  | ||||
| after_script: | ||||
|   - php vendor/bin/coveralls | ||||
|   - CODECLIMATE_REPO_TOKEN=26489f9e854fcdf7e7660ba29c1455694685465b1f90329a79f7d2bf448acb61 ./vendor/bin/test-reporter --stdout > codeclimate.json | ||||
|   - "curl -X POST -d @codeclimate.json -H 'Content-Type: application/json' -H 'User-Agent: Code Climate (PHP Test Reporter v0.1.1)' https://codeclimate.com/test_reports" | ||||
|   - wget https://scrutinizer-ci.com/ocular.phar | ||||
|   - php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml | ||||
|   | ||||
							
								
								
									
										43
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,7 +1,9 @@ | ||||
| # Firefly III (v3.4.6) | ||||
| # Firefly III | ||||
|  | ||||
| [](https://packagist.org/packages/grumpydictator/firefly-iii) | ||||
| [](https://packagist.org/packages/grumpydictator/firefly-iii) | ||||
|  | ||||
| [](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master) | ||||
| [](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master) | ||||
| [](https://scrutinizer-ci.com/g/JC5/firefly-iii/build-status/master) | ||||
|  | ||||
| ## About | ||||
| @@ -9,15 +11,12 @@ | ||||
| "Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared  | ||||
| household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money. | ||||
|   | ||||
| _Firefly is a system you'll have install yourself on webhosting of your choosing._ | ||||
|  | ||||
| Personal financial management is pretty difficult, and everybody has their own approach to it. Some people | ||||
| make budgets, other people limit their cashflow by throwing away their credit cards, others try to increase | ||||
| their current cashflow. There are tons of ways to save and earn money. | ||||
|  | ||||
| Firefly works on the principle that if you know where you're money is going, you can stop it from going there. | ||||
|  | ||||
|   | ||||
| To get to know Firefly, and to see if it fits you, check out these resources: | ||||
|  | ||||
| - The screenshots below on this very page. | ||||
| @@ -25,6 +24,11 @@ To get to know Firefly, and to see if it fits you, check out these resources: | ||||
| - The [full description](https://github.com/JC5/firefly-iii/wiki/full-description), which will tell you how Firefly works, | ||||
| and the philosophy behind it. | ||||
|  | ||||
| #### A quick technical overview | ||||
|  | ||||
| Firefly is a system you'll have install yourself on webhosting of your choosing. It needs PHP and MySQL. The current version of Firefly III requires PHP 5.6.4 or | ||||
| higher. Soon, this will be PHP 7.0.0 or higher. | ||||
|  | ||||
|  | ||||
| #### About the name (should you care) | ||||
|  | ||||
| @@ -59,19 +63,19 @@ Everything is organised: | ||||
|  | ||||
| _Please note that everything in these screenshots is fictional and may not be realistic._ | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ## Running and installing | ||||
|  | ||||
| @@ -80,7 +84,19 @@ If you're still interested please read [the installation guide](https://github.c | ||||
| and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**. | ||||
|   | ||||
| If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/).  | ||||
| This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site. | ||||
| This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site. Accounts on the demo sites will stop working after one month. It's a trial. | ||||
|  | ||||
| ## Security | ||||
|  | ||||
| You should always run Firefly III on a site with TLS enabled (https://). Please note that although some parts of the | ||||
| database are encrypted (transaction descriptions, names, etc.) some parts are _not_ (amounts, dates, etc). If you need | ||||
| more security, you must enable transparent database encryption or a comparable technology. Please remember that this | ||||
| is open source software under active development, and it is in no way guaranteed to be safe or secure. | ||||
|  | ||||
| ## Translations | ||||
|  | ||||
| Firefly III is currently available in Dutch and English. Support for other languages is being worked on. I could use | ||||
| your help. Checkout [Crowdin](https://crowdin.com/project/firefly-iii) for more information. | ||||
|  | ||||
| ## Credits | ||||
|  | ||||
| @@ -88,6 +104,7 @@ Firefly III uses the following libraries and tools: | ||||
|  | ||||
| * The AdminLTE template by [Almsaseed Studio](https://almsaeedstudio.com/) | ||||
| * The [Google charts](https://developers.google.com/chart/) library. | ||||
| * [Chart.js](http://www.chartjs.org/) | ||||
| * [Bootstrap](http://getbootstrap.com/) | ||||
| * [Laravel](http://laravel.com/) | ||||
| * [Twig](http://twig.sensiolabs.org/) | ||||
|   | ||||
| @@ -1,25 +0,0 @@ | ||||
| <?php namespace FireflyIII\Events; | ||||
|  | ||||
| use Illuminate\Queue\SerializesModels; | ||||
|  | ||||
| /** | ||||
|  * Class JournalDeleted | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class JournalDeleted extends Event | ||||
| { | ||||
|  | ||||
|     use SerializesModels; | ||||
|  | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -14,15 +14,6 @@ use Illuminate\Support\Collection; | ||||
| interface AccountChartGenerator | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function all(Collection $accounts, Carbon $start, Carbon $end); | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
| @@ -40,4 +31,13 @@ interface AccountChartGenerator | ||||
|      * @return array | ||||
|      */ | ||||
|     public function single(Account $account, Carbon $start, Carbon $end); | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end); | ||||
| } | ||||
|   | ||||
| @@ -3,10 +3,8 @@ | ||||
| namespace FireflyIII\Generator\Chart\Account; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Config; | ||||
| use FireflyIII\Models\Account; | ||||
| use Illuminate\Support\Collection; | ||||
| use Preferences; | ||||
| use Steam; | ||||
|  | ||||
| /** | ||||
| @@ -17,20 +15,69 @@ use Steam; | ||||
| class ChartJsAccountChartGenerator implements AccountChartGenerator | ||||
| { | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function all(Collection $accounts, Carbon $start, Carbon $end) | ||||
|     public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         return $this->frontpage($accounts, $start, $end); | ||||
|         $data = [ | ||||
|             'count'  => 1, | ||||
|             'labels' => [], 'datasets' => [[ | ||||
|                                                'label' => trans('firefly.spent'), | ||||
|                                                'data'  => []]]]; | ||||
|  | ||||
|         bcscale(2); | ||||
|         $start->subDay(); | ||||
|         $ids           = $this->getIdsFromCollection($accounts); | ||||
|         $startBalances = Steam::balancesById($ids, $start); | ||||
|         $endBalances   = Steam::balancesById($ids, $end); | ||||
|  | ||||
|         $accounts->each( | ||||
|             function (Account $account) use ($startBalances, $endBalances) { | ||||
|                 $id                  = $account->id; | ||||
|                 $startBalance        = $this->isInArray($startBalances, $id); | ||||
|                 $endBalance          = $this->isInArray($endBalances, $id); | ||||
|                 $diff                = bcsub($endBalance, $startBalance); | ||||
|                 $account->difference = round($diff, 2); | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         $accounts = $accounts->sortByDesc( | ||||
|             function (Account $account) { | ||||
|                 return $account->difference; | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         foreach ($accounts as $account) { | ||||
|             if ($account->difference > 0) { | ||||
|                 $data['labels'][]              = $account->name; | ||||
|                 $data['datasets'][0]['data'][] = $account->difference; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $array | ||||
|      * @param $entryId | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function isInArray($array, $entryId) | ||||
|     { | ||||
|         if (isset($array[$entryId])) { | ||||
|             return $array[$entryId]; | ||||
|         } | ||||
|  | ||||
|         return '0'; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
| @@ -41,21 +88,21 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator | ||||
|     public function frontpage(Collection $accounts, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         // language: | ||||
|         $language = Preferences::get('language', 'en')->data; | ||||
|         $format   = Config::get('firefly.monthAndDay.' . $language); | ||||
|         $data     = [ | ||||
|         $format  = trans('config.month_and_day'); | ||||
|         $data    = [ | ||||
|             'count'    => 0, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [], | ||||
|         ]; | ||||
|         $current  = clone $start; | ||||
|         $current = clone $start; | ||||
|         while ($current <= $end) { | ||||
|             $data['labels'][] = $current->formatLocalized($format); | ||||
|             $current->addDay(); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         foreach ($accounts as $account) { | ||||
|             $set     = [ | ||||
|             $set      = [ | ||||
|                 'label'                => $account->name, | ||||
|                 'fillColor'            => 'rgba(220,220,220,0.2)', | ||||
|                 'strokeColor'          => 'rgba(220,220,220,1)', | ||||
| @@ -65,9 +112,15 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator | ||||
|                 'pointHighlightStroke' => 'rgba(220,220,220,1)', | ||||
|                 'data'                 => [], | ||||
|             ]; | ||||
|             $current = clone $start; | ||||
|             $current  = clone $start; | ||||
|             $range    = Steam::balanceInRange($account, $start, clone $end); | ||||
|             $previous = array_values($range)[0]; | ||||
|             while ($current <= $end) { | ||||
|                 $set['data'][] = Steam::balance($account, $current); | ||||
|                 $format  = $current->format('Y-m-d'); | ||||
|                 $balance = isset($range[$format]) ? $range[$format] : $previous; | ||||
|  | ||||
|                 $set['data'][] = $balance; | ||||
|                 $previous      = $balance; | ||||
|                 $current->addDay(); | ||||
|             } | ||||
|             $data['datasets'][] = $set; | ||||
| @@ -87,10 +140,9 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator | ||||
|     public function single(Account $account, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         // language: | ||||
|         $language = Preferences::get('language', 'en')->data; | ||||
|         $format   = Config::get('firefly.monthAndDay.' . $language); | ||||
|         $format = trans('config.month_and_day'); | ||||
|  | ||||
|         $data = [ | ||||
|         $data     = [ | ||||
|             'count'    => 1, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [ | ||||
| @@ -100,15 +152,36 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator | ||||
|                 ] | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         $current = clone $start; | ||||
|         $range    = Steam::balanceInRange($account, $start, $end); | ||||
|         $current  = clone $start; | ||||
|         $previous = array_values($range)[0]; | ||||
|  | ||||
|         while ($end >= $current) { | ||||
|             $theDate = $current->format('Y-m-d'); | ||||
|             $balance = isset($range[$theDate]) ? $range[$theDate] : $previous; | ||||
|  | ||||
|             $data['labels'][]              = $current->formatLocalized($format); | ||||
|             $data['datasets'][0]['data'][] = Steam::balance($account, $current); | ||||
|             $data['datasets'][0]['data'][] = $balance; | ||||
|             $previous                      = $balance; | ||||
|             $current->addDay(); | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $collection | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function getIdsFromCollection(Collection $collection) | ||||
|     { | ||||
|         $ids = []; | ||||
|         foreach ($collection as $entry) { | ||||
|             $ids[] = $entry->id; | ||||
|         } | ||||
|  | ||||
|         return array_unique($ids); | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,122 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Account; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Models\Account; | ||||
| use Grumpydictator\Gchart\GChart; | ||||
| use Illuminate\Support\Collection; | ||||
| use Steam; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class GoogleAccountChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Account | ||||
|  */ | ||||
| class GoogleAccountChartGenerator implements AccountChartGenerator | ||||
| { | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function all(Collection $accounts, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         // make chart (original code): | ||||
|         $chart = new GChart; | ||||
|         $chart->addColumn(trans('firefly.dayOfMonth'), 'date'); | ||||
|         $index = 1; | ||||
|         /** @var Account $account */ | ||||
|         foreach ($accounts as $account) { | ||||
|             $chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number'); | ||||
|             $chart->addCertainty($index); | ||||
|             $index++; | ||||
|         } | ||||
|         $current = clone $start; | ||||
|         $current->subDay(); | ||||
|         $today = Carbon::now(); | ||||
|         while ($end >= $current) { | ||||
|             $row     = [clone $current]; | ||||
|             $certain = $current < $today; | ||||
|             foreach ($accounts as $account) { | ||||
|                 $row[] = Steam::balance($account, $current); | ||||
|                 $row[] = $certain; | ||||
|             } | ||||
|             $chart->addRowArray($row); | ||||
|             $current->addDay(); | ||||
|         } | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(Collection $accounts, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $chart = new GChart; | ||||
|         $chart->addColumn(trans('firefly.dayOfMonth'), 'date'); | ||||
|  | ||||
|         $index = 1; | ||||
|         /** @var Account $account */ | ||||
|         foreach ($accounts as $account) { | ||||
|             $chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number'); | ||||
|             $chart->addCertainty($index); | ||||
|             $index++; | ||||
|         } | ||||
|         $current = clone $start; | ||||
|         $current->subDay(); | ||||
|         $today = Carbon::now(); | ||||
|         while ($end >= $current) { | ||||
|             $row     = [clone $current]; | ||||
|             $certain = $current < $today; | ||||
|             foreach ($accounts as $account) { | ||||
|                 $row[] = Steam::balance($account, $current); | ||||
|                 $row[] = $certain; | ||||
|             } | ||||
|             $chart->addRowArray($row); | ||||
|             $current->addDay(); | ||||
|         } | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function single(Account $account, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $current = clone $start; | ||||
|         $today   = new Carbon; | ||||
|         $chart   = new GChart; | ||||
|         $chart->addColumn(trans('firefly.dayOfMonth'), 'date'); | ||||
|         $chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number'); | ||||
|         $chart->addCertainty(1); | ||||
|  | ||||
|         while ($end >= $current) { | ||||
|             $certain = $current < $today; | ||||
|             $chart->addRow(clone $current, Steam::balance($account, $current), $certain); | ||||
|             $current->addDay(); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|     } | ||||
| } | ||||
| @@ -14,12 +14,12 @@ interface BillChartGenerator | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $paid | ||||
|      * @param Collection $unpaid | ||||
|      * @param string $paid | ||||
|      * @param string $unpaid | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(Collection $paid, Collection $unpaid); | ||||
|     public function frontpage($paid, $unpaid); | ||||
|  | ||||
|     /** | ||||
|      * @param Bill       $bill | ||||
|   | ||||
| @@ -1,18 +1,10 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Created by PhpStorm. | ||||
|  * User: sander | ||||
|  * Date: 27/06/15 | ||||
|  * Time: 17:21 | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Bill; | ||||
|  | ||||
| use Config; | ||||
| use FireflyIII\Models\Bill; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Support\Collection; | ||||
| use Preferences; | ||||
|  | ||||
| /** | ||||
|  * Class ChartJsBillChartGenerator | ||||
| @@ -23,47 +15,23 @@ class ChartJsBillChartGenerator implements BillChartGenerator | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $paid | ||||
|      * @param Collection $unpaid | ||||
|      * @param string $paid | ||||
|      * @param string $unpaid | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(Collection $paid, Collection $unpaid) | ||||
|     public function frontpage($paid, $unpaid) | ||||
|     { | ||||
|  | ||||
|         // loop paid and create single entry: | ||||
|         $paidDescriptions   = []; | ||||
|         $paidAmount         = 0; | ||||
|         $unpaidDescriptions = []; | ||||
|         $unpaidAmount       = 0; | ||||
|  | ||||
|  | ||||
|         /** @var TransactionJournal $entry */ | ||||
|         foreach ($paid as $entry) { | ||||
|  | ||||
|             $paidDescriptions[] = $entry->description; | ||||
|             $paidAmount += floatval($entry->amount); | ||||
|         } | ||||
|  | ||||
|         // loop unpaid: | ||||
|         /** @var Bill $entry */ | ||||
|         foreach ($unpaid as $entry) { | ||||
|             $description          = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')'; | ||||
|             $amount               = ($entry[0]->amount_max + $entry[0]->amount_min) / 2; | ||||
|             $unpaidDescriptions[] = $description; | ||||
|             $unpaidAmount += $amount; | ||||
|             unset($amount, $description); | ||||
|         } | ||||
|  | ||||
|         bcscale(2); | ||||
|         $data = [ | ||||
|             [ | ||||
|                 'value'     => $unpaidAmount, | ||||
|                 'value'     => round($unpaid, 2), | ||||
|                 'color'     => 'rgba(53, 124, 165,0.7)', | ||||
|                 'highlight' => 'rgba(53, 124, 165,0.9)', | ||||
|                 'label'     => trans('firefly.unpaid'), | ||||
|             ], | ||||
|             [ | ||||
|                 'value'     => $paidAmount, | ||||
|                 'value'     => round($paid * -1, 2), // paid is negative, must be positive. | ||||
|                 'color'     => 'rgba(0, 141, 76, 0.7)', | ||||
|                 'highlight' => 'rgba(0, 141, 76, 0.9)', | ||||
|                 'label'     => trans('firefly.paid'), | ||||
| @@ -81,28 +49,24 @@ class ChartJsBillChartGenerator implements BillChartGenerator | ||||
|      */ | ||||
|     public function single(Bill $bill, Collection $entries) | ||||
|     { | ||||
|         // language: | ||||
|         $language = Preferences::get('language', 'en')->data; | ||||
|         $format   = Config::get('firefly.month.' . $language); | ||||
|  | ||||
|         $data = [ | ||||
|         $format       = trans('config.month'); | ||||
|         $data         = [ | ||||
|             'count'    => 3, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [], | ||||
|         ]; | ||||
|  | ||||
|         // dataset: max amount | ||||
|         // dataset: min amount | ||||
|         // dataset: actual amount | ||||
|  | ||||
|         $minAmount    = []; | ||||
|         $maxAmount    = []; | ||||
|         $actualAmount = []; | ||||
|         /** @var TransactionJournal $entry */ | ||||
|         foreach ($entries as $entry) { | ||||
|             $data['labels'][] = $entry->date->formatLocalized($format); | ||||
|             $minAmount[]      = round($bill->amount_min, 2); | ||||
|             $maxAmount[]      = round($bill->amount_max, 2); | ||||
|             $actualAmount[]   = round($entry->amount, 2); | ||||
|             /* | ||||
|              * journalAmount has been collected in BillRepository::getJournals | ||||
|              */ | ||||
|             $actualAmount[] = round(($entry->journalAmount * -1), 2); | ||||
|         } | ||||
|  | ||||
|         $data['datasets'][] = [ | ||||
|   | ||||
| @@ -1,87 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Bill; | ||||
|  | ||||
| use FireflyIII\Models\Bill; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Grumpydictator\Gchart\GChart; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class GoogleBillChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Bill | ||||
|  */ | ||||
| class GoogleBillChartGenerator implements BillChartGenerator | ||||
| { | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $paid | ||||
|      * @param Collection $unpaid | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(Collection $paid, Collection $unpaid) | ||||
|     { | ||||
|         // loop paid and create single entry: | ||||
|         $paidDescriptions   = []; | ||||
|         $paidAmount         = 0; | ||||
|         $unpaidDescriptions = []; | ||||
|         $unpaidAmount       = 0; | ||||
|  | ||||
|  | ||||
|         /** @var TransactionJournal $entry */ | ||||
|         foreach ($paid as $entry) { | ||||
|  | ||||
|             $paidDescriptions[] = $entry->description; | ||||
|             $paidAmount += floatval($entry->amount); | ||||
|         } | ||||
|  | ||||
|         // loop unpaid: | ||||
|         /** @var Bill $entry */ | ||||
|         foreach ($unpaid as $entry) { | ||||
|             $description          = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')'; | ||||
|             $amount               = ($entry[0]->amount_max + $entry[0]->amount_min) / 2; | ||||
|             $unpaidDescriptions[] = $description; | ||||
|             $unpaidAmount += $amount; | ||||
|             unset($amount, $description); | ||||
|         } | ||||
|  | ||||
|         $chart = new GChart; | ||||
|         $chart->addColumn(trans('firefly.name'), 'string'); | ||||
|         $chart->addColumn(trans('firefly.amount'), 'number'); | ||||
|  | ||||
|         $chart->addRow(trans('firefly.unpaid') . ': ' . join(', ', $unpaidDescriptions), $unpaidAmount); | ||||
|         $chart->addRow(trans('firefly.paid') . ': ' . join(', ', $paidDescriptions), $paidAmount); | ||||
|  | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Bill       $bill | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function single(Bill $bill, Collection $entries) | ||||
|     { | ||||
|         // make chart: | ||||
|         $chart = new GChart; | ||||
|         $chart->addColumn(trans('firefly.date'), 'date'); | ||||
|         $chart->addColumn(trans('firefly.maxAmount'), 'number'); | ||||
|         $chart->addColumn(trans('firefly.minAmount'), 'number'); | ||||
|         $chart->addColumn(trans('firefly.billEntry'), 'number'); | ||||
|  | ||||
|         /** @var TransactionJournal $result */ | ||||
|         foreach ($entries as $result) { | ||||
|             $chart->addRow(clone $result->date, floatval($bill->amount_max), floatval($bill->amount_min), floatval($result->amount)); | ||||
|         } | ||||
|  | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|     } | ||||
| } | ||||
| @@ -32,6 +32,13 @@ interface BudgetChartGenerator | ||||
|      */ | ||||
|     public function frontpage(Collection $entries); | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYear(Collection $entries); | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * @param Collection $entries | ||||
|   | ||||
| @@ -17,29 +17,35 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * @param string     $dateFormat | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function budget(Collection $entries) | ||||
|     public function budget(Collection $entries, $dateFormat = 'month') | ||||
|     { | ||||
|         // language: | ||||
|         $language = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US'))->data; | ||||
|         $format   = Config::get('firefly.' . $dateFormat . '.' . $language); | ||||
|  | ||||
|         $data = [ | ||||
|             'count'    => 1, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => 'Amount', | ||||
|                     'data'  => [], | ||||
|                 ] | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         /** @var array $entry */ | ||||
|         foreach ($entries as $entry) { | ||||
|             $data['labels'][]              = trans('firefly.spent'); | ||||
|             $data['labels'][]              = $entry[0]->formatLocalized($format); | ||||
|             $data['datasets'][0]['data'][] = $entry[1]; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         $data['count'] = count($data['datasets']); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
| @@ -52,7 +58,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator | ||||
|      */ | ||||
|     public function budgetLimit(Collection $entries) | ||||
|     { | ||||
|         return $this->budget($entries); | ||||
|         return $this->budget($entries, 'monthAndDay'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -62,24 +68,24 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator | ||||
|      */ | ||||
|     public function frontpage(Collection $entries) | ||||
|     { | ||||
|         $data = [ | ||||
|         $data      = [ | ||||
|             'count'    => 0, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [], | ||||
|         ]; | ||||
|         // dataset: left | ||||
|         // dataset: spent | ||||
|         // dataset: overspent | ||||
|         $left      = []; | ||||
|         $spent     = []; | ||||
|         $overspent = []; | ||||
|         foreach ($entries as $entry) { | ||||
|             if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) { | ||||
|                 $data['labels'][] = $entry[0]; | ||||
|                 $left[]           = round($entry[1], 2); | ||||
|                 $spent[]          = round($entry[2], 2); | ||||
|                 $overspent[]      = round($entry[3], 2); | ||||
|         $filtered  = $entries->filter( | ||||
|             function ($entry) { | ||||
|                 return ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0); | ||||
|             } | ||||
|         ); | ||||
|         foreach ($filtered as $entry) { | ||||
|             $data['labels'][] = $entry[0]; | ||||
|             $left[]           = round($entry[1], 2); | ||||
|             $spent[]          = round($entry[2] * -1, 2); // spent is coming in negative, must be positive | ||||
|             $overspent[]      = round($entry[3] * -1, 2); // same | ||||
|         } | ||||
|  | ||||
|         $data['datasets'][] = [ | ||||
| @@ -109,11 +115,9 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator | ||||
|     public function year(Collection $budgets, Collection $entries) | ||||
|     { | ||||
|         // language: | ||||
|         $language = Preferences::get('language', 'en')->data; | ||||
|         $format   = Config::get('firefly.month.' . $language); | ||||
|         $format = trans('config.month'); | ||||
|  | ||||
|         $data = [ | ||||
|             'count'    => 0, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [], | ||||
|         ]; | ||||
| @@ -136,4 +140,37 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYear(Collection $entries) | ||||
|     { | ||||
|         // dataset: | ||||
|         $data = [ | ||||
|             'count'    => 0, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [], | ||||
|         ]; | ||||
|         // get labels from one of the budgets (assuming there's at least one): | ||||
|         $first = $entries->first(); | ||||
|         foreach ($first['budgeted'] as $year => $noInterest) { | ||||
|             $data['labels'][] = strval($year); | ||||
|         } | ||||
|  | ||||
|         // then, loop all entries and create datasets: | ||||
|         foreach ($entries as $entry) { | ||||
|             $name               = $entry['name']; | ||||
|             $spent              = $entry['spent']; | ||||
|             $budgeted           = $entry['budgeted']; | ||||
|             $data['datasets'][] = ['label' => 'Spent on ' . $name, 'data' => array_values($spent)]; | ||||
|             $data['datasets'][] = ['label' => 'Budgeted for ' . $name, 'data' => array_values($budgeted)]; | ||||
|         } | ||||
|         $data['count'] = count($data['datasets']); | ||||
|  | ||||
|         return $data; | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,101 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Budget; | ||||
|  | ||||
| use Grumpydictator\Gchart\GChart; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class GoogleBudgetChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Budget | ||||
|  */ | ||||
| class GoogleBudgetChartGenerator implements BudgetChartGenerator | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function budget(Collection $entries) | ||||
|     { | ||||
|  | ||||
|         $chart = new GChart; | ||||
|         $chart->addColumn(trans('firefly.period'), 'date'); | ||||
|         $chart->addColumn(trans('firefly.spent'), 'number'); | ||||
|  | ||||
|         /** @var array $entry */ | ||||
|         foreach ($entries as $entry) { | ||||
|             $chart->addRow($entry[0], $entry[1]); | ||||
|         } | ||||
|  | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function budgetLimit(Collection $entries) | ||||
|     { | ||||
|         return $this->budget($entries); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(Collection $entries) | ||||
|     { | ||||
|         $chart = new GChart; | ||||
|         $chart->addColumn(trans('firefly.budget'), 'string'); | ||||
|         $chart->addColumn(trans('firefly.left'), 'number'); | ||||
|         $chart->addColumn(trans('firefly.spent'), 'number'); | ||||
|         $chart->addColumn(trans('firefly.overspent'), 'number'); | ||||
|  | ||||
|         /** @var array $entry */ | ||||
|         foreach ($entries as $entry) { | ||||
|             if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) { | ||||
|                 $chart->addRow($entry[0], $entry[1], $entry[2], $entry[3]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function year(Collection $budgets, Collection $entries) | ||||
|     { | ||||
|         $chart = new GChart; | ||||
|         // add columns: | ||||
|         $chart->addColumn(trans('firefly.month'), 'date'); | ||||
|         foreach ($budgets as $budget) { | ||||
|             $chart->addColumn($budget->name, 'number'); | ||||
|         } | ||||
|  | ||||
|         /** @var array $entry */ | ||||
|         foreach ($entries as $entry) { | ||||
|  | ||||
|             $chart->addRowArray($entry); | ||||
|         } | ||||
|  | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|     } | ||||
| } | ||||
| @@ -19,6 +19,14 @@ interface CategoryChartGenerator | ||||
|      */ | ||||
|     public function all(Collection $entries); | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function earnedInPeriod(Collection $categories, Collection $entries); | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
| @@ -31,8 +39,14 @@ interface CategoryChartGenerator | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function month(Collection $entries); | ||||
|     public function multiYear(Collection $entries); | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function period(Collection $entries); | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
| @@ -40,5 +54,5 @@ interface CategoryChartGenerator | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function year(Collection $categories, Collection $entries); | ||||
|     public function spentInPeriod(Collection $categories, Collection $entries); | ||||
| } | ||||
|   | ||||
| @@ -2,9 +2,7 @@ | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Category; | ||||
|  | ||||
| use Config; | ||||
| use Illuminate\Support\Collection; | ||||
| use Preferences; | ||||
|  | ||||
|  | ||||
| /** | ||||
| @@ -22,25 +20,68 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator | ||||
|      */ | ||||
|     public function all(Collection $entries) | ||||
|     { | ||||
|  | ||||
|  | ||||
|         $data = [ | ||||
|             'count'    => 1, | ||||
|             'count'    => 2, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => trans('firefly.spent'), | ||||
|                     'data'  => [] | ||||
|                 ], | ||||
|                 [ | ||||
|                     'label' => trans('firefly.earned'), | ||||
|                     'data'  => [] | ||||
|                 ] | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         foreach ($entries as $entry) { | ||||
|             $data['labels'][]              = trans('firefly.spent'); | ||||
|             $data['datasets'][0]['data'][] = round($entry[1], 2); | ||||
|             $data['labels'][] = $entry[1]; | ||||
|             $spent            = round($entry[2], 2); | ||||
|             $earned           = round($entry[3], 2); | ||||
|  | ||||
|             $data['datasets'][0]['data'][] = $spent == 0 ? null : $spent * -1; | ||||
|             $data['datasets'][1]['data'][] = $earned == 0 ? null : $earned; | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function earnedInPeriod(Collection $categories, Collection $entries) | ||||
|     { | ||||
|  | ||||
|         // language: | ||||
|         $format = trans('config.month'); | ||||
|  | ||||
|         $data = [ | ||||
|             'count'    => 0, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [], | ||||
|         ]; | ||||
|  | ||||
|         foreach ($categories as $category) { | ||||
|             $data['labels'][] = $category->name; | ||||
|         } | ||||
|  | ||||
|         foreach ($entries as $entry) { | ||||
|             $date = $entry[0]->formatLocalized($format); | ||||
|             array_shift($entry); | ||||
|             $data['count']++; | ||||
|             $data['datasets'][] = ['label' => $date, 'data' => $entry]; | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
| @@ -59,9 +100,9 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator | ||||
|             ], | ||||
|         ]; | ||||
|         foreach ($entries as $entry) { | ||||
|             if ($entry['sum'] != 0) { | ||||
|                 $data['labels'][]              = $entry['name']; | ||||
|                 $data['datasets'][0]['data'][] = round($entry['sum'], 2); | ||||
|             if ($entry->spent != 0) { | ||||
|                 $data['labels'][]              = $entry->name; | ||||
|                 $data['datasets'][0]['data'][] = round(($entry->spent * -1), 2); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -69,12 +110,50 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function month(Collection $entries) | ||||
|     public function multiYear(Collection $entries) | ||||
|     { | ||||
|         // dataset: | ||||
|         $data = [ | ||||
|             'count'    => 0, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [], | ||||
|         ]; | ||||
|         // get labels from one of the categories (assuming there's at least one): | ||||
|         $first = $entries->first(); | ||||
|         foreach ($first['spent'] as $year => $noInterest) { | ||||
|             $data['labels'][] = strval($year); | ||||
|         } | ||||
|  | ||||
|         // then, loop all entries and create datasets: | ||||
|         foreach ($entries as $entry) { | ||||
|             $name   = $entry['name']; | ||||
|             $spent  = $entry['spent']; | ||||
|             $earned = $entry['earned']; | ||||
|             if (array_sum(array_values($spent)) != 0) { | ||||
|                 $data['datasets'][] = ['label' => 'Spent in category ' . $name, 'data' => array_values($spent)]; | ||||
|             } | ||||
|             if (array_sum(array_values($earned)) != 0) { | ||||
|                 $data['datasets'][] = ['label' => 'Earned in category ' . $name, 'data' => array_values($earned)]; | ||||
|             } | ||||
|         } | ||||
|         $data['count'] = count($data['datasets']); | ||||
|  | ||||
|         return $data; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function period(Collection $entries) | ||||
|     { | ||||
|         return $this->all($entries); | ||||
|  | ||||
| @@ -86,12 +165,11 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function year(Collection $categories, Collection $entries) | ||||
|     public function spentInPeriod(Collection $categories, Collection $entries) | ||||
|     { | ||||
|  | ||||
|         // language: | ||||
|         $language = Preferences::get('language', 'en')->data; | ||||
|         $format   = Config::get('firefly.month.' . $language); | ||||
|         $format = trans('config.month'); | ||||
|  | ||||
|         $data = [ | ||||
|             'count'    => 0, | ||||
|   | ||||
| @@ -1,105 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Category; | ||||
|  | ||||
| use Grumpydictator\Gchart\GChart; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class GoogleCategoryChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Category | ||||
|  */ | ||||
| class GoogleCategoryChartGenerator implements CategoryChartGenerator | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function all(Collection $entries) | ||||
|     { | ||||
|         $chart = new GChart; | ||||
|         $chart->addColumn(trans('firefly.period'), 'date'); | ||||
|         $chart->addColumn(trans('firefly.spent'), 'number'); | ||||
|  | ||||
|         /** @var array $entry */ | ||||
|         foreach ($entries as $entry) { | ||||
|             $chart->addRow($entry[0], $entry[1]); | ||||
|         } | ||||
|  | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(Collection $entries) | ||||
|     { | ||||
|         $chart = new GChart; | ||||
|         $chart->addColumn(trans('firefly.category'), 'string'); | ||||
|         $chart->addColumn(trans('firefly.spent'), 'number'); | ||||
|  | ||||
|  | ||||
|         /** @var array $entry */ | ||||
|         foreach ($entries as $entry) { | ||||
|             $sum = $entry['sum']; | ||||
|             if ($sum != 0) { | ||||
|                 $chart->addRow($entry['name'], $sum); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function month(Collection $entries) | ||||
|     { | ||||
|         $chart = new GChart; | ||||
|  | ||||
|         $chart->addColumn(trans('firefly.period'), 'date'); | ||||
|         $chart->addColumn(trans('firefly.spent'), 'number'); | ||||
|  | ||||
|         /** @var array $entry */ | ||||
|         foreach ($entries as $entry) { | ||||
|             $chart->addRow($entry[0], $entry[1]); | ||||
|         } | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function year(Collection $categories, Collection $entries) | ||||
|     { | ||||
|         $chart = new GChart; | ||||
|  | ||||
|         $chart->addColumn(trans('firefly.month'), 'date'); | ||||
|         foreach ($categories as $category) { | ||||
|             $chart->addColumn($category->name, 'number'); | ||||
|         } | ||||
|         /** @var array $entry */ | ||||
|         foreach ($entries as $entry) { | ||||
|             $chart->addRowArray($entry); | ||||
|         } | ||||
|         $chart->generate(); | ||||
|         return $chart->getData(); | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -3,13 +3,11 @@ | ||||
| namespace FireflyIII\Generator\Chart\PiggyBank; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Config; | ||||
| use Illuminate\Support\Collection; | ||||
| use Preferences; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class GooglePiggyBankChartGenerator | ||||
|  * Class ChartJsPiggyBankChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\PiggyBank | ||||
|  */ | ||||
| @@ -25,8 +23,7 @@ class ChartJsPiggyBankChartGenerator implements PiggyBankChartGenerator | ||||
|     { | ||||
|  | ||||
|         // language: | ||||
|         $language = Preferences::get('language', 'en')->data; | ||||
|         $format   = Config::get('firefly.month.' . $language); | ||||
|         $format = trans('config.month_and_day'); | ||||
|  | ||||
|         $data = [ | ||||
|             'count'    => 1, | ||||
|   | ||||
| @@ -1,41 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\PiggyBank; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Grumpydictator\Gchart\GChart; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class GooglePiggyBankChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\PiggyBank | ||||
|  */ | ||||
| class GooglePiggyBankChartGenerator implements PiggyBankChartGenerator | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $set | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function history(Collection $set) | ||||
|     { | ||||
|         $chart = new GChart; | ||||
|         $chart->addColumn(trans('firefly.date'), 'date'); | ||||
|         $chart->addColumn(trans('firefly.balance'), 'number'); | ||||
|  | ||||
|         $sum = '0'; | ||||
|         bcscale(2); | ||||
|  | ||||
|         foreach ($set as $entry) { | ||||
|             $sum = bcadd($sum, $entry->sum); | ||||
|             $chart->addRow(new Carbon($entry->date), $sum); | ||||
|         } | ||||
|  | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|     } | ||||
| } | ||||
| @@ -2,19 +2,80 @@ | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Report; | ||||
|  | ||||
| use Config; | ||||
| use Grumpydictator\Gchart\GChart; | ||||
| use Illuminate\Support\Collection; | ||||
| use Preferences; | ||||
|  | ||||
| /** | ||||
|  * Class GoogleReportChartGenerator | ||||
|  * Class ChartJsReportChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Report | ||||
|  */ | ||||
| class ChartJsReportChartGenerator implements ReportChartGenerator | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Same as above but other translations. | ||||
|      * | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYearInOut(Collection $entries) | ||||
|     { | ||||
|         $data = [ | ||||
|             'count'    => 2, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => trans('firefly.income'), | ||||
|                     'data'  => [] | ||||
|                 ], | ||||
|                 [ | ||||
|                     'label' => trans('firefly.expenses'), | ||||
|                     'data'  => [] | ||||
|                 ] | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         foreach ($entries as $entry) { | ||||
|             $data['labels'][]              = $entry[0]->formatLocalized('%Y'); | ||||
|             $data['datasets'][0]['data'][] = round($entry[1], 2); | ||||
|             $data['datasets'][1]['data'][] = round($entry[2], 2); | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $income | ||||
|      * @param string $expense | ||||
|      * @param int    $count | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYearInOutSummarized($income, $expense, $count) | ||||
|     { | ||||
|         $data                          = [ | ||||
|             'count'    => 2, | ||||
|             'labels'   => [trans('firefly.sum_of_years'), trans('firefly.average_of_years')], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => trans('firefly.income'), | ||||
|                     'data'  => [] | ||||
|                 ], | ||||
|                 [ | ||||
|                     'label' => trans('firefly.expenses'), | ||||
|                     'data'  => [] | ||||
|                 ] | ||||
|             ], | ||||
|         ]; | ||||
|         $data['datasets'][0]['data'][] = round($income, 2); | ||||
|         $data['datasets'][1]['data'][] = round($expense, 2); | ||||
|         $data['datasets'][0]['data'][] = round(($income / $count), 2); | ||||
|         $data['datasets'][1]['data'][] = round(($expense / $count), 2); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
| @@ -23,8 +84,7 @@ class ChartJsReportChartGenerator implements ReportChartGenerator | ||||
|     public function yearInOut(Collection $entries) | ||||
|     { | ||||
|         // language: | ||||
|         $language = Preferences::get('language', 'en')->data; | ||||
|         $format   = Config::get('firefly.month.' . $language); | ||||
|         $format = trans('config.month'); | ||||
|  | ||||
|         $data = [ | ||||
|             'count'    => 2, | ||||
| @@ -62,7 +122,7 @@ class ChartJsReportChartGenerator implements ReportChartGenerator | ||||
|  | ||||
|         $data                          = [ | ||||
|             'count'    => 2, | ||||
|             'labels'   => [], | ||||
|             'labels'   => [trans('firefly.sum_of_year'), trans('firefly.average_of_year')], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => trans('firefly.income'), | ||||
| @@ -78,6 +138,7 @@ class ChartJsReportChartGenerator implements ReportChartGenerator | ||||
|         $data['datasets'][1]['data'][] = round($expense, 2); | ||||
|         $data['datasets'][0]['data'][] = round(($income / $count), 2); | ||||
|         $data['datasets'][1]['data'][] = round(($expense / $count), 2); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,58 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Report; | ||||
|  | ||||
| use Grumpydictator\Gchart\GChart; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class GoogleReportChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Report | ||||
|  */ | ||||
| class GoogleReportChartGenerator implements ReportChartGenerator | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function yearInOut(Collection $entries) | ||||
|     { | ||||
|         $chart = new GChart; | ||||
|         $chart->addColumn(trans('firefly.month'), 'date'); | ||||
|         $chart->addColumn(trans('firefly.income'), 'number'); | ||||
|         $chart->addColumn(trans('firefly.expenses'), 'number'); | ||||
|  | ||||
|         /** @var array $entry */ | ||||
|         foreach ($entries as $entry) { | ||||
|             $chart->addRowArray($entry); | ||||
|         } | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $income | ||||
|      * @param string $expense | ||||
|      * @param int    $count | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function yearInOutSummarized($income, $expense, $count) | ||||
|     { | ||||
|         $chart = new GChart; | ||||
|  | ||||
|         $chart->addColumn(trans('firefly.summary'), 'string'); | ||||
|         $chart->addColumn(trans('firefly.income'), 'number'); | ||||
|         $chart->addColumn(trans('firefly.expenses'), 'number'); | ||||
|         $chart->addRow(trans('firefly.sum'), $income, $expense); | ||||
|         $chart->addRow(trans('firefly.average'), ($income / $count), ($expense / $count)); | ||||
|  | ||||
|         $chart->generate(); | ||||
|  | ||||
|         return $chart->getData(); | ||||
|     } | ||||
| } | ||||
| @@ -12,6 +12,22 @@ use Illuminate\Support\Collection; | ||||
| interface ReportChartGenerator | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYearInOut(Collection $entries); | ||||
|  | ||||
|     /** | ||||
|      * @param string $income | ||||
|      * @param string $expense | ||||
|      * @param int    $count | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYearInOutSummarized($income, $expense, $count); | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|   | ||||
| @@ -28,8 +28,6 @@ class ConnectJournalToPiggyBank | ||||
|     /** | ||||
|      * Handle the event when journal is saved. | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.CyclomaticComplexity) | ||||
|      * | ||||
|      * @param  JournalCreated $event | ||||
|      * | ||||
|      * @return boolean | ||||
| @@ -53,7 +51,7 @@ class ConnectJournalToPiggyBank | ||||
|         } | ||||
|         bcscale(2); | ||||
|  | ||||
|         $amount = $journal->actual_amount; | ||||
|         $amount = $journal->amount_positive; | ||||
|         // if piggy account matches source account, the amount is positive | ||||
|         if ($piggyBank->account_id == $journal->source_account->id) { | ||||
|             $amount = $amount * -1; | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| <?php namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
| use App; | ||||
| use FireflyIII\Events\JournalSaved; | ||||
| use Log; | ||||
|  | ||||
| @@ -36,7 +35,7 @@ class RescanJournal | ||||
|         Log::debug('Triggered saved event for journal #' . $journal->id . ' (' . $journal->description . ')'); | ||||
|  | ||||
|         /** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */ | ||||
|         $repository = App::make('FireflyIII\Repositories\Bill\BillRepositoryInterface'); | ||||
|         $repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface'); | ||||
|         $list       = $journal->user->bills()->where('active', 1)->where('automatch', 1)->get(); | ||||
|  | ||||
|         Log::debug('Found ' . $list->count() . ' bills to check.'); | ||||
|   | ||||
| @@ -49,10 +49,12 @@ class UpdateJournalConnection | ||||
|         if (is_null($repetition)) { | ||||
|             return; | ||||
|         } | ||||
|         $amount = $journal->amount; | ||||
|         $diff   = $amount - $event->amount; // update current repetition | ||||
|         bcscale(2); | ||||
|  | ||||
|         $repetition->currentamount += $diff; | ||||
|         $amount = $journal->amount; | ||||
|         $diff   = bcsub($amount, $event->amount); // update current repetition | ||||
|  | ||||
|         $repetition->currentamount = bcadd($repetition->currentamount, $diff); | ||||
|         $repetition->save(); | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										224
									
								
								app/Helpers/Attachments/AttachmentHelper.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								app/Helpers/Attachments/AttachmentHelper.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,224 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Attachments; | ||||
|  | ||||
| use Auth; | ||||
| use Config; | ||||
| use Crypt; | ||||
| use FireflyIII\Models\Attachment; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Support\MessageBag; | ||||
| use Input; | ||||
| use Symfony\Component\HttpFoundation\File\UploadedFile; | ||||
|  | ||||
| /** | ||||
|  * Class AttachmentHelper | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Attachments | ||||
|  */ | ||||
| class AttachmentHelper implements AttachmentHelperInterface | ||||
| { | ||||
|  | ||||
|     /** @var int */ | ||||
|     protected $maxUploadSize; | ||||
|     /** @var array */ | ||||
|     protected $allowedMimes; | ||||
|     /** @var MessageBag */ | ||||
|     public $errors; | ||||
|     /** @var MessageBag */ | ||||
|     public $messages; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->maxUploadSize = Config::get('firefly.maxUploadSize'); | ||||
|         $this->allowedMimes  = Config::get('firefly.allowedMimes'); | ||||
|         $this->errors        = new MessageBag; | ||||
|         $this->messages      = new MessageBag; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Attachment $attachment | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getAttachmentLocation(Attachment $attachment) | ||||
|     { | ||||
|         $path = storage_path('upload') . DIRECTORY_SEPARATOR . 'at-' . $attachment->id . '.data'; | ||||
|  | ||||
|         return $path; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Model $model | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function saveAttachmentsForModel(Model $model) | ||||
|     { | ||||
|         $files = Input::file('attachments'); | ||||
|  | ||||
|         if (is_array($files)) { | ||||
|             foreach ($files as $entry) { | ||||
|                 if (!is_null($entry)) { | ||||
|                     $this->processFile($entry, $model); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             if (!is_null($files)) { | ||||
|                 $this->processFile($files, $model); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param UploadedFile $file | ||||
|      * @param Model        $model | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function hasFile(UploadedFile $file, Model $model) | ||||
|     { | ||||
|         $md5   = md5_file($file->getRealPath()); | ||||
|         $name  = $file->getClientOriginalName(); | ||||
|         $class = get_class($model); | ||||
|         $count = Auth::user()->attachments()->where('md5', $md5)->where('attachable_id', $model->id)->where('attachable_type', $class)->count(); | ||||
|  | ||||
|         if ($count > 0) { | ||||
|             $msg = trans('validation.file_already_attached', ['name' => $name]); | ||||
|             $this->errors->add('attachments', $msg); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param UploadedFile $file | ||||
|      * @param Model        $model | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function validateUpload(UploadedFile $file, Model $model) | ||||
|     { | ||||
|         if (!$this->validMime($file)) { | ||||
|             return false; | ||||
|         } | ||||
|         if (!$this->validSize($file)) { | ||||
|             return false; | ||||
|         } | ||||
|         if ($this->hasFile($file, $model)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param UploadedFile $file | ||||
|      * @param Model        $model | ||||
|      * | ||||
|      * @return bool|Attachment | ||||
|      */ | ||||
|     protected function processFile(UploadedFile $file, Model $model) | ||||
|     { | ||||
|         $validation = $this->validateUpload($file, $model); | ||||
|         if ($validation === false) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         $attachment = new Attachment; // create Attachment object. | ||||
|         $attachment->user()->associate(Auth::user()); | ||||
|         $attachment->attachable()->associate($model); | ||||
|         $attachment->md5      = md5_file($file->getRealPath()); | ||||
|         $attachment->filename = $file->getClientOriginalName(); | ||||
|         $attachment->mime     = $file->getMimeType(); | ||||
|         $attachment->size     = $file->getSize(); | ||||
|         $attachment->uploaded = 0; | ||||
|         $attachment->save(); | ||||
|  | ||||
|         $path      = $file->getRealPath(); // encrypt and move file to storage. | ||||
|         $content   = file_get_contents($path); | ||||
|         $encrypted = Crypt::encrypt($content); | ||||
|  | ||||
|         // store it: | ||||
|         $upload = $this->getAttachmentLocation($attachment); | ||||
|         if (is_writable(dirname($upload))) { | ||||
|             file_put_contents($upload, $encrypted); | ||||
|         } | ||||
|  | ||||
|         $attachment->uploaded = 1; // update attachment | ||||
|         $attachment->save(); | ||||
|  | ||||
|         $name = e($file->getClientOriginalName()); // add message: | ||||
|         $msg  = trans('validation.file_attached', ['name' => $name]); | ||||
|         $this->messages->add('attachments', $msg); | ||||
|  | ||||
|         // return it. | ||||
|         return $attachment; | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param UploadedFile $file | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function validMime(UploadedFile $file) | ||||
|     { | ||||
|         $mime = e($file->getMimeType()); | ||||
|         $name = e($file->getClientOriginalName()); | ||||
|  | ||||
|         if (!in_array($mime, $this->allowedMimes)) { | ||||
|             $msg = trans('validation.file_invalid_mime', ['name' => $name, 'mime' => $mime]); | ||||
|             $this->errors->add('attachments', $msg); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param UploadedFile $file | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function validSize(UploadedFile $file) | ||||
|     { | ||||
|         $size = $file->getSize(); | ||||
|         $name = e($file->getClientOriginalName()); | ||||
|         if ($size > $this->maxUploadSize) { | ||||
|             $msg = trans('validation.file_too_large', ['name' => $name]); | ||||
|             $this->errors->add('attachments', $msg); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return MessageBag | ||||
|      */ | ||||
|     public function getErrors() | ||||
|     { | ||||
|         return $this->errors; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return MessageBag | ||||
|      */ | ||||
|     public function getMessages() | ||||
|     { | ||||
|         return $this->messages; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										41
									
								
								app/Helpers/Attachments/AttachmentHelperInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								app/Helpers/Attachments/AttachmentHelperInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Attachments; | ||||
|  | ||||
| use FireflyIII\Models\Attachment; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Support\MessageBag; | ||||
|  | ||||
| /** | ||||
|  * Interface AttachmentHelperInterface | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Attachments | ||||
|  */ | ||||
| interface AttachmentHelperInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Model $model | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function saveAttachmentsForModel(Model $model); | ||||
|  | ||||
|     /** | ||||
|      * @return MessageBag | ||||
|      */ | ||||
|     public function getErrors(); | ||||
|  | ||||
|     /** | ||||
|      * @return MessageBag | ||||
|      */ | ||||
|     public function getMessages(); | ||||
|  | ||||
|     /** | ||||
|      * @param Attachment $attachment | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getAttachmentLocation(Attachment $attachment); | ||||
|  | ||||
| } | ||||
| @@ -3,7 +3,6 @@ | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use FireflyIII\Models\Budget as BudgetModel; | ||||
| use FireflyIII\Models\LimitRepetition; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
| @@ -26,9 +25,6 @@ class BalanceLine | ||||
|     /** @var BudgetModel */ | ||||
|     protected $budget; | ||||
|  | ||||
|     /** @var  LimitRepetition */ | ||||
|     protected $repetition; | ||||
|  | ||||
|     protected $role = self::ROLE_DEFAULTROLE; | ||||
|  | ||||
|     /** | ||||
| @@ -48,24 +44,19 @@ class BalanceLine | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getTitle() | ||||
|     public function getBalanceEntries() | ||||
|     { | ||||
|         if ($this->getBudget() instanceof BudgetModel) { | ||||
|             return $this->getBudget()->name; | ||||
|         } | ||||
|         if ($this->getRole() == self::ROLE_DEFAULTROLE) { | ||||
|             return trans('firefly.noBudget'); | ||||
|         } | ||||
|         if ($this->getRole() == self::ROLE_TAGROLE) { | ||||
|             return trans('firefly.coveredWithTags'); | ||||
|         } | ||||
|         if ($this->getRole() == self::ROLE_DIFFROLE) { | ||||
|             return trans('firefly.leftUnbalanced'); | ||||
|         } | ||||
|         return $this->balanceEntries; | ||||
|     } | ||||
|  | ||||
|         return ''; | ||||
|     /** | ||||
|      * @param Collection $balanceEntries | ||||
|      */ | ||||
|     public function setBalanceEntries($balanceEntries) | ||||
|     { | ||||
|         $this->balanceEntries = $balanceEntries; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -100,6 +91,27 @@ class BalanceLine | ||||
|         $this->role = $role; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getTitle() | ||||
|     { | ||||
|         if ($this->getBudget() instanceof BudgetModel) { | ||||
|             return $this->getBudget()->name; | ||||
|         } | ||||
|         if ($this->getRole() == self::ROLE_DEFAULTROLE) { | ||||
|             return trans('firefly.noBudget'); | ||||
|         } | ||||
|         if ($this->getRole() == self::ROLE_TAGROLE) { | ||||
|             return trans('firefly.coveredWithTags'); | ||||
|         } | ||||
|         if ($this->getRole() == self::ROLE_DIFFROLE) { | ||||
|             return trans('firefly.leftUnbalanced'); | ||||
|         } | ||||
|  | ||||
|         return ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If a BalanceLine has a budget/repetition, each BalanceEntry in this BalanceLine | ||||
|      * should have a "spent" value, which is the amount of money that has been spent | ||||
| @@ -110,7 +122,7 @@ class BalanceLine | ||||
|      */ | ||||
|     public function leftOfRepetition() | ||||
|     { | ||||
|         $start = $this->getRepetition() ? $this->getRepetition()->amount : 0; | ||||
|         $start = isset($this->budget->amount) ? $this->budget->amount : 0; | ||||
|         /** @var BalanceEntry $balanceEntry */ | ||||
|         foreach ($this->getBalanceEntries() as $balanceEntry) { | ||||
|             $start += $balanceEntry->getSpent(); | ||||
| @@ -118,56 +130,4 @@ class BalanceLine | ||||
|  | ||||
|         return $start; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return LimitRepetition | ||||
|      */ | ||||
|     public function getRepetition() | ||||
|     { | ||||
|         return $this->repetition; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param LimitRepetition $repetition | ||||
|      */ | ||||
|     public function setRepetition($repetition) | ||||
|     { | ||||
|         $this->repetition = $repetition; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getBalanceEntries() | ||||
|     { | ||||
|         return $this->balanceEntries; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $balanceEntries | ||||
|      */ | ||||
|     public function setBalanceEntries($balanceEntries) | ||||
|     { | ||||
|         $this->balanceEntries = $balanceEntries; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If the BalanceEntries for a BalanceLine have a "left" value, the amount | ||||
|      * of money left in the entire BalanceLine is returned here: | ||||
|      * | ||||
|      * @return float | ||||
|      */ | ||||
|     public function sumOfLeft() | ||||
|     { | ||||
|         $sum = '0'; | ||||
|         bcscale(2); | ||||
|         /** @var BalanceEntry $balanceEntry */ | ||||
|         foreach ($this->getBalanceEntries() as $balanceEntry) { | ||||
|             $sum = bcadd($sum, $balanceEntry->getLeft()); | ||||
|         } | ||||
|  | ||||
|         return $sum; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -34,8 +34,10 @@ class Category | ||||
|      */ | ||||
|     public function addCategory(CategoryModel $category) | ||||
|     { | ||||
|         if ($category->spent > 0) { | ||||
|         // spent is minus zero for an expense report: | ||||
|         if ($category->spent < 0) { | ||||
|             $this->categories->push($category); | ||||
|             $this->addTotal($category->spent); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -54,7 +56,7 @@ class Category | ||||
|      */ | ||||
|     public function getCategories() | ||||
|     { | ||||
|         $set = $this->categories->sortByDesc( | ||||
|         $set = $this->categories->sortBy( | ||||
|             function (CategoryModel $category) { | ||||
|                 return $category->spent; | ||||
|             } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use Crypt; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Support\Collection; | ||||
| use stdClass; | ||||
| @@ -33,19 +34,24 @@ class Expense | ||||
|      */ | ||||
|     public function addOrCreateExpense(TransactionJournal $entry) | ||||
|     { | ||||
|         bcscale(2); | ||||
|  | ||||
|         $accountId = $entry->account_id; | ||||
|         $amount    = strval(round($entry->journalAmount, 2)); | ||||
|         if (bccomp('0', $amount) === -1) { | ||||
|             $amount = bcmul($amount, '-1'); | ||||
|         } | ||||
|  | ||||
|         if (!$this->expenses->has($accountId)) { | ||||
|             $newObject         = new stdClass; | ||||
|             $newObject->amount = strval(round($entry->amount, 2)); | ||||
|             $newObject->name   = $entry->name; | ||||
|             $newObject->amount = $amount; | ||||
|             $newObject->name   = Crypt::decrypt($entry->account_name); | ||||
|             $newObject->count  = 1; | ||||
|             $newObject->id     = $accountId; | ||||
|             $this->expenses->put($accountId, $newObject); | ||||
|         } else { | ||||
|             bcscale(2); | ||||
|             $existing         = $this->expenses->get($accountId); | ||||
|             $existing->amount = bcadd($existing->amount, $entry->amount); | ||||
|             $existing->amount = bcadd($existing->amount, $amount); | ||||
|             $existing->count++; | ||||
|             $this->expenses->put($accountId, $existing); | ||||
|         } | ||||
| @@ -56,8 +62,18 @@ class Expense | ||||
|      */ | ||||
|     public function addToTotal($add) | ||||
|     { | ||||
|         $add = strval(round($add, 2)); | ||||
|         bcscale(2); | ||||
|  | ||||
|  | ||||
|         $add = strval(round($add, 2)); | ||||
|         if (bccomp('0', $add) === -1) { | ||||
|             $add = bcmul($add, '-1'); | ||||
|         } | ||||
|  | ||||
|         // if amount is positive, the original transaction | ||||
|         // was a transfer. But since this is an expense report, | ||||
|         // that amount must be negative. | ||||
|  | ||||
|         $this->total = bcadd($this->total, $add); | ||||
|     } | ||||
|  | ||||
| @@ -66,7 +82,7 @@ class Expense | ||||
|      */ | ||||
|     public function getExpenses() | ||||
|     { | ||||
|         $set = $this->expenses->sortByDesc( | ||||
|         $set = $this->expenses->sortBy( | ||||
|             function (stdClass $object) { | ||||
|                 return $object->amount; | ||||
|             } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use Crypt; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Support\Collection; | ||||
| use stdClass; | ||||
| @@ -38,15 +39,15 @@ class Income | ||||
|         $accountId = $entry->account_id; | ||||
|         if (!$this->incomes->has($accountId)) { | ||||
|             $newObject         = new stdClass; | ||||
|             $newObject->amount = strval(round($entry->amount, 2)); | ||||
|             $newObject->name   = $entry->name; | ||||
|             $newObject->amount = strval(round($entry->journalAmount, 2)); | ||||
|             $newObject->name   = Crypt::decrypt($entry->account_name); | ||||
|             $newObject->count  = 1; | ||||
|             $newObject->id     = $accountId; | ||||
|             $this->incomes->put($accountId, $newObject); | ||||
|         } else { | ||||
|             bcscale(2); | ||||
|             $existing         = $this->incomes->get($accountId); | ||||
|             $existing->amount = bcadd($existing->amount, $entry->amount); | ||||
|             $existing->amount = bcadd($existing->amount, $entry->journalAmount); | ||||
|             $existing->count++; | ||||
|             $this->incomes->put($accountId, $existing); | ||||
|         } | ||||
|   | ||||
							
								
								
									
										38
									
								
								app/Helpers/Csv/Converter/AccountId.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								app/Helpers/Csv/Converter/AccountId.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| <?php | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Account; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class AccountId | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class AccountId extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return Account | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         // is mapped? Then it's easy! | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|  | ||||
|             /** @var Account $account */ | ||||
|             $account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]); | ||||
|         } else { | ||||
|  | ||||
|             /** @var Account $account */ | ||||
|             $account = Auth::user()->accounts()->find($this->value); | ||||
|  | ||||
|             if (!is_null($account)) { | ||||
|                 Log::debug('Found ' . $account->accountType->type . ' named "******" with ID: ' . $this->value . ' (not mapped) '); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $account; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										26
									
								
								app/Helpers/Csv/Converter/Amount.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								app/Helpers/Csv/Converter/Amount.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use FireflyIII\Models\Account; | ||||
|  | ||||
| /** | ||||
|  * Class Amount | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class Amount extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return Account|null | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         if (is_numeric($this->value)) { | ||||
|             return $this->value; | ||||
|         } | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										68
									
								
								app/Helpers/Csv/Converter/AssetAccountIban.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								app/Helpers/Csv/Converter/AssetAccountIban.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\AccountType; | ||||
|  | ||||
| /** | ||||
|  * Class AssetAccountIban | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class AssetAccountIban extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return Account|null | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         // is mapped? Then it's easy! | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]); | ||||
|  | ||||
|             return $account; | ||||
|         } | ||||
|         if (strlen($this->value) > 0) { | ||||
|             // find or create new account: | ||||
|             $account     = $this->findAccount(); | ||||
|             $accountType = AccountType::where('type', 'Asset account')->first(); | ||||
|  | ||||
|             if (is_null($account)) { | ||||
|                 // create it if doesn't exist. | ||||
|                 $account = Account::firstOrCreateEncrypted( | ||||
|                     [ | ||||
|                         'name'            => $this->value, | ||||
|                         'iban'            => $this->value, | ||||
|                         'user_id'         => Auth::user()->id, | ||||
|                         'account_type_id' => $accountType->id, | ||||
|                         'active'          => 1, | ||||
|                     ] | ||||
|                 ); | ||||
|             } | ||||
|  | ||||
|             return $account; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Account|null | ||||
|      */ | ||||
|     protected function findAccount() | ||||
|     { | ||||
|         $set = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']); | ||||
|         /** @var Account $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             if ($entry->iban == $this->value) { | ||||
|  | ||||
|                 return $entry; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										51
									
								
								app/Helpers/Csv/Converter/AssetAccountName.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								app/Helpers/Csv/Converter/AssetAccountName.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\AccountType; | ||||
|  | ||||
| /** | ||||
|  * Class AssetAccountName | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class AssetAccountName extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return Account|null | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         // is mapped? Then it's easy! | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]); | ||||
|  | ||||
|             return $account; | ||||
|         } | ||||
|         // find or create new account: | ||||
|         $accountType = AccountType::where('type', 'Asset account')->first(); | ||||
|         $set         = Auth::user()->accounts()->accountTypeIn(['Asset account', 'Default account'])->get(); | ||||
|         /** @var Account $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             if ($entry->name == $this->value) { | ||||
|                 return $entry; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // create it if doesnt exist. | ||||
|         $account = Account::firstOrCreateEncrypted( | ||||
|             [ | ||||
|                 'name'            => $this->value, | ||||
|                 'iban'            => '', | ||||
|                 'user_id'         => Auth::user()->id, | ||||
|                 'account_type_id' => $accountType->id, | ||||
|                 'active'          => 1, | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         return $account; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										122
									
								
								app/Helpers/Csv/Converter/BasicConverter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								app/Helpers/Csv/Converter/BasicConverter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| /** | ||||
|  * Class BasicConverter | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class BasicConverter | ||||
| { | ||||
|     /** @var  array */ | ||||
|     protected $data; | ||||
|     /** @var string */ | ||||
|     protected $field; | ||||
|     /** @var int */ | ||||
|     protected $index; | ||||
|     /** @var  array */ | ||||
|     protected $mapped; | ||||
|     /** @var string */ | ||||
|     protected $role; | ||||
|     /** @var string */ | ||||
|     protected $value; | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getData() | ||||
|     { | ||||
|         return $this->data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      */ | ||||
|     public function setData(array $data) | ||||
|     { | ||||
|         $this->data = $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getField() | ||||
|     { | ||||
|         return $this->field; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $field | ||||
|      */ | ||||
|     public function setField($field) | ||||
|     { | ||||
|         $this->field = $field; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return int | ||||
|      */ | ||||
|     public function getIndex() | ||||
|     { | ||||
|         return $this->index; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $index | ||||
|      */ | ||||
|     public function setIndex($index) | ||||
|     { | ||||
|         $this->index = $index; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getMapped() | ||||
|     { | ||||
|         return $this->mapped; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $mapped | ||||
|      */ | ||||
|     public function setMapped($mapped) | ||||
|     { | ||||
|         $this->mapped = $mapped; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getRole() | ||||
|     { | ||||
|         return $this->role; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $role | ||||
|      */ | ||||
|     public function setRole($role) | ||||
|     { | ||||
|         $this->role = $role; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getValue() | ||||
|     { | ||||
|         return $this->value; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $value | ||||
|      */ | ||||
|     public function setValue($value) | ||||
|     { | ||||
|         $this->value = $value; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										30
									
								
								app/Helpers/Csv/Converter/BillId.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								app/Helpers/Csv/Converter/BillId.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Bill; | ||||
|  | ||||
| /** | ||||
|  * Class BillId | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class BillId extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return Bill | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         // is mapped? Then it's easy! | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $bill = Auth::user()->bills()->find($this->mapped[$this->index][$this->value]); | ||||
|         } else { | ||||
|             $bill = Auth::user()->bills()->find($this->value); | ||||
|         } | ||||
|  | ||||
|         return $bill; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										38
									
								
								app/Helpers/Csv/Converter/BillName.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								app/Helpers/Csv/Converter/BillName.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Bill; | ||||
|  | ||||
| /** | ||||
|  * Class BillName | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class BillName extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return Bill | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         $bill = null; | ||||
|         // is mapped? Then it's easy! | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $bill = Auth::user()->bills()->find($this->mapped[$this->index][$this->value]); | ||||
|         } else { | ||||
|  | ||||
|             $bills = Auth::user()->bills()->get(); | ||||
|             /** @var Bill $bill */ | ||||
|             foreach ($bills as $bill) { | ||||
|                 if ($bill->name == $this->value) { | ||||
|                     return $bill; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $bill; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										29
									
								
								app/Helpers/Csv/Converter/BudgetId.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								app/Helpers/Csv/Converter/BudgetId.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| <?php | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Budget; | ||||
|  | ||||
| /** | ||||
|  * Class AccountId | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class BudgetId extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return Budget | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         // is mapped? Then it's easy! | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $budget = Auth::user()->budgets()->find($this->mapped[$this->index][$this->value]); | ||||
|         } else { | ||||
|             $budget = Auth::user()->budgets()->find($this->value); | ||||
|         } | ||||
|  | ||||
|         return $budget; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										35
									
								
								app/Helpers/Csv/Converter/BudgetName.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								app/Helpers/Csv/Converter/BudgetName.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| <?php | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Budget; | ||||
|  | ||||
| /** | ||||
|  * Class BudgetName | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class BudgetName extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return Budget | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         // is mapped? Then it's easy! | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $budget = Auth::user()->budgets()->find($this->mapped[$this->index][$this->value]); | ||||
|         } else { | ||||
|             $budget = Budget::firstOrCreateEncrypted( | ||||
|                 [ | ||||
|                     'name'    => $this->value, | ||||
|                     'user_id' => Auth::user()->id, | ||||
|                     'active'  => true, | ||||
|                 ] | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return $budget; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										29
									
								
								app/Helpers/Csv/Converter/CategoryId.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								app/Helpers/Csv/Converter/CategoryId.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| <?php | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Category; | ||||
|  | ||||
| /** | ||||
|  * Class CategoryId | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class CategoryId extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return Category | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         // is mapped? Then it's easy! | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $category = Auth::user()->categories()->find($this->mapped[$this->index][$this->value]); | ||||
|         } else { | ||||
|             $category = Auth::user()->categories()->find($this->value); | ||||
|         } | ||||
|  | ||||
|         return $category; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										34
									
								
								app/Helpers/Csv/Converter/CategoryName.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								app/Helpers/Csv/Converter/CategoryName.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| <?php | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Category; | ||||
|  | ||||
| /** | ||||
|  * Class CategoryName | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class CategoryName extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return Category | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         // is mapped? Then it's easy! | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $category = Auth::user()->categories()->find($this->mapped[$this->index][$this->value]); | ||||
|         } else { | ||||
|             $category = Category::firstOrCreateEncrypted( | ||||
|                 [ | ||||
|                     'name'    => $this->value, | ||||
|                     'user_id' => Auth::user()->id | ||||
|                 ] | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return $category; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										49
									
								
								app/Helpers/Csv/Converter/ConverterInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								app/Helpers/Csv/Converter/ConverterInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| /** | ||||
|  * Interface ConverterInterface | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| interface ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function convert(); | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      */ | ||||
|     public function setData(array $data); | ||||
|  | ||||
|     /** | ||||
|      * @param string $field | ||||
|      * | ||||
|      */ | ||||
|     public function setField($field); | ||||
|  | ||||
|     /** | ||||
|      * @param int $index | ||||
|      */ | ||||
|     public function setIndex($index); | ||||
|  | ||||
|     /** | ||||
|      * @param array $mapped | ||||
|      */ | ||||
|     public function setMapped($mapped); | ||||
|  | ||||
|     /** | ||||
|      * @param string $role | ||||
|      */ | ||||
|     public function setRole($role); | ||||
|  | ||||
|     /** | ||||
|      * @param string $value | ||||
|      */ | ||||
|     public function setValue($value); | ||||
|  | ||||
| } | ||||
							
								
								
									
										28
									
								
								app/Helpers/Csv/Converter/CurrencyCode.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/Helpers/Csv/Converter/CurrencyCode.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
|  | ||||
| /** | ||||
|  * Class CurrencyCode | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class CurrencyCode extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return mixed|static | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $currency = TransactionCurrency::find($this->mapped[$this->index][$this->value]); | ||||
|         } else { | ||||
|             $currency = TransactionCurrency::whereCode($this->value)->first(); | ||||
|         } | ||||
|  | ||||
|         return $currency; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								app/Helpers/Csv/Converter/CurrencyId.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/Helpers/Csv/Converter/CurrencyId.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
|  | ||||
| /** | ||||
|  * Class CurrencyId | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class CurrencyId extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return mixed|static | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $currency = TransactionCurrency::find($this->mapped[$this->index][$this->value]); | ||||
|         } else { | ||||
|             $currency = TransactionCurrency::find($this->value); | ||||
|         } | ||||
|  | ||||
|         return $currency; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								app/Helpers/Csv/Converter/CurrencyName.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/Helpers/Csv/Converter/CurrencyName.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
|  | ||||
| /** | ||||
|  * Class CurrencyName | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class CurrencyName extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return mixed|static | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $currency = TransactionCurrency::find($this->mapped[$this->index][$this->value]); | ||||
|         } else { | ||||
|             $currency = TransactionCurrency::whereName($this->value)->first(); | ||||
|         } | ||||
|  | ||||
|         return $currency; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								app/Helpers/Csv/Converter/CurrencySymbol.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/Helpers/Csv/Converter/CurrencySymbol.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
|  | ||||
| /** | ||||
|  * Class CurrencySymbol | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class CurrencySymbol extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return mixed|static | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $currency = TransactionCurrency::find($this->mapped[$this->index][$this->value]); | ||||
|         } else { | ||||
|             $currency = TransactionCurrency::whereSymbol($this->value)->first(); | ||||
|         } | ||||
|  | ||||
|         return $currency; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										39
									
								
								app/Helpers/Csv/Converter/Date.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								app/Helpers/Csv/Converter/Date.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use InvalidArgumentException; | ||||
| use Log; | ||||
| use Session; | ||||
|  | ||||
| /** | ||||
|  * Class Date | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class Date extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return static | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         $format = Session::get('csv-date-format'); | ||||
|         try { | ||||
|             $date = Carbon::createFromFormat($format, $this->value); | ||||
|         } catch (InvalidArgumentException $e) { | ||||
|             Log::error('Date conversion error: ' . $e->getMessage() . '. Value was "' . $this->value . '", format was "' . $format . '".'); | ||||
|  | ||||
|             $message = trans('firefly.csv_date_parse_error', ['format' => $format, 'value' => $this->value]); | ||||
|  | ||||
|             throw new FireflyException($message); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         return $date; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										21
									
								
								app/Helpers/Csv/Converter/Description.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								app/Helpers/Csv/Converter/Description.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| /** | ||||
|  * Class Description | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class Description extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         return trim($this->data['description'] . ' ' . $this->value); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										22
									
								
								app/Helpers/Csv/Converter/Ignore.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/Helpers/Csv/Converter/Ignore.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use FireflyIII\Models\Account; | ||||
|  | ||||
| /** | ||||
|  * Class Amount | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class Ignore extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return Account|null | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										57
									
								
								app/Helpers/Csv/Converter/OpposingAccountIban.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								app/Helpers/Csv/Converter/OpposingAccountIban.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Account; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class OpposingAccountIban | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class OpposingAccountIban extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * If mapped, return account. Otherwise, only return the name itself. | ||||
|      * | ||||
|      * @return Account|string | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]); | ||||
|  | ||||
|             return $account; | ||||
|         } else { | ||||
|             if (strlen($this->value) > 0) { | ||||
|                 $account = $this->findAccount(); | ||||
|                 if (!is_null($account)) { | ||||
|                     return $account; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return $this->value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Account|null | ||||
|      */ | ||||
|     protected function findAccount() | ||||
|     { | ||||
|         $set = Auth::user()->accounts()->get(); | ||||
|         /** @var Account $account */ | ||||
|         foreach ($set as $account) { | ||||
|             if ($account->iban == $this->value) { | ||||
|                 Log::debug('OpposingAccountIban::convert found an Account (#' . $account->id . ': ******) with IBAN ******'); | ||||
|  | ||||
|                 return $account; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										32
									
								
								app/Helpers/Csv/Converter/OpposingAccountId.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								app/Helpers/Csv/Converter/OpposingAccountId.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Account; | ||||
|  | ||||
| /** | ||||
|  * Class OpposingName | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class OpposingAccountId extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @return Account | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]); | ||||
|  | ||||
|         } else { | ||||
|             $account = Auth::user()->accounts()->find($this->value); | ||||
|         } | ||||
|  | ||||
|         return $account; | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										31
									
								
								app/Helpers/Csv/Converter/OpposingAccountName.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								app/Helpers/Csv/Converter/OpposingAccountName.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Account; | ||||
|  | ||||
| /** | ||||
|  * Class OpposingName | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class OpposingAccountName extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * If mapped, return account. Otherwise, only return the name itself. | ||||
|      * | ||||
|      * @return Account|string | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         if (isset($this->mapped[$this->index][$this->value])) { | ||||
|             $account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]); | ||||
|  | ||||
|             return $account; | ||||
|         } else { | ||||
|             return $this->value; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										26
									
								
								app/Helpers/Csv/Converter/RabobankDebetCredit.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								app/Helpers/Csv/Converter/RabobankDebetCredit.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class RabobankDebetCredit | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class RabobankDebetCredit extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         if ($this->value == 'D') { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         return 1; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										40
									
								
								app/Helpers/Csv/Converter/TagsComma.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								app/Helpers/Csv/Converter/TagsComma.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Bill; | ||||
| use FireflyIII\Models\Tag; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class TagsComma | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class TagsComma extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return Bill | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         $tags = new Collection; | ||||
|  | ||||
|         $strings = explode(',', $this->value); | ||||
|         foreach ($strings as $string) { | ||||
|             $tag = Tag::firstOrCreateEncrypted( | ||||
|                 [ | ||||
|                     'tag'     => $string, | ||||
|                     'tagMode' => 'nothing', | ||||
|                     'user_id' => Auth::user()->id, | ||||
|                 ] | ||||
|             ); | ||||
|             $tags->push($tag); | ||||
|         } | ||||
|         $tags = $tags->merge($this->data['tags']); | ||||
|  | ||||
|         return $tags; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										40
									
								
								app/Helpers/Csv/Converter/TagsSpace.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								app/Helpers/Csv/Converter/TagsSpace.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Converter; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Bill; | ||||
| use FireflyIII\Models\Tag; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class TagsSpace | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Converter | ||||
|  */ | ||||
| class TagsSpace extends BasicConverter implements ConverterInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return Bill | ||||
|      */ | ||||
|     public function convert() | ||||
|     { | ||||
|         $tags = new Collection; | ||||
|  | ||||
|         $strings = explode(' ', $this->value); | ||||
|         foreach ($strings as $string) { | ||||
|             $tag = Tag::firstOrCreateEncrypted( | ||||
|                 [ | ||||
|                     'tag'     => $string, | ||||
|                     'tagMode' => 'nothing', | ||||
|                     'user_id' => Auth::user()->id, | ||||
|                 ] | ||||
|             ); | ||||
|             $tags->push($tag); | ||||
|         } | ||||
|         $tags = $tags->merge($this->data['tags']); | ||||
|  | ||||
|         return $tags; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										283
									
								
								app/Helpers/Csv/Data.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								app/Helpers/Csv/Data.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,283 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv; | ||||
|  | ||||
| use Crypt; | ||||
| use League\Csv\Reader; | ||||
| use Session; | ||||
|  | ||||
| /** | ||||
|  * Class Data | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv | ||||
|  */ | ||||
| class Data | ||||
| { | ||||
|  | ||||
|     /** @var string */ | ||||
|     protected $csvFileContent; | ||||
|  | ||||
|     /** @var string */ | ||||
|     protected $csvFileLocation; | ||||
|     /** @var  string */ | ||||
|     protected $dateFormat; | ||||
|     /** @var  bool */ | ||||
|     protected $hasHeaders; | ||||
|  | ||||
|     /** @var  array */ | ||||
|     protected $map = []; | ||||
|     /** @var  array */ | ||||
|     protected $mapped = []; | ||||
|     /** @var  Reader */ | ||||
|     protected $reader; | ||||
|     /** @var  array */ | ||||
|     protected $roles = []; | ||||
|  | ||||
|     /** @var  array */ | ||||
|     protected $specifix = []; | ||||
|  | ||||
|     /** @var int */ | ||||
|     protected $importAccount = 0; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->sessionHasHeaders(); | ||||
|         $this->sessionDateFormat(); | ||||
|         $this->sessionCsvFileLocation(); | ||||
|         $this->sessionMap(); | ||||
|         $this->sessionRoles(); | ||||
|         $this->sessionMapped(); | ||||
|         $this->sessionSpecifix(); | ||||
|         $this->sessionImportAccount(); | ||||
|     } | ||||
|  | ||||
|     protected function sessionHasHeaders() | ||||
|     { | ||||
|         if (Session::has('csv-has-headers')) { | ||||
|             $this->hasHeaders = (bool)Session::get('csv-has-headers'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected function sessionImportAccount() | ||||
|     { | ||||
|         if (Session::has('csv-import-account')) { | ||||
|             $this->importAccount = intval(Session::get('csv-import-account')); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected function sessionDateFormat() | ||||
|     { | ||||
|         if (Session::has('csv-date-format')) { | ||||
|             $this->dateFormat = (string)Session::get('csv-date-format'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected function sessionCsvFileLocation() | ||||
|     { | ||||
|         if (Session::has('csv-file')) { | ||||
|             $this->csvFileLocation = (string)Session::get('csv-file'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected function sessionMap() | ||||
|     { | ||||
|         if (Session::has('csv-map')) { | ||||
|             $this->map = (array)Session::get('csv-map'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected function sessionRoles() | ||||
|     { | ||||
|         if (Session::has('csv-roles')) { | ||||
|             $this->roles = (array)Session::get('csv-roles'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected function sessionMapped() | ||||
|     { | ||||
|         if (Session::has('csv-mapped')) { | ||||
|             $this->mapped = (array)Session::get('csv-mapped'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected function sessionSpecifix() | ||||
|     { | ||||
|         if (Session::has('csv-specifix')) { | ||||
|             $this->specifix = (array)Session::get('csv-specifix'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getDateFormat() | ||||
|     { | ||||
|         return $this->dateFormat; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param mixed $dateFormat | ||||
|      */ | ||||
|     public function setDateFormat($dateFormat) | ||||
|     { | ||||
|         Session::put('csv-date-format', $dateFormat); | ||||
|         $this->dateFormat = $dateFormat; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $importAccount | ||||
|      */ | ||||
|     public function setImportAccount($importAccount) | ||||
|     { | ||||
|         Session::put('csv-import-account', $importAccount); | ||||
|         $this->importAccount = $importAccount; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function hasHeaders() | ||||
|     { | ||||
|         return $this->hasHeaders; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param bool $hasHeaders | ||||
|      */ | ||||
|     public function setHasHeaders($hasHeaders) | ||||
|     { | ||||
|         Session::put('csv-has-headers', $hasHeaders); | ||||
|         $this->hasHeaders = $hasHeaders; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getMap() | ||||
|     { | ||||
|         return $this->map; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $map | ||||
|      */ | ||||
|     public function setMap(array $map) | ||||
|     { | ||||
|         Session::put('csv-map', $map); | ||||
|         $this->map = $map; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getMapped() | ||||
|     { | ||||
|         return $this->mapped; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $mapped | ||||
|      */ | ||||
|     public function setMapped(array $mapped) | ||||
|     { | ||||
|         Session::put('csv-mapped', $mapped); | ||||
|         $this->mapped = $mapped; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Reader | ||||
|      */ | ||||
|     public function getReader() | ||||
|     { | ||||
|  | ||||
|         if (strlen($this->csvFileContent) === 0) { | ||||
|             $this->loadCsvFile(); | ||||
|         } | ||||
|  | ||||
|         if (is_null($this->reader)) { | ||||
|             $this->reader = Reader::createFromString($this->getCsvFileContent()); | ||||
|         } | ||||
|  | ||||
|         return $this->reader; | ||||
|     } | ||||
|  | ||||
|     protected function loadCsvFile() | ||||
|     { | ||||
|         $file             = $this->getCsvFileLocation(); | ||||
|         $content          = file_get_contents($file); | ||||
|         $contentDecrypted = Crypt::decrypt($content); | ||||
|         $this->setCsvFileContent($contentDecrypted); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getCsvFileLocation() | ||||
|     { | ||||
|         return $this->csvFileLocation; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $csvFileLocation | ||||
|      */ | ||||
|     public function setCsvFileLocation($csvFileLocation) | ||||
|     { | ||||
|         Session::put('csv-file', $csvFileLocation); | ||||
|         $this->csvFileLocation = $csvFileLocation; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getCsvFileContent() | ||||
|     { | ||||
|         return $this->csvFileContent; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $csvFileContent | ||||
|      */ | ||||
|     public function setCsvFileContent($csvFileContent) | ||||
|     { | ||||
|         $this->csvFileContent = $csvFileContent; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getRoles() | ||||
|     { | ||||
|         return $this->roles; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $roles | ||||
|      */ | ||||
|     public function setRoles(array $roles) | ||||
|     { | ||||
|         Session::put('csv-roles', $roles); | ||||
|         $this->roles = $roles; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getSpecifix() | ||||
|     { | ||||
|         return is_array($this->specifix) ? $this->specifix : []; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $specifix | ||||
|      */ | ||||
|     public function setSpecifix($specifix) | ||||
|     { | ||||
|         Session::put('csv-specifix', $specifix); | ||||
|         $this->specifix = $specifix; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										371
									
								
								app/Helpers/Csv/Importer.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										371
									
								
								app/Helpers/Csv/Importer.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,371 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv; | ||||
|  | ||||
| use Auth; | ||||
| use Config; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Helpers\Csv\Converter\ConverterInterface; | ||||
| use FireflyIII\Helpers\Csv\PostProcessing\PostProcessorInterface; | ||||
| use FireflyIII\Helpers\Csv\Specifix\SpecifixInterface; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Transaction; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use FireflyIII\Models\TransactionType; | ||||
| use Illuminate\Support\Collection; | ||||
| use Illuminate\Support\MessageBag; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class Importer | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv | ||||
|  */ | ||||
| class Importer | ||||
| { | ||||
|  | ||||
|     /** @var Data */ | ||||
|     protected $data; | ||||
|     /** @var array */ | ||||
|     protected $errors; | ||||
|     /** @var  array */ | ||||
|     protected $importData; | ||||
|     /** @var  array */ | ||||
|     protected $importRow; | ||||
|     /** @var int */ | ||||
|     protected $imported = 0; | ||||
|     /** @var array */ | ||||
|     protected $map; | ||||
|     /** @var  array */ | ||||
|     protected $mapped; | ||||
|     /** @var  array */ | ||||
|     protected $roles; | ||||
|     /** @var  int */ | ||||
|     protected $rows = 0; | ||||
|     /** @var array */ | ||||
|     protected $specifix = []; | ||||
|  | ||||
|     /** @var  Collection */ | ||||
|     protected $journals; | ||||
|  | ||||
|     /** | ||||
|      * Used by CsvController. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getErrors() | ||||
|     { | ||||
|         return $this->errors; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Used by CsvController | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     public function getImported() | ||||
|     { | ||||
|         return $this->imported; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Used by CsvController | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     public function getRows() | ||||
|     { | ||||
|         return $this->rows; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getJournals() | ||||
|     { | ||||
|         return $this->journals; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function run() | ||||
|     { | ||||
|         set_time_limit(0); | ||||
|  | ||||
|         $this->journals = new Collection; | ||||
|         $this->map      = $this->data->getMap(); | ||||
|         $this->roles    = $this->data->getRoles(); | ||||
|         $this->mapped   = $this->data->getMapped(); | ||||
|         $this->specifix = $this->data->getSpecifix(); | ||||
|  | ||||
|         foreach ($this->data->getReader() as $index => $row) { | ||||
|             if ($this->parseRow($index)) { | ||||
|                 Log::debug('--- Importing row ' . $index); | ||||
|                 $this->rows++; | ||||
|                 $result = $this->importRow($row); | ||||
|                 if (!($result instanceof TransactionJournal)) { | ||||
|                     Log::error('Caught error at row #' . $index . ': ' . $result); | ||||
|                     $this->errors[$index] = $result; | ||||
|                 } else { | ||||
|                     $this->imported++; | ||||
|                     $this->journals->push($result); | ||||
|                 } | ||||
|                 Log::debug('---'); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $index | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function parseRow($index) | ||||
|     { | ||||
|         return (($this->data->hasHeaders() && $index >= 1) || !$this->data->hasHeaders()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $row | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      * @return string|bool | ||||
|      */ | ||||
|     protected function importRow($row) | ||||
|     { | ||||
|  | ||||
|         $data = $this->getFiller(); // These fields are necessary to create a new transaction journal. Some are optional | ||||
|         foreach ($row as $index => $value) { | ||||
|             $role  = isset($this->roles[$index]) ? $this->roles[$index] : '_ignore'; | ||||
|             $class = Config::get('csv.roles.' . $role . '.converter'); | ||||
|             $field = Config::get('csv.roles.' . $role . '.field'); | ||||
|  | ||||
|             Log::debug('Column #' . $index . ' (role: ' . $role . ') : converter ' . $class . ' stores its data into field ' . $field . ':'); | ||||
|  | ||||
|             /** @var ConverterInterface $converter */ | ||||
|             $converter = app('FireflyIII\Helpers\Csv\Converter\\' . $class); | ||||
|             $converter->setData($data); // the complete array so far. | ||||
|             $converter->setField($field); | ||||
|             $converter->setIndex($index); | ||||
|             $converter->setMapped($this->mapped); | ||||
|             $converter->setValue($value); | ||||
|             $converter->setRole($role); | ||||
|             $data[$field] = $converter->convert(); | ||||
|         } | ||||
|         // move to class vars. | ||||
|         $this->importData = $data; | ||||
|         $this->importRow  = $row; | ||||
|         unset($data, $row); | ||||
|         // post processing and validating. | ||||
|         $this->postProcess(); | ||||
|         $result = $this->validateData(); | ||||
|  | ||||
|         if (!($result === true)) { | ||||
|             return $result; // return error. | ||||
|         } | ||||
|         $journal = $this->createTransactionJournal(); | ||||
|  | ||||
|         return $journal; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function getFiller() | ||||
|     { | ||||
|         $filler = []; | ||||
|         foreach (Config::get('csv.roles') as $role) { | ||||
|             if (isset($role['field'])) { | ||||
|                 $fieldName          = $role['field']; | ||||
|                 $filler[$fieldName] = null; | ||||
|             } | ||||
|         } | ||||
|         // some extra's: | ||||
|         $filler['bill-id']                 = null; | ||||
|         $filler['opposing-account-object'] = null; | ||||
|         $filler['asset-account-object']    = null; | ||||
|         $filler['amount-modifier']         = '1'; | ||||
|  | ||||
|         return $filler; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Row denotes the original data. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     protected function postProcess() | ||||
|     { | ||||
|         // do bank specific fixes (must be enabled but now all of them. | ||||
|  | ||||
|         foreach ($this->getSpecifix() as $className) { | ||||
|             /** @var SpecifixInterface $specifix */ | ||||
|             $specifix = app('FireflyIII\Helpers\Csv\Specifix\\' . $className); | ||||
|             $specifix->setData($this->importData); | ||||
|             $specifix->setRow($this->importRow); | ||||
|             Log::debug('Now post-process specifix named ' . $className . ':'); | ||||
|             $this->importData = $specifix->fix(); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         $set = Config::get('csv.post_processors'); | ||||
|         foreach ($set as $className) { | ||||
|             /** @var PostProcessorInterface $postProcessor */ | ||||
|             $postProcessor = app('FireflyIII\Helpers\Csv\PostProcessing\\' . $className); | ||||
|             $postProcessor->setData($this->importData); | ||||
|             Log::debug('Now post-process processor named ' . $className . ':'); | ||||
|             $this->importData = $postProcessor->process(); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getSpecifix() | ||||
|     { | ||||
|         return is_array($this->specifix) ? $this->specifix : []; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @return bool|string | ||||
|      */ | ||||
|     protected function validateData() | ||||
|     { | ||||
|         if (is_null($this->importData['date']) && is_null($this->importData['date-rent'])) { | ||||
|             return 'No date value for this row.'; | ||||
|         } | ||||
|         if (is_null($this->importData['opposing-account-object'])) { | ||||
|             return 'Opposing account is null'; | ||||
|         } | ||||
|  | ||||
|         if (!($this->importData['asset-account-object'] instanceof Account)) { | ||||
|             return 'No asset account to import into.'; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @return TransactionJournal|string | ||||
|      */ | ||||
|     protected function createTransactionJournal() | ||||
|     { | ||||
|         bcscale(2); | ||||
|         $date = $this->importData['date']; | ||||
|         if (is_null($this->importData['date'])) { | ||||
|             $date = $this->importData['date-rent']; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         $transactionType = $this->getTransactionType(); // defaults to deposit | ||||
|         $errors          = new MessageBag; | ||||
|         $journal         = TransactionJournal::create( | ||||
|             ['user_id'     => Auth::user()->id, 'transaction_type_id' => $transactionType->id, 'transaction_currency_id' => $this->importData['currency']->id, | ||||
|              'description' => $this->importData['description'], 'completed' => 0, 'date' => $date, 'bill_id' => $this->importData['bill-id'],] | ||||
|         ); | ||||
|         if ($journal->getErrors()->count() == 0) { | ||||
|             // first transaction | ||||
|             $accountId   = $this->importData['asset-account-object']->id; // create first transaction: | ||||
|             $amount      = $this->importData['amount']; | ||||
|             $transaction = Transaction::create(['transaction_journal_id' => $journal->id, 'account_id' => $accountId, 'amount' => $amount]); | ||||
|             $errors      = $transaction->getErrors(); | ||||
|  | ||||
|             // second transaction | ||||
|             $accountId   = $this->importData['opposing-account-object']->id; // create second transaction: | ||||
|             $amount      = bcmul($this->importData['amount'], -1); | ||||
|             $transaction = Transaction::create(['transaction_journal_id' => $journal->id, 'account_id' => $accountId, 'amount' => $amount]); | ||||
|             $errors      = $transaction->getErrors()->merge($errors); | ||||
|         } | ||||
|         if ($errors->count() == 0) { | ||||
|             $journal->completed = 1; | ||||
|             $journal->save(); | ||||
|         } else { | ||||
|             $text = join(',', $errors->all()); | ||||
|  | ||||
|             return $text; | ||||
|         } | ||||
|         $this->saveBudget($journal); | ||||
|         $this->saveCategory($journal); | ||||
|         $this->saveTags($journal); | ||||
|  | ||||
|         // some debug info: | ||||
|         $journalId = $journal->id; | ||||
|         $type      = $journal->getTransactionType(); | ||||
|         /** @var Account $asset */ | ||||
|         $asset = $this->importData['asset-account-object']; | ||||
|         /** @var Account $opposing */ | ||||
|         $opposing = $this->importData['opposing-account-object']; | ||||
|  | ||||
|         Log::info('Created journal #' . $journalId . ' of type ' . $type . '!'); | ||||
|         Log::info('Asset account ****** (#' . $asset->id . ') lost/gained: ' . $this->importData['amount']); | ||||
|         Log::info($opposing->accountType->type . ' ****** (#' . $opposing->id . ') lost/gained: ' . bcmul($this->importData['amount'], -1)); | ||||
|  | ||||
|         return $journal; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return TransactionType | ||||
|      */ | ||||
|     protected function getTransactionType() | ||||
|     { | ||||
|         $transactionType = TransactionType::where('type', TransactionType::DEPOSIT)->first(); | ||||
|         if ($this->importData['amount'] < 0) { | ||||
|             $transactionType = TransactionType::where('type', TransactionType::WITHDRAWAL)->first(); | ||||
|         } | ||||
|  | ||||
|         if (in_array($this->importData['opposing-account-object']->accountType->type, ['Asset account', 'Default account'])) { | ||||
|             $transactionType = TransactionType::where('type', TransactionType::TRANSFER)->first(); | ||||
|         } | ||||
|  | ||||
|         return $transactionType; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param TransactionJournal $journal | ||||
|      */ | ||||
|     protected function saveBudget(TransactionJournal $journal) | ||||
|     { | ||||
|         // add budget: | ||||
|         if (!is_null($this->importData['budget'])) { | ||||
|             $journal->budgets()->save($this->importData['budget']); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param TransactionJournal $journal | ||||
|      */ | ||||
|     protected function saveCategory(TransactionJournal $journal) | ||||
|     { | ||||
|         // add category: | ||||
|         if (!is_null($this->importData['category'])) { | ||||
|             $journal->categories()->save($this->importData['category']); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param TransactionJournal $journal | ||||
|      */ | ||||
|     protected function saveTags(TransactionJournal $journal) | ||||
|     { | ||||
|         if (!is_null($this->importData['tags'])) { | ||||
|             foreach ($this->importData['tags'] as $tag) { | ||||
|                 $journal->tags()->save($tag); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Data $data | ||||
|      */ | ||||
|     public function setData($data) | ||||
|     { | ||||
|         $this->data = $data; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										34
									
								
								app/Helpers/Csv/Mapper/AnyAccount.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								app/Helpers/Csv/Mapper/AnyAccount.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Mapper; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Account; | ||||
|  | ||||
| /** | ||||
|  * Class AnyAccount | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Mapper | ||||
|  */ | ||||
| class AnyAccount implements MapperInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getMap() | ||||
|     { | ||||
|         $result = Auth::user()->accounts()->with('accountType')->orderBy('accounts.name', 'ASC')->get(['accounts.*']); | ||||
|  | ||||
|         $list = []; | ||||
|         /** @var Account $account */ | ||||
|         foreach ($result as $account) { | ||||
|             $list[$account->id] = $account->name . ' (' . $account->accountType->type . ')'; | ||||
|         } | ||||
|         asort($list); | ||||
|  | ||||
|         $list = [0 => trans('firefly.csv_do_not_map')] + $list; | ||||
|  | ||||
|         return $list; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										45
									
								
								app/Helpers/Csv/Mapper/AssetAccount.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								app/Helpers/Csv/Mapper/AssetAccount.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Mapper; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Account; | ||||
| use Illuminate\Database\Eloquent\Relations\HasMany; | ||||
|  | ||||
| /** | ||||
|  * Class AssetAccount | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Mapper | ||||
|  */ | ||||
| class AssetAccount implements MapperInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getMap() | ||||
|     { | ||||
|         $result = Auth::user()->accounts()->with( | ||||
|             ['accountmeta' => function (HasMany $query) { | ||||
|                 $query->where('name', 'accountRole'); | ||||
|             }] | ||||
|         )->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*']); | ||||
|  | ||||
|         $list = []; | ||||
|  | ||||
|         /** @var Account $account */ | ||||
|         foreach ($result as $account) { | ||||
|             $name = $account->name; | ||||
|             if (strlen($account->iban) > 0) { | ||||
|                 $name .= ' (' . $account->iban . ')'; | ||||
|             } | ||||
|             $list[$account->id] = $name; | ||||
|         } | ||||
|  | ||||
|         asort($list); | ||||
|  | ||||
|         $list = [0 => trans('firefly.csv_do_not_map')] + $list; | ||||
|  | ||||
|         return $list; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										34
									
								
								app/Helpers/Csv/Mapper/Bill.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								app/Helpers/Csv/Mapper/Bill.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Mapper; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Bill as BillModel; | ||||
|  | ||||
| /** | ||||
|  * Class Bill | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Mapper | ||||
|  */ | ||||
| class Bill implements MapperInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getMap() | ||||
|     { | ||||
|         $result = Auth::user()->bills()->get(['bills.*']); | ||||
|         $list   = []; | ||||
|  | ||||
|         /** @var BillModel $bill */ | ||||
|         foreach ($result as $bill) { | ||||
|             $list[$bill->id] = $bill->name . ' [' . $bill->match . ']'; | ||||
|         } | ||||
|         asort($list); | ||||
|  | ||||
|         $list = [0 => trans('firefly.csv_do_not_map')] + $list; | ||||
|  | ||||
|         return $list; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										34
									
								
								app/Helpers/Csv/Mapper/Budget.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								app/Helpers/Csv/Mapper/Budget.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Mapper; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Budget as BudgetModel; | ||||
|  | ||||
| /** | ||||
|  * Class Budget | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Mapper | ||||
|  */ | ||||
| class Budget implements MapperInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getMap() | ||||
|     { | ||||
|         $result = Auth::user()->budgets()->get(['budgets.*']); | ||||
|         $list   = []; | ||||
|  | ||||
|         /** @var BudgetModel $budget */ | ||||
|         foreach ($result as $budget) { | ||||
|             $list[$budget->id] = $budget->name; | ||||
|         } | ||||
|         asort($list); | ||||
|  | ||||
|         $list = [0 => trans('firefly.csv_do_not_map')] + $list; | ||||
|  | ||||
|         return $list; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										34
									
								
								app/Helpers/Csv/Mapper/Category.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								app/Helpers/Csv/Mapper/Category.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Mapper; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Category as CategoryModel; | ||||
|  | ||||
| /** | ||||
|  * Class Category | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Mapper | ||||
|  */ | ||||
| class Category implements MapperInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getMap() | ||||
|     { | ||||
|         $result = Auth::user()->categories()->get(['categories.*']); | ||||
|         $list   = []; | ||||
|  | ||||
|         /** @var CategoryModel $category */ | ||||
|         foreach ($result as $category) { | ||||
|             $list[$category->id] = $category->name; | ||||
|         } | ||||
|         asort($list); | ||||
|  | ||||
|         $list = [0 => trans('firefly.csv_do_not_map')] + $list; | ||||
|  | ||||
|         return $list; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										16
									
								
								app/Helpers/Csv/Mapper/MapperInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/Helpers/Csv/Mapper/MapperInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Mapper; | ||||
|  | ||||
| /** | ||||
|  * Interface MapperInterface | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Mapper | ||||
|  */ | ||||
| interface MapperInterface | ||||
| { | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getMap(); | ||||
| } | ||||
							
								
								
									
										34
									
								
								app/Helpers/Csv/Mapper/Tag.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								app/Helpers/Csv/Mapper/Tag.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Mapper; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Tag as TagModel; | ||||
|  | ||||
| /** | ||||
|  * Class Tag | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Mapper | ||||
|  */ | ||||
| class Tag implements MapperInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getMap() | ||||
|     { | ||||
|         $result = Auth::user()->budgets()->get(['tags.*']); | ||||
|         $list   = []; | ||||
|  | ||||
|         /** @var TagModel $tag */ | ||||
|         foreach ($result as $tag) { | ||||
|             $list[$tag->id] = $tag->tag; | ||||
|         } | ||||
|         asort($list); | ||||
|  | ||||
|         $list = [0 => trans('firefly.csv_do_not_map')] + $list; | ||||
|  | ||||
|         return $list; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										32
									
								
								app/Helpers/Csv/Mapper/TransactionCurrency.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								app/Helpers/Csv/Mapper/TransactionCurrency.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Mapper; | ||||
|  | ||||
| use FireflyIII\Models\TransactionCurrency as TC; | ||||
|  | ||||
| /** | ||||
|  * Class TransactionCurrency | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Mapper | ||||
|  */ | ||||
| class TransactionCurrency implements MapperInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getMap() | ||||
|     { | ||||
|         $currencies = TC::get(); | ||||
|         $list       = []; | ||||
|         foreach ($currencies as $currency) { | ||||
|             $list[$currency->id] = $currency->name . ' (' . $currency->code . ')'; | ||||
|         } | ||||
|  | ||||
|         asort($list); | ||||
|  | ||||
|         $list = [0 => trans('firefly.csv_do_not_map')] + $list; | ||||
|  | ||||
|         return $list; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										35
									
								
								app/Helpers/Csv/PostProcessing/Amount.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								app/Helpers/Csv/PostProcessing/Amount.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| <?php | ||||
|  | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\PostProcessing; | ||||
|  | ||||
| /** | ||||
|  * Class Amount | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\PostProcessing | ||||
|  */ | ||||
| class Amount implements PostProcessorInterface | ||||
| { | ||||
|  | ||||
|     /** @var  array */ | ||||
|     protected $data; | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function process() | ||||
|     { | ||||
|         bcscale(2); | ||||
|         $this->data['amount'] = bcmul($this->data['amount'], $this->data['amount-modifier']); | ||||
|  | ||||
|         return $this->data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      */ | ||||
|     public function setData(array $data) | ||||
|     { | ||||
|         $this->data = $data; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										185
									
								
								app/Helpers/Csv/PostProcessing/AssetAccount.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								app/Helpers/Csv/PostProcessing/AssetAccount.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,185 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\PostProcessing; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\AccountType; | ||||
| use Log; | ||||
| use Validator; | ||||
|  | ||||
| /** | ||||
|  * Class AssetAccount | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\PostProcessing | ||||
|  */ | ||||
| class AssetAccount implements PostProcessorInterface | ||||
| { | ||||
|  | ||||
|     /** @var  array */ | ||||
|     protected $data; | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function process() | ||||
|     { | ||||
|         $result = $this->checkIdNameObject(); // has object in ID or Name? | ||||
|         if (!is_null($result)) { | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         $result = $this->checkIbanString(); | ||||
|         if (!is_null($result)) { | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         $result = $this->checkNameString(); | ||||
|         if (!is_null($result)) { | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      */ | ||||
|     public function setData(array $data) | ||||
|     { | ||||
|         $this->data = $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function checkIdNameObject() | ||||
|     { | ||||
|         if ($this->data['asset-account-id'] instanceof Account) { // first priority. try to find the account based on ID, if any | ||||
|             $this->data['asset-account-object'] = $this->data['asset-account-id']; | ||||
|  | ||||
|             return $this->data; | ||||
|         } | ||||
|         if ($this->data['asset-account-iban'] instanceof Account) { // second: try to find the account based on IBAN, if any. | ||||
|             $this->data['asset-account-object'] = $this->data['asset-account-iban']; | ||||
|  | ||||
|             return $this->data; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array|null | ||||
|      */ | ||||
|     protected function checkIbanString() | ||||
|     { | ||||
|         $rules     = ['iban' => 'iban']; | ||||
|         $check     = ['iban' => $this->data['asset-account-iban']]; | ||||
|         $validator = Validator::make($check, $rules); | ||||
|         if (!$validator->fails()) { | ||||
|             $this->data['asset-account-object'] = $this->parseIbanString(); | ||||
|  | ||||
|             return $this->data; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Account|null | ||||
|      */ | ||||
|     protected function parseIbanString() | ||||
|     { | ||||
|         // create by name and/or iban. | ||||
|         $accounts = Auth::user()->accounts()->get(); | ||||
|         foreach ($accounts as $entry) { | ||||
|             if ($entry->iban == $this->data['asset-account-iban']) { | ||||
|  | ||||
|                 return $entry; | ||||
|             } | ||||
|         } | ||||
|         $account = $this->createAccount(); | ||||
|  | ||||
|         return $account; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Account|null | ||||
|      */ | ||||
|     protected function createAccount() | ||||
|     { | ||||
|         $accountType = $this->getAccountType(); | ||||
|  | ||||
|         // create if not exists: | ||||
|         $name    = is_string($this->data['asset-account-name']) && strlen($this->data['asset-account-name']) > 0 ? $this->data['asset-account-name'] | ||||
|             : $this->data['asset-account-iban']; | ||||
|         $account = Account::firstOrCreateEncrypted( | ||||
|             [ | ||||
|                 'user_id'         => Auth::user()->id, | ||||
|                 'account_type_id' => $accountType->id, | ||||
|                 'name'            => $name, | ||||
|                 'iban'            => $this->data['asset-account-iban'], | ||||
|                 'active'          => true, | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         return $account; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @return AccountType | ||||
|      */ | ||||
|     protected function getAccountType() | ||||
|     { | ||||
|         return AccountType::where('type', 'Asset account')->first(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array|null | ||||
|      */ | ||||
|     protected function checkNameString() | ||||
|     { | ||||
|         if ($this->data['asset-account-name'] instanceof Account) { // third: try to find account based on name, if any. | ||||
|             $this->data['asset-account-object'] = $this->data['asset-account-name']; | ||||
|  | ||||
|             return $this->data; | ||||
|         } | ||||
|         if (is_string($this->data['asset-account-name'])) { | ||||
|             $this->data['asset-account-object'] = $this->parseNameString(); | ||||
|  | ||||
|             return $this->data; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Account|null | ||||
|      */ | ||||
|     protected function parseNameString() | ||||
|     { | ||||
|         $accountType = $this->getAccountType(); | ||||
|         $accounts    = Auth::user()->accounts()->where('account_type_id', $accountType->id)->get(); | ||||
|         foreach ($accounts as $entry) { | ||||
|             if ($entry->name == $this->data['asset-account-name']) { | ||||
|                 Log::debug('Found an asset account with this name (#' . $entry->id . ': ******)'); | ||||
|  | ||||
|                 return $entry; | ||||
|             } | ||||
|         } | ||||
|         // create if not exists: | ||||
|         $account = Account::firstOrCreateEncrypted( | ||||
|             [ | ||||
|                 'user_id'         => Auth::user()->id, | ||||
|                 'account_type_id' => $accountType->id, | ||||
|                 'name'            => $this->data['asset-account-name'], | ||||
|                 'iban'            => '', | ||||
|                 'active'          => true, | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         return $account; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										37
									
								
								app/Helpers/Csv/PostProcessing/Bill.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								app/Helpers/Csv/PostProcessing/Bill.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\PostProcessing; | ||||
|  | ||||
| /** | ||||
|  * Class Bill | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\PostProcessing | ||||
|  */ | ||||
| class Bill implements PostProcessorInterface | ||||
| { | ||||
|  | ||||
|     /** @var  array */ | ||||
|     protected $data; | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function process() | ||||
|     { | ||||
|  | ||||
|         // get bill id. | ||||
|         if (!is_null($this->data['bill'])) { | ||||
|             $this->data['bill-id'] = $this->data['bill']->id; | ||||
|         } | ||||
|  | ||||
|         return $this->data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      */ | ||||
|     public function setData(array $data) | ||||
|     { | ||||
|         $this->data = $data; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										41
									
								
								app/Helpers/Csv/PostProcessing/Currency.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								app/Helpers/Csv/PostProcessing/Currency.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\PostProcessing; | ||||
|  | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| use Preferences; | ||||
|  | ||||
| /** | ||||
|  * Class Currency | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\PostProcessing | ||||
|  */ | ||||
| class Currency implements PostProcessorInterface | ||||
| { | ||||
|  | ||||
|     /** @var  array */ | ||||
|     protected $data; | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function process() | ||||
|     { | ||||
|  | ||||
|         // fix currency | ||||
|         if (is_null($this->data['currency'])) { | ||||
|             $currencyPreference     = Preferences::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR')); | ||||
|             $this->data['currency'] = TransactionCurrency::whereCode($currencyPreference->data)->first(); | ||||
|         } | ||||
|  | ||||
|         return $this->data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      */ | ||||
|     public function setData(array $data) | ||||
|     { | ||||
|         $this->data = $data; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										38
									
								
								app/Helpers/Csv/PostProcessing/Description.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								app/Helpers/Csv/PostProcessing/Description.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\PostProcessing; | ||||
|  | ||||
| /** | ||||
|  * Class Description | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\PostProcessing | ||||
|  */ | ||||
| class Description implements PostProcessorInterface | ||||
| { | ||||
|  | ||||
|     /** @var  array */ | ||||
|     protected $data; | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function process() | ||||
|     { | ||||
|         $this->data['description'] = trim($this->data['description']); | ||||
|         if (strlen($this->data['description']) == 0) { | ||||
|             $this->data['description'] = trans('firefly.csv_empty_description'); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         return $this->data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      */ | ||||
|     public function setData(array $data) | ||||
|     { | ||||
|  | ||||
|         $this->data = $data; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										210
									
								
								app/Helpers/Csv/PostProcessing/OpposingAccount.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								app/Helpers/Csv/PostProcessing/OpposingAccount.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\PostProcessing; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\AccountType; | ||||
| use Log; | ||||
| use Validator; | ||||
|  | ||||
| /** | ||||
|  * Class OpposingAccount | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\PostProcessing | ||||
|  */ | ||||
| class OpposingAccount implements PostProcessorInterface | ||||
| { | ||||
|  | ||||
|     /** @var  array */ | ||||
|     protected $data; | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function process() | ||||
|     { | ||||
|         // three values: | ||||
|         // opposing-account-id, opposing-account-iban, opposing-account-name | ||||
|  | ||||
|  | ||||
|         $result = $this->checkIdNameObject(); | ||||
|         if (!is_null($result)) { | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         $result = $this->checkIbanString(); | ||||
|         if (!is_null($result)) { | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         $result = $this->checkNameString(); | ||||
|         if (!is_null($result)) { | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      */ | ||||
|     public function setData(array $data) | ||||
|     { | ||||
|         $this->data = $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function checkIdNameObject() | ||||
|     { | ||||
|         if ($this->data['opposing-account-id'] instanceof Account) { // first priority. try to find the account based on ID, if any | ||||
|             Log::debug('OpposingAccountPostProcession: opposing-account-id is an Account.'); | ||||
|             $this->data['opposing-account-object'] = $this->data['opposing-account-id']; | ||||
|  | ||||
|             return $this->data; | ||||
|         } | ||||
|         if ($this->data['opposing-account-iban'] instanceof Account) { // second: try to find the account based on IBAN, if any. | ||||
|             Log::debug('OpposingAccountPostProcession: opposing-account-iban is an Account.'); | ||||
|             $this->data['opposing-account-object'] = $this->data['opposing-account-iban']; | ||||
|  | ||||
|             return $this->data; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array|null | ||||
|      */ | ||||
|     protected function checkIbanString() | ||||
|     { | ||||
|         $rules     = ['iban' => 'iban']; | ||||
|         $iban      = $this->data['opposing-account-iban']; | ||||
|         $check     = ['iban' => $iban]; | ||||
|         $validator = Validator::make($check, $rules); | ||||
|         if (is_string($iban) && strlen($iban) > 0 && !$validator->fails()) { | ||||
|  | ||||
|             Log::debug('OpposingAccountPostProcession: opposing-account-iban is a string (******).'); | ||||
|             $this->data['opposing-account-object'] = $this->parseIbanString(); | ||||
|  | ||||
|             return $this->data; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Account|null | ||||
|      */ | ||||
|     protected function parseIbanString() | ||||
|     { | ||||
|         // create by name and/or iban. | ||||
|         $accounts = Auth::user()->accounts()->get(); | ||||
|         foreach ($accounts as $entry) { | ||||
|             if ($entry->iban == $this->data['opposing-account-iban']) { | ||||
|                 Log::debug('OpposingAccountPostProcession: opposing-account-iban matches an Account.'); | ||||
|  | ||||
|                 return $entry; | ||||
|             } | ||||
|         } | ||||
|         $account = $this->createAccount(); | ||||
|  | ||||
|  | ||||
|         return $account; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Account|null | ||||
|      */ | ||||
|     protected function createAccount() | ||||
|     { | ||||
|         $accountType = $this->getAccountType(); | ||||
|  | ||||
|         // create if not exists: | ||||
|         $name    = is_string($this->data['opposing-account-name']) && strlen($this->data['opposing-account-name']) > 0 ? $this->data['opposing-account-name'] | ||||
|             : $this->data['opposing-account-iban']; | ||||
|         $account = Account::firstOrCreateEncrypted( | ||||
|             [ | ||||
|                 'user_id'         => Auth::user()->id, | ||||
|                 'account_type_id' => $accountType->id, | ||||
|                 'name'            => $name, | ||||
|                 'iban'            => $this->data['opposing-account-iban'], | ||||
|                 'active'          => true, | ||||
|             ] | ||||
|         ); | ||||
|         Log::debug('OpposingAccountPostProcession: created a new account.'); | ||||
|  | ||||
|         return $account; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @return AccountType | ||||
|      */ | ||||
|     protected function getAccountType() | ||||
|     { | ||||
|         // opposing account type: | ||||
|         if ($this->data['amount'] < 0) { | ||||
|             // create expense account: | ||||
|  | ||||
|             return AccountType::where('type', 'Expense account')->first(); | ||||
|         } else { | ||||
|             // create revenue account: | ||||
|  | ||||
|             return AccountType::where('type', 'Revenue account')->first(); | ||||
|  | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array|null | ||||
|      */ | ||||
|     protected function checkNameString() | ||||
|     { | ||||
|         if ($this->data['opposing-account-name'] instanceof Account) { // third: try to find account based on name, if any. | ||||
|             Log::debug('OpposingAccountPostProcession: opposing-account-name is an Account.'); | ||||
|             $this->data['opposing-account-object'] = $this->data['opposing-account-name']; | ||||
|  | ||||
|             return $this->data; | ||||
|         } | ||||
|         if (is_string($this->data['opposing-account-name'])) { | ||||
|  | ||||
|             $this->data['opposing-account-object'] = $this->parseNameString(); | ||||
|  | ||||
|             return $this->data; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Account|null | ||||
|      */ | ||||
|     protected function parseNameString() | ||||
|     { | ||||
|         $accountType = $this->getAccountType(); | ||||
|         $accounts    = Auth::user()->accounts()->where('account_type_id', $accountType->id)->get(); | ||||
|         foreach ($accounts as $entry) { | ||||
|             if ($entry->name == $this->data['opposing-account-name']) { | ||||
|                 Log::debug('Found an account with this name (#' . $entry->id . ': ******)'); | ||||
|  | ||||
|                 return $entry; | ||||
|             } | ||||
|         } | ||||
|         // create if not exists: | ||||
|         $account = Account::firstOrCreateEncrypted( | ||||
|             [ | ||||
|                 'user_id'         => Auth::user()->id, | ||||
|                 'account_type_id' => $accountType->id, | ||||
|                 'name'            => $this->data['opposing-account-name'], | ||||
|                 'iban'            => '', | ||||
|                 'active'          => true, | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         return $account; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										29
									
								
								app/Helpers/Csv/PostProcessing/PostProcessorInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								app/Helpers/Csv/PostProcessing/PostProcessorInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Created by PhpStorm. | ||||
|  * User: sander | ||||
|  * Date: 05/07/15 | ||||
|  * Time: 19:20 | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\PostProcessing; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Interface PostProcessorInterface | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\PostProcessing | ||||
|  */ | ||||
| interface PostProcessorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function process(); | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      */ | ||||
|     public function setData(array $data); | ||||
| } | ||||
							
								
								
									
										45
									
								
								app/Helpers/Csv/Specifix/Dummy.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								app/Helpers/Csv/Specifix/Dummy.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Specifix; | ||||
|  | ||||
| /** | ||||
|  * Class Dummy | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Specifix | ||||
|  */ | ||||
| class Dummy | ||||
| { | ||||
|     /** @var array */ | ||||
|     protected $data; | ||||
|  | ||||
|     /** @var array */ | ||||
|     protected $row; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function fix() | ||||
|     { | ||||
|         return $this->data; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      */ | ||||
|     public function setData($data) | ||||
|     { | ||||
|         $this->data = $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $row | ||||
|      */ | ||||
|     public function setRow($row) | ||||
|     { | ||||
|         $this->row = $row; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										66
									
								
								app/Helpers/Csv/Specifix/RabobankDescription.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								app/Helpers/Csv/Specifix/RabobankDescription.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv\Specifix; | ||||
|  | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class RabobankDescription | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Specifix | ||||
|  */ | ||||
| class RabobankDescription | ||||
| { | ||||
|     /** @var array */ | ||||
|     protected $data; | ||||
|  | ||||
|     /** @var array */ | ||||
|     protected $row; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public function fix() | ||||
|     { | ||||
|         $this->rabobankFixEmptyOpposing(); | ||||
|  | ||||
|         return $this->data; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      */ | ||||
|     public function setData($data) | ||||
|     { | ||||
|         $this->data = $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $row | ||||
|      */ | ||||
|     public function setRow($row) | ||||
|     { | ||||
|         $this->row = $row; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Fixes Rabobank specific thing. | ||||
|      */ | ||||
|     protected function rabobankFixEmptyOpposing() | ||||
|     { | ||||
|         Log::debug('RaboSpecifix: Opposing account name is "******".'); | ||||
|         if (is_string($this->data['opposing-account-name']) && strlen($this->data['opposing-account-name']) == 0) { | ||||
|             Log::debug('RaboSpecifix: opp-name is zero length, changed to: "******"'); | ||||
|             $this->data['opposing-account-name'] = $this->row[10]; | ||||
|  | ||||
|             Log::debug('Description was: "******".'); | ||||
|             $this->data['description'] = trim(str_replace($this->row[10], '', $this->data['description'])); | ||||
|             Log::debug('Description is now: "******".'); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										25
									
								
								app/Helpers/Csv/Specifix/SpecifixInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/Helpers/Csv/Specifix/SpecifixInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| <?php | ||||
| namespace FireflyIII\Helpers\Csv\Specifix; | ||||
|  | ||||
| /** | ||||
|  * Interface SpecifixInterface | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv\Specifix | ||||
|  */ | ||||
| interface SpecifixInterface | ||||
| { | ||||
|     /** | ||||
|      * Implement bank and locale related fixes. | ||||
|      */ | ||||
|     public function fix(); | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      */ | ||||
|     public function setData($data); | ||||
|  | ||||
|     /** | ||||
|      * @param array $row | ||||
|      */ | ||||
|     public function setRow($row); | ||||
| } | ||||
							
								
								
									
										194
									
								
								app/Helpers/Csv/Wizard.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								app/Helpers/Csv/Wizard.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,194 @@ | ||||
| <?php | ||||
| namespace FireflyIII\Helpers\Csv; | ||||
|  | ||||
| use Auth; | ||||
| use Config; | ||||
| use Crypt; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Helpers\Csv\Mapper\MapperInterface; | ||||
| use League\Csv\Reader; | ||||
| use Log; | ||||
| use ReflectionException; | ||||
| use Session; | ||||
|  | ||||
| /** | ||||
|  * Class Wizard | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv | ||||
|  */ | ||||
| class Wizard implements WizardInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Reader $reader | ||||
|      * @param array  $map | ||||
|      * @param bool   $hasHeaders | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getMappableValues($reader, array $map, $hasHeaders) | ||||
|     { | ||||
|         $values = []; | ||||
|         /* | ||||
|          * Loop over the CSV and collect mappable data: | ||||
|          */ | ||||
|         $keys = array_keys($map); | ||||
|         foreach ($reader as $index => $row) { | ||||
|             if ($this->useRow($hasHeaders, $index)) { | ||||
|                 // collect all map values | ||||
|  | ||||
|                 foreach ($keys as $column) { | ||||
|                     $values[$column][] = $row[$column]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         /* | ||||
|          * Make each one unique. | ||||
|          */ | ||||
|         $values = $this->uniqueRecursive($values); | ||||
|  | ||||
|         return $values; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $roles | ||||
|      * @param mixed $map | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function processSelectedMapping(array $roles, $map) | ||||
|     { | ||||
|         $configRoles = Config::get('csv.roles'); | ||||
|         $maps        = []; | ||||
|  | ||||
|  | ||||
|         if (is_array($map)) { | ||||
|             foreach ($map as $index => $field) { | ||||
|                 if (isset($roles[$index])) { | ||||
|                     $name = $roles[$index]; | ||||
|                     if ($configRoles[$name]['mappable']) { | ||||
|                         $maps[$index] = $name; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $maps; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param mixed $input | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function processSelectedRoles($input) | ||||
|     { | ||||
|         $roles = []; | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Store all rows for each column: | ||||
|          */ | ||||
|         if (is_array($input)) { | ||||
|             foreach ($input as $index => $role) { | ||||
|                 if ($role != '_ignore') { | ||||
|                     $roles[$index] = $role; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $roles; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $fields | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function sessionHasValues(array $fields) | ||||
|     { | ||||
|         foreach ($fields as $field) { | ||||
|             if (!Session::has($field)) { | ||||
|                 Log::error('Session is missing field: ' . $field); | ||||
|  | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $map | ||||
|      * | ||||
|      * @return array | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function showOptions(array $map) | ||||
|     { | ||||
|         $options = []; | ||||
|         foreach ($map as $index => $columnRole) { | ||||
|  | ||||
|             $mapper = Config::get('csv.roles.' . $columnRole . '.mapper'); | ||||
|             if (is_null($mapper)) { | ||||
|                 throw new FireflyException('Cannot map field of type "' . $columnRole . '".'); | ||||
|             } | ||||
|             $class = 'FireflyIII\Helpers\Csv\Mapper\\' . $mapper; | ||||
|             try { | ||||
|                 /** @var MapperInterface $mapObject */ | ||||
|                 $mapObject = app($class); | ||||
|             } catch (ReflectionException $e) { | ||||
|                 throw new FireflyException('Column "' . $columnRole . '" cannot be mapped because class ' . $mapper . ' does not exist.'); | ||||
|             } | ||||
|             $set             = $mapObject->getMap(); | ||||
|             $options[$index] = $set; | ||||
|         } | ||||
|  | ||||
|         return $options; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $path | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function storeCsvFile($path) | ||||
|     { | ||||
|         $time             = str_replace(' ', '-', microtime()); | ||||
|         $fileName         = 'csv-upload-' . Auth::user()->id . '-' . $time . '.csv.encrypted'; | ||||
|         $fullPath         = storage_path('upload') . DIRECTORY_SEPARATOR . $fileName; | ||||
|         $content          = file_get_contents($path); | ||||
|         $contentEncrypted = Crypt::encrypt($content); | ||||
|         file_put_contents($fullPath, $contentEncrypted); | ||||
|  | ||||
|         return $fullPath; | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param bool $hasHeaders | ||||
|      * @param int  $index | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function useRow($hasHeaders, $index) | ||||
|     { | ||||
|         return ($hasHeaders && $index > 1) || !$hasHeaders; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $array | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function uniqueRecursive(array $array) | ||||
|     { | ||||
|         foreach ($array as $column => $found) { | ||||
|             $array[$column] = array_unique($found); | ||||
|         } | ||||
|  | ||||
|         return $array; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										59
									
								
								app/Helpers/Csv/WizardInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								app/Helpers/Csv/WizardInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Csv; | ||||
|  | ||||
| use League\Csv\Reader; | ||||
|  | ||||
| /** | ||||
|  * Interface WizardInterface | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Csv | ||||
|  */ | ||||
| interface WizardInterface | ||||
| { | ||||
|     /** | ||||
|      * @param Reader $reader | ||||
|      * @param array  $map | ||||
|      * @param bool   $hasHeaders | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getMappableValues($reader, array $map, $hasHeaders); | ||||
|  | ||||
|     /** | ||||
|      * @param array $roles | ||||
|      * @param mixed $map | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function processSelectedMapping(array $roles, $map); | ||||
|  | ||||
|     /** | ||||
|      * @param mixed $input | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function processSelectedRoles($input); | ||||
|  | ||||
|     /** | ||||
|      * @param array $fields | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function sessionHasValues(array $fields); | ||||
|  | ||||
|     /** | ||||
|      * @param array $map | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function showOptions(array $map); | ||||
|  | ||||
|     /** | ||||
|      * @param $path | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function storeCsvFile($path); | ||||
|  | ||||
| } | ||||
| @@ -37,10 +37,12 @@ class Help implements HelpInterface | ||||
|      */ | ||||
|     public function getFromGithub($route) | ||||
|     { | ||||
|         $uri     = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/en/' . e($route) . '.md'; | ||||
|         $content = [ | ||||
|         $uri        = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/en/' . e($route) . '.md'; | ||||
|         $routeIndex = str_replace('.', '-', $route); | ||||
|         $title      = trans('help.' . $routeIndex); | ||||
|         $content    = [ | ||||
|             'text'  => '<p>There is no help for this route!</p>', | ||||
|             'title' => $route, | ||||
|             'title' => $title, | ||||
|         ]; | ||||
|         try { | ||||
|             $content['text'] = file_get_contents($uri); | ||||
| @@ -69,6 +71,18 @@ class Help implements HelpInterface | ||||
|         return Route::has($route); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @param $route | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function inCache($route) | ||||
|     { | ||||
|         return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
| @@ -82,16 +96,4 @@ class Help implements HelpInterface | ||||
|         Cache::put('help.' . $route . '.text', $content['text'], 10080); // a week. | ||||
|         Cache::put('help.' . $route . '.title', $content['title'], 10080); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @param $route | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function inCache($route) | ||||
|     { | ||||
|         return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,8 +2,8 @@ | ||||
|  | ||||
| namespace FireflyIII\Helpers\Report; | ||||
|  | ||||
| use App; | ||||
| use Carbon\Carbon; | ||||
| use DB; | ||||
| use FireflyIII\Helpers\Collection\Account as AccountCollection; | ||||
| use FireflyIII\Helpers\Collection\Balance; | ||||
| use FireflyIII\Helpers\Collection\BalanceEntry; | ||||
| @@ -20,6 +20,9 @@ use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Bill; | ||||
| use FireflyIII\Models\Budget as BudgetModel; | ||||
| use FireflyIII\Models\LimitRepetition; | ||||
| use FireflyIII\Models\Tag; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class ReportHelper | ||||
| @@ -44,47 +47,150 @@ class ReportHelper implements ReportHelperInterface | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return CategoryCollection | ||||
|      */ | ||||
|     public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts) | ||||
|     { | ||||
|         $object = new CategoryCollection; | ||||
|  | ||||
|         /** | ||||
|          * GET CATEGORIES: | ||||
|          */ | ||||
|         /** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */ | ||||
|         $repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface'); | ||||
|  | ||||
|         $set = $repository->spentForAccountsPerMonth($accounts, $start, $end); | ||||
|         foreach ($set as $category) { | ||||
|             $object->addCategory($category); | ||||
|         } | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function listOfMonths(Carbon $date) | ||||
|     { | ||||
|  | ||||
|         $start  = clone $date; | ||||
|         $end    = Carbon::now(); | ||||
|         $months = []; | ||||
|         while ($start <= $end) { | ||||
|             $year = $start->year; | ||||
|  | ||||
|             if (!isset($months[$year])) { | ||||
|                 $months[$year] = [ | ||||
|                     'start'  => Carbon::createFromDate($year, 1, 1)->format('Y-m-d'), | ||||
|                     'end'    => Carbon::createFromDate($year, 12, 31)->format('Y-m-d'), | ||||
|                     'months' => [], | ||||
|                 ]; | ||||
|             } | ||||
|  | ||||
|             $currentEnd = clone $start; | ||||
|             $currentEnd->endOfMonth(); | ||||
|             $months[$year]['months'][] = [ | ||||
|                 'formatted' => $start->formatLocalized('%B %Y'), | ||||
|                 'start'     => $start->format('Y-m-d'), | ||||
|                 'end'       => $currentEnd->format('Y-m-d'), | ||||
|                 'month'     => $start->month, | ||||
|                 'year'      => $year, | ||||
|             ]; | ||||
|             $start->addMonth(); | ||||
|         } | ||||
|  | ||||
|         return $months; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method generates a full report for the given period on all | ||||
|      * the users asset and cash accounts. | ||||
|      * given accounts | ||||
|      * | ||||
|      * @param Carbon $date | ||||
|      * @param Carbon $end | ||||
|      * @param        $shared | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return AccountCollection | ||||
|      */ | ||||
|     public function getAccountReport(Carbon $date, Carbon $end, $shared) | ||||
|     public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts) | ||||
|     { | ||||
|         $startAmount = '0'; | ||||
|         $endAmount   = '0'; | ||||
|         $diff        = '0'; | ||||
|         $ids         = $accounts->pluck('id')->toArray(); | ||||
|  | ||||
|         $yesterday = clone $start; | ||||
|         $yesterday->subDay(); | ||||
|  | ||||
|         bcscale(2); | ||||
|  | ||||
|         // get balances for start. | ||||
|         $startSet = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id') | ||||
|                            ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                            ->whereIn('accounts.id', $ids) | ||||
|                            ->whereNull('transaction_journals.deleted_at') | ||||
|                            ->whereNull('transactions.deleted_at') | ||||
|                            ->where('transaction_journals.date', '<=', $yesterday->format('Y-m-d')) | ||||
|                            ->groupBy('accounts.id') | ||||
|                            ->get(['accounts.id', DB::Raw('SUM(`transactions`.`amount`) as `balance`')]); | ||||
|  | ||||
|         // and for end: | ||||
|         $endSet = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id') | ||||
|                          ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                          ->whereIn('accounts.id', $ids) | ||||
|                          ->whereNull('transaction_journals.deleted_at') | ||||
|                          ->whereNull('transactions.deleted_at') | ||||
|                          ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) | ||||
|                          ->groupBy('accounts.id') | ||||
|                          ->get(['accounts.id', DB::Raw('SUM(`transactions`.`amount`) as `balance`')]); | ||||
|  | ||||
|  | ||||
|         $accounts = $this->query->getAllAccounts($date, $end, $shared); | ||||
|         $start    = 0; | ||||
|         $end      = 0; | ||||
|         $diff     = 0; | ||||
|  | ||||
|         // remove cash account, if any: | ||||
|         $accounts = $accounts->filter( | ||||
|             function (Account $account) { | ||||
|                 if ($account->accountType->type != 'Cash account') { | ||||
|                     return $account; | ||||
|         $accounts->each( | ||||
|             function (Account $account) use ($startSet, $endSet) { | ||||
|                 /** | ||||
|                  * The balance for today always incorporates transactions | ||||
|                  * made on today. So to get todays "start" balance, we sub one | ||||
|                  * day. | ||||
|                  */ | ||||
|                 // | ||||
|                 $currentStart = $startSet->filter( | ||||
|                     function (Account $entry) use ($account) { | ||||
|                         return $account->id == $entry->id; | ||||
|                     } | ||||
|                 ); | ||||
|                 if ($currentStart->first()) { | ||||
|                     $account->startBalance = $currentStart->first()->balance; | ||||
|                 } | ||||
|  | ||||
|                 return null; | ||||
|                 $currentEnd = $endSet->filter( | ||||
|                     function (Account $entry) use ($account) { | ||||
|                         return $account->id == $entry->id; | ||||
|                     } | ||||
|                 ); | ||||
|                 if ($currentEnd->first()) { | ||||
|                     $account->endBalance = $currentEnd->first()->balance; | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|  | ||||
|         // summarize: | ||||
|         foreach ($accounts as $account) { | ||||
|             $start += $account->startBalance; | ||||
|             $end += $account->endBalance; | ||||
|             $diff += ($account->endBalance - $account->startBalance); | ||||
|             $startAmount = bcadd($startAmount, $account->startBalance); | ||||
|             $endAmount   = bcadd($endAmount, $account->endBalance); | ||||
|             $diff        = bcadd($diff, bcsub($account->endBalance, $account->startBalance)); | ||||
|         } | ||||
|  | ||||
|         $object = new AccountCollection; | ||||
|         $object->setStart($start); | ||||
|         $object->setEnd($end); | ||||
|         $object->setStart($startAmount); | ||||
|         $object->setEnd($endAmount); | ||||
|         $object->setDifference($diff); | ||||
|         $object->setAccounts($accounts); | ||||
|  | ||||
| @@ -92,37 +198,149 @@ class ReportHelper implements ReportHelperInterface | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a full report on the users incomes during the period for the given accounts. | ||||
|      * | ||||
|      * The balance report contains a Balance object which in turn contains: | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * A BalanceHeader object which contains all relevant user asset accounts for the report. | ||||
|      * @return Income | ||||
|      */ | ||||
|     public function getIncomeReport($start, $end, Collection $accounts) | ||||
|     { | ||||
|         $object = new Income; | ||||
|         $set    = $this->query->income($accounts, $start, $end); | ||||
|  | ||||
|         foreach ($set as $entry) { | ||||
|             $object->addToTotal($entry->journalAmount); | ||||
|             $object->addOrCreateIncome($entry); | ||||
|         } | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a full report on the users expenses during the period for a list of accounts. | ||||
|      * | ||||
|      * A number of BalanceLine objects, which hold: | ||||
|      * - A budget | ||||
|      * - A number of BalanceEntry objects. | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * The BalanceEntry object holds: | ||||
|      *   - The same budget (again) | ||||
|      *   - A user asset account as mentioned in the BalanceHeader | ||||
|      *   - The amount of money spent on the budget by the user asset account | ||||
|      * @return Expense | ||||
|      */ | ||||
|     public function getExpenseReport($start, $end, Collection $accounts) | ||||
|     { | ||||
|         $object = new Expense; | ||||
|         $set    = $this->query->expense($accounts, $start, $end); | ||||
|  | ||||
|         foreach ($set as $entry) { | ||||
|             $object->addToTotal($entry->journalAmount); // can be positive, if it's a transfer | ||||
|             $object->addOrCreateExpense($entry); | ||||
|         } | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * @return BudgetCollection | ||||
|      */ | ||||
|     public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts) | ||||
|     { | ||||
|         $object = new BudgetCollection; | ||||
|         /** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */ | ||||
|         $repository     = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); | ||||
|         $set            = $repository->getBudgets(); | ||||
|         $allRepetitions = $repository->getAllBudgetLimitRepetitions($start, $end); | ||||
|         $allTotalSpent  = $repository->spentAllPerDayForAccounts($accounts, $start, $end); | ||||
|         bcscale(2); | ||||
|  | ||||
|         foreach ($set as $budget) { | ||||
|  | ||||
|             $repetitions = $allRepetitions->filter( | ||||
|                 function (LimitRepetition $rep) use ($budget) { | ||||
|                     return $rep->budget_id == $budget->id; | ||||
|                 } | ||||
|             ); | ||||
|             $totalSpent  = isset($allTotalSpent[$budget->id]) ? $allTotalSpent[$budget->id] : []; | ||||
|  | ||||
|             // no repetition(s) for this budget: | ||||
|             if ($repetitions->count() == 0) { | ||||
|                 $spent      = array_sum($totalSpent); | ||||
|                 $budgetLine = new BudgetLine; | ||||
|                 $budgetLine->setBudget($budget); | ||||
|                 $budgetLine->setOverspent($spent); | ||||
|                 $object->addOverspent($spent); | ||||
|                 $object->addBudgetLine($budgetLine); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // one or more repetitions for budget: | ||||
|             /** @var LimitRepetition $repetition */ | ||||
|             foreach ($repetitions as $repetition) { | ||||
|                 $budgetLine = new BudgetLine; | ||||
|                 $budgetLine->setBudget($budget); | ||||
|                 $budgetLine->setRepetition($repetition); | ||||
|                 $expenses = $this->getSumOfRange($start, $end, $totalSpent); | ||||
|  | ||||
|                 // 200 en -100 is 100, vergeleken met 0 === 1 | ||||
|                 // 200 en -200 is 0, vergeleken met 0 === 0 | ||||
|                 // 200 en -300 is -100, vergeleken met 0 === -1 | ||||
|  | ||||
|                 $left      = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? bcadd($repetition->amount, $expenses) : 0; | ||||
|                 $spent     = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? $expenses : '0'; | ||||
|                 $overspent = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $repetition->amount); | ||||
|  | ||||
|                 $budgetLine->setLeft($left); | ||||
|                 $budgetLine->setSpent($spent); | ||||
|                 $budgetLine->setOverspent($overspent); | ||||
|                 $budgetLine->setBudgeted($repetition->amount); | ||||
|  | ||||
|                 $object->addBudgeted($repetition->amount); | ||||
|                 $object->addSpent($spent); | ||||
|                 $object->addLeft($left); | ||||
|                 $object->addOverspent($overspent); | ||||
|                 $object->addBudgetLine($budgetLine); | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         // stuff outside of budgets: | ||||
|         $noBudget   = $repository->getWithoutBudgetSum($start, $end); | ||||
|         $budgetLine = new BudgetLine; | ||||
|         $budgetLine->setOverspent($noBudget); | ||||
|         $budgetLine->setSpent($noBudget); | ||||
|         $object->addOverspent($noBudget); | ||||
|         $object->addBudgetLine($budgetLine); | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Balance | ||||
|      */ | ||||
|     public function getBalanceReport(Carbon $start, Carbon $end, $shared) | ||||
|     public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts) | ||||
|     { | ||||
|         $repository    = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); | ||||
|         $tagRepository = App::make('FireflyIII\Repositories\Tag\TagRepositoryInterface'); | ||||
|         $balance       = new Balance; | ||||
|         /** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */ | ||||
|         $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); | ||||
|  | ||||
|         /** @var \FireflyIII\Repositories\Tag\TagRepositoryInterface $tagRepository */ | ||||
|         $tagRepository = app('FireflyIII\Repositories\Tag\TagRepositoryInterface'); | ||||
|  | ||||
|         $balance = new Balance; | ||||
|  | ||||
|         // build a balance header: | ||||
|         $header = new BalanceHeader; | ||||
|  | ||||
|         $accounts = $this->query->getAllAccounts($start, $end, $shared); | ||||
|         $budgets  = $repository->getBudgets(); | ||||
|         $header    = new BalanceHeader; | ||||
|         $budgets   = $repository->getBudgetsAndLimitsInRange($start, $end); | ||||
|         $spentData = $repository->spentPerBudgetPerAccount($budgets, $accounts, $start, $end); | ||||
|         foreach ($accounts as $account) { | ||||
|             $header->addAccount($account); | ||||
|         } | ||||
| @@ -132,18 +350,21 @@ class ReportHelper implements ReportHelperInterface | ||||
|             $line = new BalanceLine; | ||||
|             $line->setBudget($budget); | ||||
|  | ||||
|             // get budget amount for current period: | ||||
|             $rep = $repository->getCurrentRepetition($budget, $start); | ||||
|             $line->setRepetition($rep); | ||||
|  | ||||
|             // loop accounts: | ||||
|             foreach ($accounts as $account) { | ||||
|                 $balanceEntry = new BalanceEntry; | ||||
|                 $balanceEntry->setAccount($account); | ||||
|  | ||||
|                 // get spent: | ||||
|                 $spent = $this->query->spentInBudgetCorrected($account, $budget, $start, $end); // I think shared is irrelevant. | ||||
|  | ||||
|                 $entry = $spentData->filter( | ||||
|                     function (TransactionJournal $model) use ($budget, $account) { | ||||
|                         return $model->account_id == $account->id && $model->budget_id == $budget->id; | ||||
|                     } | ||||
|                 ); | ||||
|                 $spent = 0; | ||||
|                 if (!is_null($entry->first())) { | ||||
|                     $spent = $entry->first()->spent; | ||||
|                 } | ||||
|                 $balanceEntry->setSpent($spent); | ||||
|                 $line->addBalanceEntry($balanceEntry); | ||||
|             } | ||||
| @@ -153,18 +374,36 @@ class ReportHelper implements ReportHelperInterface | ||||
|  | ||||
|         // then a new line for without budget. | ||||
|         // and one for the tags: | ||||
|         // and one for "left unbalanced". | ||||
|         $empty    = new BalanceLine; | ||||
|         $tags     = new BalanceLine; | ||||
|         $diffLine = new BalanceLine; | ||||
|         $tagsLeft = $tagRepository->allCoveredByBalancingActs($accounts, $start, $end); | ||||
|  | ||||
|         $tags->setRole(BalanceLine::ROLE_TAGROLE); | ||||
|         $diffLine->setRole(BalanceLine::ROLE_DIFFROLE); | ||||
|  | ||||
|         foreach ($accounts as $account) { | ||||
|             $spent = $this->query->spentNoBudget($account, $start, $end); | ||||
|             $left  = $tagRepository->coveredByBalancingActs($account, $start, $end); | ||||
|             $entry = $spentData->filter( | ||||
|                 function (TransactionJournal $model) use ($budget, $account) { | ||||
|                     return $model->account_id == $account->id && is_null($model->budget_id); | ||||
|                 } | ||||
|             ); | ||||
|             $spent = 0; | ||||
|             if (!is_null($entry->first())) { | ||||
|                 $spent = $entry->first()->spent; | ||||
|             } | ||||
|             $leftEntry = $tagsLeft->filter( | ||||
|                 function (Tag $tag) use ($account) { | ||||
|                     return $tag->account_id == $account->id; | ||||
|                 } | ||||
|             ); | ||||
|             $left      = 0; | ||||
|             if (!is_null($leftEntry->first())) { | ||||
|                 $left = $leftEntry->first()->sum; | ||||
|             } | ||||
|             bcscale(2); | ||||
|             $diff = bcsub($spent, $left); | ||||
|             $diff = bcadd($spent, $left); | ||||
|  | ||||
|             // budget | ||||
|             $budgetEntry = new BalanceEntry; | ||||
| @@ -199,17 +438,20 @@ class ReportHelper implements ReportHelperInterface | ||||
|      * This method generates a full report for the given period on all | ||||
|      * the users bills and their payments. | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * Excludes bills which have not had a payment on the mentioned accounts. | ||||
|      * | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return BillCollection | ||||
|      */ | ||||
|     public function getBillReport(Carbon $start, Carbon $end, $shared) | ||||
|     public function getBillReport(Carbon $start, Carbon $end, Collection $accounts) | ||||
|     { | ||||
|         /** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */ | ||||
|         $repository = App::make('FireflyIII\Repositories\Bill\BillRepositoryInterface'); | ||||
|         $bills      = $repository->getBills(); | ||||
|         $repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface'); | ||||
|         $bills      = $repository->getBillsForAccounts($accounts); | ||||
|         $journals   = $repository->getAllJournalsInRange($bills, $start, $end); | ||||
|         $collection = new BillCollection; | ||||
|  | ||||
|         /** @var Bill $bill */ | ||||
| @@ -222,16 +464,17 @@ class ReportHelper implements ReportHelperInterface | ||||
|  | ||||
|             // is hit in period? | ||||
|             bcscale(2); | ||||
|             $set = $repository->getJournalsInRange($bill, $start, $end); | ||||
|             if ($set->count() == 0) { | ||||
|                 $billLine->setHit(false); | ||||
|             } else { | ||||
|                 $billLine->setHit(true); | ||||
|                 $amount = '0'; | ||||
|                 foreach ($set as $entry) { | ||||
|                     $amount = bcadd($amount, $entry->amount); | ||||
|  | ||||
|             $entry = $journals->filter( | ||||
|                 function (TransactionJournal $journal) use ($bill) { | ||||
|                     return $journal->bill_id == $bill->id; | ||||
|                 } | ||||
|                 $billLine->setAmount($amount); | ||||
|             ); | ||||
|             if (!is_null($entry->first())) { | ||||
|                 $billLine->setAmount($entry->first()->journalAmount); | ||||
|                 $billLine->setHit(true); | ||||
|             } else { | ||||
|                 $billLine->setHit(false); | ||||
|             } | ||||
|  | ||||
|             $collection->addBill($billLine); | ||||
| @@ -239,165 +482,33 @@ class ReportHelper implements ReportHelperInterface | ||||
|         } | ||||
|  | ||||
|         return $collection; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * Take the array as returned by SingleCategoryRepositoryInterface::spentPerDay and SingleCategoryRepositoryInterface::earnedByDay | ||||
|      * and sum up everything in the array in the given range. | ||||
|      * | ||||
|      * @return BudgetCollection | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param array  $array | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getBudgetReport(Carbon $start, Carbon $end, $shared) | ||||
|     protected function getSumOfRange(Carbon $start, Carbon $end, array $array) | ||||
|     { | ||||
|         $object = new BudgetCollection; | ||||
|         /** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */ | ||||
|         $repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); | ||||
|         $set        = $repository->getBudgets(); | ||||
|         bcscale(2); | ||||
|         $sum          = '0'; | ||||
|         $currentStart = clone $start; // to not mess with the original one | ||||
|         $currentEnd   = clone $end; // to not mess with the original one | ||||
|  | ||||
|         foreach ($set as $budget) { | ||||
|  | ||||
|             $repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end); | ||||
|  | ||||
|             // no repetition(s) for this budget: | ||||
|             if ($repetitions->count() == 0) { | ||||
|                 $spent      = $repository->spentInPeriodCorrected($budget, $start, $end, $shared); | ||||
|                 $budgetLine = new BudgetLine; | ||||
|                 $budgetLine->setBudget($budget); | ||||
|                 $budgetLine->setOverspent($spent); | ||||
|                 $object->addOverspent($spent); | ||||
|                 $object->addBudgetLine($budgetLine); | ||||
|                 continue; | ||||
|         while ($currentStart <= $currentEnd) { | ||||
|             $date = $currentStart->format('Y-m-d'); | ||||
|             if (isset($array[$date])) { | ||||
|                 $sum = bcadd($sum, $array[$date]); | ||||
|             } | ||||
|  | ||||
|             // one or more repetitions for budget: | ||||
|             /** @var LimitRepetition $repetition */ | ||||
|             foreach ($repetitions as $repetition) { | ||||
|                 $budgetLine = new BudgetLine; | ||||
|                 $budgetLine->setBudget($budget); | ||||
|                 $budgetLine->setRepetition($repetition); | ||||
|                 $expenses  = $repository->spentInPeriodCorrected($budget, $repetition->startdate, $repetition->enddate, $shared); | ||||
|                 $left      = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0; | ||||
|                 $spent     = $expenses > floatval($repetition->amount) ? 0 : $expenses; | ||||
|                 $overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0; | ||||
|  | ||||
|                 $budgetLine->setLeft($left); | ||||
|                 $budgetLine->setSpent($spent); | ||||
|                 $budgetLine->setOverspent($overspent); | ||||
|                 $budgetLine->setBudgeted($repetition->amount); | ||||
|  | ||||
|                 $object->addBudgeted($repetition->amount); | ||||
|                 $object->addSpent($spent); | ||||
|                 $object->addLeft($left); | ||||
|                 $object->addOverspent($overspent); | ||||
|                 $object->addBudgetLine($budgetLine); | ||||
|  | ||||
|             } | ||||
|  | ||||
|             $currentStart->addDay(); | ||||
|         } | ||||
|  | ||||
|         // stuff outside of budgets: | ||||
|         $noBudget   = $repository->getWithoutBudgetSum($start, $end); | ||||
|         $budgetLine = new BudgetLine; | ||||
|         $budgetLine->setOverspent($noBudget); | ||||
|         $object->addOverspent($noBudget); | ||||
|         $object->addBudgetLine($budgetLine); | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * | ||||
|      * @return CategoryCollection | ||||
|      */ | ||||
|     public function getCategoryReport(Carbon $start, Carbon $end, $shared) | ||||
|     { | ||||
|         $object = new CategoryCollection; | ||||
|  | ||||
|  | ||||
|         /** | ||||
|          * GET CATEGORIES: | ||||
|          */ | ||||
|         /** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */ | ||||
|         $repository = App::make('FireflyIII\Repositories\Category\CategoryRepositoryInterface'); | ||||
|         $set        = $repository->getCategories(); | ||||
|         foreach ($set as $category) { | ||||
|             $spent           = $repository->spentInPeriodCorrected($category, $start, $end, $shared); | ||||
|             $category->spent = $spent; | ||||
|             $object->addCategory($category); | ||||
|             $object->addTotal($spent); | ||||
|         } | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a full report on the users expenses during the period. | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * | ||||
|      * @return Expense | ||||
|      */ | ||||
|     public function getExpenseReport($start, $end, $shared) | ||||
|     { | ||||
|         $object = new Expense; | ||||
|         $set    = $this->query->expenseInPeriodCorrected($start, $end, $shared); | ||||
|         foreach ($set as $entry) { | ||||
|             $object->addToTotal($entry->amount); | ||||
|             $object->addOrCreateExpense($entry); | ||||
|         } | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a full report on the users incomes during the period. | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * | ||||
|      * @return Income | ||||
|      */ | ||||
|     public function getIncomeReport($start, $end, $shared) | ||||
|     { | ||||
|         $object = new Income; | ||||
|         $set    = $this->query->incomeInPeriodCorrected($start, $end, $shared); | ||||
|         foreach ($set as $entry) { | ||||
|             $object->addToTotal($entry->amount); | ||||
|             $object->addOrCreateIncome($entry); | ||||
|         } | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function listOfMonths(Carbon $date) | ||||
|     { | ||||
|  | ||||
|         $start  = clone $date; | ||||
|         $end    = Carbon::now(); | ||||
|         $months = []; | ||||
|         while ($start <= $end) { | ||||
|             $year            = $start->year; | ||||
|             $months[$year][] = [ | ||||
|                 'formatted' => $start->formatLocalized('%B %Y'), | ||||
|                 'month'     => $start->month, | ||||
|                 'year'      => $year, | ||||
|             ]; | ||||
|             $start->addMonth(); | ||||
|         } | ||||
|  | ||||
|         return $months; | ||||
|         return $sum; | ||||
|     } | ||||
| } | ||||
| @@ -10,6 +10,7 @@ use FireflyIII\Helpers\Collection\Budget as BudgetCollection; | ||||
| use FireflyIII\Helpers\Collection\Category as CategoryCollection; | ||||
| use FireflyIII\Helpers\Collection\Expense; | ||||
| use FireflyIII\Helpers\Collection\Income; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface ReportHelperInterface | ||||
| @@ -21,76 +22,78 @@ interface ReportHelperInterface | ||||
|  | ||||
|     /** | ||||
|      * This method generates a full report for the given period on all | ||||
|      * the users asset and cash accounts. | ||||
|      * given accounts | ||||
|      * | ||||
|      * @param Carbon  $date | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return AccountCollection | ||||
|      */ | ||||
|     public function getAccountReport(Carbon $date, Carbon $end, $shared); | ||||
|     public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts); | ||||
|  | ||||
|     /** | ||||
|      * This method generates a full report for the given period on all | ||||
|      * the users bills and their payments. | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * Excludes bills which have not had a payment on the mentioned accounts. | ||||
|      * | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return BillCollection | ||||
|      */ | ||||
|     public function getBillReport(Carbon $start, Carbon $end, $shared); | ||||
|     public function getBillReport(Carbon $start, Carbon $end, Collection $accounts); | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Balance | ||||
|      */ | ||||
|     public function getBalanceReport(Carbon $start, Carbon $end, $shared); | ||||
|     public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts); | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return BudgetCollection | ||||
|      */ | ||||
|     public function getBudgetReport(Carbon $start, Carbon $end, $shared); | ||||
|     public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts); | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return CategoryCollection | ||||
|      */ | ||||
|     public function getCategoryReport(Carbon $start, Carbon $end, $shared); | ||||
|     public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts); | ||||
|  | ||||
|     /** | ||||
|      * Get a full report on the users expenses during the period. | ||||
|      * Get a full report on the users expenses during the period for a list of accounts. | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Expense | ||||
|      */ | ||||
|     public function getExpenseReport($start, $end, $shared); | ||||
|     public function getExpenseReport($start, $end, Collection $accounts); | ||||
|  | ||||
|     /** | ||||
|      * Get a full report on the users incomes during the period. | ||||
|      * Get a full report on the users incomes during the period for the given accounts. | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Income | ||||
|      */ | ||||
|     public function getIncomeReport($start, $end, $shared); | ||||
|     public function getIncomeReport($start, $end, Collection $accounts); | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|   | ||||
| @@ -4,14 +4,10 @@ namespace FireflyIII\Helpers\Report; | ||||
|  | ||||
| use Auth; | ||||
| use Carbon\Carbon; | ||||
| use Crypt; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Budget; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use DB; | ||||
| use FireflyIII\Models\TransactionType; | ||||
| use Illuminate\Database\Query\JoinClause; | ||||
| use Illuminate\Support\Collection; | ||||
| use Steam; | ||||
|  | ||||
| /** | ||||
|  * Class ReportQuery | ||||
| @@ -20,264 +16,163 @@ use Steam; | ||||
|  */ | ||||
| class ReportQuery implements ReportQueryInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * See ReportQueryInterface::incomeInPeriodCorrected | ||||
|      * Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers) | ||||
|      * grouped by month like so: "2015-01" => '123.45' | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param bool   $includeShared | ||||
|      * | ||||
|      * @return Collection | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false) | ||||
|     public function spentPerMonth(Collection $accounts, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $query = $this->queryJournalsWithTransactions($start, $end); | ||||
|         if ($includeShared === false) { | ||||
|             $query->where( | ||||
|                 function (Builder $query) { | ||||
|                     $query->where( | ||||
|                         function (Builder $q) { // only get withdrawals not from a shared account | ||||
|                             $q->where('transaction_types.type', 'Withdrawal'); | ||||
|                             $q->where('acm_from.data', '!=', '"sharedAsset"'); | ||||
|                         } | ||||
|                     ); | ||||
|                     $query->orWhere( | ||||
|                         function (Builder $q) { // and transfers from a shared account. | ||||
|                             $q->where('transaction_types.type', 'Transfer'); | ||||
|                             $q->where('acm_to.data', '=', '"sharedAsset"'); | ||||
|                         } | ||||
|                     ); | ||||
|                 } | ||||
|             ); | ||||
|         } else { | ||||
|             $query->where('transaction_types.type', 'Withdrawal'); // any withdrawal is fine. | ||||
|         $ids   = $accounts->pluck('id')->toArray(); | ||||
|         $query = Auth::user()->transactionjournals() | ||||
|                      ->leftJoin( | ||||
|                          'transactions AS t_from', function (JoinClause $join) { | ||||
|                          $join->on('transaction_journals.id', '=', 't_from.transaction_journal_id')->where('t_from.amount', '<', 0); | ||||
|                      } | ||||
|                      ) | ||||
|                      ->leftJoin( | ||||
|                          'transactions AS t_to', function (JoinClause $join) { | ||||
|                          $join->on('transaction_journals.id', '=', 't_to.transaction_journal_id')->where('t_to.amount', '>', 0); | ||||
|                      } | ||||
|                      ) | ||||
|                      ->whereIn('t_from.account_id', $ids) | ||||
|                      ->whereNotIn('t_to.account_id', $ids) | ||||
|                      ->after($start) | ||||
|                      ->before($end) | ||||
|                      ->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]) | ||||
|                      ->groupBy('dateFormatted') | ||||
|                      ->get( | ||||
|                          [ | ||||
|                              DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") AS `dateFormatted`'), | ||||
|                              DB::Raw('SUM(`t_from`.`amount`) AS `sum`') | ||||
|                          ] | ||||
|                      ); | ||||
|         $array = []; | ||||
|         foreach ($query as $result) { | ||||
|             $array[$result->dateFormatted] = $result->sum; | ||||
|         } | ||||
|         $query->orderBy('transaction_journals.date'); | ||||
|  | ||||
|         // get everything | ||||
|         $data = $query->get( | ||||
|             ['transaction_journals.*', 'transaction_types.type', 'ac_to.name as name', 'ac_to.id as account_id', 'ac_to.encrypted as account_encrypted'] | ||||
|         ); | ||||
|         return $array; | ||||
|  | ||||
|         $data->each( | ||||
|             function (TransactionJournal $journal) { | ||||
|                 if (intval($journal->account_encrypted) == 1) { | ||||
|                     $journal->name = Crypt::decrypt($journal->name); | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         $data = $data->filter( | ||||
|             function (TransactionJournal $journal) { | ||||
|                 if ($journal->amount != 0) { | ||||
|                     return $journal; | ||||
|                 } | ||||
|  | ||||
|                 return null; | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a users accounts combined with various meta-data related to the start and end date. | ||||
|      * Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers) | ||||
|      * grouped by month like so: "2015-01" => '123.45' | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param bool   $includeShared | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function earnedPerMonth(Collection $accounts, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $ids   = $accounts->pluck('id')->toArray(); | ||||
|         $query = Auth::user()->transactionjournals() | ||||
|                      ->leftJoin( | ||||
|                          'transactions AS t_from', function (JoinClause $join) { | ||||
|                          $join->on('transaction_journals.id', '=', 't_from.transaction_journal_id')->where('t_from.amount', '<', 0); | ||||
|                      } | ||||
|                      ) | ||||
|                      ->leftJoin( | ||||
|                          'transactions AS t_to', function (JoinClause $join) { | ||||
|                          $join->on('transaction_journals.id', '=', 't_to.transaction_journal_id')->where('t_to.amount', '>', 0); | ||||
|                      } | ||||
|                      ) | ||||
|                      ->whereIn('t_to.account_id', $ids) | ||||
|                      ->whereNotIn('t_from.account_id', $ids) | ||||
|                      ->after($start) | ||||
|                      ->before($end) | ||||
|                      ->transactionTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]) | ||||
|                      ->groupBy('dateFormatted') | ||||
|                      ->get( | ||||
|                          [ | ||||
|                              DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") AS `dateFormatted`'), | ||||
|                              DB::Raw('SUM(`t_to`.`amount`) AS `sum`') | ||||
|                          ] | ||||
|                      ); | ||||
|         $array = []; | ||||
|         foreach ($query as $result) { | ||||
|             $array[$result->dateFormatted] = $result->sum; | ||||
|         } | ||||
|  | ||||
|         return $array; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method returns all the "in" transaction journals for the given account and given period. The amount | ||||
|      * is stored in "journalAmount". | ||||
|      * | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false) | ||||
|     public function income(Collection $accounts, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $query = Auth::user()->accounts()->orderBy('accounts.name', 'ASC') | ||||
|                      ->accountTypeIn(['Default account', 'Asset account', 'Cash account']); | ||||
|         if ($includeShared === false) { | ||||
|             $query->leftJoin( | ||||
|                 'account_meta', function (JoinClause $join) { | ||||
|                 $join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole'); | ||||
|             } | ||||
|             ) | ||||
|                   ->where( | ||||
|                       function (Builder $query) { | ||||
|  | ||||
|                           $query->where('account_meta.data', '!=', '"sharedAsset"'); | ||||
|                           $query->orWhereNull('account_meta.data'); | ||||
|  | ||||
|                       } | ||||
|                   ); | ||||
|         } | ||||
|         $set = $query->get(['accounts.*']); | ||||
|         $set->each( | ||||
|             function (Account $account) use ($start, $end) { | ||||
|                 /** | ||||
|                  * The balance for today always incorporates transactions | ||||
|                  * made on today. So to get todays "start" balance, we sub one | ||||
|                  * day. | ||||
|                  */ | ||||
|                 $yesterday = clone $start; | ||||
|                 $yesterday->subDay(); | ||||
|  | ||||
|                 /** @noinspection PhpParamsInspection */ | ||||
|                 $account->startBalance = Steam::balance($account, $yesterday); | ||||
|                 $account->endBalance   = Steam::balance($account, $end); | ||||
|             } | ||||
|         ); | ||||
|         $ids = $accounts->pluck('id')->toArray(); | ||||
|         $set = Auth::user()->transactionjournals() | ||||
|                    ->leftJoin( | ||||
|                        'transactions as t_from', function (JoinClause $join) { | ||||
|                        $join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0); | ||||
|                    } | ||||
|                    ) | ||||
|                    ->leftJoin( | ||||
|                        'transactions as t_to', function (JoinClause $join) { | ||||
|                        $join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0); | ||||
|                    } | ||||
|                    ) | ||||
|                    ->leftJoin('accounts', 't_from.account_id', '=', 'accounts.id') | ||||
|                    ->transactionTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]) | ||||
|                    ->before($end) | ||||
|                    ->after($start) | ||||
|                    ->whereIn('t_to.account_id', $ids) | ||||
|                    ->whereNotIn('t_from.account_id', $ids) | ||||
|                    ->get(['transaction_journals.*', 't_to.amount as journalAmount', 'accounts.id as account_id', 'accounts.name as account_name']); | ||||
|  | ||||
|         return $set; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results | ||||
|      * will simply list the transaction journals only. This should allow any follow up counting to be accurate with | ||||
|      * regards to tags. | ||||
|      * This method returns all the "out" transaction journals for the given account and given period. The amount | ||||
|      * is stored in "journalAmount". | ||||
|      * | ||||
|      * This method returns all "income" journals in a certain period, which are both transfers from a shared account | ||||
|      * and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does | ||||
|      * not group and returns different fields. | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param bool   $includeShared | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false) | ||||
|     public function expense(Collection $accounts, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $query = $this->queryJournalsWithTransactions($start, $end); | ||||
|         if ($includeShared === false) { | ||||
|             // only get deposits not to a shared account | ||||
|             // and transfers to a shared account. | ||||
|             $query->where( | ||||
|                 function (Builder $query) { | ||||
|                     $query->where( | ||||
|                         function (Builder $q) { | ||||
|                             $q->where('transaction_types.type', 'Deposit'); | ||||
|                             $q->where('acm_to.data', '!=', '"sharedAsset"'); | ||||
|                         } | ||||
|                     ); | ||||
|                     $query->orWhere( | ||||
|                         function (Builder $q) { | ||||
|                             $q->where('transaction_types.type', 'Transfer'); | ||||
|                             $q->where('acm_from.data', '=', '"sharedAsset"'); | ||||
|                         } | ||||
|                     ); | ||||
|                 } | ||||
|             ); | ||||
|         } else { | ||||
|             // any deposit is fine. | ||||
|             $query->where('transaction_types.type', 'Deposit'); | ||||
|         } | ||||
|         $query->orderBy('transaction_journals.date'); | ||||
|         $ids = $accounts->pluck('id')->toArray(); | ||||
|         $set = Auth::user()->transactionjournals() | ||||
|                    ->leftJoin( | ||||
|                        'transactions as t_from', function (JoinClause $join) { | ||||
|                        $join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0); | ||||
|                    } | ||||
|                    ) | ||||
|                    ->leftJoin( | ||||
|                        'transactions as t_to', function (JoinClause $join) { | ||||
|                        $join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0); | ||||
|                    } | ||||
|                    ) | ||||
|                    ->leftJoin('accounts', 't_to.account_id', '=', 'accounts.id') | ||||
|                    ->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]) | ||||
|                    ->before($end) | ||||
|                    ->after($start) | ||||
|                    ->whereIn('t_from.account_id', $ids) | ||||
|                    ->whereNotIn('t_to.account_id', $ids) | ||||
|                    ->get(['transaction_journals.*', 't_from.amount as journalAmount', 'accounts.id as account_id', 'accounts.name as account_name']); | ||||
|  | ||||
|         // get everything | ||||
|         $data = $query->get( | ||||
|             ['transaction_journals.*', 'transaction_types.type', 'ac_from.name as name', 'ac_from.id as account_id', 'ac_from.encrypted as account_encrypted'] | ||||
|         ); | ||||
|  | ||||
|         $data->each( | ||||
|             function (TransactionJournal $journal) { | ||||
|                 if (intval($journal->account_encrypted) == 1) { | ||||
|                     $journal->name = Crypt::decrypt($journal->name); | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         $data = $data->filter( | ||||
|             function (TransactionJournal $journal) { | ||||
|                 if ($journal->amount != 0) { | ||||
|                     return $journal; | ||||
|                 } | ||||
|  | ||||
|                 return null; | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Covers tags | ||||
|      * | ||||
|      * @param Account $account | ||||
|      * @param Budget  $budget | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * | ||||
|      * @return float | ||||
|      */ | ||||
|     public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end) | ||||
|     { | ||||
|  | ||||
|         return floatval( | ||||
|                    Auth::user()->transactionjournals() | ||||
|                        ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                        ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                        ->transactionTypes(['Withdrawal']) | ||||
|                        ->where('transactions.account_id', $account->id) | ||||
|                        ->before($end) | ||||
|                        ->after($start) | ||||
|                        ->where('budget_transaction_journal.budget_id', $budget->id) | ||||
|                        ->get(['transaction_journals.*'])->sum('amount') | ||||
|                ) * -1; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param bool    $shared | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false) | ||||
|     { | ||||
|         return | ||||
|             Auth::user()->transactionjournals() | ||||
|                 ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                 ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                 ->transactionTypes(['Withdrawal']) | ||||
|                 ->where('transactions.account_id', $account->id) | ||||
|                 ->before($end) | ||||
|                 ->after($start) | ||||
|                 ->whereNull('budget_transaction_journal.budget_id')->get(['transaction_journals.*'])->sum('amount'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return Builder | ||||
|      */ | ||||
|     protected function queryJournalsWithTransactions(Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $query = TransactionJournal:: | ||||
|         leftJoin( | ||||
|             'transactions as t_from', function (JoinClause $join) { | ||||
|             $join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0); | ||||
|         } | ||||
|         ) | ||||
|                                    ->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id') | ||||
|                                    ->leftJoin( | ||||
|                                        'account_meta as acm_from', function (JoinClause $join) { | ||||
|                                        $join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole'); | ||||
|                                    } | ||||
|                                    ) | ||||
|                                    ->leftJoin( | ||||
|                                        'transactions as t_to', function (JoinClause $join) { | ||||
|                                        $join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0); | ||||
|                                    } | ||||
|                                    ) | ||||
|                                    ->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id') | ||||
|                                    ->leftJoin( | ||||
|                                        'account_meta as acm_to', function (JoinClause $join) { | ||||
|                                        $join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole'); | ||||
|                                    } | ||||
|                                    ) | ||||
|                                    ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); | ||||
|         $query->before($end)->after($start)->where('transaction_journals.user_id', Auth::user()->id); | ||||
|  | ||||
|         return $query; | ||||
|         return $set; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -3,8 +3,6 @@ | ||||
| namespace FireflyIII\Helpers\Report; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Budget; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
| @@ -16,66 +14,52 @@ interface ReportQueryInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * See ReportQueryInterface::incomeInPeriodCorrected | ||||
|      * Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers) | ||||
|      * grouped by month like so: "2015-01" => '123.45' | ||||
|      * | ||||
|      * This method returns all "expense" journals in a certain period, which are both transfers to a shared account | ||||
|      * and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does | ||||
|      * not group and returns different fields. | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param bool   $includeShared | ||||
|      * | ||||
|      * @return Collection | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false); | ||||
|     public function earnedPerMonth(Collection $accounts, Carbon $start, Carbon $end); | ||||
|  | ||||
|     /** | ||||
|      * Get a users accounts combined with various meta-data related to the start and end date. | ||||
|      * This method returns all the "out" transaction journals for the given account and given period. The amount | ||||
|      * is stored in "journalAmount". | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param bool   $includeShared | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false); | ||||
|     public function expense(Collection $accounts, Carbon $start, Carbon $end); | ||||
|  | ||||
|     /** | ||||
|      * This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results | ||||
|      * will simply list the transaction journals only. This should allow any follow up counting to be accurate with | ||||
|      * regards to tags. | ||||
|      * This method returns all the "in" transaction journals for the given account and given period. The amount | ||||
|      * is stored in "journalAmount". | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param bool   $includeShared | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false); | ||||
|     public function income(Collection $accounts, Carbon $start, Carbon $end); | ||||
|  | ||||
|     /** | ||||
|      * Covers tags as well. | ||||
|      * Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers) | ||||
|      * grouped by month like so: "2015-01" => '123.45' | ||||
|      * | ||||
|      * @param Account $account | ||||
|      * @param Budget  $budget | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return float | ||||
|      * @return array | ||||
|      */ | ||||
|     public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end); | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param bool    $shared | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false); | ||||
|     public function spentPerMonth(Collection $accounts, Carbon $start, Carbon $end); | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -3,12 +3,12 @@ | ||||
| use Auth; | ||||
| use Carbon\Carbon; | ||||
| use Config; | ||||
| use ExpandedForm; | ||||
| use FireflyIII\Http\Requests\AccountFormRequest; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; | ||||
| use Input; | ||||
| use Preferences; | ||||
| use Redirect; | ||||
| use Session; | ||||
| use Steam; | ||||
| use URL; | ||||
| @@ -38,6 +38,8 @@ class AccountController extends Controller | ||||
|      */ | ||||
|     public function create($what = 'asset') | ||||
|     { | ||||
|  | ||||
|  | ||||
|         $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); | ||||
|         $subTitle     = trans('firefly.make_new_' . $what . '_account'); | ||||
|  | ||||
| @@ -54,51 +56,54 @@ class AccountController extends Controller | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param ARI     $repository | ||||
|      * @param Account $account | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function delete(Account $account) | ||||
|     public function delete(ARI $repository, Account $account) | ||||
|     { | ||||
|         $typeName = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type); | ||||
|         $subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]); | ||||
|         $typeName    = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type); | ||||
|         $subTitle    = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]); | ||||
|         $accountList = Expandedform::makeSelectList($repository->getAccounts([$account->accountType->type]), true); | ||||
|         unset($accountList[$account->id]); | ||||
|  | ||||
|         // put previous url in session | ||||
|         Session::put('accounts.delete.url', URL::previous()); | ||||
|         Session::flash('gaEventCategory', 'accounts'); | ||||
|         Session::flash('gaEventAction', 'delete-' . $typeName); | ||||
|  | ||||
|         return view('accounts.delete', compact('account', 'subTitle')); | ||||
|         return view('accounts.delete', compact('account', 'subTitle', 'accountList')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param Account                    $account | ||||
|      * @param ARI     $repository | ||||
|      * @param Account $account | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function destroy(AccountRepositoryInterface $repository, Account $account) | ||||
|     public function destroy(ARI $repository, Account $account) | ||||
|     { | ||||
|  | ||||
|         $type     = $account->accountType->type; | ||||
|         $typeName = Config::get('firefly.shortNamesByFullName.' . $type); | ||||
|         $name     = $account->name; | ||||
|         $moveTo   = Auth::user()->accounts()->find(intval(Input::get('move_account_before_delete'))); | ||||
|  | ||||
|         $repository->destroy($account); | ||||
|         $repository->destroy($account, $moveTo); | ||||
|  | ||||
|         Session::flash('success', trans('firefly.' . $typeName . '_deleted', ['name' => $name])); | ||||
|         Preferences::mark(); | ||||
|  | ||||
|         return Redirect::to(Session::get('accounts.delete.url')); | ||||
|         return redirect(Session::get('accounts.delete.url')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param Account                    $account | ||||
|      * @param ARI     $repository | ||||
|      * @param Account $account | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function edit(AccountRepositoryInterface $repository, Account $account) | ||||
|     public function edit(ARI $repository, Account $account) | ||||
|     { | ||||
|  | ||||
|         $what           = Config::get('firefly.shortNamesByFullName')[$account->accountType->type]; | ||||
| @@ -128,7 +133,7 @@ class AccountController extends Controller | ||||
|             'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'), | ||||
|             'openingBalanceDate'   => $openingBalance ? $openingBalance->date->format('Y-m-d') : null, | ||||
|             'openingBalance'       => $openingBalanceAmount, | ||||
|             'virtualBalance'       => floatval($account->virtual_balance) | ||||
|             'virtualBalance'       => round($account->virtual_balance, 2) | ||||
|         ]; | ||||
|         Session::flash('preFilled', $preFilled); | ||||
|         Session::flash('gaEventCategory', 'accounts'); | ||||
| @@ -138,28 +143,31 @@ class AccountController extends Controller | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param ARI                        $repository | ||||
|      * @param                            $what | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function index(AccountRepositoryInterface $repository, $what) | ||||
|     public function index(ARI $repository, $what) | ||||
|     { | ||||
|         $subTitle     = trans('firefly.' . $what . '_accounts'); | ||||
|         $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); | ||||
|         $types        = Config::get('firefly.accountTypesByIdentifier.' . $what); | ||||
|         $accounts     = $repository->getAccounts($types); | ||||
|         // last activity: | ||||
|         /** | ||||
|          * HERE WE ARE | ||||
|          */ | ||||
|         $start = clone Session::get('start', Carbon::now()->startOfMonth()); | ||||
|         $start        = clone Session::get('start', Carbon::now()->startOfMonth()); | ||||
|         $end          = clone Session::get('end', Carbon::now()->endOfMonth()); | ||||
|         $start->subDay(); | ||||
|  | ||||
|         $ids           = $accounts->pluck('id')->toArray(); | ||||
|         $startBalances = Steam::balancesById($ids, $start); | ||||
|         $endBalances   = Steam::balancesById($ids, $end); | ||||
|         $activities    = Steam::getLastActivities($ids); | ||||
|  | ||||
|         $accounts->each( | ||||
|             function (Account $account) use ($start, $repository) { | ||||
|                 $account->lastActivityDate = $repository->getLastActivity($account); | ||||
|                 $account->startBalance     = Steam::balance($account, $start); | ||||
|                 $account->endBalance       = Steam::balance($account, clone Session::get('end', Carbon::now()->endOfMonth())); | ||||
|             function (Account $account) use ($activities, $startBalances, $endBalances) { | ||||
|                 $account->lastActivityDate = $this->isInArray($activities, $account->id); | ||||
|                 $account->startBalance     = $this->isInArray($startBalances, $account->id); | ||||
|                 $account->endBalance       = $this->isInArray($endBalances, $account->id); | ||||
|             } | ||||
|         ); | ||||
|  | ||||
| @@ -167,12 +175,12 @@ class AccountController extends Controller | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param Account                    $account | ||||
|      * @param ARI     $repository | ||||
|      * @param Account $account | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function show(AccountRepositoryInterface $repository, Account $account) | ||||
|     public function show(ARI $repository, Account $account) | ||||
|     { | ||||
|         $page         = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page')); | ||||
|         $subTitleIcon = Config::get('firefly.subTitlesByIdentifier.' . $account->accountType->type); | ||||
| @@ -186,26 +194,29 @@ class AccountController extends Controller | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AccountFormRequest         $request | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param AccountFormRequest $request | ||||
|      * @param ARI                $repository | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function store(AccountFormRequest $request, AccountRepositoryInterface $repository) | ||||
|     public function store(AccountFormRequest $request, ARI $repository) | ||||
|     { | ||||
|         $accountData = [ | ||||
|             'name'                   => $request->input('name'), | ||||
|             'accountType'            => $request->input('what'), | ||||
|             'virtualBalance'         => floatval($request->input('virtualBalance')), | ||||
|             'virtualBalance'         => round($request->input('virtualBalance'), 2), | ||||
|             'virtualBalanceCurrency' => intval($request->input('amount_currency_id_virtualBalance')), | ||||
|             'active'                 => true, | ||||
|             'user'                   => Auth::user()->id, | ||||
|             'iban'                   => $request->input('iban'), | ||||
|             'accountRole'            => $request->input('accountRole'), | ||||
|             'openingBalance'         => floatval($request->input('openingBalance')), | ||||
|             'openingBalance'         => round($request->input('openingBalance'), 2), | ||||
|             'openingBalanceDate'     => new Carbon((string)$request->input('openingBalanceDate')), | ||||
|             'openingBalanceCurrency' => intval($request->input('balance_currency_id')), | ||||
|             'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')), | ||||
|  | ||||
|         ]; | ||||
|         $account     = $repository->store($accountData); | ||||
|  | ||||
|         $account = $repository->store($accountData); | ||||
|  | ||||
|         Session::flash('success', 'New account "' . $account->name . '" stored!'); | ||||
|         Preferences::mark(); | ||||
| @@ -214,38 +225,38 @@ class AccountController extends Controller | ||||
|             // set value so create routine will not overwrite URL: | ||||
|             Session::put('accounts.create.fromStore', true); | ||||
|  | ||||
|             return Redirect::route('accounts.create')->withInput(); | ||||
|             return redirect(route('accounts.create', [$request->input('what')]))->withInput(); | ||||
|         } | ||||
|  | ||||
|         // redirect to previous URL. | ||||
|         return Redirect::to(Session::get('accounts.create.url')); | ||||
|  | ||||
|  | ||||
|         return redirect(Session::get('accounts.create.url')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AccountFormRequest         $request | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param Account                    $account | ||||
|      * @param AccountFormRequest $request | ||||
|      * @param ARI                $repository | ||||
|      * @param Account            $account | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function update(AccountFormRequest $request, AccountRepositoryInterface $repository, Account $account) | ||||
|     public function update(AccountFormRequest $request, ARI $repository, Account $account) | ||||
|     { | ||||
|  | ||||
|         $accountData = [ | ||||
|             'name'                   => $request->input('name'), | ||||
|             'active'                 => $request->input('active'), | ||||
|             'user'                   => Auth::user()->id, | ||||
|             'iban'                   => $request->input('iban'), | ||||
|             'accountRole'            => $request->input('accountRole'), | ||||
|             'virtualBalance'         => floatval($request->input('virtualBalance')), | ||||
|             'openingBalance'         => floatval($request->input('openingBalance')), | ||||
|             'virtualBalance'         => round($request->input('virtualBalance'), 2), | ||||
|             'openingBalance'         => round($request->input('openingBalance'), 2), | ||||
|             'openingBalanceDate'     => new Carbon((string)$request->input('openingBalanceDate')), | ||||
|             'openingBalanceCurrency' => intval($request->input('balance_currency_id')), | ||||
|             'ccType'                 => $request->input('ccType'), | ||||
|             'ccMonthlyPaymentDate'   => $request->input('ccMonthlyPaymentDate'), | ||||
|         ]; | ||||
|  | ||||
|  | ||||
|         $repository->update($account, $accountData); | ||||
|  | ||||
|         Session::flash('success', 'Account "' . $account->name . '" updated.'); | ||||
| @@ -255,12 +266,28 @@ class AccountController extends Controller | ||||
|             // set value so edit routine will not overwrite URL: | ||||
|             Session::put('accounts.edit.fromUpdate', true); | ||||
|  | ||||
|             return Redirect::route('accounts.edit', [$account->id])->withInput(['return_to_edit' => 1]); | ||||
|             return redirect(route('accounts.edit', [$account->id]))->withInput(['return_to_edit' => 1]); | ||||
|         } | ||||
|  | ||||
|         // redirect to previous URL. | ||||
|         return Redirect::to(Session::get('accounts.edit.url')); | ||||
|         return redirect(Session::get('accounts.edit.url')); | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param array $array | ||||
|      * @param       $entryId | ||||
|      * | ||||
|      * @return null|mixed | ||||
|      */ | ||||
|     protected function isInArray(array $array, $entryId) | ||||
|     { | ||||
|         if (isset($array[$entryId])) { | ||||
|             return $array[$entryId]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										174
									
								
								app/Http/Controllers/AttachmentController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								app/Http/Controllers/AttachmentController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Http\Controllers; | ||||
|  | ||||
|  | ||||
| use Crypt; | ||||
| use File; | ||||
| use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; | ||||
| use FireflyIII\Http\Requests\AttachmentFormRequest; | ||||
| use FireflyIII\Models\Attachment; | ||||
| use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; | ||||
| use Input; | ||||
| use Preferences; | ||||
| use Response; | ||||
| use Session; | ||||
| use URL; | ||||
| use View; | ||||
|  | ||||
| /** | ||||
|  * Class AttachmentController | ||||
|  * | ||||
|  * @package FireflyIII\Http\Controllers | ||||
|  */ | ||||
| class AttachmentController extends Controller | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         View::share('mainTitleIcon', 'fa-paperclip'); | ||||
|         View::share('title', trans('firefly.attachments')); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param Attachment $attachment | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function edit(Attachment $attachment) | ||||
|     { | ||||
|         $subTitleIcon = 'fa-pencil'; | ||||
|         $subTitle     = trans('firefly.edit_attachment', ['name' => $attachment->filename]); | ||||
|  | ||||
|         // put previous url in session if not redirect from store (not "return_to_edit"). | ||||
|         if (Session::get('attachments.edit.fromUpdate') !== true) { | ||||
|             Session::put('attachments.edit.url', URL::previous()); | ||||
|         } | ||||
|         Session::forget('attachments.edit.fromUpdate'); | ||||
|  | ||||
|         return view('attachments.edit', compact('attachment', 'subTitleIcon', 'subTitle')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Attachment $attachment | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function delete(Attachment $attachment) | ||||
|     { | ||||
|         $subTitle = trans('firefly.delete_attachment', ['name' => $attachment->filename]); | ||||
|  | ||||
|         // put previous url in session | ||||
|         Session::put('attachments.delete.url', URL::previous()); | ||||
|         Session::flash('gaEventCategory', 'attachments'); | ||||
|         Session::flash('gaEventAction', 'delete-attachment'); | ||||
|  | ||||
|         return view('attachments.delete', compact('attachment', 'subTitle')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AttachmentRepositoryInterface $repository | ||||
|      * @param Attachment                    $attachment | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function destroy(AttachmentRepositoryInterface $repository, Attachment $attachment) | ||||
|     { | ||||
|         $name = $attachment->filename; | ||||
|  | ||||
|         $repository->destroy($attachment); | ||||
|  | ||||
|         Session::flash('success', trans('firefly.attachment_deleted', ['name' => $name])); | ||||
|         Preferences::mark(); | ||||
|  | ||||
|         return redirect(Session::get('attachments.delete.url')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Attachment                $attachment | ||||
|      * @param AttachmentHelperInterface $helper | ||||
|      */ | ||||
|     public function download(Attachment $attachment, AttachmentHelperInterface $helper) | ||||
|     { | ||||
|  | ||||
|         $file = $helper->getAttachmentLocation($attachment); | ||||
|         if (file_exists($file)) { | ||||
|  | ||||
|             $quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\')); | ||||
|  | ||||
|             header('Content-Description: File Transfer'); | ||||
|             header('Content-Type: application/octet-stream'); | ||||
|             header('Content-Disposition: attachment; filename=' . $quoted); | ||||
|             header('Content-Transfer-Encoding: binary'); | ||||
|             header('Connection: Keep-Alive'); | ||||
|             header('Expires: 0'); | ||||
|             header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); | ||||
|             header('Pragma: public'); | ||||
|             header('Content-Length: ' . $attachment->size); | ||||
|  | ||||
|             echo Crypt::decrypt(file_get_contents($file)); | ||||
|  | ||||
|         } else { | ||||
|             abort(404); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Attachment $attachment | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function preview(Attachment $attachment) | ||||
|     { | ||||
|         if ($attachment->mime == 'application/pdf') { | ||||
|             $file = public_path('images/page_white_acrobat.png'); | ||||
|         } else { | ||||
|             $file = public_path('images/page_green.png'); | ||||
|         } | ||||
|  | ||||
|         $response = Response::make(File::get($file)); | ||||
|         $response->header('Content-Type', 'image/png'); | ||||
|  | ||||
|         return $response; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param AttachmentFormRequest         $request | ||||
|      * @param AttachmentRepositoryInterface $repository | ||||
|      * @param Attachment                    $attachment | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function update(AttachmentFormRequest $request, AttachmentRepositoryInterface $repository, Attachment $attachment) | ||||
|     { | ||||
|  | ||||
|         $attachmentData = [ | ||||
|             'title'       => $request->input('title'), | ||||
|             'description' => $request->input('description'), | ||||
|             'notes'       => $request->input('notes'), | ||||
|         ]; | ||||
|  | ||||
|         $repository->update($attachment, $attachmentData); | ||||
|  | ||||
|         Session::flash('success', 'Attachment "' . $attachment->filename . '" updated.'); | ||||
|         Preferences::mark(); | ||||
|  | ||||
|         if (intval(Input::get('return_to_edit')) === 1) { | ||||
|             // set value so edit routine will not overwrite URL: | ||||
|             Session::put('attachments.edit.fromUpdate', true); | ||||
|  | ||||
|             return redirect(route('attachments.edit', [$attachment->id]))->withInput(['return_to_edit' => 1]); | ||||
|         } | ||||
|  | ||||
|         // redirect to previous URL. | ||||
|         return redirect(Session::get('attachments.edit.url')); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,14 +1,17 @@ | ||||
| <?php namespace FireflyIII\Http\Controllers\Auth; | ||||
|  | ||||
| use App; | ||||
| use Auth; | ||||
| use Config; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Models\Role; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers; | ||||
| use Illuminate\Foundation\Auth\ThrottlesLogins; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Mail\Message; | ||||
| use Log; | ||||
| use Mail; | ||||
| use Request as Rq; | ||||
| use Session; | ||||
| use Twig; | ||||
| use Validator; | ||||
| @@ -20,19 +23,76 @@ use Validator; | ||||
|  */ | ||||
| class AuthController extends Controller | ||||
| { | ||||
|     use AuthenticatesAndRegistersUsers, ThrottlesLogins; | ||||
|  | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Registration & Login Controller | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | This controller handles the registration of new users, as well as the | ||||
|     | authentication of existing users. By default, this controller uses | ||||
|     | a simple trait to add these behaviors. Why don't you explore it? | ||||
|     | | ||||
|     */ | ||||
|     /** | ||||
|      * Log the user out of the application. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function getLogout() | ||||
|     { | ||||
|         Auth::logout(); | ||||
|  | ||||
|         return redirect('/auth/login'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show the application registration form. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function getRegister() | ||||
|     { | ||||
|         $host = Rq::getHttpHost(); | ||||
|  | ||||
|         return view('auth.register', compact('host')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handle a login request to the application. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function postLogin(Request $request) | ||||
|     { | ||||
|         $this->validate($request, [$this->loginUsername() => 'required', 'password' => 'required',]); | ||||
|         $throttles = $this->isUsingThrottlesLoginsTrait(); | ||||
|  | ||||
|         if ($throttles && $this->hasTooManyLoginAttempts($request)) { | ||||
|             return $this->sendLockoutResponse($request); | ||||
|         } | ||||
|  | ||||
|         $credentials            = $this->getCredentials($request); | ||||
|         $credentials['blocked'] = 0; // most not be blocked. | ||||
|  | ||||
|         if (Auth::attempt($credentials, $request->has('remember'))) { | ||||
|             return $this->handleUserWasAuthenticated($request, $throttles); | ||||
|         } | ||||
|  | ||||
|         $message = $this->getFailedLoginMessage(); | ||||
|         /** @var User $foundUser */ | ||||
|         $foundUser = User::where('email', $credentials['email'])->where('blocked', 1)->first(); | ||||
|         if (!is_null($foundUser)) { | ||||
|             // if it exists, show message: | ||||
|             $code = $foundUser->blocked_code; | ||||
|             if (strlen($code) == 0) { | ||||
|                 $code = 'general_blocked'; | ||||
|             } | ||||
|             $message = trans('firefly.' . $code . '_error', ['email' => $credentials['email']]); | ||||
|         } | ||||
|  | ||||
|         if ($throttles) { | ||||
|             $this->incrementLoginAttempts($request); | ||||
|         } | ||||
|  | ||||
|         return redirect($this->loginPath()) | ||||
|             ->withInput($request->only($this->loginUsername(), 'remember')) | ||||
|             ->withErrors([$this->loginUsername() => $message,]); | ||||
|     } | ||||
|  | ||||
|     use AuthenticatesAndRegistersUsers; | ||||
|  | ||||
|     public $redirectTo = '/'; | ||||
|  | ||||
| @@ -44,6 +104,8 @@ class AuthController extends Controller | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|  | ||||
|         $this->middleware('guest', ['except' => 'getLogout']); | ||||
|     } | ||||
|  | ||||
| @@ -78,21 +140,35 @@ class AuthController extends Controller | ||||
|         } | ||||
|         // @codeCoverageIgnoreEnd | ||||
|  | ||||
|  | ||||
|         $data             = $request->all(); | ||||
|         $data['password'] = bcrypt($data['password']); | ||||
|  | ||||
|         // is user email domain blocked? | ||||
|         if ($this->isBlockedDomain($data['email'])) { | ||||
|             $validator->getMessageBag()->add('email', trans('validation.invalid_domain')); | ||||
|             $this->throwValidationException( | ||||
|                 $request, $validator | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         Auth::login($this->create($data)); | ||||
|  | ||||
|         // get the email address | ||||
|         if (Auth::user() instanceof User) { | ||||
|             $email   = Auth::user()->email; | ||||
|             $address = route('index'); | ||||
|             $email     = Auth::user()->email; | ||||
|             $address   = route('index'); | ||||
|             $ipAddress = $request->ip(); | ||||
|             // send email. | ||||
|             Mail::send( | ||||
|                 ['emails.registered-html', 'emails.registered'], ['address' => $address], function (Message $message) use ($email) { | ||||
|                 $message->to($email, $email)->subject('Welcome to Firefly III! '); | ||||
|             try { | ||||
|                 Mail::send( | ||||
|                     ['emails.registered-html', 'emails.registered'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) { | ||||
|                     $message->to($email, $email)->subject('Welcome to Firefly III! '); | ||||
|                 } | ||||
|                 ); | ||||
|             } catch (\Swift_TransportException $e) { | ||||
|                 Log::error($e->getMessage()); | ||||
|             } | ||||
|             ); | ||||
|  | ||||
|             // set flash message | ||||
|             Session::flash('success', 'You have registered successfully!'); | ||||
| @@ -109,12 +185,46 @@ class AuthController extends Controller | ||||
|             return redirect($this->redirectPath()); | ||||
|         } | ||||
|         // @codeCoverageIgnoreStart | ||||
|         App::abort(500, 'Not a user!'); | ||||
|         abort(500, 'Not a user!'); | ||||
|  | ||||
|         return redirect('/'); | ||||
|         // @codeCoverageIgnoreEnd | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function getBlockedDomains() | ||||
|     { | ||||
|         $set     = Config::get('mail.blocked_domains'); | ||||
|         $domains = []; | ||||
|         foreach ($set as $entry) { | ||||
|             $domain = trim($entry); | ||||
|             if (strlen($domain) > 0) { | ||||
|                 $domains[] = $domain; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $domains; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $email | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function isBlockedDomain($email) | ||||
|     { | ||||
|         $parts   = explode('@', $email); | ||||
|         $blocked = $this->getBlockedDomains(); | ||||
|  | ||||
|         if (isset($parts[1]) && in_array($parts[1], $blocked)) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a validator for an incoming registration request. | ||||
|      * | ||||
|   | ||||
| @@ -1,7 +1,11 @@ | ||||
| <?php namespace FireflyIII\Http\Controllers\Auth; | ||||
|  | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Foundation\Auth\ResetsPasswords; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Mail\Message; | ||||
| use Illuminate\Support\Facades\Password; | ||||
|  | ||||
| /** | ||||
|  * Class PasswordController | ||||
| @@ -36,8 +40,46 @@ class PasswordController extends Controller | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|  | ||||
|         $this->middleware('guest'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Send a reset link to the given user. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function postEmail(Request $request) | ||||
|     { | ||||
|         $this->validate($request, ['email' => 'required|email']); | ||||
|  | ||||
|         $user = User::whereEmail($request->get('email'))->first(); | ||||
|  | ||||
|         if (!is_null($user) && intval($user->blocked) === 1) { | ||||
|             $response = 'passwords.blocked'; | ||||
|         } else { | ||||
|             $response = Password::sendResetLink( | ||||
|                 $request->only('email'), function (Message $message) { | ||||
|                 $message->subject($this->getEmailSubject()); | ||||
|             } | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         switch ($response) { | ||||
|             case Password::RESET_LINK_SENT: | ||||
|                 return redirect()->back()->with('status', trans($response)); | ||||
|  | ||||
|             case Password::INVALID_USER: | ||||
|             case 'passwords.blocked': | ||||
|                 return redirect()->back()->withErrors(['email' => trans($response)]); | ||||
|  | ||||
|         } | ||||
|         abort(404); | ||||
|  | ||||
|         return ''; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,6 @@ use FireflyIII\Models\TransactionJournal; | ||||
| use FireflyIII\Repositories\Bill\BillRepositoryInterface; | ||||
| use Input; | ||||
| use Preferences; | ||||
| use Redirect; | ||||
| use Session; | ||||
| use URL; | ||||
| use View; | ||||
| @@ -79,8 +78,7 @@ class BillController extends Controller | ||||
|         Session::flash('success', 'The bill was deleted.'); | ||||
|         Preferences::mark(); | ||||
|  | ||||
|         return Redirect::to(Session::get('bills.delete.url')); | ||||
|  | ||||
|         return redirect(Session::get('bills.delete.url')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -133,7 +131,7 @@ class BillController extends Controller | ||||
|         if (intval($bill->active) == 0) { | ||||
|             Session::flash('warning', 'Inactive bills cannot be scanned.'); | ||||
|  | ||||
|             return Redirect::to(URL::previous()); | ||||
|             return redirect(URL::previous()); | ||||
|         } | ||||
|  | ||||
|         $journals = $repository->getPossiblyRelatedJournals($bill); | ||||
| @@ -146,7 +144,7 @@ class BillController extends Controller | ||||
|         Session::flash('success', 'Rescanned everything.'); | ||||
|         Preferences::mark(); | ||||
|  | ||||
|         return Redirect::to(URL::previous()); | ||||
|         return redirect(URL::previous()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -182,11 +180,11 @@ class BillController extends Controller | ||||
|             // set value so create routine will not overwrite URL: | ||||
|             Session::put('bills.create.fromStore', true); | ||||
|  | ||||
|             return Redirect::route('bills.create')->withInput(); | ||||
|             return redirect(route('bills.create'))->withInput(); | ||||
|         } | ||||
|  | ||||
|         // redirect to previous URL. | ||||
|         return Redirect::to(Session::get('bills.create.url')); | ||||
|         return redirect(Session::get('bills.create.url')); | ||||
|  | ||||
|     } | ||||
|  | ||||
| @@ -209,11 +207,11 @@ class BillController extends Controller | ||||
|             // set value so edit routine will not overwrite URL: | ||||
|             Session::put('bills.edit.fromUpdate', true); | ||||
|  | ||||
|             return Redirect::route('bills.edit', [$bill->id])->withInput(['return_to_edit' => 1]); | ||||
|             return redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]); | ||||
|         } | ||||
|  | ||||
|         // redirect to previous URL. | ||||
|         return Redirect::to(Session::get('bills.edit.url')); | ||||
|         return redirect(Session::get('bills.edit.url')); | ||||
|  | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -6,10 +6,12 @@ use Carbon\Carbon; | ||||
| use FireflyIII\Http\Requests\BudgetFormRequest; | ||||
| use FireflyIII\Models\Budget; | ||||
| use FireflyIII\Models\LimitRepetition; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; | ||||
| use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; | ||||
| use Illuminate\Support\Collection; | ||||
| use Input; | ||||
| use Navigation; | ||||
| use Preferences; | ||||
| use Redirect; | ||||
| use Response; | ||||
| use Session; | ||||
| use URL; | ||||
| @@ -19,7 +21,6 @@ use View; | ||||
|  * Class BudgetController | ||||
|  * | ||||
|  * @package FireflyIII\Http\Controllers | ||||
|  * @SuppressWarnings(PHPMD.TooManyMethods) | ||||
|  */ | ||||
| class BudgetController extends Controller | ||||
| { | ||||
| @@ -106,7 +107,7 @@ class BudgetController extends Controller | ||||
|         Preferences::mark(); | ||||
|  | ||||
|  | ||||
|         return Redirect::to(Session::get('budgets.delete.url')); | ||||
|         return redirect(Session::get('budgets.delete.url')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -133,28 +134,35 @@ class BudgetController extends Controller | ||||
|     /** | ||||
|      * @param BudgetRepositoryInterface $repository | ||||
|      * | ||||
|      * @param ARI                       $accountRepository | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function index(BudgetRepositoryInterface $repository) | ||||
|     public function index(BudgetRepositoryInterface $repository, ARI $accountRepository) | ||||
|     { | ||||
|         $budgets  = $repository->getActiveBudgets(); | ||||
|         $inactive = $repository->getInactiveBudgets(); | ||||
|         $spent    = '0'; | ||||
|         $budgeted = '0'; | ||||
|         $budgets           = $repository->getActiveBudgets(); | ||||
|         $inactive          = $repository->getInactiveBudgets(); | ||||
|         $spent             = '0'; | ||||
|         $budgeted          = '0'; | ||||
|         $range             = Preferences::get('viewRange', '1M')->data; | ||||
|         $start             = Navigation::startOfPeriod(Session::get('start', new Carbon), $range); | ||||
|         $end               = Navigation::endOfPeriod($start, $range); | ||||
|         $key               = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd'); | ||||
|         $budgetIncomeTotal = Preferences::get($key, 1000)->data; | ||||
|         $period            = Navigation::periodShow($start, $range); | ||||
|         $accounts          = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']); | ||||
|  | ||||
|         bcscale(2); | ||||
|         /** | ||||
|          * Do some cleanup: | ||||
|          */ | ||||
|         $repository->cleanupBudgets(); | ||||
|  | ||||
|  | ||||
|         // loop the budgets: | ||||
|         /** @var Budget $budget */ | ||||
|         foreach ($budgets as $budget) { | ||||
|             $date               = Session::get('start', Carbon::now()->startOfMonth()); | ||||
|             $end                = Session::get('end', Carbon::now()->endOfMonth()); | ||||
|             $budget->spent      = $repository->spentInPeriodCorrected($budget, $date, $end); | ||||
|             $budget->currentRep = $repository->getCurrentRepetition($budget, $date); | ||||
|             $budget->spent      = $repository->balanceInPeriod($budget, $start, $end, $accounts); | ||||
|             $budget->currentRep = $repository->getCurrentRepetition($budget, $start, $end); | ||||
|             if ($budget->currentRep) { | ||||
|                 $budgeted = bcadd($budgeted, $budget->currentRep->amount); | ||||
|             } | ||||
| @@ -162,13 +170,12 @@ class BudgetController extends Controller | ||||
|  | ||||
|         } | ||||
|  | ||||
|         $dateAsString      = Session::get('start', Carbon::now()->startOfMonth())->format('FY'); | ||||
|         $budgetIncomeTotal = Preferences::get('budgetIncomeTotal' . $dateAsString, 1000)->data; | ||||
|         $budgetMaximum     = Preferences::get('budgetMaximum', 1000)->data; | ||||
|         $defaultCurrency   = Amount::getDefaultCurrency(); | ||||
|  | ||||
|         $budgetMaximum   = Preferences::get('budgetMaximum', 1000)->data; | ||||
|         $defaultCurrency = Amount::getDefaultCurrency(); | ||||
|  | ||||
|         return view( | ||||
|             'budgets.index', compact('budgetMaximum', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted') | ||||
|             'budgets.index', compact('budgetMaximum', 'period', 'range', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted') | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| @@ -179,8 +186,9 @@ class BudgetController extends Controller | ||||
|      */ | ||||
|     public function noBudget(BudgetRepositoryInterface $repository) | ||||
|     { | ||||
|         $start    = Session::get('start', Carbon::now()->startOfMonth()); | ||||
|         $end      = Session::get('end', Carbon::now()->startOfMonth()); | ||||
|         $range    = Preferences::get('viewRange', '1M')->data; | ||||
|         $start    = Navigation::startOfPeriod(Session::get('start', new Carbon), $range); | ||||
|         $end      = Navigation::endOfPeriod($start, $range); | ||||
|         $list     = $repository->getWithoutBudget($start, $end); | ||||
|         $subTitle = trans( | ||||
|             'firefly.without_budget_between', | ||||
| @@ -195,12 +203,15 @@ class BudgetController extends Controller | ||||
|      */ | ||||
|     public function postUpdateIncome() | ||||
|     { | ||||
|         $range = Preferences::get('viewRange', '1M')->data; | ||||
|         $start = Navigation::startOfPeriod(Session::get('start', new Carbon), $range); | ||||
|         $end   = Navigation::endOfPeriod($start, $range); | ||||
|         $key   = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd'); | ||||
|  | ||||
|         $date = Session::get('start', Carbon::now()->startOfMonth())->format('FY'); | ||||
|         Preferences::set('budgetIncomeTotal' . $date, intval(Input::get('amount'))); | ||||
|         Preferences::set($key, intval(Input::get('amount'))); | ||||
|         Preferences::mark(); | ||||
|  | ||||
|         return Redirect::route('budgets.index'); | ||||
|         return redirect(route('budgets.index')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -221,13 +232,26 @@ class BudgetController extends Controller | ||||
|         $journals = $repository->getJournals($budget, $repetition); | ||||
|  | ||||
|         if (is_null($repetition->id)) { | ||||
|             $limits   = $repository->getBudgetLimits($budget); | ||||
|             $start    = $repository->firstActivity($budget); | ||||
|             $end      = new Carbon; | ||||
|             $set      = $budget->limitrepetitions()->orderBy('startdate', 'DESC')->get(); | ||||
|             $subTitle = e($budget->name); | ||||
|         } else { | ||||
|             $limits   = [$repetition->budgetLimit]; | ||||
|             $start    = $repetition->startdate; | ||||
|             $end      = $repetition->enddate; | ||||
|             $set      = new Collection([$repetition]); | ||||
|             $subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]); | ||||
|         } | ||||
|  | ||||
|         $spentArray = $repository->spentPerDay($budget, $start, $end); | ||||
|         $limits     = new Collection(); | ||||
|  | ||||
|         /** @var LimitRepetition $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $entry->spent = $this->getSumOfRange($entry->startdate, $entry->enddate, $spentArray); | ||||
|             $limits->push($entry); | ||||
|         } | ||||
|  | ||||
|         $journals->setPath('/budgets/show/' . $budget->id); | ||||
|  | ||||
|         return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle')); | ||||
| @@ -254,11 +278,11 @@ class BudgetController extends Controller | ||||
|             // set value so create routine will not overwrite URL: | ||||
|             Session::put('budgets.create.fromStore', true); | ||||
|  | ||||
|             return Redirect::route('budgets.create')->withInput(); | ||||
|             return redirect(route('budgets.create'))->withInput(); | ||||
|         } | ||||
|  | ||||
|         // redirect to previous URL. | ||||
|         return Redirect::to(Session::get('budgets.create.url')); | ||||
|         return redirect(Session::get('budgets.create.url')); | ||||
|  | ||||
|     } | ||||
|  | ||||
| @@ -273,7 +297,7 @@ class BudgetController extends Controller | ||||
|     { | ||||
|         $budgetData = [ | ||||
|             'name'   => $request->input('name'), | ||||
|             'active' => intval($request->input('active')) == 1 | ||||
|             'active' => intval($request->input('active')) == 1, | ||||
|         ]; | ||||
|  | ||||
|         $repository->update($budget, $budgetData); | ||||
| @@ -285,11 +309,11 @@ class BudgetController extends Controller | ||||
|             // set value so edit routine will not overwrite URL: | ||||
|             Session::put('budgets.edit.fromUpdate', true); | ||||
|  | ||||
|             return Redirect::route('budgets.edit', [$budget->id])->withInput(['return_to_edit' => 1]); | ||||
|             return redirect(route('budgets.edit', [$budget->id]))->withInput(['return_to_edit' => 1]); | ||||
|         } | ||||
|  | ||||
|         // redirect to previous URL. | ||||
|         return Redirect::to(Session::get('budgets.edit.url')); | ||||
|         return redirect(Session::get('budgets.edit.url')); | ||||
|  | ||||
|     } | ||||
|  | ||||
| @@ -298,8 +322,11 @@ class BudgetController extends Controller | ||||
|      */ | ||||
|     public function updateIncome() | ||||
|     { | ||||
|         $date   = Session::get('start', Carbon::now()->startOfMonth())->format('FY'); | ||||
|         $amount = Preferences::get('budgetIncomeTotal' . $date, 1000); | ||||
|         $range  = Preferences::get('viewRange', '1M')->data; | ||||
|         $start  = Navigation::startOfPeriod(Session::get('start', new Carbon), $range); | ||||
|         $end    = Navigation::endOfPeriod($start, $range); | ||||
|         $key    = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd'); | ||||
|         $amount = Preferences::get($key, 1000); | ||||
|  | ||||
|         return view('budgets.income', compact('amount')); | ||||
|     } | ||||
|   | ||||
| @@ -4,11 +4,14 @@ use Auth; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Http\Requests\CategoryFormRequest; | ||||
| use FireflyIII\Models\Category; | ||||
| use FireflyIII\Repositories\Category\CategoryRepositoryInterface; | ||||
| use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; | ||||
| use FireflyIII\Repositories\Category\SingleCategoryRepositoryInterface as SCRI; | ||||
| use FireflyIII\Support\CacheProperties; | ||||
| use Illuminate\Pagination\LengthAwarePaginator; | ||||
| use Illuminate\Support\Collection; | ||||
| use Input; | ||||
| use Navigation; | ||||
| use Preferences; | ||||
| use Redirect; | ||||
| use Session; | ||||
| use URL; | ||||
| use View; | ||||
| @@ -66,12 +69,12 @@ class CategoryController extends Controller | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param CategoryRepositoryInterface $repository | ||||
|      * @param Category                    $category | ||||
|      * @param SCRI     $repository | ||||
|      * @param Category $category | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function destroy(CategoryRepositoryInterface $repository, Category $category) | ||||
|     public function destroy(SCRI $repository, Category $category) | ||||
|     { | ||||
|  | ||||
|         $name = $category->name; | ||||
| @@ -80,7 +83,7 @@ class CategoryController extends Controller | ||||
|         Session::flash('success', 'The  category "' . e($name) . '" was deleted.'); | ||||
|         Preferences::mark(); | ||||
|  | ||||
|         return Redirect::to(Session::get('categories.delete.url')); | ||||
|         return redirect(Session::get('categories.delete.url')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -105,17 +108,18 @@ class CategoryController extends Controller | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param CategoryRepositoryInterface $repository | ||||
|      * @param CRI  $repository | ||||
|      * @param SCRI $singleRepository | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function index(CategoryRepositoryInterface $repository) | ||||
|     public function index(CRI $repository, SCRI $singleRepository) | ||||
|     { | ||||
|         $categories = $repository->getCategories(); | ||||
|         $categories = $repository->listCategories(); | ||||
|  | ||||
|         $categories->each( | ||||
|             function (Category $category) use ($repository) { | ||||
|                 $category->lastActivity = $repository->getLatestActivity($category); | ||||
|             function (Category $category) use ($singleRepository) { | ||||
|                 $category->lastActivity = $singleRepository->getLatestActivity($category); | ||||
|             } | ||||
|         ); | ||||
|  | ||||
| @@ -123,15 +127,15 @@ class CategoryController extends Controller | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param CategoryRepositoryInterface $repository | ||||
|      * @param CRI $repository | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function noCategory(CategoryRepositoryInterface $repository) | ||||
|     public function noCategory(CRI $repository) | ||||
|     { | ||||
|         $start    = Session::get('start', Carbon::now()->startOfMonth()); | ||||
|         $end      = Session::get('end', Carbon::now()->startOfMonth()); | ||||
|         $list     = $repository->getWithoutCategory($start, $end); | ||||
|         $list     = $repository->listNoCategory($start, $end); | ||||
|         $subTitle = trans( | ||||
|             'firefly.without_category_between', | ||||
|             ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] | ||||
| @@ -141,30 +145,100 @@ class CategoryController extends Controller | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param CategoryRepositoryInterface $repository | ||||
|      * @param Category                    $category | ||||
|      * @param SCRI                              $repository | ||||
|      * @param Category                          $category | ||||
|      * | ||||
|      * @param                                   $date | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function show(CategoryRepositoryInterface $repository, Category $category) | ||||
|     public function showWithDate(SCRI $repository, Category $category, $date) | ||||
|     { | ||||
|         $carbon   = new Carbon($date); | ||||
|         $range    = Preferences::get('viewRange', '1M')->data; | ||||
|         $start    = Navigation::startOfPeriod($carbon, $range); | ||||
|         $end      = Navigation::endOfPeriod($carbon, $range); | ||||
|         $subTitle = $category->name; | ||||
|  | ||||
|         $hideCategory = true; // used in list. | ||||
|         $page         = intval(Input::get('page')); | ||||
|  | ||||
|         $set      = $repository->getJournalsInRange($category, $page, $start, $end); | ||||
|         $count    = $repository->countJournalsInRange($category, $start, $end); | ||||
|         $journals = new LengthAwarePaginator($set, $count, 50, $page); | ||||
|         $journals->setPath('categories/show/' . $category->id . '/' . $date); | ||||
|  | ||||
|         return view('categories.show_with_date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param SCRI     $repository | ||||
|      * @param Category $category | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function show(SCRI $repository, Category $category) | ||||
|     { | ||||
|         $hideCategory = true; // used in list. | ||||
|         $page         = intval(Input::get('page')); | ||||
|         $set          = $repository->getJournals($category, $page); | ||||
|         $count        = $repository->countJournals($category); | ||||
|         $subTitle     = $category->name; | ||||
|         $journals     = new LengthAwarePaginator($set, $count, 50, $page); | ||||
|         $journals->setPath('categories/show/' . $category->id); | ||||
|  | ||||
|         return view('categories.show', compact('category', 'journals', 'hideCategory')); | ||||
|         // list of ranges for list of periods: | ||||
|  | ||||
|         // oldest transaction in category: | ||||
|         $start   = $repository->getFirstActivityDate($category); | ||||
|         $range   = Preferences::get('viewRange', '1M')->data; | ||||
|         $start   = Navigation::startOfPeriod($start, $range); | ||||
|         $end     = Navigation::endOfX(new Carbon, $range); | ||||
|         $entries = new Collection; | ||||
|  | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty('category-show'); | ||||
|         $cache->addProperty($category->id); | ||||
|  | ||||
|         // get all spent and earned data: | ||||
|         // get amount earned in period, grouped by day. | ||||
|         $spentArray  = $repository->spentPerDay($category, $start, $end); | ||||
|         $earnedArray = $repository->earnedPerDay($category, $start, $end); | ||||
|  | ||||
|         if ($cache->has()) { | ||||
|             $entries = $cache->get(); | ||||
|         } else { | ||||
|  | ||||
|             while ($end >= $start) { | ||||
|                 $end        = Navigation::startOfPeriod($end, $range); | ||||
|                 $currentEnd = Navigation::endOfPeriod($end, $range); | ||||
|  | ||||
|                 // get data from spentArray: | ||||
|                 $spent    = $this->getSumOfRange($end, $currentEnd, $spentArray); | ||||
|                 $earned   = $this->getSumOfRange($end, $currentEnd, $earnedArray); | ||||
|                 $dateStr  = $end->format('Y-m-d'); | ||||
|                 $dateName = Navigation::periodShow($end, $range); | ||||
|                 $entries->push([$dateStr, $dateName, $spent, $earned]); | ||||
|  | ||||
|                 $end = Navigation::subtractPeriod($end, $range, 1); | ||||
|  | ||||
|             } | ||||
|             $cache->store($entries); | ||||
|         } | ||||
|  | ||||
|         return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param CategoryFormRequest         $request | ||||
|      * @param CategoryRepositoryInterface $repository | ||||
|      * @param CategoryFormRequest $request | ||||
|      * @param SCRI                $repository | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function store(CategoryFormRequest $request, CategoryRepositoryInterface $repository) | ||||
|     public function store(CategoryFormRequest $request, SCRI $repository) | ||||
|     { | ||||
|         $categoryData = [ | ||||
|             'name' => $request->input('name'), | ||||
| @@ -178,21 +252,21 @@ class CategoryController extends Controller | ||||
|         if (intval(Input::get('create_another')) === 1) { | ||||
|             Session::put('categories.create.fromStore', true); | ||||
|  | ||||
|             return Redirect::route('categories.create')->withInput(); | ||||
|             return redirect(route('categories.create'))->withInput(); | ||||
|         } | ||||
|  | ||||
|         return Redirect::route('categories.index'); | ||||
|         return redirect(route('categories.index')); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param CategoryFormRequest         $request | ||||
|      * @param CategoryRepositoryInterface $repository | ||||
|      * @param Category                    $category | ||||
|      * @param CategoryFormRequest $request | ||||
|      * @param SCRI                $repository | ||||
|      * @param Category            $category | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function update(CategoryFormRequest $request, CategoryRepositoryInterface $repository, Category $category) | ||||
|     public function update(CategoryFormRequest $request, SCRI $repository, Category $category) | ||||
|     { | ||||
|         $categoryData = [ | ||||
|             'name' => $request->input('name'), | ||||
| @@ -206,11 +280,11 @@ class CategoryController extends Controller | ||||
|         if (intval(Input::get('return_to_edit')) === 1) { | ||||
|             Session::put('categories.edit.fromUpdate', true); | ||||
|  | ||||
|             return Redirect::route('categories.edit', [$category->id]); | ||||
|             return redirect(route('categories.edit', [$category->id])); | ||||
|         } | ||||
|  | ||||
|         // redirect to previous URL. | ||||
|         return Redirect::to(Session::get('categories.edit.url')); | ||||
|         return redirect(Session::get('categories.edit.url')); | ||||
|  | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -2,11 +2,10 @@ | ||||
|  | ||||
| namespace FireflyIII\Http\Controllers\Chart; | ||||
|  | ||||
| use App; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; | ||||
| use FireflyIII\Support\CacheProperties; | ||||
| use Illuminate\Support\Collection; | ||||
| use Preferences; | ||||
| @@ -31,63 +30,80 @@ class AccountController extends Controller | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         // create chart generator: | ||||
|         $this->generator = App::make('FireflyIII\Generator\Chart\Account\AccountChartGenerator'); | ||||
|         $this->generator = app('FireflyIII\Generator\Chart\Account\AccountChartGenerator'); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Shows the balances for all the user's accounts. | ||||
|      * Shows the balances for a given set of dates and accounts. | ||||
|      * | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param            $reportType | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @param                            $year | ||||
|      * @param                            $month | ||||
|      * @param bool                       $shared | ||||
|      * | ||||
|      * @return \Symfony\Component\HttpFoundation\Response | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function all(AccountRepositoryInterface $repository, $year, $month, $shared = false) | ||||
|     public function report($reportType, Carbon $start, Carbon $end, Collection $accounts) | ||||
|     { | ||||
|         $start = new Carbon($year . '-' . $month . '-01'); | ||||
|         $end   = clone $start; | ||||
|         $end->endOfMonth(); | ||||
|  | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty('all'); | ||||
|         $cache->addProperty('accounts'); | ||||
|         $cache->addProperty('default'); | ||||
|         $cache->addProperty($reportType); | ||||
|         $cache->addProperty($accounts); | ||||
|         if ($cache->has()) { | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|  | ||||
|         /** @var Collection $accounts */ | ||||
|         $accounts = $repository->getAccounts(['Default account', 'Asset account']); | ||||
|         if ($shared === false) { | ||||
|             /** @var Account $account */ | ||||
|             foreach ($accounts as $index => $account) { | ||||
|                 if ($account->getMeta('accountRole') == 'sharedAsset') { | ||||
|                     $accounts->forget($index); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // make chart: | ||||
|         $data = $this->generator->all($accounts, $start, $end); | ||||
|         $data = $this->generator->frontpage($accounts, $start, $end); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return Response::json($data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Shows the balances for all the user's frontpage accounts. | ||||
|      * Shows the balances for all the user's expense accounts. | ||||
|      * | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param ARI $repository | ||||
|      * | ||||
|      * @return \Symfony\Component\HttpFoundation\Response | ||||
|      */ | ||||
|     public function frontpage(AccountRepositoryInterface $repository) | ||||
|     public function expenseAccounts(ARI $repository) | ||||
|     { | ||||
|         $start    = clone Session::get('start', Carbon::now()->startOfMonth()); | ||||
|         $end      = clone Session::get('end', Carbon::now()->endOfMonth()); | ||||
|         $accounts = $repository->getAccounts(['Expense account', 'Beneficiary account']); | ||||
|  | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty('expenseAccounts'); | ||||
|         $cache->addProperty('accounts'); | ||||
|         if ($cache->has()) { | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->expenseAccounts($accounts, $start, $end); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return Response::json($data); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Shows the balances for all the user's frontpage accounts. | ||||
|      * | ||||
|      * @param ARI $repository | ||||
|      * | ||||
|      * @return \Symfony\Component\HttpFoundation\Response | ||||
|      */ | ||||
|     public function frontpage(ARI $repository) | ||||
|     { | ||||
|         $frontPage = Preferences::get('frontPageAccounts', []); | ||||
|         $start     = clone Session::get('start', Carbon::now()->startOfMonth()); | ||||
|   | ||||
| @@ -2,17 +2,14 @@ | ||||
|  | ||||
| namespace FireflyIII\Http\Controllers\Chart; | ||||
|  | ||||
| use App; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Models\Bill; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use FireflyIII\Repositories\Bill\BillRepositoryInterface; | ||||
| use FireflyIII\Support\CacheProperties; | ||||
| use Illuminate\Support\Collection; | ||||
| use Response; | ||||
| use Session; | ||||
| use Steam; | ||||
|  | ||||
| /** | ||||
|  * Class BillController | ||||
| @@ -32,78 +29,36 @@ class BillController extends Controller | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         // create chart generator: | ||||
|         $this->generator = App::make('FireflyIII\Generator\Chart\Bill\BillChartGenerator'); | ||||
|         $this->generator = app('FireflyIII\Generator\Chart\Bill\BillChartGenerator'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Shows all bills and whether or not theyve been paid this month (pie chart). | ||||
|      * Shows all bills and whether or not they've been paid this month (pie chart). | ||||
|      * | ||||
|      * @param BillRepositoryInterface    $repository | ||||
|      * @param AccountRepositoryInterface $accounts | ||||
|      * @param BillRepositoryInterface $repository | ||||
|      * | ||||
|      * @return \Symfony\Component\HttpFoundation\Response | ||||
|      */ | ||||
|     public function frontpage(BillRepositoryInterface $repository, AccountRepositoryInterface $accounts) | ||||
|     public function frontpage(BillRepositoryInterface $repository) | ||||
|     { | ||||
|         $start         = Session::get('start', Carbon::now()->startOfMonth()); | ||||
|         $end           = Session::get('end', Carbon::now()->endOfMonth()); | ||||
|         $paid          = $repository->getBillsPaidInRange($start, $end); // will be a negative amount. | ||||
|         $unpaid        = $repository->getBillsUnpaidInRange($start, $end); // will be a positive amount. | ||||
|         $creditCardDue = $repository->getCreditCardBill($start, $end); | ||||
|  | ||||
|         $start = Session::get('start', Carbon::now()->startOfMonth()); | ||||
|         $end   = Session::get('end', Carbon::now()->endOfMonth()); | ||||
|  | ||||
|  | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty('bills'); | ||||
|         $cache->addProperty('frontpage'); | ||||
|         if ($cache->has()) { | ||||
|              return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|  | ||||
|         $bills  = $repository->getActiveBills(); | ||||
|         $paid   = new Collection; // journals. | ||||
|         $unpaid = new Collection; // bills | ||||
|  | ||||
|  | ||||
|         /** @var Bill $bill */ | ||||
|         foreach ($bills as $bill) { | ||||
|             $ranges = $repository->getRanges($bill, $start, $end); | ||||
|  | ||||
|             foreach ($ranges as $range) { | ||||
|                 // paid a bill in this range? | ||||
|                 $journals = $repository->getJournalsInRange($bill, $range['start'], $range['end']); | ||||
|                 if ($journals->count() == 0) { | ||||
|                     $unpaid->push([$bill, $range['start']]); | ||||
|                 } else { | ||||
|                     $paid = $paid->merge($journals); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $creditCards = $accounts->getCreditCards(); | ||||
|         foreach ($creditCards as $creditCard) { | ||||
|             $balance = Steam::balance($creditCard, $end, true); | ||||
|             $date    = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate')); | ||||
|             if ($balance < 0) { | ||||
|                 // unpaid! create a fake bill that matches the amount. | ||||
|                 $description = $creditCard->name; | ||||
|                 $amount      = $balance * -1; | ||||
|                 $fakeBill    = $repository->createFakeBill($description, $date, $amount); | ||||
|                 unset($description, $amount); | ||||
|                 $unpaid->push([$fakeBill, $date]); | ||||
|             } | ||||
|             if ($balance == 0) { | ||||
|                 // find transfer(s) TO the credit card which should account for | ||||
|                 // anything paid. If not, the CC is not yet used. | ||||
|                 $journals = $accounts->getTransfersInRange($creditCard, $start, $end); | ||||
|                 $paid     = $paid->merge($journals); | ||||
|             } | ||||
|         if ($creditCardDue < 0) { | ||||
|             // expenses are negative (bill not yet paid), | ||||
|             $creditCardDue = bcmul($creditCardDue, '-1'); | ||||
|             $unpaid        = bcadd($unpaid, $creditCardDue); | ||||
|         } else { | ||||
|             // if more than zero, the bill has been paid: (transfer = positive). | ||||
|             // amount must be negative to be added to $paid: | ||||
|             $paid = bcadd($paid, $creditCardDue); | ||||
|         } | ||||
|  | ||||
|         // build chart: | ||||
|         $data = $this->generator->frontpage($paid, $unpaid); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return Response::json($data); | ||||
|     } | ||||
| @@ -129,6 +84,13 @@ class BillController extends Controller | ||||
|         // get first transaction or today for start: | ||||
|         $results = $repository->getJournals($bill); | ||||
|  | ||||
|         // resort: | ||||
|         $results = $results->sortBy( | ||||
|             function (TransactionJournal $journal) { | ||||
|                 return $journal->date->format('U'); | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         $data = $this->generator->single($bill, $results); | ||||
|         $cache->store($data); | ||||
|  | ||||
|   | ||||
| @@ -2,11 +2,11 @@ | ||||
|  | ||||
| namespace FireflyIII\Http\Controllers\Chart; | ||||
|  | ||||
| use App; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Models\Budget; | ||||
| use FireflyIII\Models\LimitRepetition; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; | ||||
| use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; | ||||
| use FireflyIII\Support\CacheProperties; | ||||
| use Illuminate\Support\Collection; | ||||
| @@ -33,7 +33,87 @@ class BudgetController extends Controller | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         // create chart generator: | ||||
|         $this->generator = App::make('FireflyIII\Generator\Chart\Budget\BudgetChartGenerator'); | ||||
|         $this->generator = app('FireflyIII\Generator\Chart\Budget\BudgetChartGenerator'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * TODO expand with no budget chart. | ||||
|      * | ||||
|      * @param BudgetRepositoryInterface $repository | ||||
|      * @param                           $reportType | ||||
|      * @param Carbon                    $start | ||||
|      * @param Carbon                    $end | ||||
|      * @param Collection                $accounts | ||||
|      * @param Collection                $budgets | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function multiYear(BudgetRepositoryInterface $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts, Collection $budgets) | ||||
|     { | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($reportType); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty($accounts); | ||||
|         $cache->addProperty($budgets); | ||||
|         $cache->addProperty('multiYearBudget'); | ||||
|  | ||||
|         if ($cache->has()) { | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Get the budgeted amounts for each budgets in each year. | ||||
|          */ | ||||
|         $budgetedSet   = $repository->getBudgetedPerYear($budgets, $start, $end); | ||||
|         $budgetedArray = []; | ||||
|         /** @var Budget $entry */ | ||||
|         foreach ($budgetedSet as $entry) { | ||||
|             $budgetedArray[$entry->id][$entry->dateFormatted] = $entry->budgeted; | ||||
|         } | ||||
|  | ||||
|         $set     = $repository->getBudgetsAndExpensesPerYear($budgets, $accounts, $start, $end); | ||||
|         $entries = new Collection; | ||||
|         // go by budget, not by year. | ||||
|         /** @var Budget $budget */ | ||||
|         foreach ($budgets as $budget) { | ||||
|             $entry        = ['name' => '', 'spent' => [], 'budgeted' => []]; | ||||
|             $id           = $budget->id; | ||||
|             $currentStart = clone $start; | ||||
|             while ($currentStart < $end) { | ||||
|                 // fix the date: | ||||
|                 $currentEnd = clone $currentStart; | ||||
|                 $currentEnd->endOfYear(); | ||||
|  | ||||
|                 // save to array: | ||||
|                 $year          = $currentStart->year; | ||||
|                 $entry['name'] = $budget->name; | ||||
|                 $spent         = 0; | ||||
|                 $budgeted      = 0; | ||||
|                 if (isset($set[$id]['entries'][$year])) { | ||||
|                     $spent = $set[$id]['entries'][$year] * -1; | ||||
|                 } | ||||
|  | ||||
|                 if (isset($budgetedArray[$id][$year])) { | ||||
|                     $budgeted = round($budgetedArray[$id][$year], 2); | ||||
|                 } | ||||
|  | ||||
|                 $entry['spent'][$year]    = $spent; | ||||
|                 $entry['budgeted'][$year] = $budgeted; | ||||
|  | ||||
|                 // jump to next year. | ||||
|                 $currentStart = clone $currentEnd; | ||||
|                 $currentStart->addDay(); | ||||
|             } | ||||
|             $entries->push($entry); | ||||
|         } | ||||
|         // generate chart with data: | ||||
|         $data = $this->generator->multiYear($entries); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return Response::json($data); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -49,9 +129,6 @@ class BudgetController extends Controller | ||||
|         $first = $repository->getFirstBudgetLimitDate($budget); | ||||
|         $range = Preferences::get('viewRange', '1M')->data; | ||||
|         $last  = Session::get('end', new Carbon); | ||||
|         $final = clone $last; | ||||
|         $final->addYears(2); | ||||
|         $last = Navigation::endOfX($last, $range, $final); | ||||
|  | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties(); | ||||
| @@ -59,18 +136,29 @@ class BudgetController extends Controller | ||||
|         $cache->addProperty($last); | ||||
|         $cache->addProperty('budget'); | ||||
|         if ($cache->has()) { | ||||
|  | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|  | ||||
|         $final = clone $last; | ||||
|         $final->addYears(2); | ||||
|         $last    = Navigation::endOfX($last, $range, $final); | ||||
|         $entries = new Collection; | ||||
|         // get all expenses: | ||||
|         $set = $repository->getExpensesPerMonth($budget, $first, $last); // TODO | ||||
|  | ||||
|         while ($first < $last) { | ||||
|             $end = Navigation::addPeriod($first, $range, 0); | ||||
|             $end->subDay(); | ||||
|             $chartDate = clone $end; | ||||
|             $chartDate->startOfMonth(); | ||||
|             $spent = $repository->spentInPeriodCorrected($budget, $first, $end); | ||||
|             $entries->push([$chartDate, $spent]); | ||||
|             $monthFormatted = $first->format('Y-m'); | ||||
|  | ||||
|             $filtered = $set->filter( | ||||
|                 function (Budget $obj) use ($monthFormatted) { | ||||
|                     return $obj->dateFormatted == $monthFormatted; | ||||
|                 } | ||||
|             ); | ||||
|             $spent    = is_null($filtered->first()) ? '0' : $filtered->first()->monthlyAmount; | ||||
|  | ||||
|             $entries->push([$first, round(($spent * -1), 2)]); | ||||
|  | ||||
|             $first = Navigation::addPeriod($first, $range, 0); | ||||
|         } | ||||
|  | ||||
| @@ -93,6 +181,7 @@ class BudgetController extends Controller | ||||
|     { | ||||
|         $start = clone $repetition->startdate; | ||||
|         $end   = $repetition->enddate; | ||||
|         bcscale(2); | ||||
|  | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties(); | ||||
| @@ -106,15 +195,24 @@ class BudgetController extends Controller | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|  | ||||
|         $set     = $repository->getExpensesPerDay($budget, $start, $end); | ||||
|         $entries = new Collection; | ||||
|         $amount  = $repetition->amount; | ||||
|  | ||||
|         // get sum (har har)! | ||||
|         while ($start <= $end) { | ||||
|             $formatted = $start->format('Y-m-d'); | ||||
|             $filtered  = $set->filter( | ||||
|                 function (Budget $obj) use ($formatted) { | ||||
|                     return $obj->date == $formatted; | ||||
|                 } | ||||
|             ); | ||||
|             $sum       = is_null($filtered->first()) ? '0' : $filtered->first()->dailyAmount; | ||||
|  | ||||
|             /* | ||||
|              * Sum of expenses on this day: | ||||
|              */ | ||||
|             $sum = $repository->expensesOnDayCorrected($budget, $start); | ||||
|             $amount += $sum; | ||||
|             $amount = round(bcadd($amount, $sum), 2); | ||||
|             $entries->push([clone $start, $amount]); | ||||
|             $start->addDay(); | ||||
|         } | ||||
| @@ -131,14 +229,14 @@ class BudgetController extends Controller | ||||
|      * | ||||
|      * @param BudgetRepositoryInterface $repository | ||||
|      * | ||||
|      * @param ARI                       $accountRepository | ||||
|      * | ||||
|      * @return \Symfony\Component\HttpFoundation\Response | ||||
|      */ | ||||
|     public function frontpage(BudgetRepositoryInterface $repository) | ||||
|     public function frontpage(BudgetRepositoryInterface $repository, ARI $accountRepository) | ||||
|     { | ||||
|         $budgets    = $repository->getBudgets(); | ||||
|         $start      = Session::get('start', Carbon::now()->startOfMonth()); | ||||
|         $end        = Session::get('end', Carbon::now()->endOfMonth()); | ||||
|         $allEntries = new Collection; | ||||
|         $start = Session::get('start', Carbon::now()->startOfMonth()); | ||||
|         $end   = Session::get('end', Carbon::now()->endOfMonth()); | ||||
|  | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties(); | ||||
| @@ -150,81 +248,97 @@ class BudgetController extends Controller | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|  | ||||
|         $budgets    = $repository->getBudgetsAndLimitsInRange($start, $end); | ||||
|         $allEntries = new Collection; | ||||
|         $accounts   = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']); | ||||
|  | ||||
|  | ||||
|         bcscale(2); | ||||
|  | ||||
|         /** @var Budget $budget */ | ||||
|         foreach ($budgets as $budget) { | ||||
|             $repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end); | ||||
|             if ($repetitions->count() == 0) { | ||||
|                 $expenses = $repository->spentInPeriodCorrected($budget, $start, $end, true); | ||||
|                 $allEntries->push([$budget->name, 0, 0, $expenses, 0, 0]); | ||||
|                 continue; | ||||
|             // we already have amount, startdate and enddate. | ||||
|             // if this "is" a limit repetition (as opposed to a budget without one entirely) | ||||
|             // depends on whether startdate and enddate are null. | ||||
|             $name = $budget->name; | ||||
|             if (is_null($budget->startdate) && is_null($budget->enddate)) { | ||||
|                 $currentStart = clone $start; | ||||
|                 $currentEnd   = clone $end; | ||||
|                 $expenses     = $repository->balanceInPeriod($budget, $currentStart, $currentEnd, $accounts); | ||||
|                 $amount       = 0; | ||||
|                 $left         = 0; | ||||
|                 $spent        = $expenses; | ||||
|                 $overspent    = 0; | ||||
|             } else { | ||||
|                 $currentStart = clone $budget->startdate; | ||||
|                 $currentEnd   = clone $budget->enddate; | ||||
|                 $expenses     = $repository->balanceInPeriod($budget, $currentStart, $currentEnd, $accounts); | ||||
|                 $amount       = $budget->amount; | ||||
|                 // smaller than 1 means spent MORE than budget allows. | ||||
|                 $left      = bccomp(bcadd($budget->amount, $expenses), '0') < 1 ? 0 : bcadd($budget->amount, $expenses); | ||||
|                 $spent     = bccomp(bcadd($budget->amount, $expenses), '0') < 1 ? ($amount * -1) : $expenses; | ||||
|                 $overspent = bccomp(bcadd($budget->amount, $expenses), '0') < 1 ? bcadd($budget->amount, $expenses) : 0; | ||||
|             } | ||||
|             /** @var LimitRepetition $repetition */ | ||||
|             foreach ($repetitions as $repetition) { | ||||
|                 $expenses = $repository->spentInPeriodCorrected($budget, $repetition->startdate, $repetition->enddate, true); | ||||
|                 // $left can be less than zero. | ||||
|                 // $overspent can be more than zero ( = overspending) | ||||
|  | ||||
|                 $left      = max(bcsub($repetition->amount, $expenses), 0); // limited at zero. | ||||
|                 $overspent = max(bcsub($expenses, $repetition->amount), 0); // limited at zero. | ||||
|                 $name      = $budget->name; | ||||
|  | ||||
|                 // $spent is maxed to the repetition amount: | ||||
|                 $spent = $expenses > $repetition->amount ? $repetition->amount : $expenses; | ||||
|  | ||||
|  | ||||
|                 $allEntries->push([$name, $left, $spent, $overspent, $repetition->amount, $expenses]); | ||||
|             } | ||||
|             $allEntries->push([$name, $left, $spent, $overspent, $amount, $expenses]); | ||||
|         } | ||||
|  | ||||
|         $noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end) * -1; | ||||
|         $noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end); | ||||
|         $allEntries->push([trans('firefly.noBudget'), 0, 0, $noBudgetExpenses, 0, 0]); | ||||
|  | ||||
|         $data = $this->generator->frontpage($allEntries); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return Response::json($data); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show a yearly overview for a budget. | ||||
|      * TODO expand with no budget chart. | ||||
|      * | ||||
|      * @param BudgetRepositoryInterface $repository | ||||
|      * @param                           $year | ||||
|      * @param bool                      $shared | ||||
|      * @param                           $reportType | ||||
|      * @param Carbon                    $start | ||||
|      * @param Carbon                    $end | ||||
|      * @param Collection                $accounts | ||||
|      * | ||||
|      * @return \Symfony\Component\HttpFoundation\Response | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function year(BudgetRepositoryInterface $repository, $year, $shared = false) | ||||
|     public function year(BudgetRepositoryInterface $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts) | ||||
|     { | ||||
|         $start   = new Carbon($year . '-01-01'); | ||||
|         $end     = new Carbon($year . '-12-31'); | ||||
|         $shared  = $shared == 'shared' ? true : false; | ||||
|         $budgets = $repository->getBudgets(); | ||||
|  | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty($reportType); | ||||
|         $cache->addProperty($accounts); | ||||
|         $cache->addProperty('budget'); | ||||
|         $cache->addProperty('year'); | ||||
|         if ($cache->has()) { | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|  | ||||
|         $entries = new Collection; | ||||
|         $budgetInformation = $repository->getBudgetsAndExpensesPerMonth($accounts, $start, $end); | ||||
|         $budgets           = new Collection; | ||||
|         $entries           = new Collection; | ||||
|  | ||||
|         /** @var array $row */ | ||||
|         foreach ($budgetInformation as $row) { | ||||
|             $budgets->push($row['budget']); | ||||
|         } | ||||
|  | ||||
|         while ($start < $end) { | ||||
|             // month is the current end of the period: | ||||
|             $month = clone $start; | ||||
|             $month->endOfMonth(); | ||||
|             $row = [clone $start]; | ||||
|             $row           = [clone $start]; | ||||
|             $dateFormatted = $start->format('Y-m'); | ||||
|  | ||||
|             // each budget, fill the row: | ||||
|             foreach ($budgets as $budget) { | ||||
|                 $spent = $repository->spentInPeriodCorrected($budget, $start, $month, $shared); | ||||
|             // each budget, check if there is an entry for this month: | ||||
|             /** @var array $row */ | ||||
|             foreach ($budgetInformation as $budgetRow) { | ||||
|                 $spent = 0; // nothing spent. | ||||
|                 if (isset($budgetRow['entries'][$dateFormatted])) { | ||||
|                     $spent = $budgetRow['entries'][$dateFormatted] * -1; // to fit array | ||||
|                 } | ||||
|                 $row[] = $spent; | ||||
|             } | ||||
|             $entries->push($row); | ||||
|   | ||||
| @@ -3,17 +3,18 @@ | ||||
| namespace FireflyIII\Http\Controllers\Chart; | ||||
|  | ||||
|  | ||||
| use App; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Models\Category; | ||||
| use FireflyIII\Repositories\Category\CategoryRepositoryInterface; | ||||
| use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; | ||||
| use FireflyIII\Repositories\Category\SingleCategoryRepositoryInterface as SCRI; | ||||
| use FireflyIII\Support\CacheProperties; | ||||
| use Illuminate\Support\Collection; | ||||
| use Navigation; | ||||
| use Preferences; | ||||
| use Response; | ||||
| use Session; | ||||
| use stdClass; | ||||
|  | ||||
| /** | ||||
|  * Class CategoryController | ||||
| @@ -32,26 +33,25 @@ class CategoryController extends Controller | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         // create chart generator: | ||||
|         $this->generator = App::make('FireflyIII\Generator\Chart\Category\CategoryChartGenerator'); | ||||
|         $this->generator = app('FireflyIII\Generator\Chart\Category\CategoryChartGenerator'); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Show an overview for a category for all time, per month/week/year. | ||||
|      * | ||||
|      * @param CategoryRepositoryInterface $repository | ||||
|      * @param Category                    $category | ||||
|      * @param SCRI     $repository | ||||
|      * @param Category $category | ||||
|      * | ||||
|      * @return \Symfony\Component\HttpFoundation\Response | ||||
|      */ | ||||
|     public function all(CategoryRepositoryInterface $repository, Category $category) | ||||
|     public function all(SCRI $repository, Category $category) | ||||
|     { | ||||
|         // oldest transaction in category: | ||||
|         $start = $repository->getFirstActivityDate($category); | ||||
|         $range = Preferences::get('viewRange', '1M')->data; | ||||
|         $start = Navigation::startOfPeriod($start, $range); | ||||
|         $end   = new Carbon; | ||||
|  | ||||
|         $start   = $repository->getFirstActivityDate($category); | ||||
|         $range   = Preferences::get('viewRange', '1M')->data; | ||||
|         $start   = Navigation::startOfPeriod($start, $range); | ||||
|         $end     = new Carbon; | ||||
|         $entries = new Collection; | ||||
|  | ||||
|  | ||||
| @@ -64,14 +64,25 @@ class CategoryController extends Controller | ||||
|         if ($cache->has()) { | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|         $spentArray  = $repository->spentPerDay($category, $start, $end); | ||||
|         $earnedArray = $repository->earnedPerDay($category, $start, $end); | ||||
|  | ||||
|  | ||||
|         while ($start <= $end) { | ||||
|             $currentEnd = Navigation::endOfPeriod($start, $range); | ||||
|             $spent      = $repository->spentInPeriodCorrected($category, $start, $currentEnd); | ||||
|             $entries->push([clone $start, $spent]); | ||||
|             $start = Navigation::addPeriod($start, $range, 0); | ||||
|  | ||||
|             // get the sum from $spentArray and $earnedArray: | ||||
|             $spent  = $this->getSumOfRange($start, $currentEnd, $spentArray); | ||||
|             $earned = $this->getSumOfRange($start, $currentEnd, $earnedArray); | ||||
|  | ||||
|             $date = Navigation::periodShow($start, $range); | ||||
|             $entries->push([clone $start, $date, $spent, $earned]); | ||||
|             $start = Navigation::addPeriod($start, $range, 0); | ||||
|         } | ||||
|         // limit the set to the last 40: | ||||
|         $entries = $entries->reverse(); | ||||
|         $entries = $entries->slice(0, 48); | ||||
|         $entries = $entries->reverse(); | ||||
|  | ||||
|         $data = $this->generator->all($entries); | ||||
|         $cache->store($data); | ||||
| @@ -81,14 +92,15 @@ class CategoryController extends Controller | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Show this month's category overview. | ||||
|      * | ||||
|      * @param CategoryRepositoryInterface $repository | ||||
|      * @param CRI $repository | ||||
|      * | ||||
|      * @return \Symfony\Component\HttpFoundation\Response | ||||
|      */ | ||||
|     public function frontpage(CategoryRepositoryInterface $repository) | ||||
|     public function frontpage(CRI $repository) | ||||
|     { | ||||
|  | ||||
|         $start = Session::get('start', Carbon::now()->startOfMonth()); | ||||
| @@ -104,32 +116,130 @@ class CategoryController extends Controller | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|  | ||||
|         $array = $repository->getCategoriesAndExpensesCorrected($start, $end); | ||||
|         // sort by callback: | ||||
|         uasort( | ||||
|             $array, | ||||
|             function ($left, $right) { | ||||
|                 if ($left['sum'] == $right['sum']) { | ||||
|                     return 0; | ||||
|                 } | ||||
|         // get data for categories (and "no category"): | ||||
|         $set     = $repository->spentForAccountsPerMonth(new Collection, $start, $end); | ||||
|         $outside = $repository->sumSpentNoCategory(new Collection, $start, $end); | ||||
|  | ||||
|                 return ($left['sum'] < $right['sum']) ? 1 : -1; | ||||
|             } | ||||
|         ); | ||||
|         $set  = new Collection($array); | ||||
|         // this is a "fake" entry for the "no category" entry. | ||||
|         $entry = new stdClass(); | ||||
|         $entry->name = trans('firefly.no_category'); | ||||
|         $entry->spent = $outside; | ||||
|         $set->push($entry); | ||||
|  | ||||
|         $set = $set->sortBy('spent'); | ||||
|         $data = $this->generator->frontpage($set); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return Response::json($data); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param CategoryRepositoryInterface $repository | ||||
|      * @param Category                    $category | ||||
|      * @param                             $reportType | ||||
|      * @param Carbon                      $start | ||||
|      * @param Carbon                      $end | ||||
|      * @param Collection                  $accounts | ||||
|      * @param Collection                  $categories | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function multiYear($reportType, Carbon $start, Carbon $end, Collection $accounts, Collection $categories) | ||||
|     { | ||||
|         /** @var CRI $repository */ | ||||
|         $repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface'); | ||||
|  | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($reportType); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty($accounts); | ||||
|         $cache->addProperty($categories); | ||||
|         $cache->addProperty('multiYearCategory'); | ||||
|  | ||||
|         if ($cache->has()) { | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          *  category | ||||
|          *   year: | ||||
|          *    spent: x | ||||
|          *    earned: x | ||||
|          *   year | ||||
|          *    spent: x | ||||
|          *    earned: x | ||||
|          */ | ||||
|         $entries = new Collection; | ||||
|         // go by category, not by year. | ||||
|  | ||||
|         // given a set of categories and accounts, it should not be difficult to get | ||||
|         // the exact array of data we need. | ||||
|  | ||||
|         // then get the data for "no category". | ||||
|         $set = $repository->listMultiYear($categories, $accounts, $start, $end); | ||||
|  | ||||
|         /** @var Category $category */ | ||||
|         foreach ($categories as $category) { | ||||
|             $entry = ['name' => '', 'spent' => [], 'earned' => []]; | ||||
|  | ||||
|             $currentStart = clone $start; | ||||
|             while ($currentStart < $end) { | ||||
|                 // fix the date: | ||||
|                 $year       = $currentStart->year; | ||||
|                 $currentEnd = clone $currentStart; | ||||
|                 $currentEnd->endOfYear(); | ||||
|  | ||||
|  | ||||
|                 // get data: | ||||
|                 if (is_null($category->id)) { | ||||
|                     $name   = trans('firefly.noCategory'); | ||||
|                     $spent  = $repository->sumSpentNoCategory($accounts, $currentStart, $currentEnd); | ||||
|                     $earned = $repository->sumEarnedNoCategory($accounts, $currentStart, $currentEnd); | ||||
|                 } else { | ||||
|                     // get from set: | ||||
|                     $entrySpent  = $set->filter( | ||||
|                         function (Category $cat) use ($year, $category) { | ||||
|                             return ($cat->type == 'Withdrawal' && $cat->dateFormatted == $year && $cat->id == $category->id); | ||||
|                         } | ||||
|                     )->first(); | ||||
|                     $entryEarned = $set->filter( | ||||
|                         function (Category $cat) use ($year, $category) { | ||||
|                             return ($cat->type == 'Deposit' && $cat->dateFormatted == $year && $cat->id == $category->id); | ||||
|                         } | ||||
|                     )->first(); | ||||
|  | ||||
|                     $name   = $category->name; | ||||
|                     $spent  = !is_null($entrySpent) ? $entrySpent->sum : 0; | ||||
|                     $earned = !is_null($entryEarned) ? $entryEarned->sum : 0; | ||||
|                 } | ||||
|  | ||||
|                 // save to array: | ||||
|                 $entry['name']          = $name; | ||||
|                 $entry['spent'][$year]  = ($spent * -1); | ||||
|                 $entry['earned'][$year] = $earned; | ||||
|  | ||||
|                 // jump to next year. | ||||
|                 $currentStart = clone $currentEnd; | ||||
|                 $currentStart->addDay(); | ||||
|             } | ||||
|             $entries->push($entry); | ||||
|         } | ||||
|         // generate chart with data: | ||||
|         $data = $this->generator->multiYear($entries); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return Response::json($data); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param SCRI     $repository | ||||
|      * @param Category $category | ||||
|      * | ||||
|      * @return \Symfony\Component\HttpFoundation\Response | ||||
|      */ | ||||
|     public function month(CategoryRepositoryInterface $repository, Category $category) | ||||
|     public function currentPeriod(SCRI $repository, Category $category) | ||||
|     { | ||||
|         $start = clone Session::get('start', Carbon::now()->startOfMonth()); | ||||
|         $end   = Session::get('end', Carbon::now()->endOfMonth()); | ||||
| @@ -140,21 +250,75 @@ class CategoryController extends Controller | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty($category->id); | ||||
|         $cache->addProperty('category'); | ||||
|         $cache->addProperty('month'); | ||||
|         $cache->addProperty('current-period'); | ||||
|         if ($cache->has()) { | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|         $entries = new Collection; | ||||
|  | ||||
|         // get amount earned in period, grouped by day. | ||||
|         // get amount spent in period, grouped by day. | ||||
|         $spentArray  = $repository->spentPerDay($category, $start, $end); | ||||
|         $earnedArray = $repository->earnedPerDay($category, $start, $end); | ||||
|  | ||||
|         while ($start <= $end) { | ||||
|             $spent = $repository->spentOnDaySumCorrected($category, $start); | ||||
|  | ||||
|             $entries->push([clone $start, $spent]); | ||||
|             $str    = $start->format('Y-m-d'); | ||||
|             $spent  = isset($spentArray[$str]) ? $spentArray[$str] : 0; | ||||
|             $earned = isset($earnedArray[$str]) ? $earnedArray[$str] : 0; | ||||
|             $date   = Navigation::periodShow($start, '1D'); | ||||
|             $entries->push([clone $start, $date, $spent, $earned]); | ||||
|             $start->addDay(); | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->month($entries); | ||||
|         $data = $this->generator->period($entries); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return Response::json($data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param SCRI                        $repository | ||||
|      * @param Category                    $category | ||||
|      * | ||||
|      * @param                             $date | ||||
|      * | ||||
|      * @return \Symfony\Component\HttpFoundation\Response | ||||
|      */ | ||||
|     public function specificPeriod(SCRI $repository, Category $category, $date) | ||||
|     { | ||||
|         $carbon = new Carbon($date); | ||||
|         $range  = Preferences::get('viewRange', '1M')->data; | ||||
|         $start  = Navigation::startOfPeriod($carbon, $range); | ||||
|         $end    = Navigation::endOfPeriod($carbon, $range); | ||||
|  | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties; | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty($category->id); | ||||
|         $cache->addProperty('category'); | ||||
|         $cache->addProperty('specificPeriod'); | ||||
|         $cache->addProperty($date); | ||||
|         if ($cache->has()) { | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|         $entries = new Collection; | ||||
|  | ||||
|         // get amount earned in period, grouped by day. | ||||
|         $spentArray  = $repository->spentPerDay($category, $start, $end); | ||||
|         $earnedArray = $repository->earnedPerDay($category, $start, $end); | ||||
|         // get amount spent in period, grouped by day. | ||||
|  | ||||
|         while ($start <= $end) { | ||||
|             $str    = $start->format('Y-m-d'); | ||||
|             $spent  = isset($spentArray[$str]) ? $spentArray[$str] : 0; | ||||
|             $earned = isset($earnedArray[$str]) ? $earnedArray[$str] : 0; | ||||
|             $date   = Navigation::periodShow($start, '1D'); | ||||
|             $entries->push([clone $start, $date, $spent, $earned]); | ||||
|             $start->addDay(); | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->period($entries); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return Response::json($data); | ||||
| @@ -163,54 +327,137 @@ class CategoryController extends Controller | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This chart will only show expenses. | ||||
|      * Returns a chart of what has been earned in this period in each category | ||||
|      * grouped by month. | ||||
|      * | ||||
|      * @param CategoryRepositoryInterface $repository | ||||
|      * @param                             $year | ||||
|      * @param bool                        $shared | ||||
|      * @param CRI                         $repository | ||||
|      * @param                             $reportType | ||||
|      * @param Carbon                      $start | ||||
|      * @param Carbon                      $end | ||||
|      * @param Collection                  $accounts | ||||
|      * | ||||
|      * @return \Symfony\Component\HttpFoundation\Response | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function year(CategoryRepositoryInterface $repository, $year, $shared = false) | ||||
|     public function earnedInPeriod(CRI $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts) | ||||
|     { | ||||
|         $start = new Carbon($year . '-01-01'); | ||||
|         $end   = new Carbon($year . '-12-31'); | ||||
|  | ||||
|  | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties; | ||||
|         $cache = new CacheProperties; // chart properties for cache: | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty($reportType); | ||||
|         $cache->addProperty($accounts); | ||||
|         $cache->addProperty('category'); | ||||
|         $cache->addProperty('year'); | ||||
|         $cache->addProperty('earned-in-period'); | ||||
|         if ($cache->has()) { | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|  | ||||
|         $shared     = $shared == 'shared' ? true : false; | ||||
|         $categories = $repository->getCategories(); | ||||
|         $set        = $repository->earnedForAccountsPerMonth($accounts, $start, $end); | ||||
|         $categories = $set->unique('id')->sortBy( | ||||
|             function (Category $category) { | ||||
|                 return $category->name; | ||||
|             } | ||||
|         ); | ||||
|         $entries    = new Collection; | ||||
|  | ||||
|         while ($start < $end) { | ||||
|             // month is the current end of the period: | ||||
|             $month = clone $start; | ||||
|             $month->endOfMonth(); | ||||
|             // make a row: | ||||
|         while ($start < $end) { // filter the set: | ||||
|             $row = [clone $start]; | ||||
|  | ||||
|             // each budget, fill the row: | ||||
|             // get possibly relevant entries from the big $set | ||||
|             $currentSet = $set->filter( | ||||
|                 function (Category $category) use ($start) { | ||||
|                     return $category->dateFormatted == $start->format("Y-m"); | ||||
|                 } | ||||
|             ); | ||||
|             // check for each category if its in the current set. | ||||
|             /** @var Category $category */ | ||||
|             foreach ($categories as $category) { | ||||
|                 $spent = $repository->spentInPeriodCorrected($category, $start, $month, $shared); | ||||
|                 $row[] = $spent; | ||||
|                 // if its in there, use the value. | ||||
|                 $entry = $currentSet->filter( | ||||
|                     function (Category $cat) use ($category) { | ||||
|                         return ($cat->id == $category->id); | ||||
|                     } | ||||
|                 )->first(); | ||||
|                 if (!is_null($entry)) { | ||||
|                     $row[] = round($entry->earned, 2); | ||||
|                 } else { | ||||
|                     $row[] = 0; | ||||
|                 } | ||||
|             } | ||||
|             $entries->push($row); | ||||
|  | ||||
|             $entries->push($row); | ||||
|             $start->addMonth(); | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->year($categories, $entries); | ||||
|         $data = $this->generator->earnedInPeriod($categories, $entries); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return Response::json($data); | ||||
|         return $data; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns a chart of what has been spent in this period in each category | ||||
|      * grouped by month. | ||||
|      * | ||||
|      * @param CRI                         $repository | ||||
|      * @param                             $reportType | ||||
|      * @param Carbon                      $start | ||||
|      * @param Carbon                      $end | ||||
|      * @param Collection                  $accounts | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function spentInPeriod(CRI $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts) | ||||
|     { | ||||
|         $cache = new CacheProperties; // chart properties for cache: | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty($reportType); | ||||
|         $cache->addProperty($accounts); | ||||
|         $cache->addProperty('category'); | ||||
|         $cache->addProperty('spent-in-period'); | ||||
|         if ($cache->has()) { | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|  | ||||
|         $set        = $repository->spentForAccountsPerMonth($accounts, $start, $end); | ||||
|         $categories = $set->unique('id')->sortBy( | ||||
|             function (Category $category) { | ||||
|                 return $category->name; | ||||
|             } | ||||
|         ); | ||||
|         $entries    = new Collection; | ||||
|  | ||||
|         while ($start < $end) { // filter the set: | ||||
|             $row = [clone $start]; | ||||
|             // get possibly relevant entries from the big $set | ||||
|             $currentSet = $set->filter( | ||||
|                 function (Category $category) use ($start) { | ||||
|                     return $category->dateFormatted == $start->format("Y-m"); | ||||
|                 } | ||||
|             ); | ||||
|             // check for each category if its in the current set. | ||||
|             /** @var Category $category */ | ||||
|             foreach ($categories as $category) { | ||||
|                 // if its in there, use the value. | ||||
|                 $entry = $currentSet->filter( | ||||
|                     function (Category $cat) use ($category) { | ||||
|                         return ($cat->id == $category->id); | ||||
|                     } | ||||
|                 )->first(); | ||||
|                 if (!is_null($entry)) { | ||||
|                     $row[] = round(($entry->spent * -1), 2); | ||||
|                 } else { | ||||
|                     $row[] = 0; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             $entries->push($row); | ||||
|             $start->addMonth(); | ||||
|         } | ||||
|         $data = $this->generator->spentInPeriod($categories, $entries); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
|  | ||||
| namespace FireflyIII\Http\Controllers\Chart; | ||||
|  | ||||
| use App; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; | ||||
| @@ -29,7 +28,7 @@ class PiggyBankController extends Controller | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         // create chart generator: | ||||
|         $this->generator = App::make('FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGenerator'); | ||||
|         $this->generator = app('FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGenerator'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -3,7 +3,6 @@ | ||||
| namespace FireflyIII\Http\Controllers\Chart; | ||||
|  | ||||
|  | ||||
| use App; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Helpers\Report\ReportQueryInterface; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| @@ -29,7 +28,7 @@ class ReportController extends Controller | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         // create chart generator: | ||||
|         $this->generator = App::make('FireflyIII\Generator\Chart\Report\ReportChartGenerator'); | ||||
|         $this->generator = app('FireflyIII\Generator\Chart\Report\ReportChartGenerator'); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -37,40 +36,39 @@ class ReportController extends Controller | ||||
|      * Summarizes all income and expenses, per month, for a given year. | ||||
|      * | ||||
|      * @param ReportQueryInterface $query | ||||
|      * @param                      $year | ||||
|      * @param bool                 $shared | ||||
|      * @param                      $reportType | ||||
|      * @param Carbon               $start | ||||
|      * @param Carbon               $end | ||||
|      * @param Collection           $accounts | ||||
|      * | ||||
|      * @return \Symfony\Component\HttpFoundation\Response | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function yearInOut(ReportQueryInterface $query, $year, $shared = false) | ||||
|     public function yearInOut(ReportQueryInterface $query, $reportType, Carbon $start, Carbon $end, Collection $accounts) | ||||
|     { | ||||
|         // get start and end of year | ||||
|         $start  = new Carbon($year . '-01-01'); | ||||
|         $end    = new Carbon($year . '-12-31'); | ||||
|         $shared = $shared == 'shared' ? true : false; | ||||
|  | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties; | ||||
|         $cache->addProperty('yearInOut'); | ||||
|         $cache->addProperty($year); | ||||
|         $cache->addProperty($shared); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($reportType); | ||||
|         $cache->addProperty($accounts); | ||||
|         $cache->addProperty($end); | ||||
|         if ($cache->has()) { | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|  | ||||
|         $entries = new Collection; | ||||
|         while ($start < $end) { | ||||
|             $month = clone $start; | ||||
|             $month->endOfMonth(); | ||||
|             // total income and total expenses: | ||||
|             $incomeSum  = $query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount'); | ||||
|             $expenseSum = $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount'); | ||||
|         // spent per month, and earned per month. For a specific set of accounts | ||||
|         // grouped by month | ||||
|         $spentArray  = $query->spentPerMonth($accounts, $start, $end); | ||||
|         $earnedArray = $query->earnedPerMonth($accounts, $start, $end); | ||||
|  | ||||
|             $entries->push([clone $start, $incomeSum, $expenseSum]); | ||||
|             $start->addMonth(); | ||||
|         if ($start->diffInMonths($end) > 12) { | ||||
|             // data = method X | ||||
|             $data = $this->multiYearInOut($earnedArray, $spentArray, $start, $end); | ||||
|         } else { | ||||
|             // data = method Y | ||||
|             $data = $this->singleYearInOut($earnedArray, $spentArray, $start, $end); | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->yearInOut($entries); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return Response::json($data); | ||||
| @@ -81,46 +79,170 @@ class ReportController extends Controller | ||||
|      * Summarizes all income and expenses for a given year. Gives a total and an average. | ||||
|      * | ||||
|      * @param ReportQueryInterface $query | ||||
|      * @param                      $year | ||||
|      * @param bool                 $shared | ||||
|      * @param                      $reportType | ||||
|      * @param Carbon               $start | ||||
|      * @param Carbon               $end | ||||
|      * @param Collection           $accounts | ||||
|      * | ||||
|      * @return \Symfony\Component\HttpFoundation\Response | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function yearInOutSummarized(ReportQueryInterface $query, $year, $shared = false) | ||||
|     public function yearInOutSummarized(ReportQueryInterface $query, $reportType, Carbon $start, Carbon $end, Collection $accounts) | ||||
|     { | ||||
|  | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties; | ||||
|         $cache->addProperty('yearInOutSummarized'); | ||||
|         $cache->addProperty($year); | ||||
|         $cache->addProperty($shared); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty($reportType); | ||||
|         $cache->addProperty($accounts); | ||||
|         if ($cache->has()) { | ||||
|             return Response::json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|         // spent per month, and earned per month. For a specific set of accounts | ||||
|         // grouped by month | ||||
|         $spentArray  = $query->spentPerMonth($accounts, $start, $end); | ||||
|         $earnedArray = $query->earnedPerMonth($accounts, $start, $end); | ||||
|         if ($start->diffInMonths($end) > 12) { | ||||
|             // per year | ||||
|             $data = $this->multiYearInOutSummarized($earnedArray, $spentArray, $start, $end); | ||||
|         } else { | ||||
|             // per month! | ||||
|             $data = $this->singleYearInOutSummarized($earnedArray, $spentArray, $start, $end); | ||||
|         } | ||||
|         $cache->store($data); | ||||
|  | ||||
|         $start   = new Carbon($year . '-01-01'); | ||||
|         $end     = new Carbon($year . '-12-31'); | ||||
|         $shared  = $shared == 'shared' ? true : false; | ||||
|         return Response::json($data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array  $earned | ||||
|      * @param array  $spent | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function singleYearInOutSummarized(array $earned, array $spent, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $income  = '0'; | ||||
|         $expense = '0'; | ||||
|         $count   = 0; | ||||
|  | ||||
|         bcscale(2); | ||||
|  | ||||
|         while ($start < $end) { | ||||
|             $month = clone $start; | ||||
|             $month->endOfMonth(); | ||||
|             // total income and total expenses: | ||||
|             $income  = bcadd($income, $query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount')); | ||||
|             $expense = bcadd($expense, $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount')); | ||||
|             $date           = $start->format('Y-m'); | ||||
|             $currentIncome  = isset($earned[$date]) ? $earned[$date] : 0; | ||||
|             $currentExpense = isset($spent[$date]) ? ($spent[$date] * -1) : 0; | ||||
|             $income         = bcadd($income, $currentIncome); | ||||
|             $expense        = bcadd($expense, $currentExpense); | ||||
|  | ||||
|             $count++; | ||||
|             $start->addMonth(); | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->yearInOutSummarized($income, $expense, $count); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return Response::json($data); | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array  $earned | ||||
|      * @param array  $spent | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function multiYearInOutSummarized(array $earned, array $spent, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $income  = '0'; | ||||
|         $expense = '0'; | ||||
|         $count   = 0; | ||||
|         while ($start < $end) { | ||||
|  | ||||
|             $currentIncome  = $this->pluckFromArray($start->year, $earned); | ||||
|             $currentExpense = $this->pluckFromArray($start->year, $spent) * -1; | ||||
|             $income         = bcadd($income, $currentIncome); | ||||
|             $expense        = bcadd($expense, $currentExpense); | ||||
|  | ||||
|             $count++; | ||||
|             $start->addYear(); | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->multiYearInOutSummarized($income, $expense, $count); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array  $earned | ||||
|      * @param array  $spent | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function multiYearInOut(array $earned, array $spent, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $entries = new Collection; | ||||
|         while ($start < $end) { | ||||
|  | ||||
|             $incomeSum  = $this->pluckFromArray($start->year, $earned); | ||||
|             $expenseSum = $this->pluckFromArray($start->year, $spent) * -1; | ||||
|  | ||||
|             $entries->push([clone $start, $incomeSum, $expenseSum]); | ||||
|             $start->addYear(); | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->multiYearInOut($entries); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array  $earned | ||||
|      * @param array  $spent | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function singleYearInOut(array $earned, array $spent, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         // per month? simply use each month. | ||||
|  | ||||
|         $entries = new Collection; | ||||
|         while ($start < $end) { | ||||
|             // total income and total expenses: | ||||
|             $date       = $start->format('Y-m'); | ||||
|             $incomeSum  = isset($earned[$date]) ? $earned[$date] : 0; | ||||
|             $expenseSum = isset($spent[$date]) ? ($spent[$date] * -1) : 0; | ||||
|  | ||||
|             $entries->push([clone $start, $incomeSum, $expenseSum]); | ||||
|             $start->addMonth(); | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->yearInOut($entries); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int   $year | ||||
|      * @param array $set | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function pluckFromArray($year, array $set) | ||||
|     { | ||||
|         bcscale(2); | ||||
|         $sum = '0'; | ||||
|         foreach ($set as $date => $amount) { | ||||
|             if (substr($date, 0, 4) == $year) { | ||||
|                 $sum = bcadd($sum, $amount); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $sum; | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| <?php namespace FireflyIII\Http\Controllers; | ||||
|  | ||||
| use Auth; | ||||
| use Config; | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Foundation\Bus\DispatchesJobs; | ||||
| use Illuminate\Foundation\Validation\ValidatesRequests; | ||||
| use Illuminate\Routing\Controller as BaseController; | ||||
| @@ -34,14 +34,43 @@ abstract class Controller extends BaseController | ||||
|         View::share('hideTags', false); | ||||
|  | ||||
|         if (Auth::check()) { | ||||
|             $pref                    = Preferences::get('language', 'en'); | ||||
|             $pref                    = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US')); | ||||
|             $lang                    = $pref->data; | ||||
|             $this->monthFormat       = Config::get('firefly.month.' . $lang); | ||||
|             $this->monthAndDayFormat = Config::get('firefly.monthAndDay.' . $lang); | ||||
|             $this->monthFormat       = trans('config.month'); | ||||
|             $this->monthAndDayFormat = trans('config.month_and_day'); | ||||
|  | ||||
|             View::share('monthFormat', $this->monthFormat); | ||||
|             View::share('monthAndDayFormat', $this->monthAndDayFormat); | ||||
|             View::share('language', $lang); | ||||
|             View::share('localeconv', localeconv()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Take the array as returned by SingleCategoryRepositoryInterface::spentPerDay and SingleCategoryRepositoryInterface::earnedByDay | ||||
|      * and sum up everything in the array in the given range. | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param array  $array | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function getSumOfRange(Carbon $start, Carbon $end, array $array) | ||||
|     { | ||||
|         bcscale(2); | ||||
|         $sum          = '0'; | ||||
|         $currentStart = clone $start; // to not mess with the original one | ||||
|         $currentEnd   = clone $end; // to not mess with the original one | ||||
|  | ||||
|         while ($currentStart <= $currentEnd) { | ||||
|             $date = $currentStart->format('Y-m-d'); | ||||
|             if (isset($array[$date])) { | ||||
|                 $sum = bcadd($sum, $array[$date]); | ||||
|             } | ||||
|             $currentStart->addDay(); | ||||
|         } | ||||
|  | ||||
|         return $sum; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										412
									
								
								app/Http/Controllers/CsvController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										412
									
								
								app/Http/Controllers/CsvController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,412 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Http\Controllers; | ||||
|  | ||||
| use Config; | ||||
| use ExpandedForm; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Helpers\Csv\Data; | ||||
| use FireflyIII\Helpers\Csv\Importer; | ||||
| use FireflyIII\Helpers\Csv\WizardInterface; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; | ||||
| use Illuminate\Http\Request; | ||||
| use Input; | ||||
| use Log; | ||||
| use Preferences; | ||||
| use Session; | ||||
| use View; | ||||
|  | ||||
| /** | ||||
|  * Class CsvController | ||||
|  * | ||||
|  * @package FireflyIII\Http\Controllers | ||||
|  */ | ||||
| class CsvController extends Controller | ||||
| { | ||||
|  | ||||
|     /** @var  Data */ | ||||
|     protected $data; | ||||
|     /** @var  WizardInterface */ | ||||
|     protected $wizard; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         View::share('title', trans('firefly.csv')); | ||||
|         View::share('mainTitleIcon', 'fa-file-text-o'); | ||||
|  | ||||
|         if (Config::get('firefly.csv_import_enabled') === false) { | ||||
|             throw new FireflyException('CSV Import is not enabled.'); | ||||
|         } | ||||
|  | ||||
|         $this->wizard = app('FireflyIII\Helpers\Csv\WizardInterface'); | ||||
|         $this->data   = app('FireflyIII\Helpers\Csv\Data'); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Define column roles and mapping. | ||||
|      * | ||||
|      * STEP THREE | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View | ||||
|      */ | ||||
|     public function columnRoles() | ||||
|     { | ||||
|  | ||||
|         $fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-import-account']; | ||||
|         if (!$this->wizard->sessionHasValues($fields)) { | ||||
|             Session::flash('warning', 'Could not recover upload.'); | ||||
|  | ||||
|             return redirect(route('csv.index')); | ||||
|         } | ||||
|  | ||||
|         $subTitle       = trans('firefly.csv_define_column_roles'); | ||||
|         $firstRow       = $this->data->getReader()->fetchOne(); | ||||
|         $count          = count($firstRow); | ||||
|         $headers        = []; | ||||
|         $example        = $this->data->getReader()->fetchOne(1); | ||||
|         $availableRoles = []; | ||||
|         $roles          = $this->data->getRoles(); | ||||
|         $map            = $this->data->getMap(); | ||||
|  | ||||
|         for ($i = 1; $i <= $count; $i++) { | ||||
|             $headers[] = trans('firefly.csv_column') . ' #' . $i; | ||||
|         } | ||||
|         if ($this->data->hasHeaders()) { | ||||
|             $headers = $firstRow; | ||||
|         } | ||||
|  | ||||
|         foreach (Config::get('csv.roles') as $name => $role) { | ||||
|             $availableRoles[$name] = trans('firefly.csv_column_' . $name);//$role['name']; | ||||
|         } | ||||
|         ksort($availableRoles); | ||||
|  | ||||
|         return view('csv.column-roles', compact('availableRoles', 'map', 'roles', 'headers', 'example', 'subTitle')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Optional download of mapping. | ||||
|      * | ||||
|      * STEP FOUR THREE-A | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse|string | ||||
|      */ | ||||
|     public function downloadConfig() | ||||
|     { | ||||
|         $fields = ['csv-date-format', 'csv-has-headers']; | ||||
|         if (!$this->wizard->sessionHasValues($fields)) { | ||||
|             Session::flash('warning', 'Could not recover upload.'); | ||||
|  | ||||
|             return redirect(route('csv.index')); | ||||
|         } | ||||
|         $data = [ | ||||
|             'date-format' => Session::get('csv-date-format'), | ||||
|             'has-headers' => Session::get('csv-has-headers') | ||||
|         ]; | ||||
|         if (Session::has('csv-map')) { | ||||
|             $data['map'] = Session::get('csv-map'); | ||||
|         } | ||||
|         if (Session::has('csv-roles')) { | ||||
|             $data['roles'] = Session::get('csv-roles'); | ||||
|         } | ||||
|         if (Session::has('csv-mapped')) { | ||||
|             $data['mapped'] = Session::get('csv-mapped'); | ||||
|         } | ||||
|  | ||||
|         if (Session::has('csv-specifix')) { | ||||
|             $data['specifix'] = Session::get('csv-specifix'); | ||||
|         } | ||||
|  | ||||
|         $result = json_encode($data, JSON_PRETTY_PRINT); | ||||
|         $name   = 'csv-configuration-' . date('Y-m-d') . '.json'; | ||||
|  | ||||
|         header('Content-disposition: attachment; filename=' . $name); | ||||
|         header('Content-type: application/json'); | ||||
|         echo $result; | ||||
|  | ||||
|         return ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function downloadConfigPage() | ||||
|     { | ||||
|         $subTitle = trans('firefly.csv_download_config_title'); | ||||
|  | ||||
|         return view('csv.download-config', compact('subTitle')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method shows the initial upload form. | ||||
|      * | ||||
|      * STEP ONE | ||||
|      * | ||||
|      * @param ARI $repository | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function index(ARI $repository) | ||||
|     { | ||||
|         $subTitle = trans('firefly.csv_import'); | ||||
|  | ||||
|         Session::forget('csv-date-format'); | ||||
|         Session::forget('csv-has-headers'); | ||||
|         Session::forget('csv-file'); | ||||
|         Session::forget('csv-import-account'); | ||||
|         Session::forget('csv-map'); | ||||
|         Session::forget('csv-roles'); | ||||
|         Session::forget('csv-mapped'); | ||||
|         Session::forget('csv-specifix'); | ||||
|  | ||||
|         // get list of supported specifix | ||||
|         $specifix = []; | ||||
|         foreach (Config::get('csv.specifix') as $entry) { | ||||
|             $specifix[$entry] = trans('firefly.csv_specifix_' . $entry); | ||||
|         } | ||||
|  | ||||
|         // get a list of asset accounts: | ||||
|         $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Asset account', 'Default account'])); | ||||
|  | ||||
|         // can actually upload? | ||||
|         $uploadPossible = is_writable(storage_path('upload')); | ||||
|         $path           = storage_path('upload'); | ||||
|  | ||||
|         return view('csv.index', compact('subTitle', 'uploadPossible', 'path', 'specifix', 'accounts')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Parse the file. | ||||
|      * | ||||
|      * STEP FOUR | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function initialParse() | ||||
|     { | ||||
|         $fields = ['csv-file', 'csv-date-format', 'csv-has-headers']; | ||||
|         if (!$this->wizard->sessionHasValues($fields)) { | ||||
|             Session::flash('warning', 'Could not recover upload.'); | ||||
|  | ||||
|             return redirect(route('csv.index')); | ||||
|         } | ||||
|  | ||||
|         // process given roles and mapping: | ||||
|         $roles = $this->wizard->processSelectedRoles(Input::get('role')); | ||||
|         $maps  = $this->wizard->processSelectedMapping($roles, Input::get('map')); | ||||
|  | ||||
|         Session::put('csv-map', $maps); | ||||
|         Session::put('csv-roles', $roles); | ||||
|  | ||||
|         // Go back when no roles defined: | ||||
|         if (count($roles) === 0) { | ||||
|             Session::flash('warning', 'Please select some roles.'); | ||||
|  | ||||
|             return redirect(route('csv.column-roles')); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Continue with map specification when necessary. | ||||
|          */ | ||||
|         if (count($maps) > 0) { | ||||
|             return redirect(route('csv.map')); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Or simply start processing. | ||||
|          */ | ||||
|  | ||||
|         // proceed to download config | ||||
|         return redirect(route('csv.download-config-page')); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * Map first if necessary, | ||||
|      * | ||||
|      * STEP FIVE. | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function map() | ||||
|     { | ||||
|  | ||||
|         // Make sure all fields we need are accounted for. | ||||
|         $fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-map', 'csv-roles']; | ||||
|         if (!$this->wizard->sessionHasValues($fields)) { | ||||
|             Session::flash('warning', 'Could not recover upload.'); | ||||
|  | ||||
|             return redirect(route('csv.index')); | ||||
|         } | ||||
|         /* | ||||
|          * The "options" array contains all options the user has | ||||
|          * per column, where the key represents the column. | ||||
|          * | ||||
|          * For each key there is an array which in turn represents | ||||
|          * all the options available: grouped by ID. | ||||
|          * | ||||
|          * options[column index] = [ | ||||
|          *       field id => field identifier. | ||||
|          * ] | ||||
|          */ | ||||
|         try { | ||||
|             $options = $this->wizard->showOptions($this->data->getMap()); | ||||
|         } catch (FireflyException $e) { | ||||
|             return view('error', ['message' => $e->getMessage()]); | ||||
|         } | ||||
|  | ||||
|         // After these values are prepped, read the actual CSV file | ||||
|         $reader     = $this->data->getReader(); | ||||
|         $map        = $this->data->getMap(); | ||||
|         $hasHeaders = $this->data->hasHeaders(); | ||||
|         $values     = $this->wizard->getMappableValues($reader, $map, $hasHeaders); | ||||
|         $map        = $this->data->getMap(); | ||||
|         $mapped     = $this->data->getMapped(); | ||||
|         $subTitle   = trans('firefly.csv_map_values'); | ||||
|  | ||||
|         return view('csv.map', compact('map', 'options', 'values', 'mapped', 'subTitle')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * Finally actually process the CSV file. | ||||
|      * | ||||
|      * STEP SEVEN | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View | ||||
|      */ | ||||
|     public function process() | ||||
|     { | ||||
|         /* | ||||
|          * Make sure all fields we need are accounted for. | ||||
|          */ | ||||
|         $fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-map', 'csv-roles', 'csv-mapped']; | ||||
|         if (!$this->wizard->sessionHasValues($fields)) { | ||||
|             Session::flash('warning', 'Could not recover upload.'); | ||||
|  | ||||
|             return redirect(route('csv.index')); | ||||
|         } | ||||
|  | ||||
|         Log::debug('Created importer'); | ||||
|         $importer = new Importer; | ||||
|         $importer->setData($this->data); | ||||
|         try { | ||||
|             $importer->run(); | ||||
|         } catch (FireflyException $e) { | ||||
|             Log::error('Catch error: ' . $e->getMessage()); | ||||
|  | ||||
|             return view('error', ['message' => $e->getMessage()]); | ||||
|         } | ||||
|         Log::debug('Done importing!'); | ||||
|  | ||||
|         $rows     = $importer->getRows(); | ||||
|         $errors   = $importer->getErrors(); | ||||
|         $imported = $importer->getImported(); | ||||
|         $journals = $importer->getJournals(); | ||||
|  | ||||
|         Preferences::mark(); | ||||
|  | ||||
|         $subTitle = trans('firefly.csv_process_title'); | ||||
|  | ||||
|         return view('csv.process', compact('rows', 'errors', 'imported', 'subTitle', 'journals')); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store the mapping the user has made. This is | ||||
|      * | ||||
|      * STEP SIX | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function saveMapping() | ||||
|     { | ||||
|         /* | ||||
|          * Make sure all fields we need are accounted for. | ||||
|          */ | ||||
|         $fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-map', 'csv-roles']; | ||||
|         if (!$this->wizard->sessionHasValues($fields)) { | ||||
|             Session::flash('warning', 'Could not recover upload.'); | ||||
|  | ||||
|             return redirect(route('csv.index')); | ||||
|         } | ||||
|  | ||||
|         // save mapping to session. | ||||
|         $mapped = []; | ||||
|         if (!is_array(Input::get('mapping'))) { | ||||
|             Session::flash('warning', 'Invalid mapping.'); | ||||
|  | ||||
|             return redirect(route('csv.map')); | ||||
|         } | ||||
|  | ||||
|         foreach (Input::get('mapping') as $index => $data) { | ||||
|             $mapped[$index] = []; | ||||
|             foreach ($data as $value => $mapping) { | ||||
|                 if (intval($mapping) !== 0) { | ||||
|                     $mapped[$index][$value] = $mapping; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         Session::put('csv-mapped', $mapped); | ||||
|  | ||||
|         // proceed to process. | ||||
|         return redirect(route('csv.download-config-page')); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * This method processes the file, puts it away somewhere safe | ||||
|      * and sends you onwards. | ||||
|      * | ||||
|      * STEP TWO | ||||
|      * | ||||
|      * @param Request $request | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function upload(Request $request) | ||||
|     { | ||||
|         if (!$request->hasFile('csv')) { | ||||
|             Session::flash('warning', 'No file uploaded.'); | ||||
|  | ||||
|             return redirect(route('csv.index')); | ||||
|         } | ||||
|  | ||||
|         $fullPath                   = $this->wizard->storeCsvFile($request->file('csv')->getRealPath()); | ||||
|         $settings                   = []; | ||||
|         $settings['date-format']    = Input::get('date_format'); | ||||
|         $settings['has-headers']    = intval(Input::get('has_headers')) === 1; | ||||
|         $settings['specifix']       = Input::get('specifix'); | ||||
|         $settings['import-account'] = intval(Input::get('csv_import_account')); | ||||
|         $settings['map']            = []; | ||||
|         $settings['mapped']         = []; | ||||
|         $settings['roles']          = []; | ||||
|  | ||||
|         if ($request->hasFile('csv_config')) { // Process config file if present. | ||||
|             $data = file_get_contents($request->file('csv_config')->getRealPath()); | ||||
|             $json = json_decode($data, true); | ||||
|             if (is_array($json)) { | ||||
|                 $settings = array_merge($settings, $json); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $this->data->setCsvFileLocation($fullPath); | ||||
|         $this->data->setDateFormat($settings['date-format']); | ||||
|         $this->data->setHasHeaders($settings['has-headers']); | ||||
|         $this->data->setMap($settings['map']); | ||||
|         $this->data->setMapped($settings['mapped']); | ||||
|         $this->data->setRoles($settings['roles']); | ||||
|         $this->data->setSpecifix($settings['specifix']); | ||||
|         $this->data->setImportAccount($settings['import-account']); | ||||
|  | ||||
|         return redirect(route('csv.column-roles')); | ||||
|  | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user