mirror of
				https://github.com/crater-invoice/crater.git
				synced 2025-10-27 19:51:09 -04:00 
			
		
		
		
	Compare commits
	
		
			666 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 082d5cacf2 | |||
| d332712c22 | |||
| f434c3e0d1 | |||
| c810ad5246 | |||
| 29705662e3 | |||
| 31bfaadfad | |||
| e2299794ad | |||
| 7d71cc1607 | |||
| 40d9ecfe02 | |||
| 3d3d7d0ec0 | |||
| 63b1ee5849 | |||
| bfe13ef79d | |||
| 010c7b02ed | |||
| 81739d4db7 | |||
| 05110526a7 | |||
| 15bff7cd3f | |||
| 37470fe7a2 | |||
| 1a50aca26d | |||
| 8219d19222 | |||
| 8fa0252779 | |||
| 6bfab30f14 | |||
| d972279adc | |||
| 900a18c479 | |||
| 433c6a7c44 | |||
| 9f673d7eb3 | |||
| d3a745605e | |||
| b004e7e379 | |||
| 9893108539 | |||
| 2b80082996 | |||
| 8d596f5c19 | |||
| ce4335d8fa | |||
| 5eaea3ad59 | |||
| 375d4d428e | |||
| 2ec87baa6f | |||
| 18a2e83348 | |||
| d53e227b08 | |||
| 7721bf6f93 | |||
| 76977c3d2a | |||
| 512983deee | |||
| f5c23b33da | |||
| a0898a1fa0 | |||
| 236db087ba | |||
| 4772079d13 | |||
| 0e0ef1d99e | |||
| 3d9a73f9ec | |||
| b452c3f173 | |||
| 87667be90c | |||
| 31481739f1 | |||
| da43797d9c | |||
| e1fcf0695c | |||
| a52d5bc582 | |||
| e7bf86f9c0 | |||
| fca7d164a6 | |||
| 4b603f8823 | |||
| 4552138787 | |||
| d1dd704cdf | |||
| 00961bcae1 | |||
| 98ba17e56a | |||
| 8eb47b553e | |||
| 2fb46a4ab3 | |||
| 6fcddd6feb | |||
| 43c871f374 | |||
| 4ec9b2e817 | |||
| f2c6e06c54 | |||
| f95b292717 | |||
| 6ecd9728d4 | |||
| b2d4b7212b | |||
| b4956d38f7 | |||
| ed966b02eb | |||
| bcbffdcf30 | |||
| 3cf70135f3 | |||
| 90bb90cd99 | |||
| ebad76c8d7 | |||
| c35b00493c | |||
| e5ae937a88 | |||
| 736df5fcae | |||
| 89c264c6a9 | |||
| 1837bf3f3e | |||
| 83234cfac3 | |||
| 5ccc9eba4c | |||
| 3f0008ec95 | |||
| 9e98a96d61 | |||
| 27bc89bf76 | |||
| e4c2b44da5 | |||
| 633cad9b89 | |||
| f5bbe68a4b | |||
| 1fac9c9dde | |||
| 1208d3a924 | |||
| 3257b18d58 | |||
| 16a941f787 | |||
| 4f5cd9fba1 | |||
| 80e6a3c757 | |||
| d4e7420640 | |||
| ac30ba599b | |||
| 6dd0ccce8b | |||
| 2f32ff4d5b | |||
| 54a5ca83f8 | |||
| eede2fc17f | |||
| e586adde26 | |||
| 1ffb1b2eec | |||
| 55d838371a | |||
| cdaa1ff103 | |||
| 63f7cdec82 | |||
| fadba7fa3a | |||
| d0f6e207ee | |||
| 845f01f170 | |||
| 7f9938c80d | |||
| 16e4212300 | |||
| 109de237b1 | |||
| 82cf63f200 | |||
| 03fc0fd659 | |||
| c3d3e5e35f | |||
| 909e4f93e4 | |||
| bfd9850bf6 | |||
| 3f7db2793f | |||
| 8693672b09 | |||
| a9b0d62cd6 | |||
| 9f57a4200f | |||
| 9e130b3db8 | |||
| 7c9a846090 | |||
| b2e08dda7f | |||
| a2026fbc8b | |||
| db1b0db461 | |||
| 17c00c322d | |||
| 331b8ea44e | |||
| 428be640c3 | |||
| 51f79433b9 | |||
| 8008ed0527 | |||
| 57b302666a | |||
| ee1d1ccff4 | |||
| 81e7109ad1 | |||
| 784bf39df5 | |||
| f66755c4aa | |||
| b4e1e99d37 | |||
| d8aa3dc8a6 | |||
| 9af51660cb | |||
| 38d0da9618 | |||
| 761c0143ec | |||
| fdc4de5093 | |||
| f8591f96a9 | |||
| e47cb01ce2 | |||
| 454ad3091a | |||
| cc73a8a842 | |||
| cea8405ace | |||
| 49a6e03e9d | |||
| f9d6e8b0cc | |||
| 2b78aacc83 | |||
| 1932c5a75e | |||
| 449968ae88 | |||
| 02a2db4417 | |||
| a53582f916 | |||
| 340bf3be06 | |||
| 7f0da9dc36 | |||
| c4ace76275 | |||
| 553bcc053b | |||
| 375a59a504 | |||
| cfc0a1ef75 | |||
| 392f6f469b | |||
| 6cb8d30915 | |||
| 9b55e84724 | |||
| cf5da7684b | |||
| f47029ca78 | |||
| 761df2ffac | |||
| 8fbc257b23 | |||
| baebfedf37 | |||
| 9321eb9d86 | |||
| 70bed01e7b | |||
| 739efcce79 | |||
| 3695e7d075 | |||
| 8f2033f621 | |||
| 7b95ccb5fc | |||
| b7370ed85c | |||
| 818f2fbaf7 | |||
| 91a1dbf91c | |||
| a4d556f41e | |||
| 83de1c47ee | |||
| 678ace744a | |||
| 9e26fee1b0 | |||
| e455c3de50 | |||
| c4db50d3f0 | |||
| 411ee57946 | |||
| 9c5e9e56f5 | |||
| 8eeafd3fed | |||
| ed978d0174 | |||
| 607b1795bb | |||
| a6c3c815b5 | |||
| ead0ebb012 | |||
| d4550e62f8 | |||
| ff3846137e | |||
| b55f8e5391 | |||
| 0f1d7627ea | |||
| 1be462ef82 | |||
| 8b04e94446 | |||
| fa512629d9 | |||
| 0f99be6cf1 | |||
| fae3af2592 | |||
| d1e70c8989 | |||
| 63094e8112 | |||
| 1b46bddf56 | |||
| 422bf74600 | |||
| d003e61d41 | |||
| 09f74eb37c | |||
| 9227973a7a | |||
| 5011543198 | |||
| d07b63c365 | |||
| c12e27dbfe | |||
| 4ea32b94ed | |||
| fe93d5236c | |||
| 5992394bca | |||
| 6c9f5800e5 | |||
| 227cebcb0b | |||
| 6fd4cc6b3f | |||
| 594c8965c1 | |||
| 2241145a17 | |||
| 34f252b1c9 | |||
| 89e1d7bc84 | |||
| f0368deda2 | |||
| 4028551fd8 | |||
| 06f385a28e | |||
| 44aeff425b | |||
| 35a38d719c | |||
| f6ba81e8b7 | |||
| 64cb55fd5d | |||
| 13fe38517c | |||
| 3bdb501d1d | |||
| b8adbf4b07 | |||
| aa85a9950c | |||
| 8e63efee1b | |||
| ea98f03aeb | |||
| 90cd6226be | |||
| 9d2df64b2e | |||
| bade86a24f | |||
| c3b57b4888 | |||
| fcfedc5414 | |||
| 8dee0da434 | |||
| e4ab024422 | |||
| 2bd01c4014 | |||
| 034c7e385c | |||
| c9086747a3 | |||
| ab1ed269fd | |||
| f7a823e9bc | |||
| b6301b08f8 | |||
| 265c219fdc | |||
| 729db694a3 | |||
| e2e6415cb6 | |||
| 419104a4ec | |||
| f18eac2001 | |||
| 7bffbe59ed | |||
| bf53b4f5eb | |||
| 21440230f3 | |||
| 99c27ba8ef | |||
| cb7a925cd0 | |||
| d2e11bd7df | |||
| 1b74a3720e | |||
| e222af335e | |||
| 599e6270e8 | |||
| da7ddcd4f1 | |||
| bd01039fb8 | |||
| 718f01ccd4 | |||
| fd3742b926 | |||
| e8b954d1bd | |||
| 58042decd5 | |||
| fa500b3865 | |||
| e03924fe9d | |||
| 3f308a2d3b | |||
| 6cdd93bab0 | |||
| 69c6c883c2 | |||
| 69dcf1299b | |||
| b6cd78ca4c | |||
| d18116bd7d | |||
| 6b30c2028b | |||
| 8c4a358072 | |||
| fe8482fc71 | |||
| 28217df654 | |||
| 3723697add | |||
| b3e3839fb5 | |||
| 89ee58590c | |||
| 326508e567 | |||
| 21e01bc1db | |||
| ed58dfaef6 | |||
| c2c0e2809c | |||
| bafda5ca46 | |||
| a7eaf5dbe4 | |||
| 805a287129 | |||
| 61fe16cab4 | |||
| 33085a0e3c | |||
| 2df09a20f1 | |||
| a7d61b802d | |||
| 88bcb1c2b4 | |||
| af194c1851 | |||
| 71193727c9 | |||
| bc7571832a | |||
| e379e13802 | |||
| 4484765358 | |||
| 5d4ea2a308 | |||
| 0db9f9df0c | |||
| 9301e67e3f | |||
| a019403b1b | |||
| c99d7ee677 | |||
| 94a2306b7f | |||
| 73bd28aeb8 | |||
| 904f2641c5 | |||
| 4372195547 | |||
| 14f8094a9d | |||
| 4770c27d7d | |||
| f4baf308a4 | |||
| 6f852ab88a | |||
| 14bb6edec8 | |||
| b4a7c724db | |||
| 9fb4b4dec5 | |||
| 4bbc92ccaf | |||
| dd12662b17 | |||
| c364de1b17 | |||
| a06e8e3039 | |||
| b9021cb664 | |||
| a2c3fa2cc5 | |||
| 517f3bd1b8 | |||
| 29b4b92fab | |||
| e8f046f02e | |||
| 2bf6778c8a | |||
| e11c713ce3 | |||
| b7189595bd | |||
| c5d4a62930 | |||
| 378f216f6d | |||
| 7654c30b1b | |||
| f4767ffb26 | |||
| e06639daa5 | |||
| 9bd23ffac8 | |||
| 60f5d29570 | |||
| b2497e5f81 | |||
| 771a447e2c | |||
| a964f1d841 | |||
| 57d5356bfe | |||
| fd29487121 | |||
| 5fb6797577 | |||
| 3d0f77ab0b | |||
| dbc249b6bc | |||
| 08c6218440 | |||
| 737c933b8c | |||
| b9ab87a308 | |||
| 2e5cb58c39 | |||
| db622e7458 | |||
| 09bbf98e61 | |||
| ca90ff2767 | |||
| 368dd16c9b | |||
| 251648f53c | |||
| ffa5b6b2ad | |||
| bd9beaa343 | |||
| 3e4decdfb9 | |||
| 165907d144 | |||
| 6278417423 | |||
| dc37f565c4 | |||
| 532196a9b4 | |||
| 6a4009e13a | |||
| a8f98e51bb | |||
| 2b03bc798e | |||
| 482556d378 | |||
| deb525af6e | |||
| cb2bfbb91c | |||
| 654395a175 | |||
| e31b60bc48 | |||
| 183953f4c4 | |||
| 98d15143c2 | |||
| a24d8d3ebc | |||
| 4ca574c581 | |||
| c7ce8c87dd | |||
| f64d546672 | |||
| 96187870b4 | |||
| d4a1f1a784 | |||
| e07532961e | |||
| 450c265ded | |||
| f8502c3ca8 | |||
| 899da6990d | |||
| e7675f938e | |||
| b08138e9e0 | |||
| 5df4abdc4b | |||
| a2fa8afa72 | |||
| 7670cd67dc | |||
| 8446ac2b27 | |||
| 63a80e44d5 | |||
| 076df75322 | |||
| 11db99da73 | |||
| 050dca5a50 | |||
| 3c096f1386 | |||
| 0f3e8fce3b | |||
| 325f90bba5 | |||
| a739a938fc | |||
| b30e3a9b11 | |||
| fc1a7c7438 | |||
| 6046113cb1 | |||
| 611ffafec5 | |||
| c497b906df | |||
| c68fce19f9 | |||
| f8913531b6 | |||
| 30f76e2088 | |||
| 39556892cd | |||
| 2fd66bf748 | |||
| d4f1428d5f | |||
| 189141c84d | |||
| f8ccfece09 | |||
| 9a7c926d53 | |||
| c5c1674153 | |||
| 8562ee5414 | |||
| 06c66a756c | |||
| b66d07d21b | |||
| 05001b6a79 | |||
| f02f4ba9d3 | |||
| fbace98aac | |||
| 8f0af3dcd6 | |||
| e8e44c5dc8 | |||
| ac33164342 | |||
| 5c7c0d84ea | |||
| 7ca725ac37 | |||
| 510a4b3dbb | |||
| 0f130ab1b8 | |||
| 25114009e3 | |||
| 79c16d74ce | |||
| 742e1e445a | |||
| 386f96d60e | |||
| 82d85af672 | |||
| 4d1b267688 | |||
| bc99ad63a6 | |||
| 4bb44f8c93 | |||
| c72265ed50 | |||
| b8958c9eb6 | |||
| e33e314cb7 | |||
| 84cebee9da | |||
| 34d3cf7ae8 | |||
| 93d0da836a | |||
| fd51276948 | |||
| d6274854ba | |||
| ea4bd1a31d | |||
| 286e047963 | |||
| be16f48f3d | |||
| ebea1e0813 | |||
| cc8d08f829 | |||
| aacffc22eb | |||
| d71ca4ffb9 | |||
| 186004f7f8 | |||
| c2eb22d666 | |||
| af189b15b6 | |||
| 1c19be85c3 | |||
| 4bb4362d23 | |||
| f6f66b3ae6 | |||
| b4ccecbcf1 | |||
| 92f1f196bb | |||
| ee14070a7b | |||
| 8ce7e14a02 | |||
| 3401ca049e | |||
| 5dcc7b9efd | |||
| 06a538bb81 | |||
| 7ab0419f27 | |||
| a7275aaa42 | |||
| bc4e6a05ea | |||
| ca170f5a87 | |||
| 22e7e96dfa | |||
| fcfd1ddb7a | |||
| daf8c9265b | |||
| 406d098172 | |||
| 824d2e3e8d | |||
| 3cd975dbbd | |||
| 8e50c36a71 | |||
| b499741ab4 | |||
| b409cdb913 | |||
| 13e56105e3 | |||
| f68e86e4cf | |||
| da996c1f33 | |||
| 0990ce4678 | |||
| bb6fb2f49d | |||
| 400296575e | |||
| d58c790b1f | |||
| c674c2ab9e | |||
| d79692cf3b | |||
| 353c2479f1 | |||
| 0176a854b8 | |||
| 00548ea908 | |||
| 09e335a8a7 | |||
| 53e2ed253f | |||
| 06b035d9ac | |||
| 5c88cbcc42 | |||
| d9b175a676 | |||
| f5b9bc95c6 | |||
| 586dcdea0d | |||
| 50957fc179 | |||
| c725e4744b | |||
| 7479ce237e | |||
| 84420441c0 | |||
| ac96721e87 | |||
| db4c7f5e32 | |||
| 87dc78eea0 | |||
| e9c2898056 | |||
| b9c4570137 | |||
| 8e2525cc6c | |||
| 8862a93f23 | |||
| 82efd88920 | |||
| 0ef528d296 | |||
| 4c33a5d88c | |||
| d64d06181b | |||
| ef9cf2db22 | |||
| 96c295a003 | |||
| 22528f5b66 | |||
| 1cd9c72537 | |||
| 7253b43eb4 | |||
| 181964cf03 | |||
| 5914245ae4 | |||
| 1bf3d28d4e | |||
| dbcbd93ace | |||
| 1729c6a308 | |||
| 019493cbfa | |||
| 60540ba966 | |||
| 601ad419ec | |||
| 80e7bab891 | |||
| f5c8befbf0 | |||
| 09c984baa7 | |||
| 0d93260672 | |||
| 3e9e217f92 | |||
| 3cd8859c62 | |||
| 4493b4a419 | |||
| 79e3e70bd6 | |||
| 56a955befd | |||
| 496c28f80d | |||
| 7a59f3fe0c | |||
| 3d6875a532 | |||
| 15bf380f4f | |||
| 0b910db039 | |||
| 0e5e8f602f | |||
| d9db0f9401 | |||
| c0da7c7339 | |||
| 7e7599b4a7 | |||
| 99cd88e6c6 | |||
| 053a06229c | |||
| 1a6e8280a8 | |||
| 125e8be83c | |||
| 4d89ca2101 | |||
| e8aee3bb32 | |||
| d3c7ca75f0 | |||
| 694d5f56d5 | |||
| a48439785c | |||
| 7c9bd84f00 | |||
| bd5a93d81c | |||
| 858e10953b | |||
| 3617032735 | |||
| 75ddc51b1e | |||
| 1dfa36e396 | |||
| d9e9a5a540 | |||
| ea6e11c324 | |||
| f55dfe0b46 | |||
| 3eac3b8af5 | |||
| 2b2bd4351a | |||
| b9c32bbdc1 | |||
| bceffbf6a0 | |||
| 7c6a40374d | |||
| d04e142a3e | |||
| f11436736b | |||
| 9271ceba45 | |||
| c88eb24265 | |||
| 5eb0a04378 | |||
| d926073095 | |||
| a691969025 | |||
| 7df06fb005 | |||
| 45db850025 | |||
| 18a50315ba | |||
| bce1b4bb3e | |||
| ddd204105f | |||
| e030d4b9d0 | |||
| 302968225a | |||
| e59bf288ce | |||
| b1fcd90b62 | |||
| ddb0ff1b8a | |||
| ce99fa3d82 | |||
| e28c89085d | |||
| 34f7e33abc | |||
| 80be7a492d | |||
| 387cb4490d | |||
| 09829a559e | |||
| b06fc5f0b9 | |||
| 5f7401f622 | |||
| 4f6dae919b | |||
| 36be395579 | |||
| 79e77f9e16 | |||
| cbf0af2120 | |||
| 92f754e888 | |||
| a6896eaa01 | |||
| 01f3646869 | |||
| b2918e9dbb | |||
| 0a064ec5ba | |||
| 5f0b4b3496 | |||
| 17b59f0d19 | |||
| 283b910cc3 | |||
| 81739827c0 | |||
| 90edc3a85e | |||
| 655c2a7849 | |||
| ca833d174e | |||
| 33e8381fc4 | |||
| 9b5125d440 | |||
| 2899021804 | |||
| d8f6d03d1e | |||
| c474e98925 | |||
| 799d212d9b | |||
| 9424dc6c27 | |||
| fa15502ce7 | |||
| 1f4d3bf784 | |||
| 3692373cd2 | |||
| 7bba576dca | |||
| 05454af593 | |||
| 74fe481ed5 | |||
| 1cd654b0cc | |||
| f4a4c05b61 | |||
| 24637bff4a | |||
| 887ad9a73d | |||
| 339099bd34 | |||
| 9b9761aa5a | |||
| 0c71356f59 | |||
| e539bb501d | |||
| 2fcd169270 | |||
| 00c917853c | |||
| 122c4f478f | |||
| edc0e115e4 | |||
| b388e7a237 | |||
| 6e3ed9b4f6 | |||
| 338dbb26a1 | |||
| f10e5e9d11 | |||
| 3a046b638c | |||
| 36242c516a | |||
| 14d71fedb3 | |||
| c8843eb544 | |||
| 7fe9a4c2a2 | |||
| 37f2b6dfc7 | |||
| c90c14312a | |||
| b5b861bb36 | |||
| ab041743a2 | |||
| 9bcec9bd75 | |||
| 7b697a477e | |||
| 146cf835b9 | |||
| ec87e72547 | |||
| bf2e8c9c99 | |||
| b6096aadfa | |||
| 1cbc41c3ce | |||
| f4ca6d4b73 | |||
| 66649590e5 | |||
| 1e8cc475ca | |||
| 98d76a2f92 | |||
| 7b50148c43 | |||
| aef6da8b7b | |||
| 0fb14afc08 | |||
| dad71b04dd | |||
| 80014b02b5 | |||
| 7c2a6700eb | |||
| 7e81013b15 | |||
| f0107129fb | |||
| 04cce64859 | |||
| 3b7637f3cf | |||
| 7333746948 | |||
| 0fa6049298 | |||
| 64be6ce957 | |||
| 0e075c03d8 | |||
| 5de195b56b | |||
| cd60f1b096 | |||
| 8058f9b022 | |||
| a484506029 | |||
| a69f99dc61 | |||
| c3583c98be | |||
| 813e574425 | |||
| ac431ca815 | |||
| dbc5950294 | |||
| 8bc5ea2d5e | 
							
								
								
									
										10
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| .dockerignore | ||||
| .gitignore | ||||
| *.md | ||||
| .git/ | ||||
| .idea/ | ||||
| .DS_Store/ | ||||
| docker-compose.* | ||||
| LICENSE | ||||
| nginx.conf | ||||
| yarn.lock | ||||
| @ -19,4 +19,7 @@ indent_size = 2 | ||||
| indent_size = 2 | ||||
|  | ||||
| [*.js] | ||||
| indent_size = 2 | ||||
| indent_size = 2 | ||||
|  | ||||
| [*.json] | ||||
| indent_size = 2 | ||||
|  | ||||
							
								
								
									
										18
									
								
								.env.example
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								.env.example
									
									
									
									
									
								
							| @ -1,20 +1,21 @@ | ||||
| APP_ENV=production | ||||
| APP_KEY=base64:kgk/4DW1vEVy7aEvet5FPp5un6PIGe/so8H0mvoUtW0= | ||||
| APP_DEBUG=false | ||||
| APP_DEBUG=true | ||||
| APP_LOG_LEVEL=debug | ||||
| APP_URL=http://crater.test | ||||
|  | ||||
| DB_CONNECTION=mysql | ||||
| DB_HOST=127.0.0.1 | ||||
| DB_HOST=db | ||||
| DB_PORT=3306 | ||||
| DB_DATABASE=crater | ||||
| DB_USERNAME=root | ||||
| DB_PASSWORD=bytefury | ||||
| DB_USERNAME=crater | ||||
| DB_PASSWORD="crater" | ||||
|  | ||||
| BROADCAST_DRIVER=log | ||||
| CACHE_DRIVER=file | ||||
| SESSION_DRIVER=file | ||||
| QUEUE_DRIVER=sync | ||||
| SESSION_DRIVER=cookie | ||||
| SESSION_LIFETIME=1440 | ||||
|  | ||||
| REDIS_HOST=127.0.0.1 | ||||
| REDIS_PASSWORD=null | ||||
| @ -31,6 +32,7 @@ PUSHER_APP_ID= | ||||
| PUSHER_KEY= | ||||
| PUSHER_SECRET= | ||||
|  | ||||
| PROXY_OAUTH_CLIENT_ID=2 | ||||
| PROXY_OAUTH_CLIENT_SECRET=SECRET-GENERATED-KEY-HERE | ||||
| PROXY_OAUTH_GRANT_TYPE=password | ||||
| SANCTUM_STATEFUL_DOMAINS=crater.test | ||||
| SESSION_DOMAIN=crater.test | ||||
|  | ||||
| TRUSTED_PROXIES="*" | ||||
|  | ||||
| @ -2,3 +2,12 @@ APP_ENV=testing | ||||
| APP_DEBUG=true | ||||
| APP_KEY=base64:IdDlpLmYyWA9z4Ruj5st1FSYrhCR7lPOscLGCz2Jf4I= | ||||
| DB_CONNECTION=sqlite | ||||
|  | ||||
| MAIL_DRIVER=smtp | ||||
| MAIL_HOST=smtp.mailtrap.io | ||||
| MAIL_PORT=587 | ||||
| MAIL_USERNAME=ff538f0e1037f4 | ||||
| MAIL_PASSWORD=c04c81145fcb73 | ||||
| MAIL_ENCRYPTION=tls | ||||
| MAIL_FROM_ADDRESS="admin@craterapp.com" | ||||
| MAIL_FROM_NAME="John Doe" | ||||
|  | ||||
							
								
								
									
										10
									
								
								.eslintrc
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								.eslintrc
									
									
									
									
									
								
							| @ -1,10 +0,0 @@ | ||||
| { | ||||
|   "root": true, | ||||
|   "extends": [ | ||||
|     "plugin:vue/recommended", | ||||
|     "standard" | ||||
|   ], | ||||
|   "rules": { | ||||
|     "vue/max-attributes-per-line" : 3 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										14
									
								
								.eslintrc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								.eslintrc.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| // .eslintrc.js | ||||
|  | ||||
| module.exports = { | ||||
|   extends: [ | ||||
|     // add more generic rulesets here, such as: | ||||
|     // 'eslint:recommended', | ||||
|     "plugin:vue/vue3-recommended", | ||||
|     "prettier", | ||||
|   ], | ||||
|   rules: { | ||||
|     // override/add rules settings here, such as: | ||||
|     // 'vue/no-unused-vars': 'error' | ||||
|   }, | ||||
| }; | ||||
							
								
								
									
										26
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| --- | ||||
| name: Bug report | ||||
| about: Create a report to help us improve | ||||
| title: '' | ||||
| labels: '' | ||||
| assignees: '' | ||||
|  | ||||
| --- | ||||
|  | ||||
| **Describe the bug** | ||||
| A clear and concise description of what the bug is. | ||||
|  | ||||
| **Expected behavior** | ||||
| A clear and concise description of what you expected to happen. | ||||
|  | ||||
| **Screenshots** | ||||
| If applicable, add screenshots to help explain your problem. | ||||
|  | ||||
| **Please complete the following information:** | ||||
| - Crater version:  | ||||
| - PHP version:  | ||||
| - Database type and version:  | ||||
|  | ||||
| **Optional info** | ||||
| - OS:  [e.g. Ubuntu] | ||||
| - Browser: [e.g. chrome, safari] | ||||
							
								
								
									
										17
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| --- | ||||
| name: Feature request | ||||
| about: Suggest an idea for this project | ||||
| title: '' | ||||
| labels: '' | ||||
| assignees: '' | ||||
|  | ||||
| --- | ||||
|  | ||||
| **Is your feature request related to a problem? Please describe.** | ||||
| A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] | ||||
|  | ||||
| **Describe the solution you'd like** | ||||
| A clear and concise description of what you want to happen. | ||||
|  | ||||
| **Describe alternatives you've considered** | ||||
| A clear and concise description of any alternative solutions or features you've considered. | ||||
							
								
								
									
										37
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| name: CI | ||||
|  | ||||
| on: [push, pull_request] | ||||
|  | ||||
| jobs: | ||||
|   build-test: | ||||
|     runs-on: ubuntu-latest | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         php: ['7.4', '8.0'] | ||||
|  | ||||
|     name: PHP ${{ matrix.php }} | ||||
|  | ||||
|     steps: | ||||
|     - name: Checkout | ||||
|       uses: actions/checkout@v2 | ||||
|  | ||||
|     - name: Install dependencies | ||||
|       uses: shivammathur/setup-php@v2 | ||||
|       with: | ||||
|         php-version: ${{ matrix.php }} | ||||
|         extensions: exif | ||||
|  | ||||
|     - name: Install PHP 7 dependencies | ||||
|       run: composer update --no-interaction --no-progress | ||||
|       if: "matrix.php < 8" | ||||
|  | ||||
|     - name: Install PHP 8 dependencies | ||||
|       run: composer update --ignore-platform-req=php --no-interaction --no-progress | ||||
|       if: "matrix.php >= 8" | ||||
|  | ||||
|     - name: Check coding style | ||||
|       run: ./vendor/bin/php-cs-fixer fix -v --dry-run --using-cache=no --config=.php-cs-fixer.dist.php | ||||
|  | ||||
|     - name: Unit Tests | ||||
|       run: php ./vendor/bin/pest | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -11,3 +11,4 @@ Homestead.yaml | ||||
| .rnd | ||||
| /.expo | ||||
| /.vscode | ||||
| /docker-compose/db/data/ | ||||
|  | ||||
							
								
								
									
										42
									
								
								.php-cs-fixer.dist.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								.php-cs-fixer.dist.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| <?php | ||||
|  | ||||
| $finder = PhpCsFixer\Finder::create() | ||||
|     ->in(__DIR__) | ||||
|     ->exclude(['bootstrap', 'storage', 'vendor']) | ||||
|     ->name('*.php') | ||||
|     ->name('_ide_helper') | ||||
|     ->notName('*.blade.php') | ||||
|     ->ignoreDotFiles(true) | ||||
|     ->ignoreVCS(true); | ||||
|  | ||||
| $rules = [ | ||||
|     '@PSR12' => true, | ||||
|     'array_syntax' => ['syntax' => 'short'], | ||||
|     'ordered_imports' => ['sort_algorithm' => 'alpha'], | ||||
|     'concat_space' => true, | ||||
|     'no_unused_imports' => true, | ||||
|     'not_operator_with_successor_space' => true, | ||||
|     'phpdoc_scalar' => true, | ||||
|     'unary_operator_spaces' => true, | ||||
|     'binary_operator_spaces' => true, | ||||
|     'blank_line_before_statement' => [ | ||||
|         'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], | ||||
|     ], | ||||
|     'phpdoc_single_line_var_spacing' => true, | ||||
|     'phpdoc_var_without_name' => true, | ||||
|     'class_attributes_separation' => [ | ||||
|         'elements' => [ | ||||
|             'method' => 'one', | ||||
|             'property' => 'one', | ||||
|         ], | ||||
|     ], | ||||
|     'method_argument_space' => [ | ||||
|         'on_multiline' => 'ensure_fully_multiline', | ||||
|         'keep_multiple_spaces_after_comma' => true, | ||||
|     ], | ||||
| ]; | ||||
|  | ||||
| return (new PhpCsFixer\Config()) | ||||
|     ->setUsingCache(true) | ||||
|     ->setRules($rules) | ||||
|     ->setFinder($finder); | ||||
							
								
								
									
										5
									
								
								.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| { | ||||
|   "semi": false, | ||||
|   "singleQuote": true, | ||||
|   "tabWidth": 2 | ||||
| } | ||||
							
								
								
									
										44
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| FROM php:7.4-fpm-alpine | ||||
|  | ||||
| WORKDIR /var/www | ||||
|  | ||||
| RUN apk add --no-cache \ | ||||
|             $PHPIZE_DEPS \ | ||||
|             freetype-dev \ | ||||
|             git \ | ||||
|             zip \ | ||||
|             libzip-dev \ | ||||
|             php7-bcmath \ | ||||
|             curl \ | ||||
|             unzip \ | ||||
|             libjpeg-turbo-dev \ | ||||
|             libpng-dev \ | ||||
|             libxml2-dev \ | ||||
|             mariadb-client \ | ||||
|             sqlite \ | ||||
|             php7-json \ | ||||
|             php7-openssl \ | ||||
|             php7-pdo \ | ||||
|             php7-pdo_mysql \ | ||||
|             php7-session \ | ||||
|             php7-simplexml \ | ||||
|             php7-tokenizer \ | ||||
|             php7-xml \ | ||||
|             imagemagick \ | ||||
|             imagemagick-libs \ | ||||
|             imagemagick-dev \ | ||||
|             php7-imagick \ | ||||
|             php7-pcntl \ | ||||
|             --repository http://dl-cdn.alpinelinux.org/alpine/v3.13/community/ gnu-libiconv=1.15-r3 | ||||
|  | ||||
| ENV LD_PRELOAD /usr/lib/preloadable_libiconv.so php | ||||
|  | ||||
| RUN printf "\n" | pecl install \ | ||||
| 		imagick && \ | ||||
| 		docker-php-ext-enable --ini-name 20-imagick.ini imagick | ||||
|  | ||||
| RUN docker-php-ext-configure zip | ||||
| RUN docker-php-ext-install zip | ||||
| RUN docker-php-ext-install iconv pdo pdo_mysql bcmath pcntl exif | ||||
| RUN docker-php-ext-configure gd --with-jpeg --with-freetype | ||||
| RUN docker-php-ext-install gd | ||||
							
								
								
									
										3274
									
								
								_ide_helper.php
									
									
									
									
									
								
							
							
						
						
									
										3274
									
								
								_ide_helper.php
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,48 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Crater\User; | ||||
| use Crater\Country; | ||||
| use Crater\State; | ||||
| use Crater\City; | ||||
|  | ||||
| class Address extends Model | ||||
| { | ||||
|     const BILLING_TYPE = 'BILLING'; | ||||
|     const SHIPPING_TYPE = 'SHIPPING'; | ||||
|  | ||||
|     protected $fillable = [ | ||||
|         'name', | ||||
|         'address_street_1', | ||||
|         'address_street_2', | ||||
|         'city_id', | ||||
|         'state_id', | ||||
|         'country_id', | ||||
|         'zip', | ||||
|         'phone', | ||||
|         'fax', | ||||
|         'type', | ||||
|         'user_id' | ||||
|     ]; | ||||
|  | ||||
|     public function user() | ||||
|     { | ||||
|         return $this->belongsTo(User::class); | ||||
|     } | ||||
|  | ||||
|     public function country() | ||||
|     { | ||||
|         return $this->belongsTo(Country::class); | ||||
|     } | ||||
|  | ||||
|     public function state() | ||||
|     { | ||||
|         return $this->belongsTo(State::class); | ||||
|     } | ||||
|  | ||||
|     public function city() | ||||
|     { | ||||
|         return $this->belongsTo(City::class); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										18
									
								
								app/City.php
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								app/City.php
									
									
									
									
									
								
							| @ -1,18 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Crater\State; | ||||
|  | ||||
| class City extends Model | ||||
| { | ||||
|     public function state() | ||||
|     { | ||||
|         return $this->belongsTo(State::class); | ||||
|     } | ||||
|  | ||||
|     public function address() | ||||
|     { | ||||
|         return $this->hasMany(Address::class); | ||||
|     } | ||||
| } | ||||
| @ -1,36 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Crater\User; | ||||
| use Crater\CompanySetting; | ||||
| use Spatie\MediaLibrary\HasMedia\HasMedia; | ||||
| use Spatie\MediaLibrary\HasMedia\HasMediaTrait; | ||||
|  | ||||
| class Company extends Model implements HasMedia | ||||
| { | ||||
|     use HasMediaTrait; | ||||
|  | ||||
|     protected $fillable = ['name', 'logo', 'unique_hash']; | ||||
|  | ||||
|     protected $appends=['logo']; | ||||
|  | ||||
|     public function getLogoAttribute() | ||||
|     { | ||||
|         $logo = $this->getMedia('logo')->first(); | ||||
|         if ($logo) { | ||||
|             return  asset($logo->getUrl()); | ||||
|         } | ||||
|         return ; | ||||
|     } | ||||
|  | ||||
|     public function user() | ||||
|     { | ||||
|         return $this->hasOne(User::class); | ||||
|     } | ||||
|  | ||||
|     public function settings() | ||||
|     { | ||||
|         return $this->hasMany(CompanySetting::class); | ||||
|     } | ||||
| } | ||||
| @ -1,47 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
|  | ||||
| class CompanySetting extends Model | ||||
| { | ||||
|     protected $fillable = ['company_id', 'option', 'value']; | ||||
|  | ||||
|     public function company() | ||||
|     { | ||||
|         return $this->belongsTo(Company::class); | ||||
|     } | ||||
|  | ||||
|     public static function setSetting($key, $setting, $company_id) | ||||
|     { | ||||
|         $old = self::whereOption($key)->whereCompany($company_id)->first(); | ||||
|  | ||||
|         if ($old) { | ||||
|             $old->value = $setting; | ||||
|             $old->save(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $set = new CompanySetting(); | ||||
|         $set->option = $key; | ||||
|         $set->value = $setting; | ||||
|         $set->company_id = $company_id; | ||||
|         $set->save(); | ||||
|     } | ||||
|  | ||||
|     public static function getSetting($key, $company_id) | ||||
|     { | ||||
|         $setting = static::whereOption($key)->whereCompany($company_id)->first(); | ||||
|  | ||||
|         if ($setting) { | ||||
|             return $setting->value; | ||||
|         } else { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereCompany($query, $company_id) | ||||
|     { | ||||
|         $query->where('company_id', $company_id); | ||||
|     } | ||||
| } | ||||
| @ -1,9 +1,10 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Console\Commands; | ||||
|  | ||||
| use Illuminate\Console\Command; | ||||
| use Carbon\Carbon; | ||||
| use Crater\Estimate; | ||||
| use Crater\Models\Estimate; | ||||
| use Illuminate\Console\Command; | ||||
|  | ||||
| class CheckEstimateStatus extends Command | ||||
| { | ||||
| @ -39,7 +40,7 @@ class CheckEstimateStatus extends Command | ||||
|     public function handle() | ||||
|     { | ||||
|         $date = Carbon::now(); | ||||
|         $status = array(Estimate::STATUS_ACCEPTED, Estimate::STATUS_REJECTED, Estimate::STATUS_EXPIRED); | ||||
|         $status = [Estimate::STATUS_ACCEPTED, Estimate::STATUS_REJECTED, Estimate::STATUS_EXPIRED]; | ||||
|         $estimates = Estimate::whereNotIn('status', $status)->whereDate('expiry_date', '<', $date)->get(); | ||||
|  | ||||
|         foreach ($estimates as $estimate) { | ||||
|  | ||||
| @ -1,9 +1,10 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Console\Commands; | ||||
|  | ||||
| use Illuminate\Console\Command; | ||||
| use Carbon\Carbon; | ||||
| use Crater\Invoice; | ||||
| use Crater\Models\Invoice; | ||||
| use Illuminate\Console\Command; | ||||
|  | ||||
| class CheckInvoiceStatus extends Command | ||||
| { | ||||
| @ -39,7 +40,7 @@ class CheckInvoiceStatus extends Command | ||||
|     public function handle() | ||||
|     { | ||||
|         $date = Carbon::now(); | ||||
|         $invoices = Invoice::where('status', '<>', Invoice::STATUS_COMPLETED)->whereDate('due_date', '<',$date)->get(); | ||||
|         $invoices = Invoice::where('status', '<>', Invoice::STATUS_COMPLETED)->whereDate('due_date', '<', $date)->get(); | ||||
|  | ||||
|         foreach ($invoices as $invoice) { | ||||
|             $invoice->status = Invoice::STATUS_OVERDUE; | ||||
|  | ||||
							
								
								
									
										63
									
								
								app/Console/Commands/CreateTemplateCommand.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								app/Console/Commands/CreateTemplateCommand.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Console\Commands; | ||||
|  | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Support\Facades\Storage; | ||||
|  | ||||
| class CreateTemplateCommand extends Command | ||||
| { | ||||
|     /** | ||||
|      * The name and signature of the console command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'make:template {name} {--type=}'; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = 'Create estimate or invoice pdf template.                               '; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         $templateName = $this->argument('name'); | ||||
|         $type = $this->option('type'); | ||||
|  | ||||
|         if (! $type) { | ||||
|             $type = $this->choice('Create a template for?', ['invoice', 'estimate']); | ||||
|         } | ||||
|  | ||||
|         if (Storage::disk('views')->exists("/app/pdf/{$type}/{$templateName}.blade.php")) { | ||||
|             $this->info("Template with given name already exists."); | ||||
|  | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         Storage::disk('views')->copy("/app/pdf/{$type}/{$type}1.blade.php", "/app/pdf/{$type}/{$templateName}.blade.php"); | ||||
|         copy(resource_path("/static/img/PDF/{$type}1.png"), resource_path("/static/img/PDF/{$templateName}.png")); | ||||
|  | ||||
|         $path = resource_path("app/pdf/{$type}/{$templateName}.blade.php"); | ||||
|         $type = ucfirst($type); | ||||
|         $this->info("{$type} Template created successfully at ".$path); | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										68
									
								
								app/Console/Commands/ResetApp.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								app/Console/Commands/ResetApp.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Console\Commands; | ||||
|  | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Console\ConfirmableTrait; | ||||
| use Illuminate\Support\Facades\Artisan; | ||||
|  | ||||
| class ResetApp extends Command | ||||
| { | ||||
|     use ConfirmableTrait; | ||||
|  | ||||
|     /** | ||||
|      * The name and signature of the console command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'reset:app {--force}'; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = 'Clean database, database_created and public/storage folder'; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         if (! $this->confirmToProceed()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $this->info('Running migrate:fresh'); | ||||
|  | ||||
|         Artisan::call('migrate:fresh --seed --force'); | ||||
|  | ||||
|         $this->info('Seeding database'); | ||||
|  | ||||
|         Artisan::call('db:seed', ['--class' => 'DemoSeeder', '--force' => true]); | ||||
|  | ||||
|         $path = base_path('.env'); | ||||
|  | ||||
|         if (file_exists($path)) { | ||||
|             file_put_contents($path, str_replace( | ||||
|                 'APP_DEBUG=true', | ||||
|                 'APP_DEBUG=false', | ||||
|                 file_get_contents($path) | ||||
|             )); | ||||
|         } | ||||
|  | ||||
|         $this->info('App has been reset successfully'); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										241
									
								
								app/Console/Commands/UpdateCommand.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								app/Console/Commands/UpdateCommand.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,241 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Console\Commands; | ||||
|  | ||||
| use Crater\Models\Setting; | ||||
| use Crater\Space\Updater; | ||||
| use Illuminate\Console\Command; | ||||
|  | ||||
| // Implementation taken from Akaunting - https://github.com/akaunting/akaunting | ||||
| class UpdateCommand extends Command | ||||
| { | ||||
|     public $installed; | ||||
|  | ||||
|     public $version; | ||||
|  | ||||
|     public $response; | ||||
|  | ||||
|     /** | ||||
|      * The name and signature of the console command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'crater:update'; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = 'Automatically update your crater app'; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         set_time_limit(3600); // 1 hour | ||||
|  | ||||
|         $this->installed = $this->getInstalledVersion(); | ||||
|         $this->response = $this->getLatestVersionResponse(); | ||||
|         $this->version = ($this->response) ? $this->response->version : false; | ||||
|  | ||||
|         if ($this->response == 'extension_required') { | ||||
|             $this->info('Sorry! Your system does not meet the minimum requirements for this update.'); | ||||
|             $this->info('Please retry after installing the required version/extensions.'); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (! $this->version) { | ||||
|             $this->info('No Update Available! You are already on the latest version.'); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (! $this->confirm("Do you wish to update to {$this->version}?")) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (! $path = $this->download()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (! $path = $this->unzip($path)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (! $this->copyFiles($path)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (isset($this->response->deleted_files) && ! empty($this->response->deleted_files)) { | ||||
|             if (! $this->deleteFiles($this->response->deleted_files)) { | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (! $this->migrateUpdate()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (! $this->finish()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $this->info('Successfully updated to '.$this->version); | ||||
|     } | ||||
|  | ||||
|     public function getInstalledVersion() | ||||
|     { | ||||
|         return Setting::getSetting('version'); | ||||
|     } | ||||
|  | ||||
|     public function getLatestVersionResponse() | ||||
|     { | ||||
|         $this->info('Your currently installed version is '.$this->installed); | ||||
|         $this->line(''); | ||||
|         $this->info('Checking for update...'); | ||||
|  | ||||
|         try { | ||||
|             $response = Updater::checkForUpdate($this->installed); | ||||
|  | ||||
|             if ($response->success) { | ||||
|                 $extensions = $response->version->extensions; | ||||
|  | ||||
|                 $is_required = false; | ||||
|  | ||||
|                 foreach ($extensions as $key => $extension) { | ||||
|                     if (! $extension) { | ||||
|                         $is_required = true; | ||||
|                         $this->info('❌ '.$key); | ||||
|                     } | ||||
|  | ||||
|                     $this->info('✅ '.$key); | ||||
|                 } | ||||
|  | ||||
|                 if ($is_required) { | ||||
|                     return 'extension_required'; | ||||
|                 } | ||||
|  | ||||
|                 return $response->version; | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } catch (\Exception $e) { | ||||
|             $this->error($e->getMessage()); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function download() | ||||
|     { | ||||
|         $this->info('Downloading update...'); | ||||
|  | ||||
|         try { | ||||
|             $path = Updater::download($this->version, 1); | ||||
|             if (! is_string($path)) { | ||||
|                 $this->error('Download exception'); | ||||
|  | ||||
|                 return false; | ||||
|             } | ||||
|         } catch (\Exception $e) { | ||||
|             $this->error($e->getMessage()); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return $path; | ||||
|     } | ||||
|  | ||||
|     public function unzip($path) | ||||
|     { | ||||
|         $this->info('Unzipping update package...'); | ||||
|  | ||||
|         try { | ||||
|             $path = Updater::unzip($path); | ||||
|             if (! is_string($path)) { | ||||
|                 $this->error('Unzipping exception'); | ||||
|  | ||||
|                 return false; | ||||
|             } | ||||
|         } catch (\Exception $e) { | ||||
|             $this->error($e->getMessage()); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return $path; | ||||
|     } | ||||
|  | ||||
|     public function copyFiles($path) | ||||
|     { | ||||
|         $this->info('Copying update files...'); | ||||
|  | ||||
|         try { | ||||
|             Updater::copyFiles($path); | ||||
|         } catch (\Exception $e) { | ||||
|             $this->error($e->getMessage()); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function deleteFiles($files) | ||||
|     { | ||||
|         $this->info('Deleting unused old files...'); | ||||
|  | ||||
|         try { | ||||
|             Updater::deleteFiles($files); | ||||
|         } catch (\Exception $e) { | ||||
|             $this->error($e->getMessage()); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function migrateUpdate() | ||||
|     { | ||||
|         $this->info('Running Migrations...'); | ||||
|  | ||||
|         try { | ||||
|             Updater::migrateUpdate(); | ||||
|         } catch (\Exception $e) { | ||||
|             $this->error($e->getMessage()); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function finish() | ||||
|     { | ||||
|         $this->info('Finishing update...'); | ||||
|  | ||||
|         try { | ||||
|             Updater::finishUpdate($this->installed, $this->version); | ||||
|         } catch (\Exception $e) { | ||||
|             $this->error($e->getMessage()); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @ -1,6 +1,8 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Console; | ||||
|  | ||||
| use Crater\Models\RecurringInvoice; | ||||
| use Illuminate\Console\Scheduling\Schedule; | ||||
| use Illuminate\Foundation\Console\Kernel as ConsoleKernel; | ||||
|  | ||||
| @ -12,7 +14,9 @@ class Kernel extends ConsoleKernel | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $commands = [ | ||||
|  | ||||
|         Commands\ResetApp::class, | ||||
|         Commands\UpdateCommand::class, | ||||
|         Commands\CreateTemplateCommand::class | ||||
|     ]; | ||||
|  | ||||
|     /** | ||||
| @ -28,6 +32,13 @@ class Kernel extends ConsoleKernel | ||||
|  | ||||
|         $schedule->command('check:estimates:status') | ||||
|             ->daily(); | ||||
|  | ||||
|         $recurringInvoices = RecurringInvoice::where('status', 'ACTIVE')->get(); | ||||
|         foreach ($recurringInvoices as $recurringInvoice) { | ||||
|             $schedule->call(function () use ($recurringInvoice) { | ||||
|                 $recurringInvoice->generateInvoice(); | ||||
|             })->cron($recurringInvoice->frequency); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|  | ||||
| @ -1,17 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
|  | ||||
| class Currency extends Model | ||||
| { | ||||
|     protected $fillable = [ | ||||
|         'name', | ||||
|         'code', | ||||
|         'symbol', | ||||
|         'precision', | ||||
|         'thousand_separator', | ||||
|         'decimal_separator', | ||||
|         'position' | ||||
|     ]; | ||||
| } | ||||
							
								
								
									
										226
									
								
								app/Estimate.php
									
									
									
									
									
								
							
							
						
						
									
										226
									
								
								app/Estimate.php
									
									
									
									
									
								
							| @ -1,226 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Crater\CompanySetting; | ||||
| use Carbon\Carbon; | ||||
|  | ||||
| class Estimate extends Model | ||||
| { | ||||
|     const STATUS_DRAFT = 'DRAFT'; | ||||
|     const STATUS_SENT = 'SENT'; | ||||
|     const STATUS_VIEWED = 'VIEWED'; | ||||
|     const STATUS_EXPIRED = 'EXPIRED'; | ||||
|     const STATUS_ACCEPTED = 'ACCEPTED'; | ||||
|     const STATUS_REJECTED = 'REJECTED'; | ||||
|  | ||||
|     protected $dates = [ | ||||
|         'created_at', | ||||
|         'updated_at', | ||||
|         'deleted_at', | ||||
|         'estimate_date', | ||||
|         'expiry_date' | ||||
|     ]; | ||||
|  | ||||
|     protected $appends = [ | ||||
|         'formattedExpiryDate', | ||||
|         'formattedEstimateDate' | ||||
|     ]; | ||||
|  | ||||
|     protected $fillable = [ | ||||
|         'estimate_date', | ||||
|         'expiry_date', | ||||
|         'estimate_number', | ||||
|         'user_id', | ||||
|         'company_id', | ||||
|         'reference_number', | ||||
|         'estimate_template_id', | ||||
|         'discount', | ||||
|         'discount_type', | ||||
|         'discount_val', | ||||
|         'status', | ||||
|         'sub_total', | ||||
|         'tax_per_item', | ||||
|         'discount_per_item', | ||||
|         'total', | ||||
|         'tax', | ||||
|         'notes', | ||||
|         'unique_hash' | ||||
|     ]; | ||||
|  | ||||
|     protected $casts = [ | ||||
|         'total' => 'float', | ||||
|         'tax' => 'float', | ||||
|         'sub_total' => 'float' | ||||
|     ]; | ||||
|  | ||||
|     public static function getNextEstimateNumber() | ||||
|     { | ||||
|         // Get the last created order | ||||
|         $lastOrder = Estimate::orderBy('created_at', 'desc')->first(); | ||||
|         if (!$lastOrder) { | ||||
|             // We get here if there is no order at all | ||||
|             // If there is no number set it to 0, which will be 1 at the end. | ||||
|             $number = 0; | ||||
|         } else { | ||||
|             $number = explode("-",$lastOrder->estimate_number); | ||||
|             $number = $number[1]; | ||||
|         } | ||||
|  | ||||
|         // If we have ORD000001 in the database then we only want the number | ||||
|         // So the substr returns this 000001 | ||||
|  | ||||
|         // Add the string in front and higher up the number. | ||||
|         // the %05d part makes sure that there are always 6 numbers in the string. | ||||
|         // so it adds the missing zero's when needed. | ||||
|  | ||||
|         return sprintf('%06d', intval($number) + 1); | ||||
|     } | ||||
|  | ||||
|     public function items() | ||||
|     { | ||||
|         return $this->hasMany('Crater\EstimateItem'); | ||||
|     } | ||||
|  | ||||
|     public function user() | ||||
|     { | ||||
|         return $this->belongsTo('Crater\User'); | ||||
|     } | ||||
|  | ||||
|     public function taxes() | ||||
|     { | ||||
|         return $this->hasMany(Tax::class); | ||||
|     } | ||||
|  | ||||
|     public function estimateTemplate() | ||||
|     { | ||||
|         return $this->belongsTo('Crater\EstimateTemplate'); | ||||
|     } | ||||
|  | ||||
|     public function getEstimateNumAttribute() | ||||
|     { | ||||
|         $position = $this->strposX($this->estimate_number, "-", 2) + 1; | ||||
|         return substr($this->estimate_number, $position); | ||||
|     } | ||||
|  | ||||
|     private function strposX($haystack, $needle, $number) | ||||
|     { | ||||
|         if ($number == '1') { | ||||
|             return strpos($haystack, $needle); | ||||
|         } elseif ($number > '1') { | ||||
|             return strpos( | ||||
|                 $haystack, | ||||
|                 $needle, | ||||
|                 $this->strposX($haystack, $needle, $number - 1) + strlen($needle) | ||||
|             ); | ||||
|         } else { | ||||
|             return error_log('Error: Value for parameter $number is out of range'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function getFormattedExpiryDateAttribute($value) | ||||
|     { | ||||
|         $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); | ||||
|         return Carbon::parse($this->expiry_date)->format($dateFormat); | ||||
|     } | ||||
|  | ||||
|     public function getFormattedEstimateDateAttribute($value) | ||||
|     { | ||||
|         $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); | ||||
|         return Carbon::parse($this->estimate_date)->format($dateFormat); | ||||
|     } | ||||
|  | ||||
|     public function scopeEstimatesBetween($query, $start, $end) | ||||
|     { | ||||
|         return $query->whereBetween( | ||||
|             'estimates.estimate_date', | ||||
|             [$start->format('Y-m-d'), $end->format('Y-m-d')] | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereStatus($query, $status) | ||||
|     { | ||||
|         return $query->where('estimates.status', $status); | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereEstimateNumber($query, $estimateNumber) | ||||
|     { | ||||
|         return $query->where('estimates.estimate_number', $estimateNumber); | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereSearch($query, $search) | ||||
|     { | ||||
|         foreach (explode(' ', $search) as $term) { | ||||
|             $query->whereHas('user', function ($query) use ($term) { | ||||
|                 $query->where('name', 'LIKE', '%'.$term.'%') | ||||
|                     ->orWhere('contact_name', 'LIKE', '%'.$term.'%') | ||||
|                     ->orWhere('company_name', 'LIKE', '%'.$term.'%'); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function scopeApplyFilters($query, array $filters) | ||||
|     { | ||||
|         $filters = collect($filters); | ||||
|  | ||||
|         if ($filters->get('search')) { | ||||
|             $query->whereSearch($filters->get('search')); | ||||
|         } | ||||
|  | ||||
|         if ($filters->get('estimate_number')) { | ||||
|             $query->whereEstimateNumber($filters->get('estimate_number')); | ||||
|         } | ||||
|  | ||||
|         if ($filters->get('status')) { | ||||
|             $query->whereStatus($filters->get('status')); | ||||
|         } | ||||
|  | ||||
|         if ($filters->get('from_date') && $filters->get('to_date')) { | ||||
|             $start = Carbon::createFromFormat('d/m/Y', $filters->get('from_date')); | ||||
|             $end = Carbon::createFromFormat('d/m/Y', $filters->get('to_date')); | ||||
|             $query->estimatesBetween($start, $end); | ||||
|         } | ||||
|  | ||||
|         if ($filters->get('customer_id')) { | ||||
|             $query->whereCustomer($filters->get('customer_id')); | ||||
|         } | ||||
|  | ||||
|         if ($filters->get('orderByField') || $filters->get('orderBy')) { | ||||
|             $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'estimate_number'; | ||||
|             $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'asc'; | ||||
|             $query->whereOrder($field, $orderBy); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereOrder($query, $orderByField, $orderBy) | ||||
|     { | ||||
|         $query->orderBy($orderByField, $orderBy); | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereCompany($query, $company_id) | ||||
|     { | ||||
|         $query->where('estimates.company_id', $company_id); | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereCustomer($query, $customer_id) | ||||
|     { | ||||
|         $query->where('estimates.user_id', $customer_id); | ||||
|     } | ||||
|  | ||||
|     public static function deleteEstimate($id) | ||||
|     { | ||||
|         $estimate = Estimate::find($id); | ||||
|  | ||||
|         if ($estimate->items()->exists()) { | ||||
|             $estimate->items()->delete(); | ||||
|         } | ||||
|  | ||||
|         if ($estimate->taxes()->exists()) { | ||||
|             $estimate->taxes()->delete(); | ||||
|         } | ||||
|  | ||||
|         $estimate->delete(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @ -1,20 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Crater\Estimate; | ||||
|  | ||||
| class EstimateTemplate extends Model | ||||
| { | ||||
|     protected $fillable = ['path', 'view', 'name']; | ||||
|  | ||||
|     public function estimates() | ||||
|     { | ||||
|         return $this->hasMany(Estimate::class); | ||||
|     } | ||||
|  | ||||
|     public function getPathAttribute($value) | ||||
|     { | ||||
|         return url($value); | ||||
|     } | ||||
| } | ||||
| @ -2,13 +2,7 @@ | ||||
|  | ||||
| namespace Crater\Events; | ||||
|  | ||||
| use Illuminate\Broadcasting\Channel; | ||||
| use Illuminate\Broadcasting\InteractsWithSockets; | ||||
| use Illuminate\Broadcasting\PresenceChannel; | ||||
| use Illuminate\Broadcasting\PrivateChannel; | ||||
| use Illuminate\Contracts\Broadcasting\ShouldBroadcast; | ||||
| use Illuminate\Foundation\Events\Dispatchable; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
|  | ||||
| class UpdateFinished | ||||
| { | ||||
| @ -25,7 +19,7 @@ class UpdateFinished | ||||
|      */ | ||||
|     public function __construct($old, $new) | ||||
|     { | ||||
|         $this->old   = $old; | ||||
|         $this->new   = $new; | ||||
|         $this->old = $old; | ||||
|         $this->new = $new; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,8 +1,9 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Exceptions; | ||||
|  | ||||
| use Exception; | ||||
| use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; | ||||
| use Throwable; | ||||
|  | ||||
| class Handler extends ExceptionHandler | ||||
| { | ||||
| @ -30,10 +31,10 @@ class Handler extends ExceptionHandler | ||||
|      * | ||||
|      * This is a great spot to send exceptions to Sentry, Bugsnag, etc. | ||||
|      * | ||||
|      * @param  \Exception  $exception | ||||
|      * @param  \Throwable $exception | ||||
|      * @return void | ||||
|      */ | ||||
|     public function report(Exception $exception) | ||||
|     public function report(Throwable $exception) | ||||
|     { | ||||
|         parent::report($exception); | ||||
|     } | ||||
| @ -42,10 +43,10 @@ class Handler extends ExceptionHandler | ||||
|      * Render an exception into an HTTP response. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @param  \Exception  $exception | ||||
|      * @param  \Throwable  $exception | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function render($request, Exception $exception) | ||||
|     public function render($request, Throwable $exception) | ||||
|     { | ||||
|         return parent::render($request, $exception); | ||||
|     } | ||||
|  | ||||
							
								
								
									
										140
									
								
								app/Expense.php
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								app/Expense.php
									
									
									
									
									
								
							| @ -1,140 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Spatie\MediaLibrary\HasMedia\HasMedia; | ||||
| use Spatie\MediaLibrary\HasMedia\HasMediaTrait; | ||||
| use Crater\ExpenseCategory; | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Support\Facades\DB; | ||||
|  | ||||
| class Expense extends Model implements HasMedia | ||||
| { | ||||
|     use HasMediaTrait; | ||||
|  | ||||
|     protected $fillable = [ | ||||
|         'expense_category_id', | ||||
|         'amount', | ||||
|         'company_id', | ||||
|         'expense_date', | ||||
|         'notes', | ||||
|         'attachment_receipt' | ||||
|     ]; | ||||
|  | ||||
|     protected $appends = [ | ||||
|         'formattedExpenseDate', | ||||
|         'formattedCreatedAt', | ||||
|         'receipt' | ||||
|     ]; | ||||
|  | ||||
|     public function category() | ||||
|     { | ||||
|         return $this->belongsTo(ExpenseCategory::class, 'expense_category_id'); | ||||
|     } | ||||
|  | ||||
|     public function getFormattedExpenseDateAttribute($value) | ||||
|     { | ||||
|         $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); | ||||
|         return Carbon::parse($this->expense_date)->format($dateFormat); | ||||
|     } | ||||
|  | ||||
|     public function getFormattedCreatedAtAttribute($value) | ||||
|     { | ||||
|         $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); | ||||
|         return Carbon::parse($this->created_at)->format($dateFormat); | ||||
|     } | ||||
|  | ||||
|     public function getReceiptAttribute($value) | ||||
|     { | ||||
|         $media = $this->getFirstMedia('receipts'); | ||||
|         if($media) { | ||||
|             return $media->getPath(); | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     public function scopeExpensesBetween($query, $start, $end) | ||||
|     { | ||||
|         return $query->whereBetween( | ||||
|             'expenses.expense_date', | ||||
|             [$start->format('Y-m-d'), $end->format('Y-m-d')] | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereCategoryName($query, $search) | ||||
|     { | ||||
|         foreach (explode(' ', $search) as $term) { | ||||
|             $query->whereHas('category', function ($query) use ($term) { | ||||
|                 $query->where('name', 'LIKE', '%'.$term.'%'); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereNotes($query, $search) | ||||
|     { | ||||
|         $query->where('notes', 'LIKE', '%'.$search.'%'); | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereCategory($query, $categoryId) | ||||
|     { | ||||
|         return $query->where('expenses.expense_category_id', $categoryId); | ||||
|     } | ||||
|  | ||||
|     public function scopeApplyFilters($query, array $filters) | ||||
|     { | ||||
|         $filters = collect($filters); | ||||
|  | ||||
|         if ($filters->get('expense_category_id')) { | ||||
|             $query->whereCategory($filters->get('expense_category_id')); | ||||
|         } | ||||
|  | ||||
|         if ($filters->get('from_date') && $filters->get('to_date')) { | ||||
|             $start = Carbon::createFromFormat('d/m/Y', $filters->get('from_date')); | ||||
|             $end = Carbon::createFromFormat('d/m/Y', $filters->get('to_date')); | ||||
|             $query->expensesBetween($start, $end); | ||||
|         } | ||||
|  | ||||
|         if ($filters->get('orderByField') || $filters->get('orderBy')) { | ||||
|             $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'expense_date'; | ||||
|             $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'asc'; | ||||
|             $query->whereOrder($field, $orderBy); | ||||
|         } | ||||
|  | ||||
|         if ($filters->get('search')) { | ||||
|             $query->whereSearch($filters->get('search')); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereSearch($query, $search) | ||||
|     { | ||||
|         foreach (explode(' ', $search) as $term) { | ||||
|             $query->whereHas('category', function ($query) use ($term) { | ||||
|                 $query->where('name', 'LIKE', '%'.$term.'%'); | ||||
|             }) | ||||
|             ->orWhere('notes', 'LIKE', '%'.$term.'%'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereOrder($query, $orderByField, $orderBy) | ||||
|     { | ||||
|         $query->orderBy($orderByField, $orderBy); | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereCompany($query, $company_id) | ||||
|     { | ||||
|         $query->where('expenses.company_id', $company_id); | ||||
|     } | ||||
|  | ||||
|     public function scopeExpensesAttributes($query) | ||||
|     { | ||||
|         $query->select( | ||||
|             DB::raw(' | ||||
|                 count(*) as expenses_count, | ||||
|                 sum(amount) as total_amount, | ||||
|                 expense_category_id' | ||||
|             ) | ||||
|         ) | ||||
|         ->groupBy('expense_category_id'); | ||||
|     } | ||||
| } | ||||
| @ -1,39 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Crater\Expense; | ||||
| use Carbon\Carbon; | ||||
|  | ||||
| class ExpenseCategory extends Model | ||||
| { | ||||
|     protected $fillable = ['name', 'company_id', 'description']; | ||||
|  | ||||
|     /** | ||||
|      * The accessors to append to the model's array form. | ||||
|      * | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $appends = ['amount', 'formattedCreatedAt']; | ||||
|  | ||||
|     public function expenses() | ||||
|     { | ||||
|         return $this->hasMany(Expense::class); | ||||
|     } | ||||
|  | ||||
|     public function getFormattedCreatedAtAttribute($value) | ||||
|     { | ||||
|         $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); | ||||
|         return Carbon::parse($this->created_at)->format($dateFormat); | ||||
|     } | ||||
|  | ||||
|     public function getAmountAttribute() | ||||
|     { | ||||
|         return $this->expenses()->sum('amount'); | ||||
|     } | ||||
|  | ||||
|     public function scopeWhereCompany($query, $company_id) | ||||
|     { | ||||
|         $query->where('company_id', $company_id); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										47
									
								
								app/Generators/CustomPathGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								app/Generators/CustomPathGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Generators; | ||||
|  | ||||
| use Crater\Models\Estimate; | ||||
| use Crater\Models\Invoice; | ||||
| use Crater\Models\Payment; | ||||
| use Spatie\MediaLibrary\MediaCollections\Models\Media; | ||||
| use Spatie\MediaLibrary\Support\PathGenerator\PathGenerator; | ||||
|  | ||||
| class CustomPathGenerator implements PathGenerator | ||||
| { | ||||
|     public function getPath(Media $media): string | ||||
|     { | ||||
|         return $this->getBasePath($media).'/'; | ||||
|     } | ||||
|  | ||||
|     public function getPathForConversions(Media $media): string | ||||
|     { | ||||
|         return $this->getBasePath($media).'/conversations/'; | ||||
|     } | ||||
|  | ||||
|     public function getPathForResponsiveImages(Media $media): string | ||||
|     { | ||||
|         return $this->getBasePath($media).'/responsive-images/'; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * Get a unique base path for the given media. | ||||
|      */ | ||||
|     protected function getBasePath(Media $media): string | ||||
|     { | ||||
|         $folderName = null; | ||||
|  | ||||
|         if ($media->model_type == Invoice::class) { | ||||
|             $folderName = 'Invoices'; | ||||
|         } elseif ($media->model_type == Estimate::class) { | ||||
|             $folderName = 'Estimates'; | ||||
|         } elseif ($media->model_type == Payment::class) { | ||||
|             $folderName = 'Payments'; | ||||
|         } else { | ||||
|             $folderName = $media->getKey(); | ||||
|         } | ||||
|  | ||||
|         return $folderName; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										24
									
								
								app/Http/Controllers/AppVersionController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/Http/Controllers/AppVersionController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Crater\Models\Setting; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class AppVersionController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         $version = Setting::getSetting('version'); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'version' => $version, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -1,194 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers\Auth; | ||||
|  | ||||
| use Crater\Proxy\HttpKernelProxy; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Http\Response; | ||||
| use Illuminate\Validation\ValidationException; | ||||
| use Illuminate\Foundation\Auth\ThrottlesLogins; | ||||
| use Validator; | ||||
| use Hash; | ||||
| use Crater\User; | ||||
| use Auth; | ||||
| use Crater\Http\Controllers\Controller; | ||||
|  | ||||
| class AccessTokensController extends Controller | ||||
| { | ||||
| 	use ThrottlesLogins; | ||||
|  | ||||
| 	/** | ||||
| 	 * A tool for proxying requests to the existing application. | ||||
| 	 * | ||||
| 	 * @var HttpKernelProxy | ||||
| 	 */ | ||||
| 	protected $proxy; | ||||
|  | ||||
| 	/** | ||||
| 	 * Create a new controller instance. | ||||
| 	 * | ||||
| 	 * @return void | ||||
| 	 */ | ||||
| 	public function __construct(HttpKernelProxy $proxy) | ||||
| 	{ | ||||
| 		$this->middleware('api')->except(['store', 'update']); | ||||
| 		$this->proxy = $proxy; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Get the login username to be used by the controller. | ||||
| 	 * | ||||
| 	 * @return string | ||||
| 	 */ | ||||
| 	public function username() | ||||
| 	{ | ||||
| 		return 'email'; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Generate a new access token. | ||||
| 	 * | ||||
| 	 * @param  \Illuminate\Http\Request  $request | ||||
| 	 * @return \Illuminate\Http\Response | ||||
| 	 */ | ||||
| 	public function store(Request $request) | ||||
| 	{ | ||||
| 		$request->validate([ | ||||
| 			'username' => 'required|email', | ||||
| 			'password' => 'required|string|min:8', | ||||
| 		]); | ||||
|  | ||||
| 		if ($this->hasTooManyLoginAttempts($request)) { | ||||
| 			$this->fireLockoutEvent($request); | ||||
|  | ||||
| 			return $this->sendLockoutResponse($request); | ||||
| 		} | ||||
|  | ||||
| 		return $this->requestPasswordGrant($request); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Refresh an access token. | ||||
| 	 * | ||||
| 	 * @param  \Illuminate\Http\Request  $request | ||||
| 	 * @return \Illuminate\Http\Response | ||||
| 	 */ | ||||
| 	public function update(Request $request) | ||||
| 	{ | ||||
| 		$token = $request->cookie('refresh_token'); | ||||
|  | ||||
| 		if (!$token) { | ||||
| 			throw ValidationException::withMessages([ | ||||
| 				'refresh_token' => trans('oauth.missing_refresh_token') | ||||
| 			]); | ||||
| 		} | ||||
|  | ||||
| 		$response = $this->proxy->postJson('oauth/token', [ | ||||
| 			'client_id' => config('auth.proxy.client_id'), | ||||
| 			'client_secret' => config('auth.proxy.client_secret'), | ||||
| 			'grant_type' => 'refresh_token', | ||||
| 			'refresh_token' => $token, | ||||
| 			'scopes' => '[*]', | ||||
| 		]); | ||||
|  | ||||
| 		if ($response->isSuccessful()) { | ||||
| 			return $this->sendSuccessResponse($response); | ||||
| 		} | ||||
|  | ||||
| 		return response($response->getContent(), $response->getStatusCode()); | ||||
| 	} | ||||
|  | ||||
|    /** | ||||
|      * Get the guard to be used during authentication. | ||||
|      * | ||||
|      * @return \Illuminate\Contracts\Auth\StatefulGuard | ||||
|      */ | ||||
|     protected function guard() | ||||
|     { | ||||
|         return Auth::guard('api'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
|      * @param  int  $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function destroy(Request $request) | ||||
|     { | ||||
|         $accessToken = Auth::user()->token(); | ||||
|  | ||||
|         \DB::table('oauth_refresh_tokens') | ||||
|                 ->where('access_token_id', $accessToken->id) | ||||
|                 ->update([ | ||||
|                         'revoked' => true | ||||
|                 ]); | ||||
|  | ||||
|         $accessToken->revoke(); | ||||
|  | ||||
|         return response()->json(null, 200); | ||||
|     } | ||||
|  | ||||
| 	/** | ||||
| 	 * Create a new access token from a password grant client. | ||||
| 	 * | ||||
| 	 * @param \Illuminate\Http\Request  $request | ||||
| 	 * @return \Illuminate\Http\Response | ||||
| 	 */ | ||||
| 	public function requestPasswordGrant(Request $request) | ||||
| 	{ | ||||
| 		$response = $this->proxy->postJson('oauth/token', [ | ||||
| 			'client_id' => config('auth.proxy.client_id'), | ||||
| 			'client_secret' => config('auth.proxy.client_secret'), | ||||
| 			'grant_type' => config('auth.proxy.grant_type'), | ||||
| 			'username' => $request->username, | ||||
| 			'password' => $request->password, | ||||
| 			'scopes' => '[*]' | ||||
| 		]); | ||||
|  | ||||
|         $user = User::where('email', $request->username)->first(); | ||||
|  | ||||
|         if ($response->isSuccessful()) { | ||||
|             $this->clearLoginAttempts($request); | ||||
| 			return $this->sendSuccessResponse($response, $user); | ||||
| 		} | ||||
|  | ||||
| 		$this->incrementLoginAttempts($request); | ||||
|  | ||||
| 		return response($response->getContent(), $response->getStatusCode()); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Return a successful response for requesting an api token. | ||||
| 	 * | ||||
| 	 * @param \Illuminate\Http\Response $response | ||||
| 	 * @return \Illuminate\Http\Response | ||||
| 	 */ | ||||
| 	public function sendSuccessResponse(Response $response, $user) | ||||
| 	{ | ||||
| 		$data = json_decode($response->getContent()); | ||||
|  | ||||
|         $content = [ | ||||
| 			'access_token' => $data->access_token, | ||||
| 			'expires_in' => $data->expires_in, | ||||
| 		]; | ||||
|  | ||||
| 		return response($content, $response->getStatusCode())->cookie( | ||||
| 			'refresh_token', | ||||
| 			$data->refresh_token, | ||||
| 			10 * 24 * 60, | ||||
| 			"", | ||||
| 			"", | ||||
| 			true, | ||||
| 			true | ||||
| 		); | ||||
|     } | ||||
|  | ||||
|     public function isRegistered(Request $request) | ||||
|     { | ||||
|         if (User::whereEmail($request->email)->first()) { | ||||
|             return 'true'; | ||||
|         } else { | ||||
|             return 'false'; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,258 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\User; | ||||
| use Crater\Setting; | ||||
| use Crater\Company; | ||||
| use Crater\Address; | ||||
| use Crater\Http\Requests\SettingRequest; | ||||
| use Crater\Http\Requests\SettingKeyRequest; | ||||
| use Crater\Http\Requests\ProfileRequest; | ||||
| use Crater\Http\Requests\CompanyRequest; | ||||
| use Crater\Http\Requests\CompanySettingRequest; | ||||
| use Crater\Http\Requests\NotificationSettingsRequest; | ||||
| use Crater\Space\CurrencyFormatter; | ||||
| use Crater\Space\DateFormatter; | ||||
| use Crater\Space\TimeZones; | ||||
| use Crater\Currency; | ||||
| use Crater\CompanySetting; | ||||
|  | ||||
| class CompanyController extends Controller | ||||
| { | ||||
|     public function getAdmin() | ||||
|     { | ||||
|         return User::find(1); | ||||
|     } | ||||
|  | ||||
|     public function updateAdminProfile(ProfileRequest $request) | ||||
|     { | ||||
|         $verifyEmail = User::where('email', $request->email)->first(); | ||||
|  | ||||
|         $user = auth()->user(); | ||||
|  | ||||
|         if ($verifyEmail) { | ||||
|             if ($verifyEmail->id !== $user->id) { | ||||
|                 return response()->json([ | ||||
|                     'error' => 'Email already in use' | ||||
|                 ]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $user->name = $request->name; | ||||
|         $user->email = $request->email; | ||||
|  | ||||
|         if ($request->has('password')) { | ||||
|             $user->password = bcrypt($request->password); | ||||
|         } | ||||
|  | ||||
|         $user->save(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'user' => $user, | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function getAdminCompany() | ||||
|     { | ||||
|         $user = User::with(['addresses', 'addresses.country', 'addresses.state', 'addresses.city', 'company'])->find(1); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'user' => $user | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function updateAdminCompany(CompanyRequest $request) | ||||
|     { | ||||
|         $user = User::find(1); | ||||
|         $company = $user->company; | ||||
|         $company->name = $request->name; | ||||
|         $company->save(); | ||||
|  | ||||
|         if ($request->has('logo')) { | ||||
|             $company->clearMediaCollection('logo'); | ||||
|             $company->addMediaFromRequest('logo')->toMediaCollection('logo'); | ||||
|         } | ||||
|  | ||||
|         $fields = $request->only(['address_street_1', 'address_street_2', 'city_id', 'state_id', 'country_id', 'zip', 'phone']); | ||||
|         $address = Address::updateOrCreate(['user_id' => 1], $fields); | ||||
|         $user = User::with(['addresses', 'addresses.country', 'addresses.state', 'addresses.city', 'company'])->find(1); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'user' => $user, | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function getGeneralSettings(Request $request) | ||||
|     { | ||||
|         $date_formats = DateFormatter::get_list(); | ||||
|  | ||||
|         $time_zones = TimeZones::get_list(); | ||||
|         $fiscal_years = [ | ||||
|             ['key' => 'january-december' , 'value' => '1-12'], | ||||
|             ['key' => 'february-january' , 'value' => '2-1'], | ||||
|             ['key' => 'march-february'   , 'value' => '3-2'], | ||||
|             ['key' => 'april-march'      , 'value' => '4-3'], | ||||
|             ['key' => 'may-april'        , 'value' => '5-4'], | ||||
|             ['key' => 'june-may'         , 'value' => '6-5'], | ||||
|             ['key' => 'july-june'        , 'value' => '7-6'], | ||||
|             ['key' => 'august-july'      , 'value' => '8-7'], | ||||
|             ['key' => 'september-august' , 'value' => '9-8'], | ||||
|             ['key' => 'october-september', 'value' => '10-9'], | ||||
|             ['key' => 'november-october' , 'value' => '11-10'], | ||||
|             ['key' => 'december-november', 'value' => '12-11'], | ||||
|         ]; | ||||
|  | ||||
|         $language = CompanySetting::getSetting('language', $request->header('company')); | ||||
|         $carbon_date_format = CompanySetting::getSetting('carbon_date_format', $request->header('company')); | ||||
|         $moment_date_format = CompanySetting::getSetting('moment_date_format', $request->header('company')); | ||||
|         $time_zone = CompanySetting::getSetting('time_zone', $request->header('company')); | ||||
|         $currency = CompanySetting::getSetting('currency', $request->header('company')); | ||||
|         $fiscal_year = CompanySetting::getSetting('fiscal_year', $request->header('company')); | ||||
|  | ||||
|         $languages = [ | ||||
|             ["code"=>"en", "name" => "English"], | ||||
|             ["code"=>"fr", "name" => "French"], | ||||
|             ["code"=>"es", "name" => "Spanish"] | ||||
|         ]; | ||||
|  | ||||
|         return response()->json([ | ||||
|             'languages' => $languages, | ||||
|             'date_formats' => $date_formats, | ||||
|             'time_zones' => $time_zones, | ||||
|             'time_zone' => $time_zone, | ||||
|             'currencies' => Currency::all(), | ||||
|             'fiscal_years' => $fiscal_years, | ||||
|             'fiscal_year' => $fiscal_year, | ||||
|             'selectedLanguage' => $language, | ||||
|             'selectedCurrency' => $currency, | ||||
|             'carbon_date_format' => $carbon_date_format, | ||||
|             'moment_date_format' => $moment_date_format, | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function updateGeneralSettings(CompanySettingRequest $request) | ||||
|     { | ||||
|         $sets = [ | ||||
|             'currency', | ||||
|             'time_zone', | ||||
|             'language', | ||||
|             'carbon_date_format', | ||||
|             'fiscal_year', | ||||
|             'moment_date_format' | ||||
|         ]; | ||||
|  | ||||
|         foreach ($sets as $key) { | ||||
|             CompanySetting::setSetting($key, $request->$key, $request->header('company')); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function updateSetting(SettingRequest $request) | ||||
|     { | ||||
|         CompanySetting::setSetting($request->key, $request->value, $request->header('company')); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function getSetting(SettingKeyRequest $request) | ||||
|     { | ||||
|         $setting = CompanySetting::getSetting($request->key, $request->header('company')); | ||||
|  | ||||
|         return response()->json([ | ||||
|             $request->key => $setting | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function getColors(Request $request) | ||||
|     { | ||||
|         $colors = [ | ||||
|             'invoice_primary_color', | ||||
|             'invoice_column_heading', | ||||
|             'invoice_field_label', | ||||
|             'invoice_field_value', | ||||
|             'invoice_body_text', | ||||
|             'invoice_description_text', | ||||
|             'invoice_border_color', | ||||
|             'primary_text_color', | ||||
|             'heading_text_color', | ||||
|             'section_heading_text_color', | ||||
|             'border_color', | ||||
|             'body_text_color', | ||||
|             'footer_text_color', | ||||
|             'footer_total_color', | ||||
|             'footer_bg_color', | ||||
|             'date_text_color' | ||||
|         ]; | ||||
|  | ||||
|         $colorSettings = CompanySetting::whereIn('option', $colors) | ||||
|             ->whereCompany($request->header('company')) | ||||
|             ->get(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'colorSettings' => $colorSettings | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Upload the company logo to storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function uploadCompanyLogo(Request $request) | ||||
|     { | ||||
|         $data = json_decode($request->company_logo); | ||||
|  | ||||
|         if($data) { | ||||
|             $company = Company::find($request->header('company')); | ||||
|  | ||||
|             if($company) { | ||||
|                 $company->clearMediaCollection('logo'); | ||||
|  | ||||
|                 $company->addMediaFromBase64($data->data) | ||||
|                     ->usingFileName($data->name) | ||||
|                     ->toMediaCollection('logo'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Upload the Admin Avatar to public storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function uploadAdminAvatar(Request $request) | ||||
|     { | ||||
|         $data = json_decode($request->admin_avatar); | ||||
|  | ||||
|         if($data) { | ||||
|             $user = auth()->user(); | ||||
|  | ||||
|             if($user) { | ||||
|                 $user->clearMediaCollection('admin_avatar'); | ||||
|  | ||||
|                 $user->addMediaFromBase64($data->data) | ||||
|                     ->usingFileName($data->name) | ||||
|                     ->toMediaCollection('admin_avatar'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'user' => $user, | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -1,12 +1,15 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Foundation\Bus\DispatchesJobs; | ||||
| use Illuminate\Routing\Controller as BaseController; | ||||
| use Illuminate\Foundation\Validation\ValidatesRequests; | ||||
| use Illuminate\Foundation\Auth\Access\AuthorizesRequests; | ||||
| use Illuminate\Foundation\Bus\DispatchesJobs; | ||||
| use Illuminate\Foundation\Validation\ValidatesRequests; | ||||
| use Illuminate\Routing\Controller as BaseController; | ||||
|  | ||||
| class Controller extends BaseController | ||||
| { | ||||
|     use AuthorizesRequests, DispatchesJobs, ValidatesRequests; | ||||
|     use AuthorizesRequests; | ||||
|     use DispatchesJobs; | ||||
|     use ValidatesRequests; | ||||
| } | ||||
|  | ||||
| @ -1,234 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\Auth; | ||||
| use Crater\Conversation; | ||||
| use Crater\Group; | ||||
| use Crater\Http\Requests; | ||||
| use Crater\Notifications\CustomerAdded; | ||||
| use Crater\User; | ||||
| use Illuminate\Support\Facades\Hash; | ||||
| use Crater\Currency; | ||||
| use Crater\CompanySetting; | ||||
| use Crater\Address; | ||||
| use Illuminate\Support\Facades\DB; | ||||
|  | ||||
| class CustomersController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $limit = $request->has('limit') ? $request->limit : 10; | ||||
|  | ||||
|         $customers = User::customer() | ||||
|             ->applyFilters($request->only([ | ||||
|                 'search', | ||||
|                 'contact_name', | ||||
|                 'display_name', | ||||
|                 'phone', | ||||
|                 'orderByField', | ||||
|                 'orderBy' | ||||
|             ])) | ||||
|             ->whereCompany($request->header('company')) | ||||
|             ->select('users.*', | ||||
|                 DB::raw('sum(invoices.due_amount) as due_amount') | ||||
|             ) | ||||
|             ->groupBy('users.id') | ||||
|             ->leftJoin('invoices', 'users.id', '=', 'invoices.user_id') | ||||
|             ->paginate($limit); | ||||
|  | ||||
|         $siteData = [ | ||||
|             'customers' => $customers | ||||
|         ]; | ||||
|  | ||||
|         return response()->json($siteData); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function store(Requests\CustomerRequest $request) | ||||
|     { | ||||
|         $verifyEmail = User::where('email', $request->email)->first(); | ||||
|  | ||||
|  | ||||
|         $customer = new User(); | ||||
|         $customer->name = $request->name; | ||||
|         $customer->currency_id = $request->currency_id; | ||||
|         $customer->company_id = $request->header('company'); | ||||
|         $customer->email = $request->email; | ||||
|         $customer->phone = $request->phone; | ||||
|         $customer->company_name = $request->company_name; | ||||
|         $customer->contact_name = $request->contact_name; | ||||
|         $customer->website = $request->website; | ||||
|         $customer->enable_portal = $request->enable_portal; | ||||
|         $customer->role = 'customer'; | ||||
|         $customer->password = Hash::make($request->password); | ||||
|         $customer->save(); | ||||
|  | ||||
|         if ($request->addresses) { | ||||
|             foreach ($request->addresses as $address) { | ||||
|                 $newAddress = new Address(); | ||||
|                 $newAddress->name = $address["name"]; | ||||
|                 $newAddress->address_street_1 = $address["address_street_1"]; | ||||
|                 $newAddress->address_street_2 = $address["address_street_2"]; | ||||
|                 $newAddress->city_id = $address["city_id"]; | ||||
|                 $newAddress->state_id = $address["state_id"]; | ||||
|                 $newAddress->country_id = $address["country_id"]; | ||||
|                 $newAddress->zip = $address["zip"]; | ||||
|                 $newAddress->phone = $address["phone"]; | ||||
|                 $newAddress->type = $address["type"]; | ||||
|                 $newAddress->user_id = $customer->id; | ||||
|                 $newAddress->save(); | ||||
|                 $customer->addresses()->save($newAddress); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $customer = User::with('billingAddress', 'shippingAddress')->find($customer->id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'customer' => $customer, | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Display the specified resource. | ||||
|      * | ||||
|      * @param  int  $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function show($id) | ||||
|     { | ||||
|         $customer = User::with([ | ||||
|             'billingAddress', | ||||
|             'shippingAddress', | ||||
|             'billingAddress.country', | ||||
|             'billingAddress.state', | ||||
|             'billingAddress.city', | ||||
|             'shippingAddress.country', | ||||
|             'shippingAddress.state', | ||||
|             'shippingAddress.city', | ||||
|         ])->find($id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'customer' => $customer | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show the form for editing the specified resource. | ||||
|      * | ||||
|      * @param  int  $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function edit($id) | ||||
|     { | ||||
|         $customer = User::with('billingAddress', 'shippingAddress')->findOrFail($id); | ||||
|         $currency = $customer->currency; | ||||
|         $currencies = Currency::all(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'customer' => $customer, | ||||
|             'currencies' => $currencies, | ||||
|             'currency' => $currency | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the specified resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @param  int  $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function update($id, Requests\CustomerRequest $request) | ||||
|     { | ||||
|         $customer = User::find($id); | ||||
|  | ||||
|         if ($request->email != null) { | ||||
|             $verifyEmail = User::where('email', $request->email)->first(); | ||||
|  | ||||
|             if ($verifyEmail) { | ||||
|                 if ($verifyEmail->id !== $customer->id) { | ||||
|                     return response()->json([ | ||||
|                         'success' => false, | ||||
|                         'error' => 'Email already in use' | ||||
|                     ]); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($request->has('password')) { | ||||
|             $customer->password = Hash::make($request->password); | ||||
|         } | ||||
|  | ||||
|         $customer->name = $request->name; | ||||
|         $customer->currency_id = $request->currency_id; | ||||
|         $customer->email = $request->email; | ||||
|         $customer->phone = $request->phone; | ||||
|         $customer->company_name = $request->company_name; | ||||
|         $customer->contact_name = $request->contact_name; | ||||
|         $customer->website = $request->website; | ||||
|         $customer->enable_portal = $request->enable_portal; | ||||
|         $customer->save(); | ||||
|  | ||||
|         if ($request->addresses) { | ||||
|             foreach ($request->addresses as $address) { | ||||
|                 $newAddress = $customer->addresses()->firstOrNew(['type' => $address["type"]]); | ||||
|                 $newAddress->name = $address["name"]; | ||||
|                 $newAddress->address_street_1 = $address["address_street_1"]; | ||||
|                 $newAddress->address_street_2 = $address["address_street_2"]; | ||||
|                 $newAddress->city_id = $address["city_id"]; | ||||
|                 $newAddress->state_id = $address["state_id"]; | ||||
|                 $newAddress->country_id = $address["country_id"]; | ||||
|                 $newAddress->zip = $address["zip"]; | ||||
|                 $newAddress->phone = $address["phone"]; | ||||
|                 $newAddress->type = $address["type"]; | ||||
|                 $newAddress->user_id = $customer->id; | ||||
|                 $newAddress->save(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $customer = User::with('billingAddress', 'shippingAddress')->find($customer->id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'customer' => $customer, | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
|      * @param  int  $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function destroy($id) | ||||
|     { | ||||
|         User::deleteCustomer($id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function delete(Request $request) | ||||
|     { | ||||
|         foreach ($request->id as $id) { | ||||
|             User::deleteCustomer($id); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -1,155 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| use Crater\Estimate; | ||||
| use Crater\Http\Requests; | ||||
| use Crater\Invoice; | ||||
| use Crater\CompanySetting; | ||||
| use Crater\Expense; | ||||
| use Crater\Payment; | ||||
| use Carbon\Carbon; | ||||
| use Crater\User; | ||||
| use Illuminate\Support\Facades\DB; | ||||
|  | ||||
| class DashboardController extends Controller | ||||
| { | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $invoiceTotals = []; | ||||
|         $expenseTotals = []; | ||||
|         $receiptTotals = []; | ||||
|         $netProfits = []; | ||||
|         $i = 0; | ||||
|         $months = []; | ||||
|         $monthEnds = []; | ||||
|         $monthCounter = 0; | ||||
|         $fiscalYear = CompanySetting::getSetting('fiscal_year', $request->header('company')); | ||||
|         $startDate = Carbon::now(); | ||||
|         $start = Carbon::now(); | ||||
|         $end = Carbon::now(); | ||||
|         $terms = explode('-', $fiscalYear); | ||||
|         if ($terms[0] < $start->month) { | ||||
|             $startDate->month($terms[0])->startOfMonth(); | ||||
|             $start->month($terms[0])->startOfMonth(); | ||||
|             $end->month($terms[0])->endOfMonth(); | ||||
|         } else { | ||||
|             $startDate->subYear()->month($terms[0])->startOfMonth(); | ||||
|             $start->subYear()->month($terms[0])->startOfMonth(); | ||||
|             $end->subYear()->month($terms[0])->endOfMonth(); | ||||
|         } | ||||
|  | ||||
|         if ($request->has('previous_year')) { | ||||
|             $startDate->subYear()->startOfMonth(); | ||||
|             $start->subYear()->startOfMonth(); | ||||
|             $end->subYear()->endOfMonth(); | ||||
|         } | ||||
|  | ||||
|         while ($monthCounter < 12) { | ||||
|             array_push( | ||||
|                 $invoiceTotals, | ||||
|                 Invoice::whereBetween( | ||||
|                     'invoice_date', | ||||
|                     [$start->format('Y-m-d'), $end->format('Y-m-d')] | ||||
|                 ) | ||||
|                 ->whereCompany($request->header('company')) | ||||
|                 ->sum('total') | ||||
|             ); | ||||
|             array_push( | ||||
|                 $expenseTotals, | ||||
|                 Expense::whereBetween( | ||||
|                     'expense_date', | ||||
|                     [$start->format('Y-m-d'), $end->format('Y-m-d')] | ||||
|                 ) | ||||
|                 ->whereCompany($request->header('company')) | ||||
|                 ->sum('amount') | ||||
|             ); | ||||
|             array_push( | ||||
|                 $receiptTotals, | ||||
|                 Payment::whereBetween( | ||||
|                     'payment_date', | ||||
|                     [$start->format('Y-m-d'), $end->format('Y-m-d')] | ||||
|                 ) | ||||
|                 ->whereCompany($request->header('company')) | ||||
|                 ->sum('amount') | ||||
|             ); | ||||
|             array_push( | ||||
|                 $netProfits, | ||||
|                 ($receiptTotals[$i] - $expenseTotals[$i]) | ||||
|             ); | ||||
|             $i++; | ||||
|             array_push($months, $start->format('M')); | ||||
|             $monthCounter++; | ||||
|             $end->startOfMonth(); | ||||
|             $start->addMonth()->startOfMonth(); | ||||
|             $end->addMonth()->endOfMonth(); | ||||
|         } | ||||
|  | ||||
|         $start->subMonth()->endOfMonth(); | ||||
|         $salesTotal = Invoice::whereCompany($request->header('company')) | ||||
|             ->whereBetween( | ||||
|                 'invoice_date', | ||||
|                 [$startDate->format('Y-m-d'), $start->format('Y-m-d')] | ||||
|             ) | ||||
|             ->sum('total'); | ||||
|         $totalReceipts = Payment::whereCompany($request->header('company')) | ||||
|             ->whereBetween( | ||||
|                 'payment_date', | ||||
|                 [$startDate->format('Y-m-d'), $start->format('Y-m-d')] | ||||
|             ) | ||||
|             ->sum('amount'); | ||||
|         $totalExpenses = Expense::whereCompany($request->header('company')) | ||||
|             ->whereBetween( | ||||
|                 'expense_date', | ||||
|                 [$startDate->format('Y-m-d'), $start->format('Y-m-d')] | ||||
|             ) | ||||
|             ->sum('amount'); | ||||
|         $netProfit = (int)$totalReceipts - (int)$totalExpenses; | ||||
|  | ||||
|         $chartData = [ | ||||
|             'months'        => $months, | ||||
|             'invoiceTotals' => $invoiceTotals, | ||||
|             'expenseTotals' => $expenseTotals, | ||||
|             'receiptTotals' => $receiptTotals, | ||||
|             'netProfits'    => $netProfits | ||||
|         ]; | ||||
|  | ||||
|         $customersCount = User::customer()->whereCompany($request->header('company'))->get()->count(); | ||||
|         $invoicesCount = Invoice::whereCompany($request->header('company'))->get()->count(); | ||||
|         $estimatesCount = Estimate::whereCompany($request->header('company'))->get()->count(); | ||||
|         $totalDueAmount = Invoice::whereCompany($request->header('company'))->sum('due_amount'); | ||||
|         $dueInvoices = Invoice::with('user')->whereCompany($request->header('company'))->where('due_amount', '>', 0)->take(5)->latest()->get(); | ||||
|         $estimates = Estimate::with('user')->whereCompany($request->header('company'))->take(5)->latest()->get(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'dueInvoices' => $dueInvoices, | ||||
|             'estimates' => $estimates, | ||||
|             'estimatesCount' => $estimatesCount, | ||||
|             'totalDueAmount' => $totalDueAmount, | ||||
|             'invoicesCount' => $invoicesCount, | ||||
|             'customersCount' => $customersCount, | ||||
|             'chartData' => $chartData, | ||||
|             'salesTotal' => $salesTotal, | ||||
|             'totalReceipts' => $totalReceipts, | ||||
|             'totalExpenses' => $totalExpenses, | ||||
|             'netProfit' => $netProfit | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function getExpenseChartData(Request $request) | ||||
|     { | ||||
|         $expensesCategories = Expense::with('category') | ||||
|             ->whereCompany($request->header('company')) | ||||
|             ->expensesAttributes() | ||||
|             ->get(); | ||||
|  | ||||
|         $amounts = $expensesCategories->pluck('total_amount'); | ||||
|         $names = $expensesCategories->pluck('category.name'); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'amounts' => $amounts, | ||||
|             'categories' => $names, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -1,470 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\Estimate; | ||||
| use Crater\EstimateItem; | ||||
| use Crater\EstimateTemplate; | ||||
| use Carbon\Carbon; | ||||
| use Crater\Http\Requests\EstimatesRequest; | ||||
| use Crater\Invoice; | ||||
| use Crater\Currency; | ||||
| use Crater\User; | ||||
| use Crater\Item; | ||||
| use Validator; | ||||
| use Crater\CompanySetting; | ||||
| use Crater\Company; | ||||
| use Crater\Mail\EstimatePdf; | ||||
| use Crater\TaxType; | ||||
| use Crater\Tax; | ||||
|  | ||||
| class EstimatesController extends Controller | ||||
| { | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $limit = $request->has('limit') ? $request->limit : 10; | ||||
|  | ||||
|         $estimates = Estimate::with([ | ||||
|                 'items', | ||||
|                 'user', | ||||
|                 'estimateTemplate', | ||||
|                 'taxes' | ||||
|             ]) | ||||
|             ->join('users', 'users.id', '=', 'estimates.user_id') | ||||
|             ->applyFilters($request->only([ | ||||
|                 'status', | ||||
|                 'customer_id', | ||||
|                 'estimate_number', | ||||
|                 'from_date', | ||||
|                 'to_date', | ||||
|                 'search', | ||||
|                 'orderByField', | ||||
|                 'orderBy' | ||||
|             ])) | ||||
|             ->whereCompany($request->header('company')) | ||||
|             ->select('estimates.*', 'users.name') | ||||
|             ->latest() | ||||
|             ->paginate($limit); | ||||
|  | ||||
|         $siteData = [ | ||||
|             'estimates' => $estimates, | ||||
|             'estimateTotalCount' => Estimate::count() | ||||
|         ]; | ||||
|  | ||||
|         return response()->json($siteData); | ||||
|     } | ||||
|  | ||||
|     public function create(Request $request) | ||||
|     { | ||||
|         $nextEstimateNumber = 'EST-'.Estimate::getNextEstimateNumber(); | ||||
|         $tax_per_item = CompanySetting::getSetting('tax_per_item', $request->header('company')); | ||||
|         $discount_per_item = CompanySetting::getSetting('discount_per_item', $request->header('company')); | ||||
|         $customers = User::where('role', 'customer')->get(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'customers' => $customers, | ||||
|             'nextEstimateNumber' => $nextEstimateNumber, | ||||
|             'taxes' => Tax::whereCompany($request->header('company'))->latest()->get(), | ||||
|             'items' => Item::whereCompany($request->header('company'))->get(), | ||||
|             'tax_per_item' => $tax_per_item, | ||||
|             'discount_per_item' => $discount_per_item, | ||||
|             'estimateTemplates' => EstimateTemplate::all(), | ||||
|             'shareable_link' => '' | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function store(EstimatesRequest $request) | ||||
|     { | ||||
|         $estimate_date = Carbon::createFromFormat('d/m/Y', $request->estimate_date); | ||||
|         $expiry_date = Carbon::createFromFormat('d/m/Y', $request->expiry_date); | ||||
|         $status = Estimate::STATUS_DRAFT; | ||||
|         $tax_per_item = CompanySetting::getSetting( | ||||
|             'tax_per_item', | ||||
|             $request->header('company') | ||||
|         ) ? CompanySetting::getSetting( | ||||
|             'tax_per_item', | ||||
|             $request->header('company') | ||||
|         ) : 'NO'; | ||||
|  | ||||
|         if ($request->has('estimateSend')) { | ||||
|             $status = Estimate::STATUS_SENT; | ||||
|         } | ||||
|  | ||||
|         $discount_per_item = CompanySetting::getSetting( | ||||
|             'discount_per_item', | ||||
|             $request->header('company') | ||||
|         ) ? CompanySetting::getSetting( | ||||
|             'discount_per_item', | ||||
|             $request->header('company') | ||||
|         ) : 'NO'; | ||||
|  | ||||
|         $estimate = Estimate::create([ | ||||
|             'estimate_date' => $estimate_date, | ||||
|             'expiry_date' => $expiry_date, | ||||
|             'estimate_number' => $request->estimate_number, | ||||
|             'reference_number' => $request->reference_number, | ||||
|             'user_id' => $request->user_id, | ||||
|             'company_id' => $request->header('company'), | ||||
|             'estimate_template_id' => $request->estimate_template_id, | ||||
|             'status' => $status, | ||||
|             'discount' => $request->discount, | ||||
|             'discount_type' => $request->discount_type, | ||||
|             'discount_val' => $request->discount_val, | ||||
|             'sub_total' => $request->sub_total, | ||||
|             'total' => $request->total, | ||||
|             'tax_per_item' => $tax_per_item, | ||||
|             'discount_per_item' => $discount_per_item, | ||||
|             'tax' => $request->tax, | ||||
|             'notes' => $request->notes, | ||||
|             'unique_hash' => str_random(60) | ||||
|         ]); | ||||
|  | ||||
|         $estimateItems = $request->items; | ||||
|  | ||||
|         foreach ($estimateItems as $estimateItem) { | ||||
|             $estimateItem['company_id'] = $request->header('company'); | ||||
|             $item = $estimate->items()->create($estimateItem); | ||||
|  | ||||
|             if (array_key_exists('taxes', $estimateItem) && $estimateItem['taxes']) { | ||||
|                 foreach ($estimateItem['taxes'] as $tax) { | ||||
|                     if ($tax['amount']) { | ||||
|                         $tax['company_id'] = $request->header('company'); | ||||
|                         $item->taxes()->create($tax); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($request->has('taxes')) { | ||||
|             foreach ($request->taxes as $tax) { | ||||
|                 if ($tax['amount']) { | ||||
|                     $tax['company_id'] = $request->header('company'); | ||||
|                     $estimate->taxes()->create($tax); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($request->has('estimateSend')) { | ||||
|             $data['estimate'] = $estimate->toArray(); | ||||
|             $userId = $data['estimate']['user_id']; | ||||
|             $data['user'] = User::find($userId)->toArray(); | ||||
|             $data['company'] = Company::find($estimate->company_id); | ||||
|             $email = $data['user']['email']; | ||||
|             $notificationEmail = CompanySetting::getSetting( | ||||
|                 'notification_email', | ||||
|                 $request->header('company') | ||||
|             ); | ||||
|  | ||||
|             if (!$email) { | ||||
|                 return response()->json([ | ||||
|                     'error' => 'user_email_does_not_exist' | ||||
|                 ]); | ||||
|             } | ||||
|  | ||||
|             if (!$notificationEmail) { | ||||
|                 return response()->json([ | ||||
|                     'error' => 'notification_email_does_not_exist' | ||||
|                 ]); | ||||
|             } | ||||
|  | ||||
|             \Mail::to($email)->send(new EstimatePdf($data, $notificationEmail)); | ||||
|         } | ||||
|  | ||||
|         $estimate = Estimate::with([ | ||||
|             'items', | ||||
|             'user', | ||||
|             'estimateTemplate', | ||||
|             'taxes' | ||||
|         ])->find($estimate->id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'estimate' => $estimate, | ||||
|             'url' => url('/estimates/pdf/'.$estimate->unique_hash), | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function show(Request $request, $id) | ||||
|     { | ||||
|         $estimate = Estimate::with([ | ||||
|             'items', | ||||
|             'items.taxes', | ||||
|             'user', | ||||
|             'estimateTemplate', | ||||
|             'taxes', | ||||
|             'taxes.taxType' | ||||
|         ])->find($id); | ||||
|  | ||||
|         $siteData = [ | ||||
|             'estimate' => $estimate, | ||||
|             'shareable_link' => url('/estimates/pdf/'.$estimate->unique_hash) | ||||
|         ]; | ||||
|  | ||||
|         return response()->json($siteData); | ||||
|     } | ||||
|  | ||||
|     public function edit(Request $request,$id) | ||||
|     { | ||||
|         $estimate = Estimate::with([ | ||||
|             'items', | ||||
|             'items.taxes', | ||||
|             'user', | ||||
|             'estimateTemplate', | ||||
|             'taxes', | ||||
|             'taxes.taxType' | ||||
|         ])->find($id); | ||||
|         $customers = User::where('role', 'customer')->get(); | ||||
|  | ||||
|         return response()->json( [ | ||||
|             'customers' => $customers, | ||||
|             'nextEstimateNumber' => $estimate->estimate_number, | ||||
|             'taxes' => Tax::latest()->whereCompany($request->header('company'))->get(), | ||||
|             'estimate' => $estimate, | ||||
|             'items' => Item::whereCompany($request->header('company'))->latest()->get(), | ||||
|             'estimateTemplates' => EstimateTemplate::all(), | ||||
|             'tax_per_item' => $estimate->tax_per_item, | ||||
|             'discount_per_item' => $estimate->discount_per_item, | ||||
|             'shareable_link' => url('/estimates/pdf/'.$estimate->unique_hash) | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function update(EstimatesRequest $request, $id) | ||||
|     { | ||||
|         $estimate_date = Carbon::createFromFormat('d/m/Y', $request->estimate_date); | ||||
|         $expiry_date = Carbon::createFromFormat('d/m/Y', $request->expiry_date); | ||||
|  | ||||
|         $estimate = Estimate::find($id); | ||||
|         $estimate->estimate_date = $estimate_date; | ||||
|         $estimate->expiry_date = $expiry_date; | ||||
|         $estimate->estimate_number = $request->estimate_number; | ||||
|         $estimate->reference_number = $request->reference_number; | ||||
|         $estimate->user_id = $request->user_id; | ||||
|         $estimate->estimate_template_id = $request->estimate_template_id; | ||||
|         $estimate->discount = $request->discount; | ||||
|         $estimate->discount_type = $request->discount_type; | ||||
|         $estimate->discount_val = $request->discount_val; | ||||
|         $estimate->sub_total = $request->sub_total; | ||||
|         $estimate->total = $request->total; | ||||
|         $estimate->tax = $request->tax; | ||||
|         $estimate->notes = $request->notes; | ||||
|         $estimate->save(); | ||||
|  | ||||
|         $oldItems = $estimate->items->toArray(); | ||||
|         $oldTaxes = $estimate->taxes->toArray(); | ||||
|         $estimateItems = $request->items; | ||||
|  | ||||
|         foreach ($oldItems as $oldItem) { | ||||
|             EstimateItem::destroy($oldItem['id']); | ||||
|         } | ||||
|  | ||||
|         foreach ($oldTaxes as $oldTax) { | ||||
|             Tax::destroy($oldTax['id']); | ||||
|         } | ||||
|  | ||||
|         foreach ($estimateItems as $estimateItem) { | ||||
|             $estimateItem['company_id'] = $request->header('company'); | ||||
|             $item = $estimate->items()->create($estimateItem); | ||||
|  | ||||
|             if (array_key_exists('taxes', $estimateItem) && $estimateItem['taxes']) { | ||||
|                 foreach ($estimateItem['taxes'] as $tax) { | ||||
|                     if ($tax['amount']) { | ||||
|                         $tax['company_id'] = $request->header('company'); | ||||
|                         $item->taxes()->create($tax); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($request->has('taxes')) { | ||||
|             foreach ($request->taxes as $tax) { | ||||
|                 if ($tax['amount']) { | ||||
|                     $tax['company_id'] = $request->header('company'); | ||||
|                     $estimate->taxes()->create($tax); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $estimate = Estimate::with([ | ||||
|             'items', | ||||
|             'user', | ||||
|             'estimateTemplate', | ||||
|             'taxes' | ||||
|         ])->find($estimate->id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'estimate' => $estimate, | ||||
|             'url' => url('/estimates/pdf/'.$estimate->unique_hash), | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function destroy($id) | ||||
|     { | ||||
|         Estimate::deleteEstimate($id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function sendEstimate(Request $request) | ||||
|     { | ||||
|         $estimate = Estimate::findOrFail($request->id); | ||||
|  | ||||
|         $data['estimate'] = $estimate->toArray(); | ||||
|         $userId = $data['estimate']['user_id']; | ||||
|         $data['user'] = User::find($userId)->toArray(); | ||||
|         $data['company'] = Company::find($estimate->company_id); | ||||
|  | ||||
|         $email = $data['user']['email']; | ||||
|         $notificationEmail = CompanySetting::getSetting( | ||||
|             'notification_email', | ||||
|             $request->header('company') | ||||
|         ); | ||||
|  | ||||
|         if (!$email) { | ||||
|             return response()->json([ | ||||
|                 'error' => 'user_email_does_not_exist' | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         if (!$notificationEmail) { | ||||
|             return response()->json([ | ||||
|                 'error' => 'notification_email_does_not_exist' | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         \Mail::to($email)->send(new EstimatePdf($data, $notificationEmail)); | ||||
|  | ||||
|         if ($estimate->status == Estimate::STATUS_DRAFT) { | ||||
|             $estimate->status = Estimate::STATUS_SENT; | ||||
|             $estimate->save(); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function markEstimateAccepted(Request $request) | ||||
|     { | ||||
|         $estimate = Estimate::find($request->id); | ||||
|         $estimate->status = Estimate::STATUS_ACCEPTED; | ||||
|         $estimate->save(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function markEstimateRejected(Request $request) | ||||
|     { | ||||
|         $estimate = Estimate::find($request->id); | ||||
|         $estimate->status = Estimate::STATUS_REJECTED; | ||||
|         $estimate->save(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function markEstimateSent(Request $request) | ||||
|     { | ||||
|         $estimate = Estimate::find($request->id); | ||||
|         $estimate->status = Estimate::STATUS_SENT; | ||||
|         $estimate->save(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function estimateToInvoice(Request $request, $id) | ||||
|     { | ||||
|         $estimate = Estimate::with(['items', 'items.taxes', 'user', 'estimateTemplate', 'taxes'])->find($id); | ||||
|         $invoice_date = Carbon::parse($estimate->estimate_date); | ||||
|         $due_date = Carbon::parse($estimate->estimate_date)->addDays(7); | ||||
|         $tax_per_item = CompanySetting::getSetting( | ||||
|                 'tax_per_item', | ||||
|                 $request->header('company') | ||||
|             ) ? CompanySetting::getSetting( | ||||
|                 'tax_per_item', | ||||
|                 $request->header('company') | ||||
|             ) : 'NO'; | ||||
|         $discount_per_item = CompanySetting::getSetting( | ||||
|                 'discount_per_item', | ||||
|                 $request->header('company') | ||||
|             ) ? CompanySetting::getSetting( | ||||
|                 'discount_per_item', | ||||
|                 $request->header('company') | ||||
|             ) : 'NO'; | ||||
|  | ||||
|         $invoice = Invoice::create([ | ||||
|             'invoice_date' => $invoice_date, | ||||
|             'due_date' => $due_date, | ||||
|             'invoice_number' => "INV-".Invoice::getNextInvoiceNumber(), | ||||
|             'reference_number' => $estimate->reference_number, | ||||
|             'user_id' => $estimate->user_id, | ||||
|             'company_id' => $request->header('company'), | ||||
|             'invoice_template_id' => 1, | ||||
|             'status' => Invoice::STATUS_DRAFT, | ||||
|             'paid_status' => Invoice::STATUS_UNPAID, | ||||
|             'sub_total' => $estimate->sub_total, | ||||
|             'discount' => $estimate->discount, | ||||
|             'discount_type' => $estimate->discount_type, | ||||
|             'discount_val' => $estimate->discount_val, | ||||
|             'total' => $estimate->total, | ||||
|             'due_amount' => $estimate->total, | ||||
|             'tax_per_item' => $tax_per_item, | ||||
|             'discount_per_item' => $discount_per_item, | ||||
|             'tax' => $estimate->tax, | ||||
|             'notes' => $estimate->notes, | ||||
|             'unique_hash' => str_random(60) | ||||
|         ]); | ||||
|  | ||||
|         $invoiceItems = $estimate->items->toArray(); | ||||
|  | ||||
|         foreach ($invoiceItems as $invoiceItem) { | ||||
|             $invoiceItem['company_id'] = $request->header('company'); | ||||
|             $invoiceItem['name'] = $invoiceItem['name']; | ||||
|             $item = $invoice->items()->create($invoiceItem); | ||||
|  | ||||
|             if (array_key_exists('taxes', $invoiceItem) && $invoiceItem['taxes']) { | ||||
|                 foreach ($invoiceItem['taxes'] as $tax) { | ||||
|                     $tax['company_id'] = $request->header('company'); | ||||
|  | ||||
|                     if ($tax['amount']) { | ||||
|                         $item->taxes()->create($tax); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($estimate->taxes) { | ||||
|             foreach ($estimate->taxes->toArray() as $tax) { | ||||
|                 $tax['company_id'] = $request->header('company'); | ||||
|                 $invoice->taxes()->create($tax); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $invoice = Invoice::with([ | ||||
|             'items', | ||||
|             'user', | ||||
|             'invoiceTemplate', | ||||
|             'taxes' | ||||
|         ])->find($invoice->id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'invoice' => $invoice | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function delete(Request $request) | ||||
|     { | ||||
|         foreach ($request->id as $id) { | ||||
|             Estimate::deleteEstimate($id); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -1,122 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Crater\ExpenseCategory; | ||||
| use Crater\Expense; | ||||
| use Crater\User; | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\Http\Requests\ExpenseCategoryRequest; | ||||
|  | ||||
| class ExpenseCategoryController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $categories = ExpenseCategory::whereCompany($request->header('company'))->get(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'categories' => $categories | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show the form for creating a new resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function create() | ||||
|     { | ||||
|         // return view('app.categories.create'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function store(ExpenseCategoryRequest $request) | ||||
|     { | ||||
|         $category = new ExpenseCategory(); | ||||
|         $category->name = $request->name; | ||||
|         $category->description = $request->description; | ||||
|         $category->company_id = $request->header('company'); | ||||
|         $category->save(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'category' => $category, | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Display the specified resource. | ||||
|      * | ||||
|      * @param  \Crater\ExpenseCategory $ExpenseCategory | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function show(ExpenseCategory $ExpenseCategory) | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show the form for editing the specified resource. | ||||
|      * | ||||
|      * @param  \Crater\ExpensesCategory $ExpensesCategory | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function edit($id) | ||||
|     { | ||||
|         $category = ExpenseCategory::findOrFail($id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'category' => $category | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the specified resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @param  \Crater\ExpenseCategory $ExpenseCategory | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function update(ExpenseCategoryRequest $request, $id) | ||||
|     { | ||||
|         $category = ExpenseCategory::findOrFail($id); | ||||
|         $category->name = $request->name; | ||||
|         $category->description = $request->description; | ||||
|         $category->save(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'category' => $category, | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
|      * @param  \Crater\ExpensesCategory $expensesCategory | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function destroy($id) | ||||
|     { | ||||
|         $category = ExpenseCategory::find($id); | ||||
|         if ($category->expenses() && $category->expenses()->count() > 0) { | ||||
|             return response()->json([ | ||||
|                 'success' => false | ||||
|             ]); | ||||
|         } | ||||
|         $category->delete(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -1,260 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Crater\Expense; | ||||
| use Crater\User; | ||||
| use Crater\Currency; | ||||
| use Crater\Company; | ||||
| use Crater\CompanySetting; | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\ExpenseCategory; | ||||
| use Crater\Http\Requests\ExpenseRequest; | ||||
| use Carbon\Carbon; | ||||
| use Intervention\Image\Facades\Image; | ||||
|  | ||||
| class ExpensesController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $limit = $request->has('limit') ? $request->limit : 10; | ||||
|  | ||||
|         $expenses = Expense::with('category') | ||||
|             ->join('expense_categories', 'expense_categories.id', '=', 'expenses.expense_category_id') | ||||
|             ->applyFilters($request->only([ | ||||
|                 'expense_category_id', | ||||
|                 'search', | ||||
|                 'from_date', | ||||
|                 'to_date', | ||||
|                 'orderByField', | ||||
|                 'orderBy' | ||||
|             ])) | ||||
|             ->whereCompany($request->header('company')) | ||||
|             ->select('expenses.*', 'expense_categories.name') | ||||
|             ->paginate($limit); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'expenses' => $expenses, | ||||
|             'currency' => Currency::findOrFail( | ||||
|                 CompanySetting::getSetting('currency', $request->header('company')) | ||||
|             ) | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show the form for creating a new resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function create(Request $request) | ||||
|     { | ||||
|         $categories = ExpenseCategory::whereCompany($request->header('company'))->get(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'categories' => $categories | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function store(ExpenseRequest $request) | ||||
|     { | ||||
|         $expense_date = Carbon::createFromFormat('d/m/Y', $request->expense_date); | ||||
|  | ||||
|         $expense = new Expense(); | ||||
|         $expense->notes = $request->notes; | ||||
|         $expense->expense_category_id = $request->expense_category_id; | ||||
|         $expense->amount = $request->amount; | ||||
|         $expense->company_id = $request->header('company'); | ||||
|         $expense->expense_date = $expense_date; | ||||
|         $expense->save(); | ||||
|  | ||||
|         if ($request->hasFile('attachment_receipt')) { | ||||
|             $expense->addMediaFromRequest('attachment_receipt')->toMediaCollection('receipts', 'local'); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'expense' => $expense, | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Display the specified resource. | ||||
|      * | ||||
|      * @param  \Crater\Expense $expense | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function show(Expense $expense) | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show the form for editing the specified resource. | ||||
|      * | ||||
|      * @param  $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function edit(Request $request,$id) | ||||
|     { | ||||
|         $categories = ExpenseCategory::whereCompany($request->header('company'))->get(); | ||||
|         $customers = User::where('role', 'customer')->whereCompany($request->header('company'))->get(); | ||||
|         $expense = Expense::with('category')->where('id', $id)->first(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'categories' => $categories, | ||||
|             'customers' => $customers, | ||||
|             'expense' => $expense | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the specified resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @param  \Crater\Expense $expense | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function update(ExpenseRequest $request, Expense $expense) | ||||
|     { | ||||
|         $expense_date = Carbon::createFromFormat('d/m/Y', $request->expense_date); | ||||
|  | ||||
|         $expense = Expense::findOrFail($expense->id); | ||||
|         $expense->notes = $request->notes; | ||||
|         $expense->expense_category_id = $request->expense_category_id; | ||||
|         $expense->amount = $request->amount; | ||||
|         $expense->expense_date = $expense_date; | ||||
|         $expense->save(); | ||||
|  | ||||
|         if ($request->hasFile('attachment_receipt')) { | ||||
|             $expense->clearMediaCollection('receipts'); | ||||
|             $expense->addMediaFromRequest('attachment_receipt')->toMediaCollection('receipts', 'local'); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'expense' => $expense, | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
|      * @param  \Crater\Expense $expense | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function destroy(Expense $expense) | ||||
|     { | ||||
|         $expense->delete(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function delete(Request $request) | ||||
|     { | ||||
|         Expense::destroy($request->id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Upload the expense receipts to storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @param   $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function uploadReceipts(Request $request, $id) | ||||
|     { | ||||
|         $data = json_decode($request->attachment_receipt); | ||||
|  | ||||
|         if($data) { | ||||
|             $expense = Expense::find($id); | ||||
|  | ||||
|             if($expense) { | ||||
|                 if($request->type === 'edit') { | ||||
|                     $expense->clearMediaCollection('receipts'); | ||||
|                 } | ||||
|  | ||||
|                 $expense->addMediaFromBase64($data->data) | ||||
|                     ->usingFileName($data->name) | ||||
|                     ->toMediaCollection('receipts', 'local'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => 'Expense receipts uploaded successfully' | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function showReceipt($id) | ||||
|     { | ||||
|         $expense = Expense::find($id); | ||||
|         $imagePath  = null; | ||||
|  | ||||
|         if($expense) { | ||||
|             $media = $expense->getFirstMedia('receipts'); | ||||
|             if($media) { | ||||
|                 $imagePath = $media->getPath(); | ||||
|             } else { | ||||
|                 return response()->json([ | ||||
|                     'error' => 'receipt_does_not_exist' | ||||
|                 ]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $type = \File::mimeType($imagePath); | ||||
|  | ||||
|         $image = 'data:'.$type.';base64,'.base64_encode(file_get_contents($imagePath)); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'image' => $image, | ||||
|             'type' => $type | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function downloadReceipt($id, $hash) | ||||
|     { | ||||
|         $company = Company::where('unique_hash', $hash)->first(); | ||||
|  | ||||
|         $expense = Expense::whereCompany($company->id) | ||||
|             ->where('id', $id) | ||||
|             ->first(); | ||||
|         $imagePath  = null; | ||||
|  | ||||
|         if($expense) { | ||||
|             $media = $expense->getFirstMedia('receipts'); | ||||
|             if($media) { | ||||
|                 $imagePath = $media->getPath(); | ||||
|                 $filename = $media->getPath(); | ||||
|                 $type = \File::mimeType($imagePath); | ||||
|  | ||||
|                 $headers = array( | ||||
|                     'Content-Type' => $type, | ||||
|                 ); | ||||
|  | ||||
|                 $response = \Response::download($imagePath, $media->file_name); | ||||
|                 ob_end_clean(); | ||||
|                 return $response; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'error' => 'receipt_not_found' | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,374 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\Invoice; | ||||
| use PDF; | ||||
| use Crater\CompanySetting; | ||||
| use Crater\Estimate; | ||||
| use Crater\User; | ||||
| use Crater\Company; | ||||
| use Crater\InvoiceTemplate; | ||||
| use Crater\EstimateTemplate; | ||||
| use Crater\Mail\EstimateViewed; | ||||
| use Crater\Mail\InvoiceViewed; | ||||
|  | ||||
| class FrontendController extends Controller | ||||
| { | ||||
|     public function home() | ||||
|     { | ||||
|         return view('front.index'); | ||||
|     } | ||||
|  | ||||
|     public function getCustomerEstimatePdf($id) | ||||
|     { | ||||
|         $estimate = Estimate::with( | ||||
|                 'user', | ||||
|                 'items', | ||||
|                 'user.billingAddress', | ||||
|                 'user.shippingAddress' | ||||
|             ) | ||||
|             ->where('unique_hash', $id) | ||||
|             ->first(); | ||||
|  | ||||
|         $taxTypes = []; | ||||
|         $taxes = []; | ||||
|         $labels = []; | ||||
|  | ||||
|         if ($estimate->tax_per_item === 'YES') { | ||||
|             foreach ($estimate->items as $item) { | ||||
|                 foreach ($item->taxes as $tax) { | ||||
|                     if (!in_array($tax->name, $taxTypes)) { | ||||
|                         array_push($taxTypes, $tax->name); | ||||
|                         array_push($labels, $tax->name.' ('.$tax->percent.'%)'); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             foreach ($taxTypes as $taxType) { | ||||
|                 $total = 0; | ||||
|  | ||||
|                 foreach ($estimate->items as $item) { | ||||
|                     foreach ($item->taxes as $tax) { | ||||
|                         if($tax->name == $taxType) { | ||||
|                             $total += $tax->amount; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 array_push($taxes, $total); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $estimateTemplate = EstimateTemplate::find($estimate->estimate_template_id); | ||||
|  | ||||
|         $company = Company::find($estimate->company_id); | ||||
|  | ||||
|         $logo = $company->getMedia('logo')->first(); | ||||
|  | ||||
|         if($logo) { | ||||
|             $logo = $logo->getFullUrl(); | ||||
|         } | ||||
|  | ||||
|         if ($estimate && ($estimate->status == Estimate::STATUS_SENT || $estimate->status == Estimate::STATUS_DRAFT)) { | ||||
|             $estimate->status = Estimate::STATUS_VIEWED; | ||||
|             $estimate->save(); | ||||
|             $notifyEstimateViewed = CompanySetting::getSetting( | ||||
|                 'notify_estimate_viewed', | ||||
|                 $estimate->company_id | ||||
|             ); | ||||
|  | ||||
|             if ($notifyEstimateViewed == 'YES') { | ||||
|                 $data['estimate'] = Estimate::findOrFail($estimate->id)->toArray(); | ||||
|                 $data['user'] = User::find($estimate->user_id)->toArray(); | ||||
|                 $notificationEmail = CompanySetting::getSetting( | ||||
|                     'notification_email', | ||||
|                     $estimate->company_id | ||||
|                 ); | ||||
|  | ||||
|                 \Mail::to($notificationEmail)->send(new EstimateViewed($data)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $companyAddress = User::with(['addresses', 'addresses.country', 'addresses.state', 'addresses.city'])->find(1); | ||||
|  | ||||
|         $colors = [ | ||||
|             'invoice_primary_color', | ||||
|             'invoice_column_heading', | ||||
|             'invoice_field_label', | ||||
|             'invoice_field_value', | ||||
|             'invoice_body_text', | ||||
|             'invoice_description_text', | ||||
|             'invoice_border_color' | ||||
|         ]; | ||||
|         $colorSettings = CompanySetting::whereIn('option', $colors) | ||||
|             ->whereCompany($estimate->company_id) | ||||
|             ->get(); | ||||
|  | ||||
|         view()->share([ | ||||
|             'estimate' => $estimate, | ||||
|             'logo' => $logo ?? null, | ||||
|             'company_address' => $companyAddress, | ||||
|             'colors' => $colorSettings, | ||||
|             'labels' => $labels, | ||||
|             'taxes' => $taxes | ||||
|         ]); | ||||
|         $pdf = PDF::loadView('app.pdf.estimate.'.$estimateTemplate->view); | ||||
|  | ||||
|         return $pdf->stream(); | ||||
|     } | ||||
|  | ||||
|     public function getCustomerInvoicePdf($id) | ||||
|     { | ||||
|         $invoice = Invoice::with([ | ||||
|                 'items', | ||||
|                 'items.taxes', | ||||
|                 'user', | ||||
|                 'invoiceTemplate', | ||||
|                 'taxes' | ||||
|             ]) | ||||
|             ->where('unique_hash', $id) | ||||
|             ->first(); | ||||
|  | ||||
|         $taxTypes = []; | ||||
|         $taxes = []; | ||||
|         $labels = []; | ||||
|  | ||||
|         if ($invoice->tax_per_item === 'YES') { | ||||
|             foreach ($invoice->items as $item) { | ||||
|                 foreach ($item->taxes as $tax) { | ||||
|                     if (!in_array($tax->name, $labels)) { | ||||
|                         array_push($taxTypes, $tax->name); | ||||
|                         array_push($labels, $tax->name.' ('.$tax->percent.'%)'); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             foreach ($taxTypes as $taxType) { | ||||
|                 $total = 0; | ||||
|  | ||||
|                 foreach ($invoice->items as $item) { | ||||
|                     foreach ($item->taxes as $tax) { | ||||
|                         if($tax->name == $taxType) { | ||||
|                             $total += $tax->amount; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 array_push($taxes, $total); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $invoiceTemplate = InvoiceTemplate::find($invoice->invoice_template_id); | ||||
|  | ||||
|         $company = Company::find($invoice->company_id); | ||||
|         $logo = $company->getMedia('logo')->first(); | ||||
|  | ||||
|         if($logo) { | ||||
|             $logo = $logo->getFullUrl(); | ||||
|         } | ||||
|  | ||||
|         if ($invoice && ($invoice->status == Invoice::STATUS_SENT || $invoice->status == Invoice::STATUS_DRAFT)) { | ||||
|             $invoice->status = Invoice::STATUS_VIEWED; | ||||
|             $invoice->viewed = true; | ||||
|             $invoice->save(); | ||||
|             $notifyInvoiceViewed = CompanySetting::getSetting( | ||||
|                 'notify_invoice_viewed', | ||||
|                 $invoice->company_id | ||||
|             ); | ||||
|  | ||||
|             if ($notifyInvoiceViewed == 'YES') { | ||||
|                 $data['invoice'] = Invoice::findOrFail($invoice->id)->toArray(); | ||||
|                 $data['user'] = User::find($invoice->user_id)->toArray(); | ||||
|                 $notificationEmail = CompanySetting::getSetting( | ||||
|                     'notification_email', | ||||
|                     $invoice->company_id | ||||
|                 ); | ||||
|  | ||||
|                 \Mail::to($notificationEmail)->send(new InvoiceViewed($data)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $companyAddress = User::with(['addresses', 'addresses.country', 'addresses.state', 'addresses.city'])->find(1); | ||||
|  | ||||
|         $colors = [ | ||||
|             'invoice_primary_color', | ||||
|             'invoice_column_heading', | ||||
|             'invoice_field_label', | ||||
|             'invoice_field_value', | ||||
|             'invoice_body_text', | ||||
|             'invoice_description_text', | ||||
|             'invoice_border_color' | ||||
|         ]; | ||||
|         $colorSettings = CompanySetting::whereIn('option', $colors) | ||||
|             ->whereCompany($invoice->company_id) | ||||
|             ->get(); | ||||
|  | ||||
|         view()->share([ | ||||
|             'invoice' => $invoice, | ||||
|             'colors' => $colorSettings, | ||||
|             'company_address' => $companyAddress, | ||||
|             'logo' => $logo ?? null, | ||||
|             'labels' => $labels, | ||||
|             'taxes' => $taxes | ||||
|         ]); | ||||
|         $pdf = PDF::loadView('app.pdf.invoice.'.$invoiceTemplate->view); | ||||
|  | ||||
|         return $pdf->stream(); | ||||
|     } | ||||
|  | ||||
|     public function getEstimatePdf($id) | ||||
|     { | ||||
|         $estimate = Estimate::with([ | ||||
|                 'items', | ||||
|                 'items.taxes', | ||||
|                 'user', | ||||
|                 'estimateTemplate', | ||||
|                 'taxes', | ||||
|                 'taxes.taxType' | ||||
|             ]) | ||||
|             ->where('unique_hash', $id) | ||||
|             ->first(); | ||||
|  | ||||
|         $taxTypes = []; | ||||
|         $taxes = []; | ||||
|         $labels = []; | ||||
|  | ||||
|         if ($estimate->tax_per_item === 'YES') { | ||||
|             foreach ($estimate->items as $item) { | ||||
|                 foreach ($item->taxes as $tax) { | ||||
|                     if (!in_array($tax->name, $taxTypes)) { | ||||
|                         array_push($taxTypes, $tax->name); | ||||
|                         array_push($labels, $tax->name.' ('.$tax->percent.'%)'); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             foreach ($taxTypes as $taxType) { | ||||
|                 $total = 0; | ||||
|  | ||||
|                 foreach ($estimate->items as $item) { | ||||
|                     foreach ($item->taxes as $tax) { | ||||
|                         if($tax->name == $taxType) { | ||||
|                             $total += $tax->amount; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 array_push($taxes, $total); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $estimateTemplate = EstimateTemplate::find($estimate->estimate_template_id); | ||||
|  | ||||
|         $company = Company::find($estimate->company_id); | ||||
|         $companyAddress = User::with(['addresses', 'addresses.country', 'addresses.state', 'addresses.city'])->find(1); | ||||
|         $logo = $company->getMedia('logo')->first(); | ||||
|  | ||||
|         if($logo) { | ||||
|             $logo = $logo->getFullUrl(); | ||||
|         } | ||||
|  | ||||
|         $colors = [ | ||||
|             'invoice_primary_color', | ||||
|             'invoice_column_heading', | ||||
|             'invoice_field_label', | ||||
|             'invoice_field_value', | ||||
|             'invoice_body_text', | ||||
|             'invoice_description_text', | ||||
|             'invoice_border_color' | ||||
|         ]; | ||||
|         $colorSettings = CompanySetting::whereIn('option', $colors) | ||||
|             ->whereCompany($estimate->company_id) | ||||
|             ->get(); | ||||
|  | ||||
|         view()->share([ | ||||
|             'estimate' => $estimate, | ||||
|             'logo' => $logo ?? null, | ||||
|             'company_address' => $companyAddress, | ||||
|             'colors' => $colorSettings, | ||||
|             'labels' => $labels, | ||||
|             'taxes' => $taxes | ||||
|         ]); | ||||
|         $pdf = PDF::loadView('app.pdf.estimate.'.$estimateTemplate->view); | ||||
|  | ||||
|         return $pdf->stream(); | ||||
|     } | ||||
|  | ||||
|     public function getInvoicePdf($id) | ||||
|     { | ||||
|         $invoice = Invoice::with([ | ||||
|                 'items', | ||||
|                 'items.taxes', | ||||
|                 'user', | ||||
|                 'invoiceTemplate', | ||||
|                 'taxes' | ||||
|             ]) | ||||
|             ->where('unique_hash', $id) | ||||
|             ->first(); | ||||
|  | ||||
|         $taxTypes = []; | ||||
|         $taxes = []; | ||||
|         $labels = []; | ||||
|  | ||||
|         if ($invoice->tax_per_item === 'YES') { | ||||
|             foreach ($invoice->items as $item) { | ||||
|                 foreach ($item->taxes as $tax) { | ||||
|                     if (!in_array($tax->name, $taxTypes)) { | ||||
|                         array_push($taxTypes, $tax->name); | ||||
|                         array_push($labels, $tax->name.' ('.$tax->percent.'%)'); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             foreach ($taxTypes as $taxType) { | ||||
|                 $total = 0; | ||||
|  | ||||
|                 foreach ($invoice->items as $item) { | ||||
|                     foreach ($item->taxes as $tax) { | ||||
|                         if($tax->name == $taxType) { | ||||
|                             $total += $tax->amount; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 array_push($taxes, $total); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $invoiceTemplate = InvoiceTemplate::find($invoice->invoice_template_id); | ||||
|         $company = Company::find($invoice->company_id); | ||||
|         $companyAddress = User::with(['addresses', 'addresses.country', 'addresses.state', 'addresses.city'])->find(1); | ||||
|  | ||||
|         $logo = $company->getMedia('logo')->first(); | ||||
|  | ||||
|         if($logo) { | ||||
|             $logo = $logo->getFullUrl(); | ||||
|         } | ||||
|  | ||||
|         $colors = [ | ||||
|             'invoice_primary_color', | ||||
|             'invoice_column_heading', | ||||
|             'invoice_field_label', | ||||
|             'invoice_field_value', | ||||
|             'invoice_body_text', | ||||
|             'invoice_description_text', | ||||
|             'invoice_border_color' | ||||
|         ]; | ||||
|         $colorSettings = CompanySetting::whereIn('option', $colors) | ||||
|             ->whereCompany($invoice->company_id) | ||||
|             ->get(); | ||||
|  | ||||
|         view()->share([ | ||||
|             'invoice' => $invoice, | ||||
|             'company_address' => $companyAddress, | ||||
|             'logo' => $logo ?? null, | ||||
|             'colors' => $colorSettings, | ||||
|             'labels' => $labels, | ||||
|             'taxes' => $taxes | ||||
|         ]); | ||||
|         $pdf = PDF::loadView('app.pdf.invoice.'.$invoiceTemplate->view); | ||||
|  | ||||
|         return $pdf->stream(); | ||||
|     } | ||||
| } | ||||
| @ -1,448 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\CompanySetting; | ||||
| use Crater\Company; | ||||
| use Illuminate\Support\Collection; | ||||
| use Crater\Currency; | ||||
| use Crater\InvoiceTemplate; | ||||
| use Crater\Http\Requests; | ||||
| use Crater\Invoice; | ||||
| use Crater\InvoiceItem; | ||||
| use Carbon\Carbon; | ||||
| use Crater\Item; | ||||
| use Crater\Mail\invoicePdf; | ||||
| use function MongoDB\BSON\toJSON; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Crater\User; | ||||
| use Mailgun\Mailgun; | ||||
| use PDF; | ||||
| use Validator; | ||||
| use Crater\TaxType; | ||||
| use Crater\Tax; | ||||
|  | ||||
| class InvoicesController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $limit = $request->has('limit') ? $request->limit : 10; | ||||
|  | ||||
|         $invoices = Invoice::with(['items', 'user', 'invoiceTemplate', 'taxes']) | ||||
|             ->join('users', 'users.id', '=', 'invoices.user_id') | ||||
|             ->applyFilters($request->only([ | ||||
|                 'status', | ||||
|                 'paid_status', | ||||
|                 'customer_id', | ||||
|                 'invoice_number', | ||||
|                 'from_date', | ||||
|                 'to_date', | ||||
|                 'orderByField', | ||||
|                 'orderBy', | ||||
|                 'search', | ||||
|             ])) | ||||
|             ->whereCompany($request->header('company')) | ||||
|             ->select('invoices.*', 'users.name') | ||||
|             ->latest() | ||||
|             ->paginate($limit); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'invoices' => $invoices, | ||||
|             'invoiceTotalCount' => Invoice::count() | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show the form for creating a new resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function create(Request $request) | ||||
|     { | ||||
|         $tax_per_item = CompanySetting::getSetting('tax_per_item', $request->header('company')); | ||||
|         $discount_per_item = CompanySetting::getSetting('discount_per_item', $request->header('company')); | ||||
|         $nextInvoiceNumber = "INV-".Invoice::getNextInvoiceNumber(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'nextInvoiceNumber' => $nextInvoiceNumber, | ||||
|             'items' => Item::with('taxes')->whereCompany($request->header('company'))->get(), | ||||
|             'invoiceTemplates' => InvoiceTemplate::all(), | ||||
|             'tax_per_item' => $tax_per_item, | ||||
|             'discount_per_item' => $discount_per_item | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function store(Requests\InvoicesRequest $request) | ||||
|     { | ||||
|         $invoice_date = Carbon::createFromFormat('d/m/Y', $request->invoice_date); | ||||
|         $due_date = Carbon::createFromFormat('d/m/Y', $request->due_date); | ||||
|         $status = Invoice::STATUS_DRAFT; | ||||
|  | ||||
|         $tax_per_item = CompanySetting::getSetting('tax_per_item', $request->header('company')) ?? 'NO'; | ||||
|         $discount_per_item = CompanySetting::getSetting('discount_per_item', $request->header('company')) ?? 'NO'; | ||||
|  | ||||
|         if ($request->has('invoiceSend')) { | ||||
|             $status = Invoice::STATUS_SENT; | ||||
|         } | ||||
|  | ||||
|         $invoice = Invoice::create([ | ||||
|             'invoice_date' => $invoice_date, | ||||
|             'due_date' => $due_date, | ||||
|             'invoice_number' => $request->invoice_number, | ||||
|             'reference_number' => $request->reference_number, | ||||
|             'user_id' => $request->user_id, | ||||
|             'company_id' => $request->header('company'), | ||||
|             'invoice_template_id' => $request->invoice_template_id, | ||||
|             'status' => $status, | ||||
|             'paid_status' => Invoice::STATUS_UNPAID, | ||||
|             'sub_total' => $request->sub_total, | ||||
|             'discount' => $request->discount, | ||||
|             'discount_type' => $request->discount_type, | ||||
|             'discount_val' => $request->discount_val, | ||||
|             'total' => $request->total, | ||||
|             'due_amount' => $request->total, | ||||
|             'tax_per_item' => $tax_per_item, | ||||
|             'discount_per_item' => $discount_per_item, | ||||
|             'tax' => $request->tax, | ||||
|             'notes' => $request->notes, | ||||
|             'unique_hash' => str_random(60) | ||||
|         ]); | ||||
|  | ||||
|         $invoiceItems = $request->items; | ||||
|  | ||||
|         foreach ($invoiceItems as $invoiceItem) { | ||||
|             $invoiceItem['company_id'] = $request->header('company'); | ||||
|             $item = $invoice->items()->create($invoiceItem); | ||||
|  | ||||
|             if (array_key_exists('taxes', $invoiceItem) && $invoiceItem['taxes']) { | ||||
|                 foreach ($invoiceItem['taxes'] as $tax) { | ||||
|                     $tax['company_id'] = $request->header('company'); | ||||
|  | ||||
|                     if ($tax['amount']) { | ||||
|                         $item->taxes()->create($tax); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($request->has('taxes')) { | ||||
|             foreach ($request->taxes as $tax) { | ||||
|                 $tax['company_id'] = $request->header('company'); | ||||
|  | ||||
|                 if ($tax['amount']) { | ||||
|                     $invoice->taxes()->create($tax); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($request->has('invoiceSend')) { | ||||
|             $data['invoice'] = Invoice::findOrFail($invoice->id)->toArray(); | ||||
|             $data['user'] = User::find($request->user_id)->toArray(); | ||||
|             $data['company'] = Company::find($invoice->company_id); | ||||
|  | ||||
|             $notificationEmail = CompanySetting::getSetting( | ||||
|                 'notification_email', | ||||
|                 $request->header('company') | ||||
|             ); | ||||
|  | ||||
|             $email = $data['user']['email']; | ||||
|  | ||||
|             if (!$email) { | ||||
|                 return response()->json([ | ||||
|                     'error' => 'user_email_does_not_exist' | ||||
|                 ]); | ||||
|             } | ||||
|  | ||||
|             if (!$notificationEmail) { | ||||
|                 return response()->json([ | ||||
|                     'error' => 'notification_email_does_not_exist' | ||||
|                 ]); | ||||
|             } | ||||
|  | ||||
|             \Mail::to($email)->send(new invoicePdf($data, $notificationEmail)); | ||||
|         } | ||||
|  | ||||
|         $invoice = Invoice::with(['items', 'user', 'invoiceTemplate', 'taxes'])->find($invoice->id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'url' => url('/invoices/pdf/'.$invoice->unique_hash), | ||||
|             'invoice' => $invoice | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Display the specified resource. | ||||
|      * | ||||
|      * @param  int $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function show(Request $request, $id) | ||||
|     { | ||||
|         $invoice = Invoice::with([ | ||||
|             'items', | ||||
|             'items.taxes', | ||||
|             'user', | ||||
|             'invoiceTemplate', | ||||
|             'taxes.taxType' | ||||
|         ])->find($id); | ||||
|  | ||||
|         $siteData = [ | ||||
|             'invoice' => $invoice, | ||||
|             'shareable_link' => url('/invoices/pdf/' . $invoice->unique_hash) | ||||
|         ]; | ||||
|  | ||||
|         return response()->json($siteData); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show the form for editing the specified resource. | ||||
|      * | ||||
|      * @param  int $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function edit(Request $request,$id) | ||||
|     { | ||||
|         $invoice = Invoice::with([ | ||||
|             'items', | ||||
|             'items.taxes', | ||||
|             'user', | ||||
|             'invoiceTemplate', | ||||
|             'taxes.taxType' | ||||
|         ])->find($id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'nextInvoiceNumber' => $invoice->invoice_number, | ||||
|             'invoice' => $invoice, | ||||
|             'invoiceTemplates' => InvoiceTemplate::all(), | ||||
|             'tax_per_item' => $invoice->tax_per_item, | ||||
|             'discount_per_item' => $invoice->discount_per_item, | ||||
|             'shareable_link' => url('/invoices/pdf/'.$invoice->unique_hash) | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the specified resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @param  int $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function update(Requests\InvoicesRequest $request, $id) | ||||
|     { | ||||
|         $invoice_date = Carbon::createFromFormat('d/m/Y', $request->invoice_date); | ||||
|         $due_date = Carbon::createFromFormat('d/m/Y', $request->due_date); | ||||
|  | ||||
|         $invoice = Invoice::find($id); | ||||
|         $oldAmount = $invoice->total; | ||||
|  | ||||
|         if ($oldAmount != $request->total) { | ||||
|             $oldAmount = (int)round($request->total) - (int)$oldAmount; | ||||
|         } else { | ||||
|             $oldAmount = 0; | ||||
|         } | ||||
|  | ||||
|         $invoice->due_amount = ($invoice->due_amount + $oldAmount); | ||||
|  | ||||
|         if ($invoice->due_amount == 0 && $invoice->paid_status != Invoice::STATUS_PAID) { | ||||
|             $invoice->status = Invoice::STATUS_COMPLETED; | ||||
|             $invoice->paid_status = Invoice::STATUS_PAID; | ||||
|         } elseif ($invoice->due_amount < 0 && $invoice->paid_status != Invoice::STATUS_UNPAID) { | ||||
|             return response()->json([ | ||||
|                 'error' => 'invalid_due_amount' | ||||
|             ]); | ||||
|         } elseif ($invoice->due_amount != 0 && $invoice->paid_status == Invoice::STATUS_PAID) { | ||||
|             $invoice->status = $invoice->getPreviousStatus(); | ||||
|             $invoice->paid_status = Invoice::STATUS_PARTIALLY_PAID; | ||||
|         } | ||||
|  | ||||
|         $invoice->invoice_date = $invoice_date; | ||||
|         $invoice->due_date = $due_date; | ||||
|         $invoice->invoice_number = $request->invoice_number; | ||||
|         $invoice->reference_number = $request->reference_number; | ||||
|         $invoice->user_id = $request->user_id; | ||||
|         $invoice->invoice_template_id = $request->invoice_template_id; | ||||
|         $invoice->sub_total = $request->sub_total; | ||||
|         $invoice->total = $request->total; | ||||
|         $invoice->discount = $request->discount; | ||||
|         $invoice->discount_type = $request->discount_type; | ||||
|         $invoice->discount_val = $request->discount_val; | ||||
|         $invoice->tax = $request->tax; | ||||
|         $invoice->notes = $request->notes; | ||||
|         $invoice->save(); | ||||
|  | ||||
|         $oldItems = $invoice->items->toArray(); | ||||
|         $oldTaxes = $invoice->taxes->toArray(); | ||||
|         $invoiceItems = $request->items; | ||||
|  | ||||
|         foreach ($oldItems as $oldItem) { | ||||
|             InvoiceItem::destroy($oldItem['id']); | ||||
|         } | ||||
|  | ||||
|         foreach ($oldTaxes as $oldTax) { | ||||
|             Tax::destroy($oldTax['id']); | ||||
|         } | ||||
|  | ||||
|         foreach ($invoiceItems as $invoiceItem) { | ||||
|             $invoiceItem['company_id'] = $request->header('company'); | ||||
|             $item = $invoice->items()->create($invoiceItem); | ||||
|  | ||||
|             if (array_key_exists('taxes', $invoiceItem) && $invoiceItem['taxes']) { | ||||
|                 foreach ($invoiceItem['taxes'] as $tax) { | ||||
|                     $tax['company_id'] = $request->header('company'); | ||||
|  | ||||
|                     if ($tax['amount']) { | ||||
|                         $item->taxes()->create($tax); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($request->has('taxes')) { | ||||
|             foreach ($request->taxes as $tax) { | ||||
|                 $tax['company_id'] = $request->header('company'); | ||||
|  | ||||
|                 if ($tax['amount']) { | ||||
|                     $invoice->taxes()->create($tax); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $invoice = Invoice::with(['items', 'user', 'invoiceTemplate', 'taxes'])->find($invoice->id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'url' => url('/invoices/pdf/' . $invoice->unique_hash), | ||||
|             'invoice' => $invoice, | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
|      * @param  int $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function destroy($id) | ||||
|     { | ||||
|         $invoice = Invoice::find($id); | ||||
|  | ||||
|         if ($invoice->payments()->exists() && $invoice->payments()->count() > 0) { | ||||
|             return response()->json([ | ||||
|                 'error' => 'payment_attached' | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         $invoice = Invoice::destroy($id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function delete(Request $request) | ||||
|     { | ||||
|         foreach ($request->id as $id) { | ||||
|             $invoice = Invoice::find($id); | ||||
|  | ||||
|             if ($invoice->payments()->exists() && $invoice->payments()->count() > 0) { | ||||
|                 return response()->json([ | ||||
|                     'error' => 'payment_attached' | ||||
|                 ]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $invoice = Invoice::destroy($request->id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function sendInvoice(Request $request) | ||||
|     { | ||||
|         $invoice = Invoice::findOrFail($request->id); | ||||
|  | ||||
|         $data['invoice'] = $invoice->toArray(); | ||||
|         $userId = $data['invoice']['user_id']; | ||||
|         $data['user'] = User::find($userId)->toArray(); | ||||
|         $data['company'] = Company::find($invoice->company_id); | ||||
|         $email = $data['user']['email']; | ||||
|         $notificationEmail = CompanySetting::getSetting( | ||||
|             'notification_email', | ||||
|             $request->header('company') | ||||
|         ); | ||||
|  | ||||
|         if (!$email) { | ||||
|             return response()->json([ | ||||
|                 'error' => 'user_email_does_not_exist' | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         if (!$notificationEmail) { | ||||
|             return response()->json([ | ||||
|                 'error' => 'notification_email_does_not_exist' | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         \Mail::to($email)->send(new invoicePdf($data, $notificationEmail)); | ||||
|  | ||||
|         if ($invoice->status == Invoice::STATUS_DRAFT) { | ||||
|             $invoice->status = Invoice::STATUS_SENT; | ||||
|             $invoice->sent = true; | ||||
|             $invoice->save(); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function markAsSent(Request $request) | ||||
|     { | ||||
|         $invoice = Invoice::findOrFail($request->id); | ||||
|         $invoice->status = Invoice::STATUS_SENT; | ||||
|         $invoice->sent = true; | ||||
|         $invoice->save(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function markAsPaid(Request $request) | ||||
|     { | ||||
|         $invoice = Invoice::findOrFail($request->id); | ||||
|         $invoice->status = Invoice::STATUS_COMPLETED; | ||||
|         $invoice->paid_status = Invoice::STATUS_PAID; | ||||
|         $invoice->due_amount = 0; | ||||
|         $invoice->save(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function getCustomersUnpaidInvoices(Request $request, $id) | ||||
|     { | ||||
|         $invoices = Invoice::where('paid_status', '<>', Invoice::STATUS_PAID) | ||||
|             ->where('user_id', $id)->where('due_amount', '>', 0) | ||||
|             ->whereCompany($request->header('company')) | ||||
|             ->get(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'invoices' => $invoices | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -1,134 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\Http\Requests; | ||||
| use Crater\Item; | ||||
| use Crater\TaxType; | ||||
| use Crater\Tax; | ||||
| use Crater\User; | ||||
|  | ||||
| class ItemsController extends Controller | ||||
| { | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $limit = $request->has('limit') ? $request->limit : 10; | ||||
|  | ||||
|         $items = Item::applyFilters($request->only([ | ||||
|                 'search', | ||||
|                 'price', | ||||
|                 'unit', | ||||
|                 'orderByField', | ||||
|                 'orderBy' | ||||
|             ])) | ||||
|             ->whereCompany($request->header('company')) | ||||
|             ->latest() | ||||
|             ->paginate($limit); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'items' => $items, | ||||
|             'taxTypes' => TaxType::latest()->get() | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function edit(Request $request, $id) | ||||
|     { | ||||
|         $item = Item::with('taxes')->find($id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'item' => $item, | ||||
|             'taxes' => Tax::whereCompany($request->header('company')) | ||||
|                 ->latest() | ||||
|                 ->get() | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function store(Requests\ItemsRequest $request) | ||||
|     { | ||||
|         $item = new Item(); | ||||
|         $item->name = $request->name; | ||||
|         $item->unit = $request->unit; | ||||
|         $item->description = $request->description; | ||||
|         $item->company_id = $request->header('company'); | ||||
|         $item->price = $request->price; | ||||
|         $item->save(); | ||||
|  | ||||
|         if ($request->has('taxes')) { | ||||
|             foreach ($request->taxes as $tax) { | ||||
|                 $tax['company_id'] = $request->header('company'); | ||||
|                 $item->taxes()->create($tax); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $item = Item::with('taxes')->find($item->id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'item' => $item | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function update(Requests\ItemsRequest $request, $id) | ||||
|     { | ||||
|         $item = Item::find($id); | ||||
|         $item->name = $request->name; | ||||
|         $item->unit = $request->unit; | ||||
|         $item->description = $request->description; | ||||
|         $item->price = $request->price; | ||||
|         $item->save(); | ||||
|  | ||||
|         $oldTaxes = $item->taxes->toArray(); | ||||
|  | ||||
|         foreach ($oldTaxes as $oldTax) { | ||||
|             Tax::destroy($oldTax['id']); | ||||
|         } | ||||
|  | ||||
|         if ($request->has('taxes')) { | ||||
|             foreach ($request->taxes as $tax) { | ||||
|                 $tax['company_id'] = $request->header('company'); | ||||
|                 $item->taxes()->create($tax); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $item = Item::with('taxes')->find($item->id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'item' => $item | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function destroy($id) | ||||
|     { | ||||
|         $data = Item::deleteItem($id); | ||||
|  | ||||
|         if (!$data) { | ||||
|             return response()->json([ | ||||
|                 'error' => 'item_attached' | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => $data | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function delete(Request $request) | ||||
|     { | ||||
|         $items = []; | ||||
|         foreach ($request->id as $id) { | ||||
|             $item = Item::deleteItem($id); | ||||
|             if (!$item) { | ||||
|                 array_push($items, $id); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (empty($items)) { | ||||
|             return response()->json([ | ||||
|                 'success' => true | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'items' => $items | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\Country; | ||||
| use Crater\State; | ||||
| use Crater\City; | ||||
|  | ||||
| class LocationController extends Controller | ||||
| { | ||||
|     public function getCountries() | ||||
|     { | ||||
|         return response()->json([ | ||||
|             'countries' => Country::all() | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function getStates($id) | ||||
|     { | ||||
|         return response()->json([ | ||||
|             'states' => Country::find($id)->states | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function getCities($id) | ||||
|     { | ||||
|         return response()->json([ | ||||
|             'cities' => State::find($id)->cities | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -1,251 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\User; | ||||
| use Crater\Company; | ||||
| use Crater\Address; | ||||
| use Crater\Http\Requests\ProfileRequest; | ||||
| use Crater\Http\Requests\CompanyRequest; | ||||
| use Crater\Http\Requests\CompanySettingRequest; | ||||
| use Crater\Space\DateFormatter; | ||||
| use Crater\Space\TimeZones; | ||||
| use Crater\Currency; | ||||
| use Crater\Setting; | ||||
| use Crater\CompanySetting; | ||||
| use Illuminate\Support\Facades\DB; | ||||
| use Illuminate\Support\Facades\Artisan; | ||||
|  | ||||
| class OnboardingController extends Controller | ||||
| { | ||||
|     public function getOnboardingData(Request $request) | ||||
|     { | ||||
|         if (!\Storage::disk('local')->has('database_created')) { | ||||
|             return response()->json([ | ||||
|                 'profile_complete' => '0' | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         $setting = Setting::getSetting('profile_complete'); | ||||
|  | ||||
|         if ($setting !== 'COMPLETED' && $setting < 4){ | ||||
|             return response()->json([ | ||||
|                 'profile_complete' => $setting | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         $date_formats = DateFormatter::get_list(); | ||||
|         $time_zones = TimeZones::get_list(); | ||||
|         $languages = [ | ||||
|             ["code"=>"en", "name" => "English"], | ||||
|             ["code"=>"fr", "name" => "French"], | ||||
|             ["code"=>"es", "name" => "Spanish"] | ||||
|         ]; | ||||
|         $fiscal_years = [ | ||||
|             ['key' => 'january-december' , 'value' => '1-12'], | ||||
|             ['key' => 'february-january' , 'value' => '2-1'], | ||||
|             ['key' => 'march-february'   , 'value' => '3-2'], | ||||
|             ['key' => 'april-march'      , 'value' => '4-3'], | ||||
|             ['key' => 'may-april'        , 'value' => '5-4'], | ||||
|             ['key' => 'june-may'         , 'value' => '6-5'], | ||||
|             ['key' => 'july-june'        , 'value' => '7-6'], | ||||
|             ['key' => 'august-july'      , 'value' => '8-7'], | ||||
|             ['key' => 'september-august' , 'value' => '9-8'], | ||||
|             ['key' => 'october-september', 'value' => '10-9'], | ||||
|             ['key' => 'november-october' , 'value' => '11-10'], | ||||
|             ['key' => 'december-november', 'value' => '12-11'], | ||||
|         ]; | ||||
|         $user = User::with([ | ||||
|             'addresses', | ||||
|             'addresses.country', | ||||
|             'addresses.state', | ||||
|             'addresses.city', | ||||
|             'company' | ||||
|         ])->find(1); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'user' => $user, | ||||
|             'profile_complete' => $setting, | ||||
|             'languages' => $languages, | ||||
|             'date_formats' => $date_formats, | ||||
|             'time_zones' => $time_zones, | ||||
|             'fiscal_years' => $fiscal_years, | ||||
|             'currencies' => Currency::all() | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function adminProfile(ProfileRequest $request) | ||||
|     { | ||||
|         $setting = Setting::getSetting('profile_complete'); | ||||
|  | ||||
|         if ($setting == '1' || $setting == 'COMPLETED') { | ||||
|             return response()->json(['error' => 'Profile already created.']); | ||||
|         } else { | ||||
|             Setting::setSetting('profile_complete', 5); | ||||
|         } | ||||
|  | ||||
|         $user = User::find(1); | ||||
|         $user->name = $request->name; | ||||
|         $user->email = $request->email; | ||||
|  | ||||
|         if ($request->has('password')) { | ||||
|             $user->password = bcrypt($request->password); | ||||
|         } | ||||
|  | ||||
|         $user->save(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'user' => $user | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function uploadAdminAvatar(Request $request) | ||||
|     { | ||||
|         $setting = Setting::getSetting('profile_complete'); | ||||
|  | ||||
|         if ($setting == '1' || $setting == 'COMPLETED') { | ||||
|             return response()->json(['error' => 'Profile already created.']); | ||||
|         } | ||||
|         $data = json_decode($request->admin_avatar); | ||||
|  | ||||
|         if($data) { | ||||
|             $user = User::find($data->id); | ||||
|             if($user) { | ||||
|                 $user->clearMediaCollection('admin_avatar'); | ||||
|  | ||||
|                 $user->addMediaFromBase64($data->data) | ||||
|                     ->usingFileName($data->name) | ||||
|                     ->toMediaCollection('admin_avatar'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'user' => $user, | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function adminCompany(CompanyRequest $request) | ||||
|     { | ||||
|         $setting = Setting::getSetting('profile_complete'); | ||||
|  | ||||
|         if ($setting == '6' || $setting == 'COMPLETED') { | ||||
|             return response()->json(['error' => 'Company already created.']); | ||||
|         } else { | ||||
|             Setting::setSetting('profile_complete', 6); | ||||
|         } | ||||
|  | ||||
|         $user = User::find(1); | ||||
|         $company = $user->company; | ||||
|  | ||||
|         if (!$company) { | ||||
|             $company = new Company(); | ||||
|         } | ||||
|  | ||||
|         $company->name = $request->name; | ||||
|         $company->unique_hash = str_random(60); | ||||
|         $company->save(); | ||||
|         $user->company()->associate($company); | ||||
|         $user->save(); | ||||
|  | ||||
|         if ($request->has('logo') && $request->logo !== null && $request->logo !== 'undefined' ) { | ||||
|             $company->addMediaFromRequest('logo')->toMediaCollection('logo'); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         $fields = $request->only([ | ||||
|             'address_street_1', | ||||
|             'address_street_2', | ||||
|             'city_id', | ||||
|             'state_id', | ||||
|             'country_id', | ||||
|             'zip', | ||||
|             'phone' | ||||
|         ]); | ||||
|         $address = Address::updateOrCreate(['user_id' => 1], $fields); | ||||
|         $user = User::with('addresses', 'company')->find(1); | ||||
|  | ||||
|         CompanySetting::setSetting( | ||||
|             'notification_email', | ||||
|             $user->email, | ||||
|             $company->id | ||||
|         ); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'user' => $user | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function companySettings(CompanySettingRequest $request) | ||||
|     { | ||||
|         $setting = Setting::getSetting('profile_complete'); | ||||
|  | ||||
|         if($setting == 'COMPLETED') { | ||||
|             return response()->json(['error' => 'Settings already saved.']); | ||||
|         } else { | ||||
|             Setting::setSetting('profile_complete', 'COMPLETED'); | ||||
|         } | ||||
|  | ||||
|         $user = User::find(1); | ||||
|  | ||||
|         $sets = ['currency', | ||||
|             'time_zone', | ||||
|             'language', | ||||
|             'carbon_date_format', | ||||
|             'moment_date_format', | ||||
|             'fiscal_year' | ||||
|         ]; | ||||
|  | ||||
|         foreach ($sets as $key) { | ||||
|             CompanySetting::setSetting( | ||||
|                 $key, | ||||
|                 $request->$key, | ||||
|                 $user->company_id | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $colors = [ | ||||
|             'primary_text_color' => '#5851D8', | ||||
|             'heading_text_color' => '#595959', | ||||
|             'section_heading_text_color' => '#040405', | ||||
|             'border_color' => '#EAF1FB', | ||||
|             'body_text_color' => '#595959', | ||||
|             'footer_text_color' => '#595959', | ||||
|             'footer_total_color' => '#5851D8', | ||||
|             'footer_bg_color' => '#F9FBFF', | ||||
|             'date_text_color' => '#A5ACC1', | ||||
|             'invoice_primary_color' => '#5851D8', | ||||
|             'invoice_column_heading' => '#55547A', | ||||
|             'invoice_field_label' => '#55547A', | ||||
|             'invoice_field_value' => '#040405', | ||||
|             'invoice_body_text' => '#040405', | ||||
|             'invoice_description_text' => '#595959', | ||||
|             'invoice_border_color' => '#EAF1FB' | ||||
|         ]; | ||||
|         foreach ($colors as $key => $value) { | ||||
|             CompanySetting::setSetting( | ||||
|                 $key, | ||||
|                 $value, | ||||
|                 $user->company_id | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         Setting::setSetting('version', config('crater.version')); | ||||
|  | ||||
|         Artisan::call('passport:install --force'); | ||||
|  | ||||
|         $client = DB::table('oauth_clients')->find(2); | ||||
|  | ||||
|         $path = base_path('.env'); | ||||
|  | ||||
|         if (file_exists($path)) { | ||||
|             file_put_contents($path, str_replace( | ||||
|                 'PROXY_OAUTH_CLIENT_SECRET='.config('auth.proxy.client_secret'), 'PROXY_OAUTH_CLIENT_SECRET='.$client->secret, file_get_contents($path) | ||||
|             )); | ||||
|         } | ||||
|  | ||||
|         $data['token'] = $user->createToken('password')->accessToken; | ||||
|  | ||||
|         return response()->json($data); | ||||
|     } | ||||
| } | ||||
| @ -1,252 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\CompanySetting; | ||||
| use Crater\Currency; | ||||
| use Crater\Invoice; | ||||
| use Crater\Payment; | ||||
| use Carbon\Carbon; | ||||
| use function MongoDB\BSON\toJSON; | ||||
| use Crater\User; | ||||
| use Crater\Http\Requests\PaymentRequest; | ||||
|  | ||||
| class PaymentController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $limit = $request->has('limit') ? $request->limit : 10; | ||||
|  | ||||
|         $payments = Payment::with('user', 'invoice') | ||||
|             ->join('users', 'users.id', '=', 'payments.user_id') | ||||
|             ->leftJoin('invoices', 'invoices.id', '=', 'payments.invoice_id') | ||||
|             ->applyFilters($request->only([ | ||||
|                 'search', | ||||
|                 'payment_number', | ||||
|                 'payment_mode', | ||||
|                 'customer_id', | ||||
|                 'orderByField', | ||||
|                 'orderBy' | ||||
|             ])) | ||||
|             ->whereCompany($request->header('company')) | ||||
|             ->select('payments.*', 'users.name', 'invoices.invoice_number') | ||||
|             ->latest() | ||||
|             ->paginate($limit); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'payments' => $payments | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show the form for creating a new resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function create(Request $request) | ||||
|     { | ||||
|         $nextPaymentNumber = 'PAY-'.Payment::getNextPaymentNumber(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'customers' => User::where('role', 'customer') | ||||
|                 ->whereCompany($request->header('company')) | ||||
|                 ->get(), | ||||
|             'nextPaymentNumber' => $nextPaymentNumber | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function store(PaymentRequest $request) | ||||
|     { | ||||
|         $payment_date = Carbon::createFromFormat('d/m/Y', $request->payment_date); | ||||
|  | ||||
|         if ($request->has('invoice_id') && $request->invoice_id != null) { | ||||
|             $invoice = Invoice::find($request->invoice_id); | ||||
|             if ($invoice && $invoice->due_amount == $request->amount) { | ||||
|                 $invoice->status = Invoice::STATUS_COMPLETED; | ||||
|                 $invoice->paid_status = Invoice::STATUS_PAID; | ||||
|                 $invoice->due_amount = 0; | ||||
|             } elseif ($invoice && $invoice->due_amount != $request->amount) { | ||||
|                 $invoice->due_amount = (int)$invoice->due_amount - (int)$request->amount; | ||||
|                 if ($invoice->due_amount < 0) { | ||||
|                     return response()->json([ | ||||
|                         'error' => 'invalid_amount' | ||||
|                     ]); | ||||
|                 } | ||||
|                 $invoice->paid_status = Invoice::STATUS_PARTIALLY_PAID; | ||||
|             } | ||||
|             $invoice->save(); | ||||
|         } | ||||
|  | ||||
|         $payment = Payment::create([ | ||||
|             'payment_date' => $payment_date, | ||||
|             'payment_number' => $request->payment_number, | ||||
|             'user_id' => $request->user_id, | ||||
|             'company_id' => $request->header('company'), | ||||
|             'invoice_id' => $request->invoice_id, | ||||
|             'payment_mode' => $request->payment_mode, | ||||
|             'amount' => $request->amount, | ||||
|             'notes' => $request->notes, | ||||
|         ]); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'payment' => $payment, | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Display the specified resource. | ||||
|      * | ||||
|      * @param  int  $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function show($id) | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show the form for editing the specified resource. | ||||
|      * | ||||
|      * @param  int  $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function edit(Request $request, $id) | ||||
|     { | ||||
|         $payment = Payment::with('user', 'invoice')->find($id); | ||||
|  | ||||
|         $invoices = Invoice::where('paid_status', '<>', Invoice::STATUS_PAID) | ||||
|             ->where('user_id', $payment->user_id)->where('due_amount', '>', 0) | ||||
|             ->whereCompany($request->header('company')) | ||||
|             ->get(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'customers' => User::where('role', 'customer') | ||||
|                 ->whereCompany($request->header('company')) | ||||
|                 ->get(), | ||||
|             'nextPaymentNumber' => $payment->payment_number, | ||||
|             'payment' => $payment, | ||||
|             'invoices' => $invoices | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the specified resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @param  int  $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function update(PaymentRequest $request, $id) | ||||
|     { | ||||
|         $payment_date = Carbon::createFromFormat('d/m/Y', $request->payment_date); | ||||
|  | ||||
|         $payment = Payment::find($id); | ||||
|         $oldAmount = $payment->amount; | ||||
|  | ||||
|         if ($request->has('invoice_id') && $request->invoice_id && ($oldAmount != $request->amount)) { | ||||
|             $amount = (int)$request->amount - (int)$oldAmount; | ||||
|             $invoice = Invoice::find($request->invoice_id); | ||||
|             $invoice->due_amount = (int)$invoice->due_amount - (int)$amount; | ||||
|  | ||||
|             if ($invoice->due_amount < 0) { | ||||
|                 return response()->json([ | ||||
|                     'error' => 'invalid_amount' | ||||
|                 ]); | ||||
|             } | ||||
|  | ||||
|             if ($invoice->due_amount == 0) { | ||||
|                 $invoice->status = Invoice::STATUS_COMPLETED; | ||||
|                 $invoice->paid_status = Invoice::STATUS_PAID; | ||||
|             } else { | ||||
|                 $invoice->status = $invoice->getPreviousStatus(); | ||||
|                 $invoice->paid_status = Invoice::STATUS_PARTIALLY_PAID; | ||||
|             } | ||||
|  | ||||
|             $invoice->save(); | ||||
|         } | ||||
|  | ||||
|         $payment->payment_date = $payment_date; | ||||
|         $payment->payment_number = $request->payment_number; | ||||
|         $payment->user_id = $request->user_id; | ||||
|         $payment->invoice_id = $request->invoice_id; | ||||
|         $payment->payment_mode = $request->payment_mode; | ||||
|         $payment->amount = $request->amount; | ||||
|         $payment->notes = $request->notes; | ||||
|         $payment->save(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'payment' => $payment, | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
|      * @param  int  $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function destroy($id) | ||||
|     { | ||||
|         $payment = Payment::find($id); | ||||
|  | ||||
|         if ($payment->invoice_id != null) { | ||||
|             $invoice = Invoice::find($payment->invoice_id); | ||||
|             $invoice->due_amount = ((int)$invoice->due_amount + (int)$payment->amount); | ||||
|  | ||||
|             if ($invoice->due_amount == $invoice->total) { | ||||
|                 $invoice->paid_status = Invoice::STATUS_UNPAID; | ||||
|             } else { | ||||
|                 $invoice->paid_status = Invoice::STATUS_PARTIALLY_PAID; | ||||
|             } | ||||
|  | ||||
|             $invoice->status = $invoice->getPreviousStatus(); | ||||
|             $invoice->save(); | ||||
|         } | ||||
|  | ||||
|         $payment->delete(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function delete(Request $request) | ||||
|     { | ||||
|         foreach ($request->id as $id) { | ||||
|             $payment = Payment::find($id); | ||||
|  | ||||
|             if ($payment->invoice_id != null) { | ||||
|                 $invoice = Invoice::find($payment->invoice_id); | ||||
|                 $invoice->due_amount = ((int)$invoice->due_amount + (int)$payment->amount); | ||||
|  | ||||
|                 if ($invoice->due_amount == $invoice->total) { | ||||
|                     $invoice->paid_status = Invoice::STATUS_UNPAID; | ||||
|                 } else { | ||||
|                     $invoice->paid_status = Invoice::STATUS_PARTIALLY_PAID; | ||||
|                 } | ||||
|  | ||||
|                 $invoice->status = $invoice->getPreviousStatus(); | ||||
|                 $invoice->save(); | ||||
|             } | ||||
|  | ||||
|             $payment->delete(); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -1,293 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\User; | ||||
| use Crater\Invoice; | ||||
| use Crater\Company; | ||||
| use Crater\InvoiceItem; | ||||
| use Crater\Expense; | ||||
| use Crater\CompanySetting; | ||||
| use Crater\Tax; | ||||
| use PDF; | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
|  | ||||
| class ReportController extends Controller | ||||
| { | ||||
|     public function customersSalesReport($hash, Request $request) | ||||
|     { | ||||
|         $company = Company::where('unique_hash', $hash)->first(); | ||||
|  | ||||
|         $start = Carbon::createFromFormat('d/m/Y', $request->from_date); | ||||
|         $end = Carbon::createFromFormat('d/m/Y', $request->to_date); | ||||
|  | ||||
|         $customers = User::with(['invoices' => function ($query) use ($start, $end) { | ||||
|                 $query->whereBetween( | ||||
|                     'invoice_date', | ||||
|                     [$start->format('Y-m-d'), $end->format('Y-m-d')] | ||||
|                 ); | ||||
|             }]) | ||||
|             ->customer() | ||||
|             ->whereCompany($company->id) | ||||
|             ->applyInvoiceFilters($request->only(['from_date', 'to_date'])) | ||||
|             ->get(); | ||||
|  | ||||
|         $totalAmount = 0; | ||||
|         foreach ($customers as $customer) { | ||||
|             $customerTotalAmount = 0; | ||||
|             foreach ($customer->invoices as $invoice) { | ||||
|                 $customerTotalAmount += $invoice->total; | ||||
|             } | ||||
|             $customer->totalAmount = $customerTotalAmount; | ||||
|             $totalAmount += $customerTotalAmount; | ||||
|         } | ||||
|  | ||||
|         $dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id); | ||||
|         $from_date = Carbon::createFromFormat('d/m/Y', $request->from_date)->format($dateFormat); | ||||
|         $to_date = Carbon::createFromFormat('d/m/Y', $request->to_date)->format($dateFormat); | ||||
|  | ||||
|         $colors = [ | ||||
|             'primary_text_color', | ||||
|             'heading_text_color', | ||||
|             'section_heading_text_color', | ||||
|             'border_color', | ||||
|             'body_text_color', | ||||
|             'footer_text_color', | ||||
|             'footer_total_color', | ||||
|             'footer_bg_color', | ||||
|             'date_text_color' | ||||
|         ]; | ||||
|         $colorSettings = CompanySetting::whereIn('option', $colors) | ||||
|             ->whereCompany($company->id) | ||||
|             ->get(); | ||||
|  | ||||
|         view()->share([ | ||||
|             'customers' => $customers, | ||||
|             'totalAmount' => $totalAmount, | ||||
|             'colorSettings' => $colorSettings, | ||||
|             'company' => $company, | ||||
|             'from_date' => $from_date, | ||||
|             'to_date' => $to_date | ||||
|         ]); | ||||
|         $pdf = PDF::loadView('app.pdf.reports.sales-customers'); | ||||
|  | ||||
|         if ($request->has('download')) { | ||||
|             return $pdf->download(); | ||||
|         } | ||||
|  | ||||
|         return $pdf->stream(); | ||||
|     } | ||||
|  | ||||
|     public function itemsSalesReport($hash, Request $request) | ||||
|     { | ||||
|         $company = Company::where('unique_hash', $hash)->first(); | ||||
|  | ||||
|         $items = InvoiceItem::whereCompany($company->id) | ||||
|             ->applyInvoiceFilters($request->only(['from_date', 'to_date'])) | ||||
|             ->itemAttributes() | ||||
|             ->get(); | ||||
|  | ||||
|         $totalAmount = 0; | ||||
|         foreach ($items as $item) { | ||||
|             $totalAmount += $item->total_amount; | ||||
|         } | ||||
|  | ||||
|         $dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id); | ||||
|         $from_date = Carbon::createFromFormat('d/m/Y', $request->from_date)->format($dateFormat); | ||||
|         $to_date = Carbon::createFromFormat('d/m/Y', $request->to_date)->format($dateFormat); | ||||
|  | ||||
|         $colors = [ | ||||
|             'primary_text_color', | ||||
|             'heading_text_color', | ||||
|             'section_heading_text_color', | ||||
|             'border_color', | ||||
|             'body_text_color', | ||||
|             'footer_text_color', | ||||
|             'footer_total_color', | ||||
|             'footer_bg_color', | ||||
|             'date_text_color' | ||||
|         ]; | ||||
|         $colorSettings = CompanySetting::whereIn('option', $colors) | ||||
|             ->whereCompany($company->id) | ||||
|             ->get(); | ||||
|  | ||||
|         view()->share([ | ||||
|             'items' => $items, | ||||
|             'colorSettings' => $colorSettings, | ||||
|             'totalAmount' => $totalAmount, | ||||
|             'company' => $company, | ||||
|             'from_date' => $from_date, | ||||
|             'to_date' => $to_date | ||||
|         ]); | ||||
|         $pdf = PDF::loadView('app.pdf.reports.sales-items'); | ||||
|  | ||||
|         if ($request->has('download')) { | ||||
|             return $pdf->download(); | ||||
|         } | ||||
|  | ||||
|         return $pdf->stream(); | ||||
|     } | ||||
|  | ||||
|     public function expensesReport($hash, Request $request) | ||||
|     { | ||||
|         $company = Company::where('unique_hash', $hash)->first(); | ||||
|  | ||||
|         $expenseCategories = Expense::with('category') | ||||
|             ->whereCompany($company->id) | ||||
|             ->applyFilters($request->only(['from_date', 'to_date'])) | ||||
|             ->expensesAttributes() | ||||
|             ->get(); | ||||
|  | ||||
|         $totalAmount = 0; | ||||
|         foreach ($expenseCategories as $category) { | ||||
|             $totalAmount += $category->total_amount; | ||||
|         } | ||||
|  | ||||
|         $dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id); | ||||
|         $from_date = Carbon::createFromFormat('d/m/Y', $request->from_date)->format($dateFormat); | ||||
|         $to_date = Carbon::createFromFormat('d/m/Y', $request->to_date)->format($dateFormat); | ||||
|  | ||||
|         $colors = [ | ||||
|             'primary_text_color', | ||||
|             'heading_text_color', | ||||
|             'section_heading_text_color', | ||||
|             'border_color', | ||||
|             'body_text_color', | ||||
|             'footer_text_color', | ||||
|             'footer_total_color', | ||||
|             'footer_bg_color', | ||||
|             'date_text_color' | ||||
|         ]; | ||||
|         $colorSettings = CompanySetting::whereIn('option', $colors) | ||||
|             ->whereCompany($company->id) | ||||
|             ->get(); | ||||
|  | ||||
|         view()->share([ | ||||
|             'expenseCategories' => $expenseCategories, | ||||
|             'colorSettings' => $colorSettings, | ||||
|             'totalExpense' => $totalAmount, | ||||
|             'company' => $company, | ||||
|             'from_date' => $from_date, | ||||
|             'to_date' => $to_date | ||||
|         ]); | ||||
|         $pdf = PDF::loadView('app.pdf.reports.expenses'); | ||||
|  | ||||
|         if ($request->has('download')) { | ||||
|             return $pdf->download(); | ||||
|         } | ||||
|  | ||||
|         return $pdf->stream(); | ||||
|     } | ||||
|  | ||||
|     public function taxSummery($hash, Request $request) | ||||
|     { | ||||
|         $company = Company::where('unique_hash', $hash)->first(); | ||||
|  | ||||
|         $taxTypes = Tax::with('taxType', 'invoice', 'invoiceItem') | ||||
|             ->whereCompany($company->id) | ||||
|             ->whereInvoicesFilters($request->only(['from_date', 'to_date'])) | ||||
|             ->taxAttributes() | ||||
|             ->get(); | ||||
|  | ||||
|         $totalAmount = 0; | ||||
|         foreach ($taxTypes as $taxType) { | ||||
|             $totalAmount += $taxType->total_tax_amount; | ||||
|         } | ||||
|  | ||||
|         $dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id); | ||||
|         $from_date = Carbon::createFromFormat('d/m/Y', $request->from_date)->format($dateFormat); | ||||
|         $to_date = Carbon::createFromFormat('d/m/Y', $request->to_date)->format($dateFormat); | ||||
|  | ||||
|         $colors = [ | ||||
|             'primary_text_color', | ||||
|             'heading_text_color', | ||||
|             'section_heading_text_color', | ||||
|             'border_color', | ||||
|             'body_text_color', | ||||
|             'footer_text_color', | ||||
|             'footer_total_color', | ||||
|             'footer_bg_color', | ||||
|             'date_text_color' | ||||
|         ]; | ||||
|  | ||||
|         $colorSettings = CompanySetting::whereIn('option', $colors) | ||||
|             ->whereCompany($company->id) | ||||
|             ->get(); | ||||
|  | ||||
|         view()->share([ | ||||
|             'taxTypes' => $taxTypes, | ||||
|             'totalTaxAmount' => $totalAmount, | ||||
|             'colorSettings' => $colorSettings, | ||||
|             'company' => $company, | ||||
|             'from_date' => $from_date, | ||||
|             'to_date' => $to_date | ||||
|         ]); | ||||
|  | ||||
|         $pdf = PDF::loadView('app.pdf.reports.tax-summary'); | ||||
|  | ||||
|         if ($request->has('download')) { | ||||
|             return $pdf->download(); | ||||
|         } | ||||
|  | ||||
|         return $pdf->stream(); | ||||
|     } | ||||
|  | ||||
|     public function profitLossReport($hash, Request $request) | ||||
|     { | ||||
|         $company = Company::where('unique_hash', $hash)->first(); | ||||
|  | ||||
|         $invoicesAmount = Invoice::whereCompany($company->id) | ||||
|             ->applyFilters($request->only(['from_date', 'to_date'])) | ||||
|             ->wherePaidStatus(Invoice::STATUS_PAID) | ||||
|             ->sum('total'); | ||||
|  | ||||
|         $expenseCategories = Expense::with('category') | ||||
|             ->whereCompany($company->id) | ||||
|             ->applyFilters($request->only(['from_date', 'to_date'])) | ||||
|             ->expensesAttributes() | ||||
|             ->get(); | ||||
|  | ||||
|         $totalAmount = 0; | ||||
|         foreach ($expenseCategories as $category) { | ||||
|             $totalAmount += $category->total_amount; | ||||
|         } | ||||
|  | ||||
|         $dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id); | ||||
|         $from_date = Carbon::createFromFormat('d/m/Y', $request->from_date)->format($dateFormat); | ||||
|         $to_date = Carbon::createFromFormat('d/m/Y', $request->to_date)->format($dateFormat); | ||||
|  | ||||
|         $colors = [ | ||||
|             'primary_text_color', | ||||
|             'heading_text_color', | ||||
|             'section_heading_text_color', | ||||
|             'border_color', | ||||
|             'body_text_color', | ||||
|             'footer_text_color', | ||||
|             'footer_total_color', | ||||
|             'footer_bg_color', | ||||
|             'date_text_color' | ||||
|         ]; | ||||
|         $colorSettings = CompanySetting::whereIn('option', $colors) | ||||
|             ->whereCompany($company->id) | ||||
|             ->get(); | ||||
|  | ||||
|         view()->share([ | ||||
|             'company' => $company, | ||||
|             'income' => $invoicesAmount, | ||||
|             'expenseCategories' => $expenseCategories, | ||||
|             'totalExpense' => $totalAmount, | ||||
|             'colorSettings' => $colorSettings, | ||||
|             'company' => $company, | ||||
|             'from_date' => $from_date, | ||||
|             'to_date' => $to_date | ||||
|         ]); | ||||
|         $pdf = PDF::loadView('app.pdf.reports.profit-loss'); | ||||
|  | ||||
|         if ($request->has('download')) { | ||||
|             return $pdf->download(); | ||||
|         } | ||||
|  | ||||
|         return $pdf->stream(); | ||||
|     } | ||||
| } | ||||
| @ -1,19 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\Setting; | ||||
|  | ||||
| class SettingsController extends Controller | ||||
| { | ||||
|     public function getAppVersion(Request $request) | ||||
|     { | ||||
|         $version = Setting::getSetting('version'); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'version' => $version, | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,126 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Crater\TaxType; | ||||
| use Crater\User; | ||||
| use Crater\Http\Requests\TaxTypeRequest; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class TaxTypeController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $taxTypes = TaxType::whereCompany($request->header('company')) | ||||
|             ->latest() | ||||
|             ->get(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'taxTypes' => $taxTypes | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show the form for creating a new resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function create() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function store(TaxTypeRequest $request) | ||||
|     { | ||||
|         $taxType = new TaxType(); | ||||
|         $taxType->name = $request->name; | ||||
|         $taxType->percent = $request->percent; | ||||
|         $taxType->description = $request->description; | ||||
|         if ($request->has('compound_tax')) { | ||||
|             $taxType->compound_tax = $request->compound_tax; | ||||
|         } | ||||
|         $taxType->company_id = $request->header('company'); | ||||
|         $taxType->save(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'taxType' => $taxType, | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Display the specified resource. | ||||
|      * | ||||
|      * @param  \Crater\TaxType  $taxType | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function show(TaxType $taxType) | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show the form for editing the specified resource. | ||||
|      * | ||||
|      * @param  \Crater\TaxType  $taxType | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function edit(TaxType $taxType) | ||||
|     { | ||||
|         return response()->json([ | ||||
|             'taxType' => $taxType | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the specified resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @param  \Crater\TaxType  $taxType | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function update(TaxTypeRequest $request, TaxType $taxType) | ||||
|     { | ||||
|         $taxType->name = $request->name; | ||||
|         $taxType->percent = $request->percent; | ||||
|         $taxType->description = $request->description; | ||||
|         if ($request->has('collective_tax')) { | ||||
|             $taxType->collective_tax = $request->collective_tax; | ||||
|         } | ||||
|         $taxType->compound_tax = $request->compound_tax; | ||||
|         $taxType->save(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'taxType' => $taxType, | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
|      * @param  \Crater\TaxType  $taxType | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function destroy(TaxType $taxType) | ||||
|     { | ||||
|         if ($taxType->taxes() && $taxType->taxes()->count() > 0) { | ||||
|             return response()->json([ | ||||
|                 'success' => false | ||||
|             ]); | ||||
|         } | ||||
|         $taxType->delete(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -1,35 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\Space\Updater; | ||||
| use Crater\Space\SiteApi; | ||||
|  | ||||
| class UpdateController extends Controller | ||||
| { | ||||
|     public function update(Request $request) | ||||
|     { | ||||
|         set_time_limit(600); // 10 minutes | ||||
|  | ||||
|         $json = Updater::update($request->installed, $request->version); | ||||
|  | ||||
|         return response()->json($json); | ||||
|     } | ||||
|  | ||||
|     public function finishUpdate(Request $request) | ||||
|     { | ||||
|         $json = Updater::finishUpdate($request->installed, $request->version); | ||||
|  | ||||
|         return response()->json($json); | ||||
|     } | ||||
|  | ||||
|     public function checkLatestVersion(Request $request) | ||||
|     { | ||||
|         set_time_limit(600); // 10 minutes | ||||
|  | ||||
|         $json = Updater::checkForUpdate(); | ||||
|  | ||||
|         return response()->json($json); | ||||
|     } | ||||
| } | ||||
| @ -1,74 +0,0 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Crater\Http\Requests; | ||||
| use Crater\User; | ||||
| use Crater\Currency; | ||||
| use Crater\Setting; | ||||
| use Crater\Item; | ||||
| use Crater\TaxType; | ||||
| use DB; | ||||
| use Carbon\Carbon; | ||||
| use Auth; | ||||
| use Crater\Company; | ||||
| use Crater\CompanySetting; | ||||
|  | ||||
| class UsersController extends Controller | ||||
| { | ||||
|     public function getBootstrap(Request $request) | ||||
|     { | ||||
|         $user = Auth::user(); | ||||
|  | ||||
|         $company = $request->header('company') ?? 1; | ||||
|  | ||||
|         $customers = User::with('billingAddress', 'shippingAddress') | ||||
|             ->customer() | ||||
|             ->whereCompany($company) | ||||
|             ->latest() | ||||
|             ->get(); | ||||
|  | ||||
|         $currencies = Currency::latest()->get(); | ||||
|  | ||||
|         $default_language = CompanySetting::getSetting('language', $company); | ||||
|  | ||||
|         $default_currency = Currency::findOrFail( | ||||
|             CompanySetting::getSetting('currency', $company) | ||||
|         ); | ||||
|  | ||||
|         $moment_date_format = CompanySetting::getSetting( | ||||
|             'moment_date_format', | ||||
|             $request->header('company') | ||||
|         ); | ||||
|  | ||||
|         $fiscal_year = CompanySetting::getSetting( | ||||
|             'fiscal_year', | ||||
|             $request->header('company') | ||||
|         ); | ||||
|  | ||||
|         $items = Item::all(); | ||||
|  | ||||
|         $taxTypes = TaxType::latest()->get(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'user' => $user, | ||||
|             'customers' => $customers, | ||||
|             'currencies' => $currencies, | ||||
|             'default_currency' => $default_currency, | ||||
|             'default_language' => $default_language, | ||||
|             'company' => $user->company, | ||||
|             'companies' => Company::all(), | ||||
|             'items' => $items, | ||||
|             'taxTypes' => $taxTypes, | ||||
|             'moment_date_format' => $moment_date_format, | ||||
|             'fiscal_year' => $fiscal_year, | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function ping() | ||||
|     { | ||||
|         return response()->json([ | ||||
|             'success' => 'crater-self-hosted' | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,40 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Auth; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Providers\RouteServiceProvider; | ||||
| use Illuminate\Foundation\Auth\ConfirmsPasswords; | ||||
|  | ||||
| class ConfirmPasswordController extends Controller | ||||
| { | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Confirm Password Controller | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | This controller is responsible for handling password confirmations and | ||||
|     | uses a simple trait to include the behavior. You're free to explore | ||||
|     | this trait and override any functions that require customization. | ||||
|     | | ||||
|     */ | ||||
|  | ||||
|     use ConfirmsPasswords; | ||||
|  | ||||
|     /** | ||||
|      * Where to redirect users when the intended url fails. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $redirectTo = RouteServiceProvider::HOME; | ||||
|  | ||||
|     /** | ||||
|      * Create a new controller instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->middleware('auth'); | ||||
|     } | ||||
| } | ||||
| @ -1,5 +1,6 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers\Auth; | ||||
| 
 | ||||
| namespace Crater\Http\Controllers\V1\Admin\Auth; | ||||
| 
 | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Illuminate\Foundation\Auth\SendsPasswordResetEmails; | ||||
| @ -20,16 +21,6 @@ class ForgotPasswordController extends Controller | ||||
| 
 | ||||
|     use SendsPasswordResetEmails; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new controller instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         // $this->middleware('guest');
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the response for a successful password reset link. | ||||
|      * | ||||
							
								
								
									
										40
									
								
								app/Http/Controllers/V1/Admin/Auth/LoginController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								app/Http/Controllers/V1/Admin/Auth/LoginController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Auth; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Providers\RouteServiceProvider; | ||||
| use Illuminate\Foundation\Auth\AuthenticatesUsers; | ||||
|  | ||||
| class LoginController extends Controller | ||||
| { | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Login Controller | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | This controller handles authenticating users for the application and | ||||
|     | redirecting them to your home screen. The controller uses a trait | ||||
|     | to conveniently provide its functionality to your applications. | ||||
|     | | ||||
|     */ | ||||
|  | ||||
|     use AuthenticatesUsers; | ||||
|  | ||||
|     /** | ||||
|      * Where to redirect users after login. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $redirectTo = RouteServiceProvider::HOME; | ||||
|  | ||||
|     /** | ||||
|      * Create a new controller instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->middleware('guest')->except('logout'); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										72
									
								
								app/Http/Controllers/V1/Admin/Auth/RegisterController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								app/Http/Controllers/V1/Admin/Auth/RegisterController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Auth; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\User; | ||||
| use Crater\Providers\RouteServiceProvider; | ||||
| use Illuminate\Foundation\Auth\RegistersUsers; | ||||
| use Illuminate\Support\Facades\Validator; | ||||
|  | ||||
| class RegisterController extends Controller | ||||
| { | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Register Controller | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | This controller handles the registration of new users as well as their | ||||
|     | validation and creation. By default this controller uses a trait to | ||||
|     | provide this functionality without requiring any additional code. | ||||
|     | | ||||
|     */ | ||||
|  | ||||
|     use RegistersUsers; | ||||
|  | ||||
|     /** | ||||
|      * Where to redirect users after registration. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $redirectTo = RouteServiceProvider::HOME; | ||||
|  | ||||
|     /** | ||||
|      * Create a new controller instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->middleware('guest'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a validator for an incoming registration request. | ||||
|      * | ||||
|      * @param  array  $data | ||||
|      * @return \Illuminate\Contracts\Validation\Validator | ||||
|      */ | ||||
|     protected function validator(array $data) | ||||
|     { | ||||
|         return Validator::make($data, [ | ||||
|             'name' => ['required', 'string', 'max:255'], | ||||
|             'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], | ||||
|             'password' => ['required', 'string', 'min:8', 'confirmed'], | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new user instance after a valid registration. | ||||
|      * | ||||
|      * @param  array  $data | ||||
|      * @return \App\User | ||||
|      */ | ||||
|     protected function create(array $data) | ||||
|     { | ||||
|         return User::create([ | ||||
|             'name' => $data['name'], | ||||
|             'email' => $data['email'], | ||||
|             'password' => $data['password'], | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -1,11 +1,13 @@ | ||||
| <?php | ||||
| namespace Crater\Http\Controllers\Auth; | ||||
| 
 | ||||
| namespace Crater\Http\Controllers\V1\Admin\Auth; | ||||
| 
 | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Providers\RouteServiceProvider; | ||||
| use Illuminate\Auth\Events\PasswordReset; | ||||
| use Illuminate\Foundation\Auth\ResetsPasswords; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Str; | ||||
| use Illuminate\Auth\Events\PasswordReset; | ||||
| 
 | ||||
| class ResetPasswordController extends Controller | ||||
| { | ||||
| @ -27,17 +29,7 @@ class ResetPasswordController extends Controller | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $redirectTo = '/'; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new controller instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         // $this->middleware('guest');
 | ||||
|     } | ||||
|     protected $redirectTo = RouteServiceProvider::HOME; | ||||
| 
 | ||||
|     /** | ||||
|      * Get the response for a successful password reset. | ||||
| @ -49,7 +41,7 @@ class ResetPasswordController extends Controller | ||||
|     protected function sendResetResponse(Request $request, $response) | ||||
|     { | ||||
|         return response()->json([ | ||||
|             'message' => 'Password reset successfully.' | ||||
|             'message' => 'Password reset successfully.', | ||||
|         ]); | ||||
|     } | ||||
| 
 | ||||
| @ -62,7 +54,7 @@ class ResetPasswordController extends Controller | ||||
|      */ | ||||
|     protected function resetPassword($user, $password) | ||||
|     { | ||||
|         $user->password = \Hash::make($password); | ||||
|         $user->password = $password; | ||||
| 
 | ||||
|         $user->setRememberToken(Str::random(60)); | ||||
| 
 | ||||
| @ -0,0 +1,42 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Auth; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Providers\RouteServiceProvider; | ||||
| use Illuminate\Foundation\Auth\VerifiesEmails; | ||||
|  | ||||
| class VerificationController extends Controller | ||||
| { | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Email Verification Controller | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | This controller is responsible for handling email verification for any | ||||
|     | user that recently registered with the application. Emails may also | ||||
|     | be re-sent if the user didn't receive the original email message. | ||||
|     | | ||||
|     */ | ||||
|  | ||||
|     use VerifiesEmails; | ||||
|  | ||||
|     /** | ||||
|      * Where to redirect users after verification. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $redirectTo = RouteServiceProvider::HOME; | ||||
|  | ||||
|     /** | ||||
|      * Create a new controller instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->middleware('auth'); | ||||
|         $this->middleware('signed')->only('verify'); | ||||
|         $this->middleware('throttle:6,1')->only('verify', 'resend'); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										22
									
								
								app/Http/Controllers/V1/Admin/Backup/ApiController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/Http/Controllers/V1/Admin/Backup/ApiController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| <?php | ||||
|  | ||||
| // Implementation taken from nova-backup-tool - https://github.com/spatie/nova-backup-tool/ | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Backup; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Illuminate\Http\JsonResponse; | ||||
|  | ||||
| class ApiController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function respondSuccess(): JsonResponse | ||||
|     { | ||||
|         return response()->json([ | ||||
|             'success' => true, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										99
									
								
								app/Http/Controllers/V1/Admin/Backup/BackupsController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								app/Http/Controllers/V1/Admin/Backup/BackupsController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | ||||
| <?php | ||||
|  | ||||
| // Implementation taken from nova-backup-tool - https://github.com/spatie/nova-backup-tool/ | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Backup; | ||||
|  | ||||
| use Crater\Jobs\CreateBackupJob; | ||||
| use Crater\Rules\Backup\PathToZip; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
| use Spatie\Backup\BackupDestination\Backup; | ||||
| use Spatie\Backup\BackupDestination\BackupDestination; | ||||
| use Spatie\Backup\Helpers\Format; | ||||
|  | ||||
| class BackupsController extends ApiController | ||||
| { | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $this->authorize('manage backups'); | ||||
|  | ||||
|         $configuredBackupDisks = config('backup.backup.destination.disks'); | ||||
|  | ||||
|         try { | ||||
|             $backupDestination = BackupDestination::create(config('filesystems.default'), config('backup.backup.name')); | ||||
|  | ||||
|             $backups = Cache::remember("backups-{$request->file_disk_id}", now()->addSeconds(4), function () use ($backupDestination) { | ||||
|                 return $backupDestination | ||||
|                     ->backups() | ||||
|                     ->map(function (Backup $backup) { | ||||
|                         return [ | ||||
|                             'path' => $backup->path(), | ||||
|                             'created_at' => $backup->date()->format('Y-m-d H:i:s'), | ||||
|                             'size' => Format::humanReadableSize($backup->size()), | ||||
|                         ]; | ||||
|                     }) | ||||
|                     ->toArray(); | ||||
|             }); | ||||
|  | ||||
|             return response()->json([ | ||||
|                 'backups' => $backups, | ||||
|                 'disks' => $configuredBackupDisks, | ||||
|             ]); | ||||
|         } catch (\Exception $e) { | ||||
|             return response()->json([ | ||||
|                 'backups' => [], | ||||
|                 'error' => 'invalid_disk_credentials', | ||||
|                 'error_message' => $e->getMessage(), | ||||
|                 'disks' => $configuredBackupDisks, | ||||
|             ]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function store(Request $request) | ||||
|     { | ||||
|         $this->authorize('manage backups'); | ||||
|  | ||||
|         dispatch(new CreateBackupJob($request->all()))->onQueue(config('backup.queue.name')); | ||||
|  | ||||
|         return $this->respondSuccess(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function destroy($disk, Request $request) | ||||
|     { | ||||
|         $this->authorize('manage backups'); | ||||
|  | ||||
|         $validated = $request->validate([ | ||||
|             'path' => ['required', new PathToZip()], | ||||
|         ]); | ||||
|  | ||||
|         $backupDestination = BackupDestination::create(config('filesystems.default'), config('backup.backup.name')); | ||||
|  | ||||
|         $backupDestination | ||||
|             ->backups() | ||||
|             ->first(function (Backup $backup) use ($validated) { | ||||
|                 return $backup->path() === $validated['path']; | ||||
|             }) | ||||
|             ->delete(); | ||||
|  | ||||
|         return $this->respondSuccess(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,59 @@ | ||||
| <?php | ||||
|  | ||||
| // Implementation taken from nova-backup-tool - https://github.com/spatie/nova-backup-tool/ | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Backup; | ||||
|  | ||||
| use Crater\Rules\Backup\PathToZip; | ||||
| use Illuminate\Http\Request; | ||||
| use Spatie\Backup\BackupDestination\Backup; | ||||
| use Spatie\Backup\BackupDestination\BackupDestination; | ||||
| use Symfony\Component\HttpFoundation\Response; | ||||
| use Symfony\Component\HttpFoundation\StreamedResponse; | ||||
|  | ||||
| class DownloadBackupController extends ApiController | ||||
| { | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         $this->authorize('manage backups'); | ||||
|  | ||||
|         $validated = $request->validate([ | ||||
|             'path' => ['required', new PathToZip()], | ||||
|         ]); | ||||
|  | ||||
|         $backupDestination = BackupDestination::create(config('filesystems.default'), config('backup.backup.name')); | ||||
|  | ||||
|         $backup = $backupDestination->backups()->first(function (Backup $backup) use ($validated) { | ||||
|             return $backup->path() === $validated['path']; | ||||
|         }); | ||||
|  | ||||
|         if (! $backup) { | ||||
|             return response('Backup not found', Response::HTTP_UNPROCESSABLE_ENTITY); | ||||
|         } | ||||
|  | ||||
|         return $this->respondWithBackupStream($backup); | ||||
|     } | ||||
|  | ||||
|     public function respondWithBackupStream(Backup $backup): StreamedResponse | ||||
|     { | ||||
|         $fileName = pathinfo($backup->path(), PATHINFO_BASENAME); | ||||
|  | ||||
|         $downloadHeaders = [ | ||||
|             'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0', | ||||
|             'Content-Type' => 'application/zip', | ||||
|             'Content-Length' => $backup->size(), | ||||
|             'Content-Disposition' => 'attachment; filename="'.$fileName.'"', | ||||
|             'Pragma' => 'public', | ||||
|         ]; | ||||
|  | ||||
|         return response()->stream(function () use ($backup) { | ||||
|             $stream = $backup->stream(); | ||||
|  | ||||
|             fpassthru($stream); | ||||
|  | ||||
|             if (is_resource($stream)) { | ||||
|                 fclose($stream); | ||||
|             } | ||||
|         }, 200, $downloadHeaders); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,85 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Company; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Requests\CompaniesRequest; | ||||
| use Crater\Http\Resources\CompanyResource; | ||||
| use Crater\Models\Company; | ||||
| use Crater\Models\User; | ||||
| use Illuminate\Http\Request; | ||||
| use Silber\Bouncer\BouncerFacade; | ||||
| use Vinkla\Hashids\Facades\Hashids; | ||||
|  | ||||
| class CompaniesController extends Controller | ||||
| { | ||||
|     public function store(CompaniesRequest $request) | ||||
|     { | ||||
|         $this->authorize('create company'); | ||||
|  | ||||
|         $user = $request->user(); | ||||
|  | ||||
|         $company = Company::create($request->getCompanyPayload()); | ||||
|         $company->unique_hash = Hashids::connection(Company::class)->encode($company->id); | ||||
|         $company->save(); | ||||
|         $company->setupDefaultData(); | ||||
|         $user->companies()->attach($company->id); | ||||
|         $user->assign('super admin'); | ||||
|  | ||||
|         if ($request->address) { | ||||
|             $company->address()->create($request->address); | ||||
|         } | ||||
|  | ||||
|         return new CompanyResource($company); | ||||
|     } | ||||
|  | ||||
|     public function destroy(Request $request) | ||||
|     { | ||||
|         $company = Company::find($request->header('company')); | ||||
|  | ||||
|         $this->authorize('delete company', $company); | ||||
|  | ||||
|         $user = $request->user(); | ||||
|  | ||||
|         if ($request->name !== $company->name) { | ||||
|             return respondJson('company_name_must_match_with_given_name', 'Company name must match with given name'); | ||||
|         } | ||||
|  | ||||
|         if ($user->loadCount('companies')->companies_count <= 1) { | ||||
|             return respondJson('You_cannot_delete_all_companies', 'You cannot delete all companies'); | ||||
|         } | ||||
|  | ||||
|         $company->deleteCompany($user); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function transferOwnership(Request $request, User $user) | ||||
|     { | ||||
|         $company = Company::find($request->header('company')); | ||||
|         $this->authorize('transfer company ownership', $company); | ||||
|  | ||||
|         if ($user->hasCompany($company->id)) { | ||||
|             return response()->json([ | ||||
|                 'success' => false, | ||||
|                 'message' => 'User does not belongs to this company.' | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         $company->update(['owner_id' => $user->id]); | ||||
|         BouncerFacade::sync($user)->roles(['super admin']); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function getUserCompanies(Request $request) | ||||
|     { | ||||
|         $companies = $request->user()->companies; | ||||
|  | ||||
|         return CompanyResource::collection($companies); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										24
									
								
								app/Http/Controllers/V1/Admin/Company/CompanyController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/Http/Controllers/V1/Admin/Company/CompanyController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Company; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Resources\CompanyResource; | ||||
| use Crater\Models\Company; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class CompanyController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         $company = Company::find($request->header('company')); | ||||
|  | ||||
|         return new CompanyResource($company); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Config; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class FiscalYearsController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         return response()->json([ | ||||
|             'fiscal_years' => config('crater.fiscal_years'), | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										22
									
								
								app/Http/Controllers/V1/Admin/Config/LanguagesController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/Http/Controllers/V1/Admin/Config/LanguagesController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Config; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class LanguagesController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         return response()->json([ | ||||
|             'languages' => config('crater.languages'), | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Config; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class RetrospectiveEditsController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         return response()->json([ | ||||
|             'retrospective_edits' => config('crater.retrospective_edits'), | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,96 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\CustomField; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Requests\CustomFieldRequest; | ||||
| use Crater\Http\Resources\CustomFieldResource; | ||||
| use Crater\Models\CustomField; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class CustomFieldsController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $this->authorize('viewAny', CustomField::class); | ||||
|  | ||||
|         $limit = $request->has('limit') ? $request->limit : 5; | ||||
|  | ||||
|         $customFields = CustomField::applyFilters($request->all()) | ||||
|             ->whereCompany() | ||||
|             ->latest() | ||||
|             ->paginateData($limit); | ||||
|  | ||||
|         return CustomFieldResource::collection($customFields); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\CustomFieldRequest  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function store(CustomFieldRequest $request) | ||||
|     { | ||||
|         $this->authorize('create', CustomField::class); | ||||
|  | ||||
|         $customField = CustomField::createCustomField($request); | ||||
|  | ||||
|         return new CustomFieldResource($customField); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Display the specified resource. | ||||
|      * | ||||
|      * @param  int  $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function show(CustomField $customField) | ||||
|     { | ||||
|         $this->authorize('view', $customField); | ||||
|  | ||||
|         return new CustomFieldResource($customField); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the specified resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @param  int  $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function update(CustomFieldRequest $request, CustomField $customField) | ||||
|     { | ||||
|         $this->authorize('update', $customField); | ||||
|  | ||||
|         $customField->updateCustomField($request); | ||||
|  | ||||
|         return new CustomFieldResource($customField); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
|      * @param  int  $id | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function destroy(CustomField $customField) | ||||
|     { | ||||
|         $this->authorize('delete', $customField); | ||||
|  | ||||
|         if ($customField->customFieldValues()->exists()) { | ||||
|             $customField->customFieldValues()->delete(); | ||||
|         } | ||||
|  | ||||
|         $customField->forceDelete(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,142 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Customer; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Resources\CustomerResource; | ||||
| use Crater\Models\CompanySetting; | ||||
| use Crater\Models\Customer; | ||||
| use Crater\Models\Expense; | ||||
| use Crater\Models\Invoice; | ||||
| use Crater\Models\Payment; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class CustomerStatsController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request, Customer $customer) | ||||
|     { | ||||
|         $this->authorize('view', $customer); | ||||
|  | ||||
|         $i = 0; | ||||
|         $months = []; | ||||
|         $invoiceTotals = []; | ||||
|         $expenseTotals = []; | ||||
|         $receiptTotals = []; | ||||
|         $netProfits = []; | ||||
|         $monthCounter = 0; | ||||
|         $fiscalYear = CompanySetting::getSetting('fiscal_year', $request->header('company')); | ||||
|         $startDate = Carbon::now(); | ||||
|         $start = Carbon::now(); | ||||
|         $end = Carbon::now(); | ||||
|         $terms = explode('-', $fiscalYear); | ||||
|  | ||||
|         if ($terms[0] <= $start->month) { | ||||
|             $startDate->month($terms[0])->startOfMonth(); | ||||
|             $start->month($terms[0])->startOfMonth(); | ||||
|             $end->month($terms[0])->endOfMonth(); | ||||
|         } else { | ||||
|             $startDate->subYear()->month($terms[0])->startOfMonth(); | ||||
|             $start->subYear()->month($terms[0])->startOfMonth(); | ||||
|             $end->subYear()->month($terms[0])->endOfMonth(); | ||||
|         } | ||||
|  | ||||
|         if ($request->has('previous_year')) { | ||||
|             $startDate->subYear()->startOfMonth(); | ||||
|             $start->subYear()->startOfMonth(); | ||||
|             $end->subYear()->endOfMonth(); | ||||
|         } | ||||
|         while ($monthCounter < 12) { | ||||
|             array_push( | ||||
|                 $invoiceTotals, | ||||
|                 Invoice::whereBetween( | ||||
|                     'invoice_date', | ||||
|                     [$start->format('Y-m-d'), $end->format('Y-m-d')] | ||||
|                 ) | ||||
|                     ->whereCompany() | ||||
|                     ->whereCustomer($customer->id) | ||||
|                     ->sum('total') ?? 0 | ||||
|             ); | ||||
|             array_push( | ||||
|                 $expenseTotals, | ||||
|                 Expense::whereBetween( | ||||
|                     'expense_date', | ||||
|                     [$start->format('Y-m-d'), $end->format('Y-m-d')] | ||||
|                 ) | ||||
|                     ->whereCompany() | ||||
|                     ->whereUser($customer->id) | ||||
|                     ->sum('amount') ?? 0 | ||||
|             ); | ||||
|             array_push( | ||||
|                 $receiptTotals, | ||||
|                 Payment::whereBetween( | ||||
|                     'payment_date', | ||||
|                     [$start->format('Y-m-d'), $end->format('Y-m-d')] | ||||
|                 ) | ||||
|                     ->whereCompany() | ||||
|                     ->whereCustomer($customer->id) | ||||
|                     ->sum('amount') ?? 0 | ||||
|             ); | ||||
|             array_push( | ||||
|                 $netProfits, | ||||
|                 ($receiptTotals[$i] - $expenseTotals[$i]) | ||||
|             ); | ||||
|             $i++; | ||||
|             array_push($months, $start->format('M')); | ||||
|             $monthCounter++; | ||||
|             $end->startOfMonth(); | ||||
|             $start->addMonth()->startOfMonth(); | ||||
|             $end->addMonth()->endOfMonth(); | ||||
|         } | ||||
|  | ||||
|         $start->subMonth()->endOfMonth(); | ||||
|  | ||||
|         $salesTotal = Invoice::whereBetween( | ||||
|             'invoice_date', | ||||
|             [$startDate->format('Y-m-d'), $start->format('Y-m-d')] | ||||
|         ) | ||||
|             ->whereCompany() | ||||
|             ->whereCustomer($customer->id) | ||||
|             ->sum('total'); | ||||
|         $totalReceipts = Payment::whereBetween( | ||||
|             'payment_date', | ||||
|             [$startDate->format('Y-m-d'), $start->format('Y-m-d')] | ||||
|         ) | ||||
|             ->whereCompany() | ||||
|             ->whereCustomer($customer->id) | ||||
|             ->sum('amount'); | ||||
|         $totalExpenses = Expense::whereBetween( | ||||
|             'expense_date', | ||||
|             [$startDate->format('Y-m-d'), $start->format('Y-m-d')] | ||||
|         ) | ||||
|             ->whereCompany() | ||||
|             ->whereUser($customer->id) | ||||
|             ->sum('amount'); | ||||
|         $netProfit = (int) $totalReceipts - (int) $totalExpenses; | ||||
|  | ||||
|         $chartData = [ | ||||
|             'months' => $months, | ||||
|             'invoiceTotals' => $invoiceTotals, | ||||
|             'expenseTotals' => $expenseTotals, | ||||
|             'receiptTotals' => $receiptTotals, | ||||
|             'netProfit' => $netProfit, | ||||
|             'netProfits' => $netProfits, | ||||
|             'salesTotal' => $salesTotal, | ||||
|             'totalReceipts' => $totalReceipts, | ||||
|             'totalExpenses' => $totalExpenses, | ||||
|         ]; | ||||
|  | ||||
|         $customer = Customer::find($customer->id); | ||||
|  | ||||
|         return (new CustomerResource($customer)) | ||||
|             ->additional(['meta' => [ | ||||
|                 'chartData' => $chartData | ||||
|             ]]); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										108
									
								
								app/Http/Controllers/V1/Admin/Customer/CustomersController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								app/Http/Controllers/V1/Admin/Customer/CustomersController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Customer; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Requests; | ||||
| use Crater\Http\Requests\DeleteCustomersRequest; | ||||
| use Crater\Http\Resources\CustomerResource; | ||||
| use Crater\Models\Customer; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\DB; | ||||
|  | ||||
| class CustomersController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $this->authorize('viewAny', Customer::class); | ||||
|  | ||||
|         $limit = $request->has('limit') ? $request->limit : 10; | ||||
|  | ||||
|         $customers = Customer::with('creator') | ||||
|             ->whereCompany() | ||||
|             ->applyFilters($request->all()) | ||||
|             ->select( | ||||
|                 'customers.*', | ||||
|                 DB::raw('sum(invoices.base_due_amount) as base_due_amount'), | ||||
|                 DB::raw('sum(invoices.due_amount) as due_amount'), | ||||
|             ) | ||||
|             ->groupBy('customers.id') | ||||
|             ->leftJoin('invoices', 'customers.id', '=', 'invoices.customer_id') | ||||
|             ->paginateData($limit); | ||||
|  | ||||
|         return (CustomerResource::collection($customers)) | ||||
|             ->additional(['meta' => [ | ||||
|                 'customer_total_count' => Customer::whereCompany()->count(), | ||||
|             ]]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function store(Requests\CustomerRequest $request) | ||||
|     { | ||||
|         $this->authorize('create', Customer::class); | ||||
|  | ||||
|         $customer = Customer::createCustomer($request); | ||||
|  | ||||
|         return new CustomerResource($customer); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Display the specified resource. | ||||
|      * | ||||
|      * @param  Customer $customer | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function show(Customer $customer) | ||||
|     { | ||||
|         $this->authorize('view', $customer); | ||||
|  | ||||
|         return new CustomerResource($customer); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the specified resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @param  \Crater\Models\Customer $customer | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function update(Requests\CustomerRequest $request, Customer $customer) | ||||
|     { | ||||
|         $this->authorize('update', $customer); | ||||
|  | ||||
|         $customer = Customer::updateCustomer($request, $customer); | ||||
|  | ||||
|         if (is_string($customer)) { | ||||
|             return respondJson('you_cannot_edit_currency', 'Cannot change currency once transactions created'); | ||||
|         } | ||||
|  | ||||
|         return new CustomerResource($customer); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove a list of Customers along side all their resources (ie. Estimates, Invoices, Payments and Addresses) | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function delete(DeleteCustomersRequest $request) | ||||
|     { | ||||
|         $this->authorize('delete multiple customers'); | ||||
|  | ||||
|         Customer::deleteCustomers($request->ids); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										165
									
								
								app/Http/Controllers/V1/Admin/Dashboard/DashboardController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								app/Http/Controllers/V1/Admin/Dashboard/DashboardController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,165 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Dashboard; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\Company; | ||||
| use Crater\Models\CompanySetting; | ||||
| use Crater\Models\Customer; | ||||
| use Crater\Models\Estimate; | ||||
| use Crater\Models\Expense; | ||||
| use Crater\Models\Invoice; | ||||
| use Crater\Models\Payment; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class DashboardController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         $company = Company::find($request->header('company')); | ||||
|  | ||||
|         $this->authorize('view dashboard', $company); | ||||
|  | ||||
|         $invoice_totals = []; | ||||
|         $expense_totals = []; | ||||
|         $receipt_totals = []; | ||||
|         $net_income_totals = []; | ||||
|  | ||||
|         $i = 0; | ||||
|         $months = []; | ||||
|         $monthCounter = 0; | ||||
|         $fiscalYear = CompanySetting::getSetting('fiscal_year', $request->header('company')); | ||||
|         $startDate = Carbon::now(); | ||||
|         $start = Carbon::now(); | ||||
|         $end = Carbon::now(); | ||||
|         $terms = explode('-', $fiscalYear); | ||||
|  | ||||
|         if ($terms[0] <= $start->month) { | ||||
|             $startDate->month($terms[0])->startOfMonth(); | ||||
|             $start->month($terms[0])->startOfMonth(); | ||||
|             $end->month($terms[0])->endOfMonth(); | ||||
|         } else { | ||||
|             $startDate->subYear()->month($terms[0])->startOfMonth(); | ||||
|             $start->subYear()->month($terms[0])->startOfMonth(); | ||||
|             $end->subYear()->month($terms[0])->endOfMonth(); | ||||
|         } | ||||
|  | ||||
|         if ($request->has('previous_year')) { | ||||
|             $startDate->subYear()->startOfMonth(); | ||||
|             $start->subYear()->startOfMonth(); | ||||
|             $end->subYear()->endOfMonth(); | ||||
|         } | ||||
|  | ||||
|         while ($monthCounter < 12) { | ||||
|             array_push( | ||||
|                 $invoice_totals, | ||||
|                 Invoice::whereBetween( | ||||
|                     'invoice_date', | ||||
|                     [$start->format('Y-m-d'), $end->format('Y-m-d')] | ||||
|                 ) | ||||
|                 ->whereCompany() | ||||
|                 ->sum('base_total') | ||||
|             ); | ||||
|             array_push( | ||||
|                 $expense_totals, | ||||
|                 Expense::whereBetween( | ||||
|                     'expense_date', | ||||
|                     [$start->format('Y-m-d'), $end->format('Y-m-d')] | ||||
|                 ) | ||||
|                 ->whereCompany() | ||||
|                 ->sum('base_amount') | ||||
|             ); | ||||
|             array_push( | ||||
|                 $receipt_totals, | ||||
|                 Payment::whereBetween( | ||||
|                     'payment_date', | ||||
|                     [$start->format('Y-m-d'), $end->format('Y-m-d')] | ||||
|                 ) | ||||
|                 ->whereCompany() | ||||
|                 ->sum('base_amount') | ||||
|             ); | ||||
|             array_push( | ||||
|                 $net_income_totals, | ||||
|                 ($receipt_totals[$i] - $expense_totals[$i]) | ||||
|             ); | ||||
|             $i++; | ||||
|             array_push($months, $start->format('M')); | ||||
|             $monthCounter++; | ||||
|             $end->startOfMonth(); | ||||
|             $start->addMonth()->startOfMonth(); | ||||
|             $end->addMonth()->endOfMonth(); | ||||
|         } | ||||
|  | ||||
|         $start->subMonth()->endOfMonth(); | ||||
|  | ||||
|         $total_sales = Invoice::whereBetween( | ||||
|             'invoice_date', | ||||
|             [$startDate->format('Y-m-d'), $start->format('Y-m-d')] | ||||
|         ) | ||||
|             ->whereCompany() | ||||
|             ->sum('base_total'); | ||||
|  | ||||
|         $total_receipts = Payment::whereBetween( | ||||
|             'payment_date', | ||||
|             [$startDate->format('Y-m-d'), $start->format('Y-m-d')] | ||||
|         ) | ||||
|             ->whereCompany() | ||||
|             ->sum('base_amount'); | ||||
|  | ||||
|         $total_expenses = Expense::whereBetween( | ||||
|             'expense_date', | ||||
|             [$startDate->format('Y-m-d'), $start->format('Y-m-d')] | ||||
|         ) | ||||
|             ->whereCompany() | ||||
|             ->sum('base_amount'); | ||||
|  | ||||
|         $total_net_income = (int)$total_receipts - (int)$total_expenses; | ||||
|  | ||||
|         $chart_data = [ | ||||
|             'months' => $months, | ||||
|             'invoice_totals' => $invoice_totals, | ||||
|             'expense_totals' => $expense_totals, | ||||
|             'receipt_totals' => $receipt_totals, | ||||
|             'net_income_totals' => $net_income_totals, | ||||
|         ]; | ||||
|  | ||||
|         $total_customer_count = Customer::whereCompany()->count(); | ||||
|         $total_invoice_count = Invoice::whereCompany() | ||||
|             ->count(); | ||||
|         $total_estimate_count = Estimate::whereCompany()->count(); | ||||
|         $total_amount_due = Invoice::whereCompany() | ||||
|             ->sum('base_due_amount'); | ||||
|  | ||||
|         $recent_due_invoices = Invoice::with('customer') | ||||
|             ->whereCompany() | ||||
|             ->where('base_due_amount', '>', 0) | ||||
|             ->take(5) | ||||
|             ->latest() | ||||
|             ->get(); | ||||
|         $recent_estimates = Estimate::with('customer')->whereCompany()->take(5)->latest()->get(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'total_amount_due' => $total_amount_due, | ||||
|             'total_customer_count' => $total_customer_count, | ||||
|             'total_invoice_count' => $total_invoice_count, | ||||
|             'total_estimate_count' => $total_estimate_count, | ||||
|  | ||||
|             'recent_due_invoices' => $recent_due_invoices, | ||||
|             'recent_estimates' => $recent_estimates, | ||||
|  | ||||
|             'chart_data' => $chart_data, | ||||
|  | ||||
|             'total_sales' => $total_sales, | ||||
|             'total_receipts' => $total_receipts, | ||||
|             'total_expenses' => $total_expenses, | ||||
|             'total_net_income' => $total_net_income, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,28 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Estimate; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\Estimate; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class ChangeEstimateStatusController extends Controller | ||||
| { | ||||
|     /** | ||||
|     * Handle the incoming request. | ||||
|     * | ||||
|     * @param  \Illuminate\Http\Request  $request | ||||
|     * @param  Estimate $estimate | ||||
|     * @return \Illuminate\Http\Response | ||||
|     */ | ||||
|     public function __invoke(Request $request, Estimate $estimate) | ||||
|     { | ||||
|         $this->authorize('send estimate', $estimate); | ||||
|  | ||||
|         $estimate->update($request->only('status')); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,132 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Estimate; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Resources\InvoiceResource; | ||||
| use Crater\Models\CompanySetting; | ||||
| use Crater\Models\Estimate; | ||||
| use Crater\Models\Invoice; | ||||
| use Crater\Services\SerialNumberFormatter; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\Auth; | ||||
| use Vinkla\Hashids\Facades\Hashids; | ||||
|  | ||||
| class ConvertEstimateController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @param  \Crater\Models\Estimate $estimate | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request, Estimate $estimate, Invoice $invoice) | ||||
|     { | ||||
|         $this->authorize('create', Invoice::class); | ||||
|  | ||||
|         $estimate->load(['items', 'items.taxes', 'customer', 'taxes']); | ||||
|  | ||||
|         $invoice_date = Carbon::now(); | ||||
|         $due_date = null; | ||||
|  | ||||
|         $dueDateEnabled = CompanySetting::getSetting( | ||||
|             'invoice_set_due_date_automatically', | ||||
|             $request->header('company') | ||||
|         ); | ||||
|  | ||||
|         if ($dueDateEnabled === 'YES') { | ||||
|             $dueDateDays = CompanySetting::getSetting( | ||||
|                 'invoice_due_date_days', | ||||
|                 $request->header('company') | ||||
|             ); | ||||
|             $due_date = Carbon::now()->addDays($dueDateDays)->format('Y-m-d'); | ||||
|         } | ||||
|  | ||||
|         $serial = (new SerialNumberFormatter()) | ||||
|             ->setModel($invoice) | ||||
|             ->setCompany($estimate->company_id) | ||||
|             ->setCustomer($estimate->customer_id) | ||||
|             ->setNextNumbers(); | ||||
|  | ||||
|         $templateName = $estimate->getInvoiceTemplateName(); | ||||
|  | ||||
|         $exchange_rate = $estimate->exchange_rate; | ||||
|  | ||||
|         $invoice = Invoice::create([ | ||||
|             'creator_id' => Auth::id(), | ||||
|             'invoice_date' => $invoice_date->format('Y-m-d'), | ||||
|             'due_date' => $due_date, | ||||
|             'invoice_number' => $serial->getNextNumber(), | ||||
|             'sequence_number' => $serial->nextSequenceNumber, | ||||
|             'customer_sequence_number' => $serial->nextCustomerSequenceNumber, | ||||
|             'reference_number' => $serial->getNextNumber(), | ||||
|             'customer_id' => $estimate->customer_id, | ||||
|             'company_id' => $request->header('company'), | ||||
|             'template_name' => $templateName, | ||||
|             'status' => Invoice::STATUS_DRAFT, | ||||
|             'paid_status' => Invoice::STATUS_UNPAID, | ||||
|             'sub_total' => $estimate->sub_total, | ||||
|             'discount' => $estimate->discount, | ||||
|             'discount_type' => $estimate->discount_type, | ||||
|             'discount_val' => $estimate->discount_val, | ||||
|             'total' => $estimate->total, | ||||
|             'due_amount' => $estimate->total, | ||||
|             'tax_per_item' => $estimate->tax_per_item, | ||||
|             'discount_per_item' => $estimate->discount_per_item, | ||||
|             'tax' => $estimate->tax, | ||||
|             'notes' => $estimate->notes, | ||||
|             'exchange_rate' => $exchange_rate, | ||||
|             'base_discount_val' => $estimate->discount_val * $exchange_rate, | ||||
|             'base_sub_total' => $estimate->sub_total * $exchange_rate, | ||||
|             'base_total' => $estimate->total * $exchange_rate, | ||||
|             'base_tax' => $estimate->tax * $exchange_rate, | ||||
|             'currency_id' => $estimate->currency_id, | ||||
|         ]); | ||||
|  | ||||
|         $invoice->unique_hash = Hashids::connection(Invoice::class)->encode($invoice->id); | ||||
|         $invoice->save(); | ||||
|         $invoiceItems = $estimate->items->toArray(); | ||||
|  | ||||
|         foreach ($invoiceItems as $invoiceItem) { | ||||
|             $invoiceItem['company_id'] = $request->header('company'); | ||||
|             $invoiceItem['name'] = $invoiceItem['name']; | ||||
|             $estimateItem['exchange_rate'] = $exchange_rate; | ||||
|             $estimateItem['base_price'] = $invoiceItem['price'] * $exchange_rate; | ||||
|             $estimateItem['base_discount_val'] = $invoiceItem['discount_val'] * $exchange_rate; | ||||
|             $estimateItem['base_tax'] = $invoiceItem['tax'] * $exchange_rate; | ||||
|             $estimateItem['base_total'] = $invoiceItem['total'] * $exchange_rate; | ||||
|  | ||||
|             $item = $invoice->items()->create($invoiceItem); | ||||
|  | ||||
|             if (array_key_exists('taxes', $invoiceItem) && $invoiceItem['taxes']) { | ||||
|                 foreach ($invoiceItem['taxes'] as $tax) { | ||||
|                     $tax['company_id'] = $request->header('company'); | ||||
|  | ||||
|                     if ($tax['amount']) { | ||||
|                         $item->taxes()->create($tax); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($estimate->taxes) { | ||||
|             foreach ($estimate->taxes->toArray() as $tax) { | ||||
|                 $tax['company_id'] = $request->header('company'); | ||||
|                 $tax['exchange_rate'] = $exchange_rate; | ||||
|                 $tax['base_amount'] = $tax['amount'] * $exchange_rate; | ||||
|                 $tax['currency_id'] = $estimate->currency_id; | ||||
|                 unset($tax['estimate_id']); | ||||
|  | ||||
|                 $invoice->taxes()->create($tax); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $estimate->checkForEstimateConvertAction(); | ||||
|  | ||||
|         $invoice = Invoice::find($invoice->id); | ||||
|  | ||||
|         return new InvoiceResource($invoice); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,27 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Estimate; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\Estimate; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class EstimateTemplatesController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         $this->authorize('viewAny', Estimate::class); | ||||
|  | ||||
|         $estimateTemplates = Estimate::estimateTemplates(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'estimateTemplates' => $estimateTemplates | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,77 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Estimate; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Requests\DeleteEstimatesRequest; | ||||
| use Crater\Http\Requests\EstimatesRequest; | ||||
| use Crater\Http\Resources\EstimateResource; | ||||
| use Crater\Jobs\GenerateEstimatePdfJob; | ||||
| use Crater\Models\Estimate; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class EstimatesController extends Controller | ||||
| { | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $this->authorize('viewAny', Estimate::class); | ||||
|  | ||||
|         $limit = $request->has('limit') ? $request->limit : 10; | ||||
|  | ||||
|         $estimates = Estimate::whereCompany() | ||||
|             ->join('customers', 'customers.id', '=', 'estimates.customer_id') | ||||
|             ->applyFilters($request->all()) | ||||
|             ->select('estimates.*', 'customers.name') | ||||
|             ->latest() | ||||
|             ->paginateData($limit); | ||||
|  | ||||
|         return (EstimateResource::collection($estimates)) | ||||
|             ->additional(['meta' => [ | ||||
|                 'estimate_total_count' => Estimate::whereCompany()->count(), | ||||
|             ]]); | ||||
|     } | ||||
|  | ||||
|     public function store(EstimatesRequest $request) | ||||
|     { | ||||
|         $this->authorize('create', Estimate::class); | ||||
|  | ||||
|         $estimate = Estimate::createEstimate($request); | ||||
|  | ||||
|         if ($request->has('estimateSend')) { | ||||
|             $estimate->send($request->title, $request->body); | ||||
|         } | ||||
|  | ||||
|         GenerateEstimatePdfJob::dispatch($estimate); | ||||
|  | ||||
|         return new EstimateResource($estimate); | ||||
|     } | ||||
|  | ||||
|     public function show(Request $request, Estimate $estimate) | ||||
|     { | ||||
|         $this->authorize('view', $estimate); | ||||
|  | ||||
|         return new EstimateResource($estimate); | ||||
|     } | ||||
|  | ||||
|     public function update(EstimatesRequest $request, Estimate $estimate) | ||||
|     { | ||||
|         $this->authorize('update', $estimate); | ||||
|  | ||||
|         $estimate = $estimate->updateEstimate($request); | ||||
|  | ||||
|         GenerateEstimatePdfJob::dispatch($estimate, true); | ||||
|  | ||||
|         return new EstimateResource($estimate); | ||||
|     } | ||||
|  | ||||
|     public function delete(DeleteEstimatesRequest $request) | ||||
|     { | ||||
|         $this->authorize('delete multiple estimates'); | ||||
|  | ||||
|         Estimate::destroy($request->ids); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,25 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Estimate; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Requests\SendEstimatesRequest; | ||||
| use Crater\Models\Estimate; | ||||
|  | ||||
| class SendEstimateController extends Controller | ||||
| { | ||||
|     /** | ||||
|     * Handle the incoming request. | ||||
|     * | ||||
|     * @param  \Crater\Http\Requests\SendEstimatesRequest  $request | ||||
|     * @return \Illuminate\Http\JsonResponse | ||||
|     */ | ||||
|     public function __invoke(SendEstimatesRequest $request, Estimate $estimate) | ||||
|     { | ||||
|         $this->authorize('send estimate', $estimate); | ||||
|  | ||||
|         $response = $estimate->send($request->all()); | ||||
|  | ||||
|         return response()->json($response); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Estimate; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Requests\SendEstimatesRequest; | ||||
| use Crater\Models\Estimate; | ||||
| use Illuminate\Mail\Markdown; | ||||
|  | ||||
| class SendEstimatePreviewController extends Controller | ||||
| { | ||||
|     /** | ||||
|     * Handle the incoming request. | ||||
|     * | ||||
|     * @param  \Crater\Http\Requests\SendEstimatesRequest  $request | ||||
|     * @return \Illuminate\Http\JsonResponse | ||||
|     */ | ||||
|     public function __invoke(SendEstimatesRequest $request, Estimate $estimate) | ||||
|     { | ||||
|         $this->authorize('send estimate', $estimate); | ||||
|  | ||||
|         $markdown = new Markdown(view(), config('mail.markdown')); | ||||
|  | ||||
|         return $markdown->render('emails.send.estimate', ['data' => $estimate->sendEstimateData($request->all())]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,117 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\ExchangeRate; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Requests\ExchangeRateProviderRequest; | ||||
| use Crater\Http\Resources\ExchangeRateProviderResource; | ||||
| use Crater\Models\ExchangeRateProvider; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class ExchangeRateProviderController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $this->authorize('viewAny', ExchangeRateProvider::class); | ||||
|  | ||||
|         $limit = $request->has('limit') ? $request->limit : 5; | ||||
|  | ||||
|         $exchangeRateProviders = ExchangeRateProvider::whereCompany()->paginate($limit); | ||||
|  | ||||
|         return ExchangeRateProviderResource::collection($exchangeRateProviders); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function store(ExchangeRateProviderRequest $request) | ||||
|     { | ||||
|         $this->authorize('create', ExchangeRateProvider::class); | ||||
|  | ||||
|         $query = ExchangeRateProvider::checkActiveCurrencies($request); | ||||
|  | ||||
|         if (count($query) !== 0) { | ||||
|             return respondJson('currency_used', 'Currency used.'); | ||||
|         } | ||||
|  | ||||
|         $checkConverterApi = ExchangeRateProvider::checkExchangeRateProviderStatus($request); | ||||
|  | ||||
|         if ($checkConverterApi->status() == 200) { | ||||
|             $exchangeRateProvider = ExchangeRateProvider::createFromRequest($request); | ||||
|  | ||||
|             return new ExchangeRateProviderResource($exchangeRateProvider); | ||||
|         } | ||||
|  | ||||
|         return $checkConverterApi; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Display the specified resource. | ||||
|      * | ||||
|      * @param  \Crater\Models\ExchangeRateProvider  $exchangeRateProvider | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function show(ExchangeRateProvider $exchangeRateProvider) | ||||
|     { | ||||
|         $this->authorize('view', $exchangeRateProvider); | ||||
|  | ||||
|         return new ExchangeRateProviderResource($exchangeRateProvider); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the specified resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @param  \Crater\Models\ExchangeRateProvider  $exchangeRateProvider | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function update(ExchangeRateProviderRequest $request, ExchangeRateProvider $exchangeRateProvider) | ||||
|     { | ||||
|         $this->authorize('update', $exchangeRateProvider); | ||||
|  | ||||
|         $query = $exchangeRateProvider->checkUpdateActiveCurrencies($request); | ||||
|  | ||||
|         if (count($query) !== 0) { | ||||
|             return respondJson('currency_used', 'Currency used.'); | ||||
|         } | ||||
|  | ||||
|         $checkConverterApi = ExchangeRateProvider::checkExchangeRateProviderStatus($request); | ||||
|  | ||||
|         if ($checkConverterApi->status() == 200) { | ||||
|             $exchangeRateProvider->updateFromRequest($request); | ||||
|  | ||||
|             return new ExchangeRateProviderResource($exchangeRateProvider); | ||||
|         } | ||||
|  | ||||
|         return $checkConverterApi; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
|      * @param  \Crater\Models\ExchangeRateProvider  $exchangeRateProvider | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function destroy(ExchangeRateProvider $exchangeRateProvider) | ||||
|     { | ||||
|         $this->authorize('delete', $exchangeRateProvider); | ||||
|  | ||||
|         if ($exchangeRateProvider->active == true) { | ||||
|             return respondJson('provider_active', 'Provider Active.'); | ||||
|         } | ||||
|  | ||||
|         $exchangeRateProvider->delete(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,35 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\ExchangeRate; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\Currency; | ||||
| use Crater\Models\ExchangeRateProvider; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class GetActiveProviderController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request, Currency $currency) | ||||
|     { | ||||
|         $query = ExchangeRateProvider::whereCompany()->whereJsonContains('currencies', $currency->code) | ||||
|                 ->where('active', true) | ||||
|                 ->get(); | ||||
|  | ||||
|         if (count($query) !== 0) { | ||||
|             return response()->json([ | ||||
|                 'success' => true, | ||||
|                 'message' => 'provider_active', | ||||
|             ], 200); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'error' => 'no_active_provider', | ||||
|         ], 200); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,57 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\ExchangeRate; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\CompanySetting; | ||||
| use Crater\Models\Currency; | ||||
| use Crater\Models\ExchangeRateLog; | ||||
| use Crater\Models\ExchangeRateProvider; | ||||
| use Crater\Traits\ExchangeRateProvidersTrait; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Arr; | ||||
|  | ||||
| class GetExchangeRateController extends Controller | ||||
| { | ||||
|     use ExchangeRateProvidersTrait; | ||||
|  | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request, Currency $currency) | ||||
|     { | ||||
|         $settings = CompanySetting::getSettings(['currency'], $request->header('company')); | ||||
|         $baseCurrency = Currency::findOrFail($settings['currency']); | ||||
|  | ||||
|         $query = ExchangeRateProvider::whereJsonContains('currencies', $currency->code) | ||||
|                 ->where('active', true) | ||||
|                 ->get() | ||||
|                 ->toArray(); | ||||
|  | ||||
|         $exchange_rate = ExchangeRateLog::where('base_currency_id', $currency->id) | ||||
|                 ->where('currency_id', $baseCurrency->id) | ||||
|                 ->orderBy('created_at', 'desc') | ||||
|                 ->value('exchange_rate'); | ||||
|  | ||||
|         if ($query) { | ||||
|             $filter = Arr::only($query[0], ['key', 'driver', 'driver_config']); | ||||
|             $exchange_rate_value = $this->getExchangeRate($filter, $currency->code, $baseCurrency->code); | ||||
|  | ||||
|             if ($exchange_rate_value->status() == 200) { | ||||
|                 return $exchange_rate_value; | ||||
|             } | ||||
|         } | ||||
|         if ($exchange_rate) { | ||||
|             return response()->json([ | ||||
|                 'exchangeRate' => [$exchange_rate], | ||||
|             ], 200); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'error' => 'no_exchange_rate_available', | ||||
|         ], 200); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\ExchangeRate; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\ExchangeRateProvider; | ||||
| use Crater\Traits\ExchangeRateProvidersTrait; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class GetSupportedCurrenciesController extends Controller | ||||
| { | ||||
|     use ExchangeRateProvidersTrait; | ||||
|  | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         $this->authorize('viewAny', ExchangeRateProvider::class); | ||||
|  | ||||
|         return $this->getSupportedCurrencies($request); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,55 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\ExchangeRate; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\ExchangeRateProvider; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class GetUsedCurrenciesController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         $this->authorize('viewAny', ExchangeRateProvider::class); | ||||
|  | ||||
|         $providerId = $request->provider_id; | ||||
|  | ||||
|         $activeExchangeRateProviders = ExchangeRateProvider::where('active', true) | ||||
|             ->whereCompany() | ||||
|             ->when($providerId, function ($query) use ($providerId) { | ||||
|                 return $query->where('id', '<>', $providerId); | ||||
|             }) | ||||
|             ->pluck('currencies'); | ||||
|         $activeExchangeRateProvider = []; | ||||
|  | ||||
|         foreach ($activeExchangeRateProviders as $data) { | ||||
|             if (is_array($data)) { | ||||
|                 for ($limit = 0; $limit < count($data); $limit++) { | ||||
|                     $activeExchangeRateProvider[] = $data[$limit]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $allExchangeRateProviders = ExchangeRateProvider::whereCompany()->pluck('currencies'); | ||||
|         $allExchangeRateProvider = []; | ||||
|  | ||||
|         foreach ($allExchangeRateProviders as $data) { | ||||
|             if (is_array($data)) { | ||||
|                 for ($limit = 0; $limit < count($data); $limit++) { | ||||
|                     $allExchangeRateProvider[] = $data[$limit]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'allUsedCurrencies' => $allExchangeRateProvider ? $allExchangeRateProvider : [], | ||||
|             'activeUsedCurrencies' => $activeExchangeRateProvider ? $activeExchangeRateProvider : [], | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,96 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Expense; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Requests\ExpenseCategoryRequest; | ||||
| use Crater\Http\Resources\ExpenseCategoryResource; | ||||
| use Crater\Models\ExpenseCategory; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class ExpenseCategoriesController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $this->authorize('viewAny', ExpenseCategory::class); | ||||
|  | ||||
|         $limit = $request->has('limit') ? $request->limit : 5; | ||||
|  | ||||
|         $categories = ExpenseCategory::applyFilters($request->all()) | ||||
|             ->whereCompany() | ||||
|             ->latest() | ||||
|             ->paginateData($limit); | ||||
|  | ||||
|         return ExpenseCategoryResource::collection($categories); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function store(ExpenseCategoryRequest $request) | ||||
|     { | ||||
|         $this->authorize('create', ExpenseCategory::class); | ||||
|  | ||||
|         $category = ExpenseCategory::create($request->getExpenseCategoryPayload()); | ||||
|  | ||||
|         return new ExpenseCategoryResource($category); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Display the specified resource. | ||||
|      * | ||||
|      * @param  \Crater\Models\ExpenseCategory $category | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function show(ExpenseCategory $category) | ||||
|     { | ||||
|         $this->authorize('view', $category); | ||||
|  | ||||
|         return new ExpenseCategoryResource($category); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the specified resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @param  \Crater\Models\ExpenseCategory $ExpenseCategory | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function update(ExpenseCategoryRequest $request, ExpenseCategory $category) | ||||
|     { | ||||
|         $this->authorize('update', $category); | ||||
|  | ||||
|         $category->update($request->getExpenseCategoryPayload()); | ||||
|  | ||||
|         return new ExpenseCategoryResource($category); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
|      * @param  \Crater\ExpensesCategory $category | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function destroy(ExpenseCategory $category) | ||||
|     { | ||||
|         $this->authorize('delete', $category); | ||||
|  | ||||
|         if ($category->expenses() && $category->expenses()->count() > 0) { | ||||
|             return respondJson('expense_attached', 'Expense Attached'); | ||||
|         } | ||||
|  | ||||
|         $category->delete(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										93
									
								
								app/Http/Controllers/V1/Admin/Expense/ExpensesController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								app/Http/Controllers/V1/Admin/Expense/ExpensesController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Expense; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Requests\DeleteExpensesRequest; | ||||
| use Crater\Http\Requests\ExpenseRequest; | ||||
| use Crater\Http\Resources\ExpenseResource; | ||||
| use Crater\Models\Expense; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class ExpensesController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $this->authorize('viewAny', Expense::class); | ||||
|  | ||||
|         $limit = $request->has('limit') ? $request->limit : 10; | ||||
|  | ||||
|         $expenses = Expense::with('category', 'creator', 'fields') | ||||
|             ->whereCompany() | ||||
|             ->leftJoin('customers', 'customers.id', '=', 'expenses.customer_id') | ||||
|             ->join('expense_categories', 'expense_categories.id', '=', 'expenses.expense_category_id') | ||||
|             ->applyFilters($request->all()) | ||||
|             ->select('expenses.*', 'expense_categories.name', 'customers.name as user_name') | ||||
|             ->paginateData($limit); | ||||
|  | ||||
|         return (ExpenseResource::collection($expenses)) | ||||
|             ->additional(['meta' => [ | ||||
|                 'expense_total_count' => Expense::whereCompany()->count(), | ||||
|             ]]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function store(ExpenseRequest $request) | ||||
|     { | ||||
|         $this->authorize('create', Expense::class); | ||||
|  | ||||
|         $expense = Expense::createExpense($request); | ||||
|  | ||||
|         return new ExpenseResource($expense); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Display the specified resource. | ||||
|      * | ||||
|      * @param  \Crater\Models\Expense $expense | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function show(Expense $expense) | ||||
|     { | ||||
|         $this->authorize('view', $expense); | ||||
|  | ||||
|         return new ExpenseResource($expense); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the specified resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @param  \Crater\Models\Expense $expense | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function update(ExpenseRequest $request, Expense $expense) | ||||
|     { | ||||
|         $this->authorize('update', $expense); | ||||
|  | ||||
|         $expense->updateExpense($request); | ||||
|  | ||||
|         return new ExpenseResource($expense); | ||||
|     } | ||||
|  | ||||
|     public function delete(DeleteExpensesRequest $request) | ||||
|     { | ||||
|         $this->authorize('delete multiple expenses'); | ||||
|  | ||||
|         Expense::destroy($request->ids); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Expense; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\Expense; | ||||
|  | ||||
| class ShowReceiptController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Retrieve details of an expense receipt from storage. | ||||
|      * | ||||
|      * @param   \Crater\Models\Expense $expense | ||||
|      * @return  \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function __invoke(Expense $expense) | ||||
|     { | ||||
|         $this->authorize('view', $expense); | ||||
|  | ||||
|         if ($expense) { | ||||
|             $media = $expense->getFirstMedia('receipts'); | ||||
|  | ||||
|             if ($media) { | ||||
|                 return response()->file($media->getPath()); | ||||
|             } | ||||
|  | ||||
|             return respondJson('receipt_does_not_exist', 'Receipt does not exist.'); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,38 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Expense; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\Expense; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class UploadReceiptController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Upload the expense receipts to storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @param  Expense $expense | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function __invoke(Request $request, Expense $expense) | ||||
|     { | ||||
|         $this->authorize('update', $expense); | ||||
|  | ||||
|         $data = json_decode($request->attachment_receipt); | ||||
|  | ||||
|         if ($data) { | ||||
|             if ($request->type === 'edit') { | ||||
|                 $expense->clearMediaCollection('receipts'); | ||||
|             } | ||||
|  | ||||
|             $expense->addMediaFromBase64($data->data) | ||||
|                 ->usingFileName($data->name) | ||||
|                 ->toMediaCollection('receipts'); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => 'Expense receipts uploaded successfully', | ||||
|         ], 200); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,63 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\General; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Resources\CompanyResource; | ||||
| use Crater\Http\Resources\UserResource; | ||||
| use Crater\Models\Company; | ||||
| use Crater\Models\CompanySetting; | ||||
| use Crater\Models\Currency; | ||||
| use Crater\Traits\GeneratesMenuTrait; | ||||
| use Illuminate\Http\Request; | ||||
| use Silber\Bouncer\BouncerFacade; | ||||
|  | ||||
| class BootstrapController extends Controller | ||||
| { | ||||
|     use GeneratesMenuTrait; | ||||
|  | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         $current_user = $request->user(); | ||||
|         $current_user_settings = $current_user->getAllSettings(); | ||||
|  | ||||
|         $main_menu = $this->generateMenu('main_menu', $current_user); | ||||
|  | ||||
|         $setting_menu = $this->generateMenu('setting_menu', $current_user); | ||||
|  | ||||
|         $companies = $current_user->companies; | ||||
|  | ||||
|         $current_company = Company::find($request->header('company')); | ||||
|  | ||||
|         if ((! $current_company) || ($current_company && ! $current_user->hasCompany($current_company->id))) { | ||||
|             $current_company = $current_user->companies()->first(); | ||||
|         } | ||||
|  | ||||
|         $current_company_settings = CompanySetting::getAllSettings($current_company->id); | ||||
|  | ||||
|         $current_company_currency = $current_company_settings->has('currency') | ||||
|             ? Currency::find($current_company_settings->get('currency')) | ||||
|             : Currency::first(); | ||||
|  | ||||
|         BouncerFacade::refreshFor($current_user); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'current_user' => new UserResource($current_user), | ||||
|             'current_user_settings' => $current_user_settings, | ||||
|             'current_user_abilities' => $current_user->getAbilities(), | ||||
|             'companies' => CompanyResource::collection($companies), | ||||
|             'current_company' => new CompanyResource($current_company), | ||||
|             'current_company_settings' => $current_company_settings, | ||||
|             'current_company_currency' => $current_company_currency, | ||||
|             'config' => config('crater'), | ||||
|             'main_menu' => $main_menu, | ||||
|             'setting_menu' => $setting_menu, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,128 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\General; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Requests\BulkExchangeRateRequest; | ||||
| use Crater\Models\CompanySetting; | ||||
| use Crater\Models\Estimate; | ||||
| use Crater\Models\Invoice; | ||||
| use Crater\Models\Payment; | ||||
| use Crater\Models\Tax; | ||||
|  | ||||
| class BulkExchangeRateController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(BulkExchangeRateRequest $request) | ||||
|     { | ||||
|         $bulkExchangeRate = CompanySetting::getSetting('bulk_exchange_rate_configured', $request->header('company')); | ||||
|  | ||||
|         if ($bulkExchangeRate == 'NO') { | ||||
|             if ($request->currencies) { | ||||
|                 foreach ($request->currencies as $currency) { | ||||
|                     $currency['exchange_rate'] = $currency['exchange_rate'] ?? 1; | ||||
|  | ||||
|                     $invoices = Invoice::where('currency_id', $currency['id'])->get(); | ||||
|  | ||||
|                     if ($invoices) { | ||||
|                         foreach ($invoices as $invoice) { | ||||
|                             $invoice->update([ | ||||
|                                 'exchange_rate' => $currency['exchange_rate'], | ||||
|                                 'base_discount_val' => $invoice->sub_total * $currency['exchange_rate'], | ||||
|                                 'base_sub_total' => $invoice->sub_total * $currency['exchange_rate'], | ||||
|                                 'base_total' => $invoice->total * $currency['exchange_rate'], | ||||
|                                 'base_tax' => $invoice->tax * $currency['exchange_rate'], | ||||
|                                 'base_due_amount' => $invoice->due_amount * $currency['exchange_rate'] | ||||
|                             ]); | ||||
|  | ||||
|                             $this->items($invoice); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     $estimates = Estimate::where('currency_id', $currency['id'])->get(); | ||||
|  | ||||
|                     if ($estimates) { | ||||
|                         foreach ($estimates as $estimate) { | ||||
|                             $estimate->update([ | ||||
|                                 'exchange_rate' => $currency['exchange_rate'], | ||||
|                                 'base_discount_val' => $estimate->sub_total * $currency['exchange_rate'], | ||||
|                                 'base_sub_total' => $estimate->sub_total * $currency['exchange_rate'], | ||||
|                                 'base_total' => $estimate->total * $currency['exchange_rate'], | ||||
|                                 'base_tax' => $estimate->tax * $currency['exchange_rate'] | ||||
|                             ]); | ||||
|  | ||||
|                             $this->items($estimate); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     $taxes = Tax::where('currency_id', $currency['id'])->get(); | ||||
|  | ||||
|                     if ($taxes) { | ||||
|                         foreach ($taxes as $tax) { | ||||
|                             $tax->base_amount = $tax->base_amount * $currency['exchange_rate']; | ||||
|                             $tax->save(); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     $payments = Payment::where('currency_id', $currency['id'])->get(); | ||||
|  | ||||
|                     if ($payments) { | ||||
|                         foreach ($payments as $payment) { | ||||
|                             $payment->exchange_rate = $currency['exchange_rate']; | ||||
|                             $payment->base_amount = $payment->amount * $currency['exchange_rate']; | ||||
|                             $payment->save(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             $settings = [ | ||||
|                 'bulk_exchange_rate_configured' => 'YES' | ||||
|             ]; | ||||
|  | ||||
|             CompanySetting::setSettings($settings, $request->header('company')); | ||||
|  | ||||
|             return response()->json([ | ||||
|                 'success' => true | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'error' => false | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function items($model) | ||||
|     { | ||||
|         foreach ($model->items as $item) { | ||||
|             $item->update([ | ||||
|                 'exchange_rate' => $model->exchange_rate, | ||||
|                 'base_discount_val' => $item->discount_val * $model->exchange_rate, | ||||
|                 'base_price' => $item->price * $model->exchange_rate, | ||||
|                 'base_tax' => $item->tax * $model->exchange_rate, | ||||
|                 'base_total' => $item->total * $model->exchange_rate | ||||
|             ]); | ||||
|  | ||||
|             $this->taxes($item); | ||||
|         } | ||||
|  | ||||
|         $this->taxes($model); | ||||
|     } | ||||
|  | ||||
|     public function taxes($model) | ||||
|     { | ||||
|         if ($model->taxes()->exists()) { | ||||
|             $model->taxes->map(function ($tax) use ($model) { | ||||
|                 $tax->update([ | ||||
|                     'exchange_rate' => $model->exchange_rate, | ||||
|                     'base_amount' => $tax->amount * $model->exchange_rate, | ||||
|                 ]); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										22
									
								
								app/Http/Controllers/V1/Admin/General/ConfigController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/Http/Controllers/V1/Admin/General/ConfigController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\General; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class ConfigController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         return response()->json([ | ||||
|             $request->key => config('crater.'.$request->key), | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\General; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Resources\CountryResource; | ||||
| use Crater\Models\Country; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class CountriesController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         $countries = Country::all(); | ||||
|  | ||||
|         return CountryResource::collection($countries); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\General; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Resources\CurrencyResource; | ||||
| use Crater\Models\Currency; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class CurrenciesController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         $currencies = Currency::latest()->get(); | ||||
|  | ||||
|         return CurrencyResource::collection($currencies); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,23 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\General; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Space\DateFormatter; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class DateFormatsController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         return response()->json([ | ||||
|             'date_formats' => DateFormatter::get_list(), | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,37 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\General; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\Currency; | ||||
| use Crater\Models\Estimate; | ||||
| use Crater\Models\Invoice; | ||||
| use Crater\Models\Payment; | ||||
| use Crater\Models\Tax; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class GetAllUsedCurrenciesController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         $invoices = Invoice::where('exchange_rate', null)->pluck('currency_id')->toArray(); | ||||
|  | ||||
|         $taxes = Tax::where('exchange_rate', null)->pluck('currency_id')->toArray(); | ||||
|  | ||||
|         $estimates = Estimate::where('exchange_rate', null)->pluck('currency_id')->toArray(); | ||||
|  | ||||
|         $payments = Payment::where('exchange_rate', null)->pluck('currency_id')->toArray(); | ||||
|  | ||||
|         $currencies = array_merge($invoices, $taxes, $estimates, $payments); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'currencies' => Currency::whereIn('id', $currencies)->get() | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,66 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\General; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\Estimate; | ||||
| use Crater\Models\Invoice; | ||||
| use Crater\Models\Payment; | ||||
| use Crater\Services\SerialNumberFormatter; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class NextNumberController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request, Invoice $invoice, Estimate $estimate, Payment $payment) | ||||
|     { | ||||
|         $key = $request->key; | ||||
|         $nextNumber = null; | ||||
|         $serial = (new SerialNumberFormatter()) | ||||
|             ->setCompany($request->header('company')) | ||||
|             ->setCustomer($request->userId); | ||||
|  | ||||
|         try { | ||||
|             switch ($key) { | ||||
|                 case 'invoice': | ||||
|                     $nextNumber = $serial->setModel($invoice) | ||||
|                         ->setModelObject($request->model_id) | ||||
|                         ->getNextNumber(); | ||||
|  | ||||
|                     break; | ||||
|  | ||||
|                 case 'estimate': | ||||
|                     $nextNumber = $serial->setModel($estimate) | ||||
|                         ->setModelObject($request->model_id) | ||||
|                         ->getNextNumber(); | ||||
|  | ||||
|                     break; | ||||
|  | ||||
|                 case 'payment': | ||||
|                     $nextNumber = $serial->setModel($payment) | ||||
|                         ->setModelObject($request->model_id) | ||||
|                         ->getNextNumber(); | ||||
|  | ||||
|                     break; | ||||
|  | ||||
|                 default: | ||||
|                     return; | ||||
|             } | ||||
|         } catch (\Exception $exception) { | ||||
|             return response()->json([ | ||||
|                 'success' => false, | ||||
|                 'message' => $exception->getMessage() | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true, | ||||
|             'nextNumber' => $nextNumber, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										92
									
								
								app/Http/Controllers/V1/Admin/General/NotesController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								app/Http/Controllers/V1/Admin/General/NotesController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,92 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\General; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Requests\NotesRequest; | ||||
| use Crater\Http\Resources\NoteResource; | ||||
| use Crater\Models\Note; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class NotesController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $this->authorize('view notes'); | ||||
|  | ||||
|         $limit = $request->limit ?? 10; | ||||
|  | ||||
|         $notes = Note::latest() | ||||
|             ->whereCompany() | ||||
|             ->applyFilters($request->all()) | ||||
|             ->paginate($limit); | ||||
|  | ||||
|         return NoteResource::collection($notes); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function store(NotesRequest $request) | ||||
|     { | ||||
|         $this->authorize('manage notes'); | ||||
|  | ||||
|         $note = Note::create($request->getNotesPayload()); | ||||
|  | ||||
|         return new NoteResource($note); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Display the specified resource. | ||||
|      * | ||||
|      * @param  \Crater\Models\Note  $note | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function show(Note $note) | ||||
|     { | ||||
|         $this->authorize('view notes'); | ||||
|  | ||||
|         return new NoteResource($note); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the specified resource in storage. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @param  \Crater\Models\Note  $note | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function update(NotesRequest $request, Note $note) | ||||
|     { | ||||
|         $this->authorize('manage notes'); | ||||
|  | ||||
|         $note->update($request->getNotesPayload()); | ||||
|  | ||||
|         return new NoteResource($note); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
|      * @param  \Crater\Models\Note  $note | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function destroy(Note $note) | ||||
|     { | ||||
|         $this->authorize('manage notes'); | ||||
|  | ||||
|         $note->delete(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\General; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Services\SerialNumberFormatter; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class NumberPlaceholdersController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         if ($request->format) { | ||||
|             $placeholders = SerialNumberFormatter::getPlaceholders($request->format); | ||||
|         } else { | ||||
|             $placeholders = []; | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true, | ||||
|             'placeholders' => $placeholders, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										37
									
								
								app/Http/Controllers/V1/Admin/General/SearchController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								app/Http/Controllers/V1/Admin/General/SearchController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\General; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\Customer; | ||||
| use Crater\Models\User; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class SearchController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         $user = $request->user(); | ||||
|  | ||||
|         $customers = Customer::applyFilters($request->only(['search'])) | ||||
|             ->latest() | ||||
|             ->paginate(10); | ||||
|  | ||||
|         if ($user->isOwner()) { | ||||
|             $users = User::applyFilters($request->only(['search'])) | ||||
|                 ->latest() | ||||
|                 ->paginate(10); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'customers' => $customers, | ||||
|             'users' => $users ?? [], | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,27 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\General; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\User; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class SearchUsersController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         $this->authorize('create', User::class); | ||||
|  | ||||
|         $users = User::whereEmail($request->email) | ||||
|             ->latest() | ||||
|             ->paginate(10); | ||||
|  | ||||
|         return response()->json(['users' => $users]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,23 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\General; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Space\TimeZones; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class TimezonesController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Handle the incoming request. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request  $request | ||||
|      * @return \Illuminate\Http\Response | ||||
|      */ | ||||
|     public function __invoke(Request $request) | ||||
|     { | ||||
|         return response()->json([ | ||||
|             'time_zones' => TimeZones::get_list(), | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,36 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Invoice; | ||||
|  | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Models\Invoice; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| class ChangeInvoiceStatusController extends Controller | ||||
| { | ||||
|     /** | ||||
|     * Handle the incoming request. | ||||
|     * | ||||
|     * @param  \Illuminate\Http\Request  $request | ||||
|     * @return \Illuminate\Http\JsonResponse | ||||
|     */ | ||||
|     public function __invoke(Request $request, Invoice $invoice) | ||||
|     { | ||||
|         $this->authorize('send invoice', $invoice); | ||||
|  | ||||
|         if ($request->status == Invoice::STATUS_SENT) { | ||||
|             $invoice->status = Invoice::STATUS_SENT; | ||||
|             $invoice->sent = true; | ||||
|             $invoice->save(); | ||||
|         } elseif ($request->status == Invoice::STATUS_COMPLETED) { | ||||
|             $invoice->status = Invoice::STATUS_COMPLETED; | ||||
|             $invoice->paid_status = Invoice::STATUS_PAID; | ||||
|             $invoice->due_amount = 0; | ||||
|             $invoice->save(); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										131
									
								
								app/Http/Controllers/V1/Admin/Invoice/CloneInvoiceController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								app/Http/Controllers/V1/Admin/Invoice/CloneInvoiceController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,131 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Http\Controllers\V1\Admin\Invoice; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Crater\Http\Controllers\Controller; | ||||
| use Crater\Http\Resources\InvoiceResource; | ||||
| use Crater\Models\CompanySetting; | ||||
| use Crater\Models\Invoice; | ||||
| use Crater\Services\SerialNumberFormatter; | ||||
| use Illuminate\Http\Request; | ||||
| use Vinkla\Hashids\Facades\Hashids; | ||||
|  | ||||
| class CloneInvoiceController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Mail a specific invoice to the corresponding customer's email address. | ||||
|      * | ||||
|      * @param  \Illuminate\Http\Request $request | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function __invoke(Request $request, Invoice $invoice) | ||||
|     { | ||||
|         $this->authorize('create', Invoice::class); | ||||
|  | ||||
|         $date = Carbon::now(); | ||||
|  | ||||
|         $serial = (new SerialNumberFormatter()) | ||||
|             ->setModel($invoice) | ||||
|             ->setCompany($invoice->company_id) | ||||
|             ->setCustomer($invoice->customer_id) | ||||
|             ->setNextNumbers(); | ||||
|  | ||||
|         $due_date = null; | ||||
|         $dueDateEnabled = CompanySetting::getSetting( | ||||
|             'invoice_set_due_date_automatically', | ||||
|             $request->header('company') | ||||
|         ); | ||||
|  | ||||
|         if ($dueDateEnabled === 'YES') { | ||||
|             $dueDateDays = CompanySetting::getSetting( | ||||
|                 'invoice_due_date_days', | ||||
|                 $request->header('company') | ||||
|             ); | ||||
|             $due_date = Carbon::now()->addDays($dueDateDays)->format('Y-m-d'); | ||||
|         } | ||||
|  | ||||
|         $exchange_rate = $invoice->exchange_rate; | ||||
|  | ||||
|         $newInvoice = Invoice::create([ | ||||
|             'invoice_date' => $date->format('Y-m-d'), | ||||
|             'due_date' => $due_date, | ||||
|             'invoice_number' => $serial->getNextNumber(), | ||||
|             'sequence_number' => $serial->nextSequenceNumber, | ||||
|             'customer_sequence_number' => $serial->nextCustomerSequenceNumber, | ||||
|             'reference_number' => $invoice->reference_number, | ||||
|             'customer_id' => $invoice->customer_id, | ||||
|             'company_id' => $request->header('company'), | ||||
|             'template_name' => $invoice->template_name, | ||||
|             'status' => Invoice::STATUS_DRAFT, | ||||
|             'paid_status' => Invoice::STATUS_UNPAID, | ||||
|             'sub_total' => $invoice->sub_total, | ||||
|             'discount' => $invoice->discount, | ||||
|             'discount_type' => $invoice->discount_type, | ||||
|             'discount_val' => $invoice->discount_val, | ||||
|             'total' => $invoice->total, | ||||
|             'due_amount' => $invoice->total, | ||||
|             'tax_per_item' => $invoice->tax_per_item, | ||||
|             'discount_per_item' => $invoice->discount_per_item, | ||||
|             'tax' => $invoice->tax, | ||||
|             'notes' => $invoice->notes, | ||||
|             'exchange_rate' => $exchange_rate, | ||||
|             'base_total' => $invoice->total * $exchange_rate, | ||||
|             'base_discount_val' => $invoice->discount_val * $exchange_rate, | ||||
|             'base_sub_total' => $invoice->sub_total * $exchange_rate, | ||||
|             'base_tax' => $invoice->tax * $exchange_rate, | ||||
|             'base_due_amount' => $invoice->total * $exchange_rate, | ||||
|             'currency_id' => $invoice->currency_id, | ||||
|         ]); | ||||
|  | ||||
|         $newInvoice->unique_hash = Hashids::connection(Invoice::class)->encode($newInvoice->id); | ||||
|         $newInvoice->save(); | ||||
|         $invoice->load('items.taxes'); | ||||
|  | ||||
|         $invoiceItems = $invoice->items->toArray(); | ||||
|  | ||||
|         foreach ($invoiceItems as $invoiceItem) { | ||||
|             $invoiceItem['company_id'] = $request->header('company'); | ||||
|             $invoiceItem['name'] = $invoiceItem['name']; | ||||
|             $invoiceItem['exchange_rate'] = $exchange_rate; | ||||
|             $invoiceItem['base_price'] = $invoiceItem['price'] * $exchange_rate; | ||||
|             $invoiceItem['base_discount_val'] = $invoiceItem['discount_val'] * $exchange_rate; | ||||
|             $invoiceItem['base_tax'] = $invoiceItem['tax'] * $exchange_rate; | ||||
|             $invoiceItem['base_total'] = $invoiceItem['total'] * $exchange_rate; | ||||
|  | ||||
|             $item = $newInvoice->items()->create($invoiceItem); | ||||
|  | ||||
|             if (array_key_exists('taxes', $invoiceItem) && $invoiceItem['taxes']) { | ||||
|                 foreach ($invoiceItem['taxes'] as $tax) { | ||||
|                     $tax['company_id'] = $request->header('company'); | ||||
|  | ||||
|                     if ($tax['amount']) { | ||||
|                         $item->taxes()->create($tax); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($invoice->taxes) { | ||||
|             foreach ($invoice->taxes->toArray() as $tax) { | ||||
|                 $tax['company_id'] = $request->header('company'); | ||||
|                 $newInvoice->taxes()->create($tax); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($invoice->fields()->exists()) { | ||||
|             $customFields = []; | ||||
|  | ||||
|             foreach ($invoice->fields as $data) { | ||||
|                 $customFields[] = [ | ||||
|                     'id' => $data->custom_field_id, | ||||
|                     'value' => $data->defaultAnswer | ||||
|                 ]; | ||||
|             } | ||||
|  | ||||
|             $newInvoice->addCustomFields($customFields); | ||||
|         } | ||||
|  | ||||
|         return new InvoiceResource($newInvoice); | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	