diff --git a/sage_connector.py b/sage_connector.py index 086c362..ad6d1da 100644 --- a/sage_connector.py +++ b/sage_connector.py @@ -4122,27 +4122,24 @@ class SageConnector: def modifier_commande(self, numero: str, commande_data: Dict) -> Dict: """ - ✏️ Modification commande - VERSION ULTRA-DIAGNOSTIQUE + ✏️ Modification commande - VERSION SIMPLIFIÉE - 🔬 OBJECTIF: Identifier EXACTEMENT où ça plante et pourquoi - - Stratégies testées dans l'ordre : - 1. Diagnostic complet de l'état du document - 2. Comparaison avec un document qui fonctionne (BC00068) - 3. Approche minimaliste (modifier 1 seul champ à la fois) - 4. Approche progressive (ajouter champs un par un) + 🔧 STRATÉGIE REMPLACEMENT LIGNES: + - Si nouvelles lignes fournies → Supprime TOUTES les anciennes puis ajoute les nouvelles + - Utilise .Remove() pour la suppression + - Simple, robuste, prévisible """ if not self.cial: raise RuntimeError("Connexion Sage non établie") try: with self._com_context(), self._lock_com: - logger.info(f"🔬 === DIAGNOSTIC MODIFICATION COMMANDE {numero} ===") + logger.info(f"🔬 === MODIFICATION COMMANDE {numero} ===") # ======================================== - # PHASE 0 : DIAGNOSTIC COMPLET INITIAL + # ÉTAPE 1 : CHARGER LE DOCUMENT # ======================================== - logger.info("📊 PHASE 0: Diagnostic état initial...") + logger.info("📂 Chargement document...") factory = self.cial.FactoryDocumentVente persist = None @@ -4164,20 +4161,14 @@ class SageConnector: doc = win32com.client.CastTo(persist, "IBODocumentVente3") doc.Read() - # Diagnostic complet de l'état initial - diag_initial = { - "DO_Piece": getattr(doc, "DO_Piece", ""), - "DO_Type": getattr(doc, "DO_Type", -1), - "DO_Statut": getattr(doc, "DO_Statut", -1), - "DO_Date": str(getattr(doc, "DO_Date", "")), - "DO_Ref": getattr(doc, "DO_Ref", ""), - "DO_TotalHT": float(getattr(doc, "DO_TotalHT", 0.0)), - "DO_TotalTTC": float(getattr(doc, "DO_TotalTTC", 0.0)), - } + statut_actuel = getattr(doc, "DO_Statut", 0) + type_reel = getattr(doc, "DO_Type", -1) - logger.info(f" 📋 État initial: {diag_initial}") + logger.info(f" 📊 Type={type_reel}, Statut={statut_actuel}") - # Charger le client INITIAL + # ======================================== + # ÉTAPE 2 : VÉRIFIER CLIENT INITIAL + # ======================================== client_code_initial = "" try: client_obj = getattr(doc, "Client", None) @@ -4213,56 +4204,25 @@ class SageConnector: logger.warning(f" ⚠️ Erreur comptage lignes: {e}") # ======================================== - # PHASE 1 : COMPARAISON AVEC BC00068 (qui fonctionne) + # ÉTAPE 3 : DÉTERMINER LES MODIFICATIONS # ======================================== - if numero != "BC00068": - logger.info("🔍 PHASE 1: Comparaison avec BC00068...") - - try: - persist_ref = None - for type_test in [10, 3, settings.SAGE_TYPE_BON_COMMANDE]: - try: - persist_ref = factory.ReadPiece(type_test, "BC00068") - if persist_ref: - break - except: - continue - - if persist_ref: - doc_ref = win32com.client.CastTo(persist_ref, "IBODocumentVente3") - doc_ref.Read() - - # Comparer tous les champs critiques - champs_a_comparer = [ - "DO_Type", "DO_Statut", "DO_Domaine", "DO_Souche", - "DO_Transfere", "DO_Valide", "DO_Cloture" - ] - - differences = {} - for champ in champs_a_comparer: - val_doc = getattr(doc, champ, None) - val_ref = getattr(doc_ref, champ, None) - - if val_doc != val_ref: - differences[champ] = { - "doc_actuel": str(val_doc), - "bc00068": str(val_ref) - } - - if differences: - logger.warning(f" ⚠️ DIFFÉRENCES avec BC00068:") - for champ, vals in differences.items(): - logger.warning(f" {champ}: {vals['doc_actuel']} != {vals['bc00068']}") - else: - logger.info(f" ✅ Aucune différence critique avec BC00068") - - except Exception as e: - logger.warning(f" ⚠️ Impossible de comparer avec BC00068: {e}") + champs_modifies = [] + + modif_date = "date_commande" in commande_data + modif_statut = "statut" in commande_data + modif_ref = "reference" in commande_data + modif_lignes = "lignes" in commande_data and commande_data["lignes"] is not None + + logger.info(f"📋 Modifications demandées:") + logger.info(f" Date: {modif_date}") + logger.info(f" Statut: {modif_statut}") + logger.info(f" Référence: {modif_ref}") + logger.info(f" Lignes: {modif_lignes}") # ======================================== - # PHASE 2 : TEST WRITE() BASIQUE (SANS AUCUNE MODIFICATION) + # ÉTAPE 4 : TEST WRITE() BASIQUE # ======================================== - logger.info("🧪 PHASE 2: Test Write() basique (sans modification)...") + logger.info("🧪 Test Write() basique (sans modification)...") try: doc.Write() @@ -4287,27 +4247,10 @@ class SageConnector: raise ValueError(f"Document verrouillé, impossible de modifier: {e}") # ======================================== - # PHASE 3 : STRATÉGIE SELON LES MODIFICATIONS DEMANDÉES - # ======================================== - champs_modifies = [] - - # Détecter ce qui doit être modifié - modif_date = "date_commande" in commande_data - modif_statut = "statut" in commande_data - modif_ref = "reference" in commande_data - modif_lignes = "lignes" in commande_data and commande_data["lignes"] is not None - - logger.info(f"📋 Modifications demandées:") - logger.info(f" Date: {modif_date}") - logger.info(f" Statut: {modif_statut}") - logger.info(f" Référence: {modif_ref}") - logger.info(f" Lignes: {modif_lignes}") - - # ======================================== - # STRATÉGIE A : MODIFICATIONS SIMPLES (pas de lignes) + # ÉTAPE 5 : MODIFICATIONS SIMPLES (pas de lignes) # ======================================== if not modif_lignes and (modif_date or modif_statut or modif_ref): - logger.info("🎯 STRATÉGIE A: Modifications simples (sans lignes)...") + logger.info("🎯 Modifications simples (sans lignes)...") if modif_date: logger.info(" 📅 Modification date...") @@ -4372,15 +4315,15 @@ class SageConnector: raise ValueError(f"Sage refuse: {error_msg}") # ======================================== - # STRATÉGIE B : MODIFICATION LIGNES (approche minimaliste) + # ÉTAPE 6 : REMPLACEMENT COMPLET DES LIGNES # ======================================== elif modif_lignes: - logger.info("🎯 STRATÉGIE B: Modification lignes (approche minimaliste)...") + logger.info("🎯 REMPLACEMENT COMPLET DES LIGNES...") nouvelles_lignes = commande_data["lignes"] nb_nouvelles = len(nouvelles_lignes) - logger.info(f" 📊 {nb_lignes_initial} lignes existantes → {nb_nouvelles} nouvelles") + logger.info(f" 📊 {nb_lignes_initial} lignes existantes → {nb_nouvelles} nouvelles lignes") try: factory_lignes = doc.FactoryDocumentLigne @@ -4389,21 +4332,36 @@ class SageConnector: factory_article = self.cial.FactoryArticle - # APPROCHE MINIMALISTE: Tester avec UNE SEULE ligne d'abord - logger.info(" 🧪 TEST: Modification de la ligne 1 uniquement...") - - ligne_data = nouvelles_lignes[0] - - try: - ligne_p = factory_lignes.List(1) - if ligne_p is None: - raise ValueError("Aucune ligne existante") + # ============================================ + # SOUS-ÉTAPE 1 : SUPPRIMER TOUTES LES LIGNES EXISTANTES + # ============================================ + if nb_lignes_initial > 0: + logger.info(f" 🗑️ Suppression de {nb_lignes_initial} lignes existantes...") - ligne = win32com.client.CastTo(ligne_p, "IBODocumentLigne3") - ligne.Read() + # Supprimer depuis la fin pour éviter les problèmes d'index + for idx in range(nb_lignes_initial, 0, -1): + try: + ligne_p = factory_lignes.List(idx) + if ligne_p: + ligne = win32com.client.CastTo(ligne_p, "IBODocumentLigne3") + ligne.Read() + + # ✅ Utiliser .Remove() comme indiqué + ligne.Remove() + logger.debug(f" ✅ Ligne {idx} supprimée") + except Exception as e: + logger.warning(f" ⚠️ Impossible de supprimer ligne {idx}: {e}") + # Continuer même si une suppression échoue - logger.info(f" Article: {ligne_data['article_code']}") - logger.info(f" Quantité: {ligne_data['quantite']}") + logger.info(" ✅ Toutes les lignes existantes supprimées") + + # ============================================ + # SOUS-ÉTAPE 2 : AJOUTER LES NOUVELLES LIGNES + # ============================================ + logger.info(f" ➕ Ajout de {nb_nouvelles} nouvelles lignes...") + + for idx, ligne_data in enumerate(nouvelles_lignes, 1): + logger.info(f" --- Ligne {idx}/{nb_nouvelles}: {ligne_data['article_code']} ---") # Charger l'article persist_article = factory_article.ReadReference(ligne_data["article_code"]) @@ -4413,50 +4371,66 @@ class SageConnector: article_obj = win32com.client.CastTo(persist_article, "IBOArticle3") article_obj.Read() - # MÉTHODE ULTRA-MINIMALISTE : Juste changer la quantité - logger.info(" 🔧 Modification quantité uniquement...") + # Créer nouvelle ligne + ligne_persist = factory_lignes.Create() + + try: + ligne_obj = win32com.client.CastTo(ligne_persist, "IBODocumentLigne3") + except: + ligne_obj = win32com.client.CastTo(ligne_persist, "IBODocumentVenteLigne3") + quantite = float(ligne_data["quantite"]) - ligne.DL_Qte = quantite + + # Associer article + try: + ligne_obj.SetDefaultArticleReference(ligne_data["article_code"], quantite) + except: + try: + ligne_obj.SetDefaultArticle(article_obj, quantite) + except: + ligne_obj.DL_Design = ligne_data.get("designation", "") + ligne_obj.DL_Qte = quantite + + # Prix + if ligne_data.get("prix_unitaire_ht"): + ligne_obj.DL_PrixUnitaire = float(ligne_data["prix_unitaire_ht"]) + + # Remise + if ligne_data.get("remise_pourcentage", 0) > 0: + try: + ligne_obj.DL_Remise01REM_Valeur = float(ligne_data["remise_pourcentage"]) + ligne_obj.DL_Remise01REM_Type = 0 + except: + pass # Écrire la ligne - logger.info(" 💾 Write() ligne...") - ligne.Write() - logger.info(" ✅ Ligne 1 modifiée") - - # Écrire le document - logger.info(" 💾 Write() document après ligne...") - doc.Write() - logger.info(" ✅ Document écrit") - - doc.Read() - - # Vérifier client - client_obj = getattr(doc, "Client", None) - if client_obj: - client_obj.Read() - client_apres = getattr(client_obj, "CT_Num", "") - logger.info(f" 👤 Client après modif: {client_apres}") - else: - logger.error(" ❌ Client NULL après modif") - - champs_modifies.append("lignes") + ligne_obj.Write() + logger.info(f" ✅ Ligne {idx} ajoutée") - except Exception as e: - error_msg = str(e) - try: - sage_error = self.cial.CptaApplication.LastError - if sage_error: - error_msg = f"{sage_error.Description} (Code: {sage_error.Number})" - except: - pass - - logger.error(f" ❌ Modification ligne échoue: {error_msg}") - raise ValueError(f"Sage refuse modification ligne: {error_msg}") + logger.info(f" ✅ {nb_nouvelles} nouvelles lignes ajoutées") + + # Écrire le document + logger.info(" 💾 Write() document après remplacement lignes...") + doc.Write() + logger.info(" ✅ Document écrit") + + doc.Read() + + # Vérifier client + client_obj = getattr(doc, "Client", None) + if client_obj: + client_obj.Read() + client_apres = getattr(client_obj, "CT_Num", "") + logger.info(f" 👤 Client après remplacement: {client_apres}") + else: + logger.error(" ❌ Client NULL après remplacement") + + champs_modifies.append("lignes") # ======================================== - # PHASE FINALE : RELECTURE ET RETOUR + # ÉTAPE 7 : RELECTURE ET RETOUR # ======================================== - logger.info("📊 PHASE FINALE: Relecture...") + logger.info("📊 Relecture finale...") import time time.sleep(1) @@ -4522,7 +4496,5 @@ class SageConnector: - -