diff --git a/main.py b/main.py index 8bd9682..216730a 100644 --- a/main.py +++ b/main.py @@ -1513,6 +1513,185 @@ def diagnostiquer_exigences_facture(): raise HTTPException(500, str(e)) +@app.post("/sage/test/creer-facture-vide", dependencies=[Depends(verify_token)]) +def tester_creation_facture_vide(): + """ + 🧪 TEST: Crée une facture vide pour identifier les champs obligatoires + + Ce test permet de découvrir EXACTEMENT quels champs Sage exige + """ + try: + if not sage or not sage.cial: + raise HTTPException(503, "Service Sage indisponible") + + with sage._com_context(), sage._lock_com: + logger.info("[TEST] Creation facture test...") + + # 1. Créer le process + process = sage.cial.CreateProcess_Document(60) + doc = process.Document + + try: + doc = win32com.client.CastTo(doc, "IBODocumentVente3") + except: + pass + + # 2. Définir UNIQUEMENT les champs absolument critiques + import pywintypes + + # Date (obligatoire) + doc.DO_Date = pywintypes.Time(datetime.now()) + + # Client (obligatoire) - Utiliser le premier client disponible + factory_client = sage.cial.CptaApplication.FactoryClient + persist_client = factory_client.List(1) + + if persist_client: + client = sage._cast_client(persist_client) + if client: + doc.SetDefaultClient(client) + client_code = getattr(client, "CT_Num", "?") + logger.info(f"[TEST] Client test: {client_code}") + + # 3. Écrire sans Process() pour voir les valeurs par défaut + doc.Write() + doc.Read() + + # 4. Analyser tous les champs + champs_analyse = {} + + for attr in dir(doc): + if attr.startswith("DO_") or attr.startswith("CT_"): + try: + valeur = getattr(doc, attr, None) + if valeur is not None: + champs_analyse[attr] = { + "valeur": str(valeur), + "type": type(valeur).__name__, + } + except: + pass + + logger.info(f"[TEST] {len(champs_analyse)} champs analyses") + + # 5. Tester Process() pour voir l'erreur exacte + erreur_process = None + try: + process.Process() + logger.info("[TEST] Process() reussi (inattendu!)") + except Exception as e: + erreur_process = str(e) + logger.info(f"[TEST] Process() echoue comme prevu: {e}") + + # Ne pas commit - c'est juste un test + try: + sage.cial.CptaApplication.RollbackTrans() + except: + pass + + return { + "success": True, + "champs_definis": champs_analyse, + "erreur_process": erreur_process, + "conseil": "Les champs manquants dans l'erreur sont probablement obligatoires", + } + + except Exception as e: + logger.error(f"[TEST] Erreur: {e}", exc_info=True) + raise HTTPException(500, str(e)) + + +@app.get("/sage/config/parametres-facture", dependencies=[Depends(verify_token)]) +def verifier_parametres_facture(): + """ + 🔍 Vérifie les paramètres Sage pour la création de factures + """ + try: + if not sage or not sage.cial: + raise HTTPException(503, "Service Sage indisponible") + + with sage._com_context(), sage._lock_com: + parametres = {} + + # Paramètres société + try: + param_societe = sage.cial.CptaApplication.ParametreSociete + + parametres["societe"] = { + "journal_vente_defaut": getattr( + param_societe, "P_CodeJournalVte", "N/A" + ), + "mode_reglement_defaut": getattr( + param_societe, "P_ModeRegl", "N/A" + ), + "souche_facture": getattr(param_societe, "P_SoucheFacture", "N/A"), + } + except Exception as e: + parametres["erreur_societe"] = str(e) + + # Tester un client existant + try: + factory_client = sage.cial.CptaApplication.FactoryClient + persist = factory_client.List(1) + + if persist: + client = sage._cast_client(persist) + if client: + parametres["exemple_client"] = { + "code": getattr(client, "CT_Num", "?"), + "mode_reglement": getattr(client, "CT_ModeRegl", "N/A"), + "conditions_reglement": getattr( + client, "CT_CondRegl", "N/A" + ), + } + except Exception as e: + parametres["erreur_client"] = str(e) + + # Journaux disponibles + try: + factory_journal = sage.cial.CptaApplication.FactoryJournal + journaux = [] + + index = 1 + while index <= 20: # Max 20 journaux + try: + persist_journal = factory_journal.List(index) + if persist_journal is None: + break + + # Cast en journal + journal = win32com.client.CastTo(persist_journal, "IBOJournal3") + journal.Read() + + journaux.append( + { + "code": getattr(journal, "JO_Num", "?"), + "intitule": getattr(journal, "JO_Intitule", "?"), + "type": getattr(journal, "JO_Type", "?"), + } + ) + + index += 1 + except: + index += 1 + break + + parametres["journaux_disponibles"] = journaux + + except Exception as e: + parametres["erreur_journaux"] = str(e) + + return { + "success": True, + "parametres": parametres, + "conseil": "Utilisez ces valeurs pour remplir les champs obligatoires des factures", + } + + except Exception as e: + logger.error(f"Erreur verification config: {e}", exc_info=True) + raise HTTPException(500, str(e)) + + # ===================================================== # LANCEMENT # ===================================================== diff --git a/sage_connector.py b/sage_connector.py index 7ae40be..9eeb42f 100644 --- a/sage_connector.py +++ b/sage_connector.py @@ -1355,104 +1355,200 @@ class SageConnector: # ÉTAPE 8 : COMPLÉTER LES CHAMPS OBLIGATOIRES POUR FACTURE # ======================================== if type_cible == 60: # Facture - logger.info( - "[TRANSFORM] Completion champs obligatoires facture..." - ) - - # Mode de règlement (obligatoire pour facture) + logger.info("[TRANSFORM] Completion champs obligatoires facture...") + + # 1. Mode de règlement (CRITIQUE) try: - # Récupérer le mode de règlement du client + mode_reglement = None + + # Essayer de récupérer du client if client_obj_cible: - mode_reglement = getattr( - client_obj_cible, "CT_ModeRegl", None - ) - if mode_reglement: - doc_cible.DO_ModeRegl = mode_reglement - logger.info( - f"[TRANSFORM] Mode reglement: {mode_reglement}" - ) - except Exception as e: - logger.warning( - f"[TRANSFORM] Impossible de definir mode reglement: {e}" - ) - # Forcer un mode par défaut (0 = Aucun) - try: - doc_cible.DO_ModeRegl = 0 - except: - pass - - # Conditions de règlement - try: - if client_obj_cible: - cond_reglement = getattr( - client_obj_cible, "CT_CondRegl", None - ) - if cond_reglement: - doc_cible.DO_CondRegl = cond_reglement - except Exception as e: - logger.warning( - f"[TRANSFORM] Impossible de definir conditions reglement: {e}" - ) - - # Code journal (critique pour facture) - try: - # Vérifier si un code journal est déjà défini - journal_actuel = getattr(doc_cible, "DO_CodeJournal", None) - if not journal_actuel: - # Essayer de récupérer le journal par défaut try: - # Généralement "VE" pour ventes - doc_cible.DO_CodeJournal = "VE" - logger.info("[TRANSFORM] Code journal: VE") + mode_reglement = getattr(client_obj_cible, 'CT_ModeRegl', None) + if mode_reglement is not None: + logger.info(f"[TRANSFORM] Mode reglement client: {mode_reglement}") except: pass + + # Si pas trouvé, utiliser celui du document source + if mode_reglement is None: + try: + mode_reglement = getattr(doc_source, 'DO_ModeRegl', None) + if mode_reglement is not None: + logger.info(f"[TRANSFORM] Mode reglement source: {mode_reglement}") + except: + pass + + # Forcer une valeur par défaut si toujours None + if mode_reglement is None: + mode_reglement = 0 # 0 = Aucun + logger.info("[TRANSFORM] Mode reglement defaut: 0 (Aucun)") + + doc_cible.DO_ModeRegl = mode_reglement + except Exception as e: - logger.warning( - f"[TRANSFORM] Impossible de definir code journal: {e}" - ) - - # Souche de numérotation + logger.error(f"[TRANSFORM] Erreur mode reglement: {e}") + raise + + # 2. Conditions de règlement try: - souche = getattr(doc_cible, "DO_Souche", None) - if not souche or souche == 0: - # Utiliser souche par défaut (généralement 0) - doc_cible.DO_Souche = 0 + cond_reglement = None + + if client_obj_cible: + try: + cond_reglement = getattr(client_obj_cible, 'CT_CondRegl', None) + if cond_reglement is not None: + doc_cible.DO_CondRegl = cond_reglement + logger.info(f"[TRANSFORM] Conditions reglement: {cond_reglement}") + except: + pass + + # Fallback sur document source + if cond_reglement is None: + try: + cond_reglement = getattr(doc_source, 'DO_CondRegl', None) + if cond_reglement is not None: + doc_cible.DO_CondRegl = cond_reglement + except: + pass + except Exception as e: - logger.warning( - f"[TRANSFORM] Impossible de definir souche: {e}" - ) + logger.warning(f"[TRANSFORM] Conditions reglement non definies: {e}") + + # 3. Code journal (CRITIQUE pour comptabilisation) + try: + journal = None + + # Essayer de récupérer du document source + try: + journal = getattr(doc_source, 'DO_CodeJournal', None) + if journal: + logger.info(f"[TRANSFORM] Journal source: {journal}") + except: + pass + + # Si pas trouvé, essayer le journal par défaut de Sage + if not journal: + try: + # Récupérer le paramètre société + param_societe = self.cial.CptaApplication.ParametreSociete + if param_societe: + # Journal vente par défaut + journal = getattr(param_societe, 'P_CodeJournalVte', 'VE') + logger.info(f"[TRANSFORM] Journal societe: {journal}") + except: + pass + + # Dernier recours : "VE" (standard Sage) + if not journal: + journal = "VE" + logger.info("[TRANSFORM] Journal par defaut: VE") + + doc_cible.DO_CodeJournal = journal + + except Exception as e: + logger.error(f"[TRANSFORM] Erreur code journal: {e}") + raise + + # 4. Souche de numérotation + try: + souche = getattr(doc_source, 'DO_Souche', 0) + doc_cible.DO_Souche = souche + logger.info(f"[TRANSFORM] Souche: {souche}") + except Exception as e: + logger.warning(f"[TRANSFORM] Erreur souche: {e}") + try: + doc_cible.DO_Souche = 0 + except: + pass + + # 5. Type de calcul (taxes) + try: + type_calcul = getattr(doc_source, 'DO_TypeCalcul', None) + if type_calcul is not None: + doc_cible.DO_TypeCalcul = type_calcul + logger.info(f"[TRANSFORM] Type calcul: {type_calcul}") + except Exception as e: + logger.debug(f"[TRANSFORM] Type calcul non defini: {e}") + + # 6. Code taxe (important si TVA) + try: + code_taxe = getattr(doc_source, 'DO_CodeTaxe1', None) + if code_taxe is not None: + doc_cible.DO_CodeTaxe1 = code_taxe + logger.info(f"[TRANSFORM] Code taxe: {code_taxe}") + except Exception as e: + logger.debug(f"[TRANSFORM] Code taxe non defini: {e}") + + # 7. Écrire le document avec tous les champs + logger.info("[TRANSFORM] Ecriture document avec champs obligatoires...") + doc_cible.Write() + + + # ============================================================================== + # PARTIE 3 : AMÉLIORATION ÉTAPE 9 - Validation avec diagnostic détaillé + # ============================================================================== # ======================================== # ÉTAPE 9 : VALIDER LE DOCUMENT # ======================================== logger.info("[TRANSFORM] Validation document cible...") - doc_cible.Write() + # Relire pour vérifier que tout est OK + doc_cible.Read() + # Diagnostic pré-validation + logger.info("[TRANSFORM] === PRE-VALIDATION CHECK ===") + champs_critiques = { + "Type": getattr(doc_cible, 'DO_Type', '?'), + "Client": getattr(doc_cible, 'CT_Num', '?'), + "Date": getattr(doc_cible, 'DO_Date', '?'), + "Mode reglement": getattr(doc_cible, 'DO_ModeRegl', '?'), + "Code journal": getattr(doc_cible, 'DO_CodeJournal', '?'), + "Souche": getattr(doc_cible, 'DO_Souche', '?'), + "Statut": getattr(doc_cible, 'DO_Statut', '?'), + } + + for nom, valeur in champs_critiques.items(): + logger.info(f" {nom}: {valeur}") + + # Vérifier que les champs critiques ne sont pas vides + champs_manquants = [] + if not getattr(doc_cible, 'CT_Num', None): + champs_manquants.append("Client (CT_Num)") + if type_cible == 60: # Facture + if not getattr(doc_cible, 'DO_CodeJournal', None): + champs_manquants.append("Code journal (DO_CodeJournal)") + if getattr(doc_cible, 'DO_ModeRegl', None) is None: + champs_manquants.append("Mode reglement (DO_ModeRegl)") + + if champs_manquants: + erreur = f"Champs obligatoires manquants: {', '.join(champs_manquants)}" + logger.error(f"[TRANSFORM] {erreur}") + raise ValueError(erreur) + + # Lancer le processus try: + logger.info("[TRANSFORM] Appel Process()...") process.Process() - logger.info("[TRANSFORM] Document cible valide") + logger.info("[TRANSFORM] Document cible valide avec succes") + except Exception as e: - logger.error(f"[TRANSFORM] Erreur Process(): {e}") - - # Diagnostic détaillé + logger.error(f"[TRANSFORM] ERREUR Process(): {e}") + logger.error("[TRANSFORM] === DIAGNOSTIC COMPLET ===") + + # Afficher TOUS les attributs du document try: - logger.info("[TRANSFORM] === DIAGNOSTIC DOCUMENT ===") - logger.info(f"Type: {getattr(doc_cible, 'DO_Type', '?')}") - logger.info(f"Client: {getattr(doc_cible, 'CT_Num', '?')}") - logger.info(f"Date: {getattr(doc_cible, 'DO_Date', '?')}") - logger.info( - f"Mode reglement: {getattr(doc_cible, 'DO_ModeRegl', '?')}" - ) - logger.info( - f"Code journal: {getattr(doc_cible, 'DO_CodeJournal', '?')}" - ) - logger.info( - f"Souche: {getattr(doc_cible, 'DO_Souche', '?')}" - ) + for attr in dir(doc_cible): + if attr.startswith('DO_') or attr.startswith('CT_'): + try: + valeur = getattr(doc_cible, attr, 'N/A') + logger.error(f" {attr}: {valeur}") + except: + pass except: pass - + raise # ========================================