283 lines
12 KiB
PHP
283 lines
12 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Enums\NotificationType;
|
|
use App\Models\AttendanceHistory;
|
|
use App\Models\AttendanceHistoryDaily;
|
|
use App\Models\Notification;
|
|
use Illuminate\Http\Request;
|
|
|
|
use App\Models\Permission;
|
|
use App\Models\Schedule;
|
|
use App\Models\Student;
|
|
use App\Services\NotificationHelperService;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class PermissionController extends Controller
|
|
{
|
|
|
|
protected NotificationHelperService $notificationHelper;
|
|
public $latOfAttendance;
|
|
public $lonOfAttendance;
|
|
|
|
public function __construct(NotificationHelperService $notificationHelper)
|
|
{
|
|
$this->notificationHelper = $notificationHelper;
|
|
$this->initCoordinatesFromConfig();
|
|
}
|
|
|
|
|
|
|
|
private function initCoordinatesFromConfig(): void
|
|
{
|
|
$coordinate = config('coordinate.coordinate', '-7.604032330848524, 112.10176449791652');
|
|
[$lat, $lon] = array_map('trim', explode(',', $coordinate));
|
|
$this->latOfAttendance = (float) $lat;
|
|
$this->lonOfAttendance = (float) $lon;
|
|
}
|
|
|
|
public function index()
|
|
{
|
|
$permissions = Permission::all();
|
|
return view('pages.perizinan.perizinan', compact('permissions'));
|
|
}
|
|
|
|
public function permissionAccept(Request $request, int $id): RedirectResponse
|
|
{
|
|
try {
|
|
// session-auth user for web controller
|
|
$user = $request->user();
|
|
if (!$user) {
|
|
return redirect()->back()->with('error', 'Unauthorized');
|
|
}
|
|
|
|
// find permission by id
|
|
$permission = Permission::find($id);
|
|
if (!$permission) {
|
|
return redirect()->back()->with('error', 'Data perizinan tidak ditemukan');
|
|
}
|
|
|
|
// permission must be processed only, if already accepted or rejected, cannot be processed again.
|
|
if ($permission->status === 'diterima') {
|
|
return redirect()->back()->with('warning', 'Perizinan sudah diterima sebelumnya');
|
|
}
|
|
if ($permission->status === 'ditolak') {
|
|
return redirect()->back()->with('warning', 'Perizinan sudah ditolak sebelumnya');
|
|
}
|
|
|
|
$student = Student::find($permission->id_student);
|
|
if (!$student) {
|
|
return redirect()->back()->with('error', 'Data siswa tidak ditemukan');
|
|
}
|
|
|
|
DB::transaction(function () use ($permission, $user, $request, $student) {
|
|
// update status to accepted
|
|
$permission->status = 'diterima';
|
|
$permission->approved_by = $user->id; // set approved by teacher id
|
|
$permission->feedback = $request->input('feedback', null); // optional feedback message from teacher
|
|
$permission->save();
|
|
|
|
// * create attendance record history to related date (only fill with schedule that has active academic period, and match with date permission).
|
|
$permissionReason = $permission->reason; // contoh: 'sakit', 'izin', 'dispen', 'lainnya'
|
|
if ($permissionReason === 'lainnya') {
|
|
$permissionReason = 'izin';
|
|
}
|
|
|
|
$permissionDate = Carbon::parse($permission->date_permission)->startOfDay();
|
|
$permissionTimePeriod = (int) $permission->time_period;
|
|
$createdSchoolDays = 0;
|
|
$currentDate = $permissionDate->copy();
|
|
|
|
// Count time_period as school days; weekend days are skipped and replaced by the next weekday.
|
|
while ($createdSchoolDays < $permissionTimePeriod) {
|
|
if ($currentDate->isWeekend()) {
|
|
$currentDate->addDay();
|
|
continue;
|
|
}
|
|
|
|
$dayName = strtolower($currentDate->locale('id')->dayName);
|
|
$dayIndex = $currentDate->dayOfWeekIso; // 1..7
|
|
|
|
// search related schedule with day_of_week same and active academic period
|
|
$schedules = Schedule::query()
|
|
->where('id_class', $student->id_class)
|
|
->whereHas('academicPeriods', function ($q) {
|
|
$q->where('is_active', 1);
|
|
})
|
|
->where(function ($q) use ($dayName, $dayIndex) {
|
|
$q->where('day_of_week', $dayName)
|
|
->orWhere('day_of_week', (string) $dayIndex);
|
|
})
|
|
->get();
|
|
|
|
foreach ($schedules as $schedule) {
|
|
AttendanceHistory::updateOrCreate(
|
|
[
|
|
'id_schedule' => $schedule->id,
|
|
'id_student' => $student->id,
|
|
'attendance_date' => $currentDate->toDateString(),
|
|
],
|
|
[
|
|
'id_class' => $schedule->id_class,
|
|
'period_number' => $schedule->period_start,
|
|
'coordinate' => $this->latOfAttendance . ', ' . $this->lonOfAttendance,
|
|
'status' => $permissionReason,
|
|
]
|
|
);
|
|
}
|
|
|
|
// update attendancehistorydaily to 'izin'
|
|
// Cari berdasarkan id_student, id_class, dan tanggal dari created_at
|
|
// Jika ada, update. Jika tidak ada, create
|
|
$attendanceDaily = AttendanceHistoryDaily::whereDate('created_at', $currentDate->toDateString())
|
|
->where('id_student', $student->id)
|
|
->where('id_class', $student->id_class)
|
|
->first();
|
|
|
|
if ($attendanceDaily) {
|
|
$attendanceDaily->update([
|
|
'status' => 'izin',
|
|
'picture' => '',
|
|
]);
|
|
} else {
|
|
AttendanceHistoryDaily::create([
|
|
'id_student' => $student->id,
|
|
'id_class' => $student->id_class,
|
|
'created_at' => $currentDate,
|
|
'status' => 'izin',
|
|
'picture' => '',
|
|
]);
|
|
}
|
|
|
|
$createdSchoolDays++;
|
|
$currentDate->addDay();
|
|
}
|
|
});
|
|
|
|
// notification zone: send notification to related student after permission approved
|
|
$userStudent = $student->user;
|
|
if (!$userStudent) {
|
|
return redirect()->back()->with('error', 'User siswa tidak ditemukan untuk notifikasi');
|
|
}
|
|
|
|
$fcmToken = $userStudent->device_token;
|
|
$title = 'Perizinan Disetujui';
|
|
$body = 'Permohonan izin Anda telah disetujui.';
|
|
|
|
// Push notification is optional; database notification must still be created.
|
|
if (!empty($fcmToken)) {
|
|
try {
|
|
$template = $this->notificationHelper->templatePermissionApproval($fcmToken);
|
|
$this->notificationHelper->send($template);
|
|
$title = $template->title;
|
|
$body = $template->body;
|
|
} catch (\Throwable $th) {
|
|
// Ignore push errors so approval flow and DB notification keep working.
|
|
}
|
|
}
|
|
|
|
$createTemplate = $this->notificationHelper->createTemplateForStudent(
|
|
(int) $userStudent->id,
|
|
$title,
|
|
$body,
|
|
NotificationType::Permission,
|
|
(int) $user->id
|
|
);
|
|
|
|
Notification::create($createTemplate->toArray());
|
|
|
|
return redirect()->back()->with('success', 'Izin diterima untuk siswa ' . $student->name);
|
|
} catch (\Exception $e) {
|
|
return redirect()->back()->with('error', 'Terjadi kesalahan: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
public function permissionReject(Request $request, int $id): RedirectResponse
|
|
{
|
|
try {
|
|
// session-auth user for web controller
|
|
$user = $request->user();
|
|
if (!$user) {
|
|
return redirect()->back()->with('error', 'Unauthorized');
|
|
}
|
|
|
|
// find permission by id
|
|
$permission = Permission::find($id);
|
|
if (!$permission) {
|
|
return redirect()->back()->with('error', 'Data perizinan tidak ditemukan');
|
|
}
|
|
|
|
// permission must be processed only, if already accepted or rejected, cannot be processed again.
|
|
if ($permission->status === 'diterima') {
|
|
return redirect()->back()->with('warning', 'Perizinan sudah diterima sebelumnya');
|
|
}
|
|
if ($permission->status === 'ditolak') {
|
|
return redirect()->back()->with('warning', 'Perizinan sudah ditolak sebelumnya');
|
|
}
|
|
|
|
$student = Student::find($permission->id_student);
|
|
if (!$student) {
|
|
return redirect()->back()->with('error', 'Data siswa tidak ditemukan');
|
|
}
|
|
|
|
DB::transaction(function () use ($permission, $user, $request) {
|
|
|
|
// update status to rejected
|
|
$permission->status = 'ditolak';
|
|
$permission->approved_by = $user->id; // set approved by teacher id
|
|
$permission->feedback = $request->input('feedback', null); // optional feedback message from teacher
|
|
$permission->save();
|
|
|
|
});
|
|
|
|
// notification zone: send notification to related student after permission rejected
|
|
|
|
$userStudent = $student->user;
|
|
if (!$userStudent) {
|
|
return redirect()->back()->with('error', 'User siswa tidak ditemukan untuk notifikasi');
|
|
}
|
|
|
|
$fcmToken = $userStudent->device_token;
|
|
|
|
// Push notification is optional; database notification must still be created.
|
|
if (!empty($fcmToken)) {
|
|
try {
|
|
$template = $this->notificationHelper->templatePermissionRejection(
|
|
$fcmToken,
|
|
$request->input('feedback', null) // optional custom message from teacher feedback
|
|
);
|
|
$this->notificationHelper->send($template);
|
|
$title = $template->title;
|
|
$body = $template->body;
|
|
} catch (\Throwable $th) {
|
|
// Ignore push errors so rejection flow and DB notification keep working.
|
|
$title = 'Perizinan Ditolak';
|
|
$body = 'Permohonan izin Anda telah ditolak.';
|
|
}
|
|
} else {
|
|
$title = 'Perizinan Ditolak';
|
|
$body = 'Permohonan izin Anda telah ditolak.';
|
|
}
|
|
|
|
$createTemplate = $this->notificationHelper->createTemplateForStudent(
|
|
(int) $userStudent->id,
|
|
$title,
|
|
$body,
|
|
NotificationType::Permission,
|
|
(int) $user->id
|
|
);
|
|
|
|
Notification::create($createTemplate->toArray());
|
|
|
|
return redirect()->back()->with('success', 'Izin ditolak untuk siswa ' . $student->name);
|
|
|
|
} catch (\Exception $e) {
|
|
return redirect()->back()->with('error', 'Terjadi kesalahan: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
}
|