diff --git a/api.py b/api.py index d81bec6..f574f08 100644 --- a/api.py +++ b/api.py @@ -3170,6 +3170,186 @@ async def livraison_vers_facture(id: str, session: AsyncSession = Depends(get_se raise HTTPException(500, str(e)) +@app.post("/workflow/devis/{id}/to-facture", tags=["US-A2"]) +async def devis_vers_facture_direct(id: str, session: AsyncSession = Depends(get_session)): + """ + 🔧 Transformation Devis → Facture (DIRECT, sans commande) + + ✅ Utilise les VRAIS types Sage (0 → 60) + ✅ Met à jour le statut du devis source à 5 (Transformé) + + **Workflow raccourci** : Permet de facturer directement depuis un devis + sans passer par la création d'une commande. + + **Cas d'usage** : + - Prestations de services facturées directement + - Petites commandes sans besoin de suivi intermédiaire + - Ventes au comptoir + + Args: + id: Numéro du devis source + + Returns: + Informations de la facture créée + """ + try: + # Étape 1: Vérifier que le devis n'a pas déjà été transformé + devis_existant = sage_client.lire_devis(id) + if not devis_existant: + raise HTTPException(404, f"Devis {id} introuvable") + + statut_devis = devis_existant.get("statut", 0) + if statut_devis == 5: + raise HTTPException( + 400, + f"Le devis {id} a déjà été transformé (statut=5). " + f"Vérifiez les documents déjà créés depuis ce devis." + ) + + # Étape 2: Transformation + resultat = sage_client.transformer_document( + numero_source=id, + type_source=settings.SAGE_TYPE_DEVIS, # = 0 + type_cible=settings.SAGE_TYPE_FACTURE, # = 60 + ) + + # Étape 3: Mettre à jour le statut du devis à 5 (Transformé) + try: + sage_client.changer_statut_devis(id, nouveau_statut=5) + logger.info(f"✅ Statut devis {id} mis à jour: 5 (Transformé)") + except Exception as e: + logger.warning(f"⚠️ Impossible de mettre à jour le statut du devis {id}: {e}") + # On continue même si la MAJ statut échoue + + # Étape 4: Logger la transformation + workflow_log = WorkflowLog( + id=str(uuid.uuid4()), + document_source=id, + type_source=TypeDocument.DEVIS, + document_cible=resultat.get("document_cible", ""), + type_cible=TypeDocument.FACTURE, + nb_lignes=resultat.get("nb_lignes", 0), + date_transformation=datetime.now(), + succes=True, + ) + + session.add(workflow_log) + await session.commit() + + logger.info( + f"✅ Transformation DIRECTE: Devis {id} → Facture {resultat['document_cible']}" + ) + + return { + "success": True, + "workflow": "devis_to_facture_direct", + "document_source": id, + "document_cible": resultat["document_cible"], + "nb_lignes": resultat["nb_lignes"], + "statut_devis_mis_a_jour": True, + "message": f"Facture {resultat['document_cible']} créée directement depuis le devis {id}", + } + + except HTTPException: + raise + except Exception as e: + logger.error(f"❌ Erreur transformation devis→facture: {e}", exc_info=True) + raise HTTPException(500, str(e)) + + +@app.post("/workflow/commande/{id}/to-livraison", tags=["US-A2"]) +async def commande_vers_livraison(id: str, session: AsyncSession = Depends(get_session)): + """ + 🔧 Transformation Commande → Bon de livraison + + ✅ Utilise les VRAIS types Sage (10 → 30) + + **Workflow typique** : Après validation d'une commande, génère + le bon de livraison pour préparer l'expédition. + + **Cas d'usage** : + - Préparation d'une expédition + - Génération du bordereau de livraison + - Suivi logistique + + **Workflow complet** : + 1. Devis → Commande (via `/workflow/devis/{id}/to-commande`) + 2. **Commande → Livraison** (cette route) + 3. Livraison → Facture (via `/workflow/livraison/{id}/to-facture`) + + Args: + id: Numéro de la commande source + + Returns: + Informations du bon de livraison créé + """ + try: + # Étape 1: Vérifier que la commande existe + commande_existante = sage_client.lire_document( + id, + settings.SAGE_TYPE_BON_COMMANDE + ) + + if not commande_existante: + raise HTTPException(404, f"Commande {id} introuvable") + + statut_commande = commande_existante.get("statut", 0) + if statut_commande == 5: + raise HTTPException( + 400, + f"La commande {id} a déjà été transformée (statut=5). " + f"Un bon de livraison existe probablement déjà." + ) + + if statut_commande == 6: + raise HTTPException( + 400, + f"La commande {id} est annulée (statut=6) et ne peut pas être transformée." + ) + + # Étape 2: Transformation + resultat = sage_client.transformer_document( + numero_source=id, + type_source=settings.SAGE_TYPE_BON_COMMANDE, # = 10 + type_cible=settings.SAGE_TYPE_BON_LIVRAISON, # = 30 + ) + + # Étape 3: Logger la transformation + workflow_log = WorkflowLog( + id=str(uuid.uuid4()), + document_source=id, + type_source=TypeDocument.BON_COMMANDE, + document_cible=resultat.get("document_cible", ""), + type_cible=TypeDocument.BON_LIVRAISON, + nb_lignes=resultat.get("nb_lignes", 0), + date_transformation=datetime.now(), + succes=True, + ) + + session.add(workflow_log) + await session.commit() + + logger.info( + f"✅ Transformation: Commande {id} → Livraison {resultat['document_cible']}" + ) + + return { + "success": True, + "workflow": "commande_to_livraison", + "document_source": id, + "document_cible": resultat["document_cible"], + "nb_lignes": resultat["nb_lignes"], + "message": f"Bon de livraison {resultat['document_cible']} créé depuis la commande {id}", + "next_step": f"Utilisez /workflow/livraison/{resultat['document_cible']}/to-facture pour créer la facture", + } + + except HTTPException: + raise + except Exception as e: + logger.error(f"❌ Erreur transformation commande→livraison: {e}", exc_info=True) + raise HTTPException(500, str(e)) + + @app.get("/debug/users", response_model=List[UserResponse], tags=["Debug"]) async def lister_utilisateurs_debug( session: AsyncSession = Depends(get_session),