Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 225 |
|
0.00% |
0 / 15 |
CRAP | |
0.00% |
0 / 1 |
ClientManagementUsersController | |
0.00% |
0 / 225 |
|
0.00% |
0 / 15 |
1406 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
users | |
0.00% |
0 / 31 |
|
0.00% |
0 / 1 |
30 | |||
usersCategory | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
createUserManually | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
createUserByEmails | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
resendInvitation | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
updateUser | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
resetPassword | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
assignRole | |
0.00% |
0 / 75 |
|
0.00% |
0 / 1 |
210 | |||
moveToInvitedUsers | |
0.00% |
0 / 30 |
|
0.00% |
0 / 1 |
2 | |||
moveToUsers | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
exportUsersCsv | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
deactivateUsers | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
20 | |||
deleteUsers | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
unassignUsers | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace App\Http\Controllers\v1\AdminPortal; |
4 | |
5 | use App\Enums\HttpStatusCode; |
6 | use App\Http\Models\Plans; |
7 | use Illuminate\Http\Request; |
8 | use App\Helpers\FlyMSGLogger; |
9 | use App\Http\Models\Auth\Role; |
10 | use App\Http\Models\Auth\User; |
11 | use App\Events\User\Registered; |
12 | use App\Exceptions\ExpectedException; |
13 | use Illuminate\Validation\Rule; |
14 | use Illuminate\Http\JsonResponse; |
15 | use App\Http\Controllers\Controller; |
16 | use App\Http\Models\Admin\CompanyGroup; |
17 | use App\Http\Models\SalesProTeamManager; |
18 | use App\Http\Requests\AssignRoleRequest; |
19 | use App\Http\Requests\UpdateUserRequest; |
20 | use App\Http\Models\Admin\CompanyLicenses; |
21 | use App\Http\Requests\ResetPasswordRequest; |
22 | use App\Exports\ClientManagementUsersExport; |
23 | use App\Http\Models\Admin\AdminUserInvitation; |
24 | use App\Http\Models\Auth\UserRole; |
25 | use App\Http\Requests\AssignMoveUserToCompanyRequest; |
26 | use App\Http\Requests\ResendInvitationRequest; |
27 | use Illuminate\Validation\ValidationException; |
28 | use App\Http\Requests\CreateUserByEmailRequest; |
29 | use App\Http\Requests\CreateUserManuallyRequest; |
30 | use App\Http\Requests\UsersCategoryFilterRequest; |
31 | use App\Http\Resources\ClientManagementInvitedUserResource; |
32 | use App\Http\Services\ClientManagementUsersService; |
33 | use App\Http\Services\ClientManagementInvitedUserService; |
34 | use App\Http\Resources\ClientManagementUserResource; |
35 | use App\Http\Resources\CMCEditUserResource; |
36 | use App\Http\Services\Admin\Users\AdminUsersService; |
37 | use GPBMetadata\Google\Api\Http; |
38 | use Illuminate\Support\Facades\Cache; |
39 | |
40 | class ClientManagementUsersController extends Controller |
41 | { |
42 | |
43 | public function __construct( |
44 | private AdminUsersService $adminUsersService, |
45 | public ClientManagementUsersService $clientManagementUsersService, |
46 | public ClientManagementInvitedUserService $clientManagementInvitedUserService |
47 | ) {} |
48 | |
49 | public function users(Request $request): JsonResponse |
50 | { |
51 | $deactivated = $request->has('deactivated') && $request->deactivated == "true"; |
52 | $perPage = intval(request('perPage', 0)); |
53 | $page = intval(request('page', 1)); |
54 | $filter = request('filter', ''); |
55 | $categories = request('categories', ''); |
56 | $license_types = request('selectedSubscriptionTypes', ''); |
57 | $account_status_list = request('selectedAccountStatus', ''); |
58 | $sortBy = request('sort_by', ''); |
59 | $sortOrder = request('sort_order', ''); |
60 | |
61 | if ($categories) { |
62 | $categories = explode(',', urldecode($categories)); |
63 | } else { |
64 | $categories = []; |
65 | } |
66 | |
67 | if ($license_types) { |
68 | $license_types = explode(',', urldecode($license_types)); |
69 | } else { |
70 | $license_types = []; |
71 | } |
72 | |
73 | $account_status = [$account_status_list]; |
74 | |
75 | if (strpos($account_status_list, ',') !== false) { |
76 | $account_status = explode(",", $account_status_list); |
77 | } |
78 | |
79 | $userData = $this->clientManagementUsersService->getUsersPaginated($filter, $deactivated, $perPage, $page, $categories, $sortBy, $sortOrder, $license_types, $account_status); |
80 | |
81 | return response()->json( |
82 | data: [ |
83 | 'success' => true, |
84 | 'data' => [ |
85 | 'items' => ClientManagementUserResource::collection($userData['users']), |
86 | 'total' => $userData['total'], |
87 | 'total_pages' => $userData['total_pages'], |
88 | 'current_page' => $userData['current_page'], |
89 | ], |
90 | ], |
91 | status: HttpStatusCode::OK->value |
92 | ); |
93 | } |
94 | |
95 | public function usersCategory(UsersCategoryFilterRequest $request): JsonResponse |
96 | { |
97 | $validatedData = $request->validated(); |
98 | |
99 | $data = $this->clientManagementUsersService->categorizeUsers($validatedData['status'] ?? null); |
100 | |
101 | return response()->json([ |
102 | 'success' => true, |
103 | 'data' => $data |
104 | ]); |
105 | } |
106 | |
107 | public function createUserManually(CreateUserManuallyRequest $request): JsonResponse |
108 | { |
109 | $validatedData = $request->validated(); |
110 | |
111 | $user = $this->clientManagementUsersService->createUserManually(auth()->user(), $validatedData); |
112 | |
113 | return response()->json([ |
114 | 'success' => true, |
115 | 'data' => (new ClientManagementUserResource($user)) |
116 | ]); |
117 | } |
118 | |
119 | public function createUserByEmails(CreateUserByEmailRequest $request): JsonResponse |
120 | { |
121 | $validatedData = $request->validated(); |
122 | $emails = $validatedData['emails']; |
123 | |
124 | $invitations = $this->clientManagementUsersService->createUserByEmails(auth()->user(), $emails); |
125 | |
126 | return response()->json([ |
127 | 'success' => true, |
128 | 'message' => "Users invited successfully", |
129 | 'data' => ClientManagementUserResource::collection($invitations), |
130 | ]); |
131 | } |
132 | |
133 | public function resendInvitation(ResendInvitationRequest $request): JsonResponse |
134 | { |
135 | $validatedData = $request->validated(); |
136 | |
137 | $this->clientManagementUsersService->resendInvitations(auth()->user(), $validatedData['emails']); |
138 | |
139 | return response()->json([ |
140 | 'success' => true, |
141 | 'message' => "Invitation resent", |
142 | ]); |
143 | } |
144 | |
145 | public function updateUser(UpdateUserRequest $request): JsonResponse |
146 | { |
147 | $user = User::find($request->user); |
148 | |
149 | $user->fill($request->validated()); |
150 | $user->save(); |
151 | |
152 | return response()->json([ |
153 | 'success' => true, |
154 | 'user' => new ClientManagementUserResource($user) |
155 | ]); |
156 | } |
157 | |
158 | public function resetPassword(ResetPasswordRequest $request): JsonResponse |
159 | { |
160 | $validatedData = $request->validated(); |
161 | |
162 | $this->clientManagementUsersService->resetPassword(auth()->user(), $validatedData['user_ids']); |
163 | |
164 | return response()->json([ |
165 | 'success' => true, |
166 | 'message' => "Password reset successfully. User will get the link to update their password.", |
167 | ]); |
168 | } |
169 | |
170 | public function assignRole(AssignRoleRequest $request, User $user): JsonResponse |
171 | { |
172 | $validated_data = $request->validated(); |
173 | |
174 | $role = Role::find($validated_data['role_name']); |
175 | if (!$role) { |
176 | $role = Role::where("name", $validated_data['role_name'])->first(); |
177 | if (!$role) { |
178 | throw new ExpectedException("Role not found"); |
179 | } |
180 | } |
181 | |
182 | $adminRole = Role::where("name", Role::GLOBAL_ADMIN)->first(); |
183 | |
184 | if ($role->name != Role::GLOBAL_ADMIN) { |
185 | $isAdmin = UserRole::where("role_id", $adminRole->id) |
186 | ->where("user_id", $user->id) |
187 | ->count() > 0; |
188 | |
189 | if ($isAdmin) { |
190 | $roleId = $adminRole->id; |
191 | $companyId = $user->company_id; |
192 | $currentUserId = $user->id; |
193 | |
194 | $exists = UserRole::raw(function ($collection) use ($roleId, $companyId, $currentUserId) { |
195 | return $collection->aggregate([ |
196 | [ |
197 | '$lookup' => [ |
198 | 'from' => 'users', |
199 | 'let' => ['userId' => ['$toObjectId' => '$user_id']], |
200 | 'pipeline' => [ |
201 | ['$match' => [ |
202 | '$expr' => [ |
203 | '$eq' => ['$$userId', '$_id'] |
204 | ] |
205 | ]], |
206 | ], |
207 | 'as' => 'user' |
208 | ] |
209 | ], |
210 | [ |
211 | '$match' => [ |
212 | 'role_id' => $roleId, |
213 | 'user.company_id' => $companyId, |
214 | 'user_id' => ['$ne' => $currentUserId] |
215 | ] |
216 | ], |
217 | [ |
218 | '$limit' => 1 |
219 | ] |
220 | ]); |
221 | }); |
222 | |
223 | $isLastAdmin = empty($exists->toArray()); |
224 | |
225 | if ($isAdmin && $isLastAdmin) { |
226 | throw new ExpectedException("Last admin cannot be demoted", 409); |
227 | } |
228 | } |
229 | } |
230 | |
231 | UserRole::where("user_id", $user->id)->delete(); |
232 | |
233 | UserRole::create([ |
234 | 'user_id' => $user->id, |
235 | 'role_id' => $role->id, |
236 | ]); |
237 | if ($role->name == Role::GROUP_ADMIN || $role->name = Role::REPORTING_ADMIN) { |
238 | $group_ids = $validated_data['groups'] ?? []; |
239 | |
240 | $management_record = $user->sales_pro_team_manager; |
241 | if (!$management_record) { |
242 | $management_record = new SalesProTeamManager(); |
243 | $management_record->user_id = $user->id; |
244 | $management_record->company_id = $user->company_id; |
245 | $management_record->first_name = $user->first_name; |
246 | $management_record->last_name = $user->last_name; |
247 | $management_record->email = $user->email; |
248 | $management_record->save(); |
249 | } |
250 | |
251 | if (!empty($group_ids)) { |
252 | $groups = CompanyGroup::whereIn('_id', $group_ids)->get(); |
253 | if ($groups->isNotEmpty()) { |
254 | $management_record->groups()->sync($groups); |
255 | } |
256 | } |
257 | |
258 | $new_group_id = !empty($group_ids) ? $group_ids[0] : null; |
259 | if ($user->company_group_id !== $new_group_id) { |
260 | $user->company_group_id = $new_group_id; |
261 | $user->save(); |
262 | } |
263 | } |
264 | |
265 | return response()->json([ |
266 | 'success' => true, |
267 | 'message' => "User role updated successfully", |
268 | 'user' => new CMCEditUserResource($user->fresh()), |
269 | ]); |
270 | } |
271 | |
272 | public function moveToInvitedUsers(Request $request): JsonResponse |
273 | { |
274 | $validatedData = $request->validate([ |
275 | 'company_id' => 'required|exists:companies,_id', |
276 | 'plan_identifier' => ['required', Rule::in([ |
277 | Plans::STARTER_YEARLY_IDENTIFIER, |
278 | Plans::GROWTH_YEARLY_IDENTIFIER, |
279 | Plans::PROFESSIONAL_YEARLY_IDENTIFIER, |
280 | Plans::ProPlanTeamsSMB, |
281 | Plans::FREEMIUM_IDENTIFIER |
282 | ])], |
283 | 'role_name' => [ |
284 | 'required', |
285 | 'string', |
286 | Rule::in([ |
287 | Role::USER, |
288 | Role::REPORTING_ADMIN, |
289 | Role::GROUP_ADMIN, |
290 | Role::GLOBAL_ADMIN, |
291 | Role::VENGRESO_ADMIN, |
292 | ]) |
293 | ], |
294 | 'user_ids' => 'required|array|min:1', |
295 | 'group_id' => 'sometimes|string|exists:company_groups,_id', |
296 | 'subgroup_id' => 'sometimes|string|exists:company_groups,_id', |
297 | ]); |
298 | |
299 | $data = $this->clientManagementInvitedUserService->moveToInvitedUsers($validatedData); |
300 | |
301 | return response()->json([ |
302 | 'success' => true, |
303 | 'message' => "Admin Invitation updated successfully", |
304 | 'data' => ClientManagementInvitedUserResource::collection($data), |
305 | ]); |
306 | } |
307 | |
308 | public function moveToUsers(AssignMoveUserToCompanyRequest $request): JsonResponse |
309 | { |
310 | $validatedData = $request->validated(); |
311 | $admin = auth()->user(); |
312 | |
313 | $data = $this->clientManagementUsersService->moveToUsers($validatedData, $admin); |
314 | |
315 | return response()->json([ |
316 | 'success' => true, |
317 | 'message' => "Users updated successfully", |
318 | 'data' => CMCEditUserResource::collection($data), |
319 | ]); |
320 | } |
321 | |
322 | public function exportUsersCsv(Request $request) |
323 | { |
324 | try { |
325 | $userIds = $request->users ? explode(',', $request->users) : []; |
326 | $onlyDeactivatedUsers = $request->deactivated == "true"; |
327 | |
328 | return (new ClientManagementUsersExport($userIds, $onlyDeactivatedUsers))->download('Users.csv'); |
329 | } catch (\Throwable $th) { |
330 | // Send dev notification |
331 | FlyMSGLogger::logError(__METHOD__, $th); |
332 | return response()->json([ |
333 | 'success' => false, |
334 | 'message' => $th->getMessage(), |
335 | ], 500); |
336 | } |
337 | } |
338 | |
339 | public function deactivateUsers(Request $request): JsonResponse |
340 | { |
341 | $validatedData = $request->validate([ |
342 | 'user_ids' => 'required|array|min:1', |
343 | 'user_ids.*' => ['required', function ($attribute, $value, $fail) { |
344 | // Disallow deleting user own account |
345 | if ($value == auth()->user()->id) { |
346 | return $fail('You cannot deactivate your own account.'); |
347 | } |
348 | |
349 | // Check if user exists in either 'users' or 'admin_user_invitations' |
350 | $existsInUsers = \DB::table('users')->where('_id', $value)->exists(); |
351 | $existsInInvites = \DB::table('admin_user_invitations')->where('_id', $value)->exists(); |
352 | |
353 | if (!($existsInUsers || $existsInInvites)) { |
354 | return $fail("The user ID {$value} does not exist in users or admin_user_invitations."); |
355 | } |
356 | }], |
357 | ]); |
358 | |
359 | $this->adminUsersService->deactivateUserBulk($validatedData['user_ids'], auth()->user()->id, ""); |
360 | |
361 | return response()->json([ |
362 | 'success' => true, |
363 | 'message' => "Users deactivated successfully", |
364 | ]); |
365 | } |
366 | |
367 | public function deleteUsers(Request $request): JsonResponse |
368 | { |
369 | $validatedData = $request->validate([ |
370 | 'users' => 'required', |
371 | ]); |
372 | |
373 | $this->adminUsersService->deleteUsers($validatedData['users'], auth()->user(), null); |
374 | |
375 | return response()->json([ |
376 | 'success' => true, |
377 | 'message' => "Users deleted successfully", |
378 | ]); |
379 | } |
380 | |
381 | public function unassignUsers(Request $request): JsonResponse |
382 | { |
383 | $validatedData = $request->validate([ |
384 | 'users' => 'required', |
385 | ]); |
386 | |
387 | $this->adminUsersService->unassignUsers($validatedData['users'], auth()->user(), null); |
388 | |
389 | return response()->json([ |
390 | 'success' => true, |
391 | 'message' => "User unassigned successfully", |
392 | ]); |
393 | } |
394 | } |