Add security remove paste text in editor
Fix design for mobile formule product
This commit is contained in:
@@ -3,7 +3,7 @@ export class CrmEditor extends HTMLTextAreaElement {
|
||||
this.style.display = 'none';
|
||||
|
||||
this.editorContainer = document.createElement('div');
|
||||
this.editorContainer.className = "w-full rounded-2xl overflow-hidden ring-1 ring-white/20 bg-slate-950/40 backdrop-blur-2xl shadow-2xl transition-all duration-300 focus-within:ring-blue-500/50 my-4";
|
||||
this.editorContainer.className = "w-full rounded-2xl overflow-hidden ring-1 ring-white/20 bg-slate-950/40 backdrop-blur-2xl shadow-2xl transition-all duration-300 focus-within:ring-blue-500/50 my-4 relative";
|
||||
|
||||
this.editorContainer.innerHTML = `
|
||||
<div class="flex items-center gap-1.5 p-3 border-b border-white/10 bg-black/40">
|
||||
@@ -37,6 +37,11 @@ export class CrmEditor extends HTMLTextAreaElement {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Toast de sécurité -->
|
||||
<div id="paste-error" class="absolute top-16 left-1/2 -translate-x-1/2 bg-red-500 text-white px-4 py-2 rounded-full text-xs font-bold shadow-xl opacity-0 transition-all duration-300 pointer-events-none z-50">
|
||||
Copie impossible
|
||||
</div>
|
||||
|
||||
<div id="wysiwyg-area"
|
||||
contenteditable="true"
|
||||
class="p-6 min-h-[300px] max-h-[600px] overflow-y-auto focus:outline-none bg-white text-slate-900 prose prose-slate max-w-none shadow-inner break-words w-full">
|
||||
@@ -55,12 +60,19 @@ export class CrmEditor extends HTMLTextAreaElement {
|
||||
this.wysiwyg = this.editorContainer.querySelector('#wysiwyg-area');
|
||||
this.colorInput = this.editorContainer.querySelector('#text-color-input');
|
||||
this.colorBar = this.editorContainer.querySelector('#color-bar');
|
||||
this.pasteError = this.editorContainer.querySelector('#paste-error');
|
||||
|
||||
this.initEvents();
|
||||
this.updateStats();
|
||||
}
|
||||
|
||||
initEvents() {
|
||||
// Sécurité anti-collage
|
||||
this.wysiwyg.addEventListener('paste', (e) => {
|
||||
e.preventDefault();
|
||||
this.showPasteWarning();
|
||||
});
|
||||
|
||||
// Boutons standards
|
||||
const buttons = this.editorContainer.querySelectorAll('button[data-cmd]');
|
||||
buttons.forEach(btn => {
|
||||
@@ -90,6 +102,16 @@ export class CrmEditor extends HTMLTextAreaElement {
|
||||
});
|
||||
}
|
||||
|
||||
showPasteWarning() {
|
||||
this.pasteError.classList.remove('opacity-0', 'scale-90');
|
||||
this.pasteError.classList.add('opacity-100', 'scale-100');
|
||||
|
||||
setTimeout(() => {
|
||||
this.pasteError.classList.add('opacity-0', 'scale-90');
|
||||
this.pasteError.classList.remove('opacity-100', 'scale-100');
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
updateStats() {
|
||||
const text = this.wysiwyg.innerText || "";
|
||||
const count = text.trim().length;
|
||||
|
||||
@@ -201,6 +201,7 @@ const initBackToTop = () => {
|
||||
const initMobileMenu = () => {
|
||||
const btn = document.getElementById('menu-button');
|
||||
const menu = document.getElementById('mobile-menu');
|
||||
console.log(btn,menu)
|
||||
if (!btn || !menu) return;
|
||||
btn.onclick = () => {
|
||||
const open = menu.classList.toggle('hidden');
|
||||
|
||||
@@ -178,26 +178,43 @@ class ContratController extends AbstractController
|
||||
$totalDays = $dateStart->diff($dateEnd)->days + 1;
|
||||
|
||||
$totalHT = 0;
|
||||
$totalTTC = 0;
|
||||
$totalCaution = 0;
|
||||
|
||||
// Calcul des lignes (Location dégressive)
|
||||
foreach ($contrat->getContratsLines() as $line) {
|
||||
if( isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true") {
|
||||
$totalTTC += $line->getPrice1DayHt() * 1.20;
|
||||
}
|
||||
$priceLine = $line->getPrice1DayHt();
|
||||
if ($totalDays > 1) {
|
||||
$priceLine += ($line->getPriceSupDayHt() * ($totalDays - 1));
|
||||
if( isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true") {
|
||||
$totalTTC += ($line->getPriceSupDayHt()*1.20 * ($totalDays - 1));
|
||||
}
|
||||
$priceLine += (($line->getPriceSupDayHt()) * ($totalDays - 1));
|
||||
}
|
||||
$totalHT += $priceLine;
|
||||
$totalCaution += $line->getCaution();
|
||||
}
|
||||
|
||||
// Ajout des options (Forfait)
|
||||
foreach ($contrat->getContratsOptions() as $option) {
|
||||
if( isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true") {
|
||||
$totalTTC += ($option->getPrice()*1.20);
|
||||
}
|
||||
$totalHT += $option->getPrice();
|
||||
}
|
||||
|
||||
// --- NOUVEAUX CALCULS ---
|
||||
$arrhes = $totalHT * 0.25; // 25%
|
||||
$solde = $totalHT;
|
||||
if( isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true") {
|
||||
$arrhes = $totalTTC * 0.25; // 25%
|
||||
} else {
|
||||
$arrhes = $totalHT * 0.25; // 25%
|
||||
}
|
||||
if( isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true") {
|
||||
$solde = $totalTTC;
|
||||
} else {
|
||||
$solde = $totalHT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @var ContratsPayments $paymentList */
|
||||
$paymentList =[];
|
||||
@@ -406,9 +423,11 @@ class ContratController extends AbstractController
|
||||
'totalHT' => $totalHT,
|
||||
'totalCaution' => $totalCaution,
|
||||
'arrhes' => $arrhes,
|
||||
'totalTTC' => $totalTTC,
|
||||
'paymentList' => $paymentList,
|
||||
'paymentCtaList' => $paymentCtaList,
|
||||
'paymentCaution' => $paymentCaution,
|
||||
'tvaEnabled' => isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true",
|
||||
'solde' => $solde,
|
||||
'signUrl' => (!$contrat->isSigned())?$client->getLinkSign($contrat->getSignID()):null,
|
||||
'signEvents' => ($contrat->getSignID() !="")?$client->eventSign($contrat):[],
|
||||
|
||||
@@ -142,6 +142,8 @@ class ReserverController extends AbstractController
|
||||
|
||||
return $this->render('revervation/catalogue.twig',[
|
||||
'products' => $productRepository->findAll(),
|
||||
'tvaEnabled' => isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true",
|
||||
|
||||
]);
|
||||
}
|
||||
#[Route('/formules', name: 'reservation_formules')]
|
||||
@@ -150,6 +152,8 @@ class ReserverController extends AbstractController
|
||||
|
||||
return $this->render('revervation/formules.twig',[
|
||||
'formules' => $formulesRepository->findBy(['isPublish'=>true],['updatedAt' => 'DESC']),
|
||||
'tvaEnabled' => isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true",
|
||||
|
||||
]);
|
||||
}
|
||||
#[Route('/formules/{slug}', name: 'reservation_formule_show')]
|
||||
@@ -166,7 +170,9 @@ class ReserverController extends AbstractController
|
||||
}
|
||||
|
||||
return $this->render('revervation/formule/show.twig',[
|
||||
'formule' => $formule
|
||||
'formule' => $formule,
|
||||
'tvaEnabled' => isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true",
|
||||
|
||||
]);
|
||||
}
|
||||
#[Route('/comment-reserver', name: 'reservation_workflow')]
|
||||
@@ -207,6 +213,7 @@ class ReserverController extends AbstractController
|
||||
|
||||
return $this->render('revervation/produit.twig', [
|
||||
'product' => $product,
|
||||
'tvaEnabled' => isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true",
|
||||
'otherProducts' => array_slice($otherProducts, 0, 4)
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -315,6 +315,11 @@ class ContratPdfService extends Fpdf
|
||||
|
||||
private function renderMainContent(): void
|
||||
{
|
||||
$tvaEnabled = isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true";
|
||||
// Configuration TVA
|
||||
$tvaRate = $tvaEnabled ? 0.20 : 0;
|
||||
$tvaLabel = $tvaEnabled ? '20%' : '0%';
|
||||
|
||||
// --- 1. BLOCS IDENTITÉ (LOCATAIRE & LIEU) ---
|
||||
$this->SetY(55);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
@@ -368,10 +373,11 @@ class ContratPdfService extends Fpdf
|
||||
$this->SetFont('Arial', 'B', 9);
|
||||
$this->SetFillColor(37, 99, 235);
|
||||
$this->SetTextColor(255, 255, 255);
|
||||
$this->Cell(100, 8, $this->clean("Désignation"), 1, 0, 'L', true);
|
||||
$this->Cell(30, 8, $this->clean("Tarif HT"), 1, 0, 'C', true);
|
||||
$this->Cell(30, 8, $this->clean("Durée"), 1, 0, 'C', true);
|
||||
$this->Cell(30, 8, $this->clean("Sous-Total"), 1, 1, 'C', true);
|
||||
$this->Cell(85, 8, $this->clean("Désignation"), 1, 0, 'L', true);
|
||||
$this->Cell(25, 8, $this->clean("Tarif HT"), 1, 0, 'C', true);
|
||||
$this->Cell(15, 8, $this->clean("Durée"), 1, 0, 'C', true);
|
||||
$this->Cell(25, 8, $this->clean("TVA"), 1, 0, 'C', true);
|
||||
$this->Cell(40, 8, $this->clean("Total HT"), 1, 1, 'C', true);
|
||||
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
$this->SetFont('Arial', '', 9);
|
||||
@@ -382,34 +388,52 @@ class ContratPdfService extends Fpdf
|
||||
$totalHt += $sousTotal;
|
||||
$totalCaution += $line->getCaution();
|
||||
|
||||
$this->Cell(100, 7, $this->clean($line->getName()), 1, 0, 'L');
|
||||
$this->Cell(30, 7, number_format($line->getPrice1DayHt(), 2) . $this->euro(), 1, 0, 'C');
|
||||
$this->Cell(30, 7, $nbJoursTotal . " j.", 1, 0, 'C');
|
||||
$this->Cell(30, 7, number_format($sousTotal, 2) . $this->euro(), 1, 1, 'R');
|
||||
$this->Cell(85, 7, $this->clean($line->getName()), 1, 0, 'L');
|
||||
$this->Cell(25, 7, number_format($line->getPrice1DayHt(), 2, ',', ' ') . $this->euro(), 1, 0, 'R');
|
||||
$this->Cell(15, 7, $nbJoursTotal . " j.", 1, 0, 'C');
|
||||
$this->Cell(25, 7, $tvaLabel, 1, 0, 'C');
|
||||
$this->Cell(40, 7, number_format($sousTotal, 2, ',', ' ') . $this->euro(), 1, 1, 'R');
|
||||
}
|
||||
|
||||
foreach ($this->contrats->getContratsOptions() as $opt) {
|
||||
$totalHt += $opt->getPrice();
|
||||
$this->SetFillColor(245, 245, 245);
|
||||
$this->Cell(100, 7, $this->clean("[Option] " . $opt->getName()), 1, 0, 'L', true);
|
||||
$this->Cell(30, 7, number_format($opt->getPrice(), 2) . $this->euro(), 1, 0, 'C', true);
|
||||
$this->Cell(30, 7, "Forfait", 1, 0, 'C', true);
|
||||
$this->Cell(30, 7, number_format($opt->getPrice(), 2) . $this->euro(), 1, 1, 'R', true);
|
||||
$this->Cell(85, 7, $this->clean("[Option] " . $opt->getName()), 1, 0, 'L', true);
|
||||
$this->Cell(25, 7, number_format($opt->getPrice(), 2, ',', ' ') . $this->euro(), 1, 0, 'R', true);
|
||||
$this->Cell(15, 7, "Forfait", 1, 0, 'C', true);
|
||||
$this->Cell(25, 7, $tvaLabel, 1, 0, 'C', true);
|
||||
$this->Cell(40, 7, number_format($opt->getPrice(), 2, ',', ' ') . $this->euro(), 1, 1, 'R', true);
|
||||
}
|
||||
|
||||
$this->Ln(5);
|
||||
|
||||
// --- 4. RÉCAPITULATIF FINANCIER ---
|
||||
$arrhes = $totalHt * 0.25;
|
||||
$amountTva = $totalHt * $tvaRate;
|
||||
$totalTtc = $totalHt + $amountTva;
|
||||
$arrhes = $totalTtc * 0.25; // Arrhes calculées sur le TTC
|
||||
|
||||
$this->Ln(5);
|
||||
$this->SetX(110);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(60, 9, $this->clean("TOTAL GÉNÉRAL HT"), 1, 0, 'L');
|
||||
$this->Cell(30, 9, number_format($totalHt, 2) . $this->euro(), 1, 1, 'R');
|
||||
$this->SetFont('Arial', 'B', 9);
|
||||
$this->Cell(50, 7, $this->clean("TOTAL HT"), 'B', 0, 'L');
|
||||
$this->Cell(40, 7, number_format($totalHt, 2, ',', ' ') . $this->euro(), 'B', 1, 'R');
|
||||
|
||||
if($tvaEnabled) {
|
||||
$this->SetX(110);
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$this->Cell(50, 7, $this->clean("TVA (" . $tvaLabel . ")"), 'B', 0, 'L');
|
||||
$this->Cell(40, 7, number_format($amountTva, 2, ',', ' ') . $this->euro(), 'B', 1, 'R');
|
||||
}
|
||||
|
||||
$this->SetX(110);
|
||||
$this->SetTextColor(37, 99, 235);
|
||||
$this->Cell(60, 9, $this->clean("ARRHES À VERSER (25%)"), 1, 0, 'L');
|
||||
$this->Cell(30, 9, number_format($arrhes, 2) . $this->euro(), 1, 1, 'R');
|
||||
$this->SetFont('Arial', 'B', 11);
|
||||
$this->SetFillColor(37, 99, 235); $this->SetTextColor(255, 255, 255);
|
||||
$this->Cell(50, 9, $this->clean(" TOTAL TTC"), 0, 0, 'L', true);
|
||||
$this->Cell(40, 9, number_format($totalTtc, 2, ',', ' ') . $this->euro() . " ", 0, 1, 'R', true);
|
||||
|
||||
$this->Ln(2);
|
||||
$this->SetX(110);
|
||||
$this->SetTextColor(37, 99, 235); $this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(50, 9, $this->clean("ARRHES À VERSER (25%)"), 'B', 0, 'L');
|
||||
$this->Cell(40, 9, number_format($arrhes, 2, ',', ' ') . $this->euro(), 'B', 1, 'R');
|
||||
|
||||
$this->SetX(110);
|
||||
$this->SetFont('Arial', 'I', 7); $this->SetTextColor(100, 100, 100);
|
||||
@@ -418,8 +442,15 @@ class ContratPdfService extends Fpdf
|
||||
$this->Ln(2);
|
||||
$this->SetX(110);
|
||||
$this->SetFont('Arial', 'B', 10); $this->SetTextColor(220, 38, 38);
|
||||
$this->Cell(60, 9, $this->clean("CAUTION DE GARANTIE"), 1, 0, 'L');
|
||||
$this->Cell(30, 9, number_format($totalCaution, 2) . $this->euro(), 1, 1, 'R');
|
||||
$this->Cell(50, 9, $this->clean("CAUTION DE GARANTIE"), 'B', 0, 'L');
|
||||
$this->Cell(40, 9, number_format($totalCaution, 2, ',', ' ') . $this->euro(), 'B', 1, 'R');
|
||||
|
||||
// Mentions légales
|
||||
if (!$tvaEnabled) {
|
||||
$this->Ln(5);
|
||||
$this->SetFont('Arial', 'I', 8); $this->SetTextColor(0, 0, 0);
|
||||
$this->Cell(0, 5, $this->clean('TVA non applicable, art. 293 B du CGI'), 0, 1, 'R');
|
||||
}
|
||||
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
}
|
||||
|
||||
@@ -87,6 +87,11 @@ class DevisPdfService extends Fpdf
|
||||
{
|
||||
$this->AddPage();
|
||||
|
||||
// Vérification de l'activation de la TVA
|
||||
$tvaEnabled = isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true";
|
||||
$tvaRate = $tvaEnabled ? 0.20 : 0;
|
||||
$tvaLabel = $tvaEnabled ? '20%' : '0%';
|
||||
|
||||
// --- BLOC CLIENT ---
|
||||
$customer = $this->devis->getCustomer();
|
||||
$this->SetY(55);
|
||||
@@ -107,11 +112,11 @@ class DevisPdfService extends Fpdf
|
||||
$this->renderAddressBlock('ADRESSE DE FACTURATION', $this->devis->getBillAddress(), 'L', 10, $yAddress);
|
||||
$this->renderAddressBlock('ADRESSE DE PRESTATION', $this->devis->getAddressShip(), 'R', 110, $yAddress);
|
||||
|
||||
// --- AJOUT : BLOC DATES DE L'ÉVÉNEMENT ---
|
||||
// --- BLOC DATES DE L'ÉVÉNEMENT ---
|
||||
$this->SetY($yAddress + 25);
|
||||
$this->SetDrawColor(230, 230, 230);
|
||||
$this->SetFillColor(250, 250, 250);
|
||||
$this->Rect(10, $this->GetY(), 190, 12, 2, 'DF'); // Petit encadré gris clair
|
||||
$this->Rect(10, $this->GetY(), 190, 12, 2, 'DF');
|
||||
|
||||
$this->SetY($this->GetY() + 3.5);
|
||||
$this->SetFont('Arial', 'B', 9);
|
||||
@@ -123,7 +128,6 @@ class DevisPdfService extends Fpdf
|
||||
|
||||
$dateStart = $this->devis->getStartAt() ? $this->devis->getStartAt()->format('d/m/Y à H:i') : 'N/C';
|
||||
$dateEnd = $this->devis->getEndAt() ? $this->devis->getEndAt()->format('d/m/Y à H:i') : 'N/C';
|
||||
|
||||
$this->Cell(0, 5, $this->clean("Du $dateStart au $dateEnd"), 0, 1, 'L');
|
||||
|
||||
$this->SetY($this->GetY() + 8);
|
||||
@@ -146,81 +150,65 @@ class DevisPdfService extends Fpdf
|
||||
foreach ($this->devis->getDevisLines() as $line) {
|
||||
$p = $this->productRepository->findOneBy(['name'=>$line->getProduct()]);
|
||||
if($p instanceof Product) {
|
||||
$totalCaution = $totalCaution + $p->getCaution();
|
||||
$totalCaution += $p->getCaution();
|
||||
}
|
||||
|
||||
$price1Day = $line->getPriceHt();
|
||||
$priceSupHT = $line->getPriceHtSup() ?? 0;
|
||||
$nbDays = $line->getDay();
|
||||
|
||||
// Calcul : J1 + (Jours restants * Prix HT Sup)
|
||||
$lineTotalHT = $price1Day + (max(0, $nbDays - 1) * $priceSupHT);
|
||||
$totalHT += $lineTotalHT;
|
||||
|
||||
$productName = $line->getProduct();
|
||||
$ref= "";
|
||||
if($p instanceof Product) {
|
||||
$ref = $totalCaution + $p->getRef();
|
||||
}
|
||||
$ref = ($p instanceof Product) ? $p->getRef() : "";
|
||||
|
||||
$currentY = $this->GetY();
|
||||
|
||||
// --- COLONNE DÉSIGNATION (NOM + REF / DATES) ---
|
||||
// Ligne Produit
|
||||
$this->SetXY(10, $currentY+2.5);
|
||||
$this->SetFont('Arial', 'B', 8);
|
||||
$this->Cell(70, 5, $this->clean($productName . ' (Ref: ' . $ref . ')'), 0, 0, 'L');
|
||||
// --- COLONNES NUMÉRIQUES ---
|
||||
$this->Cell(70, 5, $this->clean($productName . ($ref ? ' (Ref: ' . $ref . ')' : '')), 0, 0, 'L');
|
||||
|
||||
$this->SetXY(80, $currentY);
|
||||
$this->SetFont('Arial', '', 8);
|
||||
$this->Cell(15, 10, $nbDays, 'LRB', 0, 'C');
|
||||
$this->Cell(25, 10, number_format($price1Day, 2, ',', ' ') . $this->euro(), 'RB', 0, 'R');
|
||||
$this->Cell(25, 10, number_format($priceSupHT, 2, ',', ' ') . $this->euro(), 'RB', 0, 'R');
|
||||
$this->Cell(15, 10, '0%', 'RB', 0, 'C');
|
||||
$this->Cell(15, 10, $tvaLabel, 'RB', 0, 'C');
|
||||
$this->Cell(40, 10, number_format($lineTotalHT, 2, ',', ' ') . $this->euro(), 'RB', 1, 'R');
|
||||
|
||||
// Fermeture de la bordure pour la désignation
|
||||
$this->Line(10, $currentY, 10, $currentY + 10);
|
||||
$this->Line(10, $currentY + 10, 80, $currentY + 10);
|
||||
|
||||
if ($this->GetY() > 250) {
|
||||
$this->AddPage();
|
||||
}
|
||||
if ($this->GetY() > 250) { $this->AddPage(); }
|
||||
}
|
||||
|
||||
//options
|
||||
// --- 2. TABLEAU DES OPTIONS (SÉPARÉ) ---
|
||||
// --- TABLEAU DES OPTIONS ---
|
||||
if (count($this->devis->getDevisOptions()) > 0) {
|
||||
$this->Ln(5); // Espace entre les deux tableaux
|
||||
|
||||
// En-tête du tableau des options
|
||||
$this->Ln(5);
|
||||
$this->SetFont('Arial', 'B', 8);
|
||||
$this->SetFillColor(230, 235, 245); // Bleu très léger pour différencier
|
||||
$this->Cell(150, 8, $this->clean('Options & Services additionnels'), 1, 0, 'L', true);
|
||||
$this->SetFillColor(230, 235, 245);
|
||||
$this->Cell(135, 8, $this->clean('Options & Services additionnels'), 1, 0, 'L', true);
|
||||
$this->Cell(15, 8, $this->clean('TVA'), 1, 0, 'C', true);
|
||||
$this->Cell(40, 8, $this->clean('Total HT'), 1, 1, 'R', true);
|
||||
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
|
||||
foreach ($this->devis->getDevisOptions() as $devisOption) {
|
||||
$option = $devisOption->getOption();
|
||||
$priceHT = $devisOption->getPriceHt();
|
||||
$totalHT += $priceHT; // On l'ajoute au total général
|
||||
$totalHT += $priceHT;
|
||||
|
||||
$currentY = $this->GetY();
|
||||
|
||||
// Colonne Désignation
|
||||
$this->SetXY(10, $currentY);
|
||||
$this->Cell(150, 8, $this->clean($option." - ".$devisOption->getDetails()), 'LRB', 0, 'L');
|
||||
|
||||
// Colonne Prix
|
||||
$this->Cell(135, 8, $this->clean($devisOption->getOption()." - ".$devisOption->getDetails()), 'LRB', 0, 'L');
|
||||
$this->Cell(15, 8, $tvaLabel, 'RB', 0, 'C');
|
||||
$this->Cell(40, 8, number_format($priceHT, 2, ',', ' ') . $this->euro(), 'RB', 1, 'R');
|
||||
|
||||
if ($this->GetY() > 260) {
|
||||
$this->AddPage();
|
||||
}
|
||||
if ($this->GetY() > 260) { $this->AddPage(); }
|
||||
}
|
||||
}
|
||||
|
||||
// --- CALCULS FINAUX ---
|
||||
$amountTVA = $totalHT * $tvaRate;
|
||||
$totalTTC = $totalHT + $amountTVA;
|
||||
|
||||
// --- BLOC TOTAUX ---
|
||||
$this->Ln(5);
|
||||
@@ -231,23 +219,21 @@ class DevisPdfService extends Fpdf
|
||||
|
||||
$this->Cell(130);
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$this->Cell(30, 8, 'TVA (0%)', 0, 0, 'L');
|
||||
$this->Cell(30, 8, '0,00' . $this->euro(), 0, 1, 'R');
|
||||
$this->Cell(30, 8, 'TVA ('.$tvaLabel.')', 0, 0, 'L');
|
||||
$this->Cell(30, 8, number_format($amountTVA, 2, ',', ' ') . $this->euro(), 0, 1, 'R');
|
||||
|
||||
$this->Ln(2);
|
||||
$this->Cell(130);
|
||||
$this->SetFont('Arial', 'B', 11);
|
||||
$this->SetFillColor(37, 99, 235); $this->SetTextColor(255, 255, 255);
|
||||
$this->Cell(30, 10, ' TOTAL TTC', 0, 0, 'L', true);
|
||||
$this->Cell(30, 10, number_format($totalHT, 2, ',', ' ') . $this->euro() . ' ', 0, 1, 'R', true);
|
||||
$this->Cell(30, 10, number_format($totalTTC, 2, ',', ' ') . $this->euro() . ' ', 0, 1, 'R', true);
|
||||
|
||||
// --- AJOUT : TOTAL DE LA CAUTION ---
|
||||
// --- CAUTION ---
|
||||
$this->Ln(2);
|
||||
$this->SetTextColor(80, 80, 80);
|
||||
$this->Cell(130);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->SetTextColor(80, 80, 80); // Gris pour distinguer de la vente
|
||||
$this->SetDrawColor(200, 200, 200);
|
||||
// On crée un petit encadré pour la caution
|
||||
$this->Cell(30, 8, ' TOTAL CAUTION', 'B', 0, 'L');
|
||||
$this->Cell(30, 8, number_format($totalCaution, 2, ',', ' ') . $this->euro(), 'B', 1, 'R');
|
||||
|
||||
@@ -255,11 +241,13 @@ class DevisPdfService extends Fpdf
|
||||
$this->Cell(130);
|
||||
$this->Cell(60, 4, $this->clean('(Caution non encaissée, voir CGV Article 6)'), 0, 1, 'R');
|
||||
|
||||
// Mention légale auto-entrepreneur
|
||||
$this->Ln(5);
|
||||
$this->SetTextColor(80, 80, 80);
|
||||
$this->SetFont('Arial', 'I', 8);
|
||||
$this->Cell(0, 5, $this->clean('TVA non applicable, art. 293 B du CGI'), 0, 1, 'R');
|
||||
// Mention légale auto-entrepreneur (uniquement si TVA désactivée)
|
||||
if (!$tvaEnabled) {
|
||||
$this->Ln(5);
|
||||
$this->SetTextColor(80, 80, 80);
|
||||
$this->SetFont('Arial', 'I', 8);
|
||||
$this->Cell(0, 5, $this->clean('TVA non applicable, art. 293 B du CGI'), 0, 1, 'R');
|
||||
}
|
||||
|
||||
$this->addCGV();
|
||||
$this->addSignaturePage();
|
||||
|
||||
@@ -51,26 +51,45 @@ class StripeExtension extends AbstractExtension
|
||||
public function totalQuoto(Devis $devis): float
|
||||
{
|
||||
$totalHT = 0;
|
||||
$totalCautions = 0;
|
||||
$tvaEnabled = isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true";
|
||||
$tvaRate = 0.20; // 20% de TVA
|
||||
|
||||
// 1. Calcul des lignes de produits (Location)
|
||||
// 1. Calcul du montant HT des lignes et des cautions
|
||||
foreach ($devis->getDevisLines() as $line) {
|
||||
$price1Day = $line->getPriceHt() ?? 0;
|
||||
$priceSupHT = $line->getPriceHtSup() ?? 0;
|
||||
$nbDays = $line->getDay() ?? 1;
|
||||
$price1Day = (float) ($line->getPriceHt() ?? 0);
|
||||
$priceSupHT = (float) ($line->getPriceHtSup() ?? 0);
|
||||
$nbDays = (int) ($line->getDay() ?? 1);
|
||||
|
||||
// Calcul : J1 + (Jours supplémentaires * Prix Sup)
|
||||
$lineTotalHT = $price1Day + (max(0, $nbDays - 1) * $priceSupHT);
|
||||
// Calcul HT de la ligne
|
||||
$lineHT = $price1Day + (max(0, $nbDays - 1) * $priceSupHT);
|
||||
$totalHT += $lineHT;
|
||||
|
||||
$totalHT += $lineTotalHT;
|
||||
// Récupération de la caution liée au produit
|
||||
$product = $this->em->getRepository(Product::class)->findOneBy([
|
||||
'name' => $line->getProduct()
|
||||
]);
|
||||
|
||||
if ($product) {
|
||||
$totalCautions += (float) ($product->getCaution() ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Calcul des options additionnelles
|
||||
/** @var DevisOptions $devisOption */
|
||||
foreach ($devis->getDevisOptions() as $devisOption) {
|
||||
$totalHT += $devisOption->getPriceHt() ?? 0;
|
||||
// 2. Ajout des options au montant HT
|
||||
foreach ($devis->getDevisOptions() as $option) {
|
||||
$totalHT += (float) ($option->getPriceHt() ?? 0);
|
||||
}
|
||||
|
||||
return (float) $totalHT;
|
||||
// 3. Application de la TVA si activée
|
||||
$montantPrestation = $totalHT;
|
||||
if ($tvaEnabled) {
|
||||
$montantPrestation = $totalHT * (1 + $tvaRate);
|
||||
}
|
||||
|
||||
// 4. Total final = Prestation (HT ou TTC) + Cautions
|
||||
$totalFinal = $montantPrestation + $totalCautions;
|
||||
|
||||
return round($totalFinal, 2);
|
||||
}
|
||||
public function totalContratAccompte(Contrats $devis): float
|
||||
{
|
||||
@@ -81,23 +100,25 @@ class StripeExtension extends AbstractExtension
|
||||
$totalHT = $totalHT + $line->getCaution();
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (float) $totalHT;
|
||||
}
|
||||
public function totalQuotoAccompte(Devis $devis): float
|
||||
{
|
||||
$totalHT = 0;
|
||||
$totalCautions = 0;
|
||||
|
||||
// 1. Calcul des lignes de produits (Location)
|
||||
foreach ($devis->getDevisLines() as $line) {
|
||||
$p = $this->em->getRepository(Product::class)->findOneBy(['name'=>$line->getProduct()]);
|
||||
$totalHT = $totalHT + $p->getCaution();
|
||||
// On récupère le produit en base via le nom stocké dans la ligne
|
||||
$product = $this->em->getRepository(Product::class)->findOneBy([
|
||||
'name' => $line->getProduct()
|
||||
]);
|
||||
|
||||
if ($product) {
|
||||
// Additionne la caution du produit (valeur par défaut 0 si nulle)
|
||||
$totalCautions += (float) ($product->getCaution() ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (float) $totalHT;
|
||||
return round($totalCautions, 2);
|
||||
}
|
||||
|
||||
public function totalPayContrat(Contrats $contrats)
|
||||
@@ -132,15 +153,15 @@ class StripeExtension extends AbstractExtension
|
||||
|
||||
// Calcul du nombre de jours (minimum 1 jour)
|
||||
$nbDays = 1;
|
||||
if ($dateDebut && $dateFin) {
|
||||
if ($dateDebut instanceof \DateTimeInterface && $dateFin instanceof \DateTimeInterface) {
|
||||
$diff = $dateDebut->diff($dateFin);
|
||||
$nbDays = $diff->days + 1; // +1 pour inclure le jour de départ
|
||||
}
|
||||
|
||||
// 1. Calcul des lignes de produits (Location)
|
||||
foreach ($devis->getContratsLines() as $line) {
|
||||
$price1Day = $line->getPrice1DayHt() ?? 0;
|
||||
$priceSupHT = $line->getPriceSupDayHt() ?? 0;
|
||||
$price1Day = (float) ($line->getPrice1DayHt() ?? 0);
|
||||
$priceSupHT = (float) ($line->getPriceSupDayHt() ?? 0);
|
||||
|
||||
// Calcul : J1 + (Jours restants * Prix Sup)
|
||||
$lineTotalHT = $price1Day + (max(0, $nbDays - 1) * $priceSupHT);
|
||||
@@ -149,7 +170,16 @@ class StripeExtension extends AbstractExtension
|
||||
|
||||
// 2. Calcul des options additionnelles
|
||||
foreach ($devis->getContratsOptions() as $devisOption) {
|
||||
$totalHT += $devisOption->getPrice() ?? 0;
|
||||
$totalHT += (float) ($devisOption->getPrice() ?? 0);
|
||||
}
|
||||
|
||||
// 3. Gestion de la TVA (20%)
|
||||
// Vérification de l'activation via la variable d'environnement
|
||||
$tvaEnabled = isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true";
|
||||
|
||||
if ($tvaEnabled) {
|
||||
$tauxTVA = 1.20; // Taux de 20%
|
||||
return (float) ($totalHT * $tauxTVA);
|
||||
}
|
||||
|
||||
return (float) $totalHT;
|
||||
|
||||
@@ -89,13 +89,26 @@
|
||||
<table class="w-full text-left">
|
||||
<tbody class="divide-y divide-slate-50">
|
||||
{% for line in contrat.contratsLines %}
|
||||
{% set priceLine = line.price1DayHt + (line.priceSupDayHt * (days - 1)) %}
|
||||
{% if tvaEnabled %}
|
||||
{% set priceLine = (line.price1DayHt*1.20) + ((line.priceSupDayHt*1.20) * (days - 1)) %}
|
||||
{% else %}
|
||||
{% set priceLine = line.price1DayHt + (line.priceSupDayHt * (days - 1)) %}
|
||||
{% endif %}
|
||||
<tr class="hover:bg-slate-50/30 transition-colors">
|
||||
<td class="px-8 py-6">
|
||||
<p class="font-black text-slate-900 uppercase text-sm leading-tight">{{ line.name }}</p>
|
||||
{% if tvaEnabled %}
|
||||
<p class="text-[10px] text-slate-400 font-bold italic mt-1 uppercase">Prix 1 Jour : {{ (line.price1DayHt*1.20)|number_format(0, ',', ' ') }}€ TTC</p>
|
||||
<p class="text-[10px] text-slate-400 font-bold italic mt-1 uppercase">Prix Jour suplémentaire : {{ (line.priceSupDayHt*1.20)|number_format(0, ',', ' ') }}€ TTC</p>
|
||||
{% else %}
|
||||
<p class="text-[10px] text-slate-400 font-bold italic mt-1 uppercase">Prix 1 Jour : {{ line.price1DayHt|number_format(0, ',', ' ') }}€ HT</p>
|
||||
<p class="text-[10px] text-slate-400 font-bold italic mt-1 uppercase">Prix Jour suplémentaire : {{ line.priceSupDayHt|number_format(0, ',', ' ') }}€ HT</p>
|
||||
{% endif %}
|
||||
|
||||
<p class="text-[10px] text-slate-400 font-bold italic mt-1 uppercase">Caution : {{ line.caution|number_format(0, ',', ' ') }}€</p>
|
||||
</td>
|
||||
<td class="px-8 py-6 text-right font-black text-slate-900 italic text-lg">{{ priceLine|number_format(2, ',', ' ') }}€</td>
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% for line in contrat.contratsOptions %}
|
||||
@@ -104,7 +117,11 @@
|
||||
<p class="font-bold text-blue-600 uppercase text-xs italic tracking-widest mb-1">Option</p>
|
||||
<p class="font-black text-slate-900 uppercase text-sm leading-tight">{{ line.name }}</p>
|
||||
</td>
|
||||
<td class="px-8 py-6 text-right font-black text-slate-900 italic text-lg">{{ line.price|number_format(2, ',', ' ') }}€</td>
|
||||
{% if tvaEnabled %}
|
||||
<td class="px-8 py-6 text-right font-black text-slate-900 italic text-lg">{{ (line.price*1.20)|number_format(2, ',', ' ') }}€</td>
|
||||
{% else %}
|
||||
<td class="px-8 py-6 text-right font-black text-slate-900 italic text-lg">{{ line.price|number_format(2, ',', ' ') }}€</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -145,7 +162,22 @@
|
||||
</div>
|
||||
<p class="text-3xl font-black text-slate-900 italic tracking-tighter">{{ totalHT|number_format(2, ',', ' ') }}€</p>
|
||||
</div>
|
||||
|
||||
{% if tvaEnabled %}
|
||||
<div class="bg-white rounded-[1.5rem] p-6 border border-slate-100 shadow-sm flex justify-between items-center">
|
||||
<div>
|
||||
<p class="text-[9px] font-black text-slate-400 uppercase tracking-widest">Total Prestations</p>
|
||||
<p class="text-xs font-bold text-slate-400 italic">Total TVA</p>
|
||||
</div>
|
||||
<p class="text-3xl font-black text-slate-900 italic tracking-tighter">{{ (totalTTC-totalHT)|number_format(2, ',', ' ') }}€</p>
|
||||
</div>
|
||||
<div class="bg-white rounded-[1.5rem] p-6 border border-slate-100 shadow-sm flex justify-between items-center">
|
||||
<div>
|
||||
<p class="text-[9px] font-black text-slate-400 uppercase tracking-widest">Total Prestations</p>
|
||||
<p class="text-xs font-bold text-slate-400 italic">Total TTC</p>
|
||||
</div>
|
||||
<p class="text-3xl font-black text-slate-900 italic tracking-tighter">{{ totalTTC|number_format(2, ',', ' ') }}€</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{# SOLDE FINAL - Bloc Principal avec saisie du montant #}
|
||||
<div class="bg-slate-900 rounded-[2rem] p-8 text-white shadow-xl shadow-slate-200 relative overflow-hidden">
|
||||
<div class="absolute top-0 right-0 -mt-4 -mr-4 w-24 h-24 bg-blue-600/10 rounded-full blur-2xl"></div>
|
||||
|
||||
@@ -110,6 +110,22 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{# Menu Mobile #}
|
||||
<div id="mobile-menu" class="hidden md:hidden bg-white border-t border-gray-100 shadow-xl">
|
||||
<div class="px-4 pt-2 pb-6 space-y-2">
|
||||
<a href="{{ path('reservation') }}" class="block px-3 py-2 text-base font-medium text-gray-700 hover:bg-gray-50 rounded-xl">{{ 'Accueil'|trans }}</a>
|
||||
<a href="{{ path('reservation_catalogue') }}" class="block px-3 py-2 text-base font-medium text-gray-700 hover:bg-gray-50 rounded-xl">{{ 'Nos structures'|trans }}</a>
|
||||
<a href="{{ path('reservation_formules') }}" class="block px-3 py-2 text-base font-medium text-gray-700 hover:bg-gray-50 rounded-xl">{{ 'Nos Formules'|trans }}</a>
|
||||
<a target="_blank" href="/provider/Catalogue.pdf" class="block px-3 py-2 text-base font-medium text-gray-700 hover:bg-gray-50 rounded-xl">{{ 'Catalogue'|trans }}</a>
|
||||
<a href="{{ path('reservation_workflow') }}" class="block px-3 py-2 text-base font-medium text-gray-700 hover:bg-gray-50 rounded-xl">{{ 'Comment reserver'|trans }}</a>
|
||||
<a href="{{ path('reservation_search') }}" class="block px-3 py-2 text-base font-medium text-gray-700 hover:bg-gray-50 rounded-xl">{{ 'Rechercher'|trans }}</a>
|
||||
<div class="pt-4 border-t border-gray-50">
|
||||
<a href="tel:0614172447" class="block px-3 py-3 text-center bg-blue-600 text-white rounded-xl font-bold">
|
||||
{{ 'Appeler le'|trans }} 06 14 17 24 47
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{# --- MESSAGES FLASH --- #}
|
||||
|
||||
@@ -64,44 +64,63 @@
|
||||
<div class="product-item group transition-all duration-500" data-category="{{ product.category|lower }}">
|
||||
<a href="{{ path('reservation_product_show', {id: product.slug}) }}" class="block">
|
||||
|
||||
<div class="relative overflow-hidden rounded-[1rem] bg-slate-100 aspect-square mb-6 shadow-sm group-hover:shadow-2xl transition-all duration-700">
|
||||
<div class="relative overflow-hidden rounded-[1.25rem] md:rounded-[1rem] bg-slate-100 aspect-square mb-4 md:mb-6 shadow-sm group-hover:shadow-2xl transition-all duration-700">
|
||||
{% if product.imageName %}
|
||||
<img src="{{ vich_uploader_asset(product,'imageFile') | imagine_filter('webp') }}" alt="{{ product.name }}" class="w-full h-full object-cover transform group-hover:scale-110 transition-transform duration-1000">
|
||||
{% else %}
|
||||
<img src="{{ asset('provider/images/favicon.png') | imagine_filter('webp') }}" alt="{{ product.name }}" class="w-full h-full object-cover opacity-50 transform group-hover:scale-110 transition-transform duration-1000">
|
||||
{% endif %}
|
||||
|
||||
{# PRIX AVEC CLÉ #}
|
||||
<div class="absolute top-5 right-5 bg-white/95 backdrop-blur-md px-4 py-2 rounded-2xl shadow-md border border-slate-100">
|
||||
<p class="text-slate-900 font-black text-sm italic leading-none">
|
||||
{{ 'catalog.product.from'|trans }} {{ product.priceDay|format_currency('EUR') }}
|
||||
{% if product.category == "barnums" %}
|
||||
/ {{ 'catalog.product.weekend'|trans }}
|
||||
{# PRIX FLOTTANT (Ultra-minimal sur mobile) #}
|
||||
<div class="absolute top-3 right-3 md:top-5 md:right-5 bg-white/95 backdrop-blur-md px-3 py-1.5 md:px-4 md:py-2 rounded-xl md:rounded-2xl shadow-md border border-slate-100">
|
||||
<p class="text-slate-900 font-black text-xs md:text-sm italic leading-none whitespace-nowrap">
|
||||
{# "Dès" masqué sur mobile pour gagner de la place #}
|
||||
<span class="hidden md:inline text-[10px] opacity-60 not-italic mr-1">{{ 'catalog.product.from'|trans }}</span>
|
||||
|
||||
{% if tvaEnabled %}
|
||||
{{ (product.priceDay*1.20)|format_currency('EUR') }} <span class="text-[8px] md:text-[10px]">TTC</span>
|
||||
{% else %}
|
||||
/ {{ 'catalog.product.day'|trans }}
|
||||
{{ product.priceDay|format_currency('EUR') }}
|
||||
{% endif %}
|
||||
|
||||
{# Suffixe masqué sur mobile #}
|
||||
<span class="hidden md:inline text-slate-400">
|
||||
{% if product.category == "barnums" %} / {{ 'catalog.product.weekend'|trans }}{% else %} / {{ 'catalog.product.day'|trans }}{% endif %}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-2">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<span class="text-[10px] font-black text-blue-700 uppercase tracking-[0.2em] italic">
|
||||
{{ product.category }} {# Souvent laissé brut car vient de la base #}
|
||||
</span>
|
||||
<span class="w-4 h-[1px] bg-slate-300"></span>
|
||||
<span class="text-[9px] font-bold text-slate-500 uppercase tracking-widest">
|
||||
{{ 'catalog.product.ref'|trans }} {{ product.ref }}
|
||||
</span>
|
||||
<div class="px-1 md:px-2">
|
||||
{# META DATA (Avec mention de durée sur mobile) #}
|
||||
<div class="flex flex-wrap items-center gap-x-2 gap-y-1 mb-2">
|
||||
<span class="text-[9px] md:text-[10px] font-black text-blue-700 uppercase tracking-[0.15em] md:tracking-[0.2em] italic">
|
||||
{{ product.category }}
|
||||
</span>
|
||||
|
||||
<span class="w-3 h-[1px] bg-slate-300"></span>
|
||||
|
||||
<span class="text-[9px] font-bold text-slate-500 uppercase tracking-widest flex items-center gap-1">
|
||||
{{ 'catalog.product.ref'|trans }} {{ product.ref }}
|
||||
|
||||
{# Mention de durée visible UNIQUEMENT sur mobile #}
|
||||
<span class="md:hidden inline-flex items-center before:content-['•'] before:mr-1 before:text-slate-300 text-slate-400 font-medium lowercase">
|
||||
{% if product.category == "barnums" %}
|
||||
{{ 'catalog.product.weekend'|trans }}
|
||||
{% else %}
|
||||
{{ 'catalog.product.day'|trans }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3 class="text-2xl font-black text-slate-900 uppercase tracking-tighter leading-[0.95] group-hover:text-[#f39e36] transition-colors line-clamp-2">
|
||||
<h3 class="text-lg md:text-2xl font-black text-slate-900 uppercase tracking-tighter leading-[0.95] group-hover:text-[#f39e36] transition-colors line-clamp-2">
|
||||
{{ product.name }}
|
||||
</h3>
|
||||
|
||||
<div class="mt-5 flex items-center gap-2 text-[11px] font-black uppercase tracking-[0.15em] text-slate-600 group-hover:text-[#f39e36] transition-colors">
|
||||
<div class="mt-4 md:mt-5 flex items-center gap-2 text-[10px] md:text-[11px] font-black uppercase tracking-[0.15em] text-slate-600 group-hover:text-[#f39e36] transition-colors">
|
||||
<span>{{ 'catalog.product.view_more'|trans }}</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 transform group-hover:translate-x-2 transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5 md:h-4 md:w-4 transform group-hover:translate-x-2 transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M17 8l4 4m0 0l-4-4m4-4H3" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
@@ -64,19 +64,44 @@
|
||||
</div>
|
||||
|
||||
{# Grille de Tarifs Dégressifs #}
|
||||
<div class="grid grid-cols-3 gap-3 mt-8 p-3 bg-slate-50 rounded-3xl border-2 border-slate-900/5">
|
||||
<div class="text-center">
|
||||
<p class="text-[8px] font-black text-slate-400 uppercase">1 JOUR</p>
|
||||
<p class="text-sm font-black text-slate-900">{{ formule.price1j|format_currency('EUR') }}</p>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-3 mt-8 p-4 md:p-3 bg-slate-50 rounded-2xl md:rounded-3xl border-2 border-slate-900/5">
|
||||
|
||||
<!-- 1 JOUR -->
|
||||
<div class="text-center pb-3 md:pb-0 border-b border-slate-200 md:border-b-0">
|
||||
<p class="text-[10px] md:text-[8px] font-black text-slate-400 uppercase tracking-wider">1 JOUR</p>
|
||||
<p class="text-base md:text-sm font-black text-slate-900">
|
||||
{% if tvaEnabled %}
|
||||
{{ (formule.price1j * 1.20)|format_currency('EUR') }} <span class="text-[10px] opacity-75">TTC</span>
|
||||
{% else %}
|
||||
{{ formule.price1j|format_currency('EUR') }}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-center border-x border-slate-200">
|
||||
<p class="text-[8px] font-black text-slate-400 uppercase">2 JOURS</p>
|
||||
<p class="text-sm font-black text-blue-600">{{ formule.price2j|format_currency('EUR') }}</p>
|
||||
|
||||
<!-- 2 JOURS -->
|
||||
<div class="text-center py-3 md:py-0 border-b border-slate-200 md:border-b-0 md:border-x">
|
||||
<p class="text-[10px] md:text-[8px] font-black text-slate-400 uppercase tracking-wider">2 JOURS</p>
|
||||
<p class="text-base md:text-sm font-black text-blue-600 md:text-blue-900">
|
||||
{% if tvaEnabled %}
|
||||
{{ (formule.price2j * 1.20)|format_currency('EUR') }} <span class="text-[10px] opacity-75">TTC</span>
|
||||
{% else %}
|
||||
{{ formule.price2j|format_currency('EUR') }}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[8px] font-black text-slate-400 uppercase">5 JOURS</p>
|
||||
<p class="text-sm font-black text-emerald-600">{{ formule.price5j|format_currency('EUR') }}</p>
|
||||
|
||||
<!-- 5 JOURS -->
|
||||
<div class="text-center pt-3 md:pt-0">
|
||||
<p class="text-[10px] md:text-[8px] font-black text-slate-400 uppercase tracking-wider">5 JOURS</p>
|
||||
<p class="text-base md:text-sm font-black text-emerald-600">
|
||||
{% if tvaEnabled %}
|
||||
{{ (formule.price5j * 1.20)|format_currency('EUR') }} <span class="text-[10px] opacity-75">TTC</span>
|
||||
{% else %}
|
||||
{{ formule.price5j|format_currency('EUR') }}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
|
||||
@@ -109,7 +109,11 @@
|
||||
<div class="flex flex-col items-center md:items-start space-y-6">
|
||||
{# Prix principal #}
|
||||
<div class="flex flex-col md:flex-row items-center md:items-baseline gap-2 md:gap-4 text-center md:text-left">
|
||||
<span class="text-6xl md:text-7xl font-black text-[#f39e36] leading-none">{{ product.priceDay|format_currency('EUR') }}</span>
|
||||
{% if tvaEnabled %}
|
||||
<span class="text-4xl md:text-3xl font-black text-[#f39e36] leading-none">{{ (product.priceDay*1.20)|format_currency('EUR') }} TTC</span>
|
||||
{% else %}
|
||||
<span class="text-4xl md:text-3xl font-black text-[#f39e36] leading-none">{{ product.priceDay|format_currency('EUR') }}</span>
|
||||
{% endif %}
|
||||
{% if product.category == "barnums" %}
|
||||
<span class="text-[10px] md:text-sm font-bold text-slate-400 uppercase tracking-widest italic">Week-End</span>
|
||||
{% else %}
|
||||
@@ -121,7 +125,11 @@
|
||||
<div class="flex flex-wrap justify-center md:justify-start gap-3 w-full">
|
||||
{% if product.category != "barnums" %}
|
||||
<div class="flex items-center gap-3 bg-slate-50 px-5 py-3 rounded-2xl border border-slate-100 shadow-sm">
|
||||
<span class="text-xl md:text-2xl font-black text-slate-900 italic">+ {{ product.priceSup|format_currency('EUR') }}</span>
|
||||
{% if tvaEnabled %}
|
||||
<span class="text-md md:text-2xl font-black text-slate-900 italic">+ {{ (product.priceSup*1.20)|format_currency('EUR') }} TTC</span>
|
||||
{% else %}
|
||||
<span class="text-md md:text-2xl font-black text-slate-900 italic">+ {{ product.priceSup|format_currency('EUR') }}</span>
|
||||
{% endif %}
|
||||
<span class="text-[9px] font-black text-slate-400 uppercase tracking-widest italic">Jour supplémentaire</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -185,12 +193,12 @@
|
||||
{# Chiffres #}
|
||||
<div class="grid grid-cols-1 gap-3 md:gap-4 w-full">
|
||||
<div class="flex items-center justify-between border-b border-slate-200/60 pb-3">
|
||||
<span class="text-[9px] md:text-[10px] font-black text-slate-400 uppercase italic">Largeur</span>
|
||||
<span class="text-lg md:text-xl font-black text-slate-900 italic">{{ product.dimW }} m</span>
|
||||
<span class="text-[9px] md:text-[10px] font-black text-slate-400 uppercase italic">Longueur </span>
|
||||
<span class="text-lg md:text-xl font-black text-slate-900 italic">{{ product.dimP }} m</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between border-b border-slate-200/60 pb-3">
|
||||
<span class="text-[9px] md:text-[10px] font-black text-slate-400 uppercase italic">Profondeur</span>
|
||||
<span class="text-lg md:text-xl font-black text-slate-900 italic">{{ product.dimP }} m</span>
|
||||
<span class="text-[9px] md:text-[10px] font-black text-slate-400 uppercase italic">Largeur </span>
|
||||
<span class="text-lg md:text-xl font-black text-slate-900 italic">{{ product.dimW }} m</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-[9px] md:text-[10px] font-black text-slate-400 uppercase italic">Hauteur</span>
|
||||
|
||||
Reference in New Issue
Block a user