diff --git a/main.py b/main.py index b378533..5fbe2a7 100644 --- a/main.py +++ b/main.py @@ -107,7 +107,26 @@ class FournisseurCreateRequest(BaseModel): siret: Optional[str] = None tva_intra: Optional[str] = None - + +class FournisseurCreateRequest(BaseModel): + intitule: str = Field(..., description="Raison sociale du fournisseur") + compte_collectif: str = Field("401000", description="Compte général rattaché") + num: Optional[str] = Field(None, description="Code fournisseur (auto si vide)") + adresse: Optional[str] = None + code_postal: Optional[str] = None + ville: Optional[str] = None + pays: Optional[str] = None + email: Optional[str] = None + telephone: Optional[str] = None + siret: Optional[str] = None + tva_intra: Optional[str] = None + + +class FournisseurUpdateGatewayRequest(BaseModel): + """Modèle pour modification fournisseur côté gateway""" + code: str + fournisseur_data: Dict + # ===================================================== # SÉCURITÉ # ===================================================== @@ -2636,6 +2655,22 @@ def create_fournisseur_endpoint(req: FournisseurCreateRequest): logger.error(f"❌ Erreur technique création fournisseur: {e}") raise HTTPException(500, str(e)) +@app.post("/sage/fournisseurs/update", dependencies=[Depends(verify_token)]) +def modifier_fournisseur_endpoint(req: FournisseurUpdateGatewayRequest): + """ + ✏️ Modification d'un fournisseur dans Sage + """ + try: + resultat = sage.modifier_fournisseur(req.code, req.fournisseur_data) + return {"success": True, "data": resultat} + + except ValueError as e: + logger.warning(f"Erreur métier modification fournisseur: {e}") + raise HTTPException(404, str(e)) + except Exception as e: + logger.error(f"Erreur technique modification fournisseur: {e}") + raise HTTPException(500, str(e)) + @app.post("/sage/fournisseurs/get", dependencies=[Depends(verify_token)]) def fournisseur_get(req: CodeRequest): """ diff --git a/sage_connector.py b/sage_connector.py index d65682a..bf4e52d 100644 --- a/sage_connector.py +++ b/sage_connector.py @@ -710,6 +710,212 @@ class SageConnector: raise RuntimeError(f"Erreur technique Sage: {error_message}") + def modifier_fournisseur(self, code: str, fournisseur_data: Dict) -> Dict: + """ + ✏️ Modification d'un fournisseur existant dans Sage 100c + + IMPORTANT: Utilise FactoryFournisseur.ReadNumero() pour charger le fournisseur + + Args: + code: Code du fournisseur à modifier + fournisseur_data: Dictionnaire avec les champs à mettre à jour + + Returns: + Fournisseur modifié + """ + if not self.cial: + raise RuntimeError("Connexion Sage non établie") + + try: + with self._com_context(), self._lock_com: + # ======================================== + # ÉTAPE 1 : CHARGER LE FOURNISSEUR EXISTANT + # ======================================== + logger.info(f"🔍 Recherche fournisseur {code}...") + + factory_fournisseur = self.cial.CptaApplication.FactoryFournisseur + persist = factory_fournisseur.ReadNumero(code) + + if not persist: + raise ValueError(f"Fournisseur {code} introuvable") + + fournisseur = self._cast_client(persist) # ✅ Réutiliser _cast_client + if not fournisseur: + raise ValueError(f"Impossible de charger le fournisseur {code}") + + logger.info(f"✅ Fournisseur {code} trouvé: {getattr(fournisseur, 'CT_Intitule', '')}") + + # ======================================== + # ÉTAPE 2 : METTRE À JOUR LES CHAMPS FOURNIS + # ======================================== + logger.info("📝 Mise à jour des champs...") + + champs_modifies = [] + + # Intitulé + if "intitule" in fournisseur_data: + intitule = str(fournisseur_data["intitule"])[:69].strip() + fournisseur.CT_Intitule = intitule + champs_modifies.append(f"intitule='{intitule}'") + + # Adresse + if any(k in fournisseur_data for k in ["adresse", "code_postal", "ville", "pays"]): + try: + adresse_obj = fournisseur.Adresse + + if "adresse" in fournisseur_data: + adresse = str(fournisseur_data["adresse"])[:35].strip() + adresse_obj.Adresse = adresse + champs_modifies.append("adresse") + + if "code_postal" in fournisseur_data: + cp = str(fournisseur_data["code_postal"])[:9].strip() + adresse_obj.CodePostal = cp + champs_modifies.append("code_postal") + + if "ville" in fournisseur_data: + ville = str(fournisseur_data["ville"])[:35].strip() + adresse_obj.Ville = ville + champs_modifies.append("ville") + + if "pays" in fournisseur_data: + pays = str(fournisseur_data["pays"])[:35].strip() + adresse_obj.Pays = pays + champs_modifies.append("pays") + + except Exception as e: + logger.warning(f"⚠️ Erreur mise à jour adresse: {e}") + + # Télécom + if "email" in fournisseur_data or "telephone" in fournisseur_data: + try: + telecom_obj = fournisseur.Telecom + + if "email" in fournisseur_data: + email = str(fournisseur_data["email"])[:69].strip() + telecom_obj.EMail = email + champs_modifies.append("email") + + if "telephone" in fournisseur_data: + tel = str(fournisseur_data["telephone"])[:21].strip() + telecom_obj.Telephone = tel + champs_modifies.append("telephone") + + except Exception as e: + logger.warning(f"⚠️ Erreur mise à jour télécom: {e}") + + # SIRET + if "siret" in fournisseur_data: + try: + siret = str(fournisseur_data["siret"])[:14].strip() + fournisseur.CT_Siret = siret + champs_modifies.append("siret") + except Exception as e: + logger.warning(f"⚠️ Erreur mise à jour SIRET: {e}") + + # TVA Intracommunautaire + if "tva_intra" in fournisseur_data: + try: + tva = str(fournisseur_data["tva_intra"])[:25].strip() + fournisseur.CT_Identifiant = tva + champs_modifies.append("tva_intra") + except Exception as e: + logger.warning(f"⚠️ Erreur mise à jour TVA: {e}") + + if not champs_modifies: + logger.warning("⚠️ Aucun champ à modifier") + # Retourner les données actuelles via extraction directe + return { + "numero": getattr(fournisseur, "CT_Num", "").strip(), + "intitule": getattr(fournisseur, "CT_Intitule", "").strip(), + "type": 1, + "est_fournisseur": True + } + + logger.info(f"📝 Champs à modifier: {', '.join(champs_modifies)}") + + # ======================================== + # ÉTAPE 3 : ÉCRIRE LES MODIFICATIONS + # ======================================== + logger.info("💾 Écriture des modifications...") + + try: + fournisseur.Write() + logger.info("✅ Write() réussi !") + + except Exception as e: + error_detail = str(e) + + try: + sage_error = self.cial.CptaApplication.LastError + if sage_error: + error_detail = f"{sage_error.Description} (Code: {sage_error.Number})" + except: + pass + + logger.error(f"❌ Erreur Write(): {error_detail}") + raise RuntimeError(f"Échec modification: {error_detail}") + + # ======================================== + # ÉTAPE 4 : RELIRE ET RETOURNER + # ======================================== + fournisseur.Read() + + logger.info(f"✅✅✅ FOURNISSEUR MODIFIÉ: {code} ({len(champs_modifies)} champs) ✅✅✅") + + # Extraction directe (comme lire_fournisseur) + numero = getattr(fournisseur, "CT_Num", "").strip() + intitule = getattr(fournisseur, "CT_Intitule", "").strip() + + data = { + "numero": numero, + "intitule": intitule, + "type": 1, + "est_fournisseur": True + } + + # Adresse + try: + adresse_obj = getattr(fournisseur, "Adresse", None) + if adresse_obj: + data["adresse"] = getattr(adresse_obj, "Adresse", "").strip() + data["code_postal"] = getattr(adresse_obj, "CodePostal", "").strip() + data["ville"] = getattr(adresse_obj, "Ville", "").strip() + except: + data["adresse"] = "" + data["code_postal"] = "" + data["ville"] = "" + + # Télécom + try: + telecom_obj = getattr(fournisseur, "Telecom", None) + if telecom_obj: + data["telephone"] = getattr(telecom_obj, "Telephone", "").strip() + data["email"] = getattr(telecom_obj, "EMail", "").strip() + except: + data["telephone"] = "" + data["email"] = "" + + return data + + except ValueError as e: + logger.error(f"❌ Erreur métier: {e}") + raise + + except Exception as e: + logger.error(f"❌ Erreur modification fournisseur: {e}", exc_info=True) + + error_message = str(e) + if self.cial: + try: + err = self.cial.CptaApplication.LastError + if err: + error_message = f"Erreur Sage: {err.Description}" + except: + pass + + raise RuntimeError(f"Erreur technique Sage: {error_message}") + def lire_fournisseur(self, code): """ ✅ NOUVEAU : Lecture d'un fournisseur par code