From f0c84c9cb6a2fc0b10e7c7fec4723871a6ca986d Mon Sep 17 00:00:00 2001 From: mickael Date: Sat, 20 Dec 2025 14:06:04 +0100 Subject: [PATCH] edited pydantic schemas to permit date_livraison and date_expedition gathering --- main.py | 65 ++-- sage_connector.py | 751 +++++++++++++++++++++++++++++----------------- 2 files changed, 513 insertions(+), 303 deletions(-) diff --git a/main.py b/main.py index acaba56..c6da58f 100644 --- a/main.py +++ b/main.py @@ -63,6 +63,8 @@ class ChampLibreRequest(BaseModel): class DevisRequest(BaseModel): client_id: str date_devis: Optional[date] = None + date_livraison: Optional[date] = None + date_expedition: Optional[date] = None reference: Optional[str] = None lignes: List[Dict] @@ -145,6 +147,8 @@ class CommandeCreateRequest(BaseModel): client_id: str date_commande: Optional[date] = None + date_livraison: Optional[date] = None + date_expedition: Optional[date] = None reference: Optional[str] = None lignes: List[Dict] @@ -161,6 +165,8 @@ class LivraisonCreateGatewayRequest(BaseModel): client_id: str date_livraison: Optional[date] = None + date_livraison_prevue: Optional[date] = None + date_expedition: Optional[date] = None lignes: List[Dict] reference: Optional[str] = None @@ -177,6 +183,8 @@ class AvoirCreateGatewayRequest(BaseModel): client_id: str date_avoir: Optional[date] = None + date_livraison: Optional[date] = None + date_expedition: Optional[date] = None lignes: List[Dict] reference: Optional[str] = None @@ -193,6 +201,8 @@ class FactureCreateGatewayRequest(BaseModel): client_id: str date_facture: Optional[date] = None + date_livraison: Optional[date] = None + date_expedition: Optional[date] = None lignes: List[Dict] reference: Optional[str] = None @@ -513,6 +523,8 @@ def creer_devis(req: DevisRequest): devis_data = { "client": {"code": req.client_id, "intitule": ""}, "date_devis": req.date_devis or date.today(), + "date_livraison": req.date_livraison or date.today(), + "date_expedition": req.date_expedition or date.today(), "reference": req.reference, "lignes": req.lignes, } @@ -1041,6 +1053,8 @@ def creer_commande_endpoint(req: CommandeCreateRequest): commande_data = { "client": {"code": req.client_id, "intitule": ""}, "date_commande": req.date_commande or date.today(), + "date_livraison": req.date_livraison or date.today(), + "date_expedition": req.date_expedition or date.today(), "reference": req.reference, "lignes": req.lignes, } @@ -1082,6 +1096,8 @@ def creer_livraison_endpoint(req: LivraisonCreateGatewayRequest): livraison_data = { "client": {"code": req.client_id, "intitule": ""}, "date_livraison": req.date_livraison or date.today(), + "date_livraison_prevue": req.date_livraison or date.today(), + "date_expedition": req.date_expedition or date.today(), "reference": req.reference, "lignes": req.lignes, } @@ -1123,6 +1139,8 @@ def creer_avoir_endpoint(req: AvoirCreateGatewayRequest): avoir_data = { "client": {"code": req.client_id, "intitule": ""}, "date_avoir": req.date_avoir or date.today(), + "date_livraison": req.date_livraison or date.today(), + "date_expedition": req.date_expedition or date.today(), "reference": req.reference, "lignes": req.lignes, } @@ -1167,6 +1185,8 @@ def creer_facture_endpoint(req: FactureCreateGatewayRequest): facture_data = { "client": {"code": req.client_id, "intitule": ""}, "date_facture": req.date_facture or date.today(), + "date_livraison": req.date_livraison or date.today(), + "date_expedition": req.date_expedition or date.today(), "reference": req.reference, "lignes": req.lignes, } @@ -1615,16 +1635,14 @@ def lire_mouvement_stock(numero: str): logger.error(f"❌ Erreur lecture mouvement : {e}") raise HTTPException(500, str(e)) + @app.get("/sage/modeles/list") def lister_modeles_disponibles(): """Liste tous les modèles .bgc disponibles pour chaque type de document""" try: modeles = sage.lister_modeles_crystal() - - return { - "success": True, - "data": modeles - } + + return {"success": True, "data": modeles} except Exception as e: logger.error(f"❌ Erreur listage modèles: {e}") raise HTTPException(500, str(e)) @@ -1635,33 +1653,34 @@ def generer_pdf_document( numero: str, type_doc: int = Query(..., description="Type document (0=devis, 60=facture, etc.)"), modele: str = Query(None, description="Nom du modèle .bgc (optionnel)"), - base64_encode: bool = Query(True, description="Retourner en base64") + base64_encode: bool = Query(True, description="Retourner en base64"), ): """ 📄 Génère un PDF d'un document Sage avec le modèle spécifié """ try: # ✅ LOG pour debug - logger.info(f"📄 PDF Request: numero={numero}, type={type_doc}, modele={modele}, base64={base64_encode}") - + logger.info( + f"📄 PDF Request: numero={numero}, type={type_doc}, modele={modele}, base64={base64_encode}" + ) + # Générer le PDF pdf_bytes = sage.generer_pdf_document( - numero=numero, - type_doc=type_doc, - modele=modele + numero=numero, type_doc=type_doc, modele=modele ) - + if not pdf_bytes: raise HTTPException(404, f"Impossible de générer le PDF pour {numero}") - + # ✅ LOG taille PDF logger.info(f"✅ PDF généré: {len(pdf_bytes)} octets") - + if base64_encode: # Retour en JSON avec base64 import base64 - pdf_base64 = base64.b64encode(pdf_bytes).decode('utf-8') - + + pdf_base64 = base64.b64encode(pdf_bytes).decode("utf-8") + return { "success": True, "data": { @@ -1670,22 +1689,22 @@ def generer_pdf_document( "modele": modele or "défaut", "pdf_base64": pdf_base64, "size_bytes": len(pdf_bytes), - "size_readable": f"{len(pdf_bytes) / 1024:.1f} KB" - } + "size_readable": f"{len(pdf_bytes) / 1024:.1f} KB", + }, } else: # Retour direct du fichier PDF from fastapi.responses import Response - + return Response( content=pdf_bytes, media_type="application/pdf", headers={ "Content-Disposition": f'inline; filename="{numero}.pdf"', - "Content-Length": str(len(pdf_bytes)) # ✅ Taille explicite - } + "Content-Length": str(len(pdf_bytes)), # ✅ Taille explicite + }, ) - + except HTTPException: raise except ValueError as e: @@ -1694,7 +1713,7 @@ def generer_pdf_document( except Exception as e: logger.error(f"❌ Erreur technique: {e}", exc_info=True) raise HTTPException(500, str(e)) - + # ===================================================== # LANCEMENT diff --git a/sage_connector.py b/sage_connector.py index b50950a..4bb15eb 100644 --- a/sage_connector.py +++ b/sage_connector.py @@ -1170,7 +1170,6 @@ class SageConnector: mapping = {0: 0, 1: 10, 2: 20, 3: 30, 4: 40, 5: 50, 6: 60} return mapping.get(type_sql, type_sql) - def _lire_document_sql(self, numero: str, type_doc: int): """ Lit un document spécifique par son numéro. @@ -1230,9 +1229,7 @@ class SageConnector: "numero": numero_piece, "reference": self._safe_strip(row[2]), # DO_Ref "date": str(row[1]) if row[1] else "", # DO_Date - "date_livraison": ( - str(row[7]) if row[7] else "" - ), # DO_DateLivr + "date_livraison": (str(row[7]) if row[7] else ""), # DO_DateLivr "date_expedition": ( str(row[8]) if row[8] else "" ), # DO_DateExpedition @@ -3100,7 +3097,6 @@ class SageConnector: "date_modification": "", } - def normaliser_date(self, valeur): if isinstance(valeur, str): try: @@ -3117,11 +3113,10 @@ class SageConnector: else: return datetime.now() - def creer_devis_enrichi(self, devis_data: dict, forcer_brouillon: bool = False): """ Crée un devis dans Sage avec support de la référence et des dates. - + Args: devis_data: dict contenant: - client: {code: str} @@ -3163,14 +3158,21 @@ class SageConnector: logger.info("📄 Document devis créé") # ===== DATES ===== - doc.DO_Date = pywintypes.Time(self.normaliser_date(devis_data.get("date_devis"))) - - if "date_expedition" in devis_data and devis_data["date_expedition"]: + doc.DO_Date = pywintypes.Time( + self.normaliser_date(devis_data.get("date_devis")) + ) + + if ( + "date_expedition" in devis_data + and devis_data["date_expedition"] + ): doc.DO_DateExpedition = pywintypes.Time( self.normaliser_date(devis_data["date_expedition"]) ) - logger.info(f"📅 Date expédition: {devis_data['date_expedition']}") - + logger.info( + f"📅 Date expédition: {devis_data['date_expedition']}" + ) + if "date_livraison" in devis_data and devis_data["date_livraison"]: doc.DO_DateLivr = pywintypes.Time( self.normaliser_date(devis_data["date_livraison"]) @@ -3330,33 +3332,43 @@ class SageConnector: # ===== ATTENTE POUR STABILISATION ===== import time + time.sleep(0.5) # ===== RÉFÉRENCE (RECHARGER D'ABORD LE DOCUMENT) ===== if "reference" in devis_data and devis_data["reference"]: try: - logger.info(f"🔖 Application de la référence: {devis_data['reference']}") - + logger.info( + f"🔖 Application de la référence: {devis_data['reference']}" + ) + # RECHARGER le document par son numéro doc_reload = self._charger_devis(numero_devis) - + # Appliquer la référence nouvelle_reference = devis_data["reference"] - doc_reload.DO_Ref = str(nouvelle_reference) if nouvelle_reference else "" + doc_reload.DO_Ref = ( + str(nouvelle_reference) if nouvelle_reference else "" + ) doc_reload.Write() - + time.sleep(0.5) - + doc_reload.Read() - + logger.info(f"✅ Référence définie: {nouvelle_reference}") except Exception as e: - logger.warning(f"⚠️ Impossible de définir la référence: {e}", exc_info=True) + logger.warning( + f"⚠️ Impossible de définir la référence: {e}", + exc_info=True, + ) # ===== RELECTURE FINALE ===== time.sleep(0.5) - doc_final_data = self._relire_devis(numero_devis, devis_data, forcer_brouillon) + doc_final_data = self._relire_devis( + numero_devis, devis_data, forcer_brouillon + ) logger.info( f"✅ ✅ ✅ DEVIS CRÉÉ: {numero_devis} - {doc_final_data['total_ttc']}€ TTC ✅ ✅ ✅" @@ -3377,7 +3389,6 @@ class SageConnector: logger.error(f"❌ ERREUR CRÉATION DEVIS: {e}", exc_info=True) raise RuntimeError(f"Échec création devis: {str(e)}") - def _recuperer_numero_devis(self, process, doc): """Récupère le numéro du devis créé via plusieurs méthodes.""" numero_devis = None @@ -3386,9 +3397,7 @@ class SageConnector: try: doc_result = process.DocumentResult if doc_result: - doc_result = win32com.client.CastTo( - doc_result, "IBODocumentVente3" - ) + doc_result = win32com.client.CastTo(doc_result, "IBODocumentVente3") doc_result.Read() numero_devis = getattr(doc_result, "DO_Piece", "") except: @@ -3410,7 +3419,6 @@ class SageConnector: return numero_devis - def _relire_devis(self, numero_devis, devis_data, forcer_brouillon): """Relit le devis créé et extrait les informations finales.""" factory_doc = self.cial.FactoryDocumentVente @@ -3418,31 +3426,31 @@ class SageConnector: if not persist_reread: logger.debug("ReadPiece échoué, recherche dans List()...") - persist_reread = self._rechercher_devis_dans_liste(numero_devis, factory_doc) + persist_reread = self._rechercher_devis_dans_liste( + numero_devis, factory_doc + ) # Extraction des informations if persist_reread: - doc_final = win32com.client.CastTo( - persist_reread, "IBODocumentVente3" - ) + doc_final = win32com.client.CastTo(persist_reread, "IBODocumentVente3") doc_final.Read() total_ht = float(getattr(doc_final, "DO_TotalHT", 0.0)) total_ttc = float(getattr(doc_final, "DO_TotalTTC", 0.0)) statut_final = getattr(doc_final, "DO_Statut", 0) reference_final = getattr(doc_final, "DO_Ref", "") - + # Dates date_expedition_final = None date_livraison_final = None - + try: date_exped = getattr(doc_final, "DO_DateExpedition", None) if date_exped: date_expedition_final = date_exped.strftime("%Y-%m-%d") except: pass - + try: date_livr = getattr(doc_final, "DO_DateLivr", None) if date_livr: @@ -3493,9 +3501,7 @@ class SageConnector: if persist_test is None: break - doc_test = win32com.client.CastTo( - persist_test, "IBODocumentVente3" - ) + doc_test = win32com.client.CastTo(persist_test, "IBODocumentVente3") doc_test.Read() if ( @@ -3508,9 +3514,8 @@ class SageConnector: index += 1 except: index += 1 - - return None + return None def modifier_devis(self, numero: str, devis_data: Dict) -> Dict: """Modifie un devis existant dans Sage.""" @@ -3531,15 +3536,19 @@ class SageConnector: devis_data_temp = devis_data.copy() reference_a_modifier = None statut_a_modifier = None - + if "reference" in devis_data_temp: reference_a_modifier = devis_data_temp.pop("reference") - logger.info("🔖 Modification de la référence reportée après les lignes") - + logger.info( + "🔖 Modification de la référence reportée après les lignes" + ) + if "lignes" in devis_data and devis_data["lignes"] is not None: if "statut" in devis_data_temp: statut_a_modifier = devis_data_temp.pop("statut") - logger.info("📊 Modification du statut reportée après les lignes") + logger.info( + "📊 Modification du statut reportée après les lignes" + ) # ===== MODIFIER CHAMPS SIMPLES (sauf référence et statut) ===== champs_modifies = self._modifier_champs_simples(doc, devis_data_temp) @@ -3548,31 +3557,37 @@ class SageConnector: if "lignes" in devis_data and devis_data["lignes"] is not None: self._modifier_lignes_devis(doc, devis_data["lignes"]) champs_modifies.append("lignes") - + logger.info("💾 Sauvegarde après modification des lignes...") doc.Write() - + import time + time.sleep(0.5) - + doc.Read() # ===== MODIFIER LA RÉFÉRENCE (APRÈS les lignes) ===== if reference_a_modifier is not None: try: ancienne_reference = getattr(doc, "DO_Ref", "") - nouvelle_reference = str(reference_a_modifier) if reference_a_modifier else "" - + nouvelle_reference = ( + str(reference_a_modifier) if reference_a_modifier else "" + ) + doc.DO_Ref = nouvelle_reference doc.Write() - + import time + time.sleep(0.5) - + doc.Read() - + champs_modifies.append("reference") - logger.info(f"🔖 Référence modifiée: '{ancienne_reference}' → '{nouvelle_reference}'") + logger.info( + f"🔖 Référence modifiée: '{ancienne_reference}' → '{nouvelle_reference}'" + ) except Exception as e: logger.warning(f"⚠️ Impossible de modifier la référence: {e}") @@ -3581,18 +3596,26 @@ class SageConnector: try: statut_actuel = getattr(doc, "DO_Statut", 0) nouveau_statut = int(statut_a_modifier) - - if nouveau_statut != statut_actuel and nouveau_statut in [0, 1, 2, 3]: + + if nouveau_statut != statut_actuel and nouveau_statut in [ + 0, + 1, + 2, + 3, + ]: doc.DO_Statut = nouveau_statut doc.Write() - + import time + time.sleep(0.5) - + doc.Read() - + champs_modifies.append("statut") - logger.info(f"📊 Statut modifié: {statut_actuel} → {nouveau_statut}") + logger.info( + f"📊 Statut modifié: {statut_actuel} → {nouveau_statut}" + ) except Exception as e: logger.warning(f"⚠️ Impossible de modifier le statut: {e}") @@ -3602,17 +3625,20 @@ class SageConnector: doc.Write() except: pass - + import time + time.sleep(0.5) doc.Read() # ===== RÉSULTAT ===== resultat = self._extraire_infos_devis(doc, numero, champs_modifies) - + logger.info(f"✅✅✅ DEVIS MODIFIÉ: {numero} ✅✅✅") - logger.info(f"💰 Totaux: {resultat['total_ht']}€ HT / {resultat['total_ttc']}€ TTC") + logger.info( + f"💰 Totaux: {resultat['total_ht']}€ HT / {resultat['total_ttc']}€ TTC" + ) return resultat @@ -3624,7 +3650,6 @@ class SageConnector: logger.error(f"❌ Erreur technique: {e}", exc_info=True) raise RuntimeError(f"Erreur technique Sage: {str(e)}") - def _charger_devis(self, numero: str): """Charge un devis depuis Sage.""" factory = self.cial.FactoryDocumentVente @@ -3639,9 +3664,8 @@ class SageConnector: doc = win32com.client.CastTo(persist, "IBODocumentVente3") doc.Read() - - return doc + return doc def _rechercher_devis_par_numero(self, numero: str, factory): """Recherche un devis par son numéro dans la liste.""" @@ -3652,9 +3676,7 @@ class SageConnector: if persist_test is None: break - doc_test = win32com.client.CastTo( - persist_test, "IBODocumentVente3" - ) + doc_test = win32com.client.CastTo(persist_test, "IBODocumentVente3") doc_test.Read() if ( @@ -3666,9 +3688,8 @@ class SageConnector: index += 1 except: index += 1 - - return None + return None def _verifier_devis_non_transforme(self, numero: str, doc): """Vérifie que le devis n'a pas déjà été transformé.""" @@ -3685,7 +3706,6 @@ class SageConnector: if statut_actuel == 5: raise ValueError(f"Devis {numero} déjà transformé (statut=5)") - def _modifier_champs_simples(self, doc, devis_data: Dict) -> list: """Modifie les champs simples du devis (date, dates expédition/livraison, référence, statut).""" champs_modifies = [] @@ -3699,10 +3719,12 @@ class SageConnector: # DATE DEVIS - Modifier et sauvegarder immédiatement if "date_devis" in devis_data: try: - doc.DO_Date = pywintypes.Time(self.normaliser_date(devis_data.get("date_devis"))) + doc.DO_Date = pywintypes.Time( + self.normaliser_date(devis_data.get("date_devis")) + ) doc.Write() doc.Read() - + champs_modifies.append("date") logger.info(f"📅 Date devis modifiée: {devis_data['date_devis']}") except Exception as e: @@ -3714,10 +3736,10 @@ class SageConnector: self.normaliser_date(devis_data["date_expedition"]) ) logger.info(f"📅 Date expédition modifiée: {devis_data['date_expedition']}") - + doc.Write() doc.Read() - + champs_modifies.append("date_expedition") # DATE LIVRAISON - Modifier et sauvegarder immédiatement @@ -3727,7 +3749,9 @@ class SageConnector: doc.DO_DateLivr = pywintypes.Time( self.normaliser_date(devis_data["date_livraison"]) ) - logger.info(f"📅 Date livraison modifiée: {devis_data['date_livraison']}") + logger.info( + f"📅 Date livraison modifiée: {devis_data['date_livraison']}" + ) else: # Si None ou vide, effacer la date try: @@ -3735,10 +3759,10 @@ class SageConnector: logger.info("📅 Date livraison effacée") except: pass - + doc.Write() doc.Read() - + champs_modifies.append("date_livraison") except Exception as e: logger.warning(f"⚠️ Impossible de modifier la date de livraison: {e}") @@ -3748,13 +3772,15 @@ class SageConnector: try: nouvelle_reference = devis_data["reference"] ancienne_reference = getattr(doc, "DO_Ref", "") - + doc.DO_Ref = str(nouvelle_reference) if nouvelle_reference else "" doc.Write() doc.Read() - + champs_modifies.append("reference") - logger.info(f"🔖 Référence: '{ancienne_reference}' → '{nouvelle_reference}'") + logger.info( + f"🔖 Référence: '{ancienne_reference}' → '{nouvelle_reference}'" + ) except Exception as e: logger.warning(f"⚠️ Impossible de modifier la référence: {e}") @@ -3764,7 +3790,7 @@ class SageConnector: try: statut_actuel = getattr(doc, "DO_Statut", 0) nouveau_statut = int(devis_data["statut"]) - + # Vérifier que le changement de statut est valide if nouveau_statut != statut_actuel: # Statuts valides: 0=Brouillon, 1=Refusé, 2=Accepté, 3=Confirmé, 5=Transformé @@ -3772,17 +3798,20 @@ class SageConnector: doc.DO_Statut = nouveau_statut doc.Write() doc.Read() - + champs_modifies.append("statut") - logger.info(f"📊 Statut modifié: {statut_actuel} → {nouveau_statut}") + logger.info( + f"📊 Statut modifié: {statut_actuel} → {nouveau_statut}" + ) else: - logger.warning(f"⚠️ Statut {nouveau_statut} invalide ou non modifiable") + logger.warning( + f"⚠️ Statut {nouveau_statut} invalide ou non modifiable" + ) except Exception as e: logger.warning(f"⚠️ Impossible de modifier le statut: {e}") return champs_modifies - def _modifier_lignes_devis(self, doc, nouvelles_lignes: list): """Modifie intelligemment les lignes du devis.""" logger.info(f"🔄 Modification intelligente des lignes...") @@ -3811,32 +3840,33 @@ class SageConnector: # C'est plus sûr que de modifier en place if nb_nouvelles != nb_existantes: logger.info("🔄 Stratégie: Suppression puis recréation des lignes") - + # Supprimer toutes les lignes existantes self._supprimer_toutes_les_lignes(factory_lignes, nb_existantes) - + # Sauvegarder après suppression doc.Write() doc.Read() - + # Recréer toutes les lignes for idx, ligne_data in enumerate(nouvelles_lignes, 1): - self._ajouter_nouvelle_ligne(factory_lignes, factory_article, ligne_data, idx) - + self._ajouter_nouvelle_ligne( + factory_lignes, factory_article, ligne_data, idx + ) + else: # STRATÉGIE : Modification en place si même nombre de lignes logger.info("🔄 Stratégie: Modification en place") - + for idx in range(1, nb_nouvelles + 1): self._modifier_ligne_existante( factory_lignes, factory_article, idx, nouvelles_lignes[idx - 1] ) - def _supprimer_toutes_les_lignes(self, factory_lignes, nb_existantes: int): """Supprime toutes les lignes du devis.""" logger.info(f"🗑️ Suppression de {nb_existantes} lignes...") - + # Supprimer en partant de la fin pour éviter les problèmes d'index for idx in range(nb_existantes, 0, -1): try: @@ -3845,10 +3875,12 @@ class SageConnector: try: ligne = win32com.client.CastTo(ligne_p, "IBODocumentLigne3") except: - ligne = win32com.client.CastTo(ligne_p, "IBODocumentVenteLigne3") - + ligne = win32com.client.CastTo( + ligne_p, "IBODocumentVenteLigne3" + ) + ligne.Read() - + try: ligne.Remove() logger.debug(f" ✅ Ligne {idx} supprimée") @@ -3856,27 +3888,32 @@ class SageConnector: # Si Remove() n'existe pas, essayer WriteDefault() try: ligne.WriteDefault() - logger.debug(f" ⚠️ Ligne {idx} réinitialisée (Remove indisponible)") + logger.debug( + f" ⚠️ Ligne {idx} réinitialisée (Remove indisponible)" + ) except: - logger.warning(f" ⚠️ Impossible de supprimer la ligne {idx}") + logger.warning( + f" ⚠️ Impossible de supprimer la ligne {idx}" + ) except Exception as e: logger.warning(f" ⚠️ Erreur suppression ligne {idx}: {e}") except Exception as e: logger.debug(f" ⚠️ Ligne {idx} non accessible: {e}") - - def _modifier_ligne_existante(self, factory_lignes, factory_article, idx: int, ligne_data: dict): + def _modifier_ligne_existante( + self, factory_lignes, factory_article, idx: int, ligne_data: dict + ): """Modifie une ligne existante du devis.""" try: ligne_p = factory_lignes.List(idx) if not ligne_p: raise ValueError(f"Ligne {idx} introuvable") - + try: ligne = win32com.client.CastTo(ligne_p, "IBODocumentLigne3") except: ligne = win32com.client.CastTo(ligne_p, "IBODocumentVenteLigne3") - + ligne.Read() persist_article = factory_article.ReadReference(ligne_data["article_code"]) @@ -3911,18 +3948,19 @@ class SageConnector: # Remise if ligne_data.get("remise_pourcentage", 0) > 0: try: - ligne.DL_Remise01REM_Valeur = float(ligne_data["remise_pourcentage"]) + ligne.DL_Remise01REM_Valeur = float( + ligne_data["remise_pourcentage"] + ) ligne.DL_Remise01REM_Type = 0 except: pass ligne.Write() logger.debug(f" ✅ Ligne {idx} modifiée: {ligne_data['article_code']}") - + except Exception as e: logger.error(f" ❌ Erreur modification ligne {idx}: {e}") raise - def _compter_lignes_existantes(self, factory_lignes) -> int: """Compte le nombre de lignes existantes dans le document.""" @@ -3937,11 +3975,12 @@ class SageConnector: index += 1 except: break - + return nb_existantes - - def _ajouter_nouvelle_ligne(self, factory_lignes, factory_article, ligne_data: dict, idx: int): + def _ajouter_nouvelle_ligne( + self, factory_lignes, factory_article, ligne_data: dict, idx: int + ): """Ajoute une nouvelle ligne au devis.""" persist_article = factory_article.ReadReference(ligne_data["article_code"]) if not persist_article: @@ -3976,7 +4015,9 @@ class SageConnector: # Remise if ligne_data.get("remise_pourcentage", 0) > 0: try: - ligne_obj.DL_Remise01REM_Valeur = float(ligne_data["remise_pourcentage"]) + ligne_obj.DL_Remise01REM_Valeur = float( + ligne_data["remise_pourcentage"] + ) ligne_obj.DL_Remise01REM_Type = 0 except: pass @@ -3984,8 +4025,9 @@ class SageConnector: ligne_obj.Write() logger.debug(f" ✅ Ligne {idx} ajoutée") - - def _supprimer_lignes_en_trop(self, factory_lignes, nb_existantes: int, nb_nouvelles: int): + def _supprimer_lignes_en_trop( + self, factory_lignes, nb_existantes: int, nb_nouvelles: int + ): """Supprime les lignes en trop du devis.""" for idx in range(nb_existantes, nb_nouvelles, -1): try: @@ -4003,25 +4045,24 @@ class SageConnector: except: pass - def _extraire_infos_devis(self, doc, numero: str, champs_modifies: list) -> Dict: """Extrait les informations du devis modifié.""" total_ht = float(getattr(doc, "DO_TotalHT", 0.0)) total_ttc = float(getattr(doc, "DO_TotalTTC", 0.0)) statut = getattr(doc, "DO_Statut", 0) reference = getattr(doc, "DO_Ref", "") - + # Extraction des dates date_expedition = None date_livraison = None - + try: date_exped = getattr(doc, "DO_DateExpedition", None) if date_exped: date_expedition = date_exped.strftime("%Y-%m-%d") except: pass - + try: date_livr = getattr(doc, "DO_DateLivr", None) if date_livr: @@ -4039,7 +4080,6 @@ class SageConnector: "champs_modifies": champs_modifies, "statut": statut, } - def lire_devis(self, numero_devis): try: @@ -4055,7 +4095,6 @@ class SageConnector: logger.error(f"❌ Erreur SQL lecture devis {numero_devis}: {e}") return None - def lire_document(self, numero, type_doc): return self._lire_document_sql(numero, type_doc) @@ -5846,7 +5885,7 @@ class SageConnector: def creer_commande_enrichi(self, commande_data: dict) -> Dict: """ Crée une commande dans Sage avec support des dates. - + Args: commande_data: dict contenant: - client: {code: str} @@ -5891,18 +5930,28 @@ class SageConnector: doc.DO_Date = pywintypes.Time( self.normaliser_date(commande_data.get("date_commande")) ) - - if "date_expedition" in commande_data and commande_data["date_expedition"]: + + if ( + "date_expedition" in commande_data + and commande_data["date_expedition"] + ): doc.DO_DateExpedition = pywintypes.Time( self.normaliser_date(commande_data["date_expedition"]) ) - logger.info(f"📅 Date expédition: {commande_data['date_expedition']}") - - if "date_livraison" in commande_data and commande_data["date_livraison"]: + logger.info( + f"📅 Date expédition: {commande_data['date_expedition']}" + ) + + if ( + "date_livraison" in commande_data + and commande_data["date_livraison"] + ): doc.DO_DateLivr = pywintypes.Time( self.normaliser_date(commande_data["date_livraison"]) ) - logger.info(f"📅 Date livraison: {commande_data['date_livraison']}") + logger.info( + f"📅 Date livraison: {commande_data['date_livraison']}" + ) # ===== CLIENT (CRITIQUE) ===== factory_client = self.cial.CptaApplication.FactoryClient @@ -6108,18 +6157,18 @@ class SageConnector: total_ht = float(getattr(doc_final, "DO_TotalHT", 0.0)) total_ttc = float(getattr(doc_final, "DO_TotalTTC", 0.0)) reference_finale = getattr(doc_final, "DO_Ref", "") - + # Dates date_expedition_final = None date_livraison_final = None - + try: date_exped = getattr(doc_final, "DO_DateExpedition", None) if date_exped: date_expedition_final = date_exped.strftime("%Y-%m-%d") except: pass - + try: date_livr = getattr(doc_final, "DO_DateLivr", None) if date_livr: @@ -6147,7 +6196,9 @@ class SageConnector: "total_ttc": total_ttc, "nb_lignes": len(commande_data["lignes"]), "client_code": commande_data["client"]["code"], - "date_commande": str(self.normaliser_date(commande_data.get("date_commande"))), + "date_commande": str( + self.normaliser_date(commande_data.get("date_commande")) + ), "date_expedition": date_expedition_final, "date_livraison": date_livraison_final, "reference": reference_finale, @@ -6165,11 +6216,10 @@ class SageConnector: logger.error(f"❌ Erreur création commande: {e}", exc_info=True) raise RuntimeError(f"Échec création commande: {str(e)}") - def modifier_commande(self, numero: str, commande_data: Dict) -> Dict: """ Modifie une commande existante dans Sage. - + Args: numero: Numéro de la commande commande_data: dict contenant les champs à modifier: @@ -6282,16 +6332,20 @@ class SageConnector: commande_data_temp = commande_data.copy() reference_a_modifier = None statut_a_modifier = None - + if modif_lignes: if modif_ref: reference_a_modifier = commande_data_temp.pop("reference") - logger.info("🔖 Modification de la référence reportée après les lignes") + logger.info( + "🔖 Modification de la référence reportée après les lignes" + ) modif_ref = False - + if modif_statut: statut_a_modifier = commande_data_temp.pop("statut") - logger.info("📊 Modification du statut reportée après les lignes") + logger.info( + "📊 Modification du statut reportée après les lignes" + ) modif_statut = False # ======================================== @@ -6328,13 +6382,21 @@ class SageConnector: # ======================================== # ÉTAPE 5 : MODIFICATIONS SIMPLES (pas de lignes) # ======================================== - if not modif_lignes and (modif_date or modif_date_expedition or modif_date_livraison or modif_statut or modif_ref): + if not modif_lignes and ( + modif_date + or modif_date_expedition + or modif_date_livraison + or modif_statut + or modif_ref + ): logger.info("🎯 Modifications simples (sans lignes)...") if modif_date: logger.info(" 📅 Modification date commande...") doc.DO_Date = pywintypes.Time( - self.normaliser_date(commande_data_temp.get("date_commande")) + self.normaliser_date( + commande_data_temp.get("date_commande") + ) ) champs_modifies.append("date") @@ -6344,13 +6406,15 @@ class SageConnector: self.normaliser_date(commande_data_temp["date_expedition"]) ) champs_modifies.append("date_expedition") - + if modif_date_livraison: logger.info(" 📅 Modification date livraison...") doc.DO_DateLivr = pywintypes.Time( self.normaliser_date(commande_data_temp["date_livraison"]) ) - logger.info(f" ✅ Date livraison: {commande_data_temp['date_livraison']}") + logger.info( + f" ✅ Date livraison: {commande_data_temp['date_livraison']}" + ) champs_modifies.append("date_livraison") if modif_statut: @@ -6412,7 +6476,9 @@ class SageConnector: # D'abord modifier les dates si demandées if modif_date: doc.DO_Date = pywintypes.Time( - self.normaliser_date(commande_data_temp.get("date_commande")) + self.normaliser_date( + commande_data_temp.get("date_commande") + ) ) champs_modifies.append("date") logger.info(" 📅 Date commande modifiée") @@ -6552,6 +6618,7 @@ class SageConnector: logger.info(" ✅ Document écrit") import time + time.sleep(0.5) doc.Read() @@ -6573,18 +6640,23 @@ class SageConnector: if reference_a_modifier is not None: try: ancienne_reference = getattr(doc, "DO_Ref", "") - nouvelle_reference = str(reference_a_modifier) if reference_a_modifier else "" - + nouvelle_reference = ( + str(reference_a_modifier) if reference_a_modifier else "" + ) + doc.DO_Ref = nouvelle_reference doc.Write() - + import time + time.sleep(0.5) - + doc.Read() - + champs_modifies.append("reference") - logger.info(f"🔖 Référence modifiée: '{ancienne_reference}' → '{nouvelle_reference}'") + logger.info( + f"🔖 Référence modifiée: '{ancienne_reference}' → '{nouvelle_reference}'" + ) except Exception as e: logger.warning(f"⚠️ Impossible de modifier la référence: {e}") @@ -6595,18 +6667,21 @@ class SageConnector: try: statut_actuel = getattr(doc, "DO_Statut", 0) nouveau_statut = int(statut_a_modifier) - + if nouveau_statut != statut_actuel: doc.DO_Statut = nouveau_statut doc.Write() - + import time + time.sleep(0.5) - + doc.Read() - + champs_modifies.append("statut") - logger.info(f"📊 Statut modifié: {statut_actuel} → {nouveau_statut}") + logger.info( + f"📊 Statut modifié: {statut_actuel} → {nouveau_statut}" + ) except Exception as e: logger.warning(f"⚠️ Impossible de modifier le statut: {e}") @@ -6616,6 +6691,7 @@ class SageConnector: logger.info("📊 Relecture finale...") import time + time.sleep(0.5) doc.Read() @@ -6631,18 +6707,18 @@ class SageConnector: total_ht = float(getattr(doc, "DO_TotalHT", 0.0)) total_ttc = float(getattr(doc, "DO_TotalTTC", 0.0)) reference_finale = getattr(doc, "DO_Ref", "") - + # Extraire les dates date_expedition_final = None date_livraison_final = None - + try: date_exped = getattr(doc, "DO_DateExpedition", None) if date_exped: date_expedition_final = date_exped.strftime("%Y-%m-%d") except: pass - + try: date_livr = getattr(doc, "DO_DateLivr", None) if date_livr: @@ -6692,11 +6768,10 @@ class SageConnector: raise RuntimeError(f"Erreur Sage: {error_message}") - def creer_livraison_enrichi(self, livraison_data: dict) -> Dict: """ Crée une livraison dans Sage avec support des dates. - + Args: livraison_data: dict contenant: - client: {code: str} @@ -6742,20 +6817,32 @@ class SageConnector: doc.DO_Date = pywintypes.Time( self.normaliser_date(livraison_data.get("date_livraison")) ) - + # Date d'expédition prévue (DO_DateExpedition) - if "date_expedition" in livraison_data and livraison_data["date_expedition"]: + if ( + "date_expedition" in livraison_data + and livraison_data["date_expedition"] + ): doc.DO_DateExpedition = pywintypes.Time( self.normaliser_date(livraison_data["date_expedition"]) ) - logger.info(f"📅 Date expédition: {livraison_data['date_expedition']}") - - # Date de livraison prévue chez le client (DO_DateLivr) - if "date_livraison_prevue" in livraison_data and livraison_data["date_livraison_prevue"]: - doc.DO_DateLivr = pywintypes.Time( - self.normaliser_date(livraison_data["date_livraison_prevue"]) + logger.info( + f"📅 Date expédition: {livraison_data['date_expedition']}" + ) + + # Date de livraison prévue chez le client (DO_DateLivr) + if ( + "date_livraison_prevue" in livraison_data + and livraison_data["date_livraison_prevue"] + ): + doc.DO_DateLivr = pywintypes.Time( + self.normaliser_date( + livraison_data["date_livraison_prevue"] + ) + ) + logger.info( + f"📅 Date livraison prévue: {livraison_data['date_livraison_prevue']}" ) - logger.info(f"📅 Date livraison prévue: {livraison_data['date_livraison_prevue']}") # ===== CLIENT (CRITIQUE) ===== factory_client = self.cial.CptaApplication.FactoryClient @@ -6942,22 +7029,24 @@ class SageConnector: total_ht = float(getattr(doc_final, "DO_TotalHT", 0.0)) total_ttc = float(getattr(doc_final, "DO_TotalTTC", 0.0)) reference_finale = getattr(doc_final, "DO_Ref", "") - + # Dates date_expedition_final = None date_livraison_prevue_final = None - + try: date_exped = getattr(doc_final, "DO_DateExpedition", None) if date_exped: date_expedition_final = date_exped.strftime("%Y-%m-%d") except: pass - + try: date_livr = getattr(doc_final, "DO_DateLivr", None) if date_livr: - date_livraison_prevue_final = date_livr.strftime("%Y-%m-%d") + date_livraison_prevue_final = date_livr.strftime( + "%Y-%m-%d" + ) except: pass else: @@ -6965,7 +7054,9 @@ class SageConnector: total_ttc = 0.0 reference_finale = livraison_data.get("reference", "") date_expedition_final = livraison_data.get("date_expedition") - date_livraison_prevue_final = livraison_data.get("date_livraison_prevue") + date_livraison_prevue_final = livraison_data.get( + "date_livraison_prevue" + ) logger.info( f"✅✅✅ LIVRAISON CRÉÉE: {numero_livraison} - {total_ttc}€ TTC ✅✅✅" @@ -6973,7 +7064,9 @@ class SageConnector: if date_expedition_final: logger.info(f"📅 Date expédition: {date_expedition_final}") if date_livraison_prevue_final: - logger.info(f"📅 Date livraison prévue: {date_livraison_prevue_final}") + logger.info( + f"📅 Date livraison prévue: {date_livraison_prevue_final}" + ) return { "numero_livraison": numero_livraison, @@ -6981,7 +7074,9 @@ class SageConnector: "total_ttc": total_ttc, "nb_lignes": len(livraison_data["lignes"]), "client_code": livraison_data["client"]["code"], - "date_livraison": str(self.normaliser_date(livraison_data.get("date_livraison"))), + "date_livraison": str( + self.normaliser_date(livraison_data.get("date_livraison")) + ), "date_expedition": date_expedition_final, "date_livraison_prevue": date_livraison_prevue_final, "reference": reference_finale, @@ -6999,11 +7094,10 @@ class SageConnector: logger.error(f"❌ Erreur création livraison: {e}", exc_info=True) raise RuntimeError(f"Échec création livraison: {str(e)}") - def modifier_livraison(self, numero: str, livraison_data: Dict) -> Dict: """ Modifie une livraison existante dans Sage. - + Args: numero: Numéro de la livraison livraison_data: dict contenant les champs à modifier: @@ -7104,28 +7198,40 @@ class SageConnector: livraison_data_temp = livraison_data.copy() reference_a_modifier = None statut_a_modifier = None - + if modif_lignes: if modif_ref: reference_a_modifier = livraison_data_temp.pop("reference") - logger.info("🔖 Modification de la référence reportée après les lignes") + logger.info( + "🔖 Modification de la référence reportée après les lignes" + ) modif_ref = False - + if modif_statut: statut_a_modifier = livraison_data_temp.pop("statut") - logger.info("📊 Modification du statut reportée après les lignes") + logger.info( + "📊 Modification du statut reportée après les lignes" + ) modif_statut = False # ======================================== # ÉTAPE 3 : MODIFICATIONS SIMPLES (pas de lignes) # ======================================== - if not modif_lignes and (modif_date or modif_date_expedition or modif_date_livraison_prevue or modif_statut or modif_ref): + if not modif_lignes and ( + modif_date + or modif_date_expedition + or modif_date_livraison_prevue + or modif_statut + or modif_ref + ): logger.info("🎯 Modifications simples (sans lignes)...") if modif_date: logger.info(" 📅 Modification date livraison...") doc.DO_Date = pywintypes.Time( - self.normaliser_date(livraison_data_temp.get("date_livraison")) + self.normaliser_date( + livraison_data_temp.get("date_livraison") + ) ) champs_modifies.append("date") @@ -7134,15 +7240,21 @@ class SageConnector: doc.DO_DateExpedition = pywintypes.Time( self.normaliser_date(livraison_data_temp["date_expedition"]) ) - logger.info(f" ✅ Date expédition: {livraison_data_temp['date_expedition']}") + logger.info( + f" ✅ Date expédition: {livraison_data_temp['date_expedition']}" + ) champs_modifies.append("date_expedition") if modif_date_livraison_prevue: logger.info(" 📅 Modification date livraison prévue...") doc.DO_DateLivr = pywintypes.Time( - self.normaliser_date(livraison_data_temp["date_livraison_prevue"]) + self.normaliser_date( + livraison_data_temp["date_livraison_prevue"] + ) + ) + logger.info( + f" ✅ Date livraison prévue: {livraison_data_temp['date_livraison_prevue']}" ) - logger.info(f" ✅ Date livraison prévue: {livraison_data_temp['date_livraison_prevue']}") champs_modifies.append("date_livraison_prevue") if modif_statut: @@ -7157,7 +7269,9 @@ class SageConnector: try: doc.DO_Ref = livraison_data_temp["reference"] champs_modifies.append("reference") - logger.info(f" ✅ Référence définie: {livraison_data_temp['reference']}") + logger.info( + f" ✅ Référence définie: {livraison_data_temp['reference']}" + ) except Exception as e: logger.warning(f" ⚠️ Référence non définie: {e}") @@ -7174,7 +7288,9 @@ class SageConnector: # D'abord modifier les dates si demandées if modif_date: doc.DO_Date = pywintypes.Time( - self.normaliser_date(livraison_data_temp.get("date_livraison")) + self.normaliser_date( + livraison_data_temp.get("date_livraison") + ) ) champs_modifies.append("date") logger.info(" 📅 Date livraison modifiée") @@ -7188,7 +7304,9 @@ class SageConnector: if modif_date_livraison_prevue: doc.DO_DateLivr = pywintypes.Time( - self.normaliser_date(livraison_data_temp["date_livraison_prevue"]) + self.normaliser_date( + livraison_data_temp["date_livraison_prevue"] + ) ) logger.info(" 📅 Date livraison prévue modifiée") champs_modifies.append("date_livraison_prevue") @@ -7310,6 +7428,7 @@ class SageConnector: logger.info(" ✅ Document écrit") import time + time.sleep(0.5) doc.Read() @@ -7322,18 +7441,23 @@ class SageConnector: if reference_a_modifier is not None: try: ancienne_reference = getattr(doc, "DO_Ref", "") - nouvelle_reference = str(reference_a_modifier) if reference_a_modifier else "" - - logger.info(f"🔖 Modification référence: '{ancienne_reference}' → '{nouvelle_reference}'") - + nouvelle_reference = ( + str(reference_a_modifier) if reference_a_modifier else "" + ) + + logger.info( + f"🔖 Modification référence: '{ancienne_reference}' → '{nouvelle_reference}'" + ) + doc.DO_Ref = nouvelle_reference doc.Write() - + import time + time.sleep(0.5) - + doc.Read() - + champs_modifies.append("reference") logger.info(f" ✅ Référence modifiée avec succès") except Exception as e: @@ -7346,18 +7470,21 @@ class SageConnector: try: statut_actuel = getattr(doc, "DO_Statut", 0) nouveau_statut = int(statut_a_modifier) - + if nouveau_statut != statut_actuel: - logger.info(f"📊 Modification statut: {statut_actuel} → {nouveau_statut}") - + logger.info( + f"📊 Modification statut: {statut_actuel} → {nouveau_statut}" + ) + doc.DO_Statut = nouveau_statut doc.Write() - + import time + time.sleep(0.5) - + doc.Read() - + champs_modifies.append("statut") logger.info(f" ✅ Statut modifié avec succès") except Exception as e: @@ -7369,6 +7496,7 @@ class SageConnector: logger.info("📊 Relecture finale...") import time + time.sleep(0.5) doc.Read() @@ -7377,18 +7505,18 @@ class SageConnector: total_ttc = float(getattr(doc, "DO_TotalTTC", 0.0)) reference_finale = getattr(doc, "DO_Ref", "") statut_final = getattr(doc, "DO_Statut", 0) - + # Extraire les dates date_expedition_final = None date_livraison_prevue_final = None - + try: date_exped = getattr(doc, "DO_DateExpedition", None) if date_exped: date_expedition_final = date_exped.strftime("%Y-%m-%d") except: pass - + try: date_livr = getattr(doc, "DO_DateLivr", None) if date_livr: @@ -7403,7 +7531,9 @@ class SageConnector: if date_expedition_final: logger.info(f" 📅 Date expédition: {date_expedition_final}") if date_livraison_prevue_final: - logger.info(f" 📅 Date livraison prévue: {date_livraison_prevue_final}") + logger.info( + f" 📅 Date livraison prévue: {date_livraison_prevue_final}" + ) logger.info(f" 📝 Champs modifiés: {champs_modifies}") return { @@ -7436,12 +7566,11 @@ class SageConnector: pass raise RuntimeError(f"Erreur Sage: {error_message}") - - + def creer_avoir_enrichi(self, avoir_data: dict) -> Dict: """ Crée un avoir dans Sage avec support des dates. - + Args: avoir_data: dict contenant: - client: {code: str} @@ -7486,18 +7615,25 @@ class SageConnector: doc.DO_Date = pywintypes.Time( self.normaliser_date(avoir_data.get("date_avoir")) ) - - if "date_expedition" in avoir_data and avoir_data["date_expedition"]: + + if ( + "date_expedition" in avoir_data + and avoir_data["date_expedition"] + ): doc.DO_DateExpedition = pywintypes.Time( self.normaliser_date(avoir_data["date_expedition"]) ) - logger.info(f"📅 Date expédition: {avoir_data['date_expedition']}") - + logger.info( + f"📅 Date expédition: {avoir_data['date_expedition']}" + ) + if "date_livraison" in avoir_data and avoir_data["date_livraison"]: doc.DO_DateLivr = pywintypes.Time( self.normaliser_date(avoir_data["date_livraison"]) ) - logger.info(f"📅 Date livraison: {avoir_data['date_livraison']}") + logger.info( + f"📅 Date livraison: {avoir_data['date_livraison']}" + ) # ===== CLIENT (CRITIQUE) ===== factory_client = self.cial.CptaApplication.FactoryClient @@ -7682,18 +7818,18 @@ class SageConnector: total_ht = float(getattr(doc_final, "DO_TotalHT", 0.0)) total_ttc = float(getattr(doc_final, "DO_TotalTTC", 0.0)) reference_finale = getattr(doc_final, "DO_Ref", "") - + # Dates date_expedition_final = None date_livraison_final = None - + try: date_exped = getattr(doc_final, "DO_DateExpedition", None) if date_exped: date_expedition_final = date_exped.strftime("%Y-%m-%d") except: pass - + try: date_livr = getattr(doc_final, "DO_DateLivr", None) if date_livr: @@ -7721,7 +7857,9 @@ class SageConnector: "total_ttc": total_ttc, "nb_lignes": len(avoir_data["lignes"]), "client_code": avoir_data["client"]["code"], - "date_avoir": str(self.normaliser_date(avoir_data.get("date_avoir"))), + "date_avoir": str( + self.normaliser_date(avoir_data.get("date_avoir")) + ), "date_expedition": date_expedition_final, "date_livraison": date_livraison_final, "reference": reference_finale, @@ -7739,11 +7877,10 @@ class SageConnector: logger.error(f"❌ Erreur création avoir: {e}", exc_info=True) raise RuntimeError(f"Échec création avoir: {str(e)}") - def modifier_avoir(self, numero: str, avoir_data: Dict) -> Dict: """ Modifie un avoir existant dans Sage. - + Args: numero: Numéro de l'avoir avoir_data: dict contenant les champs à modifier: @@ -7863,16 +8000,20 @@ class SageConnector: avoir_data_temp = avoir_data.copy() reference_a_modifier = None statut_a_modifier = None - + if modif_lignes: if modif_ref: reference_a_modifier = avoir_data_temp.pop("reference") - logger.info("🔖 Modification de la référence reportée après les lignes") + logger.info( + "🔖 Modification de la référence reportée après les lignes" + ) modif_ref = False - + if modif_statut: statut_a_modifier = avoir_data_temp.pop("statut") - logger.info("📊 Modification du statut reportée après les lignes") + logger.info( + "📊 Modification du statut reportée après les lignes" + ) modif_statut = False # ======================================== @@ -7909,7 +8050,13 @@ class SageConnector: # ======================================== # ÉTAPE 5 : MODIFICATIONS SIMPLES (pas de lignes) # ======================================== - if not modif_lignes and (modif_date or modif_date_expedition or modif_date_livraison or modif_statut or modif_ref): + if not modif_lignes and ( + modif_date + or modif_date_expedition + or modif_date_livraison + or modif_statut + or modif_ref + ): logger.info("🎯 Modifications simples (sans lignes)...") if modif_date: @@ -7924,7 +8071,9 @@ class SageConnector: doc.DO_DateExpedition = pywintypes.Time( self.normaliser_date(avoir_data_temp["date_expedition"]) ) - logger.info(f" ✅ Date expédition: {avoir_data_temp['date_expedition']}") + logger.info( + f" ✅ Date expédition: {avoir_data_temp['date_expedition']}" + ) champs_modifies.append("date_expedition") if modif_date_livraison: @@ -7932,7 +8081,9 @@ class SageConnector: doc.DO_DateLivr = pywintypes.Time( self.normaliser_date(avoir_data_temp["date_livraison"]) ) - logger.info(f" ✅ Date livraison: {avoir_data_temp['date_livraison']}") + logger.info( + f" ✅ Date livraison: {avoir_data_temp['date_livraison']}" + ) champs_modifies.append("date_livraison") if modif_statut: @@ -8134,6 +8285,7 @@ class SageConnector: logger.info(" ✅ Document écrit") import time + time.sleep(0.5) doc.Read() @@ -8155,18 +8307,23 @@ class SageConnector: if reference_a_modifier is not None: try: ancienne_reference = getattr(doc, "DO_Ref", "") - nouvelle_reference = str(reference_a_modifier) if reference_a_modifier else "" - - logger.info(f"🔖 Modification référence: '{ancienne_reference}' → '{nouvelle_reference}'") - + nouvelle_reference = ( + str(reference_a_modifier) if reference_a_modifier else "" + ) + + logger.info( + f"🔖 Modification référence: '{ancienne_reference}' → '{nouvelle_reference}'" + ) + doc.DO_Ref = nouvelle_reference doc.Write() - + import time + time.sleep(0.5) - + doc.Read() - + champs_modifies.append("reference") logger.info(f" ✅ Référence modifiée avec succès") except Exception as e: @@ -8179,18 +8336,21 @@ class SageConnector: try: statut_actuel = getattr(doc, "DO_Statut", 0) nouveau_statut = int(statut_a_modifier) - + if nouveau_statut != statut_actuel: - logger.info(f"📊 Modification statut: {statut_actuel} → {nouveau_statut}") - + logger.info( + f"📊 Modification statut: {statut_actuel} → {nouveau_statut}" + ) + doc.DO_Statut = nouveau_statut doc.Write() - + import time + time.sleep(0.5) - + doc.Read() - + champs_modifies.append("statut") logger.info(f" ✅ Statut modifié avec succès") except Exception as e: @@ -8202,6 +8362,7 @@ class SageConnector: logger.info("📊 Relecture finale...") import time + time.sleep(0.5) doc.Read() @@ -8218,18 +8379,18 @@ class SageConnector: total_ttc = float(getattr(doc, "DO_TotalTTC", 0.0)) reference_finale = getattr(doc, "DO_Ref", "") statut_final = getattr(doc, "DO_Statut", 0) - + # Extraire les dates date_expedition_final = None date_livraison_final = None - + try: date_exped = getattr(doc, "DO_DateExpedition", None) if date_exped: date_expedition_final = date_exped.strftime("%Y-%m-%d") except: pass - + try: date_livr = getattr(doc, "DO_DateLivr", None) if date_livr: @@ -8279,12 +8440,11 @@ class SageConnector: pass raise RuntimeError(f"Erreur Sage: {error_message}") - - + def creer_facture_enrichi(self, facture_data: dict) -> Dict: """ Crée une facture dans Sage avec support des dates. - + Args: facture_data: dict contenant: - client: {code: str} @@ -8329,18 +8489,28 @@ class SageConnector: doc.DO_Date = pywintypes.Time( self.normaliser_date(facture_data.get("date_facture")) ) - - if "date_expedition" in facture_data and facture_data["date_expedition"]: + + if ( + "date_expedition" in facture_data + and facture_data["date_expedition"] + ): doc.DO_DateExpedition = pywintypes.Time( self.normaliser_date(facture_data["date_expedition"]) ) - logger.info(f"📅 Date expédition: {facture_data['date_expedition']}") - - if "date_livraison" in facture_data and facture_data["date_livraison"]: + logger.info( + f"📅 Date expédition: {facture_data['date_expedition']}" + ) + + if ( + "date_livraison" in facture_data + and facture_data["date_livraison"] + ): doc.DO_DateLivr = pywintypes.Time( self.normaliser_date(facture_data["date_livraison"]) ) - logger.info(f"📅 Date livraison: {facture_data['date_livraison']}") + logger.info( + f"📅 Date livraison: {facture_data['date_livraison']}" + ) # ===== CLIENT (CRITIQUE) ===== factory_client = self.cial.CptaApplication.FactoryClient @@ -8583,18 +8753,18 @@ class SageConnector: total_ht = float(getattr(doc_final, "DO_TotalHT", 0.0)) total_ttc = float(getattr(doc_final, "DO_TotalTTC", 0.0)) reference_finale = getattr(doc_final, "DO_Ref", "") - + # Dates date_expedition_final = None date_livraison_final = None - + try: date_exped = getattr(doc_final, "DO_DateExpedition", None) if date_exped: date_expedition_final = date_exped.strftime("%Y-%m-%d") except: pass - + try: date_livr = getattr(doc_final, "DO_DateLivr", None) if date_livr: @@ -8622,7 +8792,9 @@ class SageConnector: "total_ttc": total_ttc, "nb_lignes": len(facture_data["lignes"]), "client_code": facture_data["client"]["code"], - "date_facture": str(self.normaliser_date(facture_data.get("date_facture"))), + "date_facture": str( + self.normaliser_date(facture_data.get("date_facture")) + ), "date_expedition": date_expedition_final, "date_livraison": date_livraison_final, "reference": reference_finale, @@ -8641,11 +8813,10 @@ class SageConnector: logger.error(f"❌ Erreur création facture: {e}", exc_info=True) raise RuntimeError(f"Échec création facture: {str(e)}") - def modifier_facture(self, numero: str, facture_data: Dict) -> Dict: """ Modifie une facture existante dans Sage. - + Args: numero: Numéro de la facture facture_data: dict contenant les champs à modifier: @@ -8765,16 +8936,20 @@ class SageConnector: facture_data_temp = facture_data.copy() reference_a_modifier = None statut_a_modifier = None - + if modif_lignes: if modif_ref: reference_a_modifier = facture_data_temp.pop("reference") - logger.info("🔖 Modification de la référence reportée après les lignes") + logger.info( + "🔖 Modification de la référence reportée après les lignes" + ) modif_ref = False - + if modif_statut: statut_a_modifier = facture_data_temp.pop("statut") - logger.info("📊 Modification du statut reportée après les lignes") + logger.info( + "📊 Modification du statut reportée après les lignes" + ) modif_statut = False # ======================================== @@ -8811,7 +8986,13 @@ class SageConnector: # ======================================== # ÉTAPE 5 : MODIFICATIONS SIMPLES (pas de lignes) # ======================================== - if not modif_lignes and (modif_date or modif_date_expedition or modif_date_livraison or modif_statut or modif_ref): + if not modif_lignes and ( + modif_date + or modif_date_expedition + or modif_date_livraison + or modif_statut + or modif_ref + ): logger.info("🎯 Modifications simples (sans lignes)...") if modif_date: @@ -8826,7 +9007,9 @@ class SageConnector: doc.DO_DateExpedition = pywintypes.Time( self.normaliser_date(facture_data_temp["date_expedition"]) ) - logger.info(f" ✅ Date expédition: {facture_data_temp['date_expedition']}") + logger.info( + f" ✅ Date expédition: {facture_data_temp['date_expedition']}" + ) champs_modifies.append("date_expedition") if modif_date_livraison: @@ -8834,7 +9017,9 @@ class SageConnector: doc.DO_DateLivr = pywintypes.Time( self.normaliser_date(facture_data_temp["date_livraison"]) ) - logger.info(f" ✅ Date livraison: {facture_data_temp['date_livraison']}") + logger.info( + f" ✅ Date livraison: {facture_data_temp['date_livraison']}" + ) champs_modifies.append("date_livraison") if modif_statut: @@ -9036,6 +9221,7 @@ class SageConnector: logger.info(" ✅ Document écrit") import time + time.sleep(0.5) doc.Read() @@ -9057,18 +9243,23 @@ class SageConnector: if reference_a_modifier is not None: try: ancienne_reference = getattr(doc, "DO_Ref", "") - nouvelle_reference = str(reference_a_modifier) if reference_a_modifier else "" - - logger.info(f"🔖 Modification référence: '{ancienne_reference}' → '{nouvelle_reference}'") - + nouvelle_reference = ( + str(reference_a_modifier) if reference_a_modifier else "" + ) + + logger.info( + f"🔖 Modification référence: '{ancienne_reference}' → '{nouvelle_reference}'" + ) + doc.DO_Ref = nouvelle_reference doc.Write() - + import time + time.sleep(0.5) - + doc.Read() - + champs_modifies.append("reference") logger.info(f" ✅ Référence modifiée avec succès") except Exception as e: @@ -9081,18 +9272,21 @@ class SageConnector: try: statut_actuel = getattr(doc, "DO_Statut", 0) nouveau_statut = int(statut_a_modifier) - + if nouveau_statut != statut_actuel: - logger.info(f"📊 Modification statut: {statut_actuel} → {nouveau_statut}") - + logger.info( + f"📊 Modification statut: {statut_actuel} → {nouveau_statut}" + ) + doc.DO_Statut = nouveau_statut doc.Write() - + import time + time.sleep(0.5) - + doc.Read() - + champs_modifies.append("statut") logger.info(f" ✅ Statut modifié avec succès") except Exception as e: @@ -9104,6 +9298,7 @@ class SageConnector: logger.info("📊 Relecture finale...") import time + time.sleep(0.5) doc.Read() @@ -9120,18 +9315,18 @@ class SageConnector: total_ttc = float(getattr(doc, "DO_TotalTTC", 0.0)) reference_finale = getattr(doc, "DO_Ref", "") statut_final = getattr(doc, "DO_Statut", 0) - + # Extraire les dates date_expedition_final = None date_livraison_final = None - + try: date_exped = getattr(doc, "DO_DateExpedition", None) if date_exped: date_expedition_final = date_exped.strftime("%Y-%m-%d") except: pass - + try: date_livr = getattr(doc, "DO_DateLivr", None) if date_livr: @@ -9181,7 +9376,6 @@ class SageConnector: pass raise RuntimeError(f"Erreur Sage: {error_message}") - def creer_article(self, article_data: dict) -> dict: with self._com_context(), self._lock_com: @@ -10930,8 +11124,6 @@ class SageConnector: doc = win32com.client.CastTo(persist_doc, "IBODocumentStock3") doc.SetDefault() - - date_mouv = entree_data.get("date_mouvement") if isinstance(date_mouv, date): doc.DO_Date = pywintypes.Time( @@ -12127,7 +12319,6 @@ class SageConnector: doc.SetDefault() # Date - date_mouv = sortie_data.get("date_mouvement") if isinstance(date_mouv, date):