From 9ffad8287dc3258339ea78addfcd2e36cc680c92 Mon Sep 17 00:00:00 2001 From: fanilo Date: Sun, 18 Jan 2026 09:31:09 +0100 Subject: [PATCH] Simple cleaning of some files --- .env.example | 7 +--- .gitignore | 4 -- main.py | 2 +- sage_connector.py | 54 -------------------------- schemas/articles/famille_d_articles.py | 7 +--- schemas/documents/reglements.py | 5 --- utils/articles/articles_data_sql.py | 10 ----- utils/documents/settle.py | 38 +++++++++++++++--- utils/documents/validations.py | 52 ++++--------------------- utils/functions/data/create_doc.py | 28 ------------- utils/tiers/tiers_data_sql.py | 1 - 11 files changed, 44 insertions(+), 164 deletions(-) diff --git a/.env.example b/.env.example index 4a78b08..41085af 100644 --- a/.env.example +++ b/.env.example @@ -1,14 +1,9 @@ -# ============================================================================ -# SAGE 100 CLOUD - CONNEXION BOI/COM -# ============================================================================ + CHEMIN_BASE= UTILISATEUR= MOT_DE_PASSE= SAGE_GATEWAY_TOKEN= -# ============================================================================ -# API - CONFIGURATION SERVEUR -# ============================================================================ API_HOST=0.0.0.0 API_PORT=8000 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7f792c9..711a83c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,3 @@ -# ================================ -# Python / FastAPI -# ================================ - # Environnements virtuels venv/ .env diff --git a/main.py b/main.py index cd370af..b6fc307 100644 --- a/main.py +++ b/main.py @@ -1491,7 +1491,7 @@ def regler_factures_client_endpoint(req: ReglementMultipleRequest): date_reglement=date_reg, reference=req.reference or "", libelle=req.libelle or "", - code_journal=req.code_journal, # None = auto + code_journal=req.code_journal, numeros_factures=req.numeros_factures, devise_code=req.devise_code, cours_devise=req.cours_devise, diff --git a/sage_connector.py b/sage_connector.py index 99195d8..a7ea568 100644 --- a/sage_connector.py +++ b/sage_connector.py @@ -3714,9 +3714,6 @@ class SageConnector: client = win32com.client.CastTo(persist, "IBOClient3") - # ============================================================ - # DEBUG TEMPORAIRE : Lister les méthodes disponibles - # ============================================================ methodes_client = [m for m in dir(client) if not m.startswith("_")] logger.info( f" [DEBUG] Méthodes disponibles sur client: {methodes_client}" @@ -3732,11 +3729,6 @@ class SageConnector: ] logger.info(f" [DEBUG] Méthodes lock/read/write: {lock_methods}") - # ============================================================ - # VERROUILLAGE : Tenter plusieurs approches - # ============================================================ - import time - max_retries = 3 retry_delay = 1.0 locked = False @@ -3811,9 +3803,6 @@ class SageConnector: f" Client chargé: {getattr(client, 'CT_Intitule', '')} (via {lock_method_used})" ) - # ============================================================ - # ETAPE 2: IDENTIFICATION - # ============================================================ logger.info("[ETAPE 2] IDENTIFICATION") if "intitule" in client_data: @@ -3871,9 +3860,6 @@ class SageConnector: ): champs_modifies.append("code_naf") - # ============================================================ - # ETAPE 3: ADRESSE - # ============================================================ adresse_keys = [ "contact", "adresse", @@ -3947,9 +3933,6 @@ class SageConnector: except Exception as e: logger.error(f" Adresse erreur: {e}") - # ============================================================ - # ETAPE 4: TELECOM - # ============================================================ telecom_keys = [ "telephone", "telecopie", @@ -4010,9 +3993,6 @@ class SageConnector: except Exception as e: logger.error(f" Telecom erreur: {e}") - # ============================================================ - # ETAPE 5: COMPTE GENERAL - # ============================================================ if "compte_general" in client_data: logger.info("[ETAPE 5] COMPTE GENERAL") @@ -4039,9 +4019,6 @@ class SageConnector: except Exception as e: logger.warning(f" CompteGPrinc erreur: {e}") - # ============================================================ - # ETAPE 6: CATEGORIES - # ============================================================ if ( "categorie_tarifaire" in client_data or "categorie_comptable" in client_data @@ -4086,9 +4063,6 @@ class SageConnector: except Exception as e: logger.warning(f" CatCompta erreur: {e}") - # ============================================================ - # ETAPE 7: TAUX - # ============================================================ taux_modifies = False for i in range(1, 5): key = f"taux{i:02d}" @@ -4101,9 +4075,6 @@ class SageConnector: if try_set_attribute(client, f"CT_Taux{i:02d}", val): champs_modifies.append(key) - # ============================================================ - # ETAPE 8: STATISTIQUES - # ============================================================ stat_modifies = False stat01 = client_data.get("statistique01") or client_data.get("secteur") @@ -4131,9 +4102,6 @@ class SageConnector: ): champs_modifies.append(key) - # ============================================================ - # ETAPE 9: COMMERCIAL - # ============================================================ commercial_keys = [ "encours_autorise", "assurance_credit", @@ -4185,9 +4153,6 @@ class SageConnector: except Exception as e: logger.warning(f" Collaborateur erreur: {e}") - # ============================================================ - # ETAPE 10: FACTURATION - # ============================================================ facturation_keys = [ "lettrage_auto", "est_actif", @@ -4250,9 +4215,6 @@ class SageConnector: ): champs_modifies.append(key) - # ============================================================ - # ETAPE 11: LOGISTIQUE - # ============================================================ logistique_keys = [ "priorite_livraison", "livraison_partielle", @@ -4277,9 +4239,6 @@ class SageConnector: ): champs_modifies.append(key) - # ============================================================ - # ETAPE 12: COMMENTAIRE - # ============================================================ if "commentaire" in client_data: logger.info("[ETAPE 12] COMMENTAIRE") if try_set_attribute( @@ -4289,9 +4248,6 @@ class SageConnector: ): champs_modifies.append("commentaire") - # ============================================================ - # ETAPE 13: ANALYTIQUE - # ============================================================ if "section_analytique" in client_data: logger.info("[ETAPE 13] ANALYTIQUE") if try_set_attribute( @@ -4301,9 +4257,6 @@ class SageConnector: ): champs_modifies.append("section_analytique") - # ============================================================ - # ETAPE 14: ORGANISATION & SURVEILLANCE - # ============================================================ organisation_keys = [ "mode_reglement_code", "surveillance_active", @@ -4411,9 +4364,6 @@ class SageConnector: ): champs_modifies.append("sv_resultat") - # ============================================================ - # VERIFICATION AVANT WRITE - # ============================================================ if not champs_modifies: logger.warning("Aucun champ à modifier") # Déverrouiller si nécessaire @@ -4432,10 +4382,6 @@ class SageConnector: for i, champ in enumerate(champs_modifies, 1): logger.info(f" {i}. {champ}") logger.info("=" * 80) - - # ============================================================ - # WRITE AVEC GESTION DU DEVERROUILLAGE - # ============================================================ try: client.Write() logger.info("[OK] Write réussi") diff --git a/schemas/articles/famille_d_articles.py b/schemas/articles/famille_d_articles.py index 5a6e60e..0eaffcf 100644 --- a/schemas/articles/famille_d_articles.py +++ b/schemas/articles/famille_d_articles.py @@ -1,10 +1,7 @@ +from pydantic import BaseModel, Field +from typing import Optional -from pydantic import BaseModel, Field, validator, EmailStr, field_validator -from typing import Optional, List, Dict -from enum import Enum, IntEnum -from datetime import datetime, date - class FamilleCreate(BaseModel): """Modèle pour créer une famille d'articles""" diff --git a/schemas/documents/reglements.py b/schemas/documents/reglements.py index ae8faa4..21eb1f9 100644 --- a/schemas/documents/reglements.py +++ b/schemas/documents/reglements.py @@ -1,8 +1,3 @@ -""" -Schémas Pydantic pour les règlements de factures -Module: schemas/reglements.py -""" - from pydantic import BaseModel, Field, field_validator from typing import List, Optional from datetime import datetime diff --git a/utils/articles/articles_data_sql.py b/utils/articles/articles_data_sql.py index d2642a0..fbaa083 100644 --- a/utils/articles/articles_data_sql.py +++ b/utils/articles/articles_data_sql.py @@ -929,10 +929,6 @@ def enrichir_conditionnements(articles: List[Dict], cursor) -> List[Dict]: def _mapper_article_depuis_row(row_data: Dict, colonnes_config: Dict) -> Dict: - """ - Mappe les données brutes de la DB vers le format ArticleResponse - avec normalisation des types et génération des libellés - """ article = {} def get_val(sql_col, default=None, convert_type=None): @@ -1492,12 +1488,6 @@ def enrichir_tva_articles(articles: List[Dict], cursor) -> List[Dict]: return articles -def _get_type_article_libelle(type_val: int) -> str: - """Retourne le libellé du type d'article""" - types = {0: "Article", 1: "Prestation", 2: "Divers / Frais", 3: "Nomenclature"} - return types.get(type_val, f"Type {type_val}") - - def _cast_article(persist_obj): try: obj = win32com.client.CastTo(persist_obj, "IBOArticle3") diff --git a/utils/documents/settle.py b/utils/documents/settle.py index 748afda..becfe8d 100644 --- a/utils/documents/settle.py +++ b/utils/documents/settle.py @@ -1369,43 +1369,66 @@ def regler_factures_client( reference="", libelle="", numeros_factures=None, + code_journal=None, + devise_code=0, + cours_devise=1.0, + tva_encaissement=False, ): + """Règle plusieurs factures d'un client""" if not self.cial: raise RuntimeError("Connexion Sage non établie") if montant_total <= 0: raise ValueError("Le montant total doit être positif") + date_reglement = date_reglement or datetime.now() + + # Déduction automatique du journal si non fourni + if not code_journal: + code_journal = _get_journal_auto(self, mode_reglement) + factures = _get_factures_non_soldees_client_sql(self, client_code, numeros_factures) if not factures: raise ValueError(f"Aucune facture à régler pour {client_code}") + solde_total = sum(f["solde"] for f in factures) if montant_total > solde_total + 0.01: raise ValueError( f"Montant ({montant_total}€) supérieur au solde ({solde_total:.2f}€)" ) + reglements = [] restant = montant_total + for fac in factures: if restant < 0.01: break + a_regler = min(restant, fac["solde"]) + try: res = regler_facture( self, fac["numero"], a_regler, - mode_reglement, - date_reglement, - reference, - libelle, + mode_reglement=mode_reglement, + date_reglement=date_reglement, + reference=reference, + libelle=libelle, + code_journal=code_journal, + devise_code=devise_code, + cours_devise=cours_devise, + tva_encaissement=tva_encaissement, + compte_general=None, # Optionnel, peut être ajouté si nécessaire ) reglements.append(res) restant -= a_regler except Exception as e: - logger.error(f"Erreur {fac['numero']}: {e}") + logger.error(f"Erreur règlement {fac['numero']}: {e}") break + if not reglements: raise RuntimeError("Aucun règlement effectué") + return { "client_code": client_code, "montant_demande": montant_total, @@ -1416,6 +1439,11 @@ def regler_factures_client( "mode_reglement": mode_reglement, "mode_reglement_libelle": ModeReglement.get_libelle(mode_reglement), "reference": reference, + "libelle": libelle, + "code_journal": code_journal, + "devise_code": devise_code, + "cours_devise": cours_devise, + "tva_encaissement": tva_encaissement, "reglements": reglements, } diff --git a/utils/documents/validations.py b/utils/documents/validations.py index b2c3fcc..5588dd4 100644 --- a/utils/documents/validations.py +++ b/utils/documents/validations.py @@ -1,22 +1,10 @@ -""" -Validation de factures Sage 100c -Module: utils/documents/validation.py (Windows Server) - -Version diagnostic - Introspection complète pour trouver la méthode de validation -""" - -from typing import Dict, List +from typing import Dict import win32com.client import logging logger = logging.getLogger(__name__) -# ============================================================ -# FONCTIONS SQL (LECTURE) -# ============================================================ - - def get_statut_validation(connector, numero_facture: str) -> Dict: """Retourne le statut de validation d'une facture (SQL)""" with connector._get_sql_connection() as conn: @@ -95,16 +83,7 @@ def _get_facture_info_sql(connector, numero_facture: str) -> Dict: } -# ============================================================ -# INTROSPECTION COMPLÈTE -# ============================================================ - - def introspecter_document_complet(connector, numero_facture: str) -> Dict: - """ - Introspection COMPLÈTE d'un document pour découvrir toutes les méthodes - et propriétés disponibles. - """ if not connector.cial: raise RuntimeError("Connexion Sage non établie") @@ -174,7 +153,7 @@ def introspecter_document_complet(connector, numero_facture: str) -> Dict: val = getattr(doc, attr, None) if callable(val): result["IBODocumentVente3"]["methods"].append(attr) - except: + except Exception: pass # Chercher DO_* properties @@ -249,9 +228,6 @@ def introspecter_document_complet(connector, numero_facture: str) -> Dict: def introspecter_validation(connector, numero_facture: str = None) -> Dict: - """ - Introspection pour découvrir les méthodes de validation. - """ if not connector.cial: raise RuntimeError("Connexion Sage non établie") @@ -292,7 +268,7 @@ def introspecter_validation(connector, numero_facture: str = None) -> Dict: def valider_facture(connector, numero_facture: str) -> Dict: - logger.info(f"🔒 Validation facture {numero_facture} (SQL direct)") + logger.info(f" Validation facture {numero_facture} (SQL direct)") # Vérifications préalables with connector._get_sql_connection() as conn: @@ -357,13 +333,10 @@ def valider_facture(connector, numero_facture: str) -> Dict: def devalider_facture(connector, numero_facture: str) -> Dict: - """ - Dévalide une facture (retire le cadenas) - """ if not connector.cial: raise RuntimeError("Connexion Sage non établie") - logger.info(f"🔓 Dévalidation facture {numero_facture}") + logger.info(f" Dévalidation facture {numero_facture}") info = _get_facture_info_sql(connector, numero_facture) if not info: @@ -395,7 +368,7 @@ def devalider_facture(connector, numero_facture: str) -> Dict: if not success: raise RuntimeError("La dévalidation COM a échoué") - logger.info(f"✅ Facture {numero_facture} dévalidée") + logger.info(f" Facture {numero_facture} dévalidée") except ValueError: raise @@ -415,9 +388,6 @@ def devalider_facture(connector, numero_facture: str) -> Dict: def _valider_document_com(connector, numero_facture: str, valider: bool = True) -> bool: - """ - Tente de valider/dévalider un document via COM. - """ erreurs = [] action = "validation" if valider else "dévalidation" valeur_cible = 1 if valider else 0 @@ -452,7 +422,7 @@ def _valider_document_com(connector, numero_facture: str, valider: bool = True) logger.info(f" DO_Valide après: {valeur_apres}") if valeur_apres == valeur_cible: - logger.info(f" ✅ DO_Valide modifié avec succès!") + logger.info(" DO_Valide modifié avec succès!") return True else: erreurs.append( @@ -534,9 +504,6 @@ def _valider_document_com(connector, numero_facture: str, valider: bool = True) def explorer_toutes_interfaces_validation(connector, numero_facture: str) -> Dict: """Explorer TOUTES les interfaces possibles pour trouver un setter DO_Valide""" - import win32com.client - import pythoncom - result = {"numero_facture": numero_facture, "interfaces": {}} with connector._com_context(), connector._lock_com: @@ -597,17 +564,12 @@ def explorer_toutes_interfaces_validation(connector, numero_facture: str) -> Dic for a in factory_attrs if any(x in a.lower() for x in ["valid", "lock", "confirm", "imprim"]) ] - except: + except Exception: pass return result -# ============================================================ -# FONCTIONS UTILITAIRES -# ============================================================ - - def _build_response_sql( connector, numero_facture: str, deja_valide: bool, action: str ) -> Dict: diff --git a/utils/functions/data/create_doc.py b/utils/functions/data/create_doc.py index 0dff2fc..2cc2275 100644 --- a/utils/functions/data/create_doc.py +++ b/utils/functions/data/create_doc.py @@ -425,9 +425,6 @@ def modifier_document_vente( try: with self._com_context(), self._lock_com: - # ========================================== - # 1. CHARGEMENT DOCUMENT - # ========================================== logger.info("📂 Chargement document...") factory = self.cial.FactoryDocumentVente persist = None @@ -458,9 +455,6 @@ def modifier_document_vente( 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) @@ -474,9 +468,6 @@ def modifier_document_vente( 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( @@ -496,9 +487,6 @@ def modifier_document_vente( 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 @@ -531,9 +519,6 @@ def modifier_document_vente( statut_a_modifier = doc_data_temp.pop("statut") modif_statut = False - # ========================================== - # 5. TEST WRITE BASIQUE - # ========================================== logger.info("🔍 Test Write() basique...") try: doc.Write() @@ -543,9 +528,6 @@ def modifier_document_vente( 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 ): @@ -576,16 +558,12 @@ def modifier_document_vente( doc.DO_Ref = doc_data_temp["reference"] champs_modifies.append("reference") - # 🔥 CONFIGURATION SPÉCIFIQUE FACTURE if type_document == TypeDocumentVente.FACTURE: _configurer_facture(self, doc) doc.Write() logger.info(" ✓ Modifications appliquées") - # ========================================== - # 7. REMPLACEMENT LIGNES - # ========================================== elif modif_lignes: logger.info("🔄 REMPLACEMENT COMPLET DES LIGNES...") @@ -655,9 +633,6 @@ def modifier_document_vente( champs_modifies.append("lignes") - # ========================================== - # 8. MODIFICATIONS REPORTÉES - # ========================================== if reference_a_modifier is not None: try: logger.info( @@ -684,9 +659,6 @@ def modifier_document_vente( except Exception as e: logger.warning(f" Statut: {e}") - # ========================================== - # 9. RELECTURE FINALE - # ========================================== resultat = _relire_document_final( self, config, numero, doc_data, client_code_fallback=client_code_initial ) diff --git a/utils/tiers/tiers_data_sql.py b/utils/tiers/tiers_data_sql.py index 5b2becc..8873091 100644 --- a/utils/tiers/tiers_data_sql.py +++ b/utils/tiers/tiers_data_sql.py @@ -1,5 +1,4 @@ def _build_tiers_select_query(): - """Construit la partie SELECT de la requête avec tous les champs tiers + collaborateur""" return """ SELECT -- IDENTIFICATION TIERS (9)