From de0053b98bc742fa1c91d416efbfb252366d2e14 Mon Sep 17 00:00:00 2001 From: Fanilo-Nantenaina Date: Fri, 28 Nov 2025 22:30:06 +0300 Subject: [PATCH] Diagnostic transformation selective error --- main.py | 287 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 234 insertions(+), 53 deletions(-) diff --git a/main.py b/main.py index 216730a..04c2c51 100644 --- a/main.py +++ b/main.py @@ -489,74 +489,255 @@ def lire_document(numero: str, type_doc: int): @app.post("/sage/documents/transform", dependencies=[Depends(verify_token)]) def transformer_document( - numero_source: str = Query(..., description="Numéro du document source"), - type_source: int = Query(..., description="Type document source"), - type_cible: int = Query(..., description="Type document cible"), + numero: str, type_source: int = Query(...), type_cible: int = Query(...) ): """ - 🔧 Transformation de document + 🔍 DIAGNOSTIC AVANCÉ: Analyse pourquoi une transformation échoue - ✅ CORRECTION : Utilise les VRAIS types Sage Dataven - - Types valides : - - 0: Devis - - 10: Bon de commande - - 20: Préparation - - 30: Bon de livraison - - 40: Bon de retour - - 50: Bon d'avoir - - 60: Facture - - Transformations autorisées : - - Devis (0) → Commande (10) - - Commande (10) → Bon livraison (30) - - Commande (10) → Facture (60) - - Bon livraison (30) → Facture (60) + Vérifie: + - Statut du document source + - Statuts autorisés + - Lignes du document + - Client associé + - Champs obligatoires manquants """ try: - logger.info( - f"🔄 Transformation demandée: {numero_source} " - f"(type {type_source}) → type {type_cible}" - ) + if not sage or not sage.cial: + raise HTTPException(503, "Service Sage indisponible") - # ✅ Matrice des transformations valides pour VOTRE Sage - transformations_valides = { - (0, 10), # Devis → Commande - (10, 30), # Commande → Bon de livraison - (10, 60), # Commande → Facture - (30, 60), # Bon de livraison → Facture - (0, 60), # Devis → Facture (si autorisé) - } + with sage._com_context(), sage._lock_com: + factory = sage.cial.FactoryDocumentVente - if (type_source, type_cible) not in transformations_valides: - logger.error( - f"❌ Transformation non autorisée: {type_source} → {type_cible}" - ) - raise HTTPException( - 400, - f"Transformation non autorisée: type {type_source} → type {type_cible}. " - f"Transformations valides: {transformations_valides}", + # Lire le document source + persist = factory.ReadPiece(type_source, numero) + + if not persist: + persist = sage._find_document_in_list(numero, type_source) + + if not persist: + raise HTTPException( + 404, f"Document {numero} (type {type_source}) introuvable" + ) + + doc = win32com.client.CastTo(persist, "IBODocumentVente3") + doc.Read() + + diagnostic = { + "numero": numero, + "type_source": type_source, + "type_cible": type_cible, + "problemes_detectes": [], + "avertissements": [], + "suggestions": [], + } + + # 1. Vérifier le statut + statut_actuel = getattr(doc, "DO_Statut", -1) + diagnostic["statut_actuel"] = statut_actuel + + if statut_actuel == 5: + diagnostic["problemes_detectes"].append( + { + "severite": "BLOQUANT", + "champ": "DO_Statut", + "valeur": 5, + "message": "Document déjà transformé (statut=5)", + } + ) + + elif statut_actuel == 6: + diagnostic["problemes_detectes"].append( + { + "severite": "BLOQUANT", + "champ": "DO_Statut", + "valeur": 6, + "message": "Document annulé (statut=6)", + } + ) + + elif statut_actuel in [3, 4]: + diagnostic["avertissements"].append( + { + "severite": "ATTENTION", + "champ": "DO_Statut", + "valeur": statut_actuel, + "message": f"Document déjà réalisé (statut={statut_actuel}). " + f"Un document cible existe peut-être déjà.", + } + ) + + elif statut_actuel == 0: + diagnostic["suggestions"].append( + "Le document est en 'Brouillon' (statut=0). " + "Le système le passera automatiquement à 'Accepté' (statut=2) avant transformation." + ) + + # 2. Vérifier le client + client_code = "" + try: + client_obj = getattr(doc, "Client", None) + if client_obj: + client_obj.Read() + client_code = getattr(client_obj, "CT_Num", "").strip() + except: + pass + + if not client_code: + diagnostic["problemes_detectes"].append( + { + "severite": "BLOQUANT", + "champ": "CT_Num", + "valeur": None, + "message": "Aucun client associé au document", + } + ) + else: + diagnostic["client_code"] = client_code + + # 3. Vérifier les lignes + try: + factory_lignes = getattr(doc, "FactoryDocumentLigne", None) or getattr( + doc, "FactoryDocumentVenteLigne", None + ) + + nb_lignes = 0 + lignes_problemes = [] + + if factory_lignes: + index = 1 + while index <= 100: + try: + ligne_p = factory_lignes.List(index) + if ligne_p is None: + break + + ligne = win32com.client.CastTo(ligne_p, "IBODocumentLigne3") + ligne.Read() + + nb_lignes += 1 + + # Vérifier article + article_ref = "" + try: + article_ref = getattr(ligne, "AR_Ref", "").strip() + if not article_ref: + article_obj = getattr(ligne, "Article", None) + if article_obj: + article_obj.Read() + article_ref = getattr( + article_obj, "AR_Ref", "" + ).strip() + except: + pass + + if not article_ref: + lignes_problemes.append( + { + "ligne": index, + "probleme": "Aucune référence article", + } + ) + + # Vérifier prix + prix = float(getattr(ligne, "DL_PrixUnitaire", 0.0)) + if prix == 0: + lignes_problemes.append( + {"ligne": index, "probleme": "Prix unitaire = 0"} + ) + + index += 1 + except: + break + + diagnostic["nb_lignes"] = nb_lignes + + if nb_lignes == 0: + diagnostic["problemes_detectes"].append( + { + "severite": "BLOQUANT", + "champ": "Lignes", + "valeur": 0, + "message": "Document vide (aucune ligne)", + } + ) + + if lignes_problemes: + diagnostic["avertissements"].append( + { + "severite": "ATTENTION", + "champ": "Lignes", + "message": f"{len(lignes_problemes)} ligne(s) avec des problèmes", + "details": lignes_problemes, + } + ) + + except Exception as e: + diagnostic["avertissements"].append( + { + "severite": "ERREUR", + "champ": "Lignes", + "message": f"Impossible de lire les lignes: {e}", + } + ) + + # 4. Vérifier les totaux + total_ht = float(getattr(doc, "DO_TotalHT", 0.0)) + total_ttc = float(getattr(doc, "DO_TotalTTC", 0.0)) + + diagnostic["totaux"] = {"total_ht": total_ht, "total_ttc": total_ttc} + + if total_ht == 0 and total_ttc == 0: + diagnostic["avertissements"].append( + { + "severite": "ATTENTION", + "champ": "Totaux", + "message": "Tous les totaux sont à 0", + } + ) + + # 5. Vérifier si la transformation est autorisée + transformations_valides = {(0, 10), (10, 30), (10, 60), (30, 60), (0, 60)} + + if (type_source, type_cible) not in transformations_valides: + diagnostic["problemes_detectes"].append( + { + "severite": "BLOQUANT", + "champ": "Transformation", + "message": f"Transformation {type_source} → {type_cible} non autorisée. " + f"Transformations valides: {transformations_valides}", + } + ) + + # Résumé + nb_bloquants = sum( + 1 + for p in diagnostic["problemes_detectes"] + if p.get("severite") == "BLOQUANT" ) + nb_avertissements = len(diagnostic["avertissements"]) - # Appel au connecteur Sage - resultat = sage.transformer_document(numero_source, type_source, type_cible) + diagnostic["resume"] = { + "peut_transformer": nb_bloquants == 0, + "nb_problemes_bloquants": nb_bloquants, + "nb_avertissements": nb_avertissements, + } - logger.info( - f"✅ Transformation réussie: {numero_source} → " - f"{resultat.get('document_cible', '?')} " - f"({resultat.get('nb_lignes', 0)} lignes)" - ) + if nb_bloquants == 0: + diagnostic["suggestions"].append( + "✅ Aucun problème bloquant détecté. La transformation devrait fonctionner." + ) + else: + diagnostic["suggestions"].append( + f"❌ {nb_bloquants} problème(s) bloquant(s) doivent être résolus avant la transformation." + ) - return {"success": True, "data": resultat} + return {"success": True, "diagnostic": diagnostic} except HTTPException: raise - except ValueError as e: - logger.error(f"❌ Erreur métier transformation: {e}") - raise HTTPException(400, str(e)) except Exception as e: - logger.error(f"❌ Erreur technique transformation: {e}", exc_info=True) - raise HTTPException(500, f"Erreur transformation: {str(e)}") + logger.error(f"[DIAG] Erreur diagnostic transformation: {e}", exc_info=True) + raise HTTPException(500, str(e)) @app.post("/sage/documents/champ-libre", dependencies=[Depends(verify_token)])