157 lines
4.3 KiB
PHP
157 lines
4.3 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 Carbon\Carbon;
|
|
use Illuminate\Support\Str;
|
|
|
|
class EmployeeImportTransformer
|
|
{
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function toAttributes(EmployeeRowDto $dto): array
|
|
{
|
|
return [
|
|
'employee_number' => $dto->employeeNumber,
|
|
'full_name' => $dto->fullName,
|
|
'national_id' => $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->employeeNumber === '') {
|
|
$errors[] = 'Employee number is required.';
|
|
}
|
|
|
|
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.";
|
|
}
|
|
|
|
return $errors;
|
|
}
|
|
|
|
public function isDuplicate(EmployeeRowDto $dto): bool
|
|
{
|
|
return Employee::query()
|
|
->where(function ($query) use ($dto): void {
|
|
$query->where('employee_number', $dto->employeeNumber);
|
|
|
|
if ($dto->nationalId) {
|
|
$query->orWhere('national_id', $dto->nationalId);
|
|
}
|
|
})
|
|
->exists();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|