feat: Add API endpoints and Sage connector methods for managing prospects, suppliers, credit notes, and delivery notes.
This commit is contained in:
parent
53ecccd712
commit
ae5fa9e0be
2 changed files with 657 additions and 0 deletions
117
main.py
117
main.py
|
|
@ -37,6 +37,7 @@ class TypeDocument(int, Enum):
|
|||
# MODÈLES
|
||||
# =====================================================
|
||||
|
||||
|
||||
class DocumentGetRequest(BaseModel):
|
||||
numero: str
|
||||
type_doc: int
|
||||
|
|
@ -2450,6 +2451,122 @@ def diagnostiquer_erreur_transformation(
|
|||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
# =====================================================
|
||||
# ENDPOINTS - PROSPECTS
|
||||
# =====================================================
|
||||
@app.post("/sage/prospects/list", dependencies=[Depends(verify_token)])
|
||||
def prospects_list(req: FiltreRequest):
|
||||
"""📋 Liste tous les prospects (CT_Type=0 AND CT_Prospect=1)"""
|
||||
try:
|
||||
prospects = sage.lister_tous_prospects(req.filtre)
|
||||
return {"success": True, "data": prospects}
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur liste prospects: {e}")
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@app.post("/sage/prospects/get", dependencies=[Depends(verify_token)])
|
||||
def prospect_get(req: CodeRequest):
|
||||
"""📄 Lecture d'un prospect par code"""
|
||||
try:
|
||||
prospect = sage.lire_prospect(req.code)
|
||||
if not prospect:
|
||||
raise HTTPException(404, f"Prospect {req.code} non trouvé")
|
||||
return {"success": True, "data": prospect}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lecture prospect: {e}")
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
# =====================================================
|
||||
# ENDPOINTS - FOURNISSEURS
|
||||
# =====================================================
|
||||
@app.post("/sage/fournisseurs/list", dependencies=[Depends(verify_token)])
|
||||
def fournisseurs_list(req: FiltreRequest):
|
||||
"""📋 Liste tous les fournisseurs (CT_Type=1)"""
|
||||
try:
|
||||
fournisseurs = sage.lister_tous_fournisseurs(req.filtre)
|
||||
return {"success": True, "data": fournisseurs}
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur liste fournisseurs: {e}")
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@app.post("/sage/fournisseurs/get", dependencies=[Depends(verify_token)])
|
||||
def fournisseur_get(req: CodeRequest):
|
||||
"""📄 Lecture d'un fournisseur par code"""
|
||||
try:
|
||||
fournisseur = sage.lire_fournisseur(req.code)
|
||||
if not fournisseur:
|
||||
raise HTTPException(404, f"Fournisseur {req.code} non trouvé")
|
||||
return {"success": True, "data": fournisseur}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lecture fournisseur: {e}")
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
# =====================================================
|
||||
# ENDPOINTS - AVOIRS
|
||||
# =====================================================
|
||||
@app.post("/sage/avoirs/list", dependencies=[Depends(verify_token)])
|
||||
def avoirs_list(limit: int = 100, statut: Optional[int] = None):
|
||||
"""📋 Liste tous les avoirs (DO_Domaine=0 AND DO_Type=5)"""
|
||||
try:
|
||||
avoirs = sage.lister_avoirs(limit=limit, statut=statut)
|
||||
return {"success": True, "data": avoirs}
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur liste avoirs: {e}")
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@app.post("/sage/avoirs/get", dependencies=[Depends(verify_token)])
|
||||
def avoir_get(req: CodeRequest):
|
||||
"""📄 Lecture d'un avoir avec ses lignes"""
|
||||
try:
|
||||
avoir = sage.lire_avoir(req.code)
|
||||
if not avoir:
|
||||
raise HTTPException(404, f"Avoir {req.code} non trouvé")
|
||||
return {"success": True, "data": avoir}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lecture avoir: {e}")
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
# =====================================================
|
||||
# ENDPOINTS - LIVRAISONS
|
||||
# =====================================================
|
||||
@app.post("/sage/livraisons/list", dependencies=[Depends(verify_token)])
|
||||
def livraisons_list(limit: int = 100, statut: Optional[int] = None):
|
||||
"""📋 Liste tous les bons de livraison (DO_Domaine=0 AND DO_Type=30)"""
|
||||
try:
|
||||
livraisons = sage.lister_livraisons(limit=limit, statut=statut)
|
||||
return {"success": True, "data": livraisons}
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur liste livraisons: {e}")
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@app.post("/sage/livraisons/get", dependencies=[Depends(verify_token)])
|
||||
def livraison_get(req: CodeRequest):
|
||||
"""📄 Lecture d'une livraison avec ses lignes"""
|
||||
try:
|
||||
livraison = sage.lire_livraison(req.code)
|
||||
if not livraison:
|
||||
raise HTTPException(404, f"Livraison {req.code} non trouvée")
|
||||
return {"success": True, "data": livraison}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lecture livraison: {e}")
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
# =====================================================
|
||||
# LANCEMENT
|
||||
# =====================================================
|
||||
|
|
|
|||
|
|
@ -1805,3 +1805,543 @@ class SageConnector:
|
|||
return self.mettre_a_jour_champ_libre(
|
||||
doc_id, type_doc, "DerniereRelance", date_relance
|
||||
)
|
||||
|
||||
# =========================================================================
|
||||
# PROSPECTS (CT_Type = 0 AND CT_Prospect = 1)
|
||||
# =========================================================================
|
||||
def lister_tous_prospects(self, filtre=""):
|
||||
"""Liste tous les prospects depuis le cache"""
|
||||
with self._lock_clients:
|
||||
if not filtre:
|
||||
return [
|
||||
c
|
||||
for c in self._cache_clients
|
||||
if c.get("type") == 0 and c.get("est_prospect")
|
||||
]
|
||||
|
||||
filtre_lower = filtre.lower()
|
||||
return [
|
||||
c
|
||||
for c in self._cache_clients
|
||||
if c.get("type") == 0
|
||||
and c.get("est_prospect")
|
||||
and (
|
||||
filtre_lower in c["numero"].lower()
|
||||
or filtre_lower in c["intitule"].lower()
|
||||
)
|
||||
]
|
||||
|
||||
def lire_prospect(self, code_prospect):
|
||||
"""Retourne un prospect depuis le cache"""
|
||||
with self._lock_clients:
|
||||
prospect = self._cache_clients_dict.get(code_prospect)
|
||||
if prospect and prospect.get("type") == 0 and prospect.get("est_prospect"):
|
||||
return prospect
|
||||
return None
|
||||
|
||||
# =========================================================================
|
||||
# FOURNISSEURS (CT_Type = 1)
|
||||
# =========================================================================
|
||||
def lister_tous_fournisseurs(self, filtre=""):
|
||||
"""Liste tous les fournisseurs depuis le cache"""
|
||||
with self._lock_clients:
|
||||
if not filtre:
|
||||
return [c for c in self._cache_clients if c.get("type") == 1]
|
||||
|
||||
filtre_lower = filtre.lower()
|
||||
return [
|
||||
c
|
||||
for c in self._cache_clients
|
||||
if c.get("type") == 1
|
||||
and (
|
||||
filtre_lower in c["numero"].lower()
|
||||
or filtre_lower in c["intitule"].lower()
|
||||
)
|
||||
]
|
||||
|
||||
def lire_fournisseur(self, code_fournisseur):
|
||||
"""Retourne un fournisseur depuis le cache"""
|
||||
with self._lock_clients:
|
||||
fournisseur = self._cache_clients_dict.get(code_fournisseur)
|
||||
if fournisseur and fournisseur.get("type") == 1:
|
||||
return fournisseur
|
||||
return None
|
||||
|
||||
# =========================================================================
|
||||
# EXTRACTION CLIENTS (Mise à jour pour inclure prospects)
|
||||
# =========================================================================
|
||||
def _extraire_client(self, client_obj):
|
||||
"""MISE À JOUR : Extraction avec détection prospect"""
|
||||
data = {
|
||||
"numero": getattr(client_obj, "CT_Num", ""),
|
||||
"intitule": getattr(client_obj, "CT_Intitule", ""),
|
||||
"type": getattr(
|
||||
client_obj, "CT_Type", 0
|
||||
), # 0=Client/Prospect, 1=Fournisseur
|
||||
"est_prospect": getattr(client_obj, "CT_Prospect", 0) == 1, # ✅ NOUVEAU
|
||||
}
|
||||
|
||||
try:
|
||||
adresse = getattr(client_obj, "Adresse", None)
|
||||
if adresse:
|
||||
data["adresse"] = getattr(adresse, "Adresse", "")
|
||||
data["code_postal"] = getattr(adresse, "CodePostal", "")
|
||||
data["ville"] = getattr(adresse, "Ville", "")
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
telecom = getattr(client_obj, "Telecom", None)
|
||||
if telecom:
|
||||
data["telephone"] = getattr(telecom, "Telephone", "")
|
||||
data["email"] = getattr(telecom, "EMail", "")
|
||||
except:
|
||||
pass
|
||||
|
||||
return data
|
||||
|
||||
# =========================================================================
|
||||
# AVOIRS (DO_Domaine = 0 AND DO_Type = 5)
|
||||
# =========================================================================
|
||||
def lister_avoirs(self, limit=100, statut=None):
|
||||
"""Liste tous les avoirs de vente"""
|
||||
if not self.cial:
|
||||
return []
|
||||
|
||||
avoirs = []
|
||||
|
||||
try:
|
||||
with self._com_context(), self._lock_com:
|
||||
factory = self.cial.FactoryDocumentVente
|
||||
index = 1
|
||||
max_iterations = limit * 3
|
||||
erreurs_consecutives = 0
|
||||
max_erreurs = 50
|
||||
|
||||
while (
|
||||
len(avoirs) < limit
|
||||
and index < max_iterations
|
||||
and erreurs_consecutives < max_erreurs
|
||||
):
|
||||
try:
|
||||
persist = factory.List(index)
|
||||
if persist is None:
|
||||
break
|
||||
|
||||
doc = win32com.client.CastTo(persist, "IBODocumentVente3")
|
||||
doc.Read()
|
||||
|
||||
# ✅ Filtrer : DO_Domaine = 0 (Vente) AND DO_Type = 5 (Avoir)
|
||||
doc_type = getattr(doc, "DO_Type", -1)
|
||||
doc_domaine = getattr(doc, "DO_Domaine", -1)
|
||||
|
||||
if doc_domaine != 0 or doc_type != settings.SAGE_TYPE_BON_AVOIR:
|
||||
index += 1
|
||||
continue
|
||||
|
||||
doc_statut = getattr(doc, "DO_Statut", 0)
|
||||
|
||||
# Filtre statut optionnel
|
||||
if statut is not None and doc_statut != statut:
|
||||
index += 1
|
||||
continue
|
||||
|
||||
# Charger client
|
||||
client_code = ""
|
||||
client_intitule = ""
|
||||
|
||||
try:
|
||||
client_obj = getattr(doc, "Client", None)
|
||||
if client_obj:
|
||||
client_obj.Read()
|
||||
client_code = getattr(client_obj, "CT_Num", "").strip()
|
||||
client_intitule = getattr(
|
||||
client_obj, "CT_Intitule", ""
|
||||
).strip()
|
||||
except:
|
||||
pass
|
||||
|
||||
avoirs.append(
|
||||
{
|
||||
"numero": getattr(doc, "DO_Piece", ""),
|
||||
"reference": getattr(doc, "DO_Ref", ""),
|
||||
"date": str(getattr(doc, "DO_Date", "")),
|
||||
"client_code": client_code,
|
||||
"client_intitule": client_intitule,
|
||||
"total_ht": float(getattr(doc, "DO_TotalHT", 0.0)),
|
||||
"total_ttc": float(getattr(doc, "DO_TotalTTC", 0.0)),
|
||||
"statut": doc_statut,
|
||||
}
|
||||
)
|
||||
|
||||
erreurs_consecutives = 0
|
||||
index += 1
|
||||
|
||||
except:
|
||||
erreurs_consecutives += 1
|
||||
index += 1
|
||||
|
||||
if erreurs_consecutives >= max_erreurs:
|
||||
break
|
||||
|
||||
logger.info(f"✅ {len(avoirs)} avoirs retournés")
|
||||
return avoirs
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur liste avoirs: {e}", exc_info=True)
|
||||
return []
|
||||
|
||||
def lire_avoir(self, numero):
|
||||
"""Lecture d'un avoir avec ses lignes"""
|
||||
if not self.cial:
|
||||
return None
|
||||
|
||||
try:
|
||||
with self._com_context(), self._lock_com:
|
||||
factory = self.cial.FactoryDocumentVente
|
||||
|
||||
# Essayer ReadPiece
|
||||
persist = factory.ReadPiece(settings.SAGE_TYPE_BON_AVOIR, numero)
|
||||
|
||||
if not persist:
|
||||
# Chercher 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)
|
||||
== settings.SAGE_TYPE_BON_AVOIR
|
||||
and getattr(doc_test, "DO_Piece", "") == numero
|
||||
):
|
||||
persist = persist_test
|
||||
break
|
||||
|
||||
index += 1
|
||||
except:
|
||||
index += 1
|
||||
|
||||
if not persist:
|
||||
return None
|
||||
|
||||
doc = win32com.client.CastTo(persist, "IBODocumentVente3")
|
||||
doc.Read()
|
||||
|
||||
# Charger client
|
||||
client_code = ""
|
||||
client_intitule = ""
|
||||
|
||||
try:
|
||||
client_obj = getattr(doc, "Client", None)
|
||||
if client_obj:
|
||||
client_obj.Read()
|
||||
client_code = getattr(client_obj, "CT_Num", "").strip()
|
||||
client_intitule = getattr(client_obj, "CT_Intitule", "").strip()
|
||||
except:
|
||||
pass
|
||||
|
||||
avoir = {
|
||||
"numero": getattr(doc, "DO_Piece", ""),
|
||||
"reference": getattr(doc, "DO_Ref", ""),
|
||||
"date": str(getattr(doc, "DO_Date", "")),
|
||||
"client_code": client_code,
|
||||
"client_intitule": client_intitule,
|
||||
"total_ht": float(getattr(doc, "DO_TotalHT", 0.0)),
|
||||
"total_ttc": float(getattr(doc, "DO_TotalTTC", 0.0)),
|
||||
"statut": getattr(doc, "DO_Statut", 0),
|
||||
"lignes": [],
|
||||
}
|
||||
|
||||
# Charger lignes
|
||||
try:
|
||||
factory_lignes = getattr(
|
||||
doc, "FactoryDocumentLigne", None
|
||||
) or getattr(doc, "FactoryDocumentVenteLigne", None)
|
||||
|
||||
if factory_lignes:
|
||||
index = 1
|
||||
while index <= 100:
|
||||
try:
|
||||
ligne_persist = factory_lignes.List(index)
|
||||
if ligne_persist is None:
|
||||
break
|
||||
|
||||
ligne = win32com.client.CastTo(
|
||||
ligne_persist, "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
|
||||
|
||||
avoir["lignes"].append(
|
||||
{
|
||||
"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
|
||||
except:
|
||||
pass
|
||||
|
||||
logger.info(f"✅ Avoir {numero} lu: {len(avoir['lignes'])} lignes")
|
||||
return avoir
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur lecture avoir {numero}: {e}")
|
||||
return None
|
||||
|
||||
# =========================================================================
|
||||
# LIVRAISONS (DO_Domaine = 0 AND DO_Type = 3)
|
||||
# =========================================================================
|
||||
def lister_livraisons(self, limit=100, statut=None):
|
||||
"""Liste tous les bons de livraison"""
|
||||
if not self.cial:
|
||||
return []
|
||||
|
||||
livraisons = []
|
||||
|
||||
try:
|
||||
with self._com_context(), self._lock_com:
|
||||
factory = self.cial.FactoryDocumentVente
|
||||
index = 1
|
||||
max_iterations = limit * 3
|
||||
erreurs_consecutives = 0
|
||||
max_erreurs = 50
|
||||
|
||||
while (
|
||||
len(livraisons) < limit
|
||||
and index < max_iterations
|
||||
and erreurs_consecutives < max_erreurs
|
||||
):
|
||||
try:
|
||||
persist = factory.List(index)
|
||||
if persist is None:
|
||||
break
|
||||
|
||||
doc = win32com.client.CastTo(persist, "IBODocumentVente3")
|
||||
doc.Read()
|
||||
|
||||
# ✅ Filtrer : DO_Domaine = 0 (Vente) AND DO_Type = 30 (Livraison)
|
||||
doc_type = getattr(doc, "DO_Type", -1)
|
||||
doc_domaine = getattr(doc, "DO_Domaine", -1)
|
||||
|
||||
if (
|
||||
doc_domaine != 0
|
||||
or doc_type != settings.SAGE_TYPE_BON_LIVRAISON
|
||||
):
|
||||
index += 1
|
||||
continue
|
||||
|
||||
doc_statut = getattr(doc, "DO_Statut", 0)
|
||||
|
||||
if statut is not None and doc_statut != statut:
|
||||
index += 1
|
||||
continue
|
||||
|
||||
# Charger client
|
||||
client_code = ""
|
||||
client_intitule = ""
|
||||
|
||||
try:
|
||||
client_obj = getattr(doc, "Client", None)
|
||||
if client_obj:
|
||||
client_obj.Read()
|
||||
client_code = getattr(client_obj, "CT_Num", "").strip()
|
||||
client_intitule = getattr(
|
||||
client_obj, "CT_Intitule", ""
|
||||
).strip()
|
||||
except:
|
||||
pass
|
||||
|
||||
livraisons.append(
|
||||
{
|
||||
"numero": getattr(doc, "DO_Piece", ""),
|
||||
"reference": getattr(doc, "DO_Ref", ""),
|
||||
"date": str(getattr(doc, "DO_Date", "")),
|
||||
"client_code": client_code,
|
||||
"client_intitule": client_intitule,
|
||||
"total_ht": float(getattr(doc, "DO_TotalHT", 0.0)),
|
||||
"total_ttc": float(getattr(doc, "DO_TotalTTC", 0.0)),
|
||||
"statut": doc_statut,
|
||||
}
|
||||
)
|
||||
|
||||
erreurs_consecutives = 0
|
||||
index += 1
|
||||
|
||||
except:
|
||||
erreurs_consecutives += 1
|
||||
index += 1
|
||||
|
||||
if erreurs_consecutives >= max_erreurs:
|
||||
break
|
||||
|
||||
logger.info(f"✅ {len(livraisons)} livraisons retournées")
|
||||
return livraisons
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur liste livraisons: {e}", exc_info=True)
|
||||
return []
|
||||
|
||||
def lire_livraison(self, numero):
|
||||
"""Lecture d'une livraison avec ses lignes"""
|
||||
if not self.cial:
|
||||
return None
|
||||
|
||||
try:
|
||||
with self._com_context(), self._lock_com:
|
||||
factory = self.cial.FactoryDocumentVente
|
||||
|
||||
# Essayer ReadPiece
|
||||
persist = factory.ReadPiece(settings.SAGE_TYPE_BON_LIVRAISON, numero)
|
||||
|
||||
if not persist:
|
||||
# Chercher 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)
|
||||
== settings.SAGE_TYPE_BON_LIVRAISON
|
||||
and getattr(doc_test, "DO_Piece", "") == numero
|
||||
):
|
||||
persist = persist_test
|
||||
break
|
||||
|
||||
index += 1
|
||||
except:
|
||||
index += 1
|
||||
|
||||
if not persist:
|
||||
return None
|
||||
|
||||
doc = win32com.client.CastTo(persist, "IBODocumentVente3")
|
||||
doc.Read()
|
||||
|
||||
# Charger client
|
||||
client_code = ""
|
||||
client_intitule = ""
|
||||
|
||||
try:
|
||||
client_obj = getattr(doc, "Client", None)
|
||||
if client_obj:
|
||||
client_obj.Read()
|
||||
client_code = getattr(client_obj, "CT_Num", "").strip()
|
||||
client_intitule = getattr(client_obj, "CT_Intitule", "").strip()
|
||||
except:
|
||||
pass
|
||||
|
||||
livraison = {
|
||||
"numero": getattr(doc, "DO_Piece", ""),
|
||||
"reference": getattr(doc, "DO_Ref", ""),
|
||||
"date": str(getattr(doc, "DO_Date", "")),
|
||||
"client_code": client_code,
|
||||
"client_intitule": client_intitule,
|
||||
"total_ht": float(getattr(doc, "DO_TotalHT", 0.0)),
|
||||
"total_ttc": float(getattr(doc, "DO_TotalTTC", 0.0)),
|
||||
"statut": getattr(doc, "DO_Statut", 0),
|
||||
"lignes": [],
|
||||
}
|
||||
|
||||
# Charger lignes
|
||||
try:
|
||||
factory_lignes = getattr(
|
||||
doc, "FactoryDocumentLigne", None
|
||||
) or getattr(doc, "FactoryDocumentVenteLigne", None)
|
||||
|
||||
if factory_lignes:
|
||||
index = 1
|
||||
while index <= 100:
|
||||
try:
|
||||
ligne_persist = factory_lignes.List(index)
|
||||
if ligne_persist is None:
|
||||
break
|
||||
|
||||
ligne = win32com.client.CastTo(
|
||||
ligne_persist, "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
|
||||
|
||||
livraison["lignes"].append(
|
||||
{
|
||||
"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
|
||||
except:
|
||||
pass
|
||||
|
||||
logger.info(
|
||||
f"✅ Livraison {numero} lue: {len(livraison['lignes'])} lignes"
|
||||
)
|
||||
return livraison
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur lecture livraison {numero}: {e}")
|
||||
return None
|
||||
|
|
|
|||
Loading…
Reference in a new issue