2026-03-12 18:25:32 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
|
|
|
|
|
|
use App\Enums\ActorType;
|
|
|
|
|
use App\Enums\DeclarationStatus;
|
|
|
|
|
use App\Enums\MessageType;
|
|
|
|
|
use App\Http\Requests\StoreDeclarationMessageRequest;
|
|
|
|
|
use App\Mail\DeclarationConfirmationMail;
|
|
|
|
|
use App\Mail\DeclarationFileRequestMail;
|
|
|
|
|
use App\Mail\DeclarationInviteMail;
|
|
|
|
|
use App\Mail\DeclarationSituationMail;
|
|
|
|
|
use App\Mail\DeclarationTextMessageMail;
|
|
|
|
|
use App\Models\Declaration;
|
|
|
|
|
use App\Models\DeclarationInvitation;
|
|
|
|
|
use App\Models\Message;
|
|
|
|
|
use App\Models\Workspace;
|
2026-03-26 14:31:36 +01:00
|
|
|
use App\Observers\DeclarationObserver;
|
2026-03-12 18:25:32 +00:00
|
|
|
use Carbon\Carbon;
|
|
|
|
|
use Illuminate\Http\RedirectResponse;
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
use Illuminate\Support\Facades\Mail;
|
|
|
|
|
|
|
|
|
|
class DeclarationMessageController extends Controller
|
|
|
|
|
{
|
|
|
|
|
protected function currentWorkspace(Request $request): Workspace
|
|
|
|
|
{
|
|
|
|
|
$workspaceId = $request->session()->get('current_workspace_id');
|
|
|
|
|
|
|
|
|
|
return Workspace::query()->findOrFail($workspaceId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Store a newly created message.
|
|
|
|
|
*/
|
|
|
|
|
public function store(StoreDeclarationMessageRequest $request, Declaration $declaration): RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
$workspace = $this->currentWorkspace($request);
|
|
|
|
|
|
|
|
|
|
if ($declaration->workspace_id !== $workspace->id) {
|
|
|
|
|
abort(404);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$user = $request->user();
|
|
|
|
|
$type = MessageType::fromValue($request->input('type'));
|
|
|
|
|
$body = $request->input('body');
|
|
|
|
|
|
|
|
|
|
$invitation = $type->is(MessageType::Invite)
|
|
|
|
|
? $this->createInvitation($declaration)
|
|
|
|
|
: $this->getOrCreateInvitation($declaration);
|
|
|
|
|
|
|
|
|
|
$metadata = ['invitation_id' => $invitation->id];
|
|
|
|
|
$message = $declaration->messages()->create([
|
|
|
|
|
'type' => $type,
|
|
|
|
|
'body' => $body,
|
|
|
|
|
'sent_by_type' => ActorType::User,
|
|
|
|
|
'sent_by_id' => $user->id,
|
|
|
|
|
'metadata' => $metadata,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$mediaIds = [];
|
|
|
|
|
|
|
|
|
|
if ($request->hasFile('files')) {
|
|
|
|
|
foreach ($request->file('files') as $file) {
|
|
|
|
|
$media = $declaration->addMedia($file)
|
|
|
|
|
->withCustomProperties([
|
|
|
|
|
'message_id' => $message->id,
|
|
|
|
|
'uploaded_by_type' => ActorType::User,
|
|
|
|
|
'uploaded_by_id' => $user->id,
|
|
|
|
|
])
|
|
|
|
|
->toMediaCollection('documents');
|
|
|
|
|
$mediaIds[] = $media->id;
|
|
|
|
|
}
|
|
|
|
|
$message->update(['metadata' => array_merge($metadata, ['media_ids' => $mediaIds])]);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 14:31:36 +01:00
|
|
|
// Suppress observer email — this controller sends its own email below
|
|
|
|
|
DeclarationObserver::$suppressEmail = true;
|
2026-03-12 18:25:32 +00:00
|
|
|
$this->updateDeclarationStatusAndConfirmation($declaration, $type, $mediaIds);
|
|
|
|
|
|
|
|
|
|
$emailSent = $this->sendEmailForMessage($declaration, $invitation, $message, $body, $type);
|
|
|
|
|
$flashMessage = $emailSent
|
|
|
|
|
? 'Message envoyé.'
|
|
|
|
|
: 'Message enregistré, mais l\'email du client n\'est pas configuré.';
|
|
|
|
|
|
|
|
|
|
return back()->with('flash', ['type' => 'success', 'message' => $flashMessage]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function createInvitation(Declaration $declaration): DeclarationInvitation
|
|
|
|
|
{
|
|
|
|
|
$declaration->load('client.primaryContact');
|
|
|
|
|
|
|
|
|
|
return $declaration->invitations()->create([
|
|
|
|
|
'email' => $declaration->client->primary_contact_email,
|
|
|
|
|
'expires_at' => Carbon::now()->addDays(7),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function getOrCreateInvitation(Declaration $declaration): DeclarationInvitation
|
|
|
|
|
{
|
|
|
|
|
$invitation = $declaration->invitations()
|
|
|
|
|
->where('expires_at', '>', now())
|
|
|
|
|
->latest()
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
if ($invitation) {
|
|
|
|
|
return $invitation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->createInvitation($declaration);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array<int> $mediaIds
|
|
|
|
|
*/
|
|
|
|
|
protected function updateDeclarationStatusAndConfirmation(Declaration $declaration, MessageType $type, array $mediaIds): void
|
|
|
|
|
{
|
|
|
|
|
// Transition through en_cours first if declaration is still in created status,
|
|
|
|
|
// since created → en_attente_client is not a valid direct transition.
|
|
|
|
|
if ($declaration->status->is(DeclarationStatus::Created)) {
|
|
|
|
|
$declaration->update(['status' => DeclarationStatus::EnCours]);
|
|
|
|
|
$declaration->refresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match ($type->value) {
|
|
|
|
|
'invite' => $declaration->update(['status' => DeclarationStatus::EnAttenteClient]),
|
|
|
|
|
'situation', 'file_request' => $declaration->update(['status' => DeclarationStatus::EnAttenteClient]),
|
|
|
|
|
'confirmation' => $declaration->update([
|
|
|
|
|
'status' => DeclarationStatus::EnAttenteClient,
|
|
|
|
|
'confirmation_requested_at' => now(),
|
|
|
|
|
'confirmation_media_id' => $mediaIds[0] ?? null,
|
|
|
|
|
]),
|
|
|
|
|
default => null,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function sendEmailForMessage(Declaration $declaration, DeclarationInvitation $invitation, Message $message, string $body, MessageType $type): bool
|
|
|
|
|
{
|
|
|
|
|
$declaration->load('client.primaryContact');
|
|
|
|
|
$clientEmail = $declaration->client->primary_contact_email;
|
|
|
|
|
|
|
|
|
|
if (empty($clientEmail)) {
|
|
|
|
|
\Illuminate\Support\Facades\Log::warning("No primary contact email for client #{$declaration->client_id}, skipping email.");
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match ($type->value) {
|
|
|
|
|
'invite' => Mail::to($clientEmail)->send(new DeclarationInviteMail($declaration, $invitation)),
|
|
|
|
|
'situation' => Mail::to($clientEmail)->send(new DeclarationSituationMail($declaration, $invitation, $body)),
|
|
|
|
|
'file_request' => Mail::to($clientEmail)->send(new DeclarationFileRequestMail($declaration, $invitation, $body)),
|
|
|
|
|
'confirmation' => Mail::to($clientEmail)->send(new DeclarationConfirmationMail($declaration, $invitation, $body)),
|
|
|
|
|
'text' => Mail::to($clientEmail)->send(new DeclarationTextMessageMail($declaration, $body, $invitation->token)),
|
|
|
|
|
default => null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|