Added endpoints for collaborators/commercials/representants
This commit is contained in:
parent
96021205a4
commit
f80ad1adee
5 changed files with 587 additions and 48 deletions
68
main.py
68
main.py
|
|
@ -38,6 +38,10 @@ from schemas import (
|
||||||
FamilleCreate,
|
FamilleCreate,
|
||||||
PDFGeneration,
|
PDFGeneration,
|
||||||
DevisUpdate,
|
DevisUpdate,
|
||||||
|
CollaborateurCreateRequest,
|
||||||
|
CollaborateurListRequest,
|
||||||
|
CollaborateurNumeroRequest,
|
||||||
|
CollaborateurUpdateRequest,
|
||||||
)
|
)
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
|
|
@ -1344,6 +1348,70 @@ def tiers_get(req: CodeRequest):
|
||||||
raise HTTPException(500, str(e))
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/sage/collaborateurs/list", dependencies=[Depends(verify_token)])
|
||||||
|
def collaborateurs_list(req: CollaborateurListRequest):
|
||||||
|
"""Liste tous les collaborateurs"""
|
||||||
|
try:
|
||||||
|
collaborateurs = sage.lister_tous_collaborateurs(
|
||||||
|
req.filtre, req.actifs_seulement
|
||||||
|
)
|
||||||
|
return {"success": True, "data": collaborateurs}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur liste collaborateurs: {e}")
|
||||||
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/sage/collaborateurs/get", dependencies=[Depends(verify_token)])
|
||||||
|
def collaborateur_get(req: CollaborateurNumeroRequest):
|
||||||
|
"""Lecture d'un collaborateur par numéro"""
|
||||||
|
try:
|
||||||
|
collaborateur = sage.lire_collaborateur(req.numero)
|
||||||
|
if not collaborateur:
|
||||||
|
raise HTTPException(404, f"Collaborateur {req.numero} non trouvé")
|
||||||
|
return {"success": True, "data": collaborateur}
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur lecture collaborateur: {e}")
|
||||||
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/sage/collaborateurs/create", dependencies=[Depends(verify_token)])
|
||||||
|
def collaborateur_create(req: CollaborateurCreateRequest):
|
||||||
|
"""Création d'un collaborateur"""
|
||||||
|
try:
|
||||||
|
data = req.model_dump(exclude_none=True)
|
||||||
|
nouveau = sage.creer_collaborateur(data)
|
||||||
|
|
||||||
|
if not nouveau:
|
||||||
|
raise HTTPException(500, "Échec création collaborateur")
|
||||||
|
|
||||||
|
return {"success": True, "data": nouveau}
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur création collaborateur: {e}")
|
||||||
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/sage/collaborateurs/update", dependencies=[Depends(verify_token)])
|
||||||
|
def collaborateur_update(req: CollaborateurUpdateRequest):
|
||||||
|
"""Modification d'un collaborateur"""
|
||||||
|
try:
|
||||||
|
data = req.model_dump(exclude_unset=True, exclude={"numero"})
|
||||||
|
modifie = sage.modifier_collaborateur(req.numero, data)
|
||||||
|
|
||||||
|
if not modifie:
|
||||||
|
raise HTTPException(404, f"Collaborateur {req.numero} non trouvé")
|
||||||
|
|
||||||
|
return {"success": True, "data": modifie}
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur modification collaborateur: {e}")
|
||||||
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
"main:app",
|
"main:app",
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ from utils import (
|
||||||
)
|
)
|
||||||
|
|
||||||
from schemas.documents.doc_config import TypeDocumentVente
|
from schemas.documents.doc_config import TypeDocumentVente
|
||||||
from utils.functions.data.create_doc import _creer_document_vente_unifie
|
from utils.functions.data.create_doc import creer_document_vente
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -1219,7 +1219,7 @@ class SageConnector:
|
||||||
|
|
||||||
def creer_devis_enrichi(self, devis_data: dict) -> Dict:
|
def creer_devis_enrichi(self, devis_data: dict) -> Dict:
|
||||||
"""Crée un devis"""
|
"""Crée un devis"""
|
||||||
return _creer_document_vente_unifie(self, devis_data, TypeDocumentVente.DEVIS)
|
return creer_document_vente(self, devis_data, TypeDocumentVente.DEVIS)
|
||||||
|
|
||||||
def _relire_devis(self, numero_devis, devis_data):
|
def _relire_devis(self, numero_devis, devis_data):
|
||||||
"""Relit le devis créé et extrait les informations finales."""
|
"""Relit le devis créé et extrait les informations finales."""
|
||||||
|
|
@ -4857,7 +4857,7 @@ class SageConnector:
|
||||||
|
|
||||||
def creer_commande_enrichi(self, commande_data: dict) -> Dict:
|
def creer_commande_enrichi(self, commande_data: dict) -> Dict:
|
||||||
"""Crée une commande"""
|
"""Crée une commande"""
|
||||||
return _creer_document_vente_unifie(commande_data, TypeDocumentVente.COMMANDE)
|
return creer_document_vente(commande_data, TypeDocumentVente.COMMANDE)
|
||||||
|
|
||||||
def modifier_commande(self, numero: str, commande_data: Dict) -> Dict:
|
def modifier_commande(self, numero: str, commande_data: Dict) -> Dict:
|
||||||
if not self.cial:
|
if not self.cial:
|
||||||
|
|
@ -5310,7 +5310,7 @@ class SageConnector:
|
||||||
|
|
||||||
def creer_livraison_enrichi(self, livraison_data: dict) -> Dict:
|
def creer_livraison_enrichi(self, livraison_data: dict) -> Dict:
|
||||||
"""Crée un bon de livraison"""
|
"""Crée un bon de livraison"""
|
||||||
return _creer_document_vente_unifie(livraison_data, TypeDocumentVente.LIVRAISON)
|
return creer_document_vente(livraison_data, TypeDocumentVente.LIVRAISON)
|
||||||
|
|
||||||
def modifier_livraison(self, numero: str, livraison_data: Dict) -> Dict:
|
def modifier_livraison(self, numero: str, livraison_data: Dict) -> Dict:
|
||||||
if not self.cial:
|
if not self.cial:
|
||||||
|
|
@ -5696,7 +5696,7 @@ class SageConnector:
|
||||||
|
|
||||||
def creer_avoir_enrichi(self, avoir_data: dict) -> Dict:
|
def creer_avoir_enrichi(self, avoir_data: dict) -> Dict:
|
||||||
"""Crée un avoir"""
|
"""Crée un avoir"""
|
||||||
return _creer_document_vente_unifie(avoir_data, TypeDocumentVente.AVOIR)
|
return creer_document_vente(avoir_data, TypeDocumentVente.AVOIR)
|
||||||
|
|
||||||
def modifier_avoir(self, numero: str, avoir_data: Dict) -> Dict:
|
def modifier_avoir(self, numero: str, avoir_data: Dict) -> Dict:
|
||||||
if not self.cial:
|
if not self.cial:
|
||||||
|
|
@ -6162,7 +6162,7 @@ class SageConnector:
|
||||||
|
|
||||||
def creer_facture_enrichi(self, facture_data: dict) -> Dict:
|
def creer_facture_enrichi(self, facture_data: dict) -> Dict:
|
||||||
"""Crée une facture"""
|
"""Crée une facture"""
|
||||||
return _creer_document_vente_unifie(facture_data, TypeDocumentVente.FACTURE)
|
return creer_document_vente(facture_data, TypeDocumentVente.FACTURE)
|
||||||
|
|
||||||
def modifier_facture(self, numero: str, facture_data: Dict) -> Dict:
|
def modifier_facture(self, numero: str, facture_data: Dict) -> Dict:
|
||||||
if not self.cial:
|
if not self.cial:
|
||||||
|
|
@ -9615,3 +9615,422 @@ class SageConnector:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"✗ Erreur SQL tiers {code}: {e}")
|
logger.error(f"✗ Erreur SQL tiers {code}: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def lister_tous_collaborateurs(self, filtre="", actifs_seulement=True):
|
||||||
|
"""Liste tous les collaborateurs"""
|
||||||
|
try:
|
||||||
|
with self._get_sql_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
query = """
|
||||||
|
SELECT
|
||||||
|
CO_No, CO_Nom, CO_Prenom, CO_Fonction,
|
||||||
|
CO_Adresse, CO_Complement, CO_CodePostal, CO_Ville,
|
||||||
|
CO_CodeRegion, CO_Pays, CO_Service,
|
||||||
|
CO_Vendeur, CO_Caissier, CO_Acheteur,
|
||||||
|
CO_Telephone, CO_Telecopie, CO_EMail, CO_TelPortable,
|
||||||
|
CO_Matricule, CO_Facebook, CO_LinkedIn, CO_Skype,
|
||||||
|
CO_Sommeil, CO_ChefVentes, CO_NoChefVentes
|
||||||
|
FROM F_COLLABORATEUR
|
||||||
|
WHERE 1=1
|
||||||
|
"""
|
||||||
|
|
||||||
|
params = []
|
||||||
|
|
||||||
|
if actifs_seulement:
|
||||||
|
query += " AND CO_Sommeil = 0"
|
||||||
|
|
||||||
|
if filtre:
|
||||||
|
query += " AND (CO_Nom LIKE ? OR CO_Prenom LIKE ?)"
|
||||||
|
params.extend([f"%{filtre}%", f"%{filtre}%"])
|
||||||
|
|
||||||
|
query += " ORDER BY CO_Nom, CO_Prenom"
|
||||||
|
|
||||||
|
cursor.execute(query, params)
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
|
||||||
|
# ⚠️⚠️⚠️ VÉRIFIE CETTE LIGNE ⚠️⚠️⚠️
|
||||||
|
collaborateurs = [self._row_to_collaborateur_dict(row) for row in rows]
|
||||||
|
|
||||||
|
logger.info(f"✓ SQL: {len(collaborateurs)} collaborateurs")
|
||||||
|
return collaborateurs
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"✗ Erreur SQL collaborateurs: {e}")
|
||||||
|
raise RuntimeError(f"Erreur lecture collaborateurs: {str(e)}")
|
||||||
|
|
||||||
|
def lire_collaborateur(self, numero):
|
||||||
|
"""Lit un collaborateur par son numéro"""
|
||||||
|
try:
|
||||||
|
with self._get_sql_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
query = """
|
||||||
|
SELECT
|
||||||
|
CO_No, CO_Nom, CO_Prenom, CO_Fonction,
|
||||||
|
CO_Adresse, CO_Complement, CO_CodePostal, CO_Ville,
|
||||||
|
CO_CodeRegion, CO_Pays, CO_Service,
|
||||||
|
CO_Vendeur, CO_Caissier, CO_Acheteur,
|
||||||
|
CO_Telephone, CO_Telecopie, CO_EMail, CO_TelPortable,
|
||||||
|
CO_Matricule, CO_Facebook, CO_LinkedIn, CO_Skype,
|
||||||
|
CO_Sommeil, CO_ChefVentes, CO_NoChefVentes
|
||||||
|
FROM F_COLLABORATEUR
|
||||||
|
WHERE CO_No = ?
|
||||||
|
"""
|
||||||
|
|
||||||
|
cursor.execute(query, (numero,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
|
||||||
|
if not row:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ⚠️ UTILISER LA FONCTION DE CLASSE EXISTANTE
|
||||||
|
collaborateur = self._row_to_collaborateur_dict(row)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"✓ SQL: Collaborateur {numero} avec {len(collaborateur)} champs"
|
||||||
|
)
|
||||||
|
return collaborateur
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"✗ Erreur SQL collaborateur {numero}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def creer_collaborateur(self, data: dict) -> dict:
|
||||||
|
"""Crée un nouveau collaborateur via COM Sage"""
|
||||||
|
if not self.cial:
|
||||||
|
raise RuntimeError("Connexion Sage non établie")
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"📝 Début création collaborateur: {data.get('nom')} {data.get('prenom', '')}"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with self._com_context(), self._lock_com:
|
||||||
|
transaction_active = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Démarrage transaction
|
||||||
|
try:
|
||||||
|
self.cial.CptaApplication.BeginTrans()
|
||||||
|
transaction_active = True
|
||||||
|
logger.debug("✓ Transaction Sage démarrée")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"BeginTrans échoué (non critique): {e}")
|
||||||
|
|
||||||
|
# ===== FACTORY COLLABORATEUR =====
|
||||||
|
factory_collab = self.cial.FactoryCollaborateur
|
||||||
|
persist_collab = factory_collab.Create()
|
||||||
|
|
||||||
|
# Cast vers l'interface IBOCollaborateur3
|
||||||
|
try:
|
||||||
|
collab_obj = win32com.client.CastTo(
|
||||||
|
persist_collab, "IBOCollaborateur3"
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
# Fallback si IBOCollaborateur3 n'existe pas
|
||||||
|
collab_obj = win32com.client.CastTo(
|
||||||
|
persist_collab, "IBOCollaborateur"
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("✓ Objet collaborateur créé")
|
||||||
|
|
||||||
|
# ===== MAPPING ET AFFECTATION CHAMPS =====
|
||||||
|
mapping = {
|
||||||
|
"nom": "CO_Nom",
|
||||||
|
"prenom": "CO_Prenom",
|
||||||
|
"fonction": "CO_Fonction",
|
||||||
|
"adresse": "CO_Adresse",
|
||||||
|
"complement": "CO_Complement",
|
||||||
|
"code_postal": "CO_CodePostal",
|
||||||
|
"ville": "CO_Ville",
|
||||||
|
"region": "CO_CodeRegion",
|
||||||
|
"pays": "CO_Pays",
|
||||||
|
"service": "CO_Service",
|
||||||
|
"telephone": "CO_Telephone",
|
||||||
|
"telecopie": "CO_Telecopie",
|
||||||
|
"email": "CO_EMail",
|
||||||
|
"tel_portable": "CO_TelPortable",
|
||||||
|
"matricule": "CO_Matricule",
|
||||||
|
"facebook": "CO_Facebook",
|
||||||
|
"linkedin": "CO_LinkedIn",
|
||||||
|
"skype": "CO_Skype",
|
||||||
|
"chef_ventes_numero": "CO_NoChefVentes",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Champs texte
|
||||||
|
for py_field, sage_field in mapping.items():
|
||||||
|
if py_field in data and data[py_field] is not None:
|
||||||
|
try:
|
||||||
|
setattr(collab_obj, sage_field, str(data[py_field]))
|
||||||
|
logger.debug(f" ✓ {sage_field}: {data[py_field]}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f" ⚠ {sage_field} non défini: {e}")
|
||||||
|
|
||||||
|
# ===== CHAMPS BOOLÉENS =====
|
||||||
|
bool_mapping = {
|
||||||
|
"est_vendeur": "CO_Vendeur",
|
||||||
|
"est_caissier": "CO_Caissier",
|
||||||
|
"est_acheteur": "CO_Acheteur",
|
||||||
|
"est_chef_ventes": "CO_ChefVentes",
|
||||||
|
}
|
||||||
|
|
||||||
|
for py_field, sage_field in bool_mapping.items():
|
||||||
|
if py_field in data:
|
||||||
|
try:
|
||||||
|
valeur = 1 if data[py_field] else 0
|
||||||
|
setattr(collab_obj, sage_field, valeur)
|
||||||
|
logger.debug(f" ✓ {sage_field}: {valeur}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f" ⚠ {sage_field} non défini: {e}")
|
||||||
|
|
||||||
|
# ===== CO_SOMMEIL (inversé: est_actif → 0=actif, 1=inactif) =====
|
||||||
|
if "est_actif" in data:
|
||||||
|
try:
|
||||||
|
valeur_sommeil = 0 if data["est_actif"] else 1
|
||||||
|
collab_obj.CO_Sommeil = valeur_sommeil
|
||||||
|
logger.debug(
|
||||||
|
f" ✓ CO_Sommeil: {valeur_sommeil} (actif={data['est_actif']})"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f" ⚠ CO_Sommeil non défini: {e}")
|
||||||
|
|
||||||
|
# ===== WRITE =====
|
||||||
|
collab_obj.Write()
|
||||||
|
logger.info("💾 Collaborateur écrit dans Sage")
|
||||||
|
|
||||||
|
# Commit transaction
|
||||||
|
if transaction_active:
|
||||||
|
try:
|
||||||
|
self.cial.CptaApplication.CommitTrans()
|
||||||
|
logger.info("✓ Transaction committée")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ===== RÉCUPÉRATION DU NUMÉRO =====
|
||||||
|
try:
|
||||||
|
collab_obj.Read()
|
||||||
|
numero = getattr(collab_obj, "CO_No", None)
|
||||||
|
except Exception:
|
||||||
|
numero = None
|
||||||
|
|
||||||
|
if not numero:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Numéro de collaborateur vide après création"
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"📄 Collaborateur créé: CO_No={numero}")
|
||||||
|
|
||||||
|
# ===== RELECTURE POUR RETOUR COMPLET =====
|
||||||
|
collaborateur_final = self.lire_collaborateur(numero)
|
||||||
|
|
||||||
|
if not collaborateur_final:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Impossible de relire le collaborateur {numero}"
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"✅ COLLABORATEUR CRÉÉ: {numero} - "
|
||||||
|
f"{collaborateur_final['nom']} {collaborateur_final.get('prenom', '')}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return collaborateur_final
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
if transaction_active:
|
||||||
|
try:
|
||||||
|
self.cial.CptaApplication.RollbackTrans()
|
||||||
|
logger.error("❌ Transaction annulée (rollback)")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
raise
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ ERREUR CRÉATION COLLABORATEUR: {e}", exc_info=True)
|
||||||
|
raise RuntimeError(f"Échec création collaborateur: {str(e)}")
|
||||||
|
|
||||||
|
def modifier_collaborateur(self, numero: int, data: dict) -> dict:
|
||||||
|
"""Modifie un collaborateur existant via COM Sage"""
|
||||||
|
if not self.cial:
|
||||||
|
raise RuntimeError("Connexion Sage non établie")
|
||||||
|
|
||||||
|
logger.info(f"📝 Début modification collaborateur CO_No={numero}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with self._com_context(), self._lock_com:
|
||||||
|
transaction_active = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Démarrage transaction
|
||||||
|
try:
|
||||||
|
self.cial.CptaApplication.BeginTrans()
|
||||||
|
transaction_active = True
|
||||||
|
logger.debug("✓ Transaction Sage démarrée")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"BeginTrans échoué (non critique): {e}")
|
||||||
|
|
||||||
|
# ===== LECTURE DU COLLABORATEUR EXISTANT =====
|
||||||
|
factory_collab = self.cial.FactoryCollaborateur
|
||||||
|
persist_collab = factory_collab.ReadNumero(numero)
|
||||||
|
|
||||||
|
if not persist_collab:
|
||||||
|
raise ValueError(f"Collaborateur {numero} introuvable")
|
||||||
|
|
||||||
|
# Cast vers l'interface IBOCollaborateur3
|
||||||
|
try:
|
||||||
|
collab_obj = win32com.client.CastTo(
|
||||||
|
persist_collab, "IBOCollaborateur3"
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
collab_obj = win32com.client.CastTo(
|
||||||
|
persist_collab, "IBOCollaborateur"
|
||||||
|
)
|
||||||
|
|
||||||
|
collab_obj.Read()
|
||||||
|
logger.info(f"✓ Collaborateur {numero} chargé")
|
||||||
|
|
||||||
|
# ===== MAPPING ET MODIFICATION CHAMPS =====
|
||||||
|
mapping = {
|
||||||
|
"nom": "CO_Nom",
|
||||||
|
"prenom": "CO_Prenom",
|
||||||
|
"fonction": "CO_Fonction",
|
||||||
|
"adresse": "CO_Adresse",
|
||||||
|
"complement": "CO_Complement",
|
||||||
|
"code_postal": "CO_CodePostal",
|
||||||
|
"ville": "CO_Ville",
|
||||||
|
"region": "CO_CodeRegion",
|
||||||
|
"pays": "CO_Pays",
|
||||||
|
"service": "CO_Service",
|
||||||
|
"telephone": "CO_Telephone",
|
||||||
|
"telecopie": "CO_Telecopie",
|
||||||
|
"email": "CO_EMail",
|
||||||
|
"tel_portable": "CO_TelPortable",
|
||||||
|
"matricule": "CO_Matricule",
|
||||||
|
"facebook": "CO_Facebook",
|
||||||
|
"linkedin": "CO_LinkedIn",
|
||||||
|
"skype": "CO_Skype",
|
||||||
|
"chef_ventes_numero": "CO_NoChefVentes",
|
||||||
|
}
|
||||||
|
|
||||||
|
champs_modifies = []
|
||||||
|
|
||||||
|
# Champs texte
|
||||||
|
for py_field, sage_field in mapping.items():
|
||||||
|
if py_field in data:
|
||||||
|
try:
|
||||||
|
valeur = (
|
||||||
|
str(data[py_field])
|
||||||
|
if data[py_field] is not None
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
setattr(collab_obj, sage_field, valeur)
|
||||||
|
champs_modifies.append(sage_field)
|
||||||
|
logger.debug(f" ✓ {sage_field}: {valeur}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f" ⚠ {sage_field} non modifié: {e}")
|
||||||
|
|
||||||
|
# ===== CHAMPS BOOLÉENS =====
|
||||||
|
bool_mapping = {
|
||||||
|
"est_vendeur": "CO_Vendeur",
|
||||||
|
"est_caissier": "CO_Caissier",
|
||||||
|
"est_acheteur": "CO_Acheteur",
|
||||||
|
"est_chef_ventes": "CO_ChefVentes",
|
||||||
|
}
|
||||||
|
|
||||||
|
for py_field, sage_field in bool_mapping.items():
|
||||||
|
if py_field in data:
|
||||||
|
try:
|
||||||
|
valeur = 1 if data[py_field] else 0
|
||||||
|
setattr(collab_obj, sage_field, valeur)
|
||||||
|
champs_modifies.append(sage_field)
|
||||||
|
logger.debug(f" ✓ {sage_field}: {valeur}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f" ⚠ {sage_field} non modifié: {e}")
|
||||||
|
|
||||||
|
# ===== CO_SOMMEIL (inversé) =====
|
||||||
|
if "est_actif" in data:
|
||||||
|
try:
|
||||||
|
valeur_sommeil = 0 if data["est_actif"] else 1
|
||||||
|
collab_obj.CO_Sommeil = valeur_sommeil
|
||||||
|
champs_modifies.append("CO_Sommeil")
|
||||||
|
logger.debug(f" ✓ CO_Sommeil: {valeur_sommeil}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f" ⚠ CO_Sommeil non modifié: {e}")
|
||||||
|
|
||||||
|
if not champs_modifies:
|
||||||
|
logger.info("ℹ Aucun champ à modifier")
|
||||||
|
return self.lire_collaborateur(numero)
|
||||||
|
|
||||||
|
# ===== WRITE =====
|
||||||
|
collab_obj.Write()
|
||||||
|
logger.info(
|
||||||
|
f"💾 Collaborateur modifié ({len(champs_modifies)} champs)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Commit transaction
|
||||||
|
if transaction_active:
|
||||||
|
try:
|
||||||
|
self.cial.CptaApplication.CommitTrans()
|
||||||
|
logger.info("✓ Transaction committée")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ===== RELECTURE POUR RETOUR COMPLET =====
|
||||||
|
collaborateur_final = self.lire_collaborateur(numero)
|
||||||
|
|
||||||
|
if not collaborateur_final:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Impossible de relire le collaborateur {numero}"
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"✅ COLLABORATEUR MODIFIÉ: {numero} - "
|
||||||
|
f"{collaborateur_final['nom']} {collaborateur_final.get('prenom', '')}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return collaborateur_final
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
if transaction_active:
|
||||||
|
try:
|
||||||
|
self.cial.CptaApplication.RollbackTrans()
|
||||||
|
logger.error("❌ Transaction annulée (rollback)")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
raise
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(
|
||||||
|
f"❌ ERREUR MODIFICATION COLLABORATEUR {numero}: {e}", exc_info=True
|
||||||
|
)
|
||||||
|
raise RuntimeError(f"Échec modification collaborateur: {str(e)}")
|
||||||
|
|
||||||
|
def _row_to_collaborateur_dict(self, row):
|
||||||
|
"""Convertit une ligne SQL en dictionnaire collaborateur"""
|
||||||
|
return {
|
||||||
|
"numero": row.CO_No,
|
||||||
|
"nom": _safe_strip(row.CO_Nom), # ⚠️ Utiliser _safe_strip
|
||||||
|
"prenom": _safe_strip(row.CO_Prenom),
|
||||||
|
"fonction": _safe_strip(row.CO_Fonction),
|
||||||
|
"adresse": _safe_strip(row.CO_Adresse),
|
||||||
|
"complement": _safe_strip(row.CO_Complement),
|
||||||
|
"code_postal": _safe_strip(row.CO_CodePostal),
|
||||||
|
"ville": _safe_strip(row.CO_Ville),
|
||||||
|
"code_region": _safe_strip(row.CO_CodeRegion),
|
||||||
|
"pays": _safe_strip(row.CO_Pays),
|
||||||
|
"service": _safe_strip(row.CO_Service),
|
||||||
|
"vendeur": bool(row.CO_Vendeur),
|
||||||
|
"caissier": bool(row.CO_Caissier),
|
||||||
|
"acheteur": bool(row.CO_Acheteur),
|
||||||
|
"telephone": _safe_strip(row.CO_Telephone),
|
||||||
|
"telecopie": _safe_strip(row.CO_Telecopie),
|
||||||
|
"email": _safe_strip(row.CO_EMail),
|
||||||
|
"tel_portable": _safe_strip(row.CO_TelPortable),
|
||||||
|
"matricule": _safe_strip(row.CO_Matricule),
|
||||||
|
"facebook": _safe_strip(row.CO_Facebook),
|
||||||
|
"linkedin": _safe_strip(row.CO_LinkedIn),
|
||||||
|
"skype": _safe_strip(row.CO_Skype),
|
||||||
|
"sommeil": bool(row.CO_Sommeil),
|
||||||
|
"chef_ventes": bool(row.CO_ChefVentes),
|
||||||
|
"numero_chef_ventes": row.CO_NoChefVentes if row.CO_NoChefVentes else None,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,4 @@
|
||||||
from schemas.tiers.tiers import (
|
from schemas.tiers.tiers import TiersList, TypeTiers
|
||||||
TiersList,
|
|
||||||
TypeTiers
|
|
||||||
)
|
|
||||||
from schemas.tiers.contact import (
|
from schemas.tiers.contact import (
|
||||||
ContactCreate,
|
ContactCreate,
|
||||||
ContactDelete,
|
ContactDelete,
|
||||||
|
|
@ -9,65 +6,51 @@ from schemas.tiers.contact import (
|
||||||
ContactList,
|
ContactList,
|
||||||
ContactUpdate,
|
ContactUpdate,
|
||||||
)
|
)
|
||||||
from schemas.tiers.clients import (
|
from schemas.tiers.clients import ClientCreate, ClientUpdate
|
||||||
ClientCreate,
|
|
||||||
ClientUpdate
|
|
||||||
)
|
|
||||||
|
|
||||||
from schemas.others.general_schema import (
|
from schemas.others.general_schema import (
|
||||||
FiltreRequest,
|
FiltreRequest,
|
||||||
ChampLibre,
|
ChampLibre,
|
||||||
CodeRequest,
|
CodeRequest,
|
||||||
StatutRequest
|
StatutRequest,
|
||||||
)
|
)
|
||||||
|
|
||||||
from schemas.documents.documents import (
|
from schemas.documents.documents import (
|
||||||
TransformationRequest,
|
TransformationRequest,
|
||||||
TypeDocument,
|
TypeDocument,
|
||||||
DocumentGet,
|
DocumentGet,
|
||||||
PDFGeneration
|
PDFGeneration,
|
||||||
)
|
)
|
||||||
|
|
||||||
from schemas.documents.devis import (
|
from schemas.documents.devis import DevisRequest, DevisUpdate
|
||||||
DevisRequest,
|
|
||||||
DevisUpdate
|
|
||||||
)
|
|
||||||
|
|
||||||
from schemas.tiers.fournisseurs import (
|
from schemas.tiers.fournisseurs import FournisseurCreate, FournisseurUpdate
|
||||||
FournisseurCreate,
|
|
||||||
FournisseurUpdate
|
|
||||||
)
|
|
||||||
|
|
||||||
from schemas.documents.avoirs import (
|
from schemas.documents.avoirs import AvoirCreate, AvoirUpdate
|
||||||
AvoirCreate,
|
|
||||||
AvoirUpdate
|
|
||||||
)
|
|
||||||
|
|
||||||
from schemas.documents.commandes import (
|
from schemas.documents.commandes import CommandeCreate, CommandeUpdate
|
||||||
CommandeCreate,
|
|
||||||
CommandeUpdate
|
|
||||||
)
|
|
||||||
|
|
||||||
from schemas.documents.factures import (
|
from schemas.documents.factures import FactureCreate, FactureUpdate
|
||||||
FactureCreate,
|
|
||||||
FactureUpdate
|
|
||||||
)
|
|
||||||
|
|
||||||
from schemas.documents.livraisons import (
|
from schemas.documents.livraisons import LivraisonCreate, LivraisonUpdate
|
||||||
LivraisonCreate,
|
|
||||||
LivraisonUpdate
|
|
||||||
)
|
|
||||||
|
|
||||||
from schemas.articles.articles import (
|
from schemas.articles.articles import (
|
||||||
ArticleCreate,
|
ArticleCreate,
|
||||||
ArticleUpdate,
|
ArticleUpdate,
|
||||||
MouvementStockLigneRequest,
|
MouvementStockLigneRequest,
|
||||||
EntreeStock,
|
EntreeStock,
|
||||||
SortieStock
|
SortieStock,
|
||||||
)
|
)
|
||||||
|
|
||||||
from schemas.articles.famille_d_articles import FamilleCreate
|
from schemas.articles.famille_d_articles import FamilleCreate
|
||||||
|
|
||||||
|
from schemas.tiers.commercial import (
|
||||||
|
CollaborateurCreateRequest,
|
||||||
|
CollaborateurListRequest,
|
||||||
|
CollaborateurNumeroRequest,
|
||||||
|
CollaborateurUpdateRequest,
|
||||||
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"TiersList",
|
"TiersList",
|
||||||
"ContactCreate",
|
"ContactCreate",
|
||||||
|
|
@ -103,5 +86,9 @@ __all__ = [
|
||||||
"EntreeStock",
|
"EntreeStock",
|
||||||
"SortieStock",
|
"SortieStock",
|
||||||
"FamilleCreate",
|
"FamilleCreate",
|
||||||
"PDFGeneration"
|
"PDFGeneration",
|
||||||
|
"CollaborateurCreateRequest",
|
||||||
|
"CollaborateurListRequest",
|
||||||
|
"CollaborateurNumeroRequest",
|
||||||
|
"CollaborateurUpdateRequest",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
65
schemas/tiers/commercial.py
Normal file
65
schemas/tiers/commercial.py
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
class CollaborateurListRequest(BaseModel):
|
||||||
|
filtre: str = ""
|
||||||
|
actifs_seulement: bool = True
|
||||||
|
|
||||||
|
|
||||||
|
class CollaborateurNumeroRequest(BaseModel):
|
||||||
|
numero: int
|
||||||
|
|
||||||
|
|
||||||
|
class CollaborateurCreateRequest(BaseModel):
|
||||||
|
nom: str
|
||||||
|
prenom: Optional[str] = None
|
||||||
|
fonction: Optional[str] = None
|
||||||
|
adresse: Optional[str] = None
|
||||||
|
complement: Optional[str] = None
|
||||||
|
code_postal: Optional[str] = None
|
||||||
|
ville: Optional[str] = None
|
||||||
|
code_region: Optional[str] = None
|
||||||
|
pays: Optional[str] = None
|
||||||
|
service: Optional[str] = None
|
||||||
|
vendeur: bool = False
|
||||||
|
caissier: bool = False
|
||||||
|
acheteur: bool = False
|
||||||
|
chef_ventes: bool = False
|
||||||
|
numero_chef_ventes: Optional[int] = None
|
||||||
|
telephone: Optional[str] = None
|
||||||
|
telecopie: Optional[str] = None
|
||||||
|
email: Optional[str] = None
|
||||||
|
tel_portable: Optional[str] = None
|
||||||
|
facebook: Optional[str] = None
|
||||||
|
linkedin: Optional[str] = None
|
||||||
|
skype: Optional[str] = None
|
||||||
|
matricule: Optional[str] = None
|
||||||
|
sommeil: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
class CollaborateurUpdateRequest(CollaborateurNumeroRequest):
|
||||||
|
nom: Optional[str] = None
|
||||||
|
prenom: Optional[str] = None
|
||||||
|
fonction: Optional[str] = None
|
||||||
|
adresse: Optional[str] = None
|
||||||
|
complement: Optional[str] = None
|
||||||
|
code_postal: Optional[str] = None
|
||||||
|
ville: Optional[str] = None
|
||||||
|
code_region: Optional[str] = None
|
||||||
|
pays: Optional[str] = None
|
||||||
|
service: Optional[str] = None
|
||||||
|
vendeur: Optional[bool] = None
|
||||||
|
caissier: Optional[bool] = None
|
||||||
|
acheteur: Optional[bool] = None
|
||||||
|
chef_ventes: Optional[bool] = None
|
||||||
|
numero_chef_ventes: Optional[int] = None
|
||||||
|
telephone: Optional[str] = None
|
||||||
|
telecopie: Optional[str] = None
|
||||||
|
email: Optional[str] = None
|
||||||
|
tel_portable: Optional[str] = None
|
||||||
|
facebook: Optional[str] = None
|
||||||
|
linkedin: Optional[str] = None
|
||||||
|
skype: Optional[str] = None
|
||||||
|
matricule: Optional[str] = None
|
||||||
|
sommeil: Optional[bool] = None
|
||||||
|
|
@ -11,7 +11,7 @@ from utils.tiers.clients.clients_data import _cast_client
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _creer_document_vente_unifie(
|
def creer_document_vente(
|
||||||
self, doc_data: dict, type_document: TypeDocumentVente
|
self, doc_data: dict, type_document: TypeDocumentVente
|
||||||
) -> Dict:
|
) -> Dict:
|
||||||
if not self.cial:
|
if not self.cial:
|
||||||
|
|
@ -206,7 +206,7 @@ def _appliquer_remise_ligne(ligne_obj, remise_pourcent: float) -> bool:
|
||||||
# 3. Calcul (optionnel mais recommandé)
|
# 3. Calcul (optionnel mais recommandé)
|
||||||
try:
|
try:
|
||||||
remise_wrapper.Calcul()
|
remise_wrapper.Calcul()
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 4. Write la ligne
|
# 4. Write la ligne
|
||||||
|
|
@ -240,7 +240,7 @@ def _ajouter_ligne_document(
|
||||||
ligne_persist = factory_lignes.Create()
|
ligne_persist = factory_lignes.Create()
|
||||||
try:
|
try:
|
||||||
ligne_obj = win32com.client.CastTo(ligne_persist, "IBODocumentLigne3")
|
ligne_obj = win32com.client.CastTo(ligne_persist, "IBODocumentLigne3")
|
||||||
except:
|
except Exception:
|
||||||
ligne_obj = win32com.client.CastTo(ligne_persist, "IBODocumentVenteLigne3")
|
ligne_obj = win32com.client.CastTo(ligne_persist, "IBODocumentVenteLigne3")
|
||||||
|
|
||||||
quantite = float(ligne_data["quantite"])
|
quantite = float(ligne_data["quantite"])
|
||||||
|
|
@ -248,10 +248,10 @@ def _ajouter_ligne_document(
|
||||||
# ===== ASSOCIATION ARTICLE =====
|
# ===== ASSOCIATION ARTICLE =====
|
||||||
try:
|
try:
|
||||||
ligne_obj.SetDefaultArticleReference(ligne_data["article_code"], quantite)
|
ligne_obj.SetDefaultArticleReference(ligne_data["article_code"], quantite)
|
||||||
except:
|
except Exception:
|
||||||
try:
|
try:
|
||||||
ligne_obj.SetDefaultArticle(article_obj, quantite)
|
ligne_obj.SetDefaultArticle(article_obj, quantite)
|
||||||
except:
|
except Exception:
|
||||||
ligne_obj.DL_Design = designation_sage
|
ligne_obj.DL_Design = designation_sage
|
||||||
ligne_obj.DL_Qte = quantite
|
ligne_obj.DL_Qte = quantite
|
||||||
|
|
||||||
|
|
@ -384,7 +384,7 @@ def _relire_document_final(
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"_creer_document_vente_unifie",
|
"creer_document_vente",
|
||||||
"_ajouter_ligne_document",
|
"_ajouter_ligne_document",
|
||||||
"_configurer_facture",
|
"_configurer_facture",
|
||||||
"_recuperer_numero_document",
|
"_recuperer_numero_document",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue