feat: Add Tickets state export notification

This commit is contained in:
Vincent BRIDIER 2025-05-27 10:29:56 +02:00
parent 7116c5d41f
commit 88457bb44a
No known key found for this signature in database
GPG key ID: 8571BFA20420D512
11 changed files with 237 additions and 9 deletions

View file

@ -30,8 +30,8 @@ MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=noreply-sw@ldc.fr
MAIL_FROM_NAME="${APP_NAME}"
MAIL_LIST_SERVICEWEB=equipe.web@ldc.fr
MAIL_ADMIN=equipe.web@ldc.fr
MAIL_LIST_SERVICEWEB=digital@ldc.fr
MAIL_ADMIN=digital@ldc.fr
# Sentry
SENTRY_LARAVEL_DSN=

View file

@ -117,7 +117,7 @@ class CreateUser extends Command
{
while (is_null($this->email)) {
try {
$tmp = $this->ask('Type the wanted user email', 'equipe.web@ldc.fr');
$tmp = $this->ask('Type the wanted user email', 'digital@ldc.fr');
$validator = Validator::make(['email' => $tmp], [
'email' => 'required|unique:users,email|email:rfc,dns'
]);

View file

@ -0,0 +1,38 @@
<?php
namespace App\Console\Commands;
use App\Jobs\ExportTicketsState as JobsExportTicketsState;
use Illuminate\Console\Command;
class ExportTicketsState extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'tickets:export-state';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Export the state of tickets to a CSV file';
/**
* Execute the console command.
*
* @return integer
*/
public function handle()
{
$this->info('Running the Job, this could take some time.');
$job = new JobsExportTicketsState();
$job->handle();
$this->info('Job completed');
return 0;
}
}

View file

@ -19,7 +19,7 @@ class TestMail extends Command
*
* @var string
*/
protected $description = 'Send test email to equipe.web@ldc.fr';
protected $description = 'Send test email to digital@ldc.fr';
/**
* Execute the console command.
@ -29,7 +29,7 @@ class TestMail extends Command
public function handle()
{
Mail::raw(sprintf('UWIL mail-test from %s %s', \config('app.name'), \config('app.url')), function ($msg) {
$msg->to('equipe.web@ldc.fr')->subject('Test Email');
$msg->to('digital@ldc.fr')->subject('Test Email');
});
return 0;
}

View file

@ -3,6 +3,7 @@
namespace App\Console;
use App\Jobs\AutoCloseOldTickets;
use App\Jobs\ExportTicketsState;
use App\Jobs\WarnBeforeAutoCloseTicket;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@ -24,6 +25,7 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
$schedule->job(ExportTicketsState::class)->weekly()->mondays()->at('07:00');
$schedule->job(AutoCloseOldTickets::class)->dailyAt('08:00');
$schedule->job(WarnBeforeAutoCloseTicket::class)->dailyAt('08:05');
}

View file

@ -0,0 +1,117 @@
<?php
namespace App\Jobs;
use App\Enums\Tickets\TicketStatusEnum;
use App\Models\Site;
use App\Models\Ticket;
use App\Notifications\ExportTicketsStateNotification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Notification;
class ExportTicketsState implements ShouldQueue
{
use Queueable;
/**
* CSV File headers
*
* @var array
*/
private $header = [
"Priorité service",
"Site",
"Numéro de ticket",
"Priorité",
"Date de MEP souhaitée",
"Type de ticket",
"Titre du ticket",
"Statut du ticket",
"Chef de projet",
"Utilisateur assigné",
"Date de dernière activité",
"Date de création",
"Lien vers le ticket",
"Lien vers l'issue GitLab"
];
/**
* Execute the job.
*
* @return void
*/
public function handle(): void
{
// L'ordre commence à 0 et reprendra à 1 pour chaque site.
$order = 0;
// Dernier site traité.
$lastSiteId = null;
// Insertion de la ligne d'en-têtes.
$csvlines[] = $this->header;
// Récupération des tickets.
Ticket::query()
->whereNotIn(
'status',
[TicketStatusEnum::archived, TicketStatusEnum::production]
)
->orderBy(Site::select('name')
->whereColumn('sites.id', 'tickets.site_id'))
->orderBy('order')->get()->each(function (Ticket $ticket) use (&$csvlines, &$lastSiteId, &$order) {
$csvlines[] = [
"",
$ticket->site->name,
$ticket->id,
$lastSiteId === $ticket->site_id ? $order + 1 : 1,
$ticket->date_mep?->format('d-m-Y') ?? 'No MEP Date',
$ticket->type->label(),
$ticket->title,
$ticket->status->label(),
$ticket->projectManager->name ?? 'No PM',
$ticket->assignedUser()->name ?? 'No Assigned User',
Carbon::create($ticket->getRawOriginal('last_activity'))->format('d-m-Y H:i:s'),
$ticket->created_at->format('d-m-Y H:i:s'),
route('bo.ticket.show', $ticket->id),
$ticket->gitlab_issue ?? 'No GitLab Issue'
];
$order = $lastSiteId === $ticket->site_id ? $order + 1 : 1;
$lastSiteId = $ticket->site_id;
});
Notification::route('mail', config('mail.MAIL_ADMIN'))
->notify(new ExportTicketsStateNotification($this->array2csv($csvlines, ";")));
}
/**
* converts an array to a csv stream
*
* @param array $data
* @param string $delimiter
* @param string $enclosure
* @param string $escape_char
*
* @return string
*/
protected function array2csv(
array $data,
string $delimiter = ',',
string $enclosure = '"',
string $escape_char = "\\"
) {
$f = fopen('php://memory', 'r+');
// UTF-8 BOM.
$BOM = "\xEF\xBB\xBF";
fwrite($f, $BOM);
foreach ($data as $item) {
fputcsv($f, $item, $delimiter, $enclosure, $escape_char);
}
rewind($f);
return stream_get_contents($f);
}
}

View file

@ -152,6 +152,17 @@ class Ticket extends Model
return $this->belongsTo(User::class, 'assigned_to');
}
/**
* 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');
}
/**
* HasMany Message relation
*

View file

@ -0,0 +1,60 @@
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Carbon;
class ExportTicketsStateNotification extends Notification implements ShouldQueue
{
use Queueable;
/**
* The CSV content to be sent.
*
* @var string
*/
private string $csvContent;
/**
* Create a new notification instance.
*
* @param string $csvContent The content of the CSV file to be sent.
*/
public function __construct(string $csvContent)
{
$this->csvContent = $csvContent;
}
/**
* Get the notification's delivery channels.
*
* @return array<int, string>
*/
public function via(): array
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail(): \Illuminate\Notifications\Messages\MailMessage
{
return (new MailMessage())
->subject(trans("Export de l'état des lieux des tickets"))
->line(trans("Vous trouverez ci-joint le fichier CSV contenant l'état des tickets."))
->attachData(
$this->csvContent,
sprintf('tickets_state_%s.csv', Carbon::now()->format('Y-m-d_H-i-s')),
[
'mime' => 'text/csv',
]
);
}
}

View file

@ -4,7 +4,7 @@
"type": "project",
"description": "Support web",
"support": {
"email": "equipe.web@ldc.fr"
"email": "digital@ldc.fr"
},
"keywords": [
"framework",

2
composer.lock generated
View file

@ -8700,7 +8700,7 @@
"authors": [
{
"name": "Equipe Web",
"email": "equipe.web@ldc.fr",
"email": "digital@ldc.fr",
"homepage": "https://www.ldc.fr"
}
],

View file

@ -16,7 +16,7 @@ return [
'default' => env('MAIL_MAILER', 'smtp'),
// * Mail admin
'MAIL_ADMIN' => env('MAIL_ADMIN', 'equipe.web@ldc.fr'),
'MAIL_ADMIN' => env('MAIL_ADMIN', 'digital@ldc.fr'),
/*
|--------------------------------------------------------------------------
@ -100,7 +100,7 @@ return [
],
'contactsList' => [
'serviceweb' => env('MAIL_LIST_SERVICEWEB', 'equipe.web@ldc.fr')
'serviceweb' => env('MAIL_LIST_SERVICEWEB', 'digital@ldc.fr')
],
/*