fix: auto-violasi dari E-Jurnal via ViolationService + notifikasi realtime
Sync controller sekarang pake ViolationService::recordViolation() biar notifikasi realtime (AppNotification + broadcast event) dan pengecekan SP threshold tetap jalan untuk data dari E-Jurnal. ViolationService: recordViolation() & generateSpLetter() diubah jadi nullable userId supaya bisa dipanggil tanpa user (0 = system-generated dari sync)
This commit is contained in:
@@ -14,11 +14,10 @@ class AttendanceSyncController extends Controller
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Receive attendance push from E-Jurnal.
|
* Receive attendance push from E-Jurnal.
|
||||||
* Auto-generate violations for alpha students.
|
* Auto-generate violations for alpha students (with notifications).
|
||||||
*/
|
*/
|
||||||
public function sync(Request $request)
|
public function sync(Request $request)
|
||||||
{
|
{
|
||||||
// Validate token
|
|
||||||
$expectedToken = Setting::getValue('ejurnal_sync_token', '');
|
$expectedToken = Setting::getValue('ejurnal_sync_token', '');
|
||||||
$providedToken = $request->input('token');
|
$providedToken = $request->input('token');
|
||||||
|
|
||||||
@@ -33,7 +32,6 @@ class AttendanceSyncController extends Controller
|
|||||||
], 401);
|
], 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate payload structure
|
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'attendances' => 'required|array|min:1|max:500',
|
'attendances' => 'required|array|min:1|max:500',
|
||||||
'attendances.*.source_student_id' => 'required|integer',
|
'attendances.*.source_student_id' => 'required|integer',
|
||||||
@@ -52,13 +50,11 @@ class AttendanceSyncController extends Controller
|
|||||||
'violations' => 0,
|
'violations' => 0,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Lookup map: source_student_id -> local student id
|
|
||||||
$sourceIds = array_unique(array_column($attendances, 'source_student_id'));
|
$sourceIds = array_unique(array_column($attendances, 'source_student_id'));
|
||||||
$studentMap = Student::whereIn('source_id', $sourceIds)
|
$studentMap = Student::whereIn('source_id', $sourceIds)
|
||||||
->where('is_active', true)
|
->where('is_active', true)
|
||||||
->pluck('id', 'source_id');
|
->pluck('id', 'source_id');
|
||||||
|
|
||||||
// Track alpha counts per student for auto-violation
|
|
||||||
$alphaCounts = [];
|
$alphaCounts = [];
|
||||||
$alphaDate = null;
|
$alphaDate = null;
|
||||||
|
|
||||||
@@ -73,7 +69,6 @@ class AttendanceSyncController extends Controller
|
|||||||
|
|
||||||
$localStudentId = $studentMap[$sourceId];
|
$localStudentId = $studentMap[$sourceId];
|
||||||
|
|
||||||
// Map status from e-jurnal to aplikasi-ketertiban format
|
|
||||||
$statusMap = [
|
$statusMap = [
|
||||||
'present' => 'hadir',
|
'present' => 'hadir',
|
||||||
'sick' => 'sakit',
|
'sick' => 'sakit',
|
||||||
@@ -83,7 +78,6 @@ class AttendanceSyncController extends Controller
|
|||||||
$localStatus = $statusMap[$item['status']] ?? $item['status'];
|
$localStatus = $statusMap[$item['status']] ?? $item['status'];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Upsert: unique constraint on (student_id, date, lesson_hour)
|
|
||||||
$attendance = Attendance::updateOrCreate(
|
$attendance = Attendance::updateOrCreate(
|
||||||
[
|
[
|
||||||
'student_id' => $localStudentId,
|
'student_id' => $localStudentId,
|
||||||
@@ -93,7 +87,7 @@ class AttendanceSyncController extends Controller
|
|||||||
[
|
[
|
||||||
'status' => $localStatus,
|
'status' => $localStatus,
|
||||||
'notes' => $item['notes'] ?? null,
|
'notes' => $item['notes'] ?? null,
|
||||||
'recorded_by' => null, // system-generated
|
'recorded_by' => null,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -103,7 +97,6 @@ class AttendanceSyncController extends Controller
|
|||||||
$results['updated']++;
|
$results['updated']++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track alpha for auto-violation
|
|
||||||
if ($localStatus === 'alpha') {
|
if ($localStatus === 'alpha') {
|
||||||
$alphaCounts[$localStudentId] = ($alphaCounts[$localStudentId] ?? 0) + 1;
|
$alphaCounts[$localStudentId] = ($alphaCounts[$localStudentId] ?? 0) + 1;
|
||||||
$alphaDate = $item['date'];
|
$alphaDate = $item['date'];
|
||||||
@@ -114,22 +107,23 @@ class AttendanceSyncController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-generate violation for alpha students
|
// Auto-generate violations via service (includes realtime notifications)
|
||||||
if (!empty($alphaCounts)) {
|
if (!empty($alphaCounts)) {
|
||||||
$alphaType = ViolationType::where('slug', 'alpha')->first();
|
$alphaType = ViolationType::where('slug', 'alpha')->first();
|
||||||
|
|
||||||
if ($alphaType) {
|
if ($alphaType) {
|
||||||
|
$violationService = app(\App\Services\ViolationService::class);
|
||||||
foreach ($alphaCounts as $studentId => $count) {
|
foreach ($alphaCounts as $studentId => $count) {
|
||||||
try {
|
try {
|
||||||
\App\Models\Violation::create([
|
$violationService->recordViolation([
|
||||||
'student_id' => $studentId,
|
'student_id' => $studentId,
|
||||||
'violation_type_id' => $alphaType->id,
|
'violation_type_id' => $alphaType->id,
|
||||||
'violation_date' => $alphaDate,
|
'violation_date' => $alphaDate,
|
||||||
'points' => max(1, (int) round(($alphaType->points / 10) * $count)),
|
'points' => max(1, (int) round(($alphaType->points / 10) * $count)),
|
||||||
'description' => "Alpha - Tidak hadir tanpa keterangan ({$count} jam pelajaran)",
|
'description' => "Alpha - Tidak hadir tanpa keterangan ({$count} jam pelajaran)",
|
||||||
'notes' => 'Dibuat otomatis dari sinkron E-Jurnal.',
|
'notes' => 'Dibuat otomatis dari sinkron E-Jurnal.',
|
||||||
'recorded_by' => null,
|
'evidences' => [],
|
||||||
]);
|
], null);
|
||||||
$results['violations']++;
|
$results['violations']++;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$results['errors'][] = "Violation creation failed for student={$studentId}: " . $e->getMessage();
|
$results['errors'][] = "Violation creation failed for student={$studentId}: " . $e->getMessage();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use Illuminate\Support\Facades\DB;
|
|||||||
|
|
||||||
class ViolationService
|
class ViolationService
|
||||||
{
|
{
|
||||||
public function recordViolation(array $data, int $userId): Violation
|
public function recordViolation(array $data, ?int $userId = null): Violation
|
||||||
{
|
{
|
||||||
$violation = DB::transaction(function () use ($data, $userId) {
|
$violation = DB::transaction(function () use ($data, $userId) {
|
||||||
$violation = Violation::create([
|
$violation = Violation::create([
|
||||||
@@ -72,7 +72,7 @@ class ViolationService
|
|||||||
return $violation;
|
return $violation;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function generateSpLetter(Student $student, SpThreshold $threshold, int $userId): SpLetter
|
protected function generateSpLetter(Student $student, SpThreshold $threshold, ?int $userId = null): SpLetter
|
||||||
{
|
{
|
||||||
$schoolName = Setting::getValue('school_name', 'SMK');
|
$schoolName = Setting::getValue('school_name', 'SMK');
|
||||||
$letterCount = SpLetter::where('sp_threshold_id', $threshold->id)->count() + 1;
|
$letterCount = SpLetter::where('sp_threshold_id', $threshold->id)->count() + 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user