FEUILLE DE ROUTE TECHNIQUE
PARTIE 1 : Architecture & Mise en Place Initiale
2 Fronts / 1 Back-office 3xFiltré & Kanalpha WordPress Multi-domaine

Objectif du projet : Créer 2 expériences de marque distinctes (B2C 3xFiltré + B2B Kanalpha) sur une seule infrastructure WordPressSystème de gestion de contenu (CMS) qui servira de base technique unique pour les deux marques + WooCommerceExtension e-commerce de WordPress qui gère produits, commandes, paiements et clients.

Principe central : 1 seul back-office (produits, stocks, commandes, clients) → 2 fronts totalement différents selon le domaine.

Vue d'ensemble : Les 4 phases du développement

1
FONDATIONS
Stack technique + taxonomies + structure URLs
2
PRODUITS & VISIBILITÉ
Catégories + B2BKing + ATUM + règles affichage
3
FRONT-END
Templates Elementor + styles domaine + emails
4
RÔLES & TESTS
Back-office + commerciaux + QA complet

Règles non négociables

Ces règles s'appliquent du début à la fin :

  • !
    Une seule base de données (produitsChaque produit existe une seule fois dans WooCommerce, sa visibilité est contrôlée par des règles / stocksGéré par ATUM, un seul chiffre de stock par produit/variation, affiché différemment selon le profil / commandes / clients) — pas de duplication
  • !
    Toute différenciation se fait par règles : domaine, rôle, groupe B2B, catégorie, indexation
  • !
    URLs finalesLes adresses web des pages doivent être définies dès le développement et ne jamais changer après la mise en production figées dès le DEV (zéro renommage après mise en prod)
  • !
    Indexation maîtrisée : DEV = non indexéGoogle ne doit pas voir le site en développement. On utilisera noindex et robots.txt pour bloquer ; PROD = indexation progressive
  • !
    Identité de marque hermétique : header/footer/styles/emails séparés, zéro fuite entre 3xFiltré et Kanalpha

Stack technique & outils

Voici l'ensemble des outils qui seront installés et configurés. Chaque outil a un rôle précis dans l'architecture.

⚙️
WordPress
CMS central — version latest
🛒
WooCommerce
E-commerce — produits, panier, checkout
🎨
Elementor Pro
Page builder — templates visuels
📦
ATUM Inventory
Gestion stocks avancée
🏢
B2BKing
Prix B2B, visibilité, groupes, commerciaux
📈
Rank Math SEO
Indexation contrôlée, sitemaps
✉️
EmailKit
Design emails transactionnels
🔧
Thème Striz
Thème léger et compatible Elementor
mu-plugin custom
Logique domaine, headers, emails, stock

Ordre d'implémentation (squelette)

Suivre cet ordre garantit une base solide sans avoir à revenir en arrière. Chaque étape s'appuie sur la précédente.

1
Taxonomies & Attributs
Créer les catégories racines (CBD / Streetwear) et les attributs de variations (Poids, Taille, Couleur, etc.)
2
Structure WooCommerce
Configurer les pages shop, compte, panier, checkout avec les slugs finaux
3
ATUM
Installer et configurer la gestion de stock (fournisseurs, coûts, seuils, alertes)
4
B2BKing
Créer les groupes, règles de visibilité, prix B2B, rôles commerciaux
5
Templates Elementor
Créer les templates Single/Archive par famille de produits + Headers/Footers
6
Règles "domaine"
mu-plugin pour switch header/footer, body class, CSS, assets selon domaine
7
Règles "stock visible"
Texte de disponibilité selon profil (B2C / B2B client / Commercial)
8
Emails
2 SMTP + routing + branding EmailKit selon domaine
9
Rôles back-office
Capabilities + menus + restrictions (stock manager, accounting view, etc.)
10
Tests QA
Front B2C vs B2B, mails, commandes, stocks, exports, rôles

TABLEAU 1 : Catégories racines & sous-catégories

Ces catégories servent à déclencher les templates Elementor et les règles B2BKing. Ne pas improviser d'autres catégories racines.

Racine Slug Sous-catégorie Slug sous-cat Type produit
CBD cbd Fumable fumable Variations "Poids" (grammes)
Non-fumable non-fumable Unités ou variations (formats)
Streetwear streetwear Variations "Taille/Couleur"

Pourquoi ces catégories racines ? Elles permettent à Elementor de savoir quel template afficher (Single_CBD_Fumable vs Single_Streetwear) et à B2BKing de savoir quels produits montrer sur quel domaine.

TABLEAU 2 : Attributs WooCommerce (variations)

Ces attributs créent les variations de produits. Chaque variation aura son propre SKUStock Keeping Unit = identifiant unique du produit ou de sa variation, utilisé pour la gestion de stock et son propre stock dans ATUM.

Attribut Slug technique Valeurs possibles Utilisation
Poids pa_poids 1g, 3g, 5g, 10g, 20g, 50g CBD fumable (fleurs, résines)
Taille pa_taille S, M, L, XL, XXL Streetwear (t-shirts, hoodies)
Couleur pa_couleur Noir, Blanc, Gris, Bleu, etc. Streetwear
Volume pa_volume 10ml, 30ml, 50ml CBD non-fumable (huiles)
Dosage pa_dosage 5%, 10%, 20%, 30% CBD non-fumable (huiles)
Spectre pa_spectre Isolat, Broad, Full CBD (fiches produits)

Règle interne : CBD fumable = toujours produit variableType de produit WooCommerce qui permet d'avoir plusieurs versions (variations) d'un même produit avec des SKU et stocks différents "Poids". Streetwear = toujours variable "Taille/Couleur". CBD non-fumable = simple si 1 format, variable si multi-formats.

TABLEAU 3 : Structure URLs (slugs finaux)

Ces URLs doivent exister identiques en DEV et en PROD. Aucun renommage après mise en production. C'est la base du SEOSearch Engine Optimization = référencement naturel. Des URLs stables et bien structurées sont essentielles pour Google.

A. Pages communes WooCommerce

Page Slug Note
Boutique (générique) /boutique/ Si besoin, sinon redirection
Panier /panier/ Noindex obligatoire
Commande /commande/ Noindex obligatoire
Mon compte /mon-compte/ Noindex obligatoire

B. Pages B2C (3xFiltré)

Zone Slug Indexation
Accueil / Index
Shop public /shop/ Selon stratégie
Catégorie /categorie/{slug}/ Selon stratégie
Produit /produit/{slug}/ Contrôlé par produit
Blog /blog/ Index
Article /blog/{slug}/ Index
À propos /a-propos/ Index
Contact /contact/ Index

C. Pages B2B (Kanalpha)

Zone Slug Indexation
Hub pro /pro/ Noindex strict
Catalogue pro /pro/catalogue/ Noindex strict
Commandes /pro/commandes/ Noindex strict
Clients (commercial) /pro/clients/ Noindex strict
Statistiques /pro/statistiques/ Noindex strict

Règle Rank Math : Toutes les pages sous /pro/* doivent être en noindexDirective qui dit à Google de ne pas indexer la page. Elle existe mais n'apparaîtra jamais dans les résultats de recherche automatique. Configuration à faire dans Rank Math : Add Rule → URL contains "/pro/" → Set Robots Meta: noindex, nofollow

Phase 1 complète : Architecture et fondations posées
Suite : PARTIE 2 - Produits, B2BKing & Gestion de stock
FEUILLE DE ROUTE TECHNIQUE
PARTIE 2 : Produits, B2BKing & Gestion de Stock
Configuration B2BKing Visibilité produits Stock ATUM Prix B2B

Objectif de cette partie : Configurer la gestion des produits, leur visibilité selon les groupes B2B, les prix différenciés, et la gestion de stock avec ATUM. À la fin de cette partie, chaque produit saura s'afficher (ou se cacher) selon le domaine et le profil utilisateur.

TABLEAU 4 : Groupes B2BKing (visibilité & prix)

Les groupes B2BKingGroupes d'utilisateurs qui déterminent quels produits sont visibles, à quels prix, et sur quel domaine. C'est le cœur de la différenciation B2C/B2B contrôlent qui voit quoi et à quel prix. Chaque produit sera assigné à un ou plusieurs groupes.

Groupe Usage Visible 3xFiltré (B2C) Visible Kanalpha (B2B) Prix Indexation
PUBLIC_B2C Produits grand public ✅ Oui ❌ Non Prix public Selon stratégie SEO
PRO_B2B Produits professionnels ❌ Non ✅ Oui Prix B2B Noindex
MIXTE Visible des deux côtés ✅ Oui ✅ Oui Prix différenciés Très contrôlé
INTERNE Caché partout (dev/test) ❌ Non ❌ Non Noindex strict
SENSIBLE_CBD CBD sensible (B2B only) ❌ Non ✅ Oui Prix B2B Noindex strict

Règle d'or : Les groupes contrôlent l'affichage, PAS le stock. Le stock est géré par ATUM et reste unique pour tous les groupes. Seul l'affichage du stock (texte visible) change selon le profil.

Configuration B2BKing : Créer les groupes

1
Accéder aux groupes B2BKing

B2BKing → Groups → Add New Group

2
Créer chaque groupe avec ces paramètres
  • PUBLIC_B2C : Name = "PUBLIC_B2C" / Type = Customer / Status = Active
  • PRO_B2B : Name = "PRO_B2B" / Type = Business / Status = Active
  • MIXTE : Name = "MIXTE" / Type = Business / Status = Active
  • INTERNE : Name = "INTERNE" / Type = Hidden / Status = Active
  • SENSIBLE_CBD : Name = "SENSIBLE_CBD" / Type = Business / Status = Active

Gestion stock CBD fumable : La vraie logique (vrac + conditionnement)

Réalité du terrain : Pour les fleurs et résines CBD, on ne stocke PAS par gramme pré-conditionné (1g, 3g, 5g). On stocke en vrac (kilos), puis on conditionne à la demande ou par batch. Le stock réel = poids brut en kg, pas des "unités 1g".

Modèle de stock pour CBD fumable

Produit Stock ATUM (réel) Unité stock Date péremption / Lot Délai conditionnement
Fleur Amnesia Indoor 10,5 kg Kilogrammes (vrac) Lot 2026-01 / Péremption : 31/12/2026 Immédiat (pesé à la commande)
Résine Marocaine 8,2 kg Kilogrammes (vrac) Lot 2025-12 / Péremption : 30/06/2026 Immédiat

Logique : Un client commande "5g de Fleur Amnesia" → Le stock ATUM passe de 10,5 kg à 10,495 kg. On ne gère pas "combien de sachets 5g il reste", mais "combien de kg brut il reste".

Configuration ATUM pour stock en vrac (kg)

1
Unité de stock = Kilogrammes

Dans ATUM → Settings → General → Stock Units

  • Créer une unité custom "kg" (kilogrammes)
  • Assigner cette unité aux produits CBD fumables
2
Produit WooCommerce = Variable (choix poids)

Le produit reste variable (attribut "Poids" : 1g, 3g, 5g, 10g) pour le front-end, mais toutes les variations pointent vers le même stock ATUM en kg.

  • Variation "1g" → Prix 10 € → Décompte 0,001 kg du stock
  • Variation "3g" → Prix 27 € → Décompte 0,003 kg du stock
  • Variation "5g" → Prix 42 € → Décompte 0,005 kg du stock
  • Variation "10g" → Prix 80 € → Décompte 0,01 kg du stock
3
Champs custom ATUM : Lot + Péremption

Ajouter des champs personnalisés dans ATUM (via Settings → Custom Fields ou plugin ACF)

  • Lot n° (texte) : Ex "2026-01-AMNESIA"
  • Date péremption (date) : Ex "31/12/2026"
  • Date conditionnement (date) : Ex "15/01/2026"
  • Délai conditionnement (texte) : "Immédiat" ou "24-48h"
  • Origine lot (texte) : "Italie - Fournisseur X"
4
Affichage front : "Disponible en vrac"

Le texte stock affiché ne dit pas "42 sachets de 5g restants" mais :

  • B2C : "En stock" (tant que kg > seuil)
  • B2B client : "Disponible" ou "Stock limité" (si < seuil kg)
  • Commercial : "Disponible (8,2 kg)" → quantité brute visible

TABLEAU 5 : Modèle produits (champs obligatoires)

Pour chaque produit créé dans WooCommerce, ces champs doivent être remplis dès le DEV. Pas d'improvisation après.

Champ Usage Obligatoire Exemple CBD fumable
SKUStock Keeping Unit = identifiant unique du produit. Ne jamais modifier après création. Format : 3XF-CBD-001 ou KA-STR-T001 Identifiant unique produit ✅ Oui 3XF-CBD-AMNESIA
Catégorie Déclenche template Elementor ✅ Oui CBD → Fumable
Groupe B2BKing Visibilité / prix ✅ Oui PUBLIC_B2C ou PRO_B2B
Stock ATUM (vrac) Quantité en kg ✅ Oui 10,5 kg
Lot n° Traçabilité ✅ Oui 2026-01-AMNESIA
Date péremption DLUO / fin de vie ✅ Oui 31/12/2026
Délai conditionnement Info logistique Recommandé Immédiat / 24-48h
Indexation (Rank Math) Contrôle SEO ✅ Oui Index ou Noindex
Image principale Visuel produit ✅ Oui amnesia-indoor-20.webp
Prix par gramme Base calcul variations ✅ Oui 10 €/g (B2C) / 7 €/g (B2B)

TABLEAU 6 : Typologie produits (classification technique)

Selon la famille de produit, le type WooCommerce et la gestion des variations changent. Voici la matrice complète.

Famille Type WooCommerce Variation Attribut(s) Stock ATUM
CBD fumable (grammes) Variable Oui Poids (1g/3g/5g/10g/20g/50g) Stock unique en kg (vrac) — toutes variations décomptent du même stock
CBD non-fumable (unités) Simple ou Variable Si formats multiples Volume (ml), Dosage (mg) Stock par unité (flacons/boîtes)
Streetwear Variable Oui Taille, Couleur Stock par variation (T-shirt Noir M = X unités)

Règle interne CBD fumable : 1 produit "Fleur Amnesia" = 1 stock ATUM en kg. Les variations (1g, 3g, 5g) sont des présentations commerciales, pas des stocks séparés. Quand un client achète 5g, on décompte 0,005 kg du stock unique.

Exemple concret : Fleur Amnesia Indoor 20%

📦
Stock réel ATUM
Quantité brute 10,5 kg
Lot 2026-01-AMNESIA
Péremption 31/12/2026
Seuil alerte < 2 kg
💰
Variations front-end
1g 10 € (B2C) / 7 € (B2B)
3g 27 € (B2C) / 19 € (B2B)
5g 42 € (B2C) / 30 € (B2B)
10g 80 € (B2C) / 56 € (B2B)

Scénario : Client B2C commande 5g → Décompte 0,005 kg → Nouveau stock : 10,495 kg. Client B2B commande 50g → Décompte 0,05 kg → Nouveau stock : 10,445 kg. Tout automatique via WooCommerce + ATUM.

Configuration ATUM : Gestion de stock avancée

ATUMATUM Inventory Management for WooCommerce = plugin de gestion de stock avancée. Il permet de gérer les fournisseurs, coûts, seuils d'alerte, et statistiques de stock gère le stock réel (quantité physique). WooCommerce affiche ce stock selon les règles définies par profil.

Étapes de configuration ATUM

1
Installer ATUM Inventory

Extensions → Ajouter → Rechercher "ATUM Inventory Management for WooCommerce" → Installer et Activer

2
Configuration initiale ATUM

ATUM → Settings → General Settings

  • Activer "Manage Stock at Product Level"
  • Activer "Manage Stock at Variation Level" (pour produits variables)
  • Définir les seuils d'alerte (Low Stock, Out of Stock)
3
Créer les fournisseurs

ATUM → Suppliers → Add New

  • Nom du fournisseur (ex: "Fournisseur CBD Italie")
  • Informations contact (email, téléphone)
  • Délai de livraison moyen
  • Notes internes
4
Configurer les produits dans ATUM

Pour chaque produit : Éditer le produit → Onglet "ATUM"

  • Activer "ATUM Control" sur ce produit
  • Définir le fournisseur
  • Définir le coût d'achat (prix d'achat fournisseur)
  • Définir le seuil de stock minimum (Low Stock Threshold)
  • Définir le niveau de réapprovisionnement (Reorder Level)

TABLEAU 7 : Stock visible selon profil

Le stock réel est dans ATUM. Mais le texte affiché à l'utilisateur change selon son profil. C'est géré par le mu-pluginMust-Use Plugin = plugin qui se charge automatiquement avant tout le reste. Placé dans /wp-content/mu-plugins/, il ne peut pas être désactivé (Partie 3).

Profil Affichage stock Quantité exacte Objectif
Visiteur / Client B2C "En stock" / "Rupture" ❌ Non Simplicité, pas d'info sensible
Client B2B "Disponible" / "Stock limité" / "Rupture" ❌ Non (par défaut) Éviter tensions / promesses
Commercial B2B "Disponible (42)" → avec quantité ✅ Oui Vendre sans surpromettre
Admin / Stock Manager Tout visible ✅ Oui Pilotage

Texte stock standardisé (front)

Niveau stock Texte affiché Condition
OK "En stock" / "Disponible" stock > seuil
Limité "Stock limité" stock ≤ seuil_limite
Rupture "Rupture" stock = 0
Backorder (si autorisé) "Sur commande" backorders enabled

Seuils "stock limité" recommandés

🌿
CBD Fumable
Seuil limite ≤ 10 unités/variation
Exemple Amnesia 5g : 8 restants → "Stock limité"
👕
Streetwear
Seuil limite ≤ 3-5 par variation
Exemple T-shirt Noir M : 2 restants → "Stock limité"

TABLEAU 8 : Prix et règles d'affichage (B2BKing)

Les prix diffèrent selon le profil. B2BKing gère cela automatiquement une fois configuré.

Profil Domaine Prix affiché Remises TVA / règles
B2C 3xFiltré Prix public TTC Coupons publics B2C (20% TVA affichée)
Client B2B Kanalpha Prix B2B (groupes) Tarifs par groupe/client B2B (HT par défaut)
Commercial B2B Kanalpha Prix B2B client Commission séparée B2B
Admin Tous Tous Tous Tous

Configuration prix B2B dans B2BKing

1
Prix par groupe B2B

B2BKing → Dynamic Rules → Add New Rule

  • Type : "Discount (Percentage)" ou "Fixed Price"
  • Applies to : "All Products" ou catégorie spécifique
  • For : Groupe "PRO_B2B"
  • Value : -30% ou prix fixe
2
Prix par client spécifique (VIP)

B2BKing → Dynamic Rules → Add New Rule

  • Type : "Fixed Price"
  • Applies to : Produit spécifique
  • For : User "Client VIP X"
  • Value : 25 € (au lieu de 42 €)
3
Masquer prix B2B sur B2C

B2BKing → Settings → Hide Prices

  • Activer "Hide Prices for Guest Users" (sur Kanalpha)
  • Message personnalisé : "Connectez-vous pour voir les prix"

Checklist : Créer un produit CBD fumable complet

À chaque fois que vous créez un nouveau produit CBD fumable, suivez cette checklist complète. Ne rien oublier.

Checklist produit CBD fumable (vrac + conditionnement)
  • Nom produit : "Fleur CBD Amnesia Indoor 20%"
  • SKU : 3XF-CBD-AMNESIA
  • Type : Variable
  • Catégorie : CBD → Fumable
  • Attribut : Poids (1g, 3g, 5g, 10g, 20g, 50g)
  • Stock ATUM : 10,5 kg (vrac) — stock unique pour toutes variations
  • Lot n° : 2026-01-AMNESIA
  • Date péremption : 31/12/2026
  • Délai conditionnement : Immédiat
  • Origine : Italie - Fournisseur X

Variations créées (décomptent du stock vrac) :

VariationPrix B2CPrix B2BDécompte stock
1g10 €7 €-0,001 kg
3g27 €19 €-0,003 kg
5g42 €30 €-0,005 kg
10g80 €56 €-0,01 kg
20g150 €105 €-0,02 kg
50g350 €245 €-0,05 kg
  • Groupe B2BKing : PUBLIC_B2C (ou MIXTE si B2B aussi)
  • Image principale uploadée (nom : amnesia-indoor-20.webp)
  • Galerie images (3-5 photos produit)
  • Description courte (150 mots)
  • Description longue (600-1000 mots, optimisée SEO)
  • ATUM configuré : fournisseur, coût/kg, seuil alerte (< 2 kg)
  • Rank Math : Index ou Noindex selon stratégie
  • Balise ALT image : "Fleur CBD Amnesia indoor 20% trichomes"

Note importante : Les variations ne sont que des présentations commerciales. Le stock réel reste unique (10,5 kg). Quand une commande arrive, WooCommerce décompte automatiquement le bon poids (0,005 kg pour 5g, 0,01 kg pour 10g, etc.).

Phase 2 complète : Produits configurés, B2BKing opérationnel, ATUM en place
Suite : PARTIE 3 - Templates Elementor, mu-plugin & Front-end
FEUILLE DE ROUTE TECHNIQUE
PARTIE 3 : Templates, mu-plugin & Front-end (FINALE)
Elementor + Striz mu-plugin complet Emails routing CSS domaine

Objectif final : Créer les templates Elementor, installer le mu-plugin complet qui gère les headers/footers/emails/stock selon domaine, configurer le thème Striz, et finaliser l'expérience front-end. À la fin de cette partie, les 2 fronts seront 100% opérationnels.

TABLEAU 9 : Templates Elementor à créer

Dans Elementor → Templates → Theme Builder, créer ces templates avec un naming strict. Noter les IDs de chaque template (visible dans l'URL lors de l'édition).

Template Type Elementor Nom technique Condition d'affichage Note ID
Header B2C Header HDR_3XF Géré par mu-plugin (domaine 3xFiltré) _____
Footer B2C Footer FTR_3XF Géré par mu-plugin (domaine 3xFiltré) _____
Header B2B Header HDR_KA Géré par mu-plugin (domaine Kanalpha) _____
Footer B2B Footer FTR_KA Géré par mu-plugin (domaine Kanalpha) _____
Single CBD Fumable Single Product SP_CBD_FUMABLE Products in Category "CBD → Fumable"
Single CBD Non-fumable Single Product SP_CBD_NONFUMABLE Products in Category "CBD → Non-fumable"
Single Streetwear Single Product SP_STREETWEAR Products in Category "Streetwear"
Archive CBD Product Archive AR_CBD Categories under "CBD"
Archive Streetwear Product Archive AR_STREETWEAR Categories under "Streetwear"

IMPORTANT : Pour les headers/footers, NE PAS définir de conditions dans Elementor. Le mu-plugin se chargera de les afficher selon le domaine. Pour les Single/Archive, définir les conditions normalement (catégories).

Étapes création template Elementor

1
Créer le template

Elementor → Templates → Theme Builder → Add New → Choisir le type (Header, Footer, Single Product, Archive)

2
Designer le template

Utiliser les widgets Elementor + widgets Woo (pour Single/Archive). Construire le design complet.

3
Sauvegarder et noter l'ID

Après sauvegarde, l'URL contient post=XXXX. Noter cet ID pour les headers/footers (nécessaire pour le mu-plugin).

Configuration thème Striz

Puisque tu utilises déjà Striz et qu'il a ses propres headers/footers, il faut désactiver les headers/footers Striz pour laisser Elementor + mu-plugin gérer tout.

1
Désactiver header/footer Striz

Apparence → Personnaliser → Header & Footer → Désactiver l'affichage du header et footer natifs

OU si Striz n'a pas cette option :

Le mu-plugin inclut du CSS pour cacher automatiquement les headers/footers Striz. Vérifier les sélecteurs CSS dans la config du mu-plugin (section CSS_HIDE_THEME_HEADER).

2
Vérifier les hooks Striz

Inspecter le code source d'une page en dev. Repérer les sélecteurs du header/footer Striz (ex: header.site-header, footer.site-footer). Les noter pour le mu-plugin.

Le mu-plugin complet (cœur du système)

Fichier unique à créer
/wp-content/mu-plugins/xf-core.php
Gère domaine, headers/footers, stock, emails, restrictions back-office

Installation mu-plugin

1
Créer le dossier mu-plugins

Via FTP ou File Manager : créer /wp-content/mu-plugins/ s'il n'existe pas

2
Créer le fichier xf-core.php

Dans /wp-content/mu-plugins/, créer le fichier xf-core.php

3
Copier le code complet ci-dessous

Copier tout le code PHP dans xf-core.php

4
Remplir la section CONFIG

Dans le code, chercher la section CONFIG et remplir les IDs des templates Elementor + domaines + seuils

5
Tester sur chaque domaine

Visiter 3xfiltre.local et kanalpha.local (ou vos domaines de dev) et vérifier que les bons headers/footers s'affichent

Code complet mu-plugin (xf-core.php) — À copier

Templates > cliquer template > URL contient post=XXXX public const ELM_HDR_B2C_ID = 0; // HDR_3XF (remplacer 0 par ID réel) public const ELM_FTR_B2C_ID = 0; // FTR_3XF public const ELM_HDR_B2B_ID = 0; // HDR_KA public const ELM_FTR_B2B_ID = 0; // FTR_KA // Email identities public const FROM_NAME_B2C = '3xFiltré'; public const FROM_EMAIL_B2C = 'no-reply@3xfiltre.fr'; public const FROM_NAME_B2B = 'Kanalpha'; public const FROM_EMAIL_B2B = 'no-reply@kanalpha.fr'; // Stock thresholds pour "Stock limité" (en kg pour CBD fumable) public const LOW_STOCK_CBD_KG = 2.0; // < 2 kg = stock limité public const LOW_STOCK_STREETWEAR = 3; // < 3 unités par variation // Roles public const ROLE_CUSTOMER_B2B = 'customer_b2b'; public const ROLE_SALES_AGENT = 'sales_agent_b2b'; public const ROLE_STOCK_MANAGER = 'stock_manager'; public const ROLE_ACCOUNTING_VIEW = 'accounting_view'; // Striz theme header/footer selectors (à vérifier sur votre site) public const CSS_HIDE_THEME_HEADER = 'header#masthead, header.site-header, .site-header, .striz-header'; public const CSS_HIDE_THEME_FOOTER = 'footer#colophon, footer.site-footer, .site-footer, .striz-footer'; } /** * ========================= * DOMAIN DETECTION * ========================= */ final class XF_Core_Domain { public static function host(): string { $host = $_SERVER['HTTP_HOST'] ?? ''; return strtolower(preg_replace('/:\d+$/', '', trim($host))); } public static function is_b2b(): bool { return (strpos(self::host(), XF_Core_Config::DOMAIN_B2B_PATTERN) !== false); } public static function is_b2c(): bool { return !self::is_b2b(); } public static function body_class(): string { return self::is_b2b() ? 'domain-kanalpha' : 'domain-3xfiltre'; } } /** * Add body class for CSS domain overrides */ add_filter('body_class', function(array $classes) { $classes[] = XF_Core_Domain::body_class(); return $classes; }, 20); /** * Hide Striz theme native header/footer via CSS */ add_action('wp_head', function() { $hide_header = XF_Core_Config::CSS_HIDE_THEME_HEADER; $hide_footer = XF_Core_Config::CSS_HIDE_THEME_FOOTER; echo ""; }, 5); /** * ========================= * ELEMENTOR HEADER/FOOTER INJECTION * ========================= */ function xf_render_elementor_template(int $template_id): void { if ($template_id <= 0) return; if (!did_action('elementor/loaded')) return; $content = \Elementor\Plugin::$instance->frontend->get_builder_content_for_display($template_id, true); if (!empty($content)) { echo $content; } } // Header: render right after add_action('wp_body_open', function() { $tpl = XF_Core_Domain::is_b2b() ? XF_Core_Config::ELM_HDR_B2B_ID : XF_Core_Config::ELM_HDR_B2C_ID; xf_render_elementor_template((int)$tpl); }, 5); // Footer: render near end add_action('wp_footer', function() { $tpl = XF_Core_Domain::is_b2b() ? XF_Core_Config::ELM_FTR_B2B_ID : XF_Core_Config::ELM_FTR_B2C_ID; xf_render_elementor_template((int)$tpl); }, 5); /** * ========================= * EMAIL BRANDING ROUTING * ========================= */ add_filter('wp_mail_from', function($from_email) { return XF_Core_Domain::is_b2b() ? XF_Core_Config::FROM_EMAIL_B2B : XF_Core_Config::FROM_EMAIL_B2C; }, 20); add_filter('wp_mail_from_name', function($from_name) { return XF_Core_Domain::is_b2b() ? XF_Core_Config::FROM_NAME_B2B : XF_Core_Config::FROM_NAME_B2C; }, 20); /** * ========================= * STOCK VISIBILITY (WooCommerce) * ========================= * Levels: * - B2C: "En stock" / "Rupture" (no quantity) * - B2B client: "Disponible" / "Stock limité" / "Rupture" (no quantity) * - Sales agent: shows exact quantity ("Disponible (8,2 kg)") */ function xf_user_is_sales_agent(): bool { return is_user_logged_in() && current_user_can(XF_Core_Config::ROLE_SALES_AGENT); } function xf_user_is_b2b_customer(): bool { return is_user_logged_in() && current_user_can(XF_Core_Config::ROLE_CUSTOMER_B2B); } add_filter('woocommerce_get_availability_text', function($availability, $product) { if (!($product instanceof WC_Product)) return $availability; $stock_qty = $product->get_stock_quantity(); $in_stock = $product->is_in_stock(); if (!$in_stock) { return XF_Core_Domain::is_b2b() ? 'Rupture' : 'Rupture'; } // Determine low stock threshold $threshold = XF_Core_Config::LOW_STOCK_CBD_KG; // If streetwear category, use lower threshold $cats = wc_get_product_term_ids($product->get_id(), 'product_cat'); foreach ($cats as $cat_id) { $term = get_term($cat_id, 'product_cat'); if ($term && !is_wp_error($term) && $term->slug === 'streetwear') { $threshold = XF_Core_Config::LOW_STOCK_STREETWEAR; break; } } // Sales agent can see exact quantity if (XF_Core_Domain::is_b2b() && xf_user_is_sales_agent()) { if (is_numeric($stock_qty)) { // For CBD (kg), show with unit $unit = (strpos($product->get_sku(), 'CBD') !== false) ? ' kg' : ''; return "Disponible ({$stock_qty}{$unit})"; } return "Disponible"; } // B2B customer: "Stock limité" if below threshold (no exact qty) if (XF_Core_Domain::is_b2b() && xf_user_is_b2b_customer()) { if (is_numeric($stock_qty) && $stock_qty <= $threshold) return 'Stock limité'; return 'Disponible'; } // B2C (and guests): simple status return 'En stock'; }, 20, 2); /** * ========================= * ADMIN RESTRICTIONS (Back-office) * ========================= */ add_action('admin_menu', function() { if (!is_user_logged_in()) return; // Stock manager restrictions if (current_user_can(XF_Core_Config::ROLE_STOCK_MANAGER) && !current_user_can('administrator')) { remove_menu_page('plugins.php'); remove_menu_page('tools.php'); remove_menu_page('options-general.php'); remove_menu_page('users.php'); remove_menu_page('themes.php'); remove_menu_page('edit.php'); // posts } // Accounting view restrictions if (current_user_can(XF_Core_Config::ROLE_ACCOUNTING_VIEW) && !current_user_can('administrator')) { remove_menu_page('plugins.php'); remove_menu_page('tools.php'); remove_menu_page('options-general.php'); remove_menu_page('users.php'); remove_menu_page('themes.php'); remove_menu_page('edit.php'); // posts remove_menu_page('edit.php?post_type=product'); // products } }, 99); /** * Block direct access to sensitive admin pages for restricted roles */ add_action('admin_init', function() { if (!is_user_logged_in()) return; $screen = function_exists('get_current_screen') ? get_current_screen() : null; $base = $screen->base ?? ''; // Stock manager: block options/plugins/users if (current_user_can(XF_Core_Config::ROLE_STOCK_MANAGER) && !current_user_can('administrator')) { $blocked = ['plugins', 'plugin-install', 'users', 'user-edit', 'options-general', 'themes']; foreach ($blocked as $b) { if (strpos($base, $b) !== false) { wp_safe_redirect(admin_url()); exit; } } } // Accounting view: block settings/plugins/users/themes/products editing if (current_user_can(XF_Core_Config::ROLE_ACCOUNTING_VIEW) && !current_user_can('administrator')) { $blocked = ['plugins', 'plugin-install', 'users', 'user-edit', 'options-general', 'themes', 'post', 'edit']; foreach ($blocked as $b) { if (strpos($base, $b) !== false) { wp_safe_redirect(admin_url()); exit; } } } });

Le mu-plugin est maintenant actif ! Il se charge automatiquement à chaque requête. Pas besoin de l'activer dans l'admin WordPress.

CSS par domaine (surcharge styles)

Pour différencier visuellement 3xFiltré et Kanalpha (couleurs, polices, etc.), ajouter du CSS qui cible la classe body ajoutée par le mu-plugin.

Où ajouter le CSS ?

Apparence → Personnaliser → CSS Additionnel (ou dans le fichier style.css de votre thème enfant)

Exemple CSS domaine

/* ========================= STYLES 3xFiltré (B2C) ========================= */ .domain-3xfiltre { --primary-color: #3B82F6; --secondary-color: #F59E0B; --text-color: #1a202c; } .domain-3xfiltre .elementor-button { background: var(--primary-color); } .domain-3xfiltre .site-logo img { content: url('/wp-content/uploads/logo-3xfiltre.svg'); } /* ========================= STYLES Kanalpha (B2B) ========================= */ .domain-kanalpha { --primary-color: #10B981; --secondary-color: #6366F1; --text-color: #0f172a; } .domain-kanalpha .elementor-button { background: var(--primary-color); } .domain-kanalpha .site-logo img { content: url('/wp-content/uploads/logo-kanalpha.svg'); }

Configuration emails (EmailKit + routing)

Avec EmailKitPlugin qui permet de designer visuellement les emails transactionnels WooCommerce (commande, confirmation, facture, etc.), créer 2 sets de templates email : un pour 3xFiltré, un pour Kanalpha.

Étapes EmailKit

1
Installer EmailKit

Extensions → Ajouter → Rechercher "EmailKit" → Installer et Activer

2
Créer les templates B2C (3xFiltré)

EmailKit → Templates → Créer les emails suivants avec branding 3xFiltré :

  • New Order (admin)
  • Processing Order (client)
  • Completed Order (client)
  • Customer Invoice
  • New Account / Reset Password
3
Dupliquer pour B2B (Kanalpha)

Dupliquer chaque template et modifier le branding (logo Kanalpha, couleurs vertes, footer B2B)

Limitation EmailKit : EmailKit ne permet pas nativement de "switcher" de template selon le domaine. Le mu-plugin gère déjà le From/From Name. Pour le branding visuel (logo/couleurs), deux options :

Option A (simple) : Utiliser des images conditionnelles dans le template email (via PHP dans le template WooCommerce)

Option B (avancée) : Hook EmailKit pour charger template B2C ou B2B selon domaine (nécessite code custom supplémentaire)

Tests finaux avant mise en prod

Checklist complète à valider sur les 2 domaines de dev.

Tests domaine 3xFiltré (B2C)

  • Header B2C s'affiche correctement
  • Footer B2C s'affiche correctement
  • Produits PUBLIC_B2C visibles, PRO_B2B cachés
  • Stock affiché : "En stock" / "Rupture" (pas de quantité)
  • Prix B2C affichés
  • Template Single CBD/Streetwear correct
  • Email test : From = 3xFiltré, logo 3xFiltré
  • CSS couleurs 3xFiltré appliqué
  • Classe body = domain-3xfiltre

Tests domaine Kanalpha (B2B)

  • Header B2B s'affiche correctement
  • Footer B2B s'affiche correctement
  • Produits PRO_B2B visibles, PUBLIC_B2C cachés
  • Stock affiché client B2B : "Disponible" / "Stock limité"
  • Stock affiché commercial : "Disponible (8,2 kg)" (quantité visible)
  • Prix B2B affichés (différents de B2C)
  • Pages /pro/* accessibles et noindex
  • Email test : From = Kanalpha, logo Kanalpha
  • CSS couleurs Kanalpha appliqué
  • Classe body = domain-kanalpha

Test commande complète

  • B2C : Ajouter 5g CBD au panier → Stock décompté de 0,005 kg
  • B2B : Ajouter 50g CBD au panier → Stock décompté de 0,05 kg
  • Commande finalisée → Email envoyé avec bon branding
  • Stock ATUM mis à jour correctement
  • Commercial peut voir quantité exacte après connexion
Projet finalisé
2 Fronts Opérationnels
3xFiltré & Kanalpha fonctionnels sur 1 seul back-office
Félicitations ! L'architecture technique est complète.
Prochain step : Remplir les produits, tester en réel, puis migration en production

Toutes les fondations sont posées. Le système est scalable, maintenable, et prêt pour la croissance.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

CLOSE
Select the fields to be shown. Others will be hidden. Drag and drop to rearrange the order.
  • Image
  • SKU
  • Rating
  • Price
  • Stock
  • Availability
  • Add to cart
  • Description
  • Content
  • Weight
  • Dimensions
  • Additional information
Click outside to hide the comparison bar
Compare
Add to cart