Unified document's modification function
This commit is contained in:
parent
f80ad1adee
commit
57103d406d
3 changed files with 361 additions and 2358 deletions
2395
sage_connector.py
2395
sage_connector.py
File diff suppressed because it is too large
Load diff
|
|
@ -383,10 +383,302 @@ def _relire_document_final(
|
|||
return resultat
|
||||
|
||||
|
||||
def modifier_document_vente(
|
||||
self, numero: str, doc_data: dict, type_document: TypeDocumentVente
|
||||
) -> Dict:
|
||||
"""
|
||||
Méthode unifiée de modification de documents de vente
|
||||
RÉUTILISE les mêmes sous-méthodes que la création
|
||||
"""
|
||||
if not self.cial:
|
||||
raise RuntimeError("Connexion Sage non établie")
|
||||
|
||||
config = ConfigDocument(type_document)
|
||||
logger.info(f"📝 === MODIFICATION {config.nom_document.upper()} {numero} ===")
|
||||
|
||||
try:
|
||||
with self._com_context(), self._lock_com:
|
||||
# ==========================================
|
||||
# 1. CHARGEMENT DOCUMENT
|
||||
# ==========================================
|
||||
logger.info("📂 Chargement document...")
|
||||
factory = self.cial.FactoryDocumentVente
|
||||
persist = None
|
||||
|
||||
for type_test in [config.type_sage, 50]:
|
||||
try:
|
||||
persist_test = factory.ReadPiece(type_test, numero)
|
||||
if persist_test:
|
||||
persist = persist_test
|
||||
logger.info(f" ✓ Document trouvé (type={type_test})")
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not persist:
|
||||
raise ValueError(
|
||||
f"{config.nom_document.capitalize()} {numero} introuvable"
|
||||
)
|
||||
|
||||
doc = win32com.client.CastTo(persist, "IBODocumentVente3")
|
||||
doc.Read()
|
||||
|
||||
# Vérifications
|
||||
statut_actuel = getattr(doc, "DO_Statut", 0)
|
||||
|
||||
if statut_actuel == 5:
|
||||
raise ValueError(f"Le {config.nom_document} a déjà été transformé")
|
||||
if statut_actuel == 6:
|
||||
raise ValueError(f"Le {config.nom_document} est annulé")
|
||||
|
||||
# ==========================================
|
||||
# 2. SAUVEGARDE CLIENT INITIAL
|
||||
# ==========================================
|
||||
client_code_initial = ""
|
||||
try:
|
||||
client_obj = getattr(doc, "Client", None)
|
||||
if client_obj:
|
||||
client_obj.Read()
|
||||
client_code_initial = getattr(client_obj, "CT_Num", "").strip()
|
||||
logger.info(f" Client initial: {client_code_initial}")
|
||||
except Exception as e:
|
||||
logger.error(f" Erreur lecture client: {e}")
|
||||
|
||||
if not client_code_initial:
|
||||
raise ValueError("Client introuvable dans le document")
|
||||
|
||||
# ==========================================
|
||||
# 3. COMPTAGE LIGNES EXISTANTES
|
||||
# ==========================================
|
||||
nb_lignes_initial = 0
|
||||
try:
|
||||
factory_lignes = getattr(doc, "FactoryDocumentLigne", None) or getattr(
|
||||
doc, "FactoryDocumentVenteLigne", None
|
||||
)
|
||||
index = 1
|
||||
while index <= 100:
|
||||
try:
|
||||
ligne_p = factory_lignes.List(index)
|
||||
if ligne_p is None:
|
||||
break
|
||||
nb_lignes_initial += 1
|
||||
index += 1
|
||||
except:
|
||||
break
|
||||
logger.info(f" Lignes existantes: {nb_lignes_initial}")
|
||||
except Exception as e:
|
||||
logger.warning(f" Erreur comptage lignes: {e}")
|
||||
|
||||
# ==========================================
|
||||
# 4. ANALYSE MODIFICATIONS
|
||||
# ==========================================
|
||||
champs_modifies = []
|
||||
|
||||
modif_date = config.champ_date_principale in doc_data
|
||||
modif_date_sec = (
|
||||
config.champ_date_secondaire
|
||||
and config.champ_date_secondaire in doc_data
|
||||
)
|
||||
modif_statut = "statut" in doc_data
|
||||
modif_ref = "reference" in doc_data
|
||||
modif_lignes = "lignes" in doc_data and doc_data["lignes"] is not None
|
||||
|
||||
logger.info("📋 Modifications demandées:")
|
||||
logger.info(f" {config.champ_date_principale}: {modif_date}")
|
||||
if config.champ_date_secondaire:
|
||||
logger.info(f" {config.champ_date_secondaire}: {modif_date_sec}")
|
||||
logger.info(f" Statut: {modif_statut}")
|
||||
logger.info(f" Référence: {modif_ref}")
|
||||
logger.info(f" Lignes: {modif_lignes}")
|
||||
|
||||
# Reporter référence et statut après les lignes
|
||||
doc_data_temp = doc_data.copy()
|
||||
reference_a_modifier = None
|
||||
statut_a_modifier = None
|
||||
|
||||
if modif_lignes:
|
||||
if modif_ref:
|
||||
reference_a_modifier = doc_data_temp.pop("reference")
|
||||
modif_ref = False
|
||||
if modif_statut:
|
||||
statut_a_modifier = doc_data_temp.pop("statut")
|
||||
modif_statut = False
|
||||
|
||||
# ==========================================
|
||||
# 5. TEST WRITE BASIQUE
|
||||
# ==========================================
|
||||
logger.info("🔍 Test Write() basique...")
|
||||
try:
|
||||
doc.Write()
|
||||
doc.Read()
|
||||
logger.info(" ✓ Write() basique OK")
|
||||
except Exception as e:
|
||||
logger.error(f" ❌ Document verrouillé: {e}")
|
||||
raise ValueError(f"Document verrouillé: {e}")
|
||||
|
||||
# ==========================================
|
||||
# 6. MODIFICATIONS SIMPLES (sans lignes)
|
||||
# ==========================================
|
||||
if not modif_lignes and (
|
||||
modif_date or modif_date_sec or modif_statut or modif_ref
|
||||
):
|
||||
logger.info("📝 Modifications simples...")
|
||||
|
||||
if modif_date:
|
||||
doc.DO_Date = pywintypes.Time(
|
||||
normaliser_date(doc_data_temp.get(config.champ_date_principale))
|
||||
)
|
||||
champs_modifies.append(config.champ_date_principale)
|
||||
|
||||
if modif_date_sec:
|
||||
doc.DO_DateLivr = pywintypes.Time(
|
||||
normaliser_date(doc_data_temp[config.champ_date_secondaire])
|
||||
)
|
||||
champs_modifies.append(config.champ_date_secondaire)
|
||||
|
||||
if modif_statut:
|
||||
doc.DO_Statut = doc_data_temp["statut"]
|
||||
champs_modifies.append("statut")
|
||||
|
||||
if modif_ref:
|
||||
doc.DO_Ref = doc_data_temp["reference"]
|
||||
champs_modifies.append("reference")
|
||||
|
||||
# 🔥 CONFIGURATION SPÉCIFIQUE FACTURE
|
||||
if type_document == TypeDocumentVente.FACTURE:
|
||||
self._configurer_facture(doc)
|
||||
|
||||
doc.Write()
|
||||
logger.info(" ✓ Modifications appliquées")
|
||||
|
||||
# ==========================================
|
||||
# 7. REMPLACEMENT LIGNES
|
||||
# ==========================================
|
||||
elif modif_lignes:
|
||||
logger.info("🔄 REMPLACEMENT COMPLET DES LIGNES...")
|
||||
|
||||
# Dates
|
||||
if modif_date:
|
||||
doc.DO_Date = pywintypes.Time(
|
||||
normaliser_date(doc_data_temp.get(config.champ_date_principale))
|
||||
)
|
||||
champs_modifies.append(config.champ_date_principale)
|
||||
|
||||
if modif_date_sec:
|
||||
doc.DO_DateLivr = pywintypes.Time(
|
||||
normaliser_date(doc_data_temp[config.champ_date_secondaire])
|
||||
)
|
||||
champs_modifies.append(config.champ_date_secondaire)
|
||||
|
||||
# 🔥 CONFIGURATION SPÉCIFIQUE FACTURE (avant lignes)
|
||||
if type_document == TypeDocumentVente.FACTURE:
|
||||
self._configurer_facture(doc)
|
||||
|
||||
nouvelles_lignes = doc_data["lignes"]
|
||||
nb_nouvelles = len(nouvelles_lignes)
|
||||
|
||||
logger.info(f" {nb_lignes_initial} → {nb_nouvelles} lignes")
|
||||
|
||||
try:
|
||||
factory_lignes = doc.FactoryDocumentLigne
|
||||
except:
|
||||
factory_lignes = doc.FactoryDocumentVenteLigne
|
||||
|
||||
factory_article = self.cial.FactoryArticle
|
||||
|
||||
# Suppression lignes existantes
|
||||
if nb_lignes_initial > 0:
|
||||
logger.info(f" 🗑️ Suppression {nb_lignes_initial} lignes...")
|
||||
for idx in range(nb_lignes_initial, 0, -1):
|
||||
try:
|
||||
ligne_p = factory_lignes.List(idx)
|
||||
if ligne_p:
|
||||
ligne = win32com.client.CastTo(
|
||||
ligne_p, "IBODocumentLigne3"
|
||||
)
|
||||
ligne.Read()
|
||||
ligne.Remove()
|
||||
except Exception as e:
|
||||
logger.warning(f" Ligne {idx}: {e}")
|
||||
logger.info(" ✓ Lignes supprimées")
|
||||
|
||||
# Ajout nouvelles lignes avec REMISES
|
||||
logger.info(f" ➕ Ajout {nb_nouvelles} lignes...")
|
||||
for idx, ligne_data in enumerate(nouvelles_lignes, 1):
|
||||
# 🔥 UTILISE _ajouter_ligne_document qui applique les remises
|
||||
_ajouter_ligne_document(
|
||||
cial=self.cial,
|
||||
factory_lignes=factory_lignes,
|
||||
factory_article=factory_article,
|
||||
ligne_data=ligne_data,
|
||||
idx=idx,
|
||||
doc=doc,
|
||||
)
|
||||
|
||||
logger.info(" ✓ Nouvelles lignes ajoutées avec remises")
|
||||
|
||||
doc.Write()
|
||||
time.sleep(0.5)
|
||||
doc.Read()
|
||||
|
||||
champs_modifies.append("lignes")
|
||||
|
||||
# ==========================================
|
||||
# 8. MODIFICATIONS REPORTÉES
|
||||
# ==========================================
|
||||
if reference_a_modifier is not None:
|
||||
try:
|
||||
logger.info(
|
||||
f" 📝 Modification référence: '{reference_a_modifier}'"
|
||||
)
|
||||
doc.DO_Ref = (
|
||||
str(reference_a_modifier) if reference_a_modifier else ""
|
||||
)
|
||||
doc.Write()
|
||||
time.sleep(0.5)
|
||||
doc.Read()
|
||||
champs_modifies.append("reference")
|
||||
except Exception as e:
|
||||
logger.warning(f" Référence: {e}")
|
||||
|
||||
if statut_a_modifier is not None:
|
||||
try:
|
||||
logger.info(f" 📝 Modification statut: {statut_a_modifier}")
|
||||
doc.DO_Statut = int(statut_a_modifier)
|
||||
doc.Write()
|
||||
time.sleep(0.5)
|
||||
doc.Read()
|
||||
champs_modifies.append("statut")
|
||||
except Exception as e:
|
||||
logger.warning(f" Statut: {e}")
|
||||
|
||||
# ==========================================
|
||||
# 9. RELECTURE FINALE
|
||||
# ==========================================
|
||||
resultat = self._relire_document_final(config, numero, doc_data)
|
||||
resultat["champs_modifies"] = champs_modifies
|
||||
|
||||
logger.info(f"✅ {config.nom_document.upper()} {numero} MODIFIÉ")
|
||||
logger.info(
|
||||
f" Totaux: {resultat['total_ht']}€ HT / {resultat['total_ttc']}€ TTC"
|
||||
)
|
||||
logger.info(f" Champs modifiés: {champs_modifies}")
|
||||
|
||||
return resultat
|
||||
|
||||
except ValueError as e:
|
||||
logger.error(f"❌ ERREUR MÉTIER: {e}")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"❌ ERREUR TECHNIQUE: {e}", exc_info=True)
|
||||
raise RuntimeError(f"Erreur Sage: {str(e)}")
|
||||
|
||||
|
||||
__all__ = [
|
||||
"creer_document_vente",
|
||||
"_ajouter_ligne_document",
|
||||
"_configurer_facture",
|
||||
"_recuperer_numero_document",
|
||||
"_relire_document_final",
|
||||
"modifier_document_vente",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -123,6 +123,37 @@ def _row_to_collaborateur_dict(row) -> Optional[dict]:
|
|||
}
|
||||
|
||||
|
||||
def row_to_collaborateur_dict(row):
|
||||
"""Convertit une ligne SQL en dictionnaire collaborateur"""
|
||||
return {
|
||||
"numero": row.CO_No,
|
||||
"nom": _safe_strip(row.CO_Nom), # ⚠️ Utiliser _safe_strip
|
||||
"prenom": _safe_strip(row.CO_Prenom),
|
||||
"fonction": _safe_strip(row.CO_Fonction),
|
||||
"adresse": _safe_strip(row.CO_Adresse),
|
||||
"complement": _safe_strip(row.CO_Complement),
|
||||
"code_postal": _safe_strip(row.CO_CodePostal),
|
||||
"ville": _safe_strip(row.CO_Ville),
|
||||
"code_region": _safe_strip(row.CO_CodeRegion),
|
||||
"pays": _safe_strip(row.CO_Pays),
|
||||
"service": _safe_strip(row.CO_Service),
|
||||
"vendeur": bool(row.CO_Vendeur),
|
||||
"caissier": bool(row.CO_Caissier),
|
||||
"acheteur": bool(row.CO_Acheteur),
|
||||
"telephone": _safe_strip(row.CO_Telephone),
|
||||
"telecopie": _safe_strip(row.CO_Telecopie),
|
||||
"email": _safe_strip(row.CO_EMail),
|
||||
"tel_portable": _safe_strip(row.CO_TelPortable),
|
||||
"matricule": _safe_strip(row.CO_Matricule),
|
||||
"facebook": _safe_strip(row.CO_Facebook),
|
||||
"linkedin": _safe_strip(row.CO_LinkedIn),
|
||||
"skype": _safe_strip(row.CO_Skype),
|
||||
"sommeil": bool(row.CO_Sommeil),
|
||||
"chef_ventes": bool(row.CO_ChefVentes),
|
||||
"numero_chef_ventes": row.CO_NoChefVentes if row.CO_NoChefVentes else None,
|
||||
}
|
||||
|
||||
|
||||
def _row_to_tiers_dict(row) -> dict:
|
||||
"""Convertit une ligne SQL en dictionnaire tiers"""
|
||||
tiers = {
|
||||
|
|
@ -218,4 +249,5 @@ __all__ = [
|
|||
"_contact_to_dict",
|
||||
"_row_to_contact_dict",
|
||||
"_row_to_tiers_dict",
|
||||
"row_to_collaborateur_dict"
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in a new issue