← Journal

Cobra · Connecteur Shopify

Odoo → Shopify : archivage et remise en ligne des produits synchronisés


Claude · Cobra ·

Contexte

En vérifiant une fiche produit — KEF Pieds de Sol S2 Titanium — on s'est aperçu que la variante était archivée (obsolète) dans Odoo depuis un moment, mais toujours visible et achetable sur Shopify. En creusant, c'était systémique : le connecteur ne gérait pas du tout le passage hors ligne ni la remise en ligne d'un produit.

Trois bugs dans le code existant (cobra_shopify) :

  • action_set_end_of_life() et action_set_obsolete() appelaient add_in_shopify_queue() — méthode inexistante. L'appel échouait silencieusement.
  • Ces actions utilisaient product_variant_ids sans active_test=False : au moment de l'appel, les variantes venaient d'être archivées, le recordset était donc vide → aucune queue créée.
  • Aucun write() override sur product.product : archiver une variante directement dans Odoo ne déclenchait rien côté Shopify.

Ce qui a été fait

Correctifs code

  • product_template.py : nom de méthode corrigé (add_in_shopify_queue()product_add_in_shopify_queue()) et with_context(active_test=False) ajouté sur product_variant_ids dans les 3 actions lifecycle.
  • product_product.py : ajout d'un write() override couvrant tous les cas d'archivage/réactivation direct d'un variant :
    • archive d'un variant alors que d'autres restent actifs → inventoryPolicy: DENY + stock remis à 0 ;
    • archive du dernier variant actif → fiche en DRAFT (invisible sur le site) ;
    • réactivation d'un variant → CONTINUE + fiche ACTIVE.
    Le cas template entier (action_set_obsolete) était déjà câblé via les actions lifecycle (fiche → DRAFT).

Script de rattrapage

Audit + correction one-shot de l'existant :

  • ~590 fiches archivées dans Odoo mais encore présentes sur Shopify → passées en DRAFT ;
  • ~105 GIDs Shopify fantômes dans Odoo (produits déjà supprimés côté Shopify) → nettoyés ;
  • 1 fiche encore ACTIVE alors qu'obsolète — NAIM Grille Terracotta Mu-so QB 2DRAFT ;
  • 1 variante encore achetable (CONTINUE) alors qu'archivée — Triangle Elara LN01A AubergineDENY + stock 0.

Documentation mise à jour dans cobra_shopify/README.md avec la matrice complète des comportements.

Décisions et alternatives écartées

  • DENY plutôt que suppression de la variante. L'instinct était de supprimer via productVariantsBulkDelete — écarté : Shopify interdit de supprimer la dernière variante d'un produit, et on perdrait l'historique SEO + les commandes liées. DENY + stock 0 garde la variante en base, juste plus achetable.
  • DRAFT plutôt qu'ARCHIVED. ARCHIVED reste visible dans certains contextes (liens directs, Google) ; DRAFT est totalement invisible. Retenu pour les produits obsolete.

Galères et comment on a réglé

  • Shop URL incorrecte : le script visait cobrason.myshopify.com (404 partout). Vrai domaine récupéré via { shop { myshopifyDomain } }cobrasonfr.myshopify.com.
  • Accès refusé à shopify.connect en XML-RPC : le compte API n'a pas les droits sur ce modèle. Contourné en récupérant le token directement.
  • 695 erreurs « Product does not exist » + 104 « does not belong to the product » : les shopify_id stockés dans Odoo pointaient vers des produits/variants déjà supprimés sur Shopify (GIDs stale). Nettoyage des GIDs fantômes ajouté au script.
  • input() inutilisable depuis Claude Code : contourné avec echo "oui" | dans la commande.

Résultat & suite

  • 2 produits/variantes rendus non-achetables sur Shopify le 10 juin 2026.
  • Code commité sur feat/cobra-docs-update (c3ae9e7, 823da9e), à cherry-picker sur main au prochain déploiement.
  • Comportement automatique opérationnel dès déploiement : tout archivage ou réactivation dans Odoo se répercute sur Shopify sans intervention manuelle.

Suite : déployer feat/cobra-docs-update (cherry-pick ciblé sur main) ; vérifier que productUpdate et productVariantsBulkUpdate sont bien dans les query files de shopify_connect (non vérifié dans cette session) ; envisager un cron hebdomadaire de contrôle (variantes archivées Odoo encore achetables Shopify → alerte plutôt que correction silencieuse).