Campagne marketing - Réservations non converties
Contexte
Cette campagne concerne les clients ayant réservé un produit mais n'ayant pas finalisé leur achat malgré les relances existantes.
Logidav reste responsable de la cible et des données client. Menzzo/Magento reste responsable de la destination d'achat via le module Axelites_Restock.
Objectif
Cette étape met à disposition un espace de travail pour préparer et valider les deux maquettes emails :
- produit de retour en stock
- fin imminente de validité du code
Elle permet de valider les objets, les textes, les maquettes desktop/mobile, les CTA, les variables dynamiques et le comportement de preview avant intégration finale.
Règle de ciblage
Une réservation non convertie est une ligne mz_product_reservation :
status = confirmedis_test = 0- aucune commande
processingoucomplete - même
customer_email+sku - commande recherchée après
reserved_at
Les réservations sont exclues de la campagne si elles sont déjà appelées et :
called_status = promo_code, c'est-à-dire "A récupéré le code promo"called_status_reason = not_interested, c'est-à-dire "N'est pas intéressé"
Les autres statuts d'appel ne sont pas exclus pour l'instant afin de garder un changement minimal.
Fichiers principaux
src/AppBundle/Repository/ProductReservationRepository.phpsrc/AppBundle/Command/Product/DebugUnconvertedProductReservationsCommand.phpsrc/AppBundle/Command/Product/PreviewUnconvertedReservationMarketingEmailsCommand.phpsrc/AppBundle/Command/Product/SendTestUnconvertedReservationMarketingEmailCommand.phpsrc/AppBundle/Command/Product/GenerateUnconvertedReservationMagentoCtaCommand.phpsrc/AppBundle/Services/Marketing/UnconvertedReservationCtaBuilder.phpsrc/AppBundle/Services/Marketing/UnconvertedReservationMarketingEmailService.phpsrc/AppBundle/Services/Restock/MenzzoRestockApiClient.phpsrc/AppBundle/Resources/views/UnconvertedReservationMarketing/_layout.html.twigsrc/AppBundle/Resources/views/UnconvertedReservationMarketing/stock_available.html.twigsrc/AppBundle/Resources/views/UnconvertedReservationMarketing/expires_soon.html.twig
Email 1 - Produit de retour en stock
Objet :
Vite, votre produit est EN STOCK (Zéro attente + votre remise expire) ⏰
Objectif :
Informer le client que son produit réservé est disponible et l'inciter à finaliser son achat.
CTA label :
Exp édier mon produit tout de suite avec mon code
Variables utilisées :
customerFirstNameproductNameproductPhotoUrlcouponCodediscountLabeldeliveryDelayLabelexpirationLabelctaUrlctaLabelunsubscribeUrl
Email 2 - Fin imminente de validité du code
Objet :
⏳ Plus que 4 heures pour vos -25% (Après, il sera trop tard)
Objectif :
Créer un sentiment d'urgence avant expiration du code.
CTA label :
Ajouter au panier avec mon code
Variables utilisées :
customerFirstNameproductNameproductPhotoUrlcouponCodediscountLabelremainingValidityLabelctaUrlctaLabelunsubscribeUrl
Variables dynamiques
| Variable | Source actuelle | Fallback | Commentaire |
|---|---|---|---|
customerFirstName | Email client de la réservation ou données injectées en preview | Chaîne vide | Permet une personnalisation légère si disponible. |
productName | Réservation enrichie via les données produit | votre produit réservé | Nom affiché dans le contenu de l'email. |
productPhotoUrl | Image produit ou image de réservation | Chaîne vide | L'image est masquée si aucune URL n'est disponible. |
couponCode | CTA dry-run, CTA Magento fourni, ou short URL Magento générée manuellement | Code démo dry-run | Le code réel doit venir de Magento/Axelites_Restock. |
discountLabel | Configuration de la campagne | -25% | Libellé de remise affiché dans les maquettes. |
deliveryDelayLabel | Configuration de la campagne | moins de 5 jours | Utilisé dans l'email produit de retour en stock. |
expirationLabel | expiresAt du CTA Magento/dry-run | dans 48 heures | Texte d'expiration affiché dans l'email 1, derive du meme timestamp que l'expiration reelle du coupon/CTA. |
remainingValidityLabel | Configuration de la campagne | 4 heures | Texte d'urgence affiché dans l'email 2. |
ctaUrl | CTA dry-run ou short URL Magento déjà générée | URL de preview | Destination du bouton principal. |
ctaLabel | Type d'email | Label par défaut du template | Texte du bouton principal. |
unsubscribeUrl | CTA dry-run ou short URL unsubscribe déjà générée | URL de preview | Lien de désinscription à valider avant activation. |
Commandes de validation
Preview HTML :
php bin/console menzzo:product-reservation:unconverted:email-preview
Preview avec réservation réelle :
php bin/console menzzo:product-reservation:unconverted:email-preview --reservation-id=1
Lint Twig :
php bin/console lint:twig src/AppBundle/Resources/views/UnconvertedReservationMarketing
Envoi manuel de test en dry-run :
php bin/console menzzo:product-reservation:unconverted:email-send-test --reservation-id=1 --type=stock_available --to=EMAIL_TEST --dry-run
Envoi manuel de test après validation :
php bin/console menzzo:product-reservation:unconverted:email-send-test --reservation-id=1 --type=stock_available --to=EMAIL_TEST
Vidage manuel du spool après un envoi de test :
php bin/console swiftmailer:spool:send --message-limit=1 -vvv
Vérification read-only de la cible :
php bin/console menzzo:product-reservation:unconverted:debug --limit=10
Génération manuelle du CTA Magento :
php bin/console menzzo:product-reservation:unconverted:cta-generate --reservation-id=1 --type=stock_available --website-ids=1
Cette commande est dry-run par défaut. L'option --execute crée un vrai coupon Magento et une vraie short URL Menzzo. Elle ne doit pas être utilisée sans validation explicite.
CTA Magento
Le CTA final ne doit pas être inventé côté logidav.
Logidav prépare la cible et les variables nécessaires à l'email. La destination d'achat est fournie par Menzzo/Magento via Axelites_Restock, qui gère la short URL, la redirection, l'ajout panier, le coupon et la désinscription.
Le type de route attendu pour le bouton principal est cart_redirect.
Les commandes CLI restent des points d'entrée manuels. La logique de ciblage, preview, résolution CTA, envoi de test et génération CTA Magento est portée par des services dédiés sous AppBundle\Services\Marketing\UnconvertedReservation.
Historique des envois email
Chaque envoi manuel de test non dry-run via menzzo:product-reservation:unconverted:email-send-test est historise dans mz_product_reservation_email_log.
Le statut enregistre est queued apres retour positif de SwiftMailer, car le projet utilise le spool SwiftMailer : cela ne signifie pas que le message est deja livre.
L'historique est consultable depuis /product/product-reservations, via le bouton Historique emails de la ligne de reservation.
Aucun envoi automatique n'est ajoute dans cette etape. Aucun cron n'est ajoute.
La creation de table est documentee dans docs/sql/create_product_reservation_email_log.md.
Les champs ajoutes pour le batch manuel (cta_expires_at, source_email_log_id) sont documentes dans docs/sql/alter_product_reservation_email_log_campaign.md.
Lancement manuel de campagne
La commande batch manuelle est :
php bin/console menzzo:product-reservation:unconverted:campaign-send
Elle n'a pas d'option --execute.
Sans --type, elle traite les deux campagnes dans le meme run :
stock_availableexpires_soon
L'option --type=stock_available ou --type=expires_soon reste disponible uniquement pour cibler un seul flux.
Modes disponibles :
--dry-run: simulation uniquement, aucun email, aucun coupon reel, aucune short URL reelle, aucun historique.--test-recipient=EMAILsans--dry-run: envoi reel controle vers une seule adresse de test, jamais vers les clients.- sans
--dry-runet sans--test-recipient: envoi reel vers les emails clients.
Garde-fous production :
- sans
--limit, la commande traite tous les eligibles. --limit=Nlimite le batch et reste plafonne a 200.- en mode reel client sans
--limit, la commande affiche le warning :Real customer mode without --limit will process all eligible reservations. stock_availablene selectionne que les reservations dont le SKU pointe vers un produit Logidav marqueEn_Stock,is_available = 1, avecinventory_qtyouqtypositif.- le mode reel sans
--typeexige--website-ids, carstock_availablecree un coupon Magento reel. stock_availableseul en mode reel exige aussi--website-ids.expires_soonreutilise le dernier historiquestock_availablequeued et ne cree pas de nouveau coupon par defaut.- La commande continue le batch si une reservation echoue et historise l'echec quand c'est possible.
- Le spool SwiftMailer n'est jamais lance automatiquement.
Dry-run complet stock_available + expires_soon :
php bin/console menzzo:product-reservation:unconverted:campaign-send --dry-run
Envoi reel complet vers clients eligibles :
php bin/console menzzo:product-reservation:unconverted:campaign-send \
--website-ids=1
Envoi reel stock_available seul vers tous les clients eligibles :
php bin/console menzzo:product-reservation:unconverted:campaign-send \
--type=stock_available \
--website-ids=1
Envoi reel expires_soon seul vers tous les clients eligibles :
php bin/console menzzo:product-reservation:unconverted:campaign-send \
--type=expires_soon
Batch limite :
php bin/console menzzo:product-reservation:unconverted:campaign-send \
--website-ids=1 \
--limit=50
Envoi reel controle vers adresse de test :
php bin/console menzzo:product-reservation:unconverted:campaign-send \
--website-ids=1 \
--test-recipient=malek.kadri100@gmail.com
Vidage manuel du spool apres envoi reel :
php bin/console swiftmailer:spool:send --message-limit=<queued_count> -vvv
Timing Email 1 / Email 2
Email 1 stock_available est envoye quand le produit reserve est revenu en stock. Il cree un CTA/coupon Magento avec une validite de 48 heures (cta_expires_at = now + 48 hours).
Email 2 expires_soon cible les historiques stock_available queued dont cta_expires_at est entre maintenant et maintenant + 4 heures. Il reutilise le coupon/CTA de l'email 1 et ne cree pas de nouveau coupon par defaut.
Pour respecter la regle metier "a 20h la veille de l'expiration", un cron futur devrait lancer expires_soon autour de 20h. Aucun cron n'est ajoute dans cette PR.
Exemple de cron futur, non installe par cette PR :
0 20 * * * php bin/console menzzo:product-reservation:unconverted:campaign-send --type=expires_soon
Hors scope volontaire
- pas de cron ajoute dans cette PR
- pas d'envoi automatique planifie
- envoi client reel possible uniquement par lancement manuel explicite de la commande
- sans
--limit, la commande traite tous les eligibles - utiliser
--limitpour un batch controle si necessaire - validation marketing nécessaire avant intégration finale
Checklist validation marketing
- Objet email 1 validé
- Objet email 2 validé
- Texte email 1 validé
- Texte email 2 validé
- CTA email 1 validé
- CTA email 2 validé
- Version desktop validée
- Version mobile validée
- Variables dynamiques validées
- Désinscription validée