Finished firt time restructuration and reorganization
This commit is contained in:
parent
2f3b0ade5e
commit
d6d01fee9f
9 changed files with 1637 additions and 1602 deletions
22
cleaner.py
22
cleaner.py
|
|
@ -1,17 +1,33 @@
|
|||
from pathlib import Path
|
||||
|
||||
def supprimer_commentaires_ligne(fichier: str) -> None:
|
||||
def supprimer_lignes_logger(fichier: str) -> None:
|
||||
path = Path(fichier)
|
||||
|
||||
lignes_filtrees = []
|
||||
skip_block = False
|
||||
parentheses_balance = 0
|
||||
|
||||
with path.open("r", encoding="utf-8") as f:
|
||||
for ligne in f:
|
||||
if ligne.lstrip().startswith("logger"):
|
||||
stripped = ligne.lstrip()
|
||||
|
||||
# Détection du début d'un appel logger
|
||||
if not skip_block and stripped.startswith("logger"):
|
||||
skip_block = True
|
||||
parentheses_balance += ligne.count("(") - ligne.count(")")
|
||||
continue
|
||||
|
||||
if skip_block:
|
||||
parentheses_balance += ligne.count("(") - ligne.count(")")
|
||||
if parentheses_balance <= 0:
|
||||
skip_block = False
|
||||
parentheses_balance = 0
|
||||
continue
|
||||
|
||||
lignes_filtrees.append(ligne)
|
||||
|
||||
path.write_text("".join(lignes_filtrees), encoding="utf-8")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
supprimer_commentaires_ligne("sage_connector.py")
|
||||
supprimer_lignes_logger("sage_connector.py")
|
||||
|
|
|
|||
1602
sage_connector.py
1602
sage_connector.py
File diff suppressed because it is too large
Load diff
120
utils/documents/devis/devis_check.py
Normal file
120
utils/documents/devis/devis_check.py
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
import win32com.client
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def _rechercher_devis_dans_liste(numero_devis, factory_doc):
|
||||
"""Recherche un devis dans les 100 premiers éléments de la liste."""
|
||||
index = 1
|
||||
while index < 100:
|
||||
try:
|
||||
persist_test = factory_doc.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_devis
|
||||
):
|
||||
logger.info(f" Document trouvé à l'index {index}")
|
||||
return persist_test
|
||||
|
||||
index += 1
|
||||
except:
|
||||
index += 1
|
||||
|
||||
return None
|
||||
|
||||
def _recuperer_numero_devis(self, process, doc):
|
||||
"""Récupère le numéro du devis créé via plusieurs méthodes."""
|
||||
numero_devis = None
|
||||
|
||||
try:
|
||||
doc_result = process.DocumentResult
|
||||
if doc_result:
|
||||
doc_result = win32com.client.CastTo(doc_result, "IBODocumentVente3")
|
||||
doc_result.Read()
|
||||
numero_devis = getattr(doc_result, "DO_Piece", "")
|
||||
except:
|
||||
pass
|
||||
|
||||
if not numero_devis:
|
||||
numero_devis = getattr(doc, "DO_Piece", "")
|
||||
|
||||
if not numero_devis:
|
||||
try:
|
||||
doc.SetDefaultNumPiece()
|
||||
doc.Write()
|
||||
doc.Read()
|
||||
numero_devis = getattr(doc, "DO_Piece", "")
|
||||
except:
|
||||
pass
|
||||
|
||||
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
|
||||
persist_reread = factory_doc.ReadPiece(0, numero_devis)
|
||||
|
||||
if not persist_reread:
|
||||
logger.debug("ReadPiece échoué, recherche dans List()...")
|
||||
persist_reread = _rechercher_devis_dans_liste(
|
||||
numero_devis, factory_doc
|
||||
)
|
||||
|
||||
if persist_reread:
|
||||
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", "")
|
||||
|
||||
date_livraison_final = None
|
||||
|
||||
try:
|
||||
date_livr = getattr(doc_final, "DO_DateLivr", None)
|
||||
if date_livr:
|
||||
date_livraison_final = date_livr.strftime("%Y-%m-%d")
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
total_calcule = sum(
|
||||
l.get("montant_ligne_ht", 0) for l in devis_data["lignes"]
|
||||
)
|
||||
total_ht = total_calcule
|
||||
total_ttc = round(total_calcule * 1.20, 2)
|
||||
statut_final = 0 if forcer_brouillon else 2
|
||||
reference_final = devis_data.get("reference", "")
|
||||
date_livraison_final = devis_data.get("date_livraison")
|
||||
|
||||
logger.info(f" Total HT: {total_ht}€")
|
||||
logger.info(f" Total TTC: {total_ttc}€")
|
||||
logger.info(f" Statut final: {statut_final}")
|
||||
if reference_final:
|
||||
logger.info(f" Référence: {reference_final}")
|
||||
if date_livraison_final:
|
||||
logger.info(f" Date livraison: {date_livraison_final}")
|
||||
|
||||
return {
|
||||
"numero_devis": numero_devis,
|
||||
"total_ht": total_ht,
|
||||
"total_ttc": total_ttc,
|
||||
"nb_lignes": len(devis_data["lignes"]),
|
||||
"client_code": devis_data["client"]["code"],
|
||||
"date_devis": str(devis_data.get("date_devis", "")),
|
||||
"date_livraison": date_livraison_final,
|
||||
"reference": reference_final,
|
||||
"statut": statut_final,
|
||||
}
|
||||
|
||||
|
||||
__all__ = [
|
||||
"_recuperer_numero_devis",
|
||||
"_relire_devis"
|
||||
]
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
import win32com.client
|
||||
from typing import Optional
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def _afficher_etat_document(doc, titre: str):
|
||||
"""Affiche l'état complet d'un document."""
|
||||
logger.info("-" * 80)
|
||||
logger.info(titre)
|
||||
logger.info("-" * 80)
|
||||
try:
|
||||
logger.info(f" DO_Piece: {getattr(doc, 'DO_Piece', 'N/A')}")
|
||||
logger.info(f" DO_Ref: '{getattr(doc, 'DO_Ref', 'N/A')}'")
|
||||
logger.info(f" DO_Statut: {getattr(doc, 'DO_Statut', 'N/A')}")
|
||||
|
||||
date_doc = getattr(doc, 'DO_Date', None)
|
||||
date_str = date_doc.strftime('%Y-%m-%d') if date_doc else 'None'
|
||||
logger.info(f" DO_Date: {date_str}")
|
||||
|
||||
date_livr = getattr(doc, 'DO_DateLivr', None)
|
||||
date_livr_str = date_livr.strftime('%Y-%m-%d') if date_livr else 'None'
|
||||
logger.info(f" DO_DateLivr: {date_livr_str}")
|
||||
|
||||
logger.info(f" DO_TotalHT: {getattr(doc, 'DO_TotalHT', 0)}€")
|
||||
logger.info(f" DO_TotalTTC: {getattr(doc, 'DO_TotalTTC', 0)}€")
|
||||
except Exception as e:
|
||||
logger.error(f" Erreur affichage état: {e}")
|
||||
logger.info("-" * 80)
|
||||
|
||||
|
||||
def _compter_lignes_document(doc) -> int:
|
||||
"""Compte les lignes d'un document."""
|
||||
try:
|
||||
try:
|
||||
factory_lignes = doc.FactoryDocumentLigne
|
||||
except:
|
||||
factory_lignes = doc.FactoryDocumentVenteLigne
|
||||
|
||||
count = 0
|
||||
index = 1
|
||||
while index <= 100:
|
||||
try:
|
||||
ligne_p = factory_lignes.List(index)
|
||||
if ligne_p is None:
|
||||
break
|
||||
count += 1
|
||||
index += 1
|
||||
except:
|
||||
break
|
||||
return count
|
||||
except Exception as e:
|
||||
logger.warning(f" Erreur comptage lignes: {e}")
|
||||
return 0
|
||||
|
||||
|
||||
def _rechercher_devis_par_numero(numero: str, factory):
|
||||
"""Recherche un devis par numéro dans la liste."""
|
||||
logger.info(f" Recherche de {numero} dans la liste...")
|
||||
|
||||
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
|
||||
):
|
||||
logger.info(f" Trouvé à l'index {index}")
|
||||
return persist_test
|
||||
|
||||
index += 1
|
||||
except:
|
||||
index += 1
|
||||
|
||||
logger.error(f" Devis {numero} non trouvé dans la liste")
|
||||
return None
|
||||
|
||||
__all__ = [
|
||||
"_afficher_etat_document",
|
||||
"_compter_lignes_document",
|
||||
"_rechercher_devis_par_numero"
|
||||
]
|
||||
|
|
@ -0,0 +1,913 @@
|
|||
import win32com.client
|
||||
from typing import Optional
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from utils.functions.functions import (
|
||||
_convertir_type_depuis_sql,
|
||||
_convertir_type_pour_sql,
|
||||
_safe_strip
|
||||
)
|
||||
|
||||
def _afficher_etat_document(doc, titre: str):
|
||||
"""Affiche l'état complet d'un document."""
|
||||
logger.info("-" * 80)
|
||||
logger.info(titre)
|
||||
logger.info("-" * 80)
|
||||
try:
|
||||
logger.info(f" DO_Piece: {getattr(doc, 'DO_Piece', 'N/A')}")
|
||||
logger.info(f" DO_Ref: '{getattr(doc, 'DO_Ref', 'N/A')}'")
|
||||
logger.info(f" DO_Statut: {getattr(doc, 'DO_Statut', 'N/A')}")
|
||||
|
||||
date_doc = getattr(doc, 'DO_Date', None)
|
||||
date_str = date_doc.strftime('%Y-%m-%d') if date_doc else 'None'
|
||||
logger.info(f" DO_Date: {date_str}")
|
||||
|
||||
date_livr = getattr(doc, 'DO_DateLivr', None)
|
||||
date_livr_str = date_livr.strftime('%Y-%m-%d') if date_livr else 'None'
|
||||
logger.info(f" DO_DateLivr: {date_livr_str}")
|
||||
|
||||
logger.info(f" DO_TotalHT: {getattr(doc, 'DO_TotalHT', 0)}€")
|
||||
logger.info(f" DO_TotalTTC: {getattr(doc, 'DO_TotalTTC', 0)}€")
|
||||
except Exception as e:
|
||||
logger.error(f" Erreur affichage état: {e}")
|
||||
logger.info("-" * 80)
|
||||
|
||||
|
||||
def _compter_lignes_document(doc) -> int:
|
||||
"""Compte les lignes d'un document."""
|
||||
try:
|
||||
try:
|
||||
factory_lignes = doc.FactoryDocumentLigne
|
||||
except:
|
||||
factory_lignes = doc.FactoryDocumentVenteLigne
|
||||
|
||||
count = 0
|
||||
index = 1
|
||||
while index <= 100:
|
||||
try:
|
||||
ligne_p = factory_lignes.List(index)
|
||||
if ligne_p is None:
|
||||
break
|
||||
count += 1
|
||||
index += 1
|
||||
except:
|
||||
break
|
||||
return count
|
||||
except Exception as e:
|
||||
logger.warning(f" Erreur comptage lignes: {e}")
|
||||
return 0
|
||||
|
||||
|
||||
def _rechercher_devis_par_numero(numero: str, factory):
|
||||
"""Recherche un devis par numéro dans la liste."""
|
||||
logger.info(f" Recherche de {numero} dans la liste...")
|
||||
|
||||
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
|
||||
):
|
||||
logger.info(f" Trouvé à l'index {index}")
|
||||
return persist_test
|
||||
|
||||
index += 1
|
||||
except:
|
||||
index += 1
|
||||
|
||||
logger.error(f" Devis {numero} non trouvé dans la liste")
|
||||
return None
|
||||
|
||||
|
||||
def _lire_document_sql(cursor, numero: str, type_doc: int):
|
||||
try:
|
||||
|
||||
query = """
|
||||
SELECT
|
||||
d.DO_Piece, d.DO_Date, d.DO_Ref, d.DO_TotalHT, d.DO_TotalTTC,
|
||||
d.DO_Statut, d.DO_Tiers, d.DO_DateLivr, d.DO_DateExpedition,
|
||||
d.DO_Contact, d.DO_TotalHTNet, d.DO_NetAPayer,
|
||||
d.DO_MontantRegle, d.DO_Reliquat, d.DO_TxEscompte, d.DO_Escompte,
|
||||
d.DO_Taxe1, d.DO_Taxe2, d.DO_Taxe3,
|
||||
d.DO_CodeTaxe1, d.DO_CodeTaxe2, d.DO_CodeTaxe3,
|
||||
d.DO_EStatut, d.DO_Imprim, d.DO_Valide, d.DO_Cloture,
|
||||
d.DO_Transfere, d.DO_Souche, d.DO_PieceOrig, d.DO_GUID,
|
||||
d.CA_Num, d.CG_Num, d.DO_Expedit, d.DO_Condition,
|
||||
d.DO_Tarif, d.DO_TypeFrais, d.DO_ValFrais,
|
||||
d.DO_TypeFranco, d.DO_ValFranco,
|
||||
c.CT_Intitule, c.CT_Adresse, c.CT_CodePostal,
|
||||
c.CT_Ville, c.CT_Telephone, c.CT_EMail
|
||||
FROM F_DOCENTETE d
|
||||
LEFT JOIN F_COMPTET c ON d.DO_Tiers = c.CT_Num
|
||||
WHERE d.DO_Piece = ? AND d.DO_Type = ?
|
||||
"""
|
||||
|
||||
logger.info(f"[SQL READ] Lecture directe de {numero} (type={type_doc})")
|
||||
|
||||
cursor.execute(query, (numero, type_doc))
|
||||
row = cursor.fetchone()
|
||||
|
||||
if not row:
|
||||
logger.warning(
|
||||
f"[SQL READ] Document {numero} (type={type_doc}) INTROUVABLE dans F_DOCENTETE"
|
||||
)
|
||||
return None
|
||||
|
||||
numero_piece = _safe_strip(row[0])
|
||||
logger.info(f"[SQL READ] Document trouvé: {numero_piece}")
|
||||
|
||||
|
||||
doc = {
|
||||
"numero": numero_piece,
|
||||
"reference": _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_expedition": (
|
||||
str(row[8]) if row[8] else ""
|
||||
), # DO_DateExpedition
|
||||
"client_code": _safe_strip(row[6]), # DO_Tiers
|
||||
"client_intitule": _safe_strip(row[39]), # CT_Intitule
|
||||
"client_adresse": _safe_strip(row[40]), # CT_Adresse
|
||||
"client_code_postal": _safe_strip(row[41]), # CT_CodePostal
|
||||
"client_ville": _safe_strip(row[42]), # CT_Ville
|
||||
"client_telephone": _safe_strip(row[43]), # CT_Telephone
|
||||
"client_email": _safe_strip(row[44]), # CT_EMail
|
||||
"contact": _safe_strip(row[9]), # DO_Contact
|
||||
"total_ht": float(row[3]) if row[3] else 0.0, # DO_TotalHT
|
||||
"total_ht_net": float(row[10]) if row[10] else 0.0, # DO_TotalHTNet
|
||||
"total_ttc": float(row[4]) if row[4] else 0.0, # DO_TotalTTC
|
||||
"net_a_payer": float(row[11]) if row[11] else 0.0, # DO_NetAPayer
|
||||
"montant_regle": (
|
||||
float(row[12]) if row[12] else 0.0
|
||||
), # DO_MontantRegle
|
||||
"reliquat": float(row[13]) if row[13] else 0.0, # DO_Reliquat
|
||||
"taux_escompte": (
|
||||
float(row[14]) if row[14] else 0.0
|
||||
), # DO_TxEscompte
|
||||
"escompte": float(row[15]) if row[15] else 0.0, # DO_Escompte
|
||||
"taxe1": float(row[16]) if row[16] else 0.0, # DO_Taxe1
|
||||
"taxe2": float(row[17]) if row[17] else 0.0, # DO_Taxe2
|
||||
"taxe3": float(row[18]) if row[18] else 0.0, # DO_Taxe3
|
||||
"code_taxe1": _safe_strip(row[19]), # DO_CodeTaxe1
|
||||
"code_taxe2": _safe_strip(row[20]), # DO_CodeTaxe2
|
||||
"code_taxe3": _safe_strip(row[21]), # DO_CodeTaxe3
|
||||
"statut": int(row[5]) if row[5] is not None else 0, # DO_Statut
|
||||
"statut_estatut": (
|
||||
int(row[22]) if row[22] is not None else 0
|
||||
), # DO_EStatut
|
||||
"imprime": int(row[23]) if row[23] is not None else 0, # DO_Imprim
|
||||
"valide": int(row[24]) if row[24] is not None else 0, # DO_Valide
|
||||
"cloture": int(row[25]) if row[25] is not None else 0, # DO_Cloture
|
||||
"transfere": (
|
||||
int(row[26]) if row[26] is not None else 0
|
||||
), # DO_Transfere
|
||||
"souche": int(row[27]) if row[27] is not None else 0, # DO_Souche
|
||||
"piece_origine": _safe_strip(row[28]), # DO_PieceOrig
|
||||
"guid": _safe_strip(row[29]), # DO_GUID
|
||||
"ca_num": _safe_strip(row[30]), # CA_Num
|
||||
"cg_num": _safe_strip(row[31]), # CG_Num
|
||||
"expedition": (
|
||||
int(row[32]) if row[32] is not None else 1
|
||||
), # DO_Expedit
|
||||
"condition": (
|
||||
int(row[33]) if row[33] is not None else 1
|
||||
), # DO_Condition
|
||||
"tarif": int(row[34]) if row[34] is not None else 1, # DO_Tarif
|
||||
"type_frais": (
|
||||
int(row[35]) if row[35] is not None else 0
|
||||
), # DO_TypeFrais
|
||||
"valeur_frais": float(row[36]) if row[36] else 0.0, # DO_ValFrais
|
||||
"type_franco": (
|
||||
int(row[37]) if row[37] is not None else 0
|
||||
), # DO_TypeFranco
|
||||
"valeur_franco": float(row[38]) if row[38] else 0.0, # DO_ValFranco
|
||||
}
|
||||
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT
|
||||
dl.*,
|
||||
a.AR_Design, a.FA_CodeFamille, a.AR_PrixTTC, a.AR_PrixVen, a.AR_PrixAch,
|
||||
a.AR_Gamme1, a.AR_Gamme2, a.AR_CodeBarre, a.AR_CoutStd,
|
||||
a.AR_PoidsNet, a.AR_PoidsBrut, a.AR_UniteVen,
|
||||
a.AR_Type, a.AR_Nature, a.AR_Escompte, a.AR_Garantie
|
||||
FROM F_DOCLIGNE dl
|
||||
LEFT JOIN F_ARTICLE a ON dl.AR_Ref = a.AR_Ref
|
||||
WHERE dl.DO_Piece = ? AND dl.DO_Type = ?
|
||||
ORDER BY dl.DL_Ligne
|
||||
""",
|
||||
(numero, type_doc),
|
||||
)
|
||||
|
||||
lignes = []
|
||||
for ligne_row in cursor.fetchall():
|
||||
montant_ht = (
|
||||
float(ligne_row.DL_MontantHT) if ligne_row.DL_MontantHT else 0.0
|
||||
)
|
||||
montant_net = (
|
||||
float(ligne_row.DL_MontantNet)
|
||||
if hasattr(ligne_row, "DL_MontantNet")
|
||||
and ligne_row.DL_MontantNet
|
||||
else montant_ht
|
||||
)
|
||||
|
||||
taux_taxe1 = (
|
||||
float(ligne_row.DL_Taxe1)
|
||||
if hasattr(ligne_row, "DL_Taxe1") and ligne_row.DL_Taxe1
|
||||
else 0.0
|
||||
)
|
||||
taux_taxe2 = (
|
||||
float(ligne_row.DL_Taxe2)
|
||||
if hasattr(ligne_row, "DL_Taxe2") and ligne_row.DL_Taxe2
|
||||
else 0.0
|
||||
)
|
||||
taux_taxe3 = (
|
||||
float(ligne_row.DL_Taxe3)
|
||||
if hasattr(ligne_row, "DL_Taxe3") and ligne_row.DL_Taxe3
|
||||
else 0.0
|
||||
)
|
||||
|
||||
total_taux_taxes = taux_taxe1 + taux_taxe2 + taux_taxe3
|
||||
montant_ttc = montant_net * (1 + total_taux_taxes / 100)
|
||||
|
||||
montant_taxe1 = montant_net * (taux_taxe1 / 100)
|
||||
montant_taxe2 = montant_net * (taux_taxe2 / 100)
|
||||
montant_taxe3 = montant_net * (taux_taxe3 / 100)
|
||||
|
||||
ligne = {
|
||||
"numero_ligne": (
|
||||
int(ligne_row.DL_Ligne) if ligne_row.DL_Ligne else 0
|
||||
),
|
||||
"article_code": _safe_strip(ligne_row.AR_Ref),
|
||||
"designation": _safe_strip(ligne_row.DL_Design),
|
||||
"designation_article": _safe_strip(ligne_row.AR_Design),
|
||||
"quantite": (
|
||||
float(ligne_row.DL_Qte) if ligne_row.DL_Qte else 0.0
|
||||
),
|
||||
"quantite_livree": (
|
||||
float(ligne_row.DL_QteLiv)
|
||||
if hasattr(ligne_row, "DL_QteLiv") and ligne_row.DL_QteLiv
|
||||
else 0.0
|
||||
),
|
||||
"quantite_reservee": (
|
||||
float(ligne_row.DL_QteRes)
|
||||
if hasattr(ligne_row, "DL_QteRes") and ligne_row.DL_QteRes
|
||||
else 0.0
|
||||
),
|
||||
"unite": (
|
||||
_safe_strip(ligne_row.DL_Unite)
|
||||
if hasattr(ligne_row, "DL_Unite")
|
||||
else ""
|
||||
),
|
||||
"prix_unitaire_ht": (
|
||||
float(ligne_row.DL_PrixUnitaire)
|
||||
if ligne_row.DL_PrixUnitaire
|
||||
else 0.0
|
||||
),
|
||||
"prix_unitaire_achat": (
|
||||
float(ligne_row.AR_PrixAch) if ligne_row.AR_PrixAch else 0.0
|
||||
),
|
||||
"prix_unitaire_vente": (
|
||||
float(ligne_row.AR_PrixVen) if ligne_row.AR_PrixVen else 0.0
|
||||
),
|
||||
"prix_unitaire_ttc": (
|
||||
float(ligne_row.AR_PrixTTC) if ligne_row.AR_PrixTTC else 0.0
|
||||
),
|
||||
"montant_ligne_ht": montant_ht,
|
||||
"montant_ligne_net": montant_net,
|
||||
"montant_ligne_ttc": montant_ttc,
|
||||
"remise_valeur1": (
|
||||
float(ligne_row.DL_Remise01REM_Valeur)
|
||||
if hasattr(ligne_row, "DL_Remise01REM_Valeur")
|
||||
and ligne_row.DL_Remise01REM_Valeur
|
||||
else 0.0
|
||||
),
|
||||
"remise_type1": (
|
||||
int(ligne_row.DL_Remise01REM_Type)
|
||||
if hasattr(ligne_row, "DL_Remise01REM_Type")
|
||||
and ligne_row.DL_Remise01REM_Type
|
||||
else 0
|
||||
),
|
||||
"remise_valeur2": (
|
||||
float(ligne_row.DL_Remise02REM_Valeur)
|
||||
if hasattr(ligne_row, "DL_Remise02REM_Valeur")
|
||||
and ligne_row.DL_Remise02REM_Valeur
|
||||
else 0.0
|
||||
),
|
||||
"remise_type2": (
|
||||
int(ligne_row.DL_Remise02REM_Type)
|
||||
if hasattr(ligne_row, "DL_Remise02REM_Type")
|
||||
and ligne_row.DL_Remise02REM_Type
|
||||
else 0
|
||||
),
|
||||
"remise_article": (
|
||||
float(ligne_row.AR_Escompte)
|
||||
if ligne_row.AR_Escompte
|
||||
else 0.0
|
||||
),
|
||||
"taux_taxe1": taux_taxe1,
|
||||
"montant_taxe1": montant_taxe1,
|
||||
"taux_taxe2": taux_taxe2,
|
||||
"montant_taxe2": montant_taxe2,
|
||||
"taux_taxe3": taux_taxe3,
|
||||
"montant_taxe3": montant_taxe3,
|
||||
"total_taxes": montant_taxe1 + montant_taxe2 + montant_taxe3,
|
||||
"famille_article": _safe_strip(ligne_row.FA_CodeFamille),
|
||||
"gamme1": _safe_strip(ligne_row.AR_Gamme1),
|
||||
"gamme2": _safe_strip(ligne_row.AR_Gamme2),
|
||||
"code_barre": _safe_strip(ligne_row.AR_CodeBarre),
|
||||
"type_article": _safe_strip(ligne_row.AR_Type),
|
||||
"nature_article": _safe_strip(ligne_row.AR_Nature),
|
||||
"garantie": _safe_strip(ligne_row.AR_Garantie),
|
||||
"cout_standard": (
|
||||
float(ligne_row.AR_CoutStd) if ligne_row.AR_CoutStd else 0.0
|
||||
),
|
||||
"poids_net": (
|
||||
float(ligne_row.AR_PoidsNet)
|
||||
if ligne_row.AR_PoidsNet
|
||||
else 0.0
|
||||
),
|
||||
"poids_brut": (
|
||||
float(ligne_row.AR_PoidsBrut)
|
||||
if ligne_row.AR_PoidsBrut
|
||||
else 0.0
|
||||
),
|
||||
"unite_vente": _safe_strip(ligne_row.AR_UniteVen),
|
||||
"date_livraison_ligne": (
|
||||
str(ligne_row.DL_DateLivr)
|
||||
if hasattr(ligne_row, "DL_DateLivr")
|
||||
and ligne_row.DL_DateLivr
|
||||
else ""
|
||||
),
|
||||
"statut_ligne": (
|
||||
int(ligne_row.DL_Statut)
|
||||
if hasattr(ligne_row, "DL_Statut")
|
||||
and ligne_row.DL_Statut is not None
|
||||
else 0
|
||||
),
|
||||
"depot": (
|
||||
_safe_strip(ligne_row.DE_No)
|
||||
if hasattr(ligne_row, "DE_No")
|
||||
else ""
|
||||
),
|
||||
"numero_commande": (
|
||||
_safe_strip(ligne_row.DL_NoColis)
|
||||
if hasattr(ligne_row, "DL_NoColis")
|
||||
else ""
|
||||
),
|
||||
"num_colis": (
|
||||
_safe_strip(ligne_row.DL_Colis)
|
||||
if hasattr(ligne_row, "DL_Colis")
|
||||
else ""
|
||||
),
|
||||
}
|
||||
lignes.append(ligne)
|
||||
|
||||
doc["lignes"] = lignes
|
||||
doc["nb_lignes"] = len(lignes)
|
||||
|
||||
total_ht_calcule = sum(l.get("montant_ligne_ht", 0) for l in lignes)
|
||||
total_ttc_calcule = sum(l.get("montant_ligne_ttc", 0) for l in lignes)
|
||||
total_taxes_calcule = sum(l.get("total_taxes", 0) for l in lignes)
|
||||
|
||||
doc["total_ht_calcule"] = total_ht_calcule
|
||||
doc["total_ttc_calcule"] = total_ttc_calcule
|
||||
doc["total_taxes_calcule"] = total_taxes_calcule
|
||||
|
||||
return doc
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f" Erreur SQL lecture document {numero}: {e}", exc_info=True)
|
||||
return None
|
||||
|
||||
def _lister_documents_avec_lignes_sql(
|
||||
cursor,
|
||||
type_doc: int,
|
||||
filtre: str = "",
|
||||
limit: int = None,
|
||||
):
|
||||
"""Liste les documents avec leurs lignes."""
|
||||
try:
|
||||
type_doc_sql = _convertir_type_pour_sql(type_doc)
|
||||
logger.info(f"[SQL LIST] ═══ Type COM {type_doc} → SQL {type_doc_sql} ═══")
|
||||
|
||||
query = """
|
||||
SELECT DISTINCT
|
||||
d.DO_Piece, d.DO_Type, d.DO_Date, d.DO_Ref, d.DO_Tiers,
|
||||
d.DO_TotalHT, d.DO_TotalTTC, d.DO_NetAPayer, d.DO_Statut,
|
||||
d.DO_DateLivr, d.DO_DateExpedition, d.DO_Contact, d.DO_TotalHTNet,
|
||||
d.DO_MontantRegle, d.DO_Reliquat, d.DO_TxEscompte, d.DO_Escompte,
|
||||
d.DO_Taxe1, d.DO_Taxe2, d.DO_Taxe3,
|
||||
d.DO_CodeTaxe1, d.DO_CodeTaxe2, d.DO_CodeTaxe3,
|
||||
d.DO_EStatut, d.DO_Imprim, d.DO_Valide, d.DO_Cloture, d.DO_Transfere,
|
||||
d.DO_Souche, d.DO_PieceOrig, d.DO_GUID,
|
||||
d.CA_Num, d.CG_Num, d.DO_Expedit, d.DO_Condition, d.DO_Tarif,
|
||||
d.DO_TypeFrais, d.DO_ValFrais, d.DO_TypeFranco, d.DO_ValFranco,
|
||||
c.CT_Intitule, c.CT_Adresse, c.CT_CodePostal,
|
||||
c.CT_Ville, c.CT_Telephone, c.CT_EMail
|
||||
FROM F_DOCENTETE d
|
||||
LEFT JOIN F_COMPTET c ON d.DO_Tiers = c.CT_Num
|
||||
WHERE d.DO_Type = ?
|
||||
"""
|
||||
|
||||
params = [type_doc_sql]
|
||||
|
||||
if filtre:
|
||||
query += " AND (d.DO_Piece LIKE ? OR c.CT_Intitule LIKE ? OR d.DO_Ref LIKE ?)"
|
||||
params.extend([f"%{filtre}%", f"%{filtre}%", f"%{filtre}%"])
|
||||
|
||||
query += " ORDER BY d.DO_Date DESC"
|
||||
|
||||
if limit:
|
||||
query = f"SELECT TOP ({limit}) * FROM ({query}) AS subquery"
|
||||
|
||||
cursor.execute(query, params)
|
||||
entetes = cursor.fetchall()
|
||||
|
||||
logger.info(f"[SQL LIST] {len(entetes)} documents SQL")
|
||||
|
||||
documents = []
|
||||
stats = {
|
||||
"total": len(entetes),
|
||||
"exclus_prefixe": 0,
|
||||
"erreur_construction": 0,
|
||||
"erreur_lignes": 0,
|
||||
"erreur_transformations": 0,
|
||||
"erreur_liaisons": 0,
|
||||
"succes": 0,
|
||||
}
|
||||
|
||||
for idx, entete in enumerate(entetes):
|
||||
numero = _safe_strip(entete.DO_Piece)
|
||||
logger.info(
|
||||
f"[SQL LIST] [{idx+1}/{len(entetes)}] Traitement {numero}..."
|
||||
)
|
||||
|
||||
try:
|
||||
prefixes_vente = {
|
||||
0: ["DE"],
|
||||
10: ["BC"],
|
||||
30: ["BL"],
|
||||
50: ["AV", "AR"],
|
||||
60: ["FA", "FC"],
|
||||
}
|
||||
|
||||
prefixes_acceptes = prefixes_vente.get(type_doc, [])
|
||||
|
||||
if prefixes_acceptes:
|
||||
est_vente = any(
|
||||
numero.upper().startswith(p) for p in prefixes_acceptes
|
||||
)
|
||||
if not est_vente:
|
||||
logger.info(
|
||||
f"[SQL LIST] {numero} : exclu (préfixe achat)"
|
||||
)
|
||||
stats["exclus_prefixe"] += 1
|
||||
continue
|
||||
|
||||
logger.debug(f"[SQL LIST] {numero} : préfixe OK")
|
||||
|
||||
try:
|
||||
type_doc_depuis_sql = _convertir_type_depuis_sql(
|
||||
int(entete.DO_Type)
|
||||
)
|
||||
|
||||
doc = {
|
||||
"numero": numero,
|
||||
"type": type_doc_depuis_sql,
|
||||
"reference": _safe_strip(entete.DO_Ref),
|
||||
"date": str(entete.DO_Date) if entete.DO_Date else "",
|
||||
"date_livraison": (
|
||||
str(entete.DO_DateLivr)
|
||||
if entete.DO_DateLivr
|
||||
else ""
|
||||
),
|
||||
"date_expedition": (
|
||||
str(entete.DO_DateExpedition)
|
||||
if entete.DO_DateExpedition
|
||||
else ""
|
||||
),
|
||||
"client_code": _safe_strip(entete.DO_Tiers),
|
||||
"client_intitule": _safe_strip(entete.CT_Intitule),
|
||||
"client_adresse": _safe_strip(entete.CT_Adresse),
|
||||
"client_code_postal": _safe_strip(
|
||||
entete.CT_CodePostal
|
||||
),
|
||||
"client_ville": _safe_strip(entete.CT_Ville),
|
||||
"client_telephone": _safe_strip(
|
||||
entete.CT_Telephone
|
||||
),
|
||||
"client_email": _safe_strip(entete.CT_EMail),
|
||||
"contact": _safe_strip(entete.DO_Contact),
|
||||
"total_ht": (
|
||||
float(entete.DO_TotalHT)
|
||||
if entete.DO_TotalHT
|
||||
else 0.0
|
||||
),
|
||||
"total_ht_net": (
|
||||
float(entete.DO_TotalHTNet)
|
||||
if entete.DO_TotalHTNet
|
||||
else 0.0
|
||||
),
|
||||
"total_ttc": (
|
||||
float(entete.DO_TotalTTC)
|
||||
if entete.DO_TotalTTC
|
||||
else 0.0
|
||||
),
|
||||
"net_a_payer": (
|
||||
float(entete.DO_NetAPayer)
|
||||
if entete.DO_NetAPayer
|
||||
else 0.0
|
||||
),
|
||||
"montant_regle": (
|
||||
float(entete.DO_MontantRegle)
|
||||
if entete.DO_MontantRegle
|
||||
else 0.0
|
||||
),
|
||||
"reliquat": (
|
||||
float(entete.DO_Reliquat)
|
||||
if entete.DO_Reliquat
|
||||
else 0.0
|
||||
),
|
||||
"taux_escompte": (
|
||||
float(entete.DO_TxEscompte)
|
||||
if entete.DO_TxEscompte
|
||||
else 0.0
|
||||
),
|
||||
"escompte": (
|
||||
float(entete.DO_Escompte)
|
||||
if entete.DO_Escompte
|
||||
else 0.0
|
||||
),
|
||||
"taxe1": (
|
||||
float(entete.DO_Taxe1) if entete.DO_Taxe1 else 0.0
|
||||
),
|
||||
"taxe2": (
|
||||
float(entete.DO_Taxe2) if entete.DO_Taxe2 else 0.0
|
||||
),
|
||||
"taxe3": (
|
||||
float(entete.DO_Taxe3) if entete.DO_Taxe3 else 0.0
|
||||
),
|
||||
"code_taxe1": _safe_strip(entete.DO_CodeTaxe1),
|
||||
"code_taxe2": _safe_strip(entete.DO_CodeTaxe2),
|
||||
"code_taxe3": _safe_strip(entete.DO_CodeTaxe3),
|
||||
"statut": (
|
||||
int(entete.DO_Statut)
|
||||
if entete.DO_Statut is not None
|
||||
else 0
|
||||
),
|
||||
"statut_estatut": (
|
||||
int(entete.DO_EStatut)
|
||||
if entete.DO_EStatut is not None
|
||||
else 0
|
||||
),
|
||||
"imprime": (
|
||||
int(entete.DO_Imprim)
|
||||
if entete.DO_Imprim is not None
|
||||
else 0
|
||||
),
|
||||
"valide": (
|
||||
int(entete.DO_Valide)
|
||||
if entete.DO_Valide is not None
|
||||
else 0
|
||||
),
|
||||
"cloture": (
|
||||
int(entete.DO_Cloture)
|
||||
if entete.DO_Cloture is not None
|
||||
else 0
|
||||
),
|
||||
"transfere": (
|
||||
int(entete.DO_Transfere)
|
||||
if entete.DO_Transfere is not None
|
||||
else 0
|
||||
),
|
||||
"souche": _safe_strip(entete.DO_Souche),
|
||||
"piece_origine": _safe_strip(entete.DO_PieceOrig),
|
||||
"guid": _safe_strip(entete.DO_GUID),
|
||||
"ca_num": _safe_strip(entete.CA_Num),
|
||||
"cg_num": _safe_strip(entete.CG_Num),
|
||||
"expedition": _safe_strip(entete.DO_Expedit),
|
||||
"condition": _safe_strip(entete.DO_Condition),
|
||||
"tarif": _safe_strip(entete.DO_Tarif),
|
||||
"type_frais": (
|
||||
int(entete.DO_TypeFrais)
|
||||
if entete.DO_TypeFrais is not None
|
||||
else 0
|
||||
),
|
||||
"valeur_frais": (
|
||||
float(entete.DO_ValFrais)
|
||||
if entete.DO_ValFrais
|
||||
else 0.0
|
||||
),
|
||||
"type_franco": (
|
||||
int(entete.DO_TypeFranco)
|
||||
if entete.DO_TypeFranco is not None
|
||||
else 0
|
||||
),
|
||||
"valeur_franco": (
|
||||
float(entete.DO_ValFranco)
|
||||
if entete.DO_ValFranco
|
||||
else 0.0
|
||||
),
|
||||
"lignes": [],
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
f"[SQL LIST] {numero} : document de base créé"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"[SQL LIST] {numero} : ERREUR construction base: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
stats["erreur_construction"] += 1
|
||||
continue
|
||||
|
||||
try:
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT dl.*,
|
||||
a.AR_Design, a.FA_CodeFamille, a.AR_PrixTTC, a.AR_PrixVen, a.AR_PrixAch,
|
||||
a.AR_Gamme1, a.AR_Gamme2, a.AR_CodeBarre, a.AR_CoutStd,
|
||||
a.AR_PoidsNet, a.AR_PoidsBrut, a.AR_UniteVen,
|
||||
a.AR_Type, a.AR_Nature, a.AR_Escompte, a.AR_Garantie
|
||||
FROM F_DOCLIGNE dl
|
||||
LEFT JOIN F_ARTICLE a ON dl.AR_Ref = a.AR_Ref
|
||||
WHERE dl.DO_Piece = ? AND dl.DO_Type = ?
|
||||
ORDER BY dl.DL_Ligne
|
||||
""",
|
||||
(numero, type_doc_sql),
|
||||
)
|
||||
|
||||
for ligne_row in cursor.fetchall():
|
||||
montant_ht = (
|
||||
float(ligne_row.DL_MontantHT)
|
||||
if ligne_row.DL_MontantHT
|
||||
else 0.0
|
||||
)
|
||||
montant_net = (
|
||||
float(ligne_row.DL_MontantNet)
|
||||
if hasattr(ligne_row, "DL_MontantNet")
|
||||
and ligne_row.DL_MontantNet
|
||||
else montant_ht
|
||||
)
|
||||
|
||||
taux_taxe1 = (
|
||||
float(ligne_row.DL_Taxe1)
|
||||
if hasattr(ligne_row, "DL_Taxe1")
|
||||
and ligne_row.DL_Taxe1
|
||||
else 0.0
|
||||
)
|
||||
taux_taxe2 = (
|
||||
float(ligne_row.DL_Taxe2)
|
||||
if hasattr(ligne_row, "DL_Taxe2")
|
||||
and ligne_row.DL_Taxe2
|
||||
else 0.0
|
||||
)
|
||||
taux_taxe3 = (
|
||||
float(ligne_row.DL_Taxe3)
|
||||
if hasattr(ligne_row, "DL_Taxe3")
|
||||
and ligne_row.DL_Taxe3
|
||||
else 0.0
|
||||
)
|
||||
|
||||
total_taux_taxes = taux_taxe1 + taux_taxe2 + taux_taxe3
|
||||
montant_ttc = montant_net * (1 + total_taux_taxes / 100)
|
||||
|
||||
montant_taxe1 = montant_net * (taux_taxe1 / 100)
|
||||
montant_taxe2 = montant_net * (taux_taxe2 / 100)
|
||||
montant_taxe3 = montant_net * (taux_taxe3 / 100)
|
||||
|
||||
ligne = {
|
||||
"numero_ligne": (
|
||||
int(ligne_row.DL_Ligne)
|
||||
if ligne_row.DL_Ligne
|
||||
else 0
|
||||
),
|
||||
"article_code": _safe_strip(ligne_row.AR_Ref),
|
||||
"designation": _safe_strip(
|
||||
ligne_row.DL_Design
|
||||
),
|
||||
"designation_article": _safe_strip(
|
||||
ligne_row.AR_Design
|
||||
),
|
||||
"quantite": (
|
||||
float(ligne_row.DL_Qte)
|
||||
if ligne_row.DL_Qte
|
||||
else 0.0
|
||||
),
|
||||
"quantite_livree": (
|
||||
float(ligne_row.DL_QteLiv)
|
||||
if hasattr(ligne_row, "DL_QteLiv")
|
||||
and ligne_row.DL_QteLiv
|
||||
else 0.0
|
||||
),
|
||||
"quantite_reservee": (
|
||||
float(ligne_row.DL_QteRes)
|
||||
if hasattr(ligne_row, "DL_QteRes")
|
||||
and ligne_row.DL_QteRes
|
||||
else 0.0
|
||||
),
|
||||
"unite": (
|
||||
_safe_strip(ligne_row.DL_Unite)
|
||||
if hasattr(ligne_row, "DL_Unite")
|
||||
else ""
|
||||
),
|
||||
"prix_unitaire_ht": (
|
||||
float(ligne_row.DL_PrixUnitaire)
|
||||
if ligne_row.DL_PrixUnitaire
|
||||
else 0.0
|
||||
),
|
||||
"prix_unitaire_achat": (
|
||||
float(ligne_row.AR_PrixAch)
|
||||
if ligne_row.AR_PrixAch
|
||||
else 0.0
|
||||
),
|
||||
"prix_unitaire_vente": (
|
||||
float(ligne_row.AR_PrixVen)
|
||||
if ligne_row.AR_PrixVen
|
||||
else 0.0
|
||||
),
|
||||
"prix_unitaire_ttc": (
|
||||
float(ligne_row.AR_PrixTTC)
|
||||
if ligne_row.AR_PrixTTC
|
||||
else 0.0
|
||||
),
|
||||
"montant_ligne_ht": montant_ht,
|
||||
"montant_ligne_net": montant_net,
|
||||
"montant_ligne_ttc": montant_ttc,
|
||||
"remise_valeur1": (
|
||||
float(ligne_row.DL_Remise01REM_Valeur)
|
||||
if hasattr(ligne_row, "DL_Remise01REM_Valeur")
|
||||
and ligne_row.DL_Remise01REM_Valeur
|
||||
else 0.0
|
||||
),
|
||||
"remise_type1": (
|
||||
int(ligne_row.DL_Remise01REM_Type)
|
||||
if hasattr(ligne_row, "DL_Remise01REM_Type")
|
||||
and ligne_row.DL_Remise01REM_Type
|
||||
else 0
|
||||
),
|
||||
"remise_valeur2": (
|
||||
float(ligne_row.DL_Remise02REM_Valeur)
|
||||
if hasattr(ligne_row, "DL_Remise02REM_Valeur")
|
||||
and ligne_row.DL_Remise02REM_Valeur
|
||||
else 0.0
|
||||
),
|
||||
"remise_type2": (
|
||||
int(ligne_row.DL_Remise02REM_Type)
|
||||
if hasattr(ligne_row, "DL_Remise02REM_Type")
|
||||
and ligne_row.DL_Remise02REM_Type
|
||||
else 0
|
||||
),
|
||||
"remise_article": (
|
||||
float(ligne_row.AR_Escompte)
|
||||
if ligne_row.AR_Escompte
|
||||
else 0.0
|
||||
),
|
||||
"taux_taxe1": taux_taxe1,
|
||||
"montant_taxe1": montant_taxe1,
|
||||
"taux_taxe2": taux_taxe2,
|
||||
"montant_taxe2": montant_taxe2,
|
||||
"taux_taxe3": taux_taxe3,
|
||||
"montant_taxe3": montant_taxe3,
|
||||
"total_taxes": montant_taxe1
|
||||
+ montant_taxe2
|
||||
+ montant_taxe3,
|
||||
"famille_article": _safe_strip(
|
||||
ligne_row.FA_CodeFamille
|
||||
),
|
||||
"gamme1": _safe_strip(ligne_row.AR_Gamme1),
|
||||
"gamme2": _safe_strip(ligne_row.AR_Gamme2),
|
||||
"code_barre": _safe_strip(
|
||||
ligne_row.AR_CodeBarre
|
||||
),
|
||||
"type_article": _safe_strip(ligne_row.AR_Type),
|
||||
"nature_article": _safe_strip(
|
||||
ligne_row.AR_Nature
|
||||
),
|
||||
"garantie": _safe_strip(ligne_row.AR_Garantie),
|
||||
"cout_standard": (
|
||||
float(ligne_row.AR_CoutStd)
|
||||
if ligne_row.AR_CoutStd
|
||||
else 0.0
|
||||
),
|
||||
"poids_net": (
|
||||
float(ligne_row.AR_PoidsNet)
|
||||
if ligne_row.AR_PoidsNet
|
||||
else 0.0
|
||||
),
|
||||
"poids_brut": (
|
||||
float(ligne_row.AR_PoidsBrut)
|
||||
if ligne_row.AR_PoidsBrut
|
||||
else 0.0
|
||||
),
|
||||
"unite_vente": _safe_strip(
|
||||
ligne_row.AR_UniteVen
|
||||
),
|
||||
"date_livraison_ligne": (
|
||||
str(ligne_row.DL_DateLivr)
|
||||
if hasattr(ligne_row, "DL_DateLivr")
|
||||
and ligne_row.DL_DateLivr
|
||||
else ""
|
||||
),
|
||||
"statut_ligne": (
|
||||
int(ligne_row.DL_Statut)
|
||||
if hasattr(ligne_row, "DL_Statut")
|
||||
and ligne_row.DL_Statut is not None
|
||||
else 0
|
||||
),
|
||||
"depot": (
|
||||
_safe_strip(ligne_row.DE_No)
|
||||
if hasattr(ligne_row, "DE_No")
|
||||
else ""
|
||||
),
|
||||
"numero_commande": (
|
||||
_safe_strip(ligne_row.DL_NoColis)
|
||||
if hasattr(ligne_row, "DL_NoColis")
|
||||
else ""
|
||||
),
|
||||
"num_colis": (
|
||||
_safe_strip(ligne_row.DL_Colis)
|
||||
if hasattr(ligne_row, "DL_Colis")
|
||||
else ""
|
||||
),
|
||||
}
|
||||
doc["lignes"].append(ligne)
|
||||
|
||||
doc["nb_lignes"] = len(doc["lignes"])
|
||||
doc["total_ht_calcule"] = sum(
|
||||
l.get("montant_ligne_ht", 0) for l in doc["lignes"]
|
||||
)
|
||||
doc["total_ttc_calcule"] = sum(
|
||||
l.get("montant_ligne_ttc", 0) for l in doc["lignes"]
|
||||
)
|
||||
doc["total_taxes_calcule"] = sum(
|
||||
l.get("total_taxes", 0) for l in doc["lignes"]
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
f"[SQL LIST] {numero} : {doc['nb_lignes']} lignes chargées"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"[SQL LIST] {numero} : ERREUR lignes: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
stats["erreur_lignes"] += 1
|
||||
|
||||
documents.append(doc)
|
||||
stats["succes"] += 1
|
||||
logger.info(
|
||||
f"[SQL LIST] {numero} : AJOUTÉ à la liste (total: {len(documents)})"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"[SQL LIST] {numero} : EXCEPTION GLOBALE - DOCUMENT EXCLU: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
continue
|
||||
|
||||
logger.info(f"[SQL LIST] ═══════════════════════════")
|
||||
logger.info(f"[SQL LIST] STATISTIQUES FINALES:")
|
||||
logger.info(f"[SQL LIST] Total SQL: {stats['total']}")
|
||||
logger.info(f"[SQL LIST] Exclus préfixe: {stats['exclus_prefixe']}")
|
||||
logger.info(
|
||||
f"[SQL LIST] Erreur construction: {stats['erreur_construction']}"
|
||||
)
|
||||
logger.info(f"[SQL LIST] Erreur lignes: {stats['erreur_lignes']}")
|
||||
logger.info(
|
||||
f"[SQL LIST] Erreur transformations: {stats['erreur_transformations']}"
|
||||
)
|
||||
logger.info(f"[SQL LIST] Erreur liaisons: {stats['erreur_liaisons']}")
|
||||
logger.info(f"[SQL LIST] SUCCÈS: {stats['succes']}")
|
||||
logger.info(f"[SQL LIST] Documents retournés: {len(documents)}")
|
||||
logger.info(f"[SQL LIST] ═══════════════════════════")
|
||||
|
||||
return documents
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f" Erreur GLOBALE listage: {e}", exc_info=True)
|
||||
return []
|
||||
|
||||
|
||||
__all__ = [
|
||||
"_afficher_etat_document",
|
||||
"_compter_lignes_document",
|
||||
"_rechercher_devis_par_numero",
|
||||
"_lire_document_sql",
|
||||
"_lister_documents_avec_lignes_sql",
|
||||
]
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
from typing import Optional
|
||||
from datetime import datetime, timedelta, date
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -77,12 +78,12 @@ def _get_type_libelle(type_doc: int) -> str:
|
|||
return f"Type {type_doc}"
|
||||
|
||||
|
||||
def _convertir_type_pour_sql(self, type_doc: int) -> int:
|
||||
def _convertir_type_pour_sql(type_doc: int) -> int:
|
||||
"""COM → SQL : 0, 10, 20, 30... → 0, 1, 2, 3..."""
|
||||
mapping = {0: 0, 10: 1, 20: 2, 30: 3, 40: 4, 50: 5, 60: 6}
|
||||
return mapping.get(type_doc, type_doc)
|
||||
|
||||
def _convertir_type_depuis_sql(self, type_sql: int) -> int:
|
||||
def _convertir_type_depuis_sql(type_sql: int) -> int:
|
||||
"""SQL → COM : 0, 1, 2, 3... → 0, 10, 20, 30..."""
|
||||
mapping = {0: 0, 1: 10, 2: 20, 3: 30, 4: 40, 5: 50, 6: 60}
|
||||
return mapping.get(type_sql, type_sql)
|
||||
|
|
@ -105,6 +106,22 @@ def _normaliser_type_document(type_doc: int) -> int:
|
|||
|
||||
return mapping_normalisation.get(type_doc, type_doc)
|
||||
|
||||
def normaliser_date(valeur):
|
||||
if isinstance(valeur, str):
|
||||
try:
|
||||
return datetime.fromisoformat(valeur)
|
||||
except ValueError:
|
||||
return datetime.now()
|
||||
|
||||
elif isinstance(valeur, date):
|
||||
return datetime.combine(valeur, datetime.min.time())
|
||||
|
||||
elif isinstance(valeur, datetime):
|
||||
return valeur
|
||||
|
||||
else:
|
||||
return datetime.now()
|
||||
|
||||
__all__ = [
|
||||
"_clean_str",
|
||||
"_safe_strip",
|
||||
|
|
@ -113,6 +130,6 @@ __all__ = [
|
|||
"_get_type_libelle",
|
||||
"_normaliser_type_document",
|
||||
"_convertir_type_depuis_sql",
|
||||
"_convertir_type_pour_sql"
|
||||
|
||||
"_convertir_type_pour_sql",
|
||||
"normaliser_date"
|
||||
]
|
||||
|
|
@ -1,37 +1,62 @@
|
|||
from typing import Dict, List, Optional, Any
|
||||
from utils.functions.items_to_dict import _row_to_contact_dict
|
||||
import logging
|
||||
from utils.functions.functions import _safe_strip
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def obtenir_contact(self, numero: str, contact_numero: int) -> Optional[Dict]:
|
||||
"""
|
||||
Récupère un contact spécifique par son CT_No
|
||||
"""
|
||||
try:
|
||||
with self._get_sql_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
query = """
|
||||
SELECT
|
||||
CT_Num, CT_No, N_Contact,
|
||||
CT_Civilite, CT_Nom, CT_Prenom, CT_Fonction,
|
||||
N_Service,
|
||||
CT_Telephone, CT_TelPortable, CT_Telecopie, CT_EMail,
|
||||
CT_Facebook, CT_LinkedIn, CT_Skype
|
||||
FROM F_CONTACTT
|
||||
WHERE CT_Num = ? AND CT_No = ?
|
||||
"""
|
||||
|
||||
cursor.execute(query, [numero, contact_numero])
|
||||
row = cursor.fetchone()
|
||||
|
||||
if not row:
|
||||
return None
|
||||
|
||||
return _row_to_contact_dict(row)
|
||||
|
||||
def _get_contacts_client(numero: str, conn) -> list:
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
|
||||
query = """
|
||||
SELECT
|
||||
CT_Num, CT_No, N_Contact,
|
||||
CT_Civilite, CT_Nom, CT_Prenom, CT_Fonction,
|
||||
N_Service,
|
||||
CT_Telephone, CT_TelPortable, CT_Telecopie, CT_EMail,
|
||||
CT_Facebook, CT_LinkedIn, CT_Skype
|
||||
FROM F_CONTACTT
|
||||
WHERE CT_Num = ?
|
||||
ORDER BY N_Contact, CT_Nom, CT_Prenom
|
||||
"""
|
||||
|
||||
cursor.execute(query, [numero])
|
||||
rows = cursor.fetchall()
|
||||
|
||||
query_client = """
|
||||
SELECT CT_Contact
|
||||
FROM F_COMPTET
|
||||
WHERE CT_Num = ?
|
||||
"""
|
||||
cursor.execute(query_client, [numero])
|
||||
client_row = cursor.fetchone()
|
||||
|
||||
nom_contact_defaut = None
|
||||
if client_row:
|
||||
nom_contact_defaut = _safe_strip(client_row.CT_Contact)
|
||||
|
||||
contacts = []
|
||||
for row in rows:
|
||||
contact = _row_to_contact_dict(row)
|
||||
|
||||
if nom_contact_defaut:
|
||||
nom_complet = f"{contact.get('prenom', '')} {contact['nom']}".strip()
|
||||
contact["est_defaut"] = (
|
||||
nom_complet == nom_contact_defaut or
|
||||
contact['nom'] == nom_contact_defaut
|
||||
)
|
||||
else:
|
||||
contact["est_defaut"] = False
|
||||
|
||||
contacts.append(contact)
|
||||
|
||||
return contacts
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur obtention contact: {e}")
|
||||
raise RuntimeError(f"Erreur lecture contact: {str(e)}")
|
||||
logger.warning(f" Impossible de récupérer contacts pour {numero}: {e}")
|
||||
return []
|
||||
|
||||
__all__ = [
|
||||
"_get_contacts_client"
|
||||
]
|
||||
383
utils/tiers/fournisseurs/fournisseurs_extraction.py
Normal file
383
utils/tiers/fournisseurs/fournisseurs_extraction.py
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
|
||||
import win32com.client
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def _extraire_fournisseur_enrichi(fourn_obj):
|
||||
try:
|
||||
numero = getattr(fourn_obj, "CT_Num", "").strip()
|
||||
if not numero:
|
||||
return None
|
||||
|
||||
intitule = getattr(fourn_obj, "CT_Intitule", "").strip()
|
||||
|
||||
data = {
|
||||
"numero": numero,
|
||||
"intitule": intitule,
|
||||
"type": 1, # Fournisseur
|
||||
"est_fournisseur": True,
|
||||
}
|
||||
|
||||
try:
|
||||
sommeil = getattr(fourn_obj, "CT_Sommeil", 0)
|
||||
data["est_actif"] = sommeil == 0
|
||||
data["en_sommeil"] = sommeil == 1
|
||||
except:
|
||||
data["est_actif"] = True
|
||||
data["en_sommeil"] = False
|
||||
|
||||
try:
|
||||
adresse_obj = getattr(fourn_obj, "Adresse", None)
|
||||
if adresse_obj:
|
||||
data["adresse"] = getattr(adresse_obj, "Adresse", "").strip()
|
||||
data["complement"] = getattr(adresse_obj, "Complement", "").strip()
|
||||
data["code_postal"] = getattr(adresse_obj, "CodePostal", "").strip()
|
||||
data["ville"] = getattr(adresse_obj, "Ville", "").strip()
|
||||
data["region"] = getattr(adresse_obj, "Region", "").strip()
|
||||
data["pays"] = getattr(adresse_obj, "Pays", "").strip()
|
||||
|
||||
parties_adresse = []
|
||||
if data["adresse"]:
|
||||
parties_adresse.append(data["adresse"])
|
||||
if data["complement"]:
|
||||
parties_adresse.append(data["complement"])
|
||||
if data["code_postal"] or data["ville"]:
|
||||
ville_cp = f"{data['code_postal']} {data['ville']}".strip()
|
||||
if ville_cp:
|
||||
parties_adresse.append(ville_cp)
|
||||
if data["pays"]:
|
||||
parties_adresse.append(data["pays"])
|
||||
|
||||
data["adresse_complete"] = ", ".join(parties_adresse)
|
||||
else:
|
||||
data["adresse"] = ""
|
||||
data["complement"] = ""
|
||||
data["code_postal"] = ""
|
||||
data["ville"] = ""
|
||||
data["region"] = ""
|
||||
data["pays"] = ""
|
||||
data["adresse_complete"] = ""
|
||||
except Exception as e:
|
||||
logger.debug(f"Erreur adresse fournisseur {numero}: {e}")
|
||||
data["adresse"] = ""
|
||||
data["complement"] = ""
|
||||
data["code_postal"] = ""
|
||||
data["ville"] = ""
|
||||
data["region"] = ""
|
||||
data["pays"] = ""
|
||||
data["adresse_complete"] = ""
|
||||
|
||||
try:
|
||||
telecom_obj = getattr(fourn_obj, "Telecom", None)
|
||||
if telecom_obj:
|
||||
data["telephone"] = getattr(telecom_obj, "Telephone", "").strip()
|
||||
data["portable"] = getattr(telecom_obj, "Portable", "").strip()
|
||||
data["telecopie"] = getattr(telecom_obj, "Telecopie", "").strip()
|
||||
data["email"] = getattr(telecom_obj, "EMail", "").strip()
|
||||
|
||||
try:
|
||||
site = (
|
||||
getattr(telecom_obj, "Site", None)
|
||||
or getattr(telecom_obj, "Web", None)
|
||||
or getattr(telecom_obj, "SiteWeb", "")
|
||||
)
|
||||
data["site_web"] = str(site).strip() if site else ""
|
||||
except:
|
||||
data["site_web"] = ""
|
||||
else:
|
||||
data["telephone"] = ""
|
||||
data["portable"] = ""
|
||||
data["telecopie"] = ""
|
||||
data["email"] = ""
|
||||
data["site_web"] = ""
|
||||
except Exception as e:
|
||||
logger.debug(f"Erreur telecom fournisseur {numero}: {e}")
|
||||
data["telephone"] = ""
|
||||
data["portable"] = ""
|
||||
data["telecopie"] = ""
|
||||
data["email"] = ""
|
||||
data["site_web"] = ""
|
||||
|
||||
try:
|
||||
data["siret"] = getattr(fourn_obj, "CT_Siret", "").strip()
|
||||
except:
|
||||
data["siret"] = ""
|
||||
|
||||
try:
|
||||
if data["siret"] and len(data["siret"]) >= 9:
|
||||
data["siren"] = data["siret"][:9]
|
||||
else:
|
||||
data["siren"] = getattr(fourn_obj, "CT_Siren", "").strip()
|
||||
except:
|
||||
data["siren"] = ""
|
||||
|
||||
try:
|
||||
data["tva_intra"] = getattr(fourn_obj, "CT_Identifiant", "").strip()
|
||||
except:
|
||||
data["tva_intra"] = ""
|
||||
|
||||
try:
|
||||
data["code_naf"] = (
|
||||
getattr(fourn_obj, "CT_CodeNAF", "").strip()
|
||||
or getattr(fourn_obj, "CT_APE", "").strip()
|
||||
)
|
||||
except:
|
||||
data["code_naf"] = ""
|
||||
|
||||
try:
|
||||
data["forme_juridique"] = getattr(
|
||||
fourn_obj, "CT_FormeJuridique", ""
|
||||
).strip()
|
||||
except:
|
||||
data["forme_juridique"] = ""
|
||||
|
||||
try:
|
||||
cat_tarif = getattr(fourn_obj, "N_CatTarif", None)
|
||||
data["categorie_tarifaire"] = (
|
||||
int(cat_tarif) if cat_tarif is not None else None
|
||||
)
|
||||
except:
|
||||
data["categorie_tarifaire"] = None
|
||||
|
||||
try:
|
||||
cat_compta = getattr(fourn_obj, "N_CatCompta", None)
|
||||
data["categorie_comptable"] = (
|
||||
int(cat_compta) if cat_compta is not None else None
|
||||
)
|
||||
except:
|
||||
data["categorie_comptable"] = None
|
||||
|
||||
try:
|
||||
cond_regl = getattr(fourn_obj, "CT_CondRegl", "").strip()
|
||||
data["conditions_reglement_code"] = cond_regl
|
||||
|
||||
if cond_regl:
|
||||
try:
|
||||
cond_obj = getattr(fourn_obj, "ConditionReglement", None)
|
||||
if cond_obj:
|
||||
cond_obj.Read()
|
||||
data["conditions_reglement_libelle"] = getattr(
|
||||
cond_obj, "C_Intitule", ""
|
||||
).strip()
|
||||
else:
|
||||
data["conditions_reglement_libelle"] = ""
|
||||
except:
|
||||
data["conditions_reglement_libelle"] = ""
|
||||
else:
|
||||
data["conditions_reglement_libelle"] = ""
|
||||
except:
|
||||
data["conditions_reglement_code"] = ""
|
||||
data["conditions_reglement_libelle"] = ""
|
||||
|
||||
try:
|
||||
mode_regl = getattr(fourn_obj, "CT_ModeRegl", "").strip()
|
||||
data["mode_reglement_code"] = mode_regl
|
||||
|
||||
if mode_regl:
|
||||
try:
|
||||
mode_obj = getattr(fourn_obj, "ModeReglement", None)
|
||||
if mode_obj:
|
||||
mode_obj.Read()
|
||||
data["mode_reglement_libelle"] = getattr(
|
||||
mode_obj, "M_Intitule", ""
|
||||
).strip()
|
||||
else:
|
||||
data["mode_reglement_libelle"] = ""
|
||||
except:
|
||||
data["mode_reglement_libelle"] = ""
|
||||
else:
|
||||
data["mode_reglement_libelle"] = ""
|
||||
except:
|
||||
data["mode_reglement_code"] = ""
|
||||
data["mode_reglement_libelle"] = ""
|
||||
|
||||
data["coordonnees_bancaires"] = []
|
||||
|
||||
try:
|
||||
factory_banque = getattr(fourn_obj, "FactoryBanque", None)
|
||||
|
||||
if factory_banque:
|
||||
index = 1
|
||||
while index <= 5: # Max 5 comptes bancaires
|
||||
try:
|
||||
banque_persist = factory_banque.List(index)
|
||||
if banque_persist is None:
|
||||
break
|
||||
|
||||
banque = win32com.client.CastTo(
|
||||
banque_persist, "IBOBanque3"
|
||||
)
|
||||
banque.Read()
|
||||
|
||||
compte_bancaire = {
|
||||
"banque_nom": getattr(
|
||||
banque, "BI_Intitule", ""
|
||||
).strip(),
|
||||
"iban": getattr(banque, "RIB_Iban", "").strip(),
|
||||
"bic": getattr(banque, "RIB_Bic", "").strip(),
|
||||
"code_banque": getattr(
|
||||
banque, "RIB_Banque", ""
|
||||
).strip(),
|
||||
"code_guichet": getattr(
|
||||
banque, "RIB_Guichet", ""
|
||||
).strip(),
|
||||
"numero_compte": getattr(
|
||||
banque, "RIB_Compte", ""
|
||||
).strip(),
|
||||
"cle_rib": getattr(banque, "RIB_Cle", "").strip(),
|
||||
}
|
||||
|
||||
if (
|
||||
compte_bancaire["iban"]
|
||||
or compte_bancaire["numero_compte"]
|
||||
):
|
||||
data["coordonnees_bancaires"].append(compte_bancaire)
|
||||
|
||||
index += 1
|
||||
except:
|
||||
break
|
||||
except Exception as e:
|
||||
logger.debug(
|
||||
f"Erreur coordonnées bancaires fournisseur {numero}: {e}"
|
||||
)
|
||||
|
||||
if data["coordonnees_bancaires"]:
|
||||
data["iban_principal"] = data["coordonnees_bancaires"][0].get(
|
||||
"iban", ""
|
||||
)
|
||||
data["bic_principal"] = data["coordonnees_bancaires"][0].get("bic", "")
|
||||
else:
|
||||
data["iban_principal"] = ""
|
||||
data["bic_principal"] = ""
|
||||
|
||||
data["contacts"] = []
|
||||
|
||||
try:
|
||||
factory_contact = getattr(fourn_obj, "FactoryContact", None)
|
||||
|
||||
if factory_contact:
|
||||
index = 1
|
||||
while index <= 20: # Max 20 contacts
|
||||
try:
|
||||
contact_persist = factory_contact.List(index)
|
||||
if contact_persist is None:
|
||||
break
|
||||
|
||||
contact = win32com.client.CastTo(
|
||||
contact_persist, "IBOContact3"
|
||||
)
|
||||
contact.Read()
|
||||
|
||||
contact_data = {
|
||||
"nom": getattr(contact, "CO_Nom", "").strip(),
|
||||
"prenom": getattr(contact, "CO_Prenom", "").strip(),
|
||||
"fonction": getattr(contact, "CO_Fonction", "").strip(),
|
||||
"service": getattr(contact, "CO_Service", "").strip(),
|
||||
"telephone": getattr(
|
||||
contact, "CO_Telephone", ""
|
||||
).strip(),
|
||||
"portable": getattr(contact, "CO_Portable", "").strip(),
|
||||
"email": getattr(contact, "CO_EMail", "").strip(),
|
||||
}
|
||||
|
||||
nom_complet = f"{contact_data['prenom']} {contact_data['nom']}".strip()
|
||||
if nom_complet:
|
||||
contact_data["nom_complet"] = nom_complet
|
||||
else:
|
||||
contact_data["nom_complet"] = contact_data["nom"]
|
||||
|
||||
if contact_data["nom"]:
|
||||
data["contacts"].append(contact_data)
|
||||
|
||||
index += 1
|
||||
except:
|
||||
break
|
||||
except Exception as e:
|
||||
logger.debug(f"Erreur contacts fournisseur {numero}: {e}")
|
||||
|
||||
data["nb_contacts"] = len(data["contacts"])
|
||||
|
||||
if data["contacts"]:
|
||||
data["contact_principal"] = data["contacts"][0]
|
||||
else:
|
||||
data["contact_principal"] = None
|
||||
|
||||
try:
|
||||
data["encours_autorise"] = float(getattr(fourn_obj, "CT_Encours", 0.0))
|
||||
except:
|
||||
data["encours_autorise"] = 0.0
|
||||
|
||||
try:
|
||||
data["ca_annuel"] = float(getattr(fourn_obj, "CT_ChiffreAffaire", 0.0))
|
||||
except:
|
||||
data["ca_annuel"] = 0.0
|
||||
|
||||
try:
|
||||
data["compte_general"] = getattr(fourn_obj, "CG_Num", "").strip()
|
||||
except:
|
||||
data["compte_general"] = ""
|
||||
|
||||
try:
|
||||
date_creation = getattr(fourn_obj, "CT_DateCreate", None)
|
||||
data["date_creation"] = str(date_creation) if date_creation else ""
|
||||
except:
|
||||
data["date_creation"] = ""
|
||||
|
||||
try:
|
||||
date_modif = getattr(fourn_obj, "CT_DateModif", None)
|
||||
data["date_modification"] = str(date_modif) if date_modif else ""
|
||||
except:
|
||||
data["date_modification"] = ""
|
||||
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f" Erreur extraction fournisseur: {e}", exc_info=True)
|
||||
return {
|
||||
"numero": getattr(fourn_obj, "CT_Num", "").strip(),
|
||||
"intitule": getattr(fourn_obj, "CT_Intitule", "").strip(),
|
||||
"type": 1,
|
||||
"est_fournisseur": True,
|
||||
"est_actif": True,
|
||||
"en_sommeil": False,
|
||||
"adresse": "",
|
||||
"complement": "",
|
||||
"code_postal": "",
|
||||
"ville": "",
|
||||
"region": "",
|
||||
"pays": "",
|
||||
"adresse_complete": "",
|
||||
"telephone": "",
|
||||
"portable": "",
|
||||
"telecopie": "",
|
||||
"email": "",
|
||||
"site_web": "",
|
||||
"siret": "",
|
||||
"siren": "",
|
||||
"tva_intra": "",
|
||||
"code_naf": "",
|
||||
"forme_juridique": "",
|
||||
"categorie_tarifaire": None,
|
||||
"categorie_comptable": None,
|
||||
"conditions_reglement_code": "",
|
||||
"conditions_reglement_libelle": "",
|
||||
"mode_reglement_code": "",
|
||||
"mode_reglement_libelle": "",
|
||||
"iban_principal": "",
|
||||
"bic_principal": "",
|
||||
"coordonnees_bancaires": [],
|
||||
"contacts": [],
|
||||
"nb_contacts": 0,
|
||||
"contact_principal": None,
|
||||
"encours_autorise": 0.0,
|
||||
"ca_annuel": 0.0,
|
||||
"compte_general": "",
|
||||
"date_creation": "",
|
||||
"date_modification": "",
|
||||
}
|
||||
|
||||
|
||||
__all__ = [
|
||||
"_extraire_fournisseur_enrichi"
|
||||
]
|
||||
Loading…
Reference in a new issue