, * assignee_user_id?: int|null, * description: string|null, * parent_ticket_id?: int|null, * story_points?: int|null, * ticket_priority_id: int, * ticket_status_id: int, * ticket_type_id: int, * title: string * } $attributes */ public function handle(Project $project, array $attributes, ?User $actor = null): Ticket { return DB::transaction(function () use ($actor, $attributes, $project): Ticket { $acceptanceCriteria = AcceptanceCriteriaChecklistMapper::normalize( Arr::get($attributes, 'acceptance_criteria'), ); $nextSequenceNumber = (int) Ticket::query() ->where('project_id', $project->id) ->max('sequence_number') + 1; $ticketStatusQuery = TicketStatus::query()->where('project_id', $project->id); $ticketStatus = $ticketStatusQuery->findOrFail(Arr::get($attributes, 'ticket_status_id')); $ticket = Ticket::query()->create([ 'assignee_user_id' => Arr::get($attributes, 'assignee_user_id'), 'author_user_id' => $actor?->id, 'description' => Arr::get($attributes, 'description'), 'parent_ticket_id' => Arr::get($attributes, 'parent_ticket_id'), 'project_id' => $project->id, 'sequence_number' => $nextSequenceNumber, 'ticket_key' => "{$project->key}-{$nextSequenceNumber}", 'ticket_priority_id' => Arr::get($attributes, 'ticket_priority_id'), 'ticket_status_id' => $ticketStatus->id, 'ticket_type_id' => Arr::get($attributes, 'ticket_type_id'), 'title' => Arr::get($attributes, 'title'), 'story_points' => Arr::get($attributes, 'story_points'), ]); AcceptanceCriteriaChecklistMapper::syncTicket($ticket, $acceptanceCriteria); $occurredAt = CarbonImmutable::now(); if ($actor !== null) { $this->createTicketWorklogEntry->handle( $ticket, $actor, TicketWorklogEntryType::Created, "Ticket was created in {$ticketStatus->name}.", [ 'from' => null, 'to' => [ 'label' => $ticketStatus->name, 'ticket_status_id' => $ticketStatus->id, ], ], ); } TicketCreated::dispatch( $this->buildTicketCreatedPayload->handle( $project, $ticket, $ticketStatus, $actor, $occurredAt, ), ); return $ticket; }); } }