current_consultant_id; if (! $consultantId) { throw ValidationException::withMessages([ 'data_centre_id' => 'Pusat data ini tiada perunding aktif. Sila hubungi JPP.', ]); } return Submission::firstOrCreate( [ 'data_centre_id' => $dataCentre->id, 'reporting_cycle_id' => $cycle->id, ], [ 'consultant_id' => $consultantId, 'checklist_template_id' => $cycle->checklist_template_id ?? ChecklistTemplate::activeTemplate()?->id, 'status' => 'draf', ] ); } /** * Simpan jawapan senarai semak (draf). $answers = [item_id => [value, unit, page_reference, consultant_note]] */ public function saveAnswers(Submission $submission, array $answers): void { DB::transaction(function () use ($submission, $answers) { foreach ($answers as $itemId => $data) { $submission->answers()->updateOrCreate( ['checklist_item_id' => $itemId], [ 'value' => $data['value'] ?? null, 'unit' => $data['unit'] ?? null, 'page_reference' => $data['page_reference'] ?? null, 'consultant_note' => $data['consultant_note'] ?? null, ] ); } // Kira semula nilai formula (A, B, C, D, E ...). $submission->load('answers.item'); $this->formulaService->recalculateAndStore($submission); }); } /** * Hantar serahan secara muktamad (draf -> baru). */ public function submit(Submission $submission, User $user): void { if ($submission->is_locked) { throw ValidationException::withMessages(['status' => 'Serahan telah dikunci dan tidak boleh dihantar semula.']); } // Mesti ada sekurang-kurangnya satu laporan PDF aktif sebelum hantar. if (! $submission->activeDocument()->exists()) { throw ValidationException::withMessages(['document' => 'Sila muat naik laporan PDF sebelum menghantar serahan.']); } $from = $submission->status; // Jika sedang dalam pembetulan, hantar semula sebagai 'dalam_tindakan'. $to = $from === 'pembetulan_perunding' ? 'dalam_tindakan' : 'baru'; $this->changeStatus($submission, $to, $user, 'Serahan dihantar oleh perunding.', [ 'submitted_at' => now(), 'submitted_by' => $user->id, ]); $this->formulaService->recalculateAndStore($submission); } /** * Tukar status serahan dengan rekod sejarah & audit. */ public function changeStatus(Submission $submission, string $to, User $user, ?string $notes = null, array $extra = []): void { DB::transaction(function () use ($submission, $to, $user, $notes, $extra) { $from = $submission->status; $submission->fill(array_merge(['status' => $to], $extra)); $submission->save(); $submission->statusHistories()->create([ 'from_status' => $from, 'to_status' => $to, 'changed_by' => $user->id, 'notes' => $notes, ]); activity('serahan') ->performedOn($submission) ->causedBy($user) ->withProperties(['from' => $from, 'to' => $to, 'notes' => $notes]) ->log("Status serahan ditukar daripada {$from} kepada {$to}"); }); } public function lock(Submission $submission, User $user, ?string $reason = null): void { $submission->update([ 'is_locked' => true, 'locked_by' => $user->id, 'locked_at' => now(), 'lock_reason' => $reason, ]); activity('serahan')->performedOn($submission)->causedBy($user) ->withProperties(['reason' => $reason])->log('Serahan dikunci'); } public function unlock(Submission $submission, User $user, string $reason): void { $submission->update([ 'is_locked' => false, 'lock_reason' => $reason, 'locked_by' => $user->id, 'locked_at' => now(), ]); activity('serahan')->performedOn($submission)->causedBy($user) ->withProperties(['reason' => $reason])->log('Serahan dibuka semula'); } }