Diagnostic devis
This commit is contained in:
parent
c522aa5a64
commit
3505ecfd2b
2 changed files with 281 additions and 72 deletions
195
main.py
195
main.py
|
|
@ -866,6 +866,201 @@ def cache_info_get():
|
||||||
raise HTTPException(500, str(e))
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
|
# Script à ajouter temporairement dans main.py pour diagnostiquer
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/sage/devis/{numero}/diagnostic", dependencies=[Depends(verify_token)])
|
||||||
|
def diagnostiquer_devis(numero: str):
|
||||||
|
"""
|
||||||
|
ENDPOINT DE DIAGNOSTIC: Affiche TOUTES les infos d'un devis
|
||||||
|
|
||||||
|
Permet de comprendre pourquoi un devis ne peut pas être transformé
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
# Essayer ReadPiece
|
||||||
|
persist = factory.ReadPiece(0, numero)
|
||||||
|
|
||||||
|
# Si échec, chercher dans List()
|
||||||
|
if not persist:
|
||||||
|
logger.info(f"[DIAG] ReadPiece echoue, recherche dans List()...")
|
||||||
|
index = 1
|
||||||
|
while index < 10000:
|
||||||
|
try:
|
||||||
|
persist_test = factory.List(index)
|
||||||
|
if persist_test is None:
|
||||||
|
break
|
||||||
|
|
||||||
|
doc_test = win32com.client.CastTo(
|
||||||
|
persist_test, "IBODocumentVente3"
|
||||||
|
)
|
||||||
|
doc_test.Read()
|
||||||
|
|
||||||
|
if (
|
||||||
|
getattr(doc_test, "DO_Type", -1) == 0
|
||||||
|
and getattr(doc_test, "DO_Piece", "") == numero
|
||||||
|
):
|
||||||
|
persist = persist_test
|
||||||
|
logger.info(f"[DIAG] Trouve a l'index {index}")
|
||||||
|
break
|
||||||
|
|
||||||
|
index += 1
|
||||||
|
except:
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
if not persist:
|
||||||
|
raise HTTPException(404, f"Devis {numero} introuvable")
|
||||||
|
|
||||||
|
doc = win32com.client.CastTo(persist, "IBODocumentVente3")
|
||||||
|
doc.Read()
|
||||||
|
|
||||||
|
# EXTRACTION COMPLÈTE
|
||||||
|
diagnostic = {
|
||||||
|
"numero": getattr(doc, "DO_Piece", ""),
|
||||||
|
"type": getattr(doc, "DO_Type", -1),
|
||||||
|
"statut": getattr(doc, "DO_Statut", -1),
|
||||||
|
"statut_libelle": {
|
||||||
|
0: "Brouillon",
|
||||||
|
1: "Soumis",
|
||||||
|
2: "Accepte",
|
||||||
|
3: "Realise partiellement",
|
||||||
|
4: "Realise totalement",
|
||||||
|
5: "Transforme",
|
||||||
|
6: "Annule",
|
||||||
|
}.get(getattr(doc, "DO_Statut", -1), "Inconnu"),
|
||||||
|
"date": str(getattr(doc, "DO_Date", "")),
|
||||||
|
"total_ht": float(getattr(doc, "DO_TotalHT", 0.0)),
|
||||||
|
"total_ttc": float(getattr(doc, "DO_TotalTTC", 0.0)),
|
||||||
|
"est_transformable": False,
|
||||||
|
"raison_blocage": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Client
|
||||||
|
try:
|
||||||
|
client_obj = getattr(doc, "Client", None)
|
||||||
|
if client_obj:
|
||||||
|
client_obj.Read()
|
||||||
|
diagnostic["client_code"] = getattr(
|
||||||
|
client_obj, "CT_Num", ""
|
||||||
|
).strip()
|
||||||
|
diagnostic["client_intitule"] = getattr(
|
||||||
|
client_obj, "CT_Intitule", ""
|
||||||
|
).strip()
|
||||||
|
except Exception as e:
|
||||||
|
diagnostic["erreur_client"] = str(e)
|
||||||
|
|
||||||
|
# Lignes
|
||||||
|
try:
|
||||||
|
factory_lignes = doc.FactoryDocumentLigne
|
||||||
|
except:
|
||||||
|
factory_lignes = doc.FactoryDocumentVenteLigne
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
lignes.append(
|
||||||
|
{
|
||||||
|
"index": index,
|
||||||
|
"article": article_ref,
|
||||||
|
"designation": getattr(ligne, "DL_Design", ""),
|
||||||
|
"quantite": float(getattr(ligne, "DL_Qte", 0.0)),
|
||||||
|
"prix_unitaire": float(
|
||||||
|
getattr(ligne, "DL_PrixUnitaire", 0.0)
|
||||||
|
),
|
||||||
|
"montant_ht": float(getattr(ligne, "DL_MontantHT", 0.0)),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
index += 1
|
||||||
|
except:
|
||||||
|
break
|
||||||
|
|
||||||
|
diagnostic["nb_lignes"] = len(lignes)
|
||||||
|
diagnostic["lignes"] = lignes
|
||||||
|
|
||||||
|
# ANALYSE TRANSFORMABILITÉ
|
||||||
|
statut = diagnostic["statut"]
|
||||||
|
|
||||||
|
if statut == 5:
|
||||||
|
diagnostic["raison_blocage"] = "Document deja transforme (statut=5)"
|
||||||
|
elif statut == 6:
|
||||||
|
diagnostic["raison_blocage"] = "Document annule (statut=6)"
|
||||||
|
elif statut in [3, 4]:
|
||||||
|
diagnostic["raison_blocage"] = (
|
||||||
|
f"Document deja realise partiellement ou totalement (statut={statut}). "
|
||||||
|
f"Une commande/BL/facture existe probablement deja."
|
||||||
|
)
|
||||||
|
diagnostic["suggestion"] = (
|
||||||
|
"Cherchez les documents lies a ce devis dans Sage. "
|
||||||
|
"Il a peut-etre deja ete transforme manuellement."
|
||||||
|
)
|
||||||
|
elif statut == 0:
|
||||||
|
diagnostic["est_transformable"] = True
|
||||||
|
diagnostic["action_requise"] = (
|
||||||
|
"Statut 'Brouillon'. Le systeme le passera automatiquement a 'Accepte' "
|
||||||
|
"avant transformation."
|
||||||
|
)
|
||||||
|
elif statut == 2:
|
||||||
|
diagnostic["est_transformable"] = True
|
||||||
|
diagnostic["action_requise"] = (
|
||||||
|
"Statut 'Accepte'. Transformation possible."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
diagnostic["raison_blocage"] = f"Statut inconnu ou non gere: {statut}"
|
||||||
|
|
||||||
|
# Champs libres (pour Universign, etc.)
|
||||||
|
champs_libres = {}
|
||||||
|
try:
|
||||||
|
for champ in ["UniversignID", "DerniereRelance", "DO_Ref"]:
|
||||||
|
try:
|
||||||
|
valeur = getattr(doc, f"DO_{champ}", None)
|
||||||
|
if valeur:
|
||||||
|
champs_libres[champ] = str(valeur)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if champs_libres:
|
||||||
|
diagnostic["champs_libres"] = champs_libres
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"[DIAG] Devis {numero}: statut={statut}, transformable={diagnostic['est_transformable']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return {"success": True, "diagnostic": diagnostic}
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[DIAG] Erreur diagnostic: {e}", exc_info=True)
|
||||||
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
# =====================================================
|
# =====================================================
|
||||||
# LANCEMENT
|
# LANCEMENT
|
||||||
# =====================================================
|
# =====================================================
|
||||||
|
|
|
||||||
|
|
@ -1026,25 +1026,27 @@ class SageConnector:
|
||||||
|
|
||||||
def transformer_document(self, numero_source, type_source, type_cible):
|
def transformer_document(self, numero_source, type_source, type_cible):
|
||||||
"""
|
"""
|
||||||
Transformation avec transaction
|
Transformation de document avec gestion complète des statuts
|
||||||
✅ CORRECTIONS:
|
|
||||||
- Validation stricte des types
|
CORRECTIONS:
|
||||||
- Gestion explicite des statuts Sage
|
- Pas d'émojis dans les logs
|
||||||
- Meilleure gestion d'erreurs
|
- Validation stricte des statuts Sage
|
||||||
|
- Gestion des documents "Réalisé partiellement"
|
||||||
|
- Meilleure détection d'erreurs
|
||||||
"""
|
"""
|
||||||
if not self.cial:
|
if not self.cial:
|
||||||
raise RuntimeError("Connexion Sage non établie")
|
raise RuntimeError("Connexion Sage non etablie")
|
||||||
|
|
||||||
# ✅ CORRECTION 1: Convertir en int si enum passé
|
# Convertir en int si enum
|
||||||
type_source = int(type_source)
|
type_source = int(type_source)
|
||||||
type_cible = int(type_cible)
|
type_cible = int(type_cible)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"🔄 Transformation demandée: {numero_source} "
|
f"[TRANSFORM] Demande: {numero_source} "
|
||||||
f"(type {type_source}) → type {type_cible}"
|
f"(type {type_source}) -> type {type_cible}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# ✅ CORRECTION 2: Validation des types AVANT d'accéder à Sage
|
# Validation des types
|
||||||
types_valides = {0, 1, 2, 3, 4, 5}
|
types_valides = {0, 1, 2, 3, 4, 5}
|
||||||
if type_source not in types_valides or type_cible not in types_valides:
|
if type_source not in types_valides or type_cible not in types_valides:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
|
@ -1052,24 +1054,23 @@ class SageConnector:
|
||||||
f"Valeurs valides: {types_valides}"
|
f"Valeurs valides: {types_valides}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# ✅ CORRECTION 3: Matrice de transformations autorisées par Sage
|
# Matrice de transformations Sage 100c
|
||||||
# Basé sur la doc Sage 100c
|
|
||||||
transformations_autorisees = {
|
transformations_autorisees = {
|
||||||
(0, 3): "Devis → Commande",
|
(0, 3): "Devis -> Commande",
|
||||||
(0, 1): "Devis → Bon de livraison",
|
(0, 1): "Devis -> Bon de livraison",
|
||||||
(3, 1): "Commande → Bon de livraison",
|
(3, 1): "Commande -> Bon de livraison",
|
||||||
(3, 4): "Commande → Préparation",
|
(3, 4): "Commande -> Preparation",
|
||||||
(1, 5): "Bon de livraison → Facture",
|
(1, 5): "Bon de livraison -> Facture",
|
||||||
(4, 1): "Préparation → Bon de livraison",
|
(4, 1): "Preparation -> Bon de livraison",
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type_source, type_cible) not in transformations_autorisees:
|
if (type_source, type_cible) not in transformations_autorisees:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"❌ Transformation non autorisée par Sage: "
|
f"Transformation non autorisee par Sage: "
|
||||||
f"{type_source} → {type_cible}. "
|
f"{type_source} -> {type_cible}. "
|
||||||
f"Transformations valides:\n"
|
f"Valides: "
|
||||||
+ "\n".join(
|
+ ", ".join(
|
||||||
f" - {k}: {v}" for k, v in transformations_autorisees.items()
|
f"{k[0]}->{k[1]}" for k in transformations_autorisees.keys()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1080,8 +1081,7 @@ class SageConnector:
|
||||||
persist_source = factory.ReadPiece(type_source, numero_source)
|
persist_source = factory.ReadPiece(type_source, numero_source)
|
||||||
|
|
||||||
if not persist_source:
|
if not persist_source:
|
||||||
# ✅ CORRECTION 4: Chercher dans List() si ReadPiece échoue
|
logger.warning(f"ReadPiece failed, searching in List()...")
|
||||||
logger.warning(f"ReadPiece échoué, recherche dans List()...")
|
|
||||||
persist_source = self._find_document_in_list(
|
persist_source = self._find_document_in_list(
|
||||||
numero_source, type_source
|
numero_source, type_source
|
||||||
)
|
)
|
||||||
|
|
@ -1094,60 +1094,72 @@ class SageConnector:
|
||||||
doc_source = win32com.client.CastTo(persist_source, "IBODocumentVente3")
|
doc_source = win32com.client.CastTo(persist_source, "IBODocumentVente3")
|
||||||
doc_source.Read()
|
doc_source.Read()
|
||||||
|
|
||||||
# ✅ CORRECTION 5: Vérifications de statut AVANT transformation
|
# VÉRIFICATIONS STATUT
|
||||||
statut_actuel = getattr(doc_source, "DO_Statut", 0)
|
statut_actuel = getattr(doc_source, "DO_Statut", 0)
|
||||||
type_reel = getattr(doc_source, "DO_Type", -1)
|
type_reel = getattr(doc_source, "DO_Type", -1)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"📊 Document source: type={type_reel}, statut={statut_actuel}, "
|
f"[TRANSFORM] Document source: type={type_reel}, "
|
||||||
f"numéro={numero_source}"
|
f"statut={statut_actuel}, numero={numero_source}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Vérifier cohérence type
|
# Vérifier cohérence type
|
||||||
if type_reel != type_source:
|
if type_reel != type_source:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Incohérence: document {numero_source} est de type {type_reel}, "
|
f"Incoherence: document {numero_source} est de type {type_reel}, "
|
||||||
f"pas de type {type_source}"
|
f"pas de type {type_source}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# ✅ CORRECTION 6: Règles de statut Sage pour transformations
|
# RÈGLES DE STATUT SAGE
|
||||||
# Statuts Sage: 0=Brouillon, 1=Soumis, 2=Accepté, 3=Réalisé partiellement,
|
# 0=Brouillon, 1=Soumis, 2=Accepte, 3=Realise partiellement,
|
||||||
# 4=Réalisé totalement, 5=Transformé, 6=Annulé
|
# 4=Realise totalement, 5=Transforme, 6=Annule
|
||||||
|
|
||||||
|
# CORRECTION CRITIQUE: Statut 3 = "Réalisé partiellement"
|
||||||
|
# Cela signifie qu'une partie du document a déjà été transformée
|
||||||
|
# mais pas tout. Sage REFUSE de créer un nouveau document dans ce cas.
|
||||||
|
|
||||||
if statut_actuel == 5:
|
if statut_actuel == 5:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Document {numero_source} déjà transformé (statut=5). "
|
f"Document {numero_source} deja transforme (statut=5). "
|
||||||
f"Impossible de le transformer à nouveau."
|
f"Impossible de le transformer a nouveau."
|
||||||
)
|
)
|
||||||
|
|
||||||
if statut_actuel == 6:
|
if statut_actuel == 6:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Document {numero_source} annulé (statut=6). "
|
f"Document {numero_source} annule (statut=6). "
|
||||||
f"Impossible de le transformer."
|
f"Impossible de le transformer."
|
||||||
)
|
)
|
||||||
|
|
||||||
# ✅ CORRECTION 7: Forcer statut "Accepté" si nécessaire
|
# CORRECTION: Statut 3 ou 4 = document déjà réalisé/livré
|
||||||
if type_source == 0 and statut_actuel == 0: # Devis brouillon
|
if statut_actuel in [3, 4]:
|
||||||
|
raise ValueError(
|
||||||
|
f"Document {numero_source} deja realise (statut={statut_actuel}). "
|
||||||
|
f"Ce document a deja ete transforme partiellement ou totalement. "
|
||||||
|
f"Verifiez si une commande/BL/facture n'existe pas deja pour ce document."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Forcer statut "Accepté" si brouillon
|
||||||
|
if type_source == 0 and statut_actuel == 0:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"⚠️ Devis en brouillon (statut=0), "
|
f"[TRANSFORM] Devis en brouillon (statut=0), "
|
||||||
f"passage à 'Accepté' (statut=2) requis pour transformation"
|
f"passage a 'Accepte' (statut=2)"
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
doc_source.DO_Statut = 2 # Accepté
|
doc_source.DO_Statut = 2
|
||||||
doc_source.Write()
|
doc_source.Write()
|
||||||
logger.info(f"✅ Statut changé: 0 → 2")
|
logger.info(f"[TRANSFORM] Statut change: 0 -> 2")
|
||||||
|
|
||||||
# Re-lire pour confirmer
|
# Re-lire
|
||||||
doc_source.Read()
|
doc_source.Read()
|
||||||
nouveau_statut = getattr(doc_source, "DO_Statut", 0)
|
nouveau_statut = getattr(doc_source, "DO_Statut", 0)
|
||||||
if nouveau_statut != 2:
|
if nouveau_statut != 2:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f"Échec changement statut: toujours à {nouveau_statut}"
|
f"Echec changement statut: toujours a {nouveau_statut}"
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f"Impossible de changer le statut du devis: {e}. "
|
f"Impossible de changer le statut du devis: {e}. "
|
||||||
f"Le devis doit être accepté avant transformation."
|
f"Le devis doit etre accepte avant transformation."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Récupérer client
|
# Récupérer client
|
||||||
|
|
@ -1170,22 +1182,23 @@ class SageConnector:
|
||||||
try:
|
try:
|
||||||
self.cial.CptaApplication.BeginTrans()
|
self.cial.CptaApplication.BeginTrans()
|
||||||
transaction_active = True
|
transaction_active = True
|
||||||
logger.debug("✅ Transaction démarrée")
|
logger.debug("[TRANSFORM] Transaction demarree")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"⚠️ BeginTrans échoué: {e}")
|
logger.warning(f"[TRANSFORM] BeginTrans echoue: {e}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# ✅ CORRECTION 8: Créer le process avec le type cible VALIDÉ
|
# CRÉATION DOCUMENT CIBLE
|
||||||
logger.info(f"🔨 CreateProcess_Document({type_cible})...")
|
logger.info(f"[TRANSFORM] CreateProcess_Document({type_cible})...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
process = self.cial.CreateProcess_Document(type_cible)
|
process = self.cial.CreateProcess_Document(type_cible)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"❌ CreateProcess_Document échoué pour type {type_cible}: {e}"
|
f"[TRANSFORM] CreateProcess_Document echoue pour type {type_cible}: {e}"
|
||||||
)
|
)
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f"Sage refuse de créer un document de type {type_cible}. "
|
f"Sage refuse de creer un document de type {type_cible}. "
|
||||||
|
f"Verifiez la configuration Sage et les permissions. "
|
||||||
f"Erreur: {e}"
|
f"Erreur: {e}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1198,7 +1211,7 @@ class SageConnector:
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
logger.info(f"📄 Document cible créé (type {type_cible})")
|
logger.info(f"[TRANSFORM] Document cible cree (type {type_cible})")
|
||||||
|
|
||||||
# Associer client
|
# Associer client
|
||||||
try:
|
try:
|
||||||
|
|
@ -1214,9 +1227,9 @@ class SageConnector:
|
||||||
client_obj_cible.Read()
|
client_obj_cible.Read()
|
||||||
doc_cible.SetDefaultClient(client_obj_cible)
|
doc_cible.SetDefaultClient(client_obj_cible)
|
||||||
doc_cible.Write()
|
doc_cible.Write()
|
||||||
logger.info(f"👤 Client {client_code} associé")
|
logger.info(f"[TRANSFORM] Client {client_code} associe")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ Erreur association client: {e}")
|
logger.error(f"[TRANSFORM] Erreur association client: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# Date
|
# Date
|
||||||
|
|
@ -1340,17 +1353,17 @@ class SageConnector:
|
||||||
|
|
||||||
if nb_lignes == 0:
|
if nb_lignes == 0:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f"Aucune ligne copiée. Erreurs: {'; '.join(erreurs_lignes)}"
|
f"Aucune ligne copiee. Erreurs: {'; '.join(erreurs_lignes)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f"✅ {nb_lignes} lignes copiées")
|
logger.info(f"[TRANSFORM] {nb_lignes} lignes copiees")
|
||||||
|
|
||||||
# ===== VALIDATION =====
|
# ===== VALIDATION =====
|
||||||
doc_cible.Write()
|
doc_cible.Write()
|
||||||
logger.info("💾 Document cible écrit")
|
logger.info("[TRANSFORM] Document cible ecrit")
|
||||||
|
|
||||||
process.Process()
|
process.Process()
|
||||||
logger.info("⚙️ Process() exécuté")
|
logger.info("[TRANSFORM] Process() execute")
|
||||||
|
|
||||||
# Récupérer numéro
|
# Récupérer numéro
|
||||||
numero_cible = None
|
numero_cible = None
|
||||||
|
|
@ -1369,24 +1382,28 @@ class SageConnector:
|
||||||
numero_cible = getattr(doc_cible, "DO_Piece", "")
|
numero_cible = getattr(doc_cible, "DO_Piece", "")
|
||||||
|
|
||||||
if not numero_cible:
|
if not numero_cible:
|
||||||
raise RuntimeError("Numéro document cible vide après création")
|
raise RuntimeError("Numero document cible vide apres creation")
|
||||||
|
|
||||||
# Commit
|
# Commit
|
||||||
if transaction_active:
|
if transaction_active:
|
||||||
self.cial.CptaApplication.CommitTrans()
|
self.cial.CptaApplication.CommitTrans()
|
||||||
logger.info("✅ Transaction committée")
|
logger.info("[TRANSFORM] Transaction committee")
|
||||||
|
|
||||||
# MAJ statut source → Transformé
|
# MAJ statut source -> Transformé
|
||||||
try:
|
try:
|
||||||
doc_source.DO_Statut = 5 # Transformé
|
doc_source.DO_Statut = 5
|
||||||
doc_source.Write()
|
doc_source.Write()
|
||||||
logger.info(f"✅ Statut source mis à jour: → 5 (TRANSFORMÉ)")
|
logger.info(
|
||||||
|
f"[TRANSFORM] Statut source mis a jour: -> 5 (TRANSFORME)"
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"⚠️ Impossible de MAJ statut source: {e}")
|
logger.warning(
|
||||||
|
f"[TRANSFORM] Impossible de MAJ statut source: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"✅✅✅ TRANSFORMATION RÉUSSIE: "
|
f"[TRANSFORM] SUCCES: "
|
||||||
f"{numero_source} ({type_source}) → "
|
f"{numero_source} ({type_source}) -> "
|
||||||
f"{numero_cible} ({type_cible}) - {nb_lignes} lignes"
|
f"{numero_cible} ({type_cible}) - {nb_lignes} lignes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1401,20 +1418,17 @@ class SageConnector:
|
||||||
if transaction_active:
|
if transaction_active:
|
||||||
try:
|
try:
|
||||||
self.cial.CptaApplication.RollbackTrans()
|
self.cial.CptaApplication.RollbackTrans()
|
||||||
logger.error("❌ Transaction annulée")
|
logger.error("[TRANSFORM] Transaction annulee")
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
raise
|
raise
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ Erreur transformation: {e}", exc_info=True)
|
logger.error(f"[TRANSFORM] Erreur: {e}", exc_info=True)
|
||||||
raise RuntimeError(f"Échec transformation: {str(e)}")
|
raise RuntimeError(f"Echec transformation: {str(e)}")
|
||||||
|
|
||||||
def _find_document_in_list(self, numero, type_doc):
|
def _find_document_in_list(self, numero, type_doc):
|
||||||
"""
|
"""Cherche un document dans List() si ReadPiece échoue"""
|
||||||
✅ NOUVEAU: Cherche un document dans List() si ReadPiece échoue
|
|
||||||
Utile pour les documents en brouillon
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
factory = self.cial.FactoryDocumentVente
|
factory = self.cial.FactoryDocumentVente
|
||||||
index = 1
|
index = 1
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue