This commit is contained in:
@@ -290,11 +290,16 @@ func RespondToAddressProposal(c *gin.Context) {
|
|||||||
|
|
||||||
// La colonne proposed_address est vidée par RespondToAddressProposal dès
|
// 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
|
// qu'elle est traitée : on la lit avant l'appel pour pouvoir regéocoder la
|
||||||
// nouvelle adresse en cas d'acceptation.
|
// nouvelle adresse en cas d'acceptation. On récupère aussi le livreur
|
||||||
|
// assigné : ProposeAddressChange autorise un changement d'adresse même en
|
||||||
|
// "en_route" (voir commands.go ~L236-240), donc un livreur peut déjà être
|
||||||
|
// en train de rouler vers l'ancienne adresse au moment de l'acceptation.
|
||||||
var proposedAddress string
|
var proposedAddress string
|
||||||
|
var livreurAssign string
|
||||||
if req.Accepted {
|
if req.Accepted {
|
||||||
if command, err := database.GetCommandByID(commandID); err == nil {
|
if command, err := database.GetCommandByID(commandID); err == nil {
|
||||||
proposedAddress, _ = command["proposed_address"].(string)
|
proposedAddress, _ = command["proposed_address"].(string)
|
||||||
|
livreurAssign, _ = command["livreur_assign"].(string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,6 +310,19 @@ func RespondToAddressProposal(c *gin.Context) {
|
|||||||
|
|
||||||
if req.Accepted && proposedAddress != "" {
|
if req.Accepted && proposedAddress != "" {
|
||||||
updateCommandDestinationCoords(database, geoService, commandID, proposedAddress)
|
updateCommandDestinationCoords(database, geoService, commandID, proposedAddress)
|
||||||
|
|
||||||
|
// L'ETA en cache correspond au trajet vers l'ancienne destination —
|
||||||
|
// on l'invalide pour forcer un recalcul sur les nouvelles coordonnées.
|
||||||
|
db.Redis.Del(db.RedisCtx, fmt.Sprintf("command:eta:%d", commandID))
|
||||||
|
|
||||||
|
// Sans cette notification, le livreur déjà en route continuerait à
|
||||||
|
// naviguer vers l'ancienne adresse sans savoir que la destination a
|
||||||
|
// changé en base.
|
||||||
|
if livreurAssign != "" {
|
||||||
|
clientOrderID := database.GetClientOrderID(commandID)
|
||||||
|
livreurMsg := fmt.Sprintf("⚠️ Le client a changé l'adresse de livraison de la commande #%d : %s. Merci de mettre à jour votre itinéraire.", clientOrderID, proposedAddress)
|
||||||
|
database.NotifyLivreur(livreurAssign, commandID, "address_changed", livreurMsg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
action := "refusée"
|
action := "refusée"
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gestion/db"
|
||||||
|
"gestion/handlers"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Si le livreur est déjà en route quand le client accepte une proposition
|
||||||
|
// d'adresse (ProposeAddressChange autorise ce cas même en "en_route" — voir
|
||||||
|
// commands.go ~L236-240), il doit être notifié que la destination a changé,
|
||||||
|
// sans quoi il continuerait à naviguer vers l'ancienne adresse alors que la
|
||||||
|
// nouvelle est déjà enregistrée en base (voir RespondToAddressProposal).
|
||||||
|
func TestRespondToAddressProposal_NotifiesLivreurWhenAlreadyEnRoute(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("appelle le vrai service Nominatim en réseau — sauté en mode -short")
|
||||||
|
}
|
||||||
|
cleanupStockTestData(t)
|
||||||
|
username := newTestClient(t, "respond_addr_notif_client")
|
||||||
|
livreur := newTestClient(t, "respond_addr_notif_livreur")
|
||||||
|
productID := newTestProduct(t, "RespondAddrNotif", 10)
|
||||||
|
cmdID := newTestCommandWithItem(t, username, "en_route", livreur, productID, 1, 10)
|
||||||
|
|
||||||
|
if err := testDB.ProposeAddressChange(cmdID, "12 rue Crebillon, 44000 Nantes", "admin_test"); err != nil {
|
||||||
|
t.Fatalf("ProposeAddressChange: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
notifKey := fmt.Sprintf("notifications:%s", livreur)
|
||||||
|
db.Redis.Del(db.RedisCtx, notifKey)
|
||||||
|
|
||||||
|
body := []byte(`{"accepted":true}`)
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/commands/address/respond", bytes.NewReader(body))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(rec)
|
||||||
|
c.Request = req
|
||||||
|
c.Set("database", testDB)
|
||||||
|
c.Set("geoService", ensureTestGeoService())
|
||||||
|
c.Set("username", username)
|
||||||
|
c.Set("role", "client")
|
||||||
|
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", cmdID)}}
|
||||||
|
|
||||||
|
handlers.RespondToAddressProposal(c)
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("RespondToAddressProposal doit réussir: got=%d body=%s", rec.Code, rec.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := db.Redis.LRange(db.RedisCtx, notifKey, 0, -1).Result()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("lecture notifications livreur: %v", err)
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
for _, raw := range entries {
|
||||||
|
var notif struct {
|
||||||
|
CommandID int `json:"command_id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(raw), ¬if); err == nil && notif.CommandID == cmdID && notif.Type == "address_changed" {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Errorf("le livreur déjà en route doit être notifié du changement d'adresse (type=address_changed, command_id=%d): notifications=%v", cmdID, entries)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si aucun livreur n'est assigné, il ne doit évidemment pas y avoir de
|
||||||
|
// notification livreur envoyée (pas de panique/erreur non plus).
|
||||||
|
func TestRespondToAddressProposal_NoLivreurNotificationWhenNoneAssigned(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("appelle le vrai service Nominatim en réseau — sauté en mode -short")
|
||||||
|
}
|
||||||
|
cleanupStockTestData(t)
|
||||||
|
username := newTestClient(t, "respond_addr_notif_nolivreur")
|
||||||
|
productID := newTestProduct(t, "RespondAddrNotifNoLivreur", 10)
|
||||||
|
cmdID := newTestCommandWithItem(t, username, "pending", "", productID, 1, 10)
|
||||||
|
|
||||||
|
if err := testDB.ProposeAddressChange(cmdID, "12 rue Crebillon, 44000 Nantes", "admin_test"); err != nil {
|
||||||
|
t.Fatalf("ProposeAddressChange: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := []byte(`{"accepted":true}`)
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/commands/address/respond", bytes.NewReader(body))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(rec)
|
||||||
|
c.Request = req
|
||||||
|
c.Set("database", testDB)
|
||||||
|
c.Set("geoService", ensureTestGeoService())
|
||||||
|
c.Set("username", username)
|
||||||
|
c.Set("role", "client")
|
||||||
|
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", cmdID)}}
|
||||||
|
|
||||||
|
handlers.RespondToAddressProposal(c)
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("RespondToAddressProposal doit réussir: got=%d body=%s", rec.Code, rec.Body.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user