diff --git a/main.py b/main.py index bc60d10..de540b7 100644 --- a/main.py +++ b/main.py @@ -1277,6 +1277,222 @@ def diagnostic_configuration(): raise HTTPException(500, str(e)) +@app.get("/sage/diagnostic/types-reels", dependencies=[Depends(verify_token)]) +def decouvrir_types_reels(): + """ + DIAGNOSTIC CRITIQUE: Découvre les VRAIS types de documents Sage + + Au lieu de deviner les types (0-5), on va: + 1. Créer manuellement un document de chaque type dans Sage + 2. Les lister ici pour voir leurs vrais numéros de type + """ + try: + if not sage or not sage.cial: + raise HTTPException(503, "Service Sage indisponible") + + with sage._com_context(), sage._lock_com: + factory = sage.cial.FactoryDocumentVente + + # Parcourir TOUS les documents + documents_par_type = {} + index = 1 + max_docs = 500 # Limiter pour ne pas bloquer + + logger.info("[DIAG] Scan de tous les documents...") + + while index < max_docs: + try: + persist = factory.List(index) + if persist is None: + break + + doc = win32com.client.CastTo(persist, "IBODocumentVente3") + doc.Read() + + # Récupérer le type ET le sous-type + type_doc = getattr(doc, "DO_Type", -1) + piece = getattr(doc, "DO_Piece", "") + statut = getattr(doc, "DO_Statut", -1) + + # Essayer de récupérer le domaine (vente/achat) + domaine = "Inconnu" + try: + domaine_val = getattr(doc, "DO_Domaine", -1) + domaine = {0: "Vente", 1: "Achat"}.get( + domaine_val, f"Code {domaine_val}" + ) + except: + pass + + # Récupérer la catégorie + categorie = "Inconnue" + try: + cat_val = getattr(doc, "DO_Categorie", -1) + categorie = str(cat_val) + except: + pass + + # Grouper par type + if type_doc not in documents_par_type: + documents_par_type[type_doc] = { + "count": 0, + "exemples": [], + "domaine": domaine, + "categorie": categorie, + } + + documents_par_type[type_doc]["count"] += 1 + + # Garder quelques exemples + if len(documents_par_type[type_doc]["exemples"]) < 3: + documents_par_type[type_doc]["exemples"].append( + { + "numero": piece, + "statut": statut, + "domaine": domaine, + "categorie": categorie, + } + ) + + index += 1 + + except Exception as e: + logger.debug(f"Erreur index {index}: {e}") + index += 1 + + # Formater le résultat + types_trouves = [] + + for type_num, infos in sorted(documents_par_type.items()): + types_trouves.append( + { + "type_code": type_num, + "nombre_documents": infos["count"], + "domaine": infos["domaine"], + "categorie": infos["categorie"], + "exemples": infos["exemples"], + "suggestion_libelle": _deviner_libelle_type( + type_num, infos["exemples"] + ), + } + ) + + logger.info( + f"[DIAG] {len(types_trouves)} types de documents distincts trouves" + ) + + return { + "success": True, + "types_documents_reels": types_trouves, + "instructions": ( + "Pour identifier les types corrects:\n" + "1. Creez manuellement dans Sage: 1 Bon de commande, 1 BL, 1 Facture\n" + "2. Appelez de nouveau cet endpoint\n" + "3. Les nouveaux types apparaitront avec leurs numeros corrects" + ), + "total_documents_scannes": index - 1, + } + + except Exception as e: + logger.error(f"[DIAG] Erreur decouverte types: {e}", exc_info=True) + raise HTTPException(500, str(e)) + + +def _deviner_libelle_type(type_num, exemples): + """Devine le libellé d'un type basé sur les numéros de pièce""" + if not exemples: + return "Type inconnu" + + # Analyser les préfixes des numéros + prefixes = [ex["numero"][:2] for ex in exemples if ex["numero"]] + prefix_commun = max(set(prefixes), key=prefixes.count) if prefixes else "" + + # Deviner selon le type_num et les préfixes + suggestions = { + 0: "Devis (DE)", + 1: "Bon de livraison (BL)", + 2: "Bon de retour (BR)", + 3: "Bon de commande (BC)", + 4: "Preparation de livraison (PL)", + 5: "Facture (FA)", + 6: "Facture d'avoir (AV)", + 7: "Bon d'avoir financier (BA)", + } + + libelle_base = suggestions.get(type_num, f"Type {type_num}") + + if prefix_commun: + libelle_base += f" - Detecte: prefix '{prefix_commun}'" + + return libelle_base + + +@app.post("/sage/test-creation-par-type", dependencies=[Depends(verify_token)]) +def tester_creation_par_type(type_doc: int = Query(..., ge=0, le=20)): + """ + TEST: Essaie de créer un document d'un type spécifique + + Permet de tester tous les types possibles (0-20) pour trouver + lesquels fonctionnent sur votre installation + """ + try: + if not sage or not sage.cial: + raise HTTPException(503, "Service Sage indisponible") + + with sage._com_context(), sage._lock_com: + logger.info(f"[TEST] Tentative creation type {type_doc}...") + + try: + # Essayer de créer un process + process = sage.cial.CreateProcess_Document(type_doc) + + if not process: + return { + "success": False, + "type": type_doc, + "resultat": "Process NULL retourne", + } + + # Si on arrive ici, le type est valide ! + doc = process.Document + + try: + doc = win32com.client.CastTo(doc, "IBODocumentVente3") + except: + pass + + # Récupérer les infos du document créé + type_reel = getattr(doc, "DO_Type", -1) + domaine = getattr(doc, "DO_Domaine", -1) + + # NE PAS VALIDER le document (pas de Write/Process) + # On veut juste savoir si la création est possible + + del process + del doc + + return { + "success": True, + "type_demande": type_doc, + "type_reel_doc": type_reel, + "domaine": {0: "Vente", 1: "Achat"}.get(domaine, domaine), + "resultat": "CREATION POSSIBLE", + "note": "Document non valide (test uniquement)", + } + + except Exception as e: + return { + "success": False, + "type": type_doc, + "erreur": str(e), + "resultat": "CREATION IMPOSSIBLE", + } + + except Exception as e: + logger.error(f"[TEST] Erreur test type {type_doc}: {e}") + raise HTTPException(500, str(e)) + + # ===================================================== # LANCEMENT # =====================================================