feat: Add API endpoints and SageClient methods for managing prospects, suppliers, credit notes, and delivery notes.

This commit is contained in:
Fanilo-Nantenaina 2025-12-04 13:47:28 +03:00
parent a73bdc4d9e
commit b4a76579b8
2 changed files with 288 additions and 127 deletions

361
api.py
View file

@ -154,8 +154,10 @@ from datetime import datetime
# MODÈLES PYDANTIC POUR USERS # MODÈLES PYDANTIC POUR USERS
# ===================================================== # =====================================================
class UserResponse(BaseModel): class UserResponse(BaseModel):
"""Modèle de réponse pour un utilisateur""" """Modèle de réponse pour un utilisateur"""
id: str id: str
email: str email: str
nom: str nom: str
@ -166,10 +168,11 @@ class UserResponse(BaseModel):
created_at: str created_at: str
last_login: Optional[str] = None last_login: Optional[str] = None
failed_login_attempts: int = 0 failed_login_attempts: int = 0
class Config: class Config:
from_attributes = True from_attributes = True
# ===================================================== # =====================================================
# SERVICES EXTERNES (Universign) # SERVICES EXTERNES (Universign)
# ===================================================== # =====================================================
@ -300,7 +303,7 @@ async def lifespan(app: FastAPI):
# ✅ CORRECTION: Injecter session_factory ET sage_client dans email_queue # ✅ CORRECTION: Injecter session_factory ET sage_client dans email_queue
email_queue.session_factory = async_session_factory email_queue.session_factory = async_session_factory
email_queue.sage_client = sage_client email_queue.sage_client = sage_client
logger.info("✅ sage_client injecté dans email_queue") logger.info("✅ sage_client injecté dans email_queue")
# Démarrer queue # Démarrer queue
@ -1583,116 +1586,237 @@ async def statut_queue():
} }
# =====================================================
# ENDPOINTS - PROSPECTS
# =====================================================
@app.get("/prospects", tags=["Prospects"])
async def rechercher_prospects(query: Optional[str] = Query(None)):
"""🔍 Recherche prospects via gateway Windows"""
try:
prospects = sage_client.lister_prospects(filtre=query or "")
return prospects
except Exception as e:
logger.error(f"Erreur recherche prospects: {e}")
raise HTTPException(500, str(e))
@app.get("/prospects/{code}", tags=["Prospects"])
async def lire_prospect(code: str):
"""📄 Lecture d'un prospect par code"""
try:
prospect = sage_client.lire_prospect(code)
if not prospect:
raise HTTPException(404, f"Prospect {code} introuvable")
return prospect
except HTTPException:
raise
except Exception as e:
logger.error(f"Erreur lecture prospect: {e}")
raise HTTPException(500, str(e))
# =====================================================
# ENDPOINTS - FOURNISSEURS
# =====================================================
@app.get("/fournisseurs", tags=["Fournisseurs"])
async def rechercher_fournisseurs(query: Optional[str] = Query(None)):
"""🔍 Recherche fournisseurs via gateway Windows"""
try:
fournisseurs = sage_client.lister_fournisseurs(filtre=query or "")
return fournisseurs
except Exception as e:
logger.error(f"Erreur recherche fournisseurs: {e}")
raise HTTPException(500, str(e))
@app.get("/fournisseurs/{code}", tags=["Fournisseurs"])
async def lire_fournisseur(code: str):
"""📄 Lecture d'un fournisseur par code"""
try:
fournisseur = sage_client.lire_fournisseur(code)
if not fournisseur:
raise HTTPException(404, f"Fournisseur {code} introuvable")
return fournisseur
except HTTPException:
raise
except Exception as e:
logger.error(f"Erreur lecture fournisseur: {e}")
raise HTTPException(500, str(e))
# =====================================================
# ENDPOINTS - AVOIRS
# =====================================================
@app.get("/avoirs", tags=["Avoirs"])
async def lister_avoirs(
limit: int = Query(100, le=1000), statut: Optional[int] = Query(None)
):
"""📋 Liste tous les avoirs via gateway Windows"""
try:
avoirs = sage_client.lister_avoirs(limit=limit, statut=statut)
return avoirs
except Exception as e:
logger.error(f"Erreur liste avoirs: {e}")
raise HTTPException(500, str(e))
@app.get("/avoirs/{numero}", tags=["Avoirs"])
async def lire_avoir(numero: str):
"""📄 Lecture d'un avoir avec ses lignes"""
try:
avoir = sage_client.lire_avoir(numero)
if not avoir:
raise HTTPException(404, f"Avoir {numero} introuvable")
return avoir
except HTTPException:
raise
except Exception as e:
logger.error(f"Erreur lecture avoir: {e}")
raise HTTPException(500, str(e))
# =====================================================
# ENDPOINTS - LIVRAISONS
# =====================================================
@app.get("/livraisons", tags=["Livraisons"])
async def lister_livraisons(
limit: int = Query(100, le=1000), statut: Optional[int] = Query(None)
):
"""📋 Liste tous les bons de livraison via gateway Windows"""
try:
livraisons = sage_client.lister_livraisons(limit=limit, statut=statut)
return livraisons
except Exception as e:
logger.error(f"Erreur liste livraisons: {e}")
raise HTTPException(500, str(e))
@app.get("/livraisons/{numero}", tags=["Livraisons"])
async def lire_livraison(numero: str):
"""📄 Lecture d'une livraison avec ses lignes"""
try:
livraison = sage_client.lire_livraison(numero)
if not livraison:
raise HTTPException(404, f"Livraison {numero} introuvable")
return livraison
except HTTPException:
raise
except Exception as e:
logger.error(f"Erreur lecture livraison: {e}")
raise HTTPException(500, str(e))
@app.get("/debug/users", response_model=List[UserResponse], tags=["Debug"]) @app.get("/debug/users", response_model=List[UserResponse], tags=["Debug"])
async def lister_utilisateurs_debug( async def lister_utilisateurs_debug(
session: AsyncSession = Depends(get_session), session: AsyncSession = Depends(get_session),
limit: int = Query(100, le=1000), limit: int = Query(100, le=1000),
role: Optional[str] = Query(None), role: Optional[str] = Query(None),
verified_only: bool = Query(False) verified_only: bool = Query(False),
): ):
""" """
🔓 **ROUTE DEBUG** - Liste tous les utilisateurs inscrits 🔓 **ROUTE DEBUG** - Liste tous les utilisateurs inscrits
**ATTENTION**: Cette route n'est PAS protégée par authentification. **ATTENTION**: Cette route n'est PAS protégée par authentification.
À utiliser uniquement en développement ou à sécuriser en production. À utiliser uniquement en développement ou à sécuriser en production.
Args: Args:
limit: Nombre maximum d'utilisateurs à retourner limit: Nombre maximum d'utilisateurs à retourner
role: Filtrer par rôle (user, admin, commercial) role: Filtrer par rôle (user, admin, commercial)
verified_only: Afficher uniquement les utilisateurs vérifiés verified_only: Afficher uniquement les utilisateurs vérifiés
Returns: Returns:
Liste des utilisateurs avec leurs informations (mot de passe masqué) Liste des utilisateurs avec leurs informations (mot de passe masqué)
""" """
from database import User from database import User
from sqlalchemy import select from sqlalchemy import select
try: try:
# Construction de la requête # Construction de la requête
query = select(User) query = select(User)
# Filtres optionnels # Filtres optionnels
if role: if role:
query = query.where(User.role == role) query = query.where(User.role == role)
if verified_only: if verified_only:
query = query.where(User.is_verified == True) query = query.where(User.is_verified == True)
# Tri par date de création (plus récents en premier) # Tri par date de création (plus récents en premier)
query = query.order_by(User.created_at.desc()).limit(limit) query = query.order_by(User.created_at.desc()).limit(limit)
# Exécution # Exécution
result = await session.execute(query) result = await session.execute(query)
users = result.scalars().all() users = result.scalars().all()
# Conversion en réponse # Conversion en réponse
users_response = [] users_response = []
for user in users: for user in users:
users_response.append(UserResponse( users_response.append(
id=user.id, UserResponse(
email=user.email, id=user.id,
nom=user.nom, email=user.email,
prenom=user.prenom, nom=user.nom,
role=user.role, prenom=user.prenom,
is_verified=user.is_verified, role=user.role,
is_active=user.is_active, is_verified=user.is_verified,
created_at=user.created_at.isoformat() if user.created_at else "", is_active=user.is_active,
last_login=user.last_login.isoformat() if user.last_login else None, created_at=user.created_at.isoformat() if user.created_at else "",
failed_login_attempts=user.failed_login_attempts or 0 last_login=user.last_login.isoformat() if user.last_login else None,
)) failed_login_attempts=user.failed_login_attempts or 0,
)
logger.info(f"📋 Liste utilisateurs retournée: {len(users_response)} résultat(s)") )
logger.info(
f"📋 Liste utilisateurs retournée: {len(users_response)} résultat(s)"
)
return users_response return users_response
except Exception as e: except Exception as e:
logger.error(f"❌ Erreur liste utilisateurs: {e}") logger.error(f"❌ Erreur liste utilisateurs: {e}")
raise HTTPException(500, str(e)) raise HTTPException(500, str(e))
@app.get("/debug/users/stats", tags=["Debug"]) @app.get("/debug/users/stats", tags=["Debug"])
async def statistiques_utilisateurs( async def statistiques_utilisateurs(session: AsyncSession = Depends(get_session)):
session: AsyncSession = Depends(get_session)
):
""" """
📊 **ROUTE DEBUG** - Statistiques sur les utilisateurs 📊 **ROUTE DEBUG** - Statistiques sur les utilisateurs
Non protégée - à sécuriser en production Non protégée - à sécuriser en production
""" """
from database import User from database import User
from sqlalchemy import select, func from sqlalchemy import select, func
try: try:
# Total utilisateurs # Total utilisateurs
total_query = select(func.count(User.id)) total_query = select(func.count(User.id))
total_result = await session.execute(total_query) total_result = await session.execute(total_query)
total = total_result.scalar() total = total_result.scalar()
# Utilisateurs vérifiés # Utilisateurs vérifiés
verified_query = select(func.count(User.id)).where(User.is_verified == True) verified_query = select(func.count(User.id)).where(User.is_verified == True)
verified_result = await session.execute(verified_query) verified_result = await session.execute(verified_query)
verified = verified_result.scalar() verified = verified_result.scalar()
# Utilisateurs actifs # Utilisateurs actifs
active_query = select(func.count(User.id)).where(User.is_active == True) active_query = select(func.count(User.id)).where(User.is_active == True)
active_result = await session.execute(active_query) active_result = await session.execute(active_query)
active = active_result.scalar() active = active_result.scalar()
# Par rôle # Par rôle
roles_query = select(User.role, func.count(User.id)).group_by(User.role) roles_query = select(User.role, func.count(User.id)).group_by(User.role)
roles_result = await session.execute(roles_query) roles_result = await session.execute(roles_query)
roles_stats = {role: count for role, count in roles_result.all()} roles_stats = {role: count for role, count in roles_result.all()}
return { return {
"total_utilisateurs": total, "total_utilisateurs": total,
"utilisateurs_verifies": verified, "utilisateurs_verifies": verified,
"utilisateurs_actifs": active, "utilisateurs_actifs": active,
"utilisateurs_non_verifies": total - verified, "utilisateurs_non_verifies": total - verified,
"repartition_roles": roles_stats, "repartition_roles": roles_stats,
"taux_verification": f"{(verified/total*100):.1f}%" if total > 0 else "0%" "taux_verification": f"{(verified/total*100):.1f}%" if total > 0 else "0%",
} }
except Exception as e: except Exception as e:
logger.error(f"❌ Erreur stats utilisateurs: {e}") logger.error(f"❌ Erreur stats utilisateurs: {e}")
raise HTTPException(500, str(e)) raise HTTPException(500, str(e))
@ -1700,25 +1824,24 @@ async def statistiques_utilisateurs(
@app.get("/debug/users/{user_id}", response_model=UserResponse, tags=["Debug"]) @app.get("/debug/users/{user_id}", response_model=UserResponse, tags=["Debug"])
async def lire_utilisateur_debug( async def lire_utilisateur_debug(
user_id: str, user_id: str, session: AsyncSession = Depends(get_session)
session: AsyncSession = Depends(get_session)
): ):
""" """
👤 **ROUTE DEBUG** - Détails d'un utilisateur par ID 👤 **ROUTE DEBUG** - Détails d'un utilisateur par ID
Non protégée - à sécuriser en production Non protégée - à sécuriser en production
""" """
from database import User from database import User
from sqlalchemy import select from sqlalchemy import select
try: try:
query = select(User).where(User.id == user_id) query = select(User).where(User.id == user_id)
result = await session.execute(query) result = await session.execute(query)
user = result.scalar_one_or_none() user = result.scalar_one_or_none()
if not user: if not user:
raise HTTPException(404, f"Utilisateur {user_id} introuvable") raise HTTPException(404, f"Utilisateur {user_id} introuvable")
return UserResponse( return UserResponse(
id=user.id, id=user.id,
email=user.email, email=user.email,
@ -1729,9 +1852,9 @@ async def lire_utilisateur_debug(
is_active=user.is_active, is_active=user.is_active,
created_at=user.created_at.isoformat() if user.created_at else "", created_at=user.created_at.isoformat() if user.created_at else "",
last_login=user.last_login.isoformat() if user.last_login else None, last_login=user.last_login.isoformat() if user.last_login else None,
failed_login_attempts=user.failed_login_attempts or 0 failed_login_attempts=user.failed_login_attempts or 0,
) )
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
@ -1739,98 +1862,85 @@ async def lire_utilisateur_debug(
raise HTTPException(500, str(e)) raise HTTPException(500, str(e))
# À ajouter dans api.py dans la section Debug
@app.get("/debug/database/check", tags=["Debug"]) @app.get("/debug/database/check", tags=["Debug"])
async def verifier_integrite_database(session: AsyncSession = Depends(get_session)): async def verifier_integrite_database(session: AsyncSession = Depends(get_session)):
""" """
🔍 Vérification de l'intégrité de la base de données 🔍 Vérification de l'intégrité de la base de données
Retourne des statistiques détaillées sur toutes les tables Retourne des statistiques détaillées sur toutes les tables
""" """
from database import User, RefreshToken, LoginAttempt, EmailLog, SignatureLog from database import User, RefreshToken, LoginAttempt, EmailLog, SignatureLog
from sqlalchemy import func, text from sqlalchemy import func, text
try: try:
diagnostics = {} diagnostics = {}
# === TABLE USERS === # === TABLE USERS ===
# Compter tous les users # Compter tous les users
total_users = await session.execute(select(func.count(User.id))) total_users = await session.execute(select(func.count(User.id)))
diagnostics["users"] = { diagnostics["users"] = {"total": total_users.scalar(), "details": []}
"total": total_users.scalar(),
"details": []
}
# Lister tous les users avec détails # Lister tous les users avec détails
all_users = await session.execute(select(User)) all_users = await session.execute(select(User))
users_list = all_users.scalars().all() users_list = all_users.scalars().all()
for u in users_list: for u in users_list:
diagnostics["users"]["details"].append({ diagnostics["users"]["details"].append(
"id": u.id, {
"email": u.email, "id": u.id,
"nom": f"{u.prenom} {u.nom}", "email": u.email,
"role": u.role, "nom": f"{u.prenom} {u.nom}",
"is_active": u.is_active, "role": u.role,
"is_verified": u.is_verified, "is_active": u.is_active,
"created_at": u.created_at.isoformat() if u.created_at else None, "is_verified": u.is_verified,
"has_reset_token": u.reset_token is not None, "created_at": u.created_at.isoformat() if u.created_at else None,
"has_verification_token": u.verification_token is not None, "has_reset_token": u.reset_token is not None,
}) "has_verification_token": u.verification_token is not None,
}
)
# === TABLE REFRESH_TOKENS === # === TABLE REFRESH_TOKENS ===
total_tokens = await session.execute(select(func.count(RefreshToken.id))) total_tokens = await session.execute(select(func.count(RefreshToken.id)))
diagnostics["refresh_tokens"] = { diagnostics["refresh_tokens"] = {"total": total_tokens.scalar()}
"total": total_tokens.scalar()
}
# === TABLE LOGIN_ATTEMPTS === # === TABLE LOGIN_ATTEMPTS ===
total_attempts = await session.execute(select(func.count(LoginAttempt.id))) total_attempts = await session.execute(select(func.count(LoginAttempt.id)))
diagnostics["login_attempts"] = { diagnostics["login_attempts"] = {"total": total_attempts.scalar()}
"total": total_attempts.scalar()
}
# === TABLE EMAIL_LOGS === # === TABLE EMAIL_LOGS ===
total_emails = await session.execute(select(func.count(EmailLog.id))) total_emails = await session.execute(select(func.count(EmailLog.id)))
diagnostics["email_logs"] = { diagnostics["email_logs"] = {"total": total_emails.scalar()}
"total": total_emails.scalar()
}
# === TABLE SIGNATURE_LOGS === # === TABLE SIGNATURE_LOGS ===
total_signatures = await session.execute(select(func.count(SignatureLog.id))) total_signatures = await session.execute(select(func.count(SignatureLog.id)))
diagnostics["signature_logs"] = { diagnostics["signature_logs"] = {"total": total_signatures.scalar()}
"total": total_signatures.scalar()
}
# === VÉRIFIER LES FICHIERS SQLITE === # === VÉRIFIER LES FICHIERS SQLITE ===
import os import os
db_file = "sage_dataven.db" db_file = "sage_dataven.db"
diagnostics["database_file"] = { diagnostics["database_file"] = {
"exists": os.path.exists(db_file), "exists": os.path.exists(db_file),
"size_bytes": os.path.getsize(db_file) if os.path.exists(db_file) else 0, "size_bytes": os.path.getsize(db_file) if os.path.exists(db_file) else 0,
"path": os.path.abspath(db_file) "path": os.path.abspath(db_file),
} }
# === TESTER UNE REQUÊTE RAW SQL === # === TESTER UNE REQUÊTE RAW SQL ===
try: try:
raw_count = await session.execute(text("SELECT COUNT(*) FROM users")) raw_count = await session.execute(text("SELECT COUNT(*) FROM users"))
diagnostics["raw_sql_check"] = { diagnostics["raw_sql_check"] = {
"users_count": raw_count.scalar(), "users_count": raw_count.scalar(),
"status": "✅ Connexion DB OK" "status": "✅ Connexion DB OK",
} }
except Exception as e: except Exception as e:
diagnostics["raw_sql_check"] = { diagnostics["raw_sql_check"] = {"status": "❌ Erreur", "error": str(e)}
"status": "❌ Erreur",
"error": str(e)
}
return { return {
"success": True, "success": True,
"timestamp": datetime.now().isoformat(), "timestamp": datetime.now().isoformat(),
"diagnostics": diagnostics "diagnostics": diagnostics,
} }
except Exception as e: except Exception as e:
logger.error(f"❌ Erreur diagnostic DB: {e}", exc_info=True) logger.error(f"❌ Erreur diagnostic DB: {e}", exc_info=True)
raise HTTPException(500, f"Erreur diagnostic: {str(e)}") raise HTTPException(500, f"Erreur diagnostic: {str(e)}")
@ -1840,7 +1950,7 @@ async def verifier_integrite_database(session: AsyncSession = Depends(get_sessio
async def tester_persistance_utilisateur(session: AsyncSession = Depends(get_session)): async def tester_persistance_utilisateur(session: AsyncSession = Depends(get_session)):
""" """
🧪 Test de création/lecture/modification d'un utilisateur de test 🧪 Test de création/lecture/modification d'un utilisateur de test
Crée un utilisateur de test, le modifie, et vérifie la persistance Crée un utilisateur de test, le modifie, et vérifie la persistance
""" """
import uuid import uuid
@ -1849,7 +1959,7 @@ async def tester_persistance_utilisateur(session: AsyncSession = Depends(get_ses
try: try:
test_email = f"test_{uuid.uuid4().hex[:8]}@example.com" test_email = f"test_{uuid.uuid4().hex[:8]}@example.com"
# === ÉTAPE 1: CRÉATION === # === ÉTAPE 1: CRÉATION ===
test_user = User( test_user = User(
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
@ -1860,65 +1970,61 @@ async def tester_persistance_utilisateur(session: AsyncSession = Depends(get_ses
role="user", role="user",
is_verified=True, is_verified=True,
is_active=True, is_active=True,
created_at=datetime.now() created_at=datetime.now(),
) )
session.add(test_user) session.add(test_user)
await session.flush() await session.flush()
user_id = test_user.id user_id = test_user.id
await session.commit() await session.commit()
logger.info(f"✅ ÉTAPE 1: User créé - {user_id}") logger.info(f"✅ ÉTAPE 1: User créé - {user_id}")
# === ÉTAPE 2: LECTURE === # === ÉTAPE 2: LECTURE ===
result = await session.execute( result = await session.execute(select(User).where(User.id == user_id))
select(User).where(User.id == user_id)
)
loaded_user = result.scalar_one_or_none() loaded_user = result.scalar_one_or_none()
if not loaded_user: if not loaded_user:
return { return {
"success": False, "success": False,
"error": "❌ User introuvable après création !", "error": "❌ User introuvable après création !",
"step": "LECTURE" "step": "LECTURE",
} }
logger.info(f"✅ ÉTAPE 2: User chargé - {loaded_user.email}") logger.info(f"✅ ÉTAPE 2: User chargé - {loaded_user.email}")
# === ÉTAPE 3: MODIFICATION (simulate reset password) === # === ÉTAPE 3: MODIFICATION (simulate reset password) ===
loaded_user.hashed_password = hash_password("NewPassword456!") loaded_user.hashed_password = hash_password("NewPassword456!")
loaded_user.reset_token = None loaded_user.reset_token = None
loaded_user.reset_token_expires = None loaded_user.reset_token_expires = None
session.add(loaded_user) session.add(loaded_user)
await session.flush() await session.flush()
await session.commit() await session.commit()
await session.refresh(loaded_user) await session.refresh(loaded_user)
logger.info(f"✅ ÉTAPE 3: User modifié") logger.info(f"✅ ÉTAPE 3: User modifié")
# === ÉTAPE 4: RE-LECTURE === # === ÉTAPE 4: RE-LECTURE ===
result2 = await session.execute( result2 = await session.execute(select(User).where(User.id == user_id))
select(User).where(User.id == user_id)
)
reloaded_user = result2.scalar_one_or_none() reloaded_user = result2.scalar_one_or_none()
if not reloaded_user: if not reloaded_user:
return { return {
"success": False, "success": False,
"error": "❌ User DISPARU après modification !", "error": "❌ User DISPARU après modification !",
"step": "RE-LECTURE", "step": "RE-LECTURE",
"user_id": user_id "user_id": user_id,
} }
logger.info(f"✅ ÉTAPE 4: User re-chargé - {reloaded_user.email}") logger.info(f"✅ ÉTAPE 4: User re-chargé - {reloaded_user.email}")
# === ÉTAPE 5: SUPPRESSION DU TEST === # === ÉTAPE 5: SUPPRESSION DU TEST ===
await session.delete(reloaded_user) await session.delete(reloaded_user)
await session.commit() await session.commit()
logger.info(f"✅ ÉTAPE 5: User test supprimé") logger.info(f"✅ ÉTAPE 5: User test supprimé")
return { return {
"success": True, "success": True,
"message": "✅ Tous les tests de persistance sont OK", "message": "✅ Tous les tests de persistance sont OK",
@ -1929,22 +2035,23 @@ async def tester_persistance_utilisateur(session: AsyncSession = Depends(get_ses
"2. Lecture", "2. Lecture",
"3. Modification (reset password simulé)", "3. Modification (reset password simulé)",
"4. Re-lecture (vérification persistance)", "4. Re-lecture (vérification persistance)",
"5. Suppression (cleanup)" "5. Suppression (cleanup)",
] ],
} }
except Exception as e: except Exception as e:
logger.error(f"❌ Erreur test persistance: {e}", exc_info=True) logger.error(f"❌ Erreur test persistance: {e}", exc_info=True)
# Rollback en cas d'erreur # Rollback en cas d'erreur
await session.rollback() await session.rollback()
return { return {
"success": False, "success": False,
"error": str(e), "error": str(e),
"traceback": str(e.__class__.__name__) "traceback": str(e.__class__.__name__),
} }
# ===================================================== # =====================================================
# LANCEMENT # LANCEMENT
# ===================================================== # =====================================================

View file

@ -256,6 +256,60 @@ class SageGatewayClient:
logger.error(f"Erreur génération PDF: {e}") logger.error(f"Erreur génération PDF: {e}")
raise raise
# =====================================================
# PROSPECTS
# =====================================================
def lister_prospects(self, filtre: str = "") -> List[Dict]:
"""Liste tous les prospects avec filtre optionnel"""
return self._post("/sage/prospects/list", {"filtre": filtre}).get("data", [])
def lire_prospect(self, code: str) -> Optional[Dict]:
"""Lecture d'un prospect par code"""
return self._post("/sage/prospects/get", {"code": code}).get("data")
# =====================================================
# FOURNISSEURS
# =====================================================
def lister_fournisseurs(self, filtre: str = "") -> List[Dict]:
"""Liste tous les fournisseurs avec filtre optionnel"""
return self._post("/sage/fournisseurs/list", {"filtre": filtre}).get("data", [])
def lire_fournisseur(self, code: str) -> Optional[Dict]:
"""Lecture d'un fournisseur par code"""
return self._post("/sage/fournisseurs/get", {"code": code}).get("data")
# =====================================================
# AVOIRS
# =====================================================
def lister_avoirs(
self, limit: int = 100, statut: Optional[int] = None
) -> List[Dict]:
"""Liste tous les avoirs"""
payload = {"limit": limit}
if statut is not None:
payload["statut"] = statut
return self._post("/sage/avoirs/list", payload).get("data", [])
def lire_avoir(self, numero: str) -> Optional[Dict]:
"""Lecture d'un avoir avec ses lignes"""
return self._post("/sage/avoirs/get", {"code": numero}).get("data")
# =====================================================
# LIVRAISONS
# =====================================================
def lister_livraisons(
self, limit: int = 100, statut: Optional[int] = None
) -> List[Dict]:
"""Liste tous les bons de livraison"""
payload = {"limit": limit}
if statut is not None:
payload["statut"] = statut
return self._post("/sage/livraisons/list", payload).get("data", [])
def lire_livraison(self, numero: str) -> Optional[Dict]:
"""Lecture d'une livraison avec ses lignes"""
return self._post("/sage/livraisons/get", {"code": numero}).get("data")
# ===================================================== # =====================================================
# CACHE (ADMIN) # CACHE (ADMIN)
# ===================================================== # =====================================================