package db import ( "database/sql" "fmt" "log" "slices" "time" ) // GetAvailableDeliveryPersons récupère tous les livreurs disponibles func (d *Database) GetAvailableDeliveryPersons() ([]map[string]any, error) { query := `SELECT id, username, total, livraison FROM users WHERE role = 'livreur' ORDER BY username` rows, err := d.Query(query) if err != nil { return nil, fmt.Errorf("erreur lors de la récupération des livreurs: %w", err) } defer rows.Close() var livreurs []map[string]any for rows.Next() { var id int var username string var total, livraison float64 err := rows.Scan(&id, &username, &total, &livraison) if err != nil { return nil, fmt.Errorf("erreur lors du scan du livreur: %w", err) } livreur := map[string]any{ "id": id, "username": username, "total": total, "livraison": livraison, } livreurs = append(livreurs, livreur) } 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) tx, err := d.Begin() if err != nil { log.Printf("❌ Erreur démarrage transaction: %v", err) return fmt.Errorf("erreur démarrage transaction: %w", err) } defer tx.Rollback() var role string checkQuery := `SELECT role FROM users WHERE username = $1 FOR UPDATE` err = tx.QueryRow(checkQuery, livreurUsername).Scan(&role) if err == sql.ErrNoRows { log.Printf("❌ Livreur '%s' non trouvé", livreurUsername) return fmt.Errorf("livreur non trouvé") } 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 role != "livreur" { log.Printf("❌ L'utilisateur '%s' n'est pas un livreur (role=%s)", livreurUsername, role) return fmt.Errorf("l'utilisateur n'est pas un livreur") } var currentStatus string var currentLivreur sql.NullString statusQuery := `SELECT status, livreur_assign FROM commandes WHERE id = $1 FOR UPDATE` err = tx.QueryRow(statusQuery, commandID).Scan(¤tStatus, ¤tLivreur) if err == sql.ErrNoRows { log.Printf("❌ Commande %d non trouvée", commandID) return fmt.Errorf("commande non trouvée") } 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) } validStatusesForAssignment := []string{"pending", "assigned"} isValidStatus := slices.Contains(validStatusesForAssignment, currentStatus) if !isValidStatus { log.Printf("❌ Statut invalide pour assignation: %s", currentStatus) return fmt.Errorf("commande en statut '%s', impossible d'assigner un livreur", currentStatus) } updateQuery := `UPDATE commandes SET livreur_assign = $1, status = 'assigned', updated_at = CURRENT_TIMESTAMP WHERE id = $2 AND status IN ('pending', 'assigned')` result, err := tx.Exec(updateQuery, livreurUsername, commandID) if err != nil { log.Printf("❌ Erreur UPDATE: %v", err) return fmt.Errorf("erreur lors de l'assignation du livreur: %w", err) } rowsAffected, err := result.RowsAffected() if err != nil { log.Printf("❌ Erreur RowsAffected: %v", err) return fmt.Errorf("erreur lors de la vérification: %w", err) } if 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é)") } logQuery := `INSERT INTO command_logs (command_id, status, message, author, created_at) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP)` _, err = tx.Exec(logQuery, commandID, "assigned", fmt.Sprintf("Livraison assignée au livreur %s", livreurUsername), "admin") if err != nil { log.Printf("⚠️ Erreur ajout log: %v", err) // Non bloquant } err = tx.Commit() if err != nil { log.Printf("❌ Erreur COMMIT: %v", err) return fmt.Errorf("erreur commit transaction: %w", err) } return nil } // GetDeliveryPersonCommands récupère les commandes assignées à un livreur func (d *Database) GetDeliveryPersonCommands(livreurUsername string, status string) ([]map[string]any, error) { query := `SELECT id, username, status, adresse, total_prix, livreur_assign, created_at, updated_at FROM commandes WHERE livreur_assign = $1` args := []any{livreurUsername} if status != "" { query += " AND status = $2" args = append(args, status) } query += " ORDER BY created_at DESC" rows, err := d.Query(query, args...) if err != nil { return nil, fmt.Errorf("erreur lors de la récupération des commandes: %w", err) } defer rows.Close() var commands []map[string]any for rows.Next() { var id int var username, status, adresse string var livreurAssign sql.NullString var totalPrix float64 var createdAt, updatedAt time.Time err := rows.Scan(&id, &username, &status, &adresse, &totalPrix, &livreurAssign, &createdAt, &updatedAt) if err != nil { return nil, fmt.Errorf("erreur lors du scan: %w", err) } command := map[string]any{ "id": id, "username": username, "status": status, "adresse": adresse, "total_prix": totalPrix, "livreur_assign": livreurAssign.String, "created_at": createdAt, "updated_at": updatedAt, } commands = append(commands, command) } return commands, nil } func (d *Database) IncrementLivreurDeliveryCount(livreurUsername string) error { query := `UPDATE users SET livraison = livraison + 1, total = total + 1, updated_at = CURRENT_TIMESTAMP WHERE username = $1 AND role = 'livreur'` result, err := d.Exec(query, livreurUsername) if err != nil { return fmt.Errorf("erreur lors de l'incrémentation des livraisons: %w", err) } rowsAffected, err := result.RowsAffected() if err != nil { return fmt.Errorf("erreur lors de la vérification: %w", err) } if rowsAffected == 0 { return fmt.Errorf("livreur non trouvé") } return nil } func (d *Database) ApproveDelivery(commandID int, clientUsername string) error { tx, err := d.Begin() if err != nil { log.Printf("❌ Erreur démarrage transaction: %v", err) return fmt.Errorf("erreur démarrage transaction: %w", err) } defer tx.Rollback() var commandUsername, currentStatus string var livreurAssign sql.NullString checkQuery := `SELECT username, status, livreur_assign FROM commandes WHERE id = $1 FOR UPDATE` err = tx.QueryRow(checkQuery, commandID).Scan(&commandUsername, ¤tStatus, &livreurAssign) if err == sql.ErrNoRows { return fmt.Errorf("commande non trouvée") } if err != nil { return fmt.Errorf("erreur lors de la vérification de la commande: %w", err) } if commandUsername != clientUsername { return fmt.Errorf("cette commande ne vous appartient pas") } if currentStatus != "livre" { return fmt.Errorf("cette commande n'est pas encore livrée (statut actuel: %s)", currentStatus) } updateQuery := `UPDATE commandes SET status = 'approved', updated_at = CURRENT_TIMESTAMP WHERE id = $1 AND status = 'livre' AND username = $2` result, err := tx.Exec(updateQuery, commandID, clientUsername) if err != nil { return fmt.Errorf("erreur lors de l'approbation de la livraison: %w", err) } rowsAffected, err := result.RowsAffected() if err != nil { return fmt.Errorf("erreur lors de la vérification: %w", err) } if rowsAffected == 0 { return fmt.Errorf("impossible d'approuver: statut changé ou commande introuvable") } if livreurAssign.Valid && livreurAssign.String != "" { incrementQuery := `UPDATE users SET livraison = livraison + 1, total = total + 1, updated_at = CURRENT_TIMESTAMP WHERE username = $1 AND role = 'livreur'` result, err := tx.Exec(incrementQuery, livreurAssign.String) if err != nil { log.Printf("⚠️ Erreur incrémentation livreur: %v", err) } else { rows, _ := result.RowsAffected() if rows > 0 { log.Printf(" ✅ Compteur livreur incrémenté: %s", livreurAssign.String) } } } logQuery := `INSERT INTO command_logs (command_id, status, message, author, created_at) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP)` _, err = tx.Exec(logQuery, commandID, "approved", fmt.Sprintf("Livraison approuvée par le client %s", clientUsername), clientUsername) if err != nil { log.Printf("⚠️ Erreur ajout log: %v", err) } err = tx.Commit() if err != nil { log.Printf("❌ Erreur COMMIT: %v", err) return fmt.Errorf("erreur commit transaction: %w", err) } return nil }