package db import ( "fmt" "log" "slices" "gorm.io/gorm" ) // GetAvailableDeliveryPersons récupère tous les livreurs disponibles func (d *Database) GetAvailableDeliveryPersons() ([]map[string]any, error) { var livreurs []map[string]any err := d.GDB.Raw(` SELECT id, username, total, livraison FROM users WHERE role = 'livreur' ORDER BY username`).Scan(&livreurs).Error if err != nil { return nil, fmt.Errorf("erreur lors de la récupération des livreurs: %w", err) } return livreurs, nil } // Admin appelle cette fonction pour assigner un livreur func (d *Database) AssignDeliveryPerson(commandID int, livreurUsername string) error { log.Printf("📦 [AssignDeliveryPerson] START - commandID=%d, livreur=%s", commandID, livreurUsername) return d.GDB.Transaction(func(tx *gorm.DB) error { var roleResult struct { Role string `gorm:"column:role"` } err := tx.Raw(`SELECT role FROM users WHERE username = ? FOR UPDATE`, livreurUsername).Scan(&roleResult).Error if err != nil { log.Printf("❌ Erreur vérification livreur: %v", err) return fmt.Errorf("erreur lors de la vérification du livreur: %w", err) } if roleResult.Role == "" { log.Printf("❌ Livreur '%s' non trouvé", livreurUsername) return fmt.Errorf("livreur non trouvé") } if roleResult.Role != "livreur" { log.Printf("❌ L'utilisateur '%s' n'est pas un livreur (role=%s)", livreurUsername, roleResult.Role) return fmt.Errorf("l'utilisateur n'est pas un livreur") } var cmdResult struct { Status string `gorm:"column:status"` LivreurAssign *string `gorm:"column:livreur_assign"` } err = tx.Raw(`SELECT status, livreur_assign FROM commandes WHERE id = ? FOR UPDATE`, commandID).Scan(&cmdResult).Error if err != nil { log.Printf("❌ Erreur vérification commande: %v", err) return fmt.Errorf("erreur lors de la vérification de la commande: %w", err) } if cmdResult.Status == "" { log.Printf("❌ Commande %d non trouvée", commandID) return fmt.Errorf("commande non trouvée") } validStatusesForAssignment := []string{"pending", "assigned"} if !slices.Contains(validStatusesForAssignment, cmdResult.Status) { log.Printf("❌ Statut invalide pour assignation: %s", cmdResult.Status) return fmt.Errorf("commande en statut '%s', impossible d'assigner un livreur", cmdResult.Status) } result := tx.Exec(` UPDATE commandes SET livreur_assign = ?, status = 'assigned', updated_at = CURRENT_TIMESTAMP WHERE id = ? AND status IN ('pending', 'assigned')`, livreurUsername, commandID) if result.Error != nil { log.Printf("❌ Erreur UPDATE: %v", result.Error) return fmt.Errorf("erreur lors de l'assignation du livreur: %w", result.Error) } if result.RowsAffected == 0 { log.Printf("❌ Impossible d'assigner: conditions non remplies") return fmt.Errorf("impossible d'assigner la commande (déjà assignée ou statut changé)") } if err := tx.Exec(` INSERT INTO command_logs (command_id, status, message, author, created_at) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)`, commandID, "assigned", fmt.Sprintf("Livraison assignée au livreur %s", livreurUsername), "admin", ).Error; err != nil { log.Printf("⚠️ Erreur ajout log: %v", err) // Non bloquant } return nil }) } func (d *Database) GetDeliveryPersonCommands(livreurUsername string, status string) ([]map[string]any, error) { query := `SELECT id, username, status, adresse, total_prix::float8 as total_prix, livreur_assign, created_at, updated_at FROM commandes WHERE livreur_assign = ?` args := []any{livreurUsername} if status != "" { query += " AND status = ?" args = append(args, status) } else { // Ne retourner que les commandes actives — exclure l'historique terminé // pour éviter le N+1 sur 66+ commandes qui fait timeout le mobile query += " AND status IN ('assigned', 'en_route', 'arrived', 'livre')" } query += " ORDER BY created_at DESC" var commands []map[string]any if err := d.GDB.Raw(query, args...).Scan(&commands).Error; err != nil { return nil, fmt.Errorf("erreur lors de la récupération des commandes: %w", err) } return commands, nil } func (d *Database) ApproveDelivery(commandID int, clientUsername string) error { return d.GDB.Transaction(func(tx *gorm.DB) error { var cmdResult struct { Username string `gorm:"column:username"` Status string `gorm:"column:status"` LivreurAssign *string `gorm:"column:livreur_assign"` } err := tx.Raw(` SELECT username, status, livreur_assign FROM commandes WHERE id = ? FOR UPDATE`, commandID).Scan(&cmdResult).Error if err != nil { return fmt.Errorf("erreur lors de la vérification de la commande: %w", err) } if cmdResult.Username == "" { return fmt.Errorf("commande non trouvée") } if cmdResult.Username != clientUsername { return fmt.Errorf("cette commande ne vous appartient pas") } if cmdResult.Status != "livre" { return fmt.Errorf("cette commande n'est pas encore livrée (statut actuel: %s)", cmdResult.Status) } result := tx.Exec(` UPDATE commandes SET status = 'approved', updated_at = CURRENT_TIMESTAMP WHERE id = ? AND status = 'livre' AND username = ?`, commandID, clientUsername) if result.Error != nil { return fmt.Errorf("erreur lors de l'approbation de la livraison: %w", result.Error) } if result.RowsAffected == 0 { return fmt.Errorf("impossible d'approuver: statut changé ou commande introuvable") } if cmdResult.LivreurAssign != nil && *cmdResult.LivreurAssign != "" { res := tx.Exec(` UPDATE users SET livraison = livraison + 1, total = total + 1, updated_at = CURRENT_TIMESTAMP WHERE username = ? AND role = 'livreur'`, *cmdResult.LivreurAssign) if res.Error != nil { log.Printf("⚠️ Erreur incrémentation livreur: %v", res.Error) } else if res.RowsAffected > 0 { log.Printf(" ✅ Compteur livreur incrémenté: %s", *cmdResult.LivreurAssign) } } if err := tx.Exec(` INSERT INTO command_logs (command_id, status, message, author, created_at) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)`, commandID, "approved", fmt.Sprintf("Livraison approuvée par le client %s", clientUsername), clientUsername, ).Error; err != nil { log.Printf("⚠️ Erreur ajout log: %v", err) } return nil }) }