feat(ReserverController): Génère un devis PDF provisoire basé sur la session.

🎨 style(flow_confirmed.twig): Ajoute un lien pour télécharger le devis provisoire.
```
This commit is contained in:
Serreau Jovann
2026-02-02 12:14:07 +01:00
parent 7ab37b4d8b
commit 6c6324addc
2 changed files with 94 additions and 5 deletions

View File

@@ -24,11 +24,15 @@ use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
use Symfony\Component\HttpKernel\KernelInterface;
use App\Service\Pdf\DevisPdfService;
use App\Entity\Devis;
use App\Entity\DevisLine;
use App\Entity\CustomerAddress;
class ReserverController extends AbstractController
{
@@ -40,6 +44,88 @@ class ReserverController extends AbstractController
$this->kernel = $kernel;
}
#[Route('/flow/{sessionId}/devis', name: 'reservation_generate_devis', methods: ['GET'])]
public function generateDevis(
string $sessionId,
OrderSessionRepository $repository,
ProductRepository $productRepository,
KernelInterface $kernel
): Response {
$session = $repository->findOneBy(['uuid' => $sessionId]);
if (!$session) {
return $this->redirectToRoute('reservation');
}
$sessionData = $session->getProducts();
$ids = $sessionData['ids'] ?? [];
$startStr = $sessionData['start'] ?? null;
$endStr = $sessionData['end'] ?? null;
// Calcul de la durée
$duration = 1;
$start = null;
$end = null;
if ($startStr && $endStr) {
try {
$start = new \DateTimeImmutable($startStr);
$end = new \DateTimeImmutable($endStr);
if ($end >= $start) {
$duration = $start->diff($end)->days + 1;
}
} catch (\Exception $e) {
$duration = 1;
}
}
// Création des objets temporaires pour le PDF
$customer = new Customer();
$customer->setName($session->getCustomer() ? $session->getCustomer()->getName() : 'Client');
$customer->setSurname($session->getCustomer() ? $session->getCustomer()->getSurname() : 'Temporaire');
$customer->setEmail($session->getCustomer() ? $session->getCustomer()->getEmail() : '');
$customer->setPhone($session->getCustomer() ? $session->getCustomer()->getPhone() : '');
// Adresse de facturation
$billAddress = new CustomerAddress();
$billAddress->setAddress($session->getBillingAddress() ?? '');
$billAddress->setZipcode($session->getBillingZipCode() ?? '');
$billAddress->setCity($session->getBillingTown() ?? '');
// Adresse de prestation
$shipAddress = new CustomerAddress();
$shipAddress->setAddress($session->getAdressEvent() ?? '');
$shipAddress->setAddress2($session->getAdress2Event() ?? '');
$shipAddress->setZipcode($session->getZipCodeEvent() ?? '');
$shipAddress->setCity($session->getTownEvent() ?? '');
$devis = new Devis();
$devis->setCustomer($customer);
$devis->setBillAddress($billAddress);
$devis->setAddressShip($shipAddress);
$devis->setNum('PROVISOIRE');
$devis->setStartAt($start);
$devis->setEndAt($end);
if (!empty($ids)) {
$products = $productRepository->findBy(['id' => $ids]);
foreach ($products as $product) {
$line = new DevisLine();
$line->setProduct($product->getName());
$line->setPriceHt($product->getPriceDay());
$line->setPriceHtSup($product->getPriceSup());
$line->setDay($duration);
$devis->addDevisLine($line);
}
}
$pdfService = new DevisPdfService($kernel, $devis, $productRepository);
$content = $pdfService->generate();
return new Response($content, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="devis_provisoire.pdf"'
]);
}
private function loadSimplifiedCommunes(): array
{
if ($this->simplifiedCommunes !== null) {

View File

@@ -40,7 +40,7 @@
<div class="flex-1">
<h4 class="font-bold text-slate-800">{{ item.product.name }}</h4>
<p class="text-xs text-slate-500 line-clamp-1 mb-2">{{ item.product.description }}</p>
<div class="text-xs text-slate-600 bg-slate-50 p-2 rounded-lg border border-slate-100 inline-block">
<div class="flex flex-wrap gap-x-3 gap-y-1">
<span>1er jour : <strong class="text-slate-800">{{ item.price1Day|number_format(2, ',', ' ') }} €</strong></span>
@@ -115,7 +115,7 @@
</svg>
Informations de l'événement
</h3>
{# Billing Address #}
<div>
<div class="flex items-center gap-3 mb-6 pb-2 border-b border-slate-100">
@@ -124,7 +124,7 @@
</span>
<h4 class="font-bold text-lg text-slate-900">Adresse de facturation</h4>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="md:col-span-2">
<span class="block text-xs text-slate-500 uppercase tracking-wide mb-2">Adresse complète</span>
@@ -214,7 +214,10 @@
</div>
<div class="pt-6 border-t border-slate-100 mt-8 flex flex-col md:flex-row justify-center gap-4">
<a href="#" class="w-full md:w-auto px-8 py-4 bg-gray-200 text-gray-800 font-bold rounded-2xl shadow-sm hover:shadow-md hover:scale-[1.01] transition-all flex items-center justify-center gap-2 text-lg">
<a data-turbo="false" href="{{ path('reservation_generate_devis', {sessionId: session.uuid}) }}" class="w-full md:w-auto px-8 py-4 bg-gray-200 text-gray-800 font-bold rounded-2xl shadow-sm hover:shadow-md hover:scale-[1.01] transition-all flex items-center justify-center gap-2 text-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
Télécharger le devis
</a>
<a href="#" class="w-full md:w-auto px-8 py-4 bg-green-500 text-white font-bold rounded-2xl shadow-lg shadow-green-200 hover:shadow-xl hover:scale-[1.02] transition-all flex items-center justify-center gap-2 text-lg">