*/ public function list(Tenant $tenant, ?string $search = null): Collection { return $this->staffQuery($tenant) ->with(['role', 'scanCategories' => fn ($query) => $query->orderBy('nombre')]) ->when($search, function (Builder $query, string $search): void { $query->where(function (Builder $query) use ($search): void { $query->where('nombre_apellido', 'like', "%{$search}%") ->orWhere('dni', 'like', "%{$search}%") ->orWhere('email', 'like', "%{$search}%"); }); }) ->orderBy('nombre_apellido') ->get(); } /** @return Collection */ private function assignableCategories(Tenant $tenant): Collection { return Category::query() ->whereNull('categoria_id') ->where(function (Builder $query) use ($tenant): void { $query->where('tenant_code', $tenant->codigo) ->orWhereHas('catalogItems', fn (Builder $items) => $items ->where('tenant_code', $tenant->codigo)); }) ->orderBy('nombre') ->get(); } /** @param array $data */ public function create(Tenant $tenant, array $data): User { $this->assertCategoriesBelongToTenant($tenant, $data['category_ids']); return DB::transaction(function () use ($tenant, $data): User { $staff = User::query()->create([ ...Arr::only($data, ['nombre_apellido', 'dni', 'email']), 'email' => mb_strtolower(trim((string) $data['email'])), 'password' => Str::random(64), 'rol_codigo' => RoleCode::Scanner->value, 'tenant_codigo' => $tenant->codigo, ]); $staff->scanCategories()->sync($data['category_ids']); return $staff->load('role', 'scanCategories'); }); } /** @param array $data */ public function update(Tenant $tenant, int $staffId, array $data): User { $staff = $this->find($tenant, $staffId); $this->assertCategoriesBelongToTenant($tenant, $data['category_ids']); return DB::transaction(function () use ($staff, $data): User { $attributes = Arr::only($data, ['nombre_apellido', 'dni', 'email']); $attributes['email'] = mb_strtolower(trim((string) $data['email'])); $staff->update($attributes); $staff->scanCategories()->sync($data['category_ids']); return $staff->load('role', 'scanCategories'); }); } public function delete(Tenant $tenant, int $staffId): void { $this->find($tenant, $staffId)->delete(); } public function find(Tenant $tenant, int $staffId): User { return $this->staffQuery($tenant)->findOrFail($staffId); } private function staffQuery(Tenant $tenant): Builder { return User::query() ->where('tenant_codigo', $tenant->codigo) ->where('rol_codigo', RoleCode::Scanner->value); } /** @param array $categoryIds */ private function assertCategoriesBelongToTenant(Tenant $tenant, array $categoryIds): void { $validIds = $this->assignableCategories($tenant) ->whereIn('id', $categoryIds) ->pluck('id'); if ($validIds->count() !== count($categoryIds)) { throw ValidationException::withMessages([ 'category_ids' => 'Una o más categorías no pertenecen al tenant.', ]); } } }