Update devis, Create and Update Command
This commit is contained in:
parent
a5dd81ddfb
commit
608ba12c50
2 changed files with 393 additions and 0 deletions
331
api.py
331
api.py
|
|
@ -265,6 +265,87 @@ class FournisseurUpdateRequest(BaseModel):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DevisUpdateRequest(BaseModel):
|
||||||
|
"""Modèle pour modification d'un devis existant"""
|
||||||
|
date_devis: Optional[date] = None
|
||||||
|
lignes: Optional[List[LigneDevis]] = None
|
||||||
|
statut: Optional[int] = Field(None, ge=0, le=6)
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
json_schema_extra = {
|
||||||
|
"example": {
|
||||||
|
"date_devis": "2024-01-15",
|
||||||
|
"lignes": [
|
||||||
|
{
|
||||||
|
"article_code": "ART001",
|
||||||
|
"quantite": 5.0,
|
||||||
|
"prix_unitaire_ht": 100.0,
|
||||||
|
"remise_pourcentage": 10.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"statut": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class LigneCommande(BaseModel):
|
||||||
|
"""Ligne de commande"""
|
||||||
|
article_code: str
|
||||||
|
quantite: float
|
||||||
|
prix_unitaire_ht: Optional[float] = None
|
||||||
|
remise_pourcentage: Optional[float] = 0.0
|
||||||
|
|
||||||
|
@field_validator("article_code", mode="before")
|
||||||
|
def strip_insecables(cls, v):
|
||||||
|
return v.replace("\xa0", "").strip()
|
||||||
|
|
||||||
|
|
||||||
|
class CommandeCreateRequest(BaseModel):
|
||||||
|
"""Création d'une commande"""
|
||||||
|
client_id: str
|
||||||
|
date_commande: Optional[date] = None
|
||||||
|
lignes: List[LigneCommande]
|
||||||
|
reference: Optional[str] = None # Référence externe
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
json_schema_extra = {
|
||||||
|
"example": {
|
||||||
|
"client_id": "CLI000001",
|
||||||
|
"date_commande": "2024-01-15",
|
||||||
|
"reference": "CMD-EXT-001",
|
||||||
|
"lignes": [
|
||||||
|
{
|
||||||
|
"article_code": "ART001",
|
||||||
|
"quantite": 10.0,
|
||||||
|
"prix_unitaire_ht": 50.0,
|
||||||
|
"remise_pourcentage": 5.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CommandeUpdateRequest(BaseModel):
|
||||||
|
"""Modification d'une commande existante"""
|
||||||
|
date_commande: Optional[date] = None
|
||||||
|
lignes: Optional[List[LigneCommande]] = None
|
||||||
|
statut: Optional[int] = Field(None, ge=0, le=6)
|
||||||
|
reference: Optional[str] = None
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
json_schema_extra = {
|
||||||
|
"example": {
|
||||||
|
"lignes": [
|
||||||
|
{
|
||||||
|
"article_code": "ART001",
|
||||||
|
"quantite": 15.0,
|
||||||
|
"prix_unitaire_ht": 45.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"statut": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# =====================================================
|
# =====================================================
|
||||||
# SERVICES EXTERNES (Universign)
|
# SERVICES EXTERNES (Universign)
|
||||||
|
|
@ -592,6 +673,256 @@ async def creer_devis(devis: DevisRequest):
|
||||||
raise HTTPException(500, str(e))
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.put("/devis/{id}", tags=["US-A1"])
|
||||||
|
async def modifier_devis(
|
||||||
|
id: str,
|
||||||
|
devis_update: DevisUpdateRequest,
|
||||||
|
session: AsyncSession = Depends(get_session)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
✏️ Modification d'un devis existant
|
||||||
|
|
||||||
|
**Champs modifiables:**
|
||||||
|
- `date_devis`: Nouvelle date du devis
|
||||||
|
- `lignes`: Nouvelles lignes (remplace toutes les lignes existantes)
|
||||||
|
- `statut`: Nouveau statut (0=Brouillon, 2=Accepté, 5=Transformé, 6=Annulé)
|
||||||
|
|
||||||
|
**Note importante:**
|
||||||
|
- Si `lignes` est fourni, TOUTES les lignes existantes seront remplacées
|
||||||
|
- Pour ajouter/supprimer une ligne, il faut envoyer TOUTES les lignes
|
||||||
|
- Un devis transformé (statut=5) ne peut plus être modifié
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id: Numéro du devis à modifier
|
||||||
|
devis_update: Champs à mettre à jour
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Devis modifié avec ses nouvelles valeurs
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Vérifier que le devis existe
|
||||||
|
devis_existant = sage_client.lire_devis(id)
|
||||||
|
if not devis_existant:
|
||||||
|
raise HTTPException(404, f"Devis {id} introuvable")
|
||||||
|
|
||||||
|
# Vérifier qu'il n'est pas déjà transformé
|
||||||
|
if devis_existant.get("statut") == 5:
|
||||||
|
raise HTTPException(
|
||||||
|
400,
|
||||||
|
f"Le devis {id} a déjà été transformé et ne peut plus être modifié"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Construire les données de mise à jour
|
||||||
|
update_data = {}
|
||||||
|
|
||||||
|
if devis_update.date_devis:
|
||||||
|
update_data["date_devis"] = devis_update.date_devis.isoformat()
|
||||||
|
|
||||||
|
if devis_update.lignes is not None:
|
||||||
|
update_data["lignes"] = [
|
||||||
|
{
|
||||||
|
"article_code": l.article_code,
|
||||||
|
"quantite": l.quantite,
|
||||||
|
"prix_unitaire_ht": l.prix_unitaire_ht,
|
||||||
|
"remise_pourcentage": l.remise_pourcentage,
|
||||||
|
}
|
||||||
|
for l in devis_update.lignes
|
||||||
|
]
|
||||||
|
|
||||||
|
if devis_update.statut is not None:
|
||||||
|
update_data["statut"] = devis_update.statut
|
||||||
|
|
||||||
|
# Appel à la gateway Windows
|
||||||
|
resultat = sage_client.modifier_devis(id, update_data)
|
||||||
|
|
||||||
|
logger.info(f"✅ Devis {id} modifié avec succès")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"message": f"Devis {id} modifié avec succès",
|
||||||
|
"devis": resultat
|
||||||
|
}
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur modification devis {id}: {e}")
|
||||||
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/commandes", status_code=201, tags=["US-A2"])
|
||||||
|
async def creer_commande(
|
||||||
|
commande: CommandeCreateRequest,
|
||||||
|
session: AsyncSession = Depends(get_session)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
➕ Création d'une nouvelle commande (Bon de commande)
|
||||||
|
|
||||||
|
**Workflow typique:**
|
||||||
|
1. Création d'un devis → transformation en commande (automatique)
|
||||||
|
2. OU création directe d'une commande (cette route)
|
||||||
|
|
||||||
|
**Champs obligatoires:**
|
||||||
|
- `client_id`: Code du client
|
||||||
|
- `lignes`: Liste des lignes (min 1)
|
||||||
|
|
||||||
|
**Champs optionnels:**
|
||||||
|
- `date_commande`: Date de la commande (par défaut: aujourd'hui)
|
||||||
|
- `reference`: Référence externe (ex: numéro de commande client)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
commande: Données de la commande à créer
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Commande créée avec son numéro et ses totaux
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Vérifier que le client existe
|
||||||
|
client = sage_client.lire_client(commande.client_id)
|
||||||
|
if not client:
|
||||||
|
raise HTTPException(404, f"Client {commande.client_id} introuvable")
|
||||||
|
|
||||||
|
# Préparer les données pour la gateway
|
||||||
|
commande_data = {
|
||||||
|
"client_id": commande.client_id,
|
||||||
|
"date_commande": (
|
||||||
|
commande.date_commande.isoformat()
|
||||||
|
if commande.date_commande
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
"reference": commande.reference,
|
||||||
|
"lignes": [
|
||||||
|
{
|
||||||
|
"article_code": l.article_code,
|
||||||
|
"quantite": l.quantite,
|
||||||
|
"prix_unitaire_ht": l.prix_unitaire_ht,
|
||||||
|
"remise_pourcentage": l.remise_pourcentage,
|
||||||
|
}
|
||||||
|
for l in commande.lignes
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Appel à la gateway Windows
|
||||||
|
resultat = sage_client.creer_commande(commande_data)
|
||||||
|
|
||||||
|
logger.info(f"✅ Commande créée: {resultat.get('numero_commande')}")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"message": "Commande créée avec succès",
|
||||||
|
"data": {
|
||||||
|
"numero_commande": resultat["numero_commande"],
|
||||||
|
"client_id": commande.client_id,
|
||||||
|
"date_commande": resultat["date_commande"],
|
||||||
|
"total_ht": resultat["total_ht"],
|
||||||
|
"total_ttc": resultat["total_ttc"],
|
||||||
|
"nb_lignes": resultat["nb_lignes"],
|
||||||
|
"reference": commande.reference
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur création commande: {e}")
|
||||||
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.put("/commandes/{id}", tags=["US-A2"])
|
||||||
|
async def modifier_commande(
|
||||||
|
id: str,
|
||||||
|
commande_update: CommandeUpdateRequest,
|
||||||
|
session: AsyncSession = Depends(get_session)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
✏️ Modification d'une commande existante
|
||||||
|
|
||||||
|
**Champs modifiables:**
|
||||||
|
- `date_commande`: Nouvelle date
|
||||||
|
- `lignes`: Nouvelles lignes (remplace toutes les lignes existantes)
|
||||||
|
- `statut`: Nouveau statut
|
||||||
|
- `reference`: Référence externe
|
||||||
|
|
||||||
|
**Restrictions:**
|
||||||
|
- Une commande transformée (statut=5) ne peut plus être modifiée
|
||||||
|
- Une commande annulée (statut=6) ne peut plus être modifiée
|
||||||
|
|
||||||
|
**Note importante:**
|
||||||
|
Si `lignes` est fourni, TOUTES les lignes existantes seront remplacées
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id: Numéro de la commande à modifier
|
||||||
|
commande_update: Champs à mettre à jour
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Commande modifiée avec ses nouvelles valeurs
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 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")
|
||||||
|
|
||||||
|
# Vérifier le statut
|
||||||
|
statut_actuel = commande_existante.get("statut", 0)
|
||||||
|
|
||||||
|
if statut_actuel == 5:
|
||||||
|
raise HTTPException(
|
||||||
|
400,
|
||||||
|
f"La commande {id} a déjà été transformée et ne peut plus être modifiée"
|
||||||
|
)
|
||||||
|
|
||||||
|
if statut_actuel == 6:
|
||||||
|
raise HTTPException(
|
||||||
|
400,
|
||||||
|
f"La commande {id} est annulée et ne peut plus être modifiée"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Construire les données de mise à jour
|
||||||
|
update_data = {}
|
||||||
|
|
||||||
|
if commande_update.date_commande:
|
||||||
|
update_data["date_commande"] = commande_update.date_commande.isoformat()
|
||||||
|
|
||||||
|
if commande_update.lignes is not None:
|
||||||
|
update_data["lignes"] = [
|
||||||
|
{
|
||||||
|
"article_code": l.article_code,
|
||||||
|
"quantite": l.quantite,
|
||||||
|
"prix_unitaire_ht": l.prix_unitaire_ht,
|
||||||
|
"remise_pourcentage": l.remise_pourcentage,
|
||||||
|
}
|
||||||
|
for l in commande_update.lignes
|
||||||
|
]
|
||||||
|
|
||||||
|
if commande_update.statut is not None:
|
||||||
|
update_data["statut"] = commande_update.statut
|
||||||
|
|
||||||
|
if commande_update.reference is not None:
|
||||||
|
update_data["reference"] = commande_update.reference
|
||||||
|
|
||||||
|
# Appel à la gateway Windows
|
||||||
|
resultat = sage_client.modifier_commande(id, update_data)
|
||||||
|
|
||||||
|
logger.info(f"✅ Commande {id} modifiée avec succès")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"message": f"Commande {id} modifiée avec succès",
|
||||||
|
"commande": resultat
|
||||||
|
}
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur modification commande {id}: {e}")
|
||||||
|
raise HTTPException(500, str(e))
|
||||||
|
|
||||||
|
|
||||||
@app.get("/devis", tags=["US-A1"])
|
@app.get("/devis", tags=["US-A1"])
|
||||||
async def lister_devis(
|
async def lister_devis(
|
||||||
limit: int = Query(100, le=1000),
|
limit: int = Query(100, le=1000),
|
||||||
|
|
|
||||||
|
|
@ -382,6 +382,68 @@ class SageGatewayClient:
|
||||||
"code": code,
|
"code": code,
|
||||||
"client_data": client_data
|
"client_data": client_data
|
||||||
}).get("data", {})
|
}).get("data", {})
|
||||||
|
|
||||||
|
|
||||||
|
def modifier_devis(self, numero: str, devis_data: Dict) -> Dict:
|
||||||
|
"""
|
||||||
|
✏️ Modification d'un devis existant
|
||||||
|
|
||||||
|
Args:
|
||||||
|
numero: Numéro du devis à modifier
|
||||||
|
devis_data: Dictionnaire contenant les champs à modifier:
|
||||||
|
- date_devis (str, optional): Nouvelle date
|
||||||
|
- lignes (List, optional): Nouvelles lignes
|
||||||
|
- statut (int, optional): Nouveau statut
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Devis modifié avec totaux recalculés
|
||||||
|
"""
|
||||||
|
return self._post("/sage/devis/update", {
|
||||||
|
"numero": numero,
|
||||||
|
"devis_data": devis_data
|
||||||
|
}).get("data", {})
|
||||||
|
|
||||||
|
def creer_commande(self, commande_data: Dict) -> Dict:
|
||||||
|
"""
|
||||||
|
➕ Création d'une nouvelle commande (Bon de commande)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
commande_data: Dictionnaire contenant:
|
||||||
|
- client_id (str): Code du client
|
||||||
|
- date_commande (str, optional): Date au format ISO
|
||||||
|
- reference (str, optional): Référence externe
|
||||||
|
- lignes (List[Dict]): Liste des lignes avec:
|
||||||
|
- article_code (str)
|
||||||
|
- quantite (float)
|
||||||
|
- prix_unitaire_ht (float, optional)
|
||||||
|
- remise_pourcentage (float, optional)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Commande créée avec son numéro et ses totaux
|
||||||
|
"""
|
||||||
|
return self._post("/sage/commandes/create", commande_data).get("data", {})
|
||||||
|
|
||||||
|
|
||||||
|
def modifier_commande(self, numero: str, commande_data: Dict) -> Dict:
|
||||||
|
"""
|
||||||
|
✏️ Modification d'une commande existante
|
||||||
|
|
||||||
|
Args:
|
||||||
|
numero: Numéro de la commande à modifier
|
||||||
|
commande_data: Dictionnaire contenant les champs à modifier:
|
||||||
|
- date_commande (str, optional): Nouvelle date
|
||||||
|
- lignes (List, optional): Nouvelles lignes
|
||||||
|
- statut (int, optional): Nouveau statut
|
||||||
|
- reference (str, optional): Nouvelle référence
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Commande modifiée avec totaux recalculés
|
||||||
|
"""
|
||||||
|
return self._post("/sage/commandes/update", {
|
||||||
|
"numero": numero,
|
||||||
|
"commande_data": commande_data
|
||||||
|
}).get("data", {})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Instance globale
|
# Instance globale
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue