Files
ludikevent_crm/src/Controller/SignatureController.php
Serreau Jovann 1896f83107 ```
 feat(reservation/flow): Améliore le flux de réservation et ajoute des options.

Cette commit améliore le flux de réservation, ajoute une estimation des
frais de livraison et gère les options de produit et les paiements.
```
2026-02-05 08:18:29 +01:00

211 lines
9.9 KiB
PHP

<?php
namespace App\Controller;
use App\Entity\Account;
use App\Entity\AccountResetPasswordRequest;
use App\Entity\Devis;
use App\Entity\ProductReserve;
use App\Form\RequestPasswordConfirmType;
use App\Form\RequestPasswordRequestType;
use App\Logger\AppLogger;
use App\Repository\ContratsRepository;
use App\Repository\DevisRepository;
use App\Repository\ProductRepository;
use App\Service\Mailer\Mailer;
use App\Service\ResetPassword\Event\ResetPasswordConfirmEvent;
use App\Service\ResetPassword\Event\ResetPasswordEvent;
use App\Service\Signature\Client;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class SignatureController extends AbstractController
{
#[Route('/signature/complete', name: 'app_sign_complete')]
public function appSignComplete(
Client $client,
DevisRepository $devisRepository,
ContratsRepository $contratsRepository,
ProductRepository $productRepository,
EntityManagerInterface $entityManager,
Request $request,
Mailer $mailer,
): Response {
if ($request->get('type') === "contrat") {
$contrats = $contratsRepository->find($request->get('id'));
if (!$contrats) {
throw $this->createNotFoundException("Contrat introuvable.");
}
// On évite de retraiter un devis déjà marqué comme signé
if ($contrats->isSigned()) {
return $this->render('sign/contrat_sign_success.twig', ['contrat' => $contrats]);
}
$submiter = $client->getSubmiter($contrats->getSignID());
$submission = $client->getSubmition($submiter['submission_id']);
if ($submission['status'] === "completed") {
$contrats->setIsSigned(true);
$auditUrl = $submission['audit_log_url'];
$signedDocUrl = $submission['documents'][0]['url'];
try {
// 1. Gestion du PDF SIGNÉ
$tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf';
$signedContent = file_get_contents($signedDocUrl);
file_put_contents($tmpSigned, $signedContent);
// On utilise UploadedFile pour simuler un upload propre pour VichUploader
$contrats->setDevisSignFile(new UploadedFile($tmpSigned, "sign-" . $contrats->getNumReservation() . ".pdf", "application/pdf", null, true));
// 2. Gestion de l'AUDIT LOG
$tmpAudit = sys_get_temp_dir() . '/audit_' . uniqid() . '.pdf';
$auditContent = file_get_contents($auditUrl);
file_put_contents($tmpAudit, $auditContent);
$contrats->setDevisAuditFile(new UploadedFile($tmpAudit, "audit-" . $contrats->getNumReservation() . ".pdf", "application/pdf", null, true));
// 3. Préparation des pièces jointes pour le mail (Le PDF signé est le plus important)
$attachments = [
new DataPart($signedContent, "Contrat -" . $contrats->getNumReservation() . "-Signe.pdf", "application/pdf"),
new DataPart($auditContent, "Certificat-Signature-" . $contrats->getNumReservation() . ".pdf", "application/pdf"),
];
$entityManager->persist($contrats);
foreach ($contrats->getContratsLines() as $line) {
$p = $productRepository->findOneBy(['name' => $line->getName()]);
$pr = new ProductReserve();
$pr->setContrat($contrats);
$pr->setProduct($p);
$pr->setStartAt($contrats->getDateAt());
$pr->setEndAt($contrats->getEndAt());
$pr->setCustomer($contrats->getCustomer());
$pr->setDevis($contrats->getDevis());
$entityManager->persist($pr);
}
$entityManager->flush();
// 5. Envoi du mail de confirmation avec le récapitulatif
$mailer->send(
$contrats->getCustomer()->getEmail(),
$contrats->getCustomer()->getName() . " " . $contrats->getCustomer()->getSurname(),
"[Ludikevent] Confirmation de signature - Contrat " . $contrats->getNumReservation(),
"mails/sign/signed_contrat.twig",
[
'contrats' => $contrats // Correction ici : passage de l'objet, pas d'un string
],
$attachments
);
$mailer->send(
"contact@ludikevent.fr",
"Ludikevent",
"[Intranet Ludikevent] Confirmation signature d'un client pour - Contrat " . $contrats->getNumReservation(),
"mails/sign/signed_contrat_notification.twig",
[
'contrats' => $contrats // Correction ici : passage de l'objet, pas d'un string
],
$attachments
);
} catch (\Exception $e) {
return new Response("Erreur lors de la récupération ou de l'envoi des documents : " . $e->getMessage(), 500);
}
}
return $this->render('sign/contrat_sign_success.twig', ['contrat' => $contrats]);
}
if ($request->get('type') === "devis") {
$devis = $devisRepository->find($request->get('id'));
if (!$devis) {
throw $this->createNotFoundException("Devis introuvable.");
}
// On évite de retraiter un devis déjà marqué comme signé
if ($devis->getState() === 'signed') {
return $this->render('sign/sign_success.twig', ['devis' => $devis]);
}
$submiter = $client->getSubmiter($devis->getSignatureId());
$submission = $client->getSubmition($submiter['submission_id']);
if ($submission['status'] === "completed") {
$devis->setState("signed");
$auditUrl = $submission['audit_log_url'];
$signedDocUrl = $submission['documents'][0]['url'];
try {
// 1. Gestion du PDF SIGNÉ
$tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf';
$signedContent = file_get_contents($signedDocUrl);
file_put_contents($tmpSigned, $signedContent);
// On utilise UploadedFile pour simuler un upload propre pour VichUploader
$devis->setDevisSignFile(new UploadedFile($tmpSigned, "sign-" . $devis->getNum() . ".pdf", "application/pdf", null, true));
// 2. Gestion de l'AUDIT LOG
$tmpAudit = sys_get_temp_dir() . '/audit_' . uniqid() . '.pdf';
$auditContent = file_get_contents($auditUrl);
file_put_contents($tmpAudit, $auditContent);
$devis->setDevisAuditFile(new UploadedFile($tmpAudit, "audit-" . $devis->getNum() . ".pdf", "application/pdf", null, true));
// 3. Préparation des pièces jointes pour le mail (Le PDF signé est le plus important)
$attachments = [
new DataPart($signedContent, "Devis-" . $devis->getNum() . "-Signe.pdf", "application/pdf"),
new DataPart($auditContent, "Certificat-Signature-" . $devis->getNum() . ".pdf", "application/pdf"),
];
// 4. Sauvegarde en base de données
$devis->setUpdateAt(new \DateTimeImmutable());
$entityManager->persist($devis);
$entityManager->flush();
// 5. Envoi du mail de confirmation avec le récapitulatif
$mailer->send(
$devis->getCustomer()->getEmail(),
$devis->getCustomer()->getName() . " " . $devis->getCustomer()->getSurname(),
"[Ludikevent] Confirmation de signature - Devis " . $devis->getNum(),
"mails/sign/signed.twig",
[
'devis' => $devis // Correction ici : passage de l'objet, pas d'un string
],
$attachments
);
$mailer->send(
"contact@ludikevent.fr",
"Ludikevent",
"[Intranet Ludikevent] Confirmation signature d'un client pour - Devis " . $devis->getNum(),
"mails/sign/signed_notification.twig",
[
'devis' => $devis // Correction ici : passage de l'objet, pas d'un string
],
$attachments
);
} catch (\Exception $e) {
return new Response("Erreur lors de la récupération ou de l'envoi des documents : " . $e->getMessage(), 500);
}
}
}
return $this->render('sign/sign_success.twig', [
'devis' => $devis ?? null
]);
}
}