feat: Add functionality to list quotes, orders, and invoices, change quote status, read client discounts, and retrieve cache information.

This commit is contained in:
Fanilo-Nantenaina 2025-11-27 12:41:52 +03:00
parent 643250850b
commit 636e2a96a7

View file

@ -5,38 +5,63 @@ import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class SageGatewayClient: class SageGatewayClient:
""" """
Client HTTP pour communiquer avec la gateway Sage Windows Client HTTP pour communiquer avec la gateway Sage Windows
""" """
def __init__(self): def __init__(self):
self.url = settings.sage_gateway_url.rstrip("/") self.url = settings.sage_gateway_url.rstrip("/")
self.headers = { self.headers = {
"X-Sage-Token": settings.sage_gateway_token, "X-Sage-Token": settings.sage_gateway_token,
"Content-Type": "application/json" "Content-Type": "application/json",
} }
self.timeout = 30 self.timeout = 30
def _post(self, endpoint: str, data: dict = None, retries: int = 3) -> dict: def _post(self, endpoint: str, data: dict = None, retries: int = 3) -> dict:
"""POST avec retry automatique""" """POST avec retry automatique"""
import time import time
for attempt in range(retries): for attempt in range(retries):
try: try:
r = requests.post( r = requests.post(
f"{self.url}{endpoint}", f"{self.url}{endpoint}",
json=data or {}, json=data or {},
headers=self.headers, headers=self.headers,
timeout=self.timeout timeout=self.timeout,
) )
r.raise_for_status() r.raise_for_status()
return r.json() return r.json()
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
if attempt == retries - 1: if attempt == retries - 1:
logger.error(f"❌ Échec après {retries} tentatives sur {endpoint}: {e}") logger.error(
f"❌ Échec après {retries} tentatives sur {endpoint}: {e}"
)
raise raise
time.sleep(2 ** attempt) # Backoff exponentiel 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 === # === CLIENTS ===
def lister_clients(self, filtre: str = "") -> List[Dict]: def lister_clients(self, filtre: str = "") -> List[Dict]:
@ -59,34 +84,93 @@ class SageGatewayClient:
def lire_devis(self, numero: str) -> Optional[Dict]: def lire_devis(self, numero: str) -> Optional[Dict]:
return self._post("/sage/devis/get", {"code": numero}).get("data") 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 === # === DOCUMENTS ===
def lire_document(self, numero: str, type_doc: int) -> Optional[Dict]: 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") 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: def transformer_document(
return self._post("/sage/documents/transform", { self, numero_source: str, type_source: int, type_cible: int
"numero_source": numero_source, ) -> Dict:
"type_source": type_source, return self._post(
"type_cible": type_cible "/sage/documents/transform",
}).get("data", {}) {
"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: def mettre_a_jour_champ_libre(
resp = self._post("/sage/documents/champ-libre", { self, doc_id: str, type_doc: int, nom_champ: str, valeur: str
"doc_id": doc_id, ) -> bool:
"type_doc": type_doc, resp = self._post(
"nom_champ": nom_champ, "/sage/documents/champ-libre",
"valeur": valeur {
}) "doc_id": doc_id,
"type_doc": type_doc,
"nom_champ": nom_champ,
"valeur": valeur,
},
)
return resp.get("success", False) 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 === # === CONTACTS ===
def lire_contact_client(self, code_client: str) -> Optional[Dict]: def lire_contact_client(self, code_client: str) -> Optional[Dict]:
return self._post("/sage/contact/read", {"code": code_client}).get("data") 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 === # === CACHE ===
def refresh_cache(self) -> Dict: def refresh_cache(self) -> Dict:
return self._post("/sage/cache/refresh") 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 === # === HEALTH ===
def health(self) -> dict: def health(self) -> dict:
try: try:
@ -95,5 +179,6 @@ class SageGatewayClient:
except: except:
return {"status": "down"} return {"status": "down"}
# Instance globale # Instance globale
sage_client = SageGatewayClient() sage_client = SageGatewayClient()