ticker work proses
This commit is contained in:
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Ticket;
|
||||
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketLog;
|
||||
use App\Models\TicketApproval;
|
||||
use App\Models\TicketAssignment;
|
||||
use App\Services\Ticket\TicketWorkflowValidator;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class TicketWorkflowService
|
||||
{
|
||||
/**
|
||||
* Workflow History
|
||||
*/
|
||||
public function history(Ticket $ticket)
|
||||
{
|
||||
return TicketLog::where('ticket_id', $ticket->id)
|
||||
->orderByDesc('id')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* =========================
|
||||
* VALIDATION RULE ENGINE
|
||||
* =========================
|
||||
*/
|
||||
|
||||
private function type(Ticket $ticket)
|
||||
{
|
||||
return $ticket->ticketType;
|
||||
}
|
||||
|
||||
private function ensureRequiredAssets(Ticket $ticket, array $payload = [])
|
||||
{
|
||||
$type = $this->type($ticket);
|
||||
|
||||
// PHOTO VALIDATION
|
||||
if ($type?->require_photo) {
|
||||
$photoCount = $payload['photo_count'] ?? 0;
|
||||
|
||||
if ($photoCount <= 0) {
|
||||
throw ValidationException::withMessages([
|
||||
'photo' => 'Foto wajib diupload untuk ticket ini'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// MATERIAL VALIDATION
|
||||
if ($type?->require_material) {
|
||||
$materialCount = $payload['material_count'] ?? 0;
|
||||
|
||||
if ($materialCount <= 0) {
|
||||
throw ValidationException::withMessages([
|
||||
'material' => 'Material wajib diinput untuk ticket ini'
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function ensureCustomer(Ticket $ticket)
|
||||
{
|
||||
$type = $this->type($ticket);
|
||||
|
||||
if ($type?->need_customer && empty($ticket->customer_id)) {
|
||||
throw ValidationException::withMessages([
|
||||
'customer_id' => 'Customer wajib ada untuk ticket ini'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* =========================
|
||||
* ASSIGN / REASSIGN
|
||||
* =========================
|
||||
*/
|
||||
|
||||
public function assign(
|
||||
Ticket $ticket,
|
||||
array $technicians,
|
||||
int $leaderId,
|
||||
int $userId
|
||||
): Ticket {
|
||||
|
||||
TicketWorkflowValidator::allow($ticket, ['approved']);
|
||||
|
||||
DB::transaction(function () use (
|
||||
$ticket,
|
||||
$technicians,
|
||||
$leaderId,
|
||||
$userId
|
||||
) {
|
||||
|
||||
foreach ($technicians as $technicianId) {
|
||||
TicketAssignment::create([
|
||||
'ticket_id' => $ticket->id,
|
||||
'user_id' => $technicianId,
|
||||
'is_leader' => ($technicianId == $leaderId),
|
||||
'assigned_by' => $userId,
|
||||
'assigned_at' => now(),
|
||||
'status' => 'assigned'
|
||||
]);
|
||||
}
|
||||
|
||||
$ticket->update([
|
||||
'status' => 'assigned',
|
||||
'assigned_at' => now()
|
||||
]);
|
||||
|
||||
$this->log($ticket->id, $userId, 'Assign', 'Teknisi ditugaskan');
|
||||
});
|
||||
|
||||
return $ticket->fresh();
|
||||
}
|
||||
|
||||
public function reassign(
|
||||
Ticket $ticket,
|
||||
array $technicians,
|
||||
int $leaderId,
|
||||
int $userId
|
||||
): Ticket {
|
||||
|
||||
DB::transaction(function () use (
|
||||
$ticket,
|
||||
$technicians,
|
||||
$leaderId,
|
||||
$userId
|
||||
) {
|
||||
|
||||
TicketAssignment::where('ticket_id', $ticket->id)->delete();
|
||||
|
||||
foreach ($technicians as $technicianId) {
|
||||
TicketAssignment::create([
|
||||
'ticket_id' => $ticket->id,
|
||||
'user_id' => $technicianId,
|
||||
'is_leader' => ($technicianId == $leaderId),
|
||||
'assigned_by' => $userId,
|
||||
'assigned_at' => now(),
|
||||
'status' => 'assigned'
|
||||
]);
|
||||
}
|
||||
|
||||
$this->log($ticket->id, $userId, 'Reassign', 'Penugasan diperbarui');
|
||||
});
|
||||
|
||||
return $ticket->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* =========================
|
||||
* APPROVAL FLOW
|
||||
* =========================
|
||||
*/
|
||||
|
||||
public function approve(
|
||||
Ticket $ticket,
|
||||
?string $notes,
|
||||
int $userId
|
||||
): Ticket {
|
||||
|
||||
TicketWorkflowValidator::allow($ticket, ['open']);
|
||||
|
||||
DB::transaction(function () use ($ticket, $notes, $userId) {
|
||||
|
||||
TicketApproval::create([
|
||||
'ticket_id' => $ticket->id,
|
||||
'approver_id' => $userId,
|
||||
'status' => 'approved',
|
||||
'notes' => $notes,
|
||||
'approved_at' => now(),
|
||||
'created_at' => now()
|
||||
]);
|
||||
|
||||
$ticket->update([
|
||||
'status' => 'approved',
|
||||
'approved_by' => $userId,
|
||||
'approved_at' => now()
|
||||
]);
|
||||
|
||||
$this->log($ticket->id, $userId, 'Approved', $notes);
|
||||
});
|
||||
|
||||
return $ticket->fresh();
|
||||
}
|
||||
|
||||
public function reject(Ticket $ticket, string $reason, int $userId): Ticket
|
||||
{
|
||||
DB::transaction(function () use ($ticket, $reason, $userId) {
|
||||
|
||||
TicketApproval::create([
|
||||
'ticket_id' => $ticket->id,
|
||||
'approver_id' => $userId,
|
||||
'status' => 'rejected',
|
||||
'notes' => $reason,
|
||||
'created_at' => now()
|
||||
]);
|
||||
|
||||
$ticket->update([
|
||||
'status' => 'rejected',
|
||||
'rejected_by' => $userId,
|
||||
'rejected_at' => now(),
|
||||
'rejection_reason' => $reason
|
||||
]);
|
||||
|
||||
$this->log($ticket->id, $userId, 'Rejected', $reason);
|
||||
});
|
||||
|
||||
return $ticket->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* =========================
|
||||
* WORKFLOW START
|
||||
* =========================
|
||||
*/
|
||||
|
||||
public function start(Ticket $ticket, array $location, int $userId): Ticket
|
||||
{
|
||||
TicketWorkflowValidator::allow($ticket, ['assigned']);
|
||||
|
||||
DB::transaction(function () use ($ticket, $location, $userId) {
|
||||
|
||||
$ticket->update([
|
||||
'status' => 'progress'
|
||||
]);
|
||||
|
||||
$this->log(
|
||||
$ticket->id,
|
||||
$userId,
|
||||
'Start Work',
|
||||
'Pekerjaan dimulai'
|
||||
);
|
||||
});
|
||||
|
||||
return $ticket->fresh();
|
||||
}
|
||||
|
||||
public function pending(Ticket $ticket, string $reason, int $userId): Ticket
|
||||
{
|
||||
$ticket->update(['status' => 'pending']);
|
||||
|
||||
$this->log($ticket->id, $userId, 'Pending', $reason);
|
||||
|
||||
return $ticket->fresh();
|
||||
}
|
||||
|
||||
public function resume(Ticket $ticket, int $userId): Ticket
|
||||
{
|
||||
$ticket->update(['status' => 'progress']);
|
||||
|
||||
$this->log($ticket->id, $userId, 'Resume', 'Dilanjutkan');
|
||||
|
||||
return $ticket->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* =========================
|
||||
* RESOLVE (VALIDATION POINT)
|
||||
* =========================
|
||||
*/
|
||||
|
||||
public function resolve(
|
||||
Ticket $ticket,
|
||||
string $notes,
|
||||
int $userId,
|
||||
array $payload = []
|
||||
): Ticket {
|
||||
|
||||
TicketWorkflowValidator::allow($ticket, ['progress']);
|
||||
|
||||
// 🔥 VALIDASI MATERIAL + PHOTO + CUSTOMER
|
||||
$this->ensureRequiredAssets($ticket, $payload);
|
||||
$this->ensureCustomer($ticket);
|
||||
|
||||
DB::transaction(function () use ($ticket, $notes, $userId) {
|
||||
|
||||
$ticket->update([
|
||||
'status' => 'resolved',
|
||||
'resolved_at' => now()
|
||||
]);
|
||||
|
||||
$this->log($ticket->id, $userId, 'Resolved', $notes);
|
||||
});
|
||||
|
||||
return $ticket->fresh();
|
||||
}
|
||||
|
||||
public function close(Ticket $ticket, ?string $notes, int $userId): Ticket
|
||||
{
|
||||
TicketWorkflowValidator::allow($ticket, ['resolved']);
|
||||
|
||||
$ticket->update([
|
||||
'status' => 'closed',
|
||||
'closed_at' => now()
|
||||
]);
|
||||
|
||||
$this->log($ticket->id, $userId, 'Closed', $notes);
|
||||
|
||||
return $ticket->fresh();
|
||||
}
|
||||
|
||||
public function cancel(Ticket $ticket, string $reason, int $userId): Ticket
|
||||
{
|
||||
$ticket->update(['status' => 'cancelled']);
|
||||
|
||||
$this->log($ticket->id, $userId, 'Cancelled', $reason);
|
||||
|
||||
return $ticket->fresh();
|
||||
}
|
||||
|
||||
public function escalate(Ticket $ticket, string $reason, int $userId): Ticket
|
||||
{
|
||||
$this->log($ticket->id, $userId, 'Escalated', $reason);
|
||||
|
||||
return $ticket->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* =========================
|
||||
* LOG SYSTEM
|
||||
* =========================
|
||||
*/
|
||||
|
||||
private function log(
|
||||
int $ticketId,
|
||||
int $userId,
|
||||
string $activity,
|
||||
?string $notes = null
|
||||
): void {
|
||||
|
||||
TicketLog::create([
|
||||
'ticket_id' => $ticketId,
|
||||
'user_id' => $userId,
|
||||
'activity' => $activity,
|
||||
'notes' => $notes,
|
||||
'created_at'=> now()
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user