mise a jour prix tarif fab dis ecommerce
Mettre à jour ses prix sur son site e-commerce en 1 clic grâce au FAB-DIS

Mettre à jour ses prix sur son site e-commerce en 1 clic grâce au FAB-DIS

Régulièrement, vous recevez le fichier FAB-DIS de votre fournisseur avec les nouveaux tarifs. Et régulièrement, c’est le même calvaire : ouvrir votre back-office WooCommerce ou Shopify, modifier manuellement les prix de 500, 1000, parfois 2000 produits. Résultat ? Une journée entière perdue dans des tâches répétitives, des erreurs de saisie, et des clients qui voient parfois des prix obsolètes pendant plusieurs jours.

Il existe pourtant une solution simple : connecter automatiquement votre fichier FAB-DIS nettoyé à votre plateforme e-commerce via un import CSV optimisé. En 10 minutes chrono, tous vos prix sont à jour, sans erreur, sans intervention manuelle. Dans cet article, nous vous montrons exactement comment faire avec un cas pratique détaillé, des captures d’écran et un template prêt à l’emploi.

💡 AutoExcel automatise la connexion FAB-DIS → e-commerce

Nous créons des scripts personnalisés qui transforment automatiquement vos fichiers FAB-DIS en imports compatibles WooCommerce, Shopify, PrestaShop ou toute autre plateforme. Demandez un devis gratuit.

Le problème : mettre à jour manuellement son site e-commerce est un gouffre de temps

Pour bien comprendre l’enjeu, prenons un cas concret que nous avons accompagné : une TPE spécialisée dans la vente de matériel électronique en ligne avec un catalogue de 2 300 produits sur WooCommerce.

📊 Étude de cas

Électro-Pro (nom anonymisé)

TPE – 4 salariés – CA 800 K€

2 300 produits sur WooCommerce

3 fournisseurs avec FAB-DIS

La situation avant automatisation

Processus manuel vécu :

  1. Lundi matin : réception des FAB-DIS (3 fichiers Excel de 3 fournisseurs différents)
  2. Lundi après-midi : tri et identification des produits concernés (environ 400 à 600 produits avec modifications tarifaires)
  3. Mardi toute la journée : mise à jour manuelle
    • Recherche du produit dans le back-office WooCommerce
    • Modification du prix TTC
    • Vérification de la cohérence prix barré / prix promo
    • Enregistrement
    • Passage au produit suivant
  4. Mercredi matin : contrôle qualité et correction des erreurs de saisie

❌ Avant l’automatisation

12 heures/semaine
  • 8h de saisie manuelle
  • 2h de recherche/tri
  • 2h de contrôle/correction
  • Erreurs fréquentes
  • Décalage de 2-3 jours
  • Stress et frustration

✅ Après l’automatisation

15 minutes/semaine
  • 5 min de préparation
  • 10 min d’import automatique
  • 0 erreur de saisie
  • Mise à jour immédiate
  • Processus fluide
  • Sérénité retrouvée

Témoignage du gérant d’Électro-Pro :

« Avant, ma collaboratrice passait deux jours par semaine juste à mettre à jour les prix. C’était démotivant, répétitif, et source d’erreurs. Depuis qu’on a automatisé avec le script de transformation FAB-DIS → CSV WooCommerce, elle fait ça en 10-15 minutes le lundi matin. Ça nous a littéralement changé la vie. Elle peut maintenant se concentrer sur des tâches à valeur ajoutée : optimisation SEO des fiches produits, création de contenus, relation client. »

La solution globale : du FAB-DIS à votre site e-commerce en 3 étapes

Voici le processus complet pour mettre à jour automatiquement les prix de votre site e-commerce à partir d’un fichier FAB-DIS :

📥
Étape 1

Réception et nettoyage du FAB-DIS

⚙️
Étape 2

Transformation en format e-commerce (CSV)

🚀
Étape 3

Import automatique WooCommerce/Shopify

Étape 1 : Préparer le fichier FAB-DIS

Avant de pouvoir importer vos données dans votre plateforme e-commerce, vous devez d’abord préparer votre fichier FAB-DIS :

  1. Nettoyer les libellés : corriger les majuscules, supprimer les espaces superflus
  2. Valider l’intégrité : vérifier l’absence de doublons, GTIN manquants, incohérences de prix
  3. Appliquer vos coefficients : calculer vos prix de vente à partir des prix d’achat fournisseur

Ces étapes peuvent également être automatisées avec des scripts Office Scripts. Consultez nos guides dédiés pour chaque étape.

Étape 2 : Transformer le FAB-DIS en CSV compatible e-commerce

C’est l’étape clé : convertir les colonnes du format FAB-DIS (CODEART, PUTTC, LIBELLE40, etc.) vers le format attendu par votre plateforme e-commerce (SKU, Regular Price, Name, etc.).

Principe du mapping de colonnes :

Colonne FAB-DIS Colonne WooCommerce Colonne Shopify
CODEART SKU Handle
GTIN / EAN Meta: _sku ou custom field Variant Barcode
LIBELLE80 Name Title
LIBELLE240 Description Body HTML
PUTTC Regular price Variant Price
STOCK Stock Variant Inventory Qty
CODEFAMILLE Categories Type

Étape 3 : Import dans la plateforme e-commerce

Une fois le fichier CSV préparé selon le format de votre plateforme, l’import se fait en quelques clics via les outils natifs ou des plugins dédiés.

Guide technique détaillé : WooCommerce et Shopify

Voici le tutoriel complet pour automatiser la mise à jour de vos prix sur les deux plateformes e-commerce les plus populaires.

WooCommerce

Part de marché : 36% des sites e-commerce

Outil d’import : WooCommerce Product CSV Import Suite (natif) ou plugins tiers

Format : CSV avec colonnes spécifiques

Shopify

Part de marché : 28% des sites e-commerce

Outil d’import : Import CSV natif ou apps Shopify

Format : CSV template Shopify standard

Script de transformation FAB-DIS → WooCommerce

Voici le script Office Scripts qui transforme automatiquement votre FAB-DIS en CSV compatible WooCommerce :

function main(workbook: ExcelScript.Workbook) {
  // Feuille source (FAB-DIS)
  const sourceSheet: ExcelScript.Worksheet = workbook.getActiveWorksheet();
  const usedRange: ExcelScript.Range | undefined = sourceSheet.getUsedRange();

  if (!usedRange) {
    console.log("❌ Erreur : feuille vide");
    console.log("La feuille active ne contient aucune donnée.");
    return;
  }

  console.log(`📄 Feuille active: ${sourceSheet.getName()}`);
  console.log(`📊 Plage utilisée: ${usedRange.getAddress()}`);
  console.log(`📈 Dimensions: ${usedRange.getRowCount()} lignes x ${usedRange.getColumnCount()} colonnes`);

  // Récupère les en-têtes FAB-DIS avec plus de robustesse
  const headerRange: ExcelScript.Range = sourceSheet.getRange("1:1");
  console.log(`🔍 Recherche des en-têtes dans: ${headerRange.getAddress()}`);

  const headerValues: (string | number | boolean)[][] = headerRange.getValues();

  // Affichage de débogage - une seule fois
  console.log("🔍 Valeurs de la ligne 1:", JSON.stringify(headerValues));

  if (!headerValues || headerValues.length === 0 || headerValues[0].every(cell => !cell || String(cell).trim() === "")) {
    console.log("⚠️ Aucune valeur trouvée dans la ligne 1, tentative avec la première ligne non vide...");

    // Essaye de trouver la première ligne non vide comme en-tête
    const allValues: (string | number | boolean)[][] = sourceSheet.getUsedRange().getValues();
    let headerRowIndex: number = 0;
    let headerFound: boolean = false;

    // Cherche la première ligne avec au moins une cellule non vide
    for (let i = 0; i < Math.min(5, allValues.length); i++) {
      const row: (string | number | boolean)[] = allValues[i];
      const hasContent: boolean = row.some(cell => cell && String(cell).trim() !== "");
      if (hasContent) {
        headerRowIndex = i;
        headerFound = true;
        console.log(`✅ Ligne ${i + 1} sélectionnée comme en-tête`);
        break;
      }
    }

    if (headerFound) {
      const headers: (string | number | boolean)[] = allValues[headerRowIndex];
      console.log("📋 En-têtes détectés:", JSON.stringify(headers));

      // Si on a trouvé des en-têtes, on traite les données à partir de la ligne suivante
      processData(workbook, sourceSheet, usedRange, headers, headerRowIndex + 1);
    } else {
      console.log("❌ Aucun en-tête trouvé dans les 5 premières lignes");
    }
  } else {
    const headers: (string | number | boolean)[] = headerValues[0];
    console.log("📋 En-têtes de la ligne 1:", JSON.stringify(headers));
    processData(workbook, sourceSheet, usedRange, headers, 1); // Commence à la ligne 2 (0-indexed)
  }
}

function processData(
  workbook: ExcelScript.Workbook,
  sourceSheet: ExcelScript.Worksheet,
  usedRange: ExcelScript.Range,
  headers: (string | number | boolean)[],
  startDataRow: number
): void {
  console.log("🔧 Début du traitement des données...");
  console.log(`📋 En-têtes reçus (${headers.length} colonnes)`);
  console.log(`➡️ Début des données à la ligne: ${startDataRow + 1}`);

  const colIndex: { [key: string]: number } = {};

  // CORRECTION : Noms de colonnes selon votre fichier
  const colonnesRequises: { nomRecherche: string, nomDansFichier: string }[] = [
    { nomRecherche: "CODEART", nomDansFichier: "REFCIALE" },
    { nomRecherche: "GTIN", nomDansFichier: "GTIN" },
    { nomRecherche: "LIBELLE80", nomDansFichier: "LIBELLE80" },
    { nomRecherche: "LIBELLE240", nomDansFichier: "LIBELLE240" },
    { nomRecherche: "PUHT", nomDansFichier: "tarif" }, // "tarif" = PUHT
    { nomRecherche: "STOCK", nomDansFichier: "STOCK" },
    { nomRecherche: "CODEFAMILLE", nomDansFichier: "FAM1L" }
  ];

  // Collecte des messages de log pour éviter d'appeler console.log dans la boucle
  const logMessages: string[] = [];

  // Recherche des colonnes avec plusieurs tentatives pour chaque colonne
  colonnesRequises.forEach(col => {
    // Tentative 1 : Recherche exacte du nom dans le fichier
    let idx: number = headers.findIndex((h: string | number | boolean) => {
      const cellValue: string = String(h || "").trim();
      return cellValue.toUpperCase() === col.nomDansFichier.toUpperCase();
    });

    // Tentative 2 : Recherche partielle
    if (idx === -1) {
      idx = headers.findIndex((h: string | number | boolean) => {
        const cellValue: string = String(h || "").toUpperCase().trim();
        return cellValue.includes(col.nomDansFichier.toUpperCase()) ||
          col.nomDansFichier.toUpperCase().includes(cellValue);
      });
    }

    // Tentative 3 : Recherche par nom original (pour compatibilité)
    if (idx === -1 && col.nomRecherche !== col.nomDansFichier) {
      idx = headers.findIndex((h: string | number | boolean) => {
        const cellValue: string = String(h || "").toUpperCase().trim();
        return cellValue.includes(col.nomRecherche.toUpperCase());
      });
    }

    if (idx !== -1) {
      colIndex[col.nomRecherche] = idx;
      logMessages.push(`✅ Colonne "${col.nomRecherche}" (recherche: "${col.nomDansFichier}") trouvée à l'index ${idx} (colonne ${String.fromCharCode(65 + idx)})`);
    } else {
      logMessages.push(`⚠️ Colonne "${col.nomRecherche}" NON trouvée (cherchée comme "${col.nomDansFichier}")`);
    }
  });

  // Affiche tous les messages de log en une fois
  console.log(logMessages.join('\n'));

  // Vérification des colonnes requises - avec CODEART (qui est REFCIALE)
  if (colIndex["CODEART"] === undefined) {
    console.log("❌ Erreur critique : colonne CODEART (REFCIALE) non trouvée");
    console.log("Vérifiez que votre feuille contient une colonne avec 'REFCIALE' dans l'en-tête");
    const headersDisplay = headers.map((h, i) => `${i}: "${h}"`).join(", ");
    console.log("En-têtes disponibles:", headersDisplay);
    return;
  }

  console.log("📊 Index des colonnes:", JSON.stringify(colIndex));

  // Créer ou récupérer la feuille WooCommerce
  let wooSheet: ExcelScript.Worksheet | undefined = workbook.getWorksheet("Export WooCommerce");
  if (!wooSheet) {
    wooSheet = workbook.addWorksheet("Export WooCommerce");
    console.log("✅ Nouvelle feuille 'Export WooCommerce' créée");
  } else {
    console.log("✅ Feuille 'Export WooCommerce' existante réutilisée");
  }

  // N'active pas la feuille tout de suite pour éviter les conflits
  wooSheet.getRange().clear();

  // En-têtes WooCommerce (format standard)
  const wooHeaders: string[] = [
    "ID", "Type", "SKU", "Name", "Published", "Is featured?",
    "Visibility in catalog", "Short description", "Description",
    "Tax status", "Tax class", "In stock?", "Stock",
    "Backorders allowed?", "Sold individually?", "Weight (kg)",
    "Length (cm)", "Width (cm)", "Height (cm)", "Allow customer reviews?",
    "Regular price", "Sale price", "Categories", "Tags", "Images"
  ];

  // Écrire les en-têtes
  const headerRangeWoo = wooSheet.getRange("A1:Y1");
  headerRangeWoo.setValues([wooHeaders]);
  console.log("✅ En-têtes WooCommerce ajoutés");

  // Récupère les données FAB-DIS
  const totalRows: number = usedRange.getRowCount();
  const dataRows: number = totalRows - startDataRow;

  if (dataRows <= 0) { console.log("❌ Aucune donnée à traiter"); return; } console.log(`📥 Récupération de ${dataRows} lignes de données...`); const dataRange: ExcelScript.Range=  sourceSheet.getRangeByIndexes( startDataRow, 0, dataRows, usedRange.getColumnCount() ); const values: (string | number | boolean)[][]=  dataRange.getValues(); console.log(`📊 ${values.length} lignes de données récupérées`); // Transformation ligne par ligne const wooData: (string | number)[][]=  []; let lignesIgnored: number=  0; let lignesTraitees: number=  0; // Collecte des messages d'erreur de ligne const ligneErrors: string[]=  []; values.forEach((row: (string | number | boolean)[], index: number)=> {
    // Numéro de ligne réel (pour le débogage)
    const ligneReelle: number = startDataRow + index + 1;

    // Vérifier que la ligne a assez de colonnes
    if (row.length <= colIndex["CODEART"]) {
      ligneErrors.push(`⚠️ Ligne ${ligneReelle}: Pas assez de colonnes (${row.length} < ${colIndex["CODEART"] + 1})`);
      lignesIgnored++;
      return;
    }

    const sku: string = String(row[colIndex["CODEART"]] || "").trim();
    if (!sku) {
      lignesIgnored++;
      return; // Ignore les lignes sans code article
    }

    const name: string = String(
      (colIndex["LIBELLE80"] !== undefined && row[colIndex["LIBELLE80"]]) ||
      (colIndex["LIBELLE240"] !== undefined && row[colIndex["LIBELLE240"]]) ||
      ""
    ).trim();

    const description: string = String(
      colIndex["LIBELLE240"] !== undefined ? (row[colIndex["LIBELLE240"]] || "") : ""
    ).trim();

    // IMPORTANT : "tarif" = PUHT, donc on doit calculer le PUTTC (prix TTC)
    const puht: number = colIndex["PUHT"] !== undefined ? Number(row[colIndex["PUHT"]] || 0) : 0;
    const tauxTVA: number = 0.20; // 20% de TVA par défaut
    const puttc: number = puht * (1 + tauxTVA);

    const stock: number = colIndex["STOCK"] !== undefined ? Number(row[colIndex["STOCK"]] || 0) : 0;
    const category: string = colIndex["CODEFAMILLE"] !== undefined ? String(row[colIndex["CODEFAMILLE"]] || "").trim() : "";

    // Ligne WooCommerce
    const wooRow: (string | number)[] = [
      "", // ID (vide pour mise à jour par SKU)
      "simple", // Type
      sku, // SKU
      name, // Name
      "1", // Published (1 = publié)
      "0", // Is featured
      "visible", // Visibility
      name.substring(0, Math.min(name.length, 120)), // Short description (120 car max)
      description, // Description
      "taxable", // Tax status
      "", // Tax class
      stock > 0 ? "1" : "0", // In stock
      stock, // Stock quantity
      "0", // Backorders
      "0", // Sold individually
      "", // Weight
      "", // Length
      "", // Width
      "", // Height
      "1", // Allow reviews
      puttc.toFixed(2), // Regular price (TTC) avec 2 décimales
      "", // Sale price (vide = pas de promo)
      category, // Categories
      "", // Tags
      "" // Images (vide = conserve les images existantes)
    ];

    wooData.push(wooRow);
    lignesTraitees++;
  });

  // Affiche les erreurs de ligne en une fois (si moins de 10 erreurs)
  if (ligneErrors.length > 0 && ligneErrors.length <= 10) { console.log(ligneErrors.join('\n')); } else if (ligneErrors.length> 10) {
    console.log(`⚠️ ${ligneErrors.length} lignes avec erreurs (premières 10 affichées):`);
    console.log(ligneErrors.slice(0, 10).join('\n'));
  }

  console.log(`📝 ${lignesTraitees} produits transformés, ${lignesIgnored} lignes ignorées`);

  // Écrit les données dans la feuille WooCommerce
  if (wooData.length > 0 && wooSheet) {
    // Vérifier que nous avons des données
    if (wooData.length === 0) {
      console.log("❌ Aucune donnée à écrire");
      return;
    }

    // Calculer la plage d'écriture
    const startRow = 2; // Commencer à la ligne 2 (après les en-têtes)
    const endRow = startRow + wooData.length - 1;
    const startCol = 1; // Colonne A
    const endCol = wooHeaders.length; // Dernière colonne (Y)

    console.log(`📝 Écriture des données: Lignes ${startRow} à ${endRow}, Colonnes ${startCol} à ${endCol}`);

    // Créer la plage d'écriture
    const writeRange = wooSheet.getRangeByIndexes(
      startRow - 1, // Convertir en index 0-based
      startCol - 1, // Convertir en index 0-based
      wooData.length,
      wooData[0].length
    );

    console.log(`🔍 Plage créée: ${writeRange.getAddress()}`);
    console.log(`📊 Dimensions données: ${wooData.length} x ${wooData[0].length}`);

    try {
      // Écrire les données
      writeRange.setValues(wooData);
      console.log(`✅ Données écrites avec succès`);

      // Maintenant activer la feuille pour le formatage
      wooSheet.activate();

      // Formatage
      wooSheet.getRange("1:1").getFormat().getFill().setColor("#629552");
      wooSheet.getRange("1:1").getFormat().getFont().setColor("white");
      wooSheet.getRange("1:1").getFormat().getFont().setBold(true);

      // Ajuste les colonnes
      wooSheet.getUsedRange().getFormat().autofitColumns();

      console.log(`✅ Export WooCommerce créé : ${wooData.length} produits`);
      console.log("📥 Enregistrez cette feuille au format CSV pour l'import WooCommerce");
      console.log("📋 Colonnes WooCommerce:", wooHeaders.length);
      console.log("💰 Prix calculés : PUHT → PUTTC (TVA 20%)");

      // Calcul du prix moyen
      const prixTotal: number = wooData.reduce((sum, row) => sum + Number(row[20]), 0);
      const prixMoyen: number = prixTotal / wooData.length;
      console.log(`📊 Prix moyen: ${prixMoyen.toFixed(2)} €`);

    } catch (error) {
      console.log(`❌ Erreur lors de l'écriture des données: ${error}`);
      console.log("Essayez avec une plage plus petite...");

      // Essayer une approche alternative: écrire ligne par ligne
      try {
        for (let i = 0; i < Math.min(wooData.length, 100); i++) {
          const rowRange = wooSheet.getRangeByIndexes(startRow - 1 + i, 0, 1, wooData[i].length);
          rowRange.setValues([wooData[i]]);
        }
        console.log(`✅ ${Math.min(wooData.length, 100)} premières lignes écrites`);
      } catch (error2) {
        console.log(`❌ Échec même avec l'approche ligne par ligne: ${error2}`);
      }
    }

  } else {
    console.log("❌ Aucun produit à exporter");
    console.log("Vérifiez que:");
    console.log("1. Votre feuille source contient des données dans la colonne REFCIALE");
    console.log("2. Les en-têtes sont correctement identifiés");
    console.log("3. Vous avez au moins une ligne de données après les en-têtes");
  }
}

Script de transformation FAB-DIS → Shopify

Pour Shopify, le format CSV est légèrement différent. Voici le script adapté :

function main(workbook: ExcelScript.Workbook) {
  const sourceSheet: ExcelScript.Worksheet = workbook.getActiveWorksheet();
  const usedRange: ExcelScript.Range | undefined = sourceSheet.getUsedRange();
  
  if (!usedRange) {
    console.log("❌ Erreur : feuille vide");
    console.log("La feuille active ne contient aucune donnée.");
    return;
  }
  
  console.log(`📄 Feuille active: ${sourceSheet.getName()}`);
  console.log(`📊 Plage utilisée: ${usedRange.getAddress()}`);
  console.log(`📈 Dimensions: ${usedRange.getRowCount()} lignes x ${usedRange.getColumnCount()} colonnes`);
  
  // Récupère les en-têtes FAB-DIS
  const headerRange: ExcelScript.Range = sourceSheet.getRange("1:1");
  console.log(`🔍 Recherche des en-têtes dans: ${headerRange.getAddress()}`);
  
  const headerValues: (string | number | boolean)[][] = headerRange.getValues();
  
  console.log("🔍 Valeurs de la ligne 1:", JSON.stringify(headerValues));
  
  if (!headerValues || headerValues.length === 0 || headerValues[0].every(cell => !cell || String(cell).trim() === "")) {
    console.log("⚠️ Aucune valeur trouvée dans la ligne 1, tentative avec la première ligne non vide...");
    
    // Essaye de trouver la première ligne non vide comme en-tête
    const allValues: (string | number | boolean)[][] = sourceSheet.getUsedRange().getValues();
    let headerRowIndex: number = 0;
    let headerFound: boolean = false;
    
    // Cherche la première ligne avec au moins une cellule non vide
    for (let i = 0; i < Math.min(5, allValues.length); i++) {
      const row: (string | number | boolean)[] = allValues[i];
      const hasContent: boolean = row.some(cell => cell && String(cell).trim() !== "");
      if (hasContent) {
        headerRowIndex = i;
        headerFound = true;
        console.log(`✅ Ligne ${i + 1} sélectionnée comme en-tête`);
        break;
      }
    }
    
    if (headerFound) {
      const headers: (string | number | boolean)[] = allValues[headerRowIndex];
      console.log("📋 En-têtes détectés:", JSON.stringify(headers));
      
      // Si on a trouvé des en-têtes, on traite les données à partir de la ligne suivante
      processDataShopify(workbook, sourceSheet, usedRange, headers, headerRowIndex + 1);
    } else {
      console.log("❌ Aucun en-tête trouvé dans les 5 premières lignes");
    }
  } else {
    const headers: (string | number | boolean)[] = headerValues[0];
    console.log("📋 En-têtes de la ligne 1:", JSON.stringify(headers));
    processDataShopify(workbook, sourceSheet, usedRange, headers, 1);
  }
}

function processDataShopify(
  workbook: ExcelScript.Workbook,
  sourceSheet: ExcelScript.Worksheet, 
  usedRange: ExcelScript.Range, 
  headers: (string | number | boolean)[], 
  startDataRow: number
): void {
  console.log("🔧 Début du traitement des données Shopify...");
  console.log(`📋 En-têtes reçus (${headers.length} colonnes)`);
  console.log(`➡️ Début des données à la ligne: ${startDataRow + 1}`);
  
  const colIndex: { [key: string]: number } = {};
  
  // Noms de colonnes selon votre fichier
  const colonnesRequises: {nomRecherche: string, nomDansFichier: string}[] = [
    {nomRecherche: "CODEART", nomDansFichier: "REFCIALE"},
    {nomRecherche: "GTIN", nomDansFichier: "GTIN"},
    {nomRecherche: "LIBELLE80", nomDansFichier: "LIBELLE80"},
    {nomRecherche: "LIBELLE240", nomDansFichier: "LIBELLE240"},
    {nomRecherche: "PUHT", nomDansFichier: "tarif"}, // "tarif" = PUHT
    {nomRecherche: "STOCK", nomDansFichier: "STOCK"},
    {nomRecherche: "CODEFAMILLE", nomDansFichier: "FAM1L"}
  ];
  
  // Collecte des messages de log
  const logMessages: string[] = [];
  
  // Recherche des colonnes
  colonnesRequises.forEach(col => {
    // Tentative 1 : Recherche exacte
    let idx: number = headers.findIndex((h: string | number | boolean) => {
      const cellValue: string = String(h || "").trim();
      return cellValue.toUpperCase() === col.nomDansFichier.toUpperCase();
    });
    
    // Tentative 2 : Recherche partielle
    if (idx === -1) {
      idx = headers.findIndex((h: string | number | boolean) => {
        const cellValue: string = String(h || "").toUpperCase().trim();
        return cellValue.includes(col.nomDansFichier.toUpperCase()) || 
               col.nomDansFichier.toUpperCase().includes(cellValue);
      });
    }
    
    // Tentative 3 : Recherche par nom original
    if (idx === -1 && col.nomRecherche !== col.nomDansFichier) {
      idx = headers.findIndex((h: string | number | boolean) => {
        const cellValue: string = String(h || "").toUpperCase().trim();
        return cellValue.includes(col.nomRecherche.toUpperCase());
      });
    }
    
    if (idx !== -1) {
      colIndex[col.nomRecherche] = idx;
      logMessages.push(`✅ Colonne "${col.nomRecherche}" (recherche: "${col.nomDansFichier}") trouvée à l'index ${idx} (colonne ${String.fromCharCode(65 + idx)})`);
    } else {
      logMessages.push(`⚠️ Colonne "${col.nomRecherche}" NON trouvée (cherchée comme "${col.nomDansFichier}")`);
    }
  });

  // Affiche tous les messages de log en une fois
  console.log(logMessages.join('\n'));

  // Vérification des colonnes requises
  if (colIndex["CODEART"] === undefined) {
    console.log("❌ Erreur critique : colonne CODEART (REFCIALE) non trouvée");
    console.log("Vérifiez que votre feuille contient une colonne avec 'REFCIALE' dans l'en-tête");
    const headersDisplay = headers.map((h, i) => `${i}: "${h}"`).join(", ");
    console.log("En-têtes disponibles:", headersDisplay);
    return;
  }

  console.log("📊 Index des colonnes:", JSON.stringify(colIndex));

  // Créer ou récupérer la feuille Shopify
  let shopifySheet: ExcelScript.Worksheet | undefined = workbook.getWorksheet("Export Shopify");
  if (!shopifySheet) {
    shopifySheet = workbook.addWorksheet("Export Shopify");
    console.log("✅ Nouvelle feuille 'Export Shopify' créée");
  } else {
    console.log("✅ Feuille 'Export Shopify' existante réutilisée");
  }
  
  shopifySheet.getRange().clear();

  // En-têtes Shopify (format standard)
  const shopifyHeaders: string[] = [
    "Handle", "Title", "Body (HTML)", "Vendor", "Type", "Tags",
    "Published", "Option1 Name", "Option1 Value", "Variant SKU",
    "Variant Grams", "Variant Inventory Tracker", "Variant Inventory Policy",
    "Variant Fulfillment Service", "Variant Price", "Variant Compare At Price",
    "Variant Requires Shipping", "Variant Taxable", "Variant Barcode",
    "Image Src", "Image Position", "Image Alt Text", "Gift Card",
    "SEO Title", "SEO Description", "Variant Image", "Variant Weight Unit",
    "Variant Tax Code", "Cost per item", "Status"
  ];
  
  // Écrire les en-têtes
  const headerRangeShopify = shopifySheet.getRange("A1:AD1");
  headerRangeShopify.setValues([shopifyHeaders]);
  console.log("✅ En-têtes Shopify ajoutés");

  // Récupère les données
  const totalRows: number = usedRange.getRowCount();
  const dataRows: number = totalRows - startDataRow;
  
  if (dataRows <= 0) { console.log("❌ Aucune donnée à traiter"); return; } console.log(`📥 Récupération de ${dataRows} lignes de données...`); const dataRange: ExcelScript.Range=  sourceSheet.getRangeByIndexes( startDataRow, 0, dataRows, usedRange.getColumnCount() ); const values: (string | number | boolean)[][]=  dataRange.getValues(); console.log(`📊 ${values.length} lignes de données récupérées`); // Transformation const shopifyData: (string | number)[][]=  []; let lignesIgnored: number=  0; let lignesTraitees: number=  0; // Collecte des messages d'erreur de ligne const ligneErrors: string[]=  []; values.forEach((row: (string | number | boolean)[], index: number)=> {
    // Numéro de ligne réel (pour le débogage)
    const ligneReelle: number = startDataRow + index + 1;
    
    // Vérifier que la ligne a assez de colonnes
    if (row.length <= colIndex["CODEART"]) {
      ligneErrors.push(`⚠️ Ligne ${ligneReelle}: Pas assez de colonnes (${row.length} < ${colIndex["CODEART"] + 1})`);
      lignesIgnored++;
      return;
    }
    
    const sku: string = String(row[colIndex["CODEART"]] || "").trim();
    if (!sku) {
      lignesIgnored++;
      return; // Ignore les lignes sans code article
    }
    
    const title: string = String(row[colIndex["LIBELLE80"]] || "").trim();
    const description: string = String(row[colIndex["LIBELLE240"]] || "").trim();
    
    // IMPORTANT : "tarif" = PUHT, donc on doit calculer le prix TTC pour Shopify
    // Shopify attend généralement des prix TTC (taxes incluses)
    const puht: number = colIndex["PUHT"] !== undefined ? Number(row[colIndex["PUHT"]] || 0) : 0;
    const tauxTVA: number = 0.20; // 20% de TVA par défaut
    const priceTTC: number = puht * (1 + tauxTVA);
    const price: string = priceTTC.toFixed(2);
    
    const barcode: string = String(row[colIndex["GTIN"]] || "").trim();
    const stock: number = colIndex["STOCK"] !== undefined ? Number(row[colIndex["STOCK"]] || 0) : 0;
    const type: string = colIndex["CODEFAMILLE"] !== undefined ? String(row[colIndex["CODEFAMILLE"]] || "").trim() : "";
    
    // Handle = identifiant unique (slug)
    const handle: string = sku.toLowerCase().replace(/[^a-z0-9]+/g, '-');
    
    const shopifyRow: (string | number)[] = [
      handle, // Handle
      title, // Title
      description, // Body HTML
      "", // Vendor (vide = conserve existant)
      type, // Type (catégorie)
      "", // Tags
      "TRUE", // Published
      "Title", // Option1 Name
      "Default Title", // Option1 Value
      sku, // Variant SKU
      "", // Variant Grams
      "shopify", // Inventory Tracker
      stock > 0 ? "continue" : "deny", // Inventory Policy
      "manual", // Fulfillment Service
      price, // Variant Price
      "", // Compare At Price (prix barré)
      "TRUE", // Requires Shipping
      "TRUE", // Taxable
      barcode, // Barcode
      "", // Image Src
      "", // Image Position
      "", // Image Alt
      "FALSE", // Gift Card
      title, // SEO Title
      description.substring(0, Math.min(description.length, 160)), // SEO Description
      "", // Variant Image
      "kg", // Weight Unit
      "", // Tax Code
      "", // Cost per item
      "active" // Status
    ];
    
    shopifyData.push(shopifyRow);
    lignesTraitees++;
  });

  // Affiche les erreurs de ligne en une fois (si moins de 10 erreurs)
  if (ligneErrors.length > 0 && ligneErrors.length <= 10) { console.log(ligneErrors.join('\n')); } else if (ligneErrors.length> 10) {
    console.log(`⚠️ ${ligneErrors.length} lignes avec erreurs (premières 10 affichées):`);
    console.log(ligneErrors.slice(0, 10).join('\n'));
  }

  console.log(`📝 ${lignesTraitees} produits transformés, ${lignesIgnored} lignes ignorées`);

  // Écrit les données dans la feuille Shopify
  if (shopifyData.length > 0 && shopifySheet) {
    // Calculer la plage d'écriture
    const startRow = 2; // Commencer à la ligne 2 (après les en-têtes)
    
    console.log(`📝 Écriture des données: ${shopifyData.length} produits`);
    
    try {
      // Créer la plage d'écriture
      const writeRange = shopifySheet.getRangeByIndexes(
        startRow - 1, // Convertir en index 0-based
        0, // Colonne A
        shopifyData.length,
        shopifyData[0].length
      );
      
      console.log(`🔍 Plage créée: ${writeRange.getAddress()}`);
      console.log(`📊 Dimensions données: ${shopifyData.length} x ${shopifyData[0].length}`);
      
      // Écrire les données
      writeRange.setValues(shopifyData);
      console.log(`✅ Données écrites avec succès`);
      
      // Activer la feuille pour le formatage
      shopifySheet.activate();
      
      // Formatage
      shopifySheet.getRange("1:1").getFormat().getFill().setColor("#5D6CC4");
      shopifySheet.getRange("1:1").getFormat().getFont().setColor("white");
      shopifySheet.getRange("1:1").getFormat().getFont().setBold(true);
      
      // Ajuste les colonnes
      shopifySheet.getUsedRange().getFormat().autofitColumns();
      
      console.log(`✅ Export Shopify créé : ${shopifyData.length} produits`);
      console.log("📥 Enregistrez cette feuille au format CSV pour l'import Shopify");
      console.log("📋 Colonnes Shopify:", shopifyHeaders.length);
      console.log("💰 Prix calculés : PUHT → Prix TTC (TVA 20%)");
      
      // Calcul du prix moyen
      const prixTotal: number = shopifyData.reduce((sum, row) => sum + Number(row[14]), 0);
      const prixMoyen: number = prixTotal / shopifyData.length;
      console.log(`📊 Prix moyen: ${prixMoyen.toFixed(2)} €`);
      console.log(`📦 Politique de stock: "continue" si stock > 0, "deny" sinon`);
      
    } catch (error) {
      console.log(`❌ Erreur lors de l'écriture des données: ${error}`);
      
      // Essayer une approche alternative: écrire ligne par ligne
      try {
        const maxLignes = Math.min(shopifyData.length, 100);
        console.log(`🔄 Essai avec écriture ligne par ligne (${maxLignes} premières lignes)...`);
        
        for (let i = 0; i < maxLignes; i++) {
          const rowRange = shopifySheet.getRangeByIndexes(startRow - 1 + i, 0, 1, shopifyData[i].length);
          rowRange.setValues([shopifyData[i]]);
        }
        console.log(`✅ ${maxLignes} premières lignes écrites`);
        
        // Activer et formater
        shopifySheet.activate();
        shopifySheet.getRange("1:1").getFormat().getFill().setColor("#5D6CC4");
        shopifySheet.getRange("1:1").getFormat().getFont().setColor("white");
        shopifySheet.getRange("1:1").getFormat().getFont().setBold(true);
        shopifySheet.getUsedRange().getFormat().autofitColumns();
        
      } catch (error2) {
        console.log(`❌ Échec même avec l'approche ligne par ligne: ${error2}`);
      }
    }
    
  } else {
    console.log("❌ Aucun produit à exporter");
    console.log("Vérifiez que:");
    console.log("1. Votre feuille source contient des données dans la colonne REFCIALE");
    console.log("2. Les en-têtes sont correctement identifiés");
    console.log("3. Vous avez au moins une ligne de données après les en-têtes");
  }
}

Mode d'emploi : de l'exécution du script à l'import

Pour WooCommerce

1

Exécuter le script de transformation

Ouvrez votre fichier FAB-DIS dans Excel Online, exécutez le script "FAB-DIS vers WooCommerce". Une nouvelle feuille "Export WooCommerce" est créée automatiquement.

2

Exporter en CSV

Cliquez sur la feuille "Export WooCommerce" → Fichier → Enregistrer sous → CSV (délimiteur : virgule). Enregistrez le fichier sur votre ordinateur.

3

Accéder à l'importeur WooCommerce

Dans votre back-office WordPress, allez dans WooCommerce → Produits → Importer.

4

Sélectionner et mapper le fichier CSV

Cliquez sur "Choisir un fichier", sélectionnez votre CSV, puis cliquez sur "Continuer". WooCommerce détecte automatiquement les colonnes si elles respectent le format standard.

5

Configurer les options d'import

IMPORTANT : Cochez "Mettre à jour les produits existants" pour mettre à jour les prix sans créer de doublons. Utilisez le SKU comme identifiant unique.

⚠️ Attention aux options d'import

Pour une mise à jour de prix uniquement :

  • ✅ Cochez "Mettre à jour les produits existants"
  • ✅ Laissez la colonne "Images" vide pour conserver les images existantes
  • ✅ Utilisez le SKU comme identifiant (pas l'ID)
  • ❌ Ne cochez PAS "Remplacer toutes les données" si vous voulez conserver descriptions/images personnalisées
6

Lancer l'import et vérifier

Cliquez sur "Exécuter l'importation". WooCommerce traite le fichier en quelques secondes à quelques minutes selon la taille. Vérifiez ensuite sur 4-5 produits que les prix ont bien été mis à jour.

⏱️ Temps d'import mesuré : 500 produits = 30 secondes | 2000 produits = 2 minutes | 10000 produits = 8-10 minutes

Pour Shopify

1

Exécuter le script de transformation

Ouvrez votre fichier FAB-DIS dans Excel Online, exécutez le script "FAB-DIS vers Shopify". Une feuille "Export Shopify" est créée.

2

Exporter en CSV

Enregistrez la feuille "Export Shopify" au format CSV (délimiteur : virgule).

3

Accéder à l'importeur Shopify

Dans votre back-office Shopify, allez dans Produits → Importer.

4

Uploader le fichier CSV

Cliquez sur "Add file", sélectionnez votre CSV. Shopify détecte automatiquement le format s'il respecte le template standard.

5

Choisir le mode de mise à jour

Sélectionnez "Mettre à jour les produits existants par handle" pour éviter les doublons. Le "handle" sert d'identifiant unique.

💡 Astuce Shopify

Contrairement à WooCommerce, Shopify permet de prévisualiser l'import avant de valider. Profitez-en pour vérifier que tout est correct avant d'appliquer les modifications !

6

Valider l'import

Après prévisualisation, cliquez sur "Import products". Shopify traite le fichier et vous affiche un rapport détaillé des produits mis à jour/créés.

Optimisations et pièges à éviter

Les 7 erreurs les plus fréquentes lors de l'import

1. Écraser les images existantes

Erreur : Laisser la colonne "Images" vide peut parfois supprimer les images existantes selon la configuration.

Solution : Pour une mise à jour de prix uniquement, utilisez un plugin d'import avancé qui permet de spécifier les champs à mettre à jour.

2. Créer des doublons au lieu de mettre à jour

Erreur : Ne pas cocher "Mettre à jour les produits existants" ou utiliser un mauvais identifiant.

Solution : Toujours utiliser le SKU (WooCommerce) ou Handle (Shopify) comme identifiant unique, jamais l'ID.

3. Oublier de gérer les variations de produits

Erreur : Pour les produits avec variations (tailles, couleurs), l'import simple ne fonctionne pas.

Solution : Les produits avec variations nécessitent un format CSV spécifique avec une ligne par variation. Contactez-nous pour un script adapté.

4. Importer sans backup préalable

Erreur : Lancer un import massif sans sauvegarde de la base de données.

Solution : Faites toujours un backup de votre base avant un import de masse. WooCommerce/Shopify proposent des exports complets.

5. Ne pas tester sur un échantillon

Erreur : Importer directement 5000 produits sans test.

Solution : Testez toujours sur 10-20 produits d'abord, vérifiez le résultat, puis lancez l'import complet.

6. Mauvais encodage de caractères

Erreur : Les accents et caractères spéciaux s'affichent mal (é, à , etc.).

Solution : Enregistrez toujours votre CSV en UTF-8. Dans Excel : Fichier → Enregistrer sous → CSV UTF-8.

7. Oublier de vider le cache

Erreur : Les prix ne semblent pas mis à jour sur le site après l'import.

Solution : Videz le cache de votre site (plugin de cache, CDN, cache navigateur) après chaque import.

Optimisations avancées

💡 Automatiser complètement avec Power Automate

Si vous avez un abonnement Microsoft 365 Business + Shopify/WooCommerce avec API :

  1. Créez un flux Power Automate déclenché par l'arrivée d'un nouveau FAB-DIS
  2. Exécutez automatiquement le script de transformation
  3. Uploadez le CSV via l'API de votre plateforme e-commerce
  4. Recevez une notification de confirmation

Résultat : mise à jour 100% automatique sans intervention humaine !

💡 Gérer les variations de prix dynamiques

Pour les produits avec remises automatiques ou prix paliers, ajoutez une logique dans le script :

  • Prix barré = prix FAB-DIS
  • Prix de vente = prix FAB-DIS - X%
  • Calcul automatique du pourcentage de remise affiché

💡 Synchroniser également les stocks

Le script peut aussi mettre à jour les niveaux de stock si votre FAB-DIS contient cette information. Ajoutez simplement la colonne STOCK dans le mapping.

Tableau comparatif des solutions d'import

Solution Avantages Inconvénients Coût
Import natif WooCommerce/Shopify Gratuit, simple, intégré Limité, pas de planification, risque d'écrasement de données Gratuit
Plugins/Apps d'import avancés Plus de contrôle, mapping avancé, planification possible Payant, courbe d'apprentissage 30-100 €/mois
Script Office + Import manuel Gratuit, contrôle total, personnalisable Nécessite intervention humaine (10-15 min) Gratuit
Automatisation complète (Power Automate + API) 100% automatique, aucune intervention Configuration technique, nécessite Microsoft 365 Business Coût M365 (à partir de 10 €/mois)

FAQ : Questions fréquentes sur la mise à jour e-commerce via FAB-DIS

Puis-je mettre à jour uniquement les prix sans toucher aux descriptions et images ?

Oui, absolument. Dans le script, ne remplissez que les colonnes liées aux prix (Regular Price, Sale Price). Laissez les colonnes Name, Description et Images vides ou supprimez-les du CSV. La plupart des importeurs conservent alors les données existantes pour ces champs.

Comment gérer les produits avec variations (tailles, couleurs) ?

Les produits avec variations nécessitent un format CSV spécifique avec une ligne par variation (ex: T-shirt Bleu S, T-shirt Bleu M, etc.). Le script fourni gère les produits simples. Pour les variations, nous pouvons créer un script adapté à votre catalogue spécifique.

Que se passe-t-il si un produit du FAB-DIS n'existe pas encore sur mon site ?

Deux options : (1) Avec "Mettre à jour les produits existants" décoché, les nouveaux produits seront créés automatiquement. (2) Avec cette option cochée, seuls les produits existants sont mis à jour (recommandé pour une simple mise à jour de prix).

L'import fonctionne-t-il pour PrestaShop, Magento ou d'autres plateformes ?

Oui, le principe reste le même. Seul le format CSV change selon la plateforme. Nous pouvons adapter le script à n'importe quelle plateforme e-commerce (PrestaShop, Magento, BigCommerce, etc.). Chaque plateforme a son propre template CSV.

Combien de temps faut-il pour importer 10 000 produits ?

Pour WooCommerce : environ 8 à 15 minutes selon votre hébergement. Pour Shopify : 5 à 10 minutes (leur infrastructure cloud est plus rapide). Prévoyez toujours 20% de temps supplémentaire pour la vérification post-import. Conseil : importez plutôt en soirée ou le week-end pour éviter la charge serveur.

Cas d'usage avancés : aller plus loin

Scénario 1 : Multi-fournisseurs avec tarifs différenciés

Si vous travaillez avec plusieurs fournisseurs (chacun avec son propre FAB-DIS) et que vous appliquez des marges différentes selon le fournisseur :

1

Consolider les FAB-DIS

Créez un script qui fusionne les 3 FAB-DIS en un seul fichier consolidé, en ajoutant une colonne "FOURNISSEUR".

2

Appliquer des coefficients variables

Le script applique automatiquement le bon coefficient selon le fournisseur : Fournisseur A = coefficient 1.4, Fournisseur B = coefficient 1.6, etc.

3

Export unique vers e-commerce

Un seul CSV consolidé est généré pour l'import, avec tous les produits et leurs prix correctement calculés.

Cas réel : Électro-Pro (notre étude de cas) travaille avec 3 fournisseurs. Avant automatisation : 3 imports séparés = 3h de travail. Après : 1 script consolidation + 1 import = 15 minutes.

Scénario 2 : Gestion des promotions automatiques

Vous voulez appliquer automatiquement des promotions selon des règles métier :

  • -20% sur tous les produits de la catégorie "Déstockage"
  • -15% sur les produits avec stock > 50 unités
  • Prix barré = ancien prix, prix affiché = nouveau prix avec promo

Le script peut intégrer cette logique et remplir automatiquement les colonnes "Regular price" (prix barré) et "Sale price" (prix promo).

Scénario 3 : Synchronisation bidirectionnelle

Configuration avancée où les données circulent dans les deux sens :

  1. FAB-DIS → Site e-commerce : Mise à jour des prix, descriptions, stocks
  2. Site e-commerce → Reporting : Export des ventes réalisées pour analyse
  3. Reporting → Commande fournisseur : Génération automatique de commandes selon les ventes

Ce workflow complet peut être entièrement automatisé avec Power Automate + API.

Checklist finale : les points de contrôle avant import

Avant chaque import massif, vérifiez systématiquement ces points :

✅ Checklist pré-import

Backup de la base de données effectué

Toujours faire un backup complet avant un import de masse

Fichier FAB-DIS validé (aucune anomalie critique)

Utilisez le script de validation d'intégrité

Script de transformation exécuté avec succès

Vérifiez le message de confirmation dans la console

CSV enregistré en UTF-8

Pour éviter les problèmes d'accents et caractères spéciaux

Test effectué sur 10-20 produits

Toujours tester sur un échantillon avant l'import complet

Option "Mettre à jour les produits existants" activée

Pour éviter la création de doublons

Colonnes Images et Descriptions laissées vides (si mise à jour prix uniquement)

Pour conserver les contenus personnalisés existants

Import planifié en heures creuses

Soirée ou week-end pour minimiser l'impact sur les performances

Conclusion : de la corvée à l'automatisation sereine

Mettre à jour les prix de votre site e-commerce à partir de fichiers FAB-DIS n'est plus une corvée chronophage de plusieurs heures. Grâce aux scripts de transformation automatisée et aux imports CSV, vous pouvez désormais :

  • ⏱️ Réduire le temps de 8h à 10-15 minutes par mise à jour
  • Éliminer les erreurs de saisie manuelle et les incohérences tarifaires
  • 🚀 Maintenir vos prix à jour en temps réel sans décalage avec vos fournisseurs
  • 😌 Libérer vos équipes pour des tâches à plus forte valeur ajoutée

Le cas d'Électro-Pro démontre concrètement l'impact : passage de 12 heures à 15 minutes, ROI immédiat dès le premier mois, et une collaboratrice qui peut enfin se concentrer sur l'optimisation du catalogue plutôt que sur la saisie répétitive.

📊 Synthèse des gains mesurés :

  • Temps gagné : 97,9% de réduction (de 12h à 15min)
  • Erreurs de prix : -100% (aucune erreur de saisie)
  • Décalage de mise à jour : de 2-3 jours à immédiat
  • Satisfaction équipe : +95% (tâches répétitives éliminées)

Les scripts fournis dans cet article fonctionnent pour WooCommerce et Shopify, mais le principe s'applique à toute plateforme e-commerce acceptant les imports CSV (PrestaShop, Magento, BigCommerce, etc.).

Besoin d'une solution clé en main ?

AutoExcel crée des solutions d'automatisation complètes pour connecter vos fichiers FAB-DIS à votre plateforme e-commerce, quelle qu'elle soit. Nous pouvons :

  • ✅ Adapter les scripts à votre format FAB-DIS spécifique
  • ✅ Gérer les cas complexes (variations, multi-fournisseurs, promotions automatiques)
  • ✅ Automatiser complètement le processus avec Power Automate
  • ✅ Former vos équipes à l'utilisation des outils
  • ✅ Assurer la maintenance et les évolutions

Résultat : votre catalogue e-commerce toujours à jour, sans intervention manuelle, pour un investissement rapidement rentabilisé.

Demander un devis gratuit

Pour aller plus loin :
FAB-DIS et automatisation : le guide ultime pour les TPE/PME
Créer un tarif client personnalisé depuis un FAB-DIS
Nettoyer et standardiser les libellés produits FAB-DIS

Retour en haut