Files
hr/app/Services/Import/EmployeeImportTransformer.php

174 lines
5.0 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\Import;
use App\DTOs\Import\EmployeeRowDto;
use App\Enums\EmploymentStatus;
use App\Enums\Gender;
use App\Models\Department;
use App\Models\Employee;
use App\Models\Position;
use App\Models\Shift;
use App\Support\Countries;
use Carbon\Carbon;
use Illuminate\Support\Str;
class EmployeeImportTransformer
{
/**
* @return array<string, mixed>
*/
public function toAttributes(EmployeeRowDto $dto): array
{
return [
'employee_number' => $dto->employeeNumber !== '' ? $dto->employeeNumber : null,
'full_name' => $dto->fullName,
'national_id' => $this->resolveNationality($dto->nationalId),
'gender' => $this->resolveGender($dto->gender),
'birth_date' => $this->parseDate($dto->birthDate),
'phone' => $dto->phone ?? config('hr.default_phone'),
'address' => $dto->address,
'department_id' => $this->resolveDepartmentId($dto->department),
'position_id' => $this->resolvePositionId($dto->position),
'shift_id' => $this->resolveShiftId($dto->shift),
'employment_status' => $this->resolveEmploymentStatus($dto->employmentStatus),
'hire_date' => $this->parseDate($dto->hireDate),
'notes' => $dto->notes,
];
}
/**
* @return array<int, string>
*/
public function validate(EmployeeRowDto $dto): array
{
$errors = [];
if ($dto->fullName === '') {
$errors[] = 'Full name is required.';
}
if ($dto->department && $this->resolveDepartmentId($dto->department) === null) {
$errors[] = "Department \"{$dto->department}\" was not found.";
}
if ($dto->position && $this->resolvePositionId($dto->position) === null) {
$errors[] = "Position \"{$dto->position}\" was not found.";
}
if ($dto->shift && $this->resolveShiftId($dto->shift) === null) {
$errors[] = "Shift \"{$dto->shift}\" was not found.";
}
if ($dto->nationalId && $this->resolveNationality($dto->nationalId) === null) {
$errors[] = "Nationality \"{$dto->nationalId}\" was not found.";
}
return $errors;
}
public function isDuplicate(EmployeeRowDto $dto): bool
{
$hasEmployeeNumber = $dto->employeeNumber !== '';
$nationality = $this->resolveNationality($dto->nationalId);
if (! $hasEmployeeNumber && $nationality === null) {
return false;
}
return Employee::query()
->where(function ($query) use ($dto, $hasEmployeeNumber, $nationality): void {
if ($hasEmployeeNumber) {
$query->where('employee_number', $dto->employeeNumber);
}
if ($nationality !== null) {
$hasEmployeeNumber
? $query->orWhere('national_id', $nationality)
: $query->where('national_id', $nationality);
}
})
->exists();
}
private function resolveNationality(?string $value): ?string
{
return Countries::resolve($value);
}
private function resolveGender(?string $value): ?Gender
{
if ($value === null) {
return null;
}
$normalized = Str::lower(trim($value));
return Gender::tryFrom($normalized)
?? match ($normalized) {
'm', 'man' => Gender::Male,
'f', 'woman' => Gender::Female,
default => null,
};
}
private function resolveEmploymentStatus(?string $value): EmploymentStatus
{
if ($value === null || trim($value) === '') {
return EmploymentStatus::Active;
}
$normalized = Str::lower(str_replace(' ', '_', trim($value)));
return EmploymentStatus::tryFrom($normalized) ?? EmploymentStatus::Active;
}
private function resolveDepartmentId(?string $value): ?int
{
if ($value === null || trim($value) === '') {
return null;
}
return Department::query()
->where('name', $value)
->orWhere('code', $value)
->value('id');
}
private function resolvePositionId(?string $value): ?int
{
if ($value === null || trim($value) === '') {
return null;
}
return Position::query()->where('name', $value)->value('id');
}
private function resolveShiftId(?string $value): ?int
{
if ($value === null || trim($value) === '') {
return null;
}
return Shift::query()
->where('name', $value)
->orWhere('code', $value)
->value('id');
}
private function parseDate(?string $value): ?string
{
if ($value === null || trim($value) === '') {
return null;
}
try {
return Carbon::parse($value)->toDateString();
} catch (\Throwable) {
return null;
}
}
}