1
0

ticker work proses

This commit is contained in:
Wian Drs
2026-06-22 13:34:49 +07:00
parent 844b8a0ff0
commit b52e67c428
22 changed files with 1193 additions and 16 deletions
@@ -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()
]);
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Services\Ticket;
use App\Models\Ticket;
use Exception;
class TicketWorkflowValidator
{
public static function allow(
Ticket $ticket,
array $allowedStatuses
): void {
if (! in_array($ticket->status, $allowedStatuses)) {
throw new Exception(
"Ticket status '{$ticket->status}' tidak valid untuk proses ini"
);
}
}
}