feat: Enhance mandatory invoice field population by adding fallback logic for payment, journal, and numbering, and including tax type/code.
This commit is contained in:
parent
0763a56b06
commit
8b676f7195
2 changed files with 352 additions and 77 deletions
179
main.py
179
main.py
|
|
@ -1513,6 +1513,185 @@ def diagnostiquer_exigences_facture():
|
||||||
raise HTTPException(500, str(e))
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/sage/test/creer-facture-vide", dependencies=[Depends(verify_token)])
|
||||||
|
def tester_creation_facture_vide():
|
||||||
|
"""
|
||||||
|
🧪 TEST: Crée une facture vide pour identifier les champs obligatoires
|
||||||
|
|
||||||
|
Ce test permet de découvrir EXACTEMENT quels champs Sage exige
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not sage or not sage.cial:
|
||||||
|
raise HTTPException(503, "Service Sage indisponible")
|
||||||
|
|
||||||
|
with sage._com_context(), sage._lock_com:
|
||||||
|
logger.info("[TEST] Creation facture test...")
|
||||||
|
|
||||||
|
# 1. Créer le process
|
||||||
|
process = sage.cial.CreateProcess_Document(60)
|
||||||
|
doc = process.Document
|
||||||
|
|
||||||
|
try:
|
||||||
|
doc = win32com.client.CastTo(doc, "IBODocumentVente3")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 2. Définir UNIQUEMENT les champs absolument critiques
|
||||||
|
import pywintypes
|
||||||
|
|
||||||
|
# Date (obligatoire)
|
||||||
|
doc.DO_Date = pywintypes.Time(datetime.now())
|
||||||
|
|
||||||
|
# Client (obligatoire) - Utiliser le premier client disponible
|
||||||
|
factory_client = sage.cial.CptaApplication.FactoryClient
|
||||||
|
persist_client = factory_client.List(1)
|
||||||
|
|
||||||
|
if persist_client:
|
||||||
|
client = sage._cast_client(persist_client)
|
||||||
|
if client:
|
||||||
|
doc.SetDefaultClient(client)
|
||||||
|
client_code = getattr(client, "CT_Num", "?")
|
||||||
|
logger.info(f"[TEST] Client test: {client_code}")
|
||||||
|
|
||||||
|
# 3. Écrire sans Process() pour voir les valeurs par défaut
|
||||||
|
doc.Write()
|
||||||
|
doc.Read()
|
||||||
|
|
||||||
|
# 4. Analyser tous les champs
|
||||||
|
champs_analyse = {}
|
||||||
|
|
||||||
|
for attr in dir(doc):
|
||||||
|
if attr.startswith("DO_") or attr.startswith("CT_"):
|
||||||
|
try:
|
||||||
|
valeur = getattr(doc, attr, None)
|
||||||
|
if valeur is not None:
|
||||||
|
champs_analyse[attr] = {
|
||||||
|
"valeur": str(valeur),
|
||||||
|
"type": type(valeur).__name__,
|
||||||
|
}
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
logger.info(f"[TEST] {len(champs_analyse)} champs analyses")
|
||||||
|
|
||||||
|
# 5. Tester Process() pour voir l'erreur exacte
|
||||||
|
erreur_process = None
|
||||||
|
try:
|
||||||
|
process.Process()
|
||||||
|
logger.info("[TEST] Process() reussi (inattendu!)")
|
||||||
|
except Exception as e:
|
||||||
|
erreur_process = str(e)
|
||||||
|
logger.info(f"[TEST] Process() echoue comme prevu: {e}")
|
||||||
|
|
||||||
|
# Ne pas commit - c'est juste un test
|
||||||
|
try:
|
||||||
|
sage.cial.CptaApplication.RollbackTrans()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"champs_definis": champs_analyse,
|
||||||
|
"erreur_process": erreur_process,
|
||||||
|
"conseil": "Les champs manquants dans l'erreur sont probablement obligatoires",
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[TEST] Erreur: {e}", exc_info=True)
|
||||||
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/sage/config/parametres-facture", dependencies=[Depends(verify_token)])
|
||||||
|
def verifier_parametres_facture():
|
||||||
|
"""
|
||||||
|
🔍 Vérifie les paramètres Sage pour la création de factures
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not sage or not sage.cial:
|
||||||
|
raise HTTPException(503, "Service Sage indisponible")
|
||||||
|
|
||||||
|
with sage._com_context(), sage._lock_com:
|
||||||
|
parametres = {}
|
||||||
|
|
||||||
|
# Paramètres société
|
||||||
|
try:
|
||||||
|
param_societe = sage.cial.CptaApplication.ParametreSociete
|
||||||
|
|
||||||
|
parametres["societe"] = {
|
||||||
|
"journal_vente_defaut": getattr(
|
||||||
|
param_societe, "P_CodeJournalVte", "N/A"
|
||||||
|
),
|
||||||
|
"mode_reglement_defaut": getattr(
|
||||||
|
param_societe, "P_ModeRegl", "N/A"
|
||||||
|
),
|
||||||
|
"souche_facture": getattr(param_societe, "P_SoucheFacture", "N/A"),
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
parametres["erreur_societe"] = str(e)
|
||||||
|
|
||||||
|
# Tester un client existant
|
||||||
|
try:
|
||||||
|
factory_client = sage.cial.CptaApplication.FactoryClient
|
||||||
|
persist = factory_client.List(1)
|
||||||
|
|
||||||
|
if persist:
|
||||||
|
client = sage._cast_client(persist)
|
||||||
|
if client:
|
||||||
|
parametres["exemple_client"] = {
|
||||||
|
"code": getattr(client, "CT_Num", "?"),
|
||||||
|
"mode_reglement": getattr(client, "CT_ModeRegl", "N/A"),
|
||||||
|
"conditions_reglement": getattr(
|
||||||
|
client, "CT_CondRegl", "N/A"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
parametres["erreur_client"] = str(e)
|
||||||
|
|
||||||
|
# Journaux disponibles
|
||||||
|
try:
|
||||||
|
factory_journal = sage.cial.CptaApplication.FactoryJournal
|
||||||
|
journaux = []
|
||||||
|
|
||||||
|
index = 1
|
||||||
|
while index <= 20: # Max 20 journaux
|
||||||
|
try:
|
||||||
|
persist_journal = factory_journal.List(index)
|
||||||
|
if persist_journal is None:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Cast en journal
|
||||||
|
journal = win32com.client.CastTo(persist_journal, "IBOJournal3")
|
||||||
|
journal.Read()
|
||||||
|
|
||||||
|
journaux.append(
|
||||||
|
{
|
||||||
|
"code": getattr(journal, "JO_Num", "?"),
|
||||||
|
"intitule": getattr(journal, "JO_Intitule", "?"),
|
||||||
|
"type": getattr(journal, "JO_Type", "?"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
index += 1
|
||||||
|
except:
|
||||||
|
index += 1
|
||||||
|
break
|
||||||
|
|
||||||
|
parametres["journaux_disponibles"] = journaux
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
parametres["erreur_journaux"] = str(e)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"parametres": parametres,
|
||||||
|
"conseil": "Utilisez ces valeurs pour remplir les champs obligatoires des factures",
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur verification config: {e}", exc_info=True)
|
||||||
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
# =====================================================
|
# =====================================================
|
||||||
# LANCEMENT
|
# LANCEMENT
|
||||||
# =====================================================
|
# =====================================================
|
||||||
|
|
|
||||||
|
|
@ -1355,104 +1355,200 @@ class SageConnector:
|
||||||
# ÉTAPE 8 : COMPLÉTER LES CHAMPS OBLIGATOIRES POUR FACTURE
|
# ÉTAPE 8 : COMPLÉTER LES CHAMPS OBLIGATOIRES POUR FACTURE
|
||||||
# ========================================
|
# ========================================
|
||||||
if type_cible == 60: # Facture
|
if type_cible == 60: # Facture
|
||||||
logger.info(
|
logger.info("[TRANSFORM] Completion champs obligatoires facture...")
|
||||||
"[TRANSFORM] Completion champs obligatoires facture..."
|
|
||||||
)
|
# 1. Mode de règlement (CRITIQUE)
|
||||||
|
|
||||||
# Mode de règlement (obligatoire pour facture)
|
|
||||||
try:
|
try:
|
||||||
# Récupérer le mode de règlement du client
|
mode_reglement = None
|
||||||
|
|
||||||
|
# Essayer de récupérer du client
|
||||||
if client_obj_cible:
|
if client_obj_cible:
|
||||||
mode_reglement = getattr(
|
|
||||||
client_obj_cible, "CT_ModeRegl", None
|
|
||||||
)
|
|
||||||
if mode_reglement:
|
|
||||||
doc_cible.DO_ModeRegl = mode_reglement
|
|
||||||
logger.info(
|
|
||||||
f"[TRANSFORM] Mode reglement: {mode_reglement}"
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(
|
|
||||||
f"[TRANSFORM] Impossible de definir mode reglement: {e}"
|
|
||||||
)
|
|
||||||
# Forcer un mode par défaut (0 = Aucun)
|
|
||||||
try:
|
|
||||||
doc_cible.DO_ModeRegl = 0
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Conditions de règlement
|
|
||||||
try:
|
|
||||||
if client_obj_cible:
|
|
||||||
cond_reglement = getattr(
|
|
||||||
client_obj_cible, "CT_CondRegl", None
|
|
||||||
)
|
|
||||||
if cond_reglement:
|
|
||||||
doc_cible.DO_CondRegl = cond_reglement
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(
|
|
||||||
f"[TRANSFORM] Impossible de definir conditions reglement: {e}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Code journal (critique pour facture)
|
|
||||||
try:
|
|
||||||
# Vérifier si un code journal est déjà défini
|
|
||||||
journal_actuel = getattr(doc_cible, "DO_CodeJournal", None)
|
|
||||||
if not journal_actuel:
|
|
||||||
# Essayer de récupérer le journal par défaut
|
|
||||||
try:
|
try:
|
||||||
# Généralement "VE" pour ventes
|
mode_reglement = getattr(client_obj_cible, 'CT_ModeRegl', None)
|
||||||
doc_cible.DO_CodeJournal = "VE"
|
if mode_reglement is not None:
|
||||||
logger.info("[TRANSFORM] Code journal: VE")
|
logger.info(f"[TRANSFORM] Mode reglement client: {mode_reglement}")
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Si pas trouvé, utiliser celui du document source
|
||||||
|
if mode_reglement is None:
|
||||||
|
try:
|
||||||
|
mode_reglement = getattr(doc_source, 'DO_ModeRegl', None)
|
||||||
|
if mode_reglement is not None:
|
||||||
|
logger.info(f"[TRANSFORM] Mode reglement source: {mode_reglement}")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Forcer une valeur par défaut si toujours None
|
||||||
|
if mode_reglement is None:
|
||||||
|
mode_reglement = 0 # 0 = Aucun
|
||||||
|
logger.info("[TRANSFORM] Mode reglement defaut: 0 (Aucun)")
|
||||||
|
|
||||||
|
doc_cible.DO_ModeRegl = mode_reglement
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(
|
logger.error(f"[TRANSFORM] Erreur mode reglement: {e}")
|
||||||
f"[TRANSFORM] Impossible de definir code journal: {e}"
|
raise
|
||||||
)
|
|
||||||
|
# 2. Conditions de règlement
|
||||||
# Souche de numérotation
|
|
||||||
try:
|
try:
|
||||||
souche = getattr(doc_cible, "DO_Souche", None)
|
cond_reglement = None
|
||||||
if not souche or souche == 0:
|
|
||||||
# Utiliser souche par défaut (généralement 0)
|
if client_obj_cible:
|
||||||
doc_cible.DO_Souche = 0
|
try:
|
||||||
|
cond_reglement = getattr(client_obj_cible, 'CT_CondRegl', None)
|
||||||
|
if cond_reglement is not None:
|
||||||
|
doc_cible.DO_CondRegl = cond_reglement
|
||||||
|
logger.info(f"[TRANSFORM] Conditions reglement: {cond_reglement}")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Fallback sur document source
|
||||||
|
if cond_reglement is None:
|
||||||
|
try:
|
||||||
|
cond_reglement = getattr(doc_source, 'DO_CondRegl', None)
|
||||||
|
if cond_reglement is not None:
|
||||||
|
doc_cible.DO_CondRegl = cond_reglement
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(
|
logger.warning(f"[TRANSFORM] Conditions reglement non definies: {e}")
|
||||||
f"[TRANSFORM] Impossible de definir souche: {e}"
|
|
||||||
)
|
# 3. Code journal (CRITIQUE pour comptabilisation)
|
||||||
|
try:
|
||||||
|
journal = None
|
||||||
|
|
||||||
|
# Essayer de récupérer du document source
|
||||||
|
try:
|
||||||
|
journal = getattr(doc_source, 'DO_CodeJournal', None)
|
||||||
|
if journal:
|
||||||
|
logger.info(f"[TRANSFORM] Journal source: {journal}")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Si pas trouvé, essayer le journal par défaut de Sage
|
||||||
|
if not journal:
|
||||||
|
try:
|
||||||
|
# Récupérer le paramètre société
|
||||||
|
param_societe = self.cial.CptaApplication.ParametreSociete
|
||||||
|
if param_societe:
|
||||||
|
# Journal vente par défaut
|
||||||
|
journal = getattr(param_societe, 'P_CodeJournalVte', 'VE')
|
||||||
|
logger.info(f"[TRANSFORM] Journal societe: {journal}")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Dernier recours : "VE" (standard Sage)
|
||||||
|
if not journal:
|
||||||
|
journal = "VE"
|
||||||
|
logger.info("[TRANSFORM] Journal par defaut: VE")
|
||||||
|
|
||||||
|
doc_cible.DO_CodeJournal = journal
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[TRANSFORM] Erreur code journal: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
# 4. Souche de numérotation
|
||||||
|
try:
|
||||||
|
souche = getattr(doc_source, 'DO_Souche', 0)
|
||||||
|
doc_cible.DO_Souche = souche
|
||||||
|
logger.info(f"[TRANSFORM] Souche: {souche}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"[TRANSFORM] Erreur souche: {e}")
|
||||||
|
try:
|
||||||
|
doc_cible.DO_Souche = 0
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 5. Type de calcul (taxes)
|
||||||
|
try:
|
||||||
|
type_calcul = getattr(doc_source, 'DO_TypeCalcul', None)
|
||||||
|
if type_calcul is not None:
|
||||||
|
doc_cible.DO_TypeCalcul = type_calcul
|
||||||
|
logger.info(f"[TRANSFORM] Type calcul: {type_calcul}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"[TRANSFORM] Type calcul non defini: {e}")
|
||||||
|
|
||||||
|
# 6. Code taxe (important si TVA)
|
||||||
|
try:
|
||||||
|
code_taxe = getattr(doc_source, 'DO_CodeTaxe1', None)
|
||||||
|
if code_taxe is not None:
|
||||||
|
doc_cible.DO_CodeTaxe1 = code_taxe
|
||||||
|
logger.info(f"[TRANSFORM] Code taxe: {code_taxe}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"[TRANSFORM] Code taxe non defini: {e}")
|
||||||
|
|
||||||
|
# 7. Écrire le document avec tous les champs
|
||||||
|
logger.info("[TRANSFORM] Ecriture document avec champs obligatoires...")
|
||||||
|
doc_cible.Write()
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# PARTIE 3 : AMÉLIORATION ÉTAPE 9 - Validation avec diagnostic détaillé
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
# ========================================
|
# ========================================
|
||||||
# ÉTAPE 9 : VALIDER LE DOCUMENT
|
# ÉTAPE 9 : VALIDER LE DOCUMENT
|
||||||
# ========================================
|
# ========================================
|
||||||
logger.info("[TRANSFORM] Validation document cible...")
|
logger.info("[TRANSFORM] Validation document cible...")
|
||||||
|
|
||||||
doc_cible.Write()
|
# Relire pour vérifier que tout est OK
|
||||||
|
doc_cible.Read()
|
||||||
|
|
||||||
|
# Diagnostic pré-validation
|
||||||
|
logger.info("[TRANSFORM] === PRE-VALIDATION CHECK ===")
|
||||||
|
champs_critiques = {
|
||||||
|
"Type": getattr(doc_cible, 'DO_Type', '?'),
|
||||||
|
"Client": getattr(doc_cible, 'CT_Num', '?'),
|
||||||
|
"Date": getattr(doc_cible, 'DO_Date', '?'),
|
||||||
|
"Mode reglement": getattr(doc_cible, 'DO_ModeRegl', '?'),
|
||||||
|
"Code journal": getattr(doc_cible, 'DO_CodeJournal', '?'),
|
||||||
|
"Souche": getattr(doc_cible, 'DO_Souche', '?'),
|
||||||
|
"Statut": getattr(doc_cible, 'DO_Statut', '?'),
|
||||||
|
}
|
||||||
|
|
||||||
|
for nom, valeur in champs_critiques.items():
|
||||||
|
logger.info(f" {nom}: {valeur}")
|
||||||
|
|
||||||
|
# Vérifier que les champs critiques ne sont pas vides
|
||||||
|
champs_manquants = []
|
||||||
|
if not getattr(doc_cible, 'CT_Num', None):
|
||||||
|
champs_manquants.append("Client (CT_Num)")
|
||||||
|
if type_cible == 60: # Facture
|
||||||
|
if not getattr(doc_cible, 'DO_CodeJournal', None):
|
||||||
|
champs_manquants.append("Code journal (DO_CodeJournal)")
|
||||||
|
if getattr(doc_cible, 'DO_ModeRegl', None) is None:
|
||||||
|
champs_manquants.append("Mode reglement (DO_ModeRegl)")
|
||||||
|
|
||||||
|
if champs_manquants:
|
||||||
|
erreur = f"Champs obligatoires manquants: {', '.join(champs_manquants)}"
|
||||||
|
logger.error(f"[TRANSFORM] {erreur}")
|
||||||
|
raise ValueError(erreur)
|
||||||
|
|
||||||
|
# Lancer le processus
|
||||||
try:
|
try:
|
||||||
|
logger.info("[TRANSFORM] Appel Process()...")
|
||||||
process.Process()
|
process.Process()
|
||||||
logger.info("[TRANSFORM] Document cible valide")
|
logger.info("[TRANSFORM] Document cible valide avec succes")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[TRANSFORM] Erreur Process(): {e}")
|
logger.error(f"[TRANSFORM] ERREUR Process(): {e}")
|
||||||
|
logger.error("[TRANSFORM] === DIAGNOSTIC COMPLET ===")
|
||||||
# Diagnostic détaillé
|
|
||||||
|
# Afficher TOUS les attributs du document
|
||||||
try:
|
try:
|
||||||
logger.info("[TRANSFORM] === DIAGNOSTIC DOCUMENT ===")
|
for attr in dir(doc_cible):
|
||||||
logger.info(f"Type: {getattr(doc_cible, 'DO_Type', '?')}")
|
if attr.startswith('DO_') or attr.startswith('CT_'):
|
||||||
logger.info(f"Client: {getattr(doc_cible, 'CT_Num', '?')}")
|
try:
|
||||||
logger.info(f"Date: {getattr(doc_cible, 'DO_Date', '?')}")
|
valeur = getattr(doc_cible, attr, 'N/A')
|
||||||
logger.info(
|
logger.error(f" {attr}: {valeur}")
|
||||||
f"Mode reglement: {getattr(doc_cible, 'DO_ModeRegl', '?')}"
|
except:
|
||||||
)
|
pass
|
||||||
logger.info(
|
|
||||||
f"Code journal: {getattr(doc_cible, 'DO_CodeJournal', '?')}"
|
|
||||||
)
|
|
||||||
logger.info(
|
|
||||||
f"Souche: {getattr(doc_cible, 'DO_Souche', '?')}"
|
|
||||||
)
|
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# ========================================
|
# ========================================
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue