validate([ 'status' => ['nullable', 'in:active,inactive,standby'], 'area_id' => ['nullable', 'integer'], 'q' => ['nullable', 'string', 'max:100'], 'per_page' => ['nullable', 'integer', 'min:1', 'max:500'], ]); $perPage = (int) $request->input('per_page', 25); $teams = CollectionTeam::query() ->with(['area', 'driver', 'scanner', 'truck', 'helpers.user', 'currentTrip', 'tenant']) ->when($request->filled('status'), fn ($q) => $q->where('status', $request->string('status'))) ->when($request->filled('area_id'), fn ($q) => $q->where('area_id', $request->integer('area_id'))) ->when($request->filled('q'), fn ($q) => $q->where('name', 'like', '%'.$request->string('q').'%')) ->orderBy('name') ->paginate($perPage); return $this->ok( CollectionTeamResource::collection($teams), null, [ 'page' => $teams->currentPage(), 'per_page' => $teams->perPage(), 'total' => $teams->total(), 'last_page' => $teams->lastPage(), ], ); } public function store(StoreCollectionTeamRequest $request): JsonResponse { $data = $request->validated(); $override = (bool) ($data['override_conflicts'] ?? false); if (! isset($data['helper_count']) && isset($data['helper_ids'])) { $data['helper_count'] = count($data['helper_ids']); $data['helper_assignment_mode'] = 'manual'; } $helperIds = $this->resolveHelpers($data); $expectedCount = (int) ($data['helper_count'] ?? 0); if ($expectedCount > 0 && count($helperIds) < $expectedCount) { return $this->fail('Not enough available helpers found.', [ 'helper_count' => ['Only '.count($helperIds).' available helper(s) found.'], ], 422); } if (! $override) { $conflicts = $this->conflicts->check( $data['driver_id'] ?? null, $data['scanner_id'] ?? null, $data['truck_id'] ?? null, $helperIds, ); if (! empty($conflicts)) { return $this->fail('Team has assignment conflicts', ['conflicts' => $conflicts], 422); } } unset($data['helper_ids'], $data['override_conflicts'], $data['helper_assignment_mode']); $team = DB::transaction(function () use ($data, $helperIds) { $team = CollectionTeam::create($data); $today = now()->toDateString(); foreach ($helperIds as $uid) { TeamMember::create([ 'team_id' => $team->id, 'user_id' => $uid, 'role_in_team' => TeamMember::ROLE_HELPER, 'assigned_from' => $today, ]); } return $team; }); return $this->created( new CollectionTeamResource( $team->fresh()->load(['area', 'driver', 'scanner', 'truck', 'helpers.user']), ), 'Team created', ); } public function show(CollectionTeam $team): JsonResponse { $team->load(['area', 'driver', 'scanner', 'truck', 'helpers.user', 'currentTrip']); return $this->ok(new CollectionTeamResource($team)); } public function update(StoreCollectionTeamRequest $request, CollectionTeam $team): JsonResponse { $data = $request->validated(); $override = (bool) ($data['override_conflicts'] ?? false); $helperIds = null; if (array_key_exists('helper_count', $data) || array_key_exists('helper_ids', $data)) { if (! isset($data['helper_count']) && isset($data['helper_ids'])) { $data['helper_count'] = count($data['helper_ids']); $data['helper_assignment_mode'] = 'manual'; } $helperIds = $this->resolveHelpers($data, $team->id); $expectedCount = (int) ($data['helper_count'] ?? 0); if ($expectedCount > 0 && count($helperIds) < $expectedCount) { return $this->fail('Not enough available helpers found.', [ 'helper_count' => ['Only '.count($helperIds).' available helper(s) found.'], ], 422); } } if (! $override) { $conflicts = $this->conflicts->check( $data['driver_id'] ?? $team->driver_id, $data['scanner_id'] ?? $team->scanner_id, $data['truck_id'] ?? $team->truck_id, $helperIds ?? $team->helpers()->pluck('user_id')->toArray(), $team->id, ); if (! empty($conflicts)) { return $this->fail('Team has assignment conflicts', ['conflicts' => $conflicts], 422); } } unset($data['helper_ids'], $data['override_conflicts'], $data['helper_assignment_mode']); DB::transaction(function () use ($team, $data, $helperIds) { $team->update($data); if ($helperIds !== null) { $today = now()->toDateString(); $team->members()->where('role_in_team', TeamMember::ROLE_HELPER)->delete(); foreach ($helperIds as $uid) { TeamMember::create([ 'team_id' => $team->id, 'user_id' => $uid, 'role_in_team' => TeamMember::ROLE_HELPER, 'assigned_from' => $today, ]); } } }); return $this->ok( new CollectionTeamResource( $team->fresh()->load(['area', 'driver', 'scanner', 'truck', 'helpers.user']), ), 'Team updated', ); } protected function resolveHelpers(array $data, ?int $excludeTeamId = null): array { $mode = $data['helper_assignment_mode'] ?? 'manual'; $count = (int) ($data['helper_count'] ?? 0); if ($count <= 0) { return []; } if ($mode === 'manual') { return $data['helper_ids'] ?? []; } $override = (bool) ($data['override_conflicts'] ?? false); $query = User::where('role', User::ROLE_HELPER)->where('status', User::STATUS_ACTIVE); if (! $override) { $today = now()->toDateString(); $assignedHelperIds = TeamMember::query() ->where('role_in_team', TeamMember::ROLE_HELPER) ->whereDate('assigned_from', '<=', $today) ->where(function ($q) use ($today) { $q->whereNull('assigned_until')->orWhereDate('assigned_until', '>=', $today); }) ->when($excludeTeamId, fn ($q, $id) => $q->where('team_id', '!=', $id)) ->pluck('user_id') ->toArray(); $query->whereNotIn('id', $assignedHelperIds); } return $query->inRandomOrder()->take($count)->pluck('id')->toArray(); } public function destroy(CollectionTeam $team): JsonResponse { $team->delete(); return $this->ok(null, 'Team deleted'); } }