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
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 :
- Lundi matin : réception des FAB-DIS (3 fichiers Excel de 3 fournisseurs différents)
- Lundi après-midi : tri et identification des produits concernés (environ 400 à 600 produits avec modifications tarifaires)
- 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
- Mercredi matin : contrôle qualité et correction des erreurs de saisie
❌ Avant l’automatisation
- 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
- 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 :
Réception et nettoyage du FAB-DIS
Transformation en format e-commerce (CSV)
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 :
- Nettoyer les libellés : corriger les majuscules, supprimer les espaces superflus
- Valider l’intégrité : vérifier l’absence de doublons, GTIN manquants, incohérences de prix
- 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
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.
Exporter en CSV
Cliquez sur la feuille "Export WooCommerce" → Fichier → Enregistrer sous → CSV (délimiteur : virgule). Enregistrez le fichier sur votre ordinateur.
Accéder à l'importeur WooCommerce
Dans votre back-office WordPress, allez dans WooCommerce → Produits → Importer.
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.
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
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
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.
Exporter en CSV
Enregistrez la feuille "Export Shopify" au format CSV (délimiteur : virgule).
Accéder à l'importeur Shopify
Dans votre back-office Shopify, allez dans Produits → Importer.
Uploader le fichier CSV
Cliquez sur "Add file", sélectionnez votre CSV. Shopify détecte automatiquement le format s'il respecte le template standard.
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 !
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 :
- Créez un flux Power Automate déclenché par l'arrivée d'un nouveau FAB-DIS
- Exécutez automatiquement le script de transformation
- Uploadez le CSV via l'API de votre plateforme e-commerce
- 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
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.
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.
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).
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.
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 :
Consolider les FAB-DIS
Créez un script qui fusionne les 3 FAB-DIS en un seul fichier consolidé, en ajoutant une colonne "FOURNISSEUR".
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.
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 :
- FAB-DIS → Site e-commerce : Mise à jour des prix, descriptions, stocks
- Site e-commerce → Reporting : Export des ventes réalisées pour analyse
- 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
Toujours faire un backup complet avant un import de masse
Utilisez le script de validation d'intégrité
Vérifiez le message de confirmation dans la console
Pour éviter les problèmes d'accents et caractères spéciaux
Toujours tester sur un échantillon avant l'import complet
Pour éviter la création de doublons
Pour conserver les contenus personnalisés existants
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 gratuitPour 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

