chore: build
This commit is contained in:
@@ -73,8 +73,47 @@ func validateAddress(address string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateCommandDestinationCoords regéocode l'adresse et met à jour
|
||||
// dest_latitude/dest_longitude après tout changement d'adresse de livraison.
|
||||
// Sans cet appel, ces coordonnées restent celles de l'ANCIENNE adresse
|
||||
// (géocodées une seule fois à l'assignation) : la vérification GPS de
|
||||
// handlers/deleviry.go compare alors la position réelle du livreur à un point
|
||||
// périmé et peut refuser à tort une validation "trop loin de la destination"
|
||||
// alors que le livreur est bien arrivé à la nouvelle adresse. En cas d'échec
|
||||
// de géocodage, on réinitialise les coordonnées plutôt que de laisser
|
||||
// l'ancienne valeur périmée : le contrôle GPS est alors ignoré (comportement
|
||||
// déjà prévu quand dest_latitude/dest_longitude sont absentes) au lieu de
|
||||
// bloquer sur un point qui ne correspond plus à l'adresse réelle.
|
||||
func updateCommandDestinationCoords(database *db.Database, geoService *services.GeoService, commandID int, address string) {
|
||||
if geoService == nil || strings.TrimSpace(address) == "" {
|
||||
return
|
||||
}
|
||||
|
||||
location, err := geoService.GeocodeAddress(address)
|
||||
if err != nil || location == nil {
|
||||
log.Printf("⚠️ [ADDR_GEOCODE] Échec géocodage cmd %d (%q): %v — coordonnées de destination réinitialisées", commandID, address, err)
|
||||
if err := database.GDB.Exec(
|
||||
`UPDATE commandes SET dest_latitude = NULL, dest_longitude = NULL WHERE id = ?`,
|
||||
commandID,
|
||||
).Error; err != nil {
|
||||
log.Printf("⚠️ [ADDR_GEOCODE] Erreur reset coordonnées cmd %d: %v", commandID, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.GDB.Exec(
|
||||
`UPDATE commandes SET dest_latitude = ?, dest_longitude = ? WHERE id = ?`,
|
||||
location.Latitude, location.Longitude, commandID,
|
||||
).Error; err != nil {
|
||||
log.Printf("⚠️ [ADDR_GEOCODE] Erreur mise à jour coordonnées cmd %d: %v", commandID, err)
|
||||
return
|
||||
}
|
||||
log.Printf("✅ [ADDR_GEOCODE] Coordonnées de destination mises à jour pour cmd %d", commandID)
|
||||
}
|
||||
|
||||
func UpdateCommandAddress(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
geoService := c.MustGet("geoService").(*services.GeoService)
|
||||
|
||||
userRole := c.GetString("role")
|
||||
if !utils.CheckRoleAdmin(c, userRole) {
|
||||
@@ -138,6 +177,8 @@ func UpdateCommandAddress(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
updateCommandDestinationCoords(database, geoService, commandID, req.DeliveryAddress)
|
||||
|
||||
database.AddCommandLog(commandID, "address_updated",
|
||||
fmt.Sprintf("Adresse mise à jour par admin %s", adminUsername),
|
||||
adminUsername)
|
||||
@@ -220,6 +261,7 @@ func ProposeAddressChange(c *gin.Context) {
|
||||
// POST /api/v1/commands/:id/address/respond
|
||||
func RespondToAddressProposal(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
geoService := c.MustGet("geoService").(*services.GeoService)
|
||||
|
||||
userRole := c.GetString("role")
|
||||
if !utils.CheckRoleClient(c, userRole) {
|
||||
@@ -246,11 +288,25 @@ func RespondToAddressProposal(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// La colonne proposed_address est vidée par RespondToAddressProposal dès
|
||||
// qu'elle est traitée : on la lit avant l'appel pour pouvoir regéocoder la
|
||||
// nouvelle adresse en cas d'acceptation.
|
||||
var proposedAddress string
|
||||
if req.Accepted {
|
||||
if command, err := database.GetCommandByID(commandID); err == nil {
|
||||
proposedAddress, _ = command["proposed_address"].(string)
|
||||
}
|
||||
}
|
||||
|
||||
if err := database.RespondToAddressProposal(commandID, clientUsername, req.Accepted); err != nil {
|
||||
utils.ServerErr(c, "Impossible de traiter la réponse", err)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Accepted && proposedAddress != "" {
|
||||
updateCommandDestinationCoords(database, geoService, commandID, proposedAddress)
|
||||
}
|
||||
|
||||
action := "refusée"
|
||||
if req.Accepted {
|
||||
action = "acceptée"
|
||||
@@ -263,6 +319,64 @@ func RespondToAddressProposal(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateOwnCommandAddress permet à un client de corriger l'adresse de sa
|
||||
// propre commande (ex: suite à un échec de géocodage bloquant l'assignation
|
||||
// auto). Refusé si la commande est déjà en_route ou terminée (voir requête
|
||||
// SQL dans db.UpdateOwnCommandAddress).
|
||||
func UpdateOwnCommandAddress(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
geoService := c.MustGet("geoService").(*services.GeoService)
|
||||
|
||||
userRole := c.GetString("role")
|
||||
if !utils.CheckRoleClient(c, userRole) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
|
||||
return
|
||||
}
|
||||
clientUsername, err := safeGetUsername(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentification requise"})
|
||||
return
|
||||
}
|
||||
|
||||
rateLimitKey := fmt.Sprintf("update_own_addr:%s", clientUsername)
|
||||
if !checkRateLimit(rateLimitKey) {
|
||||
c.JSON(http.StatusTooManyRequests, gin.H{"error": "Trop de requêtes, réessayez plus tard"})
|
||||
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 {
|
||||
DeliveryAddress string `json:"delivery_address" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Données invalides"})
|
||||
return
|
||||
}
|
||||
|
||||
if !geoService.IsValidAddress(req.DeliveryAddress) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Adresse introuvable, vérifiez l'orthographe ou le code postal"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.UpdateOwnCommandAddress(commandID, clientUsername, req.DeliveryAddress); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
updateCommandDestinationCoords(database, geoService, commandID, req.DeliveryAddress)
|
||||
|
||||
log.Printf("✅ [UPD_OWN_ADDR] Commande %d mise à jour par %s", commandID, clientUsername)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Adresse mise à jour",
|
||||
})
|
||||
}
|
||||
|
||||
func ExportApprovedCommandsCSV(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
@@ -556,7 +670,7 @@ func ValidateDelivery(c *gin.Context) {
|
||||
|
||||
currentStatus, _ := command["status"].(string)
|
||||
|
||||
validStatuses := []string{"assigned", "en_route", "pending", "livre"}
|
||||
validStatuses := []string{"assigned", "en_route", "arrived", "pending", "livre"}
|
||||
if !slices.Contains(validStatuses, currentStatus) {
|
||||
failed = append(failed, gin.H{
|
||||
"command_id": commandID,
|
||||
|
||||
Reference in New Issue
Block a user