184 lines
6.2 KiB
Python
184 lines
6.2 KiB
Python
import requests
|
|
from typing import Dict, List, Optional
|
|
from config import settings
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class SageGatewayClient:
|
|
"""
|
|
Client HTTP pour communiquer avec la gateway Sage Windows
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.url = settings.sage_gateway_url.rstrip("/")
|
|
self.headers = {
|
|
"X-Sage-Token": settings.sage_gateway_token,
|
|
"Content-Type": "application/json",
|
|
}
|
|
self.timeout = 30
|
|
|
|
def _post(self, endpoint: str, data: dict = None, retries: int = 3) -> dict:
|
|
"""POST avec retry automatique"""
|
|
import time
|
|
|
|
for attempt in range(retries):
|
|
try:
|
|
r = requests.post(
|
|
f"{self.url}{endpoint}",
|
|
json=data or {},
|
|
headers=self.headers,
|
|
timeout=self.timeout,
|
|
)
|
|
r.raise_for_status()
|
|
return r.json()
|
|
except requests.exceptions.RequestException as e:
|
|
if attempt == retries - 1:
|
|
logger.error(
|
|
f"❌ Échec après {retries} tentatives sur {endpoint}: {e}"
|
|
)
|
|
raise
|
|
time.sleep(2**attempt)
|
|
|
|
def _get(self, endpoint: str, params: dict = None, retries: int = 3) -> dict:
|
|
"""GET avec retry automatique"""
|
|
import time
|
|
|
|
for attempt in range(retries):
|
|
try:
|
|
r = requests.get(
|
|
f"{self.url}{endpoint}",
|
|
params=params or {},
|
|
headers=self.headers,
|
|
timeout=self.timeout,
|
|
)
|
|
r.raise_for_status()
|
|
return r.json()
|
|
except requests.exceptions.RequestException as e:
|
|
if attempt == retries - 1:
|
|
logger.error(
|
|
f"❌ Échec GET après {retries} tentatives sur {endpoint}: {e}"
|
|
)
|
|
raise
|
|
time.sleep(2**attempt)
|
|
|
|
# === CLIENTS ===
|
|
def lister_clients(self, filtre: str = "") -> List[Dict]:
|
|
return self._post("/sage/clients/list", {"filtre": filtre}).get("data", [])
|
|
|
|
def lire_client(self, code: str) -> Optional[Dict]:
|
|
return self._post("/sage/clients/get", {"code": code}).get("data")
|
|
|
|
# === ARTICLES ===
|
|
def lister_articles(self, filtre: str = "") -> List[Dict]:
|
|
return self._post("/sage/articles/list", {"filtre": filtre}).get("data", [])
|
|
|
|
def lire_article(self, ref: str) -> Optional[Dict]:
|
|
return self._post("/sage/articles/get", {"code": ref}).get("data")
|
|
|
|
# === DEVIS ===
|
|
def creer_devis(self, devis_data: Dict) -> Dict:
|
|
return self._post("/sage/devis/create", devis_data).get("data", {})
|
|
|
|
def lire_devis(self, numero: str) -> Optional[Dict]:
|
|
return self._post("/sage/devis/get", {"code": numero}).get("data")
|
|
|
|
# 🆕 US-A1: Lister devis
|
|
def lister_devis(
|
|
self, limit: int = 100, statut: Optional[int] = None
|
|
) -> List[Dict]:
|
|
"""Liste tous les devis avec filtres"""
|
|
payload = {"limit": limit}
|
|
if statut is not None:
|
|
payload["statut"] = statut
|
|
return self._post("/sage/devis/list", payload).get("data", [])
|
|
|
|
# 🆕 US-A1: Changer statut devis
|
|
def changer_statut_devis(self, numero: str, nouveau_statut: int) -> Dict:
|
|
"""Change le statut d'un devis"""
|
|
return self._post(
|
|
"/sage/devis/statut", {"numero": numero, "nouveau_statut": nouveau_statut}
|
|
).get("data", {})
|
|
|
|
# === DOCUMENTS ===
|
|
def lire_document(self, numero: str, type_doc: int) -> Optional[Dict]:
|
|
return self._post(
|
|
"/sage/documents/get", {"numero": numero, "type_doc": type_doc}
|
|
).get("data")
|
|
|
|
def transformer_document(
|
|
self, numero_source: str, type_source: int, type_cible: int
|
|
) -> Dict:
|
|
return self._post(
|
|
"/sage/documents/transform",
|
|
{
|
|
"numero_source": numero_source,
|
|
"type_source": type_source,
|
|
"type_cible": type_cible,
|
|
},
|
|
).get("data", {})
|
|
|
|
def mettre_a_jour_champ_libre(
|
|
self, doc_id: str, type_doc: int, nom_champ: str, valeur: str
|
|
) -> bool:
|
|
resp = self._post(
|
|
"/sage/documents/champ-libre",
|
|
{
|
|
"doc_id": doc_id,
|
|
"type_doc": type_doc,
|
|
"nom_champ": nom_champ,
|
|
"valeur": valeur,
|
|
},
|
|
)
|
|
return resp.get("success", False)
|
|
|
|
# 🆕 US-A2: Lister commandes
|
|
def lister_commandes(
|
|
self, limit: int = 100, statut: Optional[int] = None
|
|
) -> List[Dict]:
|
|
"""Liste toutes les commandes"""
|
|
payload = {"limit": limit}
|
|
if statut is not None:
|
|
payload["statut"] = statut
|
|
return self._post("/sage/commandes/list", payload).get("data", [])
|
|
|
|
# 🆕 US-A7: Lister factures
|
|
def lister_factures(
|
|
self, limit: int = 100, statut: Optional[int] = None
|
|
) -> List[Dict]:
|
|
"""Liste toutes les factures"""
|
|
payload = {"limit": limit}
|
|
if statut is not None:
|
|
payload["statut"] = statut
|
|
return self._post("/sage/factures/list", payload).get("data", [])
|
|
|
|
# === CONTACTS ===
|
|
def lire_contact_client(self, code_client: str) -> Optional[Dict]:
|
|
return self._post("/sage/contact/read", {"code": code_client}).get("data")
|
|
|
|
# 🆕 US-A5: Lire remise max client
|
|
def lire_remise_max_client(self, code_client: str) -> float:
|
|
"""Récupère la remise max autorisée pour un client"""
|
|
result = self._post("/sage/client/remise-max", {"code": code_client})
|
|
return result.get("data", {}).get("remise_max", 10.0)
|
|
|
|
# === CACHE ===
|
|
def refresh_cache(self) -> Dict:
|
|
return self._post("/sage/cache/refresh")
|
|
|
|
def get_cache_info(self) -> Dict:
|
|
"""Récupère les infos du cache"""
|
|
return self._get("/sage/cache/info").get("data", {})
|
|
|
|
# === HEALTH ===
|
|
def health(self) -> dict:
|
|
try:
|
|
r = requests.get(f"{self.url}/health", timeout=5)
|
|
return r.json()
|
|
except:
|
|
return {"status": "down"}
|
|
|
|
|
|
# Instance globale
|
|
sage_client = SageGatewayClient()
|