mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 02:36:28 +00:00 
			
		
		
		
	Compare commits
	
		
			2352 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 094f6a7476 | ||
|  | c3b4849fa0 | ||
|  | dfd6c5379c | ||
|  | 9aa53c11e0 | ||
|  | b0d93621a8 | ||
|  | a337d9a599 | ||
|  | 7ffd77dc76 | ||
|  | 96fdf4fd93 | ||
|  | 27b8ce0f7f | ||
|  | 2f943c91d2 | ||
|  | 91c96311de | ||
|  | 0f929faa16 | ||
|  | 7a40c34cf0 | ||
|  | 462d987de9 | ||
|  | f64e8d8973 | ||
|  | 21222eb697 | ||
|  | e47d6fb3ac | ||
|  | c7fc10ac89 | ||
|  | e8b528f520 | ||
|  | b22de7bf70 | ||
|  | ec119c8f6e | ||
|  | a20b38598e | ||
|  | aa0eb47205 | ||
|  | 723db9d71e | ||
|  | 1d8dc3d65d | ||
|  | fe2716876a | ||
|  | fac0e97e5d | ||
|  | 449d009c28 | ||
|  | 55b2e6fe25 | ||
|  | 9989b3b9da | ||
|  | 7ab1cbfc1f | ||
|  | 62d19c3902 | ||
|  | 19700e7ee3 | ||
|  | de3e8edd6d | ||
|  | deda48af4a | ||
|  | 7688d7c619 | ||
|  | 76328b5c45 | ||
|  | 1de17bf06f | ||
|  | 6724daf995 | ||
|  | 7caca053a1 | ||
|  | ae1bf8c017 | ||
|  | d20b0da438 | ||
|  | a0218d7df1 | ||
|  | 5ae5d67b91 | ||
|  | 8493ed7603 | ||
|  | 804b681d40 | ||
|  | e8303bd059 | ||
|  | ac6c5d4e32 | ||
|  | 90b0d0d52c | ||
|  | 4093bdbd3e | ||
|  | a2097cf981 | ||
|  | 6a33d0c9dc | ||
|  | 525d5fb427 | ||
|  | e4946b8cd5 | ||
|  | 76b32df622 | ||
|  | bc1079364d | ||
|  | 8602febe9d | ||
|  | d55dfe27dc | ||
|  | 90c16e2a07 | ||
|  | 149c1cd9b1 | ||
|  | 20f1a43369 | ||
|  | e8fb8f993d | ||
|  | f0c782dc01 | ||
|  | 50c13e6d20 | ||
|  | 69bedd035f | ||
|  | 85337f0a31 | ||
|  | f8a7e2f98e | ||
|  | ec90a49d43 | ||
|  | 5812b150c6 | ||
|  | c7ebc7273f | ||
|  | 5226c87304 | ||
|  | 25dd1c5d35 | ||
|  | c5a9e5e56d | ||
|  | 47ed70d671 | ||
|  | cb5526f469 | ||
|  | a4f128077f | ||
|  | 7140ba76d5 | ||
|  | 872e8f2de6 | ||
|  | 6c14e9d083 | ||
|  | e4b1812b46 | ||
|  | 1c2c6bb1d0 | ||
|  | baefd4f93b | ||
|  | 4270fe07ab | ||
|  | e4ae925d2b | ||
|  | dc599361a4 | ||
|  | 738a311f49 | ||
|  | 71f6ba3418 | ||
|  | d1d573c408 | ||
|  | 50e39a4a75 | ||
|  | 8635fe7ebb | ||
|  | 6b57d4397a | ||
|  | 8f2b898b2b | ||
|  | 0d1d360d18 | ||
|  | def3b3a155 | ||
|  | d344512743 | ||
|  | 19eef71133 | ||
|  | 61d58a354e | ||
|  | be868d37f2 | ||
|  | 20bb151cf3 | ||
|  | 77f889aba6 | ||
|  | 1e69a54972 | ||
|  | 6b7a47ca28 | ||
|  | c3fdd3b5f7 | ||
|  | e9f2121667 | ||
|  | 161e9e1e11 | ||
|  | e336a45f79 | ||
|  | 9c09f93908 | ||
|  | 582398e7f6 | ||
|  | b118635abd | ||
|  | ac0d4a75b5 | ||
|  | c212d5c5ea | ||
|  | 08ac27cccf | ||
|  | 0b5cab99cf | ||
|  | cc0057cc56 | ||
|  | 1ce49b814b | ||
|  | 5bbaaece38 | ||
|  | 30bc4ccfa7 | ||
|  | 4f64f1d754 | ||
|  | c0e578dd47 | ||
|  | 2b82fca2cf | ||
|  | f0028b33e9 | ||
|  | 7ddea23375 | ||
|  | 83edccacc6 | ||
|  | 75e95d6452 | ||
|  | 423bb4bbcd | ||
|  | 43585c563c | ||
|  | 2564a41d05 | ||
|  | a0bb1e3625 | ||
|  | 9b4fd57f51 | ||
|  | e67709e339 | ||
|  | 0c4e913f30 | ||
|  | c6de0e51c7 | ||
|  | 69e85adadf | ||
|  | b34068207f | ||
|  | 68b7b4b3a4 | ||
|  | 5e3ee30e66 | ||
|  | aaf7d12b46 | ||
|  | 729a348657 | ||
|  | 0fca6eb810 | ||
|  | 5a0ae8530c | ||
|  | 7949c9ad74 | ||
|  | 6fb9362f7e | ||
|  | 3481d364cc | ||
|  | 373b9cdd9f | ||
|  | 75af63e6ac | ||
|  | 5aa62a1be4 | ||
|  | aede8bf0e0 | ||
|  | 9ab7abcb95 | ||
|  | f87b28afd9 | ||
|  | 8661f6d1ac | ||
|  | 4536b4b2b4 | ||
|  | 655f03940b | ||
|  | 4122de7823 | ||
|  | 0f4c67d24e | ||
|  | 20e8c45819 | ||
|  | 2b8b844fb2 | ||
|  | 3284b8764f | ||
|  | d19946336e | ||
|  | 770a220808 | ||
|  | 78b71e72f1 | ||
|  | 19990f49b0 | ||
|  | 8208d44466 | ||
|  | 002b2b6dee | ||
|  | c207167b14 | ||
|  | cfc066e911 | ||
|  | 3a1d011841 | ||
|  | 7d05c0da9c | ||
|  | 1d7f2ca9e4 | ||
|  | ea2e0d7546 | ||
|  | 64b79ee64c | ||
|  | 8a00101470 | ||
|  | 01aba73f5b | ||
|  | 71e31346e8 | ||
|  | 483cce9880 | ||
|  | c8db39a91e | ||
|  | 6d398a2edf | ||
|  | bd3c8119ba | ||
|  | 16aa78d13c | ||
|  | 3be5cca60a | ||
|  | bc3dfb96fd | ||
|  | e78e98a6cf | ||
|  | 9db0e48f63 | ||
|  | 3de52b6bc1 | ||
|  | be52abbe3b | ||
|  | ac55b0fafb | ||
|  | 887b6789fc | ||
|  | ff50fec112 | ||
|  | 4538ef3cf9 | ||
|  | a872cf7061 | ||
|  | 2d8ca363db | ||
|  | 8e8b011587 | ||
|  | 4241ae035e | ||
|  | 3ef569d280 | ||
|  | 6fe28b15df | ||
|  | a609a47138 | ||
|  | b575b87f77 | ||
|  | 7c5ee8a67d | ||
|  | 452c14bece | ||
|  | 57f63ba752 | ||
|  | 5f153b8a01 | ||
|  | 1be49876df | ||
|  | a79b2a7773 | ||
|  | cdf6e5a487 | ||
|  | 7c82f45604 | ||
|  | 4d49701203 | ||
|  | d48cc69898 | ||
|  | af466a1d75 | ||
|  | b9599d3aa1 | ||
|  | dbebfe7c07 | ||
|  | ddf54fdb83 | ||
|  | 619138d294 | ||
|  | 126b19bf2d | ||
|  | cc76adf7b6 | ||
|  | 83bcb56a6a | ||
|  | 6e88a70661 | ||
|  | 6755a9878b | ||
|  | b8ef7593ee | ||
|  | 602cc26f0f | ||
|  | 62271fe064 | ||
|  | 83f5f776a6 | ||
|  | 2a5566a820 | ||
|  | 398e547d06 | ||
|  | ba957196da | ||
|  | b5c4a24133 | ||
|  | cc688dc112 | ||
|  | 91b5eaff80 | ||
|  | 4a52503cb3 | ||
|  | bcd7e7ea94 | ||
|  | ba9ae54fbb | ||
|  | 39e05c9991 | ||
|  | 8962f90bcc | ||
|  | daf3a95db0 | ||
|  | 1c9ebafe2b | ||
|  | 00b3df4455 | ||
|  | 600c3e75bb | ||
|  | 6349fccd0f | ||
|  | 6ececdad26 | ||
|  | c67f1a7b93 | ||
|  | 8617ea760a | ||
|  | 41a2406f07 | ||
|  | adae8e45a9 | ||
|  | e346ae533d | ||
|  | 31789255c9 | ||
|  | dbe6edd133 | ||
|  | 7cfbcec56e | ||
|  | 9f9a055f64 | ||
|  | d3614d3505 | ||
|  | 800f67908e | ||
|  | e2c613c422 | ||
|  | 457037ed99 | ||
|  | f9f21efd36 | ||
|  | 2d59b6718d | ||
|  | 0c6d213296 | ||
|  | c34fb7f037 | ||
|  | 796be319b7 | ||
|  | d28fcdc6a5 | ||
|  | d0afcb6cfa | ||
|  | 7bd4de937a | ||
|  | 3025693178 | ||
|  | c9cc3bf3ff | ||
|  | 1f670f7a05 | ||
|  | 48d735b53b | ||
|  | b91f6c7ce6 | ||
|  | ad116d1959 | ||
|  | a0de10870d | ||
|  | eb0c00896f | ||
|  | deccd4e9fe | ||
|  | 8be4ec08ad | ||
|  | 59ad0624f2 | ||
|  | f0c69ca84f | ||
|  | 3ba1c07f68 | ||
|  | 14cd4aaac8 | ||
|  | 8a1fae5d9d | ||
|  | e323f5a2d5 | ||
|  | c5c1cbd66f | ||
|  | 4cc9dbbe6a | ||
|  | 3649991ad6 | ||
|  | 1d25691aa2 | ||
|  | 235076b465 | ||
|  | c2670fa379 | ||
|  | a769a5391d | ||
|  | 1f58c46f67 | ||
|  | f4c9f2d0e7 | ||
|  | 851b9136fe | ||
|  | 0fe10e470d | ||
|  | 8c8ea17fac | ||
|  | 7c546b8d3a | ||
|  | 63334a61ad | ||
|  | f61e65cf54 | ||
|  | 05bf752629 | ||
|  | 5096a90e34 | ||
|  | 03792b3905 | ||
|  | 995b049a5f | ||
|  | bde37ec2c7 | ||
|  | d6b3fe7e1b | ||
|  | 954b394987 | ||
|  | 97dae6dde5 | ||
|  | fe039500d6 | ||
|  | 6952957794 | ||
|  | 01cc97ad55 | ||
|  | b5c8e005e2 | ||
|  | 1c2602438f | ||
|  | 33da756a2f | ||
|  | 488d4a38b8 | ||
|  | e60f60b0f8 | ||
|  | 8aa2e3d2f5 | ||
|  | d5f65e5d07 | ||
|  | c8511a6e2a | ||
|  | 379b15be1d | ||
|  | 2ee1fea293 | ||
|  | 4385d71c6f | ||
|  | cf6ea64aba | ||
|  | 101317cb16 | ||
|  | 5990a21c46 | ||
|  | a9bc007327 | ||
|  | 0c71770b1d | ||
|  | 5bae7e9bdb | ||
|  | 1818a596fe | ||
|  | 8f7541b841 | ||
|  | 090546cda3 | ||
|  | dcd89d38e7 | ||
|  | 800478d437 | ||
|  | f797344106 | ||
|  | 9352ee3e25 | ||
|  | 811026dc4a | ||
|  | 479a4dac7b | ||
|  | 499fbbeb17 | ||
|  | a35bcf6415 | ||
|  | 818ffdfc85 | ||
|  | d5e19c7ac0 | ||
|  | 37639b0ff4 | ||
|  | 740d89dce6 | ||
|  | 4a7b08fc4e | ||
|  | 48a5f83f00 | ||
|  | 48819c928d | ||
|  | 45a6866dd0 | ||
|  | 6690586406 | ||
|  | 909e54845c | ||
|  | a7204eb9fa | ||
|  | 6463166c00 | ||
|  | f8268a864b | ||
|  | 721fff29b3 | ||
|  | 4cf312d3d4 | ||
|  | 36f1b6a834 | ||
|  | 050d7e8f00 | ||
|  | 7c5bed2bb5 | ||
|  | 87316cf7c1 | ||
|  | f7d61e5a9b | ||
|  | b2030a72a0 | ||
|  | 533797fc9e | ||
|  | 5688234b9d | ||
|  | 9335789362 | ||
|  | 9e6a2a3fa4 | ||
|  | 122fc77357 | ||
|  | c978e7965f | ||
|  | b0e4e24603 | ||
|  | 56de307a3e | ||
|  | e1dd9ed41b | ||
|  | 17a64764d3 | ||
|  | 3cd0540474 | ||
|  | 27cd9fac8a | ||
|  | 1d2012cc23 | ||
|  | 1b00835dd1 | ||
|  | 413dcf8164 | ||
|  | 7f17e8fb2f | ||
|  | 254d8994d0 | ||
|  | 4f72519ad9 | ||
|  | 900b246183 | ||
|  | abddb29f37 | ||
|  | 8d429ef753 | ||
|  | b7679b5c60 | ||
|  | 49982d6eb1 | ||
|  | 3191a6c12b | ||
|  | 32f8747f2e | ||
|  | 38e45a62cf | ||
|  | c0e2e78780 | ||
|  | 3fe3ddbc49 | ||
|  | 5ca532a54a | ||
|  | a120df090a | ||
|  | 22d359503a | ||
|  | e8d84abe43 | ||
|  | 98937cedaa | ||
|  | d592d6cd7a | ||
|  | 0341a04ee3 | ||
|  | 540fc4f924 | ||
|  | 04290bf9b6 | ||
|  | ecbc0c1778 | ||
|  | 44b8e48c3a | ||
|  | a5036c86dc | ||
|  | ac86e75233 | ||
|  | 9ec3febbfa | ||
|  | 1c5dc6ab6d | ||
|  | abb8eafec2 | ||
|  | eb8f5512c5 | ||
|  | d146476c91 | ||
|  | 7a57670925 | ||
|  | 8a49e98246 | ||
|  | cf0845d190 | ||
|  | 02bbdcc251 | ||
|  | 13f6bd759b | ||
|  | 497400587d | ||
|  | a58cd83ea7 | ||
|  | 3f802fe27a | ||
|  | 6a13dd317d | ||
|  | a442d3d952 | ||
|  | 0d4febff85 | ||
|  | ba222eaf77 | ||
|  | b14719464c | ||
|  | c756b80962 | ||
|  | a54a886bf0 | ||
|  | dbe9628cc5 | ||
|  | 7a3b39886e | ||
|  | fab511cc53 | ||
|  | 4979d9d0bf | ||
|  | 45914b2e9e | ||
|  | 1e9aaf2d2a | ||
|  | de56c18c6e | ||
|  | eaefb7136a | ||
|  | fe9344cd0a | ||
|  | f010c17ae6 | ||
|  | f63cd74965 | ||
|  | 9e8f8f76a4 | ||
|  | d88c6a82d0 | ||
|  | a8fdf7ffad | ||
|  | 245389d74f | ||
|  | 26933637dd | ||
|  | 98312ac554 | ||
|  | 1ba03088c9 | ||
|  | c0dfc554b3 | ||
|  | 5c691491e8 | ||
|  | 9731b59174 | ||
|  | 52bf358978 | ||
|  | c92a56c980 | ||
|  | 3142151fc3 | ||
|  | fb555f5b96 | ||
|  | 8f1c693d3d | ||
|  | b8a8becd0c | ||
|  | b71abd3f6a | ||
|  | 9ae74b4278 | ||
|  | bdbf434006 | ||
|  | 1a5e93c739 | ||
|  | 8e42ba74c6 | ||
|  | 42bb083e99 | ||
|  | ae4eecc7f2 | ||
|  | c4f25b6191 | ||
|  | 29b200040f | ||
|  | 7cb1598fb1 | ||
|  | 65b6f162d8 | ||
|  | c56d2e08f4 | ||
|  | ca0a0886b1 | ||
|  | e9822ae1a3 | ||
|  | 04b284f030 | ||
|  | 9ef24c0a43 | ||
|  | 7ee650ba7a | ||
|  | 96cafed154 | ||
|  | f65c2ff4fb | ||
|  | 121deec62f | ||
|  | 838d0808c0 | ||
|  | 974fbe9e5b | ||
|  | f26f94ad3b | ||
|  | 7410f1944c | ||
|  | c34947f657 | ||
|  | 54092118e1 | ||
|  | 866a7d7401 | ||
|  | 32ab916707 | ||
|  | 1a245f1303 | ||
|  | a23c61ee3c | ||
|  | f44336f7aa | ||
|  | 98d4bc48b6 | ||
|  | a3f1b72bac | ||
|  | a37f70947b | ||
|  | 71195aa789 | ||
|  | f6511bed32 | ||
|  | 619500ca64 | ||
|  | 986d290434 | ||
|  | 878f8c58bb | ||
|  | e067da1fe9 | ||
|  | f340c636fe | ||
|  | ce260a1a1e | ||
|  | a21c9f15e3 | ||
|  | e64b778d13 | ||
|  | a1f139f62a | ||
|  | 8ae1d1c963 | ||
|  | 8f8016179b | ||
|  | 2e32e994c3 | ||
|  | 1575e3b045 | ||
|  | 9ab5f68601 | ||
|  | 7fcb806dfe | ||
|  | 5ae736c7cc | ||
|  | d77ba9970b | ||
|  | 49f97a2c7b | ||
|  | 659ff89062 | ||
|  | 5529641bea | ||
|  | b38f1d7b2a | ||
|  | 53ba202b14 | ||
|  | 11cc333de7 | ||
|  | 70e47ab4d0 | ||
|  | 1582b35ae2 | ||
|  | 62c27cee6c | ||
|  | 81b8bc9e93 | ||
|  | 49758c4e72 | ||
|  | 001ef4fe1c | ||
|  | 94d0401f4e | ||
|  | 2dbd9bd0b1 | ||
|  | 9168c97eb6 | ||
|  | 758953b6e3 | ||
|  | 8ccdf9ea83 | ||
|  | 9c6a3e4ad5 | ||
|  | 6151d4a0ec | ||
|  | 61014d45f4 | ||
|  | 05a93a2426 | ||
|  | a4c7412220 | ||
|  | 94e51952f4 | ||
|  | ebdd64f46f | ||
|  | 2dc70ece44 | ||
|  | c23ea5ea76 | ||
|  | 6521a7c604 | ||
|  | 02e792148c | ||
|  | 69c350dcca | ||
|  | 1aee3d8e2c | ||
|  | 02695d852c | ||
|  | 7405138489 | ||
|  | 4804257fd1 | ||
|  | 67f2e3a32a | ||
|  | 8d709f9cf4 | ||
|  | 4d3132f1c9 | ||
|  | 36b44f1814 | ||
|  | 32761aeda0 | ||
|  | 851b05c110 | ||
|  | 997e951aca | ||
|  | 448dc6b7c6 | ||
|  | 84458fa46f | ||
|  | 50bb8a0d91 | ||
|  | 997b3c3061 | ||
|  | 4f240c004c | ||
|  | 597a8d36af | ||
|  | cf52a4c5c2 | ||
|  | c29180a094 | ||
|  | 10f4304559 | ||
|  | 30447bcf70 | ||
|  | 9ff9385c47 | ||
|  | 6c5499e848 | ||
|  | 3bacbe8536 | ||
|  | 09c7a69050 | ||
|  | 5dc727580f | ||
|  | 248a4ed527 | ||
|  | db95185eee | ||
|  | 85dae15a0d | ||
|  | 3e61a1e12b | ||
|  | 6cd4186ac9 | ||
|  | cbbadc3d6d | ||
|  | fc0024faa2 | ||
|  | 0f3d4062d7 | ||
|  | 7ba8a88989 | ||
|  | 349d254193 | ||
|  | be201e811d | ||
|  | 84a032fbb4 | ||
|  | 4815602558 | ||
|  | e4b83392be | ||
|  | 0658c17adb | ||
|  | bdc72aee42 | ||
|  | 689d91e30f | ||
|  | 6b785e4318 | ||
|  | f46cf55912 | ||
|  | d520849ce1 | ||
|  | 50661bbb3b | ||
|  | d2d5b1ac76 | ||
|  | 244972e0f8 | ||
|  | f80e6c2efa | ||
|  | e9e32eda3c | ||
|  | 73844e223f | ||
|  | 6583a6d9c6 | ||
|  | ca4824adcd | ||
|  | 80b5cc08bb | ||
|  | afbcc79a06 | ||
|  | 3371bd2e04 | ||
|  | 5efdf53c06 | ||
|  | c9112de8ba | ||
|  | fd4b589a13 | ||
|  | df813dbac9 | ||
|  | 004fb362ec | ||
|  | 3cd749753a | ||
|  | c7964f7693 | ||
|  | 57bba2fd3f | ||
|  | 04c9b2a7a8 | ||
|  | b9d142c2b7 | ||
|  | 6ab52e282f | ||
|  | b14adf8c3f | ||
|  | 4e0b162f5f | ||
|  | 62d47ff7f0 | ||
|  | 7f025380f0 | ||
|  | 7d1e981bca | ||
|  | a08103f996 | ||
|  | dd4991a4f8 | ||
|  | 5442292d23 | ||
|  | 3f050d3d03 | ||
|  | ad1e9c27e9 | ||
|  | ab761696bf | ||
|  | 0713273a99 | ||
|  | 5668a3271b | ||
|  | 1eca105a91 | ||
|  | 3883b99c24 | ||
|  | d6adbc697a | ||
|  | a5789b1085 | ||
|  | a6ccbcb795 | ||
|  | 1a6067f7ae | ||
|  | cb735b18a9 | ||
|  | 909bd11147 | ||
|  | 1a76c606ed | ||
|  | 8c9b6796a1 | ||
|  | 844ab608d4 | ||
|  | dc39094975 | ||
|  | b32184d525 | ||
|  | d95ae53ce2 | ||
|  | 5e3147ddeb | ||
|  | 9e594c6075 | ||
|  | c0058c51ea | ||
|  | b0b68d4243 | ||
|  | 22eb90212d | ||
|  | 94e264b6ce | ||
|  | 7ea15761a6 | ||
|  | 1ced4a089d | ||
|  | 648e63628c | ||
|  | 2847e2aff5 | ||
|  | 9dfaabb5d0 | ||
|  | 6a21f98ea4 | ||
|  | 4d5f4cc1c0 | ||
|  | 970ce6cb0d | ||
|  | 31cad5de00 | ||
|  | e06db9e620 | ||
|  | f57ac64dc2 | ||
|  | 57d7c1623f | ||
|  | c86aa9cb3f | ||
|  | 48209d0d22 | ||
|  | 8f6a271cc0 | ||
|  | a9b610f367 | ||
|  | 1046930f29 | ||
|  | 1b16e5e216 | ||
|  | e16ba9ac70 | ||
|  | 71ac676b83 | ||
|  | 1b6c0d5d86 | ||
|  | 14db016e98 | ||
|  | 7e2e1626ac | ||
|  | bce4e7e2bf | ||
|  | ede327f3d3 | ||
|  | 82718a74dc | ||
|  | eefd6141a1 | ||
|  | 7894f1871e | ||
|  | 0ef9b5b462 | ||
|  | 9ca75d134e | ||
|  | b78776e1f7 | ||
|  | f2f9f8fbab | ||
|  | 5b5acba816 | ||
|  | 9f2729d0ff | ||
|  | afe98cda9f | ||
|  | 9c4d2e8791 | ||
|  | cea170359f | ||
|  | 70bb8fbc89 | ||
|  | 82cd0adca6 | ||
|  | e821f5b2b6 | ||
|  | 4cade467c6 | ||
|  | b6c9639948 | ||
|  | ca9319db34 | ||
|  | beaec9a4c1 | ||
|  | cbc44e8200 | ||
|  | 017b1a481a | ||
|  | e15932fe4a | ||
|  | 08c044fe52 | ||
|  | 0e11245cb4 | ||
|  | cde494d3ef | ||
|  | 9a15decdff | ||
|  | 186b986e02 | ||
|  | cdbf5653ac | ||
|  | c403dd7490 | ||
|  | d15d9fdf2a | ||
|  | 0b618de44c | ||
|  | 875f19f728 | ||
|  | 7bb549732c | ||
|  | b9baa93ae4 | ||
|  | 315479fcd3 | ||
|  | 1f1334a1fc | ||
|  | bf0744e03a | ||
|  | 8fb9577660 | ||
|  | 90d58c5c39 | ||
|  | b6aa79bb38 | ||
|  | 14a0de6b6a | ||
|  | 13e56b7249 | ||
|  | 3753901e38 | ||
|  | e76075e29f | ||
|  | 284db7f90b | ||
|  | cabdf4e380 | ||
|  | 9859052c4d | ||
|  | 0feeac9160 | ||
|  | 54b33a0b69 | ||
|  | e08e7b2c9b | ||
|  | 782e2add88 | ||
|  | f18a5a6f1b | ||
|  | 6fc971c4cb | ||
|  | 3250c4830d | ||
|  | 9e1a69217d | ||
|  | 46c26a64d8 | ||
|  | 2f12a70647 | ||
|  | be190d1fa0 | ||
|  | 1e4888209b | ||
|  | 8aa2961c19 | ||
|  | 304cdabc96 | ||
|  | c60e272eb3 | ||
|  | c074f55cb2 | ||
|  | e6af29646e | ||
|  | b4213328fe | ||
|  | 8a7628c9dc | ||
|  | d52c146e12 | ||
|  | 1910a4bd4b | ||
|  | bd0c552f54 | ||
|  | b29ea98de4 | ||
|  | dd1db87806 | ||
|  | 6f9e446577 | ||
|  | 664230dca8 | ||
|  | 1a24e7e0aa | ||
|  | 9239815ce6 | ||
|  | 116e19ec06 | ||
|  | fc0ad622eb | ||
|  | 2c5cdb8780 | ||
|  | 9a309f32fa | ||
|  | e2e54d342a | ||
|  | 42f7529495 | ||
|  | f172151252 | ||
|  | e2ad38d3e0 | ||
|  | 40cc32fc5a | ||
|  | 436c034fdd | ||
|  | 286b1848d9 | ||
|  | 7fffebf6df | ||
|  | b1764478ec | ||
|  | 6b6a799206 | ||
|  | 0a82ed901e | ||
|  | d733c9ed14 | ||
|  | a752ea489c | ||
|  | 876a24586f | ||
|  | ea2779cf9a | ||
|  | 77aa36163d | ||
|  | b581d8ecb7 | ||
|  | 83b404d01e | ||
|  | 8deb92c3e5 | ||
|  | 20a6e0170c | ||
|  | 944a78807c | ||
|  | 0b02d294f4 | ||
|  | a5d86536c3 | ||
|  | 71c08cfe0c | ||
|  | 8ab0d5fc48 | ||
|  | 57f81ee4c8 | ||
|  | 5c28adf266 | ||
|  | 5a57398f81 | ||
|  | 8121a384ef | ||
|  | 8666197e05 | ||
|  | 1ea2b8bbcb | ||
|  | a71cedd8a9 | ||
|  | 04c5f583f6 | ||
|  | 7716ff4e8c | ||
|  | 6b51a116d1 | ||
|  | b2f14dc177 | ||
|  | da1d3b82f9 | ||
|  | 6282d8c828 | ||
|  | 73129b0ce5 | ||
|  | f71e7a2f28 | ||
|  | 341da327e3 | ||
|  | 3d8adfa7e4 | ||
|  | 279d7769f5 | ||
|  | b7d3b40353 | ||
|  | 7ecd691ee2 | ||
|  | f3398c7dec | ||
|  | 90644e662d | ||
|  | f5c5cb7fb9 | ||
|  | 312e79921a | ||
|  | b83d346a86 | ||
|  | 3eed67f108 | ||
|  | 15f0bc63b2 | ||
|  | 0a4b0ec929 | ||
|  | 560f6cbf24 | ||
|  | 9165e0238f | ||
|  | 97d6be6809 | ||
|  | 4de14eba0c | ||
|  | 6c64023bf7 | ||
|  | a923c288e6 | ||
|  | 4c1d8e8e85 | ||
|  | 02f2def88b | ||
|  | 4bcacc5d68 | ||
|  | 913dbe6b1a | ||
|  | ce8164dd87 | ||
|  | a5b412f546 | ||
|  | 82bb352624 | ||
|  | ebbf2659b1 | ||
|  | d0084becea | ||
|  | 6af2b37ac2 | ||
|  | 814fc6eabd | ||
|  | 50278a679a | ||
|  | d42e9c75ef | ||
|  | 00b3dced2c | ||
|  | 5c0c00188f | ||
|  | 2ec56626f3 | ||
|  | e87456b2f8 | ||
|  | 3609b515e5 | ||
|  | a1609542c3 | ||
|  | 4c4625583a | ||
|  | 7b479316ea | ||
|  | b021c7690f | ||
|  | 2be060796e | ||
|  | 1b4d55cca4 | ||
|  | a8cea4119d | ||
|  | e247aace8d | ||
|  | 41553e9b86 | ||
|  | e875587260 | ||
|  | 5377483345 | ||
|  | 4112acfb8d | ||
|  | f3bc02e11c | ||
|  | 8e411a898b | ||
|  | 915edbecc9 | ||
|  | 975a6c34bf | ||
|  | cdd988b4de | ||
|  | b58bc97422 | ||
|  | 482688ac3c | ||
|  | aea31b5e28 | ||
|  | d7cbc53b4b | ||
|  | f74c6c2d19 | ||
|  | 3080d2ddc4 | ||
|  | 4ad5881760 | ||
|  | 7e55d1a4fd | ||
|  | 7ef5eed6e2 | ||
|  | 10aa41a7ea | ||
|  | 1f9b362b6f | ||
|  | 4bf9bfb521 | ||
|  | 1d7119114d | ||
|  | e1b6df6fb1 | ||
|  | 7cf38bb01e | ||
|  | e34ec22845 | ||
|  | 46506abeb8 | ||
|  | 95654cc4d4 | ||
|  | 47aded820d | ||
|  | 24444ebf08 | ||
|  | bdc0df8350 | ||
|  | b2c9a2973c | ||
|  | da2a347511 | ||
|  | 6fbc3ba060 | ||
|  | 02eff06cd3 | ||
|  | 7586d4b494 | ||
|  | 9059f0fee6 | ||
|  | c2af9e3d20 | ||
|  | 0b51366526 | ||
|  | e40260bd9c | ||
|  | cf2842840d | ||
|  | 17fa8fcb2c | ||
|  | 0d2f9864e2 | ||
|  | 89cbd91204 | ||
|  | f4d9b57887 | ||
|  | 4b2e4afca5 | ||
|  | dd1ba30c48 | ||
|  | 3ba4570691 | ||
|  | 848cfabcba | ||
|  | 1bbd10b909 | ||
|  | a16a4f813d | ||
|  | 91cfa963b2 | ||
|  | a35557eb62 | ||
|  | aad4e47b6a | ||
|  | 1b177723ae | ||
|  | 99dba92bd3 | ||
|  | e13ccff056 | ||
|  | 46528dd29d | ||
|  | 4f611ad810 | ||
|  | af41985a64 | ||
|  | d0864e06b5 | ||
|  | 6f0366e146 | ||
|  | e0cdbcb28c | ||
|  | f19b99194c | ||
|  | 43a55e2e35 | ||
|  | b2743825ca | ||
|  | d4f6cce56e | ||
|  | 6092d206b6 | ||
|  | c8ad83cc91 | ||
|  | 7d31071ff8 | ||
|  | c975ef15f1 | ||
|  | f855011d34 | ||
|  | fbcf0929d8 | ||
|  | d89e75cbe8 | ||
|  | ccaa42ad74 | ||
|  | 56d8dce622 | ||
|  | c79baf98cf | ||
|  | d1cab9f68c | ||
|  | 69c5c93353 | ||
|  | 28ebd683e4 | ||
|  | d752edd625 | ||
|  | 1dab45d493 | ||
|  | b99982d02b | ||
|  | fff17ac6c1 | ||
|  | 4086257983 | ||
|  | bd9e0ac281 | ||
|  | b075d6db5e | ||
|  | befd79cf14 | ||
|  | 07f68d2b14 | ||
|  | d14889bd27 | ||
|  | 91e40c14f9 | ||
|  | b7b2206262 | ||
|  | f344d0319c | ||
|  | 0c8a1682b6 | ||
|  | 39866be3f1 | ||
|  | 947e82fa0f | ||
|  | 0335a64a21 | ||
|  | a9e57e1c34 | ||
|  | 8a8279f97a | ||
|  | b968889552 | ||
|  | 4068df5e50 | ||
|  | dc42370322 | ||
|  | 8c24f14ee5 | ||
|  | 494d1743a2 | ||
|  | 4a30d9f6bb | ||
|  | ed6d25067c | ||
|  | 445ae7e10e | ||
|  | 6f45609161 | ||
|  | f1230e47f7 | ||
|  | 7e0ef6d43e | ||
|  | 14f9da544a | ||
|  | 5a84036e16 | ||
|  | 4dccf7b7b5 | ||
|  | 66060dbed4 | ||
|  | cfb824588f | ||
|  | d2b4316d7a | ||
|  | 3af69b433d | ||
|  | a6733fa255 | ||
|  | 4277c54009 | ||
|  | 66baa7554a | ||
|  | ffca4b0543 | ||
|  | 3e3c48314f | ||
|  | 06ff450d31 | ||
|  | 07c57cc640 | ||
|  | a67f10c99e | ||
|  | 2882bcbf7b | ||
|  | 67cc5b0280 | ||
|  | b42b178b71 | ||
|  | 7de05cd173 | ||
|  | 3db43743d9 | ||
|  | 14638e4ed8 | ||
|  | e756b93810 | ||
|  | 358d83dcfc | ||
|  | 331c231a94 | ||
|  | 4403b65bae | ||
|  | a27d80d765 | ||
|  | 04272fff81 | ||
|  | e963708c54 | ||
|  | 08c4542847 | ||
|  | 553e9270e5 | ||
|  | 8a7297e131 | ||
|  | 0f260da8e6 | ||
|  | 77560ab3a8 | ||
|  | e3b2f2d9a8 | ||
|  | 74e01a52b9 | ||
|  | dc28ba42ef | ||
|  | 406150620a | ||
|  | 43f59a1135 | ||
|  | 5c02eaa66c | ||
|  | b4eac84097 | ||
|  | ec3b356f86 | ||
|  | bf99d5c299 | ||
|  | a297131440 | ||
|  | bae2161ee3 | ||
|  | 0fe0de1a7f | ||
|  | e7845115f6 | ||
|  | bc11c3fab2 | ||
|  | 1b7546f3f9 | ||
|  | 663be30117 | ||
|  | cf34713518 | ||
|  | 3f56a8ec53 | ||
|  | 35d105588b | ||
|  | 122d988ed2 | ||
|  | 9fcc5e7a67 | ||
|  | 9a492c3731 | ||
|  | 4f752031f3 | ||
|  | 19be8bb891 | ||
|  | 693e1b08c7 | ||
|  | 9aad380518 | ||
|  | 8c518c8d58 | ||
|  | 9af89a19db | ||
|  | 939b18b86c | ||
|  | 108e775a15 | ||
|  | 653692ade0 | ||
|  | 72c6bfee7e | ||
|  | ac92939429 | ||
|  | 052957bbd0 | ||
|  | 97e6afe3dc | ||
|  | 1fd028dfb8 | ||
|  | c73866f47c | ||
|  | b0e120abee | ||
|  | b2da38d401 | ||
|  | cabe2579fa | ||
|  | 18a845ac55 | ||
|  | a4d14f8259 | ||
|  | 9d084e62f7 | ||
|  | 0393fcd704 | ||
|  | edb5b2ed5e | ||
|  | 529bab1112 | ||
|  | ab9212a4c9 | ||
|  | b2cbba0f3b | ||
|  | ca73ef8531 | ||
|  | d13490cb6e | ||
|  | 73566e11c0 | ||
|  | 36ebd0f0ee | ||
|  | efe290d96c | ||
|  | da3988cc63 | ||
|  | df6f4aecf8 | ||
|  | db1a60b6df | ||
|  | d79866f115 | ||
|  | cdd18b229e | ||
|  | cca2de9f1b | ||
|  | 6a58dbb207 | ||
|  | 779f461491 | ||
|  | 085eca6c02 | ||
|  | 25db11a8c7 | ||
|  | 76bcc68ab9 | ||
|  | 9daefaaca4 | ||
|  | fbbbcc4e74 | ||
|  | 2e1f31a7f8 | ||
|  | 8a0ac81fd0 | ||
|  | cdd50dfdd2 | ||
|  | a05c8ca351 | ||
|  | 2ca584f097 | ||
|  | 687da83feb | ||
|  | c799fc655d | ||
|  | 27848f55ce | ||
|  | 001a6e310e | ||
|  | ad00bc2806 | ||
|  | d8e3365345 | ||
|  | 5849fe2c30 | ||
|  | 690b498197 | ||
|  | d5ddd447bc | ||
|  | 628c7cd055 | ||
|  | f4887bbbf7 | ||
|  | d8f291be6e | ||
|  | 02257e3887 | ||
|  | bebfbf0b90 | ||
|  | 9cb3bfaa57 | ||
|  | 8e2c035536 | ||
|  | 6b56c2bf7c | ||
|  | d91b9e71d5 | ||
|  | 344916d57e | ||
|  | b1ef225bd0 | ||
|  | b713eae009 | ||
|  | 098cc88d5f | ||
|  | 2476dd38b3 | ||
|  | 8fec569dbb | ||
|  | ba92aa207c | ||
|  | f7abf132e2 | ||
|  | 38919ae300 | ||
|  | bba15cef24 | ||
|  | e8792fa218 | ||
|  | c5f81d4a94 | ||
|  | a7b8c9d94d | ||
|  | f4b9b7ae84 | ||
|  | 905a2432c6 | ||
|  | 89e4c3de25 | ||
|  | 86ea9db37e | ||
|  | 62a9fda1c2 | ||
|  | 49f7c1bbc1 | ||
|  | 9dc6f41c18 | ||
|  | 0a844e4313 | ||
|  | 53daa89fcb | ||
|  | c5d31bccc5 | ||
|  | b032825342 | ||
|  | 8377a2a0de | ||
|  | 57e49c225b | ||
|  | 6638f6fb5c | ||
|  | 71e1b58f1d | ||
|  | a87cb0fc0b | ||
|  | 2e65f63e4a | ||
|  | 5fb2db4e28 | ||
|  | 238ae125b5 | ||
|  | 110d7f691c | ||
|  | 9fb9c7e3ee | ||
|  | a95b1857fe | ||
|  | ea97b817fc | ||
|  | 0eea85a884 | ||
|  | eae4e988be | ||
|  | bdf752bf7e | ||
|  | a19fed5959 | ||
|  | 7474553832 | ||
|  | 52567116c2 | ||
|  | a70b369aaf | ||
|  | 33a9e80d9d | ||
|  | 96ef409f75 | ||
|  | 8f5152e185 | ||
|  | f5f17d1f40 | ||
|  | b960f50f38 | ||
|  | 72e357b673 | ||
|  | b33aa733c7 | ||
|  | a6a2c0c182 | ||
|  | 3097ab84fa | ||
|  | dd9ce3e06d | ||
|  | 560fc8b01c | ||
|  | f72aba6939 | ||
|  | 03bc74cae9 | ||
|  | 7eaf8e3eeb | ||
|  | b2b4732657 | ||
|  | 70473b7635 | ||
|  | e4a9e23dfb | ||
|  | f0fd5324ea | ||
|  | addebad810 | ||
|  | 253466c533 | ||
|  | 885d0f1464 | ||
|  | 4743cc40a2 | ||
|  | 92bf9c9214 | ||
|  | cd80d82ad4 | ||
|  | 1b7b6a676d | ||
|  | 1c61afca07 | ||
|  | d4d812c195 | ||
|  | ab7803f210 | ||
|  | 11007f0476 | ||
|  | 6b1884a9e0 | ||
|  | 1112a0761f | ||
|  | 807947fcd8 | ||
|  | 7afd8f99cb | ||
|  | b14a15ce49 | ||
|  | 2e6ad0ce5d | ||
|  | 8cdbc96aa5 | ||
|  | 956019ff4a | ||
|  | 8279cf0e88 | ||
|  | 43c32abfe8 | ||
|  | 0e66939408 | ||
|  | 22d2a523fb | ||
|  | bc825a8603 | ||
|  | c9cfda34a1 | ||
|  | e8dfbff73f | ||
|  | 62e41f1997 | ||
|  | 8c9f90f1b4 | ||
|  | 1453a78e49 | ||
|  | 7efaf51595 | ||
|  | 6bc6674ab1 | ||
|  | d6c7ff0ccb | ||
|  | 28f655dba1 | ||
|  | 6a3de12894 | ||
|  | c7940333ec | ||
|  | 8860378757 | ||
|  | 728fda0116 | ||
|  | 0c72e1831f | ||
|  | 7da21976ec | ||
|  | b739859c64 | ||
|  | d25665f843 | ||
|  | 1f41f7bd0f | ||
|  | dd8638ca98 | ||
|  | 4ba9ff05b0 | ||
|  | 618aad5432 | ||
|  | e46fc7501e | ||
|  | 7b91e98d46 | ||
|  | 85be218f92 | ||
|  | 71206e395e | ||
|  | 9b2d2e16b0 | ||
|  | 5ae01b382e | ||
|  | d92a0753a6 | ||
|  | f937a74507 | ||
|  | c3584ad20c | ||
|  | c049d5cfa6 | ||
|  | 6c9990e0be | ||
|  | b34e4cd31b | ||
|  | 7852b8a785 | ||
|  | 6eeb60db5c | ||
|  | d076cfc08f | ||
|  | 68a93ff97c | ||
|  | 295dcb4f65 | ||
|  | d9849f60c0 | ||
|  | 7ebb68e36c | ||
|  | f029f7607b | ||
|  | 2ba5733ebc | ||
|  | 3fe1d1d368 | ||
|  | 438c372583 | ||
|  | 797aa4858e | ||
|  | 8c858cd066 | ||
|  | 85aebd39b9 | ||
|  | 9a5a037424 | ||
|  | 7d557cbf91 | ||
|  | dbbc85a576 | ||
|  | eb78cf20c2 | ||
|  | 4a99399952 | ||
|  | 6075d75ee2 | ||
|  | f4c56fee66 | ||
|  | 04c59304da | ||
|  | 4b3c31a11a | ||
|  | 14576d2753 | ||
|  | 72ca1c20c7 | ||
|  | 93645819b8 | ||
|  | 39468f871b | ||
|  | faa47781d2 | ||
|  | 2c196bab6d | ||
|  | 9ae71075ef | ||
|  | 0013cdfa78 | ||
|  | 52f3f64f7b | ||
|  | 670fa77dd7 | ||
|  | 8baea2feb9 | ||
|  | c56f937521 | ||
|  | 0b613c3b8c | ||
|  | 78f297e18f | ||
|  | bd8a285d6d | ||
|  | b44602fd55 | ||
|  | 41238903e1 | ||
|  | a0c88e9b33 | ||
|  | 5d184aa53e | ||
|  | 9f9bf86a9f | ||
|  | 53af9345eb | ||
|  | da6bcf04df | ||
|  | ec4ec1a147 | ||
|  | 350e0b08b1 | ||
|  | 9340ca09e6 | ||
|  | a1cef5c339 | ||
|  | 94875adb6c | ||
|  | 75a524c656 | ||
|  | e1e94a788c | ||
|  | 8417f45d02 | ||
|  | 685310a368 | ||
|  | 45e7a4576a | ||
|  | f8c5c15655 | ||
|  | 26190524f4 | ||
|  | 5d901a7ecb | ||
|  | 929d8b3adc | ||
|  | cd6e37b9cb | ||
|  | b647386541 | ||
|  | 174fd88435 | ||
|  | cc9211b7c2 | ||
|  | a9795fb095 | ||
|  | 8554aae21e | ||
|  | 5a2ef36f2a | ||
|  | 01e3f91ece | ||
|  | 7ec9c090cc | ||
|  | b057d69f8e | ||
|  | ff4e1838bc | ||
|  | e4ecd0b7ff | ||
|  | 1ba35f73e1 | ||
|  | 240f3c126b | ||
|  | 23925a0076 | ||
|  | 50b72cf229 | ||
|  | ee6b72afa5 | ||
|  | 781621960d | ||
|  | e15ea04186 | ||
|  | 73f0cc705b | ||
|  | 0c072c7d51 | ||
|  | 884bed85a1 | ||
|  | a319264428 | ||
|  | 6506e70a91 | ||
|  | e6fcb19db7 | ||
|  | a3fba53182 | ||
|  | f8438dd9d3 | ||
|  | 028a0dcae1 | ||
|  | b4a06b5bbd | ||
|  | 4fe1a5d527 | ||
|  | 865930c5b2 | ||
|  | 96b4e2c196 | ||
|  | b7e7c7e9e2 | ||
|  | 7771669db7 | ||
|  | ef59eb6e1f | ||
|  | a14b2bc5a7 | ||
|  | 47349589cb | ||
|  | 79afe84f30 | ||
|  | 171187b25c | ||
|  | 7f1b661e61 | ||
|  | 2c2a3a5475 | ||
|  | 1677ca9619 | ||
|  | 204da3e846 | ||
|  | f36d423b1e | ||
|  | 79c7280046 | ||
|  | e10fc4a854 | ||
|  | 5088df103f | ||
|  | 13b96f6136 | ||
|  | 757662ca4b | ||
|  | 4ef324cf24 | ||
|  | cb02e0ee71 | ||
|  | ec3a90688e | ||
|  | 6dcecdcc64 | ||
|  | 25d917240d | ||
|  | 0906915a87 | ||
|  | 9c92a94177 | ||
|  | 1b125ecd22 | ||
|  | 25a2bcd76e | ||
|  | b2e09f4240 | ||
|  | 560165850f | ||
|  | 0bb07e1eeb | ||
|  | 0c0f2109f6 | ||
|  | f546670342 | ||
|  | eecb6c6679 | ||
|  | 750b9d8038 | ||
|  | 9ce28fdd2e | ||
|  | 07af64ada5 | ||
|  | a0ab0ec902 | ||
|  | 752f8582aa | ||
|  | 4d0eed8c9b | ||
|  | 0d7a8305f3 | ||
|  | 2e8c0ec537 | ||
|  | 3155ec9e2b | ||
|  | 7bbca7f6a8 | ||
|  | f7579db4ad | ||
|  | 2f47c58df5 | ||
|  | 7e7ac264d2 | ||
|  | 98d6c90e90 | ||
|  | da49afa37b | ||
|  | 64364c3e77 | ||
|  | b6f0fd1949 | ||
|  | 0663a18f3a | ||
|  | c5928897eb | ||
|  | 570373e875 | ||
|  | 228afc2eea | ||
|  | 6b61621d6a | ||
|  | 424133fa83 | ||
|  | 02e30c1fcc | ||
|  | e17a9d559b | ||
|  | 36744377f6 | ||
|  | 7c479f73c0 | ||
|  | 85b3c4683b | ||
|  | c5d2fabfec | ||
|  | a294f757ff | ||
|  | 04515da0bc | ||
|  | 6d60d64a82 | ||
|  | 32b5a84a0c | ||
|  | 4b42ef0db8 | ||
|  | abc7b9912d | ||
|  | 727717931a | ||
|  | 1d66b16468 | ||
|  | b918429c43 | ||
|  | 888273d4a0 | ||
|  | 31b5d5ba72 | ||
|  | b148d0868e | ||
|  | c1491383a8 | ||
|  | 5f07918682 | ||
|  | 8de6bd7ceb | ||
|  | 5d4f1bc76d | ||
|  | 8583b574ac | ||
|  | 3600e1b5e7 | ||
|  | fe57648349 | ||
|  | f0e0cdb49b | ||
|  | cf69333c6d | ||
|  | 3f7e16d270 | ||
|  | a63f1638f4 | ||
|  | 8ec2a3a391 | ||
|  | d875f0e580 | ||
|  | 729534b4f3 | ||
|  | bd6a56a55e | ||
|  | 96976db350 | ||
|  | 8735190461 | ||
|  | 709a14e5c9 | ||
|  | c89d2a52b5 | ||
|  | 6084d16ea8 | ||
|  | 1688fdb786 | ||
|  | 6cfb5ee2e9 | ||
|  | 2db560ed7d | ||
|  | 45567cdf65 | ||
|  | 508ad5157b | ||
|  | 8fc41e0226 | ||
|  | a08dfe1e3c | ||
|  | 49cc8a97a3 | ||
|  | 5b8583dd2b | ||
|  | f653bc5f6e | ||
|  | a6a9794fc7 | ||
|  | fdb8f61e37 | ||
|  | 69422cc796 | ||
|  | 5f9a9bc89a | ||
|  | 4d0d05e0f8 | ||
|  | 0113fedbd4 | ||
|  | a7d35cd1c3 | ||
|  | 43600fe6cb | ||
|  | 0b5e25960f | ||
|  | 0c8a1b51e9 | ||
|  | cb49f5e8d8 | ||
|  | a0e3088ca3 | ||
|  | b86be6f52f | ||
|  | 4c573e1300 | ||
|  | 1a3d77f117 | ||
|  | 2656da13b1 | ||
|  | d272ebd95c | ||
|  | 7612f1f91a | ||
|  | 22a2fe3f61 | ||
|  | 1ebb59b352 | ||
|  | 77e2cf40df | ||
|  | 0edffd8ea1 | ||
|  | ee6e047596 | ||
|  | bd55636b3f | ||
|  | b24e97a449 | ||
|  | d45355fc3f | ||
|  | b2206f640a | ||
|  | 962cad33e2 | ||
|  | d65214b75a | ||
|  | 7b4c151df5 | ||
|  | 28d6f51961 | ||
|  | d2f9deb82b | ||
|  | d9b05b5f59 | ||
|  | a8f4b33c57 | ||
|  | ee849ea12f | ||
|  | f9d3cf231f | ||
|  | 0713ca7709 | ||
|  | 1e2124c5ed | ||
|  | 37435da459 | ||
|  | 05dbd30bbd | ||
|  | 4b947638a7 | ||
|  | 3d113b9aae | ||
|  | d1b3681bf3 | ||
|  | 9dd4b07314 | ||
|  | 3814f0f3c3 | ||
|  | b1e907fae9 | ||
|  | 5c03a1a9c8 | ||
|  | 20ac07a386 | ||
|  | 13e1292bb7 | ||
|  | 8e542531b3 | ||
|  | 43afdb021a | ||
|  | aeca2ef3b2 | ||
|  | 205a593721 | ||
|  | 46649fe228 | ||
|  | adb97fcb05 | ||
|  | 98160e9b63 | ||
|  | 9c5d192d90 | ||
|  | 47bebb614e | ||
|  | 5f7fb77db2 | ||
|  | 1d15bc0b10 | ||
|  | 7bc4c6d115 | ||
|  | 45973a53f5 | ||
|  | 8e5e3de8b0 | ||
|  | 8738cd4b04 | ||
|  | 24a7dac235 | ||
|  | a3088f6806 | ||
|  | 72f7b5f3ea | ||
|  | a636c508a2 | ||
|  | 599db95f73 | ||
|  | f5f78ab79b | ||
|  | 9af9383c29 | ||
|  | 4b97b86c09 | ||
|  | 11fb46830c | ||
|  | e8dec6d95c | ||
|  | bb4ee7470d | ||
|  | 2e8071db9e | ||
|  | 4d2901aa02 | ||
|  | 37bbfab20a | ||
|  | fb9161b82d | ||
|  | 000c9d8974 | ||
|  | 878b664930 | ||
|  | afe28b5581 | ||
|  | 4106b2e4c0 | ||
|  | e1be4909b9 | ||
|  | 7a0347c0c2 | ||
|  | a7e0e3fc15 | ||
|  | 5e480eca36 | ||
|  | 6c8d594df7 | ||
|  | e24f5ec9f3 | ||
|  | 1379c0652e | ||
|  | 1f87b0bd2d | ||
|  | 787a437ca4 | ||
|  | c0bdb35cb3 | ||
|  | 4b9cf67413 | ||
|  | 86ff3be741 | ||
|  | 8bc8e8d9fe | ||
|  | 227a12d75d | ||
|  | 2ddd4314f1 | ||
|  | b980b5baea | ||
|  | 4ba34ab511 | ||
|  | 5be317d73c | ||
|  | af16205965 | ||
|  | 39917b77c1 | ||
|  | 124ecb1372 | ||
|  | 33c0c1bea6 | ||
|  | a66990459e | ||
|  | fecbdc7fbf | ||
|  | 0369ace5f7 | ||
|  | 1657048181 | ||
|  | b9bdaa7a56 | ||
|  | f28d07e17b | ||
|  | 8b8bf1debc | ||
|  | aff1c1e3ef | ||
|  | 169bb2c9bb | ||
|  | fb1eafef43 | ||
|  | bfe26ceb39 | ||
|  | 050f305e80 | ||
|  | 63a6a4f823 | ||
|  | a3b167cab5 | ||
|  | 48327948e2 | ||
|  | 93856d4577 | ||
|  | 7ff068aa95 | ||
|  | b2f00c869e | ||
|  | b717cab8f6 | ||
|  | adaff52707 | ||
|  | 54050edcc6 | ||
|  | 9acbb69a6a | ||
|  | a5e6de047a | ||
|  | 3d8d35207b | ||
|  | 0a95f59813 | ||
|  | 43a3d28dbd | ||
|  | 685cb7a505 | ||
|  | dd82466d07 | ||
|  | 2cbe4a013e | ||
|  | fb85341844 | ||
|  | 116b3ecdad | ||
|  | af85fbf0a3 | ||
|  | 1d250593c0 | ||
|  | ed33a054ad | ||
|  | 4e3e015912 | ||
|  | 7821c52842 | ||
|  | 0a6f299ae6 | ||
|  | 73f87e30c2 | ||
|  | 838ece2c89 | ||
|  | d8b88ea2c0 | ||
|  | 5908951b75 | ||
|  | 0b41f4c4d2 | ||
|  | 35439d4fbc | ||
|  | fdce40310f | ||
|  | 6b4785ae32 | ||
|  | f74e8e9cb7 | ||
|  | 5a4eb7e09e | ||
|  | 3bc4df03cc | ||
|  | 5d585132fb | ||
|  | eff4905883 | ||
|  | 8923ac4fe3 | ||
|  | 073535e5ed | ||
|  | d304b90ca6 | ||
|  | 816c26e14e | ||
|  | b1244ffa01 | ||
|  | fc1342bff9 | ||
|  | d7b95194b5 | ||
|  | b58bdeccd2 | ||
|  | f260b9bdee | ||
|  | fb1bdc9ec5 | ||
|  | 697eff48fc | ||
|  | c05019339a | ||
|  | 8438efaf41 | ||
|  | 81c019cc99 | ||
|  | c773fdc435 | ||
|  | c1406f51f1 | ||
|  | 92affd3440 | ||
|  | d3da0652ef | ||
|  | e3fbbd6cf1 | ||
|  | fcff13470c | ||
|  | e3061ee7e7 | ||
|  | 0ee305fc4a | ||
|  | 58b93fd0c4 | ||
|  | b30217fa2d | ||
|  | ae48eec3a2 | ||
|  | 948233ba27 | ||
|  | c2db9b183a | ||
|  | 6d2b88fa0b | ||
|  | 1d5da825c5 | ||
|  | 330c9b53d6 | ||
|  | 751fe7d4fb | ||
|  | 9df1fc6e5d | ||
|  | 8d660f1701 | ||
|  | 4d61d3c4aa | ||
|  | 0457088c99 | ||
|  | 8e575da74e | ||
|  | 48ed28888e | ||
|  | 4084b1124e | ||
|  | 60ba607027 | ||
|  | 3df2c11b4a | ||
|  | c93221923a | ||
|  | 375317e932 | ||
|  | 7ce527957a | ||
|  | 6946521199 | ||
|  | 18ee20e680 | ||
|  | c53da15219 | ||
|  | d4995e342f | ||
|  | c9f14da294 | ||
|  | e9c2446cba | ||
|  | 35f179625c | ||
|  | 39749aa113 | ||
|  | ba65e982fd | ||
|  | b50e5d7e59 | ||
|  | a3148dc172 | ||
|  | 73f1491d2d | ||
|  | 28eb54dc96 | ||
|  | 21fb426524 | ||
|  | d5710ca809 | ||
|  | 0ba6cdda17 | ||
|  | afdcfa8525 | ||
|  | 5db4f8512b | ||
|  | dc0c1b73bc | ||
|  | f999257095 | ||
|  | 7182909e28 | ||
|  | fe3f015171 | ||
|  | 5bb668be63 | ||
|  | 01de147900 | ||
|  | a7e5fcc806 | ||
|  | e2d187d74b | ||
|  | 48b0620629 | ||
|  | 19e9f382e4 | ||
|  | 446eaf6588 | ||
|  | 78deb1420d | ||
|  | e092515dff | ||
|  | 81f6fef978 | ||
|  | 6a2f8fa9ee | ||
|  | a79a8c8874 | ||
|  | c39659b064 | ||
|  | 9a30fbd05a | ||
|  | 83f48418f6 | ||
|  | bcd7b41c91 | ||
|  | cefb7d12bc | ||
|  | 3c0c15103e | ||
|  | a8a8afc2be | ||
|  | 49e32abd3f | ||
|  | 7977eefaca | ||
|  | f1fa6c3108 | ||
|  | 2fa0d55f39 | ||
|  | 5bff509346 | ||
|  | a147e9b74a | ||
|  | 0d87f7c4ca | ||
|  | 8c675615df | ||
|  | 7edd1bff40 | ||
|  | 3bfcb1f3ab | ||
|  | 7b6c63e6a8 | ||
|  | 5500e5b0aa | ||
|  | e4d249e73c | ||
|  | 091f6e918b | ||
|  | 5d9b68c3e7 | ||
|  | 12a6a61100 | ||
|  | 7ce3b8d4ef | ||
|  | 3d9b855849 | ||
|  | 2346d2ec05 | ||
|  | a4c081c8a5 | ||
|  | 316980efbd | ||
|  | a05bc0eed0 | ||
|  | 4d1c271da6 | ||
|  | 0dd7ecbfbe | ||
|  | 0dc188b083 | ||
|  | 6a553f77f3 | ||
|  | a74cef439b | ||
|  | 9a3cd27700 | ||
|  | 801c7c0ab6 | ||
|  | a95a4e783a | ||
|  | af1ee9db93 | ||
|  | fcdb6fd2a7 | ||
|  | 97c0fb389d | ||
|  | a9c3992331 | ||
|  | a38e057fa7 | ||
|  | f83aaf77f1 | ||
|  | d92768ecbf | ||
|  | b9308cd74a | ||
|  | 78b577bc9d | ||
|  | 7d247897ed | ||
|  | 5dcbdec491 | ||
|  | 9bf980431e | ||
|  | da60bfbcff | ||
|  | 92553cbc7e | ||
|  | 8e48e53f17 | ||
|  | 2f9a4bb79a | ||
|  | ac968dd6cd | ||
|  | 6e4f2c0c8a | ||
|  | d662c18ed7 | ||
|  | e4ea234707 | ||
|  | 0b526c0168 | ||
|  | 2acde5c72a | ||
|  | ec8cf2c459 | ||
|  | 3598780d54 | ||
|  | 35dd8ac6e6 | ||
|  | 5ff7c7ffab | ||
|  | 399db47826 | ||
|  | 148956a60d | ||
|  | 3670053a58 | ||
|  | e8e2b9704f | ||
|  | fcdeebcc06 | ||
|  | 586ed82e88 | ||
|  | cc400d1e2e | ||
|  | 6edbfb27aa | ||
|  | 8fc9251b93 | ||
|  | 10af888a97 | ||
|  | 89f2328846 | ||
|  | 48e8cd20b4 | ||
|  | 394ef23eda | ||
|  | 62aa1eb487 | ||
|  | 1500018ccc | ||
|  | 23fad62d46 | ||
|  | 3cbf00734f | ||
|  | 1dc17dd59d | ||
|  | f8935c92ea | ||
|  | de6f838413 | ||
|  | e8a095e543 | ||
|  | 717c1d080e | ||
|  | 0ae9afd325 | ||
|  | d1b56c2afa | ||
|  | 8ef7c5ac33 | ||
|  | 7180a40cd8 | ||
|  | 71804af624 | ||
|  | 85dc7f3643 | ||
|  | a866d13b75 | ||
|  | fcb5e4eabc | ||
|  | ade1cf9c19 | ||
|  | 0f1ec7d003 | ||
|  | 7e038afece | ||
|  | 9bb8e182fa | ||
|  | e94ae126fd | ||
|  | 5bb8c6a366 | ||
|  | 30844df5d4 | ||
|  | 63e4a410a7 | ||
|  | ee9a5d91e2 | ||
|  | 171ab8a4c3 | ||
|  | 96740aaac4 | ||
|  | 2017720096 | ||
|  | b77ea6d316 | ||
|  | f5adb4047f | ||
|  | b082858866 | ||
|  | a8a014189d | ||
|  | 39ea9e85a7 | ||
|  | a4d2ed74fc | ||
|  | 90f2e27f1f | ||
|  | a3359ba47a | ||
|  | 1d2d3523d6 | ||
|  | 3f40751a1a | ||
|  | b5b55e862c | ||
|  | c64771b76b | ||
|  | ea7ee7ee9a | ||
|  | a1f797c4d1 | ||
|  | d0c92a2244 | ||
|  | 6e90c033b1 | ||
|  | 24f62b8fce | ||
|  | d43936155c | ||
|  | 39dab4fdd9 | ||
|  | c0fdf44ad2 | ||
|  | 4d91f7d23a | ||
|  | 49af6522a8 | ||
|  | 3c5f9487a8 | ||
|  | f5cb87f5c3 | ||
|  | cf543613c9 | ||
|  | 5c239c91db | ||
|  | 9920504232 | ||
|  | 5540697dbd | ||
|  | b355c18e0c | ||
|  | 1e90485c5f | ||
|  | dc784c53b5 | ||
|  | 5a47391a64 | ||
|  | 8a106bd16a | ||
|  | a31ac79173 | ||
|  | 0d0a604254 | ||
|  | 724d25f2c2 | ||
|  | 8ed22d452d | ||
|  | d7fef45a56 | ||
|  | dc22802dec | ||
|  | ce5af7b1d9 | ||
|  | 0a147e5c9c | ||
|  | 7d21255f7f | ||
|  | 13f952f182 | ||
|  | b494be228b | ||
|  | 0fdaac53d0 | ||
|  | e1b3a08878 | ||
|  | dc893588b0 | ||
|  | b9fcc443ec | ||
|  | d8586c8043 | ||
|  | 4252a3e53b | ||
|  | dbb5cdb9cf | ||
|  | 9bdfecbfdc | ||
|  | 3ec8a8c375 | ||
|  | f85e4a24e5 | ||
|  | 0dda87c78e | ||
|  | 2fc09ff9d7 | ||
|  | e4e0e21293 | ||
|  | 15089f0d7e | ||
|  | 7232c1d7bb | ||
|  | 9a53d8c21c | ||
|  | 0d198193db | ||
|  | 45bc23b8af | ||
|  | 3323c31765 | ||
|  | bb7c26b77c | ||
|  | 9101d6a2c0 | ||
|  | ad2b254be0 | ||
|  | abc4f856ce | ||
|  | b3b66a8f92 | ||
|  | 2f7cf9b916 | ||
|  | b822e0c6e7 | ||
|  | 6fef9ee72b | ||
|  | ab6dd0a1ec | ||
|  | 9deef5ac92 | ||
|  | 577187babe | ||
|  | 577290e813 | ||
|  | f3b9798216 | ||
|  | 4dcaa96d16 | ||
|  | 0dcbf451d6 | ||
|  | e87f6ca40e | ||
|  | 1f34e33d8c | ||
|  | 258e87e127 | ||
|  | 3ec8efcfc1 | ||
|  | 70ea227bd0 | ||
|  | 27c832ed58 | ||
|  | a31b4ccf01 | ||
|  | d221ea68d0 | ||
|  | dc9fe58536 | ||
|  | f871e29bdb | ||
|  | 1357352276 | ||
|  | e169754693 | ||
|  | 1cfe4f40ba | ||
|  | 5545d1c1ba | ||
|  | e5f7228fa9 | ||
|  | 11385494eb | ||
|  | ae328de469 | ||
|  | 0574a706c8 | ||
|  | 70bb85a75b | ||
|  | 8cd901b57b | ||
|  | 8b9818c48e | ||
|  | a95099fa46 | ||
|  | 221e4b7fc0 | ||
|  | 6cff3eb61e | ||
|  | e4fd97ae77 | ||
|  | 5ca9099654 | ||
|  | 6e33e26ddf | ||
|  | 04461a4ab8 | ||
|  | a4b9bbff54 | ||
|  | 4ad4252a77 | ||
|  | aac0c9ab98 | ||
|  | c123e1044a | ||
|  | d25d0454fc | ||
|  | f38984398d | ||
|  | a07799cfa4 | ||
|  | 7c52f297ee | ||
|  | 50a3279b30 | ||
|  | 0ea14eb987 | ||
|  | cf1b98e569 | ||
|  | 63fb435002 | ||
|  | 2f8263f53a | ||
|  | fb649779d6 | ||
|  | bb58f605f7 | ||
|  | fccdf56c5d | ||
|  | bd53410c71 | ||
|  | 7d63f124c4 | ||
|  | 9ffc5d857c | ||
|  | 2f93784acd | ||
|  | d00fbe4eb3 | ||
|  | 51d9f041ae | ||
|  | b872ab8b86 | ||
|  | 3d25fd79ca | ||
|  | 3aad78e6ef | ||
|  | 7d5bb72b0c | ||
|  | 46e5aae8bb | ||
|  | b9e2ee7af3 | ||
|  | c1a2892788 | ||
|  | 2078183e0d | ||
|  | fab1d53714 | ||
|  | 3fe831c7d8 | ||
|  | 6958f71cfd | ||
|  | a7351f348d | ||
|  | 5379e03447 | ||
|  | 539058e32d | ||
|  | 11b6b5a63c | ||
|  | 1155096226 | ||
|  | 2920dd356e | ||
|  | 9c3dac8170 | ||
|  | 49f9909b15 | ||
|  | e4fef6dfc3 | ||
|  | 2da087401e | ||
|  | 629baf9de5 | ||
|  | 29a930dae5 | ||
|  | d920537dd2 | ||
|  | 106b04a5da | ||
|  | 176752e219 | ||
|  | 8d19f60091 | ||
|  | e937aa2f74 | ||
|  | c3ccc4ccdf | ||
|  | 6c5cd705c0 | ||
|  | ce003f217b | ||
|  | 8c7ef49eb6 | ||
|  | 29af4bd1b9 | ||
|  | 2ce5142b06 | ||
|  | f3a8a25872 | ||
|  | 598e97d028 | ||
|  | fa110279de | ||
|  | 8fdeaf73cc | ||
|  | a7ffdf062a | ||
|  | 3bad92dd6d | ||
|  | 2e88024bca | ||
|  | ca2301959c | ||
|  | 3285fae7f0 | ||
|  | 312079657b | ||
|  | 18c183afd6 | ||
|  | f424d9cf20 | ||
|  | 5c3da9fd9e | ||
|  | 1f321fadd4 | ||
|  | 65f5d27b12 | ||
|  | cdb591de7f | ||
|  | a0ea3882e1 | ||
|  | a9444ac702 | ||
|  | d0c6afc3a9 | ||
|  | e7a0a5937c | ||
|  | 08992b5298 | ||
|  | 6490a4240d | ||
|  | 43a7544dd7 | ||
|  | 0fe70dae17 | ||
|  | 7079e76886 | ||
|  | 0ec021c375 | ||
|  | ff3396e286 | ||
|  | 78912903ce | ||
|  | 4c015e2d12 | ||
|  | 172634a55a | ||
|  | 58ca7d551a | ||
|  | fd8ed4c9aa | ||
|  | c03f5f5ff0 | ||
|  | 6775fc58c8 | ||
|  | 9f250fc61b | ||
|  | 94609f1419 | ||
|  | 53402aa5e2 | ||
|  | 5aadb29905 | ||
|  | d4f8c41d80 | ||
|  | a2e14f8b8d | ||
|  | 98c4ac955a | ||
|  | 70b63e1736 | ||
|  | 106665a468 | ||
|  | da5e48d769 | ||
|  | 85f484e73c | ||
|  | d287ae97f8 | ||
|  | 117bb602dc | ||
|  | e440d55034 | ||
|  | 7f5b94fe43 | ||
|  | c58eea6654 | ||
|  | bbed5d0701 | ||
|  | 2775690fc8 | ||
|  | 1fd9f6e724 | ||
|  | 3d63903128 | ||
|  | ef876a165a | ||
|  | c4d6aaeef3 | ||
|  | 37b735c2e3 | ||
|  | 62d30c7b0e | ||
|  | 9cac7d46c0 | ||
|  | 99b3e24836 | ||
|  | ffb699cb06 | ||
|  | 2947ec0002 | ||
|  | 5c4d010bde | ||
|  | 955306d877 | ||
|  | 015935ed55 | ||
|  | 48f4cceb19 | ||
|  | 9850220aac | ||
|  | c4ac1460f0 | ||
|  | b9e1e01337 | ||
|  | 76649cb7de | ||
|  | 5e310776b4 | ||
|  | 4d7fa11172 | ||
|  | 28962007c1 | ||
|  | 2111873bcf | ||
|  | 0aaf9a6fda | ||
|  | 186b704509 | ||
|  | efe9933721 | ||
|  | 200366f5be | ||
|  | c9bd72337d | ||
|  | d4510440b8 | ||
|  | 5a9cf698f7 | ||
|  | 5826fec519 | ||
|  | 7035600984 | ||
|  | b1dfb5811f | ||
|  | 51570a5d08 | ||
|  | f065f3a0b8 | ||
|  | 47376f1f35 | ||
|  | bcfe2c6410 | ||
|  | 09d63b584d | ||
|  | 5f5469a7d4 | ||
|  | 38800d61b0 | ||
|  | 1186e95c51 | ||
|  | d870e0f42e | ||
|  | 0db9852769 | ||
|  | 7e3f9048fe | ||
|  | 1ba88f182b | ||
|  | c8f5a6b7ad | ||
|  | d26bbf3cdc | ||
|  | 0de72b6914 | ||
|  | e41c89bd59 | ||
|  | 541d9ebdd9 | ||
|  | 1e724712e0 | ||
|  | 3682467ae3 | ||
|  | 7707c81b2d | ||
|  | e434de72a3 | ||
|  | ce191fa6d3 | ||
|  | 90865a5284 | ||
|  | 684f6e0b5c | ||
|  | c287bc139c | ||
|  | 1392275b81 | ||
|  | 87c0f1d86e | ||
|  | a4a723cfc6 | ||
|  | 921e2c06f4 | ||
|  | cb9433f4b9 | ||
|  | 1be6af820e | ||
|  | 3b686b6d1c | ||
|  | 697566fe42 | ||
|  | 5130ba7ea4 | ||
|  | c9e46a4dd1 | ||
|  | f5b3dc36e3 | ||
|  | f1e8d1cf1e | ||
|  | e8e7ab01d2 | ||
|  | 9244994233 | ||
|  | ae768a8525 | ||
|  | 162c762973 | ||
|  | 57b5981904 | ||
|  | 275d19e71d | ||
|  | 189b11befa | ||
|  | a56a5fc228 | ||
|  | cbe3fb73a8 | ||
|  | 3d58fbebec | ||
|  | b947ff50ed | ||
|  | 18d2741814 | ||
|  | 93a54780ab | ||
|  | 3d201db6fc | ||
|  | 9ffc0936ee | ||
|  | fbf9e00208 | ||
|  | 2c826451d1 | ||
|  | 617a5c0606 | ||
|  | 8331a7e34a | ||
|  | 8ee1676f0a | ||
|  | 5dc8620c43 | ||
|  | d2733a4df0 | ||
|  | ae649223d8 | ||
|  | 6267930938 | ||
|  | bdee8cde77 | ||
|  | e63f216905 | ||
|  | ec18165698 | ||
|  | 307e6a2337 | ||
|  | b80d8cf774 | ||
|  | 7a5ef6013a | ||
|  | 13b92c47d9 | ||
|  | 5a79bc0a99 | ||
|  | beda6ec3a9 | ||
|  | c619b8730b | ||
|  | c14ec8b006 | ||
|  | 10c7786248 | ||
|  | 08ff08685c | ||
|  | 8091dbfdfa | ||
|  | 8dc106b79a | ||
|  | 7527433738 | ||
|  | 1502e08a7a | ||
|  | c9679f1d4f | ||
|  | 6b976dd6b9 | ||
|  | 8da4abf655 | ||
|  | 2e26193333 | ||
|  | ada43bc0dd | ||
|  | a447c886c4 | ||
|  | 288e713f94 | ||
|  | f8eb1fa44a | ||
|  | afc794513f | ||
|  | 7e6d3c9d6b | ||
|  | 44960e8e42 | ||
|  | 41430c3bb2 | ||
|  | 2f435019e0 | ||
|  | 480e70dfac | ||
|  | c4818334e7 | ||
|  | a5d5f86aed | ||
|  | 78afb771b1 | ||
|  | a74a646777 | ||
|  | 87f9ca3bb2 | ||
|  | d54e264a91 | ||
|  | 99aea5ce7a | ||
|  | e10d5e89e5 | ||
|  | 0105456828 | ||
|  | 5c7df5c04d | ||
|  | 563ede822f | ||
|  | 786cfc21c7 | ||
|  | 8f0856e31a | ||
|  | bea075c74d | ||
|  | 5f82accb61 | ||
|  | 95f50ca2fd | ||
|  | d8b76bdfd2 | ||
|  | 74a9edaaf7 | ||
|  | 7dd858be39 | ||
|  | 7d7ff71384 | ||
|  | 003177fa49 | ||
|  | 3c3a83330d | ||
|  | 724f423692 | ||
|  | 65b8882ed4 | ||
|  | 66d7fd7d4c | ||
|  | 782a6f289c | ||
|  | 70d936bb8f | ||
|  | dda3082c7e | ||
|  | ed948cc965 | ||
|  | 0347649f42 | ||
|  | c66fe2b541 | ||
|  | 8c1ae76c7a | ||
|  | 04dee404c0 | ||
|  | b22dd29835 | ||
|  | 536a5cd1c8 | ||
|  | b7f1bcf7c9 | ||
|  | 5e590072dd | ||
|  | d204b9b752 | ||
|  | 22d1121193 | ||
|  | e23c6521b9 | ||
|  | b0cb9663a6 | ||
|  | 95b7da89f0 | ||
|  | b0a5b53abb | ||
|  | ce78c8993f | ||
|  | 2538b4a885 | ||
|  | 109d96ad16 | ||
|  | 4cbb0d9716 | ||
|  | 65ecea3b1c | ||
|  | 495b80f5ef | ||
|  | e113736887 | ||
|  | 042b7a4966 | ||
|  | 5ab8cb38d3 | ||
|  | fc4ab29244 | ||
|  | 5a43e6cb9f | ||
|  | 4effc95c5f | ||
|  | 962965b5b7 | ||
|  | 260b611293 | ||
|  | d2131c371b | ||
|  | eedf6a07f0 | ||
|  | 5cd1e7c100 | ||
|  | 6a750a998f | ||
|  | bd818b2dea | ||
|  | 4164ebcc69 | ||
|  | 60d732067b | ||
|  | b7b52707fb | ||
|  | f373c72679 | ||
|  | ec2027b8db | ||
|  | a84de5db77 | ||
|  | 5065b1ee03 | ||
|  | a0aa114ee6 | ||
|  | 823839fbf6 | ||
|  | 1c93d8bf79 | ||
|  | 626404407e | ||
|  | 446ab62d38 | ||
|  | 0d39161ec3 | ||
|  | 29be16dcba | ||
|  | b0bb790386 | ||
|  | e64b40d58b | ||
|  | 4870945af2 | ||
|  | a547a5f3f9 | ||
|  | e5eabdf7e7 | ||
|  | f78d56b149 | ||
|  | 771926c779 | ||
|  | 6090efe2df | ||
|  | 863227c55c | ||
|  | 5a6967cefd | ||
|  | 5166171e5d | ||
|  | 3e36a29c23 | ||
|  | 20e1e50032 | ||
|  | 36bc483edb | ||
|  | aa59227786 | ||
|  | 2d8449ed68 | ||
|  | d7ab482ae1 | ||
|  | cfcc4ce88a | ||
|  | eda44bbed0 | ||
|  | 988049061d | ||
|  | ebb1c5ae25 | ||
|  | ce7eebac5c | ||
|  | b7c446f7db | ||
|  | 7c39a04c4b | ||
|  | 037d84b810 | ||
|  | 529bf50c85 | ||
|  | d2b4bd78a9 | ||
|  | e1c146a5c1 | ||
|  | ed9acbdfde | ||
|  | dc825d5a9c | ||
|  | 9f8faf15f1 | ||
|  | 934656c954 | ||
|  | d233a2df3c | ||
|  | 7c7740d3ba | ||
|  | cda6cfb4cd | ||
|  | a90d095609 | ||
|  | 98e683329e | ||
|  | 3588bd881c | ||
|  | 0460811e6c | ||
|  | 27f5fe18df | ||
|  | 6d944ec98f | ||
|  | adf6691470 | ||
|  | dd8b500efd | ||
|  | 4e1ff8c4a3 | ||
|  | e73d590ead | ||
|  | 5cc22f49cf | ||
|  | eb3d2b1749 | ||
|  | 21a197ba46 | ||
|  | 0b74707638 | ||
|  | 16dc8b7d68 | ||
|  | 5a8abe004e | ||
|  | b211d72c8b | ||
|  | 36f3eb8b2f | ||
|  | cb1cb9f328 | ||
|  | 3344bb7263 | ||
|  | 5e1167b8ae | ||
|  | b80db054e2 | ||
|  | c66df3cb2c | ||
|  | ac8ff4e565 | ||
|  | bfa7ee90f4 | ||
|  | 77c9e37584 | ||
|  | 80350f8423 | ||
|  | 3c1ff4d21f | ||
|  | 55b8f03590 | ||
|  | 1fd7852309 | ||
|  | 9c5292962f | ||
|  | c05c6e72c0 | ||
|  | bdcd033952 | ||
|  | 4ec6bcc8c7 | ||
|  | 11ea4b6d47 | ||
|  | e4f45b5370 | ||
|  | 9baadd3793 | ||
|  | 94a79876ce | ||
|  | 42c3d1fa68 | ||
|  | 0e3ccebd0b | ||
|  | 4af8272faa | ||
|  | 0ef3d0cf03 | ||
|  | f266b92ef1 | ||
|  | 462c9fb3aa | ||
|  | 568186828c | ||
|  | 8bcc319b7d | ||
|  | baff9780de | ||
|  | d8b8f98738 | ||
|  | b714eaac06 | ||
|  | 14b94a5bd2 | ||
|  | ea014a6504 | ||
|  | e28e66e8f1 | ||
|  | b47a140c2f | ||
|  | 19d7e27fa9 | ||
|  | 2d368f226e | ||
|  | 75d81f8f18 | ||
|  | e3437ba697 | ||
|  | 84f299c33b | ||
|  | 3d4489efe6 | ||
|  | 6aa50e3c00 | ||
|  | b70498c337 | ||
|  | f34aa77d1d | ||
|  | 3833a41acb | ||
|  | b5a5a216cd | ||
|  | d9da2a57b6 | ||
|  | 1591b61b77 | ||
|  | 66f2df9677 | ||
|  | 5199377113 | ||
|  | da202317c0 | ||
|  | 1d2a4e707e | ||
|  | edf9dbc6e8 | ||
|  | dfbe6e5b6e | ||
|  | d551333fa2 | ||
|  | 01cab599bb | ||
|  | 1c8834fffb | ||
|  | 22e6ea700f | ||
|  | 7f7d6cf893 | ||
|  | a94d476b75 | ||
|  | eb5e55a272 | ||
|  | 53c80aaef8 | ||
|  | 607d0115f0 | ||
|  | b4f18dbe77 | ||
|  | 51d97cdca5 | ||
|  | 2cd593157f | ||
|  | ec70fde557 | ||
|  | 950576d38b | ||
|  | ce5304277d | ||
|  | 53760766a0 | ||
|  | ed863986a7 | ||
|  | 89ff5a83b5 | ||
|  | fe0b62b9b4 | ||
|  | 32c8ddbe1b | ||
|  | 2cfbfd8649 | ||
|  | 3f6c19dec4 | ||
|  | 93421b50f9 | ||
|  | 54e829173a | ||
|  | 4fe38bd31b | ||
|  | fb0638e824 | ||
|  | 108794a6b6 | ||
|  | 9c16fc1380 | ||
|  | 99c219ed97 | ||
|  | ec12238ea1 | ||
|  | bdbd22f98b | ||
|  | b8e1944d20 | ||
|  | 8883d185fe | ||
|  | 19e40e9976 | ||
|  | fa85b2b5b2 | ||
|  | 5cf0131d75 | ||
|  | 3948cb8e6c | ||
|  | f43938726a | ||
|  | 8c8bb7a930 | ||
|  | 3972882a33 | ||
|  | 0ef5eeeb55 | ||
|  | ef48a79d0c | ||
|  | 2bb883219c | ||
|  | 23c0bb49c4 | ||
|  | 13e59105ec | ||
|  | 98c057c516 | ||
|  | e293d69798 | ||
|  | b097e29104 | ||
|  | 29fbd46e33 | ||
|  | 6a15afc723 | ||
|  | c56a39a726 | ||
|  | 4b80e46d26 | ||
|  | a6f3e61520 | ||
|  | cce4ef19e5 | ||
|  | 1951491c04 | ||
|  | 87b72e4bcd | ||
|  | 2c6643d691 | ||
|  | d02df46517 | ||
|  | 0c0cc417ee | ||
|  | f0c03e8a3b | ||
|  | 345766f387 | ||
|  | 3fa659632c | ||
|  | 95b92b7d1e | ||
|  | 466e988627 | ||
|  | dc3c967c9f | ||
|  | 1fc31b4d8f | ||
|  | e4a4b0a4eb | ||
|  | a9c026884d | ||
|  | e893000ce9 | ||
|  | 32eeb3424d | ||
|  | ab523b6102 | ||
|  | b062222c45 | ||
|  | 2dddc843ce | ||
|  | b74c1c8cf9 | ||
|  | d5d4bb2c4b | ||
|  | 89ac27ad10 | ||
|  | 48b169c026 | ||
|  | 0715b7406e | ||
|  | 6e4991a34b | ||
|  | 8730af59bc | ||
|  | bdcc2c6c9f | ||
|  | 6f0f6e86a1 | ||
|  | 3962d9da92 | ||
|  | 5ed53d5f04 | ||
|  | ddb28b78c3 | ||
|  | 7699b6b4ea | ||
|  | d783d05462 | ||
|  | 33bf373151 | ||
|  | 8116644526 | ||
|  | dc4665e82a | ||
|  | 732a85e51d | ||
|  | 32190db8bb | ||
|  | 25d3a115e0 | ||
|  | 30e3ed6410 | ||
|  | 8b5a775dc5 | ||
|  | 2942c3a4be | ||
|  | 768d3bb3e8 | ||
|  | 250b2c2f53 | ||
|  | c8440af9a5 | ||
|  | 37fe2d22b0 | ||
|  | b0b5d90976 | ||
|  | 27b0c7f425 | ||
|  | 7c23571806 | ||
|  | ea378c8d82 | ||
|  | 0fb45974ef | ||
|  | 6490f9c128 | ||
|  | 2d7f1af52c | ||
|  | ac27659a59 | ||
|  | 11d59c8bd1 | ||
|  | a6936cbd02 | ||
|  | 3be84d76ef | ||
|  | 9aed1f344f | ||
|  | 4fff264630 | ||
|  | 2e444da2a3 | ||
|  | 7a216f95ca | ||
|  | f22a9799a1 | ||
|  | 90bdc40393 | ||
|  | 622a97c8d8 | ||
|  | 03691c81c2 | ||
|  | 885b56c465 | ||
|  | 23cc7be231 | ||
|  | bb82b0eb79 | ||
|  | 2e05f640b8 | ||
|  | 697a02ffee | ||
|  | ad402021ed | ||
|  | 67caf6ef1f | ||
|  | 3277858c5a | ||
|  | 3fbedf323f | ||
|  | 144a6130f2 | ||
|  | fa38c975b6 | ||
|  | c14fa1021c | ||
|  | 5e78cc02bd | ||
|  | 429ef80fb9 | ||
|  | e4d93cad27 | ||
|  | d9a4840e37 | ||
|  | a93070b98d | ||
|  | c05a942862 | ||
|  | f9a7879c1e | ||
|  | bdfbc6d6a7 | ||
|  | 21181d8d8e | ||
|  | 1c7a27b816 | ||
|  | e953f58c74 | 
| @@ -1,50 +0,0 @@ | ||||
| --- | ||||
| engines: | ||||
|   csslint: | ||||
|     enabled: true | ||||
|   duplication: | ||||
|     enabled: true | ||||
|     config: | ||||
|       languages: | ||||
|       - ruby | ||||
|       - javascript | ||||
|       - python | ||||
|       - php | ||||
|   eslint: | ||||
|     enabled: true | ||||
|   fixme: | ||||
|     enabled: true | ||||
|   phpmd: | ||||
|     enabled: true | ||||
| ratings: | ||||
|   paths: | ||||
|   - "**.css" | ||||
|   - "**.inc" | ||||
|   - "**.js" | ||||
|   - "**.jsx" | ||||
|   - "**.module" | ||||
|   - "**.php" | ||||
|   - "**.py" | ||||
|   - "**.rb" | ||||
| exclude_paths: | ||||
| - gulpfile.js | ||||
| - public/packages/maximebf/php-debugbar/debugbar.js | ||||
| - public/packages/maximebf/php-debugbar/widgets.js | ||||
| - public/packages/maximebf/php-debugbar/openhandler.js | ||||
| - public/packages/maximebf/php-debugbar/widgets/sqlqueries/widget.js | ||||
| - public/js/bootstrap3-typeahead.min.js | ||||
| - public/js/bootstrap-sortable.js | ||||
| - public/js/bootstrap-tagsinput.min.js | ||||
| - public/js/bootstrap-tagsinput.min.js.map | ||||
| - public/js/daterangepicker.js | ||||
| - public/js/jquery-2.1.3.min.js | ||||
| - public/js/jquery-2.1.3.min.js.map | ||||
| - public/js/jquery-ui.min.js | ||||
| - public/js/metisMenu.js | ||||
| - public/js/moment.min.js | ||||
| - public/js/sb-admin-2.js | ||||
| - public/bootstrap/* | ||||
| - resources/lang/* | ||||
| - tests/* | ||||
| - database/* | ||||
| - storage/* | ||||
| @@ -1 +0,0 @@ | ||||
| src_dir: . | ||||
| @@ -1,2 +0,0 @@ | ||||
| --exclude-exts=.min.css | ||||
| --ignore=adjoining-classes,box-model,ids,order-alphabetical,unqualified-attributes | ||||
							
								
								
									
										36
									
								
								.env.example
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								.env.example
									
									
									
									
									
								
							| @@ -1,14 +1,19 @@ | ||||
| APP_ENV=production | ||||
| APP_DEBUG=false | ||||
| APP_FORCE_SSL=false | ||||
| APP_FORCE_ROOT= | ||||
| APP_KEY=SomeRandomStringOf32CharsExactly | ||||
|  | ||||
| APP_LOG_LEVEL=warning | ||||
| APP_URL=http://localhost | ||||
|  | ||||
| DB_CONNECTION=mysql | ||||
| DB_HOST=localhost | ||||
| DB_HOST=127.0.0.1 | ||||
| DB_PORT=3306 | ||||
| DB_DATABASE=homestead | ||||
| DB_USERNAME=homestead | ||||
| DB_PASSWORD=secret | ||||
|  | ||||
| BROADCAST_DRIVER=log | ||||
| CACHE_DRIVER=file | ||||
| SESSION_DRIVER=file | ||||
| QUEUE_DRIVER=sync | ||||
| @@ -17,28 +22,33 @@ COOKIE_PATH="/" | ||||
| COOKIE_DOMAIN= | ||||
| COOKIE_SECURE=false | ||||
|  | ||||
| DEFAULT_CURRENCY=EUR | ||||
| DEFAULT_LANGUAGE=en_US | ||||
|  | ||||
| REDIS_HOST=localhost | ||||
| REDIS_HOST=127.0.0.1 | ||||
| REDIS_PASSWORD=null | ||||
| REDIS_PORT=6379 | ||||
|  | ||||
| MAIL_DRIVER=smtp | ||||
| MAIL_HOST=mailtrap.io | ||||
| MAIL_PORT=2525 | ||||
| MAIL_FROM=enter_your_email_here | ||||
| MAIL_FROM=changeme@example.com | ||||
| MAIL_USERNAME=null | ||||
| MAIL_PASSWORD=null | ||||
| MAIL_ENCRYPTION=null | ||||
|  | ||||
| SEND_REGISTRATION_MAIL=true | ||||
| MUST_CONFIRM_ACCOUNT=false | ||||
|  | ||||
| SEND_ERROR_MESSAGE=true | ||||
| SHOW_INCOMPLETE_TRANSLATIONS=false | ||||
|  | ||||
| ANALYTICS_ID= | ||||
| RUNCLEANUP=true | ||||
| SITE_OWNER=mail@example.com | ||||
| CACHE_PREFIX=firefly | ||||
|  | ||||
| GOOGLE_MAPS_API_KEY= | ||||
| ANALYTICS_ID= | ||||
| SITE_OWNER=mail@example.com | ||||
| USE_ENCRYPTION=true | ||||
|  | ||||
| PUSHER_KEY= | ||||
| PUSHER_SECRET= | ||||
| PUSHER_APP_ID= | ||||
|  | ||||
| DEMO_USERNAME= | ||||
| DEMO_PASSWORD= | ||||
|  | ||||
| BLOCKED_DOMAINS= | ||||
							
								
								
									
										40
									
								
								.env.testing
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								.env.testing
									
									
									
									
									
								
							| @@ -1,37 +1,45 @@ | ||||
| APP_ENV=testing | ||||
| APP_DEBUG=true | ||||
| APP_KEY=SomeRandomStringOf32CharsExactly | ||||
|  | ||||
| APP_FORCE_SSL=false | ||||
| APP_FORCE_ROOT= | ||||
| APP_KEY=TestTestTestTestTestTestTestTest | ||||
| APP_LOG_LEVEL=debug | ||||
| APP_URL=http://localhost | ||||
|  | ||||
| DB_CONNECTION=sqlite | ||||
| DB_HOST=localhost | ||||
| DB_DATABASE=homestead | ||||
| DB_HOST=127.0.0.1 | ||||
| DB_PORT=3306 | ||||
| DB_USERNAME=homestead | ||||
| DB_PASSWORD=secret | ||||
|  | ||||
| CACHE_DRIVER=array | ||||
| SESSION_DRIVER=array | ||||
| QUEUE_DRIVER=array | ||||
| BROADCAST_DRIVER=log | ||||
| CACHE_DRIVER=file | ||||
| SESSION_DRIVER=file | ||||
| QUEUE_DRIVER=sync | ||||
|  | ||||
| DEFAULT_CURRENCY=EUR | ||||
| DEFAULT_LANGUAGE=en_US | ||||
| COOKIE_PATH="/" | ||||
| COOKIE_DOMAIN= | ||||
| COOKIE_SECURE=false | ||||
|  | ||||
| REDIS_HOST=localhost | ||||
| REDIS_HOST=127.0.0.1 | ||||
| REDIS_PASSWORD=null | ||||
| REDIS_PORT=6379 | ||||
|  | ||||
| MAIL_DRIVER=log | ||||
| MAIL_DRIVER=smtp | ||||
| MAIL_HOST=mailtrap.io | ||||
| MAIL_PORT=2525 | ||||
| MAIL_FROM=your_address_here@example.com | ||||
| MAIL_FROM=changeme@example.com | ||||
| MAIL_USERNAME=null | ||||
| MAIL_PASSWORD=null | ||||
| MAIL_ENCRYPTION=null | ||||
|  | ||||
| SEND_REGISTRATION_MAIL=true | ||||
| SEND_ERROR_MESSAGE=true | ||||
| SHOW_INCOMPLETE_TRANSLATIONS=false | ||||
|  | ||||
| ANALYTICS_ID=abcde | ||||
| RUNCLEANUP=false | ||||
| SITE_OWNER=your_address_here@example.com | ||||
| ANALYTICS_ID= | ||||
| SITE_OWNER=mail@example.com | ||||
|  | ||||
| BLOCKED_DOMAINS= | ||||
| PUSHER_KEY= | ||||
| PUSHER_SECRET= | ||||
| PUSHER_APP_ID= | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| **/*{.,-}min.js | ||||
							
								
								
									
										213
									
								
								.eslintrc
									
									
									
									
									
								
							
							
						
						
									
										213
									
								
								.eslintrc
									
									
									
									
									
								
							| @@ -1,213 +0,0 @@ | ||||
| ecmaFeatures: | ||||
|   modules: true | ||||
|   jsx: true | ||||
|  | ||||
| env: | ||||
|   amd: true | ||||
|   browser: true | ||||
|   es6: true | ||||
|   jquery: true | ||||
|   node: true | ||||
|  | ||||
| # http://eslint.org/docs/rules/ | ||||
| rules: | ||||
|   # Possible Errors | ||||
|   comma-dangle: [2, never] | ||||
|   no-cond-assign: 2 | ||||
|   no-console: 0 | ||||
|   no-constant-condition: 2 | ||||
|   no-control-regex: 2 | ||||
|   no-debugger: 2 | ||||
|   no-dupe-args: 2 | ||||
|   no-dupe-keys: 2 | ||||
|   no-duplicate-case: 2 | ||||
|   no-empty: 2 | ||||
|   no-empty-character-class: 2 | ||||
|   no-ex-assign: 2 | ||||
|   no-extra-boolean-cast: 2 | ||||
|   no-extra-parens: 0 | ||||
|   no-extra-semi: 2 | ||||
|   no-func-assign: 2 | ||||
|   no-inner-declarations: [2, functions] | ||||
|   no-invalid-regexp: 2 | ||||
|   no-irregular-whitespace: 2 | ||||
|   no-negated-in-lhs: 2 | ||||
|   no-obj-calls: 2 | ||||
|   no-regex-spaces: 2 | ||||
|   no-sparse-arrays: 2 | ||||
|   no-unexpected-multiline: 2 | ||||
|   no-unreachable: 2 | ||||
|   use-isnan: 2 | ||||
|   valid-jsdoc: 0 | ||||
|   valid-typeof: 2 | ||||
|  | ||||
|   # Best Practices | ||||
|   accessor-pairs: 2 | ||||
|   block-scoped-var: 0 | ||||
|   complexity: [2, 6] | ||||
|   consistent-return: 0 | ||||
|   curly: 0 | ||||
|   default-case: 0 | ||||
|   dot-location: 0 | ||||
|   dot-notation: 0 | ||||
|   eqeqeq: 2 | ||||
|   guard-for-in: 2 | ||||
|   no-alert: 2 | ||||
|   no-caller: 2 | ||||
|   no-case-declarations: 2 | ||||
|   no-div-regex: 2 | ||||
|   no-else-return: 0 | ||||
|   no-empty-label: 2 | ||||
|   no-empty-pattern: 2 | ||||
|   no-eq-null: 2 | ||||
|   no-eval: 2 | ||||
|   no-extend-native: 2 | ||||
|   no-extra-bind: 2 | ||||
|   no-fallthrough: 2 | ||||
|   no-floating-decimal: 0 | ||||
|   no-implicit-coercion: 0 | ||||
|   no-implied-eval: 2 | ||||
|   no-invalid-this: 0 | ||||
|   no-iterator: 2 | ||||
|   no-labels: 0 | ||||
|   no-lone-blocks: 2 | ||||
|   no-loop-func: 2 | ||||
|   no-magic-number: 0 | ||||
|   no-multi-spaces: 0 | ||||
|   no-multi-str: 0 | ||||
|   no-native-reassign: 2 | ||||
|   no-new-func: 2 | ||||
|   no-new-wrappers: 2 | ||||
|   no-new: 2 | ||||
|   no-octal-escape: 2 | ||||
|   no-octal: 2 | ||||
|   no-proto: 2 | ||||
|   no-redeclare: 2 | ||||
|   no-return-assign: 2 | ||||
|   no-script-url: 2 | ||||
|   no-self-compare: 2 | ||||
|   no-sequences: 0 | ||||
|   no-throw-literal: 0 | ||||
|   no-unused-expressions: 2 | ||||
|   no-useless-call: 2 | ||||
|   no-useless-concat: 2 | ||||
|   no-void: 2 | ||||
|   no-warning-comments: 0 | ||||
|   no-with: 2 | ||||
|   radix: 2 | ||||
|   vars-on-top: 0 | ||||
|   wrap-iife: 2 | ||||
|   yoda: 0 | ||||
|  | ||||
|   # Strict | ||||
|   strict: 0 | ||||
|  | ||||
|   # Variables | ||||
|   init-declarations: 0 | ||||
|   no-catch-shadow: 2 | ||||
|   no-delete-var: 2 | ||||
|   no-label-var: 2 | ||||
|   no-shadow-restricted-names: 2 | ||||
|   no-shadow: 0 | ||||
|   no-undef-init: 2 | ||||
|   no-undef: 0 | ||||
|   no-undefined: 0 | ||||
|   no-unused-vars: 0 | ||||
|   no-use-before-define: 0 | ||||
|  | ||||
|   # Node.js and CommonJS | ||||
|   callback-return: 2 | ||||
|   global-require: 2 | ||||
|   handle-callback-err: 2 | ||||
|   no-mixed-requires: 0 | ||||
|   no-new-require: 0 | ||||
|   no-path-concat: 2 | ||||
|   no-process-exit: 2 | ||||
|   no-restricted-modules: 0 | ||||
|   no-sync: 0 | ||||
|  | ||||
|   # Stylistic Issues | ||||
|   array-bracket-spacing: 0 | ||||
|   block-spacing: 0 | ||||
|   brace-style: 0 | ||||
|   camelcase: 0 | ||||
|   comma-spacing: 0 | ||||
|   comma-style: 0 | ||||
|   computed-property-spacing: 0 | ||||
|   consistent-this: 0 | ||||
|   eol-last: 0 | ||||
|   func-names: 0 | ||||
|   func-style: 0 | ||||
|   id-length: 0 | ||||
|   id-match: 0 | ||||
|   indent: 0 | ||||
|   jsx-quotes: 0 | ||||
|   key-spacing: 0 | ||||
|   linebreak-style: 0 | ||||
|   lines-around-comment: 0 | ||||
|   max-depth: 0 | ||||
|   max-len: 0 | ||||
|   max-nested-callbacks: 0 | ||||
|   max-params: 0 | ||||
|   max-statements: [2, 30] | ||||
|   new-cap: 0 | ||||
|   new-parens: 0 | ||||
|   newline-after-var: 0 | ||||
|   no-array-constructor: 0 | ||||
|   no-bitwise: 0 | ||||
|   no-continue: 0 | ||||
|   no-inline-comments: 0 | ||||
|   no-lonely-if: 0 | ||||
|   no-mixed-spaces-and-tabs: 0 | ||||
|   no-multiple-empty-lines: 0 | ||||
|   no-negated-condition: 0 | ||||
|   no-nested-ternary: 0 | ||||
|   no-new-object: 0 | ||||
|   no-plusplus: 0 | ||||
|   no-restricted-syntax: 0 | ||||
|   no-spaced-func: 0 | ||||
|   no-ternary: 0 | ||||
|   no-trailing-spaces: 0 | ||||
|   no-underscore-dangle: 0 | ||||
|   no-unneeded-ternary: 0 | ||||
|   object-curly-spacing: 0 | ||||
|   one-var: 0 | ||||
|   operator-assignment: 0 | ||||
|   operator-linebreak: 0 | ||||
|   padded-blocks: 0 | ||||
|   quote-props: 0 | ||||
|   quotes: 0 | ||||
|   require-jsdoc: 0 | ||||
|   semi-spacing: 0 | ||||
|   semi: 0 | ||||
|   sort-vars: 0 | ||||
|   space-after-keywords: 0 | ||||
|   space-before-blocks: 0 | ||||
|   space-before-function-paren: 0 | ||||
|   space-before-keywords: 0 | ||||
|   space-in-parens: 0 | ||||
|   space-infix-ops: 0 | ||||
|   space-return-throw-case: 0 | ||||
|   space-unary-ops: 0 | ||||
|   spaced-comment: 0 | ||||
|   wrap-regex: 0 | ||||
|  | ||||
|   # ECMAScript 6 | ||||
|   arrow-body-style: 0 | ||||
|   arrow-parens: 0 | ||||
|   arrow-spacing: 0 | ||||
|   constructor-super: 0 | ||||
|   generator-star-spacing: 0 | ||||
|   no-arrow-condition: 0 | ||||
|   no-class-assign: 0 | ||||
|   no-const-assign: 0 | ||||
|   no-dupe-class-members: 0 | ||||
|   no-this-before-super: 0 | ||||
|   no-var: 0 | ||||
|   object-shorthand: 0 | ||||
|   prefer-arrow-callback: 0 | ||||
|   prefer-const: 0 | ||||
|   prefer-reflect: 0 | ||||
|   prefer-spread: 0 | ||||
|   prefer-template: 0 | ||||
|   require-yield: 0 | ||||
							
								
								
									
										2
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,3 @@ | ||||
| * text=auto | ||||
| *.css linguist-vendored | ||||
| *.less linguist-vendored | ||||
| *.scss linguist-vendored | ||||
|   | ||||
							
								
								
									
										19
									
								
								.github/CONTRIBUTING.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								.github/CONTRIBUTING.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| # Welcome to Firefly III on Github! | ||||
|  | ||||
| :+1::tada: Thank you for taking the time to contribute something to Firefly III! | ||||
|  | ||||
| ## Feature requests | ||||
|  | ||||
| If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/). | ||||
|  | ||||
| ## Bugs | ||||
|  | ||||
| If you find a bug, include as many log files and details as you think are necessary.  | ||||
|  | ||||
| ## Installation problems | ||||
|  | ||||
| Take the time to read the [installation guide FAQ](https://firefly-iii.github.io/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your problem may be among them! | ||||
|  | ||||
| ## Pull requests | ||||
|  | ||||
| I can only accept pull requests against the `develop` branch, never the `master` branch | ||||
							
								
								
									
										14
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										14
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,6 +1,14 @@ | ||||
| /vendor | ||||
| /node_modules | ||||
| /public/storage | ||||
| /vendor | ||||
| /.idea | ||||
| Homestead.json | ||||
| Homestead.yaml | ||||
| .env | ||||
|  | ||||
| storage/ | ||||
| _development | ||||
| .env.local | ||||
| result.html | ||||
| test-import.sh | ||||
| test-import-report.txt | ||||
| public/google*.html | ||||
| .env.backup | ||||
|   | ||||
							
								
								
									
										22
									
								
								.jshintrc
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								.jshintrc
									
									
									
									
									
								
							| @@ -1,22 +0,0 @@ | ||||
| { | ||||
|   "undef": true, | ||||
|   "unused": false, | ||||
|   "strict": true, | ||||
|   "browser": true, | ||||
|   "jquery": true, | ||||
|   "devel": true, | ||||
|   "globals": [ | ||||
|     "language", | ||||
|     "token", | ||||
|     "currencyCode", | ||||
|     "$", | ||||
|     "token", | ||||
|     "accountID", | ||||
|     "billID", | ||||
|     "currentMonthName", | ||||
|     "previousMonthName", | ||||
|     "nextMonthName", | ||||
|     "everything", | ||||
|     "moment" | ||||
|   ] | ||||
| } | ||||
| @@ -2,5 +2,50 @@ | ||||
| tools: | ||||
|   external_code_coverage: false | ||||
| filter: | ||||
|     excluded_paths: | ||||
|         - app/Support/Migration/* | ||||
|   paths: | ||||
|     - app/* | ||||
|     - public/js/ff/* | ||||
|   excluded_paths: | ||||
|     - "database/migrations/*" | ||||
|     - "bootstrap/*" | ||||
|     - "config/*" | ||||
|     - "docker/*" | ||||
|     - "public/js/lib/*" | ||||
|     - "public/lib/adminlte/js/*" | ||||
|     - "public/lib/bootstrap/js/*" | ||||
|     - "resources/*" | ||||
|     - "routes/*" | ||||
|     - "storage/*" | ||||
| checks: | ||||
|     php: | ||||
|         use_self_instead_of_fqcn: true | ||||
|         uppercase_constants: true | ||||
|         return_doc_comments: true | ||||
|         return_doc_comment_if_not_inferrable: true | ||||
|         remove_extra_empty_lines: true | ||||
|         parameter_doc_comments: true | ||||
|         optional_parameters_at_the_end: true | ||||
|         no_short_variable_names: | ||||
|             minimum: '3' | ||||
|         no_short_method_names: | ||||
|             minimum: '3' | ||||
|         no_long_variable_names: | ||||
|             maximum: '20' | ||||
|         no_goto: true | ||||
|         newline_at_end_of_file: true | ||||
|         encourage_single_quotes: true | ||||
|         avoid_todo_comments: true | ||||
|         avoid_perl_style_comments: true | ||||
|         avoid_fixme_comments: true | ||||
|         avoid_multiple_statements_on_same_line: true | ||||
|         align_assignments: true | ||||
|         duplication: false | ||||
|     javascript: true | ||||
|  | ||||
| coding_style: | ||||
|     php: | ||||
|         spaces: | ||||
|             around_operators: | ||||
|                 concatenation: true | ||||
|             other: | ||||
|                 after_type_cast: false | ||||
							
								
								
									
										23
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,22 +1,23 @@ | ||||
| language: php | ||||
| sudo: false | ||||
| php: | ||||
|   - 7 | ||||
|   - 7.0 | ||||
|   - 7.1 | ||||
|  | ||||
| cache: | ||||
|     directories: | ||||
|         - vendor | ||||
|         - $HOME/.composer/cache | ||||
|  | ||||
| install: | ||||
|   - phpenv config-rm xdebug.ini | ||||
|   - composer selfupdate | ||||
|   - if [[ "$(php -v | grep 'PHP 7')" ]]; then phpenv config-rm xdebug.ini; fi | ||||
|   - rm composer.lock | ||||
|   - composer update --no-scripts | ||||
|   - cp .env.testing .env | ||||
|   - php artisan clear-compiled | ||||
|   - php artisan optimize | ||||
|   - php artisan env | ||||
|   - mv -v .env.testing .env | ||||
|   - php artisan env | ||||
|   - touch storage/upload/at-1.data | ||||
|   - touch storage/upload/at-2.data | ||||
|   - touch storage/database/testing.db | ||||
|   - php artisan migrate --seed | ||||
|   - cp .env.testing .env | ||||
|   - mv storage/database/databasecopy.sqlite storage/database/database.sqlite | ||||
|  | ||||
| script: | ||||
|   - phpunit | ||||
|   - phpunit | ||||
							
								
								
									
										475
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										475
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -2,8 +2,469 @@ | ||||
| All notable changes to this project will be documented in this file. | ||||
| This project adheres to [Semantic Versioning](http://semver.org/). | ||||
|  | ||||
| ## [Unreleased] | ||||
| - No unreleased changes yet. | ||||
|  | ||||
| ## [4.3.3] - 2017-02-02 | ||||
| ### Fixed | ||||
| - Fixed bug #550, reported by @worldworm! | ||||
| - Fixed bug #551, reported by @t-me! | ||||
|  | ||||
| ## [4.3.3] - 2017-01-30 | ||||
|  | ||||
| _The 100th release of Firefly!_ | ||||
|  | ||||
| ### Added | ||||
| - Add locales to Docker (#534) by @elohmeier. | ||||
| - Optional database encryption. On by default. | ||||
| - Datepicker for Firefox and other browsers. | ||||
| - New instruction block for updating and installing. | ||||
| - Ability to clone transactions. | ||||
| - Use multi-select Bootstrap thing instead of massive lists of checkboxes. | ||||
|  | ||||
| ### Removed | ||||
| - Lots of old Javascript | ||||
|  | ||||
| ### Fixed | ||||
| - Missing sort broke various charts | ||||
| - Bug in reports that made amounts behave weird | ||||
| - Various bug fixes | ||||
|  | ||||
| ### Security | ||||
| - Tested FF against the naughty string list. | ||||
|  | ||||
| ## [4.3.2] - 2017-01-09 | ||||
|  | ||||
| An intermediate release because something in the Twig and Twigbridge libraries is broken and I have to make sure it doesn't affect you guys. But some cool features were on their way so there's that oo. | ||||
|  | ||||
| ### Added | ||||
| - Some code for issue #475, consistent overviews. | ||||
| - Better currency display. Make sure you have locale packages installed. | ||||
|  | ||||
| ### Changed | ||||
| - Uses a new version of Laravel. | ||||
|  | ||||
| ### Fixed | ||||
| - The password reset routine was broken. | ||||
| - Issue #522, thanks to @xpfgsyb | ||||
| - Issue #524, thanks to @worldworm | ||||
| - Issue #526, thanks to @worldworm | ||||
| - Issue #528, thanks to @skibbipl | ||||
| - Various other fixes. | ||||
|  | ||||
| ## [4.3.1] - 2017-01-04 | ||||
| ### Added | ||||
| - Support for Russian and Polish.  | ||||
| - Support for a proper demo website. | ||||
| - Support for custom decimal places in currencies (#506, suggested by @xpfgsyb). | ||||
| - Most amounts are now right-aligned (#511, suggested by @xpfgsyb). | ||||
| - German is now a "complete" language, more than 75% translated! | ||||
|  | ||||
| ### Changed | ||||
| - **[New Github repository!](github.com/firefly-iii/firefly-iii)** | ||||
| - Better category overview. | ||||
| - #502, thanks to @zjean | ||||
|  | ||||
| ### Removed | ||||
| - Removed a lot of administration functions. | ||||
| - Removed ability to activate users. | ||||
|  | ||||
| ### Fixed | ||||
| - #501, thanks to @zjean | ||||
| - #513, thanks to @skibbipl  | ||||
|  | ||||
| ### Security | ||||
| - #519, thanks to @xpfgsyb | ||||
|  | ||||
| ## [4.3.0] - 2015-12-26 | ||||
| ### Added | ||||
| - New method of keeping track of available budget, see issue #489 | ||||
| - Support for Spanish | ||||
| - Firefly III now has an extended demo mode. Will expand further in the future. | ||||
|   | ||||
|  | ||||
| ### Changed | ||||
| - New favicon | ||||
| - Import routine no longer gives transactions a description #483 | ||||
|  | ||||
|  | ||||
| ### Removed | ||||
| - All test data generation code. | ||||
|  | ||||
| ### Fixed | ||||
| - Removed import accounts from search results #478 | ||||
| - Redirect after delete will no longer go back to deleted item #477 | ||||
| - Cannot math #482 | ||||
| - Fixed bug in virtual balance field #479 | ||||
|  | ||||
| ## [4.2.2] - 2016-12-18 | ||||
| ### Added | ||||
| - New budget report (still a bit of a beta) | ||||
| - Can now edit user | ||||
|  | ||||
| ### Changed | ||||
| - New config for specific events. Still need to build Notifications. | ||||
|  | ||||
| ### Fixed | ||||
| - Various bugs | ||||
| - Issue #472 thanks to @zjean | ||||
|  | ||||
| ## [4.2.1] - 2016-12-09 | ||||
| ### Added | ||||
| - BIC support (see #430) | ||||
| - New category report section and chart (see the general financial report) | ||||
|  | ||||
|  | ||||
| ### Changed | ||||
| - Date range picker now also available on mobile devices (see #435) | ||||
| - Extended range of amounts for issue #439 | ||||
| - Rewrote all routes. Old bookmarks may break. | ||||
|  | ||||
| ## [4.2.0] - 2016-11-27 | ||||
| ### Added | ||||
| - Lots of (empty) tests | ||||
| - Expanded transaction lists (#377) | ||||
| - New charts at account view | ||||
| - First code for #305 | ||||
|  | ||||
|  | ||||
| ### Changed | ||||
| - Updated all email messages. | ||||
| - Made some fonts local | ||||
|  | ||||
|  | ||||
| ### Deprecated | ||||
| - Initial release. | ||||
|  | ||||
| ### Removed | ||||
| - Initial release. | ||||
|  | ||||
| ### Fixed | ||||
| - Issue #408 | ||||
| - Various issues with split journals | ||||
| - Issue #414, thx @zjean | ||||
| - Issue #419, thx @schwalberich  | ||||
| - Issue #422, thx @xzaz | ||||
| - Various import bugs, such as #416 (@zjean) | ||||
|  | ||||
|  | ||||
| ### Security | ||||
| - Initial release. | ||||
|  | ||||
|  | ||||
| ## [4.1.7] - 2016-11-19 | ||||
| ### Added | ||||
| - Check for database table presence in console commands. | ||||
| - Category report | ||||
| - Reinstated old test routines. | ||||
|  | ||||
|  | ||||
| ### Changed | ||||
| - Confirm account setting is no longer in `.env` file. | ||||
| - Titles are now in reverse (current page > parent > firefly iii) | ||||
| - Easier update of language files thanks to Github implementation. | ||||
| - Uniform colours for charts. | ||||
|  | ||||
| ### Fixed | ||||
| - Made all pages more mobile friendly. | ||||
| - Fixed #395 found by @marcoveeneman. | ||||
| - Fixed #398 found by @marcoveeneman. | ||||
| - Fixed #401 found by @marcoveeneman. | ||||
| - Many optimizations. | ||||
| - Updated many libraries. | ||||
| - Various bugs found by myself. | ||||
|  | ||||
|  | ||||
| ## [4.1.6] - 2016-11-06 | ||||
| ### Added | ||||
| - New budget table for multi year report. | ||||
|  | ||||
| ### Changed | ||||
| - Greatly expanded help pages and their function. | ||||
| - Built a new transaction collector, which I think was the idea of @roberthorlings originally. | ||||
| - Rebuilt seach engine. | ||||
|  | ||||
| ### Fixed | ||||
| - #375, thanks to @schoentoon which made it impossible to resurrect currencies. | ||||
| - #370 thanks to @ksmolder | ||||
| - #378, thanks to @HomelessAvatar | ||||
|  | ||||
| ## [4.1.5] - 2016-11-01 | ||||
| ### Changed | ||||
| - Report parts are loaded using AJAX, making a lot of code more simple. | ||||
| - Help content will fall back to English. | ||||
| - Help content is translated through Crowdin. | ||||
|  | ||||
| ### Fixed | ||||
| - Issue #370 | ||||
|  | ||||
| ## [4.1.4] - 2016-10-30 | ||||
| ### Added | ||||
| - New Dockerfile thanks to @schoentoon | ||||
| - Added changing the destination account as rule action. | ||||
| - Added changing the source account as rule action. | ||||
| - Can convert transactions into different types. | ||||
|  | ||||
| ### Changed | ||||
| - Changed the export routine to be more future-proof. | ||||
| - Improved help routine. | ||||
| - Integrated CrowdIn translations. | ||||
| - Simplified reports | ||||
| - Change error message to refer to solution. | ||||
|  | ||||
| ### Fixed | ||||
| - #367 thanks to @HungryFeline | ||||
| - #366 thanks to @3mz3t | ||||
| - #362 and #341 thanks to @bnw | ||||
| - #355 thanks to @roberthorlings | ||||
|  | ||||
| ## [4.1.3] - 2016-10-22 | ||||
| ### Fixed | ||||
| - Some event handlers called the wrong method. | ||||
|  | ||||
| ## [4.1.2] - 2016-10-22 | ||||
|  | ||||
| ### Fixed | ||||
| - A bug is fixed in the journal event handler that prevented Firefly III from actually storing journals. | ||||
|  | ||||
| ## [4.1.1] - 2016-10-22 | ||||
|  | ||||
| ### Added | ||||
| - Option to show deposit accounts on the front page. | ||||
| - Script to upgrade split transactions | ||||
| - Can now save notes on piggy banks. | ||||
| - Extend user admin options. | ||||
| - Run import jobs from the command line | ||||
|  | ||||
|  | ||||
| ### Changed | ||||
| - New preferences screen layout. | ||||
|  | ||||
| ### Deprecated | ||||
| - ``firefly:import`` is now ``firefly:start-import`` | ||||
|  | ||||
| ### Removed | ||||
| - Lots of old code | ||||
|  | ||||
| ### Fixed | ||||
| - #357, where non utf-8 files would break Firefly. | ||||
| - Tab delimiter is not properly loaded from import configuration (@roberthorlings) | ||||
| - System response to yearly bills | ||||
|  | ||||
| ## [4.0.2] - 2016-10-14 | ||||
| ### Added | ||||
| - Added ``intl`` dependency to composer file to ease installation (thanks @telyn) | ||||
| - Added support for Croatian. | ||||
|  | ||||
| ### Changed | ||||
| - Updated all copyright notices to refer to the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/) | ||||
| - Fixed #344 | ||||
| - Fixed #346, thanks to @SanderKleykens | ||||
| - #351 | ||||
| - Did some internal remodelling. | ||||
|  | ||||
| ### Fixed | ||||
| - PostgreSQL compatibility thanks to @SanderKleykens | ||||
| - @RobertHorlings fixed a bug in the ABN Amro import specific. | ||||
|  | ||||
|  | ||||
| ## [4.0.1] - 2016-10-04 | ||||
| ### Added | ||||
| - New ING import specific by @tomwerf | ||||
| - New Presidents Choice specific to fix #307 | ||||
| - Added some trimming (#335) | ||||
|  | ||||
| ### Changed | ||||
| - Initial release. | ||||
|  | ||||
| ### Deprecated | ||||
| - Initial release. | ||||
|  | ||||
| ### Removed | ||||
| - Initial release. | ||||
|  | ||||
| ### Fixed | ||||
| - Fixed a bug where incoming transactions would not be properly filtered in several reports. | ||||
| - #334 by @cyberkov | ||||
| - #337 | ||||
| - #336 | ||||
| - #338 found by @roberthorlings | ||||
|  | ||||
| ### Security | ||||
| - Initial release. | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ## [4.0.0] - 2015-09-26 | ||||
| ### Added | ||||
| - Upgraded to Laravel 5.3, most other libraries upgraded as well. | ||||
| - Added GBP as currency, thanks to @Mortalife | ||||
|  | ||||
| ### Changed | ||||
| - Jump to version 4.0.0. | ||||
| - Firefly III is now subject to a [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/) license. Previous versions of this software are still MIT licensed. | ||||
|  | ||||
| ### Fixed | ||||
| - Support for specific decimal places, thanks to @Mortalife | ||||
| - Various CSS fixes | ||||
| - Various bugs, thanks to @fuf, @sandermulders and @vissert | ||||
| - Various queries optimized for MySQL 5.7 | ||||
|  | ||||
| ## [3.10.4] - 2015-09-14 | ||||
| ### Fixed | ||||
| - Migration fix by @sandermulders | ||||
| - Tricky import bug fix thanks to @vissert | ||||
| - Currency preference will be correctly pulled from user settings, thanks to @fuf | ||||
| - Simplified code for upgrade instructions. | ||||
|  | ||||
|  | ||||
| ## [3.10.3] - 2016-08-29 | ||||
| ### Added | ||||
| - More fields for mass-edit, thanks to @Vissert (#282) | ||||
| - First start of German translation | ||||
|  | ||||
| ### Changed | ||||
| - More optional fields for transactions and the ability to filter them. | ||||
|  | ||||
| ### Removed | ||||
| - Preference for budget maximum. | ||||
|  | ||||
| ### Fixed | ||||
| - A bug in the translation routine broke the import. | ||||
| - It was possible to destroy your Firefly installation by removing all currencies. Thanks @mondjef | ||||
| - Translation bugs. | ||||
| - Import bug. | ||||
|  | ||||
| ### Security | ||||
| - Firefly will not accept registrations beyond the first one, by default. | ||||
|  | ||||
|  | ||||
| ## [3.10.2] - 2016-08-29 | ||||
| ### Added | ||||
| - New Chinese translations. Set Firefly III to show incomplete translations to follow the progress. Want to translate Firefly III in Chinese, or in any other language? Then check out [the Crowdin project](https://crowdin.com/project/firefly-iii). | ||||
| - Added more admin pages. They do nothing yet. | ||||
|  | ||||
| ### Changed | ||||
| - Import routine will now also apply user rules. | ||||
| - Various code cleanup. | ||||
| - Some small HTML changes. | ||||
|  | ||||
| ### Fixed | ||||
| - Bug in the mass edit routines. | ||||
| - Firefly III over a proxy will now work (see [issue #290](https://github.com/firefly-iii/firefly-iii/issues/290)), thanks @dfiel for reporting. | ||||
| - Sneaky bug in the import routine, fixed by @Bonno  | ||||
|  | ||||
| ## [3.10.1] - 2016-08-25 | ||||
| ### Added | ||||
| - More feedback in the import procedure. | ||||
| - Extended model for import job. | ||||
| - Web bases import procedure. | ||||
|  | ||||
|  | ||||
| ### Changed | ||||
| - Scrutinizer configuration | ||||
| - Various code clean up. | ||||
|  | ||||
| ### Removed | ||||
| - Code climate YAML file. | ||||
|  | ||||
| ### Fixed | ||||
| - Fixed a bug where a migration would check an empty table name. | ||||
| - Fixed various bugs in the import routine. | ||||
| - Fixed various bugs in the piggy banks pages. | ||||
| - Fixed a bug in the `firefly:verify` routine | ||||
|  | ||||
| ## [3.10] - 2015-05-25 | ||||
| ### Added | ||||
| - New charts in year report | ||||
| - Can add / remove money from piggy bank on mobile device. | ||||
| - Bill overview shows some useful things. | ||||
| - Firefly will track registration / activation IP addresses. | ||||
|  | ||||
|  | ||||
| ### Changed | ||||
| - Rewrote the import routine. | ||||
| - The date picker now supports more ranges and periods. | ||||
| - Rewrote all migrations. #272 | ||||
|  | ||||
| ### Fixed | ||||
| - Issue #264 | ||||
| - Issue #265 | ||||
| - Fixed amount calculation problems, #266, thanks @xzaz | ||||
| - Issue #271 | ||||
| - Issue #278, #273, thanks @StevenReitsma and @rubella | ||||
| - Bug in attachment download routine would report the wrong size to the user's browser. | ||||
| - Various NULL errors fixed. | ||||
| - Various strict typing errors fixed. | ||||
| - Fixed pagination problems, #276, thanks @xzaz | ||||
| - Fixed a bug where an expense would be assigned to a piggy bank if you created a transfer first. | ||||
| - Bulk update problems, #280, thanks @stickgrinder | ||||
| - Fixed various problems with amount reporting of split transactions. | ||||
|  | ||||
| ## [3.9.1] | ||||
| ### Fixed | ||||
| - Fixed a bug where removing money from a piggy bank would not work. See issue #265 and #269 | ||||
|  | ||||
| ## [3.9.0] | ||||
| ### Added | ||||
| - @zjean has added code that allows you to force "https://"-URL's. | ||||
| - @tonicospinelli has added Portuguese (Brazil) translations. | ||||
| - Firefly III supports the *splitting* of transactions: | ||||
|   - A withdrawal (expense) can be split into multiple sub-transactions (with multiple destinations) | ||||
|   - Likewise for deposits (incomes). You can set multiple sources. | ||||
|   - Likewise for transfers. | ||||
|  | ||||
| ### Changed | ||||
| - Update a lot of libraries. | ||||
| - Big improvement to test data generation. | ||||
| - Cleaned up many repositories. | ||||
|  | ||||
| ### Removed | ||||
| - Front page boxes will no longer respond to credit card bills. | ||||
|  | ||||
| ### Fixed | ||||
| - Many bugs | ||||
|  | ||||
| ## [3.8.4] - 2016-04-24 | ||||
| ### Added | ||||
| - Lots of new translations. | ||||
| - Can now set page size. | ||||
| - Can now mass edit transactions. | ||||
| - Can now mass delete transactions. | ||||
| - Firefly will now attempt to verify the integrity of your database when updating. | ||||
|  | ||||
| ### Changed | ||||
| - New version of Charts library. | ||||
|  | ||||
| ### Fixed | ||||
| - Several CSV related bugs. | ||||
| - Several other bugs. | ||||
| - Bugs fixed by @Bonno. | ||||
|  | ||||
| ## [3.8.3] - 2016-04-17 | ||||
| ### Added | ||||
| - New audit report to see what happened. | ||||
|  | ||||
| ### Changed | ||||
| - New Chart JS release used. | ||||
| - Help function is more reliable. | ||||
|  | ||||
| ### Fixed | ||||
| - Expected bill amount is now correct. | ||||
| - Upgrade will now invalidate cache. | ||||
| - Search was broken. | ||||
| - Queries run better | ||||
|  | ||||
| ## [3.8.2] - 2016-04-03 | ||||
| ### Added | ||||
| - Small user administration at /admin. | ||||
| - Informational popups are working in reports. | ||||
|  | ||||
| ### Changed | ||||
| - User activation emails are better | ||||
|  | ||||
| ### Fixed | ||||
| - Some bugs related to accounts and rules. | ||||
|  | ||||
|  | ||||
| ## [3.8.1] - 2016-03-29 | ||||
| ### Added | ||||
| @@ -15,21 +476,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). | ||||
| ### Changed | ||||
| - The pages related to rules have new URL's. | ||||
|  | ||||
| ### Deprecated | ||||
| - Initial release. | ||||
|  | ||||
| ### Removed | ||||
| - Initial release. | ||||
|  | ||||
| ### Fixed | ||||
| - Spelling errors. | ||||
| - Problems related to the "account repository". | ||||
| - Some views showed empty (0.0) amounts. | ||||
|  | ||||
| ### Security | ||||
| - Initial release. | ||||
|  | ||||
|  | ||||
| ## [3.8.0] - 2016-03-20 | ||||
| ### Added | ||||
| - Two factor authentication, thanks to the excellent work of [zjean](https://github.com/zjean). | ||||
|   | ||||
							
								
								
									
										46
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| FROM php:7-apache | ||||
|  | ||||
| RUN apt-get update -y && \ | ||||
|     apt-get install -y --no-install-recommends libcurl4-openssl-dev \ | ||||
|                                                zlib1g-dev \ | ||||
|                                                libjpeg62-turbo-dev \ | ||||
|                                                libpng12-dev \ | ||||
|                                                libicu-dev \ | ||||
|                                                libmcrypt-dev \ | ||||
|                                                libedit-dev \ | ||||
|                                                libtidy-dev \ | ||||
|                                                libxml2-dev \ | ||||
|                                                libsqlite3-dev \ | ||||
|                                                libbz2-dev \ | ||||
|                                                locales && \ | ||||
|     apt-get clean && \ | ||||
|     rm -rf /var/lib/apt/lists/* | ||||
|  | ||||
| RUN docker-php-ext-install -j$(nproc) curl gd intl json mcrypt readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2 | ||||
|  | ||||
| # Generate locales supported by firefly | ||||
| RUN echo "en_US.UTF-8 UTF-8\nde_DE.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8" > /etc/locale.gen && locale-gen | ||||
|  | ||||
| # Enable apache mod rewrite.. | ||||
| RUN a2enmod rewrite | ||||
|  | ||||
| # Setup the Composer installer | ||||
| RUN curl -o /tmp/composer-setup.php https://getcomposer.org/installer && \ | ||||
|   curl -o /tmp/composer-setup.sig https://composer.github.io/installer.sig && \ | ||||
|   php -r "if (hash('SHA384', file_get_contents('/tmp/composer-setup.php')) !== trim(file_get_contents('/tmp/composer-setup.sig'))) { unlink('/tmp/composer-setup.php'); echo 'Invalid installer' . PHP_EOL; exit(1); }" && \ | ||||
|   chmod +x /tmp/composer-setup.php && \ | ||||
|   php /tmp/composer-setup.php && \ | ||||
|   mv composer.phar /usr/local/bin/composer && \ | ||||
|   rm -f /tmp/composer-setup.{php,sig} | ||||
|  | ||||
| ADD . /var/www/firefly-iii | ||||
| RUN chown -R www-data:www-data /var/www/ | ||||
| ADD docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf | ||||
|  | ||||
| USER www-data | ||||
|  | ||||
| WORKDIR /var/www/firefly-iii | ||||
|  | ||||
| RUN composer install --no-scripts --no-dev | ||||
|  | ||||
| USER root | ||||
							
								
								
									
										6
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								LICENSE
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| Copyright (C) 2016 Sander Dorigo | ||||
| Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
| This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
| https://creativecommons.org/licenses/by-sa/4.0/ | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|   | ||||
							
								
								
									
										32
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,16 +1,22 @@ | ||||
| # Firefly III | ||||
| # Firefly III: A personal finances manager  | ||||
|  | ||||
| [](https://secure.php.net/downloads.php#v7.0.4) | ||||
| [](https://packagist.org/packages/grumpydictator/firefly-iii) | ||||
| [](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master) | ||||
| [](https://scrutinizer-ci.com/g/JC5/firefly-iii/build-status/master) | ||||
| [](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102) | ||||
| [](https://codeclimate.com/github/JC5/firefly-iii) | ||||
| [](https://secure.php.net/downloads.php#v7.0.4) [](https://packagist.org/packages/grumpydictator/firefly-iii) [](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/?branch=master) [](https://travis-ci.org/firefly-iii/firefly-iii) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA)  | ||||
|  | ||||
| ## About | ||||
| [](https://i.nder.be/h2b37243) [](https://i.nder.be/hv70pbwc) | ||||
|  | ||||
| "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. | ||||
| [](https://i.nder.be/ccn0u2mp) [](https://i.nder.be/gm8hbh7z) | ||||
|  | ||||
| "Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared  household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money. | ||||
|  | ||||
| ## Try it out! | ||||
|  | ||||
| Try out Firefly III on the [demo site](https://firefly-iii.nder.be/). | ||||
|  | ||||
| ## Installation | ||||
|  | ||||
| To install Firefly III, you'll need a web server (preferrably on Linux) and access to the command line. Then, please read the [installation guide](https://firefly-iii.github.io/installation-guide/). | ||||
|  | ||||
| ## More about Firefly III | ||||
|  | ||||
| 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. | ||||
|  | ||||
| @@ -23,4 +29,8 @@ Firefly works on the principle that if you know where you're money is going, you | ||||
| - Firefly has lots of features without becoming fancy or bloated. | ||||
| - If you feel you're missing something you can just ask me and I'll add it! | ||||
|  | ||||
| Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://jc5.github.io/firefly-iii/). | ||||
| Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://firefly-iii.github.io/). | ||||
|  | ||||
| If you like Firefly and if it helps you save lots of money, why not send me [a dime for every dollar saved](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA) (this is a joke, although the Paypal form works just fine, try it!) | ||||
|  | ||||
| If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com). | ||||
|   | ||||
| @@ -1,27 +0,0 @@ | ||||
| <?xml version="1.0"?> | ||||
| <ruleset name="JamesStandard"> | ||||
|  <rule ref="Zend"> | ||||
| 	<exclude name="Zend.NamingConventions.ValidVariableName" /> | ||||
| 	<exclude name="PEAR.WhiteSpace.ScopeClosingBrace" /> | ||||
|   	<!--<exclude name="PEAR.Whitespace.ScopeIndent"/>--> | ||||
|   	<exclude name="PEAR.WhiteSpace.ScopeClosingBrace"/> | ||||
|   	<exclude name="Generic.Formatting.MultipleStatementAlignment.Incorrect" /> | ||||
|   	<exclude name="PEAR.Functions.FunctionCallSignature" /> | ||||
|  | ||||
|  </rule> | ||||
|  <!-- | ||||
|     Here we change two messages from the same sniff. Note how the | ||||
|     codes are slightly different because the sniff developer has | ||||
|     defined both a MaxExceeded message and a TooLong message. In the | ||||
|     case of this sniff, one is used for warnings and one is used | ||||
|     for errors. | ||||
|  --> | ||||
|  <rule ref="Generic.Files.LineLength"> | ||||
|   <properties> | ||||
|    <property name="lineLimit" value="160"/> | ||||
|    <property name="absoluteLineLimit" value="160"/> | ||||
|   </properties> | ||||
|  </rule> | ||||
|   | ||||
|  | ||||
| </ruleset> | ||||
| @@ -1,322 +0,0 @@ | ||||
| <code_scheme name="Use This One"> | ||||
|   <option name="RIGHT_MARGIN" value="160" /> | ||||
|   <CoffeeScriptCodeStyleSettings> | ||||
|     <option name="SPACE_BEFORE_PROPERTY_COLON" value="true" /> | ||||
|   </CoffeeScriptCodeStyleSettings> | ||||
|   <JSCodeStyleSettings> | ||||
|     <option name="ALIGN_MULTILINE_VAR_DECLARATION" value="true" /> | ||||
|   </JSCodeStyleSettings> | ||||
|   <PHPCodeStyleSettings> | ||||
|     <option name="ALIGN_KEY_VALUE_PAIRS" value="true" /> | ||||
|     <option name="ALIGN_PHPDOC_PARAM_NAMES" value="true" /> | ||||
|     <option name="ALIGN_PHPDOC_COMMENTS" value="true" /> | ||||
|     <option name="ALIGN_ASSIGNMENTS" value="true" /> | ||||
|     <option name="COMMA_AFTER_LAST_ARRAY_ELEMENT" value="true" /> | ||||
|     <option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" /> | ||||
|     <option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" /> | ||||
|     <option name="PHPDOC_WRAP_LONG_LINES" value="true" /> | ||||
|     <option name="LOWER_CASE_BOOLEAN_CONST" value="true" /> | ||||
|     <option name="LOWER_CASE_NULL_CONST" value="true" /> | ||||
|     <option name="BLANK_LINE_BEFORE_RETURN_STATEMENT" value="true" /> | ||||
|     <option name="KEEP_RPAREN_AND_LBRACE_ON_ONE_LINE" value="true" /> | ||||
|     <option name="ALIGN_CLASS_CONSTANTS" value="true" /> | ||||
|     <option name="FORCE_SHORT_DECLARATION_ARRAY_STYLE" value="true" /> | ||||
|   </PHPCodeStyleSettings> | ||||
|   <XML> | ||||
|     <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" /> | ||||
|   </XML> | ||||
|   <codeStyleSettings language="CoffeeScript"> | ||||
|     <option name="KEEP_LINE_BREAKS" value="false" /> | ||||
|     <option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" /> | ||||
|   </codeStyleSettings> | ||||
|   <codeStyleSettings language="JavaScript"> | ||||
|     <option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" /> | ||||
|     <option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" /> | ||||
|     <option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" /> | ||||
|     <option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" /> | ||||
|   </codeStyleSettings> | ||||
|   <codeStyleSettings language="PHP"> | ||||
|     <option name="RIGHT_MARGIN" value="160" /> | ||||
|     <option name="KEEP_FIRST_COLUMN_COMMENT" value="false" /> | ||||
|     <option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" /> | ||||
|     <option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" /> | ||||
|     <option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" /> | ||||
|     <option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" /> | ||||
|     <option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" /> | ||||
|     <option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" /> | ||||
|     <option name="ALIGN_GROUP_FIELD_DECLARATIONS" value="true" /> | ||||
|     <option name="CALL_PARAMETERS_WRAP" value="1" /> | ||||
|     <option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" /> | ||||
|     <option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" /> | ||||
|     <option name="METHOD_PARAMETERS_WRAP" value="1" /> | ||||
|     <option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" /> | ||||
|     <option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" /> | ||||
|     <option name="EXTENDS_LIST_WRAP" value="1" /> | ||||
|     <option name="EXTENDS_KEYWORD_WRAP" value="1" /> | ||||
|     <option name="METHOD_CALL_CHAIN_WRAP" value="1" /> | ||||
|     <option name="BINARY_OPERATION_WRAP" value="1" /> | ||||
|     <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" /> | ||||
|     <option name="TERNARY_OPERATION_WRAP" value="1" /> | ||||
|     <option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" /> | ||||
|     <option name="FOR_STATEMENT_WRAP" value="1" /> | ||||
|     <option name="FOR_STATEMENT_LPAREN_ON_NEXT_LINE" value="true" /> | ||||
|     <option name="FOR_STATEMENT_RPAREN_ON_NEXT_LINE" value="true" /> | ||||
|     <option name="ARRAY_INITIALIZER_WRAP" value="1" /> | ||||
|     <option name="ASSIGNMENT_WRAP" value="1" /> | ||||
|     <option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" /> | ||||
|     <option name="IF_BRACE_FORCE" value="3" /> | ||||
|     <option name="DOWHILE_BRACE_FORCE" value="3" /> | ||||
|     <option name="WHILE_BRACE_FORCE" value="3" /> | ||||
|     <option name="FOR_BRACE_FORCE" value="3" /> | ||||
|     <arrangement> | ||||
|       <tokens> | ||||
|         <token id="modifiers" name="modifiers"> | ||||
|           <rules> | ||||
|             <rule> | ||||
|               <match> | ||||
|                 <AND> | ||||
|                   <PUBLIC>true</PUBLIC> | ||||
|                   <STATIC>true</STATIC> | ||||
|                 </AND> | ||||
|               </match> | ||||
|             </rule> | ||||
|             <rule> | ||||
|               <match> | ||||
|                 <AND> | ||||
|                   <PROTECTED>true</PROTECTED> | ||||
|                   <STATIC>true</STATIC> | ||||
|                 </AND> | ||||
|               </match> | ||||
|             </rule> | ||||
|             <rule> | ||||
|               <match> | ||||
|                 <AND> | ||||
|                   <PRIVATE>true</PRIVATE> | ||||
|                   <STATIC>true</STATIC> | ||||
|                 </AND> | ||||
|               </match> | ||||
|             </rule> | ||||
|             <rule> | ||||
|               <match> | ||||
|                 <PUBLIC>true</PUBLIC> | ||||
|               </match> | ||||
|               <order>BY_NAME</order> | ||||
|             </rule> | ||||
|             <rule> | ||||
|               <match> | ||||
|                 <PROTECTED>true</PROTECTED> | ||||
|               </match> | ||||
|               <order>BY_NAME</order> | ||||
|             </rule> | ||||
|             <rule> | ||||
|               <match> | ||||
|                 <PRIVATE>true</PRIVATE> | ||||
|               </match> | ||||
|               <order>BY_NAME</order> | ||||
|             </rule> | ||||
|           </rules> | ||||
|         </token> | ||||
|         <token id="visibility" name="visibility"> | ||||
|           <rules> | ||||
|             <rule> | ||||
|               <match> | ||||
|                 <PUBLIC>true</PUBLIC> | ||||
|               </match> | ||||
|             </rule> | ||||
|             <rule> | ||||
|               <match> | ||||
|                 <PROTECTED>true</PROTECTED> | ||||
|               </match> | ||||
|             </rule> | ||||
|             <rule> | ||||
|               <match> | ||||
|                 <PRIVATE>true</PRIVATE> | ||||
|               </match> | ||||
|             </rule> | ||||
|           </rules> | ||||
|         </token> | ||||
|       </tokens> | ||||
|       <groups> | ||||
|         <group> | ||||
|           <type>GETTERS_AND_SETTERS</type> | ||||
|           <order>KEEP</order> | ||||
|         </group> | ||||
|       </groups> | ||||
|       <rules> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <CONST /> | ||||
|             </match> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <AND> | ||||
|                 <FIELD /> | ||||
|                 <PUBLIC /> | ||||
|                 <STATIC /> | ||||
|               </AND> | ||||
|             </match> | ||||
|             <order>BY_NAME</order> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <AND> | ||||
|                 <FIELD /> | ||||
|                 <PROTECTED /> | ||||
|                 <STATIC /> | ||||
|               </AND> | ||||
|             </match> | ||||
|             <order>BY_NAME</order> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <AND> | ||||
|                 <FIELD /> | ||||
|                 <PRIVATE /> | ||||
|                 <STATIC /> | ||||
|               </AND> | ||||
|             </match> | ||||
|             <order>BY_NAME</order> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <AND> | ||||
|                 <FIELD /> | ||||
|                 <PUBLIC /> | ||||
|               </AND> | ||||
|             </match> | ||||
|             <order>BY_NAME</order> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <AND> | ||||
|                 <FIELD /> | ||||
|                 <PROTECTED /> | ||||
|               </AND> | ||||
|             </match> | ||||
|             <order>BY_NAME</order> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <AND> | ||||
|                 <FIELD /> | ||||
|                 <PRIVATE /> | ||||
|               </AND> | ||||
|             </match> | ||||
|             <order>BY_NAME</order> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <CONSTRUCTOR /> | ||||
|             </match> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <AND> | ||||
|                 <METHOD /> | ||||
|                 <PUBLIC /> | ||||
|                 <STATIC /> | ||||
|               </AND> | ||||
|             </match> | ||||
|             <order>BY_NAME</order> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <AND> | ||||
|                 <METHOD /> | ||||
|                 <PROTECTED /> | ||||
|                 <STATIC /> | ||||
|               </AND> | ||||
|             </match> | ||||
|             <order>BY_NAME</order> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <AND> | ||||
|                 <METHOD /> | ||||
|                 <PRIVATE /> | ||||
|                 <STATIC /> | ||||
|               </AND> | ||||
|             </match> | ||||
|             <order>BY_NAME</order> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <AND> | ||||
|                 <METHOD /> | ||||
|                 <PUBLIC /> | ||||
|               </AND> | ||||
|             </match> | ||||
|             <order>BY_NAME</order> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <AND> | ||||
|                 <METHOD /> | ||||
|                 <PROTECTED /> | ||||
|               </AND> | ||||
|             </match> | ||||
|             <order>BY_NAME</order> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <AND> | ||||
|                 <METHOD /> | ||||
|                 <PRIVATE /> | ||||
|               </AND> | ||||
|             </match> | ||||
|             <order>BY_NAME</order> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <TRAIT /> | ||||
|             </match> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <INTERFACE /> | ||||
|             </match> | ||||
|           </rule> | ||||
|         </section> | ||||
|         <section> | ||||
|           <rule> | ||||
|             <match> | ||||
|               <CLASS /> | ||||
|             </match> | ||||
|           </rule> | ||||
|         </section> | ||||
|       </rules> | ||||
|     </arrangement> | ||||
|   </codeStyleSettings> | ||||
| </code_scheme> | ||||
| @@ -1,36 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ruleset name="pcsg-generated-ruleset"  | ||||
|     xmlns="http://pmd.sf.net/ruleset/1.0.0"  | ||||
|     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  | ||||
|     xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" | ||||
|     xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"> | ||||
| 	<description>Created with the PHP Coding Standard Generator. http://edorian.github.com/php-coding-standard-generator/ | ||||
| 	</description> | ||||
| 	<rule ref="rulesets/codesize.xml/CyclomaticComplexity"> | ||||
| 	    <properties> | ||||
| 	        <property name="reportLevel" value="5"/> | ||||
| 	    </properties> | ||||
| 	</rule> | ||||
| 	<rule ref="rulesets/codesize.xml/NPathComplexity"> | ||||
| 	    <properties> | ||||
| 	        <property name="minimum" value="128"/> | ||||
| 	    </properties> | ||||
| 	</rule> | ||||
| 	<rule ref="rulesets/codesize.xml/ExcessiveMethodLength"> | ||||
| 	    <properties> | ||||
| 	        <property name="minimum" value="40"/> | ||||
| 	    </properties> | ||||
| 	</rule> | ||||
| 	<rule ref="rulesets/codesize.xml/ExcessiveParameterList"> | ||||
| 	    <properties> | ||||
| 	        <property name="minimum" value="5"/> | ||||
| 	    </properties> | ||||
| 	</rule> | ||||
|  | ||||
| 	<!-- Import rule set and exclude rules --> | ||||
|     <rule ref="rulesets/controversial.xml"> | ||||
|         <exclude name="CamelCasePropertyName" /> | ||||
|     </rule> | ||||
|  | ||||
|      | ||||
| </ruleset> | ||||
| @@ -1 +0,0 @@ | ||||
| These are some of the files I use for code formatting, PHPMD and PHPCS. | ||||
							
								
								
									
										63
									
								
								app/Bootstrap/ConfigureLogging.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								app/Bootstrap/ConfigureLogging.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ConfigureLogging.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Bootstrap; | ||||
|  | ||||
| use Illuminate\Contracts\Foundation\Application; | ||||
| use Illuminate\Foundation\Bootstrap\ConfigureLogging as IlluminateConfigureLogging; | ||||
| use Illuminate\Log\Writer; | ||||
|  | ||||
| /** | ||||
|  * Class ConfigureLogging | ||||
|  * | ||||
|  * @package FireflyIII\Bootstrap | ||||
|  */ | ||||
| class ConfigureLogging extends IlluminateConfigureLogging | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Configure the Monolog handlers for the application. | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Foundation\Application $app | ||||
|      * @param  \Illuminate\Log\Writer                       $log | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     protected function configureDailyHandler(Application $app, Writer $log) | ||||
|     { | ||||
|         $config = $app->make('config'); | ||||
|  | ||||
|         $maxFiles = $config->get('app.log_max_files'); | ||||
|  | ||||
|         $log->useDailyFiles( | ||||
|             $app->storagePath() . '/logs/firefly-iii.log', is_null($maxFiles) ? 5 : $maxFiles, | ||||
|             $config->get('app.log_level', 'debug') | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Configure the Monolog handlers for the application. | ||||
|      * | ||||
|      * @param  \Illuminate\Contracts\Foundation\Application $app | ||||
|      * @param  \Illuminate\Log\Writer                       $log | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     protected function configureSingleHandler(Application $app, Writer $log) | ||||
|     { | ||||
|         $log->useFiles( | ||||
|             $app->storagePath() . '/logs/firefly-iii.log', | ||||
|             $app->make('config')->get('app.log_level', 'debug') | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										144
									
								
								app/Console/Commands/CreateImport.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								app/Console/Commands/CreateImport.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| <?php | ||||
| /** | ||||
|  * CreateImport.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Console\Commands; | ||||
|  | ||||
| use Artisan; | ||||
| use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; | ||||
| use FireflyIII\Repositories\User\UserRepositoryInterface; | ||||
| use Illuminate\Console\Command; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class CreateImport | ||||
|  * | ||||
|  * @package FireflyIII\Console\Commands | ||||
|  */ | ||||
| class CreateImport extends Command | ||||
| { | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = 'Use this command to create a new import. Your user ID can be found on the /profile page.'; | ||||
|  | ||||
|     /** | ||||
|      * The name and signature of the console command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'firefly:create-import {file} {configuration} {--user=1} {--type=csv} {--start}'; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.ExcessiveMethodLength) // cannot be helped | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         /** @var UserRepositoryInterface $userRepository */ | ||||
|         $userRepository = app(UserRepositoryInterface::class); | ||||
|         $file           = $this->argument('file'); | ||||
|         $configuration  = $this->argument('configuration'); | ||||
|         $user           = $userRepository->find(intval($this->option('user'))); | ||||
|         $cwd            = getcwd(); | ||||
|         $type           = strtolower($this->option('type')); | ||||
|  | ||||
|         if (!$this->validArguments()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $configurationData = json_decode(file_get_contents($configuration)); | ||||
|         if (is_null($configurationData)) { | ||||
|             $this->error(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd)); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $this->info(sprintf('Going to create a job to import file: %s', $file)); | ||||
|         $this->info(sprintf('Using configuration file: %s', $configuration)); | ||||
|         $this->info(sprintf('Import into user: #%d (%s)', $user->id, $user->email)); | ||||
|         $this->info(sprintf('Type of import: %s', $type)); | ||||
|  | ||||
|         /** @var ImportJobRepositoryInterface $jobRepository */ | ||||
|         $jobRepository = app(ImportJobRepositoryInterface::class, [$user]); | ||||
|         $job           = $jobRepository->create($type); | ||||
|         $this->line(sprintf('Created job "%s"...', $job->key)); | ||||
|  | ||||
|         Artisan::call('firefly:encrypt', ['file' => $file, 'key' => $job->key]); | ||||
|         $this->line('Stored import data...'); | ||||
|  | ||||
|         $job->configuration = $configurationData; | ||||
|         $job->status        = 'settings_complete'; | ||||
|         $job->save(); | ||||
|         $this->line('Stored configuration...'); | ||||
|  | ||||
|         if ($this->option('start') === true) { | ||||
|             $this->line('The import will start in a moment. This process is not visible...'); | ||||
|             Log::debug('Go for import!'); | ||||
|             Artisan::call('firefly:start-import', ['key' => $job->key]); | ||||
|             $this->line('Done!'); | ||||
|         } | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly. | ||||
|      */ | ||||
|     private function validArguments(): bool | ||||
|     { | ||||
|         /** @var UserRepositoryInterface $userRepository */ | ||||
|         $userRepository = app(UserRepositoryInterface::class); | ||||
|         $file           = $this->argument('file'); | ||||
|         $configuration  = $this->argument('configuration'); | ||||
|         $user           = $userRepository->find(intval($this->option('user'))); | ||||
|         $cwd            = getcwd(); | ||||
|         $validTypes     = array_keys(config('firefly.import_formats')); | ||||
|         $type           = strtolower($this->option('type')); | ||||
|  | ||||
|         if (is_null($user->id)) { | ||||
|             $this->error(sprintf('There is no user with ID %d.', $this->option('user'))); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|         if (!in_array($type, $validTypes)) { | ||||
|             $this->error(sprintf('Cannot import file of type "%s"', $type)); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!file_exists($file)) { | ||||
|             $this->error(sprintf('Firefly III cannot find file "%s" (working directory: "%s").', $file, $cwd)); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!file_exists($configuration)) { | ||||
|             $this->error(sprintf('Firefly III cannot find configuration file "%s" (working directory: "%s").', $configuration, $cwd)); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										68
									
								
								app/Console/Commands/EncryptFile.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								app/Console/Commands/EncryptFile.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| <?php | ||||
| /** | ||||
|  * EncryptFile.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Console\Commands; | ||||
|  | ||||
| use Crypt; | ||||
| use Illuminate\Console\Command; | ||||
|  | ||||
| /** | ||||
|  * Class EncryptFile | ||||
|  * | ||||
|  * @package FireflyIII\Console\Commands | ||||
|  */ | ||||
| class EncryptFile extends Command | ||||
| { | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = 'Encrypts a file and places it in the storage/upload directory.'; | ||||
|  | ||||
|     /** | ||||
|      * The name and signature of the console command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'firefly:encrypt-file {file} {key}'; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         $file = e(strval($this->argument('file'))); | ||||
|         if (!file_exists($file)) { | ||||
|             $this->error(sprintf('File "%s" does not seem to exist.', $file)); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|         $content = file_get_contents($file); | ||||
|         $content = Crypt::encrypt($content); | ||||
|         $newName = e(strval($this->argument('key'))) . '.upload'; | ||||
|  | ||||
|         $path = storage_path('upload') . '/' . $newName; | ||||
|         file_put_contents($path, $content); | ||||
|         $this->line(sprintf('Encrypted "%s" and put it in "%s"', $file, $path)); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										139
									
								
								app/Console/Commands/Import.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								app/Console/Commands/Import.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Import.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Console\Commands; | ||||
|  | ||||
| use FireflyIII\Import\ImportProcedure; | ||||
| use FireflyIII\Import\Logging\CommandHandler; | ||||
| use FireflyIII\Models\ImportJob; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Support\Collection; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class Import | ||||
|  * | ||||
|  * @package FireflyIII\Console\Commands | ||||
|  */ | ||||
| class Import extends Command | ||||
| { | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = 'This will start a new import.'; | ||||
|  | ||||
|     /** | ||||
|      * The name and signature of the console command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'firefly:start-import {key}'; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         Log::debug('Start start-import command'); | ||||
|         $jobKey = $this->argument('key'); | ||||
|         $job    = ImportJob::whereKey($jobKey)->first(); | ||||
|         if (!$this->isValid($job)) { | ||||
|             Log::error('Job is not valid for some reason. Exit.'); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $this->line(sprintf('Going to import job with key "%s" of type "%s"', $job->key, $job->file_type)); | ||||
|  | ||||
|         $monolog = Log::getMonolog(); | ||||
|         $handler = new CommandHandler($this); | ||||
|         $monolog->pushHandler($handler); | ||||
|         $importProcedure = new ImportProcedure; | ||||
|         $result          = $importProcedure->runImport($job); | ||||
|  | ||||
|         // display result to user: | ||||
|         $this->presentResults($result); | ||||
|         $this->line('The import has completed.'); | ||||
|  | ||||
|         // get any errors from the importer: | ||||
|         $this->presentErrors($job); | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param ImportJob $job | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function isValid(ImportJob $job): bool | ||||
|     { | ||||
|         if (is_null($job)) { | ||||
|             $this->error('This job does not seem to exist.'); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if ($job->status != 'settings_complete') { | ||||
|             $this->error('This job is not ready to be imported.'); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param ImportJob $job | ||||
|      */ | ||||
|     private function presentErrors(ImportJob $job) | ||||
|     { | ||||
|         $extendedStatus = $job->extended_status; | ||||
|         if (isset($extendedStatus['errors']) && count($extendedStatus['errors']) > 0) { | ||||
|             $this->line(sprintf('The following %d error(s) occured during the import:', count($extendedStatus['errors']))); | ||||
|             foreach ($extendedStatus['errors'] as $error) { | ||||
|                 $this->error($error); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $result | ||||
|      */ | ||||
|     private function presentResults(Collection $result) | ||||
|     { | ||||
|         /** | ||||
|          * @var int                $index | ||||
|          * @var TransactionJournal $journal | ||||
|          */ | ||||
|         foreach ($result as $index => $journal) { | ||||
|             if (!is_null($journal->id)) { | ||||
|                 $this->line(sprintf('Line #%d has been imported as transaction #%d.', $index, $journal->id)); | ||||
|                 continue; | ||||
|             } | ||||
|             $this->error(sprintf('Could not store line #%d', $index)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										85
									
								
								app/Console/Commands/ScanAttachments.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								app/Console/Commands/ScanAttachments.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ScanAttachments.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Console\Commands; | ||||
|  | ||||
| use Crypt; | ||||
| use FireflyIII\Models\Attachment; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Contracts\Encryption\DecryptException; | ||||
| use Illuminate\Contracts\Filesystem\FileNotFoundException; | ||||
| use Storage; | ||||
|  | ||||
| /** | ||||
|  * Class ScanAttachments | ||||
|  * | ||||
|  * @package FireflyIII\Console\Commands | ||||
|  */ | ||||
| class ScanAttachments extends Command | ||||
| { | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = 'Rescan all attachments and re-set the MD5 hash and mime.'; | ||||
|  | ||||
|     /** | ||||
|      * The name and signature of the console command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'firefly:scan-attachments'; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         $attachments = Attachment::get(); | ||||
|         $disk        = Storage::disk('upload'); | ||||
|         /** @var Attachment $attachment */ | ||||
|         foreach ($attachments as $attachment) { | ||||
|             $fileName = $attachment->fileName(); | ||||
|             try { | ||||
|                 $content = $disk->get($fileName); | ||||
|             } catch (FileNotFoundException $e) { | ||||
|                 $this->error(sprintf('Could not find data for attachment #%d', $attachment->id)); | ||||
|                 continue; | ||||
|             } | ||||
|             try { | ||||
|                 $decrypted = Crypt::decrypt($content); | ||||
|             } catch (DecryptException $e) { | ||||
|                 $this->error(sprintf('Could not decrypt data of attachment #%d', $attachment->id)); | ||||
|                 continue; | ||||
|             } | ||||
|             $tmpfname = tempnam(sys_get_temp_dir(), 'FireflyIII'); | ||||
|             file_put_contents($tmpfname, $decrypted); | ||||
|             $md5              = md5_file($tmpfname); | ||||
|             $mime             = mime_content_type($tmpfname); | ||||
|             $attachment->md5  = $md5; | ||||
|             $attachment->mime = $mime; | ||||
|             $attachment->save(); | ||||
|             $this->line(sprintf('Fixed attachment #%d', $attachment->id)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										159
									
								
								app/Console/Commands/UpgradeDatabase.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								app/Console/Commands/UpgradeDatabase.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,159 @@ | ||||
| <?php | ||||
| /** | ||||
|  * UpgradeDatabase.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Console\Commands; | ||||
|  | ||||
|  | ||||
| use DB; | ||||
| use FireflyIII\Models\BudgetLimit; | ||||
| use FireflyIII\Models\LimitRepetition; | ||||
| use FireflyIII\Models\Transaction; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Database\QueryException; | ||||
| use Log; | ||||
| use Schema; | ||||
|  | ||||
| /** | ||||
|  * Class UpgradeDatabase | ||||
|  * | ||||
|  * @package FireflyIII\Console\Commands | ||||
|  */ | ||||
| class UpgradeDatabase extends Command | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = 'Will run various commands to update database records.'; | ||||
|     /** | ||||
|      * The name and signature of the console command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'firefly:upgrade-database'; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         $this->setTransactionIdentifier(); | ||||
|         $this->migrateRepetitions(); | ||||
|     } | ||||
|  | ||||
|     private function migrateRepetitions() | ||||
|     { | ||||
|         if (!Schema::hasTable('budget_limits')) { | ||||
|             return; | ||||
|         } | ||||
|         // get all budget limits with end_date NULL | ||||
|         $set = BudgetLimit::whereNull('end_date')->get(); | ||||
|         $this->line(sprintf('Found %d budget limit(s) to update', $set->count())); | ||||
|         /** @var BudgetLimit $budgetLimit */ | ||||
|         foreach ($set as $budgetLimit) { | ||||
|             // get limit repetition (should be just one): | ||||
|             /** @var LimitRepetition $repetition */ | ||||
|             $repetition = $budgetLimit->limitrepetitions()->first(); | ||||
|             if (!is_null($repetition)) { | ||||
|                 $budgetLimit->end_date = $repetition->enddate; | ||||
|                 $budgetLimit->save(); | ||||
|                 $this->line(sprintf('Updated budget limit #%d', $budgetLimit->id)); | ||||
|                 $repetition->delete(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This is strangely complex, because the HAVING modifier is a no-no. And subqueries in Laravel are weird. | ||||
|      */ | ||||
|     private function setTransactionIdentifier() | ||||
|     { | ||||
|         // if table does not exist, return false | ||||
|         if (!Schema::hasTable('transaction_journals')) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         $subQuery = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                                       ->whereNull('transaction_journals.deleted_at') | ||||
|                                       ->whereNull('transactions.deleted_at') | ||||
|                                       ->groupBy(['transaction_journals.id']) | ||||
|                                       ->select(['transaction_journals.id', DB::raw('COUNT(transactions.id) AS t_count')]); | ||||
|  | ||||
|         $result     = DB::table(DB::raw('(' . $subQuery->toSql() . ') AS derived')) | ||||
|                         ->mergeBindings($subQuery->getQuery()) | ||||
|                         ->where('t_count', '>', 2) | ||||
|                         ->select(['id', 't_count']); | ||||
|         $journalIds = array_unique($result->pluck('id')->toArray()); | ||||
|  | ||||
|         foreach ($journalIds as $journalId) { | ||||
|             $this->updateJournal(intval($journalId)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * grab all positive transactiosn from this journal that are not deleted. for each one, grab the negative opposing one | ||||
|      * which has 0 as an identifier and give it the same identifier. | ||||
|      * | ||||
|      * @param int $journalId | ||||
|      */ | ||||
|     private function updateJournal(int $journalId) | ||||
|     { | ||||
|         $identifier   = 0; | ||||
|         $processed    = []; | ||||
|         $transactions = Transaction::where('transaction_journal_id', $journalId)->where('amount', '>', 0)->get(); | ||||
|         /** @var Transaction $transaction */ | ||||
|         foreach ($transactions as $transaction) { | ||||
|             // find opposing: | ||||
|             $amount = bcmul(strval($transaction->amount), '-1'); | ||||
|  | ||||
|             try { | ||||
|                 /** @var Transaction $opposing */ | ||||
|                 $opposing = Transaction::where('transaction_journal_id', $journalId) | ||||
|                                        ->where('amount', $amount)->where('identifier', '=', 0) | ||||
|                                        ->whereNotIn('id', $processed) | ||||
|                                        ->first(); | ||||
|             } catch (QueryException $e) { | ||||
|                 Log::error($e->getMessage()); | ||||
|                 $this->error('Firefly III could not find the "identifier" field in the "transactions" table.'); | ||||
|                 $this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version'))); | ||||
|                 $this->error('Please run "php artisan migrate" to add this field to the table.'); | ||||
|                 $this->info('Then, run "php artisan firefly:upgrade-database" to try again.'); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|             if (!is_null($opposing)) { | ||||
|                 // give both a new identifier: | ||||
|                 $transaction->identifier = $identifier; | ||||
|                 $transaction->save(); | ||||
|                 $opposing->identifier = $identifier; | ||||
|                 $opposing->save(); | ||||
|                 $processed[] = $transaction->id; | ||||
|                 $processed[] = $opposing->id; | ||||
|                 $this->line(sprintf('Database upgrade for journal #%d, transactions #%d and #%d', $journalId, $transaction->id, $opposing->id)); | ||||
|             } | ||||
|             $identifier++; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,9 +1,18 @@ | ||||
| <?php | ||||
| /** | ||||
|  * UpgradeFireflyInstructions.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Console\Commands; | ||||
|  | ||||
| use Config; | ||||
| use Illuminate\Console\Command; | ||||
|  | ||||
| /** | ||||
| @@ -18,13 +27,13 @@ class UpgradeFireflyInstructions extends Command | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = 'Command description'; | ||||
|     protected $description = 'Instructions in case of upgrade trouble.'; | ||||
|     /** | ||||
|      * The name and signature of the console command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'firefly:upgrade-instructions'; | ||||
|     protected $signature = 'firefly:instructions {task}'; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
| @@ -37,32 +46,118 @@ class UpgradeFireflyInstructions extends Command | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         // | ||||
|         $version = Config::get('firefly.version'); | ||||
|         $config  = Config::get('upgrade.text'); | ||||
|         $text    = $config[$version] ?? null; | ||||
|  | ||||
|         $this->line('+------------------------------------------------------------------------------+'); | ||||
|         $this->line(''); | ||||
|         if ($this->argument('task') == 'update') { | ||||
|             $this->updateInstructions(); | ||||
|         } | ||||
|         if ($this->argument('task') == 'install') { | ||||
|             $this->installInstructions(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show a nice box | ||||
|      * | ||||
|      * @param string $text | ||||
|      */ | ||||
|     private function boxed(string $text) | ||||
|     { | ||||
|         $parts = explode("\n", wordwrap($text)); | ||||
|         foreach ($parts as $string) { | ||||
|             $this->line('| ' . sprintf('%-77s', $string) . '|'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show a nice info box | ||||
|      * | ||||
|      * @param string $text | ||||
|      */ | ||||
|     private function boxedInfo(string $text) | ||||
|     { | ||||
|         $parts = explode("\n", wordwrap($text)); | ||||
|         foreach ($parts as $string) { | ||||
|             $this->info('| ' . sprintf('%-77s', $string) . '|'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show a line | ||||
|      */ | ||||
|     private function showLine() | ||||
|     { | ||||
|         $line = '+'; | ||||
|         for ($i = 0; $i < 78; $i++) { | ||||
|             $line .= '-'; | ||||
|         } | ||||
|         $line .= '+'; | ||||
|         $this->line($line); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private function installInstructions() { | ||||
|         /** @var string $version */ | ||||
|         $version = config('firefly.version'); | ||||
|         $config  = config('upgrade.text.install'); | ||||
|         $text    = ''; | ||||
|         foreach (array_keys($config) as $compare) { | ||||
|             // if string starts with: | ||||
|             $len = strlen($compare); | ||||
|             if (substr($version, 0, $len) === $compare) { | ||||
|                 $text = $config[$compare]; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         $this->showLine(); | ||||
|         $this->boxed(''); | ||||
|         if (is_null($text)) { | ||||
|             $this->line('Thank you for installing Firefly III, v' . $version); | ||||
|             $this->line('If you are upgrading from a previous version,'); | ||||
|             $this->info('there are no extra upgrade instructions.'); | ||||
|             $this->line('Firefly III should be ready for use.'); | ||||
|         } else { | ||||
|             $this->line('Thank you for installing Firefly III, v' . $version); | ||||
|             $this->line('If you are upgrading from a previous version,'); | ||||
|             $this->line('please follow these upgrade instructions carefully:'); | ||||
|             $this->info(wordwrap($text)); | ||||
|  | ||||
|             $this->boxed(sprintf('Thank you for installin Firefly III, v%s!', $version)); | ||||
|             $this->boxedInfo('There are no extra installation instructions.'); | ||||
|             $this->boxed('Firefly III should be ready for use.'); | ||||
|             $this->boxed(''); | ||||
|             $this->showLine(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $this->line(''); | ||||
|         $this->line('+------------------------------------------------------------------------------+'); | ||||
|         $this->boxed(sprintf('Thank you for installing Firefly III, v%s!', $version)); | ||||
|         $this->boxedInfo($text); | ||||
|         $this->boxed(''); | ||||
|         $this->showLine(); | ||||
|     } | ||||
|  | ||||
|     private function updateInstructions() | ||||
|     { | ||||
|         /** @var string $version */ | ||||
|         $version = config('firefly.version'); | ||||
|         $config  = config('upgrade.text.upgrade'); | ||||
|         $text    = ''; | ||||
|         foreach (array_keys($config) as $compare) { | ||||
|             // if string starts with: | ||||
|             $len = strlen($compare); | ||||
|             if (substr($version, 0, $len) === $compare) { | ||||
|                 $text = $config[$compare]; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         $this->showLine(); | ||||
|         $this->boxed(''); | ||||
|         if (is_null($text)) { | ||||
|  | ||||
|             $this->boxed(sprintf('Thank you for updating to Firefly III, v%s', $version)); | ||||
|             $this->boxedInfo('There are no extra upgrade instructions.'); | ||||
|             $this->boxed('Firefly III should be ready for use.'); | ||||
|             $this->boxed(''); | ||||
|             $this->showLine(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $this->boxed(sprintf('Thank you for updating to Firefly III, v%s!', $version)); | ||||
|         $this->boxedInfo($text); | ||||
|         $this->boxed(''); | ||||
|         $this->showLine(); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										66
									
								
								app/Console/Commands/UseEncryption.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								app/Console/Commands/UseEncryption.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Console\Commands; | ||||
|  | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Support\Str; | ||||
|  | ||||
| class UseEncryption extends Command | ||||
| { | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = 'This command will make sure that entries in the database will be encrypted (or not) according to the settings in .env'; | ||||
|     /** | ||||
|      * The name and signature of the console command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'firefly:use-encryption'; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         // | ||||
|         $this->handleObjects('Account', 'name', 'encrypted'); | ||||
|         $this->handleObjects('Bill', 'name', 'name_encrypted'); | ||||
|         $this->handleObjects('Bill', 'match', 'match_encrypted'); | ||||
|         $this->handleObjects('Budget', 'name', 'encrypted'); | ||||
|         $this->handleObjects('Category', 'name', 'encrypted'); | ||||
|         $this->handleObjects('PiggyBank', 'name', 'encrypted'); | ||||
|         $this->handleObjects('TransactionJournal', 'description', 'encrypted'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $class | ||||
|      * @param string $field | ||||
|      * @param string $indicator | ||||
|      */ | ||||
|     public function handleObjects(string $class, string $field, string $indicator) | ||||
|     { | ||||
|         $fqn     = sprintf('FireflyIII\Models\%s', $class); | ||||
|         $encrypt = config('firefly.encryption') ? 0 : 1; | ||||
|         $set     = $fqn::where($indicator, $encrypt)->get(); | ||||
|  | ||||
|         foreach ($set as $entry) { | ||||
|             $newName       = $entry->$field; | ||||
|             $entry->$field = $newName; | ||||
|             $entry->save(); | ||||
|         } | ||||
|  | ||||
|         $this->line(sprintf('Updated %d %s.', $set->count(), strtolower(Str::plural($class)))); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										348
									
								
								app/Console/Commands/VerifyDatabase.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										348
									
								
								app/Console/Commands/VerifyDatabase.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,348 @@ | ||||
| <?php | ||||
| /** | ||||
|  * VerifyDatabase.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Console\Commands; | ||||
|  | ||||
| use Crypt; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\AccountType; | ||||
| use FireflyIII\Models\Budget; | ||||
| use FireflyIII\Models\Transaction; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use FireflyIII\Models\TransactionType; | ||||
| use FireflyIII\Repositories\User\UserRepositoryInterface; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Contracts\Encryption\DecryptException; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Schema; | ||||
| use stdClass; | ||||
|  | ||||
| /** | ||||
|  * Class VerifyDatabase | ||||
|  * | ||||
|  * @package FireflyIII\Console\Commands | ||||
|  */ | ||||
| class VerifyDatabase extends Command | ||||
| { | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = 'Will verify your database.'; | ||||
|     /** | ||||
|      * The name and signature of the console command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'firefly:verify'; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         // if table does not exist, return false | ||||
|         if (!Schema::hasTable('users')) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $this->reportObject('budget'); | ||||
|         $this->reportObject('category'); | ||||
|         $this->reportObject('tag'); | ||||
|  | ||||
|         // accounts with no transactions. | ||||
|         $this->reportAccounts(); | ||||
|         // budgets with no limits | ||||
|         $this->reportBudgetLimits(); | ||||
|         // budgets with no transactions | ||||
|  | ||||
|         // sum of transactions is not zero. | ||||
|         $this->reportSum(); | ||||
|         //  any deleted transaction journals that have transactions that are NOT deleted: | ||||
|         $this->reportJournals(); | ||||
|         // deleted transactions that are connected to a not deleted journal. | ||||
|         $this->reportTransactions(); | ||||
|         // deleted accounts that still have not deleted transactions or journals attached to them. | ||||
|         $this->reportDeletedAccounts(); | ||||
|  | ||||
|         // report on journals with no transactions at all. | ||||
|         $this->reportNoTransactions(); | ||||
|  | ||||
|         // transfers with budgets. | ||||
|         $this->reportTransfersBudgets(); | ||||
|  | ||||
|         // report on journals with the wrong types of accounts. | ||||
|         $this->reportIncorrectJournals(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reports on accounts with no transactions. | ||||
|      */ | ||||
|     private function reportAccounts() | ||||
|     { | ||||
|         $set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id') | ||||
|                       ->leftJoin('users', 'accounts.user_id', '=', 'users.id') | ||||
|                       ->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']) | ||||
|                       ->whereNull('transactions.account_id') | ||||
|                       ->get( | ||||
|                           ['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'] | ||||
|                       ); | ||||
|  | ||||
|         /** @var stdClass $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $name = $entry->name; | ||||
|             $line = 'User #%d (%s) has account #%d ("%s") which has no transactions.'; | ||||
|             $line = sprintf($line, $entry->user_id, $entry->email, $entry->id, $name); | ||||
|             $this->line($line); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reports on budgets with no budget limits (which makes them pointless). | ||||
|      */ | ||||
|     private function reportBudgetLimits() | ||||
|     { | ||||
|         $set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id') | ||||
|                      ->leftJoin('users', 'budgets.user_id', '=', 'users.id') | ||||
|                      ->groupBy(['budgets.id', 'budgets.name', 'budgets.encrypted', 'budgets.user_id', 'users.email']) | ||||
|                      ->whereNull('budget_limits.id') | ||||
|                      ->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'budgets.encrypted', 'users.email']); | ||||
|  | ||||
|         /** @var Budget $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $line = sprintf( | ||||
|                 'Notice: User #%d (%s) has budget #%d ("%s") which has no budget limits.', | ||||
|                 $entry->user_id, $entry->email, $entry->id, $entry->name | ||||
|             ); | ||||
|             $this->line($line); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reports on deleted accounts that still have not deleted transactions or journals attached to them. | ||||
|      */ | ||||
|     private function reportDeletedAccounts() | ||||
|     { | ||||
|         $set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id') | ||||
|                       ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                       ->whereNotNull('accounts.deleted_at') | ||||
|                       ->whereNotNull('transactions.id') | ||||
|                       ->where( | ||||
|                           function (Builder $q) { | ||||
|                               $q->whereNull('transactions.deleted_at'); | ||||
|                               $q->orWhereNull('transaction_journals.deleted_at'); | ||||
|                           } | ||||
|                       ) | ||||
|                       ->get( | ||||
|                           ['accounts.id as account_id', 'accounts.deleted_at as account_deleted_at', 'transactions.id as transaction_id', | ||||
|                            'transactions.deleted_at as transaction_deleted_at', 'transaction_journals.id as journal_id', | ||||
|                            'transaction_journals.deleted_at as journal_deleted_at'] | ||||
|                       ); | ||||
|         /** @var stdClass $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $date = is_null($entry->transaction_deleted_at) ? $entry->journal_deleted_at : $entry->transaction_deleted_at; | ||||
|             $this->error( | ||||
|                 'Error: Account #' . $entry->account_id . ' should have been deleted, but has not.' . | ||||
|                 ' Find it in the table called "accounts" and change the "deleted_at" field to: "' . $date . '"' | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function reportIncorrectJournals() | ||||
|     { | ||||
|         $configuration = [ | ||||
|             // a withdrawal can not have revenue account: | ||||
|             TransactionType::WITHDRAWAL => [AccountType::REVENUE], | ||||
|             // deposit cannot have an expense account: | ||||
|             TransactionType::DEPOSIT    => [AccountType::EXPENSE], | ||||
|             // transfer cannot have either: | ||||
|             TransactionType::TRANSFER   => [AccountType::EXPENSE, AccountType::REVENUE], | ||||
|         ]; | ||||
|         foreach ($configuration as $transactionType => $accountTypes) { | ||||
|             $set = TransactionJournal::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') | ||||
|                                      ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                                      ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') | ||||
|                                      ->leftJoin('account_types', 'account_types.id', 'accounts.account_type_id') | ||||
|                                      ->leftJoin('users', 'users.id', '=', 'transaction_journals.user_id') | ||||
|                                      ->where('transaction_types.type', $transactionType) | ||||
|                                      ->whereIn('account_types.type', $accountTypes) | ||||
|                                      ->whereNull('transaction_journals.deleted_at') | ||||
|                                      ->get( | ||||
|                                          ['transaction_journals.id', 'transaction_journals.user_id', 'users.email', 'account_types.type as a_type', | ||||
|                                           'transaction_types.type'] | ||||
|                                      ); | ||||
|             foreach ($set as $entry) { | ||||
|                 $this->error( | ||||
|                     sprintf( | ||||
|                         'Transaction journal #%d (user #%d, %s) is of type "%s" but ' . | ||||
|                         'is linked to a "%s". The transaction journal should be recreated.', | ||||
|                         $entry->id, | ||||
|                         $entry->user_id, | ||||
|                         $entry->email, | ||||
|                         $entry->type, | ||||
|                         $entry->a_type | ||||
|                     ) | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Any deleted transaction journals that have transactions that are NOT deleted: | ||||
|      */ | ||||
|     private function reportJournals() | ||||
|     { | ||||
|         $set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                                  ->whereNotNull('transaction_journals.deleted_at')// USE THIS | ||||
|                                  ->whereNull('transactions.deleted_at') | ||||
|                                  ->whereNotNull('transactions.id') | ||||
|                                  ->get( | ||||
|                                      [ | ||||
|                                          'transaction_journals.id as journal_id', | ||||
|                                          'transaction_journals.description', | ||||
|                                          'transaction_journals.deleted_at as journal_deleted', | ||||
|                                          'transactions.id as transaction_id', | ||||
|                                          'transactions.deleted_at as transaction_deleted_at'] | ||||
|                                  ); | ||||
|         /** @var stdClass $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $this->error( | ||||
|                 'Error: Transaction #' . $entry->transaction_id . ' should have been deleted, but has not.' . | ||||
|                 ' Find it in the table called "transactions" and change the "deleted_at" field to: "' . $entry->journal_deleted . '"' | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     private function reportNoTransactions() | ||||
|     { | ||||
|         $set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                                  ->groupBy('transaction_journals.id') | ||||
|                                  ->whereNull('transactions.transaction_journal_id') | ||||
|                                  ->get(['transaction_journals.id']); | ||||
|  | ||||
|         foreach ($set as $entry) { | ||||
|             $this->error( | ||||
|                 'Error: Journal #' . $entry->id . ' has zero transactions. Open table "transaction_journals" and delete the entry with id #' . $entry->id | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $name | ||||
|      */ | ||||
|     private function reportObject(string $name) | ||||
|     { | ||||
|         $plural = str_plural($name); | ||||
|         $class  = sprintf('FireflyIII\Models\%s', ucfirst($name)); | ||||
|         $field  = $name == 'tag' ? 'tag' : 'name'; | ||||
|         $set    = $class::leftJoin($name . '_transaction_journal', $plural . '.id', '=', $name . '_transaction_journal.' . $name . '_id') | ||||
|                         ->leftJoin('users', $plural . '.user_id', '=', 'users.id') | ||||
|                         ->distinct() | ||||
|                         ->whereNull($name . '_transaction_journal.' . $name . '_id') | ||||
|                         ->whereNull($plural . '.deleted_at') | ||||
|                         ->get([$plural . '.id', $plural . '.' . $field . ' as name', $plural . '.user_id', 'users.email']); | ||||
|  | ||||
|         /** @var stdClass $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|  | ||||
|             $objName = $entry->name; | ||||
|             try { | ||||
|                 $objName = Crypt::decrypt($objName); | ||||
|             } catch (DecryptException $e) { | ||||
|                 // it probably was not encrypted. | ||||
|             } | ||||
|  | ||||
|             $line = sprintf( | ||||
|                 'Notice: User #%d (%s) has %s #%d ("%s") which has no transactions.', | ||||
|                 $entry->user_id, $entry->email, $name, $entry->id, $objName | ||||
|             ); | ||||
|             $this->line($line); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reports for each user when the sum of their transactions is not zero. | ||||
|      */ | ||||
|     private function reportSum() | ||||
|     { | ||||
|         /** @var UserRepositoryInterface $userRepository */ | ||||
|         $userRepository = app(UserRepositoryInterface::class); | ||||
|  | ||||
|         /** @var User $user */ | ||||
|         foreach ($userRepository->all() as $user) { | ||||
|             $sum = strval($user->transactions()->sum('amount')); | ||||
|             if (bccomp($sum, '0') !== 0) { | ||||
|                 $this->error('Error: Transactions for user #' . $user->id . ' (' . $user->email . ') are off by ' . $sum . '!'); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reports on deleted transactions that are connected to a not deleted journal. | ||||
|      */ | ||||
|     private function reportTransactions() | ||||
|     { | ||||
|         $set = Transaction::leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                           ->whereNotNull('transactions.deleted_at') | ||||
|                           ->whereNull('transaction_journals.deleted_at') | ||||
|                           ->get( | ||||
|                               ['transactions.id as transaction_id', 'transactions.deleted_at as transaction_deleted', 'transaction_journals.id as journal_id', | ||||
|                                'transaction_journals.deleted_at'] | ||||
|                           ); | ||||
|         /** @var stdClass $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $this->error( | ||||
|                 'Error: Transaction journal #' . $entry->journal_id . ' should have been deleted, but has not.' . | ||||
|                 ' Find it in the table called "transaction_journals" and change the "deleted_at" field to: "' . $entry->transaction_deleted . '"' | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     private function reportTransfersBudgets() | ||||
|     { | ||||
|         $set = TransactionJournal::distinct() | ||||
|                                  ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') | ||||
|                                  ->leftJoin('budget_transaction_journal', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id') | ||||
|                                  ->where('transaction_types.type', TransactionType::TRANSFER) | ||||
|                                  ->whereNotNull('budget_transaction_journal.budget_id')->get(['transaction_journals.id']); | ||||
|  | ||||
|         /** @var TransactionJournal $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $this->error( | ||||
|                 sprintf( | ||||
|                     'Error: Transaction journal #%d is a transfer, but has a budget. Edit it without changing anything, so the budget will be removed.', | ||||
|                     $entry->id | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,18 +1,26 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| /** | ||||
|  * Kernel.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Console; | ||||
|  | ||||
| use FireflyIII\Console\Commands\CreateImport; | ||||
| use FireflyIII\Console\Commands\EncryptFile; | ||||
| use FireflyIII\Console\Commands\Import; | ||||
| use FireflyIII\Console\Commands\ScanAttachments; | ||||
| use FireflyIII\Console\Commands\UpgradeDatabase; | ||||
| use FireflyIII\Console\Commands\UpgradeFireflyInstructions; | ||||
| use Illuminate\Console\Scheduling\Schedule; | ||||
| use FireflyIII\Console\Commands\UseEncryption; | ||||
| use FireflyIII\Console\Commands\VerifyDatabase; | ||||
| use Illuminate\Foundation\Console\Kernel as ConsoleKernel; | ||||
|  | ||||
| /** | ||||
| @@ -22,6 +30,25 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel; | ||||
|  */ | ||||
| class Kernel extends ConsoleKernel | ||||
| { | ||||
|     /** | ||||
|      * The bootstrap classes for the application. | ||||
|      * | ||||
|      * Next upgrade verify these are the same. | ||||
|      * | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $bootstrappers | ||||
|         = [ | ||||
|             'Illuminate\Foundation\Bootstrap\DetectEnvironment', | ||||
|             'Illuminate\Foundation\Bootstrap\LoadConfiguration', | ||||
|             'FireflyIII\Bootstrap\ConfigureLogging', | ||||
|             'Illuminate\Foundation\Bootstrap\HandleExceptions', | ||||
|             'Illuminate\Foundation\Bootstrap\RegisterFacades', | ||||
|             'Illuminate\Foundation\Bootstrap\SetRequestForConsole', | ||||
|             'Illuminate\Foundation\Bootstrap\RegisterProviders', | ||||
|             'Illuminate\Foundation\Bootstrap\BootProviders', | ||||
|         ]; | ||||
|  | ||||
|     /** | ||||
|      * The Artisan commands provided by your application. | ||||
|      * | ||||
| @@ -30,18 +57,22 @@ class Kernel extends ConsoleKernel | ||||
|     protected $commands | ||||
|         = [ | ||||
|             UpgradeFireflyInstructions::class, | ||||
|             VerifyDatabase::class, | ||||
|             Import::class, | ||||
|             CreateImport::class, | ||||
|             EncryptFile::class, | ||||
|             ScanAttachments::class, | ||||
|             UpgradeDatabase::class, | ||||
|             UseEncryption::class, | ||||
|         ]; | ||||
|  | ||||
|     /** | ||||
|      * Define the application's command schedule. | ||||
|      * | ||||
|      * @param  \Illuminate\Console\Scheduling\Schedule $schedule | ||||
|      * Register the Closure based commands for the application. | ||||
|      * | ||||
|      * @return void | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameters) | ||||
|      */ | ||||
|     protected function schedule(Schedule $schedule) | ||||
|     protected function commands() | ||||
|     { | ||||
|         require base_path('routes/console.php'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,14 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Event.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Events; | ||||
|  | ||||
|   | ||||
| @@ -1,23 +1,27 @@ | ||||
| <?php | ||||
| /** | ||||
|  * UserRegistration.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * RegisteredUser.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types = 1); | ||||
| 
 | ||||
| namespace FireflyIII\Events; | ||||
| 
 | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| /** | ||||
|  * Class UserRegistration | ||||
|  * Class RegisteredUser | ||||
|  * | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class UserRegistration extends Event | ||||
| class RegisteredUser extends Event | ||||
| { | ||||
|     use SerializesModels; | ||||
| 
 | ||||
| @@ -25,7 +29,7 @@ class UserRegistration extends Event | ||||
|     public $user; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      * Create a new event instance. This event is triggered when a new user registers. | ||||
|      * | ||||
|      * @param  User  $user | ||||
|      * @param string $ipAddress | ||||
							
								
								
									
										46
									
								
								app/Events/RequestedNewPassword.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								app/Events/RequestedNewPassword.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| <?php | ||||
| /** | ||||
|  * RequestedNewPassword.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Events; | ||||
|  | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
|  | ||||
| /** | ||||
|  * Class RequestedNewPassword | ||||
|  * | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class RequestedNewPassword extends Event | ||||
| { | ||||
|     use SerializesModels; | ||||
|  | ||||
|     public $ipAddress; | ||||
|     public $token; | ||||
|     public $user; | ||||
|  | ||||
|     /** | ||||
|      * Create a new event instance. This event is triggered when a users tries to reset his or her password. | ||||
|      * | ||||
|      * @param  User  $user | ||||
|      * @param string $token | ||||
|      * @param string $ipAddress | ||||
|      */ | ||||
|     public function __construct(User $user, string $token, string $ipAddress) | ||||
|     { | ||||
|         $this->user      = $user; | ||||
|         $this->token     = $token; | ||||
|         $this->ipAddress = $ipAddress; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,38 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ResendConfirmation.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Events; | ||||
|  | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
|  | ||||
| /** | ||||
|  * Class ResendConfirmation | ||||
|  * | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class ResendConfirmation extends Event | ||||
| { | ||||
|     use SerializesModels; | ||||
|  | ||||
|     public $ipAddress; | ||||
|     public $user; | ||||
|  | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      * | ||||
|      * @param  User  $user | ||||
|      * @param string $ipAddress | ||||
|      */ | ||||
|     public function __construct(User $user, string $ipAddress) | ||||
|     { | ||||
|         $this->user      = $user; | ||||
|         $this->ipAddress = $ipAddress; | ||||
|     } | ||||
| } | ||||
| @@ -1,25 +1,27 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * TransactionJournalStored.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * StoredTransactionJournal.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types = 1); | ||||
| 
 | ||||
| namespace FireflyIII\Events; | ||||
| 
 | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| /** | ||||
|  * Class TransactionJournalStored | ||||
|  * Class StoredTransactionJournal | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class TransactionJournalStored extends Event | ||||
| class StoredTransactionJournal extends Event | ||||
| { | ||||
| 
 | ||||
|     use SerializesModels; | ||||
| @@ -1,4 +1,14 @@ | ||||
| <?php | ||||
| /** | ||||
|  * UpdatedTransactionJournal.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types = 1); | ||||
| 
 | ||||
| namespace FireflyIII\Events; | ||||
| @@ -7,12 +17,11 @@ use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| /** | ||||
|  * Class TransactionJournalUpdated | ||||
|  * Class UpdatedTransactionJournal | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class TransactionJournalUpdated extends Event | ||||
| class UpdatedTransactionJournal extends Event | ||||
| { | ||||
| 
 | ||||
|     use SerializesModels; | ||||
| @@ -1,4 +1,14 @@ | ||||
| <?php | ||||
| /** | ||||
|  * FireflyException.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Exceptions; | ||||
|  | ||||
| @@ -6,7 +16,6 @@ namespace FireflyIII\Exceptions; | ||||
| /** | ||||
|  * Class FireflyException | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Exceptions | ||||
|  */ | ||||
| class FireflyException extends \Exception | ||||
|   | ||||
| @@ -1,15 +1,26 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Handler.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Exceptions; | ||||
|  | ||||
| use Auth; | ||||
| use ErrorException; | ||||
| use Exception; | ||||
| use FireflyIII\Jobs\MailError; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Auth\Access\AuthorizationException; | ||||
| use Illuminate\Auth\AuthenticationException; | ||||
| use Illuminate\Database\Eloquent\ModelNotFoundException; | ||||
| use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; | ||||
| use Illuminate\Session\TokenMismatchException; | ||||
| use Illuminate\Validation\ValidationException as ValException; | ||||
| use Request; | ||||
| use Symfony\Component\HttpKernel\Exception\HttpException; | ||||
|  | ||||
| @@ -27,9 +38,12 @@ class Handler extends ExceptionHandler | ||||
|      */ | ||||
|     protected $dontReport | ||||
|         = [ | ||||
|             AuthenticationException::class, | ||||
|             AuthorizationException::class, | ||||
|             HttpException::class, | ||||
|             ModelNotFoundException::class, | ||||
|             TokenMismatchException::class, | ||||
|             ValException::class, | ||||
|         ]; | ||||
|  | ||||
|     /** | ||||
| @@ -59,16 +73,22 @@ class Handler extends ExceptionHandler | ||||
|      * This is a great spot to send exceptions to Sentry, Bugsnag, etc. | ||||
|      * | ||||
|      * @param  Exception $exception | ||||
|      * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function report(Exception $exception) | ||||
|     { | ||||
|  | ||||
|         if ($exception instanceof FireflyException || $exception instanceof ErrorException) { | ||||
|  | ||||
|             $user = Auth::check() ? Auth::user() : new User; | ||||
|  | ||||
|         $doMailError = env('SEND_ERROR_MESSAGE', true); | ||||
|         if (($exception instanceof FireflyException || $exception instanceof ErrorException) && $doMailError) { | ||||
|             $userData = [ | ||||
|                 'id'    => 0, | ||||
|                 'email' => 'unknown@example.com', | ||||
|             ]; | ||||
|             if (auth()->check()) { | ||||
|                 $userData['id']    = auth()->user()->id; | ||||
|                 $userData['email'] = auth()->user()->email; | ||||
|             } | ||||
|             $data = [ | ||||
|                 'class'        => get_class($exception), | ||||
|                 'errorMessage' => $exception->getMessage(), | ||||
| @@ -80,10 +100,27 @@ class Handler extends ExceptionHandler | ||||
|             ]; | ||||
|  | ||||
|             // create job that will mail. | ||||
|             $job = new MailError($user, env('SITE_OWNER'), Request::ip(), $data); | ||||
|             $ipAddress = Request::ip() ?? '0.0.0.0'; | ||||
|             $job       = new MailError($userData, env('SITE_OWNER', ''), $ipAddress, $data); | ||||
|             dispatch($job); | ||||
|         } | ||||
|  | ||||
|         parent::report($exception); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Convert an authentication exception into an unauthenticated response. | ||||
|      * | ||||
|      * @param $request | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     protected function unauthenticated($request) | ||||
|     { | ||||
|         if ($request->expectsJson()) { | ||||
|             return response()->json(['error' => 'Unauthenticated.'], 401); | ||||
|         } | ||||
|  | ||||
|         return redirect()->guest('login'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,14 @@ | ||||
| <?php | ||||
| /** | ||||
|  * NotImplementedException.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Exceptions; | ||||
|  | ||||
| @@ -6,7 +16,6 @@ namespace FireflyIII\Exceptions; | ||||
| /** | ||||
|  * Class NotImplementedException | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Exceptions | ||||
|  */ | ||||
| class NotImplementedException extends \Exception | ||||
|   | ||||
| @@ -1,11 +1,20 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ValidationException.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Exceptions; | ||||
|  | ||||
| /** | ||||
|  * Class ValidationExceptions | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Exception | ||||
|  */ | ||||
| class ValidationException extends \Exception | ||||
|   | ||||
| @@ -1,20 +1,22 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * AttachmentCollector.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Export\Collector; | ||||
|  | ||||
| use Amount; | ||||
| use Carbon\Carbon; | ||||
| use Crypt; | ||||
| use FireflyIII\Models\Attachment; | ||||
| use FireflyIII\Models\ExportJob; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; | ||||
| use Illuminate\Contracts\Encryption\DecryptException; | ||||
| use Illuminate\Support\Collection; | ||||
| @@ -28,12 +30,14 @@ use Storage; | ||||
|  */ | ||||
| class AttachmentCollector extends BasicCollector implements CollectorInterface | ||||
| { | ||||
|     /** @var string */ | ||||
|     private $explanationString = ''; | ||||
|     /** @var  Carbon */ | ||||
|     private $end; | ||||
|     /** @var \Illuminate\Contracts\Filesystem\Filesystem */ | ||||
|     private $exportDisk; | ||||
|     /** @var  AttachmentRepositoryInterface */ | ||||
|     private $repository; | ||||
|     /** @var  Carbon */ | ||||
|     private $start; | ||||
|     /** @var \Illuminate\Contracts\Filesystem\Filesystem */ | ||||
|     private $uploadDisk; | ||||
|  | ||||
| @@ -44,7 +48,8 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface | ||||
|      */ | ||||
|     public function __construct(ExportJob $job) | ||||
|     { | ||||
|         $this->repository = app('FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface'); | ||||
|         /** @var AttachmentRepositoryInterface repository */ | ||||
|         $this->repository = app(AttachmentRepositoryInterface::class); | ||||
|         // make storage: | ||||
|         $this->uploadDisk = Storage::disk('upload'); | ||||
|         $this->exportDisk = Storage::disk('export'); | ||||
| @@ -53,9 +58,9 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function run() | ||||
|     public function run(): bool | ||||
|     { | ||||
|         // grab all the users attachments: | ||||
|         $attachments = $this->getAttachments(); | ||||
| @@ -65,32 +70,17 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface | ||||
|             $this->exportAttachment($attachment); | ||||
|         } | ||||
|  | ||||
|         // put the explanation string in a file and attach it as well. | ||||
|         $file = $this->job->key . '-Source of all your attachments explained.txt'; | ||||
|         $this->exportDisk->put($file, $this->explanationString); | ||||
|         Log::debug('Also put explanation file "' . $file . '" in the zip.'); | ||||
|         $this->getFiles()->push($file); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Attachment $attachment | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      */ | ||||
|     private function explain(Attachment $attachment) | ||||
|     public function setDates(Carbon $start, Carbon $end) | ||||
|     { | ||||
|         /** @var TransactionJournal $journal */ | ||||
|         $journal = $attachment->attachable; | ||||
|         $args    = [ | ||||
|             'attachment_name' => $attachment->filename, | ||||
|             'attachment_id'   => $attachment->id, | ||||
|             'type'            => strtolower($journal->transactionType->type), | ||||
|             'description'     => $journal->description, | ||||
|             'journal_id'      => $journal->id, | ||||
|             'date'            => $journal->date->formatLocalized(strval(trans('config.month_and_day'))), | ||||
|             'amount'          => Amount::formatJournal($journal, false), | ||||
|         ]; | ||||
|         $string  = trans('firefly.attachment_explanation', $args) . "\n"; | ||||
|         $this->explanationString .= $string; | ||||
|  | ||||
|         $this->start = $start; | ||||
|         $this->end   = $end; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -101,17 +91,13 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface | ||||
|     private function exportAttachment(Attachment $attachment): bool | ||||
|     { | ||||
|         $file = $attachment->fileName(); | ||||
|         Log::debug('Original file is at "' . $file . '".'); | ||||
|         if ($this->uploadDisk->exists($file)) { | ||||
|             try { | ||||
|                 $decrypted  = Crypt::decrypt($this->uploadDisk->get($file)); | ||||
|                 $exportFile = $this->exportFileName($attachment); | ||||
|                 $this->exportDisk->put($exportFile, $decrypted); | ||||
|                 $this->getFiles()->push($exportFile); | ||||
|                 Log::debug('Stored file content in new file "' . $exportFile . '", which will be in the final zip file.'); | ||||
|                 $this->getEntries()->push($exportFile); | ||||
|  | ||||
|                 // explain: | ||||
|                 $this->explain($attachment); | ||||
|             } catch (DecryptException $e) { | ||||
|                 Log::error('Catchable error: could not decrypt attachment #' . $attachment->id . ' because: ' . $e->getMessage()); | ||||
|             } | ||||
| @@ -139,9 +125,7 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface | ||||
|      */ | ||||
|     private function getAttachments(): Collection | ||||
|     { | ||||
|         $attachments = $this->repository->get(); | ||||
|  | ||||
|         Log::debug('Found ' . $attachments->count() . ' attachments.'); | ||||
|         $attachments = $this->repository->getBetween($this->start, $this->end); | ||||
|  | ||||
|         return $attachments; | ||||
|     } | ||||
|   | ||||
| @@ -1,13 +1,16 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * BasicCollector.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Export\Collector; | ||||
|  | ||||
|  | ||||
| @@ -24,7 +27,7 @@ class BasicCollector | ||||
|     /** @var ExportJob */ | ||||
|     protected $job; | ||||
|     /** @var Collection */ | ||||
|     private $files; | ||||
|     private $entries; | ||||
|  | ||||
|     /** | ||||
|      * BasicCollector constructor. | ||||
| @@ -33,24 +36,24 @@ class BasicCollector | ||||
|      */ | ||||
|     public function __construct(ExportJob $job) | ||||
|     { | ||||
|         $this->files = new Collection; | ||||
|         $this->job   = $job; | ||||
|         $this->entries = new Collection; | ||||
|         $this->job     = $job; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getFiles() | ||||
|     public function getEntries(): Collection | ||||
|     { | ||||
|         return $this->files; | ||||
|         return $this->entries; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $files | ||||
|      * @param Collection $entries | ||||
|      */ | ||||
|     public function setFiles(Collection $files) | ||||
|     public function setEntries(Collection $entries) | ||||
|     { | ||||
|         $this->files = $files; | ||||
|         $this->entries = $entries; | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,13 +1,16 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * CollectorInterface.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Export\Collector; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
| @@ -22,17 +25,19 @@ interface CollectorInterface | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getFiles(); | ||||
|     public function getEntries(): Collection; | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function run(); | ||||
|     public function run(): bool; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $files | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return void | ||||
|      * | ||||
|      */ | ||||
|     public function setFiles(Collection $files); | ||||
|     public function setEntries(Collection $entries); | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										347
									
								
								app/Export/Collector/JournalExportCollector.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										347
									
								
								app/Export/Collector/JournalExportCollector.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,347 @@ | ||||
| <?php | ||||
| /** | ||||
|  * JournalExportCollector.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Export\Collector; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Crypt; | ||||
| use DB; | ||||
| use FireflyIII\Models\Transaction; | ||||
| use Illuminate\Database\Query\JoinClause; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class JournalExportCollector | ||||
|  * | ||||
|  * @package FireflyIII\Export\Collector | ||||
|  */ | ||||
| class JournalExportCollector extends BasicCollector implements CollectorInterface | ||||
| { | ||||
|     /** @var  Collection */ | ||||
|     private $accounts; | ||||
|     /** @var  Carbon */ | ||||
|     private $end; | ||||
|     /** @var  Carbon */ | ||||
|     private $start; | ||||
|  | ||||
|     /** @var  Collection */ | ||||
|     private $workSet; | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function run(): bool | ||||
|     { | ||||
|         /* | ||||
|          * Instead of collecting journals we collect transactions for the given accounts. | ||||
|          * We left join the OPPOSING transaction AND some journal data. | ||||
|          * After that we complement this info with budgets, categories, etc. | ||||
|          * | ||||
|          * This is way more efficient and will also work on split journals. | ||||
|          */ | ||||
|         $this->getWorkSet(); | ||||
|  | ||||
|         /* | ||||
|          * Extract: | ||||
|          * possible budget ids for journals | ||||
|          * possible category ids journals | ||||
|          * possible budget ids for transactions | ||||
|          * possible category ids for transactions | ||||
|          * | ||||
|          * possible IBAN and account numbers? | ||||
|          * | ||||
|          */ | ||||
|         $journals     = $this->extractJournalIds(); | ||||
|         $transactions = $this->extractTransactionIds(); | ||||
|  | ||||
|  | ||||
|         // extend work set with category data from journals: | ||||
|         $this->categoryDataForJournals($journals); | ||||
|  | ||||
|         // extend work set with category cate from transactions (overrules journals): | ||||
|         $this->categoryDataForTransactions($transactions); | ||||
|  | ||||
|         // same for budgets: | ||||
|         $this->budgetDataForJournals($journals); | ||||
|         $this->budgetDataForTransactions($transactions); | ||||
|  | ||||
|         $this->setEntries($this->workSet); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      */ | ||||
|     public function setAccounts(Collection $accounts) | ||||
|     { | ||||
|         $this->accounts = $accounts; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      */ | ||||
|     public function setDates(Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $this->start = $start; | ||||
|         $this->end   = $end; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $journals | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function budgetDataForJournals(array $journals): bool | ||||
|     { | ||||
|         $set = DB::table('budget_transaction_journal') | ||||
|                  ->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id') | ||||
|                  ->whereIn('budget_transaction_journal.transaction_journal_id', $journals) | ||||
|                  ->get( | ||||
|                      [ | ||||
|                          'budget_transaction_journal.budget_id', | ||||
|                          'budget_transaction_journal.transaction_journal_id', | ||||
|                          'budgets.name', | ||||
|                          'budgets.encrypted', | ||||
|                      ] | ||||
|                  ); | ||||
|         $set->each( | ||||
|             function ($obj) { | ||||
|                 $obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name; | ||||
|             } | ||||
|         ); | ||||
|         $array = []; | ||||
|         foreach ($set as $obj) { | ||||
|             $array[$obj->transaction_journal_id] = ['id' => $obj->budget_id, 'name' => $obj->name]; | ||||
|         } | ||||
|  | ||||
|         $this->workSet->each( | ||||
|             function ($obj) use ($array) { | ||||
|                 if (isset($array[$obj->transaction_journal_id])) { | ||||
|                     $obj->budget_id   = $array[$obj->transaction_journal_id]['id']; | ||||
|                     $obj->budget_name = $array[$obj->transaction_journal_id]['name']; | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return true; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $transactions | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function budgetDataForTransactions(array $transactions): bool | ||||
|     { | ||||
|         $set = DB::table('budget_transaction') | ||||
|                  ->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction.budget_id') | ||||
|                  ->whereIn('budget_transaction.transaction_id', $transactions) | ||||
|                  ->get( | ||||
|                      [ | ||||
|                          'budget_transaction.budget_id', | ||||
|                          'budget_transaction.transaction_id', | ||||
|                          'budgets.name', | ||||
|                          'budgets.encrypted', | ||||
|                      ] | ||||
|                  ); | ||||
|         $set->each( | ||||
|             function ($obj) { | ||||
|                 $obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name; | ||||
|             } | ||||
|         ); | ||||
|         $array = []; | ||||
|         foreach ($set as $obj) { | ||||
|             $array[$obj->transaction_id] = ['id' => $obj->budget_id, 'name' => $obj->name]; | ||||
|         } | ||||
|  | ||||
|         $this->workSet->each( | ||||
|             function ($obj) use ($array) { | ||||
|  | ||||
|                 // first transaction | ||||
|                 if (isset($array[$obj->id])) { | ||||
|                     $obj->budget_id   = $array[$obj->id]['id']; | ||||
|                     $obj->budget_name = $array[$obj->id]['name']; | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return true; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $journals | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function categoryDataForJournals(array $journals): bool | ||||
|     { | ||||
|         $set = DB::table('category_transaction_journal') | ||||
|                  ->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id') | ||||
|                  ->whereIn('category_transaction_journal.transaction_journal_id', $journals) | ||||
|                  ->get( | ||||
|                      [ | ||||
|                          'category_transaction_journal.category_id', | ||||
|                          'category_transaction_journal.transaction_journal_id', | ||||
|                          'categories.name', | ||||
|                          'categories.encrypted', | ||||
|                      ] | ||||
|                  ); | ||||
|         $set->each( | ||||
|             function ($obj) { | ||||
|                 $obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name; | ||||
|             } | ||||
|         ); | ||||
|         $array = []; | ||||
|         foreach ($set as $obj) { | ||||
|             $array[$obj->transaction_journal_id] = ['id' => $obj->category_id, 'name' => $obj->name]; | ||||
|         } | ||||
|  | ||||
|         $this->workSet->each( | ||||
|             function ($obj) use ($array) { | ||||
|                 if (isset($array[$obj->transaction_journal_id])) { | ||||
|                     $obj->category_id   = $array[$obj->transaction_journal_id]['id']; | ||||
|                     $obj->category_name = $array[$obj->transaction_journal_id]['name']; | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return true; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $transactions | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function categoryDataForTransactions(array $transactions): bool | ||||
|     { | ||||
|         $set = DB::table('category_transaction') | ||||
|                  ->leftJoin('categories', 'categories.id', '=', 'category_transaction.category_id') | ||||
|                  ->whereIn('category_transaction.transaction_id', $transactions) | ||||
|                  ->get( | ||||
|                      [ | ||||
|                          'category_transaction.category_id', | ||||
|                          'category_transaction.transaction_id', | ||||
|                          'categories.name', | ||||
|                          'categories.encrypted', | ||||
|                      ] | ||||
|                  ); | ||||
|         $set->each( | ||||
|             function ($obj) { | ||||
|                 $obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name; | ||||
|             } | ||||
|         ); | ||||
|         $array = []; | ||||
|         foreach ($set as $obj) { | ||||
|             $array[$obj->transaction_id] = ['id' => $obj->category_id, 'name' => $obj->name]; | ||||
|         } | ||||
|  | ||||
|         $this->workSet->each( | ||||
|             function ($obj) use ($array) { | ||||
|  | ||||
|                 // first transaction | ||||
|                 if (isset($array[$obj->id])) { | ||||
|                     $obj->category_id   = $array[$obj->id]['id']; | ||||
|                     $obj->category_name = $array[$obj->id]['name']; | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return true; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     private function extractJournalIds(): array | ||||
|     { | ||||
|         return $this->workSet->pluck('transaction_journal_id')->toArray(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     private function extractTransactionIds() | ||||
|     { | ||||
|         $set      = $this->workSet->pluck('id')->toArray(); | ||||
|         $opposing = $this->workSet->pluck('opposing_id')->toArray(); | ||||
|         $complete = $set + $opposing; | ||||
|  | ||||
|         return array_unique($complete); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.ExcessiveMethodLength) | ||||
|      */ | ||||
|     private function getWorkSet() | ||||
|     { | ||||
|         $accountIds    = $this->accounts->pluck('id')->toArray(); | ||||
|         $this->workSet = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                                     ->leftJoin( | ||||
|                                         'transactions AS opposing', function (JoinClause $join) { | ||||
|                                         $join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id') | ||||
|                                              ->where('opposing.amount', '=', DB::raw('transactions.amount * -1')) | ||||
|                                              ->where('transactions.identifier', '=', DB::raw('opposing.identifier')); | ||||
|                                     } | ||||
|                                     ) | ||||
|                                     ->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id') | ||||
|                                     ->leftJoin('accounts AS opposing_accounts', 'opposing.account_id', '=', 'opposing_accounts.id') | ||||
|                                     ->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', 'transaction_types.id') | ||||
|                                     ->leftJoin('transaction_currencies', 'transaction_journals.transaction_currency_id', '=', 'transaction_currencies.id') | ||||
|                                     ->whereIn('transactions.account_id', $accountIds) | ||||
|                                     ->where('transaction_journals.user_id', $this->job->user_id) | ||||
|                                     ->where('transaction_journals.date', '>=', $this->start->format('Y-m-d')) | ||||
|                                     ->where('transaction_journals.date', '<=', $this->end->format('Y-m-d')) | ||||
|                                     ->where('transaction_journals.completed', 1) | ||||
|                                     ->whereNull('transaction_journals.deleted_at') | ||||
|                                     ->whereNull('transactions.deleted_at') | ||||
|                                     ->whereNull('opposing.deleted_at') | ||||
|                                     ->orderBy('transaction_journals.date', 'DESC') | ||||
|                                     ->orderBy('transactions.identifier', 'ASC') | ||||
|                                     ->get( | ||||
|                                         [ | ||||
|                                             'transactions.id', | ||||
|                                             'transactions.amount', | ||||
|                                             'transactions.description', | ||||
|                                             'transactions.account_id', | ||||
|                                             'accounts.name as account_name', | ||||
|                                             'accounts.encrypted as account_name_encrypted', | ||||
|                                             'transactions.identifier', | ||||
|  | ||||
|                                             'opposing.id as opposing_id', | ||||
|                                             'opposing.amount AS opposing_amount', | ||||
|                                             'opposing.description as opposing_description', | ||||
|                                             'opposing.account_id as opposing_account_id', | ||||
|                                             'opposing_accounts.name as opposing_account_name', | ||||
|                                             'opposing_accounts.encrypted as opposing_account_encrypted', | ||||
|                                             'opposing.identifier as opposing_identifier', | ||||
|  | ||||
|                                             'transaction_journals.id as transaction_journal_id', | ||||
|                                             'transaction_journals.date', | ||||
|                                             'transaction_journals.description as journal_description', | ||||
|                                             'transaction_journals.encrypted as journal_encrypted', | ||||
|                                             'transaction_journals.transaction_type_id', | ||||
|                                             'transaction_types.type as transaction_type', | ||||
|                                             'transaction_journals.transaction_currency_id', | ||||
|                                             'transaction_currencies.code AS transaction_currency_code', | ||||
|  | ||||
|                                         ] | ||||
|                                     ); | ||||
|     } | ||||
| } | ||||
| @@ -1,16 +1,18 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * UploadCollector.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Export\Collector; | ||||
|  | ||||
| use Auth; | ||||
| use Crypt; | ||||
| use FireflyIII\Models\ExportJob; | ||||
| use Illuminate\Contracts\Encryption\DecryptException; | ||||
| @@ -24,12 +26,12 @@ use Storage; | ||||
|  */ | ||||
| class UploadCollector extends BasicCollector implements CollectorInterface | ||||
| { | ||||
|     /** @var string */ | ||||
|     private $expected; | ||||
|     /** @var \Illuminate\Contracts\Filesystem\Filesystem */ | ||||
|     private $exportDisk; | ||||
|     /** @var \Illuminate\Contracts\Filesystem\Filesystem */ | ||||
|     private $uploadDisk; | ||||
|     /** @var string */ | ||||
|     private $vintageFormat; | ||||
|  | ||||
|     /** | ||||
|      * AttachmentCollector constructor. | ||||
| @@ -40,35 +42,81 @@ class UploadCollector extends BasicCollector implements CollectorInterface | ||||
|     { | ||||
|         parent::__construct($job); | ||||
|  | ||||
|         Log::debug('Going to collect attachments', ['key' => $job->key]); | ||||
|  | ||||
|         // make storage: | ||||
|         $this->uploadDisk = Storage::disk('upload'); | ||||
|         $this->exportDisk = Storage::disk('export'); | ||||
|         $this->expected   = 'csv-upload-' . Auth::user()->id . '-'; | ||||
|  | ||||
|         // file names associated with the old import routine. | ||||
|         $this->vintageFormat = sprintf('csv-upload-%d-', auth()->user()->id); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Is called from the outside to actually start the export. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function run() | ||||
|     public function run(): bool | ||||
|     { | ||||
|         // collect old upload files (names beginning with "csv-upload". | ||||
|         $this->collectVintageUploads(); | ||||
|  | ||||
|         // then collect current upload files: | ||||
|         $this->collectModernUploads(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method collects all the uploads that are uploaded using the new importer. So after the summer of 2016. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function collectModernUploads(): bool | ||||
|     { | ||||
|         $set  = $this->job->user->importJobs()->where('status', 'import_complete')->get(['import_jobs.*']); | ||||
|         $keys = []; | ||||
|         if ($set->count() > 0) { | ||||
|             $keys = $set->pluck('key')->toArray(); | ||||
|         } | ||||
|  | ||||
|         foreach ($keys as $key) { | ||||
|             $this->processModernUpload($key); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method collects all the uploads that are uploaded using the "old" importer. So from before the summer of 2016. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function collectVintageUploads(): bool | ||||
|     { | ||||
|         // grab upload directory. | ||||
|         $files = $this->uploadDisk->files(); | ||||
|         Log::debug('Found ' . count($files) . ' files in the upload directory.'); | ||||
|  | ||||
|         foreach ($files as $entry) { | ||||
|             $this->processOldUpload($entry); | ||||
|             $this->processVintageUpload($entry); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method tells you when the vintage upload file was actually uploaded. | ||||
|      * | ||||
|      * @param string $entry | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     private function getOriginalUploadDate(string $entry): string | ||||
|     private function getVintageUploadDate(string $entry): string | ||||
|     { | ||||
|         // this is an original upload. | ||||
|         $parts          = explode('-', str_replace(['.csv.encrypted', $this->expected], '', $entry)); | ||||
|         $parts          = explode('-', str_replace(['.csv.encrypted', $this->vintageFormat], '', $entry)); | ||||
|         $originalUpload = intval($parts[1]); | ||||
|         $date           = date('Y-m-d \a\t H-i-s', $originalUpload); | ||||
|  | ||||
| @@ -76,44 +124,97 @@ class UploadCollector extends BasicCollector implements CollectorInterface | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Tells you if a file name is a vintage upload. | ||||
|      * | ||||
|      * @param string $entry | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function isValidFile(string $entry): bool | ||||
|     private function isVintageImport(string $entry): bool | ||||
|     { | ||||
|         $len = strlen($this->expected); | ||||
|         if (substr($entry, 0, $len) === $this->expected) { | ||||
|             Log::debug($entry . ' is part of this users original uploads.'); | ||||
|         $len = strlen($this->vintageFormat); | ||||
|         // file is part of the old import routine: | ||||
|         if (substr($entry, 0, $len) === $this->vintageFormat) { | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|         Log::debug($entry . ' is not part of this users original uploads.'); | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $entry | ||||
|      * @param string $key | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function processOldUpload(string $entry) | ||||
|     private function processModernUpload(string $key): bool | ||||
|     { | ||||
|         // find job associated with import file: | ||||
|         $job = $this->job->user->importJobs()->where('key', $key)->first(); | ||||
|         if (is_null($job)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // find the file for this import: | ||||
|         $content = ''; | ||||
|         try { | ||||
|             $content = Crypt::decrypt($this->uploadDisk->get(sprintf('%s.upload', $key))); | ||||
|         } catch (DecryptException $e) { | ||||
|             Log::error(sprintf('Could not decrypt old import file "%s". Skipped because: %s', $key, $e->getMessage())); | ||||
|         } | ||||
|  | ||||
|         if (strlen($content) > 0) { | ||||
|             // add to export disk. | ||||
|             $date = $job->created_at->format('Y-m-d'); | ||||
|             $file = sprintf('%s-Old %s import dated %s.%s', $this->job->key, strtoupper($job->file_type), $date, $job->file_type); | ||||
|             $this->exportDisk->put($file, $content); | ||||
|             $this->getEntries()->push($file); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If the file is a vintage upload, process it. | ||||
|      * | ||||
|      * @param string $entry | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function processVintageUpload(string $entry): bool | ||||
|     { | ||||
|         if ($this->isVintageImport($entry)) { | ||||
|             $this->saveVintageImportFile($entry); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * This will store the content of the old vintage upload somewhere. | ||||
|      * | ||||
|      * @param string $entry | ||||
|      */ | ||||
|     private function saveVintageImportFile(string $entry) | ||||
|     { | ||||
|         $content = ''; | ||||
|  | ||||
|         if ($this->isValidFile($entry)) { | ||||
|             try { | ||||
|                 $content = Crypt::decrypt($this->uploadDisk->get($entry)); | ||||
|             } catch (DecryptException $e) { | ||||
|                 Log::error('Could not decrypt old CSV import file ' . $entry . '. Skipped because ' . $e->getMessage()); | ||||
|             } | ||||
|         try { | ||||
|             $content = Crypt::decrypt($this->uploadDisk->get($entry)); | ||||
|         } catch (DecryptException $e) { | ||||
|             Log::error('Could not decrypt old CSV import file ' . $entry . '. Skipped because ' . $e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         if (strlen($content) > 0) { | ||||
|             // continue with file: | ||||
|             $date = $this->getOriginalUploadDate($entry); | ||||
|             $file = $this->job->key . '-Old CSV import dated ' . $date . '.csv'; | ||||
|             Log::debug('Will put "' . $file . '" in the zip file.'); | ||||
|             // add to export disk. | ||||
|             $date = $this->getVintageUploadDate($entry); | ||||
|             $file = $this->job->key . '-Old import dated ' . $date . '.csv'; | ||||
|             $this->exportDisk->put($file, $content); | ||||
|             $this->getFiles()->push($file); | ||||
|             $this->getEntries()->push($file); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,67 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * ConfigurationFile.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Export; | ||||
|  | ||||
| use FireflyIII\Models\ExportJob; | ||||
| use Log; | ||||
| use Storage; | ||||
|  | ||||
| /** | ||||
|  * Class ConfigurationFile | ||||
|  * | ||||
|  * @package FireflyIII\Export | ||||
|  */ | ||||
| class ConfigurationFile | ||||
| { | ||||
|     /** @var \Illuminate\Contracts\Filesystem\Filesystem */ | ||||
|     private $exportDisk; | ||||
|     /** @var  ExportJob */ | ||||
|     private $job; | ||||
|  | ||||
|     /** | ||||
|      * ConfigurationFile constructor. | ||||
|      * | ||||
|      * @param ExportJob $job | ||||
|      */ | ||||
|     public function __construct(ExportJob $job) | ||||
|     { | ||||
|         $this->job        = $job; | ||||
|         $this->exportDisk = Storage::disk('export'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function make() | ||||
|     { | ||||
|         $fields = array_keys(get_class_vars(Entry::class)); | ||||
|         $types  = Entry::getTypes(); | ||||
|  | ||||
|         $configuration = [ | ||||
|             'date-format' => 'Y-m-d', // unfortunately, this is hard-coded. | ||||
|             'has-headers' => true, | ||||
|             'map'         => [], // we could build a map if necessary for easy re-import. | ||||
|             'roles'       => [], | ||||
|             'mapped'      => [], | ||||
|             'specifix'    => [], | ||||
|         ]; | ||||
|         foreach ($fields as $field) { | ||||
|             $configuration['roles'][] = $types[$field]; | ||||
|         } | ||||
|         $file = $this->job->key . '-configuration.json'; | ||||
|         Log::debug('Created JSON config file.'); | ||||
|         Log::debug('Will put "' . $file . '" in the ZIP file.'); | ||||
|         $this->exportDisk->put($file, json_encode($configuration, JSON_PRETTY_PRINT)); | ||||
|  | ||||
|         return $file; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,460 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * Entry.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Export; | ||||
|  | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Budget; | ||||
| use FireflyIII\Models\Category; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
|  | ||||
| /** | ||||
|  * To extend the exported object, in case of new features in Firefly III for example, | ||||
|  * do the following: | ||||
|  * | ||||
|  * - Add the field(s) to this class | ||||
|  * - Make sure the "fromJournal"-routine fills these fields. | ||||
|  * - Add them to the static function that returns its type (key=value. Remember that the only | ||||
|  *   valid types can be found in config/csv.php (under "roles"). | ||||
|  * | ||||
|  * These new entries should be should be strings and numbers as much as possible. | ||||
|  * | ||||
|  * | ||||
|  * | ||||
|  * Class Entry | ||||
|  * | ||||
|  * @package FireflyIII\Export | ||||
|  */ | ||||
| class Entry | ||||
| { | ||||
|     /** @var  string */ | ||||
|     public $amount; | ||||
|     /** @var  int */ | ||||
|     public $billId; | ||||
|     /** @var  string */ | ||||
|     public $billName; | ||||
|     /** @var  int */ | ||||
|     public $budgetId; | ||||
|     /** @var  string */ | ||||
|     public $budgetName; | ||||
|     /** @var  int */ | ||||
|     public $categoryId; | ||||
|     /** @var  string */ | ||||
|     public $categoryName; | ||||
|     /** @var  string */ | ||||
|     public $date; | ||||
|     /** @var  string */ | ||||
|     public $description; | ||||
|     /** @var  string */ | ||||
|     public $fromAccountIban; | ||||
|     /** @var  int */ | ||||
|     public $fromAccountId; | ||||
|     /** @var  string */ | ||||
|     public $fromAccountName; | ||||
|     public $fromAccountNumber; | ||||
|     /** @var  string */ | ||||
|     public $fromAccountType; | ||||
|     /** @var  string */ | ||||
|     public $toAccountIban; | ||||
|     /** @var  int */ | ||||
|     public $toAccountId; | ||||
|     /** @var  string */ | ||||
|     public $toAccountName; | ||||
|     public $toAccountNumber; | ||||
|     /** @var  string */ | ||||
|     public $toAccountType; | ||||
|  | ||||
|     /** | ||||
|      * @param TransactionJournal $journal | ||||
|      * | ||||
|      * @return Entry | ||||
|      */ | ||||
|     public static function fromJournal(TransactionJournal $journal) | ||||
|     { | ||||
|  | ||||
|         $entry = new self; | ||||
|         $entry->setDescription($journal->description); | ||||
|         $entry->setDate($journal->date->format('Y-m-d')); | ||||
|         $entry->setAmount(TransactionJournal::amount($journal)); | ||||
|  | ||||
|         /** @var Budget $budget */ | ||||
|         $budget = $journal->budgets->first(); | ||||
|         if (!is_null($budget)) { | ||||
|             $entry->setBudgetId($budget->id); | ||||
|             $entry->setBudgetName($budget->name); | ||||
|         } | ||||
|  | ||||
|         /** @var Category $category */ | ||||
|         $category = $journal->categories->first(); | ||||
|         if (!is_null($category)) { | ||||
|             $entry->setCategoryId($category->id); | ||||
|             $entry->setCategoryName($category->name); | ||||
|         } | ||||
|  | ||||
|         if (!is_null($journal->bill_id)) { | ||||
|             $entry->setBillId($journal->bill_id); | ||||
|             $entry->setBillName($journal->bill->name); | ||||
|         } | ||||
|  | ||||
|         /** @var Account $sourceAccount */ | ||||
|         $sourceAccount = TransactionJournal::sourceAccount($journal); | ||||
|         $entry->setFromAccountId($sourceAccount->id); | ||||
|         $entry->setFromAccountName($sourceAccount->name); | ||||
|         $entry->setFromAccountIban($sourceAccount->iban); | ||||
|         $entry->setFromAccountType($sourceAccount->accountType->type); | ||||
|         $entry->setFromAccountNumber($sourceAccount->getMeta('accountNumber')); | ||||
|  | ||||
|  | ||||
|         /** @var Account $destination */ | ||||
|         $destination = TransactionJournal::destinationAccount($journal); | ||||
|         $entry->setToAccountId($destination->id); | ||||
|         $entry->setToAccountName($destination->name); | ||||
|         $entry->setToAccountIban($destination->iban); | ||||
|         $entry->setToAccountType($destination->accountType->type); | ||||
|         $entry->setToAccountNumber($destination->getMeta('accountNumber')); | ||||
|  | ||||
|         return $entry; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public static function getTypes(): array | ||||
|     { | ||||
|         // key = field name (see top of class) | ||||
|         // value = field type (see csv.php under 'roles') | ||||
|         return [ | ||||
|             'amount'          => 'amount', | ||||
|             'date'            => 'date-transaction', | ||||
|             'description'     => 'description', | ||||
|             'billId'          => 'bill-id', | ||||
|             'billName'        => 'bill-name', | ||||
|             'budgetId'        => 'budget-id', | ||||
|             'budgetName'      => 'budget-name', | ||||
|             'categoryId'      => 'category-id', | ||||
|             'categoryName'    => 'category-name', | ||||
|             'fromAccountId'   => 'account-id', | ||||
|             'fromAccountName' => 'account-name', | ||||
|             'fromAccountIban' => 'account-iban', | ||||
|             'fromAccountType' => '_ignore', // no, Firefly cannot import what it exports. I know :D | ||||
|             'toAccountId'     => 'opposing-id', | ||||
|             'toAccountName'   => 'opposing-name', | ||||
|             'toAccountIban'   => 'opposing-iban', | ||||
|             'toAccountType'   => '_ignore', | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getAmount(): string | ||||
|     { | ||||
|         return $this->amount; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $amount | ||||
|      */ | ||||
|     public function setAmount(string $amount) | ||||
|     { | ||||
|         $this->amount = $amount; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return int | ||||
|      */ | ||||
|     public function getBillId() | ||||
|     { | ||||
|         return $this->billId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $billId | ||||
|      */ | ||||
|     public function setBillId($billId) | ||||
|     { | ||||
|         $this->billId = $billId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getBillName() | ||||
|     { | ||||
|         return $this->billName; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $billName | ||||
|      */ | ||||
|     public function setBillName($billName) | ||||
|     { | ||||
|         $this->billName = $billName; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return int | ||||
|      */ | ||||
|     public function getBudgetId() | ||||
|     { | ||||
|         return $this->budgetId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $budgetId | ||||
|      */ | ||||
|     public function setBudgetId($budgetId) | ||||
|     { | ||||
|         $this->budgetId = $budgetId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getBudgetName() | ||||
|     { | ||||
|         return $this->budgetName; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $budgetName | ||||
|      */ | ||||
|     public function setBudgetName($budgetName) | ||||
|     { | ||||
|         $this->budgetName = $budgetName; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return int | ||||
|      */ | ||||
|     public function getCategoryId() | ||||
|     { | ||||
|         return $this->categoryId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $categoryId | ||||
|      */ | ||||
|     public function setCategoryId($categoryId) | ||||
|     { | ||||
|         $this->categoryId = $categoryId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getCategoryName() | ||||
|     { | ||||
|         return $this->categoryName; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $categoryName | ||||
|      */ | ||||
|     public function setCategoryName($categoryName) | ||||
|     { | ||||
|         $this->categoryName = $categoryName; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getDate() | ||||
|     { | ||||
|         return $this->date; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $date | ||||
|      */ | ||||
|     public function setDate(string $date) | ||||
|     { | ||||
|         $this->date = $date; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getDescription() | ||||
|     { | ||||
|         return $this->description; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $description | ||||
|      */ | ||||
|     public function setDescription(string $description) | ||||
|     { | ||||
|         $this->description = $description; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getFromAccountIban() | ||||
|     { | ||||
|         return $this->fromAccountIban; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $fromAccountIban | ||||
|      */ | ||||
|     public function setFromAccountIban($fromAccountIban) | ||||
|     { | ||||
|         $this->fromAccountIban = $fromAccountIban; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return int | ||||
|      */ | ||||
|     public function getFromAccountId() | ||||
|     { | ||||
|         return $this->fromAccountId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $fromAccountId | ||||
|      */ | ||||
|     public function setFromAccountId($fromAccountId) | ||||
|     { | ||||
|         $this->fromAccountId = $fromAccountId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getFromAccountName() | ||||
|     { | ||||
|         return $this->fromAccountName; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $fromAccountName | ||||
|      */ | ||||
|     public function setFromAccountName($fromAccountName) | ||||
|     { | ||||
|         $this->fromAccountName = $fromAccountName; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getFromAccountNumber() | ||||
|     { | ||||
|         return $this->fromAccountNumber; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param mixed $fromAccountNumber | ||||
|      */ | ||||
|     public function setFromAccountNumber($fromAccountNumber) | ||||
|     { | ||||
|         $this->fromAccountNumber = $fromAccountNumber; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getFromAccountType() | ||||
|     { | ||||
|         return $this->fromAccountType; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $fromAccountType | ||||
|      */ | ||||
|     public function setFromAccountType($fromAccountType) | ||||
|     { | ||||
|         $this->fromAccountType = $fromAccountType; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getToAccountIban() | ||||
|     { | ||||
|         return $this->toAccountIban; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $toAccountIban | ||||
|      */ | ||||
|     public function setToAccountIban($toAccountIban) | ||||
|     { | ||||
|         $this->toAccountIban = $toAccountIban; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return int | ||||
|      */ | ||||
|     public function getToAccountId() | ||||
|     { | ||||
|         return $this->toAccountId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $toAccountId | ||||
|      */ | ||||
|     public function setToAccountId($toAccountId) | ||||
|     { | ||||
|         $this->toAccountId = $toAccountId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getToAccountName() | ||||
|     { | ||||
|         return $this->toAccountName; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $toAccountName | ||||
|      */ | ||||
|     public function setToAccountName($toAccountName) | ||||
|     { | ||||
|         $this->toAccountName = $toAccountName; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getToAccountNumber() | ||||
|     { | ||||
|         return $this->toAccountNumber; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param mixed $toAccountNumber | ||||
|      */ | ||||
|     public function setToAccountNumber($toAccountNumber) | ||||
|     { | ||||
|         $this->toAccountNumber = $toAccountNumber; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getToAccountType() | ||||
|     { | ||||
|         return $this->toAccountType; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $toAccountType | ||||
|      */ | ||||
|     public function setToAccountType($toAccountType) | ||||
|     { | ||||
|         $this->toAccountType = $toAccountType; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										113
									
								
								app/Export/Entry/Entry.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								app/Export/Entry/Entry.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Entry.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Export\Entry; | ||||
|  | ||||
| use Crypt; | ||||
|  | ||||
| /** | ||||
|  * To extend the exported object, in case of new features in Firefly III for example, | ||||
|  * do the following: | ||||
|  * | ||||
|  * - Add the field(s) to this class. If you add more than one related field, add a new object. | ||||
|  * - Make sure the "fromJournal"-routine fills these fields. | ||||
|  * - Add them to the static function that returns its type (key=value. Remember that the only | ||||
|  *   valid types can be found in config/csv.php (under "roles"). | ||||
|  * | ||||
|  * These new entries should be should be strings and numbers as much as possible. | ||||
|  * | ||||
|  * | ||||
|  * | ||||
|  * Class Entry | ||||
|  * @SuppressWarnings(PHPMD.LongVariable) | ||||
|  * | ||||
|  * @package FireflyIII\Export\Entry | ||||
|  */ | ||||
| final class Entry | ||||
| { | ||||
|     // @formatter:off | ||||
|     public $journal_id; | ||||
|     public $date; | ||||
|     public $description; | ||||
|  | ||||
|     public $currency_code; | ||||
|     public $amount; | ||||
|  | ||||
|     public $transaction_type; | ||||
|  | ||||
|     public $source_account_id; | ||||
|     public $source_account_name; | ||||
|  | ||||
|     public $destination_account_id; | ||||
|     public $destination_account_name; | ||||
|  | ||||
|     public $budget_id; | ||||
|     public $budget_name; | ||||
|     public $category_id; | ||||
|     public $category_name; | ||||
|     // @formatter:on | ||||
|  | ||||
|     /** | ||||
|      * Entry constructor. | ||||
|      */ | ||||
|     private function __construct() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $object | ||||
|      * | ||||
|      * @return Entry | ||||
|      */ | ||||
|     public static function fromObject($object): Entry | ||||
|     { | ||||
|         $entry                           = new self; | ||||
|         $entry->journal_id               = $object->transaction_journal_id; | ||||
|         $entry->description              = self::decrypt(intval($object->journal_encrypted), $object->journal_description); | ||||
|         $entry->amount                   = $object->amount; | ||||
|         $entry->date                     = $object->date; | ||||
|         $entry->transaction_type         = $object->transaction_type; | ||||
|         $entry->currency_code            = $object->transaction_currency_code; | ||||
|         $entry->source_account_id        = $object->account_id; | ||||
|         $entry->source_account_name      = self::decrypt(intval($object->account_name_encrypted), $object->account_name); | ||||
|         $entry->destination_account_id   = $object->opposing_account_id; | ||||
|         $entry->destination_account_name = self::decrypt(intval($object->opposing_account_encrypted), $object->opposing_account_name); | ||||
|         $entry->category_id              = $object->category_id ?? ''; | ||||
|         $entry->category_name            = $object->category_name ?? ''; | ||||
|         $entry->budget_id                = $object->budget_id ?? ''; | ||||
|         $entry->budget_name              = $object->budget_name ?? ''; | ||||
|  | ||||
|         // update description when transaction description is different: | ||||
|         if (!is_null($object->description) && $object->description != $entry->description) { | ||||
|             $entry->description = $entry->description . ' (' . $object->description . ')'; | ||||
|         } | ||||
|  | ||||
|         return $entry; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $isEncrypted | ||||
|      * @param     $value | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected static function decrypt(int $isEncrypted, $value) | ||||
|     { | ||||
|         if ($isEncrypted === 1) { | ||||
|             return Crypt::decrypt($value); | ||||
|         } | ||||
|  | ||||
|         return $value; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,13 +1,16 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * BasicExporter.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Export\Exporter; | ||||
|  | ||||
|  | ||||
| @@ -39,7 +42,7 @@ class BasicExporter | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getEntries() | ||||
|     public function getEntries(): Collection | ||||
|     { | ||||
|         return $this->entries; | ||||
|     } | ||||
|   | ||||
| @@ -1,16 +1,19 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * CsvExporter.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Export\Exporter; | ||||
|  | ||||
| use FireflyIII\Export\Entry; | ||||
| use FireflyIII\Export\Entry\Entry; | ||||
| use FireflyIII\Models\ExportJob; | ||||
| use League\Csv\Writer; | ||||
| use SplFileObject; | ||||
| @@ -39,41 +42,43 @@ class CsvExporter extends BasicExporter implements ExporterInterface | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getFileName() | ||||
|     public function getFileName(): string | ||||
|     { | ||||
|         return $this->fileName; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function run() | ||||
|     public function run(): bool | ||||
|     { | ||||
|         // create temporary file: | ||||
|         $this->tempFile(); | ||||
|  | ||||
|         // necessary for CSV writer: | ||||
|         $fullPath = storage_path('export') . DIRECTORY_SEPARATOR . $this->fileName; | ||||
|         $writer   = Writer::createFromPath(new SplFileObject($fullPath, 'a+'), 'w'); | ||||
|         $rows     = []; | ||||
|  | ||||
|         // create CSV writer: | ||||
|         $writer = Writer::createFromPath(new SplFileObject($fullPath, 'a+'), 'w'); | ||||
|         // get field names for header row: | ||||
|         $first   = $this->getEntries()->first(); | ||||
|         $headers = array_keys(get_object_vars($first)); | ||||
|         $rows[]  = $headers; | ||||
|  | ||||
|         // all rows: | ||||
|         $rows = []; | ||||
|  | ||||
|         // add header: | ||||
|         $first  = $this->getEntries()->first(); | ||||
|         $rows[] = array_keys(get_object_vars($first)); | ||||
|  | ||||
|         // then the rest: | ||||
|         /** @var Entry $entry */ | ||||
|         foreach ($this->getEntries() as $entry) { | ||||
|             $rows[] = array_values(get_object_vars($entry)); | ||||
|  | ||||
|             $line = []; | ||||
|             foreach ($headers as $header) { | ||||
|                 $line[] = $entry->$header; | ||||
|             } | ||||
|             $rows[] = $line; | ||||
|         } | ||||
|         $writer->insertAll($rows); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private function tempFile() | ||||
|     { | ||||
|         $this->fileName = $this->job->key . '-records.csv'; | ||||
|   | ||||
| @@ -1,13 +1,16 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * ExporterInterface.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Export\Exporter; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
| @@ -22,21 +25,23 @@ interface ExporterInterface | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getEntries(); | ||||
|     public function getEntries(): Collection; | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getFileName(); | ||||
|     public function getFileName(): string; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function run(); | ||||
|     public function run(): bool; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return void | ||||
|      * | ||||
|      */ | ||||
|     public function setEntries(Collection $entries); | ||||
|  | ||||
|   | ||||
| @@ -1,20 +1,25 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * Processor.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Export; | ||||
|  | ||||
| use Auth; | ||||
| use Config; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Export\Collector\AttachmentCollector; | ||||
| use FireflyIII\Export\Collector\JournalExportCollector; | ||||
| use FireflyIII\Export\Collector\UploadCollector; | ||||
| use FireflyIII\Export\Entry\Entry; | ||||
| use FireflyIII\Models\ExportJob; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Filesystem\FilesystemAdapter; | ||||
| use Illuminate\Support\Collection; | ||||
| use Log; | ||||
| use Storage; | ||||
| @@ -25,7 +30,7 @@ use ZipArchive; | ||||
|  * | ||||
|  * @package FireflyIII\Export | ||||
|  */ | ||||
| class Processor | ||||
| class Processor implements ProcessorInterface | ||||
| { | ||||
|  | ||||
|     /** @var Collection */ | ||||
| @@ -35,15 +40,11 @@ class Processor | ||||
|     /** @var  bool */ | ||||
|     public $includeAttachments; | ||||
|     /** @var  bool */ | ||||
|     public $includeConfig; | ||||
|     /** @var  bool */ | ||||
|     public $includeOldUploads; | ||||
|     /** @var  ExportJob */ | ||||
|     public $job; | ||||
|     /** @var array */ | ||||
|     public $settings; | ||||
|     /** @var  \FireflyIII\Export\ConfigurationFile */ | ||||
|     private $configurationMaker; | ||||
|     /** @var  Collection */ | ||||
|     private $exportEntries; | ||||
|     /** @var  Collection */ | ||||
| @@ -63,7 +64,6 @@ class Processor | ||||
|         $this->accounts           = $settings['accounts']; | ||||
|         $this->exportFormat       = $settings['exportFormat']; | ||||
|         $this->includeAttachments = $settings['includeAttachments']; | ||||
|         $this->includeConfig      = $settings['includeConfig']; | ||||
|         $this->includeOldUploads  = $settings['includeOldUploads']; | ||||
|         $this->job                = $settings['job']; | ||||
|         $this->journals           = new Collection; | ||||
| @@ -73,66 +73,73 @@ class Processor | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function collectAttachments() | ||||
|     public function collectAttachments(): bool | ||||
|     { | ||||
|         $attachmentCollector = app('FireflyIII\Export\Collector\AttachmentCollector', [$this->job]); | ||||
|         /** @var AttachmentCollector $attachmentCollector */ | ||||
|         $attachmentCollector = app(AttachmentCollector::class, [$this->job]); | ||||
|         $attachmentCollector->setDates($this->settings['startDate'], $this->settings['endDate']); | ||||
|         $attachmentCollector->run(); | ||||
|         $this->files = $this->files->merge($attachmentCollector->getFiles()); | ||||
|         $this->files = $this->files->merge($attachmentCollector->getEntries()); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function collectJournals() | ||||
|     public function collectJournals(): bool | ||||
|     { | ||||
|         $args             = [$this->accounts, Auth::user(), $this->settings['startDate'], $this->settings['endDate']]; | ||||
|         $journalCollector = app('FireflyIII\Repositories\Journal\JournalCollector', $args); | ||||
|         $this->journals   = $journalCollector->collect(); | ||||
|         Log::debug( | ||||
|             'Collected ' . | ||||
|             $this->journals->count() . ' journals (between ' . | ||||
|             $this->settings['startDate']->format('Y-m-d') . ' and ' . | ||||
|             $this->settings['endDate']->format('Y-m-d') | ||||
|             . ').' | ||||
|         ); | ||||
|         /** @var JournalExportCollector $collector */ | ||||
|         $collector = app(JournalExportCollector::class, [$this->job]); | ||||
|         $collector->setDates($this->settings['startDate'], $this->settings['endDate']); | ||||
|         $collector->setAccounts($this->settings['accounts']); | ||||
|         $collector->run(); | ||||
|         $this->journals = $collector->getEntries(); | ||||
|         Log::debug(sprintf('Count %d journals in collectJournals() ', $this->journals->count())); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function collectOldUploads() | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function collectOldUploads(): bool | ||||
|     { | ||||
|         $uploadCollector = app('FireflyIII\Export\Collector\UploadCollector', [$this->job]); | ||||
|         /** @var UploadCollector $uploadCollector */ | ||||
|         $uploadCollector = app(UploadCollector::class, [$this->job]); | ||||
|         $uploadCollector->run(); | ||||
|  | ||||
|         $this->files = $this->files->merge($uploadCollector->getFiles()); | ||||
|         $this->files = $this->files->merge($uploadCollector->getEntries()); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function convertJournals() | ||||
|     public function convertJournals(): bool | ||||
|     { | ||||
|         $count = 0; | ||||
|         /** @var TransactionJournal $journal */ | ||||
|         foreach ($this->journals as $journal) { | ||||
|             $this->exportEntries->push(Entry::fromJournal($journal)); | ||||
|         foreach ($this->journals as $object) { | ||||
|             $this->exportEntries->push(Entry::fromObject($object)); | ||||
|             $count++; | ||||
|         } | ||||
|         Log::debug('Converted ' . $count . ' journals to "Entry" objects.'); | ||||
|         Log::debug(sprintf('Count %d entries in exportEntries (convertJournals)', $this->exportEntries->count())); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function createConfigFile() | ||||
|     { | ||||
|         $this->configurationMaker = app('FireflyIII\Export\ConfigurationFile', [$this->job]); | ||||
|         $this->files->push($this->configurationMaker->make()); | ||||
|     } | ||||
|  | ||||
|     public function createZipFile() | ||||
|     /** | ||||
|      * @return bool | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function createZipFile(): bool | ||||
|     { | ||||
|         $zip      = new ZipArchive; | ||||
|         $file     = $this->job->key . '.zip'; | ||||
|         $fullPath = storage_path('export') . '/' . $file; | ||||
|         Log::debug('Will create zip file at ' . $fullPath); | ||||
|  | ||||
|         if ($zip->open($fullPath, ZipArchive::CREATE) !== true) { | ||||
|             throw new FireflyException('Cannot store zip file.'); | ||||
| @@ -142,41 +149,47 @@ class Processor | ||||
|         foreach ($this->getFiles() as $entry) { | ||||
|             // is part of this job? | ||||
|             $zipFileName = str_replace($this->job->key . '-', '', $entry); | ||||
|             $result      = $zip->addFromString($zipFileName, $disk->get($entry)); | ||||
|             if (!$result) { | ||||
|                 Log::error('Could not add "' . $entry . '" into zip file as "' . $zipFileName . '".'); | ||||
|             } | ||||
|             $zip->addFromString($zipFileName, $disk->get($entry)); | ||||
|         } | ||||
|  | ||||
|         $zip->close(); | ||||
|  | ||||
|         // delete the files: | ||||
|         foreach ($this->getFiles() as $file) { | ||||
|             Log::debug('Will now delete file "' . $file . '".'); | ||||
|             $disk->delete($file); | ||||
|         } | ||||
|         Log::debug('Done!'); | ||||
|         $this->deleteFiles(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function exportJournals() | ||||
|     public function exportJournals(): bool | ||||
|     { | ||||
|         $exporterClass = Config::get('firefly.export_formats.' . $this->exportFormat); | ||||
|         $exporterClass = config('firefly.export_formats.' . $this->exportFormat); | ||||
|         $exporter      = app($exporterClass, [$this->job]); | ||||
|         Log::debug('Going to export ' . $this->exportEntries->count() . ' export entries into ' . $this->exportFormat . ' format.'); | ||||
|         $exporter->setEntries($this->exportEntries); | ||||
|         $exporter->run(); | ||||
|         $this->files->push($exporter->getFileName()); | ||||
|         Log::debug('Added "' . $exporter->getFileName() . '" to the list of files to include in the zip.'); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getFiles() | ||||
|     public function getFiles(): Collection | ||||
|     { | ||||
|         return $this->files; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     private function deleteFiles() | ||||
|     { | ||||
|         $disk = Storage::disk('export'); | ||||
|         foreach ($this->getFiles() as $file) { | ||||
|             $disk->delete($file); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										68
									
								
								app/Export/ProcessorInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								app/Export/ProcessorInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ProcessorInterface.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Export; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface ProcessorInterface | ||||
|  * | ||||
|  * @package FireflyIII\Export | ||||
|  */ | ||||
| interface ProcessorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Processor constructor. | ||||
|      * | ||||
|      * @param array $settings | ||||
|      * | ||||
|      */ | ||||
|     public function __construct(array $settings); | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function collectAttachments(): bool; | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function collectJournals(): bool; | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function collectOldUploads(): bool; | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function convertJournals(): bool; | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function createZipFile(): bool; | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function exportJournals(): bool; | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getFiles(): Collection; | ||||
| } | ||||
| @@ -1,51 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * AccountChartGeneratorInterface.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Account; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Models\Account; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface AccountChartGeneratorInterface | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Account | ||||
|  */ | ||||
| interface AccountChartGeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(Collection $accounts, Carbon $start, Carbon $end): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function single(Account $account, Carbon $start, Carbon $end): array; | ||||
| } | ||||
| @@ -1,180 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Generator\Chart\Account; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Models\Account; | ||||
| use Illuminate\Support\Collection; | ||||
| use Steam; | ||||
|  | ||||
| /** | ||||
|  * Class ChartJsAccountChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Account | ||||
|  */ | ||||
| class ChartJsAccountChartGenerator implements AccountChartGeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end): array | ||||
|     { | ||||
|         $data = [ | ||||
|             'count'  => 1, | ||||
|             'labels' => [], 'datasets' => [[ | ||||
|                                                'label' => trans('firefly.spent'), | ||||
|                                                'data'  => []]]]; | ||||
|  | ||||
|         $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 Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(Collection $accounts, Carbon $start, Carbon $end): array | ||||
|     { | ||||
|         // language: | ||||
|         $format  = (string)trans('config.month_and_day'); | ||||
|         $data    = ['count' => 0, 'labels' => [], 'datasets' => [],]; | ||||
|         $current = clone $start; | ||||
|         while ($current <= $end) { | ||||
|             $data['labels'][] = $current->formatLocalized($format); | ||||
|             $current->addDay(); | ||||
|         } | ||||
|  | ||||
|         foreach ($accounts as $account) { | ||||
|             $set      = [ | ||||
|                 'label'                => $account->name, | ||||
|                 'fillColor'            => 'rgba(220,220,220,0.2)', | ||||
|                 'strokeColor'          => 'rgba(220,220,220,1)', | ||||
|                 'pointColor'           => 'rgba(220,220,220,1)', | ||||
|                 'pointStrokeColor'     => '#fff', | ||||
|                 'pointHighlightFill'   => '#fff', | ||||
|                 'pointHighlightStroke' => 'rgba(220,220,220,1)', | ||||
|                 'data'                 => [], | ||||
|             ]; | ||||
|             $current  = clone $start; | ||||
|             $range    = Steam::balanceInRange($account, $start, clone $end); | ||||
|             $previous = round(array_values($range)[0], 2); | ||||
|             while ($current <= $end) { | ||||
|                 $format  = $current->format('Y-m-d'); | ||||
|                 $balance = isset($range[$format]) ? round($range[$format], 2) : $previous; | ||||
|  | ||||
|                 $set['data'][] = $balance; | ||||
|                 $previous      = $balance; | ||||
|                 $current->addDay(); | ||||
|             } | ||||
|             $data['datasets'][] = $set; | ||||
|         } | ||||
|         $data['count'] = count($data['datasets']); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function single(Account $account, Carbon $start, Carbon $end): array | ||||
|     { | ||||
|         // language: | ||||
|         $format = (string)trans('config.month_and_day'); | ||||
|  | ||||
|         $data     = [ | ||||
|             'count'    => 1, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => $account->name, | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|         $range    = Steam::balanceInRange($account, $start, $end); | ||||
|         $current  = clone $start; | ||||
|         $previous = array_values($range)[0]; | ||||
|  | ||||
|         while ($end >= $current) { | ||||
|             $theDate = $current->format('Y-m-d'); | ||||
|             $balance = $range[$theDate] ?? $previous; | ||||
|  | ||||
|             $data['labels'][]              = $current->formatLocalized($format); | ||||
|             $data['datasets'][0]['data'][] = $balance; | ||||
|             $previous                      = $balance; | ||||
|             $current->addDay(); | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $collection | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function getIdsFromCollection(Collection $collection): array | ||||
|     { | ||||
|         $ids = []; | ||||
|         foreach ($collection as $entry) { | ||||
|             $ids[] = $entry->id; | ||||
|         } | ||||
|  | ||||
|         return array_unique($ids); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $array | ||||
|      * @param $entryId | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function isInArray($array, $entryId): string | ||||
|     { | ||||
|         if (isset($array[$entryId])) { | ||||
|             return $array[$entryId]; | ||||
|         } | ||||
|  | ||||
|         return '0'; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										150
									
								
								app/Generator/Chart/Basic/ChartJsGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								app/Generator/Chart/Basic/ChartJsGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ChartJsGenerator.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Basic; | ||||
|  | ||||
| use FireflyIII\Support\ChartColour; | ||||
|  | ||||
| /** | ||||
|  * Class ChartJsGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Basic | ||||
|  */ | ||||
| class ChartJsGenerator implements GeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Will generate a Chart JS compatible array from the given input. Expects this format | ||||
|      * | ||||
|      * Will take labels for all from first set. | ||||
|      * | ||||
|      * 0: [ | ||||
|      *    'label' => 'label of set', | ||||
|      *    'type' => bar or line, optional | ||||
|      *    'yAxisID' => ID of yAxis, optional, will not be included when unused. | ||||
|      *    'fill' => if to fill a line? optional, will not be included when unused. | ||||
|      *    'entries' => | ||||
|      *        [ | ||||
|      *         'label-of-entry' => 'value' | ||||
|      *        ] | ||||
|      *    ] | ||||
|      * 1: [ | ||||
|      *    'label' => 'label of another set', | ||||
|      *    'type' => bar or line, optional | ||||
|      *    'yAxisID' => ID of yAxis, optional, will not be included when unused. | ||||
|      *    'fill' => if to fill a line? optional, will not be included when unused. | ||||
|      *    'entries' => | ||||
|      *        [ | ||||
|      *         'label-of-entry' => 'value' | ||||
|      *        ] | ||||
|      *    ] | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five. | ||||
|      * | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiSet(array $data): array | ||||
|     { | ||||
|         reset($data); | ||||
|         $first  = current($data); | ||||
|         $labels = is_array($first['entries']) ? array_keys($first['entries']) : []; | ||||
|  | ||||
|         $chartData = [ | ||||
|             'count'    => count($data), | ||||
|             'labels'   => $labels, // take ALL labels from the first set. | ||||
|             'datasets' => [], | ||||
|         ]; | ||||
|         unset($first, $labels); | ||||
|  | ||||
|         foreach ($data as $set) { | ||||
|             $currentSet = [ | ||||
|                 'label' => $set['label'], | ||||
|                 'type'  => $set['type'] ?? 'line', | ||||
|                 'data'  => array_values($set['entries']), | ||||
|             ]; | ||||
|             if (isset($set['yAxisID'])) { | ||||
|                 $currentSet['yAxisID'] = $set['yAxisID']; | ||||
|             } | ||||
|             if (isset($set['fill'])) { | ||||
|                 $currentSet['fill'] = $set['fill']; | ||||
|             } | ||||
|  | ||||
|             $chartData['datasets'][] = $currentSet; | ||||
|         } | ||||
|  | ||||
|         return $chartData; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Expects data as: | ||||
|      * | ||||
|      * key => value | ||||
|      * | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function pieChart(array $data): array | ||||
|     { | ||||
|         $chartData = [ | ||||
|             'datasets' => [ | ||||
|                 0 => [], | ||||
|             ], | ||||
|             'labels'   => [], | ||||
|         ]; | ||||
|         $index     = 0; | ||||
|         foreach ($data as $key => $value) { | ||||
|  | ||||
|             // make larger than 0 | ||||
|             if (bccomp($value, '0') === -1) { | ||||
|                 $value = bcmul($value, '-1'); | ||||
|             } | ||||
|  | ||||
|             $chartData['datasets'][0]['data'][]            = $value; | ||||
|             $chartData['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index); | ||||
|             $chartData['labels'][]                         = $key; | ||||
|             $index++; | ||||
|         } | ||||
|  | ||||
|         return $chartData; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Will generate a (ChartJS) compatible array from the given input. Expects this format: | ||||
|      * | ||||
|      * 'label-of-entry' => value | ||||
|      * 'label-of-entry' => value | ||||
|      * | ||||
|      * @param string $setLabel | ||||
|      * @param array  $data | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function singleSet(string $setLabel, array $data): array | ||||
|     { | ||||
|         $chartData = [ | ||||
|             'count'    => 1, | ||||
|             'labels'   => array_keys($data), // take ALL labels from the first set. | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => $setLabel, | ||||
|                     'data'  => array_values($data), | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         return $chartData; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										73
									
								
								app/Generator/Chart/Basic/GeneratorInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								app/Generator/Chart/Basic/GeneratorInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| <?php | ||||
| /** | ||||
|  * GeneratorInterface.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Basic; | ||||
|  | ||||
| /** | ||||
|  * Interface GeneratorInterface | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Basic | ||||
|  */ | ||||
| interface GeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Will generate a (ChartJS) compatible array from the given input. Expects this format: | ||||
|      * | ||||
|      * 0: [ | ||||
|      *    'label' => 'label of set', | ||||
|      *    'entries' => | ||||
|      *        [ | ||||
|      *         'label-of-entry' => 'value' | ||||
|      *        ] | ||||
|      *    ] | ||||
|      * 1: [ | ||||
|      *    'label' => 'label of another set', | ||||
|      *    'entries' => | ||||
|      *        [ | ||||
|      *         'label-of-entry' => 'value' | ||||
|      *        ] | ||||
|      *    ] | ||||
|      * | ||||
|      * | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiSet(array $data): array; | ||||
|  | ||||
|     /** | ||||
|      * Expects data as: | ||||
|      * | ||||
|      * key => value | ||||
|      * | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function pieChart(array $data): array; | ||||
|  | ||||
|     /** | ||||
|      * Will generate a (ChartJS) compatible array from the given input. Expects this format: | ||||
|      * | ||||
|      * 'label-of-entry' => value | ||||
|      * 'label-of-entry' => value | ||||
|      * | ||||
|      * @param string $setLabel | ||||
|      * @param array  $data | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function singleSet(string $setLabel, array $data): array; | ||||
|  | ||||
| } | ||||
| @@ -1,41 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * BillChartGeneratorInterface.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Bill; | ||||
|  | ||||
|  | ||||
| use FireflyIII\Models\Bill; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface BillChartGeneratorInterface | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Bill | ||||
|  */ | ||||
| interface BillChartGeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param string $paid | ||||
|      * @param string $unpaid | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(string $paid, string $unpaid): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Bill       $bill | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function single(Bill $bill, Collection $entries): array; | ||||
|  | ||||
| } | ||||
| @@ -1,96 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * ChartJsBillChartGenerator.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Bill; | ||||
|  | ||||
| use FireflyIII\Models\Bill; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class ChartJsBillChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Bill | ||||
|  */ | ||||
| class ChartJsBillChartGenerator implements BillChartGeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param string $paid | ||||
|      * @param string $unpaid | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(string $paid, string $unpaid): array | ||||
|     { | ||||
|         $data = [ | ||||
|             [ | ||||
|                 'value'     => round($unpaid, 2), | ||||
|                 'color'     => 'rgba(53, 124, 165,0.7)', | ||||
|                 'highlight' => 'rgba(53, 124, 165,0.9)', | ||||
|                 'label'     => trans('firefly.unpaid'), | ||||
|             ], | ||||
|             [ | ||||
|                 'value'     => round(bcmul($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'), | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Bill       $bill | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function single(Bill $bill, Collection $entries): array | ||||
|     { | ||||
|         $format       = (string)trans('config.month'); | ||||
|         $data         = [ | ||||
|             'count'    => 3, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [], | ||||
|         ]; | ||||
|         $minAmount    = []; | ||||
|         $maxAmount    = []; | ||||
|         $actualAmount = []; | ||||
|         /** @var TransactionJournal $entry */ | ||||
|         foreach ($entries as $entry) { | ||||
|             $data['labels'][] = $entry->date->formatLocalized($format); | ||||
|             $minAmount[]      = round($bill->amount_min, 2); | ||||
|             $maxAmount[]      = round($bill->amount_max, 2); | ||||
|             /* | ||||
|              * journalAmount has been collected in BillRepository::getJournals | ||||
|              */ | ||||
|             $actualAmount[] = round(TransactionJournal::amountPositive($entry), 2); | ||||
|         } | ||||
|  | ||||
|         $data['datasets'][] = [ | ||||
|             'label' => trans('firefly.minAmount'), | ||||
|             'data'  => $minAmount, | ||||
|         ]; | ||||
|         $data['datasets'][] = [ | ||||
|             'label' => trans('firefly.billEntry'), | ||||
|             'data'  => $actualAmount, | ||||
|         ]; | ||||
|         $data['datasets'][] = [ | ||||
|             'label' => trans('firefly.maxAmount'), | ||||
|             'data'  => $maxAmount, | ||||
|         ]; | ||||
|  | ||||
|         $data['count'] = count($data['datasets']); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
| } | ||||
| @@ -1,58 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * BudgetChartGeneratorInterface.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Budget; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface BudgetChartGeneratorInterface | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Budget | ||||
|  */ | ||||
| interface BudgetChartGeneratorInterface | ||||
| { | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function budget(Collection $entries): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function budgetLimit(Collection $entries): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(Collection $entries): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYear(Collection $entries): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function year(Collection $budgets, Collection $entries): array; | ||||
|  | ||||
| } | ||||
| @@ -1,180 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Generator\Chart\Budget; | ||||
|  | ||||
|  | ||||
| use Config; | ||||
| use Illuminate\Support\Collection; | ||||
| use Preferences; | ||||
|  | ||||
| /** | ||||
|  * Class ChartJsBudgetChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Budget | ||||
|  */ | ||||
| class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * @param string     $dateFormat | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function budget(Collection $entries, $dateFormat = 'month'): array | ||||
|     { | ||||
|         // language: | ||||
|         $language = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US'))->data; | ||||
|         $format   = Config::get('firefly.' . $dateFormat . '.' . $language); | ||||
|  | ||||
|         $data = [ | ||||
|             'labels'   => [], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => 'Amount', | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         /** @var array $entry */ | ||||
|         foreach ($entries as $entry) { | ||||
|             $data['labels'][]              = $entry[0]->formatLocalized($format); | ||||
|             $data['datasets'][0]['data'][] = $entry[1]; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         $data['count'] = count($data['datasets']); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function budgetLimit(Collection $entries): array | ||||
|     { | ||||
|         return $this->budget($entries, 'monthAndDay'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(Collection $entries): array | ||||
|     { | ||||
|         $data      = [ | ||||
|             'count'    => 0, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [], | ||||
|         ]; | ||||
|         $left      = []; | ||||
|         $spent     = []; | ||||
|         $overspent = []; | ||||
|         $filtered  = $entries->filter( | ||||
|             function ($entry) { | ||||
|                 return ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0); | ||||
|             } | ||||
|         ); | ||||
|         foreach ($filtered as $entry) { | ||||
|             $data['labels'][] = $entry[0]; | ||||
|             $left[]           = round($entry[1], 2); | ||||
|             $spent[]          = round(bcmul($entry[2], '-1'), 2); // spent is coming in negative, must be positive | ||||
|             $overspent[]      = round(bcmul($entry[3], '-1'), 2); // same | ||||
|         } | ||||
|  | ||||
|         $data['datasets'][] = [ | ||||
|             'label' => trans('firefly.left'), | ||||
|             'data'  => $left, | ||||
|         ]; | ||||
|         $data['datasets'][] = [ | ||||
|             'label' => trans('firefly.spent'), | ||||
|             'data'  => $spent, | ||||
|         ]; | ||||
|         $data['datasets'][] = [ | ||||
|             'label' => trans('firefly.overspent'), | ||||
|             'data'  => $overspent, | ||||
|         ]; | ||||
|  | ||||
|         $data['count'] = count($data['datasets']); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYear(Collection $entries): array | ||||
|     { | ||||
|         // dataset: | ||||
|         $data = [ | ||||
|             'count'    => 0, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [], | ||||
|         ]; | ||||
|         // get labels from one of the budgets (assuming there's at least one): | ||||
|         $first = $entries->first(); | ||||
|         $keys  = array_keys($first['budgeted']); | ||||
|         foreach ($keys as $year) { | ||||
|             $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; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function year(Collection $budgets, Collection $entries): array | ||||
|     { | ||||
|         // language: | ||||
|         $format = (string)trans('config.month'); | ||||
|  | ||||
|         $data = [ | ||||
|             'labels'   => [], | ||||
|             'datasets' => [], | ||||
|         ]; | ||||
|  | ||||
|         foreach ($budgets as $budget) { | ||||
|             $data['labels'][] = $budget->name; | ||||
|         } | ||||
|         // also add "no budget" | ||||
|         $data['labels'][] = strval(trans('firefly.no_budget')); | ||||
|  | ||||
|         /** @var array $entry */ | ||||
|         foreach ($entries as $entry) { | ||||
|             $array = [ | ||||
|                 'label' => $entry[0]->formatLocalized($format), | ||||
|                 'data'  => [], | ||||
|             ]; | ||||
|             array_shift($entry); | ||||
|             $array['data']      = $entry; | ||||
|             $data['datasets'][] = $array; | ||||
|  | ||||
|         } | ||||
|         $data['count'] = count($data['datasets']); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
| } | ||||
| @@ -1,66 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * CategoryChartGeneratorInterface.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Category; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface CategoryChartGeneratorInterface | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Category | ||||
|  */ | ||||
| interface CategoryChartGeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function all(Collection $entries); | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function earnedInPeriod(Collection $categories, Collection $entries): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(Collection $entries): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYear(Collection $entries): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function period(Collection $entries): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function spentInPeriod(Collection $categories, Collection $entries): array; | ||||
| } | ||||
| @@ -1,195 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Generator\Chart\Category; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class ChartJsCategoryChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Category | ||||
|  */ | ||||
| class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function all(Collection $entries): array | ||||
|     { | ||||
|  | ||||
|  | ||||
|         $data = [ | ||||
|             'count'    => 2, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => trans('firefly.spent'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|                 [ | ||||
|                     'label' => trans('firefly.earned'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         foreach ($entries as $entry) { | ||||
|             $data['labels'][] = $entry[1]; | ||||
|             $spent            = $entry[2]; | ||||
|             $earned           = $entry[3]; | ||||
|  | ||||
|             $data['datasets'][0]['data'][] = bccomp($spent, '0') === 0 ? null : bcmul($spent, '-1'); | ||||
|             $data['datasets'][1]['data'][] = bccomp($earned, '0') === 0 ? null : $earned; | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function earnedInPeriod(Collection $categories, Collection $entries): array | ||||
|     { | ||||
|  | ||||
|         // language: | ||||
|         $format = (string)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 | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function frontpage(Collection $entries): array | ||||
|     { | ||||
|         $data = [ | ||||
|             'count'    => 1, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => trans('firefly.spent'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|         foreach ($entries as $entry) { | ||||
|             if ($entry->spent != 0) { | ||||
|                 $data['labels'][]              = $entry->name; | ||||
|                 $data['datasets'][0]['data'][] = round(bcmul($entry->spent, '-1'), 2); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYear(Collection $entries): array | ||||
|     { | ||||
|         // dataset: | ||||
|         $data = [ | ||||
|             'count'    => 0, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [], | ||||
|         ]; | ||||
|         // get labels from one of the categories (assuming there's at least one): | ||||
|         $first = $entries->first(); | ||||
|         $keys  = array_keys($first['spent']); | ||||
|         foreach ($keys as $year) { | ||||
|             $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): array | ||||
|     { | ||||
|         return $this->all($entries); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function spentInPeriod(Collection $categories, Collection $entries): array | ||||
|     { | ||||
|  | ||||
|         // language: | ||||
|         $format = (string)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; | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,48 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Generator\Chart\PiggyBank; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class ChartJsPiggyBankChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\PiggyBank | ||||
|  */ | ||||
| class ChartJsPiggyBankChartGenerator implements PiggyBankChartGeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $set | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function history(Collection $set): array | ||||
|     { | ||||
|  | ||||
|         // language: | ||||
|         $format = (string)trans('config.month_and_day'); | ||||
|  | ||||
|         $data = [ | ||||
|             'count'    => 1, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => 'Diff', | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|         $sum  = '0'; | ||||
|         foreach ($set as $entry) { | ||||
|             $date                          = new Carbon($entry->date); | ||||
|             $sum                           = bcadd($sum, $entry->sum); | ||||
|             $data['labels'][]              = $date->formatLocalized($format); | ||||
|             $data['datasets'][0]['data'][] = round($sum, 2); | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
| } | ||||
| @@ -1,28 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * PiggyBankChartGenerator.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\PiggyBank; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface PiggyBankChartGeneratorInterface | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\PiggyBank | ||||
|  */ | ||||
| interface PiggyBankChartGeneratorInterface | ||||
| { | ||||
|     /** | ||||
|      * @param Collection $set | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function history(Collection $set): array; | ||||
| } | ||||
| @@ -1,170 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Generator\Chart\Report; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class ChartJsReportChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Report | ||||
|  */ | ||||
| class ChartJsReportChartGenerator implements ReportChartGeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Same as above but other translations. | ||||
|      * | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYearInOut(Collection $entries): array | ||||
|     { | ||||
|         $data = [ | ||||
|             'count'    => 2, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => trans('firefly.income'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|                 [ | ||||
|                     'label' => trans('firefly.expenses'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         foreach ($entries as $entry) { | ||||
|             $data['labels'][]              = $entry[0]->formatLocalized('%Y'); | ||||
|             $data['datasets'][0]['data'][] = round($entry[1], 2); | ||||
|             $data['datasets'][1]['data'][] = round($entry[2], 2); | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $income | ||||
|      * @param string $expense | ||||
|      * @param int    $count | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYearInOutSummarized(string $income, string $expense, int $count): array | ||||
|     { | ||||
|         $data                          = [ | ||||
|             'count'    => 2, | ||||
|             'labels'   => [trans('firefly.sum_of_years'), trans('firefly.average_of_years')], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => trans('firefly.income'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|                 [ | ||||
|                     'label' => trans('firefly.expenses'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|         $data['datasets'][0]['data'][] = round($income, 2); | ||||
|         $data['datasets'][1]['data'][] = round($expense, 2); | ||||
|         $data['datasets'][0]['data'][] = round(($income / $count), 2); | ||||
|         $data['datasets'][1]['data'][] = round(($expense / $count), 2); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function netWorth(Collection $entries) : array | ||||
|     { | ||||
|         $format = (string)trans('config.month_and_day'); | ||||
|         $data   = [ | ||||
|             'count'    => 1, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => trans('firefly.net-worth'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|         foreach ($entries as $entry) { | ||||
|             $data['labels'][]              = trim($entry['date']->formatLocalized($format)); | ||||
|             $data['datasets'][0]['data'][] = round($entry['net-worth'], 2); | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function yearInOut(Collection $entries): array | ||||
|     { | ||||
|         // language: | ||||
|         $format = (string)trans('config.month'); | ||||
|  | ||||
|         $data = [ | ||||
|             'count'    => 2, | ||||
|             'labels'   => [], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => trans('firefly.income'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|                 [ | ||||
|                     'label' => trans('firefly.expenses'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         foreach ($entries as $entry) { | ||||
|             $data['labels'][]              = $entry[0]->formatLocalized($format); | ||||
|             $data['datasets'][0]['data'][] = round($entry[1], 2); | ||||
|             $data['datasets'][1]['data'][] = round($entry[2], 2); | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $income | ||||
|      * @param string $expense | ||||
|      * @param int    $count | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function yearInOutSummarized(string $income, string $expense, int $count): array | ||||
|     { | ||||
|  | ||||
|         $data                          = [ | ||||
|             'count'    => 2, | ||||
|             'labels'   => [trans('firefly.sum_of_year'), trans('firefly.average_of_year')], | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => trans('firefly.income'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|                 [ | ||||
|                     'label' => trans('firefly.expenses'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|         $data['datasets'][0]['data'][] = round($income, 2); | ||||
|         $data['datasets'][1]['data'][] = round($expense, 2); | ||||
|         $data['datasets'][0]['data'][] = round(($income / $count), 2); | ||||
|         $data['datasets'][1]['data'][] = round(($expense / $count), 2); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
| } | ||||
| @@ -1,62 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * ReportChartGenerator.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Generator\Chart\Report; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface ReportChartGeneratorInterface | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Report | ||||
|  */ | ||||
| interface ReportChartGeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYearInOut(Collection $entries): array; | ||||
|  | ||||
|     /** | ||||
|      * @param string $income | ||||
|      * @param string $expense | ||||
|      * @param int    $count | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function multiYearInOutSummarized(string $income, string $expense, int $count): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function netWorth(Collection $entries) : array; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function yearInOut(Collection $entries): array; | ||||
|  | ||||
|     /** | ||||
|      * @param string $income | ||||
|      * @param string $expense | ||||
|      * @param int    $count | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function yearInOutSummarized(string $income, string $expense, int $count): array; | ||||
|  | ||||
| } | ||||
							
								
								
									
										173
									
								
								app/Generator/Report/Audit/MonthReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								app/Generator/Report/Audit/MonthReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | ||||
| <?php | ||||
| /** | ||||
|  * MonthReportGenerator.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report\Audit; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Generator\Report\ReportGeneratorInterface; | ||||
| use FireflyIII\Helpers\Collector\JournalCollectorInterface; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Transaction; | ||||
| use Illuminate\Support\Collection; | ||||
| use Steam; | ||||
|  | ||||
| /** | ||||
|  * Class MonthReportGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report\Audit | ||||
|  */ | ||||
| class MonthReportGenerator implements ReportGeneratorInterface | ||||
| { | ||||
|     /** @var  Collection */ | ||||
|     private $accounts; | ||||
|     /** @var  Carbon */ | ||||
|     private $end; | ||||
|     /** @var  Carbon */ | ||||
|     private $start; | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function generate(): string | ||||
|     { | ||||
|  | ||||
|  | ||||
|         $auditData = []; | ||||
|         $dayBefore = clone $this->start; | ||||
|         $dayBefore->subDay(); | ||||
|         /** @var Account $account */ | ||||
|         foreach ($this->accounts as $account) { | ||||
|             // balance the day before: | ||||
|             $id             = $account->id; | ||||
|             $auditData[$id] = $this->getAuditReport($account, $dayBefore); | ||||
|         } | ||||
|  | ||||
|         $defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to']; | ||||
|         $reportType  = 'audit'; | ||||
|         $accountIds  = join(',', $this->accounts->pluck('id')->toArray()); | ||||
|         $hideable    = ['buttons', 'icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', | ||||
|                         'interest_date', 'book_date', 'process_date', | ||||
|                         // three new optional fields. | ||||
|                         'due_date', 'payment_date', 'invoice_date', | ||||
|                         'from', 'to', 'budget', 'category', 'bill', | ||||
|                         // more new optional fields | ||||
|                         'internal_reference', 'notes', | ||||
|                         'create_date', 'update_date', | ||||
|         ]; | ||||
|  | ||||
|  | ||||
|         return view('reports.audit.report', compact('reportType', 'accountIds', 'auditData', 'hideable', 'defaultShow')) | ||||
|             ->with('start', $this->start)->with('end', $this->end)->with('accounts', $this->accounts) | ||||
|             ->render(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setAccounts(Collection $accounts): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->accounts = $accounts; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setBudgets(Collection $budgets): ReportGeneratorInterface | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setCategories(Collection $categories): ReportGeneratorInterface | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setEndDate(Carbon $date): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->end = $date; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setStartDate(Carbon $date): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->start = $date; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * @param Carbon  $date | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getAuditReport(Account $account, Carbon $date): array | ||||
|     { | ||||
|  | ||||
|         /** @var JournalCollectorInterface $collector */ | ||||
|         $collector = app(JournalCollectorInterface::class, [auth()->user()]); | ||||
|         $collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end); | ||||
|         $journals         = $collector->getJournals(); | ||||
|         $journals         = $journals->reverse(); | ||||
|         $dayBeforeBalance = Steam::balance($account, $date); | ||||
|         $startBalance     = $dayBeforeBalance; | ||||
|  | ||||
|  | ||||
|         /** @var Transaction $journal */ | ||||
|         foreach ($journals as $transaction) { | ||||
|             $transaction->before = $startBalance; | ||||
|             $transactionAmount   = $transaction->transaction_amount; | ||||
|             $newBalance          = bcadd($startBalance, $transactionAmount); | ||||
|             $transaction->after  = $newBalance; | ||||
|             $startBalance        = $newBalance; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Reverse set again. | ||||
|          */ | ||||
|         $return = [ | ||||
|             'journals'         => $journals->reverse(), | ||||
|             'exists'           => $journals->count() > 0, | ||||
|             'end'              => $this->end->formatLocalized(strval(trans('config.month_and_day'))), | ||||
|             'endBalance'       => Steam::balance($account, $this->end), | ||||
|             'dayBefore'        => $date->formatLocalized(strval(trans('config.month_and_day'))), | ||||
|             'dayBeforeBalance' => $dayBeforeBalance, | ||||
|         ]; | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								app/Generator/Report/Audit/MultiYearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								app/Generator/Report/Audit/MultiYearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| <?php | ||||
| /** | ||||
|  * MultiYearReportGenerator.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report\Audit; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class MultiYearReportGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report\Audit | ||||
|  */ | ||||
| class MultiYearReportGenerator extends MonthReportGenerator | ||||
| { | ||||
|     /** | ||||
|      * Doesn't do anything different. | ||||
|      */ | ||||
| } | ||||
							
								
								
									
										28
									
								
								app/Generator/Report/Audit/YearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/Generator/Report/Audit/YearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
| /** | ||||
|  * YearReportGenerator.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report\Audit; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class YearReportGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report\Audit | ||||
|  */ | ||||
| class YearReportGenerator extends MonthReportGenerator | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Doesn't do anything different. | ||||
|      */ | ||||
| } | ||||
							
								
								
									
										248
									
								
								app/Generator/Report/Budget/MonthReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								app/Generator/Report/Budget/MonthReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,248 @@ | ||||
| <?php | ||||
| /** | ||||
|  * MonthReportGenerator.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report\Budget; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Generator\Report\ReportGeneratorInterface; | ||||
| use FireflyIII\Generator\Report\Support; | ||||
| use FireflyIII\Helpers\Collector\JournalCollectorInterface; | ||||
| use FireflyIII\Models\Transaction; | ||||
| use FireflyIII\Models\TransactionType; | ||||
| use Illuminate\Support\Collection; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class MonthReportGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report\Budget | ||||
|  */ | ||||
| class MonthReportGenerator extends Support implements ReportGeneratorInterface | ||||
| { | ||||
|     /** @var  Collection */ | ||||
|     private $accounts; | ||||
|     /** @var  Collection */ | ||||
|     private $budgets; | ||||
|     /** @var  Carbon */ | ||||
|     private $end; | ||||
|     /** @var Collection */ | ||||
|     private $expenses; | ||||
|     /** @var Collection */ | ||||
|     private $income; | ||||
|     /** @var  Carbon */ | ||||
|     private $start; | ||||
|  | ||||
|     /** | ||||
|      * MonthReportGenerator constructor. | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->income   = new Collection; | ||||
|         $this->expenses = new Collection; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function generate(): string | ||||
|     { | ||||
|         $accountIds      = join(',', $this->accounts->pluck('id')->toArray()); | ||||
|         $budgetIds       = join(',', $this->budgets->pluck('id')->toArray()); | ||||
|         $expenses        = $this->getExpenses(); | ||||
|         $accountSummary  = $this->summarizeByAccount($expenses); | ||||
|         $budgetSummary   = $this->summarizeByBudget($expenses); | ||||
|         $averageExpenses = $this->getAverages($expenses, SORT_ASC); | ||||
|         $topExpenses     = $this->getTopExpenses(); | ||||
|  | ||||
|         // render! | ||||
|         return view('reports.budget.month', compact('accountIds', 'budgetIds', 'accountSummary', 'budgetSummary', 'averageExpenses', 'topExpenses')) | ||||
|             ->with('start', $this->start)->with('end', $this->end) | ||||
|             ->with('budgets', $this->budgets) | ||||
|             ->with('accounts', $this->accounts) | ||||
|             ->render(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setAccounts(Collection $accounts): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->accounts = $accounts; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setBudgets(Collection $budgets): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->budgets = $budgets; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setCategories(Collection $categories): ReportGeneratorInterface | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setEndDate(Carbon $date): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->end = $date; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setStartDate(Carbon $date): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->start = $date; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $collection | ||||
|      * @param int        $sortFlag | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getAverages(Collection $collection, int $sortFlag): array | ||||
|     { | ||||
|         $result = []; | ||||
|         /** @var Transaction $transaction */ | ||||
|         foreach ($collection as $transaction) { | ||||
|             // opposing name and ID: | ||||
|             $opposingId = $transaction->opposing_account_id; | ||||
|  | ||||
|             // is not set? | ||||
|             if (!isset($result[$opposingId])) { | ||||
|                 $name                = $transaction->opposing_account_name; | ||||
|                 $result[$opposingId] = [ | ||||
|                     'name'    => $name, | ||||
|                     'count'   => 1, | ||||
|                     'id'      => $opposingId, | ||||
|                     'average' => $transaction->transaction_amount, | ||||
|                     'sum'     => $transaction->transaction_amount, | ||||
|                 ]; | ||||
|                 continue; | ||||
|             } | ||||
|             $result[$opposingId]['count']++; | ||||
|             $result[$opposingId]['sum']     = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount); | ||||
|             $result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], strval($result[$opposingId]['count'])); | ||||
|         } | ||||
|  | ||||
|         // sort result by average: | ||||
|         $average = []; | ||||
|         foreach ($result as $key => $row) { | ||||
|             $average[$key] = floatval($row['average']); | ||||
|         } | ||||
|  | ||||
|         array_multisort($average, $sortFlag, $result); | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     private function getExpenses(): Collection | ||||
|     { | ||||
|         if ($this->expenses->count() > 0) { | ||||
|             Log::debug('Return previous set of expenses.'); | ||||
|  | ||||
|             return $this->expenses; | ||||
|         } | ||||
|  | ||||
|         /** @var JournalCollectorInterface $collector */ | ||||
|         $collector = app(JournalCollectorInterface::class, [auth()->user()]); | ||||
|         $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) | ||||
|                   ->setTypes([TransactionType::WITHDRAWAL]) | ||||
|                   ->setBudgets($this->budgets)->withOpposingAccount()->disableFilter(); | ||||
|  | ||||
|         $accountIds     = $this->accounts->pluck('id')->toArray(); | ||||
|         $transactions   = $collector->getJournals(); | ||||
|         $transactions   = self::filterExpenses($transactions, $accountIds); | ||||
|         $this->expenses = $transactions; | ||||
|  | ||||
|         return $transactions; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     private function getTopExpenses(): Collection | ||||
|     { | ||||
|         $transactions = $this->getExpenses()->sortBy('transaction_amount'); | ||||
|  | ||||
|         return $transactions; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $collection | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function summarizeByAccount(Collection $collection): array | ||||
|     { | ||||
|         $result = []; | ||||
|         /** @var Transaction $transaction */ | ||||
|         foreach ($collection as $transaction) { | ||||
|             $accountId          = $transaction->account_id; | ||||
|             $result[$accountId] = $result[$accountId] ?? '0'; | ||||
|             $result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $collection | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function summarizeByBudget(Collection $collection): array | ||||
|     { | ||||
|         $result = []; | ||||
|         /** @var Transaction $transaction */ | ||||
|         foreach ($collection as $transaction) { | ||||
|             $jrnlBudId         = intval($transaction->transaction_journal_budget_id); | ||||
|             $transBudId        = intval($transaction->transaction_budget_id); | ||||
|             $budgetId          = max($jrnlBudId, $transBudId); | ||||
|             $result[$budgetId] = $result[$budgetId] ?? '0'; | ||||
|             $result[$budgetId] = bcadd($transaction->transaction_amount, $result[$budgetId]); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								app/Generator/Report/Budget/MultiYearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								app/Generator/Report/Budget/MultiYearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| <?php | ||||
| /** | ||||
|  * MultiYearReportGenerator.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report\Budget; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class MultiYearReportGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report\Budget | ||||
|  */ | ||||
| class MultiYearReportGenerator extends MonthReportGenerator | ||||
| { | ||||
|     /** | ||||
|      * Doesn't do anything different. | ||||
|      */ | ||||
| } | ||||
							
								
								
									
										28
									
								
								app/Generator/Report/Budget/YearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/Generator/Report/Budget/YearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
| /** | ||||
|  * YearReportGenerator.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report\Budget; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class YearReportGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report\Budget | ||||
|  */ | ||||
| class YearReportGenerator extends MonthReportGenerator | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Doesn't do anything different. | ||||
|      */ | ||||
| } | ||||
							
								
								
									
										331
									
								
								app/Generator/Report/Category/MonthReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								app/Generator/Report/Category/MonthReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,331 @@ | ||||
| <?php | ||||
| /** | ||||
|  * MonthReportGenerator.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report\Category; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Generator\Report\ReportGeneratorInterface; | ||||
| use FireflyIII\Generator\Report\Support; | ||||
| use FireflyIII\Helpers\Collector\JournalCollectorInterface; | ||||
| use FireflyIII\Models\Transaction; | ||||
| use FireflyIII\Models\TransactionType; | ||||
| use Illuminate\Support\Collection; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class MonthReportGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report\Category | ||||
|  */ | ||||
| class MonthReportGenerator extends Support implements ReportGeneratorInterface | ||||
| { | ||||
|     /** @var  Collection */ | ||||
|     private $accounts; | ||||
|     /** @var  Collection */ | ||||
|     private $categories; | ||||
|     /** @var  Carbon */ | ||||
|     private $end; | ||||
|     /** @var Collection */ | ||||
|     private $expenses; | ||||
|     /** @var Collection */ | ||||
|     private $income; | ||||
|     /** @var  Carbon */ | ||||
|     private $start; | ||||
|  | ||||
|     /** | ||||
|      * MonthReportGenerator constructor. | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->income   = new Collection; | ||||
|         $this->expenses = new Collection; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function generate(): string | ||||
|     { | ||||
|         $accountIds      = join(',', $this->accounts->pluck('id')->toArray()); | ||||
|         $categoryIds     = join(',', $this->categories->pluck('id')->toArray()); | ||||
|         $reportType      = 'category'; | ||||
|         $expenses        = $this->getExpenses(); | ||||
|         $income          = $this->getIncome(); | ||||
|         $accountSummary  = $this->getObjectSummary($this->summarizeByAccount($expenses), $this->summarizeByAccount($income)); | ||||
|         $categorySummary = $this->getObjectSummary($this->summarizeByCategory($expenses), $this->summarizeByCategory($income)); | ||||
|         $averageExpenses = $this->getAverages($expenses, SORT_ASC); | ||||
|         $averageIncome   = $this->getAverages($income, SORT_DESC); | ||||
|         $topExpenses     = $this->getTopExpenses(); | ||||
|         $topIncome       = $this->getTopIncome(); | ||||
|  | ||||
|  | ||||
|         // render! | ||||
|         return view( | ||||
|             'reports.category.month', | ||||
|             compact( | ||||
|                 'accountIds', 'categoryIds', 'topIncome', 'reportType', 'accountSummary', 'categorySummary', 'averageExpenses', 'averageIncome', 'topExpenses' | ||||
|             ) | ||||
|         ) | ||||
|             ->with('start', $this->start)->with('end', $this->end) | ||||
|             ->with('categories', $this->categories) | ||||
|             ->with('accounts', $this->accounts) | ||||
|             ->render(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setAccounts(Collection $accounts): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->accounts = $accounts; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setBudgets(Collection $budgets): ReportGeneratorInterface | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setCategories(Collection $categories): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->categories = $categories; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setEndDate(Carbon $date): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->end = $date; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setStartDate(Carbon $date): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->start = $date; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $collection | ||||
|      * @param int        $sortFlag | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getAverages(Collection $collection, int $sortFlag): array | ||||
|     { | ||||
|         $result = []; | ||||
|         /** @var Transaction $transaction */ | ||||
|         foreach ($collection as $transaction) { | ||||
|             // opposing name and ID: | ||||
|             $opposingId = $transaction->opposing_account_id; | ||||
|  | ||||
|             // is not set? | ||||
|             if (!isset($result[$opposingId])) { | ||||
|                 $name                = $transaction->opposing_account_name; | ||||
|                 $result[$opposingId] = [ | ||||
|                     'name'    => $name, | ||||
|                     'count'   => 1, | ||||
|                     'id'      => $opposingId, | ||||
|                     'average' => $transaction->transaction_amount, | ||||
|                     'sum'     => $transaction->transaction_amount, | ||||
|                 ]; | ||||
|                 continue; | ||||
|             } | ||||
|             $result[$opposingId]['count']++; | ||||
|             $result[$opposingId]['sum']     = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount); | ||||
|             $result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], strval($result[$opposingId]['count'])); | ||||
|         } | ||||
|  | ||||
|         // sort result by average: | ||||
|         $average = []; | ||||
|         foreach ($result as $key => $row) { | ||||
|             $average[$key] = floatval($row['average']); | ||||
|         } | ||||
|  | ||||
|         array_multisort($average, $sortFlag, $result); | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     private function getExpenses(): Collection | ||||
|     { | ||||
|         if ($this->expenses->count() > 0) { | ||||
|             Log::debug('Return previous set of expenses.'); | ||||
|  | ||||
|             return $this->expenses; | ||||
|         } | ||||
|  | ||||
|         /** @var JournalCollectorInterface $collector */ | ||||
|         $collector = app(JournalCollectorInterface::class, [auth()->user()]); | ||||
|         $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) | ||||
|                   ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) | ||||
|                   ->setCategories($this->categories)->withOpposingAccount()->disableFilter(); | ||||
|  | ||||
|         $accountIds     = $this->accounts->pluck('id')->toArray(); | ||||
|         $transactions   = $collector->getJournals(); | ||||
|         $transactions   = self::filterExpenses($transactions, $accountIds); | ||||
|         $this->expenses = $transactions; | ||||
|  | ||||
|         return $transactions; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     private function getIncome(): Collection | ||||
|     { | ||||
|         if ($this->income->count() > 0) { | ||||
|             return $this->income; | ||||
|         } | ||||
|  | ||||
|         /** @var JournalCollectorInterface $collector */ | ||||
|         $collector = app(JournalCollectorInterface::class, [auth()->user()]); | ||||
|         $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) | ||||
|                   ->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) | ||||
|                   ->setCategories($this->categories)->withOpposingAccount(); | ||||
|         $accountIds   = $this->accounts->pluck('id')->toArray(); | ||||
|         $transactions = $collector->getJournals(); | ||||
|         $transactions = self::filterIncome($transactions, $accountIds); | ||||
|         $this->income = $transactions; | ||||
|  | ||||
|         return $transactions; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five. | ||||
|      * @param array $spent | ||||
|      * @param array $earned | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getObjectSummary(array $spent, array $earned): array | ||||
|     { | ||||
|         $return = []; | ||||
|  | ||||
|         /** | ||||
|          * @var int    $accountId | ||||
|          * @var string $entry | ||||
|          */ | ||||
|         foreach ($spent as $objectId => $entry) { | ||||
|             if (!isset($return[$objectId])) { | ||||
|                 $return[$objectId] = ['spent' => 0, 'earned' => 0]; | ||||
|             } | ||||
|  | ||||
|             $return[$objectId]['spent'] = $entry; | ||||
|         } | ||||
|         unset($entry); | ||||
|  | ||||
|         /** | ||||
|          * @var int    $accountId | ||||
|          * @var string $entry | ||||
|          */ | ||||
|         foreach ($earned as $objectId => $entry) { | ||||
|             if (!isset($return[$objectId])) { | ||||
|                 $return[$objectId] = ['spent' => 0, 'earned' => 0]; | ||||
|             } | ||||
|  | ||||
|             $return[$objectId]['earned'] = $entry; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     private function getTopExpenses(): Collection | ||||
|     { | ||||
|         $transactions = $this->getExpenses()->sortBy('transaction_amount'); | ||||
|  | ||||
|         return $transactions; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     private function getTopIncome(): Collection | ||||
|     { | ||||
|         $transactions = $this->getIncome()->sortByDesc('transaction_amount'); | ||||
|  | ||||
|         return $transactions; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $collection | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function summarizeByAccount(Collection $collection): array | ||||
|     { | ||||
|         $result = []; | ||||
|         /** @var Transaction $transaction */ | ||||
|         foreach ($collection as $transaction) { | ||||
|             $accountId          = $transaction->account_id; | ||||
|             $result[$accountId] = $result[$accountId] ?? '0'; | ||||
|             $result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $collection | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function summarizeByCategory(Collection $collection): array | ||||
|     { | ||||
|         $result = []; | ||||
|         /** @var Transaction $transaction */ | ||||
|         foreach ($collection as $transaction) { | ||||
|             $jrnlCatId           = intval($transaction->transaction_journal_category_id); | ||||
|             $transCatId          = intval($transaction->transaction_category_id); | ||||
|             $categoryId          = max($jrnlCatId, $transCatId); | ||||
|             $result[$categoryId] = $result[$categoryId] ?? '0'; | ||||
|             $result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								app/Generator/Report/Category/MultiYearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								app/Generator/Report/Category/MultiYearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| <?php | ||||
| /** | ||||
|  * MultiYearReportGenerator.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report\Category; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class MultiYearReportGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report\Audit | ||||
|  */ | ||||
| class MultiYearReportGenerator extends MonthReportGenerator | ||||
| { | ||||
|     /** | ||||
|      * Doesn't do anything different. | ||||
|      */ | ||||
| } | ||||
							
								
								
									
										28
									
								
								app/Generator/Report/Category/YearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/Generator/Report/Category/YearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
| /** | ||||
|  * YearReportGenerator.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report\Category; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class YearReportGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report\Audit | ||||
|  */ | ||||
| class YearReportGenerator extends MonthReportGenerator | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Doesn't do anything different. | ||||
|      */ | ||||
| } | ||||
							
								
								
									
										60
									
								
								app/Generator/Report/ReportGeneratorFactory.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								app/Generator/Report/ReportGeneratorFactory.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ReportGeneratorFactory.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
|  | ||||
| /** | ||||
|  * Class ReportGeneratorFactory | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report | ||||
|  */ | ||||
| class ReportGeneratorFactory | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param string $type | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public static function reportGenerator(string $type, Carbon $start, Carbon $end): ReportGeneratorInterface | ||||
|     { | ||||
|         $period = 'Month'; | ||||
|         // more than two months date difference means year report. | ||||
|         if ($start->diffInMonths($end) > 1) { | ||||
|             $period = 'Year'; | ||||
|         } | ||||
|  | ||||
|         // more than one year date difference means multi year report. | ||||
|         if ($start->diffInMonths($end) > 12) { | ||||
|             $period = 'MultiYear'; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         $class = sprintf('FireflyIII\Generator\Report\%s\%sReportGenerator', $type, $period); | ||||
|         if (class_exists($class)) { | ||||
|             /** @var ReportGeneratorInterface $obj */ | ||||
|             $obj = new $class; | ||||
|             $obj->setStartDate($start); | ||||
|             $obj->setEndDate($end); | ||||
|  | ||||
|             return $obj; | ||||
|         } | ||||
|         throw new FireflyException(sprintf('Cannot generate report. There is no "%s"-report for period "%s".', $type, $period)); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										67
									
								
								app/Generator/Report/ReportGeneratorInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								app/Generator/Report/ReportGeneratorInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ReportGeneratorInterface.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface ReportGeneratorInterface | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report | ||||
|  */ | ||||
| interface ReportGeneratorInterface | ||||
| { | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function generate(): string; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setAccounts(Collection $accounts): ReportGeneratorInterface; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setBudgets(Collection $budgets): ReportGeneratorInterface; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setCategories(Collection $categories): ReportGeneratorInterface; | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setEndDate(Carbon $date): ReportGeneratorInterface; | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setStartDate(Carbon $date): ReportGeneratorInterface; | ||||
|  | ||||
| } | ||||
							
								
								
									
										109
									
								
								app/Generator/Report/Standard/MonthReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								app/Generator/Report/Standard/MonthReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| <?php | ||||
| /** | ||||
|  * MonthReportGenerator.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report\Standard; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Generator\Report\ReportGeneratorInterface; | ||||
| use FireflyIII\Helpers\Report\ReportHelperInterface; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class MonthReportGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report\Standard | ||||
|  */ | ||||
| class MonthReportGenerator implements ReportGeneratorInterface | ||||
| { | ||||
|     /** @var  Collection */ | ||||
|     private $accounts; | ||||
|     /** @var  Carbon */ | ||||
|     private $end; | ||||
|     /** @var  Carbon */ | ||||
|     private $start; | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function generate(): string | ||||
|     { | ||||
|         /** @var ReportHelperInterface $helper */ | ||||
|         $helper     = app(ReportHelperInterface::class); | ||||
|         $bills      = $helper->getBillReport($this->start, $this->end, $this->accounts); | ||||
|         $accountIds = join(',', $this->accounts->pluck('id')->toArray()); | ||||
|         $reportType = 'default'; | ||||
|  | ||||
|         // continue! | ||||
|         return view( | ||||
|             'reports.default.month', | ||||
|             compact('bills', 'accountIds', 'reportType') | ||||
|         )->with('start', $this->start)->with('end', $this->end)->render(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setAccounts(Collection $accounts): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->accounts = $accounts; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setBudgets(Collection $budgets): ReportGeneratorInterface | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setCategories(Collection $categories): ReportGeneratorInterface | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setEndDate(Carbon $date): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->end = $date; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setStartDate(Carbon $date): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->start = $date; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										106
									
								
								app/Generator/Report/Standard/MultiYearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								app/Generator/Report/Standard/MultiYearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| <?php | ||||
| /** | ||||
|  * MultiYearReportGenerator.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report\Standard; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Generator\Report\ReportGeneratorInterface; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class MonthReportGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report\Standard | ||||
|  */ | ||||
| class MultiYearReportGenerator implements ReportGeneratorInterface | ||||
| { | ||||
|     /** @var  Collection */ | ||||
|     private $accounts; | ||||
|     /** @var  Carbon */ | ||||
|     private $end; | ||||
|     /** @var  Carbon */ | ||||
|     private $start; | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function generate(): string | ||||
|     { | ||||
|         // and some id's, joined: | ||||
|         $accountIds = join(',', $this->accounts->pluck('id')->toArray()); | ||||
|         $reportType = 'default'; | ||||
|  | ||||
|         // continue! | ||||
|         return view( | ||||
|             'reports.default.multi-year', | ||||
|             compact('accountIds', 'reportType') | ||||
|         )->with('start', $this->start)->with('end', $this->end)->render(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setAccounts(Collection $accounts): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->accounts = $accounts; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setBudgets(Collection $budgets): ReportGeneratorInterface | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setCategories(Collection $categories): ReportGeneratorInterface | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setEndDate(Carbon $date): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->end = $date; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setStartDate(Carbon $date): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->start = $date; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										106
									
								
								app/Generator/Report/Standard/YearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								app/Generator/Report/Standard/YearReportGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| <?php | ||||
| /** | ||||
|  * YearReportGenerator.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report\Standard; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Generator\Report\ReportGeneratorInterface; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class MonthReportGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report\Standard | ||||
|  */ | ||||
| class YearReportGenerator implements ReportGeneratorInterface | ||||
| { | ||||
|     /** @var  Collection */ | ||||
|     private $accounts; | ||||
|     /** @var  Carbon */ | ||||
|     private $end; | ||||
|     /** @var  Carbon */ | ||||
|     private $start; | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function generate(): string | ||||
|     { | ||||
|         // and some id's, joined: | ||||
|         $accountIds = join(',', $this->accounts->pluck('id')->toArray()); | ||||
|         $reportType = 'default'; | ||||
|  | ||||
|         // continue! | ||||
|         return view( | ||||
|             'reports.default.year', | ||||
|             compact('accountIds', 'reportType') | ||||
|         )->with('start', $this->start)->with('end', $this->end)->render(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setAccounts(Collection $accounts): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->accounts = $accounts; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setBudgets(Collection $budgets): ReportGeneratorInterface | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setCategories(Collection $categories): ReportGeneratorInterface | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setEndDate(Carbon $date): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->end = $date; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return ReportGeneratorInterface | ||||
|      */ | ||||
|     public function setStartDate(Carbon $date): ReportGeneratorInterface | ||||
|     { | ||||
|         $this->start = $date; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										83
									
								
								app/Generator/Report/Support.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								app/Generator/Report/Support.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Support.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report; | ||||
|  | ||||
| use FireflyIII\Models\Transaction; | ||||
| use Illuminate\Support\Collection; | ||||
| use Log; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class Support | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Report\Category | ||||
|  */ | ||||
| class Support | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $collection | ||||
|      * @param array      $accounts | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public static function filterExpenses(Collection $collection, array $accounts): Collection | ||||
|     { | ||||
|         return self::filterTransactions($collection, $accounts, 1); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $collection | ||||
|      * @param array      $accounts | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public static function filterIncome(Collection $collection, array $accounts): Collection | ||||
|     { | ||||
|         return self::filterTransactions($collection, $accounts, -1); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $collection | ||||
|      * @param array      $accounts | ||||
|      * @param int        $modifier | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public static function filterTransactions(Collection $collection, array $accounts, int $modifier): Collection | ||||
|     { | ||||
|         $result = $collection->filter( | ||||
|             function (Transaction $transaction) use ($accounts, $modifier) { | ||||
|                 $opposing = $transaction->opposing_account_id; | ||||
|                 // remove internal transfer | ||||
|                 if (in_array($opposing, $accounts)) { | ||||
|                     Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id)); | ||||
|  | ||||
|                     return null; | ||||
|                 } | ||||
|                 // remove positive amount | ||||
|                 if (bccomp($transaction->transaction_amount, '0') === $modifier) { | ||||
|                     Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount)); | ||||
|  | ||||
|                     return null; | ||||
|                 } | ||||
|  | ||||
|                 return $transaction; | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,51 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * AttachUserRole.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
|  | ||||
| use FireflyIII\Events\UserRegistration; | ||||
| use FireflyIII\Repositories\User\UserRepositoryInterface; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class AttachUserRole | ||||
|  * | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class AttachUserRole | ||||
| { | ||||
|     /** | ||||
|      * Create the event listener. | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handle the event. | ||||
|      * | ||||
|      * @param  UserRegistration $event | ||||
|      */ | ||||
|     public function handle(UserRegistration $event) | ||||
|     { | ||||
|         Log::debug('Trigger attachuserrole'); | ||||
|         /** @var UserRepositoryInterface $repository */ | ||||
|         $repository = app('FireflyIII\Repositories\User\UserRepositoryInterface'); | ||||
|  | ||||
|         // first user ever? | ||||
|         if ($repository->count() == 1) { | ||||
|             Log::debug('Will attach role.'); | ||||
|             $repository->attachRole($event->user, 'owner'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,62 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Events\TransactionJournalStored; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Models\PiggyBankEvent; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
|  | ||||
| /** | ||||
|  * Class ConnectJournalToPiggyBank | ||||
|  * | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class ConnectJournalToPiggyBank | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Connect a new transaction journal to any related piggy banks. | ||||
|      * | ||||
|      * @param  TransactionJournalStored $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function handle(TransactionJournalStored $event): bool | ||||
|     { | ||||
|         /** @var TransactionJournal $journal */ | ||||
|         $journal     = $event->journal; | ||||
|         $piggyBankId = $event->piggyBankId; | ||||
|  | ||||
|         /** @var PiggyBank $piggyBank */ | ||||
|         $piggyBank = Auth::user()->piggybanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']); | ||||
|  | ||||
|         if (is_null($piggyBank)) { | ||||
|             return true; | ||||
|         } | ||||
|         // update piggy bank rep for date of transaction journal. | ||||
|         $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); | ||||
|         if (is_null($repetition)) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         $amount = TransactionJournal::amountPositive($journal); | ||||
|         // if piggy account matches source account, the amount is positive | ||||
|         if ($piggyBank->account_id == TransactionJournal::sourceAccount($journal)->id) { | ||||
|             $amount = bcmul($amount, '-1'); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         $repetition->currentamount = bcadd($repetition->currentamount, $amount); | ||||
|         $repetition->save(); | ||||
|  | ||||
|         PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]); | ||||
|  | ||||
|         return true; | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -1,69 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * FireRulesForStore.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
|  | ||||
| use FireflyIII\Events\TransactionJournalStored; | ||||
| use FireflyIII\Models\Rule; | ||||
| use FireflyIII\Models\RuleGroup; | ||||
| use FireflyIII\Rules\Processor; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Support\Facades\Auth; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class FireRulesForStore | ||||
|  * | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class FireRulesForStore | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Connect a new transaction journal to any related piggy banks. | ||||
|      * | ||||
|      * @param  TransactionJournalStored $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function handle(TransactionJournalStored $event): bool | ||||
|     { | ||||
|         // get all the user's rule groups, with the rules, order by 'order'. | ||||
|         /** @var User $user */ | ||||
|         $user   = Auth::user(); | ||||
|         $groups = $user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get(); | ||||
|         // | ||||
|         /** @var RuleGroup $group */ | ||||
|         foreach ($groups as $group) { | ||||
|             Log::debug('Now processing group "' . $group->title . '".'); | ||||
|             $rules = $group->rules() | ||||
|                            ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id') | ||||
|                            ->where('rule_triggers.trigger_type', 'user_action') | ||||
|                            ->where('rule_triggers.trigger_value', 'store-journal') | ||||
|                            ->where('rules.active', 1) | ||||
|                            ->get(['rules.*']); | ||||
|             /** @var Rule $rule */ | ||||
|             foreach ($rules as $rule) { | ||||
|  | ||||
|                 Log::debug('Now handling rule #' . $rule->id . ' (' . $rule->title . ')'); | ||||
|                 $processor = Processor::make($rule); | ||||
|                 $processor->handleTransactionJournal($event->journal); | ||||
|  | ||||
|                 if ($rule->stop_processing) { | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @@ -1,68 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * FireRulesForUpdate.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Events\TransactionJournalUpdated; | ||||
| use FireflyIII\Models\Rule; | ||||
| use FireflyIII\Models\RuleGroup; | ||||
| use FireflyIII\Rules\Processor; | ||||
| use FireflyIII\User; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class FireRulesForUpdate | ||||
|  * | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class FireRulesForUpdate | ||||
| { | ||||
|     /** | ||||
|      * Handle the event. | ||||
|      * | ||||
|      * @param  TransactionJournalUpdated $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function handle(TransactionJournalUpdated $event): bool | ||||
|     { | ||||
|         // get all the user's rule groups, with the rules, order by 'order'. | ||||
|         /** @var User $user */ | ||||
|         $user   = Auth::user(); | ||||
|         $groups = $user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get(); | ||||
|         // | ||||
|         /** @var RuleGroup $group */ | ||||
|         foreach ($groups as $group) { | ||||
|             Log::debug('Now processing group "' . $group->title . '".'); | ||||
|             $rules = $group->rules() | ||||
|                            ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id') | ||||
|                            ->where('rule_triggers.trigger_type', 'user_action') | ||||
|                            ->where('rule_triggers.trigger_value', 'update-journal') | ||||
|                            ->where('rules.active', 1) | ||||
|                            ->get(['rules.*']); | ||||
|             /** @var Rule $rule */ | ||||
|             foreach ($rules as $rule) { | ||||
|                 Log::debug('Now handling rule #' . $rule->id . ' (' . $rule->title . ')'); | ||||
|  | ||||
|                 Log::debug('Now handling rule #' . $rule->id . ' (' . $rule->title . ')'); | ||||
|                 $processor = Processor::make($rule); | ||||
|                 $processor->handleTransactionJournal($event->journal); | ||||
|  | ||||
|                 if ($rule->stop_processing) { | ||||
|                     break; | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @@ -1,40 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * RescanJournalAfterStore.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
| use FireflyIII\Events\TransactionJournalStored; | ||||
| use FireflyIII\Support\Events\BillScanner; | ||||
|  | ||||
| /** | ||||
|  * Class RescanJournal | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class ScanForBillsAfterStore | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Scan a transaction journal for possible links to bills, right after storing. | ||||
|      * | ||||
|      * @param  TransactionJournalStored $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function handle(TransactionJournalStored $event): bool | ||||
|     { | ||||
|         $journal = $event->journal; | ||||
|         BillScanner::scan($journal); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,39 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| /** | ||||
|  * ScanForBillsAfterUpdate.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
| use FireflyIII\Events\TransactionJournalUpdated; | ||||
| use FireflyIII\Support\Events\BillScanner; | ||||
|  | ||||
| /** | ||||
|  * Class RescanJournal | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class ScanForBillsAfterUpdate | ||||
| { | ||||
|     /** | ||||
|      * Scan a transaction journal for possibly related bills after it has been updated. | ||||
|      * | ||||
|      * @param  TransactionJournalUpdated $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function handle(TransactionJournalUpdated $event): bool | ||||
|     { | ||||
|         $journal = $event->journal; | ||||
|         BillScanner::scan($journal); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,63 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * SendRegistrationMail.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
|  | ||||
| use FireflyIII\Events\UserRegistration; | ||||
| use Illuminate\Mail\Message; | ||||
| use Log; | ||||
| use Mail; | ||||
| use Swift_TransportException; | ||||
|  | ||||
| /** | ||||
|  * Class SendRegistrationMail | ||||
|  * | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class SendRegistrationMail | ||||
| { | ||||
|     /** | ||||
|      * Create the event listener. | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handle the event. | ||||
|      * | ||||
|      * @param  UserRegistration $event | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function handle(UserRegistration $event) | ||||
|     { | ||||
|         $sendMail = env('SEND_REGISTRATION_MAIL', true); | ||||
|         if (!$sendMail) { | ||||
|             return; | ||||
|         } | ||||
|         // get the email address | ||||
|         $email     = $event->user->email; | ||||
|         $address   = route('index'); | ||||
|         $ipAddress = $event->ipAddress; | ||||
|         // send email. | ||||
|         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()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										203
									
								
								app/Handlers/Events/StoredJournalEventHandler.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								app/Handlers/Events/StoredJournalEventHandler.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,203 @@ | ||||
| <?php | ||||
| /** | ||||
|  * StoredJournalEventHandler.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
| use FireflyIII\Events\StoredTransactionJournal; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Models\PiggyBankEvent; | ||||
| use FireflyIII\Models\PiggyBankRepetition; | ||||
| use FireflyIII\Models\Rule; | ||||
| use FireflyIII\Models\RuleGroup; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use FireflyIII\Rules\Processor; | ||||
| use FireflyIII\Support\Events\BillScanner; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class StoredJournalEventHandler | ||||
|  * | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class StoredJournalEventHandler | ||||
| { | ||||
|     /** | ||||
|      * This method connects a new transfer to a piggy bank. | ||||
|      * | ||||
|      * @param StoredTransactionJournal $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function connectToPiggyBank(StoredTransactionJournal $event): bool | ||||
|     { | ||||
|         /** @var TransactionJournal $journal */ | ||||
|         $journal     = $event->journal; | ||||
|         $piggyBankId = $event->piggyBankId; | ||||
|         Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId)); | ||||
|  | ||||
|         /* | ||||
|          * Verify existence of piggy bank: | ||||
|          */ | ||||
|         if (!$this->verifyExistence($event)) { | ||||
|             Log::error(sprintf('No such piggy bank or no repetition on %s', $journal->date->format('Y-m-d'))); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Get relevant data: | ||||
|          */ | ||||
|         $piggyBank                 = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']); | ||||
|         $repetition                = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); | ||||
|         $amount                    = $this->getExactAmount($journal, $piggyBank, $repetition); | ||||
|         $repetition->currentamount = bcadd($repetition->currentamount, $amount); | ||||
|         $repetition->save(); | ||||
|  | ||||
|         /** @var PiggyBankEvent $event */ | ||||
|         $event = PiggyBankEvent::create( | ||||
|             ['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount] | ||||
|         ); | ||||
|         Log::debug(sprintf('Created piggy bank event #%d', $event->id)); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method grabs all the users rules and processes them. | ||||
|      * | ||||
|      * @param StoredTransactionJournal $storedJournalEvent | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function processRules(StoredTransactionJournal $storedJournalEvent): bool | ||||
|     { | ||||
|         // get all the user's rule groups, with the rules, order by 'order'. | ||||
|         $journal = $storedJournalEvent->journal; | ||||
|         $groups  = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get(); | ||||
|         // | ||||
|         /** @var RuleGroup $group */ | ||||
|         foreach ($groups as $group) { | ||||
|             $rules = $group->rules() | ||||
|                            ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id') | ||||
|                            ->where('rule_triggers.trigger_type', 'user_action') | ||||
|                            ->where('rule_triggers.trigger_value', 'store-journal') | ||||
|                            ->where('rules.active', 1) | ||||
|                            ->get(['rules.*']); | ||||
|             /** @var Rule $rule */ | ||||
|             foreach ($rules as $rule) { | ||||
|  | ||||
|                 $processor = Processor::make($rule); | ||||
|                 $processor->handleTransactionJournal($journal); | ||||
|  | ||||
|                 if ($rule->stop_processing) { | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method calls a special bill scanner that will check if the stored journal is part of a bill. | ||||
|      * | ||||
|      * @param StoredTransactionJournal $storedJournalEvent | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function scanBills(StoredTransactionJournal $storedJournalEvent): bool | ||||
|     { | ||||
|         $journal = $storedJournalEvent->journal; | ||||
|         BillScanner::scan($journal); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 6 but I can live with it. | ||||
|      * @param TransactionJournal  $journal | ||||
|      * @param PiggyBank           $piggyBank | ||||
|      * @param PiggyBankRepetition $repetition | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     private function getExactAmount(TransactionJournal $journal, PiggyBank $piggyBank, PiggyBankRepetition $repetition): string | ||||
|     { | ||||
|         $amount  = TransactionJournal::amountPositive($journal); | ||||
|         $sources = TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray(); | ||||
|         $room    = bcsub(strval($piggyBank->targetamount), strval($repetition->currentamount)); | ||||
|         $compare = bcmul($repetition->currentamount, '-1'); | ||||
|  | ||||
|         Log::debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); | ||||
|  | ||||
|         // if piggy account matches source account, the amount is positive | ||||
|         if (in_array($piggyBank->account_id, $sources)) { | ||||
|             $amount = bcmul($amount, '-1'); | ||||
|             Log::debug(sprintf('Account #%d is the source, so will remove amount from piggy bank.', $piggyBank->account_id)); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // if the amount is positive, make sure it fits in piggy bank: | ||||
|         if (bccomp($amount, '0') === 1 && bccomp($room, $amount) === -1) { | ||||
|             // amount is positive and $room is smaller than $amount | ||||
|             Log::debug(sprintf('Room in piggy bank for extra money is %f', $room)); | ||||
|             Log::debug(sprintf('There is NO room to add %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); | ||||
|             Log::debug(sprintf('New amount is %f', $room)); | ||||
|  | ||||
|             return $room; | ||||
|         } | ||||
|  | ||||
|         // amount is negative and $currentamount is smaller than $amount | ||||
|         if (bccomp($amount, '0') === -1 && bccomp($compare, $amount) === 1) { | ||||
|             Log::debug(sprintf('Max amount to remove is %f', $repetition->currentamount)); | ||||
|             Log::debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); | ||||
|             Log::debug(sprintf('New amount is %f', $compare)); | ||||
|  | ||||
|             return $compare; | ||||
|         } | ||||
|  | ||||
|         return $amount; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param StoredTransactionJournal $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function verifyExistence(StoredTransactionJournal $event): bool | ||||
|     { | ||||
|         /** @var TransactionJournal $journal */ | ||||
|         $journal     = $event->journal; | ||||
|         $piggyBankId = $event->piggyBankId; | ||||
|  | ||||
|         /** @var PiggyBank $piggyBank */ | ||||
|         $piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']); | ||||
|  | ||||
|         if (is_null($piggyBank)) { | ||||
|             Log::error('No such piggy bank!'); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|         Log::debug(sprintf('Found piggy bank #%d: "%s"', $piggyBank->id, $piggyBank->name)); | ||||
|         // update piggy bank rep for date of transaction journal. | ||||
|         $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); | ||||
|         if (is_null($repetition)) { | ||||
|             Log::error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d'))); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @@ -1,60 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
| use FireflyIII\Events\TransactionJournalUpdated; | ||||
| use FireflyIII\Models\PiggyBankEvent; | ||||
| use FireflyIII\Models\PiggyBankRepetition; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
|  | ||||
| /** | ||||
|  * Class UpdateJournalConnection | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class UpdateJournalConnection | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Handle the event. | ||||
|      * | ||||
|      * @param  TransactionJournalUpdated $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function handle(TransactionJournalUpdated $event):bool | ||||
|     { | ||||
|         $journal = $event->journal; | ||||
|  | ||||
|         // get the event connected to this journal: | ||||
|         /** @var PiggyBankEvent $event */ | ||||
|         $event = PiggyBankEvent::where('transaction_journal_id', $journal->id)->first(); | ||||
|         if (is_null($event)) { | ||||
|             return false; | ||||
|         } | ||||
|         $piggyBank  = $event->piggyBank()->first(); | ||||
|         $repetition = null; | ||||
|         if ($piggyBank) { | ||||
|             /** @var PiggyBankRepetition $repetition */ | ||||
|             $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); | ||||
|         } | ||||
|  | ||||
|         if (is_null($repetition)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         $amount = TransactionJournal::amount($journal); | ||||
|         $diff   = bcsub($amount, $event->amount); // update current repetition | ||||
|  | ||||
|         $repetition->currentamount = bcadd($repetition->currentamount, $diff); | ||||
|         $repetition->save(); | ||||
|  | ||||
|  | ||||
|         $event->amount = $amount; | ||||
|         $event->save(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										81
									
								
								app/Handlers/Events/UpdatedJournalEventHandler.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								app/Handlers/Events/UpdatedJournalEventHandler.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| <?php | ||||
| /** | ||||
|  * UpdatedJournalEventHandler.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
|  | ||||
| use FireflyIII\Events\UpdatedTransactionJournal; | ||||
| use FireflyIII\Models\Rule; | ||||
| use FireflyIII\Models\RuleGroup; | ||||
| use FireflyIII\Rules\Processor; | ||||
| use FireflyIII\Support\Events\BillScanner; | ||||
|  | ||||
| /** | ||||
|  * Class UpdatedJournalEventHandler | ||||
|  * | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class UpdatedJournalEventHandler | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * This method will check all the rules when a journal is updated. | ||||
|      * | ||||
|      * @param UpdatedTransactionJournal $updatedJournalEvent | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function processRules(UpdatedTransactionJournal $updatedJournalEvent): bool | ||||
|     { | ||||
|         // get all the user's rule groups, with the rules, order by 'order'. | ||||
|         $journal = $updatedJournalEvent->journal; | ||||
|         $groups  = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get(); | ||||
|         // | ||||
|         /** @var RuleGroup $group */ | ||||
|         foreach ($groups as $group) { | ||||
|             $rules = $group->rules() | ||||
|                            ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id') | ||||
|                            ->where('rule_triggers.trigger_type', 'user_action') | ||||
|                            ->where('rule_triggers.trigger_value', 'update-journal') | ||||
|                            ->where('rules.active', 1) | ||||
|                            ->get(['rules.*']); | ||||
|             /** @var Rule $rule */ | ||||
|             foreach ($rules as $rule) { | ||||
|                 $processor = Processor::make($rule); | ||||
|                 $processor->handleTransactionJournal($journal); | ||||
|  | ||||
|                 if ($rule->stop_processing) { | ||||
|                     break; | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method calls a special bill scanner that will check if the updated journal is part of a bill. | ||||
|      * | ||||
|      * @param UpdatedTransactionJournal $updatedJournalEvent | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function scanBills(UpdatedTransactionJournal $updatedJournalEvent): bool | ||||
|     { | ||||
|         $journal = $updatedJournalEvent->journal; | ||||
|         BillScanner::scan($journal); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @@ -1,114 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * UserConfirmation.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
|  | ||||
| use Exception; | ||||
| use FireflyIII\Events\ResendConfirmation; | ||||
| use FireflyIII\Events\UserRegistration; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Mail\Message; | ||||
| use Log; | ||||
| use Mail; | ||||
| use Preferences; | ||||
| use Swift_TransportException; | ||||
|  | ||||
| /** | ||||
|  * Class UserConfirmation | ||||
|  * | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class UserConfirmation | ||||
| { | ||||
|     /** | ||||
|      * Create the event listener. | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param ResendConfirmation $event | ||||
|      */ | ||||
|     public function resendConfirmation(ResendConfirmation $event) | ||||
|     { | ||||
|         $user      = $event->user; | ||||
|         $ipAddress = $event->ipAddress; | ||||
|         $this->doConfirm($user, $ipAddress); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handle the event. | ||||
|      * | ||||
|      * @param  UserRegistration $event | ||||
|      */ | ||||
|     public function sendConfirmation(UserRegistration $event) | ||||
|     { | ||||
|         $user      = $event->user; | ||||
|         $ipAddress = $event->ipAddress; | ||||
|         $this->doConfirm($user, $ipAddress); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param User   $user | ||||
|      * @param string $ipAddress | ||||
|      */ | ||||
|     private function doConfirm(User $user, string $ipAddress) | ||||
|     { | ||||
|         Log::debug('Trigger UserConfirmation::doConfirm'); | ||||
|  | ||||
|         // if user must confirm account, send email | ||||
|         $confirmAccount = env('MUST_CONFIRM_ACCOUNT', false); | ||||
|  | ||||
|         // otherwise, auto-confirm: | ||||
|         if ($confirmAccount === false) { | ||||
|             Log::debug('Confirm account is false, so user will be auto-confirmed.'); | ||||
|             Preferences::setForUser($user, 'user_confirmed', true); | ||||
|             Preferences::setForUser($user, 'user_confirmed_last_mail', 0); | ||||
|             Preferences::mark(); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // send email message: | ||||
|         $email = $user->email; | ||||
|         $code  = str_random(16); | ||||
|         $route = route('do_confirm_account', [$code]); | ||||
|  | ||||
|         // set preferences: | ||||
|         Preferences::setForUser($user, 'user_confirmed', false); | ||||
|         Preferences::setForUser($user, 'user_confirmed_last_mail', time()); | ||||
|         Preferences::setForUser($user, 'user_confirmed_code', $code); | ||||
|         Log::debug('Set preferences for user.'); | ||||
|  | ||||
|         // send email. | ||||
|         try { | ||||
|             Log::debug('Now in try block for user email message thing to ' . $email . '.'); | ||||
|             Mail::send( | ||||
|                 ['emails.confirm-account-html', 'emails.confirm-account'], ['route' => $route, 'ip' => $ipAddress], | ||||
|                 function (Message $message) use ($email) { | ||||
|                     $message->to($email, $email)->subject('Please confirm your Firefly III account'); | ||||
|                 } | ||||
|             ); | ||||
|         } catch (Swift_TransportException $e) { | ||||
|  | ||||
|             Log::error($e->getMessage()); | ||||
|         } catch (Exception $e) { | ||||
|             Log::debug('Caught general exception.'); | ||||
|             Log::error($e->getMessage()); | ||||
|         } | ||||
|         Log::debug('Finished mail handling for activation.'); | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										130
									
								
								app/Handlers/Events/UserEventHandler.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								app/Handlers/Events/UserEventHandler.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| <?php | ||||
| /** | ||||
|  * UserEventHandler.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
| use FireflyIII\Events\RegisteredUser; | ||||
| use FireflyIII\Events\RequestedNewPassword; | ||||
| use FireflyIII\Repositories\User\UserRepositoryInterface; | ||||
| use Illuminate\Mail\Message; | ||||
| use Log; | ||||
| use Mail; | ||||
| use Session; | ||||
| use Swift_TransportException; | ||||
|  | ||||
| /** | ||||
|  * Class UserEventHandler | ||||
|  * | ||||
|  * This class responds to any events that have anything to do with the User object. | ||||
|  * | ||||
|  * The method name reflects what is being done. This is in the present tense. | ||||
|  * | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class UserEventHandler | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * This method will bestow upon a user the "owner" role if he is the first user in the system. | ||||
|      * | ||||
|      * @param RegisteredUser $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function attachUserRole(RegisteredUser $event): bool | ||||
|     { | ||||
|         /** @var UserRepositoryInterface $repository */ | ||||
|         $repository = app(UserRepositoryInterface::class); | ||||
|  | ||||
|         // first user ever? | ||||
|         if ($repository->count() === 1) { | ||||
|             $repository->attachRole($event->user, 'owner'); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handle user logout events. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function logoutUser(): bool | ||||
|     { | ||||
|         // dump stuff from the session: | ||||
|         Session::forget('twofactor-authenticated'); | ||||
|         Session::forget('twofactor-authenticated-date'); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param RequestedNewPassword $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function sendNewPassword(RequestedNewPassword $event): bool | ||||
|     { | ||||
|         $email     = $event->user->email; | ||||
|         $ipAddress = $event->ipAddress; | ||||
|         $token     = $event->token; | ||||
|  | ||||
|         $url = route('password.reset', [$token]); | ||||
|  | ||||
|         // send email. | ||||
|         try { | ||||
|             Mail::send( | ||||
|                 ['emails.password-html', 'emails.password-text'], ['url' => $url, 'ip' => $ipAddress], function (Message $message) use ($email) { | ||||
|                 $message->to($email, $email)->subject('Your password reset request'); | ||||
|             } | ||||
|             ); | ||||
|         } catch (Swift_TransportException $e) { | ||||
|             Log::error($e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method will send the user a registration mail, welcoming him or her to Firefly III. | ||||
|      * This message is only sent when the configuration of Firefly III says so. | ||||
|      * | ||||
|      * @param RegisteredUser $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function sendRegistrationMail(RegisteredUser $event) | ||||
|     { | ||||
|  | ||||
|         $sendMail = env('SEND_REGISTRATION_MAIL', true); | ||||
|         if (!$sendMail) { | ||||
|             return true; | ||||
|         } | ||||
|         // get the email address | ||||
|         $email     = $event->user->email; | ||||
|         $address   = route('index'); | ||||
|         $ipAddress = $event->ipAddress; | ||||
|         // send email. | ||||
|         try { | ||||
|             Mail::send( | ||||
|                 ['emails.registered-html', 'emails.registered-text'], ['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()); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @@ -1,32 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * UserEventListener.php | ||||
|  * Copyright (C) 2016 Sander Dorigo | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms | ||||
|  * of the MIT license.  See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
| use Session; | ||||
|  | ||||
| /** | ||||
|  * Class UserEventListener | ||||
|  * | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class UserEventListener | ||||
| { | ||||
|     /** | ||||
|      * Handle user logout events. | ||||
|      */ | ||||
|     public function onUserLogout() | ||||
|     { | ||||
|         // dump stuff from the session: | ||||
|         Session::forget('twofactor-authenticated'); | ||||
|         Session::forget('twofactor-authenticated-date'); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @@ -1,18 +1,23 @@ | ||||
| <?php | ||||
| /** | ||||
|  * AttachmentHelper.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Attachments; | ||||
|  | ||||
| use Auth; | ||||
| use Config; | ||||
| use Crypt; | ||||
| use FireflyIII\Models\Attachment; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Support\MessageBag; | ||||
| use Input; | ||||
| use Log; | ||||
| use Storage; | ||||
| use Symfony\Component\HttpFoundation\File\UploadedFile; | ||||
| use TypeError; | ||||
|  | ||||
| /** | ||||
|  * Class AttachmentHelper | ||||
| @@ -27,9 +32,9 @@ class AttachmentHelper implements AttachmentHelperInterface | ||||
|     /** @var MessageBag */ | ||||
|     public $messages; | ||||
|     /** @var array */ | ||||
|     protected $allowedMimes; | ||||
|     protected $allowedMimes = []; | ||||
|     /** @var int */ | ||||
|     protected $maxUploadSize; | ||||
|     protected $maxUploadSize = 0; | ||||
|  | ||||
|     /** @var \Illuminate\Contracts\Filesystem\Filesystem */ | ||||
|     protected $uploadDisk; | ||||
| @@ -39,8 +44,8 @@ class AttachmentHelper implements AttachmentHelperInterface | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->maxUploadSize = Config::get('firefly.maxUploadSize'); | ||||
|         $this->allowedMimes  = Config::get('firefly.allowedMimes'); | ||||
|         $this->maxUploadSize = intval(config('firefly.maxUploadSize')); | ||||
|         $this->allowedMimes  = (array) config('firefly.allowedMimes'); | ||||
|         $this->errors        = new MessageBag; | ||||
|         $this->messages      = new MessageBag; | ||||
|         $this->uploadDisk    = Storage::disk('upload'); | ||||
| @@ -53,7 +58,7 @@ class AttachmentHelper implements AttachmentHelperInterface | ||||
|      */ | ||||
|     public function getAttachmentLocation(Attachment $attachment): string | ||||
|     { | ||||
|         $path = storage_path('upload') . DIRECTORY_SEPARATOR . 'at-' . $attachment->id . '.data'; | ||||
|         $path = sprintf('%s%sat-%d.data', storage_path('upload'), DIRECTORY_SEPARATOR, intval($attachment->id)); | ||||
|  | ||||
|         return $path; | ||||
|     } | ||||
| @@ -75,32 +80,19 @@ class AttachmentHelper implements AttachmentHelperInterface | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Model $model | ||||
|      * @param Model      $model | ||||
|      * @param array|null $files | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function saveAttachmentsForModel(Model $model): bool | ||||
|     public function saveAttachmentsForModel(Model $model, array $files = null): bool | ||||
|     { | ||||
|         $files = null; | ||||
|         try { | ||||
|             if (Input::hasFile('attachments')) { | ||||
|                 $files = Input::file('attachments'); | ||||
|             } | ||||
|         } catch (TypeError $e) { | ||||
|             // Log it, do nothing else. | ||||
|             Log::error($e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         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; | ||||
| @@ -117,7 +109,7 @@ class AttachmentHelper implements AttachmentHelperInterface | ||||
|         $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(); | ||||
|         $count = auth()->user()->attachments()->where('md5', $md5)->where('attachable_id', $model->id)->where('attachable_type', $class)->count(); | ||||
|  | ||||
|         if ($count > 0) { | ||||
|             $msg = (string)trans('validation.file_already_attached', ['name' => $name]); | ||||
| @@ -144,7 +136,7 @@ class AttachmentHelper implements AttachmentHelperInterface | ||||
|         } | ||||
|  | ||||
|         $attachment = new Attachment; // create Attachment object. | ||||
|         $attachment->user()->associate(Auth::user()); | ||||
|         $attachment->user()->associate(auth()->user()); | ||||
|         $attachment->attachable()->associate($model); | ||||
|         $attachment->md5      = md5_file($file->getRealPath()); | ||||
|         $attachment->filename = $file->getClientOriginalName(); | ||||
| @@ -233,6 +225,4 @@ class AttachmentHelper implements AttachmentHelperInterface | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,14 @@ | ||||
| <?php | ||||
| /** | ||||
|  * AttachmentHelperInterface.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Attachments; | ||||
|  | ||||
| @@ -36,6 +46,6 @@ interface AttachmentHelperInterface | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function saveAttachmentsForModel(Model $model): bool; | ||||
|     public function saveAttachmentsForModel(Model $model, array $files = null): bool; | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										278
									
								
								app/Helpers/Chart/MetaPieChart.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								app/Helpers/Chart/MetaPieChart.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,278 @@ | ||||
| <?php | ||||
| /** | ||||
|  * MetaPieChart.php | ||||
|  * Copyright (c) 2017 thegrumpydictator@gmail.com | ||||
|  * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Helpers\Chart; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Generator\Report\Support; | ||||
| use FireflyIII\Helpers\Collector\JournalCollectorInterface; | ||||
| use FireflyIII\Models\Transaction; | ||||
| use FireflyIII\Models\TransactionType; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; | ||||
| use FireflyIII\Repositories\Category\CategoryRepositoryInterface; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class MetaPieChart | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Chart | ||||
|  */ | ||||
| class MetaPieChart implements MetaPieChartInterface | ||||
| { | ||||
|     /** @var  Collection */ | ||||
|     protected $accounts; | ||||
|     /** @var  Collection */ | ||||
|     protected $budgets; | ||||
|     /** @var  Collection */ | ||||
|     protected $categories; | ||||
|     /** @var bool */ | ||||
|     protected $collectOtherObjects = false; | ||||
|     /** @var  Carbon */ | ||||
|     protected $end; | ||||
|     /** @var array */ | ||||
|     protected $grouping | ||||
|         = [ | ||||
|             'account'  => ['opposing_account_id'], | ||||
|             'budget'   => ['transaction_journal_budget_id', 'transaction_budget_id'], | ||||
|             'category' => ['transaction_journal_category_id', 'transaction_category_id'], | ||||
|         ]; | ||||
|  | ||||
|     /** @var array */ | ||||
|     protected $repositories | ||||
|         = [ | ||||
|             'account'  => AccountRepositoryInterface::class, | ||||
|             'budget'   => BudgetRepositoryInterface::class, | ||||
|             'category' => CategoryRepositoryInterface::class, | ||||
|         ]; | ||||
|  | ||||
|  | ||||
|     /** @var  Carbon */ | ||||
|     protected $start; | ||||
|     /** @var  string */ | ||||
|     protected $total = '0'; | ||||
|     /** @var  User */ | ||||
|     protected $user; | ||||
|  | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->accounts   = new Collection; | ||||
|         $this->budgets    = new Collection; | ||||
|         $this->categories = new Collection; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $direction | ||||
|      * @param string $group | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function generate(string $direction, string $group): array | ||||
|     { | ||||
|         $transactions = $this->getTransactions($direction); | ||||
|         $grouped      = $this->groupByFields($transactions, $this->grouping[$group]); | ||||
|         $chartData    = $this->organizeByType($group, $grouped); | ||||
|  | ||||
|         // also collect all other transactions | ||||
|         if ($this->collectOtherObjects && $direction === 'expense') { | ||||
|             /** @var JournalCollectorInterface $collector */ | ||||
|             $collector = app(JournalCollectorInterface::class, [$this->user]); | ||||
|             $collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::WITHDRAWAL]); | ||||
|             $journals                                            = $collector->getJournals(); | ||||
|             $sum                                                 = strval($journals->sum('transaction_amount')); | ||||
|             $sum                                                 = bcmul($sum, '-1'); | ||||
|             $sum                                                 = bcsub($sum, $this->total); | ||||
|             $chartData[strval(trans('firefly.everything_else'))] = $sum; | ||||
|         } | ||||
|  | ||||
|         if ($this->collectOtherObjects && $direction === 'income') { | ||||
|             /** @var JournalCollectorInterface $collector */ | ||||
|             $collector = app(JournalCollectorInterface::class, [auth()->user()]); | ||||
|             $collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::DEPOSIT]); | ||||
|             $journals                                            = $collector->getJournals(); | ||||
|             $sum                                                 = strval($journals->sum('transaction_amount')); | ||||
|             $sum                                                 = bcsub($sum, $this->total); | ||||
|             $chartData[strval(trans('firefly.everything_else'))] = $sum; | ||||
|         } | ||||
|  | ||||
|         return $chartData; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setAccounts(Collection $accounts): MetaPieChartInterface | ||||
|     { | ||||
|         $this->accounts = $accounts; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setBudgets(Collection $budgets): MetaPieChartInterface | ||||
|     { | ||||
|         $this->budgets = $budgets; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setCategories(Collection $categories): MetaPieChartInterface | ||||
|     { | ||||
|         $this->categories = $categories; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param bool $collectOtherObjects | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setCollectOtherObjects(bool $collectOtherObjects): MetaPieChartInterface | ||||
|     { | ||||
|         $this->collectOtherObjects = $collectOtherObjects; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setEnd(Carbon $end): MetaPieChartInterface | ||||
|     { | ||||
|         $this->end = $end; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $start | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setStart(Carbon $start): MetaPieChartInterface | ||||
|     { | ||||
|         $this->start = $start; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param User $user | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setUser(User $user): MetaPieChartInterface | ||||
|     { | ||||
|         $this->user = $user; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     protected function getTransactions(string $direction) | ||||
|     { | ||||
|         $types    = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; | ||||
|         $modifier = -1; | ||||
|         if ($direction === 'expense') { | ||||
|             $types    = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; | ||||
|             $modifier = 1; | ||||
|         } | ||||
|         /** @var JournalCollectorInterface $collector */ | ||||
|         $collector = app(JournalCollectorInterface::class, [auth()->user()]); | ||||
|         $collector->setAccounts($this->accounts); | ||||
|         $collector->setRange($this->start, $this->end); | ||||
|         $collector->setTypes($types); | ||||
|         $collector->withOpposingAccount(); | ||||
|  | ||||
|         if ($direction === 'income') { | ||||
|             $collector->disableFilter(); | ||||
|         } | ||||
|  | ||||
|         if ($this->budgets->count() > 0) { | ||||
|             $collector->setBudgets($this->budgets); | ||||
|         } | ||||
|         if ($this->categories->count() > 0) { | ||||
|             $collector->setCategories($this->categories); | ||||
|         } | ||||
|  | ||||
|         $accountIds   = $this->accounts->pluck('id')->toArray(); | ||||
|         $transactions = $collector->getJournals(); | ||||
|         $set          = Support::filterTransactions($transactions, $accountIds, $modifier); | ||||
|  | ||||
|         return $set; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $set | ||||
|      * @param array      $fields | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function groupByFields(Collection $set, array $fields) | ||||
|     { | ||||
|         $grouped = []; | ||||
|         /** @var Transaction $transaction */ | ||||
|         foreach ($set as $transaction) { | ||||
|             $values = []; | ||||
|             foreach ($fields as $field) { | ||||
|                 $values[] = intval($transaction->$field); | ||||
|             } | ||||
|             $value           = max($values); | ||||
|             $grouped[$value] = $grouped[$value] ?? '0'; | ||||
|             $grouped[$value] = bcadd($transaction->transaction_amount, $grouped[$value]); | ||||
|         } | ||||
|  | ||||
|         return $grouped; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $type | ||||
|      * @param array  $array | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function organizeByType(string $type, array $array): array | ||||
|     { | ||||
|         $chartData  = []; | ||||
|         $names      = []; | ||||
|         $repository = app($this->repositories[$type], [$this->user]); | ||||
|         foreach ($array as $objectId => $amount) { | ||||
|             if (!isset($names[$objectId])) { | ||||
|                 $object           = $repository->find(intval($objectId)); | ||||
|                 $names[$objectId] = $object->name; | ||||
|             } | ||||
|             if (bccomp($amount, '0') === -1) { | ||||
|                 $amount = bcmul($amount, '-1'); | ||||
|             } | ||||
|  | ||||
|             $this->total                  = bcadd($this->total, $amount); | ||||
|             $chartData[$names[$objectId]] = $amount; | ||||
|         } | ||||
|  | ||||
|         return $chartData; | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										82
									
								
								app/Helpers/Chart/MetaPieChartInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								app/Helpers/Chart/MetaPieChartInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| <?php | ||||
| /** | ||||
|  * MetaPieChartInterface.php | ||||
|  * Copyright (c) 2017 thegrumpydictator@gmail.com | ||||
|  * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Helpers\Chart; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface MetaPieChartInterface | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Chart | ||||
|  */ | ||||
| interface MetaPieChartInterface | ||||
| { | ||||
|     /** | ||||
|      * @param string $direction | ||||
|      * @param string $group | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function generate(string $direction, string $group): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setAccounts(Collection $accounts): MetaPieChartInterface; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setBudgets(Collection $budgets): MetaPieChartInterface; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $categories | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setCategories(Collection $categories): MetaPieChartInterface; | ||||
|  | ||||
|     /** | ||||
|      * @param bool $collectOtherObjects | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setCollectOtherObjects(bool $collectOtherObjects): MetaPieChartInterface; | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setEnd(Carbon $end): MetaPieChartInterface; | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $start | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setStart(Carbon $start): MetaPieChartInterface; | ||||
|  | ||||
|     /** | ||||
|      * @param User $user | ||||
|      * | ||||
|      * @return MetaPieChartInterface | ||||
|      */ | ||||
|     public function setUser(User $user): MetaPieChartInterface; | ||||
|  | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user