chore: add corrige adresse
This commit is contained in:
@@ -407,6 +407,51 @@ func (d *Database) GetCommandItemsByUsername(username string) ([]map[string]inte
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// DELETE COMMAND ITEM - ADMIN ONLY
|
||||
// ============================================
|
||||
|
||||
func (d *Database) DeleteCommandItem(commandID, itemID int) error {
|
||||
log.Printf("🗑️ [DeleteCommandItem] START - commandID=%d, itemID=%d", commandID, itemID)
|
||||
|
||||
if err := validateCommandID(commandID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateItemID(itemID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Récupérer le prix et la quantité avant suppression pour mettre à jour le total
|
||||
var prix, quantite float64
|
||||
checkQuery := `SELECT prix, quantite FROM command_items WHERE id = $1 AND command_id = $2`
|
||||
err := d.QueryRow(checkQuery, itemID, commandID).Scan(&prix, &quantite)
|
||||
if err == sql.ErrNoRows {
|
||||
return fmt.Errorf("item %d non trouvé dans la commande %d", itemID, commandID)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur vérification item: %w", err)
|
||||
}
|
||||
|
||||
// Supprimer l'item
|
||||
_, err = d.Exec(`DELETE FROM command_items WHERE id = $1`, itemID)
|
||||
if err != nil {
|
||||
log.Printf("❌ Erreur DELETE command_items: %v", err)
|
||||
return fmt.Errorf("erreur suppression item: %w", err)
|
||||
}
|
||||
|
||||
// Recalculer le total de la commande
|
||||
_, err = d.Exec(
|
||||
`UPDATE commandes SET total_prix = GREATEST(0, total_prix - $1) WHERE id = $2`,
|
||||
prix*quantite, commandID,
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("⚠️ [DeleteCommandItem] Erreur maj total commande: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("✅ [DeleteCommandItem] Item %d supprimé de la commande %d", itemID, commandID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// UPDATE COMMAND ITEM STATUS - VERSION SÉCURISÉE
|
||||
// ============================================
|
||||
|
||||
@@ -48,6 +48,7 @@ func validateCommandStatus(status string) error {
|
||||
"pending": true,
|
||||
"assigned": true,
|
||||
"en_route": true,
|
||||
"arrived": true,
|
||||
"livre": true,
|
||||
"approved": true,
|
||||
"cancelled": true,
|
||||
@@ -275,7 +276,9 @@ func (d *Database) CreateCommandWithAddress(username, deliveryAddress string) (*
|
||||
|
||||
rowsAffected, _ := result.RowsAffected()
|
||||
if rowsAffected == 0 {
|
||||
return nil, fmt.Errorf("stock insuffisant pour produit %d", item.ProductID)
|
||||
// Le stock était déjà réservé lors de l'ajout au panier (DecrementProductStockByID).
|
||||
// On ne bloque pas la commande : tous les articles doivent être insérés.
|
||||
log.Printf("⚠️ [CHECKOUT] Stock déjà réservé pour produit %d (double réservation panier/checkout)", item.ProductID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,7 +327,8 @@ func (d *Database) GetAllCommands(status, username string) ([]map[string]interfa
|
||||
}
|
||||
|
||||
query := `SELECT c.id, c.username, c.status, c.adresse, c.total_prix,
|
||||
c.livreur_assign, c.created_at, c.updated_at
|
||||
c.livreur_assign, c.created_at, c.updated_at,
|
||||
c.proposed_address, c.address_proposal_status
|
||||
FROM commandes c
|
||||
WHERE 1=1`
|
||||
|
||||
@@ -333,7 +337,7 @@ func (d *Database) GetAllCommands(status, username string) ([]map[string]interfa
|
||||
|
||||
// Filtrage du statut
|
||||
if status == "" {
|
||||
query += ` AND c.status IN ('pending', 'assigned', 'en_route', 'livre')`
|
||||
query += ` AND c.status IN ('pending', 'assigned', 'en_route', 'arrived', 'livre')`
|
||||
} else {
|
||||
query += fmt.Sprintf(" AND c.status = $%d", argPosition)
|
||||
args = append(args, status)
|
||||
@@ -362,10 +366,12 @@ func (d *Database) GetAllCommands(status, username string) ([]map[string]interfa
|
||||
var id int
|
||||
var username, status, adresse string
|
||||
var livreurAssign sql.NullString
|
||||
var proposedAddress sql.NullString
|
||||
var addressProposalStatus string
|
||||
var totalPrix float64
|
||||
var createdAt, updatedAt time.Time
|
||||
|
||||
err := rows.Scan(&id, &username, &status, &adresse, &totalPrix, &livreurAssign, &createdAt, &updatedAt)
|
||||
err := rows.Scan(&id, &username, &status, &adresse, &totalPrix, &livreurAssign, &createdAt, &updatedAt, &proposedAddress, &addressProposalStatus)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("erreur lors du scan de la commande: %w", err)
|
||||
}
|
||||
@@ -374,13 +380,14 @@ func (d *Database) GetAllCommands(status, username string) ([]map[string]interfa
|
||||
adresse = sanitizeString(adresse)
|
||||
|
||||
command := map[string]interface{}{
|
||||
"id": id,
|
||||
"username": username,
|
||||
"status": status,
|
||||
"adresse": adresse,
|
||||
"total_prix": totalPrix,
|
||||
"created_at": createdAt,
|
||||
"updated_at": updatedAt,
|
||||
"id": id,
|
||||
"username": username,
|
||||
"status": status,
|
||||
"adresse": adresse,
|
||||
"total_prix": totalPrix,
|
||||
"created_at": createdAt,
|
||||
"updated_at": updatedAt,
|
||||
"address_proposal_status": addressProposalStatus,
|
||||
}
|
||||
|
||||
if livreurAssign.Valid {
|
||||
@@ -389,6 +396,12 @@ func (d *Database) GetAllCommands(status, username string) ([]map[string]interfa
|
||||
command["livreur_assign"] = nil
|
||||
}
|
||||
|
||||
if proposedAddress.Valid {
|
||||
command["proposed_address"] = proposedAddress.String
|
||||
} else {
|
||||
command["proposed_address"] = nil
|
||||
}
|
||||
|
||||
commands = append(commands, command)
|
||||
}
|
||||
|
||||
@@ -412,12 +425,15 @@ func (d *Database) GetCommandCount() (int, error) {
|
||||
|
||||
func (d *Database) GetCommandByID(id int) (map[string]interface{}, error) {
|
||||
// ✅ Déjà sécurisé avec paramètre $1
|
||||
query := `SELECT id, username, status, adresse, total_prix, livreur_assign, created_at, updated_at
|
||||
query := `SELECT id, username, status, adresse, total_prix, livreur_assign, created_at, updated_at,
|
||||
proposed_address, address_proposal_status
|
||||
FROM commandes WHERE id = $1`
|
||||
|
||||
var commandID int
|
||||
var username, status, adresse string
|
||||
var livreurAssign sql.NullString
|
||||
var proposedAddress sql.NullString
|
||||
var addressProposalStatus string
|
||||
var totalPrix float64
|
||||
var createdAt, updatedAt time.Time
|
||||
|
||||
@@ -430,6 +446,8 @@ func (d *Database) GetCommandByID(id int) (map[string]interface{}, error) {
|
||||
&livreurAssign,
|
||||
&createdAt,
|
||||
&updatedAt,
|
||||
&proposedAddress,
|
||||
&addressProposalStatus,
|
||||
)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
@@ -440,13 +458,14 @@ func (d *Database) GetCommandByID(id int) (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
command := map[string]interface{}{
|
||||
"id": commandID,
|
||||
"username": username,
|
||||
"status": status,
|
||||
"adresse": adresse,
|
||||
"total_prix": totalPrix,
|
||||
"created_at": createdAt,
|
||||
"updated_at": updatedAt,
|
||||
"id": commandID,
|
||||
"username": username,
|
||||
"status": status,
|
||||
"adresse": adresse,
|
||||
"total_prix": totalPrix,
|
||||
"created_at": createdAt,
|
||||
"updated_at": updatedAt,
|
||||
"address_proposal_status": addressProposalStatus,
|
||||
}
|
||||
|
||||
if livreurAssign.Valid {
|
||||
@@ -455,6 +474,12 @@ func (d *Database) GetCommandByID(id int) (map[string]interface{}, error) {
|
||||
command["livreur_assign"] = nil
|
||||
}
|
||||
|
||||
if proposedAddress.Valid {
|
||||
command["proposed_address"] = proposedAddress.String
|
||||
} else {
|
||||
command["proposed_address"] = nil
|
||||
}
|
||||
|
||||
return command, nil
|
||||
}
|
||||
|
||||
@@ -503,10 +528,74 @@ func (d *Database) UpdateCommandAddress(commandID int, deliveryAddress string) e
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProposeAddressChange propose une nouvelle adresse (admin/cabine) en attente de validation client
|
||||
func (d *Database) ProposeAddressChange(commandID int, proposedAddress, proposedBy string) error {
|
||||
if err := validateAddress(proposedAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query := `UPDATE commandes
|
||||
SET proposed_address = $1, address_proposal_status = 'pending', updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $2`
|
||||
|
||||
result, err := d.Exec(query, proposedAddress, commandID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur proposition adresse: %w", err)
|
||||
}
|
||||
rowsAffected, _ := result.RowsAffected()
|
||||
if rowsAffected == 0 {
|
||||
return fmt.Errorf("commande non trouvée")
|
||||
}
|
||||
|
||||
d.AddCommandLog(commandID, "address_proposed",
|
||||
fmt.Sprintf("Nouvelle adresse proposée par %s: %s", proposedBy, proposedAddress),
|
||||
proposedBy)
|
||||
|
||||
log.Printf("✅ Adresse proposée pour commande %d par %s", commandID, proposedBy)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RespondToAddressProposal accepte ou refuse la proposition d'adresse
|
||||
func (d *Database) RespondToAddressProposal(commandID int, clientUsername string, accepted bool) error {
|
||||
var query string
|
||||
if accepted {
|
||||
// Remplace l'adresse par la proposition
|
||||
query = `UPDATE commandes
|
||||
SET adresse = proposed_address, proposed_address = NULL,
|
||||
address_proposal_status = 'accepted', updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $1 AND username = $2 AND address_proposal_status = 'pending'`
|
||||
} else {
|
||||
query = `UPDATE commandes
|
||||
SET proposed_address = NULL, address_proposal_status = 'rejected',
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $1 AND username = $2 AND address_proposal_status = 'pending'`
|
||||
}
|
||||
|
||||
result, err := d.Exec(query, commandID, clientUsername)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur réponse proposition adresse: %w", err)
|
||||
}
|
||||
rowsAffected, _ := result.RowsAffected()
|
||||
if rowsAffected == 0 {
|
||||
return fmt.Errorf("aucune proposition en attente pour cette commande")
|
||||
}
|
||||
|
||||
action := "refusée"
|
||||
if accepted {
|
||||
action = "acceptée"
|
||||
}
|
||||
d.AddCommandLog(commandID, "address_proposal_"+action,
|
||||
fmt.Sprintf("Proposition d'adresse %s par le client %s", action, clientUsername),
|
||||
clientUsername)
|
||||
|
||||
log.Printf("✅ Proposition adresse %s pour commande %d", action, commandID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateCommandStatus met à jour le statut d'une commande
|
||||
func (d *Database) UpdateCommandStatus(commandID int, status string) error {
|
||||
// ✅ SÉCURITÉ: Validation du statut
|
||||
validStatuses := []string{"pending", "assigned", "en_route", "livre", "approved", "cancelled", "disabled", "support"}
|
||||
validStatuses := []string{"pending", "assigned", "en_route", "arrived", "livre", "approved", "cancelled", "disabled", "support"}
|
||||
isValid := false
|
||||
for _, vs := range validStatuses {
|
||||
if status == vs {
|
||||
@@ -617,7 +706,7 @@ func (d *Database) GetCommandsWithFilter(status, username string, excludeApprove
|
||||
|
||||
// ✅ Filtrer par status si fourni avec validation
|
||||
if status != "" {
|
||||
validStatuses := []string{"pending", "assigned", "en_route", "livre", "approved", "cancelled", "disabled", "support"}
|
||||
validStatuses := []string{"pending", "assigned", "en_route", "arrived", "livre", "approved", "cancelled", "disabled", "support"}
|
||||
isValid := false
|
||||
for _, vs := range validStatuses {
|
||||
if status == vs {
|
||||
|
||||
@@ -86,6 +86,14 @@ func InitDB() *Database {
|
||||
log.Fatalf("❌ Erreur migration push_token users: %v", err)
|
||||
}
|
||||
|
||||
// Migration: proposition de modification d'adresse par admin/cabine
|
||||
if _, err = database.Exec(`ALTER TABLE commandes ADD COLUMN IF NOT EXISTS proposed_address TEXT`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration proposed_address: %v", err)
|
||||
}
|
||||
if _, err = database.Exec(`ALTER TABLE commandes ADD COLUMN IF NOT EXISTS address_proposal_status VARCHAR(20) NOT NULL DEFAULT 'none'`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration address_proposal_status: %v", err)
|
||||
}
|
||||
|
||||
// Migration: ajouter colonne unit pour l'unité de mesure des produits (kg, g, bag, l, cl, pcs, u)
|
||||
if _, err = database.Exec(`ALTER TABLE products ADD COLUMN IF NOT EXISTS unit VARCHAR(10) NOT NULL DEFAULT 'u'`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration unit products: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user