Ajout ligne orange "Modification des tarifs : les tarifs sont differents
de ceux de la SARL SITECONSEIL" dans la section "Ce qui change".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ajout section "Base legale" avec references juridiques :
- Article 1219 Code civil (exception d'inexecution)
- Article L.441-10 Code de commerce (suspension de plein droit)
- Liberte de suspension quelle que soit la situation de l'entreprise
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ajout section rouge "Systeme automatique de gestion des impayes" :
- Suspension automatique des services sans intervention humaine
- Aucun renouvellement sans paiement prealable
- Aucun arrangement possible (s'enerver ne changera rien)
- Rappel: contacter avant l'echeance, sans paiement pas de service
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Lien Contrats dans la sidebar (entre Clients et Facturation)
- Controller ContratController avec route /admin/contrats
- Template placeholder avec description du flow futur:
creer contrat -> signer -> creation espace client auto
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Annulation automatique:
- Apres 2 echecs de prelevement, E-Flex passe en STATE_CANCELLED
- Email d'annulation envoye au client (detail: total, paye, restant,
rejets) + notification admin
- Template eflex_cancelled.html.twig
Blocage creation:
- Controller: refuse la creation si un E-Flex est en cours (active,
pending_setup, draft) ou en defaut (cancelled avec nbFailed > 0)
- Template: bouton "Creer" remplace par "Creation bloquee (defaut)"
ou "E-Flex en cours" selon le cas
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ajout "Un credit vous engage et doit etre rembourse. Verifiez votre
capacite de remboursement avant de vous engager." dans :
- Page process E-Flex (bandeau orange avant le bouton signer)
- PDF contrat E-Flex (section dediee avant les signatures)
- Email de signature E-Flex (encadre orange)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Email eflex_paiement_recu au client lors du paiement manuel admin
(reference, echeance, montant, methode, date, progression)
- Bandeau vert si toutes echeances payees
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Ajout input date (pre-rempli aujourd'hui) dans le formulaire "Paye"
- Controller accepte la date saisie pour setPaidAt au lieu de now()
- Date et methode affiches dans le tableau des echeances
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Checkout Session avec setup_future_usage=off_session + customer Stripe
pour sauvegarder la carte et permettre les prelevements futurs
- Webhook payment_intent.succeeded stocke le PaymentMethod sur EFlex
si pas deja configure (permet cron auto ensuite)
- 1ere echeance = creation +2 jours (pas de champ startDate)
- Echeances suivantes = tous les 2 mois apres la 1ere
- Retrait du champ date 1ere echeance du formulaire (automatique)
- Info dans le formulaire: "La 1ere echeance sera due 2 jours apres
la signature. Les suivantes tous les 2 mois."
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remplace les boutons "Payer echeance X" par 3 cartes de choix :
- Virement bancaire : coordonnees bancaires + reference a indiquer
- Carte bancaire : bouton payer la prochaine echeance via Stripe Checkout
- Prelevement automatique SEPA (recommande) : configurer l'IBAN une fois
Si SEPA deja configure, affiche le statut actif avec IBAN masque.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Ajout getPdfSignedFile() et getPdfAuditFile() manquants sur entite EFlex (erreur Vich/Twig)
- Retrait du select methode de paiement dans le formulaire de creation E-Flex (le client choisira directement)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Nouveau template emails/echeancier_verify_code.html.twig
(sans reference a advert.orderNumber)
- Controller utilise le nouveau template au lieu de order_verify_code
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Route /echeancier/verify/{id}/resend (POST)
- Genere nouveau code 6 chiffres + envoie par email
- Bouton "Renvoyer le code" sous le formulaire
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Protection :
- /echeancier/verify/{id} : verification par code email 6 chiffres
(meme pattern que OrderPayment verify, 15 min expiry)
- process redirige vers verify si non authentifie
Pages client :
- Boutons "Signer l'echeancier" (vert) et "Refuser" (rouge) dans process
- /echeancier/sign/{id} : redirige vers DocuSeal
- /echeancier/refuse/{id} : passe en cancelled, affiche page refused
- echeancier/verify.html.twig : saisie code
- echeancier/refused.html.twig : confirmation refus
Email :
- Lien pointe vers /echeancier/verify/{id} (protege)
- Texte Stripe ajoute dans page process
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remplace "vous autorisez le prelevement automatique" par
"un email vous sera envoye pour effectuer la configuration
des prelevements automatiques" dans tous les templates.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entite Echeancier :
- MAJORATION_RATE = 0.05 constante
- getMajoration() : montant de la majoration
- getTotalWithMajoration() : total creance + 5%
- getMonthlyAmount() : calcule sur le total majore
Controller create :
- Echeances calculees sur le total majore (pas le total brut)
PDF EcheancierPdf :
- Bloc resume : creance, majoration (rouge), total a payer
- Tableau total : "TOTAL (creance + majoration 5%)"
Email proposition :
- Lignes creance, majoration +5% (rouge), total a payer
Page process (client) :
- 4 colonnes : creance, majoration 5%, total a payer, mensualite
Admin show :
- Carte "Creance + majoration 5%" avec detail
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Page publique /echeancier/process/{id} avec detail complet avant signature
(motif, resume, tableau echeances, conditions)
- Redirige vers page signed si deja signe/actif/termine
- Bouton unique "Envoyer pour signature" / "Renvoyer signature" selon state
- Suppression bouton "Envoyer proposition" (remplace par signature directe)
- Email signature : ajout bouton "Voir les details" + lien processUrl
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- create() genere le PDF automatiquement apres creation
- Extraction generateEcheancierPdf() methode privee reutilisable
- Bouton "Regenerer PDF" (jaune) si PDF existe, "Generer PDF" sinon
- Bouton visible dans tous les etats sauf cancelled/completed
- Redirect vers show apres creation (au lieu de l'onglet client)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Ajout methode "Echeancier" dans le select du modal paiement manuel
- Select dynamique des echeanciers du client (visible si methode=echeancier)
- AdvertPayment : relation ManyToOne vers Echeancier (nullable)
- Controller : lie l'echeancier au paiement, label avec description
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Ne passe en 'accepted' que quand totalPaid >= totalTtc
- Facture generee uniquement quand totalement paye
- Message flash avec reste a payer si paiement partiel
- Modal affiche "Deja paye / Reste" si paiements existants
- Bouton visible sur avis send + tout state non cancel/accepted
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Bouton "Enregistrer paiement" sur les avis au state 'send'
- Modal avec montant, methode (virement/cheque/especes/CB externe/autre),
reference optionnelle
- Route manualPayment : cree AdvertPayment, passe avis en accepted,
genere facture payee, indexe Meilisearch
- Methodes : virement, cheque, especes, CB terminal externe, autre
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Webhook Stripe :
- handleInvoicePaid : trouve echeancier par subscriptionId, marque la
prochaine ligne en attente comme payee, envoie email confirmation,
passe echeancier en 'completed' si toutes les echeances payees
- handleInvoiceFailed : marque ligne en echec avec raison, envoie email
echec au client + notification admin, passe en 'default' apres 2 echecs
Emails :
- echeancier_echeance_payee.html.twig : confirmation prelevement reussi
- echeancier_echeance_echec.html.twig : notification echec prelevement
Fix DevisController :
- Route #[Route] deplacee de sendDevisSignEmail vers createAdvert
(erreur "controller not callable" corrigee)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- templates/pdf/_services_list.html.twig : liste services partagee
entre pdf/cgv.html.twig et legal/cgv.html.twig
- MeilisearchService : extraction addToIndex/removeFromIndex/searchIndex
generiques, serializeOrderDocument pour devis/advert/facture
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- TrackingService : suppression TODO, retour valeurs par defaut
(trackPageView/trackEvent logguent, getVisitorStats/getPageViews
retournent structure vide avec periode)
- Suppression templates/pdf/facture.html.twig et devis.html.twig
(non utilises, PDF genere via FPDF)
- app.test.js : ajout assertion manquante ligne 541
- StatsController : constantes DQL, COLOR_GOLD, CC getServiceStats
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Navbar admin :
- Barre de recherche persistante en haut de toutes les pages admin
- Recherche dans tous les index Meilisearch simultanément :
clients (5), NDD (5), sites (5), contacts (5), revendeurs (3)
- Résultats en dropdown glassmorphism avec icône par type
- Clic sur un résultat → page + tab correspondant :
Client → /admin/clients/{id}
NDD → /admin/clients/{id}?tab=ndd
Site → /admin/clients/{id}?tab=sites
Contact → /admin/clients/{id}?tab=contacts
Revendeur → /admin/revendeurs/{id}/edit
DashboardController::globalSearch :
- Route GET /admin/global-search?q=...
- Agrège les résultats de 5 index Meilisearch
- Retourne [{type, label, sub, url}]
app.js :
- Debounce 250ms, min 2 chars
- Badges type (Client, NDD, Site, Contact, Revendeur)
- Fermeture Escape / clic extérieur
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pages services :
- /admin/services/ndd : liste tous les NDD avec client, registrar,
Cloudflare, gestion, facturation, expiration + barre recherche
- /admin/services/esyweb : liste tous les sites avec client, UUID,
type, statut + barre recherche
- Liens sidebar mis à jour (Esy-Web → esyweb, Nom de domaine → ndd)
MeilisearchService :
- Index customer_ndd : searchable fqdn/registrar/customerName/customerEmail,
filterable customerId/isGestion/isBilling
- Index customer_website : searchable name/uuid/customerName/customerEmail,
filterable customerId/type/state
- CRUD : indexDomain/removeDomain/searchDomains, indexWebsite/removeWebsite/searchWebsites
- Serializers avec infos client intégrées (customerName, customerEmail, customerId)
SyncController :
- Route POST /admin/sync/domains : sync tous les Domain dans Meilisearch
- Route POST /admin/sync/websites : sync tous les Website dans Meilisearch
- Compteurs totalDomains et totalWebsites dans index
Template admin/sync :
- Bloc "Noms de domaine" (slate) avec bouton sync
- Bloc "Sites Internet" (blue) avec bouton sync
Recherche (app.js) :
- renderHit adapté : affiche fqdn/name pour NDD/sites, customerName en sous-texte
- Lien vers la fiche client (customerId) pour les résultats NDD/Website
- setupSearch configuré pour search-ndd et search-websites
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remplace les 2 sections (changer mot de passe + générer temporaire) par
un seul bouton "Envoyer un lien de reinitialisation" qui :
1. Génère un mot de passe temporaire (bin2hex 16 chars)
2. Hash et stocke dans User (password + tempPassword)
3. Envoie l'email bienvenue avec le lien set_password au client
Plus de champ mot de passe à saisir manuellement — tout est automatique.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Onglet Securite (tab=securite) :
Statut du compte :
- Email, statut mot de passe (Temporaire jaune / Defini vert)
- 2FA Email (Active/Desactive), 2FA Google (Active/Desactive)
Changer le mot de passe :
- Formulaire avec nouveau mot de passe (min 8 chars)
- Hash via UserPasswordHasherInterface, clearTempPassword
Generer mot de passe temporaire :
- Genere 16 chars aleatoires, hash + setTempPassword
- Affiche le mot de passe en flash (pour renvoi email bienvenue)
- Modal confirmation avant action
Desactiver 2FA :
- Desactive 2FA Email + Google Authenticator + supprime secret + backup codes
- Bouton rouge avec modal confirmation
- Section visible uniquement si au moins 1 methode 2FA active
handleSecurityForm() :
- Actions : reset_password, disable_2fa, generate_temp_password
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Onglet Sites Internet (tab=sites) :
- Table : nom, UUID, type (Vitrine bleu / E-Commerce violet),
statut (Cree / Installation / En ligne / Suspendu / Ferme), date
- Badges colorés par statut
- Message "Aucun site internet" si vide
buildCustomersInfo :
- sites compte maintenant les Website liés au customer (plus hardcodé à 0)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2 configurations DNS distinctes par domaine :
- Config Esy-Mail : MX, SPF, DKIM, DMARC pour la réception (Dovecot/Mailcow)
- Config Esy-Mailer : SPF, DKIM SES, MAIL FROM pour l'envoi (AWS SES)
Les 2 sont à false/KO par défaut — seront branchés sur les checks
DNS réels quand les services seront activés sur le domaine.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sous chaque NDD dans l'onglet Noms de domaine, une ligne affiche :
- Esy-Mail : check vert si au moins 1 DomainEmail lié + nombre de boîtes
- Esy-Mailer : check vert/rouge (placeholder, false pour le moment)
- Config DNS : OK (vert) si zone Cloudflare configurée, KO (rouge) sinon
buildDomainsInfo() :
- Compte les DomainEmail par domaine
- esyMail = emailCount > 0
- esyMailerConfig = zoneIdCloudflare != null (DNS géré)
- esyMailer = false (sera branché sur l'entité service)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OvhService (ovh/ovh SDK) :
- listDomains() : liste tous les NDD du compte OVH
- isDomainManaged(fqdn) : vérifie si un domaine est chez OVH
- getDomainInfo(fqdn) : infos domaine (nameServerType, offer, etc.)
- getDomainServiceInfo(fqdn) : expiration, création, status, contacts
- getZoneInfo(fqdn) : zone DNS (OVH ou externe, DNSSEC)
Ajout NDD (onglet Noms de domaine, fiche client) :
- Formulaire : nom de domaine + registrar (auto-détection ou manuel)
- autoDetectDomain() au submit :
1. Check OVH : si trouvé → registrar=OVH, isGestion=true, isBilling=true,
expiredAt depuis serviceInfos, check zone DNS OVH
2. Check Cloudflare : si zone trouvée → zoneCloudflare=active,
zoneIdCloudflare=zoneId. Si registrar=Cloudflare → gestion+billing actifs
3. Si ni OVH ni CF : registrar manuel (Gandi/Autre), isGestion=false
- Suppression NDD avec data-confirm modal
- Colonne Actions ajoutée dans la table
Configuration :
- .env : OVH_KEY, OVH_SECRET, OVH_CUSTOMER
- .env.local : credentials OVH
- ansible/vault.yml : credentials OVH pour prod
- composer.json : ovh/ovh ^3.5
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UnsubscribeController :
- Route GET /unsubscribe/{email}/{token} (app_unsubscribe)
- Vérifie le token HMAC via UnsubscribeManager::isValidToken
- Si valide : désabonne l'email + page succès
- Si invalide : page erreur avec contact unsubscribe@siteconseil.fr
Templates :
- unsubscribe/success.html.twig : confirmation glassmorphism
- unsubscribe/invalid.html.twig : erreur glassmorphism
ClientsController :
- sendWelcomeEmail : suppression try/catch silencieux pour laisser
remonter les erreurs (sinon mail jamais envoyé sans diagnostic)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Email bienvenue (templates/emails/client_created.html.twig) :
- Envoyé automatiquement à la création du client
- Contenu : plateforme client.siteconseil.fr, identifiant email,
bouton "Choisir mon mot de passe" avec lien app_set_password
- Compatible tous clients mail (table-based, CSS longhand)
ClientsController :
- sendWelcomeEmail() : génère le lien set_password et envoie via MailerService
- Appelé dans create() après ensureDefaultContact
- Route POST /{id}/resend-welcome : renvoie l'email si tempPassword disponible
Fiche client (show.html.twig, onglet Info) :
- Si tempPassword existe : bandeau indigo "Espace client non activé"
+ lien direct vers la page activation + bouton "Renvoyer email bienvenue"
- Si tempPassword null : bandeau vert "Espace client activé"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>