feat: add CSV export for approved orders && add non-delivery reason modal for livreurs
This commit is contained in:
@@ -265,6 +265,12 @@ func (d *Database) CreateCommandWithAddress(username, deliveryAddress string) (*
|
||||
return command, nil
|
||||
}
|
||||
|
||||
func (d *Database) GetApprovedCommands() ([]models.Command, error) {
|
||||
var commands []models.Command
|
||||
err := d.GDB.Where("status = ?", "approved").Order("created_at DESC").Find(&commands).Error
|
||||
return commands, err
|
||||
}
|
||||
|
||||
func (d *Database) GetAllCommands(status, username string) ([]map[string]any, error) {
|
||||
if username != "" {
|
||||
if err := validateUsername(username); err != nil {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"gestion/db"
|
||||
"gestion/utils"
|
||||
@@ -259,6 +261,47 @@ func RespondToAddressProposal(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func ExportApprovedCommandsCSV(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
commands, err := database.GetApprovedCommands()
|
||||
if err != nil {
|
||||
log.Printf("❌ [EXPORT_CSV] Erreur: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur export CSV"})
|
||||
return
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
w := csv.NewWriter(&buf)
|
||||
|
||||
_ = w.Write([]string{
|
||||
"ID", "Client Order ID", "User ID", "Username",
|
||||
"Statut", "Total (€)", "Adresse", "Livreur",
|
||||
"Créé le", "Mis à jour le",
|
||||
})
|
||||
|
||||
for _, cmd := range commands {
|
||||
_ = w.Write([]string{
|
||||
strconv.Itoa(cmd.ID),
|
||||
strconv.Itoa(cmd.ClientOrderID),
|
||||
strconv.Itoa(cmd.UserID),
|
||||
cmd.Username,
|
||||
cmd.Status,
|
||||
strconv.FormatFloat(cmd.Total, 'f', 2, 64),
|
||||
cmd.DeliveryAddress,
|
||||
cmd.LivreurAssign,
|
||||
cmd.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
cmd.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
w.Flush()
|
||||
|
||||
filename := fmt.Sprintf("commandes_approved_%s.csv", time.Now().Format("2006-01-02"))
|
||||
c.Header("Content-Type", "text/csv; charset=utf-8")
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.String(http.StatusOK, buf.String())
|
||||
}
|
||||
|
||||
func GetAllCommands(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
|
||||
@@ -414,3 +414,61 @@ func UpdateDeliveryStatus(c *gin.Context) {
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// POST /api/v1/livreur/deliveries/:id/issue
|
||||
func ReportDeliveryIssue(c *gin.Context) {
|
||||
username := c.GetString("username")
|
||||
if c.GetString("role") != "livreur" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
|
||||
return
|
||||
}
|
||||
|
||||
commandID, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID invalide"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
IssueType string `json:"issue_type" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "issue_type requis"})
|
||||
return
|
||||
}
|
||||
|
||||
validTypes := map[string]bool{
|
||||
"client_absent": true,
|
||||
"wrong_address": true,
|
||||
"refused_delivery": true,
|
||||
"no_access": true,
|
||||
"other": true,
|
||||
}
|
||||
if !validTypes[req.IssueType] {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Type de problème invalide"})
|
||||
return
|
||||
}
|
||||
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
command, err := database.GetCommandByID(commandID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Commande introuvable"})
|
||||
return
|
||||
}
|
||||
if livreur, _ := command["livreur_assign"].(string); livreur != username {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Commande non assignée à vous"})
|
||||
return
|
||||
}
|
||||
|
||||
issue, err := database.CreateDeliveryIssue(commandID, req.IssueType, req.Description, username)
|
||||
if err != nil {
|
||||
log.Printf("❌ [ISSUE] Erreur création: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur création problème"})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("📋 [ISSUE] Créé par %s pour commande #%d: %s", username, commandID, req.IssueType)
|
||||
c.JSON(http.StatusCreated, gin.H{"success": true, "issue": issue})
|
||||
}
|
||||
|
||||
@@ -193,6 +193,7 @@ func SetupRoutes(router *gin.Engine, database *db.Database, geoService *services
|
||||
// COMMANDES - GESTION DE BASE
|
||||
// ============================================
|
||||
adminGroupV2.GET("/orders", handlers.GetAllCommands)
|
||||
adminGroupV2.GET("/orders/export/csv", handlers.ExportApprovedCommandsCSV)
|
||||
adminGroupV2.GET("/orders/:id", handlers.GetCommandByID)
|
||||
adminGroupV2.PUT("/orders/:id/address", handlers.UpdateCommandAddress)
|
||||
adminGroupV2.POST("/orders/:id/propose-address", handlers.ProposeAddressChange)
|
||||
@@ -336,7 +337,8 @@ func SetupRoutes(router *gin.Engine, database *db.Database, geoService *services
|
||||
livreurGroupV1.GET("/deliveries", handlers.GetMyDeliveries) // ✅ Données filtrées
|
||||
livreurGroupV1.GET("/deliveries/:id", handlers.GetDeliveryDetails) // ✅ Détail filtré
|
||||
livreurGroupV1.POST("/deliveries/:id/start", handlers.StartDelivery)
|
||||
livreurGroupV1.PUT("/deliveries/:id/status", handlers.UpdateDeliveryStatus) // ✅ Avec GPS
|
||||
livreurGroupV1.PUT("/deliveries/:id/status", handlers.UpdateDeliveryStatus) // ✅ Avec GPS
|
||||
livreurGroupV1.POST("/deliveries/:id/issue", handlers.ReportDeliveryIssue) // Motif non-livraison
|
||||
livreurGroupV1.GET("/deliveries/:id/nav-link", handlers.GetLivreurNavLink) // Lien Waze App
|
||||
|
||||
// ============================================
|
||||
|
||||
Reference in New Issue
Block a user