@@ -148,8 +148,26 @@ func (d *Database) CreateCommand(username string) (*models.Command, error) {
|
|||||||
return nil, fmt.Errorf("erreur lors de l'insertion des items: %w", err)
|
return nil, fmt.Errorf("erreur lors de l'insertion des items: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := d.GDB.Exec(`DELETE FROM baskets WHERE username = ?`, username).Error; err != nil {
|
// Décrémenter le stock et vider le panier de manière atomique.
|
||||||
return nil, fmt.Errorf("erreur lors du vidage du panier: %w", err)
|
if err := d.GDB.Transaction(func(tx *gorm.DB) error {
|
||||||
|
for _, item := range basketItems {
|
||||||
|
if item.IsReward {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var currentStock float64
|
||||||
|
if err := tx.Raw(`SELECT stock FROM products WHERE id = ? FOR UPDATE`, item.ProductID).Scan(¤tStock).Error; err != nil {
|
||||||
|
return fmt.Errorf("erreur lecture stock produit %d: %w", item.ProductID, err)
|
||||||
|
}
|
||||||
|
if currentStock < item.Quantity {
|
||||||
|
return fmt.Errorf("stock insuffisant pour le produit %d", item.ProductID)
|
||||||
|
}
|
||||||
|
if err := tx.Exec(`UPDATE products SET stock = stock - ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, item.Quantity, item.ProductID).Error; err != nil {
|
||||||
|
return fmt.Errorf("erreur décrémentation stock produit %d: %w", item.ProductID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tx.Exec(`DELETE FROM baskets WHERE username = ?`, username).Error
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
command := &models.Command{
|
command := &models.Command{
|
||||||
@@ -256,8 +274,27 @@ func (d *Database) CreateCommandWithAddress(username, deliveryAddress string) (*
|
|||||||
return nil, fmt.Errorf("erreur insertion items: %w", err)
|
return nil, fmt.Errorf("erreur insertion items: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := d.GDB.Exec(`DELETE FROM baskets WHERE username = ?`, username).Error; err != nil {
|
// Décrémenter le stock et vider le panier de manière atomique.
|
||||||
log.Printf("⚠️ Erreur vidage panier: %v", err)
|
if err := d.GDB.Transaction(func(tx *gorm.DB) error {
|
||||||
|
for _, item := range basketItems {
|
||||||
|
if item.IsReward {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var currentStock float64
|
||||||
|
if err := tx.Raw(`SELECT stock FROM products WHERE id = ? FOR UPDATE`, item.ProductID).Scan(¤tStock).Error; err != nil {
|
||||||
|
return fmt.Errorf("erreur lecture stock produit %d: %w", item.ProductID, err)
|
||||||
|
}
|
||||||
|
if currentStock < item.Quantity {
|
||||||
|
return fmt.Errorf("stock insuffisant pour le produit %d", item.ProductID)
|
||||||
|
}
|
||||||
|
if err := tx.Exec(`UPDATE products SET stock = stock - ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, item.Quantity, item.ProductID).Error; err != nil {
|
||||||
|
return fmt.Errorf("erreur décrémentation stock produit %d: %w", item.ProductID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tx.Exec(`DELETE FROM baskets WHERE username = ?`, username).Error
|
||||||
|
}); err != nil {
|
||||||
|
log.Printf("❌ Erreur décrément stock / vidage panier: %v", err)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sanitizedAddress := sanitizeLogMessage(deliveryAddress)
|
sanitizedAddress := sanitizeLogMessage(deliveryAddress)
|
||||||
|
|||||||
@@ -434,6 +434,10 @@ func ValidateBasket(c *gin.Context) {
|
|||||||
_ = database.CreditClientReferral(usernameStr, referralUsed)
|
_ = database.CreditClientReferral(usernameStr, referralUsed)
|
||||||
}
|
}
|
||||||
log.Printf("❌ [CHECKOUT] Erreur création commande: %v", err)
|
log.Printf("❌ [CHECKOUT] Erreur création commande: %v", err)
|
||||||
|
if strings.Contains(err.Error(), "stock insuffisant") {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Désolé ! Le stock ou le produit n'est plus disponible, repasse commande"})
|
||||||
|
return
|
||||||
|
}
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur création commande"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur création commande"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -447,7 +451,6 @@ func ValidateBasket(c *gin.Context) {
|
|||||||
|
|
||||||
log.Printf("✅ [CHECKOUT] Commande %d créée", commandID)
|
log.Printf("✅ [CHECKOUT] Commande %d créée", commandID)
|
||||||
|
|
||||||
// Pour le paiement crypto, le panier/stock sera décrémenté à la confirmation du webhook NowPayments.
|
|
||||||
if isCrypto {
|
if isCrypto {
|
||||||
np := c.MustGet("nowpayments").(*services.NowPaymentsClient)
|
np := c.MustGet("nowpayments").(*services.NowPaymentsClient)
|
||||||
ipnURL := fmt.Sprintf("%s/api/v1/webhooks/nowpayments", getBaseURL(c))
|
ipnURL := fmt.Sprintf("%s/api/v1/webhooks/nowpayments", getBaseURL(c))
|
||||||
@@ -507,27 +510,7 @@ func ValidateBasket(c *gin.Context) {
|
|||||||
go database.NotifyAllAdminCabine(commandID, usernameStr, req.DeliveryAddress)
|
go database.NotifyAllAdminCabine(commandID, usernameStr, req.DeliveryAddress)
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 3️⃣ Décrémenter le stock et vider le panier
|
// 3️⃣ Auto-assignation livreur (optionnel)
|
||||||
// ============================================
|
|
||||||
err = database.ClearBasketOnCheckout(usernameStr)
|
|
||||||
if err != nil {
|
|
||||||
// Stock insuffisant au moment du checkout (concurrent) → annuler la commande
|
|
||||||
if strings.Contains(err.Error(), "stock insuffisant") {
|
|
||||||
_ = database.CancelCryptoCommand(commandID)
|
|
||||||
if referralUsed > 0 {
|
|
||||||
_ = database.CreditClientReferral(usernameStr, referralUsed)
|
|
||||||
}
|
|
||||||
log.Printf("❌ [CHECKOUT] Stock insuffisant au moment de la validation: %v", err)
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Désolé ! Tu as trop attendu pour passer commande! Le stock ou le produit n'est plus disponible, repasse commande"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
utils.ServerErr(c, "Impossible de valider le panier", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("🧹 [CHECKOUT] Stock décrémenté et panier vidé")
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// 4️⃣ Auto-assignation livreur (optionnel)
|
|
||||||
// ============================================
|
// ============================================
|
||||||
var assigned bool
|
var assigned bool
|
||||||
var assignInfo gin.H
|
var assignInfo gin.H
|
||||||
|
|||||||
Reference in New Issue
Block a user