Sauvegarde explicite de la session avant la redirection OAuth pour garantir la persistance du state parameter. Retry automatique du flow SSO en cas d'InvalidStateAuthenticationException. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1246 lines
49 KiB
PHP
1246 lines
49 KiB
PHP
<?php
|
|
|
|
namespace App\Controller;
|
|
|
|
use App\Entity\Account;
|
|
use App\Entity\Contrats;
|
|
use App\Entity\ContratsPayments;
|
|
use App\Entity\EtatLieux;
|
|
use App\Entity\EtatLieuxComment;
|
|
use App\Entity\EtatLieuxFile;
|
|
use App\Entity\EtatLieuxPointControl;
|
|
use App\Entity\EtatLieuxReturnComment;
|
|
use App\Entity\EtatLieuxReturnPointControl;
|
|
use App\Entity\EtatReturnLieuxFile;
|
|
use App\Entity\ProductPointControll;
|
|
use App\Entity\Prestaire;
|
|
use App\Form\PrestairePasswordType;
|
|
use App\Repository\ContratsRepository;
|
|
use App\Service\Mailer\Mailer;
|
|
use App\Service\Pdf\EtatLieuxPdfService;
|
|
use App\Service\Signature\Client as SignatureClient;
|
|
use App\Service\Stripe\Client as StripeClient;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
|
use Symfony\Component\HttpKernel\KernelInterface;
|
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\Mime\Part\DataPart;
|
|
use Symfony\Component\Mime\Part\File as MimeFile;
|
|
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
|
use Symfony\Component\Routing\Attribute\Route;
|
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
|
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
|
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
|
|
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
|
|
use Vich\UploaderBundle\Storage\StorageInterface;
|
|
|
|
class EtlController extends AbstractController
|
|
{
|
|
#[Route('/etl', name: 'etl_home')]
|
|
public function eltHome(EntityManagerInterface $entityManager,ContratsRepository $contratsRepository): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
$missions = [];
|
|
$states = ['ready', 'pending', 'progress'];
|
|
|
|
$qb = $contratsRepository->createQueryBuilder('c');
|
|
$qb->select('count(c.id)');
|
|
if ($user instanceof Prestaire) {
|
|
$qb->andWhere('c.prestataire = :user')->setParameter('user', $user);
|
|
}
|
|
$totalMissions = $qb->getQuery()->getSingleScalarResult();
|
|
|
|
$qb = $contratsRepository->createQueryBuilder('c');
|
|
$qb->select('count(c.id)');
|
|
$qb->andWhere('c.dateAt >= :now')->setParameter('now', new \DateTime());
|
|
$qb->andWhere('c.reservationState IN (:states)')->setParameter('states', $states);
|
|
if ($user instanceof Prestaire) {
|
|
$qb->andWhere('c.prestataire = :user')->setParameter('user', $user);
|
|
}
|
|
$upcomingMissions = $qb->getQuery()->getSingleScalarResult();
|
|
|
|
if ($user instanceof Account) {
|
|
$missions = $contratsRepository->findBy(['reservationState' => $states], ['dateAt' => 'ASC'], 5);
|
|
} elseif ($user instanceof Prestaire) {
|
|
$missions = $contratsRepository->findBy(['reservationState' => $states, 'prestataire' => $user], ['dateAt' => 'ASC'], 5);
|
|
}
|
|
$list = [];
|
|
foreach ($missions as $mission) {
|
|
if($mission->isSigned()) {
|
|
$pl = $entityManager->getRepository(ContratsPayments::class)->findOneBy(['type'=>'accompte','contrat'=>$mission]);
|
|
if($pl instanceof ContratsPayments &&$pl->getState() == "complete") {
|
|
$etatleiux = $mission->getEtatLieux();
|
|
if(!is_null($etatleiux)) {
|
|
if($etatleiux->getStatus() != "edl_return_done" && $etatleiux->getStatus() != "edl_return_refused"){
|
|
$list[] = $mission;
|
|
}
|
|
} else {
|
|
$list[] = $mission;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return $this->render('etl/home.twig', [
|
|
'missions' => $list,
|
|
'totalMissions' => $totalMissions,
|
|
'upcomingMissions' => $upcomingMissions
|
|
]);
|
|
}
|
|
|
|
#[Route('/etl/contrats', name: 'etl_contrats')]
|
|
public function eltContrats(ContratsRepository $contratsRepository): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
$missions = [];
|
|
$states = ['ready', 'pending'];
|
|
|
|
if ($user instanceof Account) {
|
|
$missions = $contratsRepository->findBy(['reservationState' => $states], ['dateAt' => 'ASC']);
|
|
} elseif ($user instanceof Prestaire) {
|
|
$missions = $contratsRepository->findBy(['reservationState' => $states, 'prestataire' => $user], ['dateAt' => 'ASC']);
|
|
}
|
|
|
|
return $this->render('etl/contrats.twig', [
|
|
'missions' => $missions
|
|
]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}', name: 'etl_contrat_view', methods: ['GET'])]
|
|
public function eltContratView(Contrats $contrat): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
// Security check for Prestaire
|
|
if ($user instanceof Prestaire && $contrat->getPrestataire() !== $user) {
|
|
throw $this->createAccessDeniedException('Vous n\'avez pas accès à cette mission.');
|
|
}
|
|
|
|
// Calculate Totals
|
|
$totalHt = 0;
|
|
$totalCaution = 0;
|
|
$days = ($contrat->getDateAt() && $contrat->getEndAt()) ? ($contrat->getDateAt()->diff($contrat->getEndAt())->days + 1) : 1;
|
|
|
|
foreach ($contrat->getContratsLines() as $line) {
|
|
$totalHt += $line->getPrice1DayHt() + ($line->getPriceSupDayHt() * max(0, $days - 1));
|
|
$totalCaution += $line->getCaution();
|
|
}
|
|
|
|
foreach ($contrat->getContratsOptions() as $option) {
|
|
$totalHt += $option->getPrice();
|
|
}
|
|
|
|
$dejaPaye = 0;
|
|
foreach ($contrat->getContratsPayments() as $p) {
|
|
if ($p->getState() === 'complete' && $p->getType() !== 'caution') {
|
|
$dejaPaye += $p->getAmount();
|
|
}
|
|
}
|
|
$solde = $totalHt - $dejaPaye;
|
|
|
|
// Check if Caution is already paid
|
|
$cautionPaid = false;
|
|
foreach ($contrat->getContratsPayments() as $p) {
|
|
if ($p->getType() === 'caution' && $p->getState() === 'complete') {
|
|
$cautionPaid = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $this->render('etl/view.twig', [
|
|
'mission' => $contrat,
|
|
'totalCaution' => $totalCaution,
|
|
'solde' => $solde,
|
|
'cautionPaid' => $cautionPaid
|
|
]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/start', name: 'etl_mission_start', methods: ['POST'])]
|
|
public function eltMissionStart(Contrats $contrat, EntityManagerInterface $em, Mailer $mailer): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
if ($user instanceof Prestaire && $contrat->getPrestataire() !== $user) {
|
|
throw $this->createAccessDeniedException('Vous n\'avez pas accès à cette mission.');
|
|
}
|
|
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
if (!$etatLieux) {
|
|
$etatLieux = new EtatLieux();
|
|
$etatLieux->setContrat($contrat);
|
|
if ($user instanceof Prestaire) {
|
|
$etatLieux->setPrestataire($user);
|
|
} elseif ($user instanceof Account) {
|
|
$etatLieux->setAccount($user);
|
|
}
|
|
$em->persist($etatLieux);
|
|
}
|
|
|
|
$etatLieux->setStatus('delivery_progress');
|
|
$em->flush();
|
|
|
|
// Notification client
|
|
if ($contrat->getCustomer()) {
|
|
$mailer->send(
|
|
$contrat->getCustomer()->getEmail(),
|
|
$contrat->getCustomer()->getName(),
|
|
"Votre commande est en route ! - #" . $contrat->getNumReservation(),
|
|
"mails/customer/delivery_start.twig",
|
|
['contrat' => $contrat]
|
|
);
|
|
}
|
|
|
|
$this->addFlash('success', 'Livraison démarrée, le client a été notifié.');
|
|
return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/finish', name: 'etl_mission_finish', methods: ['POST'])]
|
|
public function eltMissionFinish(Contrats $contrat, EntityManagerInterface $em): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
if ($user instanceof Prestaire && $contrat->getPrestataire() !== $user) {
|
|
throw $this->createAccessDeniedException('Vous n\'avez pas accès à cette mission.');
|
|
}
|
|
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
if ($etatLieux) {
|
|
$etatLieux->setStatus('delivery_done');
|
|
$em->flush();
|
|
$this->addFlash('success', 'Livraison terminée et confirmée.');
|
|
}
|
|
|
|
return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/caution', name: 'etl_mission_caution', methods: ['POST'])]
|
|
public function eltMissionCaution(Contrats $contrat, EntityManagerInterface $em): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
// Check already paid
|
|
foreach ($contrat->getContratsPayments() as $p) {
|
|
if ($p->getType() === 'caution' && $p->getState() === 'complete') {
|
|
$this->addFlash('warning', 'Caution déjà validée.');
|
|
return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]);
|
|
}
|
|
}
|
|
|
|
// Calculate Caution Amount
|
|
$totalCaution = 0;
|
|
$days = ($contrat->getDateAt() && $contrat->getEndAt()) ? ($contrat->getDateAt()->diff($contrat->getEndAt())->days + 1) : 1;
|
|
foreach ($contrat->getContratsLines() as $line) {
|
|
$totalCaution += $line->getCaution();
|
|
}
|
|
|
|
$payment = new ContratsPayments();
|
|
$payment->setContrat($contrat)
|
|
->setType('caution')
|
|
->setAmount($totalCaution)
|
|
->setState('complete')
|
|
->setPaymentAt(new \DateTimeImmutable())
|
|
->setValidateAt(new \DateTimeImmutable())
|
|
->setCard(['type' => 'manuel', 'method' => 'Prestataire'])
|
|
->setPaymentId("CAUTION-" . uniqid());
|
|
|
|
$em->persist($payment);
|
|
$em->flush();
|
|
|
|
$this->addFlash('success', 'Caution validée.');
|
|
return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/confirme', name: 'elt_mission_confirme', methods: ['POST'])]
|
|
public function eltMissionConfirme(Contrats $contrat, StripeClient $stripeClient, EntityManagerInterface $em): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
// Calculate Solde
|
|
$totalHt = 0;
|
|
$days = ($contrat->getDateAt() && $contrat->getEndAt()) ? ($contrat->getDateAt()->diff($contrat->getEndAt())->days + 1) : 1;
|
|
foreach ($contrat->getContratsLines() as $line) {
|
|
$totalHt += $line->getPrice1DayHt() + ($line->getPriceSupDayHt() * max(0, $days - 1));
|
|
}
|
|
foreach ($contrat->getContratsOptions() as $option) {
|
|
$totalHt += $option->getPrice();
|
|
}
|
|
$dejaPaye = 0;
|
|
foreach ($contrat->getContratsPayments() as $p) {
|
|
if ($p->getState() === 'complete' && $p->getType() !== 'caution') {
|
|
$dejaPaye += $p->getAmount();
|
|
}
|
|
}
|
|
$solde = $totalHt - $dejaPaye;
|
|
|
|
if ($solde <= 0) {
|
|
$this->addFlash('info', 'Le solde est déjà réglé.');
|
|
return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]);
|
|
}
|
|
|
|
// Create Payment Intent/Session using StripeClient (Using createPaymentSolde logic but manual or new method)
|
|
// Since createPaymentSolde redirects to specific success URL, I might need to adjust or create a generic one.
|
|
// Assuming createPaymentSolde can be reused but we might want custom success URL?
|
|
// Actually, createPaymentSolde sets success_url to /contrat/payment/success/... which is the customer success page.
|
|
// That might be fine, or we want redirection back to ETL?
|
|
// If we want redirection back to ETL, we need a new method in StripeClient or pass options.
|
|
// For now, I will use getNativeClient to build session manually to control success_url.
|
|
|
|
try {
|
|
$stripe = $stripeClient->getNativeClient();
|
|
$customer = $contrat->getCustomer();
|
|
|
|
// Ensure Stripe Customer exists (Controller helper or reuse existing logic if possible)
|
|
if (!$customer->getCustomerId()) {
|
|
$stripeClient->createCustomer($customer);
|
|
$em->flush();
|
|
}
|
|
|
|
$session = $stripe->checkout->sessions->create([
|
|
'customer' => $customer->getCustomerId(),
|
|
'line_items' => [[
|
|
'price_data' => [
|
|
'currency' => 'eur',
|
|
'product_data' => [
|
|
'name' => 'Solde Réservation #' . $contrat->getNumReservation(),
|
|
'description' => 'Règlement du solde via interface prestataire',
|
|
],
|
|
'unit_amount' => (int)round($solde * 100),
|
|
],
|
|
'quantity' => 1,
|
|
]],
|
|
'mode' => 'payment',
|
|
'success_url' => $this->generateUrl('etl_contrat_view', ['id' => $contrat->getId()], UrlGeneratorInterface::ABSOLUTE_URL), // Back to ETL view
|
|
'cancel_url' => $this->generateUrl('etl_contrat_view', ['id' => $contrat->getId()], UrlGeneratorInterface::ABSOLUTE_URL),
|
|
'metadata' => [
|
|
'contrat_id' => $contrat->getId(),
|
|
'type' => 'etl_payment' // Special type to trigger specific webhook logic
|
|
]
|
|
]);
|
|
|
|
// Persist pending payment
|
|
$payment = new ContratsPayments();
|
|
$payment->setContrat($contrat)
|
|
->setType('etl_payment') // Or 'solde' if we want it standard
|
|
->setAmount($solde)
|
|
->setState('created')
|
|
->setPaymentAt(new \DateTimeImmutable())
|
|
->setPaymentId($session->id);
|
|
|
|
$em->persist($payment);
|
|
$em->flush();
|
|
|
|
return new RedirectResponse($session->url);
|
|
|
|
} catch (\Exception $e) {
|
|
$this->addFlash('error', 'Erreur Stripe: ' . $e->getMessage());
|
|
return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]);
|
|
}
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/manual-pay', name: 'etl_mission_manual_pay', methods: ['POST'])]
|
|
public function eltMissionManualPayment(Contrats $contrat, Request $request, EntityManagerInterface $em): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
$method = $request->request->get('method');
|
|
$amount = (float)$request->request->get('amount'); // Optional, or calculate solde
|
|
|
|
if ($amount <= 0) {
|
|
// Calculate Solde if not provided
|
|
$totalHt = 0;
|
|
$days = ($contrat->getDateAt() && $contrat->getEndAt()) ? ($contrat->getDateAt()->diff($contrat->getEndAt())->days + 1) : 1;
|
|
foreach ($contrat->getContratsLines() as $line) {
|
|
$totalHt += $line->getPrice1DayHt() + ($line->getPriceSupDayHt() * max(0, $days - 1));
|
|
}
|
|
foreach ($contrat->getContratsOptions() as $option) {
|
|
$totalHt += $option->getPrice();
|
|
}
|
|
$dejaPaye = 0;
|
|
foreach ($contrat->getContratsPayments() as $p) {
|
|
if ($p->getState() === 'complete' && $p->getType() !== 'caution') {
|
|
$dejaPaye += $p->getAmount();
|
|
}
|
|
}
|
|
$amount = $totalHt - $dejaPaye;
|
|
}
|
|
|
|
if ($amount > 0) {
|
|
$payment = new ContratsPayments();
|
|
$payment->setContrat($contrat)
|
|
->setType('etl_payment')
|
|
->setAmount($amount)
|
|
->setState('complete')
|
|
->setPaymentAt(new \DateTimeImmutable())
|
|
->setValidateAt(new \DateTimeImmutable())
|
|
->setCard(['type' => 'manuel', 'method' => $method])
|
|
->setPaymentId("MANUAL-" . uniqid());
|
|
|
|
$em->persist($payment);
|
|
$em->flush();
|
|
$this->addFlash('success', 'Paiement manuel enregistré.');
|
|
}
|
|
|
|
return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/edl/start', name: 'etl_mission_edl_start', methods: ['POST'])]
|
|
public function eltMissionEdlStart(Contrats $contrat, EntityManagerInterface $em): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
if ($etatLieux) {
|
|
$etatLieux->setStatus('edl_progress');
|
|
$em->flush();
|
|
$this->addFlash('success', 'État des lieux commencé.');
|
|
}
|
|
|
|
return $this->redirectToRoute('etl_mission_edl', ['id' => $contrat->getId()]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/edl', name: 'etl_mission_edl', methods: ['GET'])]
|
|
public function eltEdl(Contrats $contrat): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
// Security check
|
|
if ($user instanceof Prestaire && $contrat->getPrestataire() !== $user) {
|
|
throw $this->createAccessDeniedException('Vous n\'avez pas accès à cette mission.');
|
|
}
|
|
|
|
return $this->render('etl/edl.twig', [
|
|
'mission' => $contrat,
|
|
'etatLieux' => $contrat->getEtatLieux()
|
|
]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/edl/return', name: 'etl_mission_edl_return', methods: ['GET'])]
|
|
public function eltEdlReturn(Contrats $contrat): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
// Security check
|
|
if ($user instanceof Prestaire && $contrat->getPrestataire() !== $user) {
|
|
throw $this->createAccessDeniedException('Vous n\'avez pas accès à cette mission.');
|
|
}
|
|
|
|
return $this->render('etl/edl.twig', [
|
|
'mission' => $contrat,
|
|
'etatLieux' => $contrat->getEtatLieux(),
|
|
'isReturnEdl' => true // Flag to indicate it\'s a return EDL
|
|
]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/edl/comment', name: 'etl_edl_add_comment', methods: ['POST'])]
|
|
public function eltEdlAddComment(Contrats $contrat, Request $request, EntityManagerInterface $em): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
$content = $request->request->get('content');
|
|
if ($content) {
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
if($etatLieux->getStatus() == "return_edl_progress") {
|
|
$comment = new EtatLieuxReturnComment();
|
|
} else {
|
|
$comment = new EtatLieuxComment();
|
|
}
|
|
|
|
$comment->setContent($content);
|
|
$comment->setEtatLieux($etatLieux);
|
|
$em->persist($comment);
|
|
$em->flush();
|
|
$this->addFlash('success', 'Commentaire ajouté.');
|
|
}
|
|
|
|
return $this->redirectToRoute('etl_mission_edl', ['id' => $contrat->getId()]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/edl/save-points', name: 'etl_edl_save_points', methods: ['POST'])]
|
|
public function eltEdlSavePoints(Contrats $contrat, Request $request, EntityManagerInterface $em): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
if (!$etatLieux) {
|
|
return $this->redirectToRoute('etl_mission_edl', ['id' => $contrat->getId()]);
|
|
}
|
|
|
|
$data = $request->request->all('points');
|
|
|
|
if ($data) {
|
|
foreach ($data as $productId => $points) {
|
|
foreach ($points as $pointId => $values) {
|
|
$productPoint = $em->getRepository(ProductPointControll::class)->find($pointId);
|
|
|
|
if ($productPoint) {
|
|
$existing = null;
|
|
if($etatLieux->getStatus() == "return_edl_progress") {
|
|
foreach ($etatLieux->getPointControlsReturn() as $ep) {
|
|
if ($ep->getName() === $productPoint->getName()) {
|
|
$existing = $ep;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!$existing) {
|
|
$existing = new EtatLieuxReturnPointControl();
|
|
$existing->setName($productPoint->getName());
|
|
$existing->setEtatLieux($etatLieux);
|
|
$em->persist($existing);
|
|
}
|
|
|
|
$existing->setStatus(isset($values['status']));
|
|
$existing->setDetails($values['details'] ?? null);
|
|
} else {
|
|
foreach ($etatLieux->getPointControls() as $ep) {
|
|
if ($ep->getName() === $productPoint->getName()) {
|
|
$existing = $ep;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!$existing) {
|
|
$existing = new EtatLieuxPointControl();
|
|
$existing->setName($productPoint->getName());
|
|
$existing->setEtatLieux($etatLieux);
|
|
$em->persist($existing);
|
|
}
|
|
|
|
$existing->setStatus(isset($values['status']));
|
|
$existing->setDetails($values['details'] ?? null);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
$em->flush();
|
|
$this->addFlash('success', 'Points de contrôle enregistrés.');
|
|
}
|
|
|
|
return $this->redirectToRoute('etl_mission_edl', ['id' => $contrat->getId()]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/edl/file', name: 'etl_edl_add_file', methods: ['POST'])]
|
|
public function eltEdlAddFile(Contrats $contrat, Request $request, EntityManagerInterface $em): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
$photos = $request->files->get('photos');
|
|
$videos = $request->files->get('videos');
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
$hasFiles = false;
|
|
|
|
|
|
if ($photos) {
|
|
if (!is_array($photos)) $photos = [$photos];
|
|
foreach ($photos as $uploadedFile) {
|
|
if ($uploadedFile instanceof UploadedFile) {
|
|
$this->compressImage($uploadedFile);
|
|
|
|
if($etatLieux->getStatus() == "return_edl_progress") {
|
|
$file = new EtatReturnLieuxFile();
|
|
$file->setFile($uploadedFile);
|
|
$file->setType('photo');
|
|
$file->setEtatLieux($etatLieux);
|
|
$em->persist($file);
|
|
$hasFiles = true;
|
|
} else {
|
|
$file = new EtatLieuxFile();
|
|
$file->setFile($uploadedFile);
|
|
$file->setType('photo');
|
|
$file->setEtatLieux($etatLieux);
|
|
$em->persist($file);
|
|
$hasFiles = true;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
if ($videos) {
|
|
if (!is_array($videos)) $videos = [$videos];
|
|
foreach ($videos as $uploadedFile) {
|
|
if ($uploadedFile instanceof UploadedFile) {
|
|
$this->compressVideo($uploadedFile);
|
|
if($etatLieux->getStatus() == "return_edl_progress") {
|
|
$file = new EtatReturnLieuxFile();
|
|
$file->setFile($uploadedFile);
|
|
$file->setType('video');
|
|
$file->setEtatLieux($etatLieux);
|
|
$em->persist($file);
|
|
$hasFiles = true;
|
|
} else {
|
|
$file = new EtatLieuxFile();
|
|
$file->setFile($uploadedFile);
|
|
$file->setType('video');
|
|
$file->setEtatLieux($etatLieux);
|
|
$em->persist($file);
|
|
$hasFiles = true;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
if ($hasFiles) {
|
|
$em->flush();
|
|
$this->addFlash('success', 'Fichiers ajoutés.');
|
|
}
|
|
|
|
return $this->redirectToRoute('etl_mission_edl', ['id' => $contrat->getId()]);
|
|
|
|
}
|
|
|
|
|
|
#[Route('/etl/mission/{id}/edl/file/{fileId}/delete', name: 'etl_edl_delete_file', methods: ['POST'])]
|
|
public function eltEdlDeleteFile(Contrats $contrat, int $fileId, EntityManagerInterface $em): Response
|
|
|
|
{
|
|
|
|
$user = $this->getUser();
|
|
|
|
if (!$user) {
|
|
|
|
return $this->redirectToRoute('etl_login');
|
|
|
|
}
|
|
|
|
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
|
|
if($etatLieux->getStatus() == "return_edl_progress") {
|
|
$file = $em->getRepository(EtatReturnLieuxFile::class)->find($fileId);
|
|
} else {
|
|
$file = $em->getRepository(EtatLieuxFile::class)->find($fileId);
|
|
}
|
|
|
|
if ($file && $file->getEtatLieux() === $etatLieux) {
|
|
|
|
$em->remove($file);
|
|
|
|
$em->flush();
|
|
|
|
$this->addFlash('success', 'Fichier supprimé.');
|
|
|
|
}
|
|
|
|
|
|
return $this->redirectToRoute('etl_mission_edl', ['id' => $contrat->getId()]);
|
|
|
|
}
|
|
|
|
|
|
#[Route('/etl/mission/{id}/edl/finish', name: 'etl_edl_finish', methods: ['POST'])]
|
|
public function eltEdlFinish(Contrats $contrat, EntityManagerInterface $em, KernelInterface $kernel, SignatureClient $signatureClient): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
if($etatLieux->getStatus() == "return_edl_progress") {
|
|
$etatLieux->setStatus('edl_return_done');
|
|
$this->generateAndSendToDocuSealReturn($contrat, $em, $kernel, $signatureClient);
|
|
$this->addFlash('success', 'État des lieux terminé et PDF généré.');
|
|
return $this->redirectToRoute('etl_mission_signed_entry_state', ['id' => $contrat->getId()]);
|
|
} else {
|
|
if ($etatLieux) {
|
|
$etatLieux->setStatus('edl_done');
|
|
$this->generateAndSendToDocuSeal($contrat, $em, $kernel, $signatureClient);
|
|
|
|
$this->addFlash('success', 'État des lieux terminé et PDF généré.');
|
|
return $this->redirectToRoute('etl_mission_signed_entry_state', ['id' => $contrat->getId()]);
|
|
}
|
|
}
|
|
|
|
return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/edl/return/start', name: 'etl_mission_edl_return_start', methods: ['POST'])]
|
|
public function eltMissionEdlReturnStart(Contrats $contrat, EntityManagerInterface $em): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
if ($user instanceof Prestaire && $contrat->getPrestataire() !== $user) {
|
|
throw $this->createAccessDeniedException("Vous n'avez pas accès à cette mission.");
|
|
}
|
|
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
if ($etatLieux) {
|
|
$etatLieux->setStatus('return_edl_progress');
|
|
$em->flush();
|
|
$this->addFlash('success', 'État des lieux de retour commencé.');
|
|
}
|
|
|
|
return $this->redirectToRoute('etl_mission_edl_return', ['id' => $contrat->getId()]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/edl/regenerate-view', name: 'etl_edl_regenerate_view', methods: ['GET'])]
|
|
public function eltEdlRegenerateAndView(Contrats $contrat, EntityManagerInterface $em, KernelInterface $kernel, SignatureClient $signatureClient, UploaderHelper $uploaderHelper): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
if($contrat->getEtatLieux()->getStatus() == "edl_return_done") {
|
|
$this->generateAndSendToDocuSealReturn($contrat, $em, $kernel, $signatureClient);
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
$path = $uploaderHelper->asset($etatLieux, 'etatLieuxUnsignReturnFile');
|
|
} else {
|
|
$this->generateAndSendToDocuSeal($contrat, $em, $kernel, $signatureClient);
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
$path = $uploaderHelper->asset($etatLieux, 'etatLieuxUnsignFile');
|
|
}
|
|
|
|
|
|
return new RedirectResponse($path);
|
|
}
|
|
|
|
private function generateAndSendToDocuSeal(Contrats $contrat, EntityManagerInterface $em, KernelInterface $kernel, SignatureClient $signatureClient): void
|
|
{
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
|
|
// Generate PDF
|
|
$pdfService = new EtatLieuxPdfService($kernel, $contrat);
|
|
$pdfContent = $pdfService->generate();
|
|
|
|
// Save PDF
|
|
$tmpPath = sys_get_temp_dir() . '/edl_entrant_' . $contrat->getId() . '_' . uniqid() . '.pdf';
|
|
file_put_contents($tmpPath, $pdfContent);
|
|
|
|
// Update entity with file
|
|
$file = new UploadedFile($tmpPath, 'edl_entrant.pdf', 'application/pdf', null, true);
|
|
$etatLieux->setEtatLieuxUnsignFile($file);
|
|
$etatLieux->setUpdatedAt(new \DateTimeImmutable());
|
|
|
|
$em->persist($etatLieux);
|
|
$em->flush(); // Save file
|
|
|
|
// Send to DocuSeal
|
|
try {
|
|
$signatureClient->createSubmissionEtatLieux($etatLieux);
|
|
} catch (\Exception $e) {
|
|
// Fallback
|
|
}
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/signed-entry-state', name: 'etl_mission_signed_entry_state', methods: ['GET'])]
|
|
public function eltMissionSignedEntryState(Contrats $contrat, SignatureClient $signatureClient): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
|
|
$providerSigned = false;
|
|
$customerSigned = false;
|
|
if($contrat->getEtatLieux()->getStatus() == "edl_return_done") {
|
|
if ($etatLieux->getSignIdReturn()) {
|
|
try {
|
|
$sub = $signatureClient->getSubmiter($etatLieux->getSignIdReturn());
|
|
if ($sub && ($sub['status'] ?? '') === 'completed') $providerSigned = true;
|
|
} catch (\Exception $e) {
|
|
}
|
|
}
|
|
|
|
if ($etatLieux->getSignIdCustomerReturn()) {
|
|
try {
|
|
$sub = $signatureClient->getSubmiter($etatLieux->getSignIdCustomerReturn());
|
|
if ($sub && ($sub['status'] ?? '') === 'completed') $customerSigned = true;
|
|
} catch (\Exception $e) {
|
|
}
|
|
}
|
|
} else {
|
|
|
|
if ($etatLieux->getSignIdDelivery()) {
|
|
try {
|
|
$sub = $signatureClient->getSubmiter($etatLieux->getSignIdDelivery());
|
|
if ($sub && ($sub['status'] ?? '') === 'completed') $providerSigned = true;
|
|
} catch (\Exception $e) {
|
|
}
|
|
}
|
|
|
|
if ($etatLieux->getSignIdCustomer()) {
|
|
try {
|
|
$sub = $signatureClient->getSubmiter($etatLieux->getSignIdCustomer());
|
|
if ($sub && ($sub['status'] ?? '') === 'completed') $customerSigned = true;
|
|
} catch (\Exception $e) {
|
|
}
|
|
}
|
|
}
|
|
return $this->render('etl/signed_entry_state.twig', [
|
|
'mission' => $contrat,
|
|
'etatLieux' => $etatLieux,
|
|
'providerSigned' => $providerSigned,
|
|
'customerSigned' => $customerSigned
|
|
]);
|
|
}
|
|
|
|
#[Route('/etl/sign/provider/{id}', name: 'etl_sign_provider', methods: ['GET'])]
|
|
public function eltSignProvider(EtatLieux $etatLieux, SignatureClient $signatureClient): Response
|
|
{
|
|
// Redirect to DocuSeal URL for Provider
|
|
$url = $signatureClient->getSigningUrl($etatLieux, 'Ludikevent'); // Role name from PDF
|
|
if ($url) {
|
|
return new RedirectResponse($url);
|
|
}
|
|
$this->addFlash('error', 'Lien de signature non disponible.');
|
|
return $this->redirectToRoute('etl_mission_signed_entry_state', ['id' => $etatLieux->getContrat()->getId()]);
|
|
}
|
|
|
|
#[Route('/etl/sign/customer/{id}', name: 'etl_sign_customer', methods: ['GET'])]
|
|
public function eltSignCustomer(EtatLieux $etatLieux, SignatureClient $signatureClient): Response
|
|
{
|
|
// Redirect to DocuSeal URL for Customer
|
|
$url = $signatureClient->getSigningUrl($etatLieux, 'Client'); // Role name from PDF
|
|
if ($url) {
|
|
return new RedirectResponse($url);
|
|
}
|
|
$this->addFlash('error', 'Lien de signature non disponible.');
|
|
return $this->redirectToRoute('etl_mission_signed_entry_state', ['id' => $etatLieux->getContrat()->getId()]);
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/edl/close', name: 'etl_edl_close', methods: ['POST'])]
|
|
public function eltMissionCloseEdl(Contrats $contrat, SignatureClient $signatureClient, EntityManagerInterface $em, Mailer $mailer, StorageInterface $storage): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
if($etatLieux->getStatus() == "edl_return_done") {
|
|
if (!$etatLieux || !$etatLieux->getSignIdReturn() || !$etatLieux->getSignIdCustomerReturn()) {
|
|
$this->addFlash('error', 'Signatures manquantes.');
|
|
return $this->redirectToRoute('etl_mission_signed_entry_state', ['id' => $contrat->getId()]);
|
|
}
|
|
// Get signed documents
|
|
$sub = $signatureClient->getSubmiter($etatLieux->getSignIdReturn());
|
|
$submissionId = $sub['submission_id'];
|
|
$submission = $signatureClient->getSubmition($submissionId);
|
|
|
|
$signedPdfUrl = $submission['documents'][0]['url'] ?? null;
|
|
$auditUrl = $submission['audit_log_url'] ?? null;
|
|
|
|
if ($signedPdfUrl) {
|
|
$tmpPath = sys_get_temp_dir() . '/edl_retour_signed_' . $contrat->getId() . '.pdf';
|
|
file_put_contents($tmpPath, file_get_contents($signedPdfUrl));
|
|
$file = new UploadedFile($tmpPath, 'edl_retour_signed_.pdf', 'application/pdf', null, true);
|
|
$etatLieux->setEtatLieuxSignReturnFile($file);
|
|
}
|
|
if ($auditUrl) {
|
|
$tmpPathAudit = sys_get_temp_dir() . '/edl_retour_audit_signed_' . $contrat->getId() . '.pdf';
|
|
file_put_contents($tmpPathAudit, file_get_contents($auditUrl));
|
|
$file = new UploadedFile($tmpPathAudit, 'edl_retour_audit_signed_.pdf', 'application/pdf', null, true);
|
|
$etatLieux->setEtatLieuxAuditReturnFile($file);
|
|
}
|
|
$etatLieux->setUpdatedAt(new \DateTimeImmutable());
|
|
|
|
$etatLieux->setStatus('edl_return_finised');
|
|
$contrat->setReservationState('finished');
|
|
$em->flush();
|
|
|
|
// Emails
|
|
$recipients = [
|
|
$contrat->getCustomer()->getEmail(),
|
|
'contact@ludikevent.fr'
|
|
];
|
|
if ($etatLieux->getPrestataire()) {
|
|
$recipients[] = $etatLieux->getPrestataire()->getEmail();
|
|
}
|
|
|
|
$attachments = [];
|
|
|
|
// Try resolve path from Vich
|
|
$signPath = $storage->resolvePath($etatLieux, 'etatLieuxSignReturnFile');
|
|
// If resolvePath returns null (e.g. no mapping or file not found yet?), check manual path
|
|
// But flush() should have moved it. resolvePath usually returns absolute path.
|
|
if ($signPath && file_exists($signPath)) {
|
|
$attachments[] = DataPart::fromPath($signPath, 'Etat_des_retour_signe.pdf');
|
|
} elseif (isset($tmpPath) && file_exists($tmpPath)) {
|
|
$attachments[] = DataPart::fromPath($tmpPath, 'Etat_des_retour_signe.pdf');
|
|
}
|
|
|
|
$auditPath = $storage->resolvePath($etatLieux, 'etatLieuxAuditReturnFile');
|
|
if ($auditPath && file_exists($auditPath)) {
|
|
$attachments[] = DataPart::fromPath($auditPath, 'Audit_Etat_des_retour_signe.pdf');
|
|
} elseif (isset($tmpPathAudit) && file_exists($tmpPathAudit)) {
|
|
$attachments[] = DataPart::fromPath($tmpPathAudit, 'Audit_Etat_des_retour_signe.pdf');
|
|
}
|
|
|
|
foreach (array_unique($recipients) as $email) {
|
|
$mailer->send(
|
|
$email,
|
|
'Destinataire',
|
|
"État des lieux validé - #" . $contrat->getNumReservation(),
|
|
"mails/etl/edl_retour_confirmation.twig",
|
|
[
|
|
'contrat' => $contrat,
|
|
'etatLieux' => $etatLieux
|
|
],
|
|
$attachments
|
|
);
|
|
}
|
|
|
|
$this->addFlash('success', 'État des lieux clôturé et envoyé.');
|
|
return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]);
|
|
} else {
|
|
if (!$etatLieux || !$etatLieux->getSignIdDelivery() || !$etatLieux->getSignIdCustomer()) {
|
|
$this->addFlash('error', 'Signatures manquantes.');
|
|
return $this->redirectToRoute('etl_mission_signed_entry_state', ['id' => $contrat->getId()]);
|
|
}
|
|
// Get signed documents
|
|
$sub = $signatureClient->getSubmiter($etatLieux->getSignIdDelivery());
|
|
$submissionId = $sub['submission_id'];
|
|
$submission = $signatureClient->getSubmition($submissionId);
|
|
|
|
$signedPdfUrl = $submission['documents'][0]['url'] ?? null;
|
|
$auditUrl = $submission['audit_log_url'] ?? null;
|
|
|
|
if ($signedPdfUrl) {
|
|
$tmpPath = sys_get_temp_dir() . '/edl_signed_' . $contrat->getId() . '.pdf';
|
|
file_put_contents($tmpPath, file_get_contents($signedPdfUrl));
|
|
$file = new UploadedFile($tmpPath, 'edl_entrant_signed.pdf', 'application/pdf', null, true);
|
|
$etatLieux->setEtatLieuxSignFile($file);
|
|
}
|
|
if ($auditUrl) {
|
|
$tmpPathAudit = sys_get_temp_dir() . '/edl_audit_signed_' . $contrat->getId() . '.pdf';
|
|
file_put_contents($tmpPathAudit, file_get_contents($auditUrl));
|
|
$file = new UploadedFile($tmpPathAudit, 'edl_audit_signed.pdf', 'application/pdf', null, true);
|
|
$etatLieux->setEtatLieuxAuditFile($file);
|
|
}
|
|
$etatLieux->setUpdatedAt(new \DateTimeImmutable());
|
|
|
|
$etatLieux->setStatus('edl_validated');
|
|
$contrat->setReservationState('progress');
|
|
$em->flush();
|
|
|
|
// Emails
|
|
$recipients = [
|
|
$contrat->getCustomer()->getEmail(),
|
|
'contact@ludikevent.fr'
|
|
];
|
|
if ($etatLieux->getPrestataire()) {
|
|
$recipients[] = $etatLieux->getPrestataire()->getEmail();
|
|
}
|
|
|
|
$attachments = [];
|
|
|
|
// Try resolve path from Vich
|
|
$signPath = $storage->resolvePath($etatLieux, 'etatLieuxSignFile');
|
|
// If resolvePath returns null (e.g. no mapping or file not found yet?), check manual path
|
|
// But flush() should have moved it. resolvePath usually returns absolute path.
|
|
if ($signPath && file_exists($signPath)) {
|
|
$attachments[] = DataPart::fromPath($signPath, 'Etat_des_lieux_signe.pdf');
|
|
} elseif (isset($tmpPath) && file_exists($tmpPath)) {
|
|
$attachments[] = DataPart::fromPath($tmpPath, 'Etat_des_lieux_signe.pdf');
|
|
}
|
|
|
|
$auditPath = $storage->resolvePath($etatLieux, 'etatLieuxAuditFile');
|
|
if ($auditPath && file_exists($auditPath)) {
|
|
$attachments[] = DataPart::fromPath($auditPath, 'Audit_Etat_des_lieux_signe.pdf');
|
|
} elseif (isset($tmpPathAudit) && file_exists($tmpPathAudit)) {
|
|
$attachments[] = DataPart::fromPath($tmpPathAudit, 'Audit_Etat_des_lieux_signe.pdf');
|
|
}
|
|
|
|
foreach (array_unique($recipients) as $email) {
|
|
$mailer->send(
|
|
$email,
|
|
'Destinataire',
|
|
"État des lieux validé - #" . $contrat->getNumReservation(),
|
|
"mails/etl/edl_confirmation.twig",
|
|
[
|
|
'contrat' => $contrat,
|
|
'etatLieux' => $etatLieux
|
|
],
|
|
$attachments
|
|
);
|
|
}
|
|
|
|
$this->addFlash('success', 'État des lieux clôturé et envoyé.');
|
|
return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
#[Route('/etl/account', name: 'etl_account', methods: ['GET', 'POST'])]
|
|
public function eltAccount(
|
|
Request $request,
|
|
UserPasswordHasherInterface $passwordHasher,
|
|
EntityManagerInterface $entityManager
|
|
): Response
|
|
{
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
if ($user instanceof Account) {
|
|
$this->addFlash('warning', 'Les administrateurs ne peuvent pas modifier leur mot de passe ici.');
|
|
return $this->redirectToRoute('etl_home');
|
|
}
|
|
|
|
$form = $this->createForm(PrestairePasswordType::class, $user);
|
|
$form->handleRequest($request);
|
|
|
|
if ($form->isSubmitted() && $form->isValid()) {
|
|
$hashedPassword = $passwordHasher->hashPassword(
|
|
$user,
|
|
$form->get('password')->getData()
|
|
);
|
|
$user->setPassword($hashedPassword);
|
|
|
|
$entityManager->flush();
|
|
|
|
$this->addFlash('success', 'Votre mot de passe a été modifié avec succès.');
|
|
return $this->redirectToRoute('etl_account');
|
|
}
|
|
|
|
return $this->render('etl/account.twig', [
|
|
'form' => $form->createView(),
|
|
]);
|
|
}
|
|
|
|
#[Route('/etl/connexion', name: 'etl_login')]
|
|
public function eltLogin(AuthenticationUtils $authenticationUtils): Response
|
|
{
|
|
if ($this->getUser()) {
|
|
return $this->redirectToRoute('etl_home');
|
|
}
|
|
|
|
return $this->render('etl/login.twig', [
|
|
'last_username' => $authenticationUtils->getLastUsername(),
|
|
'error' => $authenticationUtils->getLastAuthenticationError()
|
|
]);
|
|
}
|
|
|
|
#[Route('/etl/logout', name: 'elt_logout')]
|
|
public function eltLogout(): Response
|
|
{
|
|
// This method can be blank - it will be intercepted by the logout key on your firewall
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
#[Route('/etl/connect/keycloak', name: 'connect_keycloak_etl_start')]
|
|
public function connectKeycloakEtlStart(ClientRegistry $clientRegistry, Request $request): Response
|
|
{
|
|
$response = $clientRegistry
|
|
->getClient('keycloak_etl')
|
|
->redirect(['openid', 'profile', 'email']);
|
|
$response->headers->set('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
$response->headers->set('Pragma', 'no-cache');
|
|
|
|
$request->getSession()->save();
|
|
|
|
return $response;
|
|
}
|
|
|
|
#[Route('/etl/oauth/sso', name: 'connect_keycloak_etl_check')]
|
|
public function connectKeycloakEtlCheck(): Response
|
|
{
|
|
return new Response('Auth check', 200); // Intercepted by authenticator
|
|
}
|
|
|
|
private function compressImage(UploadedFile $file): UploadedFile
|
|
{
|
|
if (!extension_loaded('gd')) {
|
|
return $file;
|
|
}
|
|
|
|
$mime = $file->getMimeType();
|
|
$path = $file->getPathname();
|
|
|
|
// Simple compression logic
|
|
switch ($mime) {
|
|
case 'image/jpeg':
|
|
$image = @imagecreatefromjpeg($path);
|
|
if ($image) {
|
|
imagejpeg($image, $path, 75); // Quality 75
|
|
imagedestroy($image);
|
|
}
|
|
break;
|
|
case 'image/png':
|
|
$image = @imagecreatefrompng($path);
|
|
if ($image) {
|
|
// PNG compression 0-9
|
|
imagepng($image, $path, 6);
|
|
imagedestroy($image);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return $file;
|
|
}
|
|
|
|
private function compressVideo(UploadedFile $file): UploadedFile
|
|
{
|
|
// Check if ffmpeg is available
|
|
$ffmpegPath = shell_exec('which ffmpeg');
|
|
if (empty($ffmpegPath)) {
|
|
return $file;
|
|
}
|
|
|
|
$inputPath = $file->getPathname();
|
|
$outputPath = $inputPath . '_compressed.mp4';
|
|
|
|
// Compress video using ffmpeg (CRF 28 for reasonable quality/size trade-off)
|
|
// -y to overwrite, -vcodec libx264, -crf 28, -preset fast
|
|
$command = sprintf(
|
|
'ffmpeg -y -i %s -vcodec libx264 -crf 28 -preset fast %s 2>&1',
|
|
escapeshellarg($inputPath),
|
|
escapeshellarg($outputPath)
|
|
);
|
|
|
|
exec($command, $output, $returnVar);
|
|
|
|
if ($returnVar === 0 && file_exists($outputPath)) {
|
|
// Replace original file with compressed one
|
|
if (rename($outputPath, $inputPath)) {
|
|
// Success
|
|
} else {
|
|
// Fallback cleanup
|
|
@unlink($outputPath);
|
|
}
|
|
}
|
|
|
|
return $file;
|
|
}
|
|
|
|
private function generateAndSendToDocuSealReturn(Contrats $contrat, EntityManagerInterface $em, KernelInterface $kernel, SignatureClient $signatureClient)
|
|
{
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
|
|
// Generate PDF
|
|
$pdfService = new EtatLieuxPdfService($kernel, $contrat);
|
|
$pdfContent = $pdfService->generate();
|
|
|
|
|
|
// Save PDF
|
|
$tmpPath = sys_get_temp_dir() . '/edl_sortant_' . $contrat->getId() . '_' . uniqid() . '.pdf';
|
|
file_put_contents($tmpPath, $pdfContent);
|
|
|
|
// Update entity with file
|
|
$file = new UploadedFile($tmpPath, 'edl_sortant_.pdf', 'application/pdf', null, true);
|
|
$etatLieux->setEtatLieuxUnsignReturnFile($file);
|
|
$etatLieux->setUpdatedAt(new \DateTimeImmutable());
|
|
|
|
$em->persist($etatLieux);
|
|
$em->flush(); // Save file
|
|
|
|
// Send to DocuSeal
|
|
try {
|
|
$signatureClient->createSubmissionEtatLieuxSortant($etatLieux);
|
|
} catch (\Exception $e) {
|
|
// Fallback
|
|
}
|
|
}
|
|
|
|
#[Route('/etl/mission/{id}/edl/refused', name: 'etl_edl_customer_refused', methods: ['POST'])]
|
|
public function eltEdlCustomerRefused(
|
|
Contrats $contrat,
|
|
StorageInterface $storage,
|
|
Request $request,
|
|
EntityManagerInterface $em,
|
|
Mailer $mailer
|
|
): Response {
|
|
$user = $this->getUser();
|
|
if (!$user) {
|
|
return $this->redirectToRoute('etl_login');
|
|
}
|
|
|
|
$etatLieux = $contrat->getEtatLieux();
|
|
|
|
// On vérifie que l'on est bien en phase de retour
|
|
if (!$etatLieux || $etatLieux->getStatus() !== 'edl_return_done') {
|
|
$this->addFlash('error', 'Action impossible dans l\'état actuel.');
|
|
return $this->redirectToRoute('etl_mission_signed_entry_state', ['id' => $contrat->getId()]);
|
|
}
|
|
|
|
// Récupération de la raison saisie
|
|
$reason = $request->request->get('refusal_reason');
|
|
|
|
// Mise à jour de l'entité
|
|
// Note : Assurez-vous d'avoir le champ raisonRefus dans votre entité EtatLieux
|
|
$etatLieux->setRaisonRefused($reason);
|
|
$etatLieux->setStatus('edl_return_refused'); // Nouveau status pour différencier
|
|
|
|
// On clôture tout de même la mission côté réservation
|
|
$contrat->setReservationState('finished');
|
|
|
|
$em->flush();
|
|
|
|
|
|
// Notification par email (Optionnel mais recommandé)
|
|
$mailer->send(
|
|
'contact@ludikevent.fr',
|
|
'Admin Ludikevent',
|
|
"Signature refusée par le client - #" . $contrat->getNumReservation(),
|
|
"mails/etl/edl_refused_alert.twig",
|
|
[
|
|
'contrat' => $contrat,
|
|
'reason' => $reason,
|
|
'prestataire' => $user
|
|
]
|
|
);
|
|
|
|
$this->addFlash('warning', 'Le refus a été enregistré. La mission est clôturée avec mention de litige.');
|
|
|
|
return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]);
|
|
}
|
|
}
|