Compare commits
771 Commits
add-openap
...
attempt-at
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6889c0d97e | ||
|
|
4e8c1e853b | ||
|
|
4509e1d1dc | ||
|
|
6b9d4941be | ||
|
|
308cd6b91d | ||
|
|
bd742aec9c | ||
|
|
d2ab3071a6 | ||
|
|
1dd4c161f0 | ||
|
|
5e48dd45b2 | ||
|
|
601d6e7377 | ||
|
|
d43be271e6 | ||
|
|
92a3bdf4e9 | ||
|
|
fa2aafe41f | ||
|
|
a7be1acbd8 | ||
|
|
fbe2ae03ff | ||
|
|
d1a492f953 | ||
|
|
352807c2d7 | ||
|
|
3aa046bfa7 | ||
|
|
9454ff677b | ||
|
|
a5c7b8f609 | ||
|
|
9365d1adc6 | ||
|
|
ddd2a96ac5 | ||
|
|
bf8ff51234 | ||
|
|
7891f52bd4 | ||
|
|
baa4a8a461 | ||
|
|
34daffcdf4 | ||
|
|
21baea27a8 | ||
|
|
e1d3714445 | ||
|
|
8b7e0a0d78 | ||
|
|
a1cc427c9c | ||
|
|
5dc675040d | ||
|
|
1258eb6533 | ||
|
|
83abfc9ca6 | ||
|
|
b42d6677cc | ||
|
|
e8c644a600 | ||
|
|
9f6a73b290 | ||
|
|
4d38bd1c62 | ||
|
|
138262114d | ||
|
|
4073c9e638 | ||
|
|
f1b4877a98 | ||
|
|
c39c92d0d7 | ||
|
|
90fc48d959 | ||
|
|
7f10a53105 | ||
|
|
7ae1b7a765 | ||
|
|
22a43a0463 | ||
|
|
dea399398a | ||
|
|
7434dd9458 | ||
|
|
88e532dbc4 | ||
|
|
c5ad451c39 | ||
|
|
44bfceeb0f | ||
|
|
37eb63837b | ||
|
|
4ada47e3b0 | ||
|
|
e5c55c9ab3 | ||
|
|
547b3df7b4 | ||
|
|
4100f2600c | ||
|
|
a9574e8fd6 | ||
|
|
c6269d6bbc | ||
|
|
c1204a5301 | ||
|
|
cc5ac65909 | ||
|
|
9ddc48e3d7 | ||
|
|
4a39d7c67a | ||
|
|
b7a6706591 | ||
|
|
ddb031f091 | ||
|
|
e906d25776 | ||
|
|
27d7449459 | ||
|
|
5d8905c997 | ||
|
|
517f4ce121 | ||
|
|
6809bbd3d5 | ||
|
|
ab555d05e1 | ||
|
|
f7bc538fdf | ||
|
|
2de66ad5db | ||
|
|
30e16b6213 | ||
|
|
92b50ca7ae | ||
|
|
9ee36df979 | ||
|
|
46d1c14e1a | ||
|
|
61895011fb | ||
|
|
32d43034bd | ||
|
|
dfc6cdc127 | ||
|
|
16e93f9e18 | ||
|
|
7395b1a4eb | ||
|
|
fa98557225 | ||
|
|
894606b62e | ||
|
|
f0a6a0026a | ||
|
|
070e0c93be | ||
|
|
2b27b733e5 | ||
|
|
0355c2b642 | ||
|
|
3bad19fb56 | ||
|
|
0f84d51a48 | ||
|
|
2e8572d9c5 | ||
|
|
df53d5d966 | ||
|
|
23838959ca | ||
|
|
6dbb836a01 | ||
|
|
3426afe5a8 | ||
|
|
4bbf923eb6 | ||
|
|
e2c3480194 | ||
|
|
73159076f6 | ||
|
|
90d040573d | ||
|
|
155481a442 | ||
|
|
2f31bfc5fe | ||
|
|
8d0c88dc74 | ||
|
|
07256fd833 | ||
|
|
776cd43a58 | ||
|
|
acb5309aab | ||
|
|
c68f81db3c | ||
|
|
ac8e341b37 | ||
|
|
36122b3966 | ||
|
|
79bcf472f0 | ||
|
|
55d86da846 | ||
|
|
e4f8c1ba3f | ||
|
|
c36236b7dc | ||
|
|
63994333d0 | ||
|
|
da4c7d8934 | ||
|
|
186721eca0 | ||
|
|
f53d939c86 | ||
|
|
23e6909708 | ||
|
|
cf421fe1c1 | ||
|
|
4d80e806e4 | ||
|
|
60a7b7f7ff | ||
|
|
90263eab06 | ||
|
|
9d8f251fc4 | ||
|
|
2b4986571c | ||
|
|
890d13bd52 | ||
|
|
e698e71137 | ||
|
|
d064a5530a | ||
|
|
ab4fbf6c19 | ||
|
|
728afa8361 | ||
|
|
b77019c16e | ||
|
|
6703448b80 | ||
|
|
776ba19a1f | ||
|
|
1f499e0d44 | ||
|
|
0a6eb61103 | ||
|
|
32a2eed5ec | ||
|
|
40a70d39d0 | ||
|
|
5697054e98 | ||
|
|
def5969e1c | ||
|
|
78a418630d | ||
|
|
6d76e7b2d4 | ||
|
|
f052c8b44c | ||
|
|
0e6991d56d | ||
|
|
06eab5f8a4 | ||
|
|
4a481e79c4 | ||
|
|
ee4abbcbaa | ||
|
|
dcc82d742f | ||
|
|
19cb2089d7 | ||
|
|
04923b06b0 | ||
|
|
e16755d491 | ||
|
|
742b0769a4 | ||
|
|
df68dca9dc | ||
|
|
4a5bf78d58 | ||
|
|
7947237489 | ||
|
|
1115205164 | ||
|
|
d5d01136c4 | ||
|
|
3d47277614 | ||
|
|
b937bea04f | ||
|
|
fff14632bc | ||
|
|
9bdf1a620f | ||
|
|
60099aa989 | ||
|
|
f3976e5dd8 | ||
|
|
d7d6893304 | ||
|
|
99549ce805 | ||
|
|
7eb15fe04d | ||
|
|
e2019a13ab | ||
|
|
4b7a06761a | ||
|
|
b96e2fb52c | ||
|
|
8f4a1f5801 | ||
|
|
891bec9cdb | ||
|
|
c5252ea583 | ||
|
|
82d553c180 | ||
|
|
71e34355b9 | ||
|
|
2bad8c72e4 | ||
|
|
6134ca01ac | ||
|
|
be8193ebff | ||
|
|
430ee46645 | ||
|
|
76fbbf29e8 | ||
|
|
3108159d95 | ||
|
|
f282a1ead7 | ||
|
|
324bc4957d | ||
|
|
c3b5c4dfae | ||
|
|
4ab5d97e86 | ||
|
|
b4696ef11e | ||
|
|
294ffb72a4 | ||
|
|
8c534d29d3 | ||
|
|
d6cb262f9d | ||
|
|
5211e2ae20 | ||
|
|
90832fd1ad | ||
|
|
889cbc69e1 | ||
|
|
15948370d4 | ||
|
|
63c5177b37 | ||
|
|
4fbfaf6b9f | ||
|
|
9e68497b63 | ||
|
|
0b9e13bf1e | ||
|
|
485d343e0f | ||
|
|
07f0e8a3be | ||
|
|
cc5afb1cd8 | ||
|
|
c69f1c0890 | ||
|
|
a8733bdedf | ||
|
|
4222e4eb51 | ||
|
|
5db4441f5c | ||
|
|
2bcfe97211 | ||
|
|
4e74c97c84 | ||
|
|
71a46c9bd6 | ||
|
|
1a7c7fdebf | ||
|
|
65ff3d414a | ||
|
|
9fa38b70c8 | ||
|
|
470172f53f | ||
|
|
81261d9e36 | ||
|
|
3ab2e20119 | ||
|
|
b773d576ea | ||
|
|
23feb64b5a | ||
|
|
87fe9d9d3d | ||
|
|
b1ef3f51cb | ||
|
|
e1b6488f8e | ||
|
|
a472dede2b | ||
|
|
263cc3f7a1 | ||
|
|
598612d4bf | ||
|
|
a07d83e583 | ||
|
|
d355812433 | ||
|
|
1afc14f5ab | ||
|
|
8947b667ae | ||
|
|
6b41796d44 | ||
|
|
6efe8eb55b | ||
|
|
4c8f8918e8 | ||
|
|
4cb5bb1855 | ||
|
|
eb007e025a | ||
|
|
18aefa9dee | ||
|
|
2ec540bd36 | ||
|
|
1374ec408a | ||
|
|
8f8fed2b79 | ||
|
|
a1e6f01fe9 | ||
|
|
d7496f22e5 | ||
|
|
7de25a1c37 | ||
|
|
78da89340c | ||
|
|
60606115fe | ||
|
|
57b49fc31c | ||
|
|
6e90c8f6e6 | ||
|
|
a7ff2595a5 | ||
|
|
1edea6abef | ||
|
|
a3bd58bda6 | ||
|
|
3339d1999f | ||
|
|
df3e8ec0f3 | ||
|
|
2dbec867d9 | ||
|
|
3fc651d659 | ||
|
|
b91d23023d | ||
|
|
cc234c60b8 | ||
|
|
f1883c8004 | ||
|
|
79f6ddc8ee | ||
|
|
89f439a18d | ||
|
|
79eb5bfad9 | ||
|
|
3a36b7dafd | ||
|
|
1fe2fd9891 | ||
|
|
5fdb999ece | ||
|
|
5a38d9c2b6 | ||
|
|
b1c7dc6cbb | ||
|
|
b21b2ac41c | ||
|
|
1396c597ef | ||
|
|
46af72ee4e | ||
|
|
e3e8f553c8 | ||
|
|
549928d3d1 | ||
|
|
abefec9628 | ||
|
|
b483eeded4 | ||
|
|
3ff516180d | ||
|
|
c0a99d6b52 | ||
|
|
cba090f8eb | ||
|
|
0d6baa1081 | ||
|
|
85a208526b | ||
|
|
cd266a6bef | ||
|
|
3c81257325 | ||
|
|
24bb45ab97 | ||
|
|
d5ef7f3204 | ||
|
|
b0f5fe7e25 | ||
|
|
c675bb7252 | ||
|
|
143a091f45 | ||
|
|
8fefc11b4d | ||
|
|
9188fb03f0 | ||
|
|
607eb6ca03 | ||
|
|
44ef39e419 | ||
|
|
0ea9c0647f | ||
|
|
884d2a9552 | ||
|
|
14e43192e6 | ||
|
|
b9f4dc1e9d | ||
|
|
b082fb6692 | ||
|
|
0c1b2a54e7 | ||
|
|
85e16ecd51 | ||
|
|
e40c532354 | ||
|
|
55b324d8d6 | ||
|
|
3152d9eadd | ||
|
|
e70f1408aa | ||
|
|
a384245368 | ||
|
|
9334b8df47 | ||
|
|
e0bc2ae86f | ||
|
|
2f019bb033 | ||
|
|
75c83236ff | ||
|
|
4ddee4ac40 | ||
|
|
6b693e2644 | ||
|
|
72cf921a4b | ||
|
|
32882f81e7 | ||
|
|
36f5099932 | ||
|
|
4dc3c30354 | ||
|
|
397cc1754a | ||
|
|
ca44ee94a4 | ||
|
|
3ae7a77032 | ||
|
|
881c789a75 | ||
|
|
fea0189479 | ||
|
|
6ca0e19819 | ||
|
|
bf6964ee62 | ||
|
|
9ac2ea2a52 | ||
|
|
62a58fa23b | ||
|
|
0ce20c1edd | ||
|
|
d31d99b40f | ||
|
|
3527c357cc | ||
|
|
2b5254e68f | ||
|
|
6f3323c195 | ||
|
|
5af85bfe7d | ||
|
|
20adad3c6b | ||
|
|
ca8eae4064 | ||
|
|
7077faaf4a | ||
|
|
c67ca500db | ||
|
|
0081e7b731 | ||
|
|
4e6483d3ed | ||
|
|
0ddf0002c4 | ||
|
|
ad0daf33b9 | ||
|
|
ab30df10ff | ||
|
|
a6cb75c481 | ||
|
|
0b60c6a939 | ||
|
|
51ce570eb3 | ||
|
|
925d48640d | ||
|
|
028b4e7b79 | ||
|
|
625a46a2c2 | ||
|
|
ebc1e27c22 | ||
|
|
357e85d358 | ||
|
|
75cfcb83aa | ||
|
|
63a4d1ad33 | ||
|
|
dcbb09bbd7 | ||
|
|
58eac619ea | ||
|
|
f04d6f37e5 | ||
|
|
469069b471 | ||
|
|
9bca5912d9 | ||
|
|
23756ba1c7 | ||
|
|
07227887f6 | ||
|
|
13d3b103f1 | ||
|
|
4bdfd0e115 | ||
|
|
786b20708e | ||
|
|
0e957cad84 | ||
|
|
85c728f313 | ||
|
|
ba3fb8cd66 | ||
|
|
d9fb7dc754 | ||
|
|
2249dad9d7 | ||
|
|
5c4fa630ae | ||
|
|
ede74ad24e | ||
|
|
27542a8f91 | ||
|
|
5dc07b94aa | ||
|
|
d7acf721ae | ||
|
|
eff5232828 | ||
|
|
3eb29b1cdb | ||
|
|
c9961f63b4 | ||
|
|
09e843a800 | ||
|
|
77b79dbd95 | ||
|
|
dafc6c5136 | ||
|
|
c790147a5c | ||
|
|
80c39c5ef3 | ||
|
|
9a3e84d84c | ||
|
|
643960c829 | ||
|
|
1737018325 | ||
|
|
484d5ba76e | ||
|
|
798685d0b8 | ||
|
|
cb7654ae90 | ||
|
|
00c394345a | ||
|
|
dffcb62fa1 | ||
|
|
8c668b72b7 | ||
|
|
b54ecd4da0 | ||
|
|
87a7e3501b | ||
|
|
daefec3013 | ||
|
|
183a9742c4 | ||
|
|
04b83f8176 | ||
|
|
335ab3f064 | ||
|
|
7dd493da35 | ||
|
|
560bd6da92 | ||
|
|
a5824ccc5f | ||
|
|
830a7964a4 | ||
|
|
b1359c3277 | ||
|
|
0ba8f5cc5a | ||
|
|
6fb9e2c38e | ||
|
|
eebc2ab8be | ||
|
|
b65b3151ee | ||
|
|
1d24b7985b | ||
|
|
526bb2c650 | ||
|
|
c450c0ddb8 | ||
|
|
13a0f49f5f | ||
|
|
199eefafa1 | ||
|
|
c5b58f9ecc | ||
|
|
6b68fe4de6 | ||
|
|
3461bbfdb3 | ||
|
|
51f6927076 | ||
|
|
1d88cf443f | ||
|
|
7b6c0c3a40 | ||
|
|
fdb0651bf4 | ||
|
|
c39d484611 | ||
|
|
c42996429f | ||
|
|
a091baf5a6 | ||
|
|
00a17cd55e | ||
|
|
643d44af22 | ||
|
|
b934f43db0 | ||
|
|
f39afe5a65 | ||
|
|
7612ee6b08 | ||
|
|
2ed2b0101a | ||
|
|
5ca9d31964 | ||
|
|
2fcd8cd261 | ||
|
|
0ffa47a2c6 | ||
|
|
e203d4dee3 | ||
|
|
b47d773e13 | ||
|
|
a8d0a4a95d | ||
|
|
3fb0804cef | ||
|
|
6811ebcd52 | ||
|
|
4fe7bfb851 | ||
|
|
fb60985d03 | ||
|
|
8f575923cf | ||
|
|
0ecfd02649 | ||
|
|
420aaf4f61 | ||
|
|
0c35f213e1 | ||
|
|
f68813af13 | ||
|
|
37a90d0ce9 | ||
|
|
02f1291e8f | ||
|
|
92e4f6b5d9 | ||
|
|
7b7738fbcc | ||
|
|
31197604a3 | ||
|
|
e33b1b6c90 | ||
|
|
30520297e8 | ||
|
|
78ca1d1335 | ||
|
|
6159ee8c2c | ||
|
|
5cd5392958 | ||
|
|
0dcdfc5d14 | ||
|
|
d0e068f1c0 | ||
|
|
f42a2d7457 | ||
|
|
d29619b67c | ||
|
|
f5235cb835 | ||
|
|
ee830e0cb4 | ||
|
|
0cd3be003d | ||
|
|
c93e35ec77 | ||
|
|
9538a76232 | ||
|
|
05876bb124 | ||
|
|
8bcd5a6d2a | ||
|
|
a36afbcb25 | ||
|
|
ebd8d085cf | ||
|
|
505148b024 | ||
|
|
3eefeec4ce | ||
|
|
b61419c1ce | ||
|
|
f590fcffbc | ||
|
|
e87e924ac2 | ||
|
|
90f261bab6 | ||
|
|
f7dfb09a4d | ||
|
|
3135917127 | ||
|
|
52afa3d36d | ||
|
|
242aa60e04 | ||
|
|
7a3c2c27ff | ||
|
|
5d124360c2 | ||
|
|
365d7448d5 | ||
|
|
9a0102c723 | ||
|
|
2f77f2cb2b | ||
|
|
528e3a2106 | ||
|
|
032a664d4c | ||
|
|
aac1864c9b | ||
|
|
e3477f3306 | ||
|
|
6620a4f87b | ||
|
|
c0e9dff5bf | ||
|
|
2d961c435a | ||
|
|
7c95f03166 | ||
|
|
31e5c13b50 | ||
|
|
4a9fe4f981 | ||
|
|
4fcc5587ee | ||
|
|
6ca49a20ce | ||
|
|
28f293fdc1 | ||
|
|
b3e7619adc | ||
|
|
6e56d56137 | ||
|
|
2528f6a07b | ||
|
|
423d07c919 | ||
|
|
cc608de4bf | ||
|
|
f999a68608 | ||
|
|
db78a9f18f | ||
|
|
816039f48e | ||
|
|
ae240bae6d | ||
|
|
9e30c69e6d | ||
|
|
43c7de9049 | ||
|
|
7e51c5db81 | ||
|
|
0ee3c45e7b | ||
|
|
981e69929c | ||
|
|
1eae5d12fc | ||
|
|
8863208333 | ||
|
|
5f38a74a72 | ||
|
|
fe15dacb1f | ||
|
|
c2d44cf2f2 | ||
|
|
7f1bdb6f34 | ||
|
|
7cdfaa93ec | ||
|
|
59ccc70303 | ||
|
|
f1584b722d | ||
|
|
b0305e12d2 | ||
|
|
4d8c5a86a4 | ||
|
|
58f76b5c99 | ||
|
|
7c4ee632cf | ||
|
|
b6b0f716eb | ||
|
|
bd0e04ed15 | ||
|
|
8599981d44 | ||
|
|
6fc6e95c67 | ||
|
|
43b585bde8 | ||
|
|
710f89291f | ||
|
|
5f835aa009 | ||
|
|
d5ca543719 | ||
|
|
4c6249eb9e | ||
|
|
016900bad8 | ||
|
|
2e8ae33761 | ||
|
|
0d325060da | ||
|
|
1a6e98e18f | ||
|
|
97e34595f6 | ||
|
|
a179d5234b | ||
|
|
64c6121fdb | ||
|
|
08d8954a85 | ||
|
|
4f20955d0d | ||
|
|
3a703c8bcf | ||
|
|
ccbffa086b | ||
|
|
07ee4be840 | ||
|
|
4cc9b2d312 | ||
|
|
24dddae1d1 | ||
|
|
ad0165d085 | ||
|
|
39dc38c5d1 | ||
|
|
046ce19dbb | ||
|
|
33263f5a93 | ||
|
|
8ef8e76300 | ||
|
|
73cfdae9e7 | ||
|
|
54a3e41281 | ||
|
|
d59ba6da84 | ||
|
|
1b28b06934 | ||
|
|
ff3e69a56c | ||
|
|
6b975a5fb4 | ||
|
|
3a02b15124 | ||
|
|
5f73d81935 | ||
|
|
002b5c0f6f | ||
|
|
8086842570 | ||
|
|
51f2d5a664 | ||
|
|
db63ad1cf4 | ||
|
|
b74b76de75 | ||
|
|
a1b1498106 | ||
|
|
1158851ea7 | ||
|
|
e1ab9e959e | ||
|
|
2bbac3ae9d | ||
|
|
e889b1d5e5 | ||
|
|
233bf856f4 | ||
|
|
bca843e06c | ||
|
|
30a79a1278 | ||
|
|
0f92dee2c4 | ||
|
|
f04efede15 | ||
|
|
f0dfdf6720 | ||
|
|
e26d731382 | ||
|
|
d684d3e559 | ||
|
|
47c54cb998 | ||
|
|
592cb2b3ec | ||
|
|
f5a7871a2e | ||
|
|
ec411fa0db | ||
|
|
5da79cd5ca | ||
|
|
a850a9bb83 | ||
|
|
13c971b171 | ||
|
|
1cee7e43ed | ||
|
|
5e81c63d6e | ||
|
|
479b7a3f94 | ||
|
|
f7cfee77c9 | ||
|
|
65a8126a13 | ||
|
|
a39bc102d5 | ||
|
|
81d930c4d2 | ||
|
|
6839623061 | ||
|
|
7de2809d42 | ||
|
|
98ec6b6886 | ||
|
|
04827f00cc | ||
|
|
660bfc6578 | ||
|
|
1152cd5537 | ||
|
|
17456482d6 | ||
|
|
a0431e1912 | ||
|
|
f30e8497b2 | ||
|
|
06495bc45d | ||
|
|
26067916b3 | ||
|
|
c36ee4852b | ||
|
|
2cb992ad44 | ||
|
|
ee5aac8008 | ||
|
|
083b7be6c0 | ||
|
|
e24854558f | ||
|
|
e4314cf426 | ||
|
|
4106e4e45c | ||
|
|
05f143db2b | ||
|
|
64aeaeeeea | ||
|
|
61db37ab0d | ||
|
|
f9c4d921e7 | ||
|
|
ca099df573 | ||
|
|
28b584b8bc | ||
|
|
70449e694d | ||
|
|
8395ea552d | ||
|
|
dc66452633 | ||
|
|
53ce44ac91 | ||
|
|
c7c3243bbc | ||
|
|
8bdd77d33d | ||
|
|
acd7d0db3a | ||
|
|
2bfadb8a3c | ||
|
|
0912e4af7b | ||
|
|
5aa5c48018 | ||
|
|
8cdd998f79 | ||
|
|
050d4d6b25 | ||
|
|
366cd11238 | ||
|
|
58d6443331 | ||
|
|
101b8afb56 | ||
|
|
5df5c47945 | ||
|
|
a04740ba86 | ||
|
|
425ad93ac5 | ||
|
|
1b397cd780 | ||
|
|
120316bae0 | ||
|
|
7571ff007f | ||
|
|
7afd7da2b4 | ||
|
|
8a44144c20 | ||
|
|
ee82c70582 | ||
|
|
c87e8e606b | ||
|
|
37a50dd953 | ||
|
|
a2669a3084 | ||
|
|
77da22f4dd | ||
|
|
7830ffe202 | ||
|
|
1c9e20d59f | ||
|
|
320edac286 | ||
|
|
d49878371d | ||
|
|
d2575a5d9b | ||
|
|
ea6cf72580 | ||
|
|
2118155b37 | ||
|
|
ba4f5bb71f | ||
|
|
d5a74a5a8b | ||
|
|
23be1df360 | ||
|
|
b5c1a1da4c | ||
|
|
c11e784f51 | ||
|
|
06f51c8f9c | ||
|
|
181bcbbda6 | ||
|
|
d008ead6a4 | ||
|
|
75924be958 | ||
|
|
b1a6e3f8a2 | ||
|
|
06712a6041 | ||
|
|
62b16339a9 | ||
|
|
9a2f1a36ba | ||
|
|
95cc4d3a73 | ||
|
|
497eeeb2e0 | ||
|
|
4be21ca249 | ||
|
|
e8598e214e | ||
|
|
54b1d65e3c | ||
|
|
f7648496d3 | ||
|
|
59a57c7197 | ||
|
|
5659b26827 | ||
|
|
ee4443aaf0 | ||
|
|
839dcad358 | ||
|
|
d67933ab49 | ||
|
|
0eb3f6b952 | ||
|
|
68b0f80fce | ||
|
|
93489529a3 | ||
|
|
511be74e74 | ||
|
|
bdee067803 | ||
|
|
32156cace3 | ||
|
|
30688114be | ||
|
|
34088bcc17 | ||
|
|
07835766cc | ||
|
|
251851ec6a | ||
|
|
049a669186 | ||
|
|
d29f13bae9 | ||
|
|
c758355df9 | ||
|
|
79d97a83af | ||
|
|
85bd47c240 | ||
|
|
473ead9616 | ||
|
|
cf2850933c | ||
|
|
ff2564c57a | ||
|
|
91d3848246 | ||
|
|
c031f0b45e | ||
|
|
fdbb9568ae | ||
|
|
d817883459 | ||
|
|
12255979ac | ||
|
|
366b61850b | ||
|
|
89be6bd183 | ||
|
|
e120331a2c | ||
|
|
cb8a212d96 | ||
|
|
7aec431ac5 | ||
|
|
d19681dea1 | ||
|
|
bf2299daf8 | ||
|
|
164930d0dd | ||
|
|
387dbac809 | ||
|
|
3b661e5a99 | ||
|
|
90c1c0e655 | ||
|
|
21d8e7695b | ||
|
|
1acc452cfe | ||
|
|
1375e1feee | ||
|
|
2187adf59a | ||
|
|
0dcb315d9d | ||
|
|
327ccbd0c9 | ||
|
|
f571d400e6 | ||
|
|
293aa52335 | ||
|
|
ca178ae9a7 | ||
|
|
d0c810e418 | ||
|
|
d496d2caeb | ||
|
|
e70b75c350 | ||
|
|
a50befeda5 | ||
|
|
e2616e8039 | ||
|
|
904266debe | ||
|
|
72d5783795 | ||
|
|
d699fb1473 | ||
|
|
fab1a6c33a | ||
|
|
be73c30194 | ||
|
|
451646fe4f | ||
|
|
decc919991 | ||
|
|
e0b4005921 | ||
|
|
3ef36e7534 | ||
|
|
1949e1e1e9 | ||
|
|
3358382358 | ||
|
|
3eca3ecd75 | ||
|
|
140c6b91b0 | ||
|
|
a5315ec240 | ||
|
|
93f1656e0b | ||
|
|
f1d006c236 | ||
|
|
b0b5a96694 | ||
|
|
7dbe9a85f4 | ||
|
|
d2c39528d5 | ||
|
|
0420543c94 | ||
|
|
aae0db902b | ||
|
|
88dae7cef7 | ||
|
|
e5cb17e934 | ||
|
|
9d609805f2 | ||
|
|
e2b9ca8254 | ||
|
|
e16a2fe8af | ||
|
|
22d61a533d | ||
|
|
af408bb45f | ||
|
|
24bfbc06f0 | ||
|
|
5eb9f353b5 | ||
|
|
473ce15f47 | ||
|
|
eb9cfbaed6 | ||
|
|
faeb037ff9 | ||
|
|
07602f697d | ||
|
|
11abb0fdb1 | ||
|
|
deeb2fa543 | ||
|
|
ef8d5ff11e | ||
|
|
91f3e07b83 | ||
|
|
c29bdbdacb | ||
|
|
a20d104d2f | ||
|
|
a61dd8ac17 | ||
|
|
7ee9a690ea | ||
|
|
5ba94c6c41 | ||
|
|
9fa855c837 | ||
|
|
9251007574 | ||
|
|
cc73b984cb | ||
|
|
548ef97c32 | ||
|
|
ed8a486726 | ||
|
|
1ab0911fc8 | ||
|
|
bdbaea7294 | ||
|
|
5cfd1f6fb2 | ||
|
|
5eda67381f | ||
|
|
2c8b8bfaf2 | ||
|
|
8f3159751a | ||
|
|
4b05e55b29 | ||
|
|
3d3c13fcd0 | ||
|
|
88e1d8a8cf | ||
|
|
e007db34e2 | ||
|
|
f9f06d2c02 | ||
|
|
234f7d00c8 | ||
|
|
9924553da5 | ||
|
|
df38d7e3ed | ||
|
|
44dd061619 | ||
|
|
7603a932b1 | ||
|
|
138e7acc13 | ||
|
|
e863d3e7e5 | ||
|
|
c8e401f5ed | ||
|
|
3ba20a8e28 | ||
|
|
ebae63752f | ||
|
|
8bc73901cf | ||
|
|
b4f70d9244 | ||
|
|
21e9f2bba3 | ||
|
|
881f4e3d6a | ||
|
|
b141945add |
@@ -4235,6 +4235,33 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "smarsching",
|
||||
"name": "Sebastian Marsching",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2880129?v=4",
|
||||
"profile": "http://sebastian.marsching.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mohammad-ahmadi1",
|
||||
"name": "Mo",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/40658372?v=4",
|
||||
"profile": "https://github.com/mohammad-ahmadi1",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MarvelousAnything",
|
||||
"name": "Owen V. Hayes",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/20994684?v=4",
|
||||
"profile": "https://github.com/MarvelousAnything",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -190,13 +190,14 @@ APP_ALLOW_INSECURE_HOSTS=false
|
||||
GOOGLE_MAPS_API=
|
||||
LDAP_MEM_LIM=500M
|
||||
LDAP_TIME_LIM=600
|
||||
BACKUP_TIME_LIMIT=600
|
||||
IMPORT_TIME_LIMIT=600
|
||||
IMPORT_MEMORY_LIMIT=500M
|
||||
REPORT_TIME_LIMIT=12000
|
||||
API_THROTTLE_PER_MINUTE=120
|
||||
CSV_ESCAPE_FORMULAS=true
|
||||
LIVEWIRE_URL_PREFIX=null
|
||||
|
||||
MAX_UNPAGINATED=5000
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: SAML SETTINGS
|
||||
|
||||
60
.github/ISSUE_TEMPLATE/Bug-Report.yml
vendored
60
.github/ISSUE_TEMPLATE/Bug-Report.yml
vendored
@@ -23,7 +23,23 @@ body:
|
||||
attributes:
|
||||
label: Snipe-IT Version
|
||||
description: What version of Snipe-IT are you seeing this issue on? You can find the version number in the footer of any page in Snipe-IT.
|
||||
placeholder: ex. v8.3.1 - build 19577 (master)
|
||||
placeholder: ex. v8.3.2 - build 19577 (master)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: php-version
|
||||
attributes:
|
||||
label: PHP Version
|
||||
description: What version of PHP are you running? You can find the version of PHP your webserver is running in the `Admin Settings` section in the footer, and the cli version by running `php -v` via command line .
|
||||
placeholder: ex. v8.3.1 (web), PHP 8.4.12 (cli)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: composer-version
|
||||
attributes:
|
||||
label: Composer Version
|
||||
description: What version of composer are you running? You can find the version number by running `composer --version`.
|
||||
placeholder: ex. 2.8.10
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
@@ -48,6 +64,16 @@ body:
|
||||
- Not sure
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: upgrade-or-fresh
|
||||
attributes:
|
||||
label: Is this a fresh install or an upgrade?
|
||||
options:
|
||||
- Fresh install
|
||||
- Upgrade
|
||||
- NA
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
@@ -67,6 +93,38 @@ body:
|
||||
- Safari
|
||||
- Microsoft Edge
|
||||
- Other
|
||||
- type: dropdown
|
||||
id: on-demo
|
||||
attributes:
|
||||
label: Can you reproduce this on the public demo?
|
||||
description: You can check this at https://demo.snipeitapp.com.
|
||||
options:
|
||||
- 'Yes'
|
||||
- 'No'
|
||||
- N/A
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: fmcs
|
||||
attributes:
|
||||
label: Do you have full multiple company support enabled?
|
||||
description: You can check this in your Snipe-IT installation at `Admin Settings > General Settings > Scoping`.
|
||||
options:
|
||||
- 'Yes'
|
||||
- 'No'
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: fmcs-location
|
||||
attributes:
|
||||
label: If you have full multiple company support enabled, do you have location scoping to company enabled?
|
||||
description: You can check this in your Snipe-IT installation at `Admin Settings > General Settings > Scoping`.
|
||||
options:
|
||||
- 'Yes'
|
||||
- 'No'
|
||||
- I do not have full multiple company support enabled
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: server-logs
|
||||
attributes:
|
||||
|
||||
6
.github/workflows/SA-codeql.yml
vendored
6
.github/workflows/SA-codeql.yml
vendored
@@ -30,10 +30,10 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
||||
2
.github/workflows/codacy-analysis.yml
vendored
2
.github/workflows/codacy-analysis.yml
vendored
@@ -52,6 +52,6 @@ jobs:
|
||||
|
||||
# Upload the SARIF file generated in the previous step
|
||||
- name: Upload SARIF results file
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
2
.github/workflows/tests-mysql.yml
vendored
2
.github/workflows/tests-mysql.yml
vendored
@@ -82,7 +82,7 @@ jobs:
|
||||
|
||||
- name: Upload Laravel logs as artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
|
||||
path: |
|
||||
|
||||
2
.github/workflows/tests-postgres.yml
vendored
2
.github/workflows/tests-postgres.yml
vendored
@@ -81,7 +81,7 @@ jobs:
|
||||
|
||||
- name: Upload Laravel logs as artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
|
||||
path: |
|
||||
|
||||
2
.github/workflows/tests-sqlite.yml
vendored
2
.github/workflows/tests-sqlite.yml
vendored
@@ -67,7 +67,7 @@ jobs:
|
||||
|
||||
- name: Upload Laravel logs as artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
|
||||
path: |
|
||||
|
||||
@@ -68,7 +68,8 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars.githubusercontent.com/u/181059?v=4" width="110px;"/><br /><sub>Juan Font</sub>](https://github.com/juanfont)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juanfont "Code") | [<img src="https://avatars.githubusercontent.com/u/13137708?v=4" width="110px;"/><br /><sub>Juho Taipale</sub>](https://github.com/juhotaipale)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juhotaipale "Code") | [<img src="https://avatars.githubusercontent.com/u/1007419?v=4" width="110px;"/><br /><sub>Korvin Szanto</sub>](https://github.com/KorvinSzanto)<br />[💻](https://github.com/snipe/snipe-it/commits?author=KorvinSzanto "Code") | [<img src="https://avatars.githubusercontent.com/u/8513053?v=4" width="110px;"/><br /><sub>Lewis Foster</sub>](https://lewisfoster.foo/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sniff122 "Code") | [<img src="https://avatars.githubusercontent.com/u/33877541?v=4" width="110px;"/><br /><sub>Logan Swartzendruber</sub>](https://github.com/loganswartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=loganswartz "Code") | [<img src="https://avatars.githubusercontent.com/u/1156208?v=4" width="110px;"/><br /><sub>Lorenzo P.</sub>](https://github.com/lopezio)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lopezio "Code") | [<img src="https://avatars.githubusercontent.com/u/33946590?v=4" width="110px;"/><br /><sub>Lukas Jung</sub>](https://github.com/m4us1ne)<br />[💻](https://github.com/snipe/snipe-it/commits?author=m4us1ne "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/10965027?v=4" width="110px;"/><br /><sub>Ellie</sub>](https://leafedfox.xyz/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeafedFox "Code") | [<img src="https://avatars.githubusercontent.com/u/20960555?v=4" width="110px;"/><br /><sub>GA Stamper</sub>](https://github.com/gastamper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gastamper "Code") | [<img src="https://avatars.githubusercontent.com/u/206553556?v=4" width="110px;"/><br /><sub>Guillaume Lefranc</sub>](https://github.com/gl-pup)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gl-pup "Code") | [<img src="https://avatars.githubusercontent.com/u/733892?v=4" width="110px;"/><br /><sub>Hajo Möller</sub>](https://github.com/dasjoe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dasjoe "Code") | [<img src="https://avatars.githubusercontent.com/u/3420063?v=4" width="110px;"/><br /><sub>Istvan Basa</sub>](https://github.com/pottom)<br />[💻](https://github.com/snipe/snipe-it/commits?author=pottom "Code") | [<img src="https://avatars.githubusercontent.com/u/810824?v=4" width="110px;"/><br /><sub>JJ Asghar</sub>](https://jjasghar.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jjasghar "Code") | [<img src="https://avatars.githubusercontent.com/u/40404495?v=4" width="110px;"/><br /><sub>James E. Msenga</sub>](https://github.com/JemCdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JemCdo "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/6865786?v=4" width="110px;"/><br /><sub>Jan Felix Wiebe</sub>](https://github.com/jfwiebe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [<img src="https://avatars.githubusercontent.com/u/43412008?v=4" width="110px;"/><br /><sub>Jo Drexl</sub>](https://www.nfon.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [<img src="https://avatars.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>Austin Sasko</sub>](https://github.com/austinsasko)<br />[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [<img src="https://avatars.githubusercontent.com/u/4875039?v=4" width="110px;"/><br /><sub>Jasson</sub>](http://jassoncordones.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") | [<img src="https://avatars.githubusercontent.com/u/76069640?v=4" width="110px;"/><br /><sub>Okean</sub>](https://github.com/Tinyblargon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tinyblargon "Code") | [<img src="https://avatars.githubusercontent.com/u/6515064?v=4" width="110px;"/><br /><sub>Alejandro Medrano</sub>](https://www.lst.tfo.upm.es/alejandro-medrano/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=amedranogil "Code") | [<img src="https://avatars.githubusercontent.com/u/58696401?v=4" width="110px;"/><br /><sub>Lukas Kraic</sub>](https://github.com/lukaskraic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukaskraic "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/1571724?v=4" width="110px;"/><br /><sub>Герхард PICCORO Lenz McKAY </sub>](https://github-readme-stats.vercel.app/api?username=mckaygerhard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mckaygerhard "Code") | [<img src="https://avatars.githubusercontent.com/u/15015119?v=4" width="110px;"/><br /><sub>Johannes Pollitt</sub>](https://github.com/FlorestanII)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorestanII "Code") | [<img src="https://avatars.githubusercontent.com/u/14185442?v=4" width="110px;"/><br /><sub>Michael Strobel</sub>](https://strobelm.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=strobelm "Code") | [<img src="https://avatars.githubusercontent.com/u/634790?v=4" width="110px;"/><br /><sub>Nicky West</sub>](http://nickwest.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nickwest "Code") | [<img src="https://avatars.githubusercontent.com/u/1347327?v=4" width="110px;"/><br /><sub>akaspeh1</sub>](https://github.com/akaspeh1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akaspeh1 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/1571724?v=4" width="110px;"/><br /><sub>Герхард PICCORO Lenz McKAY </sub>](https://github-readme-stats.vercel.app/api?username=mckaygerhard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mckaygerhard "Code") | [<img src="https://avatars.githubusercontent.com/u/15015119?v=4" width="110px;"/><br /><sub>Johannes Pollitt</sub>](https://github.com/FlorestanII)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorestanII "Code") | [<img src="https://avatars.githubusercontent.com/u/14185442?v=4" width="110px;"/><br /><sub>Michael Strobel</sub>](https://strobelm.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=strobelm "Code") | [<img src="https://avatars.githubusercontent.com/u/634790?v=4" width="110px;"/><br /><sub>Nicky West</sub>](http://nickwest.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nickwest "Code") | [<img src="https://avatars.githubusercontent.com/u/1347327?v=4" width="110px;"/><br /><sub>akaspeh1</sub>](https://github.com/akaspeh1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akaspeh1 "Code") | [<img src="https://avatars.githubusercontent.com/u/2880129?v=4" width="110px;"/><br /><sub>Sebastian Marsching</sub>](http://sebastian.marsching.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=smarsching "Code") | [<img src="https://avatars.githubusercontent.com/u/40658372?v=4" width="110px;"/><br /><sub>Mo</sub>](https://github.com/mohammad-ahmadi1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mohammad-ahmadi1 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/20994684?v=4" width="110px;"/><br /><sub>Owen V. Hayes</sub>](https://github.com/MarvelousAnything)<br />[💻](https://github.com/snipe/snipe-it/commits?author=MarvelousAnything "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
59
app/Actions/Categories/DestroyCategoryAction.php
Normal file
59
app/Actions/Categories/DestroyCategoryAction.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Categories;
|
||||
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssetModels;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DestroyCategoryAction
|
||||
{
|
||||
/**
|
||||
* @throws ItemStillHasAssets
|
||||
* @throws ItemStillHasAssetModels
|
||||
* @throws ItemStillHasComponents
|
||||
* @throws ItemStillHasAccessories
|
||||
* @throws ItemStillHasLicenses
|
||||
* @throws ItemStillHasConsumables
|
||||
*/
|
||||
static function run(Category $category): bool
|
||||
{
|
||||
$category->loadCount([
|
||||
'assets as assets_count',
|
||||
'accessories as accessories_count',
|
||||
'consumables as consumables_count',
|
||||
'components as components_count',
|
||||
'licenses as licenses_count',
|
||||
'models as models_count'
|
||||
]);
|
||||
|
||||
if ($category->assets_count > 0) {
|
||||
throw new ItemStillHasAssets($category);
|
||||
}
|
||||
if ($category->accessories_count > 0) {
|
||||
throw new ItemStillHasAccessories($category);
|
||||
}
|
||||
if ($category->consumables_count > 0) {
|
||||
throw new ItemStillHasConsumables($category);
|
||||
}
|
||||
if ($category->components_count > 0) {
|
||||
throw new ItemStillHasComponents($category);
|
||||
}
|
||||
if ($category->licenses_count > 0) {
|
||||
throw new ItemStillHasLicenses($category);
|
||||
}
|
||||
if ($category->models_count > 0) {
|
||||
throw new ItemStillHasAssetModels($category);
|
||||
}
|
||||
|
||||
Storage::disk('public')->delete('categories'.'/'.$category->image);
|
||||
$category->delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
63
app/Actions/Manufacturers/DeleteManufacturerAction.php
Normal file
63
app/Actions/Manufacturers/DeleteManufacturerAction.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Manufacturers;
|
||||
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Models\Manufacturer;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DeleteManufacturerAction
|
||||
{
|
||||
/**
|
||||
* @throws ItemStillHasAssets
|
||||
* @throws ItemStillHasComponents
|
||||
* @throws ItemStillHasAccessories
|
||||
* @throws ItemStillHasLicenses
|
||||
* @throws ItemStillHasConsumables
|
||||
*/
|
||||
static function run(Manufacturer $manufacturer): bool
|
||||
{
|
||||
$manufacturer->loadCount([
|
||||
'assets as assets_count',
|
||||
'accessories as accessories_count',
|
||||
'consumables as consumables_count',
|
||||
'components as components_count',
|
||||
'licenses as licenses_count',
|
||||
]);
|
||||
|
||||
if ($manufacturer->assets_count > 0) {
|
||||
throw new ItemStillHasAssets($manufacturer);
|
||||
}
|
||||
if ($manufacturer->accessories_count > 0) {
|
||||
throw new ItemStillHasAccessories($manufacturer);
|
||||
}
|
||||
if ($manufacturer->consumables_count > 0) {
|
||||
throw new ItemStillHasConsumables($manufacturer);
|
||||
}
|
||||
if ($manufacturer->components_count > 0) {
|
||||
throw new ItemStillHasComponents($manufacturer);
|
||||
}
|
||||
if ($manufacturer->licenses_count > 0) {
|
||||
throw new ItemStillHasLicenses($manufacturer);
|
||||
}
|
||||
|
||||
if ($manufacturer->image) {
|
||||
try {
|
||||
Storage::disk('public')->delete('manufacturers/'.$manufacturer->image);
|
||||
} catch (\Exception $e) {
|
||||
Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
$manufacturer->delete();
|
||||
//dd($manufacturer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
72
app/Actions/Suppliers/DestroySupplierAction.php
Normal file
72
app/Actions/Suppliers/DestroySupplierAction.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Suppliers;
|
||||
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Models\Supplier;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasMaintenances;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DestroySupplierAction
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @throws ItemStillHasLicenses
|
||||
* @throws ItemStillHasAssets
|
||||
* @throws ItemStillHasMaintenances
|
||||
* @throws ItemStillHasAccessories
|
||||
* @throws ItemStillHasConsumables
|
||||
* @throws ItemStillHasComponents
|
||||
*/
|
||||
static function run(Supplier $supplier): bool
|
||||
{
|
||||
$supplier->loadCount([
|
||||
'maintenances as maintenances_count',
|
||||
'assets as assets_count',
|
||||
'licenses as licenses_count',
|
||||
'accessories as accessories_count',
|
||||
'consumables as consumables_count',
|
||||
'components as components_count',
|
||||
]);
|
||||
if ($supplier->assets_count > 0) {
|
||||
throw new ItemStillHasAssets($supplier);
|
||||
}
|
||||
|
||||
if ($supplier->maintenances_count > 0) {
|
||||
throw new ItemStillHasMaintenances($supplier);
|
||||
}
|
||||
|
||||
if ($supplier->licenses_count > 0) {
|
||||
throw new ItemStillHasLicenses($supplier);
|
||||
}
|
||||
|
||||
if ($supplier->accessories_count > 0) {
|
||||
throw new ItemStillHasAccessories($supplier);
|
||||
}
|
||||
|
||||
if ($supplier->consumables_count > 0) {
|
||||
throw new ItemStillHasConsumables($supplier);
|
||||
}
|
||||
|
||||
if ($supplier->components_count > 0) {
|
||||
throw new ItemStillHasComponents($supplier);
|
||||
}
|
||||
|
||||
if ($supplier->image) {
|
||||
try {
|
||||
Storage::disk('public')->delete('suppliers/'.$supplier->image);
|
||||
} catch (\Exception $e) {
|
||||
Log::info($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$supplier->delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -317,9 +317,21 @@ class LdapSync extends Command
|
||||
if($ldap_map["jobtitle"] != null){
|
||||
$user->jobtitle = $item['jobtitle'];
|
||||
}
|
||||
if($ldap_map["address"] != null){
|
||||
$user->address = $item['address'];
|
||||
}
|
||||
if($ldap_map["city"] != null){
|
||||
$user->city = $item['city'];
|
||||
}
|
||||
if($ldap_map["state"] != null){
|
||||
$user->state = $item['state'];
|
||||
}
|
||||
if($ldap_map["country"] != null){
|
||||
$user->country = $item['country'];
|
||||
}
|
||||
if($ldap_map["zip"] != null){
|
||||
$user->zip = $item['zip'];
|
||||
}
|
||||
if($ldap_map["dept"] != null){
|
||||
$user->department_id = $department->id;
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ use Symfony\Component\Console\Input\InputOption;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Console\Helper\ProgressIndicator;
|
||||
|
||||
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes
|
||||
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
|
||||
|
||||
/**
|
||||
* Class ObjectImportCommand
|
||||
@@ -52,6 +50,9 @@ class ObjectImportCommand extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes
|
||||
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
|
||||
|
||||
$this->progressIndicator = new ProgressIndicator($this->output);
|
||||
|
||||
$filename = $this->argument('filename');
|
||||
|
||||
@@ -3,13 +3,18 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Mail\UnacceptedAssetReminderMail;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Asset;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CheckoutAssetNotification;
|
||||
use App\Notifications\CurrentInventory;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendAcceptanceReminder extends Command
|
||||
@@ -26,7 +31,7 @@ class SendAcceptanceReminder extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This will resend users with unaccepted assets a reminder to accept or decline them.';
|
||||
protected $description = 'This will resend users with unaccepted items a reminder to accept or decline them.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
@@ -45,19 +50,30 @@ class SendAcceptanceReminder extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$pending = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')
|
||||
->whereHas('checkoutable', function($query) {
|
||||
$query->where('accepted_at', null)
|
||||
->where('declined_at', null);
|
||||
})
|
||||
->with(['assignedTo', 'checkoutable.assignedTo', 'checkoutable.model', 'checkoutable.adminuser'])
|
||||
->get();
|
||||
$pending = CheckoutAcceptance::query()
|
||||
->with([
|
||||
'checkoutable' => function (MorphTo $morph) {
|
||||
$morph->morphWith([
|
||||
Asset::class => ['model.category', 'assignedTo', 'adminuser', 'company', 'checkouts'],
|
||||
Accessory::class => ['category', 'company', 'checkouts'],
|
||||
LicenseSeat::class => ['user', 'license', 'checkouts'],
|
||||
Component::class => ['assignedTo', 'company', 'checkouts'],
|
||||
Consumable::class => ['company', 'checkouts'],
|
||||
]);
|
||||
},
|
||||
'assignedTo',
|
||||
])
|
||||
->whereHasMorph(
|
||||
'checkoutable',
|
||||
[Asset::class, Accessory::class, LicenseSeat::class, Component::class, Consumable::class],
|
||||
fn ($q) => $q->whereNull('accepted_at')
|
||||
->whereNull('declined_at')
|
||||
)
|
||||
->pending()
|
||||
->get();
|
||||
|
||||
$count = 0;
|
||||
$unacceptedAssetGroups = $pending
|
||||
->filter(function($acceptance) {
|
||||
return $acceptance->checkoutable_type == 'App\Models\Asset';
|
||||
})
|
||||
->map(function($acceptance) {
|
||||
return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance];
|
||||
})
|
||||
|
||||
@@ -9,6 +9,8 @@ use App\Notifications\ExpectedCheckinAdminNotification;
|
||||
use App\Notifications\ExpectedCheckinNotification;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use App\Helpers\Helper;
|
||||
|
||||
class SendExpectedCheckinAlerts extends Command
|
||||
{
|
||||
@@ -17,7 +19,7 @@ class SendExpectedCheckinAlerts extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'snipeit:expected-checkin';
|
||||
protected $signature = 'snipeit:expected-checkin {--with-output : Display the results in a table in your console in addition to sending the email}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -42,19 +44,47 @@ class SendExpectedCheckinAlerts extends Command
|
||||
public function handle()
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$interval = $settings->audit_warning_days ?? 0;
|
||||
$interval = $settings->due_checkin_days ?? 0;
|
||||
$today = Carbon::now();
|
||||
$interval_date = $today->copy()->addDays($interval);
|
||||
|
||||
$count = 0;
|
||||
|
||||
if (!$this->option('with-output')) {
|
||||
$this->info('Run this command with the --with-output option to see the full list in the console.');
|
||||
}
|
||||
|
||||
$assets = Asset::whereNull('deleted_at')->DueOrOverdueForCheckin($settings)->orderBy('assets.expected_checkin', 'desc')->get();
|
||||
|
||||
$this->info($assets->count().' assets must be checked in on or before '.$interval_date.' is deadline');
|
||||
$this->info($assets->count().' assets must be checked on or before '.Helper::getFormattedDateObject($interval_date, 'date', false));
|
||||
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
if ($asset->assignedTo && (isset($asset->assignedTo->email)) && ($asset->assignedTo->email!='') && $asset->checkedOutToUser()) {
|
||||
$this->info('Sending User ExpectedCheckinNotification to: '.$asset->assignedTo->email);
|
||||
$asset->assignedTo->notify((new ExpectedCheckinNotification($asset)));
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->option('with-output')) {
|
||||
if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) {
|
||||
$this->table(
|
||||
[
|
||||
trans('general.id'),
|
||||
trans('admin/hardware/form.tag'),
|
||||
trans('admin/hardware/form.model'),
|
||||
trans('general.model_no'),
|
||||
trans('general.purchase_date'),
|
||||
trans('admin/hardware/form.expected_checkin'),
|
||||
],
|
||||
$assets->map(fn($assets) => [
|
||||
trans('general.id') => $assets->id,
|
||||
trans('admin/hardware/form.tag') => $assets->asset_tag,
|
||||
trans('admin/hardware/form.model') => $assets->model->name,
|
||||
trans('general.model_no') => $assets->model->model_number,
|
||||
trans('general.purchase_date') => $assets->purchase_date_formatted,
|
||||
trans('admin/hardware/form.eol_date') => $assets->expected_checkin_formattedDate ? $assets->expected_checkin_formattedDate . ' (' . $assets->expected_checkin_diff_for_humans . ')' : '',
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,10 +93,11 @@ class SendExpectedCheckinAlerts extends Command
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item) {
|
||||
return new AlertRecipient($item);
|
||||
});
|
||||
|
||||
$this->info('Sending Admin ExpectedCheckinNotification to: '.$settings->alert_email);
|
||||
\Notification::send($recipients, new ExpectedCheckinAdminNotification($assets));
|
||||
Notification::send($recipients, new ExpectedCheckinAdminNotification($assets));
|
||||
|
||||
}
|
||||
|
||||
$this->info('Sent checkin reminders to to '.$count.' users.');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ class SendExpirationAlerts extends Command
|
||||
trans('general.name') => $item->name,
|
||||
trans('general.purchase_date') => $item->purchase_date_formatted,
|
||||
trans('admin/licenses/form.expiration') => $item->expires_formatted_date,
|
||||
trans('mail.expires') => $item->expires_diff_for_humans,
|
||||
trans('mail.expires') => $item->expires_formatted_date ? $item->expires_diff_for_humans : '',
|
||||
trans('admin/licenses/form.termination_date') => $item->terminates_formatted_date,
|
||||
trans('mail.terminates') => $item->terminates_diff_for_humans
|
||||
])
|
||||
|
||||
@@ -52,7 +52,9 @@ class SendInventoryAlerts extends Command
|
||||
return new AlertRecipient($item);
|
||||
});
|
||||
|
||||
\Notification::send($recipients, new InventoryAlert($items, $settings->alert_threshold));
|
||||
Notification::send($recipients, new InventoryAlert($items, $settings->alert_threshold));
|
||||
} else {
|
||||
$this->info('No low inventory items found. No mail sent.');
|
||||
}
|
||||
} else {
|
||||
if ($settings->alert_email == '') {
|
||||
|
||||
@@ -16,7 +16,7 @@ class SendUpcomingAuditReport extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:upcoming-audits';
|
||||
protected $signature = 'snipeit:upcoming-audits {--with-output : Display the results in a table in your console in addition to sending the email}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -47,43 +47,69 @@ class SendUpcomingAuditReport extends Command
|
||||
$today = Carbon::now();
|
||||
$interval_date = $today->copy()->addDays($interval);
|
||||
|
||||
$assets = Asset::whereNull('deleted_at')->dueOrOverdueForAudit($settings)->orderBy('assets.next_audit_date', 'asc')->get();
|
||||
$this->info($assets->count() . ' assets must be audited in on or before ' . $interval_date . ' is deadline');
|
||||
|
||||
|
||||
if ((count($assets) !== 0) && ($assets->count() > 0) && ($settings->alert_email != '')) {
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))
|
||||
->map(fn($item) => trim($item))
|
||||
->filter(fn($item) => !empty($item))
|
||||
->all();
|
||||
|
||||
|
||||
$this->info('Sending Admin SendUpcomingAuditNotification to: ' . $settings->alert_email);
|
||||
Mail::to($recipients)->send(new SendUpcomingAuditMail($assets, $settings->audit_warning_days));
|
||||
|
||||
$this->table(
|
||||
[
|
||||
trans('general.id'),
|
||||
trans('general.name'),
|
||||
trans('general.last_audit'),
|
||||
trans('general.next_audit_date'),
|
||||
trans('mail.Days'),
|
||||
trans('mail.supplier'),
|
||||
trans('mail.assigned_to'),
|
||||
|
||||
],
|
||||
$assets->map(fn($item) => [
|
||||
trans('general.id') => $item->id,
|
||||
trans('general.name') => $item->display_name,
|
||||
trans('general.last_audit') => $item->last_audit_formatted_date,
|
||||
trans('general.next_audit_date') => $item->next_audit_formatted_date,
|
||||
trans('mail.Days') => round($item->next_audit_diff_in_days),
|
||||
trans('mail.supplier') => $item->supplier ? $item->supplier->name : '',
|
||||
trans('mail.assigned_to') => $item->assignedTo ? $item->assignedTo->display_name : '',
|
||||
])
|
||||
);
|
||||
$assets_query = Asset::whereNull('deleted_at')->dueOrOverdueForAudit($settings)->orderBy('assets.next_audit_date', 'asc')->with('supplier');
|
||||
$asset_count = $assets_query->count();
|
||||
$this->info(number_format($asset_count) . ' assets must be audited on or before ' . $interval_date);
|
||||
if (!$this->option('with-output')) {
|
||||
$this->info('Run this command with the --with-output option to see the full list in the console.');
|
||||
}
|
||||
|
||||
|
||||
if ($asset_count > 0) {
|
||||
|
||||
$assets_for_email = $assets_query->limit(30)->get();
|
||||
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
if ($settings->alert_email != '') {
|
||||
|
||||
$recipients = collect(explode(',', $settings->alert_email))
|
||||
->map(fn($item) => trim($item))
|
||||
->filter(fn($item) => !empty($item))
|
||||
->all();
|
||||
|
||||
Mail::to($recipients)->send(new SendUpcomingAuditMail($assets_for_email, $settings->audit_warning_days, $asset_count));
|
||||
$this->info('Audit notification sent to: ' . $settings->alert_email);
|
||||
|
||||
} else {
|
||||
$this->info('There is no admin alert email set so no email will be sent.');
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($this->option('with-output')) {
|
||||
|
||||
|
||||
// Get the full list if the user wants output in the console
|
||||
$assets_for_output = $assets_query->limit(null)->get();
|
||||
|
||||
$this->table(
|
||||
[
|
||||
trans('general.id'),
|
||||
trans('general.name'),
|
||||
trans('general.last_audit'),
|
||||
trans('general.next_audit_date'),
|
||||
trans('mail.Days'),
|
||||
trans('mail.supplier'),
|
||||
trans('mail.assigned_to'),
|
||||
|
||||
],
|
||||
$assets_for_output->map(fn($item) => [
|
||||
trans('general.id') => $item->id,
|
||||
trans('general.name') => $item->display_name,
|
||||
trans('general.last_audit') => $item->last_audit_formatted_date,
|
||||
trans('general.next_audit_date') => $item->next_audit_formatted_date,
|
||||
trans('mail.Days') => round($item->next_audit_diff_in_days),
|
||||
trans('mail.supplier') => $item->supplier ? $item->supplier->name : '',
|
||||
trans('mail.assigned_to') => $item->assignedTo ? $item->assignedTo->display_name : '',
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
$this->info('There are no assets due for audit in the next ' . $interval . ' days.');
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ class SystemBackup extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
ini_set('max_execution_time', env('BACKUP_TIME_LIMIT', 600)); //600 seconds = 10 minutes
|
||||
|
||||
if ($this->option('filename')) {
|
||||
$filename = $this->option('filename');
|
||||
|
||||
|
||||
33
app/Enums/ActionType.php
Normal file
33
app/Enums/ActionType.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
enum ActionType: string
|
||||
{
|
||||
// General
|
||||
case Create = 'create';
|
||||
case Update = 'update';
|
||||
case Delete = 'delete';
|
||||
case Restore = 'restore';
|
||||
|
||||
// Assets/Accessories/Components/Licenses/Consumables
|
||||
case Checkout = 'checkout';
|
||||
case CheckinFrom = 'checkin from';
|
||||
case Requested = 'requested';
|
||||
case RequestCanceled = 'request canceled';
|
||||
case Accepted = 'accepted';
|
||||
case Declined = 'declined';
|
||||
case Audit = 'audit';
|
||||
case NoteAdded = 'note added';
|
||||
|
||||
// Users
|
||||
case TwoFactorReset = '2FA reset';
|
||||
case Merged = 'merged';
|
||||
|
||||
// Licenses
|
||||
case DeleteSeats = 'delete seats';
|
||||
case AddSeats = 'add seats';
|
||||
|
||||
// File Uploads
|
||||
case Uploaded = 'uploaded';
|
||||
case UploadDeleted = 'upload deleted';
|
||||
}
|
||||
10
app/Exceptions/ItemStillHasAccessories.php
Normal file
10
app/Exceptions/ItemStillHasAccessories.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasAccessories extends ItemStillHasChildren
|
||||
{
|
||||
//
|
||||
}
|
||||
10
app/Exceptions/ItemStillHasAssetModels.php
Normal file
10
app/Exceptions/ItemStillHasAssetModels.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasAssetModels extends ItemStillHasChildren
|
||||
{
|
||||
//
|
||||
}
|
||||
9
app/Exceptions/ItemStillHasAssets.php
Normal file
9
app/Exceptions/ItemStillHasAssets.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasAssets extends ItemStillHasChildren
|
||||
{
|
||||
}
|
||||
14
app/Exceptions/ItemStillHasChildren.php
Normal file
14
app/Exceptions/ItemStillHasChildren.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasChildren extends Exception
|
||||
{
|
||||
//public function __construct($message, $code = 0, Exception $previous = null, $parent, $children)
|
||||
//{
|
||||
// trans()
|
||||
//
|
||||
//}
|
||||
}
|
||||
10
app/Exceptions/ItemStillHasComponents.php
Normal file
10
app/Exceptions/ItemStillHasComponents.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasComponents extends ItemStillHasChildren
|
||||
{
|
||||
//
|
||||
}
|
||||
10
app/Exceptions/ItemStillHasConsumables.php
Normal file
10
app/Exceptions/ItemStillHasConsumables.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasConsumables extends ItemStillHasChildren
|
||||
{
|
||||
//
|
||||
}
|
||||
10
app/Exceptions/ItemStillHasLicenses.php
Normal file
10
app/Exceptions/ItemStillHasLicenses.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasLicenses extends ItemStillHasChildren
|
||||
{
|
||||
//
|
||||
}
|
||||
10
app/Exceptions/ItemStillHasMaintenances.php
Normal file
10
app/Exceptions/ItemStillHasMaintenances.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasMaintenances extends ItemStillHasChildren
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -40,6 +40,8 @@ class IconHelper
|
||||
return 'fa-solid fa-trash-arrow-up';
|
||||
case 'external-link':
|
||||
return 'fa fa-external-link';
|
||||
case 'link':
|
||||
return 'fa fa-link';
|
||||
case 'email':
|
||||
return 'fa-regular fa-envelope';
|
||||
case 'phone':
|
||||
@@ -195,6 +197,10 @@ class IconHelper
|
||||
case 'note':
|
||||
case 'notes':
|
||||
return 'fas fa-sticky-note';
|
||||
case 'tip':
|
||||
return 'fa-solid fa-lightbulb';
|
||||
case 'highlight':
|
||||
return 'fa-solid fa-highlighter';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,11 @@ class AccessoriesController extends Controller
|
||||
|
||||
$accessory = $request->handleImages($accessory);
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
if($request->get('redirect_option') === 'back'){
|
||||
session()->put(['redirect_option' => 'index']);
|
||||
} else {
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
}
|
||||
|
||||
if ($accessory->save()) {
|
||||
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
|
||||
|
||||
@@ -116,8 +116,6 @@ class AcceptanceController extends Controller
|
||||
|
||||
$item = $acceptance->checkoutable_type::find($acceptance->checkoutable_id);
|
||||
|
||||
|
||||
|
||||
// If signatures are required, make sure we have one
|
||||
if (Setting::getSettings()->require_accept_signature == '1') {
|
||||
|
||||
@@ -140,7 +138,7 @@ class AcceptanceController extends Controller
|
||||
// Convert PDF logo to base64 for TCPDF
|
||||
// This is needed for TCPDF to properly embed the image if it's a png and the cache isn't writable
|
||||
$encoded_logo = null;
|
||||
if ($settings->acceptance_pdf_logo) {
|
||||
if (($settings->acceptance_pdf_logo) && (Storage::disk('public')->exists($settings->acceptance_pdf_logo))) {
|
||||
$encoded_logo = base64_encode(file_get_contents(public_path() . '/uploads/' . $settings->acceptance_pdf_logo));
|
||||
}
|
||||
|
||||
@@ -164,11 +162,9 @@ class AcceptanceController extends Controller
|
||||
'signature' => (($sig_filename && array_key_exists('1', $encoded_image))) ? $encoded_image[1] : null,
|
||||
'logo' => ($encoded_logo) ?? null,
|
||||
'date_settings' => $settings->date_display_format,
|
||||
'admin' => auth()->user()->present()?->fullName,
|
||||
'qty' => $acceptance->qty ?? 1,
|
||||
];
|
||||
|
||||
|
||||
if ($request->input('asset_acceptance') == 'accepted') {
|
||||
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ class AssetsController extends Controller
|
||||
// Search custom fields by column name
|
||||
foreach ($all_custom_fields as $field) {
|
||||
if ($request->filled($field->db_column_name()) && $field->db_column_name()) {
|
||||
$assets->where($field->db_column_name(), '=', $request->input($field->db_column_name()));
|
||||
$assets->where('assets.'.$field->db_column_name(), '=', $request->input($field->db_column_name()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Actions\Categories\DestroyCategoryAction;
|
||||
use App\Exceptions\ItemStillHasChildren;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\CategoriesTransformer;
|
||||
@@ -224,17 +226,21 @@ class CategoriesController extends Controller
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy($id) : JsonResponse
|
||||
public function destroy(Category $category): JsonResponse
|
||||
{
|
||||
$this->authorize('delete', Category::class);
|
||||
$category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count')->findOrFail($id);
|
||||
|
||||
if (! $category->isDeletable()) {
|
||||
try {
|
||||
DestroyCategoryAction::run(category: $category);
|
||||
} catch (ItemStillHasChildren $e) {
|
||||
return response()->json(
|
||||
Helper::formatStandardApiResponse('error', null, trans('admin/categories/message.assoc_items', ['asset_type'=>$category->category_type]))
|
||||
Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.general_assoc_warning', ['asset_type' => $category->category_type]))
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
return response()->json(
|
||||
Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong'))
|
||||
);
|
||||
}
|
||||
$category->delete();
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/categories/message.delete.success')));
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\StoreDepartmentRequest;
|
||||
use App\Http\Transformers\DepartmentsTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Department;
|
||||
@@ -26,18 +27,19 @@ class DepartmentsController extends Controller
|
||||
$allowed_columns = ['id', 'name', 'image', 'users_count', 'notes'];
|
||||
|
||||
$departments = Department::select(
|
||||
'departments.id',
|
||||
'departments.name',
|
||||
'departments.phone',
|
||||
'departments.fax',
|
||||
'departments.location_id',
|
||||
'departments.company_id',
|
||||
'departments.manager_id',
|
||||
'departments.created_at',
|
||||
'departments.updated_at',
|
||||
'departments.image',
|
||||
'departments.notes',
|
||||
)->with('users')->with('location')->with('manager')->with('company')->withCount('users as users_count');
|
||||
[
|
||||
'departments.id',
|
||||
'departments.name',
|
||||
'departments.phone',
|
||||
'departments.fax',
|
||||
'departments.location_id',
|
||||
'departments.company_id',
|
||||
'departments.manager_id',
|
||||
'departments.created_at',
|
||||
'departments.updated_at',
|
||||
'departments.image',
|
||||
'departments.notes'
|
||||
])->with('users')->with('location')->with('manager')->with('company')->withCount('users as users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$departments = $departments->TextSearch($request->input('search'));
|
||||
@@ -94,18 +96,17 @@ class DepartmentsController extends Controller
|
||||
* @since [v4.0]
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
*/
|
||||
public function store(ImageUploadRequest $request) : JsonResponse
|
||||
public function store(StoreDepartmentRequest $request): JsonResponse
|
||||
{
|
||||
$this->authorize('create', Department::class);
|
||||
$department = new Department;
|
||||
$department->fill($request->all());
|
||||
$department->fill($request->validated());
|
||||
$department = $request->handleImages($department);
|
||||
|
||||
$department->created_by = auth()->id();
|
||||
$department->manager_id = ($request->filled('manager_id') ? $request->input('manager_id') : null);
|
||||
|
||||
if ($department->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $department, trans('admin/departments/message.create.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new DepartmentsTransformer)->transformDepartment($department), trans('admin/departments/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $department->getErrors()));
|
||||
|
||||
@@ -121,7 +122,7 @@ class DepartmentsController extends Controller
|
||||
public function show($id) : array
|
||||
{
|
||||
$this->authorize('view', Department::class);
|
||||
$department = Department::findOrFail($id);
|
||||
$department = Department::withCount('users as users_count')->findOrFail($id);
|
||||
return (new DepartmentsTransformer)->transformDepartment($department);
|
||||
}
|
||||
|
||||
@@ -141,7 +142,7 @@ class DepartmentsController extends Controller
|
||||
$department = $request->handleImages($department);
|
||||
|
||||
if ($department->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $department, trans('admin/departments/message.update.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new DepartmentsTransformer)->transformDepartment($department), trans('admin/departments/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $department->getErrors()));
|
||||
|
||||
@@ -24,7 +24,7 @@ class GroupsController extends Controller
|
||||
|
||||
$this->authorize('view', Group::class);
|
||||
|
||||
$groups = Group::select('id', 'name', 'permissions', 'notes', 'created_at', 'updated_at', 'created_by')->with('adminuser')->withCount('users as users_count');
|
||||
$groups = Group::select(['id', 'name', 'permissions', 'notes', 'created_at', 'updated_at', 'created_by'])->with('adminuser')->withCount('users as users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$groups = $groups->TextSearch($request->input('search'));
|
||||
@@ -50,6 +50,7 @@ class GroupsController extends Controller
|
||||
'id',
|
||||
'name',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'users_count',
|
||||
];
|
||||
|
||||
|
||||
@@ -26,11 +26,11 @@ class LicenseSeatsController extends Controller
|
||||
if ($license = License::find($licenseId)) {
|
||||
$this->authorize('view', $license);
|
||||
|
||||
$seats = LicenseSeat::with('license', 'user', 'asset', 'user.department')
|
||||
$seats = LicenseSeat::with('license', 'user', 'asset', 'user.department', 'user.company', 'asset.company')
|
||||
->where('license_seats.license_id', $licenseId);
|
||||
|
||||
if ($request->input('status') == 'available') {
|
||||
$seats->whereNull('license_seats.assigned_to');
|
||||
$seats->whereNull('license_seats.assigned_to')->whereNull('license_seats.asset_id');
|
||||
}
|
||||
|
||||
if ($request->input('status') == 'assigned') {
|
||||
@@ -40,8 +40,10 @@ class LicenseSeatsController extends Controller
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
if ($request->input('sort') == 'department') {
|
||||
if ($request->input('sort') == 'assigned_user.department') {
|
||||
$seats->OrderDepartments($order);
|
||||
} elseif ($request->input('sort') == 'assigned_user.company') {
|
||||
$seats->OrderCompany($order);
|
||||
} else {
|
||||
$seats->orderBy('updated_at', $order);
|
||||
}
|
||||
@@ -83,7 +85,7 @@ class LicenseSeatsController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat not found'));
|
||||
}
|
||||
// 2. does the seat belong to the specified license?
|
||||
if (! $license = $licenseSeat->license()->first() || $license->id != intval($licenseId)) {
|
||||
if (! $licenseSeat = $licenseSeat->license()->first() || $licenseSeat->id != intval($licenseId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat does not belong to the specified license'));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Actions\Manufacturers\DeleteManufacturerAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasChildren;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\ManufacturersTransformer;
|
||||
@@ -184,19 +191,19 @@ class ManufacturersController extends Controller
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
*/
|
||||
public function destroy($id) : JsonResponse
|
||||
public function destroy(Manufacturer $manufacturer): JsonResponse
|
||||
{
|
||||
$this->authorize('delete', Manufacturer::class);
|
||||
$manufacturer = Manufacturer::findOrFail($id);
|
||||
$this->authorize('delete', $manufacturer);
|
||||
|
||||
if ($manufacturer->isDeletable()) {
|
||||
$manufacturer->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/manufacturers/message.delete.success')));
|
||||
try {
|
||||
DeleteManufacturerAction::run($manufacturer);
|
||||
} catch (ItemStillHasChildren $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.general_assoc_warning', ['item' => trans('general.manufacturer')])));
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/manufacturers/message.assoc_users')));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/manufacturers/message.delete.success')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Actions\Suppliers\DestroySupplierAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasMaintenances;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
@@ -191,27 +198,40 @@ class SuppliersController extends Controller
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
*/
|
||||
public function destroy($id) : JsonResponse
|
||||
public function destroy(Supplier $supplier): JsonResponse
|
||||
{
|
||||
$this->authorize('delete', Supplier::class);
|
||||
$supplier = Supplier::with('maintenances', 'assets', 'licenses')->withCount('maintenances as maintenances_count', 'assets as assets_count', 'licenses as licenses_count')->findOrFail($id);
|
||||
$this->authorize('delete', $supplier);
|
||||
|
||||
|
||||
if ($supplier->assets_count > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count])));
|
||||
try {
|
||||
DestroySupplierAction::run(supplier: $supplier);
|
||||
} catch (ItemStillHasAssets $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_assets', [
|
||||
'asset_count' => (int) $supplier->assets_count, 'item' => trans('general.supplier')
|
||||
])));
|
||||
} catch (ItemStillHasMaintenances $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_maintenances', [
|
||||
'asset_maintenances_count' => $supplier->asset_maintenances_count, 'item' => trans('general.supplier')
|
||||
])));
|
||||
} catch (ItemStillHasLicenses $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_licenses', [
|
||||
'licenses_count' => (int) $supplier->licenses_count, 'item' => trans('general.supplier')
|
||||
])));
|
||||
} catch (ItemStillHasAccessories $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_accessories', [
|
||||
'accessories_count' => (int) $supplier->accessories_count, 'item' => trans('general.supplier')
|
||||
])));
|
||||
} catch (ItemStillHasConsumables $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_consumables', [
|
||||
'consumables_count' => (int) $supplier->consumables_count, 'item' => trans('general.supplier')
|
||||
])));
|
||||
} catch (ItemStillHasComponents $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_components', [
|
||||
'components_count' => (int) $supplier->components_count, 'item' => trans('general.supplier')
|
||||
])));
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
|
||||
}
|
||||
|
||||
if ($supplier->maintenances_count > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_maintenances', ['maintenances_count' => $supplier->maintenances_count])));
|
||||
}
|
||||
|
||||
if ($supplier->licenses_count > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count])));
|
||||
}
|
||||
|
||||
$supplier->delete();
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/suppliers/message.delete.success')));
|
||||
}
|
||||
|
||||
|
||||
@@ -240,10 +240,6 @@ class BulkAssetsController extends Controller
|
||||
$custom_fields_to_null[str_replace('null', '', $key)] = $value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (! $request->filled('ids') || count($request->input('ids')) == 0) {
|
||||
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.update.no_assets_selected'));
|
||||
@@ -274,6 +270,7 @@ class BulkAssetsController extends Controller
|
||||
|| ($request->filled('company_id'))
|
||||
|| ($request->filled('status_id'))
|
||||
|| ($request->filled('model_id'))
|
||||
|| ($request->filled('notes'))
|
||||
|| ($request->filled('next_audit_date'))
|
||||
|| ($request->filled('asset_eol_date'))
|
||||
|| ($request->filled('null_name'))
|
||||
|
||||
59
app/Http/Controllers/BulkCategoriesController.php
Normal file
59
app/Http/Controllers/BulkCategoriesController.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Categories\DestroyCategoryAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssetModels;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BulkCategoriesController extends Controller
|
||||
{
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
$this->authorize('delete', Category::class);
|
||||
|
||||
$errors = [];
|
||||
$success_count = 0;
|
||||
|
||||
foreach ($request->ids as $id) {
|
||||
$category = Category::find($id);
|
||||
if (is_null($category)) {
|
||||
$errors[] = trans('admin/categories/message.does_not_exist');
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
DestroyCategoryAction::run(category: $category);
|
||||
$success_count++;
|
||||
} catch (ItemStillHasAccessories $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_assets_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
|
||||
} catch (ItemStillHasAssetModels) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_asset_models_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
|
||||
} catch (ItemStillHasAssets) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_assets_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
|
||||
} catch (ItemStillHasComponents) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_components_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
|
||||
} catch (ItemStillHasConsumables) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_consumables_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
|
||||
} catch (ItemStillHasLicenses) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_licenses_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);;
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
$errors[] = trans('general.something_went_wrong');
|
||||
}
|
||||
}
|
||||
if (count($errors) > 0) {
|
||||
if ($success_count > 0) {
|
||||
return redirect()->route('categories.index')->with('success', trans_choice('admin/categories/message.delete.partial_success', $success_count, ['count' => $success_count]))->with('multi_error_messages', $errors);
|
||||
}
|
||||
return redirect()->route('categories.index')->with('multi_error_messages', $errors);
|
||||
} else {
|
||||
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.delete.bulk_success'));
|
||||
}
|
||||
}
|
||||
}
|
||||
57
app/Http/Controllers/BulkManufacturersController.php
Normal file
57
app/Http/Controllers/BulkManufacturersController.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Manufacturers\DeleteManufacturerAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssetModels;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasChildren;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Models\Manufacturer;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BulkManufacturersController extends Controller
|
||||
{
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
$this->authorize('delete', Manufacturer::class);
|
||||
|
||||
$errors = [];
|
||||
$success_count = 0;
|
||||
foreach ($request->ids as $id) {
|
||||
$manufacturer = Manufacturer::find($id);
|
||||
if (is_null($manufacturer)) {
|
||||
$errors[] = trans('admin/manufacturers/message.does_not_exist');
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
DeleteManufacturerAction::run(manufacturer: $manufacturer);
|
||||
$success_count++;
|
||||
} catch (ItemStillHasAssets $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_assets_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);
|
||||
} catch (ItemStillHasAccessories $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_accessories_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);
|
||||
} catch (ItemStillHasConsumables $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_consumables_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);
|
||||
} catch (ItemStillHasComponents $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_components_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);
|
||||
} catch (ItemStillHasLicenses $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_licenses_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);;
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
$errors[] = trans('general.something_went_wrong');
|
||||
}
|
||||
}
|
||||
if (count($errors) > 0) {
|
||||
if ($success_count > 0) {
|
||||
return redirect()->route('manufacturers.index')->with('success', trans_choice('admin/manufacturers/message.delete.partial_success', $success_count, ['count' => $success_count]))->with('multi_error_messages', $errors);
|
||||
}
|
||||
return redirect()->route('manufacturers.index')->with('multi_error_messages', $errors);
|
||||
} else {
|
||||
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.delete.bulk_success'));
|
||||
}
|
||||
}
|
||||
}
|
||||
58
app/Http/Controllers/BulkSuppliersController.php
Normal file
58
app/Http/Controllers/BulkSuppliersController.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Suppliers\DestroySupplierAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasMaintenances;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Models\Supplier;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BulkSuppliersController extends Controller
|
||||
{
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
$this->authorize('delete', Supplier::class);
|
||||
|
||||
$errors = [];
|
||||
$success_count = 0;
|
||||
|
||||
foreach ($request->ids as $id) {
|
||||
$supplier = Supplier::find($id);
|
||||
if (is_null($supplier)) {
|
||||
$errors[] = trans('admin/suppliers/message.delete.not_found');
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
DestroySupplierAction::run(supplier: $supplier);
|
||||
} catch (ItemStillHasAssets $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_assets', ['asset_count' => (int) $supplier->assets_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
|
||||
} catch (ItemStillHasMaintenances $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_maintenances', ['asset_maintenances_count' => $supplier->asset_maintenances_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
|
||||
} catch (ItemStillHasLicenses $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
|
||||
} catch (ItemStillHasAccessories $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_accessories', ['accessories_count' => (int) $supplier->accessories_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
|
||||
} catch (ItemStillHasConsumables $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_consumables', ['consumables_count' => (int) $supplier->consumables_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
|
||||
} catch (ItemStillHasComponents $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_components', ['components_count' => (int) $supplier->components_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
$errors[] = trans('general.something_went_wrong');
|
||||
}
|
||||
}
|
||||
if (count($errors) > 0) {
|
||||
if ($success_count > 0) {
|
||||
return redirect()->route('suppliers.index')->with('success', trans_choice('admin/suppliers/message.delete.partial_success', $success_count, ['count' => $success_count]))->with('multi_error_messages', $errors);
|
||||
}
|
||||
return redirect()->route('suppliers.index')->with('multi_error_messages', $errors);
|
||||
} else {
|
||||
return redirect()->route('suppliers.index')->with('success', trans('admin/suppliers/message.delete.bulk_success'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Categories\DestroyCategoryAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssetModels;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasChildren;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Category;
|
||||
@@ -143,20 +151,18 @@ class CategoriesController extends Controller
|
||||
* @since [v1.0]
|
||||
* @param int $categoryId
|
||||
*/
|
||||
public function destroy($categoryId) : RedirectResponse
|
||||
public function destroy(Category $category): RedirectResponse
|
||||
{
|
||||
$this->authorize('delete', Category::class);
|
||||
// Check if the category exists
|
||||
if (is_null($category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count')->findOrFail($categoryId))) {
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.not_found'));
|
||||
try {
|
||||
DestroyCategoryAction::run($category);
|
||||
} catch (ItemStillHasChildren $e) {
|
||||
return redirect()->route('categories.index')->with('error', trans('general.bulk_delete_associations.general_assoc_warning', ['item' => trans('general.category')]));
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.delete.error'));
|
||||
}
|
||||
|
||||
if (! $category->isDeletable()) {
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=> $category->category_type]));
|
||||
}
|
||||
|
||||
Storage::disk('public')->delete('categories'.'/'.$category->image);
|
||||
$category->delete();
|
||||
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ use App\Models\Consumable;
|
||||
use App\Models\License;
|
||||
use App\Models\Location;
|
||||
use App\Models\Maintenance;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
@@ -45,12 +46,14 @@ abstract class Controller extends BaseController
|
||||
'accessories' => Accessory::class,
|
||||
'maintenances' => Maintenance::class,
|
||||
'assets' => Asset::class,
|
||||
'audits' => Asset::class,
|
||||
'components' => Component::class,
|
||||
'consumables' => Consumable::class,
|
||||
'hardware' => Asset::class,
|
||||
'licenses' => License::class,
|
||||
'locations' => Location::class,
|
||||
'models' => AssetModel::class,
|
||||
'suppliers' => Supplier::class,
|
||||
'users' => User::class,
|
||||
];
|
||||
|
||||
@@ -58,12 +61,14 @@ abstract class Controller extends BaseController
|
||||
'accessories' => 'private_uploads/accessories/',
|
||||
'maintenances' => 'private_uploads/maintenances/',
|
||||
'assets' => 'private_uploads/assets/',
|
||||
'audits' => 'private_uploads/audits/',
|
||||
'components' => 'private_uploads/components/',
|
||||
'consumables' => 'private_uploads/consumables/',
|
||||
'hardware' => 'private_uploads/assets/',
|
||||
'licenses' => 'private_uploads/licenses/',
|
||||
'locations' => 'private_uploads/locations/',
|
||||
'models' => 'private_uploads/models/',
|
||||
'suppliers' => 'private_uploads/suppliers/',
|
||||
'users' => 'private_uploads/users/',
|
||||
];
|
||||
|
||||
@@ -71,12 +76,14 @@ abstract class Controller extends BaseController
|
||||
'accessories' => 'accessory',
|
||||
'maintenances' => 'maintenance',
|
||||
'assets' => 'asset',
|
||||
'audits' => 'audits',
|
||||
'components' => 'component',
|
||||
'consumables' => 'consumable',
|
||||
'hardware' => 'asset',
|
||||
'licenses' => 'license',
|
||||
'locations' => 'location',
|
||||
'models' => 'model',
|
||||
'suppliers' => 'supplier',
|
||||
'users' => 'user',
|
||||
];
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Models\Group;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use \App\Models\User;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to User Groups for
|
||||
@@ -43,9 +44,20 @@ class GroupsController extends Controller
|
||||
$permissions = config('permissions');
|
||||
$groupPermissions = Helper::selectedPermissionsArray($permissions, $permissions);
|
||||
$selectedPermissions = $request->old('permissions', $groupPermissions);
|
||||
$users_query = User::where('show_in_list', 1)->whereNull('deleted_at');
|
||||
$users_count = $users_query->count();
|
||||
|
||||
$users = collect();
|
||||
if ($users_count <= config('app.max_unpaginated_records')) {
|
||||
$users = $users_query->orderBy('first_name', 'asc')->orderBy('last_name', 'asc')->get();
|
||||
}
|
||||
|
||||
// Show the page
|
||||
return view('groups/edit', compact('permissions', 'selectedPermissions', 'groupPermissions'))->with('group', $group);
|
||||
return view('groups/edit', compact('permissions', 'selectedPermissions', 'groupPermissions'))
|
||||
->with('group', $group)
|
||||
->with('associated_users', [])
|
||||
->with('unselected_users', $users)
|
||||
->with('all_users_count', $users_count);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,11 +72,23 @@ class GroupsController extends Controller
|
||||
// create a new group instance
|
||||
$group = new Group();
|
||||
$group->name = $request->input('name');
|
||||
|
||||
if ($request->filled('permission')) {
|
||||
$group->permissions = json_encode($request->array('permission'));
|
||||
} else {
|
||||
$group->permissions = null;
|
||||
}
|
||||
|
||||
$group->permissions = json_encode($request->input('permission'));
|
||||
$group->created_by = auth()->id();
|
||||
$group->notes = $request->input('notes');
|
||||
|
||||
if ($group->save()) {
|
||||
|
||||
if ($request->filled('users_to_sync')) {
|
||||
$associated_users = explode(',',$request->input('users_to_sync'));
|
||||
$group->users()->sync($associated_users);
|
||||
}
|
||||
return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.create'));
|
||||
}
|
||||
|
||||
@@ -87,8 +111,26 @@ class GroupsController extends Controller
|
||||
if ((!is_array($groupPermissions)) || (!$groupPermissions)) {
|
||||
$groupPermissions = [];
|
||||
}
|
||||
|
||||
$selected_array = Helper::selectedPermissionsArray($permissions, $groupPermissions);
|
||||
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'));
|
||||
|
||||
|
||||
$users_query = User::where('show_in_list', 1)->whereNull('deleted_at');
|
||||
$users_count = $users_query->count();
|
||||
|
||||
$associated_users = collect();
|
||||
$unselected_users = collect();
|
||||
|
||||
if ($users_count <= config('app.max_unpaginated_records')) {
|
||||
$associated_users = $group->users()->where('show_in_list', 1)->orderBy('first_name', 'asc')->orderBy('last_name', 'asc')->get();
|
||||
// Get the unselected users
|
||||
$unselected_users = User::where('show_in_list', 1)->whereNotIn('id', $associated_users->pluck('id')->toArray())->orderBy('first_name', 'asc')->orderBy('last_name', 'asc')->get();
|
||||
}
|
||||
|
||||
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'))
|
||||
->with('associated_users', $associated_users)
|
||||
->with('unselected_users', $unselected_users)
|
||||
->with('all_users_count', $users_count);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,11 +144,24 @@ class GroupsController extends Controller
|
||||
public function update(Request $request, Group $group) : RedirectResponse
|
||||
{
|
||||
$group->name = $request->input('name');
|
||||
$group->permissions = json_encode($request->input('permission'));
|
||||
|
||||
if ($request->filled('permission')) {
|
||||
$group->permissions = json_encode($request->array('permission'));
|
||||
} else {
|
||||
$group->permissions = null;
|
||||
}
|
||||
|
||||
$group->notes = $request->input('notes');
|
||||
|
||||
|
||||
if (! config('app.lock_passwords')) {
|
||||
if ($group->save()) {
|
||||
|
||||
if ($request->has('users_to_sync')) {
|
||||
$associated_users = explode(',',$request->input('users_to_sync'));
|
||||
$group->users()->sync($associated_users);
|
||||
}
|
||||
|
||||
return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.update'));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Manufacturers\DeleteManufacturerAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasChildren;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Manufacturer;
|
||||
@@ -157,32 +165,18 @@ class ManufacturersController extends Controller
|
||||
* @param int $manufacturerId
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function destroy($manufacturerId) : RedirectResponse
|
||||
public function destroy(Manufacturer $manufacturer): RedirectResponse
|
||||
{
|
||||
$this->authorize('delete', Manufacturer::class);
|
||||
if (is_null($manufacturer = Manufacturer::withTrashed()->withCount('models as models_count')->find($manufacturerId))) {
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.not_found'));
|
||||
$this->authorize('delete', $manufacturer);
|
||||
try {
|
||||
DeleteManufacturerAction::run($manufacturer);
|
||||
} catch (ItemStillHasChildren $e) {
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('general.bulk_delete_associations.general_assoc_warning', ['item' => trans('general.manufacturer')]));
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('general.something_went_wrong'));
|
||||
}
|
||||
|
||||
if (! $manufacturer->isDeletable()) {
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.assoc_users'));
|
||||
}
|
||||
|
||||
if ($manufacturer->image) {
|
||||
try {
|
||||
Storage::disk('public')->delete('manufacturers/'.$manufacturer->image);
|
||||
} catch (\Exception $e) {
|
||||
Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
// Soft delete the manufacturer if active, permanent delete if is already deleted
|
||||
if ($manufacturer->deleted_at === null) {
|
||||
$manufacturer->delete();
|
||||
} else {
|
||||
$manufacturer->forceDelete();
|
||||
}
|
||||
// Redirect to the manufacturers management page
|
||||
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,21 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Mail\CheckoutAccessoryMail;
|
||||
use App\Mail\CheckoutAssetMail;
|
||||
use App\Mail\CheckoutComponentMail;
|
||||
use App\Mail\CheckoutConsumableMail;
|
||||
use App\Mail\CheckoutLicenseMail;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use App\Models\Checkoutable;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Maintenance;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\Company;
|
||||
@@ -18,9 +27,11 @@ use App\Models\License;
|
||||
use App\Models\ReportTemplate;
|
||||
use App\Models\Setting;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use League\Csv\Reader;
|
||||
@@ -436,10 +447,8 @@ class ReportsController extends Controller
|
||||
// Open output stream
|
||||
$handle = fopen('php://output', 'w');
|
||||
stream_set_timeout($handle, 2000);
|
||||
|
||||
if ($request->filled('use_bom')) {
|
||||
fprintf($handle, chr(0xEF).chr(0xBB).chr(0xBF));
|
||||
}
|
||||
|
||||
fprintf($handle, chr(0xEF).chr(0xBB).chr(0xBF));
|
||||
|
||||
$header = [];
|
||||
|
||||
@@ -1118,34 +1127,30 @@ class ReportsController extends Controller
|
||||
$this->authorize('reports.view');
|
||||
$showDeleted = $deleted == 'deleted';
|
||||
|
||||
$query = CheckoutAcceptance::pending()
|
||||
->where('checkoutable_type', 'App\Models\Asset')
|
||||
$query = CheckoutAcceptance::Pending()
|
||||
->with([
|
||||
'checkoutable' => function (MorphTo $query) {
|
||||
$query->morphWith([
|
||||
AssetModel::class => ['model'],
|
||||
Company::class => ['company'],
|
||||
Asset::class => ['assignedTo'],
|
||||
])->with('model.category');
|
||||
$query->withTrashed()->morphWith([
|
||||
Asset::class => ['model.category', 'assignedTo', 'company'],
|
||||
Accessory::class => ['category','checkouts', 'company'],
|
||||
LicenseSeat::class => ['user', 'license'],
|
||||
Component::class => ['assignedTo', 'company'],
|
||||
Consumable::class => ['company'],
|
||||
]);
|
||||
},
|
||||
'assignedTo' => function($query){
|
||||
$query->withTrashed();
|
||||
}
|
||||
]);
|
||||
])->orderByDesc('checkout_acceptances.created_at');
|
||||
|
||||
|
||||
if ($showDeleted) {
|
||||
$query->withTrashed();
|
||||
}
|
||||
|
||||
$assetsForReport = $query->get()
|
||||
->map(function ($acceptance) {
|
||||
return [
|
||||
'assetItem' => $acceptance->checkoutable,
|
||||
'acceptance' => $acceptance,
|
||||
];
|
||||
});
|
||||
$itemsForReport = $query->get()->map(fn ($unaccepted) => Checkoutable::fromAcceptance($unaccepted));
|
||||
|
||||
return view('reports/unaccepted_assets', compact('assetsForReport','showDeleted' ));
|
||||
return view('reports/unaccepted_assets', compact('itemsForReport','showDeleted' ));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1157,41 +1162,77 @@ class ReportsController extends Controller
|
||||
public function sentAssetAcceptanceReminder(Request $request) : RedirectResponse
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
|
||||
if (!$acceptance = CheckoutAcceptance::pending()->find($request->input('acceptance_id'))) {
|
||||
$id = $request->input('acceptance_id');
|
||||
$query = CheckoutAcceptance::query()
|
||||
->with([
|
||||
'checkoutable' => function (MorphTo $query) {
|
||||
$query->withTrashed()->morphWith([
|
||||
Asset::class => ['model.category', 'assignedTo', 'company', 'checkouts'],
|
||||
Accessory::class => ['category', 'company', 'checkouts'],
|
||||
LicenseSeat::class => ['user', 'license', 'checkouts'],
|
||||
Component::class => ['assignedTo', 'company', 'checkouts'],
|
||||
Consumable::class => ['company', 'checkouts'],
|
||||
]);
|
||||
},
|
||||
'assignedTo' => fn ($q) => $q->withTrashed(),
|
||||
])
|
||||
->pending();
|
||||
$acceptance = $query->find($id);
|
||||
if (!$acceptance) {
|
||||
Log::debug('No pending acceptances');
|
||||
// Redirect to the unaccepted assets report page with error
|
||||
// Redirect to the unaccepted items report page with error
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
|
||||
}
|
||||
$item = $acceptance->checkoutable;
|
||||
$assignee = $acceptance->assignedTo ?? $item->assignedTo ?? null;
|
||||
$email = $assignee?->email;
|
||||
$locale = $assignee?->locale;
|
||||
|
||||
$assetItem = $acceptance->checkoutable;
|
||||
|
||||
Log::debug(print_r($assetItem, true));
|
||||
Log::debug(print_r($acceptance, true));
|
||||
|
||||
if (is_null($acceptance->created_at)){
|
||||
Log::debug('No acceptance created_at');
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
|
||||
} else {
|
||||
$logItem_res = $assetItem->checkouts()->where('created_at', '=', $acceptance->created_at)->get();
|
||||
|
||||
if($item instanceof LicenseSeat){
|
||||
$logItem_res = $item->license->checkouts()->with('adminuser')->where('created_at', '=', $acceptance->created_at)->get();
|
||||
}
|
||||
else{
|
||||
$logItem_res = $item->checkouts()->with('adminuser')->where('created_at', '=', $acceptance->created_at)->get();
|
||||
}
|
||||
if ($logItem_res->isEmpty()){
|
||||
Log::debug('Acceptance date mismatch');
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
|
||||
}
|
||||
$logItem = $logItem_res[0];
|
||||
}
|
||||
$email = $assetItem->assignedTo?->email;
|
||||
$locale = $assetItem->assignedTo?->locale;
|
||||
|
||||
if (is_null($email) || $email === '') {
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.no_email'));
|
||||
}
|
||||
|
||||
Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note, firstTimeSending: false))->locale($locale));
|
||||
$mailable = $this->getCheckoutMailType($acceptance, $logItem);
|
||||
Mail::to($email)->send($mailable->locale($locale));
|
||||
|
||||
return redirect()->route('reports/unaccepted_assets')->with('success', trans('admin/reports/general.reminder_sent'));
|
||||
}
|
||||
private function getCheckoutMailType(CheckoutAcceptance $acceptance, $logItem) : Mailable
|
||||
{
|
||||
$lookup = [
|
||||
Accessory::class => CheckoutAccessoryMail::class,
|
||||
Asset::class => CheckoutAssetMail::class,
|
||||
LicenseSeat::class => CheckoutLicenseMail::class,
|
||||
Consumable::class => CheckoutConsumableMail::class,
|
||||
Component::class => CheckoutComponentMail::class,
|
||||
];
|
||||
$mailable= $lookup[get_class($acceptance->checkoutable)];
|
||||
|
||||
return new $mailable($acceptance->checkoutable,
|
||||
$acceptance->checkedOutTo ?? $acceptance->assignedTo,
|
||||
$logItem->adminuser,
|
||||
$acceptance,
|
||||
$acceptance->note);
|
||||
|
||||
}
|
||||
/**
|
||||
* sentAssetAcceptanceReminder
|
||||
*
|
||||
@@ -1223,31 +1264,41 @@ class ReportsController extends Controller
|
||||
public function postAssetAcceptanceReport($deleted = false) : Response
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
$showDeleted = $deleted == 'deleted';
|
||||
$showDeleted = request('deleted') === 'deleted';;
|
||||
|
||||
/**
|
||||
* Get all assets with pending checkout acceptances
|
||||
*/
|
||||
if($showDeleted) {
|
||||
$acceptances = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')->withTrashed()->with(['assignedTo', 'checkoutable.assignedTo', 'checkoutable.model'])->get();
|
||||
} else {
|
||||
$acceptances = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')->with(['assignedTo', 'checkoutable.assignedTo', 'checkoutable.model'])->get();
|
||||
}
|
||||
|
||||
$assetsForReport = $acceptances
|
||||
->filter(function($acceptance) {
|
||||
return $acceptance->checkoutable_type == 'App\Models\Asset';
|
||||
})
|
||||
->map(function($acceptance) {
|
||||
return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance];
|
||||
});
|
||||
$acceptances = CheckoutAcceptance::pending()
|
||||
->with([
|
||||
'checkoutable' => function (MorphTo $acceptance) {
|
||||
$acceptance->withTrashed()->morphWith([
|
||||
Asset::class => ['model.category', 'assignedTo', 'company'],
|
||||
Accessory::class => ['category','checkouts', 'company'],
|
||||
LicenseSeat::class => ['user', 'license'],
|
||||
Component::class => ['assignedTo', 'company'],
|
||||
Consumable::class => ['company'],
|
||||
]);
|
||||
},
|
||||
'assignedTo',
|
||||
])->orderByDesc('checkout_acceptances.created_at');
|
||||
|
||||
if ($showDeleted) {
|
||||
$acceptances->withTrashed();
|
||||
}
|
||||
|
||||
$itemsForReport = $acceptances->get()->map(fn ($unaccepted) => Checkoutable::fromAcceptance($unaccepted));
|
||||
|
||||
$rows = [];
|
||||
|
||||
$header = [
|
||||
trans('general.date'),
|
||||
trans('general.type'),
|
||||
trans('admin/companies/table.title'),
|
||||
trans('general.category'),
|
||||
trans('admin/hardware/form.model'),
|
||||
trans('admin/hardware/form.name'),
|
||||
trans('general.name'),
|
||||
trans('admin/hardware/table.asset_tag'),
|
||||
trans('admin/hardware/table.checkoutto'),
|
||||
];
|
||||
@@ -1255,16 +1306,19 @@ class ReportsController extends Controller
|
||||
$header = array_map('trim', $header);
|
||||
$rows[] = implode(',', $header);
|
||||
|
||||
foreach ($assetsForReport as $item) {
|
||||
foreach ($itemsForReport as $item) {
|
||||
|
||||
if ($item['assetItem'] != null){
|
||||
if ($item != null){
|
||||
|
||||
$row = [ ];
|
||||
$row[] = str_replace(',', '', e($item['assetItem']->model->category->name));
|
||||
$row[] = str_replace(',', '', e($item['assetItem']->model->name));
|
||||
$row[] = str_replace(',', '', e($item['assetItem']->name));
|
||||
$row[] = str_replace(',', '', e($item['assetItem']->asset_tag));
|
||||
$row[] = str_replace(',', '', e(($item['acceptance']->assignedTo) ? $item['acceptance']->assignedTo->display_name : trans('admin/reports/general.deleted_user')));
|
||||
$row[] = str_replace(',', '', $item->acceptance->created_at);
|
||||
$row[] = str_replace(',', '', $item->type);
|
||||
$row[] = str_replace(',', '', $item->plain_text_company);
|
||||
$row[] = str_replace(',', '', $item->plain_text_category);
|
||||
$row[] = str_replace(',', '', $item->plain_text_model);
|
||||
$row[] = str_replace(',', '', $item->plain_text_name);
|
||||
$row[] = str_replace(',', '', $item->asset_tag);
|
||||
$row[] = str_replace(',', '', ($item->acceptance->assignedto) ? $item->acceptance->assignedto->display_name : trans('admin/reports/general.deleted_user'));
|
||||
$rows[] = implode(',', $row);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,6 +589,7 @@ class SettingsController extends Controller
|
||||
$setting->time_display_format = $request->input('time_display_format');
|
||||
$setting->digit_separator = $request->input('digit_separator');
|
||||
$setting->name_display_format = $request->input('name_display_format');
|
||||
$setting->week_start = $request->input('week_start', 0);
|
||||
|
||||
if ($setting->save()) {
|
||||
return redirect()->route('settings.index')
|
||||
@@ -772,6 +773,7 @@ class SettingsController extends Controller
|
||||
$setting->label2_asset_logo = $request->input('label2_asset_logo');
|
||||
$setting->label2_1d_type = $request->input('label2_1d_type');
|
||||
$setting->label2_2d_type = $request->input('label2_2d_type');
|
||||
$setting->label2_2d_prefix = $request->input('label2_2d_prefix');
|
||||
$setting->label2_2d_target = $request->input('label2_2d_target');
|
||||
$setting->label2_fields = $request->input('label2_fields');
|
||||
$setting->label2_empty_row_count = $request->input('label2_empty_row_count');
|
||||
|
||||
@@ -2,10 +2,18 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Suppliers\DestroySupplierAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasMaintenances;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Supplier;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\MessageBag;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Suppliers for
|
||||
@@ -118,30 +126,41 @@ class SuppliersController extends Controller
|
||||
*
|
||||
* @param int $supplierId
|
||||
*/
|
||||
public function destroy($supplierId) : RedirectResponse
|
||||
public function destroy(Supplier $supplier): RedirectResponse
|
||||
{
|
||||
$this->authorize('delete', Supplier::class);
|
||||
if (is_null($supplier = Supplier::with('maintenances', 'assets', 'licenses')->withCount('maintenances as maintenances_count', 'assets as assets_count', 'licenses as licenses_count')->find($supplierId))) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.not_found'));
|
||||
try {
|
||||
DestroySupplierAction::run(supplier: $supplier);
|
||||
} catch (ItemStillHasAssets $e) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_assets', [
|
||||
'asset_count' => (int) $supplier->assets_count, 'item' => trans('general.supplier')
|
||||
]));
|
||||
} catch (ItemStillHasMaintenances $e) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_maintenances', [
|
||||
'asset_maintenances_count' => $supplier->asset_maintenances_count, 'item' => trans('general.supplier')
|
||||
]));
|
||||
} catch (ItemStillHasLicenses $e) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_licenses', [
|
||||
'licenses_count' => (int) $supplier->licenses_count, 'item' => trans('general.supplier')
|
||||
]));
|
||||
} catch (ItemStillHasAccessories $e) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_accessories', [
|
||||
'accessories_count' => (int) $supplier->accessories_count, 'item' => trans('general.supplier')
|
||||
]));
|
||||
} catch (ItemStillHasConsumables $e) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_consumables', [
|
||||
'consumables_count' => (int) $supplier->consumables_count, 'item' => trans('general.supplier')
|
||||
]));
|
||||
} catch (ItemStillHasComponents $e) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_components', [
|
||||
'components_count' => (int) $supplier->components_count, 'item' => trans('general.supplier')
|
||||
]));
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.error'));
|
||||
}
|
||||
|
||||
if ($supplier->assets_count > 0) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count]));
|
||||
}
|
||||
|
||||
if ($supplier->maintenances_count > 0) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_maintenances', ['maintenances_count' => $supplier->maintenances_count]));
|
||||
}
|
||||
|
||||
if ($supplier->licenses_count > 0) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count]));
|
||||
}
|
||||
|
||||
$supplier->delete();
|
||||
|
||||
return redirect()->route('suppliers.index')->with('success',
|
||||
trans('admin/suppliers/message.delete.success')
|
||||
);
|
||||
return redirect()->route('suppliers.index')->with('success', trans('admin/suppliers/message.delete.success'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,6 +173,5 @@ class SuppliersController extends Controller
|
||||
{
|
||||
$this->authorize('view', Supplier::class);
|
||||
return view('suppliers/view', compact('supplier'));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\CheckoutRequests\CancelCheckoutRequestAction;
|
||||
use App\Actions\CheckoutRequests\CreateCheckoutRequestAction;
|
||||
use App\Enums\ActionType;
|
||||
use App\Exceptions\AssetNotRequestable;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
@@ -155,7 +156,19 @@ class ViewAssetsController extends Controller
|
||||
public function getRequestableIndex() : View
|
||||
{
|
||||
$assets = Asset::with('model', 'defaultLoc', 'location', 'assignedTo', 'requests')->Hardware()->RequestableAssets();
|
||||
$models = AssetModel::with('category', 'requests', 'assets')->RequestableModels()->get();
|
||||
$models = AssetModel::with([
|
||||
'category',
|
||||
'requests',
|
||||
'assets' => function ($q) {
|
||||
$q->where('requestable', 1)
|
||||
->whereHas('assetstatus', fn ($s) =>
|
||||
$s->where('archived', 0)
|
||||
->where(fn ($s) =>
|
||||
$s->where('deployable', 1)->orWhere('pending', 1)
|
||||
)
|
||||
);
|
||||
},
|
||||
])->RequestableModels()->get();
|
||||
|
||||
return view('account/requestable-assets', compact('assets', 'models'));
|
||||
}
|
||||
@@ -201,7 +214,7 @@ class ViewAssetsController extends Controller
|
||||
if (($item_request = $item->isRequestedBy($user)) || $cancel_by_admin) {
|
||||
$item->cancelRequest($requestingUser);
|
||||
$data['item_quantity'] = ($item_request) ? $item_request->qty : 1;
|
||||
$logaction->logaction('request_canceled');
|
||||
$logaction->logaction(ActionType::RequestCanceled);
|
||||
|
||||
if (($settings->alert_email != '') && ($settings->alerts_enabled == '1') && (! config('app.lock_passwords'))) {
|
||||
$settings->notify(new RequestAssetCancelation($data));
|
||||
|
||||
@@ -109,7 +109,7 @@ class SettingsSamlRequest extends FormRequest
|
||||
];
|
||||
|
||||
$pkey = openssl_pkey_new([
|
||||
'private_key_bits' => config('app.saml_key_size'),
|
||||
'private_key_bits' => (int) config('app.saml_key_size'),
|
||||
'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
||||
]);
|
||||
|
||||
|
||||
32
app/Http/Requests/StoreDepartmentRequest.php
Normal file
32
app/Http/Requests/StoreDepartmentRequest.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Department;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreDepartmentRequest extends ImageUploadRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('create', new Department);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$modelRules = (new Department)->getRules();
|
||||
|
||||
return array_merge(
|
||||
$modelRules,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ class StoreLabelSettings extends FormRequest
|
||||
'labels_pagewidth' => 'numeric|nullable',
|
||||
'labels_pageheight' => 'numeric|nullable',
|
||||
'qr_text' => 'max:31|nullable',
|
||||
'label2_2d_prefix' => 'nullable|max:191',
|
||||
'label2_template' => [
|
||||
'required',
|
||||
Rule::in($names),
|
||||
|
||||
@@ -32,7 +32,7 @@ class StoreNotificationSettings extends FormRequest
|
||||
],
|
||||
'alert_threshold' => 'numeric|nullable',
|
||||
'alert_interval' => 'numeric|nullable|gt:0',
|
||||
'audit_warning_days' => 'numeric|nullable',
|
||||
'audit_warning_days' => 'numeric|nullable|gte:0',
|
||||
'due_checkin_days' => 'numeric|nullable|gt:0',
|
||||
'audit_interval' => 'numeric|nullable|gt:0',
|
||||
];
|
||||
|
||||
@@ -28,23 +28,31 @@ class UpdateAssetRequest extends ImageUploadRequest
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$setting = Setting::getSettings();
|
||||
|
||||
$rules = array_merge(
|
||||
parent::rules(),
|
||||
(new Asset)->getRules(),
|
||||
// this is to overwrite rulesets that include required, and rewrite unique_undeleted
|
||||
// This overwrites the rulesets that are set at the model level (via Watson) but are not necessarily required at the request level when doing a PATCH update.
|
||||
// Confusingly, this skips the unique_undeleted validator at the model level (and therefore the UniqueUndeletedTrait), so we have to re-add those
|
||||
// rules here without the requiredness, since those values will already exist if you're updating an existing asset.
|
||||
[
|
||||
'model_id' => ['integer', 'exists:models,id,deleted_at,NULL', 'not_array'],
|
||||
'status_id' => ['integer', 'exists:status_labels,id'],
|
||||
'asset_tag' => [
|
||||
'min:1', 'max:255', 'not_array',
|
||||
Rule::unique('assets', 'asset_tag')->ignore($this->asset)->withoutTrashed()
|
||||
Rule::unique('assets', 'asset_tag')->ignore($this->asset)->withoutTrashed(),
|
||||
],
|
||||
'serial' => [
|
||||
'string', 'max:255', 'not_array',
|
||||
$setting->unique_serial=='1' ? Rule::unique('assets', 'serial')->ignore($this->asset)->withoutTrashed() : 'nullable',
|
||||
],
|
||||
],
|
||||
);
|
||||
|
||||
// if the purchase cost is passed in as a string **and** the digit_separator is ',' (as is common in the EU)
|
||||
// then we tweak the purchase_cost rule to make it a string
|
||||
if (Setting::getSettings()->digit_separator === '1.234,56' && is_string($this->input('purchase_cost'))) {
|
||||
if ($setting->digit_separator === '1.234,56' && is_string($this->input('purchase_cost'))) {
|
||||
$rules['purchase_cost'] = ['nullable', 'string'];
|
||||
}
|
||||
|
||||
|
||||
@@ -149,6 +149,7 @@ class ActionlogsTransformer
|
||||
'filename' => $actionlog->filename,
|
||||
'inlineable' => StorageHelper::allowSafeInline($actionlog->uploads_file_path()),
|
||||
'exists_on_disk' => Storage::exists($actionlog->uploads_file_path()) ? true : false,
|
||||
'mediatype' => StorageHelper::getMediaType($actionlog->uploads_file_path()),
|
||||
] : null,
|
||||
|
||||
'item' => ($actionlog->item) ? [
|
||||
|
||||
@@ -68,7 +68,7 @@ class AssetModelsTransformer
|
||||
'default_fieldset_values' => $default_field_values,
|
||||
'eol' => ($assetmodel->eol > 0) ? $assetmodel->eol.' months' : 'None',
|
||||
'requestable' => ($assetmodel->requestable == '1') ? true : false,
|
||||
'require_serial' => $assetmodel->require_serial,
|
||||
'require_serial' => ($assetmodel->require_serial == '1') ? true : false,
|
||||
'notes' => Helper::parseEscapedMarkedownInline($assetmodel->notes),
|
||||
'created_by' => ($assetmodel->adminuser) ? [
|
||||
'id' => (int) $assetmodel->adminuser->id,
|
||||
|
||||
@@ -43,7 +43,7 @@ class DepartmentsTransformer
|
||||
'id' => (int) $department->location->id,
|
||||
'name' => e($department->location->name),
|
||||
] : null,
|
||||
'users_count' => e($department->users_count),
|
||||
'users_count' => (int) ($department->users_count),
|
||||
'notes' => Helper::parseEscapedMarkedownInline($department->notes),
|
||||
'created_at' => Helper::getFormattedDateObject($department->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($department->updated_at, 'datetime'),
|
||||
|
||||
@@ -36,6 +36,12 @@ class LicenseSeatsTransformer
|
||||
'name' => e($seat->user->department->name),
|
||||
|
||||
] : null,
|
||||
'company'=> ($seat->user->company) ?
|
||||
[
|
||||
'id' => (int) $seat->user->company->id,
|
||||
'name' => e($seat->user->company->name),
|
||||
|
||||
] : null,
|
||||
'created_at' => Helper::getFormattedDateObject($seat->created_at, 'datetime'),
|
||||
] : null,
|
||||
'assigned_asset' => ($seat->asset) ? [
|
||||
|
||||
@@ -44,7 +44,7 @@ class AssetImporter extends ItemImporter
|
||||
foreach ($this->customFields as $customField) {
|
||||
$customFieldValue = $this->array_smart_custom_field_fetch($row, $customField);
|
||||
|
||||
if ($customFieldValue) {
|
||||
if (!is_null($customFieldValue)) {
|
||||
if ($customField->field_encrypted == 1) {
|
||||
$this->item['custom_fields'][$customField->db_column_name()] = Crypt::encrypt($customFieldValue);
|
||||
$this->log('Custom Field '.$customField->name.': '.Crypt::encrypt($customFieldValue));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Component;
|
||||
|
||||
class CategoryEditForm extends Component
|
||||
@@ -12,43 +13,25 @@ class CategoryEditForm extends Component
|
||||
|
||||
public $eulaText;
|
||||
|
||||
public $originalSendCheckInEmailValue;
|
||||
|
||||
public bool $requireAcceptance;
|
||||
|
||||
public bool $sendCheckInEmail;
|
||||
|
||||
public bool $useDefaultEula;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->originalSendCheckInEmailValue = $this->sendCheckInEmail;
|
||||
|
||||
if ($this->eulaText || $this->useDefaultEula) {
|
||||
$this->sendCheckInEmail = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.category-edit-form');
|
||||
}
|
||||
|
||||
public function updated($property, $value)
|
||||
{
|
||||
if (! in_array($property, ['eulaText', 'useDefaultEula'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->sendCheckInEmail = $this->eulaText || $this->useDefaultEula ? 1 : $this->originalSendCheckInEmailValue;
|
||||
}
|
||||
|
||||
public function getShouldDisplayEmailMessageProperty(): bool
|
||||
#[Computed]
|
||||
public function emailWillBeSendDueToEula(): bool
|
||||
{
|
||||
return $this->eulaText || $this->useDefaultEula;
|
||||
}
|
||||
|
||||
public function getEmailMessageProperty(): string
|
||||
#[Computed]
|
||||
public function emailMessage(): string
|
||||
{
|
||||
if ($this->useDefaultEula) {
|
||||
return trans('admin/categories/general.email_will_be_sent_due_to_global_eula');
|
||||
@@ -57,13 +40,9 @@ class CategoryEditForm extends Component
|
||||
return trans('admin/categories/general.email_will_be_sent_due_to_category_eula');
|
||||
}
|
||||
|
||||
public function getEulaTextDisabledProperty()
|
||||
#[Computed]
|
||||
public function eulaTextDisabled()
|
||||
{
|
||||
return (bool)$this->useDefaultEula;
|
||||
}
|
||||
|
||||
public function getSendCheckInEmailDisabledProperty()
|
||||
{
|
||||
return $this->eulaText || $this->useDefaultEula;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ class SlackSettingsForm extends Component
|
||||
]);
|
||||
|
||||
try {
|
||||
$test = $webhook->post($this->webhook_endpoint, ['body' => $payload, ['headers' => ['Content-Type' => 'application/json']]]);
|
||||
$test = $webhook->post($this->webhook_endpoint, ['body' => $payload, 'headers' => ['Content-Type' => 'application/json']]);
|
||||
|
||||
if(($test->getStatusCode() == 302)||($test->getStatusCode() == 301)){
|
||||
return session()->flash('error' , trans('admin/settings/message.webhook.error_redirect', ['endpoint' => $this->webhook_endpoint]));
|
||||
|
||||
@@ -87,7 +87,7 @@ class CheckoutAssetMail extends Mailable
|
||||
$name = $this->target->assignedto?->display_name;
|
||||
}
|
||||
else if($this->target instanceof Location){
|
||||
$name = $this->target->manager->name;
|
||||
$name = $this->target->manager?->name;
|
||||
}
|
||||
|
||||
// Check if the item has custom fields associated with it
|
||||
|
||||
@@ -17,10 +17,11 @@ class SendUpcomingAuditMail extends Mailable
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct($params, $threshold)
|
||||
public function __construct($params, $threshold, $total)
|
||||
{
|
||||
$this->assets = $params;
|
||||
$this->threshold = $threshold;
|
||||
$this->total = $total;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,7 +33,7 @@ class SendUpcomingAuditMail extends Mailable
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
subject: trans_choice('mail.upcoming-audits', $this->assets->count(), ['count' => $this->assets->count(), 'threshold' => $this->threshold]),
|
||||
subject: trans_choice('mail.upcoming-audits', $this->total, ['count' => $this->total, 'threshold' => $this->threshold]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -49,6 +50,7 @@ class SendUpcomingAuditMail extends Mailable
|
||||
with: [
|
||||
'assets' => $this->assets,
|
||||
'threshold' => $this->threshold,
|
||||
'total' => $this->total,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Helpers\Helper;
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
@@ -9,6 +9,7 @@ use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Enums\ActionType;
|
||||
|
||||
/**
|
||||
* Model for the Actionlog (the table that keeps a historical log of
|
||||
@@ -335,9 +336,12 @@ class Actionlog extends SnipeModel
|
||||
* @since [v3.0]
|
||||
* @return bool
|
||||
*/
|
||||
public function logaction($actiontype)
|
||||
public function logaction(string|ActionType $actiontype)
|
||||
{
|
||||
$this->action_type = $actiontype;
|
||||
if (is_string($actiontype)) {
|
||||
$actiontype = ActionType::from($actiontype);
|
||||
}
|
||||
$this->action_type = $actiontype->value;
|
||||
$this->remote_ip = request()->ip();
|
||||
$this->user_agent = request()->header('User-Agent');
|
||||
$this->action_source = $this->determineActionSource();
|
||||
@@ -360,7 +364,7 @@ class Actionlog extends SnipeModel
|
||||
{
|
||||
$now = Carbon::now();
|
||||
$last_audit_date = $this->created_at; // this is the action log's created at, not the asset itself
|
||||
$next_audit = $last_audit_date->addMonth($monthInterval); // this actually *modifies* the $last_audit_date
|
||||
$next_audit = $last_audit_date->addMonth((int) $monthInterval); // this actually *modifies* the $last_audit_date
|
||||
$next_audit_days = (int) round($now->diffInDays($next_audit, true));
|
||||
$override_default_next = $next_audit;
|
||||
|
||||
@@ -478,6 +482,10 @@ class Actionlog extends SnipeModel
|
||||
$object = 'models';
|
||||
}
|
||||
|
||||
if ($this->action_type == 'audit') {
|
||||
$object = 'audits';
|
||||
}
|
||||
|
||||
return route('ui.files.show', [
|
||||
'object_type' => $object,
|
||||
'id' => $this->item_id,
|
||||
@@ -514,6 +522,8 @@ class Actionlog extends SnipeModel
|
||||
return 'private_uploads/locations/'.$this->filename;
|
||||
case Maintenance::class:
|
||||
return 'private_uploads/maintenances/'.$this->filename;
|
||||
case Supplier::class:
|
||||
return 'private_uploads/suppliers/'.$this->filename;
|
||||
case User::class:
|
||||
return 'private_uploads/users/'.$this->filename;
|
||||
default:
|
||||
|
||||
@@ -9,6 +9,8 @@ use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Requestable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\AssetPresenter;
|
||||
use App\Presenters\Presentable;
|
||||
@@ -280,7 +282,7 @@ class Asset extends Depreciable
|
||||
protected function warrantyExpires(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => ($attributes['warranty_months'] && $attributes['purchase_date']) ? Carbon::parse($attributes['purchase_date'])->addMonths($attributes['warranty_months']) : null,
|
||||
get: fn(mixed $value, array $attributes) => ($attributes['warranty_months'] && $attributes['purchase_date']) ? Carbon::parse($attributes['purchase_date'])->addMonths((int)$attributes['warranty_months']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -399,6 +401,19 @@ class Asset extends Depreciable
|
||||
|
||||
}
|
||||
|
||||
protected function expectedCheckinFormattedDate(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => array_key_exists('expected_checkin', $attributes) ? Helper::getFormattedDateObject($attributes['expected_checkin'], 'date', false) : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function expectedCheckinDiffForHumans(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => array_key_exists('expected_checkin', $attributes) ? Carbon::parse($this->expected_checkin)->diffForHumans() : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the asset -> company relationship
|
||||
@@ -931,25 +946,37 @@ class Asset extends Depreciable
|
||||
*/
|
||||
public static function getExpiringWarrantyOrEol($days = 30)
|
||||
{
|
||||
|
||||
return self::where('archived', '=', '0')
|
||||
$now = now();
|
||||
$end = now()->addDays($days);
|
||||
|
||||
$expired_assets = self::query()
|
||||
->where('archived', '=', '0')
|
||||
->NotArchived()
|
||||
->whereNull('deleted_at')
|
||||
->where(function ($query) use ($days) {
|
||||
// Check for manual asset EOL first
|
||||
$query->where(function ($query) use ($days) {
|
||||
$query->whereNotNull('asset_eol_date')
|
||||
->whereBetween('asset_eol_date', [Carbon::now(), Carbon::now()->addDays($days)]);
|
||||
// Otherwise use the warranty months + purchase date + threshold
|
||||
})->orWhere(function ($query) use ($days) {
|
||||
$query->whereNotNull('purchase_date')
|
||||
->whereNotNull('warranty_months')
|
||||
->whereBetween('purchase_date', [Carbon::now(), Carbon::now()->addMonths('assets.warranty_months')->addDays($days)]);
|
||||
});
|
||||
})
|
||||
->orderBy('asset_eol_date', 'ASC')
|
||||
->orderBy('purchase_date', 'ASC')
|
||||
->whereNotNull('asset_eol_date')
|
||||
->whereBetween('asset_eol_date', [$now, $end])
|
||||
->get();
|
||||
|
||||
$assets_with_warranties = self::query()
|
||||
->where('archived', '=', '0')
|
||||
->NotArchived()
|
||||
->whereNull('deleted_at')
|
||||
->whereNotNull('purchase_date')
|
||||
->whereNotNull('warranty_months')
|
||||
->get();
|
||||
|
||||
$expired_warranties = $assets_with_warranties->filter(function ($asset) use ($now, $end) {
|
||||
$expiration_window = Carbon::parse($asset->purchase_date)->addMonths((int) $asset->warranty_months);
|
||||
|
||||
return $expiration_window->betweenIncluded($now, $end);
|
||||
});
|
||||
return $expired_assets->concat($expired_warranties)
|
||||
->unique('id')
|
||||
->sortBy([
|
||||
['asset_eol_date', 'ASC'],
|
||||
['purchase_date', 'ASC']
|
||||
])
|
||||
->values();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,16 +2,18 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Http\Traits\TwoColumnUniqueUndeletedTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Requestable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\AssetModelPresenter;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
use \App\Presenters\AssetModelPresenter;
|
||||
use App\Http\Traits\TwoColumnUniqueUndeletedTrait;
|
||||
|
||||
/**
|
||||
* Model for Asset Models. Asset Models contain higher level
|
||||
|
||||
@@ -190,6 +190,11 @@ class CheckoutAcceptance extends Model
|
||||
}
|
||||
|
||||
$pdf->Ln();
|
||||
|
||||
// Check for CJK in the translation string for date. (This is a good proxy for the rest of the document)
|
||||
Helper::hasRtl(trans('general.date')) ? $pdf->setRTL(true) : $pdf->setRTL(false);
|
||||
Helper::isCjk(trans('general.date')) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
|
||||
|
||||
$pdf->writeHTML(trans('general.date') . ': ' . Helper::getFormattedDateObject(now(), 'datetime', false), true, 0, true, 0, '');
|
||||
|
||||
if ($data['company_name'] != null) {
|
||||
@@ -224,7 +229,6 @@ class CheckoutAcceptance extends Model
|
||||
foreach ($eula_lines as $eula_line) {
|
||||
Helper::hasRtl($eula_line) ? $pdf->setRTL(true) : $pdf->setRTL(false);
|
||||
Helper::isCjk($eula_line) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
|
||||
|
||||
$pdf->writeHTML(Helper::parseEscapedMarkedown($eula_line), true, 0, true, 0, '');
|
||||
}
|
||||
$pdf->Ln();
|
||||
@@ -239,8 +243,11 @@ class CheckoutAcceptance extends Model
|
||||
$pdf->Ln();
|
||||
}
|
||||
|
||||
Helper::hasRtl(trans('general.notes')) ? $pdf->setRTL(true) : $pdf->setRTL(false);
|
||||
Helper::isCjk(trans('general.notes')) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
|
||||
|
||||
if ($data['note'] != null) {
|
||||
Helper::isCjk($data['note']) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
|
||||
Helper::isCjk(trans('general.notes')) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
|
||||
$pdf->writeHTML(trans('general.notes') . ': ' . e($data['note']), true, 0, true, 0, '');
|
||||
$pdf->Ln();
|
||||
}
|
||||
|
||||
79
app/Models/Checkoutable.php
Normal file
79
app/Models/Checkoutable.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
class Checkoutable
|
||||
{
|
||||
public function __construct(
|
||||
public int $acceptance_id,
|
||||
public string $company,
|
||||
public string $category,
|
||||
public string $model,
|
||||
public string $asset_tag,
|
||||
public string $name,
|
||||
public string $type,
|
||||
public object $acceptance,
|
||||
public object $assignee,
|
||||
public readonly string $plain_text_category,
|
||||
public readonly string $plain_text_model,
|
||||
public readonly string $plain_text_name,
|
||||
public readonly string $plain_text_company,
|
||||
){}
|
||||
|
||||
public static function fromAcceptance(CheckoutAcceptance $unaccepted): self
|
||||
{
|
||||
$unaccepted_row = $unaccepted->checkoutable;
|
||||
$acceptance = $unaccepted;
|
||||
|
||||
$assignee = $acceptance->assignedTo;
|
||||
$company = $unaccepted_row->company ? optional($unaccepted_row->company)->present()->nameUrl() : '';
|
||||
$category = $model = $name = $tag = '';
|
||||
$type = $acceptance->checkoutable_item_type ?? '';
|
||||
|
||||
|
||||
if($unaccepted_row instanceof Asset){
|
||||
$category = optional($unaccepted_row->model?->category?->present())->nameUrl() ?? '';
|
||||
$model = optional($unaccepted_row->present())->modelUrl() ?? '';
|
||||
$name = optional($unaccepted_row->present())->nameUrl() ?? '';
|
||||
$tag = (string) ($unaccepted_row->asset_tag ?? '');
|
||||
}
|
||||
if($unaccepted_row instanceof Accessory){
|
||||
$category = optional($unaccepted_row->category?->present())->nameUrl() ?? '';
|
||||
$model = $unaccepted_row->model_number ?? '';
|
||||
$name = optional($unaccepted_row->present())->nameUrl() ?? '';
|
||||
}
|
||||
if($unaccepted_row instanceof LicenseSeat){
|
||||
$category = optional($unaccepted_row->license->category?->present())->nameUrl() ?? '';
|
||||
$company = optional($unaccepted_row->license->company?->present())?->nameUrl() ?? '';
|
||||
$model = '';
|
||||
$name = $unaccepted_row->license->present()->nameUrl() ?? '';
|
||||
}
|
||||
if($unaccepted_row instanceof Consumable){
|
||||
$category = optional($unaccepted_row->category?->present())->nameUrl() ?? '';
|
||||
$model = $unaccepted_row->model_number ?? '';
|
||||
$name = $unaccepted_row?->present()?->nameUrl() ?? '';
|
||||
}
|
||||
if($unaccepted_row instanceof Component){
|
||||
$category = optional($unaccepted_row->category?->present())->nameUrl() ?? '';
|
||||
$model = $unaccepted_row->model_number ?? '';
|
||||
$name = $unaccepted_row?->present()?->nameUrl() ?? '';
|
||||
}
|
||||
|
||||
return new self(
|
||||
acceptance_id: $acceptance->id,
|
||||
company: $company,
|
||||
category: $category,
|
||||
model: $model,
|
||||
asset_tag: $tag,
|
||||
name: $name,
|
||||
type: $type,
|
||||
acceptance: $acceptance,
|
||||
assignee: $assignee,
|
||||
//plain text for CSVs
|
||||
plain_text_category: ($unaccepted_row->model?->category?->name ?? $unaccepted_row->license->category?->name ?? $unaccepted_row->category?->name ?? ''),
|
||||
plain_text_model: ($unaccepted_row->model?->name ?? $unaccepted_row->model_number ?? ''),
|
||||
plain_text_name: ($unaccepted_row->name ?? $unaccepted_row->license?->name ?? ''),
|
||||
plain_text_company: ($unaccepted_row->company)->name ?? $unaccepted_row->license->company?->name ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -274,7 +275,19 @@ class Component extends SnipeModel
|
||||
{
|
||||
return $this->category?->checkin_email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of checkouts for this License
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function checkouts()
|
||||
{
|
||||
return $this->assetlog()->where('action_type', '=', 'checkout')
|
||||
->orderBy('created_at', 'desc')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check how many items within a component are remaining
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Helpers\Helper;
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\ConsumablePresenter;
|
||||
use App\Presenters\Presentable;
|
||||
@@ -316,6 +317,20 @@ class Consumable extends SnipeModel
|
||||
|
||||
return $this->purchase_cost !== null ? $this->qty * $this->purchase_cost : null;
|
||||
}
|
||||
/**
|
||||
* Get the list of checkouts for this consumable
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function checkouts()
|
||||
{
|
||||
return $this->assetlog()->where('action_type', '=', 'checkout')
|
||||
->orderBy('created_at', 'desc')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------
|
||||
* BEGIN MUTATORS
|
||||
|
||||
@@ -31,10 +31,13 @@ class Department extends SnipeModel
|
||||
];
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|max:255|is_unique_department',
|
||||
'location_id' => 'numeric|nullable',
|
||||
'company_id' => 'numeric|nullable',
|
||||
'manager_id' => 'numeric|nullable',
|
||||
'name' => 'required|max:255|is_unique_across_company_and_location:departments,name',
|
||||
'location_id' => 'numeric|nullable|exists:locations,id',
|
||||
'company_id' => 'numeric|nullable|exists:companies,id',
|
||||
'manager_id' => 'numeric|nullable|exists:users,id',
|
||||
'phone' => 'string|max:255|nullable',
|
||||
'fax' => 'string|max:255|nullable',
|
||||
'notes' => 'string|max:255|nullable',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,15 +4,15 @@ namespace App\Models\Labels\Tapes\Brother;
|
||||
|
||||
class TZe_24mm_E extends TZe_24mm
|
||||
{
|
||||
private const BARCODE_MARGIN = 1.50;
|
||||
private const BARCODE_MARGIN = 1.75;
|
||||
private const TAG_SIZE = 2.00;
|
||||
private const TITLE_SIZE = 2.80;
|
||||
private const TITLE_MARGIN = 0.50;
|
||||
private const LABEL_SIZE = 2.00;
|
||||
private const LABEL_MARGIN = - 0.35;
|
||||
private const LABEL_MARGIN = - 0.75;
|
||||
private const FIELD_SIZE = 2.80;
|
||||
private const FIELD_MARGIN = 0.15;
|
||||
private const BARCODE1D_SIZE = - 1.00;
|
||||
private const BARCODE1D_SIZE = - 2.25;
|
||||
|
||||
public function getUnit() { return 'mm'; }
|
||||
public function getWidth() { return 45.0; }
|
||||
|
||||
@@ -104,6 +104,78 @@ class Ldap extends Model
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds user via Admin search *first*, and _then_ try to bind as that user, returning the user attributes on success,
|
||||
* or false on failure. This enables login when the DN is harder to programmatically 'guess' due to having users in
|
||||
* various different OU's or other LDAP entities.
|
||||
*/
|
||||
public static function findAndBindMultiOU(string $baseDn, string $filterQuery, string $password, int $slow_failure = 3): array|false
|
||||
{
|
||||
/**
|
||||
* If you *don't* set the slow_failure variable, do note that we might permit timing attacks in here - if
|
||||
* your find results come back 'slow' when a user *does* exist, but fast if they *don't* exist, then you
|
||||
* can use this to enumerate users.
|
||||
*
|
||||
* Even if that's *not* true, we still might have an issue: if we don't find the user, then we don't even _try_
|
||||
* to bind as them. Again, that could permit a timing attack.
|
||||
*
|
||||
* Instead of checking every little thing, we just wrap everything in a try/catch in order to unify the
|
||||
* 'slow_failure' treatment. All failures are re-raised as exceptions so that all failures exit from the
|
||||
* same place.
|
||||
*/
|
||||
$connection = null;
|
||||
$admin_conn = null;
|
||||
try {
|
||||
/**
|
||||
* First we get an 'admin' connection, which will need search permissions. That was already a requirement
|
||||
* here, so that's not a big lift. But it _is_ possible to configure LDAP to only login, and *not* to be
|
||||
* able to import lists of users. In that case, this function *will not work* - and you should use the
|
||||
* legacy 'findAndBindUserLdap' method, below. Otherwise, it looks like this would attempt an anonymous
|
||||
* bind - which you might want, but you probably don't.
|
||||
*
|
||||
**/
|
||||
$admin_conn = self::connectToLdap();
|
||||
self::bindAdminToLdap($admin_conn);
|
||||
$results = ldap_search($admin_conn, $baseDn, $filterQuery);
|
||||
$entry_count = ldap_count_entries($admin_conn, $results);
|
||||
if ($entry_count != 1) {
|
||||
throw new \Exception('Wrong number of entries found: ' . $entry_count);
|
||||
}
|
||||
$entry = ldap_first_entry($admin_conn, $results);
|
||||
$user = ldap_get_attributes($admin_conn, $entry);
|
||||
$userDn = ldap_get_dn($admin_conn, $entry);
|
||||
if (!$userDn) {
|
||||
throw new \Exception("No user DN found");
|
||||
}
|
||||
\Log::debug("FOUND DN IS: $userDn");
|
||||
// The temptation now is to do ldap_unbind on the $admin_conn, but that gets handled in the 'finally' below.
|
||||
// I don't know if that means a separate 'connection' is maintained to the LDAP server or not, and would
|
||||
// definitely prefer to not do that if we can avoid it. But I don't know enough about the LDAP protocol to
|
||||
// be certain that that happens.
|
||||
|
||||
//now we try to log in (bind) as that found user
|
||||
$connection = self::connectToLdap();
|
||||
$bind_results = ldap_bind($connection, $userDn, $password);
|
||||
if (!$bind_results) {
|
||||
throw new \Exception("Unable to bind as user");
|
||||
}
|
||||
return array_change_key_case($user);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug("Exception on fast find-and-bind: " . $e->getMessage());
|
||||
if ($slow_failure) {
|
||||
sleep($slow_failure);
|
||||
}
|
||||
return false; //TODO - make this null instead for a slightly nicer type signature
|
||||
} finally {
|
||||
if ($admin_conn) {
|
||||
ldap_unbind($admin_conn);
|
||||
}
|
||||
if ($connection) {
|
||||
ldap_unbind($connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Binds/authenticates the user to LDAP, and returns their attributes.
|
||||
@@ -147,25 +219,27 @@ class Ldap extends Model
|
||||
|
||||
Log::debug('Filter query: '.$filterQuery);
|
||||
|
||||
// only try this if we have an Admin username set; otherwise use the 'legacy' method
|
||||
if (($settings->ldap_uname) && ($baseDn)) {
|
||||
// in the fallowing call, we pick a slow-failure of 0 because we might need to fall through to 'legacy'
|
||||
$fast_bind = self::findAndBindMultiOU($baseDn, $filterQuery, $password, 0);
|
||||
if ($fast_bind) {
|
||||
\Log::debug("Fast bind worked");
|
||||
return $fast_bind;
|
||||
}
|
||||
\Log::debug("Fast bind failed; falling through to legacy bind");
|
||||
}
|
||||
|
||||
if (! $ldapbind = @ldap_bind($connection, $userDn, $password)) {
|
||||
Log::debug("Status of binding user: $userDn to directory: (directly!) ".($ldapbind ? "success" : "FAILURE"));
|
||||
if (! $ldapbind = self::bindAdminToLdap($connection)) {
|
||||
/*
|
||||
* TODO PLEASE:
|
||||
*
|
||||
* this isn't very clear, so it's important to note: the $ldapbind value is never correctly returned - we never 'return true' from self::bindAdminToLdap() (the function
|
||||
* just "falls off the end" without ever explictly returning 'true')
|
||||
*
|
||||
* but it *does* have an interesting side-effect of checking for the LDAP password being incorrectly encrypted with the wrong APP_KEY, so I'm leaving it in for now.
|
||||
*
|
||||
* If it *did* correctly return 'true' on a succesful bind, it would _probably_ allow users to log in with an incorrect password. Which would be horrible!
|
||||
*
|
||||
* Let's definitely fix this at the next refactor!!!!
|
||||
*
|
||||
*/
|
||||
Log::debug("Status of binding Admin user: $userDn to directory instead: ".($ldapbind ? "success" : "FAILURE"));
|
||||
return false;
|
||||
// replicate the old bad-decryption-key detection behavior here
|
||||
try {
|
||||
Crypt::decrypt(Setting::getSettings()->ldap_pword);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception('Your app key has changed! Could not decrypt LDAP password using your current app key, so LDAP authentication has been disabled. Login with a local account, update the LDAP password and re-enable it in Admin > Settings.');
|
||||
}
|
||||
//regardless of anything else; stuff isn't working. Return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $results = ldap_search($connection, $baseDn, $filterQuery)) {
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Carbon\Carbon;
|
||||
@@ -415,7 +416,12 @@ class License extends Depreciable
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkouts()
|
||||
{
|
||||
return $this->assetlog()->where('action_type', '=', 'checkout')
|
||||
->orderBy('created_at', 'desc')
|
||||
->withTrashed();
|
||||
}
|
||||
/**
|
||||
* Determine whether the user should be required to accept the license
|
||||
*
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Models;
|
||||
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\CompanyableChildTrait;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Notifications\CheckinLicenseNotification;
|
||||
use App\Notifications\CheckoutLicenseNotification;
|
||||
use App\Presenters\Presentable;
|
||||
@@ -75,7 +76,7 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild
|
||||
protected function displayName(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value) => $this->license->name,
|
||||
get: fn(mixed $value) => $this->license?->name,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -134,7 +135,31 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Get the list of checkouts for this License
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function checkouts()
|
||||
{
|
||||
return $this->assetlog()->where('action_type', '=', 'checkout')
|
||||
->orderBy('created_at', 'desc')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the license -> action logs relationship
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function assetlog()
|
||||
{
|
||||
return $this->hasMany(Actionlog::class, 'item_id')->where('item_type', self::class)->orderBy('created_at', 'desc')->withTrashed();
|
||||
}
|
||||
/**
|
||||
* Query builder scope to order on department
|
||||
*
|
||||
@@ -152,17 +177,24 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild
|
||||
}
|
||||
|
||||
|
||||
public function scopeOrderCompany($query, $order)
|
||||
{
|
||||
|
||||
|
||||
return $query->leftJoin('users as license_seat_users', 'license_seats.assigned_to', '=', 'license_seat_users.id')
|
||||
->leftJoin('companies as license_user_company', 'license_user_company.id', '=', 'license_seat_users.company_id')
|
||||
->whereNotNull('license_seats.assigned_to')
|
||||
->orderBy('license_user_company.name', $order);
|
||||
}
|
||||
|
||||
|
||||
public function scopeByAssigned($query)
|
||||
{
|
||||
|
||||
return $query->where(
|
||||
function ($query) {
|
||||
$query->whereNotNull('assigned_to')
|
||||
->orWhere(
|
||||
function ($query) {
|
||||
$query->whereNotNull('asset_id');
|
||||
}
|
||||
);
|
||||
->orWhereNotNull('asset_id');
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
||||
use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Traits\CompanyableChildTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
@@ -32,7 +32,7 @@ class SnipeModel extends Model
|
||||
protected function expiresDiffInDays(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => in_array('expiration_date', $attributes) ? Carbon::now()->diffInDays($attributes['expiration_date']) : null,
|
||||
get: fn(mixed $value, array $attributes) => array_key_exists('expiration_date', $attributes) ? Carbon::now()->diffInDays($attributes['expiration_date']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,14 +40,14 @@ class SnipeModel extends Model
|
||||
protected function expiresDiffForHumans(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => in_array('expiration_date', $attributes) ? Carbon::parse($attributes['expiration_date'])->diffForHumans() : null,
|
||||
get: fn(mixed $value, array $attributes) => array_key_exists('expiration_date', $attributes) ? Carbon::parse($attributes['expiration_date'])->diffForHumans() : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function expiresFormattedDate(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => in_array('expiration_date', $attributes) ? Helper::getFormattedDateObject($attributes['expiration_date'], 'date', false) : null,
|
||||
get: fn(mixed $value, array $attributes) => array_key_exists('expiration_date', $attributes) ? Helper::getFormattedDateObject($attributes['expiration_date'], 'date', false) : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,18 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Searchable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
use \Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use App\Models\Traits\Loggable;
|
||||
class Supplier extends SnipeModel
|
||||
{
|
||||
use HasFactory;
|
||||
use SoftDeletes;
|
||||
use HasUploads;
|
||||
|
||||
protected $table = 'suppliers';
|
||||
|
||||
@@ -42,6 +45,7 @@ class Supplier extends SnipeModel
|
||||
use ValidatingTrait;
|
||||
use UniqueUndeletedTrait;
|
||||
use Searchable;
|
||||
use Loggable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
namespace App\Models\Traits;
|
||||
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Location;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\AuditNotification;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -1,8 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
namespace App\Models\Traits;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Models\CheckoutRequest;
|
||||
use App\Models\User;
|
||||
|
||||
// $asset->requests
|
||||
// $asset->isRequestedBy($user)
|
||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
||||
use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use App\Presenters\UserPresenter;
|
||||
@@ -224,6 +225,29 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasIndividualPermissions()
|
||||
{
|
||||
$permissions = [];
|
||||
|
||||
if (is_object($this->permissions)) {
|
||||
$permissions = json_decode(json_encode($this->permissions), true);
|
||||
}
|
||||
|
||||
if (is_string($this->permissions)) {
|
||||
$permissions = json_decode($this->permissions, true);
|
||||
}
|
||||
|
||||
if (($permissions) && (is_array($permissions))) {
|
||||
foreach ($permissions as $permission) {
|
||||
if ($permission != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internally check the user permission for the given section
|
||||
*
|
||||
|
||||
@@ -27,14 +27,13 @@ use Illuminate\Notifications\Notification;
|
||||
$this->item_model = $params['item_model'];
|
||||
$this->item_serial = $params['item_serial'];
|
||||
$this->item_status = $params['item_status'];
|
||||
$this->accepted_date = Helper::getFormattedDateObject($params['accepted_date'], 'datetime', false);
|
||||
$this->accepted_date = $params['accepted_date'];
|
||||
$this->assigned_to = $params['assigned_to'];
|
||||
$this->company_name = $params['company_name'];
|
||||
$this->settings = Setting::getSettings();
|
||||
$this->file = $params['file'] ?? null;
|
||||
$this->qty = $params['qty'] ?? null;
|
||||
$this->note = $params['note'] ?? null;
|
||||
$this->admin = $params['admin'] ?? null;
|
||||
|
||||
}
|
||||
|
||||
@@ -77,7 +76,6 @@ use Illuminate\Notifications\Notification;
|
||||
'accepted_date' => $this->accepted_date,
|
||||
'assigned_to' => $this->assigned_to,
|
||||
'company_name' => $this->company_name,
|
||||
'admin' => $this->admin,
|
||||
'qty' => $this->qty,
|
||||
'intro_text' => trans('mail.acceptance_asset_accepted'),
|
||||
])
|
||||
|
||||
@@ -32,7 +32,6 @@ use Illuminate\Notifications\Notification;
|
||||
$this->settings = Setting::getSettings();
|
||||
$this->file = $params['file'] ?? null;
|
||||
$this->qty = $params['qty'] ?? null;
|
||||
$this->admin = $params['admin'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +69,6 @@ use Illuminate\Notifications\Notification;
|
||||
'accepted_date' => $this->accepted_date,
|
||||
'assigned_to' => $this->assigned_to,
|
||||
'company_name' => $this->company_name,
|
||||
'admin' => $this->admin,
|
||||
'qty' => $this->qty,
|
||||
'intro_text' => trans_choice('mail.acceptance_asset_accepted_to_user', $this->qty, ['qty' => $this->qty, 'site_name' => $this->settings->site_name]),
|
||||
])
|
||||
|
||||
@@ -25,12 +25,13 @@ class AcceptanceAssetDeclinedNotification extends Notification
|
||||
$this->item_model = $params['item_model'];
|
||||
$this->item_serial = $params['item_serial'];
|
||||
$this->item_status = $params['item_status'];
|
||||
$this->declined_date = Helper::getFormattedDateObject($params['declined_date'], 'date', false);
|
||||
$this->declined_date = $params['declined_date'];
|
||||
$this->note = $params['note'];
|
||||
$this->assigned_to = $params['assigned_to'];
|
||||
$this->company_name = $params['company_name'];
|
||||
$this->settings = Setting::getSettings();
|
||||
$this->qty = $params['qty'] ?? null;
|
||||
$this->admin = $params['admin'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +71,8 @@ class AcceptanceAssetDeclinedNotification extends Notification
|
||||
'declined_date' => $this->declined_date,
|
||||
'assigned_to' => $this->assigned_to,
|
||||
'company_name' => $this->company_name,
|
||||
'qty' => $this->qty,
|
||||
'qty' => $this->qty,
|
||||
'admin' => $this->admin,
|
||||
'intro_text' => trans('mail.acceptance_asset_declined'),
|
||||
])
|
||||
->subject(trans('mail.acceptance_asset_declined'));
|
||||
|
||||
@@ -93,8 +93,8 @@ class CheckoutAssetNotification extends Notification
|
||||
$channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : '';
|
||||
|
||||
$fields = [
|
||||
trans('general.to') => '<'.$target->present()->viewUrl().'|'.$target->display_name.'>',
|
||||
trans('general.by') => '<'.$admin->present()->viewUrl().'|'.$admin->display_name.'>',
|
||||
trans('general.to_user') => '<'.$target->present()->viewUrl().'|'.$target->display_name.'>',
|
||||
trans('general.by_user') => '<'.$admin->present()->viewUrl().'|'.$admin->display_name.'>',
|
||||
];
|
||||
|
||||
if ($item->location) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
@@ -47,14 +48,17 @@ class ExpectedCheckinNotification extends Notification
|
||||
*/
|
||||
public function toMail()
|
||||
{
|
||||
$today = Carbon::now();
|
||||
|
||||
$message = (new MailMessage)->markdown('notifications.markdown.expected-checkin',
|
||||
[
|
||||
'expected_checkin_date' => $this->params->expected_checkin,
|
||||
'date' => Helper::getFormattedDateObject($this->params->expected_checkin, 'date', false),
|
||||
'asset' => $this->params->display_name,
|
||||
'serial' => $this->params->serial,
|
||||
'asset_tag' => $this->params->asset_tag,
|
||||
])
|
||||
->subject(trans('mail.Expected_Checkin_Notification', ['name' => $this->params->display_name]));
|
||||
->subject(($today > $this->params->expected_checkin) ? trans('mail.Expected_Checkin_Notification_Pastdue', ['name' => $this->params->display_name]) : trans('mail.Expected_Checkin_Notification', ['name' => $this->params->display_name]));
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use AllowDynamicProperties;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class InventoryAlert extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
@@ -32,9 +34,8 @@ class InventoryAlert extends Notification
|
||||
*/
|
||||
public function via()
|
||||
{
|
||||
$notifyBy = ['mail'];
|
||||
return (!empty($this->items) && $this->threshold !== null) ? ['mail'] : [];
|
||||
|
||||
return $notifyBy;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,7 +49,8 @@ class UserObserver
|
||||
'end_date',
|
||||
'autoassign_licenses',
|
||||
'vip',
|
||||
'password'
|
||||
'password',
|
||||
'permissions'
|
||||
];
|
||||
|
||||
$changed = [];
|
||||
|
||||
@@ -102,7 +102,7 @@ class ActionlogPresenter extends Presenter
|
||||
return 'fa-solid fa-rotate-right';
|
||||
}
|
||||
|
||||
if ($this->action_type == 'note_added') {
|
||||
if ($this->action_type == 'note added') {
|
||||
return 'fas fa-sticky-note';
|
||||
}
|
||||
|
||||
|
||||
@@ -519,7 +519,7 @@ class AssetPresenter extends Presenter
|
||||
|
||||
// Asset tag
|
||||
if ($this->asset_tag) {
|
||||
$str .= ' ('.$this->model->asset_tag.')';
|
||||
$str .= ' #'.$this->model->asset_tag;
|
||||
}
|
||||
|
||||
// Asset Model name
|
||||
|
||||
@@ -14,6 +14,11 @@ class CategoryPresenter extends Presenter
|
||||
public static function dataTableLayout()
|
||||
{
|
||||
$layout = [
|
||||
[
|
||||
'field' => 'checkbox',
|
||||
'checkbox' => true,
|
||||
'titleTooltip' => trans('general.select_all_none'),
|
||||
],
|
||||
[
|
||||
'field' => 'id',
|
||||
'searchable' => false,
|
||||
|
||||
@@ -248,10 +248,20 @@ class LicensePresenter extends Presenter
|
||||
'title' => trans('admin/users/table.email'),
|
||||
'visible' => true,
|
||||
'formatter' => 'emailFormatter',
|
||||
], [
|
||||
'field' => 'department',
|
||||
],
|
||||
[
|
||||
'field' => 'assigned_user.company',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'sortable' => false,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.company'),
|
||||
'visible' => true,
|
||||
'formatter' => 'companiesLinkObjFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'assigned_user.department',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.department'),
|
||||
'visible' => false,
|
||||
|
||||
@@ -14,7 +14,11 @@ class ManufacturerPresenter extends Presenter
|
||||
public static function dataTableLayout()
|
||||
{
|
||||
$layout = [
|
||||
|
||||
[
|
||||
'field' => 'checkbox',
|
||||
'checkbox' => true,
|
||||
'titleTooltip' => trans('general.select_all_none'),
|
||||
],
|
||||
[
|
||||
'field' => 'id',
|
||||
'searchable' => false,
|
||||
|
||||
@@ -13,6 +13,11 @@ class SupplierPresenter extends Presenter
|
||||
public static function dataTableLayout()
|
||||
{
|
||||
$layout = [
|
||||
[
|
||||
'field' => 'checkbox',
|
||||
'checkbox' => true,
|
||||
'titleTooltip' => trans('general.select_all_none'),
|
||||
],
|
||||
[
|
||||
'field' => 'id',
|
||||
'searchable' => false,
|
||||
|
||||
@@ -283,41 +283,36 @@ class ValidationServiceProvider extends ServiceProvider
|
||||
}
|
||||
});
|
||||
|
||||
Validator::extend('is_unique_department', function ($attribute, $value, $parameters, $validator) {
|
||||
/**
|
||||
* Check that the 'name' field is unique in the table while within both company_id and location_id
|
||||
* This is only used by Departments right now, but could be used elsewhere in the future.
|
||||
*/
|
||||
Validator::extend('is_unique_across_company_and_location', function ($attribute, $value, $parameters, $validator) {
|
||||
$data = $validator->getData();
|
||||
$table = array_get($parameters, 0);
|
||||
|
||||
if (
|
||||
array_key_exists('location_id', $data) && $data['location_id'] !== null &&
|
||||
array_key_exists('company_id', $data) && $data['company_id'] !== null
|
||||
) {
|
||||
//for updating existing departments
|
||||
if(array_key_exists('id', $data) && $data['id'] !== null){
|
||||
$count = Department::where('name', $data['name'])
|
||||
->where('location_id', $data['location_id'])
|
||||
->where('company_id', $data['company_id'])
|
||||
->whereNotNull('company_id')
|
||||
->whereNotNull('location_id')
|
||||
->where('id', '!=', $data['id'])
|
||||
->count('name');
|
||||
$count = DB::table($table)->select($attribute)
|
||||
->where($attribute, $value)
|
||||
->whereNull('deleted_at');
|
||||
|
||||
return $count < 1;
|
||||
}else // for entering in new departments
|
||||
{
|
||||
$count = Department::where('name', $data['name'])
|
||||
->where('location_id', $data['location_id'])
|
||||
->where('company_id', $data['company_id'])
|
||||
->whereNotNull('company_id')
|
||||
->whereNotNull('location_id')
|
||||
->count('name');
|
||||
|
||||
return $count < 1;
|
||||
if (array_key_exists('id', $data) && $data['id'] !== null) {
|
||||
$count = $count->where('id', '!=', $data['id']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (array_key_exists('location_id', $data) && $data['location_id'] !== null) {
|
||||
$count = $count->where('location_id', $data['location_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('company_id', $data) && $data['company_id'] !== null) {
|
||||
$count = $count->where('company_id', $data['company_id']);
|
||||
}
|
||||
|
||||
$count = $count->count('name');
|
||||
return $count < 1;
|
||||
|
||||
});
|
||||
|
||||
|
||||
Validator::extend('not_array', function ($attribute, $value, $parameters, $validator) {
|
||||
return !is_array($value);
|
||||
});
|
||||
|
||||
@@ -140,18 +140,32 @@ class Label implements View
|
||||
if ($template->getSupport2DBarcode()) {
|
||||
$barcode2DType = $settings->label2_2d_type;
|
||||
if (($barcode2DType != 'none') && (!is_null($barcode2DType))) {
|
||||
|
||||
$label2_2d_prefix = $settings->label2_2d_prefix ? e($settings->label2_2d_prefix) : '';
|
||||
switch ($settings->label2_2d_target) {
|
||||
case 'ht_tag':
|
||||
$barcode2DTarget = route('ht/assetTag', $asset->asset_tag);
|
||||
break;
|
||||
case 'plain_asset_id':
|
||||
$barcode2DTarget = (string) $asset->id;
|
||||
$barcode2DTarget = $label2_2d_prefix.(string) $asset->id;
|
||||
break;
|
||||
case 'plain_asset_tag':
|
||||
$barcode2DTarget = $asset->asset_tag;
|
||||
$barcode2DTarget = $label2_2d_prefix.$asset->asset_tag;
|
||||
break;
|
||||
case 'plain_serial_number':
|
||||
$barcode2DTarget = $asset->serial;
|
||||
$barcode2DTarget = $label2_2d_prefix.$asset->serial;
|
||||
break;
|
||||
case 'plain_model_number':
|
||||
$barcode2DTarget = $label2_2d_prefix.$asset->model->model_number ?? '';
|
||||
break;
|
||||
case 'plain_model_name':
|
||||
$barcode2DTarget = $label2_2d_prefix.$asset->model->display_name ?? '';
|
||||
break;
|
||||
case 'plain_manufacturer_name':
|
||||
$barcode2DTarget = $label2_2d_prefix.$asset->model->display_name;
|
||||
break;
|
||||
case 'plain_location_name':
|
||||
$barcode2DTarget = $label2_2d_prefix.$asset->location->name;
|
||||
break;
|
||||
case 'location':
|
||||
$barcode2DTarget = $asset->location_id
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user