store/app/Services/Hr/EmployeeService.php

284 lines
9.7 KiB
PHP

<?php
namespace App\Services\Hr;
use App\Enums\Role;
use App\Models\Employee;
use App\Models\User;
use App\Models\UserProfile;
use App\Services\Media\MediaService;
use App\Support\Media\MediaPresenter;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;
class EmployeeService
{
public function __construct(
private readonly MediaService $mediaService,
) {}
/**
* @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery
*/
public function paginateForIndex(
array $tableQuery,
string $role = '',
string $gender = '',
string $employmentStatus = '',
string $isActive = '',
): LengthAwarePaginator {
$query = User::query()
->with(['profile', 'employee', 'roles'])
->whereDoesntHave('roles', fn ($query) => $query->where('name', Role::DEVELOPER->value))
->when($tableQuery['search'] !== '', function ($query) use ($tableQuery): void {
$search = $tableQuery['search'];
$query->where(function ($query) use ($search): void {
$query->where('email', 'like', "%{$search}%")
->orWhere('username', 'like', "%{$search}%")
->orWhereHas('profile', function ($query) use ($search): void {
$query->where('full_name', 'like', "%{$search}%")
->orWhere('phone_number', 'like', "%{$search}%");
});
});
})
->when($role !== '', fn ($query) => $query->whereHas('roles', fn ($query) => $query->where('name', $role)))
->when($gender !== '', fn ($query) => $query->whereHas('profile', fn ($query) => $query->where('gender', $gender)))
->when($employmentStatus !== '', function ($query) use ($employmentStatus): void {
$query->whereHas('employee', fn ($query) => $query->where('employment_status', $employmentStatus));
})
->when($isActive !== '', fn ($query) => $query->where('is_active', $isActive === '1'));
$this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']);
return $query
->paginate(10)
->withQueryString();
}
public function assignableRoleOptions(): array
{
return Role::assignableSelectOptions();
}
public function findForEdit(User $user): array
{
$user->load(['profile', 'employee', 'roles']);
return [
'employee' => $user,
'profilePhoto' => $user->profile
? MediaPresenter::first($user->profile, 'profile_photo')
: null,
];
}
/**
* @param array<string, mixed> $validated
*/
public function create(array $validated): void
{
try {
DB::transaction(function () use ($validated): void {
$user = User::create([
'email' => $validated['email'],
'username' => $validated['username'],
'password' => Hash::make(config('auth.password_default')),
]);
$profile = UserProfile::create([
'user_id' => $user->id,
'full_name' => $validated['full_name'],
'phone_number' => $validated['phone_number'],
'gender' => $validated['gender'],
'birth_date' => $validated['birth_date'],
'address' => $validated['address'],
]);
$this->syncProfilePhoto($profile, $validated);
if ($validated['role'] !== Role::OWNER->value) {
Employee::create([
'user_id' => $user->id,
'join_date' => $validated['join_date'],
'employment_status' => $validated['employment_status'],
'base_salary' => $validated['base_salary'],
]);
}
$user->syncRoles([$validated['role']]);
});
} catch (ValidationException $e) {
throw $e;
} catch (\Throwable $e) {
Log::error('Gagal membuat karyawan: '.$e->getMessage(), [
'trace' => $e->getTraceAsString(),
]);
throw ValidationException::withMessages([
'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
]);
}
}
/**
* @param array<string, mixed> $validated
*/
public function update(User $user, array $validated): void
{
$employee = $user->employee;
try {
DB::transaction(function () use ($validated, $user, $employee): void {
$user->update([
'email' => $validated['email'],
'username' => $validated['username'],
]);
/** @var UserProfile $profile */
$profile = $user->profile()->updateOrCreate(
['user_id' => $user->id],
[
'full_name' => $validated['full_name'],
'phone_number' => $validated['phone_number'],
'gender' => $validated['gender'],
'birth_date' => $validated['birth_date'],
'address' => $validated['address'],
],
);
$this->syncProfilePhoto($profile, $validated);
$hasEmployee = ! empty($validated['join_date']) && ! empty($validated['employment_status']) && ! empty($validated['base_salary']);
if ($hasEmployee) {
if ($employee) {
$employee->update([
'join_date' => $validated['join_date'],
'employment_status' => $validated['employment_status'],
'base_salary' => $validated['base_salary'],
]);
} else {
Employee::create([
'user_id' => $user->id,
'join_date' => $validated['join_date'],
'employment_status' => $validated['employment_status'],
'base_salary' => $validated['base_salary'],
]);
}
} else {
if ($employee) {
$employee->delete();
}
}
$user->syncRoles([$validated['role']]);
});
} catch (ValidationException $e) {
throw $e;
} catch (\Throwable $e) {
Log::error('Gagal memperbarui karyawan: '.$e->getMessage(), [
'trace' => $e->getTraceAsString(),
]);
throw ValidationException::withMessages([
'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
]);
}
}
public function toggleStatus(User $user, array $validated): void
{
$user->update([
'is_active' => $validated['is_active'],
]);
if (! $validated['is_active']) {
DB::table('sessions')->where('user_id', $user->id)->delete();
}
}
public function resetPassword(User $user): void
{
$user->update([
'password' => config('auth.password_default'),
]);
DB::table('sessions')->where('user_id', $user->id)->delete();
}
public function delete(User $user): void
{
try {
DB::transaction(function () use ($user): void {
$user->employee?->delete();
$user->profile?->delete();
$user->delete();
});
} catch (ValidationException $e) {
throw $e;
} catch (\Throwable $e) {
Log::error('Gagal menghapus karyawan: '.$e->getMessage(), [
'trace' => $e->getTraceAsString(),
]);
throw ValidationException::withMessages([
'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
]);
}
}
/**
* @param array<string, mixed> $validated
*/
private function syncProfilePhoto(UserProfile $profile, array $validated): void
{
$newFiles = ! empty($validated['profile_photo']) ? [$validated['profile_photo']] : [];
$removeIds = $validated['remove_profile_photo_ids'] ?? [];
$this->mediaService->syncCollection($profile, 'profile_photo', $newFiles, $removeIds, 1);
}
private function applySorting(Builder $query, string $sort, string $direction): void
{
$employeeSorts = [
'join_date',
'base_salary',
'employment_status',
];
if (in_array($sort, $employeeSorts, true)) {
$query->orderBy(
Employee::select($sort)
->whereColumn('employees.user_id', 'users.id')
->limit(1),
$direction
);
return;
}
if ($sort === 'full_name') {
$query->orderBy(
UserProfile::select('full_name')
->whereColumn('user_profiles.user_id', 'users.id')
->limit(1),
$direction
);
return;
}
if ($sort === 'email') {
$query->orderBy('email', $direction);
return;
}
$query->latest();
}
}