Files
WMS/Modules/Helpdesk/Services/TicketService.php
T
2026-06-12 12:58:34 +01:00

117 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
namespace Modules\Helpdesk\Services;
use App\Models\User;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Modules\Helpdesk\Models\Comment;
use Modules\Helpdesk\Models\Status;
use Modules\Helpdesk\Models\Ticket;
final class TicketService
{
public function __construct(
private readonly TicketReferenceService $references,
private readonly AgentAssignmentService $assignment,
private readonly AttachmentService $attachments,
private readonly WorkflowNotificationService $workflow,
) {}
/**
* Create a ticket and store initial attachments.
*
* @param array<string, mixed> $data
* @param array<int, UploadedFile> $files
*/
public function create(User $user, array $data, array $files = []): Ticket
{
return DB::transaction(function () use ($user, $data, $files): Ticket {
$status = Status::query()
->where('slug', config('helpdesk.default_status_slug', 'open'))
->first();
if (! $status) {
$status = Status::query()->create([
'name' => 'Open',
'slug' => config('helpdesk.default_status_slug', 'open'),
'colour' => '#2563eb',
'sort_order' => 1,
'is_default' => true,
'is_closed' => false,
]);
}
/** @var Ticket $ticket */
$ticket = Ticket::query()->create([
'reference' => $this->references->next(),
'company_id' => $data['company_id'] ?? ($user->company_id ?? null),
'user_id' => $user->id,
'agent_id' => $this->assignment->selectAgentId($data['category_id'] ?? null),
'status_id' => $status->id,
'priority_id' => $data['priority_id'],
'category_id' => $data['category_id'] ?? null,
'project_id' => $data['project_id'] ?? null,
'team_id' => $data['team_id'] ?? null,
'subject' => $data['subject'],
'body' => $data['body'],
'due_at' => $data['due_at'] ?? null,
'last_activity_at' => now(),
]);
$this->attachments->storeMany($ticket, $files, (int) $user->id);
$ticket = $ticket->fresh(['owner', 'agent', 'status', 'priority', 'category', 'project', 'team', 'attachments']) ?? $ticket;
$this->workflow->dispatch($ticket, 'ticket.created', $user);
return $ticket;
});
}
/**
* Add a public or internal comment and optional attachments.
*
* @param array<int, UploadedFile> $files
*/
public function comment(Ticket $ticket, User $user, string $body, bool $internal = false, array $files = [], ?int $parentId = null): Comment
{
return DB::transaction(function () use ($ticket, $user, $body, $internal, $files, $parentId): Comment {
/** @var Comment $comment */
$comment = $ticket->comments()->create([
'parent_id' => $parentId,
'user_id' => $user->id,
'body' => $body,
'is_internal' => $internal,
]);
$ticket->forceFill(['last_activity_at' => now()])->save();
$this->attachments->storeMany($ticket, $files, (int) $user->id, $comment);
$this->workflow->dispatch($ticket->fresh(['owner', 'agent', 'status', 'priority', 'category', 'project', 'team']) ?? $ticket, $parentId === null ? 'ticket.commented' : 'ticket.reply_created', $user);
return $comment->fresh(['user', 'attachments']) ?? $comment;
});
}
/**
* Close a ticket.
*/
public function close(Ticket $ticket, User $user): Ticket
{
$status = Status::query()->where('slug', config('helpdesk.closed_status_slug'))->firstOrFail();
$ticket->forceFill([
'status_id' => $status->id,
'completed_at' => now(),
'last_activity_at' => now(),
])->save();
$ticket = $ticket->fresh(['owner', 'agent', 'status', 'priority', 'category', 'project', 'team']) ?? $ticket;
$this->workflow->dispatch($ticket, 'ticket.closed', $user);
return $ticket;
}
}