232 lines
8.0 KiB
PHP
232 lines
8.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Administrator;
|
|
|
|
use App\Models\StaffAttendance;
|
|
use App\Models\User;
|
|
use App\Services\ApplicationUrlService;
|
|
use App\Services\SemesterRangeService;
|
|
use App\Services\Staff\StaffTimeOffLinkService;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Mail;
|
|
|
|
class AdministratorAbsenceService
|
|
{
|
|
public function __construct(
|
|
protected AdministratorSharedService $shared,
|
|
protected User $userModel,
|
|
protected StaffAttendance $staffAttendanceModel,
|
|
protected SemesterRangeService $semesterRangeService,
|
|
protected StaffTimeOffLinkService $staffTimeOffLinkService,
|
|
protected ApplicationUrlService $urls,
|
|
) {}
|
|
|
|
public function getAbsenceFormData(int $userId): array
|
|
{
|
|
$admin = $this->userModel->find($userId);
|
|
$displayName = $admin
|
|
? trim(($admin->firstname ?? '').' '.($admin->lastname ?? ''))
|
|
: 'Administrator';
|
|
|
|
$semester = $this->shared->getSemester();
|
|
$schoolYear = $this->shared->getSchoolYear();
|
|
|
|
$existing = $this->staffAttendanceModel
|
|
->where('user_id', $userId)
|
|
->where('semester', $semester)
|
|
->where('school_year', $schoolYear)
|
|
->orderByDesc('date')
|
|
->get()
|
|
->toArray();
|
|
|
|
return [
|
|
'admin_name' => $displayName,
|
|
'semester' => $semester,
|
|
'schoolYear' => $schoolYear,
|
|
'existing' => $existing,
|
|
'availableDates' => $this->shared->allowedAbsenceDates(),
|
|
];
|
|
}
|
|
|
|
public function submit(Request $request, int $userId): array
|
|
{
|
|
$semester = $this->shared->getSemester();
|
|
$schoolYear = $this->shared->getSchoolYear();
|
|
|
|
if ($schoolYear === '') {
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Semester or school year not configured.',
|
|
'status' => 422,
|
|
];
|
|
}
|
|
|
|
$dates = (array) $request->input('dates', []);
|
|
$reasonType = trim((string) $request->input('reason_type', ''));
|
|
$reasonText = trim((string) $request->input('reason', ''));
|
|
|
|
if ($reasonText === '') {
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Reason is required.',
|
|
'status' => 422,
|
|
];
|
|
}
|
|
|
|
$reasonBase = $reasonType !== '' ? strtolower($reasonType) : '';
|
|
$reason = $reasonBase !== '' ? ($reasonBase.': '.$reasonText) : $reasonText;
|
|
|
|
$allowedSet = array_fill_keys($this->shared->allowedAbsenceDates(), true);
|
|
|
|
$roleName = method_exists($this->userModel, 'getUserRole')
|
|
? ($this->userModel->getUserRole($userId) ?: null)
|
|
: null;
|
|
|
|
$saved = 0;
|
|
$invalid = [];
|
|
$savedDates = [];
|
|
|
|
$dates = array_values(array_unique(array_map('strval', $dates)));
|
|
|
|
foreach ($dates as $d) {
|
|
$d = trim($d);
|
|
if ($d === '') {
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
$parsed = Carbon::createFromFormat('Y-m-d', $d);
|
|
if ($parsed->format('Y-m-d') !== $d || empty($allowedSet[$d])) {
|
|
$invalid[] = $d;
|
|
|
|
continue;
|
|
}
|
|
} catch (\Throwable) {
|
|
$invalid[] = $d;
|
|
|
|
continue;
|
|
}
|
|
|
|
$semesterForDate = $this->semesterRangeService->getSemesterForDate($d);
|
|
if ($semesterForDate === '') {
|
|
$semesterForDate = $semester;
|
|
}
|
|
|
|
$ok = $this->staffAttendanceModel->upsertOne(
|
|
userId: $userId,
|
|
roleName: $roleName,
|
|
date: $d,
|
|
semester: $semesterForDate,
|
|
schoolYear: $schoolYear,
|
|
status: StaffAttendance::STATUS_ABSENT,
|
|
reason: $reason,
|
|
editorId: $userId
|
|
);
|
|
|
|
if ($ok) {
|
|
$saved++;
|
|
$savedDates[] = $d;
|
|
}
|
|
}
|
|
|
|
if (! empty($invalid)) {
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Invalid dates: '.implode(', ', $invalid),
|
|
'status' => 422,
|
|
];
|
|
}
|
|
|
|
$this->sendPrincipalNotification(
|
|
userId: $userId,
|
|
roleName: $roleName,
|
|
semester: $semester,
|
|
schoolYear: $schoolYear,
|
|
reasonType: $reasonType,
|
|
reasonText: $reasonText,
|
|
dates: $dates,
|
|
savedDates: $savedDates
|
|
);
|
|
|
|
return [
|
|
'success' => true,
|
|
'message' => $saved.' day(s) saved as absent.',
|
|
'saved' => $saved,
|
|
'dates' => $savedDates,
|
|
'status' => 200,
|
|
];
|
|
}
|
|
|
|
protected function sendPrincipalNotification(
|
|
int $userId,
|
|
?string $roleName,
|
|
string $semester,
|
|
string $schoolYear,
|
|
string $reasonType,
|
|
string $reasonText,
|
|
array $dates,
|
|
array $savedDates
|
|
): void {
|
|
try {
|
|
$user = $this->userModel->find($userId);
|
|
$fullName = trim(($user->firstname ?? '').' '.($user->lastname ?? '')) ?: 'Administrator';
|
|
$userEmail = $user->email ?? '';
|
|
$role = $roleName ?: 'admin';
|
|
$dateList = ! empty($savedDates) ? implode(', ', $savedDates) : implode(', ', $dates);
|
|
|
|
$subject = sprintf(
|
|
'TimeOff Request: %s (%s) — %s',
|
|
$fullName,
|
|
ucfirst((string) $role),
|
|
$dateList ?: 'No dates'
|
|
);
|
|
|
|
$submittedAt = now()->toDateTimeString();
|
|
|
|
$body = '<div style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:1.5;">'
|
|
.'<h3 style="margin:0 0 8px;">New TimeOff Request</h3>'
|
|
.'<p style="margin:0 0 12px;">A staff time-off request was submitted from the administrator portal.</p>'
|
|
.'<table cellpadding="6" cellspacing="0" style="border-collapse:collapse;">'
|
|
.'<tr><td><strong>Name</strong></td><td>'.e($fullName).'</td></tr>'
|
|
.'<tr><td><strong>Email</strong></td><td>'.e($userEmail).'</td></tr>'
|
|
.'<tr><td><strong>Role</strong></td><td>'.e((string) $role).'</td></tr>'
|
|
.'<tr><td><strong>Semester</strong></td><td>'.e($semester).'</td></tr>'
|
|
.'<tr><td><strong>School Year</strong></td><td>'.e($schoolYear).'</td></tr>'
|
|
.'<tr><td><strong>Reason Type</strong></td><td>'.e($reasonType ?: '-').'</td></tr>'
|
|
.'<tr><td><strong>Reason</strong></td><td>'.e($reasonText).'</td></tr>'
|
|
.'<tr><td><strong>Dates</strong></td><td>'.e($dateList ?: '-').'</td></tr>'
|
|
.'<tr><td><strong>Submitted At</strong></td><td>'.e($submittedAt).'</td></tr>'
|
|
.'</table>'
|
|
.'</div>';
|
|
|
|
$token = $this->staffTimeOffLinkService->createToken([
|
|
'uid' => $userId,
|
|
'email' => $userEmail,
|
|
'name' => $fullName,
|
|
'role' => $role,
|
|
'dates' => $dateList ?: '-',
|
|
'reason' => $reasonText,
|
|
'reason_type' => $reasonType,
|
|
'submitted_at' => $submittedAt,
|
|
'origin' => 'administrator portal',
|
|
]);
|
|
|
|
$notifyUrl = $this->urls->timeoffNotifyUrl($token);
|
|
|
|
$body .= '<p style="margin-top:16px;">Click <a href="'.e($notifyUrl).'">Send confirmation email to '
|
|
.e($fullName)
|
|
.'</a> so the staff member is notified automatically. This link expires in 14 days.</p>';
|
|
|
|
$principalEmail = env('PRINCIPAL_EMAIL', 'principal@alrahmaisgl.org');
|
|
|
|
Mail::html($body, function ($message) use ($principalEmail, $subject) {
|
|
$message->to($principalEmail)->subject($subject);
|
|
});
|
|
} catch (\Throwable $e) {
|
|
Log::error('Failed to send TimeOff email (admin): '.$e->getMessage());
|
|
}
|
|
}
|
|
}
|