827 lines
36 KiB
Python
827 lines
36 KiB
Python
import win32com.client
|
|
from typing import Optional
|
|
import logging
|
|
|
|
from utils.functions.functions import (
|
|
_convertir_type_depuis_sql,
|
|
_convertir_type_pour_sql,
|
|
_safe_strip,
|
|
)
|
|
|
|
|
|
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 Exception:
|
|
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 Exception:
|
|
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 Exception:
|
|
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]),
|
|
"date": str(row[1]) if row[1] else "",
|
|
"date_livraison": (str(row[7]) if row[7] else ""),
|
|
"date_expedition": (str(row[8]) if row[8] else ""),
|
|
"client_code": _safe_strip(row[6]),
|
|
"client_intitule": _safe_strip(row[39]),
|
|
"client_adresse": _safe_strip(row[40]),
|
|
"client_code_postal": _safe_strip(row[41]),
|
|
"client_ville": _safe_strip(row[42]),
|
|
"client_telephone": _safe_strip(row[43]),
|
|
"client_email": _safe_strip(row[44]),
|
|
"contact": _safe_strip(row[9]),
|
|
"total_ht": float(row[3]) if row[3] else 0.0,
|
|
"total_ht_net": float(row[10]) if row[10] else 0.0,
|
|
"total_ttc": float(row[4]) if row[4] else 0.0,
|
|
"net_a_payer": float(row[11]) if row[11] else 0.0,
|
|
"montant_regle": (float(row[12]) if row[12] else 0.0),
|
|
"reliquat": float(row[13]) if row[13] else 0.0,
|
|
"taux_escompte": (float(row[14]) if row[14] else 0.0),
|
|
"escompte": float(row[15]) if row[15] else 0.0,
|
|
"taxe1": float(row[16]) if row[16] else 0.0,
|
|
"taxe2": float(row[17]) if row[17] else 0.0,
|
|
"taxe3": float(row[18]) if row[18] else 0.0,
|
|
"code_taxe1": _safe_strip(row[19]),
|
|
"code_taxe2": _safe_strip(row[20]),
|
|
"code_taxe3": _safe_strip(row[21]),
|
|
"statut": int(row[5]) if row[5] is not None else 0,
|
|
"statut_estatut": (
|
|
int(row[22]) if row[22] is not None else 0
|
|
),
|
|
"imprime": int(row[23]) if row[23] is not None else 0,
|
|
"valide": int(row[24]) if row[24] is not None else 0,
|
|
"cloture": int(row[25]) if row[25] is not None else 0,
|
|
"transfere": (int(row[26]) if row[26] is not None else 0),
|
|
"souche": int(row[27]) if row[27] is not None else 0,
|
|
"piece_origine": _safe_strip(row[28]),
|
|
"guid": _safe_strip(row[29]),
|
|
"ca_num": _safe_strip(row[30]),
|
|
"cg_num": _safe_strip(row[31]),
|
|
"expedition": (int(row[32]) if row[32] is not None else 1),
|
|
"condition": (int(row[33]) if row[33] is not None else 1),
|
|
"tarif": int(row[34]) if row[34] is not None else 1,
|
|
"type_frais": (int(row[35]) if row[35] is not None else 0),
|
|
"valeur_frais": float(row[36]) if row[36] else 0.0,
|
|
"type_franco": (
|
|
int(row[37]) if row[37] is not None else 0
|
|
),
|
|
"valeur_franco": float(row[38]) if row[38] else 0.0,
|
|
}
|
|
|
|
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(ligne.get("montant_ligne_ht", 0) for ligne in lignes)
|
|
total_ttc_calcule = sum(ligne.get("montant_ligne_ttc", 0) for ligne in lignes)
|
|
total_taxes_calcule = sum(ligne.get("total_taxes", 0) for ligne 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(
|
|
ligne.get("montant_ligne_ht", 0) for ligne in doc["lignes"]
|
|
)
|
|
doc["total_ttc_calcule"] = sum(
|
|
ligne.get("montant_ligne_ttc", 0) for ligne in doc["lignes"]
|
|
)
|
|
doc["total_taxes_calcule"] = sum(
|
|
ligne.get("total_taxes", 0) for ligne 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("[SQL LIST] ═══════════════════════════")
|
|
logger.info("[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("[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",
|
|
]
|