Files
projet_gestion_commande/backend/gestion/db/db_command_items.go
T

462 lines
12 KiB
Go

package db
import (
"database/sql"
"fmt"
"log"
"strings"
"time"
)
// ============================================
// VALIDATION HELPERS
// ============================================
func validateCommandID(commandID int) error {
if commandID <= 0 {
return fmt.Errorf("ID commande invalide: %d", commandID)
}
if commandID > 2147483647 {
return fmt.Errorf("ID commande trop grand")
}
return nil
}
func validateItemID(itemID int) error {
if itemID <= 0 {
return fmt.Errorf("ID item invalide: %d", itemID)
}
if itemID > 2147483647 {
return fmt.Errorf("ID item trop grand")
}
return nil
}
func validateQuantite(quantite int) error {
if quantite <= 0 {
return fmt.Errorf("quantité doit être > 0")
}
if quantite > 10000 {
return fmt.Errorf("quantité trop élevée (max 10000)")
}
return nil
}
func validatePrix(prix float64) error {
if prix <= 0 {
return fmt.Errorf("prix doit être > 0")
}
if prix > 100000 {
return fmt.Errorf("prix trop élevé (max 100000)")
}
return nil
}
func validateUsername(username string) error {
if len(username) == 0 {
return fmt.Errorf("username vide")
}
if len(username) > 100 {
return fmt.Errorf("username trop long (max 100)")
}
// Sanitize
if strings.Contains(username, "..") || strings.Contains(username, "/") {
return fmt.Errorf("username invalide")
}
return nil
}
func validateDeliveryAddress(address string) error {
if len(address) == 0 {
return fmt.Errorf("adresse vide")
}
if len(address) > 500 {
return fmt.Errorf("adresse trop longue (max 500)")
}
return nil
}
func validateItemStatus(status string) error {
validStatuses := []string{"pending", "assigned", "en_route", "livre", "approved", "cancelled"}
status = strings.ToLower(strings.TrimSpace(status))
for _, valid := range validStatuses {
if status == valid {
return nil
}
}
return fmt.Errorf("statut invalide: %s", status)
}
// ============================================
// INSERT COMMAND ITEM - VERSION SÉCURISÉE
// ============================================
func (d *Database) InsertCommandItemWithClientInfo(
commandID int,
produit string,
productID, quantite int,
prix float64,
clientUsername, clientNom, clientPrenom, clientTelephone, deliveryAddress string,
) error {
log.Printf("📝 [InsertCommandItemWithClientInfo] START - commandID=%d, produit=%s", commandID, produit)
// ✅ VALIDATION COMPLÈTE
if err := validateCommandID(commandID); err != nil {
return err
}
if err := validateProductID(productID); err != nil {
return err
}
if err := validateQuantite(quantite); err != nil {
return err
}
if err := validatePrix(prix); err != nil {
return err
}
if err := validateUsername(clientUsername); err != nil {
return err
}
if err := validateDeliveryAddress(deliveryAddress); err != nil {
return err
}
// ✅ SANITIZE STRINGS
produit = strings.TrimSpace(produit)
if len(produit) > 200 {
produit = produit[:200]
}
clientNom = strings.TrimSpace(clientNom)
if len(clientNom) > 100 {
clientNom = clientNom[:100]
}
clientPrenom = strings.TrimSpace(clientPrenom)
if len(clientPrenom) > 100 {
clientPrenom = clientPrenom[:100]
}
clientTelephone = strings.TrimSpace(clientTelephone)
if len(clientTelephone) > 20 {
clientTelephone = clientTelephone[:20]
}
// ✅ VÉRIFIER QUE LA COMMANDE EXISTE
var exists bool
checkQuery := `SELECT EXISTS(SELECT 1 FROM commandes WHERE id = $1)`
err := d.QueryRow(checkQuery, commandID).Scan(&exists)
if err != nil {
log.Printf("❌ Erreur vérification commande: %v", err)
return fmt.Errorf("erreur vérification commande: %w", err)
}
if !exists {
return fmt.Errorf("commande %d n'existe pas", commandID)
}
// ✅ INSERT
query := `INSERT INTO command_items (
command_id, produit, product_id, quantite, prix,
client_username, client_nom, client_prenom, client_telephone, delivery_address,
status, created_at, updated_at
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'pending', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`
_, err = d.Exec(query,
commandID, produit, productID, quantite, prix,
clientUsername, clientNom, clientPrenom, clientTelephone, deliveryAddress,
)
if err != nil {
log.Printf("❌ Erreur INSERT command_items: %v", err)
return fmt.Errorf("erreur insertion item: %w", err)
}
log.Printf("✅ Item inséré avec infos client: %s %s", clientNom, clientPrenom)
return nil
}
// ============================================
// GET COMMAND ITEMS - VERSION SÉCURISÉE + FIX NULL
// ============================================
func (d *Database) GetCommandItems(commandID int) ([]map[string]interface{}, error) {
log.Printf("📦 [GetCommandItems] START - commandID=%d", commandID)
// ✅ VALIDATION
if err := validateCommandID(commandID); err != nil {
log.Printf("❌ [GetCommandItems] %v", err)
return nil, err
}
query := `
SELECT
ci.id,
ci.command_id,
ci.produit,
ci.product_id,
ci.quantite,
ci.prix,
ci.client_username,
ci.client_nom,
ci.client_prenom,
ci.client_telephone,
ci.delivery_address,
ci.status,
ci.created_at,
ci.updated_at,
c.status as command_status,
c.adresse as command_address,
c.total_prix,
c.livreur_assign,
c.created_at as command_created_at,
COALESCE(p.category, 'weed_hash') as category
FROM command_items ci
LEFT JOIN commandes c ON ci.command_id = c.id
LEFT JOIN products p ON ci.product_id = p.id
WHERE ci.command_id = $1
ORDER BY ci.id ASC`
rows, err := d.Query(query, commandID)
if err != nil {
log.Printf("❌ Erreur query: %v", err)
return nil, fmt.Errorf("erreur récupération items: %w", err)
}
defer rows.Close()
var items []map[string]interface{}
for rows.Next() {
var id, commandID, quantite int
var productID sql.NullInt64 // ✅ FIX: Utiliser NullInt64 pour gérer NULL
var produit, clientUsername, clientNom, clientPrenom, clientTelephone string
var deliveryAddress, status sql.NullString
var prix, totalPrix float64
var createdAt, updatedAt, commandCreatedAt time.Time
var commandStatus, commandAddress, livreurAssign sql.NullString
var category string
// ✅ FIX: Utiliser &productID (sql.NullInt64)
err := rows.Scan(
&id, &commandID, &produit, &productID, &quantite, &prix,
&clientUsername, &clientNom, &clientPrenom, &clientTelephone, &deliveryAddress, &status,
&createdAt, &updatedAt,
&commandStatus, &commandAddress, &totalPrix, &livreurAssign, &commandCreatedAt,
&category,
)
if err != nil {
log.Printf("❌ Erreur scan: %v", err)
return nil, fmt.Errorf("erreur scan: %w", err)
}
// ✅ CONVERTIR sql.NullInt64 en int (0 si NULL)
productIDValue := 0
if productID.Valid {
productIDValue = int(productID.Int64)
}
item := map[string]interface{}{
"id": id,
"command_id": commandID,
"produit": produit,
"product_id": productIDValue, // ✅ FIX: Utiliser la valeur convertie
"quantite": quantite,
"prix": prix,
"client_username": clientUsername,
"client_nom": clientNom,
"client_prenom": clientPrenom,
"client_telephone": clientTelephone,
"delivery_address": deliveryAddress.String,
"status": status.String,
"created_at": createdAt,
"updated_at": updatedAt,
// Infos commande
"command_status": commandStatus.String,
"command_address": commandAddress.String,
"total_prix": totalPrix,
"livreur_assign": livreurAssign.String,
"command_created_at": commandCreatedAt,
"category": category,
}
items = append(items, item)
}
if err = rows.Err(); err != nil {
log.Printf("❌ Erreur itération: %v", err)
return nil, fmt.Errorf("erreur itération: %w", err)
}
log.Printf("✅ %d items récupérés avec infos client et catégories", len(items))
return items, nil
}
// ============================================
// GET COMMAND ITEMS BY USERNAME - VERSION SÉCURISÉE + FIX NULL
// ============================================
func (d *Database) GetCommandItemsByUsername(username string) ([]map[string]interface{}, error) {
log.Printf("📦 [GetCommandItemsByUsername] START - username=%s", username)
// ✅ VALIDATION
if err := validateUsername(username); err != nil {
log.Printf("❌ [GetCommandItemsByUsername] %v", err)
return nil, err
}
query := `
SELECT
ci.id,
ci.command_id,
ci.produit,
ci.product_id,
ci.quantite,
ci.prix,
ci.client_username,
ci.client_nom,
ci.client_prenom,
ci.client_telephone,
ci.delivery_address,
ci.status,
ci.created_at,
ci.updated_at,
c.status as command_status,
c.adresse as command_address,
c.total_prix,
c.livreur_assign,
c.created_at as command_created_at
FROM command_items ci
LEFT JOIN commandes c ON ci.command_id = c.id
WHERE ci.client_username = $1
ORDER BY ci.command_id DESC, ci.id ASC`
rows, err := d.Query(query, username)
if err != nil {
log.Printf("❌ Erreur query: %v", err)
return nil, fmt.Errorf("erreur récupération items: %w", err)
}
defer rows.Close()
var items []map[string]interface{}
for rows.Next() {
var id, commandID, quantite int
var productID sql.NullInt64 // ✅ FIX: NullInt64
var produit, clientUsername, clientNom, clientPrenom, clientTelephone string
var deliveryAddress, status sql.NullString
var prix, totalPrix float64
var createdAt, updatedAt, commandCreatedAt time.Time
var commandStatus, commandAddress, livreurAssign sql.NullString
err := rows.Scan(
&id, &commandID, &produit, &productID, &quantite, &prix,
&clientUsername, &clientNom, &clientPrenom, &clientTelephone, &deliveryAddress, &status,
&createdAt, &updatedAt,
&commandStatus, &commandAddress, &totalPrix, &livreurAssign, &commandCreatedAt,
)
if err != nil {
log.Printf("❌ Erreur scan: %v", err)
return nil, fmt.Errorf("erreur scan: %w", err)
}
// ✅ CONVERTIR NullInt64
productIDValue := 0
if productID.Valid {
productIDValue = int(productID.Int64)
}
item := map[string]interface{}{
"id": id,
"command_id": commandID,
"produit": produit,
"product_id": productIDValue, // ✅ FIX
"quantite": quantite,
"prix": prix,
"client_username": clientUsername,
"client_nom": clientNom,
"client_prenom": clientPrenom,
"client_telephone": clientTelephone,
"delivery_address": deliveryAddress.String,
"status": status.String,
"created_at": createdAt,
"updated_at": updatedAt,
// Infos commande
"command_status": commandStatus.String,
"command_address": commandAddress.String,
"total_prix": totalPrix,
"livreur_assign": livreurAssign.String,
"command_created_at": commandCreatedAt,
}
items = append(items, item)
}
if err = rows.Err(); err != nil {
log.Printf("❌ Erreur itération: %v", err)
return nil, fmt.Errorf("erreur itération: %w", err)
}
log.Printf("✅ %d items récupérés pour l'utilisateur %s", len(items), username)
return items, nil
}
// ============================================
// UPDATE COMMAND ITEM STATUS - VERSION SÉCURISÉE
// ============================================
func (d *Database) UpdateCommandItemStatus(itemID int, status string) error {
log.Printf("📝 [UpdateCommandItemStatus] START - itemID=%d, status=%s", itemID, status)
// ✅ VALIDATION
if err := validateItemID(itemID); err != nil {
log.Printf("❌ [UpdateCommandItemStatus] %v", err)
return err
}
if err := validateItemStatus(status); err != nil {
log.Printf("❌ [UpdateCommandItemStatus] %v", err)
return err
}
// ✅ VÉRIFIER QUE L'ITEM EXISTE
var exists bool
checkQuery := `SELECT EXISTS(SELECT 1 FROM command_items WHERE id = $1)`
err := d.QueryRow(checkQuery, itemID).Scan(&exists)
if err != nil {
log.Printf("❌ Erreur vérification: %v", err)
return fmt.Errorf("erreur vérification item: %w", err)
}
if !exists {
log.Printf("❌ Item %d non trouvé", itemID)
return fmt.Errorf("item %d non trouvé", itemID)
}
// ✅ UPDATE
query := `UPDATE command_items
SET status = $1, updated_at = CURRENT_TIMESTAMP
WHERE id = $2`
result, err := d.Exec(query, status, itemID)
if err != nil {
log.Printf("❌ Erreur UPDATE: %v", err)
return fmt.Errorf("erreur mise à jour statut: %w", err)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
log.Printf("❌ Erreur RowsAffected: %v", err)
return fmt.Errorf("erreur vérification: %w", err)
}
if rowsAffected == 0 {
log.Printf("❌ Item %d non trouvé", itemID)
return fmt.Errorf("item non trouvé")
}
log.Printf("✅ Statut item %d mis à jour: %s", itemID, status)
return nil
}