chore: add corrige adresse
This commit is contained in:
@@ -131,14 +131,16 @@ func GetMyCommandsWithTracking(c *gin.Context) {
|
||||
}
|
||||
|
||||
enrichedCommands[i] = gin.H{
|
||||
"id": cmd["id"],
|
||||
"status": cmd["status"],
|
||||
"status_message": getStatusMessage(cmd["status"].(string)),
|
||||
"adresse": cmd["adresse"],
|
||||
"total_prix": cmd["total_prix"],
|
||||
"created_at": cmd["created_at"],
|
||||
"livreur": livreurInfo,
|
||||
"eta": etaData,
|
||||
"id": cmd["id"],
|
||||
"status": cmd["status"],
|
||||
"status_message": getStatusMessage(cmd["status"].(string)),
|
||||
"adresse": cmd["adresse"],
|
||||
"total_prix": cmd["total_prix"],
|
||||
"created_at": cmd["created_at"],
|
||||
"livreur": livreurInfo,
|
||||
"eta": etaData,
|
||||
"proposed_address": cmd["proposed_address"],
|
||||
"address_proposal_status": cmd["address_proposal_status"],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,120 @@ func UpdateCommandAddress(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// ProposeAddressChange propose une nouvelle adresse au client pour validation
|
||||
// POST /api/v2/admin/protected/orders/:id/propose-address
|
||||
// POST /api/v1/cabine/commands/:id/propose-address
|
||||
func ProposeAddressChange(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
userRole := c.GetString("role")
|
||||
if userRole != "admin" && userRole != "cabine" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
|
||||
return
|
||||
}
|
||||
|
||||
staffUsername, err := safeGetUsername(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
commandID, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil || commandID <= 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
ProposedAddress string `json:"proposed_address" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Données invalides"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateAddress(req.ProposedAddress); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Récupérer la commande pour vérifier statut et obtenir username client
|
||||
command, err := database.GetCommandByID(commandID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Commande non trouvée"})
|
||||
return
|
||||
}
|
||||
|
||||
status, _ := command["status"].(string)
|
||||
if status == "livre" || status == "approved" || status == "cancelled" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Impossible de modifier l'adresse d'une commande terminée"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.ProposeAddressChange(commandID, req.ProposedAddress, staffUsername); err != nil {
|
||||
log.Printf("❌ [PROPOSE_ADDR] Erreur: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Notifier le client
|
||||
clientUsername, _ := command["username"].(string)
|
||||
if clientUsername != "" {
|
||||
msg := fmt.Sprintf("📍 Une nouvelle adresse de livraison vous est proposée pour la commande #%d : %s. Veuillez l'accepter ou la refuser dans le suivi de commande.", commandID, req.ProposedAddress)
|
||||
database.NotifyClient(clientUsername, commandID, "address_proposal", msg)
|
||||
}
|
||||
|
||||
log.Printf("✅ [PROPOSE_ADDR] Commande %d - nouvelle adresse proposée par %s", commandID, staffUsername)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Nouvelle adresse proposée au client",
|
||||
"command_id": commandID,
|
||||
})
|
||||
}
|
||||
|
||||
// RespondToAddressProposal permet au client d'accepter ou refuser une proposition d'adresse
|
||||
// POST /api/v1/commands/:id/address/respond
|
||||
func RespondToAddressProposal(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
clientUsername, err := safeGetUsername(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentification requise"})
|
||||
return
|
||||
}
|
||||
|
||||
commandID, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil || commandID <= 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Données invalides"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.RespondToAddressProposal(commandID, clientUsername, req.Accepted); err != nil {
|
||||
log.Printf("❌ [RESPOND_ADDR] Erreur: %v", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
action := "refusée"
|
||||
if req.Accepted {
|
||||
action = "acceptée"
|
||||
}
|
||||
|
||||
log.Printf("✅ [RESPOND_ADDR] Commande %d - proposition %s par %s", commandID, action, clientUsername)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": fmt.Sprintf("Proposition d'adresse %s", action),
|
||||
})
|
||||
}
|
||||
|
||||
func GetAllCommands(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
@@ -768,6 +882,54 @@ func GetClientCommandsHistory(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// NOTIFICATIONS CLIENT
|
||||
// ============================================
|
||||
|
||||
// NotifyClientToDescend envoie une notification push au client pour descendre récupérer sa commande
|
||||
// POST /api/v2/admin/protected/orders/:id/notify-client
|
||||
// POST /api/v1/cabine/commands/:id/notify-client
|
||||
func NotifyClientToDescend(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
role := c.GetString("role")
|
||||
if role != "admin" && role != "cabine" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
|
||||
return
|
||||
}
|
||||
|
||||
commandID, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil || commandID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
|
||||
return
|
||||
}
|
||||
|
||||
cmd, err := database.GetCommandByID(commandID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Commande introuvable"})
|
||||
return
|
||||
}
|
||||
|
||||
clientUsername, ok := cmd["username"].(string)
|
||||
if !ok || clientUsername == "" {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Client introuvable"})
|
||||
return
|
||||
}
|
||||
|
||||
staffUsername, _ := c.Get("username")
|
||||
msg := fmt.Sprintf("Votre commande #%d est prête ! Vous pouvez descendre la récupérer.", commandID)
|
||||
database.NotifyClient(clientUsername, commandID, "ready_pickup", msg)
|
||||
database.AddCommandLog(commandID, "notification", fmt.Sprintf("Client notifié de descendre par %s", staffUsername), staffUsername.(string))
|
||||
|
||||
log.Printf("🔔 [NOTIFY] Client %s notifié pour commande %d par %s", clientUsername, commandID, staffUsername)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Client notifié",
|
||||
"client_username": clientUsername,
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// DÉTAILS COMMANDES & ITEMS
|
||||
// ============================================
|
||||
@@ -1145,6 +1307,104 @@ func GetCommandFullDetails(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteCommandItem supprime un item d'une commande
|
||||
// DELETE /api/v2/admin/protected/orders/:id/items/:item_id
|
||||
func DeleteCommandItem(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
if c.GetString("role") != "admin" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
|
||||
return
|
||||
}
|
||||
|
||||
adminUsername, err := safeGetUsername(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
commandID, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil || commandID <= 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
|
||||
return
|
||||
}
|
||||
|
||||
itemID, err := strconv.Atoi(c.Param("item_id"))
|
||||
if err != nil || itemID <= 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID d'item invalide"})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("🗑️ [DEL_ITEM] Admin %s supprime item %d de cmd %d", adminUsername, itemID, commandID)
|
||||
|
||||
if err := database.DeleteCommandItem(commandID, itemID); err != nil {
|
||||
log.Printf("❌ [DEL_ITEM] Erreur: %v", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
database.AddCommandLog(commandID, "item_deleted",
|
||||
fmt.Sprintf("Item %d supprimé par admin %s", itemID, adminUsername),
|
||||
adminUsername)
|
||||
|
||||
log.Printf("✅ [DEL_ITEM] Item %d supprimé", itemID)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Item supprimé",
|
||||
"command_id": commandID,
|
||||
"item_id": itemID,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateCommandStatusAdmin met à jour le statut d'une commande (Admin)
|
||||
// PUT /api/v2/admin/protected/orders/:id/status
|
||||
func UpdateCommandStatusAdmin(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
if c.GetString("role") != "admin" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
|
||||
return
|
||||
}
|
||||
|
||||
commandID, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil || commandID <= 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Status string `json:"status" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Statut manquant"})
|
||||
return
|
||||
}
|
||||
|
||||
allowed := map[string]bool{
|
||||
"pending": true, "assigned": true, "en_route": true,
|
||||
"livre": true, "approved": true, "cancelled": true,
|
||||
}
|
||||
if !allowed[req.Status] {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Statut invalide: " + req.Status})
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.UpdateCommandStatus(commandID, req.Status); err != nil {
|
||||
log.Printf("❌ [STATUS_ADMIN] Erreur: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("✅ [STATUS_ADMIN] Cmd %d → %s", commandID, req.Status)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Statut mis à jour",
|
||||
"command_id": commandID,
|
||||
"new_status": req.Status,
|
||||
})
|
||||
}
|
||||
|
||||
// GetCommandItemsStats récupère les stats d'une commande
|
||||
// GET /api/v1/commands/:id/stats
|
||||
func GetCommandItemsStats(c *gin.Context) {
|
||||
|
||||
@@ -373,7 +373,7 @@ func UpdateDeliveryStatus(c *gin.Context) {
|
||||
clientMsg = fmt.Sprintf("Votre commande #%d est en route !", commandID)
|
||||
}
|
||||
case "arrived":
|
||||
clientMsg = fmt.Sprintf("Votre livreur est arrivé pour la commande #%d", commandID)
|
||||
clientMsg = fmt.Sprintf("🛵 Votre livreur est là ! Il sera chez vous dans 5 minutes (commande #%d)", commandID)
|
||||
case "livre":
|
||||
clientMsg = fmt.Sprintf("Votre commande #%d a été livrée. Merci !", commandID)
|
||||
case "failed":
|
||||
|
||||
Reference in New Issue
Block a user