Files
office_translator/_bmad-output/implementation-artifacts/spec-admin-fournisseurs-ia-emails-promos.md
sepehr 0a013c679d
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m40s
feat(admin): canaux IA par palier (z.ai/DeepSeek directs), emails generes par IA, reductions Stripe
- Chaque modele d'un palier devient une route {canal, modele} : openrouter,
  deepseek direct, zhipu (z.ai, nouveau canal), minimax, openai, xai ;
  repli automatique sur la route suivante si la cle manque, compatibilite
  ascendante avec les anciens reglages a chaines
- Page Fournisseurs refondue en sections ; interrupteurs et chaine de
  secours retablis ; tests de connexion avec delai d'attente
- Emails de relance generes par IA (canal au choix, prompt construit depuis
  le plan marketing, HTML sane, RTL arabe/persan, jamais d'envoi automatique)
- Codes promo : validation locale avant Stripe, Coupon+PromotionCode avec
  double plafond, comptage par session (webhook+sync=1 fois), ecriture
  atomique, relier-a-Stripe, suppression, validation publique au checkout
- Page Tarifs : champ code promo (?promo=) avec verification traduite
- Tests : 1357 verts (facturation par palier testee dans le worker,
  point de contact providers/available, promos, generation)
2026-09-05 19:46:33 +02:00

13 KiB
Raw Blame History

title, type, created, status, baseline_commit, review_loop_iteration, context
title type created status baseline_commit review_loop_iteration context
Fournisseur par palier IA (DeepSeek/z.ai directs), page Fournisseurs, emails générés par IA, réductions Stripe feature 2026-09-05 done 6c26712 0

Intent

Problem : Le routage par palier n'accepte que des modèles OpenRouter : Sepehr paie des accès directs DeepSeek et z.ai (Zhipu) qu'il ne peut pas utiliser ; la page Fournisseurs est confuse ; les emails de relance s'écrivent à la main sans s'appuyer sur l'IA ni sur le plan marketing ; aucune réduction ne peut être proposée dans les relances de façon fiable.

Approach : (1) Chaque modèle d'un palier devient une route {provider, model} : le palier choisit le canal (openrouter, deepseek direct, zhipu nouveau = z.ai, minimax, openai, zai) ; les chaînes simples restent acceptées (= openrouter, compatibilité). (2) Nouveau fournisseur zhipu (base https://api.z.ai/api/paas/v4, OpenAI-compatible) + refonte de la page Fournisseurs par sections. (3) Génération d'email par IA dans la page Marketing : fournisseur/modèle au choix, prompt système construit depuis MARKETING_PLAN.md (positionnement, tarifs, chiffres autorisés) ; rendu {subject, html} analysé avec robustesse. (4) Réductions : codes promo dans data/promo_codes.json, création Stripe Coupon+Promotion Code (double plafond), application au checkout et comptage des utilisations, sélecteur de promo dans l'email.

Boundaries & Constraints

Always : Compatibilité ascendante : models à chaînes et default_model actuels continuent de fonctionner (testé) ; une route dont la clé manque est sautée au profit de la suivante de la liste ; tout appel LLM/Stripe a un délai d'attente, une erreur propagée claire et jamais d'HTTP 500 nu ; clés API jamais renvoyées en clair par de nouveaux endpoints ; interface en français correct, nouveaux textes dans les 13 langues ; facteur de coût : palier Premium = 5, Essentielle = 1 quel que soit le canal.

Ask First : Supprimer le fournisseur existant zai (xAI/Grok) — il reste ; changer les prix des forfaits ; créer une table de BDD (rester sur JSON dans data/).

Never : Passer Stripe en live ; appliquer une promo expirée/épuisée/hors forfait ; injecter le HTML généré sans longueur maximale et nettoyage basique ; bloquer un envoi de masse si la génération IA échoue ; toucher à office-translator-landing-page/ ni aux dossiers d'outillage.

I/O & Edge-Case Matrix

Scenario Input / State Expected Output / Behavior Error Handling
Palier avec routes multi-canaux essential.routes = [zhipu/glm-5.3-flash, openrouter/deepseek-v4-flash] Traduction via z.ai direct si clé présente, sinon openrouter ; facteur coût 1 Clé zhipu absente → route suivante, journal d'avertissement
Ancien réglage (chaînes) ai_tiers.essential.models = ["deepseek/deepseek-v4-flash"] Routé via openrouter comme aujourd'hui N/A
Code promo valide au checkout promo actif, forfait pro, 3/10 utilisations Session Stripe avec remise ; compteur incrémenté à la complétion seulement N/A
Code promo expiré/épuisé/mauvais forfait n'importe lequel Rejet explicite AVANT Stripe (message par cause) Aucune session créée
Génération IA d'email fournisseur choisi, brief + promo optionnelle {subject, html} conforme au plan marketing, pieds de page serveur inchangés Sortie non analysable → erreur claire, aucun envoi automatique

Code Map

  • routes/admin_routes.py -- AiTierSettings l.1055 (évoluer vers routes {provider, model, label} + compat chaînes), normalize_ai_tiers l.1089 (validation des routes : fournisseur connu, dédoublonnage, anti-croisement de palier inchangé), endpoints marketing l.2388-2660 (y ajouter POST /marketing/email/generate, GET/POST /marketing/promos, POST /marketing/promos/{code}/deactivate), _execute_bulk_send.
  • routes/translate_routes.py -- _resolve_tier_model l.220 (renvoyer la route complète : (provider, model)), bloc d'instanciation l.1390-1460 (dispatch par canal : openrouter/openrouter_premium déjà OpenAI-compat ; deepseek → DeepSeekTranslationProvider ; zhipu/minimax/zai/openai → OpenAITranslationProvider avec leur base_url), _compute_cost_factor l.608 (basé sur le palier, pas le nom du canal).
  • services/providers/config.py -- motif env par fournisseur : ajouter ZHIPU_ENABLED/ZHIPU_API_KEY/ZHIPU_MODEL/ZHIPU_BASE_URL (défaut https://api.z.ai/api/paas/v4, modèle glm-5.3).
  • services/payment_service.py -- create_checkout_session l.58 (paramètre promo_code, discounts=[{"promotion_code": …}]), handle_checkout_completed (incrément du compteur via metadata.promo_code), handle_webhook l.235 ; credits sans promo.
  • routes/auth_routes.py -- CheckoutRequest l.72 (champ promo_code optionnel) + endpoint l.838.
  • services/pricing_config.py -- motifs Stripe (clés, mode live/test) à réutiliser ; data/pricing_overrides.json = précédent de fichier JSON géré admin.
  • MARKETING_PLAN.md (racine) -- source du prompt : sections 1 (positionnement/USP) et 2 (forfaits et chiffres autorisés) ; extraire côté serveur, tronquer (~4000 caractères).
  • frontend/src/app/admin/settings/page.tsx -- refonte : sections « Traduction classique » (google, google_cloud), « Fournisseurs IA directs » (deepseek, zhipu nouveau, minimax, openai, zai), « Passerelles multi-modèles » (openrouter, openrouter_premium : clé seule, rappel « modèles gérés dans Modèles & abonnements »), OCR, Emails ; boutons de test existants conservés.
  • frontend/src/app/admin/models/page.tsx -- chaque modèle gagne un sélecteur de fournisseur ; combobox libre (identifiants différents par canal) + catalogue OpenRouter pour ce canal.
  • frontend/src/app/admin/marketing/page.tsx -- bloc « Générer avec l'IA » (fournisseur+modèle, brief, langue, ton, promo optionnelle → remplit sujet/HTML) + section « Réductions » (liste, création : type %/montant, forfaits, plafond, expiration ; désactivation).
  • frontend/src/app/pricing/page.tsx -- champ code promo au checkout + lecture ?promo= ; l'envoyer à create-checkout.
  • .env.example -- bloc ZHIPU_* documenté (et rappel DEEPSEEK_*) comme les autres fournisseurs.
  • i18n admin.json/pricing.json (13 langues) -- clés nouvelles (fournisseurs, génération IA, promos, champ code promo) ; admin.nav.* inchangé.
  • tests/ -- motifs test_ai_tier_routing.py, test_admin_marketing.py, test_worker_tier_routing.py, mocks Stripe éventuels dans tests/.

Tasks & Acceptance

Execution :

  • routes/admin_routes.py -- routes multi-canaux dans AiTierSettings (compat chaînes), validation dans normalize_ai_tiers, endpoints /marketing/email/generate (prompt = plan marketing + brief ; appel LLM du canal choisi ; analyse robuste JSON avec repli texte ; longueur max) et /marketing/promos (création Stripe Coupon+Promotion Code si Stripe configuré, sinon local_only refusé au checkout avec message clair ; désactivation ; plafonds côté local ET Stripe).
  • routes/translate_routes.py -- résolution (provider, model) par route de palier avec repli sur la route suivante si la clé manque, dispatch d'instanciation par canal (zhipu inclus), facteur de coût par palier.
  • services/providers/config.py + .env.example -- fournisseur zhipu (env documentées).
  • services/payment_service.py + routes/auth_routes.py -- promo_code au checkout : validation locale (actif, forfait, expiration, plafond) AVANT Stripe, remise Stripe, métadonnée promo_code, incrément à la complétion (webhook ET sync).
  • frontend/src/app/admin/settings/page.tsx -- refonte par sections + fournisseur zhipu.
  • frontend/src/app/admin/models/page.tsx -- sélecteur de fournisseur par modèle.
  • frontend/src/app/admin/marketing/page.tsx -- génération IA (choix du canal) + gestion des réductions.
  • frontend/src/app/pricing/page.tsx -- champ/URL du code promo au checkout.
  • i18n 13 langues -- toutes les nouvelles clés.
  • tests/ -- compat chaînes, multi-canaux avec repli de clé, zhipu, coût par palier ; promos : validation, checkout mocké, incrément à la complétion, rejets par cause ; génération IA : prompt contient le plan, repli d'analyse.
  • MARKETING_PLAN.md -- une ligne « relances et réductions gérées depuis l'admin ».

Acceptance Criteria :

  • Given des clés DeepSeek et z.ai renseignées, when l'admin choisit ces canaux pour un palier, then les traductions du palier partent par ces canaux (repli automatique sur la route suivante en cas de clé absente) et la facturation (1×/5×) suit le palier.
  • Given le plan marketing, when l'admin génère un email par IA, then le texte respecte le positionnement et les chiffres autorisés, sans aucun envoi automatique.
  • Given un code promo à 3/10 utilisations, when trois souscriptions se complètent, then le compteur passe à 6 via webhook ou sync, et un 11ᵉ essai est refusé avant tout appel Stripe.
  • Given pytest, npm run build:local, npm run test:run et le contrôle i18n, when exécutés, then tout est vert.

Design Notes

Route de palier : {"provider": "zhipu", "model": "glm-5.3-flash", "label": "GLM-5.3 Flash (z.ai)"} ; chaîne "z-ai/glm-5.3-flash" = {"provider": "openrouter", …}. Génération d'email : le serveur construit le message système (plan marketing tronqué + règles : jamais de chiffre hors plan, HTML simple à styles en ligne, langue imposée) puis l'appel LLM avec response attendu en JSON {"subject": …, "html": …} ; en cas d'échec d'analyse, extraire <title>/corps, sinon erreur 502 claire AI_GENERATION_FAILED. Promos : code [A-Z0-9-]{3,24} ; Stripe coupon (percent/amount EUR) + promotion_code (code lisible, max_redemptions, expires_at) ; local_only jamais appliqué au checkout ; compteur local incrémenté uniquement dans handle_checkout_completed via metadata.promo_code.

Verification

Commands :

  • ./venv/Scripts/python.exe -m pytest -q --no-cov -- expected: nouveaux tests verts, pas de nouvelle régression
  • cd frontend && npm run build:local -- expected: succès
  • cd frontend && npm run test:run -- expected: verts
  • script de comparaison i18n (clés identiques 13 langues) -- expected: OK

Manual checks (if no CLI):

  • Page Fournisseurs : sections lisibles, zhipu présent, tests de fournisseur fonctionnels.
  • Page Marketing : génération IA produit un sujet + HTML ; création d'un code promo en mode test Stripe.

Spec Change Log

Suggested Review Order

Routage multi-canaux par palier

  • Routes {canal, modele} d'un palier avec compatibilite des anciennes chaines. admin_routes.py:1064

  • Resolution unique consciente des cles (une route sans cle est sautee), utilisee par le worker ET l'affichage. translate_routes.py:250

  • Catalogue des canaux (zhipu = z.ai) et variables d'environnement. config.py:264

Reductions Stripe

Emails generes par IA

Points de contact paiement

Interfaces

  • Constante unique des canaux cote client. constants.ts:25

  • Page Marketing : generation IA + reductions. page.tsx:112

  • Page Tarifs : champ code promo, lecture ?promo=, validation avant paiement. page.tsx:280

Peripheriques