mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 02:36:28 +00:00 
			
		
		
		
	Compare commits
	
		
			2455 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 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 | ||
|  | 9250cee9e7 | ||
|  | bb075d15ff | ||
|  | a31f16bba7 | ||
|  | 88a41c37f3 | ||
|  | d21885ca98 | ||
|  | d774f8e870 | ||
|  | 08b5e66628 | ||
|  | 6fdfec3967 | ||
|  | ab4616a3ad | ||
|  | cb34ff4c83 | ||
|  | bdbead256c | ||
|  | f200086d01 | ||
|  | ef97c3b42d | ||
|  | ed8b41e8ec | ||
|  | aada3371b7 | ||
|  | 38a9782bdf | ||
|  | c6ac81dcf6 | ||
|  | 2d3f3f0fde | ||
|  | 993a2c7823 | ||
|  | 87b36cf7e3 | ||
|  | 742f2c8d9f | ||
|  | 0d7ac5f1d8 | ||
|  | 3adccff611 | ||
|  | 08b8bd27f9 | ||
|  | 1059c7e2be | ||
|  | 3ebcd5f738 | ||
|  | 4627ea1dec | ||
|  | 7ed24e78d5 | ||
|  | ae02e3fd8d | ||
|  | cc88d5962e | ||
|  | 70c8a524cd | ||
|  | 9a8efc8a58 | ||
|  | f0f67b87c4 | ||
|  | 1a3ec98896 | ||
|  | efff44cba9 | ||
|  | 7227418c4c | ||
|  | 29f93a9b73 | ||
|  | 532aa2acd0 | ||
|  | 6aca410f37 | ||
|  | f20656b516 | ||
|  | bcd7866e10 | ||
|  | c1b8d44209 | ||
|  | 50e947fbbe | ||
|  | 189d969ee6 | ||
|  | 201790ff8d | ||
|  | cc26ce4143 | ||
|  | 745e0aa525 | ||
|  | 6602b1587a | ||
|  | 6698b57f52 | ||
|  | 628268d47c | ||
|  | 56a635212a | ||
|  | 7e67eb17e0 | ||
|  | 935d72d672 | ||
|  | fccb510186 | ||
|  | e7ea0dc842 | ||
|  | a40d6b0649 | ||
|  | b7429a358f | ||
|  | cf8b4e2f76 | ||
|  | 48c865e57a | ||
|  | 059764cd23 | ||
|  | c205eee6fd | ||
|  | 7b22099608 | ||
|  | b56b42d6fb | ||
|  | ef6ef57e67 | ||
|  | 47d3cf1d45 | ||
|  | 9cfac9a884 | ||
|  | dc172476e1 | ||
|  | 6fc7763380 | ||
|  | ce45f21dba | ||
|  | 2de713c946 | ||
|  | 3d1dfe20b3 | ||
|  | 0404735ccb | ||
|  | 83f200f5a2 | ||
|  | a29eb9b993 | ||
|  | c939be97fb | ||
|  | f31e62a532 | ||
|  | 34c195159e | ||
|  | 322fef2db1 | ||
|  | 7aa2565e89 | ||
|  | 8f3572f2d3 | ||
|  | fb165ef28b | ||
|  | a26acf4a25 | ||
|  | 645400e17f | ||
|  | ccad52b80f | ||
|  | f6303deaa4 | ||
|  | d3a3083b85 | ||
|  | 99452056ec | ||
|  | fca21bab4d | ||
|  | 9c58b77f01 | ||
|  | b5c5f67fcc | ||
|  | 0f17423465 | ||
|  | f093e29bd1 | ||
|  | fe9b8e834d | ||
|  | 64f273120e | ||
|  | 31c1dd466b | ||
|  | 99b369bf45 | ||
|  | e2a1535c44 | ||
|  | 3dfa88020e | ||
|  | a220094941 | ||
|  | ababdacf89 | ||
|  | 114dd5fc2a | ||
|  | c058629172 | ||
|  | 79d7f577e4 | ||
|  | 631bafc6ab | ||
|  | be58da539e | ||
|  | a508ac9dda | ||
|  | 538fd2cef0 | ||
|  | 42557446a1 | ||
|  | b9586481fc | ||
|  | a0434e3271 | ||
|  | 15a5bcc21a | ||
|  | edb838045b | ||
|  | 41a2e657b1 | ||
|  | 18b80a69c2 | ||
|  | ce80fb39e8 | ||
|  | 2f19ff314b | ||
|  | a0fd4b505a | ||
|  | 919aa70251 | ||
|  | 1aea4045a3 | ||
|  | 4cded0bf57 | ||
|  | cd0585c7c4 | ||
|  | 8281279452 | ||
|  | 8f1bf846fe | ||
|  | c26bf557d0 | ||
|  | 499b92cdd1 | ||
|  | 3dce5e162f | ||
|  | f9de65c035 | ||
|  | 6f95e9a3cb | ||
|  | ce09da084c | ||
|  | 5d50f92dd3 | ||
|  | d054e085d6 | ||
|  | 59cdfa6fe6 | ||
|  | ee0e9011b2 | ||
|  | 654ebe0c48 | ||
|  | a40859c0bb | ||
|  | 74e3f6dee5 | ||
|  | e553bfb204 | ||
|  | 96caace068 | ||
|  | 5310ccebbe | ||
|  | ccd55257cd | ||
|  | 583687f3a7 | ||
|  | e06dc86bf7 | ||
|  | 8828aa0621 | ||
|  | e472e105f2 | ||
|  | 6ad10f1772 | ||
|  | fcf7d98834 | ||
|  | 90ad06c65c | ||
|  | 888e9beb4c | ||
|  | 69b338ca85 | ||
|  | 0c009445d4 | ||
|  | c5fb734e42 | ||
|  | d2c8475504 | ||
|  | f38d99653f | ||
|  | cdce43d226 | ||
|  | 89ab0a7e97 | ||
|  | c7250bfcba | ||
|  | 4a9b693da8 | ||
|  | fff35aa820 | ||
|  | a9a542d68f | ||
|  | 35ff3f0cbf | ||
|  | abf92b6df3 | ||
|  | 8517fc9f24 | ||
|  | 21b473108f | ||
|  | d4367f73a2 | ||
|  | 7ff46c3cd6 | ||
|  | 4a88e241b5 | ||
|  | 8da0317b19 | ||
|  | 297c2e244d | ||
|  | a51d752a35 | ||
|  | 7800b0a7f5 | ||
|  | 5ce4104644 | ||
|  | b4f1bbf793 | ||
|  | ca33bea6b7 | ||
|  | 42d20ff693 | ||
|  | 22a14416ad | ||
|  | 98bd9bdaa0 | ||
|  | 13bac92a24 | ||
|  | d9eb14d6e5 | ||
|  | 6ec0471e8b | ||
|  | 8008311d9c | ||
|  | fcf16051a2 | ||
|  | 3a236456cd | ||
|  | 2606d77c62 | ||
|  | 3d2f33c120 | ||
|  | 4f14969464 | ||
|  | 7234f011ec | ||
|  | 5b949d6e00 | ||
|  | 2fd476ada8 | ||
|  | 2e7703bc97 | ||
|  | 9b69a6addd | ||
|  | 500243b0b3 | ||
|  | 3ef84dc1fc | ||
|  | b04d68d087 | ||
|  | 715c381eb2 | ||
|  | ca32ae4561 | ||
|  | 6b277c5e67 | ||
|  | 1ac64fd0b3 | ||
|  | f2c1dd41d0 | ||
|  | 869360f26c | ||
|  | dbdc334931 | ||
|  | b317d1a171 | ||
|  | b00c7b0ce3 | ||
|  | a61eafeac2 | ||
|  | 5978b1c421 | ||
|  | 9f16799453 | ||
|  | 74fe0ee4e4 | ||
|  | e8880232b3 | ||
|  | 556e9f1df7 | ||
|  | beb301e781 | ||
|  | 056c809754 | ||
|  | fa47eac9ff | ||
|  | bf4a7846dd | ||
|  | b0cc6dd714 | ||
|  | d6e2d8e4a2 | ||
|  | bbfc962727 | ||
|  | 8ddb357e5a | ||
|  | 560c10898f | ||
|  | 04f5214bb7 | ||
|  | 4c35d52234 | ||
|  | 01be5e3e23 | ||
|  | 46dc4102e0 | ||
|  | cf31049c51 | ||
|  | c624c4342f | ||
|  | 701d7baca8 | ||
|  | 8e45959483 | ||
|  | 6858d67897 | ||
|  | 9b23cbd2c2 | ||
|  | ec1bb300e2 | ||
|  | 69a8cad47b | ||
|  | bcaca0eca3 | ||
|  | a1f79e58db | ||
|  | 2cc5fdcf62 | ||
|  | dd1b4e21f5 | ||
|  | 91eb052c22 | ||
|  | ecefcfabc0 | ||
|  | 8e42e71528 | ||
|  | a2275ae111 | ||
|  | 3976803d8f | ||
|  | 7921fc74fd | ||
|  | 1732acfee2 | ||
|  | 3a4fc65712 | ||
|  | 9ce9caba02 | ||
|  | 07f4995c8f | ||
|  | 8200c7248a | ||
|  | bd9f1d5398 | ||
|  | ce0ca23d79 | ||
|  | 35863c8d3a | ||
|  | dad73ce9df | ||
|  | f716692591 | ||
|  | 6387114a18 | ||
|  | 55ab39ca55 | ||
|  | 28a4f724d5 | ||
|  | f2d06bcea1 | ||
|  | 4d1771614a | ||
|  | 61efabb3b5 | ||
|  | b00458c2b9 | ||
|  | 594f9822c7 | ||
|  | 9b3131b95e | ||
|  | d7ad32a8cc | ||
|  | 59127e4029 | ||
|  | 7edf70a77b | ||
|  | 3fd90a37fb | ||
|  | 356ec276bc | ||
|  | ad8d945c1d | ||
|  | a3bf30a77b | ||
|  | 3f829a3114 | ||
|  | 537b4ae003 | ||
|  | 08b4c9ea5c | ||
|  | 6b00f5a97d | ||
|  | d259df9a47 | ||
|  | aeaebd082f | ||
|  | 0c9c0f2032 | ||
|  | 155801ab2b | ||
|  | deeeb06488 | ||
|  | 28fd719ce3 | ||
|  | 317075aa6d | ||
|  | eaf2efc510 | ||
|  | 88555bbea2 | ||
|  | a4d7bf4ebe | ||
|  | 50e5c21735 | ||
|  | 2ddacf48d4 | ||
|  | cd66d2c7b0 | ||
|  | 2e7c26c539 | ||
|  | f0f47530bf | ||
|  | e7be4e3e49 | ||
|  | 7a7ce7fcea | ||
|  | bd104a7ea8 | ||
|  | 31d6789ff0 | ||
|  | bdcb7372a5 | ||
|  | b8df2226ae | ||
|  | 106bae5c97 | ||
|  | 651297fa0b | ||
|  | 1b2873fc5f | ||
|  | f82a2b3bc5 | ||
|  | 4cb616ebeb | ||
|  | 1bcc975d7b | ||
|  | 98fd5b8858 | ||
|  | 6f08482aaf | ||
|  | f7d06b9759 | ||
|  | a178fed0c6 | ||
|  | ff342f6789 | ||
|  | b1c2f8faa1 | ||
|  | 6f59e79b28 | ||
|  | 6c22bad77a | ||
|  | 1cf0125d1b | ||
|  | 49211482b0 | ||
|  | b3aecec11d | ||
|  | 7cb86d0254 | ||
|  | 0ba9389ca2 | ||
|  | 1902787104 | ||
|  | b3e32db073 | ||
|  | 01d53bdb85 | ||
|  | 51acc34a80 | ||
|  | 694447e66c | ||
|  | 05b7a610ef | ||
|  | 1e799402ee | ||
|  | 5f9c61c4b4 | ||
|  | 12b6791e8b | ||
|  | 8024ad123e | ||
|  | 2bd2f5a5aa | ||
|  | 37d79b2a1c | ||
|  | cd057045e6 | ||
|  | ddfbd69e8b | ||
|  | 28fa1264b7 | ||
|  | a6c3189833 | ||
|  | 0d2fe08dc9 | ||
|  | 8cfe25bfac | ||
|  | f9e2a677d9 | ||
|  | 2ef1c54417 | ||
|  | f9c3c0c8ae | ||
|  | 44a15551ba | ||
|  | 732e181312 | ||
|  | 919187f7fd | ||
|  | 7840a5ea49 | ||
|  | 3a33ac1455 | ||
|  | 2eb7d8ba91 | ||
|  | 7ec5cce2c8 | ||
|  | 8fdc6c11e1 | ||
|  | 1a110de597 | ||
|  | 317aa591c3 | ||
|  | 0d44f82c86 | ||
|  | 9cac61dc33 | ||
|  | 52481a6e8b | ||
|  | b3e18f4e56 | ||
|  | aa25ac774e | ||
|  | af7da586aa | ||
|  | b671da900a | ||
|  | d8bb83e8c4 | ||
|  | bc3d64a2ff | ||
|  | 70e72c246d | ||
|  | 7c8c82edd7 | ||
|  | 5dc556f0af | ||
|  | c8c69f1a66 | ||
|  | 985019e117 | ||
|  | b9620b3a21 | ||
|  | e7bb4a8ec6 | ||
|  | bb5f935d7a | ||
|  | 17dad27610 | ||
|  | ee960d76c7 | ||
|  | dfb595193a | ||
|  | 0a91a40c1b | ||
|  | 12f4305691 | ||
|  | dd865b0dfb | ||
|  | 7cbfa9fcd4 | ||
|  | 28d880a7c4 | ||
|  | e83d45fce5 | ||
|  | 693ff3cc66 | ||
|  | 706b095f95 | ||
|  | 8e2e96d513 | ||
|  | 28bce44f69 | ||
|  | 53d88dfd50 | ||
|  | 42daf7ed32 | ||
|  | e5402ea7c1 | ||
|  | 05e8d6b578 | ||
|  | 505f340917 | ||
|  | 46856c9394 | ||
|  | c63a2ad39d | ||
|  | 68a7078614 | ||
|  | 2e15f1e2a3 | ||
|  | a790838222 | ||
|  | cd14360d62 | ||
|  | 74c94a60a6 | ||
|  | 56cbc7683e | ||
|  | bf778d2fca | ||
|  | 6e5bca8306 | ||
|  | 7e8fea6ed3 | ||
|  | 43ff3e11ed | ||
|  | f1750e3c35 | ||
|  | 8b1fe26c84 | ||
|  | 6ab09a3603 | ||
|  | df45298c1f | ||
|  | 4a64dce737 | ||
|  | 27ad428b5e | ||
|  | 2da33bae43 | ||
|  | 3dab683a45 | ||
|  | d08fa37ccf | ||
|  | 4cd7976f63 | ||
|  | 569e8b6180 | ||
|  | 9a443bd08e | ||
|  | efb37ae709 | ||
|  | 4658ef9918 | ||
|  | 2b6a1c9cb1 | ||
|  | c4606b1854 | ||
|  | d47b946d00 | ||
|  | a886e8087d | ||
|  | d473455680 | ||
|  | 40245ef43b | ||
|  | aa057d6a57 | ||
|  | 5c03e64f46 | ||
|  | 1d4d156749 | ||
|  | 4fc6da1fa1 | ||
|  | 9fac48fcea | ||
|  | ff4fdd3740 | ||
|  | da1cce035a | ||
|  | 49292bbb2d | ||
|  | 3e502db772 | ||
|  | 426351bb54 | ||
|  | 4d6e244100 | ||
|  | 71253b23d5 | ||
|  | 076ff7c7ba | ||
|  | 94b6c7975a | ||
|  | 0b08010221 | ||
|  | bc67113d77 | ||
|  | 5974bdcc2a | ||
|  | f9696287a4 | ||
|  | 807dede90a | ||
|  | 1f2b37b70e | ||
|  | 7288ba0fd7 | ||
|  | f48c17cf88 | ||
|  | fba9cc3739 | ||
|  | e04388a230 | ||
|  | fbe3be169d | ||
|  | 4e4ae0fca0 | ||
|  | 2f278c8618 | ||
|  | 905ae3437b | ||
|  | 7679721007 | ||
|  | 9f29a2e04f | ||
|  | ff5bf0c6e5 | ||
|  | 0eb8c2b7ba | ||
|  | deb140e3bc | ||
|  | 9a1710eb27 | ||
|  | e478238d77 | ||
|  | c9c9410908 | ||
|  | 42372cabd5 | ||
|  | ab9c991530 | ||
|  | 9429d84cf8 | ||
|  | b22774a599 | ||
|  | ca3b0a2ab1 | ||
|  | 62f76d08ad | ||
|  | 92bff24d43 | ||
|  | eddd3e508f | ||
|  | 649eb65bb1 | ||
|  | 8fb3348a7c | ||
|  | 6c2df1a783 | ||
|  | 55afc98108 | ||
|  | b875eb31d2 | ||
|  | 060b031272 | ||
|  | 95c4c4a238 | ||
|  | 4424e48926 | ||
|  | b8c7876454 | ||
|  | 8256f60340 | ||
|  | 1d0cb45e4c | ||
|  | d1a184e3f2 | ||
|  | aa38b31015 | ||
|  | 466067bd95 | ||
|  | c9b56efaaa | ||
|  | e56920edee | ||
|  | 0e0c475c83 | ||
|  | 241190c4da | ||
|  | 69d0c31ae5 | ||
|  | c3993fd943 | ||
|  | 8a3b7d7c1a | ||
|  | 87f14617cc | ||
|  | 9f24f765ea | ||
|  | 48c802e5cc | ||
|  | 388d141d82 | ||
|  | 7fa93e97db | ||
|  | 030d241130 | ||
|  | bac392d331 | ||
|  | 1542d5e386 | ||
|  | f1b6b3386a | ||
|  | 7d05999ced | ||
|  | d477feb930 | ||
|  | 9465a6d2b5 | ||
|  | c3f78b698d | ||
|  | a4e699f781 | ||
|  | cd871e5aad | ||
|  | c9b215684c | ||
|  | 82a4c706b0 | ||
|  | 082e6f5e99 | ||
|  | b90c7c09b6 | ||
|  | 1ee72a6ce5 | ||
|  | f72a8c5c06 | ||
|  | ba5db2c15f | ||
|  | f5b89ca783 | ||
|  | 4ef840e210 | ||
|  | e2d0ee125f | ||
|  | 8e1dbc03d9 | ||
|  | 9c0893fa8c | ||
|  | 0dfb97c5f7 | ||
|  | cb52af28e7 | ||
|  | 4b4384b1a8 | ||
|  | 62483e748b | ||
|  | 9b10984d81 | ||
|  | c5e283a13e | ||
|  | df918e8529 | ||
|  | aa1193a9eb | ||
|  | 8f7f263a48 | ||
|  | 0bad227548 | ||
|  | 78bcd3e1bb | ||
|  | 554640c345 | ||
|  | baecc256f6 | ||
|  | 10a2a4cf5b | ||
|  | c5dc073d49 | ||
|  | d48e4c66b2 | ||
|  | d357142075 | ||
|  | af17381e04 | ||
|  | 2e7d339d7e | ||
|  | c6cddad13c | ||
|  | 54bd54b521 | ||
|  | 8cbbb970e1 | ||
|  | fbbf7f75c3 | ||
|  | 5a23b95352 | ||
|  | 6f9675b6d6 | ||
|  | bb8ce30552 | ||
|  | 581e2ad431 | ||
|  | 45deb493ba | ||
|  | 4531d0ee32 | ||
|  | a26e8a5f83 | ||
|  | be74fbd677 | ||
|  | ee8c83bbd8 | ||
|  | dbd32abdd1 | ||
|  | f300287814 | ||
|  | dd361bb9ad | ||
|  | 744205cb6e | ||
|  | 86c22c9fdd | ||
|  | d7a66f6782 | ||
|  | 902f310eb0 | ||
|  | 3bb107e192 | ||
|  | 78f8b1454d | ||
|  | 70ba9a4db5 | ||
|  | 93d9c44585 | ||
|  | f0bb462f1c | ||
|  | 151e8e8f17 | ||
|  | 23f474b003 | ||
|  | 15104de84c | ||
|  | 55e4875662 | ||
|  | 5c3165efa2 | ||
|  | 3690a53dc9 | ||
|  | fb119cc208 | ||
|  | 7343304284 | ||
|  | c1ecc62ac1 | ||
|  | bd40615f8e | ||
|  | 0df8d096f0 | ||
|  | 749dd1e8e3 | ||
|  | ade6558769 | ||
|  | 6303b172b1 | ||
|  | 636ae193a7 | ||
|  | aff8493204 | ||
|  | 8f2d4494d5 | ||
|  | 7a2bb4eb96 | ||
|  | 4916c06797 | ||
|  | a818ab0942 | ||
|  | 9aa89393c0 | ||
|  | 216b304fe1 | ||
|  | cf8a687b4d | ||
|  | d1d6ba4114 | ||
|  | a5ac713310 | ||
|  | 6fa8c33672 | ||
|  | dd2b019d3c | ||
|  | 3a12ad192f | ||
|  | d669b75352 | ||
|  | 746d50d459 | ||
|  | f741552d91 | ||
|  | c38e4b86b4 | ||
|  | b044d85e90 | ||
|  | 24d111f026 | ||
|  | f50058e3c2 | ||
|  | ce9445168c | ||
|  | 27b8b4b35a | ||
|  | 9ccb67db8a | ||
|  | 3d82afd4e6 | ||
|  | 5f9cb61160 | ||
|  | 7f54b70c24 | ||
|  | a156dce281 | ||
|  | c0b0e58720 | ||
|  | 170ffbae04 | ||
|  | 6755ec7eb0 | ||
|  | c8bc9a096a | ||
|  | dc7f5a562b | ||
|  | 3b5b51578d | ||
|  | 41188a1bd6 | ||
|  | d9bafe34eb | ||
|  | 43dbba5378 | ||
|  | 168ed5ac56 | ||
|  | e5b4a55d8e | ||
|  | 167c057e8a | ||
|  | 71bf054ab1 | ||
|  | 111fcd77c4 | ||
|  | 287c1c4ffa | ||
|  | 3d69dc786d | ||
|  | 57a3f20c13 | ||
|  | eab2c57594 | ||
|  | b019962f34 | ||
|  | 5c59c819b6 | ||
|  | 93b97b8d72 | ||
|  | f1f922031a | ||
|  | 390cace775 | ||
|  | 28fdad9426 | ||
|  | 9155c13e08 | ||
|  | e8776d44c5 | ||
|  | 5ee8d04800 | ||
|  | eb31934fb7 | ||
|  | 000f86d318 | ||
|  | d9b3ebc82f | ||
|  | 91b3ca047a | ||
|  | 08131e42af | ||
|  | a013553a6c | ||
|  | 7b2fe8eb4a | ||
|  | 610f782054 | ||
|  | 94700f4064 | ||
|  | 0f12ebb31c | ||
|  | 00a8a9ac0e | ||
|  | 92616c6ae3 | ||
|  | 97db618cd8 | ||
|  | 2832d308f1 | ||
|  | a7ecdf715f | ||
|  | 935dc3ff9f | ||
|  | 99d14e8cbe | ||
|  | 664fde2344 | ||
|  | 27c45eface | ||
|  | f83bc3c8b3 | ||
|  | 0d5efb8d27 | ||
|  | bf9c1c1875 | ||
|  | 049d866f62 | ||
|  | d672f0c2ad | ||
|  | 7b040e8583 | ||
|  | e1cf285272 | ||
|  | 53f7f13362 | ||
|  | f710677cdc | ||
|  | b5fbc8b632 | ||
|  | 1fdc0a196c | ||
|  | 559f429f5e | ||
|  | ae4b198d3d | ||
|  | e088ecbbad | ||
|  | f6b7bd5b44 | ||
|  | f5a21f64c0 | ||
|  | f5cbed7c0c | ||
|  | 59fff8928b | ||
|  | 8743b49a17 | ||
|  | 19dc91937e | ||
|  | 4846cb7b14 | ||
|  | 54e0ac816b | ||
|  | 9ce960b3d7 | ||
|  | cc9d1c4bfd | ||
|  | d211555beb | ||
|  | 9350557d10 | ||
|  | ea6c54cad0 | ||
|  | 81287602d7 | ||
|  | 441ba79ec7 | ||
|  | c9e4a09da6 | ||
|  | c84f4e2bc0 | ||
|  | 6938f8ab89 | ||
|  | 4fdd2c851f | ||
|  | 78b56712fc | ||
|  | 6dcdb0f9a0 | ||
|  | cfab837b96 | ||
|  | b8461cdeb6 | ||
|  | e07d65ef6b | ||
|  | 80cbc35c89 | ||
|  | 2faf84780f | ||
|  | ebdb1a8836 | ||
|  | 2dfa51652a | ||
|  | 1cf1ea230f | ||
|  | 86e127ebff | ||
|  | a313265785 | ||
|  | 37693ad08d | ||
|  | e8180d252b | ||
|  | 9957c95c68 | ||
|  | 3354d53a48 | ||
|  | ddaa53b940 | ||
|  | c2e04a11bf | ||
|  | 00d059b8df | ||
|  | f3fff6f1c5 | ||
|  | 9360eb6a70 | ||
|  | 26fb03e6c8 | ||
|  | eed3d021d9 | ||
|  | 48728e8418 | ||
|  | f39f25760a | ||
|  | 10ea60daaf | ||
|  | f2fa5e140b | ||
|  | be5ff35b13 | ||
|  | bd5d73d1e6 | ||
|  | cc712b0c75 | ||
|  | a5ac84e1b9 | ||
|  | cbe1f762ca | ||
|  | e6db49c20c | ||
|  | 4909fcc8b4 | ||
|  | 68cdfd00b0 | ||
|  | 323c16ebe1 | ||
|  | c20a38b7b0 | ||
|  | 20993342a2 | ||
|  | 1b3198c143 | ||
|  | 173d0290ea | ||
|  | ae4cd8da12 | ||
|  | a494398332 | ||
|  | c82bdd1f5a | ||
|  | e6ac3ccfad | ||
|  | b13b58a2b2 | ||
|  | 2b6790f83f | ||
|  | fe30850568 | ||
|  | 493c71b89e | ||
|  | deaebc373c | ||
|  | e834489206 | ||
|  | 1b316e462e | ||
|  | 7bf75128a8 | ||
|  | 3857e8d49f | ||
|  | b16149b842 | ||
|  | 32ed8a4d8d | ||
|  | f949d2476b | ||
|  | 0620830b10 | ||
|  | 7ee25693aa | ||
|  | 7cb86add64 | ||
|  | fbc9720f7a | ||
|  | ffdd37ddd5 | ||
|  | 51062bc80b | ||
|  | 31533b5ef8 | ||
|  | 3649b595df | ||
|  | 83b7c9aa32 | ||
|  | 4e3c59a2da | ||
|  | dcbfe90cf7 | ||
|  | f69be86c74 | ||
|  | 820722f44e | ||
|  | 2ff806dedc | ||
|  | a755badd5f | ||
|  | 4df1e5393b | ||
|  | 8c55bd179f | ||
|  | ccfc5ece66 | ||
|  | 6c12f1bc86 | ||
|  | 88839e9610 | ||
|  | 4f6a733238 | ||
|  | dc9083a764 | ||
|  | 6f231b840b | ||
|  | 61a703e605 | ||
|  | 8e0e9734a5 | ||
|  | 2afb455bac | ||
|  | 01792f91e2 | ||
|  | 651dff0750 | ||
|  | 9cbfbd41dc | ||
|  | 168d6f403c | ||
|  | 41f200e630 | ||
|  | 0809cfdc6d | ||
|  | 466d739da8 | ||
|  | daf65cb387 | ||
|  | 2605f60983 | ||
|  | fcf6cdb134 | ||
|  | 209258b507 | ||
|  | f80bc214f9 | ||
|  | f76990bb9b | ||
|  | eef68c9b31 | ||
|  | 006c2ae186 | ||
|  | 8b66ff6afe | ||
|  | 4cc6d57e6e | ||
|  | af1c6b22bb | ||
|  | 5958990ed5 | ||
|  | cd4cbdc197 | ||
|  | 1f538be16e | ||
|  | 9703439a4c | ||
|  | 42203ba872 | ||
|  | cd60b852a1 | ||
|  | 5b1d9e1a0d | ||
|  | e02657a7c7 | ||
|  | c352eb0c74 | ||
|  | 81b9d5da09 | ||
|  | b9b0413510 | ||
|  | 97770da619 | ||
|  | 521623797e | ||
|  | 15d3414443 | ||
|  | 245f06c93a | ||
|  | 33899f0e2f | ||
|  | 4697fbdeef | ||
|  | 06174d6afb | ||
|  | b2bb16c1e0 | ||
|  | 27aae279e6 | ||
|  | e9ee93beb7 | ||
|  | 20941dedd3 | ||
|  | 5ac88623ed | ||
|  | 768508dd4b | ||
|  | 668633e764 | ||
|  | 3dbb1f034d | ||
|  | 1270e5d15c | ||
|  | afec8480fb | ||
|  | 8720511046 | ||
|  | a7a00ecf40 | ||
|  | 88bbafd3e8 | ||
|  | ae3258b449 | ||
|  | cf4d7cfeef | ||
|  | 46ee2a0568 | ||
|  | ce250c85fc | ||
|  | 1087b9f5df | ||
|  | 0dc74d9d14 | ||
|  | 98b272383f | ||
|  | e722daafd0 | ||
|  | 670ab059d1 | ||
|  | 09f826ceba | ||
|  | df5e3c9be9 | ||
|  | 31c336b3b1 | ||
|  | b3a419b2f3 | ||
|  | 11f63fa6ce | ||
|  | 4b35059140 | ||
|  | 7836feba63 | ||
|  | 1e54366f1f | ||
|  | d039bb0e03 | ||
|  | 166d32f073 | ||
|  | 321890992e | ||
|  | d644403d8a | ||
|  | 44e76f9518 | ||
|  | f9703eca4c | ||
|  | 48551e8bf5 | ||
|  | 74b538805b | ||
|  | 35f493beff | ||
|  | 06d11c9133 | ||
|  | da7eb615db | ||
|  | 5da5024ad3 | ||
|  | c1346d4c86 | ||
|  | c38a4a15ff | ||
|  | bdec94ead6 | ||
|  | 69bebf202f | ||
|  | 38895afea6 | ||
|  | 70ed4188b6 | ||
|  | d889094863 | ||
|  | c96eb8753e | ||
|  | 5a7607f6c6 | ||
|  | 71bb88529a | ||
|  | b26164a168 | ||
|  | 19444551e4 | ||
|  | e0b2a6e627 | ||
|  | 2973765866 | ||
|  | 2a08a25064 | ||
|  | a55e291908 | ||
|  | 2003d37a9a | ||
|  | 29145bf6cf | ||
|  | caa1ff120a | ||
|  | ef4e964c94 | ||
|  | 1f263f60a7 | ||
|  | 5fc7cafcbe | ||
|  | 397c4926eb | ||
|  | a14544398b | ||
|  | d439dceac1 | ||
|  | af29b31ea8 | ||
|  | b311ef70bc | ||
|  | 9f9d744406 | ||
|  | e094871bc9 | ||
|  | dfc95cee45 | ||
|  | fdca234721 | ||
|  | cf5cc626d7 | ||
|  | 358d9aac7d | ||
|  | 681bc580c4 | ||
|  | 4a2768f8d1 | ||
|  | 05f8773fa0 | ||
|  | 5e5fdfdd51 | ||
|  | 5e744390c0 | ||
|  | cb5fa401cb | ||
|  | 2980860377 | ||
|  | 9ff0b282f3 | ||
|  | 4bc1c032bd | ||
|  | 9fcb00f10b | ||
|  | 723e461559 | ||
|  | 9b24e6d448 | ||
|  | e622774775 | ||
|  | 84ce9bc94b | ||
|  | a3a1bc30b1 | ||
|  | b4c9a7698e | ||
|  | 8b2d7fc32f | ||
|  | 90e66cbd94 | ||
|  | fd9a7080ea | ||
|  | c0fad106f0 | ||
|  | 78c8243184 | ||
|  | 780abecd53 | ||
|  | ea6896816d | ||
|  | 95a456860a | ||
|  | b1b2fda155 | ||
|  | 5847f534c3 | ||
|  | e8e8163fa7 | ||
|  | 6a21d82dcf | ||
|  | d6b47656bc | ||
|  | 8c37ef3a95 | ||
|  | 35deed1d10 | ||
|  | ba32a665f1 | ||
|  | bbd19be554 | ||
|  | c360cc6db6 | ||
|  | 7e4b9af315 | ||
|  | 9b03e6b124 | ||
|  | 013e16e15f | ||
|  | 180ec52798 | ||
|  | c7b47b4453 | ||
|  | 4b00db7662 | ||
|  | 78a7b995d2 | ||
|  | f7c50a123a | ||
|  | 9617d17aca | ||
|  | d9884ddf73 | ||
|  | 8157f0a958 | ||
|  | b7580a5f83 | ||
|  | 66703b30b3 | ||
|  | 53677e3c64 | ||
|  | feef6a1756 | ||
|  | 5f299b895b | ||
|  | 4e1bb5fbac | ||
|  | 47ccc513ad | ||
|  | cce1a01936 | ||
|  | 6f2b1a6a76 | ||
|  | 8526907f50 | ||
|  | bc192a8e54 | ||
|  | 9ff6f8fc52 | ||
|  | 6573bd6b4b | ||
|  | 9dc3f614af | ||
|  | 3888b8cceb | ||
|  | 294df4a2b3 | ||
|  | 265dd37212 | ||
|  | eb7c79ad27 | ||
|  | de111c7100 | ||
|  | e892c9a824 | ||
|  | 5eb0e18cae | ||
|  | 27cabb398e | ||
|  | 64dbb14241 | ||
|  | bb4e2be9eb | ||
|  | 7d1de0da17 | ||
|  | bf16c9a42b | ||
|  | 1a7b1ce499 | ||
|  | efc9bc71a7 | ||
|  | fc5b315af0 | ||
|  | 7a4a78628d | ||
|  | d16fb30a62 | ||
|  | 2d177e660e | ||
|  | 2f131dc170 | ||
|  | 94810e371a | ||
|  | 59731878f6 | ||
|  | 54ede8aa18 | ||
|  | b415b6b043 | ||
|  | 70c922cdc5 | ||
|  | 068fc32cb2 | ||
|  | 3dcdacc3b8 | ||
|  | a6594358d8 | ||
|  | f98921da46 | ||
|  | 25747fbcf2 | ||
|  | aac5c2b13c | ||
|  | cc810a5b6f | ||
|  | 1b3592d959 | ||
|  | d75614e9a7 | ||
|  | 08703e282f | ||
|  | 2904baf44e | ||
|  | f99e46bf75 | ||
|  | 9f87890ead | ||
|  | 638184cf66 | ||
|  | 03babfe75c | ||
|  | 238ed3c788 | ||
|  | 6a9d931ba3 | ||
|  | a3d2a9e00b | ||
|  | 39b88e8207 | ||
|  | 449c6dfde5 | ||
|  | 7cc47ca0b1 | ||
|  | 95f4a83f41 | ||
|  | 35154dc7a3 | ||
|  | 0fd0d7d080 | ||
|  | 658265c938 | ||
|  | 38fe9e7e1c | ||
|  | 77056dcf8d | ||
|  | 026683a8e1 | ||
|  | 6ab6dd6ac3 | ||
|  | 83de3482ce | ||
|  | 919a35aed3 | ||
|  | ad3defb071 | ||
|  | 9c929ecd1b | ||
|  | f79c9f7cf1 | ||
|  | 8e75c345d9 | ||
|  | 44886d9aad | ||
|  | c2d444347d | ||
|  | 5cb497596d | ||
|  | 1857469d2f | ||
|  | ea71b4843d | ||
|  | 97727e2e3d | ||
|  | f81e7da8bb | ||
|  | 8e827bf83b | ||
|  | 9e1fa284ca | ||
|  | 3bf800be6e | ||
|  | 635b9f9dba | ||
|  | 52a0d7cf7b | ||
|  | a34516932b | ||
|  | 929a2a30a2 | ||
|  | ffa88eeb08 | ||
|  | 51b45b4ed4 | ||
|  | f263844793 | ||
|  | 18c46df9aa | ||
|  | 15846e157b | ||
|  | bc59f2db0d | ||
|  | cd2be8c1a4 | ||
|  | f958115c50 | ||
|  | e7d677bfb6 | ||
|  | 3e80ffc52b | ||
|  | d0c7a5c076 | ||
|  | f3f4e6b354 | ||
|  | 5a45b25614 | ||
|  | 0b5ee1edfc | ||
|  | da3dc599f9 | ||
|  | f013b435ab | ||
|  | 5f6975a113 | ||
|  | c5dee29e4b | ||
|  | 633ee02f13 | ||
|  | 6b750c909a | ||
|  | 5f8b6640a9 | ||
|  | dd42d8437c | ||
|  | 67a178591d | ||
|  | f5e5659c1f | ||
|  | 8b0f0fb615 | ||
|  | 209116e766 | ||
|  | 79392ab656 | ||
|  | 3ca1207231 | ||
|  | cec1b147f2 | ||
|  | 46cfcfa3e7 | ||
|  | b833e8dfa2 | ||
|  | 77b843efd8 | ||
|  | db72ad7c60 | ||
|  | eadc630fcb | ||
|  | 170c1793cc | ||
|  | 9f7c6c2d0c | ||
|  | 72d054c55c | ||
|  | 524edfe7c2 | ||
|  | c25c5623d2 | ||
|  | 4f38b77ef6 | ||
|  | 5862803434 | ||
|  | 5b3beded39 | ||
|  | c61fb7a598 | ||
|  | 33d9148029 | ||
|  | 63969f5a33 | ||
|  | edde18aeef | ||
|  | 657116d361 | ||
|  | e16269daa8 | ||
|  | c07591ff5c | ||
|  | 75a478ad54 | ||
|  | 8dae8b1a7f | ||
|  | 15fd8cf486 | ||
|  | 55333156ac | ||
|  | 8cdcba3231 | ||
|  | 8bab9e84e2 | ||
|  | 2faae83912 | ||
|  | 5a61a11a61 | ||
|  | a6d71988f2 | ||
|  | 7069e242ae | ||
|  | 56ee830558 | ||
|  | 6dd12729e6 | ||
|  | 14a48303cb | ||
|  | 72cf6c9c0f | ||
|  | 144ee6b8ca | ||
|  | 8967d86da6 | ||
|  | 18c6edbb5d | ||
|  | 53de3c4717 | ||
|  | ad577e4e81 | ||
|  | 44811a3e7c | ||
|  | 1ab3f05b3a | ||
|  | 5e76488ae7 | ||
|  | 32771fe7e1 | ||
|  | 9b40cc6881 | ||
|  | 2e35260bbb | ||
|  | a067704277 | ||
|  | de281818ac | ||
|  | c49bfad38d | ||
|  | c1ba591b26 | ||
|  | 719af38a61 | ||
|  | ac61dfae6b | ||
|  | 813fb679a7 | ||
|  | e7562781f7 | ||
|  | 56d36b7f53 | ||
|  | 53b3f7f821 | ||
|  | 08a53156bd | ||
|  | 8985cd6309 | ||
|  | 3833da7410 | ||
|  | 4210cd10db | ||
|  | a7bd1c6892 | ||
|  | 52b0111afa | ||
|  | 7921d128e4 | ||
|  | d7e838701a | ||
|  | 289bcb22aa | ||
|  | 3fe57b7983 | ||
|  | 32e92c2a16 | ||
|  | 1b3d208540 | ||
|  | 6a8bf0aa62 | ||
|  | 56715556ed | ||
|  | 838330b909 | ||
|  | 69553b138b | ||
|  | 36d7a02994 | ||
|  | 301528e2d2 | ||
|  | 0303b45707 | ||
|  | ba722e8ed5 | ||
|  | 289e5a5442 | ||
|  | fdad96e2bc | ||
|  | af994e4dae | ||
|  | 006d68e279 | ||
|  | 29dc122ad3 | ||
|  | cf4a8c6204 | ||
|  | 3c73fe92bf | ||
|  | 6637590797 | ||
|  | b8bab11acd | ||
|  | a2f600feac | ||
|  | 80dd62ef0a | ||
|  | 827b1c9cd8 | ||
|  | 2e4fcf803d | ||
|  | d00d95fc6f | ||
|  | 3e3ab9bd25 | ||
|  | 6eecc7722d | ||
|  | ada4aaf69a | ||
|  | 93244c1f78 | ||
|  | be056cea6b | ||
|  | 659ca8be14 | ||
|  | ea9af8366d | ||
|  | 80edd47d36 | ||
|  | d7746b3649 | ||
|  | c4c4fbc34c | ||
|  | 59f57c96e9 | ||
|  | a2f852fecf | ||
|  | ad114ed329 | ||
|  | c4c3d0f07f | ||
|  | 6cf8102de5 | ||
|  | e7e4aa2218 | ||
|  | 6d84f4b6c1 | ||
|  | ce3e9ffd11 | ||
|  | 3ada260e0e | ||
|  | 8fdd0cb795 | ||
|  | 913e05a2e6 | ||
|  | fa1f703ef6 | ||
|  | 4004c53e1b | ||
|  | 4838670649 | ||
|  | a985e09282 | ||
|  | 9bd1503cb4 | ||
|  | a2ccbf7844 | ||
|  | 61bbe8a905 | ||
|  | 59bc5d22d1 | ||
|  | 1423d5b314 | ||
|  | 152d0eb1d0 | ||
|  | 6426d1df06 | ||
|  | 9284eb3fe9 | ||
|  | afdae8bc1e | ||
|  | 2a7085e593 | ||
|  | 2408fb3ed4 | ||
|  | 8316afb176 | ||
|  | e59fd098a3 | ||
|  | e044199693 | ||
|  | 8f8e29fc22 | ||
|  | 8de5384158 | ||
|  | 216c659335 | ||
|  | 041ca8a5d3 | ||
|  | fe4f1b306d | ||
|  | a0972d99fb | ||
|  | e332bfef7c | ||
|  | cba5e226d8 | ||
|  | 5aff0c4943 | ||
|  | cb49c00f4d | ||
|  | e26d797d57 | ||
|  | 938581527e | ||
|  | c38ae09735 | ||
|  | 28c3cfe084 | ||
|  | 4a2823bcba | ||
|  | 18eba02026 | ||
|  | d4690ce580 | ||
|  | a785c450b1 | ||
|  | 7480dc4a19 | ||
|  | ad01891a67 | ||
|  | 67fe35d564 | ||
|  | 7f19b6957a | ||
|  | 0a54caf202 | ||
|  | 4b4c1c7f8f | ||
|  | d071f3947e | ||
|  | b3d99cd210 | ||
|  | 90e696f82c | ||
|  | 958fcd1cfa | ||
|  | 8f57c7dcb3 | ||
|  | 77262f52a4 | ||
|  | 16bfbc8a12 | ||
|  | 1fd375b875 | ||
|  | 46131ad39d | ||
|  | 0b5c5b2ae9 | ||
|  | 55be174037 | ||
|  | a17b7025f1 | ||
|  | 170cf7fd77 | ||
|  | 23cdb4d326 | ||
|  | cbbe529572 | ||
|  | 0b382426e9 | ||
|  | 1cbbf9baa4 | ||
|  | 8d41ff7b79 | ||
|  | e3b6057bf8 | ||
|  | 66a4042cad | ||
|  | 56c08d8302 | ||
|  | d4e759754d | ||
|  | a96e171cbf | ||
|  | bd4a8c8397 | ||
|  | 04f71b3b43 | ||
|  | d124de51db | ||
|  | d87d12a0f5 | ||
|  | f2b08346d0 | ||
|  | d3682a6727 | ||
|  | 371bbd9508 | ||
|  | a8a28f442f | ||
|  | 65ddd8a736 | ||
|  | 8bb27de233 | ||
|  | 37e2f097ba | ||
|  | 1966d87ce6 | ||
|  | 7b8c86e1e3 | ||
|  | de634da513 | ||
|  | 96836e2d6c | ||
|  | 8a9d576f61 | ||
|  | 791d12fbb4 | ||
|  | d1329be2fa | ||
|  | 3ed6561702 | ||
|  | 7a0587f433 | ||
|  | 0fe682bfe6 | ||
|  | 0f685e8789 | ||
|  | 420771c233 | ||
|  | 5e3e9271ca | ||
|  | 1e603c0833 | ||
|  | 03e1673e92 | ||
|  | 9f992f003d | ||
|  | f50244a41f | ||
|  | 8b9607f9b5 | ||
|  | af107ad5e8 | ||
|  | 8926d62165 | ||
|  | 45344ee347 | ||
|  | baf9ebab15 | ||
|  | b7dab817f2 | ||
|  | fb2fa54480 | ||
|  | 2c966a1234 | ||
|  | 143ea69c0d | ||
|  | 8a02ead013 | ||
|  | 930d5ab941 | ||
|  | e54a56d3a8 | ||
|  | f5216c0d85 | ||
|  | ebc77540b9 | ||
|  | 28d2583c10 | ||
|  | b0988a7b00 | ||
|  | 2c4920db2d | ||
|  | 8321663815 | ||
|  | ac0280d460 | ||
|  | ba6f4268f0 | ||
|  | 0bd18f94ac | ||
|  | 66fb63661f | ||
|  | d9b16beb0a | ||
|  | 6a01a9bdfd | ||
|  | b81b34a706 | ||
|  | 3750a00b5f | ||
|  | 05ddcc169d | ||
|  | f571a5f1bd | ||
|  | 48b786adea | ||
|  | d691fa9b4d | ||
|  | da50f9e419 | ||
|  | 103de5e18a | ||
|  | dac6efd98b | ||
|  | 46aa7f81b2 | ||
|  | 0683c7cd67 | ||
|  | 50d7aa7b6a | ||
|  | b781215d0a | ||
|  | 866bc2f3bd | ||
|  | 49d4705014 | ||
|  | 9163fcfccb | ||
|  | 7e10641461 | ||
|  | cdc0e3cfd8 | ||
|  | 466e81d56a | ||
|  | 5953f691d1 | ||
|  | e2790ca6c1 | ||
|  | 66dbd48b76 | ||
|  | 73cfbbd2ba | ||
|  | 38bc38bf26 | ||
|  | e12d13c838 | ||
|  | fcc3af6136 | ||
|  | 491298e1cb | ||
|  | 72b5895217 | ||
|  | 0a8f4017bd | ||
|  | cb985f5897 | ||
|  | 968ec0853f | ||
|  | 0bde72d3df | ||
|  | 45293fbd42 | ||
|  | 72997065f0 | ||
|  | a838dc163d | ||
|  | 3d15a4ca6d | ||
|  | 51c7d4fb1b | ||
|  | fa586dba7e | ||
|  | 8ad40389f2 | ||
|  | b3333cc2d3 | ||
|  | 3699a7ba9a | ||
|  | 204e521ba4 | ||
|  | c9bab3e5c3 | ||
|  | 3d00e20238 | ||
|  | c3958ed3c4 | ||
|  | e5b88be5fa | ||
|  | 425552988a | ||
|  | a81dd8abe5 | ||
|  | bac8154a5b | ||
|  | 737d15fa0e | ||
|  | 5f2317af7f | ||
|  | 2bd1f783e5 | ||
|  | d6c0c9f963 | ||
|  | 21b6ad7a41 | ||
|  | a65d609fdc | ||
|  | 04e676b936 | ||
|  | be8eaaffdf | ||
|  | 28c753523f | ||
|  | 7fbd0b2ffc | ||
|  | 7ffb48a87a | ||
|  | d485270e1f | ||
|  | 5fd688b266 | ||
|  | 3716668e0c | ||
|  | ae7fd18c34 | ||
|  | 4f59b1d32f | ||
|  | 90cb3279df | ||
|  | cf0c7ef6b2 | ||
|  | 47c23781d9 | ||
|  | e258c050f7 | ||
|  | 57801b2f34 | ||
|  | 710e9c9423 | ||
|  | deefef83bd | ||
|  | 51e30aed66 | ||
|  | 8d109a3cfe | ||
|  | 3424e019b5 | ||
|  | c6b4bceb67 | ||
|  | afb4155015 | ||
|  | 8d99baf38a | ||
|  | b91cb60328 | ||
|  | c0d62237fc | ||
|  | 223ea80860 | ||
|  | 5a77bef494 | ||
|  | 80c0efe821 | ||
|  | 8044d89557 | ||
|  | 4f0ed97410 | ||
|  | af7952f204 | ||
|  | d8dcae856b | ||
|  | 7296796ed9 | ||
|  | a2c2bb4948 | ||
|  | 72ebfdc20e | ||
|  | 16b95ea78a | ||
|  | c04f08dfd8 | ||
|  | a30793e818 | ||
|  | e39e1eaf21 | ||
|  | ab22d2cbaa | ||
|  | 96ddbe7227 | ||
|  | 4d09235aef | ||
|  | 136b8975e3 | ||
|  | e21b1eca17 | ||
|  | 244b90b1d4 | ||
|  | b318f3f940 | ||
|  | e211c9812e | ||
|  | eef28d96f4 | ||
|  | c8227e09ee | ||
|  | 3e05fd91d9 | ||
|  | 450baba56a | ||
|  | 17a8c4918c | ||
|  | 0e2419d61a | ||
|  | 79b1a2ca6d | ||
|  | 2213c68155 | ||
|  | 2492b1fa96 | ||
|  | 6c6598dac5 | ||
|  | a137112e66 | ||
|  | 8642ae8180 | ||
|  | 894c4dc5a7 | ||
|  | d96063ea6e | ||
|  | c3dc193f3e | ||
|  | 3c2952009e | ||
|  | f4ade470df | ||
|  | 2e33b43389 | ||
|  | 92799699bc | ||
|  | 7ab0508167 | ||
|  | 3c65c28936 | ||
|  | 43892da07e | ||
|  | 7c436920a4 | ||
|  | 89d565e63b | ||
|  | 150b6fe5b6 | ||
|  | 0e77574c26 | ||
|  | df23863443 | ||
|  | 581bf11b21 | ||
|  | d602d4b429 | ||
|  | d1d4a52934 | ||
|  | 375d113769 | ||
|  | 9b83974bff | ||
|  | 3c68c99bd5 | ||
|  | ec4b37c596 | ||
|  | ba9601d21c | ||
|  | 50c13fd469 | ||
|  | 7af072b8fc | ||
|  | faa128d41e | ||
|  | 868fe46932 | ||
|  | e9e4307ce5 | ||
|  | 774d4844a9 | ||
|  | 586c53e670 | ||
|  | 68e073fbff | ||
|  | 8101dc37b1 | ||
|  | 63f16c458d | ||
|  | 821e007e95 | ||
|  | 1656a2f11a | ||
|  | 4dbc135dce | ||
|  | fc886f6bc1 | ||
|  | f93e480466 | ||
|  | fe807e23f8 | ||
|  | ecf61c31f1 | ||
|  | 4feff18af5 | ||
|  | a07c52e0d8 | ||
|  | 7bb07d7f55 | ||
|  | f12dfc8a14 | ||
|  | be030f15c4 | ||
|  | f5fb6c063b | ||
|  | fb722f06b9 | ||
|  | c0ea19e15e | ||
|  | cdeb1ad87c | ||
|  | 0dbe4e94fa | ||
|  | b5e2e8aa1d | ||
|  | 9502010248 | ||
|  | fea0557b47 | ||
|  | ed4fcc9011 | ||
|  | ed12ea7cfb | ||
|  | 73e526645e | ||
|  | 72aeafb2b5 | ||
|  | cc1af60cb4 | ||
|  | 359fab315f | ||
|  | 6a9574bab9 | ||
|  | 83d6158483 | ||
|  | 63ef89b6cc | ||
|  | b0beab4cd3 | ||
|  | a34782575f | ||
|  | 142bdc9430 | ||
|  | 14b79cb0a4 | ||
|  | ce5beeaf2c | ||
|  | 31114a2ca5 | ||
|  | 32528094ad | ||
|  | 0a2a01c44c | ||
|  | c1888dc3ac | ||
|  | 4d76afbe01 | ||
|  | 76d7a97f93 | ||
|  | 8b1366b20a | ||
|  | e0f9685578 | ||
|  | 5235657954 | ||
|  | a15fbc8094 | ||
|  | 546f1d9c50 | ||
|  | 74231f552a | ||
|  | b250a10e3c | ||
|  | a9f1b31dd6 | ||
|  | 7fe393acaf | ||
|  | 04faba4db5 | ||
|  | 91bba40c20 | ||
|  | 79e39f7de8 | ||
|  | 9c09353559 | ||
|  | 50752a5bfe | ||
|  | d59879db7d | ||
|  | aab125da27 | ||
|  | 74fc731f96 | ||
|  | bd0050fec2 | ||
|  | aa5e313b92 | ||
|  | e89d613b7e | ||
|  | 8757929ead | ||
|  | e0a9b19802 | ||
|  | 308da6dc6e | ||
|  | b6960fb0e5 | ||
|  | 137208c3fd | ||
|  | d7a9a62a1d | ||
|  | 075315bdaa | ||
|  | 3948fcd614 | ||
|  | 8e61e129ab | ||
|  | 20cffd0502 | ||
|  | 5df09dab09 | ||
|  | 7446b911e5 | ||
|  | f15267c1ab | ||
|  | 9c9fc2b5dc | ||
|  | 28f601b54b | ||
|  | 18b8a05014 | ||
|  | 910c995ed8 | ||
|  | 498468aa2c | ||
|  | 637aebcb34 | ||
|  | 9afd5cb277 | ||
|  | bc525e7272 | ||
|  | a80180780d | ||
|  | 0d73086c37 | ||
|  | 02ae39238d | ||
|  | 43d6b51d42 | ||
|  | 84566310de | ||
|  | 6a6ec9fbe4 | ||
|  | 84a7f825d7 | ||
|  | 0372c1aaf1 | ||
|  | c9fff197f7 | ||
|  | 6900392e43 | ||
|  | c00bcd78cc | ||
|  | b2b82124e6 | ||
|  | 3de57c668f | ||
|  | 43669648ce | ||
|  | 3b73b416d5 | ||
|  | 5153591c8f | ||
|  | 3172bc90da | ||
|  | 76a1b2cd51 | ||
|  | bdf7eee72f | ||
|  | 2d4b148b2c | ||
|  | d67db74ca2 | ||
|  | 516725456f | ||
|  | 001d72a484 | ||
|  | c555e28988 | ||
|  | af13d1943f | ||
|  | 52df2edc8f | ||
|  | cd08484a13 | ||
|  | f38d38f139 | ||
|  | 93b6c68938 | ||
|  | a4cc25175a | ||
|  | 3fb14b4708 | ||
|  | 6bdb6db330 | ||
|  | d05c165ace | ||
|  | ab53cdb896 | ||
|  | c1f142af78 | ||
|  | 6e261abb73 | ||
|  | 39af9e4414 | ||
|  | 5b50abb2c7 | ||
|  | 13bda0a264 | ||
|  | 1658c666ab | ||
|  | 170aebfe54 | ||
|  | c4ef379d0e | ||
|  | 18b038d8ff | ||
|  | 12ee5da872 | ||
|  | 601f9f86bb | ||
|  | d7329a5915 | ||
|  | 9e7b730002 | ||
|  | 2d59d845bc | ||
|  | c2645894e0 | ||
|  | 3751106317 | ||
|  | 60bb639351 | ||
|  | 74c50930bd | ||
|  | 9105104303 | ||
|  | 540dde135e | ||
|  | f8936210cf | ||
|  | 1dc6d8de40 | ||
|  | 1069db3c13 | ||
|  | 65122f0144 | ||
|  | d2c018f7da | ||
|  | 46493c2af6 | ||
|  | c303e03f76 | ||
|  | d8b65f62e7 | ||
|  | 854368a8f3 | ||
|  | 7653a34aea | ||
|  | ee50b58e00 | ||
|  | 1eb60ab100 | ||
|  | 4a20eef351 | ||
|  | 26c9b2c353 | ||
|  | 16374bce9b | ||
|  | 86011d4ea2 | ||
|  | fcb8b02da9 | ||
|  | 571165c2bb | ||
|  | c842113610 | ||
|  | 974a8b3b70 | ||
|  | 2e20c99ada | ||
|  | aa88ff6f2c | ||
|  | 5e6aa63d03 | ||
|  | ad6700c114 | ||
|  | f08c6efb00 | ||
|  | cc807ec132 | ||
|  | 24e7c68243 | ||
|  | ab25edd37a | ||
|  | be47fde6c2 | ||
|  | 1ffa8c5e72 | ||
|  | d855ccb8a7 | ||
|  | d88919474b | ||
|  | e139664301 | ||
|  | 5adf5f6e3f | ||
|  | aef2075c8e | ||
|  | 922b2962a3 | ||
|  | b4a401700e | ||
|  | e9601bb9c1 | ||
|  | 58af3dc6ea | ||
|  | 074295df61 | ||
|  | ec349b31c7 | ||
|  | 5ae236e016 | ||
|  | d7c5897aba | ||
|  | 5252e7efe7 | ||
|  | fc7d65629a | ||
|  | f28fdf8252 | ||
|  | 5d07c4a949 | ||
|  | fdd9eaab4b | ||
|  | e0d863a46f | ||
|  | 3aacb6f5f3 | ||
|  | 428e331b3e | ||
|  | 847e05e9a7 | ||
|  | 087eb5dbe6 | ||
|  | f15932b2ac | ||
|  | d3a4c3795d | ||
|  | b15d55e1d9 | ||
|  | 4f5889cc5b | ||
|  | bf2a104a4e | ||
|  | 0c6dd5cd16 | ||
|  | 5efb06a7aa | ||
|  | b13acef272 | ||
|  | cfa67d6c0f | ||
|  | e70444f19a | ||
|  | 0258982e60 | ||
|  | 70eed5cb5e | ||
|  | a650fa51f7 | ||
|  | cb205580d8 | ||
|  | f9329aac00 | ||
|  | 745f4a7523 | ||
|  | 60254dafd7 | ||
|  | a8d60388ba | ||
|  | 83ec60254c | ||
|  | c15c45f765 | ||
|  | cbe52b5089 | ||
|  | e4e2921f3e | ||
|  | 4673170531 | ||
|  | 2c2ed26c38 | ||
|  | 94be5244fe | ||
|  | f137a08493 | ||
|  | 48624d0a34 | ||
|  | 4cceb3ddaa | ||
|  | f728395603 | ||
|  | 3e82d43807 | ||
|  | 2194c4e0a9 | ||
|  | c581080f3f | ||
|  | f6b1ec27e5 | ||
|  | 368b183230 | ||
|  | 9028ad36ad | ||
|  | 6cc041cd39 | ||
|  | 63ff01e78d | ||
|  | 9e5484937e | ||
|  | b8ed489b14 | ||
|  | 765152d04b | ||
|  | 14934367d8 | ||
|  | 04164500c8 | ||
|  | 5160f2c298 | ||
|  | 124c9303b9 | ||
|  | cd27f0ad69 | ||
|  | a7555bcce3 | ||
|  | 6b5c4fd3f4 | ||
|  | cc55e2acee | ||
|  | 1511f75a80 | ||
|  | f01bbefc1f | ||
|  | 1d1eb5ffa8 | ||
|  | a465cb2191 | ||
|  | 42d13e02ef | ||
|  | d00786c43f | ||
|  | 4b47f99829 | ||
|  | 35aaf40003 | ||
|  | cc5b4a1e02 | ||
|  | 7079521e8c | ||
|  | b5025560a5 | ||
|  | 3f4bdd7f0e | ||
|  | e94bb9b549 | ||
|  | 1ddaacbef5 | ||
|  | e8b40518e0 | ||
|  | 0f88cbb41b | ||
|  | 780d137b76 | ||
|  | ad8a9717d1 | ||
|  | 9d6ea6b2f6 | ||
|  | 7559383089 | ||
|  | f84381c927 | ||
|  | cb0122a43f | ||
|  | 6776b20989 | ||
|  | e98d556022 | ||
|  | 5bf18b69d7 | ||
|  | ea17f045a7 | ||
|  | 526f565ea7 | ||
|  | 4aff9d6e73 | ||
|  | bf516d4d21 | ||
|  | ae92e409d9 | ||
|  | 4d017dc8a9 | ||
|  | 707f4e2965 | ||
|  | 1c3bffdc50 | ||
|  | e54ddcb8b0 | ||
|  | ddefb0debc | ||
|  | 92d8dde90d | ||
|  | 1bb0508ddf | ||
|  | a280a326b9 | ||
|  | 683e9b7c2c | ||
|  | a44e5da421 | ||
|  | 8cd2c90ad7 | ||
|  | 5e57a390a2 | ||
|  | 620848272e | ||
|  | 1e86794416 | ||
|  | e36717259b | ||
|  | 75b9238b90 | ||
|  | ce5b20027e | ||
|  | 0de1242c83 | ||
|  | 8bd445ab19 | ||
|  | fdef0de163 | ||
|  | b1b03a4325 | ||
|  | 0587d96474 | ||
|  | c2241567e4 | ||
|  | 7ac24ba418 | ||
|  | c933ffec66 | ||
|  | e587d934b1 | ||
|  | f354e90656 | ||
|  | 1b0bc7ec6e | ||
|  | ee1acb9c00 | ||
|  | 06862a2812 | ||
|  | 5fa87e18db | ||
|  | 77989e2720 | ||
|  | 3a1102fa4e | ||
|  | 8a9974ce53 | ||
|  | 4be8f1ca03 | ||
|  | 81b3a22606 | ||
|  | d7ee03d4f9 | ||
|  | c1c06410c2 | ||
|  | e65a4c1010 | 
| @@ -1,25 +0,0 @@ | ||||
| # Save as .codeclimate.yml (note leading .) in project root directory | ||||
| languages: | ||||
|   JavaScript: true | ||||
|   PHP: true | ||||
| exclude_paths: | ||||
| - "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,3 +0,0 @@ | ||||
| src_dir: . | ||||
| coverage_clover: build/logs/clover.xml | ||||
| json_path: build/logs/coveralls-upload.json | ||||
							
								
								
									
										44
									
								
								.env.example
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										44
									
								
								.env.example
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,21 +1,47 @@ | ||||
| APP_ENV=production | ||||
| APP_DEBUG=false | ||||
| APP_KEY=SomeRandomString | ||||
| 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 | ||||
|  | ||||
| COOKIE_PATH="/" | ||||
| COOKIE_DOMAIN= | ||||
| COOKIE_SECURE=false | ||||
|  | ||||
| REDIS_HOST=127.0.0.1 | ||||
| REDIS_PASSWORD=null | ||||
| REDIS_PORT=6379 | ||||
|  | ||||
| MAIL_DRIVER=smtp | ||||
| MAIL_HOST=mailtrap.io | ||||
| MAIL_PORT=2525 | ||||
| MAIL_FROM=changeme@example.com | ||||
| MAIL_USERNAME=null | ||||
| MAIL_PASSWORD=null | ||||
| MAIL_ENCRYPTION=null | ||||
|  | ||||
| SEND_REGISTRATION_MAIL=true | ||||
| MUST_CONFIRM_ACCOUNT=false | ||||
|  | ||||
| SHOW_INCOMPLETE_TRANSLATIONS=false | ||||
|  | ||||
| EMAIL_SMTP= | ||||
| EMAIL_DRIVER=smtp | ||||
| EMAIL_USERNAME= | ||||
| EMAIL_PASSWORD= | ||||
| ANALYTICS_ID= | ||||
| EMAIL_PRETEND=false | ||||
| RUNCLEANUP=true | ||||
| SITE_OWNER=mail@example.com | ||||
| SITE_OWNER=mail@example.com | ||||
|  | ||||
| PUSHER_KEY= | ||||
| PUSHER_SECRET= | ||||
| PUSHER_APP_ID= | ||||
|   | ||||
							
								
								
									
										18
									
								
								.env.testing
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								.env.testing
									
									
									
									
									
								
							| @@ -1,18 +0,0 @@ | ||||
| APP_ENV=testing | ||||
| APP_DEBUG=true | ||||
| APP_KEY=SomeRandomString | ||||
|  | ||||
| DB_CONNECTION=sqlite | ||||
| DB_HOST=localhost | ||||
| DB_DATABASE=homestead | ||||
| DB_USERNAME=homestead | ||||
| DB_PASSWORD=secret | ||||
|  | ||||
| CACHE_DRIVER=array | ||||
| SESSION_DRIVER=array | ||||
|  | ||||
| EMAIL_SMTP= | ||||
| EMAIL_USERNAME= | ||||
| EMAIL_PASSWORD= | ||||
| ANALYTICS_ID=ABC | ||||
| EMAIL_PRETEND=true | ||||
							
								
								
									
										2
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										2
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1 +1,3 @@ | ||||
| * text=auto | ||||
| *.css linguist-vendored | ||||
| *.scss linguist-vendored | ||||
|   | ||||
							
								
								
									
										40
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										40
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,33 +1,13 @@ | ||||
| /bootstrap/compiled.php | ||||
| /node_modules | ||||
| /public/storage | ||||
| /vendor | ||||
| composer.phar | ||||
| Thumbs.db | ||||
| .idea/ | ||||
| tests/_output/* | ||||
| _ide_helper.php | ||||
| /build/logs/clover.xml | ||||
| index.html* | ||||
| app/storage/firefly-export* | ||||
| .vagrant | ||||
| firefly-iii-import-*.json | ||||
| tests/_output/* | ||||
| testing.sqlite | ||||
| _ide_helper_models.php | ||||
| clean.sqlite | ||||
| tests/acceptance/AcceptanceTester.php | ||||
| tests/functional/FunctionalTester.php | ||||
| tests/unit/UnitTester.php | ||||
| pi.php | ||||
| tests/_data/db.sqlite | ||||
| tests/_data/dump.sql | ||||
| db.sqlite_snapshot | ||||
| c3.php | ||||
| db.sqlite-journal | ||||
| tests/_output/* | ||||
| /.idea | ||||
| Homestead.json | ||||
| Homestead.yaml | ||||
| .env | ||||
| clover.xml | ||||
| node_modules/ | ||||
| addNewLines.php | ||||
| .phpstorm.meta.php | ||||
| .env.backup | ||||
| _development | ||||
| .env.local | ||||
| result.html | ||||
| test-import.sh | ||||
| test-import-report.txt | ||||
| public/google*.html | ||||
|   | ||||
							
								
								
									
										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" | ||||
|   ] | ||||
| } | ||||
| @@ -1,5 +1,8 @@ | ||||
| # .scrutinizer.yml | ||||
| tools: | ||||
|   external_code_coverage: | ||||
|     timeout: 1800    # Timeout in seconds. | ||||
|     runs: 2 | ||||
|   external_code_coverage: false | ||||
| filter: | ||||
|     excluded_paths: | ||||
|         - app/Support/Migration/* | ||||
|         - app/database/migrations/* | ||||
|         - database/migrations/* | ||||
|   | ||||
							
								
								
									
										22
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,22 +0,0 @@ | ||||
| language: php | ||||
| sudo: false | ||||
|  | ||||
|  | ||||
| php: | ||||
|   - 5.5 | ||||
|   - 5.6 | ||||
|  | ||||
| install: | ||||
|   - composer update | ||||
|   - php artisan env | ||||
|   - mv -v .env.testing .env | ||||
|  | ||||
| script: | ||||
|   - phpunit | ||||
|  | ||||
| after_script: | ||||
|   - php vendor/bin/coveralls | ||||
|   - CODECLIMATE_REPO_TOKEN=26489f9e854fcdf7e7660ba29c1455694685465b1f90329a79f7d2bf448acb61 ./vendor/bin/test-reporter --stdout > codeclimate.json | ||||
|   - "curl -X POST -d @codeclimate.json -H 'Content-Type: application/json' -H 'User-Agent: Code Climate (PHP Test Reporter v0.1.1)' https://codeclimate.com/test_reports" | ||||
|   - wget https://scrutinizer-ci.com/ocular.phar | ||||
|   - php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml | ||||
							
								
								
									
										299
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,299 @@ | ||||
| # Change Log | ||||
| All notable changes to this project will be documented in this file. | ||||
| This project adheres to [Semantic Versioning](http://semver.org/). | ||||
|  | ||||
| ## [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/JC5/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 | ||||
| - More translations | ||||
| - Extended cookie control. | ||||
| - User accounts can now be activated (disabled by default). | ||||
| - Bills can now take the source and destination account name into account. | ||||
|  | ||||
| ### Changed | ||||
| - The pages related to rules have new URL's. | ||||
|  | ||||
| ### Fixed | ||||
| - Spelling errors. | ||||
| - Problems related to the "account repository". | ||||
| - Some views showed empty (0.0) amounts. | ||||
|  | ||||
| ## [3.8.0] - 2016-03-20 | ||||
| ### Added | ||||
| - Two factor authentication, thanks to the excellent work of [zjean](https://github.com/zjean). | ||||
| - A new chart showing your net worth in year and multi-year reports. | ||||
| - You can now see if your current or future rules actually match any transactions, thanks to the excellent work of @roberthorlings. | ||||
| - New date fields for transactions. They are not used yet in reports or anything, but they can be filled in. | ||||
| - New routine to export your data. | ||||
| - Firefly III will mail the site owner when blocked users try to login, or when blocked domains are used in registrations. | ||||
|  | ||||
|  | ||||
| ### Changed | ||||
| - Firefly III now requires PHP 7.0 minimum. | ||||
|  | ||||
|  | ||||
| ### Fixed | ||||
| - HTML fixes, thanks to [roberthorlings](https://github.com/roberthorlings) and [zjean](https://github.com/zjean).. | ||||
| - A bug fix in the ABN Amro importer, thanks to [roberthorlings](https://github.com/roberthorlings) | ||||
| - It was not possible to change the opening balance, once it had been set. Thanks to [xnyhps](https://github.com/xnyhps) and [marcoveeneman](https://github.com/marcoveeneman) for spotting this. | ||||
| - Various other bug fixes. | ||||
|  | ||||
|  | ||||
|  | ||||
| ## [3.4.2] - 2015-05-25 | ||||
| ### Added | ||||
| - Initial release. | ||||
|  | ||||
| ### Changed | ||||
| - Initial release. | ||||
|  | ||||
| ### Deprecated | ||||
| - Initial release. | ||||
|  | ||||
| ### Removed | ||||
| - Initial release. | ||||
|  | ||||
| ### Fixed | ||||
| - Initial release. | ||||
|  | ||||
| ### Security | ||||
| - Initial release. | ||||
							
								
								
									
										7
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  | ||||
| This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  | ||||
| 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. | ||||
							
								
								
									
										109
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										109
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,99 +1,30 @@ | ||||
| # Firefly III (v3.4.4) | ||||
| # Firefly III [](https://secure.php.net/downloads.php#v7.0.4) [](https://packagist.org/packages/grumpydictator/firefly-iii) [](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master) | ||||
|  | ||||
| [](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master) | ||||
| [](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master) | ||||
| [](https://scrutinizer-ci.com/g/JC5/firefly-iii/build-status/master) | ||||
| ## A personal finances manager | ||||
|  | ||||
| ## About | ||||
| [](https://i.nder.be/hhfv03hp) [](https://i.nder.be/hhmwmqw9) | ||||
|  | ||||
| "Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared  | ||||
| household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money. | ||||
|   | ||||
| _Firefly is a system you'll have install yourself on webhosting of your choosing._ | ||||
| [](https://i.nder.be/g63q05m0) [](https://i.nder.be/c2g30ngg) | ||||
|  | ||||
| Personal financial management is pretty difficult, and everybody has their own approach to it. Some people | ||||
| make budgets, other people limit their cashflow by throwing away their credit cards, others try to increase | ||||
| their current cashflow. There are tons of ways to save and earn money. | ||||
| "Firefly 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. | ||||
|  | ||||
| ## 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://jc5.github.io/firefly-iii/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. | ||||
|  | ||||
| Firefly works on the principle that if you know where you're money is going, you can stop it from going there. | ||||
|  | ||||
|   | ||||
| To get to know Firefly, and to see if it fits you, check out these resources: | ||||
| #### Some advantages of using Firefly | ||||
|  | ||||
| - The screenshots below on this very page. | ||||
| - The featurelist below, also on this very page. | ||||
| - The [full description](https://github.com/JC5/firefly-iii/wiki/full-description), which will tell you how Firefly works, | ||||
| and the philosophy behind it. | ||||
| - Firefly can import any CSV file, so migrating from other systems is easy. | ||||
| - Firefly runs on your own server, so you are fully in control of your data. Remember, there is no such thing as "the cloud", it’s just somebody else’s computer! | ||||
| - 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/). | ||||
|  | ||||
| #### About the name (if you care) | ||||
|  | ||||
| It's III, or 3, because [version 2](https://github.com/JC5/Firefly) and version 1 (not online) preceded it. It has been growing steadily ever since. | ||||
|  | ||||
| ## Current features | ||||
|  | ||||
| - [A double-entry bookkeeping system](https://en.wikipedia.org/wiki/Double-entry_bookkeeping_system); | ||||
| - You can store, edit and remove [withdrawals, deposits and transfers](https://en.wikipedia.org/wiki/Financial_transaction). This allows you full financial management; | ||||
| - You can manage different types of accounts; | ||||
|   - [Asset](https://en.wikipedia.org/wiki/Asset) accounts | ||||
|   - Shared [asset accounts](https://en.wikipedia.org/wiki/Asset) ([household accounts](https://en.wikipedia.org/wiki/Household)) | ||||
|   - Saving accounts | ||||
|   - Credit cards | ||||
| - It's possible to create, change and manage money using _[budgets](https://en.wikipedia.org/wiki/Envelope_system)_; | ||||
| - Organize transactions using categories; | ||||
| - Save towards a goal using [piggy banks](https://en.wikipedia.org/wiki/Piggy_bank); | ||||
| - Predict and anticipate [bills](https://en.wikipedia.org/wiki/Invoice); | ||||
| - View income / expense [reports](https://en.wikipedia.org/wiki/Financial_statement); | ||||
| - Organize expenses using tags; | ||||
| - Lots of help text in case you don't get it. | ||||
|  | ||||
| Everything is organised: | ||||
|  | ||||
| - Clear views that should show you how you're doing; | ||||
| - Easy navigation through your records; | ||||
| - Browse back and forth to see previous months or even years; | ||||
| - Lots of charts because we all love them; | ||||
| - Financial reporting showing you how well you are doing. | ||||
|  | ||||
| ## Screenshots | ||||
|  | ||||
| _Please note that everything in these screenshots is fictional and may not be realistic._ | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ## Running and installing | ||||
|  | ||||
| If you're still interested please read [the installation guide](https://github.com/JC5/firefly-iii/wiki/Installation),  | ||||
| [the upgrade guide](https://github.com/JC5/firefly-iii/wiki/Upgrade-instructions) (if applicable)  | ||||
| and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**. | ||||
|   | ||||
| If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/).  | ||||
| This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site. | ||||
|  | ||||
|  | ||||
| ## Current state | ||||
|  | ||||
| Firefly III is pretty much all grown up. Full test coverage (nerd alert!) is coming. One of the things on the todo-list | ||||
| is adding translations. | ||||
|  | ||||
| Questions, ideas, bugs or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)! | ||||
|  | ||||
| If you like this tool, feel free to [donate me some beer money](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2ZMV952UUSCLU&lc=NL&item_name=Development%20of%20Firefly¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted). | ||||
|  | ||||
| [](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102) | ||||
| [](https://codeclimate.com/github/JC5/firefly-iii) | ||||
| [](http://stillmaintained.com/JC5/firefly-iii) | ||||
| [](https://packagist.org/packages/grumpydictator/firefly-iii) | ||||
|  | ||||
| If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com). | ||||
							
								
								
									
										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') | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -1,12 +0,0 @@ | ||||
| <?php namespace FireflyIII\Commands; | ||||
| /** | ||||
|  * Class Command | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Commands | ||||
|  */ | ||||
| abstract class Command | ||||
| { | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										152
									
								
								app/Console/Commands/CreateImport.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								app/Console/Commands/CreateImport.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| <?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(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         // find the file | ||||
|         /** @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; | ||||
|         } | ||||
|  | ||||
|         // try to parse configuration data: | ||||
|         $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)); | ||||
|  | ||||
|         // put the file in the proper place: | ||||
|         Artisan::call('firefly:encrypt', ['file' => $file, 'key' => $job->key]); | ||||
|         $this->line('Stored import data...'); | ||||
|  | ||||
|         // store the configuration in the job: | ||||
|         $job->configuration = $configurationData; | ||||
|         $job->status        = 'settings_complete'; | ||||
|         $job->save(); | ||||
|         $this->line('Stored configuration...'); | ||||
|  | ||||
|         // if user wants to run it, do! | ||||
|         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 | ||||
|      */ | ||||
|     private function validArguments(): bool | ||||
|     { | ||||
|         // find the file | ||||
|         /** @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} {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)); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										125
									
								
								app/Console/Commands/Import.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								app/Console/Commands/Import.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
| <?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 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(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     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('Going to import job with key "' . $job->key . '" of type ' . $job->file_type); | ||||
|  | ||||
|         $monolog = Log::getMonolog(); | ||||
|         $handler = new CommandHandler($this); | ||||
|         $monolog->pushHandler($handler); | ||||
|  | ||||
|         $result = ImportProcedure::runImport($job); | ||||
|  | ||||
|  | ||||
|         /** | ||||
|          * @var int                $index | ||||
|          * @var TransactionJournal $journal | ||||
|          */ | ||||
|         foreach ($result as $index => $journal) { | ||||
|             if (!is_null($journal->id)) { | ||||
|                 $this->line(sprintf('Line #%d has been imported as transaction #%d.', $index, $journal->id)); | ||||
|                 continue; | ||||
|             } | ||||
|             $this->error(sprintf('Could not store line #%d', $index)); | ||||
|         } | ||||
|  | ||||
|         $this->line('The import has completed.'); | ||||
|  | ||||
|         // get any errors from the importer: | ||||
|         $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); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										101
									
								
								app/Console/Commands/ScanAttachments.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								app/Console/Commands/ScanAttachments.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| <?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 to grab file content: | ||||
|             try { | ||||
|                 $content = $disk->get($fileName); | ||||
|             } catch (FileNotFoundException $e) { | ||||
|                 $this->error(sprintf('Could not find data for attachment #%d', $attachment->id)); | ||||
|                 continue; | ||||
|             } | ||||
|             // try to decrypt content. | ||||
|             try { | ||||
|                 $decrypted = Crypt::decrypt($content); | ||||
|             } catch (DecryptException $e) { | ||||
|                 $this->error(sprintf('Could not decrypt data of attachment #%d', $attachment->id)); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // make temp file: | ||||
|             $tmpfname = tempnam(sys_get_temp_dir(), 'FireflyIII'); | ||||
|  | ||||
|             // store content in temp file: | ||||
|             file_put_contents($tmpfname, $decrypted); | ||||
|  | ||||
|             // get md5 and mime | ||||
|             $md5  = md5_file($tmpfname); | ||||
|             $mime = mime_content_type($tmpfname); | ||||
|  | ||||
|             // update attachment: | ||||
|             $attachment->md5  = $md5; | ||||
|             $attachment->mime = $mime; | ||||
|             $attachment->save(); | ||||
|  | ||||
|  | ||||
|             $this->line(sprintf('Fixed attachment #%d', $attachment->id)); | ||||
|  | ||||
|             // find file: | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										121
									
								
								app/Console/Commands/UpgradeDatabase.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								app/Console/Commands/UpgradeDatabase.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| <?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\Transaction; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Database\QueryException; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * 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 is strangely complex, because the HAVING modifier is a no-no. And subqueries in Laravel are weird. | ||||
|      */ | ||||
|     private function setTransactionIdentifier() | ||||
|     { | ||||
|         $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) { | ||||
|             // 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. | ||||
|             $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('This field is required for Firefly III version ' . config('firefly.version') . ' to run.'); | ||||
|                     $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.'); | ||||
|                     break 2; | ||||
|                 } | ||||
|                 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++; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										83
									
								
								app/Console/Commands/UpgradeFireflyInstructions.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								app/Console/Commands/UpgradeFireflyInstructions.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| <?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 Illuminate\Console\Command; | ||||
|  | ||||
| /** | ||||
|  * Class UpgradeFireflyInstructions | ||||
|  * | ||||
|  * @package FireflyIII\Console\Commands | ||||
|  */ | ||||
| class UpgradeFireflyInstructions extends Command | ||||
| { | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = 'Instructions in case of upgrade trouble.'; | ||||
|     /** | ||||
|      * The name and signature of the console command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'firefly:upgrade-instructions'; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         // | ||||
|         /** @var string $version */ | ||||
|         $version = config('firefly.version'); | ||||
|         $config  = config('upgrade.text'); | ||||
|         $text    = null; | ||||
|         foreach (array_keys($config) as $compare) { | ||||
|             // if string starts with: | ||||
|             $len = strlen($compare); | ||||
|             if (substr($version, 0, $len) === $compare) { | ||||
|                 $text = $config[$compare]; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         $this->line('+------------------------------------------------------------------------------+'); | ||||
|         $this->line(''); | ||||
|  | ||||
|         if (is_null($text)) { | ||||
|             $this->line('Thank you for installing Firefly III, v' . $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->line(''); | ||||
|         $this->line('+------------------------------------------------------------------------------+'); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										335
									
								
								app/Console/Commands/VerifyDatabase.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										335
									
								
								app/Console/Commands/VerifyDatabase.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,335 @@ | ||||
| <?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\Budget; | ||||
| use FireflyIII\Models\Category; | ||||
| use FireflyIII\Models\Tag; | ||||
| 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\Database\Eloquent\Builder; | ||||
| 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() | ||||
|     { | ||||
|         // accounts with no transactions. | ||||
|         $this->reportAccounts(); | ||||
|         // budgets with no limits | ||||
|         $this->reportBudgetLimits(); | ||||
|         // budgets with no transactions | ||||
|         $this->reportBudgets(); | ||||
|         // categories with no transactions | ||||
|         $this->reportCategories(); | ||||
|         // tags with no transactions | ||||
|         $this->reportTags(); | ||||
|         // 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(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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.user_id', 'users.email']) | ||||
|             ->whereNull('budget_limits.id') | ||||
|             ->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email']); | ||||
|  | ||||
|         /** @var stdClass $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has budget #' . $entry->id . ' ("' . Crypt::decrypt($entry->name) | ||||
|                     . '") which has no budget limits.'; | ||||
|             $this->line($line); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reports on budgets without any transactions. | ||||
|      */ | ||||
|     private function reportBudgets() | ||||
|     { | ||||
|         $set = Budget | ||||
|             ::leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id') | ||||
|             ->leftJoin('users', 'budgets.user_id', '=', 'users.id') | ||||
|             ->distinct() | ||||
|             ->whereNull('budget_transaction_journal.budget_id') | ||||
|             ->whereNull('budgets.deleted_at') | ||||
|             ->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email']); | ||||
|  | ||||
|         /** @var stdClass $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has budget #' . $entry->id . ' ("' . Crypt::decrypt($entry->name) | ||||
|                     . '") which has no transactions.'; | ||||
|             $this->line($line); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reports on categories without any transactions. | ||||
|      */ | ||||
|     private function reportCategories() | ||||
|     { | ||||
|         $set = Category | ||||
|             ::leftJoin('category_transaction_journal', 'categories.id', '=', 'category_transaction_journal.category_id') | ||||
|             ->leftJoin('users', 'categories.user_id', '=', 'users.id') | ||||
|             ->distinct() | ||||
|             ->whereNull('category_transaction_journal.category_id') | ||||
|             ->whereNull('categories.deleted_at') | ||||
|             ->get(['categories.id', 'categories.name', 'categories.user_id', 'users.email']); | ||||
|  | ||||
|         /** @var stdClass $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has category #' . $entry->id . ' ("' . Crypt::decrypt($entry->name) | ||||
|                     . '") which has no transactions.'; | ||||
|             $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 . '"' | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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 | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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 tags without any transactions. | ||||
|      */ | ||||
|     private function reportTags() | ||||
|     { | ||||
|         $set = Tag | ||||
|             ::leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id') | ||||
|             ->leftJoin('users', 'tags.user_id', '=', 'users.id') | ||||
|             ->distinct() | ||||
|             ->whereNull('tag_transaction_journal.tag_id') | ||||
|             ->whereNull('tags.deleted_at') | ||||
|             ->get(['tags.id', 'tags.tag', 'tags.user_id', 'users.email']); | ||||
|  | ||||
|         /** @var stdClass $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has tag #' . $entry->id . ' ("' . $entry->tag | ||||
|                     . '") which has no transactions.'; | ||||
|             $this->line($line); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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 | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										62
									
								
								app/Console/Kernel.php
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										62
									
								
								app/Console/Kernel.php
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,16 +1,53 @@ | ||||
| <?php namespace FireflyIII\Console; | ||||
| <?php | ||||
| /** | ||||
|  * Kernel.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; | ||||
|  | ||||
| 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 FireflyIII\Console\Commands\VerifyDatabase; | ||||
| use Illuminate\Console\Scheduling\Schedule; | ||||
| use Illuminate\Foundation\Console\Kernel as ConsoleKernel; | ||||
|  | ||||
| /** | ||||
|  * Class Kernel | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Console | ||||
|  */ | ||||
| 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. | ||||
| @@ -19,19 +56,34 @@ class Kernel extends ConsoleKernel | ||||
|      */ | ||||
|     protected $commands | ||||
|         = [ | ||||
|             UpgradeFireflyInstructions::class, | ||||
|             VerifyDatabase::class, | ||||
|             Import::class, | ||||
|             CreateImport::class, | ||||
|             EncryptFile::class, | ||||
|             ScanAttachments::class, | ||||
|             UpgradeDatabase::class, | ||||
|  | ||||
|         ]; | ||||
|  | ||||
|     /** | ||||
|      * Register the Closure based commands for the application. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     protected function commands() | ||||
|     { | ||||
|         require base_path('routes/console.php'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Define the application's command schedule. | ||||
|      * | ||||
|      * @param  \Illuminate\Console\Scheduling\Schedule $schedule | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     protected function schedule(Schedule $schedule) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										42
									
								
								app/Events/ConfirmedUser.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								app/Events/ConfirmedUser.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ConfirmedUser.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Events; | ||||
|  | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
|  | ||||
| /** | ||||
|  * Class ConfirmedUser | ||||
|  * | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class ConfirmedUser extends Event | ||||
| { | ||||
|     use SerializesModels; | ||||
|  | ||||
|     public $ipAddress; | ||||
|     public $user; | ||||
|  | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      * | ||||
|      * @param  User  $user | ||||
|      * @param string $ipAddress | ||||
|      */ | ||||
|     public function __construct(User $user, string $ipAddress) | ||||
|     { | ||||
|         $this->user      = $user; | ||||
|         $this->ipAddress = $ipAddress; | ||||
|     } | ||||
| } | ||||
| @@ -1,14 +1,23 @@ | ||||
| <?php namespace FireflyIII\Events; | ||||
| <?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; | ||||
|  | ||||
| /** | ||||
|  * Class Event | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| abstract class Event | ||||
| { | ||||
|  | ||||
|     // | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,34 +0,0 @@ | ||||
| <?php namespace FireflyIII\Events; | ||||
|  | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
|  | ||||
| /** | ||||
|  * Class JournalCreated | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class JournalCreated extends Event | ||||
| { | ||||
|  | ||||
|     use SerializesModels; | ||||
|  | ||||
|     public $journal; | ||||
|     public $piggyBankId; | ||||
|  | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      * | ||||
|      * @param TransactionJournal $journal | ||||
|      * @param                    $piggyBankId | ||||
|      */ | ||||
|     public function __construct(TransactionJournal $journal, $piggyBankId) | ||||
|     { | ||||
|         // | ||||
|         $this->journal     = $journal; | ||||
|         $this->piggyBankId = $piggyBankId; | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,25 +0,0 @@ | ||||
| <?php namespace FireflyIII\Events; | ||||
|  | ||||
| use Illuminate\Queue\SerializesModels; | ||||
|  | ||||
| /** | ||||
|  * Class JournalDeleted | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class JournalDeleted extends Event | ||||
| { | ||||
|  | ||||
|     use SerializesModels; | ||||
|  | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,30 +0,0 @@ | ||||
| <?php namespace FireflyIII\Events; | ||||
|  | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
|  | ||||
| /** | ||||
|  * Class JournalSaved | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class JournalSaved extends Event | ||||
| { | ||||
|  | ||||
|     use SerializesModels; | ||||
|  | ||||
|     public $journal; | ||||
|  | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      * | ||||
|      * @param TransactionJournal $journal | ||||
|      */ | ||||
|     public function __construct(TransactionJournal $journal) | ||||
|     { | ||||
|         // | ||||
|         $this->journal = $journal; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										42
									
								
								app/Events/RegisteredUser.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								app/Events/RegisteredUser.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| <?php | ||||
| /** | ||||
|  * RegisteredUser.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 RegisteredUser | ||||
|  * | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class RegisteredUser 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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										42
									
								
								app/Events/ResentConfirmation.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								app/Events/ResentConfirmation.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ResentConfirmation.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Events; | ||||
|  | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
|  | ||||
| /** | ||||
|  * Class ResentConfirmation | ||||
|  * | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class ResentConfirmation extends Event | ||||
| { | ||||
|     use SerializesModels; | ||||
|  | ||||
|     public $ipAddress; | ||||
|     public $user; | ||||
|  | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      * | ||||
|      * @param  User  $user | ||||
|      * @param string $ipAddress | ||||
|      */ | ||||
|     public function __construct(User $user, string $ipAddress) | ||||
|     { | ||||
|         $this->user      = $user; | ||||
|         $this->ipAddress = $ipAddress; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										50
									
								
								app/Events/StoredBudgetLimit.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								app/Events/StoredBudgetLimit.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| <?php | ||||
| /** | ||||
|  * StoredBudgetLimit.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Events; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Models\BudgetLimit; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
|  | ||||
| /** | ||||
|  * Class StoredBudgetLimit | ||||
|  * | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class StoredBudgetLimit extends Event | ||||
| { | ||||
|  | ||||
|     use SerializesModels; | ||||
|  | ||||
|     /** @var  BudgetLimit */ | ||||
|     public $budgetLimit; | ||||
|  | ||||
|     /** @var  Carbon */ | ||||
|     public $end; // the only variable we can't get from the budget limit (if necessary). | ||||
|  | ||||
|     /** | ||||
|      * BudgetLimitEvents constructor. | ||||
|      * | ||||
|      * @param BudgetLimit $budgetLimit | ||||
|      * @param Carbon      $end | ||||
|      */ | ||||
|     public function __construct(BudgetLimit $budgetLimit, Carbon $end) | ||||
|     { | ||||
|         // | ||||
|         $this->budgetLimit = $budgetLimit; | ||||
|         $this->end         = $end; | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										46
									
								
								app/Events/StoredTransactionJournal.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								app/Events/StoredTransactionJournal.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| <?php | ||||
| /** | ||||
|  * StoredTransactionJournal.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\Models\TransactionJournal; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
|  | ||||
| /** | ||||
|  * Class StoredTransactionJournal | ||||
|  * | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class StoredTransactionJournal extends Event | ||||
| { | ||||
|  | ||||
|     use SerializesModels; | ||||
|  | ||||
|     public $journal; | ||||
|     public $piggyBankId; | ||||
|  | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      * | ||||
|      * @param TransactionJournal $journal | ||||
|      * @param int                $piggyBankId | ||||
|      */ | ||||
|     public function __construct(TransactionJournal $journal, int $piggyBankId) | ||||
|     { | ||||
|         // | ||||
|         $this->journal     = $journal; | ||||
|         $this->piggyBankId = $piggyBankId; | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										50
									
								
								app/Events/UpdatedBudgetLimit.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								app/Events/UpdatedBudgetLimit.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| <?php | ||||
| /** | ||||
|  * UpdatedBudgetLimit.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Events; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Models\BudgetLimit; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
|  | ||||
| /** | ||||
|  * Class UpdatedBudgetLimit | ||||
|  * | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class UpdatedBudgetLimit extends Event | ||||
| { | ||||
|  | ||||
|     use SerializesModels; | ||||
|  | ||||
|     /** @var  BudgetLimit */ | ||||
|     public $budgetLimit; | ||||
|  | ||||
|     /** @var  Carbon */ | ||||
|     public $end; // the only variable we can't get from the budget limit (if necessary). | ||||
|  | ||||
|     /** | ||||
|      * BudgetLimitEvents constructor. | ||||
|      * | ||||
|      * @param BudgetLimit $budgetLimit | ||||
|      * @param Carbon      $end | ||||
|      */ | ||||
|     public function __construct(BudgetLimit $budgetLimit, Carbon $end) | ||||
|     { | ||||
|         // | ||||
|         $this->budgetLimit = $budgetLimit; | ||||
|         $this->end         = $end; | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										42
									
								
								app/Events/UpdatedTransactionJournal.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								app/Events/UpdatedTransactionJournal.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| <?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; | ||||
|  | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
|  | ||||
| /** | ||||
|  * Class UpdatedTransactionJournal | ||||
|  * | ||||
|  * @package FireflyIII\Events | ||||
|  */ | ||||
| class UpdatedTransactionJournal extends Event | ||||
| { | ||||
|  | ||||
|     use SerializesModels; | ||||
|  | ||||
|     public $journal; | ||||
|  | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      * | ||||
|      * @param TransactionJournal $journal | ||||
|      */ | ||||
|     public function __construct(TransactionJournal $journal) | ||||
|     { | ||||
|         // | ||||
|         $this->journal = $journal; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,12 +1,21 @@ | ||||
| <?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; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class FireflyException | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Exceptions | ||||
|  */ | ||||
| class FireflyException extends \Exception | ||||
|   | ||||
							
								
								
									
										94
									
								
								app/Exceptions/Handler.php
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										94
									
								
								app/Exceptions/Handler.php
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,18 +1,35 @@ | ||||
| <?php namespace FireflyIII\Exceptions; | ||||
| <?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 ErrorException; | ||||
| use Exception; | ||||
| use FireflyIII\Jobs\MailError; | ||||
| 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 Symfony\Component\HttpKernel\Exception\HttpException; | ||||
|  | ||||
| /** | ||||
|  * Class Handler | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Exceptions | ||||
|  */ | ||||
| class Handler extends ExceptionHandler | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * A list of the exception types that should not be reported. | ||||
|      * | ||||
| @@ -20,40 +37,87 @@ class Handler extends ExceptionHandler | ||||
|      */ | ||||
|     protected $dontReport | ||||
|         = [ | ||||
|             'Symfony\Component\HttpKernel\Exception\HttpException' | ||||
|             AuthenticationException::class, | ||||
|             AuthorizationException::class, | ||||
|             HttpException::class, | ||||
|             ModelNotFoundException::class, | ||||
|             TokenMismatchException::class, | ||||
|             ValException::class, | ||||
|         ]; | ||||
|  | ||||
|     /** | ||||
|      * Render an exception into an HTTP response. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @param  \Exception               $e | ||||
|      * @SuppressWarnings(PHPMD.ShortVariable) | ||||
|      * @param  \Exception               $exception | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function render($request, Exception $e) | ||||
|     public function render($request, Exception $exception) | ||||
|     { | ||||
|         if ($e instanceof HttpException) { | ||||
|             return $this->renderHttpException($e); | ||||
|         } else { | ||||
|             return parent::render($request, $e); | ||||
|         if ($exception instanceof FireflyException || $exception instanceof ErrorException) { | ||||
|  | ||||
|             $isDebug = env('APP_DEBUG', false); | ||||
|  | ||||
|             return response()->view('errors.FireflyException', ['exception' => $exception, 'debug' => $isDebug], 500); | ||||
|         } | ||||
|  | ||||
|         return parent::render($request, $exception); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Report or log an exception. | ||||
|      * | ||||
|      * This is a great spot to send exceptions to Sentry, Bugsnag, etc. | ||||
|      * @SuppressWarnings(PHPMD.ShortVariable) | ||||
|      * | ||||
|      * @param  \Exception $e | ||||
|      * @param  Exception $exception | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function report(Exception $e) | ||||
|     public function report(Exception $exception) | ||||
|     { | ||||
|         parent::report($e); | ||||
|         if ($exception instanceof FireflyException || $exception instanceof ErrorException) { | ||||
|             $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(), | ||||
|                 'time'         => date('r'), | ||||
|                 'stackTrace'   => $exception->getTraceAsString(), | ||||
|                 'file'         => $exception->getFile(), | ||||
|                 'line'         => $exception->getLine(), | ||||
|                 'code'         => $exception->getCode(), | ||||
|             ]; | ||||
|  | ||||
|             // create job that will mail. | ||||
|             $ip  = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'; | ||||
|             $job = new MailError($userData, env('SITE_OWNER', ''), $ip, $data); | ||||
|             dispatch($job); | ||||
|         } | ||||
|  | ||||
|         parent::report($exception); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Convert an authentication exception into an unauthenticated response. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     protected function unauthenticated($request) | ||||
|     { | ||||
|         if ($request->expectsJson()) { | ||||
|             return response()->json(['error' => 'Unauthenticated.'], 401); | ||||
|         } | ||||
|  | ||||
|         return redirect()->guest('login'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,21 @@ | ||||
| <?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; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class NotImplementedException | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Exceptions | ||||
|  */ | ||||
| class NotImplementedException extends \Exception | ||||
|   | ||||
| @@ -1,10 +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 | ||||
|   | ||||
							
								
								
									
										150
									
								
								app/Export/Collector/AttachmentCollector.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								app/Export/Collector/AttachmentCollector.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| <?php | ||||
| /** | ||||
|  * AttachmentCollector.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 Amount; | ||||
| 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; | ||||
| use Log; | ||||
| use Storage; | ||||
|  | ||||
| /** | ||||
|  * Class AttachmentCollector | ||||
|  * | ||||
|  * @package FireflyIII\Export\Collector | ||||
|  */ | ||||
| class AttachmentCollector extends BasicCollector implements CollectorInterface | ||||
| { | ||||
|     /** @var string */ | ||||
|     private $explanationString = ''; | ||||
|     /** @var \Illuminate\Contracts\Filesystem\Filesystem */ | ||||
|     private $exportDisk; | ||||
|     /** @var  AttachmentRepositoryInterface */ | ||||
|     private $repository; | ||||
|     /** @var \Illuminate\Contracts\Filesystem\Filesystem */ | ||||
|     private $uploadDisk; | ||||
|  | ||||
|     /** | ||||
|      * AttachmentCollector constructor. | ||||
|      * | ||||
|      * @param ExportJob $job | ||||
|      */ | ||||
|     public function __construct(ExportJob $job) | ||||
|     { | ||||
|         /** @var AttachmentRepositoryInterface repository */ | ||||
|         $this->repository = app(AttachmentRepositoryInterface::class); | ||||
|         // make storage: | ||||
|         $this->uploadDisk = Storage::disk('upload'); | ||||
|         $this->exportDisk = Storage::disk('export'); | ||||
|  | ||||
|         parent::__construct($job); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function run(): bool | ||||
|     { | ||||
|         // grab all the users attachments: | ||||
|         $attachments = $this->getAttachments(); | ||||
|  | ||||
|         /** @var Attachment $attachment */ | ||||
|         foreach ($attachments as $attachment) { | ||||
|             $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); | ||||
|         $this->getFiles()->push($file); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Attachment $attachment | ||||
|      */ | ||||
|     private function explain(Attachment $attachment) | ||||
|     { | ||||
|         /** @var TransactionJournal $journal */ | ||||
|         $journal = $attachment->attachable; | ||||
|         $args    = [ | ||||
|             'attachment_name' => e($attachment->filename), | ||||
|             'attachment_id'   => $attachment->id, | ||||
|             'type'            => strtolower($journal->transactionType->type), | ||||
|             'description'     => e($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"; | ||||
|         Log::debug('Appended explanation string', ['string' => $string]); | ||||
|         $this->explanationString .= $string; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Attachment $attachment | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function exportAttachment(Attachment $attachment): bool | ||||
|     { | ||||
|         $file = $attachment->fileName(); | ||||
|         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); | ||||
|  | ||||
|                 // explain: | ||||
|                 $this->explain($attachment); | ||||
|             } catch (DecryptException $e) { | ||||
|                 Log::error('Catchable error: could not decrypt attachment #' . $attachment->id . ' because: ' . $e->getMessage()); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the new file name for the export file. | ||||
|      * | ||||
|      * @param $attachment | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     private function exportFileName($attachment): string | ||||
|     { | ||||
|  | ||||
|         return sprintf('%s-Attachment nr. %s - %s', $this->job->key, strval($attachment->id), $attachment->filename); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     private function getAttachments(): Collection | ||||
|     { | ||||
|         $attachments = $this->repository->get(); | ||||
|  | ||||
|         return $attachments; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										60
									
								
								app/Export/Collector/BasicCollector.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								app/Export/Collector/BasicCollector.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BasicCollector.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 FireflyIII\Models\ExportJob; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class BasicCollector | ||||
|  * | ||||
|  * @package FireflyIII\Export\Collector | ||||
|  */ | ||||
| class BasicCollector | ||||
| { | ||||
|     /** @var ExportJob */ | ||||
|     protected $job; | ||||
|     /** @var Collection */ | ||||
|     private $files; | ||||
|  | ||||
|     /** | ||||
|      * BasicCollector constructor. | ||||
|      * | ||||
|      * @param ExportJob $job | ||||
|      */ | ||||
|     public function __construct(ExportJob $job) | ||||
|     { | ||||
|         $this->files = new Collection; | ||||
|         $this->job   = $job; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getFiles(): Collection | ||||
|     { | ||||
|         return $this->files; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $files | ||||
|      */ | ||||
|     public function setFiles(Collection $files) | ||||
|     { | ||||
|         $this->files = $files; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										41
									
								
								app/Export/Collector/CollectorInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								app/Export/Collector/CollectorInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| <?php | ||||
| /** | ||||
|  * CollectorInterface.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 Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface CollectorInterface | ||||
|  * | ||||
|  * @package FireflyIII\Export\Collector | ||||
|  */ | ||||
| interface CollectorInterface | ||||
| { | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getFiles(): Collection; | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function run(): bool; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $files | ||||
|      * | ||||
|      */ | ||||
|     public function setFiles(Collection $files); | ||||
|  | ||||
| } | ||||
							
								
								
									
										201
									
								
								app/Export/Collector/UploadCollector.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								app/Export/Collector/UploadCollector.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,201 @@ | ||||
| <?php | ||||
| /** | ||||
|  * UploadCollector.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 Crypt; | ||||
| use FireflyIII\Models\ExportJob; | ||||
| use Illuminate\Contracts\Encryption\DecryptException; | ||||
| use Log; | ||||
| use Storage; | ||||
|  | ||||
| /** | ||||
|  * Class UploadCollector | ||||
|  * | ||||
|  * @package FireflyIII\Export\Collector | ||||
|  */ | ||||
| class UploadCollector extends BasicCollector implements CollectorInterface | ||||
| { | ||||
|     /** @var string */ | ||||
|     private $expected; | ||||
|     /** @var \Illuminate\Contracts\Filesystem\Filesystem */ | ||||
|     private $exportDisk; | ||||
|     private $importKeys = []; | ||||
|     /** @var \Illuminate\Contracts\Filesystem\Filesystem */ | ||||
|     private $uploadDisk; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * AttachmentCollector constructor. | ||||
|      * | ||||
|      * @param ExportJob $job | ||||
|      */ | ||||
|     public function __construct(ExportJob $job) | ||||
|     { | ||||
|         parent::__construct($job); | ||||
|  | ||||
|         Log::debug('Going to collect attachments', ['key' => $job->key]); | ||||
|  | ||||
|         // make storage: | ||||
|         $this->uploadDisk = Storage::disk('upload'); | ||||
|         $this->exportDisk = Storage::disk('export'); | ||||
|  | ||||
|         // file names associated with the old import routine. | ||||
|         $this->expected = 'csv-upload-' . auth()->user()->id . '-'; | ||||
|  | ||||
|         // for the new import routine: | ||||
|         $this->getImportKeys(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function run(): bool | ||||
|     { | ||||
|         // grab upload directory. | ||||
|         $files = $this->uploadDisk->files(); | ||||
|  | ||||
|         foreach ($files as $entry) { | ||||
|             $this->processUpload($entry); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     private function getImportKeys() | ||||
|     { | ||||
|         $set = auth()->user()->importJobs()->where('status', 'import_complete')->get(['import_jobs.*']); | ||||
|         if ($set->count() > 0) { | ||||
|             $keys             = $set->pluck('key')->toArray(); | ||||
|             $this->importKeys = $keys; | ||||
|  | ||||
|         } | ||||
|         Log::debug('Valid import keys are ', $this->importKeys); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $entry | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     private function getOriginalUploadDate(string $entry): string | ||||
|     { | ||||
|         // this is an original upload. | ||||
|         $parts          = explode('-', str_replace(['.csv.encrypted', $this->expected], '', $entry)); | ||||
|         $originalUpload = intval($parts[1]); | ||||
|         $date           = date('Y-m-d \a\t H-i-s', $originalUpload); | ||||
|  | ||||
|         return $date; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $entry | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function isImportFile(string $entry): bool | ||||
|     { | ||||
|         $name = str_replace('.upload', '', $entry); | ||||
|         if (in_array($name, $this->importKeys)) { | ||||
|             Log::debug(sprintf('Import file "%s" is in array', $name), $this->importKeys); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|         Log::debug(sprintf('Import file "%s" is NOT in array', $name), $this->importKeys); | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $entry | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function isOldImport(string $entry): bool | ||||
|     { | ||||
|         $len = strlen($this->expected); | ||||
|         // file is part of the old import routine: | ||||
|         if (substr($entry, 0, $len) === $this->expected) { | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $entry | ||||
|      */ | ||||
|     private function processUpload(string $entry) | ||||
|     { | ||||
|         // file is old import: | ||||
|         if ($this->isOldImport($entry)) { | ||||
|             $this->saveOldImportFile($entry); | ||||
|         } | ||||
|  | ||||
|         // file is current import. | ||||
|         if ($this->isImportFile($entry)) { | ||||
|             $this->saveImportFile($entry); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $entry | ||||
|      */ | ||||
|     private function saveImportFile(string $entry) | ||||
|     { | ||||
|         // find job associated with import file: | ||||
|         $name    = str_replace('.upload', '', $entry); | ||||
|         $job     = auth()->user()->importJobs()->where('key', $name)->first(); | ||||
|         $content = ''; | ||||
|         try { | ||||
|             $content = Crypt::decrypt($this->uploadDisk->get($entry)); | ||||
|         } catch (DecryptException $e) { | ||||
|             Log::error('Could not decrypt old import file ' . $entry . '. Skipped because ' . $e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         if (!is_null($job) && 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->getFiles()->push($file); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $entry | ||||
|      */ | ||||
|     private function saveOldImportFile(string $entry) | ||||
|     { | ||||
|         $content = ''; | ||||
|         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) { | ||||
|             // add to export disk. | ||||
|             $date = $this->getOriginalUploadDate($entry); | ||||
|             $file = $this->job->key . '-Old import dated ' . $date . '.csv'; | ||||
|             $this->exportDisk->put($file, $content); | ||||
|             $this->getFiles()->push($file); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										68
									
								
								app/Export/ConfigurationFile.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								app/Export/ConfigurationFile.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ConfigurationFile.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 FireflyIII\Export\Entry\Entry; | ||||
| use FireflyIII\Models\ExportJob; | ||||
| 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 string | ||||
|      */ | ||||
|     public function make(): string | ||||
|     { | ||||
|         $fields = array_keys(Entry::getFieldsAndTypes()); | ||||
|         $types  = Entry::getFieldsAndTypes(); | ||||
|  | ||||
|         $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'; | ||||
|         $this->exportDisk->put($file, json_encode($configuration, JSON_PRETTY_PRINT)); | ||||
|  | ||||
|         return $file; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										132
									
								
								app/Export/Entry/Entry.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								app/Export/Entry/Entry.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | ||||
| <?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 FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * 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 | ||||
|  * | ||||
|  * @package FireflyIII\Export\Entry | ||||
|  */ | ||||
| final class Entry | ||||
| { | ||||
|     /** @var  string */ | ||||
|     public $amount; | ||||
|     /** @var  EntryBill */ | ||||
|     public $bill; | ||||
|     /** @var  EntryBudget */ | ||||
|     public $budget; | ||||
|     /** @var  EntryCategory */ | ||||
|     public $category; | ||||
|     /** @var  string */ | ||||
|     public $date; | ||||
|     /** @var  string */ | ||||
|     public $description; | ||||
|     /** @var  EntryAccount */ | ||||
|     public $destinationAccount; | ||||
|     /** @var  Collection */ | ||||
|     public $destinationAccounts; | ||||
|     /** @var  EntryAccount */ | ||||
|     public $sourceAccount; | ||||
|     /** @var  Collection */ | ||||
|     public $sourceAccounts; | ||||
|  | ||||
|     /** | ||||
|      * Entry constructor. | ||||
|      */ | ||||
|     private function __construct() | ||||
|     { | ||||
|         $this->sourceAccounts      = new Collection; | ||||
|         $this->destinationAccounts = new Collection; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param TransactionJournal $journal | ||||
|      * | ||||
|      * @return Entry | ||||
|      */ | ||||
|     public static function fromJournal(TransactionJournal $journal) | ||||
|     { | ||||
|  | ||||
|         $entry              = new self; | ||||
|         $entry->description = $journal->description; | ||||
|         $entry->date        = $journal->date->format('Y-m-d'); | ||||
|         $entry->amount      = TransactionJournal::amount($journal); | ||||
|  | ||||
|         $entry->budget   = new EntryBudget($journal->budgets->first()); | ||||
|         $entry->category = new EntryCategory($journal->categories->first()); | ||||
|         $entry->bill     = new EntryBill($journal->bill); | ||||
|  | ||||
|         $sources                   = TransactionJournal::sourceAccountList($journal); | ||||
|         $destinations              = TransactionJournal::destinationAccountList($journal); | ||||
|         $entry->sourceAccount      = new EntryAccount($sources->first()); | ||||
|         $entry->destinationAccount = new EntryAccount($destinations->first()); | ||||
|  | ||||
|         foreach ($sources as $source) { | ||||
|             $entry->sourceAccounts->push(new EntryAccount($source)); | ||||
|         } | ||||
|  | ||||
|         foreach ($destinations as $destination) { | ||||
|             $entry->destinationAccounts->push(new EntryAccount($destination)); | ||||
|         } | ||||
|  | ||||
|         return $entry; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     public static function getFieldsAndTypes(): array | ||||
|     { | ||||
|         // key = field name (see top of class) | ||||
|         // value = field type (see csv.php under 'roles') | ||||
|         return [ | ||||
|             'description'                => 'description', | ||||
|             'amount'                     => 'amount', | ||||
|             'date'                       => 'date-transaction', | ||||
|             'source_account_id'          => 'account-id', | ||||
|             'source_account_name'        => 'account-name', | ||||
|             'source_account_iban'        => 'account-iban', | ||||
|             'source_account_type'        => '_ignore', | ||||
|             'source_account_number'      => 'account-number', | ||||
|             'destination_account_id'     => 'opposing-id', | ||||
|             'destination_account_name'   => 'opposing-name', | ||||
|             'destination_account_iban'   => 'opposing-iban', | ||||
|             'destination_account_type'   => '_ignore', | ||||
|             'destination_account_number' => 'account-number', | ||||
|             'budget_id'                  => 'budget-id', | ||||
|             'budget_name'                => 'budget-name', | ||||
|             'category_id'                => 'category-id', | ||||
|             'category_name'              => 'category-name', | ||||
|             'bill_id'                    => 'bill-id', | ||||
|             'bill_name'                  => 'bill-name', | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										49
									
								
								app/Export/Entry/EntryAccount.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								app/Export/Entry/EntryAccount.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| <?php | ||||
| /** | ||||
|  * EntryAccount.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 FireflyIII\Models\Account; | ||||
|  | ||||
| /** | ||||
|  * Class EntryAccount | ||||
|  * | ||||
|  * @package FireflyIII\Export\Entry | ||||
|  */ | ||||
| class EntryAccount | ||||
| { | ||||
|     /** @var  int */ | ||||
|     public $accountId; | ||||
|     /** @var  string */ | ||||
|     public $iban; | ||||
|     /** @var  string */ | ||||
|     public $name; | ||||
|     /** @var  string */ | ||||
|     public $number; | ||||
|     /** @var  string */ | ||||
|     public $type; | ||||
|  | ||||
|     /** | ||||
|      * EntryAccount constructor. | ||||
|      * | ||||
|      * @param Account $account | ||||
|      */ | ||||
|     public function __construct(Account $account) | ||||
|     { | ||||
|         $this->accountId = $account->id; | ||||
|         $this->name      = $account->name; | ||||
|         $this->iban      = $account->iban; | ||||
|         $this->type      = $account->accountType->type; | ||||
|         $this->number    = $account->getMeta('accountNumber'); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										43
									
								
								app/Export/Entry/EntryBill.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								app/Export/Entry/EntryBill.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| <?php | ||||
| /** | ||||
|  * EntryBill.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 FireflyIII\Models\Bill; | ||||
|  | ||||
| /** | ||||
|  * Class EntryBill | ||||
|  * | ||||
|  * @package FireflyIII\Export\Entry | ||||
|  */ | ||||
| class EntryBill | ||||
| { | ||||
|     /** @var  int */ | ||||
|     public $billId = ''; | ||||
|     /** @var  string */ | ||||
|     public $name = ''; | ||||
|  | ||||
|     /** | ||||
|      * EntryBill constructor. | ||||
|      * | ||||
|      * @param Bill $bill | ||||
|      */ | ||||
|     public function __construct(Bill $bill = null) | ||||
|     { | ||||
|         if (!is_null($bill)) { | ||||
|             $this->billId = $bill->id; | ||||
|             $this->name   = $bill->name; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										43
									
								
								app/Export/Entry/EntryBudget.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								app/Export/Entry/EntryBudget.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| <?php | ||||
| /** | ||||
|  * EntryBudget.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 FireflyIII\Models\Budget; | ||||
|  | ||||
| /** | ||||
|  * Class EntryBudget | ||||
|  * | ||||
|  * @package FireflyIII\Export\Entry | ||||
|  */ | ||||
| class EntryBudget | ||||
| { | ||||
|     /** @var  int */ | ||||
|     public $budgetId = ''; | ||||
|     /** @var  string */ | ||||
|     public $name = ''; | ||||
|  | ||||
|     /** | ||||
|      * EntryBudget constructor. | ||||
|      * | ||||
|      * @param Budget $budget | ||||
|      */ | ||||
|     public function __construct(Budget $budget = null) | ||||
|     { | ||||
|         if (!is_null($budget)) { | ||||
|             $this->budgetId = $budget->id; | ||||
|             $this->name     = $budget->name; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										42
									
								
								app/Export/Entry/EntryCategory.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								app/Export/Entry/EntryCategory.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| <?php | ||||
| /** | ||||
|  * EntryCategory.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 FireflyIII\Models\Category; | ||||
|  | ||||
| /** | ||||
|  * Class EntryCategory | ||||
|  * | ||||
|  * @package FireflyIII\Export\Entry | ||||
|  */ | ||||
| class EntryCategory | ||||
| { | ||||
|     /** @var  int */ | ||||
|     public $categoryId = ''; | ||||
|     /** @var  string */ | ||||
|     public $name = ''; | ||||
|  | ||||
|     /** | ||||
|      * EntryCategory constructor. | ||||
|      * | ||||
|      * @param Category $category | ||||
|      */ | ||||
|     public function __construct(Category $category = null) | ||||
|     { | ||||
|         if (!is_null($category)) { | ||||
|             $this->categoryId = $category->id; | ||||
|             $this->name       = $category->name; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										59
									
								
								app/Export/Exporter/BasicExporter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								app/Export/Exporter/BasicExporter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BasicExporter.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\Exporter; | ||||
|  | ||||
|  | ||||
| use FireflyIII\Models\ExportJob; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class BasicExporter | ||||
|  * | ||||
|  * @package FireflyIII\Export\Exporter | ||||
|  */ | ||||
| class BasicExporter | ||||
| { | ||||
|     /** @var  ExportJob */ | ||||
|     protected $job; | ||||
|     private   $entries; | ||||
|  | ||||
|     /** | ||||
|      * BasicExporter constructor. | ||||
|      * | ||||
|      * @param ExportJob $job | ||||
|      */ | ||||
|     public function __construct(ExportJob $job) | ||||
|     { | ||||
|         $this->entries = new Collection; | ||||
|         $this->job     = $job; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getEntries(): Collection | ||||
|     { | ||||
|         return $this->entries; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      */ | ||||
|     public function setEntries(Collection $entries) | ||||
|     { | ||||
|         $this->entries = $entries; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										174
									
								
								app/Export/Exporter/CsvExporter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								app/Export/Exporter/CsvExporter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| <?php | ||||
| /** | ||||
|  * CsvExporter.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\Exporter; | ||||
|  | ||||
| use FireflyIII\Export\Entry\Entry; | ||||
| use FireflyIII\Export\Entry\EntryAccount; | ||||
| use FireflyIII\Models\ExportJob; | ||||
| use Illuminate\Support\Collection; | ||||
| use League\Csv\Writer; | ||||
| use SplFileObject; | ||||
|  | ||||
| /** | ||||
|  * Class CsvExporter | ||||
|  * | ||||
|  * @package FireflyIII\Export\Exporter | ||||
|  */ | ||||
| class CsvExporter extends BasicExporter implements ExporterInterface | ||||
| { | ||||
|     /** @var  string */ | ||||
|     private $fileName; | ||||
|  | ||||
|     /** | ||||
|      * CsvExporter constructor. | ||||
|      * | ||||
|      * @param ExportJob $job | ||||
|      */ | ||||
|     public function __construct(ExportJob $job) | ||||
|     { | ||||
|         parent::__construct($job); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getFileName(): string | ||||
|     { | ||||
|         return $this->fileName; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     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     = []; | ||||
|  | ||||
|         // Count the maximum number of sources and destinations each entry has. May need to expand the number of export fields: | ||||
|         $maxSourceAccounts = 1; | ||||
|         $maxDestAccounts   = 1; | ||||
|         /** @var Entry $entry */ | ||||
|         foreach ($this->getEntries() as $entry) { | ||||
|             $sources           = $entry->sourceAccounts->count(); | ||||
|             $destinations      = $entry->destinationAccounts->count(); | ||||
|             $maxSourceAccounts = max($maxSourceAccounts, $sources); | ||||
|             $maxDestAccounts   = max($maxDestAccounts, $destinations); | ||||
|         } | ||||
|         $rows[] = array_keys($this->getFieldsAndTypes($maxSourceAccounts, $maxDestAccounts)); | ||||
|  | ||||
|         /** @var Entry $entry */ | ||||
|         foreach ($this->getEntries() as $entry) { | ||||
|             // order is defined in Entry::getFieldsAndTypes. | ||||
|             $current    = [$entry->description, $entry->amount, $entry->date]; | ||||
|             $sourceData = $this->getAccountData($maxSourceAccounts, $entry->sourceAccounts); | ||||
|             $current    = array_merge($current, $sourceData); | ||||
|             $destData   = $this->getAccountData($maxDestAccounts, $entry->destinationAccounts); | ||||
|             $current    = array_merge($current, $destData); | ||||
|             $rest       = [$entry->budget->budgetId, $entry->budget->name, $entry->category->categoryId, $entry->category->name, $entry->bill->billId, | ||||
|                            $entry->bill->name]; | ||||
|             $current    = array_merge($current, $rest); | ||||
|             $rows[]     = $current; | ||||
|         } | ||||
|         $writer->insertAll($rows); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int        $max | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getAccountData(int $max, Collection $accounts): array | ||||
|     { | ||||
|         $current = []; | ||||
|         for ($i = 0; $i < $max; $i++) { | ||||
|             /** @var EntryAccount $source */ | ||||
|             $source        = $accounts->get($i); | ||||
|             $currentId     = ''; | ||||
|             $currentName   = ''; | ||||
|             $currentIban   = ''; | ||||
|             $currentType   = ''; | ||||
|             $currentNumber = ''; | ||||
|             if ($source) { | ||||
|                 $currentId     = $source->accountId; | ||||
|                 $currentName   = $source->name; | ||||
|                 $currentIban   = $source->iban; | ||||
|                 $currentType   = $source->type; | ||||
|                 $currentNumber = $source->number; | ||||
|             } | ||||
|             $current[] = $currentId; | ||||
|             $current[] = $currentName; | ||||
|             $current[] = $currentIban; | ||||
|             $current[] = $currentType; | ||||
|             $current[] = $currentNumber; | ||||
|         } | ||||
|         unset($source); | ||||
|  | ||||
|         return $current; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $sources | ||||
|      * @param int $destinations | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getFieldsAndTypes(int $sources, int $destinations): array | ||||
|     { | ||||
|         // key = field name (see top of class) | ||||
|         // value = field type (see csv.php under 'roles') | ||||
|         $array = [ | ||||
|             'description' => 'description', | ||||
|             'amount'      => 'amount', | ||||
|             'date'        => 'date-transaction', | ||||
|         ]; | ||||
|         for ($i = 0; $i < $sources; $i++) { | ||||
|             $array['source_account_' . $i . '_id']     = 'account-id'; | ||||
|             $array['source_account_' . $i . '_name']   = 'account-name'; | ||||
|             $array['source_account_' . $i . '_iban']   = 'account-iban'; | ||||
|             $array['source_account_' . $i . '_type']   = '_ignore'; | ||||
|             $array['source_account_' . $i . '_number'] = 'account-number'; | ||||
|         } | ||||
|         for ($i = 0; $i < $destinations; $i++) { | ||||
|             $array['destination_account_' . $i . '_id']     = 'account-id'; | ||||
|             $array['destination_account_' . $i . '_name']   = 'account-name'; | ||||
|             $array['destination_account_' . $i . '_iban']   = 'account-iban'; | ||||
|             $array['destination_account_' . $i . '_type']   = '_ignore'; | ||||
|             $array['destination_account_' . $i . '_number'] = 'account-number'; | ||||
|         } | ||||
|  | ||||
|         $array['budget_id']     = 'budget-id'; | ||||
|         $array['budget_name']   = 'budget-name'; | ||||
|         $array['category_id']   = 'category-id'; | ||||
|         $array['category_name'] = 'category-name'; | ||||
|         $array['bill_id']       = 'bill-id'; | ||||
|         $array['bill_name']     = 'bill-name'; | ||||
|  | ||||
|         return $array; | ||||
|     } | ||||
|  | ||||
|     private function tempFile() | ||||
|     { | ||||
|         $this->fileName = $this->job->key . '-records.csv'; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										46
									
								
								app/Export/Exporter/ExporterInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								app/Export/Exporter/ExporterInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ExporterInterface.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\Exporter; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface ExporterInterface | ||||
|  * | ||||
|  * @package FireflyIII\Export\Exporter | ||||
|  */ | ||||
| interface ExporterInterface | ||||
| { | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getEntries(): Collection; | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getFileName(): string; | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function run(): bool; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      */ | ||||
|     public function setEntries(Collection $entries); | ||||
|  | ||||
| } | ||||
							
								
								
									
										205
									
								
								app/Export/Processor.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								app/Export/Processor.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,205 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Processor.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 FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Export\Collector\AttachmentCollector; | ||||
| use FireflyIII\Export\Collector\UploadCollector; | ||||
| use FireflyIII\Export\Entry\Entry; | ||||
| use FireflyIII\Models\ExportJob; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use FireflyIII\Repositories\Journal\JournalTaskerInterface; | ||||
| use Illuminate\Filesystem\FilesystemAdapter; | ||||
| use Illuminate\Support\Collection; | ||||
| use Storage; | ||||
| use ZipArchive; | ||||
|  | ||||
| /** | ||||
|  * Class Processor | ||||
|  * | ||||
|  * @package FireflyIII\Export | ||||
|  */ | ||||
| class Processor | ||||
| { | ||||
|  | ||||
|     /** @var Collection */ | ||||
|     public $accounts; | ||||
|     /** @var  string */ | ||||
|     public $exportFormat; | ||||
|     /** @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 */ | ||||
|     private $files; | ||||
|     /** @var  Collection */ | ||||
|     private $journals; | ||||
|  | ||||
|     /** | ||||
|      * Processor constructor. | ||||
|      * | ||||
|      * @param array $settings | ||||
|      */ | ||||
|     public function __construct(array $settings) | ||||
|     { | ||||
|         // save settings | ||||
|         $this->settings           = $settings; | ||||
|         $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; | ||||
|         $this->exportEntries      = new Collection; | ||||
|         $this->files              = new Collection; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function collectAttachments(): bool | ||||
|     { | ||||
|         /** @var AttachmentCollector $attachmentCollector */ | ||||
|         $attachmentCollector = app(AttachmentCollector::class, [$this->job]); | ||||
|         $attachmentCollector->run(); | ||||
|         $this->files = $this->files->merge($attachmentCollector->getFiles()); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function collectJournals(): bool | ||||
|     { | ||||
|         /** @var JournalTaskerInterface $tasker */ | ||||
|         $tasker         = app(JournalTaskerInterface::class); | ||||
|         $this->journals = $tasker->getJournalsInRange($this->accounts, $this->settings['startDate'], $this->settings['endDate']); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function collectOldUploads(): bool | ||||
|     { | ||||
|         /** @var UploadCollector $uploadCollector */ | ||||
|         $uploadCollector = app(UploadCollector::class, [$this->job]); | ||||
|         $uploadCollector->run(); | ||||
|  | ||||
|         $this->files = $this->files->merge($uploadCollector->getFiles()); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function convertJournals(): bool | ||||
|     { | ||||
|         $count = 0; | ||||
|         /** @var TransactionJournal $journal */ | ||||
|         foreach ($this->journals as $journal) { | ||||
|             $this->exportEntries->push(Entry::fromJournal($journal)); | ||||
|             $count++; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function createConfigFile(): bool | ||||
|     { | ||||
|         $this->configurationMaker = app(ConfigurationFile::class, [$this->job]); | ||||
|         $this->files->push($this->configurationMaker->make()); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function createZipFile(): bool | ||||
|     { | ||||
|         $zip      = new ZipArchive; | ||||
|         $file     = $this->job->key . '.zip'; | ||||
|         $fullPath = storage_path('export') . '/' . $file; | ||||
|  | ||||
|         if ($zip->open($fullPath, ZipArchive::CREATE) !== true) { | ||||
|             throw new FireflyException('Cannot store zip file.'); | ||||
|         } | ||||
|         // for each file in the collection, add it to the zip file. | ||||
|         $disk = Storage::disk('export'); | ||||
|         foreach ($this->getFiles() as $entry) { | ||||
|             // is part of this job? | ||||
|             $zipFileName = str_replace($this->job->key . '-', '', $entry); | ||||
|             $zip->addFromString($zipFileName, $disk->get($entry)); | ||||
|         } | ||||
|  | ||||
|         $zip->close(); | ||||
|  | ||||
|         // delete the files: | ||||
|         $this->deleteFiles($disk); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function exportJournals(): bool | ||||
|     { | ||||
|         $exporterClass = config('firefly.export_formats.' . $this->exportFormat); | ||||
|         $exporter      = app($exporterClass, [$this->job]); | ||||
|         $exporter->setEntries($this->exportEntries); | ||||
|         $exporter->run(); | ||||
|         $this->files->push($exporter->getFileName()); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getFiles(): Collection | ||||
|     { | ||||
|         return $this->files; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param FilesystemAdapter $disk | ||||
|      */ | ||||
|     private function deleteFiles(FilesystemAdapter $disk) | ||||
|     { | ||||
|         foreach ($this->getFiles() as $file) { | ||||
|             $disk->delete($file); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,62 @@ | ||||
| <?php | ||||
| /** | ||||
|  * AccountChartGeneratorInterface.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\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 Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function revenueAccounts(Collection $accounts, Carbon $start, Carbon $end): array; | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * @param array   $labels | ||||
|      * @param array   $dataSet | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function single(Account $account, array $labels, array $dataSet): array; | ||||
| } | ||||
							
								
								
									
										132
									
								
								app/Generator/Chart/Account/ChartJsAccountChartGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								app/Generator/Chart/Account/ChartJsAccountChartGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ChartJsAccountChartGenerator.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\Account; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Models\Account; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * 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'  => []]]]; | ||||
|         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) { | ||||
|             $data['datasets'][] = [ | ||||
|                 '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'                 => $account->balances, | ||||
|             ]; | ||||
|         } | ||||
|         $data['count'] = count($data['datasets']); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function revenueAccounts(Collection $accounts, Carbon $start, Carbon $end): array | ||||
|     { | ||||
|         $data = [ | ||||
|             'count'  => 1, | ||||
|             'labels' => [], 'datasets' => [[ | ||||
|                                                'label' => trans('firefly.earned'), | ||||
|                                                'data'  => []]]]; | ||||
|         foreach ($accounts as $account) { | ||||
|             if ($account->difference > 0) { | ||||
|                 $data['labels'][]              = $account->name; | ||||
|                 $data['datasets'][0]['data'][] = $account->difference; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * @param array   $labels | ||||
|      * @param array   $dataSet | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function single(Account $account, array $labels, array $dataSet): array | ||||
|     { | ||||
|         $data = [ | ||||
|             'count'    => 1, | ||||
|             'labels'   => $labels, | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'label' => $account->name, | ||||
|                     'data'  => $dataSet, | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										44
									
								
								app/Generator/Chart/Bill/BillChartGeneratorInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								app/Generator/Chart/Bill/BillChartGeneratorInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BillChartGeneratorInterface.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\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; | ||||
|  | ||||
| } | ||||
							
								
								
									
										93
									
								
								app/Generator/Chart/Bill/ChartJsBillChartGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								app/Generator/Chart/Bill/ChartJsBillChartGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ChartJsBillChartGenerator.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\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 = [ | ||||
|             'datasets' => [ | ||||
|                 [ | ||||
|                     'data'            => [round($unpaid, 2), round(bcmul($paid, '-1'), 2)], | ||||
|                     'backgroundColor' => ['rgba(53, 124, 165,0.7)', 'rgba(0, 141, 76, 0.7)',], | ||||
|                 ], | ||||
|  | ||||
|             ], | ||||
|             'labels'   => [strval(trans('firefly.unpaid')), strval(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'][] = [ | ||||
|             'type'  => 'bar', | ||||
|             'label' => trans('firefly.minAmount'), | ||||
|             'data'  => $minAmount, | ||||
|         ]; | ||||
|         $data['datasets'][] = [ | ||||
|             'type'  => 'line', | ||||
|             'label' => trans('firefly.billEntry'), | ||||
|             'data'  => $actualAmount, | ||||
|         ]; | ||||
|         $data['datasets'][] = [ | ||||
|             'type'  => 'bar', | ||||
|             'label' => trans('firefly.maxAmount'), | ||||
|             'data'  => $maxAmount, | ||||
|         ]; | ||||
|  | ||||
|         $data['count'] = count($data['datasets']); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										64
									
								
								app/Generator/Chart/Budget/BudgetChartGeneratorInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								app/Generator/Chart/Budget/BudgetChartGeneratorInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BudgetChartGeneratorInterface.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\Budget; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface BudgetChartGeneratorInterface | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Budget | ||||
|  */ | ||||
| interface BudgetChartGeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * @param string     $dateFormat | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function budgetLimit(Collection $entries, string $dateFormat): 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 | ||||
|      * @param string     $viewRange | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function period(Collection $entries, string $viewRange) : array; | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $budgets | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function year(Collection $budgets, Collection $entries): array; | ||||
|  | ||||
| } | ||||
							
								
								
									
										210
									
								
								app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ChartJsBudgetChartGenerator.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\Budget; | ||||
|  | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
| use Navigation; | ||||
|  | ||||
| /** | ||||
|  * Class ChartJsBudgetChartGenerator | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Budget | ||||
|  */ | ||||
| class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface | ||||
| { | ||||
|     /** | ||||
|      * | ||||
|      * @param Collection $entries | ||||
|      * @param string     $dateFormat | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function budgetLimit(Collection $entries, string $dateFormat = 'month_and_day'): array | ||||
|     { | ||||
|         $format = strval(trans('config.' . $dateFormat)); | ||||
|         $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; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @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.overspent'), | ||||
|             'data'  => $overspent, | ||||
|         ]; | ||||
|         $data['datasets'][] = [ | ||||
|             'label' => trans('firefly.left'), | ||||
|             'data'  => $left, | ||||
|         ]; | ||||
|         $data['datasets'][] = [ | ||||
|             'label' => trans('firefly.spent'), | ||||
|             'data'  => $spent, | ||||
|         ]; | ||||
|  | ||||
|         $data['count'] = 3; | ||||
|  | ||||
|         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 $entries | ||||
|      * @param string     $viewRange | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function period(Collection $entries, string $viewRange) : array | ||||
|     { | ||||
|         $data = [ | ||||
|             'labels'   => [], | ||||
|             'datasets' => [ | ||||
|                 0 => [ | ||||
|                     'label' => trans('firefly.budgeted'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|                 1 => [ | ||||
|                     'label' => trans('firefly.spent'), | ||||
|                     'data'  => [], | ||||
|                 ], | ||||
|             ], | ||||
|             'count'    => 2, | ||||
|         ]; | ||||
|         foreach ($entries as $entry) { | ||||
|             $label            = Navigation::periodShow($entry['date'], $viewRange); | ||||
|             $data['labels'][] = $label; | ||||
|             // data set 0 is budgeted | ||||
|             // data set 1 is spent: | ||||
|             $data['datasets'][0]['data'][] = $entry['budgeted']; | ||||
|             $data['datasets'][1]['data'][] = round(($entry['spent'] * -1), 2); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         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; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,69 @@ | ||||
| <?php | ||||
| /** | ||||
|  * CategoryChartGeneratorInterface.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\Category; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface CategoryChartGeneratorInterface | ||||
|  * | ||||
|  * @package FireflyIII\Generator\Chart\Category | ||||
|  */ | ||||
| interface CategoryChartGeneratorInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $entries | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function all(Collection $entries): array; | ||||
|  | ||||
|     /** | ||||
|      * @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; | ||||
| } | ||||
							
								
								
									
										192
									
								
								app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ChartJsCategoryChartGenerator.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\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 : round(bcmul($spent, '-1'), 4); | ||||
|             $data['datasets'][1]['data'][] = bccomp($earned, '0') === 0 ? null : round($earned, 4); | ||||
|         } | ||||
|  | ||||
|         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 | ||||
|     { | ||||
|         // get labels from one of the categories (assuming there's at least one): | ||||
|         $first = $entries->first(); | ||||
|         $data  = ['count' => 0, 'labels' => array_keys($first['spent']), 'datasets' => [],]; | ||||
|  | ||||
|         // 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; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @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; | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,58 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ChartJsPiggyBankChartGenerator.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\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 $key => $value) { | ||||
|             $date                          = new Carbon($key); | ||||
|             $sum                           = bcadd($sum, $value); | ||||
|             $data['labels'][]              = $date->formatLocalized($format); | ||||
|             $data['datasets'][0]['data'][] = round($sum, 2); | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,31 @@ | ||||
| <?php | ||||
| /** | ||||
|  * PiggyBankChartGeneratorInterface.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\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; | ||||
| } | ||||
							
								
								
									
										180
									
								
								app/Generator/Chart/Report/ChartJsReportChartGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								app/Generator/Chart/Report/ChartJsReportChartGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,180 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ChartJsReportChartGenerator.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\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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										65
									
								
								app/Generator/Chart/Report/ReportChartGeneratorInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								app/Generator/Chart/Report/ReportChartGeneratorInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ReportChartGeneratorInterface.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\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; | ||||
|  | ||||
| } | ||||
							
								
								
									
										110
									
								
								app/Handlers/Events/BudgetEventHandler.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								app/Handlers/Events/BudgetEventHandler.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BudgetEventHandler.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
|  | ||||
| use FireflyIII\Events\StoredBudgetLimit; | ||||
| use FireflyIII\Events\UpdatedBudgetLimit; | ||||
| use FireflyIII\Models\LimitRepetition; | ||||
| use Illuminate\Database\QueryException; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Handles budget related events. | ||||
|  * | ||||
|  * Class BudgetEventHandler | ||||
|  * | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class BudgetEventHandler | ||||
| { | ||||
|     /** | ||||
|      * This method creates a new budget limit repetition when a new budget limit has been created. | ||||
|      * | ||||
|      * @param StoredBudgetLimit $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function storeRepetition(StoredBudgetLimit $event):bool | ||||
|     { | ||||
|         $budgetLimit = $event->budgetLimit; | ||||
|         $end         = $event->end; | ||||
|         $set         = $budgetLimit->limitrepetitions() | ||||
|                                    ->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00')) | ||||
|                                    ->where('enddate', $end->format('Y-m-d 00:00:00')) | ||||
|                                    ->get(); | ||||
|         if ($set->count() == 0) { | ||||
|             $repetition            = new LimitRepetition; | ||||
|             $repetition->startdate = $budgetLimit->startdate; | ||||
|             $repetition->enddate   = $end; | ||||
|             $repetition->amount    = $budgetLimit->amount; | ||||
|             $repetition->budgetLimit()->associate($budgetLimit); | ||||
|  | ||||
|             try { | ||||
|                 $repetition->save(); | ||||
|             } catch (QueryException $e) { | ||||
|                 Log::error('Trying to save new LimitRepetition failed: ' . $e->getMessage()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($set->count() == 1) { | ||||
|             $repetition         = $set->first(); | ||||
|             $repetition->amount = $budgetLimit->amount; | ||||
|             $repetition->save(); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Updates, if present the budget limit repetition part of a budget limit. | ||||
|      * | ||||
|      * @param UpdatedBudgetLimit $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function update(UpdatedBudgetLimit $event): bool | ||||
|     { | ||||
|         $budgetLimit = $event->budgetLimit; | ||||
|         $end         = $event->end; | ||||
|         $set         = $budgetLimit->limitrepetitions() | ||||
|                                    ->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00')) | ||||
|                                    ->where('enddate', $end->format('Y-m-d 00:00:00')) | ||||
|                                    ->get(); | ||||
|         if ($set->count() == 0) { | ||||
|             $repetition            = new LimitRepetition; | ||||
|             $repetition->startdate = $budgetLimit->startdate; | ||||
|             $repetition->enddate   = $end; | ||||
|             $repetition->amount    = $budgetLimit->amount; | ||||
|             $repetition->budgetLimit()->associate($budgetLimit); | ||||
|  | ||||
|             try { | ||||
|                 $repetition->save(); | ||||
|             } catch (QueryException $e) { | ||||
|                 Log::error('Trying to save new LimitRepetition failed: ' . $e->getMessage()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($set->count() == 1) { | ||||
|             $repetition         = $set->first(); | ||||
|             $repetition->amount = $budgetLimit->amount; | ||||
|             $repetition->save(); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @@ -1,67 +0,0 @@ | ||||
| <?php namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
| use Auth; | ||||
| use FireflyIII\Events\JournalCreated; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Models\PiggyBankEvent; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
|  | ||||
| /** | ||||
|  * Class ConnectJournalToPiggyBank | ||||
|  * | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class ConnectJournalToPiggyBank | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Create the event handler. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handle the event when journal is saved. | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.CyclomaticComplexity) | ||||
|      * | ||||
|      * @param  JournalCreated $event | ||||
|      * | ||||
|      * @return boolean | ||||
|      */ | ||||
|     public function handle(JournalCreated $event) | ||||
|     { | ||||
|         /** @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 false; | ||||
|         } | ||||
|         // update piggy bank rep for date of transaction journal. | ||||
|         $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); | ||||
|         if (is_null($repetition)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         $amount = $journal->correct_amount; | ||||
|         bcscale(2); | ||||
|         $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,53 +0,0 @@ | ||||
| <?php namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
| use App; | ||||
| use FireflyIII\Events\JournalSaved; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class RescanJournal | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class RescanJournal | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Create the event handler. | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handle the event. | ||||
|      * | ||||
|      * @param  JournalSaved $event | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function handle(JournalSaved $event) | ||||
|     { | ||||
|         $journal = $event->journal; | ||||
|  | ||||
|         Log::debug('Triggered saved event for journal #' . $journal->id . ' (' . $journal->description . ')'); | ||||
|  | ||||
|         /** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */ | ||||
|         $repository = App::make('FireflyIII\Repositories\Bill\BillRepositoryInterface'); | ||||
|         $list       = $journal->user->bills()->where('active', 1)->where('automatch', 1)->get(); | ||||
|  | ||||
|         Log::debug('Found ' . $list->count() . ' bills to check.'); | ||||
|  | ||||
|         /** @var \FireflyIII\Models\Bill $bill */ | ||||
|         foreach ($list as $bill) { | ||||
|             Log::debug('Now calling bill #' . $bill->id . ' (' . $bill->name . ')'); | ||||
|             $repository->scan($bill, $journal); | ||||
|         } | ||||
|  | ||||
|         Log::debug('Done!'); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										124
									
								
								app/Handlers/Events/StoredJournalEventHandler.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								app/Handlers/Events/StoredJournalEventHandler.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| <?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\Rule; | ||||
| use FireflyIII\Models\RuleGroup; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use FireflyIII\Rules\Processor; | ||||
| use FireflyIII\Support\Events\BillScanner; | ||||
|  | ||||
| /** | ||||
|  * 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; | ||||
|  | ||||
|         /** @var PiggyBank $piggyBank */ | ||||
|         $piggyBank = $journal->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 | ||||
|         $sources = TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray(); | ||||
|         if (in_array($piggyBank->account_id, $sources)) { | ||||
|             $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; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method grabs all the users rules and processes them. | ||||
|      * | ||||
|      * @param StoredTransactionJournal $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function processRules(StoredTransactionJournal $event): bool | ||||
|     { | ||||
|         // get all the user's rule groups, with the rules, order by 'order'. | ||||
|         $journal = $event->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 $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function scanBills(StoredTransactionJournal $event): bool | ||||
|     { | ||||
|         $journal = $event->journal; | ||||
|         BillScanner::scan($journal); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @@ -1,63 +0,0 @@ | ||||
| <?php namespace FireflyIII\Handlers\Events; | ||||
|  | ||||
| use FireflyIII\Events\JournalSaved; | ||||
| use FireflyIII\Models\PiggyBankEvent; | ||||
| use FireflyIII\Models\PiggyBankRepetition; | ||||
|  | ||||
| /** | ||||
|  * Class UpdateJournalConnection | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class UpdateJournalConnection | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Create the event handler. | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handle the event. | ||||
|      * | ||||
|      * @param  JournalSaved $event | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function handle(JournalSaved $event) | ||||
|     { | ||||
|         $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; | ||||
|         } | ||||
|         $piggyBank  = $event->piggyBank()->first(); | ||||
|         $repetition = null; | ||||
|         if ($piggyBank) { | ||||
|             /** @var PiggyBankRepetition $repetition */ | ||||
|             $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); | ||||
|         } | ||||
|  | ||||
|         if (is_null($repetition)) { | ||||
|             return; | ||||
|         } | ||||
|         $amount = $journal->amount; | ||||
|         $diff   = $amount - $event->amount; // update current repetition | ||||
|  | ||||
|         $repetition->currentamount += $diff; | ||||
|         $repetition->save(); | ||||
|  | ||||
|  | ||||
|         $event->amount = $amount; | ||||
|         $event->save(); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										128
									
								
								app/Handlers/Events/UpdatedJournalEventHandler.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								app/Handlers/Events/UpdatedJournalEventHandler.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| <?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\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; | ||||
|  | ||||
| /** | ||||
|  * Class UpdatedJournalEventHandler | ||||
|  * | ||||
|  * @package FireflyIII\Handlers\Events | ||||
|  */ | ||||
| class UpdatedJournalEventHandler | ||||
| { | ||||
|     /** | ||||
|      * This method will try to reconnect a journal to a piggy bank, updating the piggy bank repetition. | ||||
|      * | ||||
|      * @param UpdatedTransactionJournal $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function connectToPiggyBank(UpdatedTransactionJournal $event): bool | ||||
|     { | ||||
|         $journal = $event->journal; | ||||
|  | ||||
|         if (!$journal->isTransfer()) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // 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 (!is_null($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; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method will check all the rules when a journal is updated. | ||||
|      * | ||||
|      * @param UpdatedTransactionJournal $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function processRules(UpdatedTransactionJournal $event):bool | ||||
|     { | ||||
|         // get all the user's rule groups, with the rules, order by 'order'. | ||||
|         $journal = $event->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 $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function scanBills(UpdatedTransactionJournal $event): bool | ||||
|     { | ||||
|         $journal = $event->journal; | ||||
|         BillScanner::scan($journal); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										225
									
								
								app/Handlers/Events/UserEventHandler.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								app/Handlers/Events/UserEventHandler.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,225 @@ | ||||
| <?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 Exception; | ||||
| use FireflyIII\Events\ConfirmedUser; | ||||
| use FireflyIII\Events\RegisteredUser; | ||||
| use FireflyIII\Events\ResentConfirmation; | ||||
| use FireflyIII\Repositories\User\UserRepositoryInterface; | ||||
| use Illuminate\Mail\Message; | ||||
| use Log; | ||||
| use Mail; | ||||
| use Preferences; | ||||
| 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 onUserLogout(): bool | ||||
|     { | ||||
|         // dump stuff from the session: | ||||
|         Session::forget('twofactor-authenticated'); | ||||
|         Session::forget('twofactor-authenticated-date'); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method will send a newly registered user a confirmation message, urging him or her to activate their account. | ||||
|      * | ||||
|      * @param RegisteredUser $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function sendConfirmationMessage(RegisteredUser $event): bool | ||||
|     { | ||||
|         $user           = $event->user; | ||||
|         $ipAddress      = $event->ipAddress; | ||||
|         $confirmAccount = env('MUST_CONFIRM_ACCOUNT', false); | ||||
|         if ($confirmAccount === false) { | ||||
|             Preferences::setForUser($user, 'user_confirmed', true); | ||||
|             Preferences::setForUser($user, 'user_confirmed_last_mail', 0); | ||||
|             Preferences::mark(); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|         $email = $user->email; | ||||
|         $code  = str_random(16); | ||||
|         $route = route('do_confirm_account', [$code]); | ||||
|         Preferences::setForUser($user, 'user_confirmed', false); | ||||
|         Preferences::setForUser($user, 'user_confirmed_last_mail', time()); | ||||
|         Preferences::setForUser($user, 'user_confirmed_code', $code); | ||||
|         try { | ||||
|             Mail::send( | ||||
|                 ['emails.confirm-account-html', 'emails.confirm-account'], ['route' => $route, 'ip' => $ipAddress], | ||||
|                 function (Message $message) use ($email) { | ||||
|                     $message->to($email, $email)->subject('Please confirm your Firefly III account'); | ||||
|                 } | ||||
|             ); | ||||
|         } catch (Swift_TransportException $e) { | ||||
|             Log::error($e->getMessage()); | ||||
|         } catch (Exception $e) { | ||||
|             Log::error($e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If the user has somehow lost his or her confirmation message, this event will send it to the user again. | ||||
|      * | ||||
|      * At the moment, this method is exactly the same as the ::sendConfirmationMessage method, but that will change. | ||||
|      * | ||||
|      * @param ResentConfirmation $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     function sendConfirmationMessageAgain(ResentConfirmation $event): bool | ||||
|     { | ||||
|         $user           = $event->user; | ||||
|         $ipAddress      = $event->ipAddress; | ||||
|         $confirmAccount = env('MUST_CONFIRM_ACCOUNT', false); | ||||
|         if ($confirmAccount === false) { | ||||
|             Preferences::setForUser($user, 'user_confirmed', true); | ||||
|             Preferences::setForUser($user, 'user_confirmed_last_mail', 0); | ||||
|             Preferences::mark(); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|         $email = $user->email; | ||||
|         $code  = str_random(16); | ||||
|         $route = route('do_confirm_account', [$code]); | ||||
|         Preferences::setForUser($user, 'user_confirmed', false); | ||||
|         Preferences::setForUser($user, 'user_confirmed_last_mail', time()); | ||||
|         Preferences::setForUser($user, 'user_confirmed_code', $code); | ||||
|         try { | ||||
|             Mail::send( | ||||
|                 ['emails.confirm-account-html', 'emails.confirm-account'], ['route' => $route, 'ip' => $ipAddress], | ||||
|                 function (Message $message) use ($email) { | ||||
|                     $message->to($email, $email)->subject('Please confirm your Firefly III account'); | ||||
|                 } | ||||
|             ); | ||||
|         } catch (Swift_TransportException $e) { | ||||
|             Log::error($e->getMessage()); | ||||
|         } catch (Exception $e) { | ||||
|             Log::error($e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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'], ['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; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * When the user is confirmed, this method stores the IP address of the user | ||||
|      * as a preference. Since this preference cannot be edited, it is effectively hidden | ||||
|      * from the user yet stored conveniently. | ||||
|      * | ||||
|      * @param ConfirmedUser $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function storeConfirmationIpAddress(ConfirmedUser $event): bool | ||||
|     { | ||||
|         Preferences::setForUser($event->user, 'confirmation_ip_address', $event->ipAddress); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This message stores the users IP address on registration, in much the same | ||||
|      * fashion as the previous method. | ||||
|      * | ||||
|      * @param RegisteredUser $event | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function storeRegistrationIpAddress(RegisteredUser $event): bool | ||||
|     { | ||||
|         Preferences::setForUser($event->user, 'registration_ip_address', $event->ipAddress); | ||||
|  | ||||
|         return true; | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										270
									
								
								app/Helpers/Attachments/AttachmentHelper.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								app/Helpers/Attachments/AttachmentHelper.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,270 @@ | ||||
| <?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 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 | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Attachments | ||||
|  */ | ||||
| class AttachmentHelper implements AttachmentHelperInterface | ||||
| { | ||||
|  | ||||
|     /** @var MessageBag */ | ||||
|     public $errors; | ||||
|     /** @var MessageBag */ | ||||
|     public $messages; | ||||
|     /** @var array */ | ||||
|     protected $allowedMimes; | ||||
|     /** @var int */ | ||||
|     protected $maxUploadSize; | ||||
|  | ||||
|     /** @var \Illuminate\Contracts\Filesystem\Filesystem */ | ||||
|     protected $uploadDisk; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->maxUploadSize = config('firefly.maxUploadSize'); | ||||
|         $this->allowedMimes  = config('firefly.allowedMimes'); | ||||
|         $this->errors        = new MessageBag; | ||||
|         $this->messages      = new MessageBag; | ||||
|         $this->uploadDisk    = Storage::disk('upload'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Attachment $attachment | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getAttachmentLocation(Attachment $attachment): string | ||||
|     { | ||||
|         $path = sprintf('%s%sat-%d.data', storage_path('upload'), DIRECTORY_SEPARATOR, intval($attachment->id)); | ||||
|  | ||||
|         return $path; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return MessageBag | ||||
|      */ | ||||
|     public function getErrors(): MessageBag | ||||
|     { | ||||
|         return $this->errors; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return MessageBag | ||||
|      */ | ||||
|     public function getMessages(): MessageBag | ||||
|     { | ||||
|         return $this->messages; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Model $model | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function saveAttachmentsForModel(Model $model): bool | ||||
|     { | ||||
|         $files = $this->getFiles(); | ||||
|  | ||||
|         if (!is_null($files) && !is_array($files)) { | ||||
|             $this->processFile($files, $model); | ||||
|         } | ||||
|  | ||||
|         if (is_array($files)) { | ||||
|             $this->processFiles($files, $model); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param UploadedFile $file | ||||
|      * @param Model        $model | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function hasFile(UploadedFile $file, Model $model): bool | ||||
|     { | ||||
|         $md5   = md5_file($file->getRealPath()); | ||||
|         $name  = $file->getClientOriginalName(); | ||||
|         $class = get_class($model); | ||||
|         $count = auth()->user()->attachments()->where('md5', $md5)->where('attachable_id', $model->id)->where('attachable_type', $class)->count(); | ||||
|  | ||||
|         if ($count > 0) { | ||||
|             $msg = (string)trans('validation.file_already_attached', ['name' => $name]); | ||||
|             $this->errors->add('attachments', $msg); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @param UploadedFile $file | ||||
|      * @param Model        $model | ||||
|      * | ||||
|      * @return Attachment | ||||
|      */ | ||||
|     protected function processFile(UploadedFile $file, Model $model): Attachment | ||||
|     { | ||||
|         $validation = $this->validateUpload($file, $model); | ||||
|         if ($validation === false) { | ||||
|             return new Attachment; | ||||
|         } | ||||
|  | ||||
|         $attachment = new Attachment; // create Attachment object. | ||||
|         $attachment->user()->associate(auth()->user()); | ||||
|         $attachment->attachable()->associate($model); | ||||
|         $attachment->md5      = md5_file($file->getRealPath()); | ||||
|         $attachment->filename = $file->getClientOriginalName(); | ||||
|         $attachment->mime     = $file->getMimeType(); | ||||
|         $attachment->size     = $file->getSize(); | ||||
|         $attachment->uploaded = 0; | ||||
|         $attachment->save(); | ||||
|  | ||||
|         $fileObject = $file->openFile('r'); | ||||
|         $fileObject->rewind(); | ||||
|         $content   = $fileObject->fread($file->getSize()); | ||||
|         $encrypted = Crypt::encrypt($content); | ||||
|  | ||||
|         // store it: | ||||
|         $this->uploadDisk->put($attachment->fileName(), $encrypted); | ||||
|  | ||||
|         $attachment->uploaded = 1; // update attachment | ||||
|         $attachment->save(); | ||||
|  | ||||
|         $name = e($file->getClientOriginalName()); // add message: | ||||
|         $msg  = (string)trans('validation.file_attached', ['name' => $name]); | ||||
|         $this->messages->add('attachments', $msg); | ||||
|  | ||||
|         // return it. | ||||
|         return $attachment; | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param UploadedFile $file | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function validMime(UploadedFile $file): bool | ||||
|     { | ||||
|         $mime = e($file->getMimeType()); | ||||
|         $name = e($file->getClientOriginalName()); | ||||
|  | ||||
|         if (!in_array($mime, $this->allowedMimes)) { | ||||
|             $msg = (string)trans('validation.file_invalid_mime', ['name' => $name, 'mime' => $mime]); | ||||
|             $this->errors->add('attachments', $msg); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param UploadedFile $file | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function validSize(UploadedFile $file): bool | ||||
|     { | ||||
|         $size = $file->getSize(); | ||||
|         $name = e($file->getClientOriginalName()); | ||||
|         if ($size > $this->maxUploadSize) { | ||||
|             $msg = (string)trans('validation.file_too_large', ['name' => $name]); | ||||
|             $this->errors->add('attachments', $msg); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param UploadedFile $file | ||||
|      * @param Model        $model | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     protected function validateUpload(UploadedFile $file, Model $model): bool | ||||
|     { | ||||
|         if (!$this->validMime($file)) { | ||||
|             return false; | ||||
|         } | ||||
|         if (!$this->validSize($file)) { | ||||
|             return false; | ||||
|         } | ||||
|         if ($this->hasFile($file, $model)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array|null|UploadedFile | ||||
|      */ | ||||
|     private function getFiles() | ||||
|     { | ||||
|         $files = null; | ||||
|         try { | ||||
|             if (Input::hasFile('attachments')) { | ||||
|                 $files = Input::file('attachments'); | ||||
|             } | ||||
|         } catch (TypeError $e) { | ||||
|             // Log it, do nothing else. | ||||
|             Log::error($e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         return $files; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $files | ||||
|      * | ||||
|      * @param Model $model | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function processFiles(array $files, Model $model): bool | ||||
|     { | ||||
|         foreach ($files as $entry) { | ||||
|             if (!is_null($entry)) { | ||||
|                 $this->processFile($entry, $model); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										51
									
								
								app/Helpers/Attachments/AttachmentHelperInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								app/Helpers/Attachments/AttachmentHelperInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| <?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; | ||||
|  | ||||
| use FireflyIII\Models\Attachment; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Support\MessageBag; | ||||
|  | ||||
| /** | ||||
|  * Interface AttachmentHelperInterface | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Attachments | ||||
|  */ | ||||
| interface AttachmentHelperInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Attachment $attachment | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getAttachmentLocation(Attachment $attachment): string; | ||||
|  | ||||
|     /** | ||||
|      * @return MessageBag | ||||
|      */ | ||||
|     public function getErrors(): MessageBag; | ||||
|  | ||||
|     /** | ||||
|      * @return MessageBag | ||||
|      */ | ||||
|     public function getMessages(): MessageBag; | ||||
|  | ||||
|     /** | ||||
|      * @param Model $model | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function saveAttachmentsForModel(Model $model): bool; | ||||
|  | ||||
| } | ||||
| @@ -1,11 +1,20 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Account.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * @codeCoverageIgnore | ||||
|  * Class Account | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Collection | ||||
| @@ -15,73 +24,81 @@ class Account | ||||
|  | ||||
|     /** @var Collection */ | ||||
|     protected $accounts; | ||||
|     /** @var float */ | ||||
|     protected $difference; | ||||
|     /** @var float */ | ||||
|     protected $end; | ||||
|     /** @var float */ | ||||
|     protected $start; | ||||
|     /** @var string */ | ||||
|     protected $difference = ''; | ||||
|     /** @var string */ | ||||
|     protected $end = ''; | ||||
|     /** @var string */ | ||||
|     protected $start = ''; | ||||
|  | ||||
|     /** | ||||
|      * @return \Illuminate\Support\Collection | ||||
|      * Account constructor. | ||||
|      */ | ||||
|     public function getAccounts() | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->accounts = new Collection; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getAccounts(): Collection | ||||
|     { | ||||
|         return $this->accounts; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Illuminate\Support\Collection $accounts | ||||
|      * @param Collection $accounts | ||||
|      */ | ||||
|     public function setAccounts($accounts) | ||||
|     public function setAccounts(Collection $accounts) | ||||
|     { | ||||
|         $this->accounts = $accounts; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return float | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getDifference() | ||||
|     public function getDifference(): string | ||||
|     { | ||||
|         return $this->difference; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $difference | ||||
|      * @param string $difference | ||||
|      */ | ||||
|     public function setDifference($difference) | ||||
|     public function setDifference(string $difference) | ||||
|     { | ||||
|         $this->difference = $difference; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return float | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getEnd() | ||||
|     public function getEnd(): string | ||||
|     { | ||||
|         return $this->end; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $end | ||||
|      * @param string $end | ||||
|      */ | ||||
|     public function setEnd($end) | ||||
|     public function setEnd(string $end) | ||||
|     { | ||||
|         $this->end = $end; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return float | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getStart() | ||||
|     public function getStart(): string | ||||
|     { | ||||
|         return $this->start; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $start | ||||
|      * @param string $start | ||||
|      */ | ||||
|     public function setStart($start) | ||||
|     public function setStart(string $start) | ||||
|     { | ||||
|         $this->start = $start; | ||||
|     } | ||||
|   | ||||
| @@ -1,11 +1,20 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Balance.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * @codeCoverageIgnore | ||||
|  * | ||||
|  * Class Balance | ||||
|  * | ||||
| @@ -39,26 +48,34 @@ class Balance | ||||
|     /** | ||||
|      * @return BalanceHeader | ||||
|      */ | ||||
|     public function getBalanceHeader() | ||||
|     public function getBalanceHeader(): BalanceHeader | ||||
|     { | ||||
|         return $this->balanceHeader; | ||||
|         return $this->balanceHeader ?? new BalanceHeader; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param BalanceHeader $balanceHeader | ||||
|      */ | ||||
|     public function setBalanceHeader($balanceHeader) | ||||
|     public function setBalanceHeader(BalanceHeader $balanceHeader) | ||||
|     { | ||||
|         $this->balanceHeader = $balanceHeader; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return \Illuminate\Support\Collection | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getBalanceLines() | ||||
|     public function getBalanceLines(): Collection | ||||
|     { | ||||
|         return $this->balanceLines; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $balanceLines | ||||
|      */ | ||||
|     public function setBalanceLines(Collection $balanceLines) | ||||
|     { | ||||
|         $this->balanceLines = $balanceLines; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,20 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BalanceEntry.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use FireflyIII\Models\Account as AccountModel; | ||||
|  | ||||
| /** | ||||
|  * @codeCoverageIgnore | ||||
|  * | ||||
|  * Class BalanceEntry | ||||
|  * | ||||
| @@ -17,15 +26,15 @@ class BalanceEntry | ||||
|  | ||||
|     /** @var  AccountModel */ | ||||
|     protected $account; | ||||
|     /** @var float */ | ||||
|     protected $left = 0.0; | ||||
|     /** @var float */ | ||||
|     protected $spent = 0.0; | ||||
|     /** @var string */ | ||||
|     protected $left = '0'; | ||||
|     /** @var string */ | ||||
|     protected $spent = '0'; | ||||
|  | ||||
|     /** | ||||
|      * @return AccountModel | ||||
|      */ | ||||
|     public function getAccount() | ||||
|     public function getAccount(): AccountModel | ||||
|     { | ||||
|         return $this->account; | ||||
|     } | ||||
| @@ -33,39 +42,39 @@ class BalanceEntry | ||||
|     /** | ||||
|      * @param AccountModel $account | ||||
|      */ | ||||
|     public function setAccount($account) | ||||
|     public function setAccount(AccountModel $account) | ||||
|     { | ||||
|         $this->account = $account; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return float | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getLeft() | ||||
|     public function getLeft(): string | ||||
|     { | ||||
|         return $this->left; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $left | ||||
|      * @param string $left | ||||
|      */ | ||||
|     public function setLeft($left) | ||||
|     public function setLeft(string $left) | ||||
|     { | ||||
|         $this->left = $left; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return float | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getSpent() | ||||
|     public function getSpent(): string | ||||
|     { | ||||
|         return $this->spent; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $spent | ||||
|      * @param string $spent | ||||
|      */ | ||||
|     public function setSpent($spent) | ||||
|     public function setSpent(string $spent) | ||||
|     { | ||||
|         $this->spent = $spent; | ||||
|     } | ||||
|   | ||||
| @@ -1,12 +1,21 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BalanceHeader.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use FireflyIII\Models\Account as AccountModel; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * @codeCoverageIgnore | ||||
|  * | ||||
|  * Class BalanceHeader | ||||
|  * | ||||
| @@ -37,7 +46,7 @@ class BalanceHeader | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getAccounts() | ||||
|     public function getAccounts(): Collection | ||||
|     { | ||||
|         return $this->accounts; | ||||
|     } | ||||
|   | ||||
| @@ -1,13 +1,22 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BalanceLine.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Models\Budget as BudgetModel; | ||||
| use FireflyIII\Models\LimitRepetition; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * @codeCoverageIgnore | ||||
|  * | ||||
|  * Class BalanceLine | ||||
|  * | ||||
| @@ -25,11 +34,12 @@ class BalanceLine | ||||
|  | ||||
|     /** @var BudgetModel */ | ||||
|     protected $budget; | ||||
|  | ||||
|     /** @var  LimitRepetition */ | ||||
|     protected $repetition; | ||||
|  | ||||
|     /** @var  Carbon */ | ||||
|     protected $endDate; | ||||
|     /** @var int */ | ||||
|     protected $role = self::ROLE_DEFAULTROLE; | ||||
|     /** @var  Carbon */ | ||||
|     protected $startDate; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
| @@ -37,6 +47,7 @@ class BalanceLine | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->balanceEntries = new Collection; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -47,16 +58,96 @@ class BalanceLine | ||||
|         $this->balanceEntries->push($balanceEntry); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getBalanceEntries(): Collection | ||||
|     { | ||||
|         return $this->balanceEntries; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $balanceEntries | ||||
|      */ | ||||
|     public function setBalanceEntries(Collection $balanceEntries) | ||||
|     { | ||||
|         $this->balanceEntries = $balanceEntries; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return BudgetModel | ||||
|      */ | ||||
|     public function getBudget(): BudgetModel | ||||
|     { | ||||
|         return $this->budget ?? new BudgetModel; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param BudgetModel $budget | ||||
|      */ | ||||
|     public function setBudget(BudgetModel $budget) | ||||
|     { | ||||
|         $this->budget = $budget; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Carbon | ||||
|      */ | ||||
|     public function getEndDate() | ||||
|     { | ||||
|         return $this->endDate; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $endDate | ||||
|      */ | ||||
|     public function setEndDate($endDate) | ||||
|     { | ||||
|         $this->endDate = $endDate; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return int | ||||
|      */ | ||||
|     public function getRole(): int | ||||
|     { | ||||
|         return $this->role; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $role | ||||
|      */ | ||||
|     public function setRole(int $role) | ||||
|     { | ||||
|         $this->role = $role; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Carbon | ||||
|      */ | ||||
|     public function getStartDate() | ||||
|     { | ||||
|         return $this->startDate; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $startDate | ||||
|      */ | ||||
|     public function setStartDate($startDate) | ||||
|     { | ||||
|         $this->startDate = $startDate; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getTitle() | ||||
|     public function getTitle(): string | ||||
|     { | ||||
|         if ($this->getBudget() instanceof BudgetModel) { | ||||
|         if ($this->getBudget() instanceof BudgetModel && !is_null($this->getBudget()->id)) { | ||||
|             return $this->getBudget()->name; | ||||
|         } | ||||
|         if ($this->getRole() == self::ROLE_DEFAULTROLE) { | ||||
|             return trans('firefly.noBudget'); | ||||
|             return trans('firefly.no_budget'); | ||||
|         } | ||||
|         if ($this->getRole() == self::ROLE_TAGROLE) { | ||||
|             return trans('firefly.coveredWithTags'); | ||||
| @@ -68,105 +159,22 @@ class BalanceLine | ||||
|         return ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return BudgetModel | ||||
|      */ | ||||
|     public function getBudget() | ||||
|     { | ||||
|         return $this->budget; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param BudgetModel $budget | ||||
|      */ | ||||
|     public function setBudget($budget) | ||||
|     { | ||||
|         $this->budget = $budget; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return int | ||||
|      */ | ||||
|     public function getRole() | ||||
|     { | ||||
|         return $this->role; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $role | ||||
|      */ | ||||
|     public function setRole($role) | ||||
|     { | ||||
|         $this->role = $role; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If a BalanceLine has a budget/repetition, each BalanceEntry in this BalanceLine | ||||
|      * should have a "spent" value, which is the amount of money that has been spent | ||||
|      * on the given budget/repetition. If you subtract all those amounts from the budget/repetition's | ||||
|      * total amount, this is returned: | ||||
|      * | ||||
|      * @return float | ||||
|      * @return string | ||||
|      */ | ||||
|     public function leftOfRepetition() | ||||
|     public function leftOfRepetition(): string | ||||
|     { | ||||
|         $start = $this->getRepetition() ? $this->getRepetition()->amount : 0; | ||||
|         $start = $this->budget->amount ?? '0'; | ||||
|         /** @var BalanceEntry $balanceEntry */ | ||||
|         foreach ($this->getBalanceEntries() as $balanceEntry) { | ||||
|             $start += $balanceEntry->getSpent(); | ||||
|             $start = bcadd($balanceEntry->getSpent(), $start); | ||||
|         } | ||||
|  | ||||
|         return $start; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return LimitRepetition | ||||
|      */ | ||||
|     public function getRepetition() | ||||
|     { | ||||
|         return $this->repetition; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param LimitRepetition $repetition | ||||
|      */ | ||||
|     public function setRepetition($repetition) | ||||
|     { | ||||
|         $this->repetition = $repetition; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getBalanceEntries() | ||||
|     { | ||||
|         return $this->balanceEntries; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $balanceEntries | ||||
|      */ | ||||
|     public function setBalanceEntries($balanceEntries) | ||||
|     { | ||||
|         $this->balanceEntries = $balanceEntries; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If the BalanceEntries for a BalanceLine have a "left" value, the amount | ||||
|      * of money left in the entire BalanceLine is returned here: | ||||
|      * | ||||
|      * @return float | ||||
|      */ | ||||
|     public function sumOfLeft() | ||||
|     { | ||||
|         $sum = 0.0; | ||||
|         /** @var BalanceEntry $balanceEntry */ | ||||
|         foreach ($this->getBalanceEntries() as $balanceEntry) { | ||||
|             $sum += $balanceEntry->getLeft(); | ||||
|         } | ||||
|  | ||||
|         return $sum; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,12 +1,21 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Bill.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * @codeCoverageIgnore | ||||
|  * Class Bill | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Collection | ||||
| @@ -38,9 +47,9 @@ class Bill | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getBills() | ||||
|     public function getBills(): Collection | ||||
|     { | ||||
|         $this->bills->sortBy( | ||||
|         $set = $this->bills->sortBy( | ||||
|             function (BillLine $bill) { | ||||
|                 $active = intval($bill->getBill()->active) == 0 ? 1 : 0; | ||||
|                 $name   = $bill->getBill()->name; | ||||
| @@ -50,7 +59,7 @@ class Bill | ||||
|         ); | ||||
|  | ||||
|  | ||||
|         return $this->bills; | ||||
|         return $set; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,20 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BillLine.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use FireflyIII\Models\Bill as BillModel; | ||||
|  | ||||
| /** | ||||
|  * @codeCoverageIgnore | ||||
|  * | ||||
|  * Class BillLine | ||||
|  * | ||||
| @@ -16,29 +25,32 @@ class BillLine | ||||
|  | ||||
|     /** @var  bool */ | ||||
|     protected $active; | ||||
|     /** @var  float */ | ||||
|     /** @var  string */ | ||||
|     protected $amount; | ||||
|     /** @var  BillModel */ | ||||
|     protected $bill; | ||||
|     /** @var  bool */ | ||||
|     protected $hit; | ||||
|     /** @var  float */ | ||||
|     /** @var  string */ | ||||
|     protected $max; | ||||
|     /** @var  float */ | ||||
|     /** @var  string */ | ||||
|     protected $min; | ||||
|  | ||||
|     /** @var  int */ | ||||
|     private $transactionJournalId; | ||||
|  | ||||
|     /** | ||||
|      * @return float | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getAmount() | ||||
|     public function getAmount(): string | ||||
|     { | ||||
|         return $this->amount; | ||||
|         return $this->amount ?? '0'; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $amount | ||||
|      * @param string $amount | ||||
|      */ | ||||
|     public function setAmount($amount) | ||||
|     public function setAmount(string $amount) | ||||
|     { | ||||
|         $this->amount = $amount; | ||||
|     } | ||||
| @@ -46,7 +58,7 @@ class BillLine | ||||
|     /** | ||||
|      * @return BillModel | ||||
|      */ | ||||
|     public function getBill() | ||||
|     public function getBill(): BillModel | ||||
|     { | ||||
|         return $this->bill; | ||||
|     } | ||||
| @@ -54,74 +66,89 @@ class BillLine | ||||
|     /** | ||||
|      * @param BillModel $bill | ||||
|      */ | ||||
|     public function setBill($bill) | ||||
|     public function setBill(BillModel $bill) | ||||
|     { | ||||
|         $this->bill = $bill; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return float | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getMax() | ||||
|     public function getMax(): string | ||||
|     { | ||||
|         return $this->max; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $max | ||||
|      * @param string $max | ||||
|      */ | ||||
|     public function setMax($max) | ||||
|     public function setMax(string $max) | ||||
|     { | ||||
|         $this->max = $max; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return float | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getMin() | ||||
|     public function getMin(): string | ||||
|     { | ||||
|         return $this->min; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $min | ||||
|      * @param string $min | ||||
|      */ | ||||
|     public function setMin($min) | ||||
|     public function setMin(string $min) | ||||
|     { | ||||
|         $this->min = $min; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return boolean | ||||
|      * @return int | ||||
|      */ | ||||
|     public function isActive() | ||||
|     public function getTransactionJournalId(): int | ||||
|     { | ||||
|         return $this->transactionJournalId ?? 0; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param int $transactionJournalId | ||||
|      */ | ||||
|     public function setTransactionJournalId(int $transactionJournalId) | ||||
|     { | ||||
|         $this->transactionJournalId = $transactionJournalId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function isActive(): bool | ||||
|     { | ||||
|         return $this->active; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param boolean $active | ||||
|      * @param bool $active | ||||
|      */ | ||||
|     public function setActive($active) | ||||
|     public function setActive(bool $active) | ||||
|     { | ||||
|         $this->active = $active; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return boolean | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function isHit() | ||||
|     public function isHit(): bool | ||||
|     { | ||||
|         return $this->hit; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param boolean $hit | ||||
|      * @param bool $hit | ||||
|      */ | ||||
|     public function setHit($hit) | ||||
|     public function setHit(bool $hit) | ||||
|     { | ||||
|         $this->hit = $hit; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,20 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Budget.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * @codeCoverageIgnore | ||||
|  * | ||||
|  * Class Budget | ||||
|  * | ||||
| @@ -34,56 +43,72 @@ class Budget | ||||
|  | ||||
|     /** | ||||
|      * @param BudgetLine $budgetLine | ||||
|      * | ||||
|      * @return Budget | ||||
|      */ | ||||
|     public function addBudgetLine(BudgetLine $budgetLine) | ||||
|     public function addBudgetLine(BudgetLine $budgetLine): Budget | ||||
|     { | ||||
|         $this->budgetLines->push($budgetLine); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $add | ||||
|      * @param string $add | ||||
|      * | ||||
|      * @return Budget | ||||
|      */ | ||||
|     public function addBudgeted($add) | ||||
|     public function addBudgeted(string $add): Budget | ||||
|     { | ||||
|         $add = strval(round($add, 2)); | ||||
|         bcscale(2); | ||||
|         $add            = strval(round($add, 2)); | ||||
|         $this->budgeted = bcadd($this->budgeted, $add); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $add | ||||
|      * @param string $add | ||||
|      * | ||||
|      * @return Budget | ||||
|      */ | ||||
|     public function addLeft($add) | ||||
|     public function addLeft(string $add): Budget | ||||
|     { | ||||
|         $add = strval(round($add, 2)); | ||||
|         bcscale(2); | ||||
|         $add        = strval(round($add, 2)); | ||||
|         $this->left = bcadd($this->left, $add); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $add | ||||
|      * @param string $add | ||||
|      * | ||||
|      * @return Budget | ||||
|      */ | ||||
|     public function addOverspent($add) | ||||
|     public function addOverspent(string $add): Budget | ||||
|     { | ||||
|         $add = strval(round($add, 2)); | ||||
|         bcscale(2); | ||||
|         $add             = strval(round($add, 2)); | ||||
|         $this->overspent = bcadd($this->overspent, $add); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $add | ||||
|      * @param string $add | ||||
|      * | ||||
|      * @return Budget | ||||
|      */ | ||||
|     public function addSpent($add) | ||||
|     public function addSpent(string $add): Budget | ||||
|     { | ||||
|         $add = strval(round($add, 2)); | ||||
|         bcscale(2); | ||||
|         $add         = strval(round($add, 2)); | ||||
|         $this->spent = bcadd($this->spent, $add); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return \Illuminate\Support\Collection | ||||
|      */ | ||||
|     public function getBudgetLines() | ||||
|     public function getBudgetLines(): Collection | ||||
|     { | ||||
|         return $this->budgetLines; | ||||
|     } | ||||
| @@ -91,65 +116,81 @@ class Budget | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getBudgeted() | ||||
|     public function getBudgeted(): string | ||||
|     { | ||||
|         return $this->budgeted; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $budgeted | ||||
|      * | ||||
|      * @return Budget | ||||
|      */ | ||||
|     public function setBudgeted($budgeted) | ||||
|     public function setBudgeted(string $budgeted): Budget | ||||
|     { | ||||
|         $this->budgeted = $budgeted; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getLeft() | ||||
|     public function getLeft(): string | ||||
|     { | ||||
|         return $this->left; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $left | ||||
|      * | ||||
|      * @return Budget | ||||
|      */ | ||||
|     public function setLeft($left) | ||||
|     public function setLeft(string $left): Budget | ||||
|     { | ||||
|         $this->left = $left; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getOverspent() | ||||
|     public function getOverspent(): string | ||||
|     { | ||||
|         return $this->overspent; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $overspent | ||||
|      * | ||||
|      * @return Budget | ||||
|      */ | ||||
|     public function setOverspent($overspent) | ||||
|     public function setOverspent(string $overspent): Budget | ||||
|     { | ||||
|         $this->overspent = strval(round($overspent, 2)); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getSpent() | ||||
|     public function getSpent(): string | ||||
|     { | ||||
|         return $this->spent; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $spent | ||||
|      * | ||||
|      * @return Budget | ||||
|      */ | ||||
|     public function setSpent($spent) | ||||
|     public function setSpent(string $spent): Budget | ||||
|     { | ||||
|         $this->spent = strval(round($spent, 2)); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,12 +1,21 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BudgetLine.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use FireflyIII\Models\Budget as BudgetModel; | ||||
| use FireflyIII\Models\LimitRepetition; | ||||
|  | ||||
| /** | ||||
|  * @codeCoverageIgnore | ||||
|  * | ||||
|  * Class BudgetLine | ||||
|  * | ||||
| @@ -17,111 +26,135 @@ class BudgetLine | ||||
|  | ||||
|     /** @var  BudgetModel */ | ||||
|     protected $budget; | ||||
|     /** @var float */ | ||||
|     protected $budgeted = 0; | ||||
|     /** @var float */ | ||||
|     protected $left = 0; | ||||
|     /** @var float */ | ||||
|     protected $overspent = 0; | ||||
|     /** @var string */ | ||||
|     protected $budgeted = '0'; | ||||
|     /** @var string */ | ||||
|     protected $left = '0'; | ||||
|     /** @var string */ | ||||
|     protected $overspent = '0'; | ||||
|     /** @var  LimitRepetition */ | ||||
|     protected $repetition; | ||||
|     /** @var float */ | ||||
|     protected $spent = 0; | ||||
|     /** @var string */ | ||||
|     protected $spent = '0'; | ||||
|  | ||||
|     /** | ||||
|      * @return BudgetModel | ||||
|      */ | ||||
|     public function getBudget() | ||||
|     public function getBudget(): BudgetModel | ||||
|     { | ||||
|         return $this->budget; | ||||
|         return $this->budget ?? new BudgetModel; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param BudgetModel $budget | ||||
|      * | ||||
|      * @return BudgetLine | ||||
|      */ | ||||
|     public function setBudget($budget) | ||||
|     public function setBudget(BudgetModel $budget): BudgetLine | ||||
|     { | ||||
|         $this->budget = $budget; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return float | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getBudgeted() | ||||
|     public function getBudgeted(): string | ||||
|     { | ||||
|         return $this->budgeted; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $budgeted | ||||
|      * @param string $budgeted | ||||
|      * | ||||
|      * @return BudgetLine | ||||
|      */ | ||||
|     public function setBudgeted($budgeted) | ||||
|     public function setBudgeted(string $budgeted): BudgetLine | ||||
|     { | ||||
|         $this->budgeted = $budgeted; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return float | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getLeft() | ||||
|     public function getLeft(): string | ||||
|     { | ||||
|         return $this->left; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $left | ||||
|      * @param string $left | ||||
|      * | ||||
|      * @return BudgetLine | ||||
|      */ | ||||
|     public function setLeft($left) | ||||
|     public function setLeft(string $left): BudgetLine | ||||
|     { | ||||
|         $this->left = $left; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return float | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getOverspent() | ||||
|     public function getOverspent(): string | ||||
|     { | ||||
|         return $this->overspent; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $overspent | ||||
|      * @param string $overspent | ||||
|      * | ||||
|      * @return BudgetLine | ||||
|      */ | ||||
|     public function setOverspent($overspent) | ||||
|     public function setOverspent(string $overspent): BudgetLine | ||||
|     { | ||||
|         $this->overspent = $overspent; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return LimitRepetition | ||||
|      */ | ||||
|     public function getRepetition() | ||||
|     public function getRepetition(): LimitRepetition | ||||
|     { | ||||
|         return $this->repetition; | ||||
|         return $this->repetition ?? new LimitRepetition; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param LimitRepetition $repetition | ||||
|      * | ||||
|      * @return BudgetLine | ||||
|      */ | ||||
|     public function setRepetition($repetition) | ||||
|     public function setRepetition(LimitRepetition $repetition): BudgetLine | ||||
|     { | ||||
|         $this->repetition = $repetition; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return float | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getSpent() | ||||
|     public function getSpent(): string | ||||
|     { | ||||
|         return $this->spent; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $spent | ||||
|      * @param string $spent | ||||
|      * | ||||
|      * @return BudgetLine | ||||
|      */ | ||||
|     public function setSpent($spent) | ||||
|     public function setSpent(string $spent): BudgetLine | ||||
|     { | ||||
|         $this->spent = $spent; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,15 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Category.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use FireflyIII\Models\Category as CategoryModel; | ||||
| @@ -7,7 +17,6 @@ use Illuminate\Support\Collection; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * @codeCoverageIgnore | ||||
|  * | ||||
|  * Class Category | ||||
|  * | ||||
| @@ -34,40 +43,41 @@ class Category | ||||
|      */ | ||||
|     public function addCategory(CategoryModel $category) | ||||
|     { | ||||
|         if ($category->spent > 0) { | ||||
|         // spent is minus zero for an expense report: | ||||
|         if ($category->spent < 0) { | ||||
|             $this->categories->push($category); | ||||
|             $this->addTotal($category->spent); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param float $add | ||||
|      * @param string $add | ||||
|      */ | ||||
|     public function addTotal($add) | ||||
|     public function addTotal(string $add) | ||||
|     { | ||||
|         $add = strval(round($add, 2)); | ||||
|         bcscale(2); | ||||
|         $add         = strval(round($add, 2)); | ||||
|         $this->total = bcadd($this->total, $add); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getCategories() | ||||
|     public function getCategories(): Collection | ||||
|     { | ||||
|         $this->categories->sortByDesc( | ||||
|         $set = $this->categories->sortBy( | ||||
|             function (CategoryModel $category) { | ||||
|                 return $category->spent; | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|  | ||||
|         return $this->categories; | ||||
|         return $set; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getTotal() | ||||
|     public function getTotal(): string | ||||
|     { | ||||
|         return strval(round($this->total, 2)); | ||||
|     } | ||||
|   | ||||
| @@ -1,13 +1,21 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Expense.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Support\Collection; | ||||
| use stdClass; | ||||
|  | ||||
| /** | ||||
|  * @codeCoverageIgnore | ||||
|  * | ||||
|  * Class Expense | ||||
|  * | ||||
| @@ -29,56 +37,48 @@ class Expense | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param TransactionJournal $entry | ||||
|      * @param stdClass $entry | ||||
|      */ | ||||
|     public function addOrCreateExpense(TransactionJournal $entry) | ||||
|     public function addOrCreateExpense(stdClass $entry) | ||||
|     { | ||||
|  | ||||
|         $accountId = $entry->account_id; | ||||
|         if (!$this->expenses->has($accountId)) { | ||||
|             $newObject         = new stdClass; | ||||
|             $newObject->amount = strval(round($entry->amount, 2)); | ||||
|             $newObject->name   = $entry->name; | ||||
|             $newObject->count  = 1; | ||||
|             $newObject->id     = $accountId; | ||||
|             $this->expenses->put($accountId, $newObject); | ||||
|         } else { | ||||
|             bcscale(2); | ||||
|             $existing         = $this->expenses->get($accountId); | ||||
|             $existing->amount = bcadd($existing->amount, $entry->amount); | ||||
|             $existing->count++; | ||||
|             $this->expenses->put($accountId, $existing); | ||||
|         } | ||||
|         $this->expenses->put($entry->id, $entry); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $add | ||||
|      * @param string $add | ||||
|      */ | ||||
|     public function addToTotal($add) | ||||
|     public function addToTotal(string $add) | ||||
|     { | ||||
|         $add = strval(round($add, 2)); | ||||
|         bcscale(2); | ||||
|         if (bccomp('0', $add) === -1) { | ||||
|             $add = bcmul($add, '-1'); | ||||
|         } | ||||
|  | ||||
|         // if amount is positive, the original transaction | ||||
|         // was a transfer. But since this is an expense report, | ||||
|         // that amount must be negative. | ||||
|  | ||||
|         $this->total = bcadd($this->total, $add); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getExpenses() | ||||
|     public function getExpenses(): Collection | ||||
|     { | ||||
|         $this->expenses->sortByDesc( | ||||
|         $set = $this->expenses->sortBy( | ||||
|             function (stdClass $object) { | ||||
|                 return $object->amount; | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return $this->expenses; | ||||
|         return $set; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getTotal() | ||||
|     public function getTotal(): string | ||||
|     { | ||||
|         return strval(round($this->total, 2)); | ||||
|     } | ||||
|   | ||||
| @@ -1,13 +1,21 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Income.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
| namespace FireflyIII\Helpers\Collection; | ||||
|  | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Support\Collection; | ||||
| use stdClass; | ||||
|  | ||||
| /** | ||||
|  * @codeCoverageIgnore | ||||
|  * | ||||
|  * Class Income | ||||
|  * | ||||
| @@ -19,7 +27,7 @@ class Income | ||||
|     /** @var Collection */ | ||||
|     protected $incomes; | ||||
|     /** @var string */ | ||||
|     protected $total; | ||||
|     protected $total = '0'; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
| @@ -30,56 +38,41 @@ class Income | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param TransactionJournal $entry | ||||
|      * @param stdClass $entry | ||||
|      */ | ||||
|     public function addOrCreateIncome(TransactionJournal $entry) | ||||
|     public function addOrCreateIncome(stdClass $entry) | ||||
|     { | ||||
|         $this->incomes->put($entry->id, $entry); | ||||
|  | ||||
|         $accountId = $entry->account_id; | ||||
|         if (!$this->incomes->has($accountId)) { | ||||
|             $newObject         = new stdClass; | ||||
|             $newObject->amount = strval(round($entry->amount, 2)); | ||||
|             $newObject->name   = $entry->name; | ||||
|             $newObject->count  = 1; | ||||
|             $newObject->id     = $accountId; | ||||
|             $this->incomes->put($accountId, $newObject); | ||||
|         } else { | ||||
|             bcscale(2); | ||||
|             $existing         = $this->incomes->get($accountId); | ||||
|             $existing->amount = bcadd($existing->amount, $entry->amount); | ||||
|             $existing->count++; | ||||
|             $this->incomes->put($accountId, $existing); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $add | ||||
|      * @param string $add | ||||
|      */ | ||||
|     public function addToTotal($add) | ||||
|     public function addToTotal(string $add) | ||||
|     { | ||||
|         $add = strval(round($add, 2)); | ||||
|         bcscale(2); | ||||
|         $add         = strval(round($add, 2)); | ||||
|         $this->total = bcadd($this->total, $add); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getIncomes() | ||||
|     public function getIncomes(): Collection | ||||
|     { | ||||
|         $this->incomes->sortByDesc( | ||||
|         $set = $this->incomes->sortByDesc( | ||||
|             function (stdClass $object) { | ||||
|                 return $object->amount; | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return $this->incomes; | ||||
|         return $set; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getTotal() | ||||
|     public function getTotal(): string | ||||
|     { | ||||
|         return strval(round($this->total, 2)); | ||||
|     } | ||||
|   | ||||
							
								
								
									
										87
									
								
								app/Helpers/FiscalHelper.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								app/Helpers/FiscalHelper.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| <?php | ||||
| /** | ||||
|  * FiscalHelper.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; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Preferences; | ||||
|  | ||||
| /** | ||||
|  * Class FiscalHelper | ||||
|  * | ||||
|  * @package FireflyIII\Helpers | ||||
|  */ | ||||
| class FiscalHelper implements FiscalHelperInterface | ||||
| { | ||||
|  | ||||
|     /** @var bool */ | ||||
|     protected $useCustomFiscalYear; | ||||
|  | ||||
|     /** | ||||
|      * FiscalHelper constructor. | ||||
|      * | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->useCustomFiscalYear = Preferences::get('customFiscalYear', false)->data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return Carbon date object | ||||
|      */ | ||||
|     public function endOfFiscalYear(Carbon $date): Carbon | ||||
|     { | ||||
|         // get start of fiscal year for passed date | ||||
|         $endDate = $this->startOfFiscalYear($date); | ||||
|         if ($this->useCustomFiscalYear === true) { | ||||
|             // add 1 year and sub 1 day | ||||
|             $endDate->addYear(); | ||||
|             $endDate->subDay(); | ||||
|  | ||||
|             return $endDate; | ||||
|         } | ||||
|         $endDate->endOfYear(); | ||||
|  | ||||
|  | ||||
|         return $endDate; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return Carbon date object | ||||
|      */ | ||||
|     public function startOfFiscalYear(Carbon $date): Carbon | ||||
|     { | ||||
|         // get start mm-dd. Then create a start date in the year passed. | ||||
|         $startDate = clone $date; | ||||
|         if ($this->useCustomFiscalYear === true) { | ||||
|             $prefStartStr = Preferences::get('fiscalYearStart', '01-01')->data; | ||||
|             list($mth, $day) = explode('-', $prefStartStr); | ||||
|             $startDate->month(intval($mth))->day(intval($day)); | ||||
|  | ||||
|             // if start date is after passed date, sub 1 year. | ||||
|             if ($startDate > $date) { | ||||
|                 $startDate->subYear(); | ||||
|             } | ||||
|  | ||||
|             return $startDate; | ||||
|         } | ||||
|         $startDate->startOfYear(); | ||||
|  | ||||
|         return $startDate; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										46
									
								
								app/Helpers/FiscalHelperInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								app/Helpers/FiscalHelperInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| <?php | ||||
| /** | ||||
|  * FiscalHelperInterface.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; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
|  | ||||
| /** | ||||
|  * Interface FiscalHelperInterface | ||||
|  * | ||||
|  * @package FireflyIII\Helpers | ||||
|  */ | ||||
| interface FiscalHelperInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * This method produces a clone of the Carbon date object passed, checks preferences | ||||
|      * and calculates the last day of the fiscal year. | ||||
|      * | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return Carbon date object | ||||
|      */ | ||||
|     public function endOfFiscalYear(Carbon $date): Carbon; | ||||
|  | ||||
|     /** | ||||
|      * This method produces a clone of the Carbon date object passed, checks preferences | ||||
|      * and calculates the first day of the fiscal year. | ||||
|      * | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return Carbon date object | ||||
|      */ | ||||
|     public function startOfFiscalYear(Carbon $date): Carbon; | ||||
|  | ||||
| } | ||||
| @@ -1,11 +1,20 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Help.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\Help; | ||||
|  | ||||
| use Cache; | ||||
| use ErrorException; | ||||
| use League\CommonMark\CommonMarkConverter; | ||||
| use Log; | ||||
| use Requests; | ||||
| use Route; | ||||
|  | ||||
| /** | ||||
| @@ -17,38 +26,44 @@ class Help implements HelpInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @param $key | ||||
|      * @param string $key | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getFromCache($key) | ||||
|     public function getFromCache(string $key): string | ||||
|     { | ||||
|         return Cache::get($key); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @param $route | ||||
|      * @param string $language | ||||
|      * @param string $route | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getFromGithub($route) | ||||
|     public function getFromGithub(string $language, string $route): array | ||||
|     { | ||||
|         $uri     = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/en/' . e($route) . '.md'; | ||||
|         $content = [ | ||||
|             'text'  => '<p>There is no help for this route!</p>', | ||||
|             'title' => $route, | ||||
|  | ||||
|         $uri        = sprintf('https://raw.githubusercontent.com/JC5/firefly-iii-help/master/%s/%s.md', $language, $route); | ||||
|         $routeIndex = str_replace('.', '-', $route); | ||||
|         $title      = trans('help.' . $routeIndex); | ||||
|         $content    = [ | ||||
|             'text'  => '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>', | ||||
|             'title' => $title, | ||||
|         ]; | ||||
|         try { | ||||
|             $content['text'] = file_get_contents($uri); | ||||
|         } catch (ErrorException $e) { | ||||
|             Log::error(trim($e->getMessage())); | ||||
|  | ||||
|  | ||||
|         $result = Requests::get($uri); | ||||
|  | ||||
|  | ||||
|         if ($result->status_code === 200) { | ||||
|             $content['text'] = $result->body; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         if (strlen(trim($content['text'])) == 0) { | ||||
|             $content['text'] = '<p>There is no help for this route.</p>'; | ||||
|             $content['text'] = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>'; | ||||
|         } | ||||
|         $converter       = new CommonMarkConverter(); | ||||
|         $content['text'] = $converter->convertToHtml($content['text']); | ||||
| @@ -58,40 +73,38 @@ class Help implements HelpInterface | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @param $route | ||||
|      * @param string $route | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function hasRoute($route) | ||||
|     public function hasRoute(string $route):bool | ||||
|     { | ||||
|         return Route::has($route); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @param       $route | ||||
|      * @param array $content | ||||
|      * | ||||
|      * @internal param $title | ||||
|      */ | ||||
|     public function putInCache($route, array $content) | ||||
|     { | ||||
|         Cache::put('help.' . $route . '.text', $content['text'], 10080); // a week. | ||||
|         Cache::put('help.' . $route . '.title', $content['title'], 10080); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @param $route | ||||
|      * @param string $route | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function inCache($route) | ||||
|     public function inCache(string $route):bool | ||||
|     { | ||||
|         return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @param string $route | ||||
|      * @param string $language | ||||
|      * @param array  $content | ||||
|      * | ||||
|      * @internal param $title | ||||
|      */ | ||||
|     public function putInCache(string $route, string $language, array $content) | ||||
|     { | ||||
|         Cache::put('help.' . $route . '.text.' . $language, $content['text'], 10080); // a week. | ||||
|         Cache::put('help.' . $route . '.title.' . $language, $content['title'], 10080); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,15 @@ | ||||
| <?php | ||||
| /** | ||||
|  * HelpInterface.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\Help; | ||||
|  | ||||
| /** | ||||
| @@ -11,38 +21,38 @@ interface HelpInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param $key | ||||
|      * @param string $key | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getFromCache($key); | ||||
|     public function getFromCache(string $key): string; | ||||
|  | ||||
|     /** | ||||
|      * @param $route | ||||
|      * @param string $language | ||||
|      * @param string $route | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getFromGithub($route); | ||||
|     public function getFromGithub(string $language, string $route):array; | ||||
|  | ||||
|     /** | ||||
|      * @param $route | ||||
|      * @param string $route | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function hasRoute($route); | ||||
|     public function hasRoute(string $route): bool; | ||||
|  | ||||
|     /** | ||||
|      * @param $route | ||||
|      * @param string $route | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function inCache($route); | ||||
|     public function inCache(string $route): bool; | ||||
|  | ||||
|     /** | ||||
|      * @param       $route | ||||
|      * @param array $content | ||||
|      * | ||||
|      * @return void | ||||
|      * @param string $route | ||||
|      * @param string $language | ||||
|      * @param array  $content | ||||
|      */ | ||||
|     public function putInCache($route, array $content); | ||||
|     public function putInCache(string $route, string $language, array $content); | ||||
| } | ||||
|   | ||||
| @@ -1,176 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Reminders; | ||||
|  | ||||
| use Amount; | ||||
| use Auth; | ||||
| use Carbon\Carbon; | ||||
| use Config; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Models\Reminder; | ||||
| use Navigation; | ||||
| use Preferences; | ||||
|  | ||||
| /** | ||||
|  * Class ReminderHelper | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Reminders | ||||
|  */ | ||||
| class ReminderHelper implements ReminderHelperInterface | ||||
| { | ||||
|     /** | ||||
|      * @param PiggyBank $piggyBank | ||||
|      * @param Carbon    $start | ||||
|      * @param Carbon    $end | ||||
|      * | ||||
|      * @return Reminder | ||||
|      */ | ||||
|     public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $reminder = Auth::user()->reminders()->where('remindersable_id', $piggyBank->id)->onDates($start, $end)->first(); | ||||
|         if (is_null($reminder)) { | ||||
|  | ||||
|             if (!is_null($piggyBank->targetdate)) { | ||||
|                 // get ranges again, but now for the start date | ||||
|                 $ranges      = $this->getReminderRanges($piggyBank, $start); | ||||
|                 $currentRep  = $piggyBank->currentRelevantRep(); | ||||
|                 $left        = $piggyBank->targetamount - $currentRep->currentamount; | ||||
|                 $perReminder = count($ranges) == 0 ? $left : $left / count($ranges); | ||||
|             } else { | ||||
|                 $perReminder = null; | ||||
|                 $ranges      = []; | ||||
|                 $left        = 0; | ||||
|             } | ||||
|             $metaData = [ | ||||
|                 'perReminder' => $perReminder, | ||||
|                 'rangesCount' => count($ranges), | ||||
|                 'ranges'      => $ranges, | ||||
|                 'leftToSave'  => $left, | ||||
|             ]; | ||||
|  | ||||
|             $reminder = new Reminder; | ||||
|             $reminder->user()->associate(Auth::user()); | ||||
|             $reminder->startdate = $start; | ||||
|             $reminder->enddate   = $end; | ||||
|             $reminder->active    = true; | ||||
|             $reminder->metadata  = $metaData; | ||||
|             $reminder->notnow    = false; | ||||
|             $reminder->remindersable()->associate($piggyBank); | ||||
|             $reminder->save(); | ||||
|  | ||||
|             return $reminder; | ||||
|  | ||||
|         } else { | ||||
|             return $reminder; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create all reminders for a piggy bank for a given date. | ||||
|      * | ||||
|      * @param PiggyBank $piggyBank | ||||
|      * | ||||
|      * @param Carbon    $date | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function createReminders(PiggyBank $piggyBank, Carbon $date) | ||||
|     { | ||||
|         $ranges = $this->getReminderRanges($piggyBank); | ||||
|  | ||||
|         foreach ($ranges as $range) { | ||||
|             if ($date < $range['end'] && $date > $range['start']) { | ||||
|                 // create a reminder here! | ||||
|                 $this->createReminder($piggyBank, $range['start'], $range['end']); | ||||
|                 // stop looping, we're done. | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This routine will return an array consisting of two dates which indicate the start | ||||
|      * and end date for each reminder that this piggy bank will have, if the piggy bank has | ||||
|      * any reminders. For example: | ||||
|      * | ||||
|      * [12 mar - 15 mar] | ||||
|      * [15 mar - 18 mar] | ||||
|      * | ||||
|      * etcetera. | ||||
|      * | ||||
|      * Array is filled with tiny arrays with Carbon objects in them. | ||||
|      * | ||||
|      * @param PiggyBank $piggyBank | ||||
|      * @param Carbon    $date ; | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getReminderRanges(PiggyBank $piggyBank, Carbon $date = null) | ||||
|     { | ||||
|         $ranges = []; | ||||
|         if (is_null($date)) { | ||||
|             $date = new Carbon; | ||||
|         } | ||||
|  | ||||
|         if ($piggyBank->remind_me === false) { | ||||
|             return $ranges; | ||||
|         } | ||||
|  | ||||
|         if (!is_null($piggyBank->targetdate)) { | ||||
|             // count back until now. | ||||
|             $start = $piggyBank->targetdate; | ||||
|             $end   = $piggyBank->startdate; | ||||
|  | ||||
|             while ($start > $end) { | ||||
|                 $currentEnd   = clone $start; | ||||
|                 $start        = Navigation::subtractPeriod($start, $piggyBank->reminder, 1); | ||||
|                 $currentStart = clone $start; | ||||
|                 $ranges[]     = ['start' => clone $currentStart, 'end' => clone $currentEnd]; | ||||
|             } | ||||
|         } else { | ||||
|             $start = clone $piggyBank->startdate; | ||||
|             while ($start < $date) { | ||||
|                 $currentStart = clone $start; | ||||
|                 $start        = Navigation::addPeriod($start, $piggyBank->reminder, 0); | ||||
|                 $currentEnd   = clone $start; | ||||
|                 $ranges[]     = ['start' => clone $currentStart, 'end' => clone $currentEnd]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $ranges; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Takes a reminder, finds the piggy bank and tells you what to do now. | ||||
|      * Aka how much money to put in. | ||||
|      * | ||||
|      * | ||||
|      * @param Reminder $reminder | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getReminderText(Reminder $reminder) | ||||
|     { | ||||
|         /** @var PiggyBank $piggyBank */ | ||||
|         $piggyBank = $reminder->remindersable; | ||||
|  | ||||
|         if (is_null($piggyBank)) { | ||||
|             return trans('firefly.piggy_bank_not_exists'); | ||||
|         } | ||||
|  | ||||
|         if (is_null($piggyBank->targetdate)) { | ||||
|             return trans('firefly.add_any_amount_to_piggy', ['amount' => Amount::format($piggyBank->targetamount)]); | ||||
|         } | ||||
|  | ||||
|         $lang = Preferences::get('language', 'en')->data; | ||||
|  | ||||
|         return trans( | ||||
|             'firefly.add_set_amount_to_piggy', | ||||
|             [ | ||||
|                 'amount' => Amount::format($reminder->metadata->perReminder), | ||||
|                 'date'   => $piggyBank->targetdate->formatLocalized(Config::get('firefly.monthAndDay.' . $lang)) | ||||
|             ] | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -1,63 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Reminders; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Models\Reminder; | ||||
|  | ||||
| /** | ||||
|  * Interface ReminderHelperInterface | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Reminders | ||||
|  */ | ||||
| interface ReminderHelperInterface | ||||
| { | ||||
|     /** | ||||
|      * Takes a reminder, finds the piggy bank and tells you what to do now. | ||||
|      * Aka how much money to put in. | ||||
|      * | ||||
|      * @param Reminder $reminder | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getReminderText(Reminder $reminder); | ||||
|  | ||||
|     /** | ||||
|      * This routine will return an array consisting of two dates which indicate the start | ||||
|      * and end date for each reminder that this piggy bank will have, if the piggy bank has | ||||
|      * any reminders. For example: | ||||
|      * | ||||
|      * [12 mar - 15 mar] | ||||
|      * [15 mar - 18 mar] | ||||
|      * | ||||
|      * etcetera. | ||||
|      * | ||||
|      * Array is filled with tiny arrays with Carbon objects in them. | ||||
|      * | ||||
|      * @param PiggyBank $piggyBank | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getReminderRanges(PiggyBank $piggyBank); | ||||
|  | ||||
|     /** | ||||
|      * @param PiggyBank $piggyBank | ||||
|      * @param Carbon    $start | ||||
|      * @param Carbon    $end | ||||
|      * | ||||
|      * @return Reminder | ||||
|      */ | ||||
|     public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end); | ||||
|  | ||||
|     /** | ||||
|      * Create all reminders for a piggy bank for a given date. | ||||
|      * | ||||
|      * @param PiggyBank $piggyBank | ||||
|      * | ||||
|      * @param Carbon    $date | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function createReminders(PiggyBank $piggyBank, Carbon $date); | ||||
| } | ||||
							
								
								
									
										293
									
								
								app/Helpers/Report/BalanceReportHelper.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								app/Helpers/Report/BalanceReportHelper.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,293 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BalanceReportHelper.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\Report; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use DB; | ||||
| use FireflyIII\Helpers\Collection\Balance; | ||||
| use FireflyIII\Helpers\Collection\BalanceEntry; | ||||
| use FireflyIII\Helpers\Collection\BalanceHeader; | ||||
| use FireflyIII\Helpers\Collection\BalanceLine; | ||||
| use FireflyIII\Models\Budget; | ||||
| use FireflyIII\Models\LimitRepetition; | ||||
| use FireflyIII\Models\Tag; | ||||
| use FireflyIII\Models\TransactionType; | ||||
| use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; | ||||
| use Illuminate\Database\Query\JoinClause; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class BalanceReportHelper | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Report | ||||
|  */ | ||||
| class BalanceReportHelper implements BalanceReportHelperInterface | ||||
| { | ||||
|  | ||||
|     /** @var  BudgetRepositoryInterface */ | ||||
|     protected $budgetRepository; | ||||
|  | ||||
|     /** | ||||
|      * ReportHelper constructor. | ||||
|      * | ||||
|      * | ||||
|      * @param BudgetRepositoryInterface $budgetRepository | ||||
|      */ | ||||
|     public function __construct(BudgetRepositoryInterface $budgetRepository) | ||||
|     { | ||||
|         $this->budgetRepository = $budgetRepository; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Balance | ||||
|      */ | ||||
|     public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts): Balance | ||||
|     { | ||||
|         $balance          = new Balance; | ||||
|         $header           = new BalanceHeader; | ||||
|         $limitRepetitions = $this->budgetRepository->getAllBudgetLimitRepetitions($start, $end); | ||||
|         foreach ($accounts as $account) { | ||||
|             $header->addAccount($account); | ||||
|         } | ||||
|  | ||||
|         /** @var LimitRepetition $repetition */ | ||||
|         foreach ($limitRepetitions as $repetition) { | ||||
|             $budget = $this->budgetRepository->find($repetition->budget_id); | ||||
|             $line   = $this->createBalanceLine($budget, $repetition, $accounts); | ||||
|             $balance->addBalanceLine($line); | ||||
|         } | ||||
|         $noBudgetLine       = $this->createNoBudgetLine($accounts, $start, $end); | ||||
|         $coveredByTagLine   = $this->createTagsBalanceLine($accounts, $start, $end); | ||||
|         $leftUnbalancedLine = $this->createLeftUnbalancedLine($noBudgetLine, $coveredByTagLine); | ||||
|  | ||||
|         $balance->addBalanceLine($noBudgetLine); | ||||
|         $balance->addBalanceLine($coveredByTagLine); | ||||
|         $balance->addBalanceLine($leftUnbalancedLine); | ||||
|         $balance->setBalanceHeader($header); | ||||
|  | ||||
|         // remove budgets without expenses from balance lines: | ||||
|         $balance = $this->removeUnusedBudgets($balance); | ||||
|  | ||||
|         return $balance; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method collects all transfers that are part of a "balancing act" tag | ||||
|      * and groups the amounts of those transfers by their destination account. | ||||
|      * | ||||
|      * This is used to indicate which expenses, usually outside of budgets, have been | ||||
|      * corrected by transfers from a savings account. | ||||
|      * | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     private function allCoveredByBalancingActs(Collection $accounts, Carbon $start, Carbon $end): Collection | ||||
|     { | ||||
|         $ids = $accounts->pluck('id')->toArray(); | ||||
|         $set = auth()->user()->tags() | ||||
|                      ->leftJoin('tag_transaction_journal', 'tag_transaction_journal.tag_id', '=', 'tags.id') | ||||
|                      ->leftJoin('transaction_journals', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                      ->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id') | ||||
|                      ->leftJoin( | ||||
|                          'transactions AS t_source', function (JoinClause $join) { | ||||
|                          $join->on('transaction_journals.id', '=', 't_source.transaction_journal_id')->where('t_source.amount', '<', 0); | ||||
|                      } | ||||
|                      ) | ||||
|                      ->leftJoin( | ||||
|                          'transactions AS t_destination', function (JoinClause $join) { | ||||
|                          $join->on('transaction_journals.id', '=', 't_destination.transaction_journal_id')->where('t_destination.amount', '>', 0); | ||||
|                      } | ||||
|                      ) | ||||
|                      ->where('tags.tagMode', 'balancingAct') | ||||
|                      ->where('transaction_types.type', TransactionType::TRANSFER) | ||||
|                      ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) | ||||
|                      ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) | ||||
|                      ->whereNull('transaction_journals.deleted_at') | ||||
|                      ->whereIn('t_source.account_id', $ids) | ||||
|                      ->whereIn('t_destination.account_id', $ids) | ||||
|                      ->groupBy('t_destination.account_id') | ||||
|                      ->get( | ||||
|                          [ | ||||
|                              't_destination.account_id', | ||||
|                              DB::raw('SUM(t_destination.amount) AS sum'), | ||||
|                          ] | ||||
|                      ); | ||||
|  | ||||
|         return $set; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param Budget          $budget | ||||
|      * @param LimitRepetition $repetition | ||||
|      * @param Collection      $accounts | ||||
|      * | ||||
|      * @return BalanceLine | ||||
|      */ | ||||
|     private function createBalanceLine(Budget $budget, LimitRepetition $repetition, Collection $accounts): BalanceLine | ||||
|     { | ||||
|         $line           = new BalanceLine; | ||||
|         $budget->amount = $repetition->amount; | ||||
|         $line->setBudget($budget); | ||||
|         $line->setStartDate($repetition->startdate); | ||||
|         $line->setEndDate($repetition->enddate); | ||||
|  | ||||
|         // loop accounts: | ||||
|         foreach ($accounts as $account) { | ||||
|             $balanceEntry = new BalanceEntry; | ||||
|             $balanceEntry->setAccount($account); | ||||
|             $spent = $this->budgetRepository->spentInPeriod( | ||||
|                 new Collection([$budget]), new Collection([$account]), $repetition->startdate, $repetition->enddate | ||||
|             ); | ||||
|             $balanceEntry->setSpent($spent); | ||||
|             $line->addBalanceEntry($balanceEntry); | ||||
|         } | ||||
|  | ||||
|         return $line; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param BalanceLine $noBudgetLine | ||||
|      * @param BalanceLine $coveredByTagLine | ||||
|      * | ||||
|      * @return BalanceLine | ||||
|      */ | ||||
|     private function createLeftUnbalancedLine(BalanceLine $noBudgetLine, BalanceLine $coveredByTagLine): BalanceLine | ||||
|     { | ||||
|         $line = new BalanceLine; | ||||
|         $line->setRole(BalanceLine::ROLE_DIFFROLE); | ||||
|         $noBudgetEntries = $noBudgetLine->getBalanceEntries(); | ||||
|         $tagEntries      = $coveredByTagLine->getBalanceEntries(); | ||||
|  | ||||
|         foreach ($noBudgetEntries as $entry) { | ||||
|             $account  = $entry->getAccount(); | ||||
|             $tagEntry = $tagEntries->filter( | ||||
|                 function (BalanceEntry $current) use ($account) { | ||||
|                     return $current->getAccount()->id === $account->id; | ||||
|                 } | ||||
|             ); | ||||
|             if ($tagEntry->first()) { | ||||
|                 // found corresponding entry. As we should: | ||||
|                 $newEntry = new BalanceEntry; | ||||
|                 $newEntry->setAccount($account); | ||||
|                 $spent = bcadd($tagEntry->first()->getLeft(), $entry->getSpent()); | ||||
|                 $newEntry->setSpent($spent); | ||||
|                 $line->addBalanceEntry($newEntry); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $line; | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return BalanceLine | ||||
|      */ | ||||
|     private function createNoBudgetLine(Collection $accounts, Carbon $start, Carbon $end): BalanceLine | ||||
|     { | ||||
|         $empty = new BalanceLine; | ||||
|  | ||||
|         foreach ($accounts as $account) { | ||||
|             $spent = $this->budgetRepository->spentInPeriodWithoutBudget(new Collection([$account]), $start, $end); | ||||
|             // budget | ||||
|             $budgetEntry = new BalanceEntry; | ||||
|             $budgetEntry->setAccount($account); | ||||
|             $budgetEntry->setSpent($spent); | ||||
|             $empty->addBalanceEntry($budgetEntry); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return $empty; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return BalanceLine | ||||
|      */ | ||||
|     private function createTagsBalanceLine(Collection $accounts, Carbon $start, Carbon $end): BalanceLine | ||||
|     { | ||||
|         $tags     = new BalanceLine; | ||||
|         $tagsLeft = $this->allCoveredByBalancingActs($accounts, $start, $end); | ||||
|  | ||||
|         $tags->setRole(BalanceLine::ROLE_TAGROLE); | ||||
|  | ||||
|         foreach ($accounts as $account) { | ||||
|             $leftEntry = $tagsLeft->filter( | ||||
|                 function (Tag $tag) use ($account) { | ||||
|                     return $tag->account_id == $account->id; | ||||
|                 } | ||||
|             ); | ||||
|             $left      = '0'; | ||||
|             if (!is_null($leftEntry->first())) { | ||||
|                 $left = $leftEntry->first()->sum; | ||||
|             } | ||||
|  | ||||
|             // balanced by tags | ||||
|             $tagEntry = new BalanceEntry; | ||||
|             $tagEntry->setAccount($account); | ||||
|             $tagEntry->setLeft($left); | ||||
|             $tags->addBalanceEntry($tagEntry); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return $tags; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Balance $balance | ||||
|      * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5. | ||||
|      * | ||||
|      * @return Balance | ||||
|      */ | ||||
|     private function removeUnusedBudgets(Balance $balance): Balance | ||||
|     { | ||||
|         $set    = $balance->getBalanceLines(); | ||||
|         $newSet = new Collection; | ||||
|         foreach ($set as $entry) { | ||||
|             if (!is_null($entry->getBudget()->id)) { | ||||
|                 $sum = '0'; | ||||
|                 foreach ($entry->getBalanceEntries() as $balanceEntry) { | ||||
|                     $sum = bcadd($sum, $balanceEntry->getSpent()); | ||||
|                 } | ||||
|                 if (bccomp($sum, '0') === -1) { | ||||
|                     $newSet->push($entry); | ||||
|                 } | ||||
|                 continue; | ||||
|             } | ||||
|             $newSet->push($entry); | ||||
|         } | ||||
|  | ||||
|         $balance->setBalanceLines($newSet); | ||||
|  | ||||
|         return $balance; | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										36
									
								
								app/Helpers/Report/BalanceReportHelperInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								app/Helpers/Report/BalanceReportHelperInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BalanceReportHelperInterface.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\Report; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Helpers\Collection\Balance; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Interface BalanceReportHelperInterface | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Report | ||||
|  */ | ||||
| interface BalanceReportHelperInterface | ||||
| { | ||||
|     /** | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Balance | ||||
|      */ | ||||
|     public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts): Balance; | ||||
| } | ||||
							
								
								
									
										277
									
								
								app/Helpers/Report/BudgetReportHelper.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								app/Helpers/Report/BudgetReportHelper.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,277 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BudgetReportHelper.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\Report; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Helpers\Collection\Budget as BudgetCollection; | ||||
| use FireflyIII\Helpers\Collection\BudgetLine; | ||||
| use FireflyIII\Models\Budget; | ||||
| use FireflyIII\Models\LimitRepetition; | ||||
| use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; | ||||
| use FireflyIII\Support\CacheProperties; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class BudgetReportHelper | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Report | ||||
|  */ | ||||
| class BudgetReportHelper implements BudgetReportHelperInterface | ||||
| { | ||||
|     /** @var BudgetRepositoryInterface */ | ||||
|     private $repository; | ||||
|  | ||||
|     /** | ||||
|      * BudgetReportHelper constructor. | ||||
|      * | ||||
|      * @param BudgetRepositoryInterface $repository | ||||
|      */ | ||||
|     public function __construct(BudgetRepositoryInterface $repository) | ||||
|     { | ||||
|         $this->repository = $repository; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.ExcessiveMethodLength) // at 43, its ok. | ||||
|      * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5. | ||||
|      * | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection | ||||
|     { | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties; | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty('budget-year'); | ||||
|         $cache->addProperty($accounts->pluck('id')->toArray()); | ||||
|         if ($cache->has()) { | ||||
|             return $cache->get(); | ||||
|         } | ||||
|  | ||||
|         $current = clone $start; | ||||
|         $return  = new Collection; | ||||
|         $set     = $this->repository->getBudgets(); | ||||
|         $budgets = []; | ||||
|         $spent   = []; | ||||
|         $headers = $this->createYearHeaders($current, $end); | ||||
|  | ||||
|         /** @var Budget $budget */ | ||||
|         foreach ($set as $budget) { | ||||
|             $id           = $budget->id; | ||||
|             $budgets[$id] = $budget->name; | ||||
|             $current      = clone $start; | ||||
|             $budgetData   = $this->getBudgetSpentData($current, $end, $budget, $accounts); | ||||
|             $sum          = $budgetData['sum']; | ||||
|             $spent[$id]   = $budgetData['spent']; | ||||
|  | ||||
|             if (bccomp('0', $sum) === 0) { | ||||
|                 // not spent anything. | ||||
|                 unset($spent[$id]); | ||||
|                 unset($budgets[$id]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $return->put('headers', $headers); | ||||
|         $return->put('budgets', $budgets); | ||||
|         $return->put('spent', $spent); | ||||
|  | ||||
|         $cache->store($return); | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return BudgetCollection | ||||
|      */ | ||||
|     public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts): BudgetCollection | ||||
|     { | ||||
|         $object = new BudgetCollection; | ||||
|         $set    = $this->repository->getBudgets(); | ||||
|  | ||||
|         /** @var Budget $budget */ | ||||
|         foreach ($set as $budget) { | ||||
|             $repetitions = $budget->limitrepetitions()->before($end)->after($start)->get(); | ||||
|  | ||||
|             // no repetition(s) for this budget: | ||||
|             if ($repetitions->count() == 0) { | ||||
|                 // spent for budget in time range: | ||||
|                 $spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end); | ||||
|  | ||||
|                 if ($spent > 0) { | ||||
|                     $budgetLine = new BudgetLine; | ||||
|                     $budgetLine->setBudget($budget)->setOverspent($spent); | ||||
|                     $object->addOverspent($spent)->addBudgetLine($budgetLine); | ||||
|                 } | ||||
|                 continue; | ||||
|             } | ||||
|             // one or more repetitions for budget: | ||||
|             /** @var LimitRepetition $repetition */ | ||||
|             foreach ($repetitions as $repetition) { | ||||
|                 $data = $this->calculateExpenses($budget, $repetition, $accounts); | ||||
|  | ||||
|                 $budgetLine = new BudgetLine; | ||||
|                 $budgetLine->setBudget($budget)->setRepetition($repetition) | ||||
|                            ->setLeft($data['left'])->setSpent($data['expenses'])->setOverspent($data['overspent']) | ||||
|                            ->setBudgeted(strval($repetition->amount)); | ||||
|  | ||||
|                 $object->addBudgeted(strval($repetition->amount))->addSpent($data['spent']) | ||||
|                        ->addLeft($data['left'])->addOverspent($data['overspent'])->addBudgetLine($budgetLine); | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         // stuff outside of budgets: | ||||
|  | ||||
|         $noBudget   = $this->repository->spentInPeriodWithoutBudget($accounts, $start, $end); | ||||
|         $budgetLine = new BudgetLine; | ||||
|         $budgetLine->setOverspent($noBudget)->setSpent($noBudget); | ||||
|         $object->addOverspent($noBudget)->addBudgetLine($budgetLine); | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getBudgetsWithExpenses(Carbon $start, Carbon $end, Collection $accounts): Collection | ||||
|     { | ||||
|         /** @var BudgetRepositoryInterface $repository */ | ||||
|         $repository = app(BudgetRepositoryInterface::class); | ||||
|         $budgets    = $repository->getActiveBudgets(); | ||||
|  | ||||
|         $set = new Collection; | ||||
|         /** @var Budget $budget */ | ||||
|         foreach ($budgets as $budget) { | ||||
|             $total = $repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end); | ||||
|             if (bccomp($total, '0') === -1) { | ||||
|                 $set->push($budget); | ||||
|             } | ||||
|         } | ||||
|         $set = $set->sortBy( | ||||
|             function (Budget $budget) { | ||||
|                 return $budget->name; | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return $set; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay | ||||
|      * and sum up everything in the array in the given range. | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param array  $array | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function getSumOfRange(Carbon $start, Carbon $end, array $array) | ||||
|     { | ||||
|         $sum          = '0'; | ||||
|         $currentStart = clone $start; // to not mess with the original one | ||||
|         $currentEnd   = clone $end; // to not mess with the original one | ||||
|  | ||||
|         while ($currentStart <= $currentEnd) { | ||||
|             $date = $currentStart->format('Y-m-d'); | ||||
|             if (isset($array[$date])) { | ||||
|                 $sum = bcadd($sum, $array[$date]); | ||||
|             } | ||||
|             $currentStart->addDay(); | ||||
|         } | ||||
|  | ||||
|         return $sum; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Budget          $budget | ||||
|      * @param LimitRepetition $repetition | ||||
|      * @param Collection      $accounts | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function calculateExpenses(Budget $budget, LimitRepetition $repetition, Collection $accounts): array | ||||
|     { | ||||
|         $array              = []; | ||||
|         $expenses           = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $repetition->startdate, $repetition->enddate); | ||||
|         $array['left']      = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? bcadd($repetition->amount, $expenses) : '0'; | ||||
|         $array['spent']     = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? $expenses : '0'; | ||||
|         $array['overspent'] = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $repetition->amount); | ||||
|         $array['expenses']  = $expenses; | ||||
|  | ||||
|         return $array; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $current | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function createYearHeaders(Carbon $current, Carbon $end): array | ||||
|     { | ||||
|         $headers = []; | ||||
|         while ($current < $end) { | ||||
|             $short           = $current->format('m-Y'); | ||||
|             $headers[$short] = $current->formatLocalized((string)trans('config.month')); | ||||
|             $current->addMonth(); | ||||
|         } | ||||
|  | ||||
|         return $headers; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon     $current | ||||
|      * @param Carbon     $end | ||||
|      * @param Budget     $budget | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getBudgetSpentData(Carbon $current, Carbon $end, Budget $budget, Collection $accounts): array | ||||
|     { | ||||
|         $sum   = '0'; | ||||
|         $spent = []; | ||||
|         while ($current < $end) { | ||||
|             $currentEnd = clone $current; | ||||
|             $currentEnd->endOfMonth(); | ||||
|             $format         = $current->format('m-Y'); | ||||
|             $budgetSpent    = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $current, $currentEnd); | ||||
|             $spent[$format] = $budgetSpent; | ||||
|             $sum            = bcadd($sum, $budgetSpent); | ||||
|             $current->addMonth(); | ||||
|         } | ||||
|  | ||||
|         return [ | ||||
|             'spent' => $spent, | ||||
|             'sum'   => $sum, | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										55
									
								
								app/Helpers/Report/BudgetReportHelperInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								app/Helpers/Report/BudgetReportHelperInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BudgetReportHelperInterface.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\Report; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Helpers\Collection\Budget as BudgetCollection; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface BudgetReportHelperInterface | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Report | ||||
|  */ | ||||
| interface BudgetReportHelperInterface | ||||
| { | ||||
|     /** | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection; | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return BudgetCollection | ||||
|      */ | ||||
|     public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts): BudgetCollection; | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getBudgetsWithExpenses(Carbon $start, Carbon $end, Collection $accounts): Collection; | ||||
|  | ||||
| } | ||||
| @@ -1,25 +1,38 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ReportHelper.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\Report; | ||||
|  | ||||
| use App; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Helpers\Collection\Account as AccountCollection; | ||||
| use FireflyIII\Helpers\Collection\Balance; | ||||
| use FireflyIII\Helpers\Collection\BalanceEntry; | ||||
| use FireflyIII\Helpers\Collection\BalanceHeader; | ||||
| use FireflyIII\Helpers\Collection\BalanceLine; | ||||
| use FireflyIII\Helpers\Collection\Bill as BillCollection; | ||||
| use FireflyIII\Helpers\Collection\BillLine; | ||||
| use FireflyIII\Helpers\Collection\Budget as BudgetCollection; | ||||
| use FireflyIII\Helpers\Collection\BudgetLine; | ||||
| use FireflyIII\Helpers\Collection\Category as CategoryCollection; | ||||
| use FireflyIII\Helpers\Collection\Expense; | ||||
| use FireflyIII\Helpers\Collection\Income; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Helpers\FiscalHelperInterface; | ||||
| use FireflyIII\Models\Bill; | ||||
| use FireflyIII\Models\Budget as BudgetModel; | ||||
| use FireflyIII\Models\LimitRepetition; | ||||
| use FireflyIII\Models\Category; | ||||
| use FireflyIII\Models\Tag; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use FireflyIII\Repositories\Account\AccountTaskerInterface; | ||||
| use FireflyIII\Repositories\Bill\BillRepositoryInterface; | ||||
| use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; | ||||
| use FireflyIII\Repositories\Category\CategoryRepositoryInterface; | ||||
| use FireflyIII\Repositories\Tag\TagRepositoryInterface; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Query\JoinClause; | ||||
| use Illuminate\Support\Collection; | ||||
| use stdClass; | ||||
|  | ||||
| /** | ||||
|  * Class ReportHelper | ||||
| @@ -29,324 +42,118 @@ use FireflyIII\Models\LimitRepetition; | ||||
| class ReportHelper implements ReportHelperInterface | ||||
| { | ||||
|  | ||||
|     /** @var ReportQueryInterface */ | ||||
|     protected $query; | ||||
|     /** @var  BudgetRepositoryInterface */ | ||||
|     protected $budgetRepository; | ||||
|     /** @var  TagRepositoryInterface */ | ||||
|     protected $tagRepository; | ||||
|  | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * ReportHelper constructor. | ||||
|      * | ||||
|      * @param ReportQueryInterface $query | ||||
|      * | ||||
|      * @param BudgetRepositoryInterface $budgetRepository | ||||
|      * @param TagRepositoryInterface    $tagRepository | ||||
|      */ | ||||
|     public function __construct(ReportQueryInterface $query) | ||||
|     public function __construct(BudgetRepositoryInterface $budgetRepository, TagRepositoryInterface $tagRepository) | ||||
|     { | ||||
|         $this->query = $query; | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * This method generates a full report for the given period on all | ||||
|      * the users asset and cash accounts. | ||||
|      * | ||||
|      * @param Carbon $date | ||||
|      * @param Carbon $end | ||||
|      * @param        $shared | ||||
|      * | ||||
|      * @return AccountCollection | ||||
|      */ | ||||
|     public function getAccountReport(Carbon $date, Carbon $end, $shared) | ||||
|     { | ||||
|  | ||||
|  | ||||
|         $accounts = $this->query->getAllAccounts($date, $end, $shared); | ||||
|         $start    = 0; | ||||
|         $end      = 0; | ||||
|         $diff     = 0; | ||||
|  | ||||
|         // remove cash account, if any: | ||||
|         $accounts = $accounts->filter( | ||||
|             function (Account $account) { | ||||
|                 if ($account->accountType->type != 'Cash account') { | ||||
|                     return $account; | ||||
|                 } | ||||
|  | ||||
|                 return null; | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         // summarize: | ||||
|         foreach ($accounts as $account) { | ||||
|             $start += $account->startBalance; | ||||
|             $end += $account->endBalance; | ||||
|             $diff += ($account->endBalance - $account->startBalance); | ||||
|         } | ||||
|  | ||||
|         $object = new AccountCollection; | ||||
|         $object->setStart($start); | ||||
|         $object->setEnd($end); | ||||
|         $object->setDifference($diff); | ||||
|         $object->setAccounts($accounts); | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * The balance report contains a Balance object which in turn contains: | ||||
|      * | ||||
|      * A BalanceHeader object which contains all relevant user asset accounts for the report. | ||||
|      * | ||||
|      * A number of BalanceLine objects, which hold: | ||||
|      * - A budget | ||||
|      * - A number of BalanceEntry objects. | ||||
|      * | ||||
|      * The BalanceEntry object holds: | ||||
|      *   - The same budget (again) | ||||
|      *   - A user asset account as mentioned in the BalanceHeader | ||||
|      *   - The amount of money spent on the budget by the user asset account | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * | ||||
|      * @return Balance | ||||
|      */ | ||||
|     public function getBalanceReport(Carbon $start, Carbon $end, $shared) | ||||
|     { | ||||
|         $repository    = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); | ||||
|         $tagRepository = App::make('FireflyIII\Repositories\Tag\TagRepositoryInterface'); | ||||
|         $balance       = new Balance; | ||||
|  | ||||
|         // build a balance header: | ||||
|         $header = new BalanceHeader; | ||||
|  | ||||
|         $accounts = $this->query->getAllAccounts($start, $end, $shared); | ||||
|         $budgets  = $repository->getBudgets(); | ||||
|         foreach ($accounts as $account) { | ||||
|             $header->addAccount($account); | ||||
|         } | ||||
|  | ||||
|         /** @var BudgetModel $budget */ | ||||
|         foreach ($budgets as $budget) { | ||||
|             $line = new BalanceLine; | ||||
|             $line->setBudget($budget); | ||||
|  | ||||
|             // get budget amount for current period: | ||||
|             $rep = $repository->getCurrentRepetition($budget, $start); | ||||
|             $line->setRepetition($rep); | ||||
|  | ||||
|             // loop accounts: | ||||
|             foreach ($accounts as $account) { | ||||
|                 $balanceEntry = new BalanceEntry; | ||||
|                 $balanceEntry->setAccount($account); | ||||
|  | ||||
|                 // get spent: | ||||
|                 $spent = $this->query->spentInBudgetCorrected($account, $budget, $start, $end); // I think shared is irrelevant. | ||||
|  | ||||
|                 $balanceEntry->setSpent($spent); | ||||
|                 $line->addBalanceEntry($balanceEntry); | ||||
|             } | ||||
|             // add line to balance: | ||||
|             $balance->addBalanceLine($line); | ||||
|         } | ||||
|  | ||||
|         // then a new line for without budget. | ||||
|         // and one for the tags: | ||||
|         $empty    = new BalanceLine; | ||||
|         $tags     = new BalanceLine; | ||||
|         $diffLine = new BalanceLine; | ||||
|  | ||||
|         $tags->setRole(BalanceLine::ROLE_TAGROLE); | ||||
|         $diffLine->setRole(BalanceLine::ROLE_DIFFROLE); | ||||
|  | ||||
|         foreach ($accounts as $account) { | ||||
|             $spent = $this->query->spentNoBudget($account, $start, $end); | ||||
|             $left  = $tagRepository->coveredByBalancingActs($account, $start, $end); | ||||
|             $diff  = $spent + $left; | ||||
|  | ||||
|             // budget | ||||
|             $budgetEntry = new BalanceEntry; | ||||
|             $budgetEntry->setAccount($account); | ||||
|             $budgetEntry->setSpent($spent); | ||||
|             $empty->addBalanceEntry($budgetEntry); | ||||
|  | ||||
|             // balanced by tags | ||||
|             $tagEntry = new BalanceEntry; | ||||
|             $tagEntry->setAccount($account); | ||||
|             $tagEntry->setLeft($left); | ||||
|             $tags->addBalanceEntry($tagEntry); | ||||
|  | ||||
|             // difference: | ||||
|             $diffEntry = new BalanceEntry; | ||||
|             $diffEntry->setAccount($account); | ||||
|             $diffEntry->setSpent($diff); | ||||
|             $diffLine->addBalanceEntry($diffEntry); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         $balance->addBalanceLine($empty); | ||||
|         $balance->addBalanceLine($tags); | ||||
|         $balance->addBalanceLine($diffLine); | ||||
|  | ||||
|         $balance->setBalanceHeader($header); | ||||
|  | ||||
|         return $balance; | ||||
|         $this->budgetRepository = $budgetRepository; | ||||
|         $this->tagRepository    = $tagRepository; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method generates a full report for the given period on all | ||||
|      * the users bills and their payments. | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * Excludes bills which have not had a payment on the mentioned accounts. | ||||
|      * | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return BillCollection | ||||
|      */ | ||||
|     public function getBillReport(Carbon $start, Carbon $end, $shared) | ||||
|     public function getBillReport(Carbon $start, Carbon $end, Collection $accounts): BillCollection | ||||
|     { | ||||
|         /** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */ | ||||
|         $repository = App::make('FireflyIII\Repositories\Bill\BillRepositoryInterface'); | ||||
|         $bills      = $repository->getBills(); | ||||
|         /** @var BillRepositoryInterface $repository */ | ||||
|         $repository = app(BillRepositoryInterface::class); | ||||
|         $bills      = $repository->getBillsForAccounts($accounts); | ||||
|         $journals   = $repository->getAllJournalsInRange($bills, $start, $end); | ||||
|         $collection = new BillCollection; | ||||
|  | ||||
|         /** @var Bill $bill */ | ||||
|         foreach ($bills as $bill) { | ||||
|             $billLine = new BillLine; | ||||
|             $billLine->setBill($bill); | ||||
|             $billLine->setActive(intval($bill->active) == 1); | ||||
|             $billLine->setMin(floatval($bill->amount_min)); | ||||
|             $billLine->setMax(floatval($bill->amount_max)); | ||||
|  | ||||
|             $billLine->setActive(intval($bill->active) === 1); | ||||
|             $billLine->setMin(strval($bill->amount_min)); | ||||
|             $billLine->setMax(strval($bill->amount_max)); | ||||
|             $billLine->setHit(false); | ||||
|             // is hit in period? | ||||
|             $set = $repository->getJournalsInRange($bill, $start, $end); | ||||
|             if ($set->count() == 0) { | ||||
|                 $billLine->setHit(false); | ||||
|             } else { | ||||
|                 $billLine->setHit(true); | ||||
|                 $amount = 0; | ||||
|                 foreach ($set as $entry) { | ||||
|                     $amount += $entry->amount; | ||||
|  | ||||
|             $entry = $journals->filter( | ||||
|                 function (TransactionJournal $journal) use ($bill) { | ||||
|                     return $journal->bill_id === $bill->id; | ||||
|                 } | ||||
|                 $billLine->setAmount($amount); | ||||
|             ); | ||||
|             $first = $entry->first(); | ||||
|             if (!is_null($first)) { | ||||
|                 $billLine->setTransactionJournalId($first->id); | ||||
|                 $billLine->setAmount($first->journalAmount); | ||||
|                 $billLine->setHit(true); | ||||
|             } | ||||
|  | ||||
|             $collection->addBill($billLine); | ||||
|  | ||||
|             if ($billLine->isActive()) { | ||||
|                 $collection->addBill($billLine); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $collection; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * | ||||
|      * @return BudgetCollection | ||||
|      */ | ||||
|     public function getBudgetReport(Carbon $start, Carbon $end, $shared) | ||||
|     { | ||||
|         $object = new BudgetCollection; | ||||
|         /** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */ | ||||
|         $repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); | ||||
|         $set        = $repository->getBudgets(); | ||||
|  | ||||
|         foreach ($set as $budget) { | ||||
|  | ||||
|             $repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end); | ||||
|  | ||||
|             // no repetition(s) for this budget: | ||||
|             if ($repetitions->count() == 0) { | ||||
|                 $spent      = $repository->spentInPeriodCorrected($budget, $start, $end, $shared); | ||||
|                 $budgetLine = new BudgetLine; | ||||
|                 $budgetLine->setBudget($budget); | ||||
|                 $budgetLine->setOverspent($spent); | ||||
|                 $object->addOverspent($spent); | ||||
|                 $object->addBudgetLine($budgetLine); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // one or more repetitions for budget: | ||||
|             /** @var LimitRepetition $repetition */ | ||||
|             foreach ($repetitions as $repetition) { | ||||
|                 $budgetLine = new BudgetLine; | ||||
|                 $budgetLine->setBudget($budget); | ||||
|                 $budgetLine->setRepetition($repetition); | ||||
|                 $expenses  = $repository->spentInPeriodCorrected($budget, $repetition->startdate, $repetition->enddate, $shared); | ||||
|                 $left      = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0; | ||||
|                 $spent     = $expenses > floatval($repetition->amount) ? 0 : $expenses; | ||||
|                 $overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0; | ||||
|  | ||||
|                 $budgetLine->setLeft($left); | ||||
|                 $budgetLine->setSpent($spent); | ||||
|                 $budgetLine->setOverspent($overspent); | ||||
|                 $budgetLine->setBudgeted($repetition->amount); | ||||
|  | ||||
|                 $object->addBudgeted($repetition->amount); | ||||
|                 $object->addSpent($spent); | ||||
|                 $object->addLeft($left); | ||||
|                 $object->addOverspent($overspent); | ||||
|                 $object->addBudgetLine($budgetLine); | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         // stuff outside of budgets: | ||||
|         $noBudget   = $repository->getWithoutBudgetSum($start, $end); | ||||
|         $budgetLine = new BudgetLine; | ||||
|         $budgetLine->setOverspent($noBudget); | ||||
|         $object->addOverspent($noBudget); | ||||
|         $object->addBudgetLine($budgetLine); | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return CategoryCollection | ||||
|      */ | ||||
|     public function getCategoryReport(Carbon $start, Carbon $end, $shared) | ||||
|     public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts): CategoryCollection | ||||
|     { | ||||
|         $object = new CategoryCollection; | ||||
|         /** @var CategoryRepositoryInterface $repository */ | ||||
|         $repository = app(CategoryRepositoryInterface::class); | ||||
|         $categories = $repository->getCategories(); | ||||
|  | ||||
|  | ||||
|         /** | ||||
|          * GET CATEGORIES: | ||||
|          */ | ||||
|         /** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */ | ||||
|         $repository = App::make('FireflyIII\Repositories\Category\CategoryRepositoryInterface'); | ||||
|         $set        = $repository->getCategories(); | ||||
|         foreach ($set as $category) { | ||||
|             $spent           = $repository->spentInPeriodCorrected($category, $start, $end, $shared); | ||||
|         /** @var Category $category */ | ||||
|         foreach ($categories as $category) { | ||||
|             $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end); | ||||
|             // CategoryCollection expects the amount in $spent: | ||||
|             $category->spent = $spent; | ||||
|             $object->addCategory($category); | ||||
|             $object->addTotal($spent); | ||||
|         } | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a full report on the users expenses during the period. | ||||
|      * Get a full report on the users expenses during the period for a list of accounts. | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Expense | ||||
|      */ | ||||
|     public function getExpenseReport($start, $end, $shared) | ||||
|     public function getExpenseReport(Carbon $start, Carbon $end, Collection $accounts): Expense | ||||
|     { | ||||
|         $object = new Expense; | ||||
|         $set    = $this->query->expenseInPeriodCorrected($start, $end, $shared); | ||||
|         foreach ($set as $entry) { | ||||
|  | ||||
|         /** @var AccountTaskerInterface $tasker */ | ||||
|         $tasker     = app(AccountTaskerInterface::class); | ||||
|         $collection = $tasker->expenseReport($accounts, $accounts, $start, $end); | ||||
|  | ||||
|         /** @var stdClass $entry */ | ||||
|         foreach ($collection as $entry) { | ||||
|             $object->addToTotal($entry->amount); | ||||
|             $object->addOrCreateExpense($entry); | ||||
|         } | ||||
| @@ -355,19 +162,23 @@ class ReportHelper implements ReportHelperInterface | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a full report on the users incomes during the period. | ||||
|      * Get a full report on the users incomes during the period for the given accounts. | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Income | ||||
|      */ | ||||
|     public function getIncomeReport($start, $end, $shared) | ||||
|     public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): Income | ||||
|     { | ||||
|         $object = new Income; | ||||
|         $set    = $this->query->incomeInPeriodCorrected($start, $end, $shared); | ||||
|         foreach ($set as $entry) { | ||||
|         /** @var AccountTaskerInterface $tasker */ | ||||
|         $tasker     = app(AccountTaskerInterface::class); | ||||
|         $collection = $tasker->incomeReport($accounts, $accounts, $start, $end); | ||||
|  | ||||
|         /** @var stdClass $entry */ | ||||
|         foreach ($collection as $entry) { | ||||
|             $object->addToTotal($entry->amount); | ||||
|             $object->addOrCreateIncome($entry); | ||||
|         } | ||||
| @@ -380,22 +191,143 @@ class ReportHelper implements ReportHelperInterface | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function listOfMonths(Carbon $date) | ||||
|     public function listOfMonths(Carbon $date): array | ||||
|     { | ||||
|  | ||||
|         $start  = clone $date; | ||||
|         $end    = Carbon::now(); | ||||
|         /** @var FiscalHelperInterface $fiscalHelper */ | ||||
|         $fiscalHelper = app(FiscalHelperInterface::class); | ||||
|         $start        = clone $date; | ||||
|         $start->startOfMonth(); | ||||
|         $end = Carbon::now(); | ||||
|         $end->endOfMonth(); | ||||
|         $months = []; | ||||
|  | ||||
|         while ($start <= $end) { | ||||
|             $year            = $start->year; | ||||
|             $months[$year][] = [ | ||||
|             $year = $fiscalHelper->endOfFiscalYear($start)->year; // current year | ||||
|             if (!isset($months[$year])) { | ||||
|                 $months[$year] = [ | ||||
|                     'fiscal_start' => $fiscalHelper->startOfFiscalYear($start)->format('Y-m-d'), | ||||
|                     'fiscal_end'   => $fiscalHelper->endOfFiscalYear($start)->format('Y-m-d'), | ||||
|                     'start'        => Carbon::createFromDate($year, 1, 1)->format('Y-m-d'), | ||||
|                     'end'          => Carbon::createFromDate($year, 12, 31)->format('Y-m-d'), | ||||
|                     'months'       => [], | ||||
|                 ]; | ||||
|             } | ||||
|  | ||||
|             $currentEnd = clone $start; | ||||
|             $currentEnd->endOfMonth(); | ||||
|             $months[$year]['months'][] = [ | ||||
|                 'formatted' => $start->formatLocalized('%B %Y'), | ||||
|                 'start'     => $start->format('Y-m-d'), | ||||
|                 'end'       => $currentEnd->format('Y-m-d'), | ||||
|                 'month'     => $start->month, | ||||
|                 'year'      => $year, | ||||
|             ]; | ||||
|             $start->addMonth(); | ||||
|  | ||||
|             $start = clone $currentEnd; // to make the hop to the next month properly | ||||
|             $start->addDay(); | ||||
|         } | ||||
|  | ||||
|         return $months; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns an array of tags and their comparitive size with amounts bla bla. | ||||
|      * | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function tagReport(Carbon $start, Carbon $end, Collection $accounts): array | ||||
|     { | ||||
|         $ids        = $accounts->pluck('id')->toArray(); | ||||
|         $set        = Tag:: | ||||
|         leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id') | ||||
|                          ->leftJoin('transaction_journals', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                          ->leftJoin( | ||||
|                              'transactions AS source', function (JoinClause $join) { | ||||
|                              $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', '0'); | ||||
|                          } | ||||
|                          ) | ||||
|                          ->leftJoin( | ||||
|                              'transactions AS destination', function (JoinClause $join) { | ||||
|                              $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', '0'); | ||||
|                          } | ||||
|                          ) | ||||
|                          ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) | ||||
|                          ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) | ||||
|                          ->where( | ||||
|                          // source.account_id in accountIds XOR destination.account_id in accountIds | ||||
|                              function (Builder $query) use ($ids) { | ||||
|                                  $query->where( | ||||
|                                      function (Builder $q1) use ($ids) { | ||||
|                                          $q1->whereIn('source.account_id', $ids) | ||||
|                                             ->whereNotIn('destination.account_id', $ids); | ||||
|                                      } | ||||
|                                  )->orWhere( | ||||
|                                      function (Builder $q2) use ($ids) { | ||||
|                                          $q2->whereIn('destination.account_id', $ids) | ||||
|                                             ->whereNotIn('source.account_id', $ids); | ||||
|                                      } | ||||
|                                  ); | ||||
|                              } | ||||
|                          ) | ||||
|                          ->get(['tags.id', 'tags.tag', 'transaction_journals.id as journal_id', 'destination.amount']); | ||||
|         $collection = []; | ||||
|         if ($set->count() === 0) { | ||||
|             return $collection; | ||||
|         } | ||||
|         /** @var Tag $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             // less than zero? multiply to be above zero. | ||||
|             $amount          = $entry->amount; | ||||
|             $id              = intval($entry->id); | ||||
|             $previousAmount  = $collection[$id]['amount'] ?? '0'; | ||||
|             $collection[$id] = [ | ||||
|                 'id'     => $id, | ||||
|                 'tag'    => $entry->tag, | ||||
|                 'amount' => bcadd($previousAmount, $amount), | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         // cleanup collection (match "fonts") | ||||
|         $max = strval(max(array_column($collection, 'amount'))); | ||||
|         foreach ($collection as $id => $entry) { | ||||
|             $size = bcdiv($entry['amount'], $max, 4); | ||||
|             if (bccomp($size, '0.25') === -1) { | ||||
|                 $size = '0.5'; | ||||
|             } | ||||
|             $collection[$id]['fontsize'] = $size; | ||||
|         } | ||||
|  | ||||
|         return $collection; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay | ||||
|      * and sum up everything in the array in the given range. | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param array  $array | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function getSumOfRange(Carbon $start, Carbon $end, array $array) | ||||
|     { | ||||
|         $sum          = '0'; | ||||
|         $currentStart = clone $start; // to not mess with the original one | ||||
|         $currentEnd   = clone $end; // to not mess with the original one | ||||
|  | ||||
|         while ($currentStart <= $currentEnd) { | ||||
|             $date = $currentStart->format('Y-m-d'); | ||||
|             if (isset($array[$date])) { | ||||
|                 $sum = bcadd($sum, $array[$date]); | ||||
|             } | ||||
|             $currentStart->addDay(); | ||||
|         } | ||||
|  | ||||
|         return $sum; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,15 +1,24 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ReportHelperInterface.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\Report; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Helpers\Collection\Account as AccountCollection; | ||||
| use FireflyIII\Helpers\Collection\Balance; | ||||
| use FireflyIII\Helpers\Collection\Bill as BillCollection; | ||||
| use FireflyIII\Helpers\Collection\Budget as BudgetCollection; | ||||
| use FireflyIII\Helpers\Collection\Category as CategoryCollection; | ||||
| use FireflyIII\Helpers\Collection\Expense; | ||||
| use FireflyIII\Helpers\Collection\Income; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface ReportHelperInterface | ||||
| @@ -19,84 +28,67 @@ use FireflyIII\Helpers\Collection\Income; | ||||
| interface ReportHelperInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * This method generates a full report for the given period on all | ||||
|      * the users asset and cash accounts. | ||||
|      * | ||||
|      * @param Carbon  $date | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * | ||||
|      * @return AccountCollection | ||||
|      */ | ||||
|     public function getAccountReport(Carbon $date, Carbon $end, $shared); | ||||
|  | ||||
|     /** | ||||
|      * This method generates a full report for the given period on all | ||||
|      * the users bills and their payments. | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * Excludes bills which have not had a payment on the mentioned accounts. | ||||
|      * | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return BillCollection | ||||
|      */ | ||||
|     public function getBillReport(Carbon $start, Carbon $end, $shared); | ||||
|     public function getBillReport(Carbon $start, Carbon $end, Collection $accounts): BillCollection; | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * | ||||
|      * @return Balance | ||||
|      */ | ||||
|     public function getBalanceReport(Carbon $start, Carbon $end, $shared); | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * | ||||
|      * @return BudgetCollection | ||||
|      */ | ||||
|     public function getBudgetReport(Carbon $start, Carbon $end, $shared); | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return CategoryCollection | ||||
|      */ | ||||
|     public function getCategoryReport(Carbon $start, Carbon $end, $shared); | ||||
|     public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts): CategoryCollection; | ||||
|  | ||||
|     /** | ||||
|      * Get a full report on the users expenses during the period. | ||||
|      * Get a full report on the users expenses during the period for a list of accounts. | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Expense | ||||
|      */ | ||||
|     public function getExpenseReport($start, $end, $shared); | ||||
|     public function getExpenseReport(Carbon $start, Carbon $end, Collection $accounts): Expense; | ||||
|  | ||||
|     /** | ||||
|      * Get a full report on the users incomes during the period. | ||||
|      * Get a full report on the users incomes during the period for the given accounts. | ||||
|      * | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param boolean $shared | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return Income | ||||
|      */ | ||||
|     public function getIncomeReport($start, $end, $shared); | ||||
|     public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): Income; | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function listOfMonths(Carbon $date); | ||||
|     public function listOfMonths(Carbon $date): array; | ||||
|  | ||||
|     /** | ||||
|      * Returns an array of tags and their comparitive size with amounts bla bla. | ||||
|      * | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function tagReport(Carbon $start, Carbon $end, Collection $accounts): array; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,285 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Report; | ||||
|  | ||||
| use Auth; | ||||
| use Carbon\Carbon; | ||||
| use Crypt; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Budget; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Query\JoinClause; | ||||
| use Illuminate\Support\Collection; | ||||
| use Steam; | ||||
|  | ||||
| /** | ||||
|  * Class ReportQuery | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Report | ||||
|  */ | ||||
| class ReportQuery implements ReportQueryInterface | ||||
| { | ||||
|     /** | ||||
|      * See ReportQueryInterface::incomeInPeriodCorrected | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param bool   $includeShared | ||||
|      * | ||||
|      * @return Collection | ||||
|      * | ||||
|      */ | ||||
|     public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false) | ||||
|     { | ||||
|         $query = $this->queryJournalsWithTransactions($start, $end); | ||||
|         if ($includeShared === false) { | ||||
|             $query->where( | ||||
|                 function (Builder $query) { | ||||
|                     $query->where( | ||||
|                         function (Builder $q) { // only get withdrawals not from a shared account | ||||
|                             $q->where('transaction_types.type', 'Withdrawal'); | ||||
|                             $q->where('acm_from.data', '!=', '"sharedAsset"'); | ||||
|                         } | ||||
|                     ); | ||||
|                     $query->orWhere( | ||||
|                         function (Builder $q) { // and transfers from a shared account. | ||||
|                             $q->where('transaction_types.type', 'Transfer'); | ||||
|                             $q->where('acm_to.data', '=', '"sharedAsset"'); | ||||
|                         } | ||||
|                     ); | ||||
|                 } | ||||
|             ); | ||||
|         } else { | ||||
|             $query->where('transaction_types.type', 'Withdrawal'); // any withdrawal is fine. | ||||
|         } | ||||
|         $query->orderBy('transaction_journals.date'); | ||||
|  | ||||
|         // get everything | ||||
|         $data = $query->get( | ||||
|             ['transaction_journals.*', 'transaction_types.type', 'ac_to.name as name', 'ac_to.id as account_id', 'ac_to.encrypted as account_encrypted'] | ||||
|         ); | ||||
|  | ||||
|         $data->each( | ||||
|             function (TransactionJournal $journal) { | ||||
|                 if (intval($journal->account_encrypted) == 1) { | ||||
|                     $journal->name = Crypt::decrypt($journal->name); | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         $data = $data->filter( | ||||
|             function (TransactionJournal $journal) { | ||||
|                 if ($journal->amount != 0) { | ||||
|                     return $journal; | ||||
|                 } | ||||
|  | ||||
|                 return null; | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a users accounts combined with various meta-data related to the start and end date. | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param bool   $includeShared | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false) | ||||
|     { | ||||
|         $query = Auth::user()->accounts()->orderBy('accounts.name', 'ASC') | ||||
|                      ->accountTypeIn(['Default account', 'Asset account', 'Cash account']); | ||||
|         if ($includeShared === false) { | ||||
|             $query->leftJoin( | ||||
|                 'account_meta', function (JoinClause $join) { | ||||
|                 $join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole'); | ||||
|             } | ||||
|             ) | ||||
|                   ->orderBy('accounts.name', 'ASC') | ||||
|                   ->where( | ||||
|                       function (Builder $query) { | ||||
|  | ||||
|                           $query->where('account_meta.data', '!=', '"sharedAsset"'); | ||||
|                           $query->orWhereNull('account_meta.data'); | ||||
|  | ||||
|                       } | ||||
|                   ); | ||||
|         } | ||||
|         $set = $query->get(['accounts.*']); | ||||
|         $set->each( | ||||
|             function (Account $account) use ($start, $end) { | ||||
|                 /** | ||||
|                  * The balance for today always incorporates transactions | ||||
|                  * made on today. So to get todays "start" balance, we sub one | ||||
|                  * day. | ||||
|                  */ | ||||
|                 $yesterday = clone $start; | ||||
|                 $yesterday->subDay(); | ||||
|  | ||||
|                 /** @noinspection PhpParamsInspection */ | ||||
|                 $account->startBalance = Steam::balance($account, $yesterday); | ||||
|                 $account->endBalance   = Steam::balance($account, $end); | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return $set; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results | ||||
|      * will simply list the transaction journals only. This should allow any follow up counting to be accurate with | ||||
|      * regards to tags. | ||||
|      * | ||||
|      * This method returns all "income" journals in a certain period, which are both transfers from a shared account | ||||
|      * and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does | ||||
|      * not group and returns different fields. | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param bool   $includeShared | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false) | ||||
|     { | ||||
|         $query = $this->queryJournalsWithTransactions($start, $end); | ||||
|         if ($includeShared === false) { | ||||
|             // only get deposits not to a shared account | ||||
|             // and transfers to a shared account. | ||||
|             $query->where( | ||||
|                 function (Builder $query) { | ||||
|                     $query->where( | ||||
|                         function (Builder $q) { | ||||
|                             $q->where('transaction_types.type', 'Deposit'); | ||||
|                             $q->where('acm_to.data', '!=', '"sharedAsset"'); | ||||
|                         } | ||||
|                     ); | ||||
|                     $query->orWhere( | ||||
|                         function (Builder $q) { | ||||
|                             $q->where('transaction_types.type', 'Transfer'); | ||||
|                             $q->where('acm_from.data', '=', '"sharedAsset"'); | ||||
|                         } | ||||
|                     ); | ||||
|                 } | ||||
|             ); | ||||
|         } else { | ||||
|             // any deposit is fine. | ||||
|             $query->where('transaction_types.type', 'Deposit'); | ||||
|         } | ||||
|         $query->orderBy('transaction_journals.date'); | ||||
|  | ||||
|         // get everything | ||||
|         $data = $query->get( | ||||
|             ['transaction_journals.*', 'transaction_types.type', 'ac_from.name as name', 'ac_from.id as account_id', 'ac_from.encrypted as account_encrypted'] | ||||
|         ); | ||||
|  | ||||
|         $data->each( | ||||
|             function (TransactionJournal $journal) { | ||||
|                 if (intval($journal->account_encrypted) == 1) { | ||||
|                     $journal->name = Crypt::decrypt($journal->name); | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         $data = $data->filter( | ||||
|             function (TransactionJournal $journal) { | ||||
|                 if ($journal->amount != 0) { | ||||
|                     return $journal; | ||||
|                 } | ||||
|  | ||||
|                 return null; | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Covers tags | ||||
|      * | ||||
|      * @param Account $account | ||||
|      * @param Budget  $budget | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * | ||||
|      * @return float | ||||
|      */ | ||||
|     public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end) | ||||
|     { | ||||
|  | ||||
|         return floatval( | ||||
|                    Auth::user()->transactionjournals() | ||||
|                        ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                        ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                        ->transactionTypes(['Withdrawal']) | ||||
|                        ->where('transactions.account_id', $account->id) | ||||
|                        ->before($end) | ||||
|                        ->after($start) | ||||
|                        ->where('budget_transaction_journal.budget_id', $budget->id) | ||||
|                        ->get(['transaction_journals.*'])->sum('amount') | ||||
|                ) * -1; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param bool    $shared | ||||
|      * | ||||
|      * @return float | ||||
|      */ | ||||
|     public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false) | ||||
|     { | ||||
|         return floatval( | ||||
|             Auth::user()->transactionjournals() | ||||
|                 ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                 ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                 ->transactionTypes(['Withdrawal']) | ||||
|                 ->where('transactions.account_id', $account->id) | ||||
|                 ->before($end) | ||||
|                 ->after($start) | ||||
|                 ->whereNull('budget_transaction_journal.budget_id')->get(['transaction_journals.*'])->sum('amount') | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return Builder | ||||
|      */ | ||||
|     protected function queryJournalsWithTransactions(Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $query = TransactionJournal:: | ||||
|         leftJoin( | ||||
|             'transactions as t_from', function (JoinClause $join) { | ||||
|             $join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0); | ||||
|         } | ||||
|         ) | ||||
|                                    ->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id') | ||||
|                                    ->leftJoin( | ||||
|                                        'account_meta as acm_from', function (JoinClause $join) { | ||||
|                                        $join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole'); | ||||
|                                    } | ||||
|                                    ) | ||||
|                                    ->leftJoin( | ||||
|                                        'transactions as t_to', function (JoinClause $join) { | ||||
|                                        $join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0); | ||||
|                                    } | ||||
|                                    ) | ||||
|                                    ->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id') | ||||
|                                    ->leftJoin( | ||||
|                                        'account_meta as acm_to', function (JoinClause $join) { | ||||
|                                        $join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole'); | ||||
|                                    } | ||||
|                                    ) | ||||
|                                    ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); | ||||
|         $query->before($end)->after($start)->where('transaction_journals.user_id', Auth::user()->id); | ||||
|  | ||||
|         return $query; | ||||
|     } | ||||
| } | ||||
| @@ -1,81 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Helpers\Report; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Budget; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface ReportQueryInterface | ||||
|  * | ||||
|  * @package FireflyIII\Helpers\Report | ||||
|  */ | ||||
| interface ReportQueryInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * See ReportQueryInterface::incomeInPeriodCorrected | ||||
|      * | ||||
|      * This method returns all "expense" journals in a certain period, which are both transfers to a shared account | ||||
|      * and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does | ||||
|      * not group and returns different fields. | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param bool   $includeShared | ||||
|      * | ||||
|      * @return Collection | ||||
|      * | ||||
|      */ | ||||
|     public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false); | ||||
|  | ||||
|     /** | ||||
|      * Get a users accounts combined with various meta-data related to the start and end date. | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param bool   $includeShared | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false); | ||||
|  | ||||
|     /** | ||||
|      * This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results | ||||
|      * will simply list the transaction journals only. This should allow any follow up counting to be accurate with | ||||
|      * regards to tags. | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * @param bool   $includeShared | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false); | ||||
|  | ||||
|     /** | ||||
|      * Covers tags as well. | ||||
|      * | ||||
|      * @param Account $account | ||||
|      * @param Budget  $budget | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * | ||||
|      * @return float | ||||
|      */ | ||||
|     public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end); | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * @param bool    $shared | ||||
|      * | ||||
|      * @return float | ||||
|      */ | ||||
|     public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false); | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -1,14 +1,31 @@ | ||||
| <?php namespace FireflyIII\Http\Controllers; | ||||
| <?php | ||||
| /** | ||||
|  * AccountController.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Http\Controllers; | ||||
|  | ||||
| use Auth; | ||||
| use Carbon\Carbon; | ||||
| use Config; | ||||
| use ExpandedForm; | ||||
| use FireflyIII\Http\Requests\AccountFormRequest; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Models\AccountType; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; | ||||
| use FireflyIII\Repositories\Account\AccountTaskerInterface; | ||||
| use FireflyIII\Support\CacheProperties; | ||||
| use Illuminate\Pagination\LengthAwarePaginator; | ||||
| use Illuminate\Support\Collection; | ||||
| use Input; | ||||
| use Navigation; | ||||
| use Preferences; | ||||
| use Redirect; | ||||
| use Session; | ||||
| use Steam; | ||||
| use URL; | ||||
| @@ -22,7 +39,7 @@ use View; | ||||
| class AccountController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
| @@ -34,15 +51,16 @@ class AccountController extends Controller | ||||
|     /** | ||||
|      * @param string $what | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      * @return View | ||||
|      */ | ||||
|     public function create($what = 'asset') | ||||
|     public function create(string $what = 'asset') | ||||
|     { | ||||
|         $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); | ||||
|         $subTitleIcon = config('firefly.subIconsByIdentifier.' . $what); | ||||
|         $subTitle     = trans('firefly.make_new_' . $what . '_account'); | ||||
|         Session::flash('preFilled', []); | ||||
|  | ||||
|         // put previous url in session if not redirect from store (not "create another"). | ||||
|         if (Session::get('accounts.create.fromStore') !== true) { | ||||
|         if (session('accounts.create.fromStore') !== true) { | ||||
|             Session::put('accounts.create.url', URL::previous()); | ||||
|         } | ||||
|         Session::forget('accounts.create.fromStore'); | ||||
| @@ -54,60 +72,61 @@ class AccountController extends Controller | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param ARI     $repository | ||||
|      * @param Account $account | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      * @return View | ||||
|      */ | ||||
|     public function delete(Account $account) | ||||
|     public function delete(ARI $repository, Account $account) | ||||
|     { | ||||
|         $typeName = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type); | ||||
|         $subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]); | ||||
|         $typeName    = config('firefly.shortNamesByFullName.' . $account->accountType->type); | ||||
|         $subTitle    = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]); | ||||
|         $accountList = ExpandedForm::makeSelectListWithEmpty($repository->getAccountsByType([$account->accountType->type])); | ||||
|         unset($accountList[$account->id]); | ||||
|  | ||||
|         // put previous url in session | ||||
|         Session::put('accounts.delete.url', URL::previous()); | ||||
|         Session::flash('gaEventCategory', 'accounts'); | ||||
|         Session::flash('gaEventAction', 'delete-' . $typeName); | ||||
|  | ||||
|         return view('accounts.delete', compact('account', 'subTitle')); | ||||
|         return view('accounts.delete', compact('account', 'subTitle', 'accountList')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param Account                    $account | ||||
|      * @param ARI     $repository | ||||
|      * @param Account $account | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector | ||||
|      */ | ||||
|     public function destroy(AccountRepositoryInterface $repository, Account $account) | ||||
|     public function destroy(ARI $repository, Account $account) | ||||
|     { | ||||
|  | ||||
|         $type     = $account->accountType->type; | ||||
|         $typeName = Config::get('firefly.shortNamesByFullName.' . $type); | ||||
|         $typeName = config('firefly.shortNamesByFullName.' . $type); | ||||
|         $name     = $account->name; | ||||
|         $moveTo   = $repository->find(intval(Input::get('move_account_before_delete'))); | ||||
|  | ||||
|         $repository->destroy($account); | ||||
|         $repository->destroy($account, $moveTo); | ||||
|  | ||||
|         Session::flash('success', trans('firefly.' . $typeName . '_deleted', ['name' => $name])); | ||||
|         Session::flash('success', strval(trans('firefly.' . $typeName . '_deleted', ['name' => $name]))); | ||||
|         Preferences::mark(); | ||||
|  | ||||
|         return Redirect::to(Session::get('accounts.delete.url')); | ||||
|         return redirect(session('accounts.delete.url')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param Account                    $account | ||||
|      * @param Account $account | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      * @return View | ||||
|      */ | ||||
|     public function edit(AccountRepositoryInterface $repository, Account $account) | ||||
|     public function edit(Account $account) | ||||
|     { | ||||
|  | ||||
|         $what           = Config::get('firefly.shortNamesByFullName')[$account->accountType->type]; | ||||
|         $subTitle       = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]); | ||||
|         $subTitleIcon   = Config::get('firefly.subIconsByIdentifier.' . $what); | ||||
|         $openingBalance = $repository->openingBalanceTransaction($account); | ||||
|         $what         = config('firefly.shortNamesByFullName')[$account->accountType->type]; | ||||
|         $subTitle     = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]); | ||||
|         $subTitleIcon = config('firefly.subIconsByIdentifier.' . $what); | ||||
|  | ||||
|         // put previous url in session if not redirect from store (not "return_to_edit"). | ||||
|         if (Session::get('accounts.edit.fromUpdate') !== true) { | ||||
|         if (session('accounts.edit.fromUpdate') !== true) { | ||||
|             Session::put('accounts.edit.url', URL::previous()); | ||||
|         } | ||||
|         Session::forget('accounts.edit.fromUpdate'); | ||||
| @@ -115,20 +134,19 @@ class AccountController extends Controller | ||||
|         // pre fill some useful values. | ||||
|  | ||||
|         // the opening balance is tricky: | ||||
|         $openingBalanceAmount = null; | ||||
|  | ||||
|         if ($openingBalance) { | ||||
|             $transaction          = $repository->getFirstTransaction($openingBalance, $account); | ||||
|             $openingBalanceAmount = $transaction->amount; | ||||
|         } | ||||
|         $openingBalanceAmount = $account->getOpeningBalanceAmount(); | ||||
|         $openingBalanceAmount = $account->getOpeningBalanceAmount() === '0' ? '' : $openingBalanceAmount; | ||||
|         $openingBalanceDate   = $account->getOpeningBalanceDate(); | ||||
|         $openingBalanceDate   = $openingBalanceDate->year === 1900 ? null : $openingBalanceDate->format('Y-m-d'); | ||||
|  | ||||
|         $preFilled = [ | ||||
|             'accountNumber'        => $account->getMeta('accountNumber'), | ||||
|             'accountRole'          => $account->getMeta('accountRole'), | ||||
|             'ccType'               => $account->getMeta('ccType'), | ||||
|             'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'), | ||||
|             'openingBalanceDate'   => $openingBalance ? $openingBalance->date->format('Y-m-d') : null, | ||||
|             'openingBalanceDate'   => $openingBalanceDate, | ||||
|             'openingBalance'       => $openingBalanceAmount, | ||||
|             'virtualBalance'       => floatval($account->virtual_balance) | ||||
|             'virtualBalance'       => round($account->virtual_balance, 2), | ||||
|         ]; | ||||
|         Session::flash('preFilled', $preFilled); | ||||
|         Session::flash('gaEventCategory', 'accounts'); | ||||
| @@ -138,28 +156,35 @@ class AccountController extends Controller | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param                            $what | ||||
|      * @param ARI    $repository | ||||
|      * @param string $what | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      * @return View | ||||
|      */ | ||||
|     public function index(AccountRepositoryInterface $repository, $what) | ||||
|     public function index(ARI $repository, string $what) | ||||
|     { | ||||
|         $what = $what ?? 'asset'; | ||||
|  | ||||
|         $subTitle     = trans('firefly.' . $what . '_accounts'); | ||||
|         $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); | ||||
|         $types        = Config::get('firefly.accountTypesByIdentifier.' . $what); | ||||
|         $accounts     = $repository->getAccounts($types); | ||||
|         // last activity: | ||||
|         /** | ||||
|          * HERE WE ARE | ||||
|          */ | ||||
|         $start = clone Session::get('start', Carbon::now()->startOfMonth()); | ||||
|         $subTitleIcon = config('firefly.subIconsByIdentifier.' . $what); | ||||
|         $types        = config('firefly.accountTypesByIdentifier.' . $what); | ||||
|         $accounts     = $repository->getAccountsByType($types); | ||||
|         /** @var Carbon $start */ | ||||
|         $start = clone session('start', Carbon::now()->startOfMonth()); | ||||
|         /** @var Carbon $end */ | ||||
|         $end = clone session('end', Carbon::now()->endOfMonth()); | ||||
|         $start->subDay(); | ||||
|  | ||||
|         $ids           = $accounts->pluck('id')->toArray(); | ||||
|         $startBalances = Steam::balancesById($ids, $start); | ||||
|         $endBalances   = Steam::balancesById($ids, $end); | ||||
|         $activities    = Steam::getLastActivities($ids); | ||||
|  | ||||
|         $accounts->each( | ||||
|             function (Account $account) use ($start, $repository) { | ||||
|                 $account->lastActivityDate = $repository->getLastActivity($account); | ||||
|                 $account->startBalance     = Steam::balance($account, $start); | ||||
|                 $account->endBalance       = Steam::balance($account, clone Session::get('end', Carbon::now()->endOfMonth())); | ||||
|             function (Account $account) use ($activities, $startBalances, $endBalances) { | ||||
|                 $account->lastActivityDate = $this->isInArray($activities, $account->id); | ||||
|                 $account->startBalance     = $this->isInArray($startBalances, $account->id); | ||||
|                 $account->endBalance       = $this->isInArray($endBalances, $account->id); | ||||
|             } | ||||
|         ); | ||||
|  | ||||
| @@ -167,100 +192,204 @@ class AccountController extends Controller | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param Account                    $account | ||||
|      * @param AccountTaskerInterface $tasker | ||||
|      * @param ARI                    $repository | ||||
|      * @param Account                $account | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      * @return View | ||||
|      */ | ||||
|     public function show(AccountRepositoryInterface $repository, Account $account) | ||||
|     public function show(AccountTaskerInterface $tasker, ARI $repository, Account $account) | ||||
|     { | ||||
|         $page         = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page')); | ||||
|         $subTitleIcon = Config::get('firefly.subTitlesByIdentifier.' . $account->accountType->type); | ||||
|         $what         = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type); | ||||
|         $journals     = $repository->getJournals($account, $page); | ||||
|         $subTitle     = trans('firefly.details_for_' . $what, ['name' => $account->name]); | ||||
|         // show journals from current period only: | ||||
|         $subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type); | ||||
|         $subTitle     = $account->name; | ||||
|         $range        = Preferences::get('viewRange', '1M')->data; | ||||
|         /** @var Carbon $start */ | ||||
|         $start = session('start', Navigation::startOfPeriod(new Carbon, $range)); | ||||
|         /** @var Carbon $end */ | ||||
|         $end      = session('end', Navigation::endOfPeriod(new Carbon, $range)); | ||||
|         $page     = intval(Input::get('page')); | ||||
|         $pageSize = Preferences::get('transactionPageSize', 50)->data; | ||||
|         $offset   = ($page - 1) * $pageSize; | ||||
|         $set      = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end); | ||||
|         $count    = $set->count(); | ||||
|         $subSet   = $set->splice($offset, $pageSize); | ||||
|         $journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page); | ||||
|         $journals->setPath('accounts/show/' . $account->id); | ||||
|  | ||||
|         // grouped other months thing: | ||||
|         // oldest transaction in account: | ||||
|         $start   = $repository->oldestJournalDate($account); | ||||
|         $range   = Preferences::get('viewRange', '1M')->data; | ||||
|         $start   = Navigation::startOfPeriod($start, $range); | ||||
|         $end     = Navigation::endOfX(new Carbon, $range); | ||||
|         $entries = new Collection; | ||||
|  | ||||
|         return view('accounts.show', compact('account', 'what', 'subTitleIcon', 'journals', 'subTitle')); | ||||
|         // chart properties for cache: | ||||
|         $cache = new CacheProperties; | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty('account-show'); | ||||
|         $cache->addProperty($account->id); | ||||
|  | ||||
|  | ||||
|         if ($cache->has()) { | ||||
|             $entries = $cache->get(); | ||||
|  | ||||
|             return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle')); | ||||
|         } | ||||
|  | ||||
|         // only include asset accounts when this account is an asset: | ||||
|         $assets = new Collection; | ||||
|         if (in_array($account->accountType->type, [AccountType::ASSET, AccountType::DEFAULT])) { | ||||
|             $assets = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); | ||||
|         } | ||||
|  | ||||
|         while ($end >= $start) { | ||||
|             $end        = Navigation::startOfPeriod($end, $range); | ||||
|             $currentEnd = Navigation::endOfPeriod($end, $range); | ||||
|             $spent      = $tasker->amountOutInPeriod(new Collection([$account]), $assets, $end, $currentEnd); | ||||
|             $earned     = $tasker->amountInInPeriod(new Collection([$account]), $assets, $end, $currentEnd); | ||||
|             $dateStr    = $end->format('Y-m-d'); | ||||
|             $dateName   = Navigation::periodShow($end, $range); | ||||
|             $entries->push([$dateStr, $dateName, $spent, $earned]); | ||||
|             $end = Navigation::subtractPeriod($end, $range, 1); | ||||
|  | ||||
|         } | ||||
|         $cache->store($entries); | ||||
|  | ||||
|         return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AccountFormRequest         $request | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param AccountTaskerInterface $tasker | ||||
|      * @param Account                $account | ||||
|      * @param string                 $date | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      * @return View | ||||
|      */ | ||||
|     public function store(AccountFormRequest $request, AccountRepositoryInterface $repository) | ||||
|     public function showWithDate(AccountTaskerInterface $tasker, Account $account, string $date) | ||||
|     { | ||||
|         $carbon   = new Carbon($date); | ||||
|         $range    = Preferences::get('viewRange', '1M')->data; | ||||
|         $start    = Navigation::startOfPeriod($carbon, $range); | ||||
|         $end      = Navigation::endOfPeriod($carbon, $range); | ||||
|         $subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')'; | ||||
|         $page     = intval(Input::get('page')); | ||||
|         $page     = $page === 0 ? 1 : $page; | ||||
|         $pageSize = Preferences::get('transactionPageSize', 50)->data; | ||||
|         $offset   = ($page - 1) * $pageSize; | ||||
|         $set      = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end); | ||||
|         $count    = $set->count(); | ||||
|         $subSet   = $set->splice($offset, $pageSize); | ||||
|         $journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page); | ||||
|         $journals->setPath('accounts/show/' . $account->id . '/' . $date); | ||||
|  | ||||
|         return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AccountFormRequest $request | ||||
|      * @param ARI                $repository | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector | ||||
|      * | ||||
|      */ | ||||
|     public function store(AccountFormRequest $request, ARI $repository) | ||||
|     { | ||||
|         $accountData = [ | ||||
|             'name'                   => $request->input('name'), | ||||
|             'name'                   => trim($request->input('name')), | ||||
|             'accountType'            => $request->input('what'), | ||||
|             'virtualBalance'         => floatval($request->input('virtualBalance')), | ||||
|             'virtualBalance'         => round($request->input('virtualBalance'), 2), | ||||
|             'virtualBalanceCurrency' => intval($request->input('amount_currency_id_virtualBalance')), | ||||
|             'active'                 => true, | ||||
|             'user'                   => Auth::user()->id, | ||||
|             'user'                   => auth()->user()->id, | ||||
|             'iban'                   => trim($request->input('iban')), | ||||
|             'accountNumber'          => trim($request->input('accountNumber')), | ||||
|             'accountRole'            => $request->input('accountRole'), | ||||
|             'openingBalance'         => floatval($request->input('openingBalance')), | ||||
|             'openingBalance'         => round($request->input('openingBalance'), 2), | ||||
|             'openingBalanceDate'     => new Carbon((string)$request->input('openingBalanceDate')), | ||||
|             'openingBalanceCurrency' => intval($request->input('balance_currency_id')), | ||||
|             'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')), | ||||
|  | ||||
|         ]; | ||||
|         $account     = $repository->store($accountData); | ||||
|  | ||||
|         Session::flash('success', 'New account "' . $account->name . '" stored!'); | ||||
|         $account = $repository->store($accountData); | ||||
|  | ||||
|         Session::flash('success', strval(trans('firefly.stored_new_account', ['name' => $account->name]))); | ||||
|         Preferences::mark(); | ||||
|  | ||||
|         // update preferences if necessary: | ||||
|         $frontPage = Preferences::get('frontPageAccounts', [])->data; | ||||
|         if (count($frontPage) > 0) { | ||||
|             $frontPage[] = $account->id; | ||||
|             Preferences::set('frontPageAccounts', $frontPage); | ||||
|         } | ||||
|  | ||||
|         if (intval(Input::get('create_another')) === 1) { | ||||
|             // set value so create routine will not overwrite URL: | ||||
|             Session::put('accounts.create.fromStore', true); | ||||
|  | ||||
|             return Redirect::route('accounts.create')->withInput(); | ||||
|             return redirect(route('accounts.create', [$request->input('what')]))->withInput(); | ||||
|         } | ||||
|  | ||||
|         // redirect to previous URL. | ||||
|         return Redirect::to(Session::get('accounts.create.url')); | ||||
|  | ||||
|  | ||||
|         return redirect(session('accounts.create.url')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param AccountFormRequest         $request | ||||
|      * @param AccountRepositoryInterface $repository | ||||
|      * @param Account                    $account | ||||
|      * @param AccountFormRequest $request | ||||
|      * @param ARI                $repository | ||||
|      * @param Account            $account | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector | ||||
|      */ | ||||
|     public function update(AccountFormRequest $request, AccountRepositoryInterface $repository, Account $account) | ||||
|     public function update(AccountFormRequest $request, ARI $repository, Account $account) | ||||
|     { | ||||
|  | ||||
|         $accountData = [ | ||||
|             'name'                   => $request->input('name'), | ||||
|             'active'                 => $request->input('active'), | ||||
|             'user'                   => Auth::user()->id, | ||||
|             'user'                   => auth()->user()->id, | ||||
|             'iban'                   => $request->input('iban'), | ||||
|             'accountNumber'          => $request->input('accountNumber'), | ||||
|             'accountRole'            => $request->input('accountRole'), | ||||
|             'virtualBalance'         => floatval($request->input('virtualBalance')), | ||||
|             'openingBalance'         => floatval($request->input('openingBalance')), | ||||
|             'virtualBalance'         => round($request->input('virtualBalance'), 2), | ||||
|             'openingBalance'         => round($request->input('openingBalance'), 2), | ||||
|             'openingBalanceDate'     => new Carbon((string)$request->input('openingBalanceDate')), | ||||
|             'openingBalanceCurrency' => intval($request->input('balance_currency_id')), | ||||
|             'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')), | ||||
|             'ccType'                 => $request->input('ccType'), | ||||
|             'ccMonthlyPaymentDate'   => $request->input('ccMonthlyPaymentDate'), | ||||
|         ]; | ||||
|  | ||||
|         $repository->update($account, $accountData); | ||||
|  | ||||
|         Session::flash('success', 'Account "' . $account->name . '" updated.'); | ||||
|         Session::flash('success', strval(trans('firefly.updated_account', ['name' => $account->name]))); | ||||
|         Preferences::mark(); | ||||
|  | ||||
|         if (intval(Input::get('return_to_edit')) === 1) { | ||||
|             // set value so edit routine will not overwrite URL: | ||||
|             Session::put('accounts.edit.fromUpdate', true); | ||||
|  | ||||
|             return Redirect::route('accounts.edit', [$account->id])->withInput(['return_to_edit' => 1]); | ||||
|             return redirect(route('accounts.edit', [$account->id]))->withInput(['return_to_edit' => 1]); | ||||
|         } | ||||
|  | ||||
|         // redirect to previous URL. | ||||
|         return Redirect::to(Session::get('accounts.edit.url')); | ||||
|         return redirect(session('accounts.edit.url')); | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param array $array | ||||
|      * @param int   $entryId | ||||
|      * | ||||
|      * @return null|mixed | ||||
|      */ | ||||
|     protected function isInArray(array $array, int $entryId) | ||||
|     { | ||||
|         if (isset($array[$entryId])) { | ||||
|             return $array[$entryId]; | ||||
|         } | ||||
|  | ||||
|         return ''; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										81
									
								
								app/Http/Controllers/Admin/ConfigurationController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								app/Http/Controllers/Admin/ConfigurationController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ConfigurationController.php | ||||
|  * Copyright (C) 2016 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This software may be modified and distributed under the terms of the | ||||
|  * Creative Commons Attribution-ShareAlike 4.0 International License. | ||||
|  * | ||||
|  * See the LICENSE file for details. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types = 1); | ||||
|  | ||||
| namespace FireflyIII\Http\Controllers\Admin; | ||||
|  | ||||
|  | ||||
| use Config; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Http\Requests\ConfigurationRequest; | ||||
| use FireflyIII\Support\Facades\FireflyConfig; | ||||
| use Preferences; | ||||
| use Redirect; | ||||
| use Session; | ||||
| use View; | ||||
|  | ||||
| /** | ||||
|  * Class ConfigurationController | ||||
|  * | ||||
|  * @package FireflyIII\Http\Controllers\Admin | ||||
|  */ | ||||
| class ConfigurationController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * ConfigurationController constructor. | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|  | ||||
|         View::share('title', strval(trans('firefly.administration'))); | ||||
|         View::share('mainTitleIcon', 'fa-hand-spock-o'); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return View | ||||
|      */ | ||||
|     public function index() | ||||
|     { | ||||
|         $subTitle     = strval(trans('firefly.instance_configuration')); | ||||
|         $subTitleIcon = 'fa-wrench'; | ||||
|  | ||||
|         // all available configuration and their default value in case | ||||
|         // they don't exist yet. | ||||
|         $singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data; | ||||
|  | ||||
|         return view('admin.configuration.index', compact('subTitle', 'subTitleIcon', 'singleUserMode')); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param ConfigurationRequest $request | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function store(ConfigurationRequest $request) | ||||
|     { | ||||
|         // get config values: | ||||
|         $singleUserMode = intval($request->get('single_user_mode')) === 1 ? true : false; | ||||
|  | ||||
|         // store config values | ||||
|         FireflyConfig::set('single_user_mode', $singleUserMode); | ||||
|  | ||||
|         // flash message | ||||
|         Session::flash('success', strval(trans('firefly.configuration_updated'))); | ||||
|         Preferences::mark(); | ||||
|  | ||||
|         return Redirect::route('admin.configuration.index'); | ||||
|     } | ||||
|  | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user