Files
ludikevent_crm/templates/dashboard/flow/view.twig
Serreau Jovann 553d12aac8 fix: supprimer le calculateur de frais de livraison et la section 7.2 des CGV
Suppression complète du système de calcul de frais de livraison (rayon 30km depuis Danizy) :
- Route /estimer-la-livraison et template estimate_delivery.twig
- Calcul automatique livraison dans FlowController et ReserverController
- Champs distance/prix livraison dans la vue admin flow
- Ligne "Frais de livraison" sur les devis générés
- Section 7.2 (mise en relation + rayon 30km) dans les CGV (twig + PDF contrat/devis)
- Liens navigation "Estimer la livraison" (desktop + mobile)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 12:03:16 +01:00

325 lines
22 KiB
Twig

{% extends 'dashboard/base.twig' %}
{% block title %}Détail de la réservation #{{ session.id }}{% endblock %}
{% block title_header %}Réservation #{{ session.id }}{% endblock %}
{% block body %}
<div class="space-y-8 pb-20">
{% set isActionable = session.typePaiement %}
<div class="flex items-center gap-3 justify-end">
<a href="{{ path('app_crm_flow') }}"
class="group relative flex items-center gap-2 px-6 py-3 bg-slate-700 hover:bg-slate-600 rounded-2xl text-white text-xs font-bold uppercase tracking-wider transition-all shadow-lg">
<svg class="w-4 h-4 transition-transform group-hover:-translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
Retour
</a>
<a href="{{ path('app_crm_flow_delete', {id: session.id}) }}"
onclick="return confirm('Voulez-vous vraiment supprimer cette demande ? Cette action est irréversible.')"
data-turbo="false"
class="group relative flex items-center gap-2 px-6 py-3 bg-red-600 hover:bg-red-500 rounded-2xl text-white text-xs font-bold uppercase tracking-wider transition-all shadow-lg shadow-red-500/20 {{ not isActionable ? 'opacity-50 pointer-events-none cursor-not-allowed' : '' }}">
<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" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
Refuser
</a>
<a href="{{ path('app_crm_flow_allow', {id: session.id}) }}"
data-turbo="false"
class="group relative flex items-center gap-2 px-6 py-3 bg-emerald-600 hover:bg-emerald-500 rounded-2xl text-white text-xs font-bold uppercase tracking-wider transition-all shadow-lg shadow-emerald-500/20 {{ not isActionable ? 'opacity-50 pointer-events-none cursor-not-allowed' : '' }}">
<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" d="M5 13l4 4L19 7" />
</svg>
Valider la demande
</a>
</div>
{# BILLING CONFIG #}
<div class="bg-slate-800/40 backdrop-blur-xl border border-slate-700/50 rounded-[2rem] p-8">
<h3 class="text-lg font-bold text-white mb-6 flex items-center gap-3">
<div class="p-2 bg-indigo-500/10 rounded-lg text-indigo-500">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 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"></path></svg>
</div>
Configuration Facturation
</h3>
<form action="{{ path('app_crm_flow_update', {id: session.id}) }}" method="post" class="space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-1">Mode de paiement</label>
<select name="typePaiement" class="w-full bg-slate-900/50 border border-slate-700 rounded-lg px-3 py-2 text-white text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none transition-all">
<option value="">Sélectionner...</option>
{% for type in ['Paiement Via Chorus', 'Paiement En ligne', 'Paiement Après événement', 'Autre mode de paiement'] %}
<option value="{{ type }}" {% if session.typePaiement == type %}selected{% endif %}>{{ type }}</option>
{% endfor %}
</select>
</div>
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-1">Prestataire</label>
<select name="prestataire" class="w-full bg-slate-900/50 border border-slate-700 rounded-lg px-3 py-2 text-white text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none transition-all">
<option value="">-- Aucun prestataire assigné --</option>
{% for p in prestataires %}
<option value="{{ p.id }}" {% if session.prestataire and session.prestataire.id == p.id %}selected{% endif %}>
{{ p.surname }} {{ p.name }} ({{ p.email }})
</option>
{% endfor %}
</select>
</div>
</div>
<div class="flex justify-end mt-4">
<button type="submit" class="py-2 px-6 bg-indigo-600 hover:bg-indigo-500 text-white text-xs font-bold uppercase tracking-widest rounded-lg transition-all">
Enregistrer les modifications
</button>
</div>
</form>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
{# CLIENT INFO #}
<div class="bg-slate-800/40 backdrop-blur-xl border border-slate-700/50 rounded-[2rem] p-8">
<h3 class="text-lg font-bold text-white mb-6 flex items-center gap-3">
<div class="p-2 bg-blue-500/10 rounded-lg text-blue-500">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path></svg>
</div>
Informations Client
</h3>
{% if session.customer %}
<div class="space-y-4">
<div class="flex items-center justify-between p-4 bg-slate-900/50 rounded-xl border border-slate-700/50">
<span class="text-slate-400 text-xs uppercase tracking-wider font-bold">Nom complet</span>
<span class="text-white font-bold">{{ session.customer.surname }} {{ session.customer.name }}</span>
</div>
<div class="flex items-center justify-between p-4 bg-slate-900/50 rounded-xl border border-slate-700/50">
<span class="text-slate-400 text-xs uppercase tracking-wider font-bold">Email</span>
<a href="mailto:{{ session.customer.email }}" class="text-blue-400 hover:text-blue-300 transition-colors font-medium">{{ session.customer.email }}</a>
</div>
<div class="flex items-center justify-between p-4 bg-slate-900/50 rounded-xl border border-slate-700/50">
<span class="text-slate-400 text-xs uppercase tracking-wider font-bold">Téléphone</span>
<a href="tel:{{ session.customer.phone }}" class="text-white font-medium hover:text-blue-400 transition-colors">{{ session.customer.phone }}</a>
</div>
</div>
{% else %}
<div class="p-8 text-center border-2 border-dashed border-slate-700 rounded-2xl">
<p class="text-slate-500 font-medium">Aucun client associé pour le moment.</p>
</div>
{% endif %}
</div>
{# LOCATION & TECHNICAL INFO #}
<div class="bg-slate-800/40 backdrop-blur-xl border border-slate-700/50 rounded-[2rem] p-8">
<h3 class="text-lg font-bold text-white mb-6 flex items-center gap-3">
<div class="p-2 bg-purple-500/10 rounded-lg text-purple-500">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" /><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>
</div>
Lieu & Technique
</h3>
<div class="space-y-4">
<div class="p-4 bg-slate-900/50 rounded-xl border border-slate-700/50">
<span class="block text-slate-400 text-[10px] uppercase tracking-wider font-bold mb-1">Adresse de l'événement</span>
<p class="text-white font-medium">{{ session.adressEvent|default('Non renseignée') }}</p>
{% if session.adress2Event %}<p class="text-slate-400 text-sm mt-1">{{ session.adress2Event }}</p>{% endif %}
<p class="text-blue-400 font-bold mt-1">{{ session.zipCodeEvent }} {{ session.townEvent }}</p>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="p-4 bg-slate-900/50 rounded-xl border border-slate-700/50">
<span class="block text-slate-400 text-[10px] uppercase tracking-wider font-bold mb-1">Type de sol</span>
<p class="text-white font-medium">{{ session.typeSol|default('-') }}</p>
</div>
<div class="p-4 bg-slate-900/50 rounded-xl border border-slate-700/50">
<span class="block text-slate-400 text-[10px] uppercase tracking-wider font-bold mb-1">Pente</span>
<p class="text-white font-medium">{{ session.pente|default('-') }}</p>
</div>
</div>
<div class="p-4 bg-slate-900/50 rounded-xl border border-slate-700/50">
<span class="block text-slate-400 text-[10px] uppercase tracking-wider font-bold mb-1">Accès</span>
<p class="text-slate-300 text-sm leading-relaxed">{{ session.access|default('Aucune information') }}</p>
</div>
<div class="p-4 bg-slate-900/50 rounded-xl border border-slate-700/50 flex justify-between items-center">
<span class="text-slate-400 text-xs uppercase tracking-wider font-bold">Distance Prise élec.</span>
<span class="text-white font-bold">{{ session.distancePower|default(0) }} m</span>
</div>
</div>
</div>
</div>
{# CART CONTENT #}
<div class="bg-slate-800/40 backdrop-blur-xl border border-slate-700/50 rounded-[2rem] p-8">
<h3 class="text-lg font-bold text-white mb-6 flex items-center gap-3">
<div class="p-2 bg-emerald-500/10 rounded-lg text-emerald-500">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"></path></svg>
</div>
Contenu de la demande
</h3>
{% if session.formule %}
<div class="mb-6 p-4 bg-blue-500/10 border border-blue-500/20 rounded-xl flex items-center gap-3">
<div class="p-2 bg-blue-500/20 rounded-lg text-blue-400">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/></svg>
</div>
<div>
<p class="text-[10px] font-bold text-blue-400 uppercase tracking-widest">Formule appliquée</p>
<p class="text-white font-bold">{{ session.formule.name }}</p>
</div>
</div>
{% endif %}
{% if session.products['start'] is defined and session.products['end'] is defined %}
<div class="flex items-center gap-4 mb-8 p-4 bg-slate-900/50 rounded-xl border border-slate-700/50 w-fit">
<div class="flex items-center gap-2">
<span class="text-slate-400 text-xs font-bold uppercase">Du</span>
<span class="text-white font-bold">{{ session.products['start']|date('d/m/Y') }}</span>
</div>
<span class="text-slate-600">➔</span>
<div class="flex items-center gap-2">
<span class="text-slate-400 text-xs font-bold uppercase">Au</span>
<span class="text-white font-bold">{{ session.products['end']|date('d/m/Y') }}</span>
</div>
</div>
{% endif %}
<div class="space-y-4">
{% if session.products['ids'] is defined %}
{% for productId in session.products['ids'] %}
{% set product = loadProductById(productId) %}
{% if product %}
<div class="flex items-center gap-4 p-4 bg-slate-900/50 rounded-xl border border-slate-700/50">
{% if product.image %}
<img src="{{ product.image }}" alt="{{ product.name }}" class="w-12 h-12 rounded-lg object-cover bg-slate-800">
{% else %}
<div class="w-12 h-12 rounded-lg bg-slate-800 flex items-center justify-center text-slate-500">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
</div>
{% endif %}
<div>
<p class="text-white font-bold">{{ product.name }}</p>
<span class="text-xs text-slate-500">Réf: #{{ productId }}</span>
<div class="mt-1 text-xs text-slate-400">
<span class="block">1er jour : {{ product.price1day|number_format(2, ',', ' ') }} €</span>
{% if product.priceSup > 0 %}
<span class="block">Jours supp : {{ product.priceSup|number_format(2, ',', ' ') }} €</span>
{% endif %}
</div>
</div>
</div>
{# LINKED OPTIONS #}
{% if session.products.options[productId] is defined and session.products.options[productId]|length > 0 %}
<div class="mt-2 ml-16 space-y-1">
{% for optionId in session.products.options[productId] %}
{% set option = loadOptionById(optionId) %}
{% if option %}
<div class="flex items-center gap-2 text-xs text-slate-400">
<span class="bg-indigo-500/10 text-indigo-400 px-1.5 py-0.5 rounded text-[10px] font-bold uppercase border border-indigo-500/20">Option</span>
<span>{{ option.name }}</span>
<span class="font-bold text-slate-300">+ {{ option.price|number_format(2, ',', ' ') }} €</span>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% else %}
<div class="flex items-center justify-between p-4 bg-slate-900/50 rounded-xl border border-slate-700/50">
<span class="text-slate-400 italic">Produit introuvable (ID #{{ productId }})</span>
</div>
{% endif %}
{% endfor %}
{% else %}
<p class="text-slate-500 italic">Aucun produit.</p>
{% endif %}
{# ORPHAN OPTIONS #}
{% if session.options is defined %}
{% set hasOrphans = false %}
{% for prodId, opts in session.options %}
{% if prodId not in session.products['ids']|default([]) %}
{% set hasOrphans = true %}
{% endif %}
{% endfor %}
{% if hasOrphans %}
<div class="mt-6 pt-6 border-t border-slate-700/50">
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-[0.2em] mb-3 block">Options supplémentaires</span>
{% for prodId, opts in session.options %}
{% if prodId not in session.products['ids']|default([]) %}
{% for optionId in opts %}
{% set option = loadOptionById(optionId) %}
{% if option %}
<div class="flex items-center gap-3 p-3 bg-slate-900/30 rounded-xl border border-slate-700/30 mb-2">
<div class="h-8 w-8 bg-indigo-500/10 rounded-lg flex items-center justify-center text-indigo-500 border border-indigo-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" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/></svg>
</div>
<span class="text-sm font-medium text-slate-300">{{ option.name }}</span>
<span class="ml-auto text-sm font-bold text-white">{{ option.price|number_format(2, ',', ' ') }} € HT</span>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endif %}
</div>
{% if session.details %}
<div class="mt-8">
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-[0.2em] mb-2 block">Notes client</span>
<div class="p-4 bg-amber-500/10 border border-amber-500/20 rounded-xl text-amber-200 text-sm leading-relaxed">
{{ session.details }}
</div>
</div>
{% endif %}
{# TOTAL #}
{% set totalData = totalSession(session) %}
<div class="mt-8 pt-6 border-t border-slate-700">
<div class="flex flex-col gap-2 items-end">
{% if session.promotion %}
<div class="flex items-center gap-2 text-sm text-amber-400 font-bold uppercase mb-1">
<span>Promotion : {{ session.promotion.name }}</span>
<span class="bg-amber-500/10 px-2 py-0.5 rounded text-amber-500">-{{ session.promotion.percentage }}%</span>
</div>
<div class="text-xs text-slate-500 font-bold line-through">
{{ totalData.originalHT|number_format(2, ',', ' ') }} € HT
</div>
{% endif %}
<div class="text-sm text-slate-400">
Durée : <span class="text-white font-bold">{{ totalData.duration }} jour{{ totalData.duration > 1 ? 's' : '' }}</span>
</div>
<div class="text-xl text-white">
Total HT : <span class="font-black">{{ totalData.ht|number_format(2, ',', ' ') }} €</span>
</div>
{% if totalData.tvaEnabled %}
<div class="text-sm text-slate-500">
Soit <span class="font-bold text-slate-300">{{ (totalData.ht * 1.20)|number_format(2, ',', ' ') }} € TTC</span> (TVA 20%)
</div>
{% endif %}
</div>
</div> </div>
{# BILLING INFO #}
<div class="bg-slate-800/40 backdrop-blur-xl border border-slate-700/50 rounded-[2rem] p-8">
<h3 class="text-lg font-bold text-white mb-6 flex items-center gap-3">
<div class="p-2 bg-indigo-500/10 rounded-lg text-indigo-500">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 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"></path></svg>
</div>
Facturation
</h3>
<div class="p-4 bg-slate-900/50 rounded-xl border border-slate-700/50">
<p class="text-white font-medium">{{ session.billingAddress|default('Même que livraison') }}</p>
<p class="text-blue-400 font-bold mt-1">{{ session.billingZipCode }} {{ session.billingTown }}</p>
</div>
</div>
</div>
{% endblock %}