Files
projet_gestion_commande/backend/gestion/db/db_command_items.go
T
2026-03-28 17:00:00 +01:00

474 lines
14 KiB
Go

package db
import (
"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 float64) 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 int,
quantite float64,
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
if err := d.GDB.Raw(`SELECT EXISTS(SELECT 1 FROM commandes WHERE id = ?)`, commandID).Scan(&exists).Error; 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
err := d.GDB.Exec(`
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
commandID, produit, productID, quantite, prix,
clientUsername, clientNom, clientPrenom, clientTelephone, deliveryAddress,
).Error
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
}
var rows []struct {
ID int `gorm:"column:id"`
CommandID int `gorm:"column:command_id"`
Produit string `gorm:"column:produit"`
ProductID *int64 `gorm:"column:product_id"`
Quantite float64 `gorm:"column:quantite"`
Prix float64 `gorm:"column:prix"`
ClientUsername string `gorm:"column:client_username"`
ClientNom string `gorm:"column:client_nom"`
ClientPrenom string `gorm:"column:client_prenom"`
ClientTelephone string `gorm:"column:client_telephone"`
DeliveryAddress *string `gorm:"column:delivery_address"`
Status *string `gorm:"column:status"`
CreatedAt time.Time `gorm:"column:created_at"`
UpdatedAt time.Time `gorm:"column:updated_at"`
CommandStatus *string `gorm:"column:command_status"`
CommandAddress *string `gorm:"column:command_address"`
TotalPrix float64 `gorm:"column:total_prix"`
ReferralUsed float64 `gorm:"column:referral_used"`
LivreurAssign *string `gorm:"column:livreur_assign"`
CommandCreatedAt *time.Time `gorm:"column:command_created_at"`
Category string `gorm:"column:category"`
}
err := d.GDB.Raw(`
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.referral_used,
c.livreur_assign,
c.created_at as command_created_at,
p.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 = ?
ORDER BY ci.id ASC`, commandID).Scan(&rows).Error
if err != nil {
log.Printf("❌ Erreur query: %v", err)
return nil, fmt.Errorf("erreur récupération items: %w", err)
}
items := make([]map[string]interface{}, 0, len(rows))
for _, row := range rows {
productIDValue := 0
if row.ProductID != nil {
productIDValue = int(*row.ProductID)
}
var commandCreatedAt interface{}
if row.CommandCreatedAt != nil {
commandCreatedAt = *row.CommandCreatedAt
}
item := map[string]interface{}{
"id": row.ID,
"command_id": row.CommandID,
"produit": row.Produit,
"product_id": productIDValue,
"quantite": row.Quantite,
"prix": row.Prix,
"client_username": row.ClientUsername,
"client_nom": row.ClientNom,
"client_prenom": row.ClientPrenom,
"client_telephone": row.ClientTelephone,
"delivery_address": ptrStr(row.DeliveryAddress),
"status": ptrStr(row.Status),
"created_at": row.CreatedAt,
"updated_at": row.UpdatedAt,
// Infos commande
"command_status": ptrStr(row.CommandStatus),
"command_address": ptrStr(row.CommandAddress),
"total_prix": row.TotalPrix,
"referral_used": row.ReferralUsed,
"livreur_assign": ptrStr(row.LivreurAssign),
"command_created_at": commandCreatedAt,
"category": row.Category,
}
items = append(items, item)
}
log.Printf("✅ %d items récupérés avec infos client et catégories", len(items))
return items, nil
}
// ptrStr retourne la valeur d'un *string ou "" si nil
func ptrStr(s *string) string {
if s == nil {
return ""
}
return *s
}
func (d *Database) GetCommandItemsByUsername(username string) ([]map[string]interface{}, error) {
if err := validateUsername(username); err != nil {
log.Printf("❌ [GetCommandItemsByUsername] %v", err)
return nil, err
}
var rows []struct {
ID int `gorm:"column:id"`
CommandID int `gorm:"column:command_id"`
Produit string `gorm:"column:produit"`
ProductID *int64 `gorm:"column:product_id"`
Quantite float64 `gorm:"column:quantite"`
Prix float64 `gorm:"column:prix"`
ClientUsername string `gorm:"column:client_username"`
ClientNom string `gorm:"column:client_nom"`
ClientPrenom string `gorm:"column:client_prenom"`
ClientTelephone string `gorm:"column:client_telephone"`
DeliveryAddress *string `gorm:"column:delivery_address"`
Status *string `gorm:"column:status"`
CreatedAt time.Time `gorm:"column:created_at"`
UpdatedAt time.Time `gorm:"column:updated_at"`
CommandStatus *string `gorm:"column:command_status"`
CommandAddress *string `gorm:"column:command_address"`
TotalPrix float64 `gorm:"column:total_prix"`
LivreurAssign *string `gorm:"column:livreur_assign"`
CommandCreatedAt *time.Time `gorm:"column:command_created_at"`
}
err := d.GDB.Raw(`
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 = ?
ORDER BY ci.command_id DESC, ci.id ASC`, username).Scan(&rows).Error
if err != nil {
log.Printf("❌ Erreur query: %v", err)
return nil, fmt.Errorf("erreur récupération items: %w", err)
}
items := make([]map[string]interface{}, 0, len(rows))
for _, row := range rows {
productIDValue := 0
if row.ProductID != nil {
productIDValue = int(*row.ProductID)
}
var commandCreatedAt interface{}
if row.CommandCreatedAt != nil {
commandCreatedAt = *row.CommandCreatedAt
}
item := map[string]interface{}{
"id": row.ID,
"command_id": row.CommandID,
"produit": row.Produit,
"product_id": productIDValue,
"quantite": row.Quantite,
"prix": row.Prix,
"client_username": row.ClientUsername,
"client_nom": row.ClientNom,
"client_prenom": row.ClientPrenom,
"client_telephone": row.ClientTelephone,
"delivery_address": ptrStr(row.DeliveryAddress),
"status": ptrStr(row.Status),
"created_at": row.CreatedAt,
"updated_at": row.UpdatedAt,
"command_status": ptrStr(row.CommandStatus),
"command_address": ptrStr(row.CommandAddress),
"total_prix": row.TotalPrix,
"livreur_assign": ptrStr(row.LivreurAssign),
"command_created_at": commandCreatedAt,
}
items = append(items, item)
}
log.Printf("✅ %d items récupérés pour l'utilisateur %s", len(items), username)
return items, nil
}
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 result struct {
Prix float64 `gorm:"column:prix"`
Quantite float64 `gorm:"column:quantite"`
}
if err := d.GDB.Raw(`SELECT prix, quantite FROM command_items WHERE id = ? AND command_id = ?`, itemID, commandID).Scan(&result).Error; err != nil {
return fmt.Errorf("erreur vérification item: %w", err)
}
if result.Prix == 0 && result.Quantite == 0 {
return fmt.Errorf("item %d non trouvé dans la commande %d", itemID, commandID)
}
// Supprimer l'item
if err := d.GDB.Exec(`DELETE FROM command_items WHERE id = ?`, itemID).Error; err != nil {
log.Printf("❌ Erreur DELETE command_items: %v", err)
return fmt.Errorf("erreur suppression item: %w", err)
}
// Recalculer le total de la commande
if err := d.GDB.Exec(
`UPDATE commandes SET total_prix = GREATEST(0, total_prix - ?) WHERE id = ?`,
result.Prix*result.Quantite, commandID,
).Error; err != nil {
log.Printf("⚠️ [DeleteCommandItem] Erreur maj total commande: %v", err)
}
return nil
}
func (d *Database) UpdateCommandItemStatus(itemID int, status string) error {
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
}
var exists bool
if err := d.GDB.Raw(`SELECT EXISTS(SELECT 1 FROM command_items WHERE id = ?)`, itemID).Scan(&exists).Error; 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)
}
result := d.GDB.Exec(`
UPDATE command_items
SET status = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?`, status, itemID)
if result.Error != nil {
log.Printf("❌ Erreur UPDATE: %v", result.Error)
return fmt.Errorf("erreur mise à jour statut: %w", result.Error)
}
if result.RowsAffected == 0 {
log.Printf("❌ Item %d non trouvé", itemID)
return fmt.Errorf("item non trouvé")
}
return nil
}