Files
KarbonDatacenter/app/Services/SubmissionWorkflowService.php
2026-06-24 20:32:14 +08:00

145 lines
5.0 KiB
PHP

<?php
namespace App\Services;
use App\Models\ChecklistTemplate;
use App\Models\DataCentre;
use App\Models\ReportingCycle;
use App\Models\Submission;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
class SubmissionWorkflowService
{
public function __construct(protected FormulaService $formulaService) {}
/**
* Dapatkan atau cipta serahan draf untuk pusat data + kitaran tertentu.
*/
public function findOrCreateDraft(DataCentre $dataCentre, ReportingCycle $cycle): Submission
{
$consultantId = $dataCentre->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');
}
}