Votre logiciel de gestion ou un logisticien demande la liste de vos produits WooCommerce avec chaque variation et son code article (SKU), pour faire la correspondance avec ses références. En fin d'exercice, il faut aussi le chiffre d'affaires. Export ponctuel : l'exporteur CSV intégré. Connecteur : l'API REST. Script sur le serveur : wc_get_products() et wc_get_orders(). Le SQL sur wp_posts ne marche plus pour les commandes.
Comment exporter les produits et leurs variations sans code ?
L'exporteur intégré se trouve dans Products > All Products > Export. Il permet de choisir les colonnes, les types de produits et les catégories. Dans notre test, chaque variation sort sur sa propre ligne :
| ID | Type | SKU | Name | Published | Parent | Attribute 1 name | Attribute 1 value(s) |
|---|---|---|---|---|---|---|---|
| 10 | variable | TSHIRT | T-shirt coton bio | 1 | Taille | L, M, S | |
| 11 | variation | TSHIRT-S | T-shirt coton bio - S | 1 | TSHIRT | Taille | S |
| 14 | simple | MUG | Mug émaillé | 1 | |||
| 15 | simple | CAP | Casquette (brouillon) | -1 |
La colonne Parent contient le SKU du parent. Attention : les brouillons sont exportés aussi (Published à -1).
Comment récupérer les produits et les variations par l'API REST ?
Un connecteur interroge la boutique sans accès au serveur, avec une clé API en lecture (WooCommerce > Settings > Advanced > REST API). Si elle répond 401, voir notre note sur l'erreur woocommerce_rest_cannot_view.
# Produits publiés, 100 par page (le maximum accepté) curl -u ck_xxx:cs_xxx "https://boutique.example/wp-json/wc/v3/products?status=publish&per_page=100&page=1" # Variations d'un produit variable (ici le produit 10) curl -u ck_xxx:cs_xxx "https://boutique.example/wp-json/wc/v3/products/10/variations?per_page=100"
- Bouclez sur
pagejusqu'à la valeur de l'en-têteX-WP-TotalPages. Avecper_page=101, l'API répond 400 (per_page must be between 1 (inclusive) and 100 (inclusive)). - Un produit variable liste ses variations dans
variations([11, 12, 13]dans notre test) ; chacune portesku,price,stock_quantityetattributes({"name": "Taille", "option": "M"}). - Pour une synchronisation régulière,
modified_afterlimite la réponse aux produits modifiés depuis une date.
Et en PHP, directement sur le serveur ?
Le script ci-dessous écrit une ligne par produit simple et par variation, avec le SKU du parent. Remplacez pa_taille par le slug de votre attribut.
<?php // export-produits.php : one CSV row per simple product and per variation $out = fopen('produits.csv', 'w'); fputcsv($out, ['product_id', 'variation_id', 'sku', 'parent_sku', 'name', 'taille', 'price', 'stock'], ';'); $page = 1; do { $products = wc_get_products([ 'status' => 'publish', 'limit' => 100, 'page' => $page, ]); foreach ($products as $product) { if ($product->is_type('variable')) { foreach ($product->get_children() as $variation_id) { $variation = wc_get_product($variation_id); fputcsv($out, [ $product->get_id(), $variation_id, $variation->get_sku(), $product->get_sku(), $product->get_name(), $variation->get_attribute('pa_taille'), $variation->get_price(), $variation->get_stock_quantity(), ], ';'); } } else { fputcsv($out, [ $product->get_id(), '', $product->get_sku(), '', $product->get_name(), '', $product->get_price(), $product->get_stock_quantity(), ], ';'); } } $page++; } while (count($products) === 100); fclose($out);
# Lancer le script depuis la racine du site wp eval-file export-produits.php
Trois différences avec l'ancien script qui circule :
$product->product_typeest obsolète (le code de WooCommerce le signale) :$product->is_type('variable')le remplace.get_available_variations()saute des variations : celles en rupture si l'option "masquer les produits en rupture" est cochée, et celles désactivées ou sans prix. Pour un export complet,get_children()renvoie toutes les variations.utf8_encode()est obsolète depuis PHP 8.2 (PHP 8.3 afficheFunction utf8_encode() is deprecated) et inutile : les noms sont déjà en UTF-8.fputcsv()gère les guillemets et les séparateurs dans les noms.
Pourquoi l'ancienne requête SQL sur wp_posts ne renvoie plus rien ?
Depuis WooCommerce 8.2, les nouvelles boutiques stockent par défaut les commandes dans leurs propres tables (HPOS) : wp_wc_orders, wp_wc_order_addresses, wp_wc_order_operational_data et wp_wc_orders_meta. Dans notre boutique de test, wp_posts ne contenait plus que des lignes shop_order_placehold. L'ancienne requête (post_type = 'shop_order') renvoyait une erreur de syntaxe (virgule avant FROM, ERROR 1064), puis, corrigée, 0 ligne. Elle excluait en plus les commandes processing, pourtant payées.
La documentation de WooCommerce déconseille le SQL direct sur les commandes et renvoie vers wc_get_orders(). Pour connaître le stockage de votre boutique (installée par WP-CLI, la nôtre était sans HPOS, activé ensuite par wp wc hpos enable) :
wp wc hpos status
Quelles commandes compter dans le chiffre d'affaires ?
| Statut | Signification (documentation WooCommerce) | Dans le CA ? |
|---|---|---|
pending |
reçue, aucun paiement | Non |
failed |
paiement refusé | Non |
on-hold |
en attente de confirmation du paiement (virement) | À part, tant que le paiement n'est pas confirmé |
processing |
payée, à expédier | Oui |
completed |
expédiée, terminée | Oui |
cancelled |
annulée | Non |
refunded |
remboursée en totalité | Non (son remboursement l'annule) |
Les outils de WooCommerce ne comptent pas la même chose. Analytics excluent par défaut pending, cancelled et failed, donc incluent on-hold. L'ancien rapport de l'API (/wp-json/wc/v3/reports/sales) compte completed, processing, on-hold et refunded. Et il place chaque remboursement à sa propre date : dans notre test, deux remboursements de janvier faits en octobre (24 €) apparaissaient en octobre, pas en janvier.
Vue commerciale : les commandes payées du mois, moins ce qui a été remboursé dessus. Vue comptable : les remboursements datés du mois où ils ont été faits. Choisissez avant de comparer deux chiffres.
Comment calculer le chiffre d'affaires en PHP, compatible HPOS ?
<?php // chiffre-affaires.php : paid orders of the period, refunds deducted $orders = wc_get_orders([ 'type' => 'shop_order', 'status' => ['wc-processing', 'wc-completed'], 'date_paid' => '2026-01-01...2026-01-31', 'limit' => -1, ]); $ttc = $tva = $port = $rembourse = 0; foreach ($orders as $order) { $ttc += (float) $order->get_total(); $tva += (float) $order->get_total_tax(); $port += (float) $order->get_shipping_total(); $rembourse += (float) $order->get_total_refunded(); } printf("Commandes : %d\n", count($orders)); printf("CA TTC : %.2f (dont TVA %.2f, port %.2f)\n", $ttc, $tva, $port); printf("Remboursements : %.2f\n", $rembourse); printf("CA TTC net : %.2f\n", $ttc - $rembourse);
Sur notre jeu d'essai de janvier 2026, le script a retenu 4 des 9 commandes : 123,40 € TTC dont 2,40 € de TVA et 5 € de port, 12 € remboursés, soit 111,40 € net. Les statuts on-hold, pending, cancelled, failed et refunded sont restés dehors.
Pour la vue comptable, on interroge les remboursements, qui sont un type de commande :
<?php // remboursements.php : refunds dated in the period (the accounting view) $refunds = wc_get_orders([ 'type' => 'shop_order_refund', 'date_created' => '2026-10-01...2026-10-31', 'limit' => -1, ]); $total = 0; foreach ($refunds as $refund) { $total += (float) $refund->get_amount(); } printf("Remboursements du mois : %d pour %.2f\n", count($refunds), $total);
date_paid accepte une plage AAAA-MM-JJ...AAAA-MM-JJ, dans le fuseau horaire du site. Sur une grosse boutique, remplacez 'limit' => -1 par une boucle sur 'page'. Côté API : /wp-json/wc/v3/orders?status=processing,completed&after=2026-01-01T00:00:00&before=2026-02-01T00:00:00 (dates de création) renvoie pour chaque commande total, total_tax et ses refunds.
Quand ces exports deviennent réguliers, un connecteur les remplace : il lit produits et commandes par l'API et les envoie au logiciel qui les attend.
- Export ponctuel : l'exporteur CSV intégré, variations en lignes avec le SKU du parent (brouillons compris).
- Connecteur :
/productspuis/products/{id}/variations, 100 par page au maximum. - Commandes :
wc_get_orders()ou l'API, jamaiswp_postsdepuis HPOS. - Chiffre d'affaires :
processing+completed, moins les remboursements, en choisissant leur date.
Testé avec WordPress 7.1.3, WooCommerce 11.2.1 (HPOS activé), PHP 8.3.35, WP-CLI 2.12.0 et MariaDB 11.4.13.
Questions fréquentes
Faut-il une clé API en écriture pour exporter les produits ?
Non. Une clé en lecture suffit pour lister les produits, les variations et les commandes. Elle agit avec les droits de l'utilisateur WordPress auquel elle est rattachée.
Comment savoir si ma boutique utilise HPOS ?
La commande wp wc hpos status l'indique, avec l'état du mode de compatibilité. Dans du code, OrderUtil::custom_orders_table_usage_is_enabled() renvoie la même information.
Le chiffre d'affaires doit-il être calculé HT ou TTC ?
get_total() renvoie le total TTC, port compris. get_total_tax() contient la TVA des produits et celle du port : la différence des deux donne le total HT, port compris.
Pourquoi mon total ne correspond-il pas aux rapports de WooCommerce ?
Les rapports Analytics comptent aussi les commandes en attente (on-hold) et se basent par défaut sur la date de paiement. L'ancien rapport de l'API place les remboursements à leur propre date. Vérifiez ces trois points avant de comparer.