company_id : null) ?? \Landlord::getTenantId('company_id'); } /** * Find a not-soft-deleted user that belongs to the current company. * * @param int|string $id * @return User|null */ public function find($id) { return User::where('id', $id) ->where('company_id', $this->resolveCompanyId()) ->whereNull('deleted_at') ->first(); } /** * Validate a single user payload — the exact CSV row rules. * * @param array $data extension (optional), name, email, password * @return array list of error messages (empty array = valid) */ public function validate(array $data) { $extension = trim((string) ($data['extension'] ?? '')); $fullName = trim((string) ($data['name'] ?? '')); $email = trim((string) ($data['email'] ?? '')); $password = trim((string) ($data['password'] ?? '')); $errors = []; if (empty($fullName)) { $errors[] = 'Full Name is required.'; } if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) { $errors[] = 'Valid Email is required.'; } if (empty($password)) { $errors[] = 'Password is required.'; } // Duplicate email check — includes soft-deleted rows if (!empty($email) && User::withTrashed()->where('email', $email)->exists()) { $errors[] = "Email '{$email}' already exists."; } // Extension / ID uniqueness check — only checked when provided if (!empty($extension)) { if (!ctype_digit($extension) || strlen($extension) < 3 || strlen($extension) > 8) { $errors[] = 'Extension must be 3-8 digits.'; } elseif (User::withTrashed()->where('id', $extension)->exists()) { $errors[] = "Extension '{$extension}' is already taken."; } } return $errors; } /** * Create one user exactly like a CSV import row. * * When `extension` is not provided the `id` key is left out of the * payload and MySQL auto-increment assigns the extension — the same way * a blank Extension cell is handled in the CSV import. * * @param array $data extension (optional), name, email, password * @param int|string|null $companyId * @return User * @throws UserImportValidationException */ public function create(array $data, $companyId = null) { $errors = $this->validate($data); if (!empty($errors)) { throw new UserImportValidationException($errors); } $extension = trim((string) ($data['extension'] ?? '')); $fullName = trim((string) ($data['name'] ?? '')); $email = trim((string) ($data['email'] ?? '')); $password = trim((string) ($data['password'] ?? '')); $context = $this->context($companyId); // ── Split full name ─────────────────────────────────────── $nameParts = explode(' ', $fullName, 2); $firstName = $nameParts[0]; $lastName = $nameParts[1] ?? ''; $userData = [ 'first_name' => $firstName, 'last_name' => $lastName, 'name' => $fullName, 'email' => $email, 'password' => bcrypt($password), 'company_id' => $context['companyId'], 'language_id' => $context['langId'], 'date_format_id' => $context['dateId'], 'time_format_id' => $context['timeId'], 'time_zone' => $context['timezone'], 'theme' => $context['theme'], 'is_active' => true, 'access_to_all_entity' => true, ]; if (!empty($extension)) { $userData['id'] = (int) $extension; } $userRepo = \App::make(UserRepository::class); $entity = $userRepo->create($userData); if ($context['userRole']) { $entity->syncRoles([$context['userRole']]); } return $entity; } /** * Resolve company defaults and the "user" role exactly like the CSV import. * * @param int|string|null $companyId * @return array */ protected function context($companyId = null) { $companyId = $companyId ?? $this->resolveCompanyId(); // Resolve defaults from first existing user in same company (or hard-code safe values) $defaults = User::where('company_id', $companyId)->first(); $langId = $defaults->language_id ?? 1; $dateId = $defaults->date_format_id ?? 1; $timeId = $defaults->time_format_id ?? 2; $timezone = $defaults->time_zone ?? 'Africa/Cairo'; $theme = $defaults->theme ?? 'theme-green'; // Resolve the "user" role for this company $userRole = Role::where('company_id', $companyId) ->where('name', 'user') ->first(); return [ 'companyId' => $companyId, 'langId' => $langId, 'dateId' => $dateId, 'timeId' => $timeId, 'timezone' => $timezone, 'theme' => $theme, 'userRole' => $userRole, ]; } }