This commit is contained in:
Serreau Jovann
2026-02-10 14:46:50 +01:00
parent 466219ffa4
commit 79443856ea
6 changed files with 313 additions and 123 deletions

View File

@@ -428,36 +428,57 @@ class ContratController extends AbstractController
$totalTTC = 0;
$totalCaution = 0;
foreach ($contrat->getContratsLines() as $line) {
$linePriceHT = $line->getPrice1DayHt();
$devis = $contrat->getDevis();
$formule = $devis?->getFormule();
if ($totalDays > 1) {
$linePriceHT += (($line->getPriceSupDayHt()) * ($totalDays - 1));
}
if ($formule) {
// --- LOGIQUE FORMULE ---
$fPrice = $formule->getPrice1j() ?? 0;
if ($totalDays >= 2 && $formule->getPrice2j()) $fPrice = $formule->getPrice2j();
if ($totalDays >= 5 && $formule->getPrice5j()) $fPrice = $formule->getPrice5j();
$totalHT += $linePriceHT;
$totalHT += $fPrice;
$totalCaution += $formule->getCaution() ?? 0;
if ($tvaEnabled) {
// Calculation matches original logic: (Price1Day * 1.2) + (PriceSup * 1.2 * days)
$linePriceTTC = $line->getPrice1DayHt() * 1.20;
if ($totalDays > 1) {
$linePriceTTC += ($line->getPriceSupDayHt() * 1.20 * ($totalDays - 1));
// En formule, les lignes produits sont incluses (prix 0 pour le calcul)
// Les options sont incluses SAUF livraison/déplacement
foreach ($contrat->getContratsOptions() as $option) {
$optName = mb_strtolower($option->getName());
if (str_contains($optName, 'livraison') || str_contains($optName, 'déplacement')) {
$totalHT += $option->getPrice();
}
$totalTTC += $linePriceTTC;
} else {
$totalTTC += $linePriceHT; // If no TVA, TTC = HT
}
$totalCaution += $line->getCaution();
} else {
// --- LOGIQUE CLASSIQUE ---
foreach ($contrat->getContratsLines() as $line) {
$linePriceHT = $line->getPrice1DayHt();
if ($totalDays > 1) {
$linePriceHT += (($line->getPriceSupDayHt()) * ($totalDays - 1));
}
$totalHT += $linePriceHT;
$totalCaution += $line->getCaution();
}
foreach ($contrat->getContratsOptions() as $option) {
$totalHT += $option->getPrice();
}
}
foreach ($contrat->getContratsOptions() as $option) {
$totalHT += $option->getPrice();
if ($tvaEnabled) {
$totalTTC += ($option->getPrice() * 1.20);
} else {
$totalTTC += $option->getPrice();
}
// --- GESTION PROMOTION ---
$promotion = $devis?->getOrderSession()?->getPromotion();
if ($promotion && isset($promotion['percentage'])) {
$discount = $totalHT * ($promotion['percentage'] / 100);
$totalHT -= $discount;
}
// --- CALCUL TVA ---
if ($tvaEnabled) {
$totalTTC = $totalHT * 1.20;
} else {
$totalTTC = $totalHT;
}
// Fix totals based on original logic (Arrhes calc uses full total)

View File

@@ -418,7 +418,6 @@ class ContratPdfService extends Fpdf
$this->Cell(0, 7, $this->clean(" LOGISTIQUE ET INSTALLATION"), 0, 1, 'L', true);
$this->Ln(2);
$this->SetFont('Arial', '', 9);
$this->SetFont('Arial', 'B', 9); $this->Cell(45, 5, $this->clean("Nature du sol :"), 0, 0);
$this->SetFont('Arial', '', 9); $this->Cell(50, 5, $this->clean($this->contrats->getTypeSol()), 0, 0);
$this->SetFont('Arial', 'B', 9); $this->Cell(45, 5, $this->clean("Distance électricité :"), 0, 0);

View File

@@ -64,7 +64,7 @@
</div>
{# 2. CLIENT #}
<div class="lg:col-span-3 p-8 border-b lg:border-b-0 lg:border-r border-white/5">
<div class="lg:col-span-2 p-8 border-b lg:border-b-0 lg:border-r border-white/5">
<div class="flex items-center gap-4">
<div class="w-12 h-12 bg-gradient-to-tr from-blue-600/20 to-indigo-600/20 rounded-2xl flex items-center justify-center border border-white/10 group-hover:scale-110 transition-transform">
<svg class="w-6 h-6 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" stroke-width="1.5"></path></svg>
@@ -85,60 +85,89 @@
</div>
</div>
{# 4. PAIEMENTS #}
<div class="lg:col-span-3 p-8 bg-black/10 lg:bg-transparent">
<div class="grid grid-cols-3 gap-2">
{# 4. PAIEMENTS & DETAILS #}
<div class="lg:col-span-4 p-4 bg-black/10 lg:bg-transparent border-b lg:border-b-0 lg:border-r border-white/5">
<div class="flex items-center justify-between gap-4 h-full">
{# INFO PRIX & FORMULE #}
<div class="flex flex-col justify-center min-w-[120px]">
{% if contrat.devis %}
{% if contrat.devis.orderSession and contrat.devis.orderSession.promotion %}
<span class="text-sm font-black text-white">
{{ (contrat.devis|totalQuotoHT)|number_format(2, ',', ' ') }}
</span>
<div class="flex items-center gap-2">
<span class="text-[10px] text-slate-500 line-through font-bold">
{{ (contrat.devis|totalQuotoBeforeDiscount)|number_format(2, ',', ' ') }}
</span>
<span class="text-[9px] text-emerald-400 font-bold uppercase tracking-wider">
-{{ contrat.devis.orderSession.promotion.percentage }}%
</span>
</div>
{% else %}
<span class="text-sm font-black text-white">
{{ (contrat.devis|totalQuotoHT)|number_format(2, ',', ' ') }}
</span>
{% endif %}
{# ACOMPTE #}
<div class="flex flex-col items-center gap-2">
<div class="w-8 h-8 rounded-xl flex items-center justify-center transition-all duration-300
{{ acompteOk ? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30' : 'bg-rose-500/10 text-rose-500 border border-rose-500/20' }}">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="{{ acompteOk ? 'M5 13l4 4L19 7' : 'M6 18L18 6M6 6l12 12' }}"></path></svg>
</div>
<span class="text-[8px] font-black uppercase tracking-tighter {{ acompteOk ? 'text-emerald-500' : 'text-rose-500' }}">Acompte</span>
</div>
{# CAUTION (MULTI-ETATS) #}
<div class="flex flex-col items-center gap-2">
{% if cautionEncaisser %}
{# ÉTAT : RÉCUPÉRÉE / ENCAISSÉE (Litige) #}
<div class="w-8 h-8 rounded-xl flex items-center justify-center bg-rose-600/30 text-rose-400 border border-rose-500/50 shadow-[0_0_15px_rgba(225,29,72,0.3)]" title="Caution encaissée">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path></svg>
</div>
<span class="text-[8px] font-black uppercase tracking-tighter text-rose-400">Encaissée</span>
{% elseif cautionRelase %}
{# ÉTAT : LIBÉRÉE / RENDUE #}
<div class="w-8 h-8 rounded-xl flex items-center justify-center bg-blue-500/20 text-blue-400 border border-blue-500/30" title="Caution libérée">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M8 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z"></path></svg>
</div>
<span class="text-[8px] font-black uppercase tracking-tighter text-blue-400">Libérée</span>
{% elseif cautionOk %}
{# ÉTAT : DÉTENUE (Payée mais pas encore libérée) #}
<div class="w-8 h-8 rounded-xl flex items-center justify-center bg-emerald-500/20 text-emerald-400 border border-emerald-500/30" title="Caution détenue">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path></svg>
</div>
<span class="text-[8px] font-black uppercase tracking-tighter text-emerald-500">Détenue</span>
{% if contrat.devis.formule %}
<span class="text-[9px] text-blue-400 font-bold uppercase tracking-widest mt-1 truncate max-w-[150px]" title="{{ contrat.devis.formule.name }}">
{{ contrat.devis.formule.name }}
</span>
{% endif %}
<span class="text-[9px] text-slate-500 font-bold mt-1">
Caution: {{ (contrat.devis|totalCaution)|number_format(2, ',', ' ') }}
</span>
{% else %}
{# ÉTAT : MANQUANTE #}
<div class="w-8 h-8 rounded-xl flex items-center justify-center bg-rose-500/10 text-rose-500 border border-rose-500/20 animate-pulse" title="Caution manquante">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg>
</div>
<span class="text-[8px] font-black uppercase tracking-tighter text-rose-500">Requise</span>
<span class="text-xs text-slate-500 italic">No Devis</span>
{% endif %}
</div>
{# SOLDE #}
<div class="flex flex-col items-center gap-2">
<div class="w-8 h-8 rounded-xl flex items-center justify-center transition-all duration-300
{{ soldeOk ? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30' : 'bg-rose-500/10 text-rose-500 border border-rose-500/20' }}">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="{{ soldeOk ? 'M5 13l4 4L19 7' : 'M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z' }}"></path></svg>
{# STATUTS PAIEMENTS (Icons) #}
<div class="grid grid-cols-3 gap-2">
{# ACOMPTE #}
<div class="flex flex-col items-center gap-1">
<div class="w-7 h-7 rounded-lg flex items-center justify-center transition-all duration-300
{{ acompteOk ? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30' : 'bg-rose-500/10 text-rose-500 border border-rose-500/20' }}">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="{{ acompteOk ? 'M5 13l4 4L19 7' : 'M6 18L18 6M6 6l12 12' }}"></path></svg>
</div>
<span class="text-[7px] font-black uppercase tracking-tighter {{ acompteOk ? 'text-emerald-500' : 'text-rose-500' }}">Acompte</span>
</div>
<span class="text-[8px] font-black uppercase tracking-tighter {{ soldeOk ? 'text-emerald-500' : 'text-rose-500' }}">Solde</span>
</div>
{# CAUTION #}
<div class="flex flex-col items-center gap-1">
{% if cautionEncaisser %}
<div class="w-7 h-7 rounded-lg flex items-center justify-center bg-rose-600/30 text-rose-400 border border-rose-500/50" title="Encaissée">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path></svg>
</div>
<span class="text-[7px] font-black uppercase tracking-tighter text-rose-400">Encaissée</span>
{% elseif cautionRelase %}
<div class="w-7 h-7 rounded-lg flex items-center justify-center bg-blue-500/20 text-blue-400 border border-blue-500/30" title="Libérée">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M8 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z"></path></svg>
</div>
<span class="text-[7px] font-black uppercase tracking-tighter text-blue-400">Libérée</span>
{% elseif cautionOk %}
<div class="w-7 h-7 rounded-lg flex items-center justify-center bg-emerald-500/20 text-emerald-400 border border-emerald-500/30" title="Détenue">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path></svg>
</div>
<span class="text-[7px] font-black uppercase tracking-tighter text-emerald-500">Détenue</span>
{% else %}
<div class="w-7 h-7 rounded-lg flex items-center justify-center bg-rose-500/10 text-rose-500 border border-rose-500/20 animate-pulse" title="Requise">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg>
</div>
<span class="text-[7px] font-black uppercase tracking-tighter text-rose-500">Requise</span>
{% endif %}
</div>
{# SOLDE #}
<div class="flex flex-col items-center gap-1">
<div class="w-7 h-7 rounded-lg flex items-center justify-center transition-all duration-300
{{ soldeOk ? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30' : 'bg-rose-500/10 text-rose-500 border border-rose-500/20' }}">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="{{ soldeOk ? 'M5 13l4 4L19 7' : 'M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z' }}"></path></svg>
</div>
<span class="text-[7px] font-black uppercase tracking-tighter {{ soldeOk ? 'text-emerald-500' : 'text-rose-500' }}">Solde</span>
</div>
</div>
</div>
</div>

View File

@@ -51,12 +51,31 @@
{% endif %}
</div>
<div class="p-4 rounded-[1.5rem] bg-white/5 border border-white/10 backdrop-blur-md flex items-center justify-between">
<div>
<span class="text-[10px] font-black uppercasetext-slate-300 tracking-widest block">Total Contrat</span>
<span class="text-[8px] text-slate-500 italic" title="Pour les cartes standard de l'Espace économique européen">Com. Stripe (EEE) : ~ {{ ((totalHT * 0.015) + 0.25)|number_format(2, ',', ' ') }} €</span>
<div class="flex flex-col">
<span class="text-[10px] font-black uppercase text-slate-300 tracking-widest block">Total Contrat</span>
{% if contrat.devis and contrat.devis.orderSession and contrat.devis.orderSession.promotion %}
<div class="flex items-center gap-2 mt-0.5">
<span class="text-[9px] text-slate-500 line-through font-bold">
{{ (contrat.devis|totalQuotoBeforeDiscount)|number_format(2, ',', ' ') }}
</span>
<span class="text-[9px] text-emerald-400 font-bold uppercase tracking-wider">
-{{ contrat.devis.orderSession.promotion.percentage }}%
</span>
</div>
{% endif %}
{% if contrat.devis and contrat.devis.formule %}
<span class="text-[9px] text-blue-400 font-bold uppercase tracking-widest mt-0.5">
{{ contrat.devis.formule.name }}
</span>
{% endif %}
<span class="text-[8px] text-slate-500 italic mt-1" title="Pour les cartes standard de l'Espace économique européen">Com. Stripe (EEE) : ~ {{ ((totalHT * 0.015) + 0.25)|number_format(2, ',', ' ') }} €</span>
</div>
<span class="text-xs font-black text-white italic">
{{ totalHT|number_format(2, ',', ' ') }}
{% if contrat.devis %}
{{ (contrat.devis|totalQuotoHT)|number_format(2, ',', ' ') }}
{% else %}
{{ totalHT|number_format(2, ',', ' ') }}
{% endif %}
</span>
</div>
<div class="p-4 rounded-[1.5rem] bg-white/5 border border-white/10 backdrop-blur-md flex items-center justify-between">
@@ -67,7 +86,16 @@
{% endif %}
</div>
<span class="text-xs font-black {{ soldeOk ? 'text-emerald-400' : 'text-rose-500' }} italic">
{{ soldeOk ? 'CONTRAT SOLDÉ' : (solde|number_format(2, ',', ' ') ~ ' €') }}
{% set totalAmount = (contrat.devis ? (contrat.devis|totalQuotoHT) : totalHT) %}
{% set totalPaid = 0 %}
{% for payment in contrat.contratsPayments %}
{% if payment.type in ['accompte', 'solde'] %}
{% set totalPaid = totalPaid + payment.amount %}
{% endif %}
{% endfor %}
{% set remaining = totalAmount - totalPaid %}
{{ remaining <= 0.05 ? 'CONTRAT SOLDÉ' : (remaining|number_format(2, ',', ' ') ~ ' €') }}
</span>
</div>
<div class="p-4 rounded-[1.5rem] bg-white/5 border border-white/10 backdrop-blur-md flex items-center justify-between">
@@ -255,16 +283,37 @@
<div class="space-y-4">
<h3 class="px-2 text-sm font-black text-slate-400 uppercase tracking-[0.3em]">Équipements loués</h3>
<div class="bg-white/[0.02] border border-white/10 rounded-[2rem] divide-y divide-white/5 overflow-hidden">
{% if contrat.devis and contrat.devis.formule %}
{# BLOC FORMULE #}
<div class="p-6 bg-blue-500/5 hover:bg-blue-500/10 transition-colors border-l-2 border-blue-500">
<div class="flex justify-between items-start">
<div>
<h4 class="text-blue-400 font-black text-base uppercase italic tracking-wider">FORMULE : {{ contrat.devis.formule.name }}</h4>
<p class="text-[10px] text-slate-400 font-bold uppercase mt-1">Caution Globale : {{ contrat.devis.formule.caution }}€</p>
</div>
<div class="text-right">
<span class="text-white font-black italic">Package</span>
</div>
</div>
</div>
{% endif %}
{% for product in contrat.contratsLines %}
<div class="p-6 hover:bg-white/[0.03] transition-colors">
<div class="flex justify-between items-start">
<div>
<h4 class="text-white font-bold text-base uppercase italic">{{ product.name }}</h4>
<p class="text-[10px text-slate-300 font-bold uppercase mt-1">Caution : {{ product.caution }}€</p>
{% if not (contrat.devis and contrat.devis.formule) %}
<p class="text-[10px] text-slate-300 font-bold uppercase mt-1">Caution : {{ product.caution }}€</p>
{% endif %}
</div>
<div class="text-right">
<span class="text-blue-400 font-black italic">{{ product.price1DayHt }}€</span>
<span class="block text-[8px text-slate-300 uppercase font-black">HT / Jour</span>
{% if contrat.devis and contrat.devis.formule %}
<span class="text-emerald-400 font-black italic text-xs uppercase tracking-widest">Inclus</span>
{% else %}
<span class="text-blue-400 font-black italic">{{ product.price1DayHt }}€</span>
<span class="block text-[8px] text-slate-300 uppercase font-black">HT / Jour</span>
{% endif %}
</div>
</div>
</div>

View File

@@ -109,6 +109,12 @@
{{ (quote|totalQuotoHT)|number_format(2, ',', ' ') }}
</span>
{% endif %}
{% if quote.formule %}
<span class="text-[9px] text-blue-400 font-bold uppercase tracking-widest mt-1">
{{ quote.formule.name }}
</span>
{% endif %}
</div>
</td>

View File

@@ -84,8 +84,10 @@
<div class="p-8 border-b border-slate-50 flex justify-between items-center bg-slate-50/30">
<div>
<h2 class="text-xs font-black uppercase tracking-widest text-slate-900">Détail des prestations & Options</h2>
{% if contrat.devis and contrat.devis.orderSession and contrat.devis.orderSession.formule %}
<p class="text-[10px] font-bold text-blue-600 uppercase tracking-widest mt-1">Formule : {{ contrat.devis.orderSession.formule.name }}</p>
{% if contrat.devis and contrat.devis.orderSession and contrat.devis.orderSession.promotion %}
<span class="inline-flex items-center gap-1.5 px-2 py-1 rounded bg-emerald-500/10 border border-emerald-500/20 text-emerald-600 text-[10px] font-black uppercase tracking-wider mt-1">
Promo -{{ contrat.devis.orderSession.promotion.percentage }}%
</span>
{% endif %}
</div>
<span class="bg-blue-600 text-white text-[10px] font-black px-4 py-1.5 rounded-full uppercase">{{ days }} Jours</span>
@@ -93,42 +95,94 @@
<div class="overflow-x-auto">
<table class="w-full text-left">
<tbody class="divide-y divide-slate-50">
{% for line in contrat.contratsLines %}
{% 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 %}
<tr class="bg-slate-50/20">
<td class="px-8 py-6">
<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>
{% if contrat.devis and contrat.devis.formule %}
{# AFFICHAGE FORMULE #}
{% set formule = contrat.devis.formule %}
{% set fPrice = formule.price1j %}
{% if days >= 2 and formule.price2j %}{% set fPrice = formule.price2j %}{% endif %}
{% if days >= 5 and formule.price5j %}{% set fPrice = formule.price5j %}{% endif %}
{% 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>
{% set fPriceDisplay = fPrice * 1.20 %}
{% else %}
<td class="px-8 py-6 text-right font-black text-slate-900 italic text-lg">{{ line.price|number_format(2, ',', ' ') }}€</td>
{% set fPriceDisplay = fPrice %}
{% endif %}
</tr>
{% endfor %}
<tr class="bg-blue-50/30">
<td class="px-8 py-6">
<p class="font-black text-blue-600 uppercase text-sm leading-tight">Formule : {{ formule.name }}</p>
<p class="text-[10px] text-slate-400 font-bold italic mt-1 uppercase">Caution Globale : {{ formule.caution|number_format(0, ',', ' ') }}€</p>
</td>
<td class="px-8 py-6 text-right font-black text-slate-900 italic text-lg">
{{ fPriceDisplay|number_format(2, ',', ' ') }}
</td>
</tr>
{# PRODUITS INCLUS DANS LA FORMULE #}
{% for line in contrat.contratsLines %}
<tr class="hover:bg-slate-50/30 transition-colors">
<td class="px-8 py-6 pl-12 border-l-4 border-blue-100">
<p class="font-bold text-slate-700 uppercase text-sm leading-tight">{{ line.name }}</p>
</td>
<td class="px-8 py-6 text-right font-black text-emerald-500 italic text-sm uppercase tracking-wider">Inclus</td>
</tr>
{% endfor %}
{% else %}
{# AFFICHAGE CLASSIQUE #}
{% for line in contrat.contratsLines %}
{% 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 %}
{% endif %}
{# OPTIONS #}
{% for line in contrat.contratsOptions %}
{% set isIncluded = false %}
{% if contrat.devis and contrat.devis.formule %}
{# Si formule, options gratuites sauf livraison/déplacement #}
{% if 'livraison' not in line.name|lower and 'déplacement' not in line.name|lower %}
{% set isIncluded = true %}
{% endif %}
{% endif %}
<tr class="bg-slate-50/20">
<td class="px-8 py-6">
<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 {{ isIncluded ? 'text-emerald-500 text-sm uppercase tracking-wider' : 'text-slate-900 text-lg' }} italic">
{% if isIncluded %}
Inclus
{% else %}
{% if tvaEnabled %}
{{ (line.price*1.20)|number_format(2, ',', ' ') }}
{% else %}
{{ line.price|number_format(2, ',', ' ') }}
{% endif %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
@@ -159,28 +213,43 @@
{# --- COLONNE DROITE : FINANCES --- #}
<div class="lg:col-span-2 space-y-4">
{# TOTAL HT - Version compacte #}
{# CALCUL DES TOTAUX AVEC PROMO #}
{% set promoPercentage = 0 %}
{% if contrat.devis and contrat.devis.orderSession and contrat.devis.orderSession.promotion %}
{% set promoPercentage = contrat.devis.orderSession.promotion.percentage %}
{% endif %}
{% set totalHT_Discounted = totalHT * (1 - (promoPercentage / 100)) %}
{% set totalTTC_Discounted = totalTTC * (1 - (promoPercentage / 100)) %}
{# TOTAL HT #}
<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">Hors Taxes (HT)</p>
{% if promoPercentage > 0 %}
<p class="text-[10px] text-slate-400 font-bold line-through decoration-red-400 decoration-2">{{ totalHT|number_format(2, ',', ' ') }}€</p>
<p class="text-[10px] text-emerald-500 font-black uppercase tracking-wider">Promo -{{ promoPercentage }}%</p>
{% endif %}
</div>
<p class="text-3xl font-black text-slate-900 italic tracking-tighter">{{ totalHT|number_format(2, ',', ' ') }}€</p>
<p class="text-3xl font-black text-slate-900 italic tracking-tighter">{{ totalHT_Discounted|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>
<p class="text-3xl font-black text-slate-900 italic tracking-tighter">{{ (totalTTC_Discounted - totalHT_Discounted)|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>
<p class="text-3xl font-black text-slate-900 italic tracking-tighter">{{ totalTTC_Discounted|number_format(2, ',', ' ') }}€</p>
</div>
{% endif %}
@@ -199,6 +268,17 @@
</div>
{% endif %}
{# CALCUL DU SOLDE RESTANT #}
{% set totalPaid = 0 %}
{% for p in paymentList %}{% set totalPaid = totalPaid + p.amount %}{% endfor %}
{% for p in paymentCtaList %}{% set totalPaid = totalPaid + p.amount %}{% endfor %}
{% if tvaEnabled %}
{% set newSolde = totalTTC_Discounted - totalPaid %}
{% else %}
{% set newSolde = totalHT_Discounted - totalPaid %}
{% 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>
@@ -206,12 +286,12 @@
<div class="relative">
<p class="text-xs font-black text-blue-400 uppercase tracking-widest mb-2">Solde restant à régler</p>
<div class="flex items-baseline gap-2 mb-8">
<p class="text-5xl font-black italic tracking-tighter">{{ solde|number_format(2, ',', ' ') }}€</p>
<p class="text-5xl font-black italic tracking-tighter">{{ newSolde|number_format(2, ',', ' ') }}€</p>
</div>
{% set acompteOk = contratPaymentPay(contrat, 'accompte') %}
{% if solde <=0 %}
{% if newSolde <= 0.05 %}
<div class="flex items-center gap-3 bg-green-500/20 text-green-400 p-6 rounded-2xl border border-green-500/30">
<svg class="w-8 h-8 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7"/>
@@ -263,6 +343,12 @@
{% if not (contrat.devis and 'Chorus' in contrat.devis.paymentMethod) %}
{# 2. ACOMPTE #}
{% if tvaEnabled %}
{% set arrhes_discounted = totalTTC_Discounted * 0.25 %}
{% else %}
{% set arrhes_discounted = totalHT_Discounted * 0.25 %}
{% endif %}
{% if not contratPaymentPay(contrat, 'accompte') %}
<div class="bg-white rounded-[2rem] border border-red-100 shadow-xl shadow-red-100/20 overflow-hidden">
<div class="bg-red-500 p-6 text-white flex items-center gap-4">
@@ -270,7 +356,7 @@
<p class="text-sm font-black uppercase italic leading-none">Acompte (25%)</p>
</div>
<div class="p-6 text-center">
<p class="text-3xl font-black text-slate-900 italic mb-4">{{ arrhes|number_format(2, ',', ' ') }}€</p>
<p class="text-3xl font-black text-slate-900 italic mb-4">{{ arrhes_discounted|number_format(2, ',', ' ') }}€</p>
{% if contrat.signed %}
<a data-turbo="false" href="{{ path('gestion_contrat_view', {'num': contrat.numReservation,'act':'accomptePay'}) }}" class="block w-full bg-slate-900 text-white py-4 rounded-xl font-black uppercase text-xs hover:bg-blue-600 transition-all shadow-md">Régler l'acompte</a>
{% else %}
@@ -307,14 +393,14 @@
{% endif %}
{# 3. SOLDE #}
{% if solde > 0 %}
{% if newSolde > 0.05 %}
<div class="bg-white rounded-[2rem] border border-blue-100 shadow-xl shadow-blue-100/20 overflow-hidden">
<div class="bg-blue-600 p-6 text-white flex items-center gap-4">
<div class="w-10 h-10 bg-white/20 rounded-xl flex items-center justify-center"><svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2M12 3v1m0 16v1m-9-9h1.79A2.76 2.76 0 005.255 12h13.49a2.76 2.76 0 002.465-1.79H23"></path></svg></div>
<p class="text-sm font-black uppercase italic leading-none">Solde restant</p>
</div>
<div class="p-6 text-center">
<p class="text-3xl font-black text-slate-900 italic mb-4">{{ solde|number_format(2, ',', ' ') }}€</p>
<p class="text-3xl font-black text-slate-900 italic mb-4">{{ newSolde|number_format(2, ',', ' ') }}€</p>
{% if contrat.signed and contratPaymentPay(contrat, 'accompte') %}
<form data-turbo="false" action="{{ path('gestion_contrat_view', {'num': contrat.numReservation}) }}" method="get">
@@ -322,7 +408,7 @@
<div class="mb-4">
<label for="amountToPay" class="block text-[9px] font-black text-slate-400 uppercase tracking-widest mb-2 text-left">Montant à régler</label>
<div class="relative">
<input type="number" step="0.01" min="1" max="{{ solde }}" name="amountToPay" id="amountToPay" value="{{ solde }}"
<input type="number" step="0.01" min="1" max="{{ newSolde }}" name="amountToPay" id="amountToPay" value="{{ newSolde }}"
class="w-full bg-slate-50 border border-slate-200 rounded-xl px-4 py-3 text-sm font-bold text-slate-800 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all text-center">
<div class="absolute right-4 top-3 text-slate-400 font-bold text-sm">€</div>
</div>