314 lines
11 KiB
Go
314 lines
11 KiB
Go
package db
|
|
|
|
import (
|
|
"fmt"
|
|
"gestion/models"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// AddProductInBasket ajoute un produit au panier de l'utilisateur
|
|
func (d *Database) AddProductInBasket(username, nameProduct string, quantity float64, category string) (*models.Panier, error) {
|
|
var productResult struct {
|
|
ID int `gorm:"column:id"`
|
|
}
|
|
err := d.GDB.Raw(`SELECT id FROM products WHERE LOWER(name) = LOWER(?) AND LOWER(category) = LOWER(?)`,
|
|
nameProduct, category).Scan(&productResult).Error
|
|
if err != nil {
|
|
return nil, fmt.Errorf("erreur lors de la recherche du produit: %w", err)
|
|
}
|
|
if productResult.ID == 0 {
|
|
return nil, fmt.Errorf("produit '%s' non trouvé dans la catégorie '%s'", nameProduct, category)
|
|
}
|
|
productID := productResult.ID
|
|
|
|
price, err := d.GetProductPrice(nameProduct, category, 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
|
|
if err != nil {
|
|
return nil, fmt.Errorf("erreur lors de la mise à jour du panier: %w", err)
|
|
}
|
|
} 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 lors de l'ajout au 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 {
|
|
Price float64 `gorm:"column:price"`
|
|
}
|
|
err := d.GDB.Raw(`
|
|
SELECT price
|
|
FROM product_prices pp
|
|
INNER JOIN products p ON pp.product_id = p.id
|
|
WHERE LOWER(p.name) = LOWER(?)
|
|
AND LOWER(p.category) = LOWER(?)
|
|
AND pp.quantity <= ?
|
|
ORDER BY pp.quantity DESC
|
|
LIMIT 1`, name, category, quantity).Scan(&result).Error
|
|
if err != nil || result.Price == 0 {
|
|
return 0, fmt.Errorf("prix produit introuvable pour %f %s: %w", quantity, name, err)
|
|
}
|
|
return result.Price, nil
|
|
}
|
|
|
|
func (d *Database) GetProductStock(name, category string) (float64, error) {
|
|
var result struct {
|
|
Stock float64 `gorm:"column:stock"`
|
|
}
|
|
err := d.GDB.Raw(`SELECT stock FROM products WHERE LOWER(name) = LOWER(?) AND LOWER(category) = LOWER(?)`,
|
|
name, category).Scan(&result).Error
|
|
if err != nil {
|
|
return 0, fmt.Errorf("produit non trouvé: %w", err)
|
|
}
|
|
return result.Stock, nil
|
|
}
|
|
|
|
func (d *Database) DecrementProductStock(name, category string, quantity float64) error {
|
|
result := d.GDB.Exec(`
|
|
UPDATE products SET stock = stock - ?
|
|
WHERE LOWER(name) = LOWER(?) AND LOWER(category) = LOWER(?) AND stock >= ?`,
|
|
quantity, name, category, quantity)
|
|
if result.Error != nil {
|
|
return fmt.Errorf("erreur mise à jour stock: %w", result.Error)
|
|
}
|
|
if result.RowsAffected == 0 {
|
|
return fmt.Errorf("stock insuffisant pour le produit")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetAllProductsInBasket récupère tous les produits du panier d'un utilisateur
|
|
func (d *Database) GetAllProductsInBasket(username string) ([]models.Panier, error) {
|
|
var baskets []models.Panier
|
|
err := d.GDB.Raw(`
|
|
SELECT b.id, b.username, b.product_id, b.quantity, b.price, b.created_at,
|
|
p.name as product_name, p.category, p.description
|
|
FROM baskets b
|
|
INNER JOIN products p ON b.product_id = p.id
|
|
WHERE b.username = ?
|
|
ORDER BY b.created_at DESC`, username).Scan(&baskets).Error
|
|
if err != nil {
|
|
return nil, fmt.Errorf("erreur lors de la récupération du panier: %w", err)
|
|
}
|
|
return baskets, nil
|
|
}
|
|
|
|
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
|
|
}
|
|
return &basket, nil
|
|
}
|
|
|
|
// DeleteProductFromBasket supprime un produit spécifique du panier et restitue le stock.
|
|
func (d *Database) DeleteProductFromBasket(basketID int) error {
|
|
return d.GDB.Transaction(func(tx *gorm.DB) error {
|
|
var item struct {
|
|
ProductID int `gorm:"column:product_id"`
|
|
Quantity float64 `gorm:"column:quantity"`
|
|
}
|
|
if err := tx.Raw(`SELECT product_id, quantity FROM baskets WHERE id = ?`, basketID).Scan(&item).Error; err != nil {
|
|
return fmt.Errorf("produit non trouvé dans le panier")
|
|
}
|
|
if item.ProductID == 0 {
|
|
return fmt.Errorf("produit non trouvé dans le panier")
|
|
}
|
|
|
|
if err := tx.Exec(`UPDATE products SET stock = stock + ? WHERE id = ?`,
|
|
item.Quantity, item.ProductID).Error; err != nil {
|
|
return fmt.Errorf("erreur restitution stock: %w", err)
|
|
}
|
|
|
|
result := tx.Exec(`DELETE FROM baskets WHERE id = ?`, basketID)
|
|
if result.Error != nil {
|
|
return fmt.Errorf("erreur lors de la suppression du produit: %w", result.Error)
|
|
}
|
|
if result.RowsAffected == 0 {
|
|
return fmt.Errorf("produit non trouvé dans le panier")
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// ClearBasket vide complètement le panier d'un utilisateur et restitue les stocks.
|
|
func (d *Database) ClearBasket(username string) error {
|
|
return d.GDB.Transaction(func(tx *gorm.DB) error {
|
|
if err := tx.Exec(`
|
|
UPDATE products p
|
|
SET stock = stock + b.quantity
|
|
FROM baskets b
|
|
WHERE b.username = ? AND b.product_id = p.id`, username).Error; err != nil {
|
|
return fmt.Errorf("erreur restitution stock: %w", err)
|
|
}
|
|
if err := tx.Exec(`DELETE FROM baskets WHERE username = ?`, username).Error; err != nil {
|
|
return fmt.Errorf("erreur lors du vidage du panier: %w", err)
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// ClearBasketOnCheckout vide le panier après commande validée SANS restituer le stock.
|
|
func (d *Database) ClearBasketOnCheckout(username string) error {
|
|
return d.GDB.Exec(`DELETE FROM baskets WHERE username = ?`, username).Error
|
|
}
|
|
|
|
// GetBasketTotal calcule le montant total du panier d'un utilisateur
|
|
func (d *Database) GetBasketTotal(username string) (float64, error) {
|
|
var result struct {
|
|
Total float64 `gorm:"column:total"`
|
|
}
|
|
err := d.GDB.Raw(`SELECT COALESCE(SUM(price), 0) as total FROM baskets WHERE username = ?`,
|
|
username).Scan(&result).Error
|
|
if err != nil {
|
|
return 0, fmt.Errorf("erreur lors du calcul du total: %w", err)
|
|
}
|
|
return result.Total, nil
|
|
}
|
|
|
|
// GetBasketItemCount compte le nombre d'items dans le panier
|
|
func (d *Database) GetBasketItemCount(username string) (int, error) {
|
|
var result struct {
|
|
Count int `gorm:"column:count"`
|
|
}
|
|
err := d.GDB.Raw(`SELECT COUNT(*) as count FROM baskets WHERE username = ?`,
|
|
username).Scan(&result).Error
|
|
if err != nil {
|
|
return 0, fmt.Errorf("erreur lors du comptage des items: %w", err)
|
|
}
|
|
return result.Count, nil
|
|
}
|
|
|
|
func (d *Database) UpdateBasketItemQuantity(basketID int, newQuantity float64) error {
|
|
if newQuantity <= 0 {
|
|
return fmt.Errorf("quantité invalide")
|
|
}
|
|
return d.GDB.Transaction(func(tx *gorm.DB) error {
|
|
var item struct {
|
|
ProductID int `gorm:"column:product_id"`
|
|
Quantity float64 `gorm:"column:quantity"`
|
|
}
|
|
if err := tx.Raw(`SELECT product_id, quantity FROM baskets WHERE id = ? FOR UPDATE`, basketID).Scan(&item).Error; err != nil {
|
|
return fmt.Errorf("produit non trouvé: %w", err)
|
|
}
|
|
if item.ProductID == 0 {
|
|
return fmt.Errorf("panier item introuvable: %d", basketID)
|
|
}
|
|
|
|
diff := newQuantity - item.Quantity
|
|
|
|
if diff > 0 {
|
|
result := tx.Exec(`UPDATE products SET stock = stock - ? WHERE id = ? AND stock >= ?`,
|
|
diff, item.ProductID, diff)
|
|
if result.Error != nil {
|
|
return fmt.Errorf("erreur stock: %w", result.Error)
|
|
}
|
|
if result.RowsAffected == 0 {
|
|
return fmt.Errorf("stock insuffisant")
|
|
}
|
|
} else if diff < 0 {
|
|
if err := tx.Exec(`UPDATE products SET stock = stock + ? WHERE id = ?`,
|
|
-diff, item.ProductID).Error; err != nil {
|
|
return fmt.Errorf("erreur stock: %w", err)
|
|
}
|
|
}
|
|
|
|
if err := tx.Exec(`UPDATE baskets SET quantity = ? WHERE id = ?`,
|
|
newQuantity, basketID).Error; err != nil {
|
|
return fmt.Errorf("erreur panier: %w", err)
|
|
}
|
|
return 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
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if username == "" {
|
|
return "", fmt.Errorf("article non trouvé")
|
|
}
|
|
return username, nil
|
|
}
|
|
|
|
func (d *Database) GetBasketItems(username string) ([]map[string]any, error) {
|
|
var items []map[string]any
|
|
if err := d.GDB.Raw(`SELECT product_id, quantity::float8 as quantity, price::float8 as price FROM baskets WHERE username = ?`,
|
|
username).Scan(&items).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|