first&last
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Enums\StatusSlug;
|
||||
use App\Events\RequestAssigned;
|
||||
use App\Events\RequestCreated;
|
||||
use App\Events\RequestStatusChanged;
|
||||
use App\Models\RequestType;
|
||||
use App\Models\ServiceRequest;
|
||||
use App\Models\Status;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class RequestService
|
||||
{
|
||||
public function __construct(private readonly SettingsService $settingsService)
|
||||
{
|
||||
}
|
||||
|
||||
public function create(array $data, User $actor): ServiceRequest
|
||||
{
|
||||
return DB::transaction(function () use ($data, $actor): ServiceRequest {
|
||||
$payload = $this->preparePayload($data);
|
||||
$payload['ticket_number'] = $this->generateTicketNumber();
|
||||
|
||||
$request = ServiceRequest::query()->create($payload);
|
||||
|
||||
$this->persistAttachments($request, Arr::get($data, 'attachments', []), $actor);
|
||||
|
||||
event(new RequestCreated($request, $actor));
|
||||
|
||||
if ($request->assigned_to) {
|
||||
event(new RequestAssigned($request, $actor));
|
||||
}
|
||||
|
||||
return $request;
|
||||
});
|
||||
}
|
||||
|
||||
public function update(ServiceRequest $request, array $data, User $actor): ServiceRequest
|
||||
{
|
||||
return DB::transaction(function () use ($request, $data, $actor): ServiceRequest {
|
||||
$oldAssignee = $request->assigned_to;
|
||||
$oldStatusId = $request->status_id;
|
||||
$payload = $this->preparePayload($data, $request);
|
||||
|
||||
$request->fill($payload);
|
||||
$request->save();
|
||||
|
||||
$this->persistAttachments($request, Arr::get($data, 'attachments', []), $actor);
|
||||
|
||||
if ($oldStatusId !== $request->status_id) {
|
||||
event(new RequestStatusChanged($request->fresh('status'), $actor, $oldStatusId, $request->status_id));
|
||||
}
|
||||
|
||||
if ($oldAssignee !== $request->assigned_to && $request->assigned_to) {
|
||||
event(new RequestAssigned($request->fresh(), $actor));
|
||||
}
|
||||
|
||||
return $request->fresh(['type', 'priority', 'status', 'client', 'assignee', 'equipment']);
|
||||
});
|
||||
}
|
||||
|
||||
public function changeStatus(ServiceRequest $request, Status $targetStatus, User $actor): ServiceRequest
|
||||
{
|
||||
$oldStatusId = $request->status_id;
|
||||
|
||||
$request->status_id = $targetStatus->id;
|
||||
|
||||
if ($targetStatus->slug === StatusSlug::IN_PROGRESS->value && !$request->started_at) {
|
||||
$request->started_at = now();
|
||||
}
|
||||
|
||||
if (in_array($targetStatus->slug, [StatusSlug::RESOLVED->value, StatusSlug::CLOSED->value], true)) {
|
||||
$request->completed_at = now();
|
||||
}
|
||||
|
||||
$request->save();
|
||||
|
||||
event(new RequestStatusChanged($request->fresh('status'), $actor, $oldStatusId, $targetStatus->id));
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
public function allowedTransitions(ServiceRequest $request, User $user): array
|
||||
{
|
||||
$staffTransitions = [
|
||||
StatusSlug::NEW->value => [StatusSlug::IN_PROGRESS->value, StatusSlug::WAITING->value, StatusSlug::CANCELLED->value],
|
||||
StatusSlug::IN_PROGRESS->value => [StatusSlug::WAITING->value, StatusSlug::RESOLVED->value, StatusSlug::CANCELLED->value],
|
||||
StatusSlug::WAITING->value => [StatusSlug::IN_PROGRESS->value, StatusSlug::RESOLVED->value, StatusSlug::CANCELLED->value],
|
||||
StatusSlug::RESOLVED->value => [StatusSlug::CLOSED->value, StatusSlug::IN_PROGRESS->value],
|
||||
StatusSlug::CLOSED->value => [],
|
||||
StatusSlug::CANCELLED->value => [],
|
||||
];
|
||||
|
||||
$technicianTransitions = [
|
||||
StatusSlug::NEW->value => [StatusSlug::IN_PROGRESS->value, StatusSlug::WAITING->value],
|
||||
StatusSlug::IN_PROGRESS->value => [StatusSlug::WAITING->value, StatusSlug::RESOLVED->value],
|
||||
StatusSlug::WAITING->value => [StatusSlug::IN_PROGRESS->value, StatusSlug::RESOLVED->value],
|
||||
StatusSlug::RESOLVED->value => [StatusSlug::IN_PROGRESS->value],
|
||||
StatusSlug::CLOSED->value => [],
|
||||
StatusSlug::CANCELLED->value => [],
|
||||
];
|
||||
|
||||
$map = $user->isTechnician() ? $technicianTransitions : $staffTransitions;
|
||||
|
||||
return $map[$request->status?->slug ?? StatusSlug::NEW->value] ?? [];
|
||||
}
|
||||
|
||||
public function generateTicketNumber(): string
|
||||
{
|
||||
$maxId = ServiceRequest::withTrashed()->max('id') ?? 0;
|
||||
|
||||
return sprintf('SRV-%04d', $maxId + 1);
|
||||
}
|
||||
|
||||
private function preparePayload(array $data, ?ServiceRequest $request = null): array
|
||||
{
|
||||
$typeId = Arr::get($data, 'type_id', $request?->type_id);
|
||||
$type = $typeId ? RequestType::query()->find($typeId) : null;
|
||||
|
||||
$deadlineAt = Arr::get($data, 'deadline_at');
|
||||
|
||||
if (!$deadlineAt && $type) {
|
||||
$baseDate = Arr::get($data, 'planned_at') ?: now();
|
||||
$deadlineAt = Carbon::parse((string) $baseDate)->addHours($type->sla_hours)->toDateTimeString();
|
||||
}
|
||||
|
||||
if (!$deadlineAt && !$type) {
|
||||
$fallbackHours = $this->settingsService->defaultSlaHours();
|
||||
$baseDate = Arr::get($data, 'planned_at') ?: now();
|
||||
$deadlineAt = Carbon::parse((string) $baseDate)->addHours($fallbackHours)->toDateTimeString();
|
||||
}
|
||||
|
||||
return [
|
||||
'title' => Arr::get($data, 'title'),
|
||||
'description' => Arr::get($data, 'description'),
|
||||
'type_id' => $typeId,
|
||||
'priority_id' => Arr::get($data, 'priority_id'),
|
||||
'status_id' => Arr::get($data, 'status_id', $request?->status_id),
|
||||
'client_id' => Arr::get($data, 'client_id', $request?->client_id),
|
||||
'assigned_to' => Arr::get($data, 'assigned_to'),
|
||||
'equipment_id' => Arr::get($data, 'equipment_id'),
|
||||
'location' => Arr::get($data, 'location'),
|
||||
'planned_at' => Arr::get($data, 'planned_at'),
|
||||
'deadline_at' => $deadlineAt,
|
||||
'started_at' => Arr::get($data, 'started_at', $request?->started_at),
|
||||
'completed_at' => Arr::get($data, 'completed_at', $request?->completed_at),
|
||||
'resolution_notes' => Arr::get($data, 'resolution_notes'),
|
||||
'client_rating' => Arr::get($data, 'client_rating'),
|
||||
'client_comment' => Arr::get($data, 'client_comment'),
|
||||
];
|
||||
}
|
||||
|
||||
private function persistAttachments(ServiceRequest $request, array $attachments, User $actor): void
|
||||
{
|
||||
/** @var UploadedFile $attachment */
|
||||
foreach ($attachments as $attachment) {
|
||||
$path = $attachment->store('request-attachments/'.$request->id, 'local');
|
||||
|
||||
$request->attachments()->create([
|
||||
'user_id' => $actor->id,
|
||||
'filename' => $attachment->getClientOriginalName(),
|
||||
'path' => $path,
|
||||
'size' => $attachment->getSize() ?: 0,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user