2020-11-18 10:23:51 +01:00
< ? php
namespace App\Models ;
2022-02-02 13:51:45 +01:00
use App\Events\NotifyAdmin ;
2021-07-23 12:06:08 +02:00
use App\Mail\TicketMention ;
2026-02-11 13:21:29 +01:00
use Illuminate\Support\Str ;
use App\Models\GitlabIssues ;
use App\Enums\Users\RoleEnum ;
2022-05-31 14:06:07 +02:00
use App\Models\Credit\Account ;
use App\Models\Credit\Estimation ;
2023-04-25 09:17:47 +00:00
use App\Models\Ticket\TicketType ;
2026-02-11 13:21:29 +01:00
use Illuminate\Support\Collection ;
use App\Events\NotifySubscriptions ;
use App\Events\TicketStatusChanged ;
use App\Jobs\SendTicketMentionMail ;
use App\Enums\Tickets\TicketTypeEnum ;
use App\Enums\Tickets\TicketStatusEnum ;
2020-11-18 10:23:51 +01:00
use Illuminate\Database\Eloquent\Model ;
2026-02-11 13:21:29 +01:00
use GrahamCampbell\GitLab\Facades\GitLab ;
2025-06-18 11:10:46 +02:00
use Illuminate\Database\Eloquent\SoftDeletes ;
2026-02-11 13:21:29 +01:00
use Illuminate\Database\Eloquent\Factories\HasFactory ;
use Illuminate\Database\Eloquent\ModelNotFoundException ;
2020-11-18 10:23:51 +01:00
2026-06-24 16:25:58 +02:00
/**
* @ property int $id
* @ property int $created_by
* @ property int | null $assigned_to La personne a qui est assigné le ticket .
* @ property int $site_id
* @ property TicketTypeEnum $type
* @ property string $title Titre du ticket
* @ property string $text
* @ property string | null $file
* @ property string | null $file2
* @ property string | null $file3 Pièce jointes n°3
* @ property string | null $file4 Pièce jointes n°4
* @ property string | null $file5 Pièce jointes n°5
* @ property TicketStatusEnum $status
* @ property int $order Ticket order .
* @ property bool $is_internal
* @ property string | null $gitlab_issue URL de l ' issue Gitlab
* @ property \Illuminate\Support\Carbon | null $date_mep Date MEP souhaitée
* @ property int $last_activity Last activity on the ticket .
* @ property \Illuminate\Support\Carbon | null $created_at
* @ property \Illuminate\Support\Carbon | null $updated_at
* @ property \Illuminate\Support\Carbon | null $deleted_at
* @ property - read \App\Models\User | null $assignedUser
* @ property - read Estimation | null $estimation
* @ property - read string $full_name
* @ property - read \Illuminate\Database\Eloquent\Collection < int , \App\Models\Label > $labels
* @ property - read \Illuminate\Database\Eloquent\Collection < int , Ticket > $linkedTickets
* @ property - read \Illuminate\Database\Eloquent\Collection < int , \App\Models\Message > $messages
* @ property - read \App\Models\User | null $projectManager
* @ property - read \App\Models\Site | null $site
* @ property - read \Illuminate\Database\Eloquent\Collection < int , \App\Models\User > $subscriptions
* @ property - read TicketType | null $ticketType
* @ property - read \App\Models\User | null $user
* @ method static \Illuminate\Database\Eloquent\Builder < static >| Ticket onlyTrashed ()
* @ method static \Illuminate\Database\Eloquent\Builder < static >| Ticket withTrashed ( bool $withTrashed = true )
* @ method static \Illuminate\Database\Eloquent\Builder < static >| Ticket withoutTrashed ()
* @ mixin \Eloquent
*/
2020-11-18 10:23:51 +01:00
class Ticket extends Model
{
use HasFactory ;
2025-06-18 11:10:46 +02:00
use SoftDeletes ;
2020-11-18 10:23:51 +01:00
2022-05-24 12:38:30 +02:00
/**
2023-08-14 09:23:39 +00:00
* The attributes that are fillable .
2022-05-24 12:38:30 +02:00
*
* @ var array
*/
2023-08-14 09:23:39 +00:00
protected $fillable = [
'site_id' ,
'type' ,
'status' ,
2025-03-12 16:56:28 +01:00
'order' ,
2025-03-13 12:14:30 +01:00
'date_mep' ,
2023-08-14 09:23:39 +00:00
'text' ,
2024-02-02 10:34:06 +00:00
'title' ,
2023-08-14 14:15:13 +00:00
'is_internal' ,
'last_activity' ,
2021-04-18 20:31:07 +02:00
];
2022-05-24 12:38:30 +02:00
/**
* The attributes that should be cast .
*
* @ var array
*/
protected $casts = [
2024-07-22 11:32:03 +02:00
'is_internal' => 'bool' ,
'type' => TicketTypeEnum :: class ,
'status' => TicketStatusEnum :: class ,
2025-03-13 12:14:30 +01:00
'date_mep' => 'date' ,
2023-08-14 14:15:13 +00:00
'last_activity' => 'timestamp' ,
2021-04-18 20:31:07 +02:00
];
2022-05-24 12:38:30 +02:00
/**
* Perform any actions required after the model boots .
*
* @ return void
*/
2021-04-18 20:31:07 +02:00
protected static function booted ()
{
2021-05-21 14:34:45 +02:00
static :: creating ( function ( Ticket $ticket ) {
2025-03-12 16:56:28 +01:00
self :: setOrder ( $ticket );
2025-11-20 17:04:08 +01:00
// If bug force the ticket status on new. All the others are estimateTodo.
2023-08-14 09:23:39 +00:00
if ( $ticket -> type -> equals ( TicketTypeEnum :: bug )) {
$ticket -> status = TicketStatusEnum :: new ;
2023-04-06 08:55:01 +00:00
} else {
2023-08-14 09:23:39 +00:00
$ticket -> status = TicketStatusEnum :: estimateTodo ;
2021-05-21 14:34:45 +02:00
}
});
2021-04-18 20:31:07 +02:00
static :: updating ( function ( Ticket $ticket ) {
2025-08-21 12:24:42 +02:00
if (
! $ticket -> original [ 'status' ] !== $ticket -> status -> value && $ticket -> original [ 'order' ] === $ticket -> order
) {
2026-02-11 13:21:29 +01:00
$user = auth () -> user ();
2022-02-02 13:51:45 +01:00
event ( new TicketStatusChanged (
$ticket ,
2023-08-14 09:23:39 +00:00
TicketStatusEnum :: make ( $ticket -> original [ 'status' ]),
2026-02-11 13:21:29 +01:00
$user ,
2023-04-17 10:54:26 +00:00
null ,
2023-04-17 13:35:33 +00:00
false
2022-02-02 13:51:45 +01:00
));
2026-02-11 13:21:29 +01:00
$message = $user ? sprintf ( '<b>%s</b> a modifié le statut en <b>%s</b>' , $user -> name , $ticket -> status -> label ()) : sprintf ( 'Le statut a été modifié en <b>%s</b>' , $ticket -> status -> label ());
event ( new NotifySubscriptions ( $ticket , $message ));
event ( new NotifyAdmin ( $ticket , $message ));
2021-04-18 20:31:07 +02:00
}
2025-03-12 16:56:28 +01:00
if ( $ticket -> status -> equals ( TicketStatusEnum :: archived )) {
self :: setOrder ( $ticket , true );
}
2021-04-18 20:31:07 +02:00
});
}
2025-03-12 16:56:28 +01:00
/**
* Set order after the last element of the list .
*
* @ param self $model
* @ param boolean $isArchived
* @ return void
*/
private static function setOrder ( self $model , bool $isArchived = false ) : void
{
2025-11-20 17:04:08 +01:00
$maxOrder = \intval ( self :: query () -> where ( 'status' , '!=' , TicketStatusEnum :: archived ) -> max ( 'order' ));
if ( $isArchived ) {
$request = Ticket :: query ()
-> where ( 'site_id' , $model -> site_id )
-> whereNot ( 'status' , TicketStatusEnum :: archived )
-> whereNot ( 'id' , $model -> id )
-> whereBetween ( 'order' , [ $model -> order , $maxOrder ]);
$request -> decrement ( 'order' , 1 );
$model -> order = 0 ;
return ;
}
$model -> order = $maxOrder + 1 ;
2025-03-12 16:56:28 +01:00
}
2022-05-24 12:38:30 +02:00
// * RELATIONSHIPS
2022-05-31 14:06:07 +02:00
/**
* HasOne Credit\Estimation relation
*
* @ return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function estimation ()
{
return $this -> hasOne ( Estimation :: class );
}
2022-05-24 12:38:30 +02:00
/**
* BelongsTo Site relation
*
* @ return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
2021-07-23 11:34:16 +02:00
public function site ()
2021-04-18 20:31:07 +02:00
{
2021-07-23 11:34:16 +02:00
return $this -> belongsTo ( Site :: class );
2021-04-18 20:31:07 +02:00
}
2022-05-24 12:38:30 +02:00
/**
* BelongsTo User relation
* The user that created the ticket
*
* @ return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
2021-04-18 20:31:07 +02:00
public function user ()
{
return $this -> belongsTo ( User :: class , 'created_by' );
2020-11-18 10:23:51 +01:00
}
2022-05-24 17:23:36 +02:00
/**
* BelongsTo User relation
* The user that is in charge of the ticket
*
* @ return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function assignedUser ()
{
return $this -> belongsTo ( User :: class , 'assigned_to' );
}
2025-05-27 10:29:56 +02:00
/**
* BelongsTo User relation
* The user that is in charge of the site linked to this ticket
*
* @ return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function projectManager ()
{
return $this -> belongsTo ( User :: class , 'project_manager_id' );
}
2022-05-24 12:38:30 +02:00
/**
* HasMany Message relation
*
* @ return \Illuminate\Database\Eloquent\Relations\HasMany
*/
2021-04-18 20:31:07 +02:00
public function messages ()
{
return $this -> hasMany ( Message :: class , 'ticket_id' );
2020-11-18 10:23:51 +01:00
}
2020-11-25 16:05:21 +01:00
2022-05-24 12:38:30 +02:00
/**
* BelongsToMany User relation
* The Users that are following this ticket
*
* @ return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
2021-04-18 20:31:07 +02:00
public function subscriptions ()
{
return $this -> belongsToMany ( User :: class , 'subscriptions' );
2020-11-25 16:05:21 +01:00
}
2020-11-26 15:48:35 +01:00
2022-05-26 13:22:50 +02:00
/**
* BelongsToMany Label relation
*
* @ return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function labels ()
{
return $this -> belongsToMany ( Label :: class , 'label_ticket' );
}
2023-04-25 09:17:47 +00:00
/**
* BelongsTo ticket_type
*
* @ return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function ticketType ()
{
return $this -> belongsTo ( TicketType :: class , 'type' , 'type' );
}
2025-03-14 14:23:39 +01:00
/**
* BelongsToMany Linked Ticket
*
* @ return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
2025-04-18 09:58:41 +02:00
public function linkedTickets ()
2025-03-14 14:23:39 +01:00
{
return $this -> belongsToMany ( Ticket :: class , 'linked_tickets' , 'ticket_id' , 'linked_ticket_id' );
}
2022-05-24 12:38:30 +02:00
// * METHODS
2021-07-23 12:06:08 +02:00
/**
* Parse message @ user tags and send emails to concerned users
* returns and array with to users notified
*
* @ param Message $message
2022-02-02 13:51:45 +01:00
* @ return Collection
2021-07-23 12:06:08 +02:00
*/
public function sendBeenMentionnedToUser ( Message $message ) : Collection
{
$ticket = $this ;
2022-05-24 12:38:30 +02:00
// Find @user + send mail si un seul user est trouvé.
2023-04-25 09:30:25 +00:00
/** @var \Illuminate\Support\Collection<\App\Models\User> */
2021-07-23 12:06:08 +02:00
$usersNotified = collect ([]);
2022-03-29 16:57:54 +02:00
$matches = [];
2021-07-23 12:06:08 +02:00
$ticket -> load ( 'site' );
\preg_match_all ( '/@\w*/' , $message -> message , $matches );
$matches = collect ( $matches [ 0 ] ? ? []) -> unique ();
foreach ( $matches as $pseudo_usertag ) {
try {
2022-03-29 16:57:54 +02:00
/** @var \App\Models\User $user */
2021-07-23 12:06:08 +02:00
$user = User :: where ( 'name' , 'LIKE' , '%' . ltrim ( $pseudo_usertag , '@' ) . '%' )
-> where ( 'email' , '!=' , $ticket -> user -> email )
-> where ( function ( $q ) use ( $ticket ) {
$q -> whereHas ( 'sites' , function ( $q ) use ( $ticket ) {
$q -> where ( 'id' , $ticket -> site -> id );
2023-08-14 09:23:39 +00:00
})
-> orWhere ( 'role' , RoleEnum :: admin )
-> orWhere ( 'role' , RoleEnum :: developper );
2021-07-23 12:06:08 +02:00
}) -> firstOrFail ();
2022-02-23 15:15:47 +00:00
2022-05-24 12:38:30 +02:00
if ( $ticket -> is_internal ) {
2022-02-23 15:15:47 +00:00
if ( $user -> isAdmin ()) {
$usersNotified -> add ( $user );
SendTicketMentionMail :: dispatchAfterResponse (
$user ,
new TicketMention ( $ticket , $message -> message )
);
}
} else {
$usersNotified -> add ( $user );
SendTicketMentionMail :: dispatchAfterResponse (
$user ,
new TicketMention ( $ticket , $message -> message )
);
}
2021-07-23 12:06:08 +02:00
} catch ( ModelNotFoundException $e ) {
continue ;
2023-04-25 09:30:25 +00:00
} //end try
} //end foreach
2021-07-23 12:06:08 +02:00
return $usersNotified ;
}
2022-05-31 14:06:07 +02:00
/**
* full_name getter
*
* @ return string
*/
public function getFullNameAttribute () : string
{
2022-06-15 18:19:26 +02:00
return trans ( 'Ticket n° :ticketId : :ticketSiteName' , [
2022-05-31 14:06:07 +02:00
'ticketId' => $this -> id ,
2022-06-15 18:19:26 +02:00
'ticketSiteName' => $this -> site -> name
2022-05-31 14:06:07 +02:00
]);
}
/**
* Check if an account has the same brand as this ticket .
*
* @ param Account $account
* @ return boolean
*/
public function accountIsValidForThisTicket ( Account $account ) : bool
{
/** @var \App\Models\Brand|null */
if ( ! ( $siteBrand = optional ( $this -> site ) -> brand )) {
return false ;
}
/** @var \App\Models\Brand */
$accountBrand = $account -> brand ;
return ! \is_null ( $accountBrand -> id ) and $accountBrand -> id === $siteBrand -> id ;
}
2023-08-14 09:23:39 +00:00
/**
* Format the ticket name to contain more information .
*
* @ param self $ticket
* @ return string
*/
2023-08-18 09:16:08 +02:00
public static function formatName ( self $ticket ) : string
2023-08-14 09:23:39 +00:00
{
return sprintf (
'Ticket %d (%s): %s' ,
$ticket -> id ,
$ticket -> user ? $ticket -> user -> name : trans ( 'Utilisateur supprimé' ),
Str :: limit ( \preg_replace ( '/[[:cntrl:]]+/' , '' , $ticket -> text ), 35 ),
);
}
2024-11-12 12:52:21 +01:00
/**
* Get an array of all files columns of the ticket .
*
* @ return array
*/
public function getFiles () : array
{
return [
$this -> file ,
$this -> file2 ,
$this -> file3 ,
$this -> file4 ,
$this -> file5 ,
];
}
2020-11-18 10:23:51 +01:00
}