big update all menus
This commit is contained in:
@@ -4,34 +4,45 @@ namespace App\Services\Ticket;
|
||||
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketLog;
|
||||
use App\Models\TicketType;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TicketService
|
||||
{
|
||||
public function create(array $data): Ticket
|
||||
public function create(array $data, User $actor, ?int $tenantId): Ticket
|
||||
{
|
||||
return DB::transaction(function () use ($data) {
|
||||
return DB::transaction(function () use ($data, $actor, $tenantId) {
|
||||
|
||||
$tenantId = $actor->isMasterAdmin() ? ($data['tenant_id'] ?? $tenantId) : $tenantId;
|
||||
abort_unless($tenantId, 422, 'Tenant wajib dipilih.');
|
||||
if (! empty($data['customer_id'])) abort_unless(\App\Models\Customer::whereKey($data['customer_id'])->where('tenant_id', $tenantId)->exists(), 422, 'Customer bukan milik tenant aktif.');
|
||||
$type = TicketType::findOrFail($data['ticket_type_id']);
|
||||
|
||||
$ticket = Ticket::create([
|
||||
'ticket_no' => $this->generateTicketNo(),
|
||||
'ticket_type_id' => $data['ticket_type_id'],
|
||||
'customer_id' => $data['customer_id'] ?? null,
|
||||
'tenant_id' => $tenantId,
|
||||
'title' => $data['title'],
|
||||
'description' => $data['description'] ?? null,
|
||||
'priority' => $data['priority'],
|
||||
'status' => 'open',
|
||||
'created_by' => 1
|
||||
'status' => $type->need_approval ? 'open' : 'approved',
|
||||
'sla_minutes' => $type->sla_minutes,
|
||||
'created_by' => $actor->id
|
||||
,'approved_by' => $type->need_approval ? null : $actor->id
|
||||
,'approved_at' => $type->need_approval ? null : now()
|
||||
]);
|
||||
|
||||
TicketLog::create([
|
||||
'ticket_id' => $ticket->id,
|
||||
'user_id' => 1,
|
||||
'user_id' => $actor->id,
|
||||
'activity' => 'Ticket Created',
|
||||
'notes' => 'Ticket dibuat oleh sistem',
|
||||
'notes' => 'Ticket dibuat oleh '.$actor->name,
|
||||
'created_at'=> now()
|
||||
]);
|
||||
|
||||
return $ticket;
|
||||
return $ticket->load(['ticketType','customer','tenant','creator','assignments.user']);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -76,4 +87,6 @@ class TicketService
|
||||
STR_PAD_LEFT
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function generateNo(): string { return $this->generateTicketNo(); }
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class TicketWorkflowService
|
||||
|
||||
// PHOTO VALIDATION
|
||||
if ($type?->require_photo) {
|
||||
$photoCount = $payload['photo_count'] ?? 0;
|
||||
$photoCount = $payload['photo_count'] ?? ($ticket->customer?->images()->count() ?? 0);
|
||||
|
||||
if ($photoCount <= 0) {
|
||||
throw ValidationException::withMessages([
|
||||
@@ -50,7 +50,7 @@ class TicketWorkflowService
|
||||
|
||||
// MATERIAL VALIDATION
|
||||
if ($type?->require_material) {
|
||||
$materialCount = $payload['material_count'] ?? 0;
|
||||
$materialCount = $payload['material_count'] ?? \App\Models\TicketMaterial::where('ticket_id', $ticket->id)->count();
|
||||
|
||||
if ($materialCount <= 0) {
|
||||
throw ValidationException::withMessages([
|
||||
@@ -85,6 +85,8 @@ class TicketWorkflowService
|
||||
): Ticket {
|
||||
|
||||
TicketWorkflowValidator::allow($ticket, ['approved']);
|
||||
$validCount=\App\Models\UserTenant::where('tenant_id',$ticket->tenant_id)->whereIn('user_id',$technicians)->distinct('user_id')->count('user_id');
|
||||
if($validCount!==count(array_unique($technicians)))throw ValidationException::withMessages(['technicians'=>'Semua teknisi harus terdaftar pada tenant ticket.']);
|
||||
|
||||
DB::transaction(function () use (
|
||||
$ticket,
|
||||
@@ -219,8 +221,10 @@ class TicketWorkflowService
|
||||
public function start(Ticket $ticket, array $location, int $userId): Ticket
|
||||
{
|
||||
TicketWorkflowValidator::allow($ticket, ['assigned']);
|
||||
$this->ensureAssignedActor($ticket,$userId);
|
||||
|
||||
DB::transaction(function () use ($ticket, $location, $userId) {
|
||||
$this->attachFiles($ticket,$location['stored_file_ids'],'start',$userId,$location['notes']??null);
|
||||
|
||||
$ticket->update([
|
||||
'status' => 'progress'
|
||||
@@ -237,17 +241,17 @@ class TicketWorkflowService
|
||||
return $ticket->fresh();
|
||||
}
|
||||
|
||||
public function pending(Ticket $ticket, string $reason, int $userId): Ticket
|
||||
public function pending(Ticket $ticket, string $reason, int $userId, array $fileIds=[]): Ticket
|
||||
{
|
||||
$ticket->update(['status' => 'pending']);
|
||||
|
||||
$this->log($ticket->id, $userId, 'Pending', $reason);
|
||||
TicketWorkflowValidator::allow($ticket, ['progress']);$this->ensureAssignedActor($ticket,$userId);
|
||||
DB::transaction(function()use($ticket,$reason,$userId,$fileIds){$this->attachFiles($ticket,$fileIds,'pending',$userId,$reason);$ticket->update(['status'=>'pending']);$this->log($ticket->id,$userId,'Pending',$reason);});
|
||||
|
||||
return $ticket->fresh();
|
||||
}
|
||||
|
||||
public function resume(Ticket $ticket, int $userId): Ticket
|
||||
{
|
||||
TicketWorkflowValidator::allow($ticket, ['pending']);$this->ensureAssignedActor($ticket,$userId);
|
||||
$ticket->update(['status' => 'progress']);
|
||||
|
||||
$this->log($ticket->id, $userId, 'Resume', 'Dilanjutkan');
|
||||
@@ -269,12 +273,15 @@ class TicketWorkflowService
|
||||
): Ticket {
|
||||
|
||||
TicketWorkflowValidator::allow($ticket, ['progress']);
|
||||
$this->ensureAssignedActor($ticket,$userId);
|
||||
if ($ticket->source === 'new_installation' && $ticket->installation_step !== 'completed') throw ValidationException::withMessages(['installation_step'=>'Seluruh proses pemasangan dan aktivasi harus diselesaikan terlebih dahulu.']);
|
||||
|
||||
// 🔥 VALIDASI MATERIAL + PHOTO + CUSTOMER
|
||||
$this->ensureRequiredAssets($ticket, $payload);
|
||||
$this->ensureCustomer($ticket);
|
||||
|
||||
DB::transaction(function () use ($ticket, $notes, $userId) {
|
||||
DB::transaction(function () use ($ticket, $notes, $userId, $payload) {
|
||||
$this->attachFiles($ticket,$payload['stored_file_ids']??[],'resolve',$userId,$notes);
|
||||
|
||||
$ticket->update([
|
||||
'status' => 'resolved',
|
||||
@@ -290,6 +297,7 @@ class TicketWorkflowService
|
||||
public function close(Ticket $ticket, ?string $notes, int $userId): Ticket
|
||||
{
|
||||
TicketWorkflowValidator::allow($ticket, ['resolved']);
|
||||
if (! $ticket->verified_at) throw ValidationException::withMessages(['verification'=>'Ticket harus diverifikasi sebelum ditutup.']);
|
||||
|
||||
$ticket->update([
|
||||
'status' => 'closed',
|
||||
@@ -338,4 +346,15 @@ class TicketWorkflowService
|
||||
'created_at'=> now()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function ensureAssignedActor(Ticket $ticket,int $userId):void
|
||||
{
|
||||
$user=\App\Models\User::findOrFail($userId);
|
||||
if($user->isMasterAdmin()||$user->isTenantOwner($ticket->tenant_id))return;
|
||||
abort_unless(TicketAssignment::where('ticket_id',$ticket->id)->where('user_id',$userId)->whereIn('status',['assigned','accepted'])->exists(),403,'Pekerjaan hanya dapat diproses teknisi yang ditugaskan.');
|
||||
}
|
||||
private function attachFiles(Ticket $ticket,array $ids,string $stage,int $userId,?string $notes=null):void
|
||||
{
|
||||
foreach(array_unique($ids) as $id){$file=\App\Models\StoredFile::findOrFail($id);abort_unless((int)$file->tenant_id===(int)$ticket->tenant_id,422,'File bukan milik tenant ticket.');\App\Models\TicketAttachment::firstOrCreate(['ticket_id'=>$ticket->id,'stored_file_id'=>$id,'stage'=>$stage],['uploaded_by'=>$userId,'notes'=>$notes]);}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace App\Services\Ticket;
|
||||
|
||||
use App\Models\Ticket;
|
||||
use Exception;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class TicketWorkflowValidator
|
||||
{
|
||||
@@ -14,9 +14,7 @@ class TicketWorkflowValidator
|
||||
|
||||
if (! in_array($ticket->status, $allowedStatuses)) {
|
||||
|
||||
throw new Exception(
|
||||
"Ticket status '{$ticket->status}' tidak valid untuk proses ini"
|
||||
);
|
||||
throw ValidationException::withMessages(['status' => "Ticket berstatus '{$ticket->status}' tidak dapat menjalankan proses ini."]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user