fix: manage stock
This commit is contained in:
+47
-134
@@ -3,8 +3,6 @@ package db
|
||||
import (
|
||||
"fmt"
|
||||
"gestion/models"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -61,78 +59,6 @@ func (d *Database) AddProductInBasket(username, nameProduct string, quantity flo
|
||||
return &basket, nil
|
||||
}
|
||||
|
||||
// GetProductPriceByID récupère le prix d'un produit par son ID et quantité
|
||||
func (d *Database) GetProductPriceByID(productID int, quantity float64) (float64, error) {
|
||||
var result struct {
|
||||
Price float64 `gorm:"column:price"`
|
||||
}
|
||||
|
||||
err := d.GDB.Raw(`
|
||||
SELECT price FROM product_prices
|
||||
WHERE product_id = ? AND quantity = ROUND(?::NUMERIC, 3)
|
||||
LIMIT 1`, productID, quantity).Scan(&result).Error
|
||||
if err == nil && result.Price > 0 {
|
||||
return result.Price, nil
|
||||
}
|
||||
|
||||
err = d.GDB.Raw(`
|
||||
SELECT price FROM product_prices
|
||||
WHERE product_id = ? AND quantity <= ROUND(?::NUMERIC, 3)
|
||||
ORDER BY quantity DESC LIMIT 1`, productID, quantity).Scan(&result).Error
|
||||
if err != nil || result.Price == 0 {
|
||||
return 0, fmt.Errorf("aucun prix trouvé pour product_id=%d qty=%.3f", productID, quantity)
|
||||
}
|
||||
return result.Price, nil
|
||||
}
|
||||
|
||||
// GetProductStockByID récupère le stock d'un produit par son ID
|
||||
func (d *Database) GetProductStockByID(productID int) (float64, error) {
|
||||
var result struct {
|
||||
Stock float64 `gorm:"column:stock"`
|
||||
}
|
||||
err := d.GDB.Raw(`SELECT stock FROM products WHERE id = ?`, productID).Scan(&result).Error
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("produit %d non trouvé: %w", productID, err)
|
||||
}
|
||||
return result.Stock, nil
|
||||
}
|
||||
|
||||
// AddProductInBasketByID ajoute un produit au panier en utilisant son ID directement
|
||||
func (d *Database) AddProductInBasketByID(username string, productID int, quantity float64) (*models.Panier, error) {
|
||||
price, err := d.GetProductPriceByID(productID, quantity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("erreur récupération prix: %w", err)
|
||||
}
|
||||
|
||||
var existing struct {
|
||||
ID int `gorm:"column:id"`
|
||||
Quantity float64 `gorm:"column:quantity"`
|
||||
Price float64 `gorm:"column:price"`
|
||||
}
|
||||
d.GDB.Raw(`SELECT id, quantity, price FROM baskets WHERE username = ? AND product_id = ?`,
|
||||
username, productID).Scan(&existing)
|
||||
|
||||
var basket models.Panier
|
||||
if existing.ID != 0 {
|
||||
newQuantity := existing.Quantity + quantity
|
||||
newPrice := existing.Price + price
|
||||
err = d.GDB.Raw(`
|
||||
UPDATE baskets SET quantity = ?, price = ?, created_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ? RETURNING id, username, product_id, quantity, price, created_at`,
|
||||
newQuantity, newPrice, existing.ID).Scan(&basket).Error
|
||||
} else {
|
||||
err = d.GDB.Raw(`
|
||||
INSERT INTO baskets (username, product_id, quantity, price, created_at)
|
||||
VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
|
||||
RETURNING id, username, product_id, quantity, price, created_at`,
|
||||
username, productID, quantity, price).Scan(&basket).Error
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("erreur panier: %w", err)
|
||||
}
|
||||
return &basket, nil
|
||||
}
|
||||
|
||||
// GetProductPrice récupère le prix réel d'un produit pour une quantité donnée (legacy)
|
||||
func (d *Database) GetProductPrice(name, category string, quantity float64) (float64, error) {
|
||||
var result struct {
|
||||
@@ -195,18 +121,54 @@ func (d *Database) GetAllProductsInBasket(username string) ([]models.Panier, err
|
||||
return baskets, nil
|
||||
}
|
||||
|
||||
// DecrementProductStockByID décrémente le stock d'un produit par son ID
|
||||
func (d *Database) DecrementProductStockByID(productID int, quantity float64) error {
|
||||
result := d.GDB.Exec(`
|
||||
UPDATE products SET stock = stock - ?
|
||||
WHERE id = ? AND stock >= ?`, quantity, productID, quantity)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("erreur lors de la mise à jour du stock: %w", result.Error)
|
||||
func (d *Database) DecrementAndAddToBasket(username string, productID int, quantity float64) (*models.Panier, error) {
|
||||
var basket models.Panier
|
||||
err := d.GDB.Transaction(func(tx *gorm.DB) error {
|
||||
result := tx.Exec(`UPDATE products SET stock = stock - ? WHERE id = ? AND stock >= ?`,
|
||||
quantity, productID, quantity)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("erreur stock: %w", result.Error)
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return fmt.Errorf("stock insuffisant")
|
||||
}
|
||||
|
||||
var priceResult struct {
|
||||
Price float64 `gorm:"column:price"`
|
||||
}
|
||||
if err := tx.Raw(`
|
||||
SELECT price FROM product_prices
|
||||
WHERE product_id = ? AND quantity <= ? AND active_price = true
|
||||
ORDER BY quantity DESC LIMIT 1`,
|
||||
productID, quantity).Scan(&priceResult).Error; err != nil || priceResult.Price == 0 {
|
||||
return fmt.Errorf("prix introuvable pour product_id=%d qty=%.3f", productID, quantity)
|
||||
}
|
||||
|
||||
var existing struct {
|
||||
ID int `gorm:"column:id"`
|
||||
Quantity float64 `gorm:"column:quantity"`
|
||||
Price float64 `gorm:"column:price"`
|
||||
}
|
||||
tx.Raw(`SELECT id, quantity, price FROM baskets WHERE username = ? AND product_id = ?`,
|
||||
username, productID).Scan(&existing)
|
||||
|
||||
if existing.ID != 0 {
|
||||
return tx.Raw(`
|
||||
UPDATE baskets SET quantity = ?, price = ?, created_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ? RETURNING id, username, product_id, quantity, price, created_at`,
|
||||
existing.Quantity+quantity, existing.Price+priceResult.Price,
|
||||
existing.ID).Scan(&basket).Error
|
||||
}
|
||||
return tx.Raw(`
|
||||
INSERT INTO baskets (username, product_id, quantity, price, created_at)
|
||||
VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
|
||||
RETURNING id, username, product_id, quantity, price, created_at`,
|
||||
username, productID, quantity, priceResult.Price).Scan(&basket).Error
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return fmt.Errorf("stock insuffisant pour le produit %d", productID)
|
||||
}
|
||||
return nil
|
||||
return &basket, nil
|
||||
}
|
||||
|
||||
// DeleteProductFromBasket supprime un produit spécifique du panier et restitue le stock.
|
||||
@@ -329,55 +291,6 @@ func (d *Database) UpdateBasketItemQuantity(basketID int, newQuantity float64) e
|
||||
})
|
||||
}
|
||||
|
||||
// ExtendBasketReservations prolonge les réservations
|
||||
func (d *Database) ExtendBasketReservations(username string) error {
|
||||
var items []struct {
|
||||
ProductID int `gorm:"column:product_id"`
|
||||
Quantity float64 `gorm:"column:quantity"`
|
||||
}
|
||||
if err := d.GDB.Raw(`SELECT product_id, quantity FROM baskets WHERE username = ?`, username).Scan(&items).Error; err != nil {
|
||||
return fmt.Errorf("erreur récupération panier: %w", err)
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
var stockResult struct {
|
||||
Stock float64 `gorm:"column:stock"`
|
||||
}
|
||||
if err := d.GDB.Raw(`SELECT stock FROM products WHERE id = ?`, item.ProductID).Scan(&stockResult).Error; err != nil {
|
||||
return fmt.Errorf("produit %d non trouvé: %w", item.ProductID, err)
|
||||
}
|
||||
if stockResult.Stock < item.Quantity {
|
||||
return fmt.Errorf("stock insuffisant pour le produit %d (demandé: %g, disponible: %g)",
|
||||
item.ProductID, item.Quantity, stockResult.Stock)
|
||||
}
|
||||
}
|
||||
|
||||
newReservation := time.Now().Add(15 * time.Minute)
|
||||
if err := d.GDB.Exec(`UPDATE baskets SET reserved_until = ? WHERE username = ?`,
|
||||
newReservation, username).Error; err != nil {
|
||||
return fmt.Errorf("erreur prolongation: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("✅ Réservations prolongées pour %s jusqu'à %s",
|
||||
username, newReservation.Format("15:04:05"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckBasketReservations vérifie si les réservations sont expirées
|
||||
func (d *Database) CheckBasketReservations(username string) (bool, error) {
|
||||
var result struct {
|
||||
Count int `gorm:"column:count"`
|
||||
}
|
||||
err := d.GDB.Raw(`
|
||||
SELECT COUNT(*) as count FROM baskets
|
||||
WHERE username = ? AND (reserved_until IS NULL OR reserved_until < CURRENT_TIMESTAMP)`,
|
||||
username).Scan(&result).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return result.Count > 0, nil
|
||||
}
|
||||
|
||||
func (d *Database) GetBasketItemOwner(basketID int) (string, error) {
|
||||
var username string
|
||||
err := d.GDB.Raw(`SELECT username FROM baskets WHERE id = ?`, basketID).Scan(&username).Error
|
||||
|
||||
@@ -179,8 +179,6 @@ func (d *Database) CheckCommandETAExistsAndValid(commandID int) bool {
|
||||
}
|
||||
|
||||
func (d *Database) DeleteCommandAtomic(commandID int, deletedBy, role string) error {
|
||||
log.Printf("🔒 [DeleteAtomic] START - cmd=%d, by=%s (%s)", commandID, deletedBy, role)
|
||||
|
||||
return d.GDB.Transaction(func(tx *gorm.DB) error {
|
||||
var cmdResult struct {
|
||||
Status string `gorm:"column:status"`
|
||||
@@ -188,8 +186,8 @@ func (d *Database) DeleteCommandAtomic(commandID int, deletedBy, role string) er
|
||||
LivreurAssign string `gorm:"column:livreur_assign"`
|
||||
}
|
||||
err := tx.Raw(`
|
||||
SELECT status, username, COALESCE(livreur_assign, '') as livreur_assign
|
||||
FROM commandes WHERE id = ? FOR UPDATE`, commandID).Scan(&cmdResult).Error
|
||||
SELECT status, username, COALESCE(livreur_assign, '') as livreur_assign
|
||||
FROM commandes WHERE id = ? FOR UPDATE`, commandID).Scan(&cmdResult).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -199,19 +197,26 @@ func (d *Database) DeleteCommandAtomic(commandID int, deletedBy, role string) er
|
||||
|
||||
log.Printf("📋 [DeleteAtomic] Trouvée - status=%s, client=%s", cmdResult.Status, cmdResult.Username)
|
||||
|
||||
if err := tx.Exec(`
|
||||
UPDATE products p
|
||||
SET stock = stock + ci.quantite, updated_at = CURRENT_TIMESTAMP
|
||||
FROM command_items ci
|
||||
WHERE ci.command_id = ? AND ci.product_id = p.id`, commandID).Error; err != nil {
|
||||
log.Printf("⚠️ [DeleteAtomic] Erreur remboursement: %v", err)
|
||||
// ✅ Ne restitue le stock QUE si pas déjà fait
|
||||
stockAlreadyRestored := cmdResult.Status == "cancelled" || cmdResult.Status == "approved"
|
||||
if !stockAlreadyRestored {
|
||||
if err := tx.Exec(`
|
||||
UPDATE products p
|
||||
SET stock = stock + ci.quantite, updated_at = CURRENT_TIMESTAMP
|
||||
FROM command_items ci
|
||||
WHERE ci.command_id = ? AND ci.product_id = p.id`, commandID).Error; err != nil {
|
||||
log.Printf("⚠️ [DeleteAtomic] Erreur remboursement: %v", err)
|
||||
} else {
|
||||
log.Printf("✅ [DeleteAtomic] Stock remboursé (statut: %s)", cmdResult.Status)
|
||||
}
|
||||
} else {
|
||||
log.Printf("✅ [DeleteAtomic] Stock remboursé")
|
||||
log.Printf("⏭️ [DeleteAtomic] Stock NON restitué - statut=%s", cmdResult.Status)
|
||||
}
|
||||
|
||||
// ✅ Log suppression
|
||||
tx.Exec(`
|
||||
INSERT INTO command_logs (command_id, status, message, author, created_at)
|
||||
VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)`,
|
||||
INSERT INTO command_logs (command_id, status, message, author, created_at)
|
||||
VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)`,
|
||||
commandID, "deleted",
|
||||
fmt.Sprintf("Supprimée par %s (%s) - Ancien statut: %s", deletedBy, role, cmdResult.Status),
|
||||
deletedBy)
|
||||
|
||||
@@ -177,12 +177,12 @@ func (d *Database) GetProductsByCategory(category string) ([]models.Product, err
|
||||
return products, nil
|
||||
}
|
||||
|
||||
func (d *Database) UpdateProduct(productID int, name, category, description, unit string, stock float64, prices []models.ProductPrice) error {
|
||||
func (d *Database) UpdateProduct(productID int, name, category, description, unit string, prices []models.ProductPrice) error {
|
||||
err := d.GDB.Exec(`
|
||||
UPDATE products
|
||||
SET name = ?, category = ?, description = ?, stock = ?, unit = ?, updated_at = ?
|
||||
SET name = ?, category = ?, description = ?, unit = ?, updated_at = ?
|
||||
WHERE id = ?`,
|
||||
name, category, description, stock, unit, time.Now(), productID).Error
|
||||
name, category, description, unit, time.Now(), productID).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur mise à jour produit: %w", err)
|
||||
}
|
||||
@@ -199,6 +199,18 @@ func (d *Database) UpdateProduct(productID int, name, category, description, uni
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Database) SetProductStock(productID int, stock float64) error {
|
||||
result := d.GDB.Exec(`UPDATE products SET stock = ?, updated_at = ? WHERE id = ?`,
|
||||
stock, time.Now(), productID)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("erreur mise à jour stock: %w", result.Error)
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return fmt.Errorf("produit non trouvé")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteProduct supprime un produit
|
||||
func (d *Database) DeleteProduct(productID int) error {
|
||||
result := d.GDB.Exec(`DELETE FROM products WHERE id = ?`, productID)
|
||||
|
||||
@@ -266,9 +266,12 @@ func GetMyCancellationHistory(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
var totalPenalty int
|
||||
penaltyQuery := `SELECT COALESCE(amende, 0) FROM clients WHERE username = $1`
|
||||
database.QueryRow(penaltyQuery).Scan(&totalPenalty)
|
||||
var penaltyResult struct {
|
||||
Amende int `gorm:"column:amende"`
|
||||
}
|
||||
database.GDB.Raw(`SELECT COALESCE(amende, 0) as amende FROM clients WHERE username = ?`,
|
||||
username).Scan(&penaltyResult)
|
||||
totalPenalty := penaltyResult.Amende
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"gestion/utils"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -48,35 +49,32 @@ func AddProductsBasket(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Quantité invalide"})
|
||||
return
|
||||
}
|
||||
|
||||
// Si product_id fourni par le mobile, on l'utilise directement (plus fiable)
|
||||
if req.ProductID > 0 || req.NameProduct == "" || req.Category == "" {
|
||||
stock, err := database.GetProductStockByID(req.ProductID)
|
||||
if err != nil {
|
||||
log.Printf("❌ [ADD_PANIER] Produit %d non trouvé: %v", req.ProductID, err)
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Produit non trouvé"})
|
||||
return
|
||||
}
|
||||
if stock < req.Quantity {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Stock insuffisant", "available": stock})
|
||||
return
|
||||
}
|
||||
if err := database.DecrementProductStockByID(req.ProductID, req.Quantity); err != nil {
|
||||
log.Printf("❌ [ADD_PANIER] Erreur décrement stock product_id=%d: %v", req.ProductID, err)
|
||||
utils.ServerErr(c, "Impossible d'ajouter le produit au panier", err)
|
||||
return
|
||||
}
|
||||
panier, err := database.AddProductInBasketByID(req.Username, req.ProductID, req.Quantity)
|
||||
if err != nil {
|
||||
log.Printf("❌ [ADD_PANIER] Erreur ajout product_id=%d qty=%.3f: %v", req.ProductID, req.Quantity, err)
|
||||
utils.ServerErr(c, "Impossible d'ajouter le produit au panier", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "Produit ajouté au panier avec succès", "panier": panier})
|
||||
if req.ProductID <= 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "product_id requis"})
|
||||
return
|
||||
}
|
||||
|
||||
// ✅ Une seule fonction, une seule transaction atomique
|
||||
panier, err := database.DecrementAndAddToBasket(req.Username, req.ProductID, req.Quantity)
|
||||
if err != nil {
|
||||
log.Printf("❌ [ADD_PANIER] product_id=%d qty=%.3f: %v", req.ProductID, req.Quantity, err)
|
||||
if err.Error() == "stock insuffisant" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Stock insuffisant"})
|
||||
return
|
||||
}
|
||||
if strings.Contains(err.Error(), "prix introuvable") {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Aucun prix configuré pour ce produit"})
|
||||
return
|
||||
}
|
||||
utils.ServerErr(c, "Impossible d'ajouter le produit au panier", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Produit ajouté au panier avec succès",
|
||||
"panier": panier,
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================
|
||||
|
||||
@@ -600,7 +600,6 @@ func UpdateProduct(c *gin.Context) {
|
||||
Name string `json:"name"`
|
||||
Category string `json:"category"`
|
||||
Description string `json:"description"`
|
||||
Stock float64 `json:"stock"`
|
||||
Unit string `json:"unit"`
|
||||
Prices []models.ProductPrice `json:"prices"`
|
||||
}
|
||||
@@ -629,12 +628,8 @@ func UpdateProduct(c *gin.Context) {
|
||||
if updateData.Unit == "" {
|
||||
updateData.Unit = "u"
|
||||
}
|
||||
if err := validateUnit(updateData.Unit); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateStock(updateData.Stock); err != nil {
|
||||
if err := validateUnit(updateData.Unit); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
@@ -653,7 +648,7 @@ func UpdateProduct(c *gin.Context) {
|
||||
|
||||
log.Printf("🔄 [UpdateProduct] %s met à jour produit #%d", username, id)
|
||||
|
||||
if err := database.UpdateProduct(id, updateData.Name, updateData.Category, updateData.Description, updateData.Unit, updateData.Stock, updateData.Prices); err != nil {
|
||||
if err := database.UpdateProduct(id, updateData.Name, updateData.Category, updateData.Description, updateData.Unit, updateData.Prices); err != nil {
|
||||
log.Printf("❌ [UpdateProduct] Erreur UPDATE: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur mise à jour"})
|
||||
return
|
||||
@@ -672,6 +667,60 @@ func UpdateProduct(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateStock(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
role := c.GetString("role")
|
||||
if role != "admin" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
|
||||
return
|
||||
}
|
||||
|
||||
username, _ := safeGetUsername(c)
|
||||
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil || id <= 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID invalide"})
|
||||
return
|
||||
}
|
||||
|
||||
// ✅ VÉRIFIER QUE LE PRODUIT EXISTE
|
||||
_, err = database.GetProductByID(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Produit non trouvé"})
|
||||
return
|
||||
}
|
||||
var req struct {
|
||||
Stock float64 `json:"stock"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Données invalides"})
|
||||
return
|
||||
}
|
||||
if err := validateStock(req.Stock); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
log.Printf("🔄 [UpdateStock] %s met à jour le stock #%d", username, id)
|
||||
|
||||
if err := database.SetProductStock(id, req.Stock); err != nil {
|
||||
log.Printf("❌ [UpdateProduct] Erreur UPDATE: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur mise à jour"})
|
||||
return
|
||||
}
|
||||
updatedProduct, _ := database.GetProductByID(id)
|
||||
media, _ := database.GetMediaByProductID(id)
|
||||
updatedProduct.Media = media
|
||||
|
||||
log.Printf("✅ [UpdateStock] le stock #%d est mis à jour", id)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"product": updatedProduct,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func DeleteMedia(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
|
||||
@@ -188,6 +188,7 @@ func SetupRoutes(router *gin.Engine, database *db.Database, geoService *services
|
||||
adminGroupV2.DELETE("/products/:id", handlers.DeleteProduct)
|
||||
adminGroupV2.POST("/products/:id/media", handlers.UploadMedia)
|
||||
adminGroupV2.DELETE("/products/:id/media/:media_id", handlers.DeleteMedia)
|
||||
adminGroupV2.POST("/products/:id/stock", handlers.UpdateStock)
|
||||
// ============================================
|
||||
// CATÉGORIES - GESTION ADMIN
|
||||
// ============================================
|
||||
|
||||
Reference in New Issue
Block a user