chore: build
This commit is contained in:
@@ -130,11 +130,14 @@ func (d *Database) HasOnlyRewardItems(username string) (bool, error) {
|
||||
func (d *Database) AddToBasket(username string, productID int, quantity float64) (*models.Panier, error) {
|
||||
var basket models.Panier
|
||||
err := d.GDB.Transaction(func(tx *gorm.DB) error {
|
||||
var currentStock float64
|
||||
if err := tx.Raw(`SELECT stock FROM products WHERE id = ? FOR UPDATE`, productID).Scan(¤tStock).Error; err != nil {
|
||||
var productInfo struct {
|
||||
Stock float64 `gorm:"column:stock"`
|
||||
Category string `gorm:"column:category"`
|
||||
}
|
||||
if err := tx.Raw(`SELECT stock, category FROM products WHERE id = ? FOR UPDATE`, productID).Scan(&productInfo).Error; err != nil {
|
||||
return fmt.Errorf("erreur lecture stock: %w", err)
|
||||
}
|
||||
if currentStock < quantity {
|
||||
if productInfo.Stock < quantity {
|
||||
return fmt.Errorf("stock insuffisant")
|
||||
}
|
||||
|
||||
@@ -148,6 +151,12 @@ func (d *Database) AddToBasket(username string, productID int, quantity float64)
|
||||
productID, quantity).Scan(&priceResult).Error; err != nil || priceResult.Price == 0 {
|
||||
return fmt.Errorf("prix introuvable pour product_id=%d qty=%.3f", productID, quantity)
|
||||
}
|
||||
// Une promotion active pour ce produit/quantité/catégorie s'applique
|
||||
// automatiquement au prix facturé — indépendamment des points de
|
||||
// fidélité (contrairement aux récompenses par palier).
|
||||
if discounted, ok := d.ApplyPromotionToPrice(productID, productInfo.Category, quantity, priceResult.Price); ok {
|
||||
priceResult.Price = discounted
|
||||
}
|
||||
|
||||
var existing struct {
|
||||
ID int `gorm:"column:id"`
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"gestion/models"
|
||||
"math"
|
||||
)
|
||||
|
||||
// ResolvePromotionDiscount retourne le pourcentage de réduction actif pour un
|
||||
// produit, sa catégorie catalogue et une quantité donnés, si une promotion
|
||||
// configurée dans les settings couvre exactement ce couple (produit,
|
||||
// quantité) — contrairement aux récompenses, aucun seuil de points n'entre
|
||||
// en jeu : la promotion s'applique à toute commande de cette quantité.
|
||||
func ResolvePromotionDiscount(settings *models.AppSettings, productID int, category string, quantity float64) (float64, bool) {
|
||||
if settings == nil || !settings.PromotionsEnabled {
|
||||
return 0, false
|
||||
}
|
||||
for _, promo := range settings.Promotions {
|
||||
if promo.Category != category || promo.DiscountPercent <= 0 {
|
||||
continue
|
||||
}
|
||||
if promo.AllProducts {
|
||||
if promo.Quantity == quantity {
|
||||
return promo.DiscountPercent, true
|
||||
}
|
||||
continue
|
||||
}
|
||||
for _, pq := range promo.Products {
|
||||
if pq.ProductID == productID && pq.Quantity == quantity {
|
||||
return promo.DiscountPercent, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// ApplyPromotionToPrice applique la réduction (si une promotion couvre ce
|
||||
// produit/quantité/catégorie) au prix catalogue donné, arrondi au centime.
|
||||
func (d *Database) ApplyPromotionToPrice(productID int, category string, quantity, price float64) (float64, bool) {
|
||||
settings, err := d.GetSettings()
|
||||
if err != nil {
|
||||
return price, false
|
||||
}
|
||||
discount, ok := ResolvePromotionDiscount(&settings, productID, category, quantity)
|
||||
if !ok {
|
||||
return price, false
|
||||
}
|
||||
return math.Round(price*(1-discount/100)*100) / 100, true
|
||||
}
|
||||
@@ -142,6 +142,13 @@ func (d *Database) GetSettings() (models.AppSettings, error) {
|
||||
settings.PointsReward = &reward
|
||||
}
|
||||
}
|
||||
case "promotions_enabled":
|
||||
settings.PromotionsEnabled = row.Value == "true"
|
||||
case "promotions":
|
||||
var promotions []models.CategoryPromotionConfig
|
||||
if err := json.Unmarshal([]byte(row.Value), &promotions); err == nil {
|
||||
settings.Promotions = promotions
|
||||
}
|
||||
case "referral_enabled":
|
||||
settings.ReferralEnabled = row.Value == "true"
|
||||
case "referral_amount":
|
||||
@@ -263,6 +270,19 @@ func (d *Database) UpdateSettings(s models.AppSettings) error {
|
||||
return fmt.Errorf("erreur sérialisation points_reward: %w", err)
|
||||
}
|
||||
|
||||
if s.Promotions == nil {
|
||||
s.Promotions = []models.CategoryPromotionConfig{}
|
||||
}
|
||||
for i := range s.Promotions {
|
||||
if s.Promotions[i].Products == nil {
|
||||
s.Promotions[i].Products = []models.PromotionProductQuantity{}
|
||||
}
|
||||
}
|
||||
promotionsJSON, err := json.Marshal(s.Promotions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur sérialisation promotions: %w", err)
|
||||
}
|
||||
|
||||
if s.NowPaymentsCurrencies == nil {
|
||||
s.NowPaymentsCurrencies = []string{}
|
||||
}
|
||||
@@ -302,6 +322,8 @@ func (d *Database) UpdateSettings(s models.AppSettings) error {
|
||||
{"points_enabled", boolStr(s.PointsEnabled)},
|
||||
{"points_pools", string(poolsJSON)},
|
||||
{"points_reward", string(rewardJSON)},
|
||||
{"promotions_enabled", boolStr(s.PromotionsEnabled)},
|
||||
{"promotions", string(promotionsJSON)},
|
||||
{"referral_enabled", boolStr(s.ReferralEnabled)},
|
||||
{"referral_amount", strconv.FormatFloat(s.ReferralAmount, 'f', 2, 64)},
|
||||
{"crypto_payment_enabled", boolStr(s.CryptoPaymentEnabled)},
|
||||
|
||||
Reference in New Issue
Block a user