chore: build
This commit is contained in:
@@ -45,7 +45,7 @@ func (d *Database) GetAllProductsInBasket(username string) ([]models.Panier, err
|
||||
|
||||
// AddRewardToBasket ajoute un produit récompense au panier (prix = 0, is_reward = true).
|
||||
// Pas de vérification de stock — les récompenses sont gérées par l'admin.
|
||||
func (d *Database) AddRewardToBasket(username string, productID int, quantity float64) (*models.Panier, error) {
|
||||
func (d *Database) AddRewardToBasket(username string, productID int, quantity float64, poolKey string) (*models.Panier, error) {
|
||||
var basket models.Panier
|
||||
err := d.GDB.Transaction(func(tx *gorm.DB) error {
|
||||
// Vérifier que le produit existe
|
||||
@@ -53,14 +53,14 @@ func (d *Database) AddRewardToBasket(username string, productID int, quantity fl
|
||||
if err := tx.Raw(`SELECT name FROM products WHERE id = ?`, productID).Scan(&productName).Error; err != nil || productName == "" {
|
||||
return fmt.Errorf("produit récompense introuvable (id=%d)", productID)
|
||||
}
|
||||
// Supprimer tout article récompense existant pour ce produit (remplacement)
|
||||
tx.Exec(`DELETE FROM baskets WHERE username = ? AND product_id = ? AND is_reward = true`, username, productID)
|
||||
// Insérer avec prix 0 et is_reward = true
|
||||
// Supprimer tout article récompense existant (remplacement)
|
||||
tx.Exec(`DELETE FROM baskets WHERE username = ? AND is_reward = true`, username)
|
||||
// Insérer avec prix 0, is_reward = true et le pool_key
|
||||
return tx.Raw(`
|
||||
INSERT INTO baskets (username, product_id, quantity, price, is_reward, created_at)
|
||||
VALUES (?, ?, ?, 0, true, CURRENT_TIMESTAMP)
|
||||
RETURNING id, username, product_id, quantity, price, is_reward, created_at`,
|
||||
username, productID, quantity).Scan(&basket).Error
|
||||
INSERT INTO baskets (username, product_id, quantity, price, is_reward, reward_pool_key, created_at)
|
||||
VALUES (?, ?, ?, 0, true, ?, CURRENT_TIMESTAMP)
|
||||
RETURNING id, username, product_id, quantity, price, is_reward, reward_pool_key, created_at`,
|
||||
username, productID, quantity, poolKey).Scan(&basket).Error
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -113,20 +113,21 @@ func (d *Database) AddToBasket(username string, productID int, quantity float64)
|
||||
Quantity float64 `gorm:"column:quantity"`
|
||||
Price float64 `gorm:"column:price"`
|
||||
}
|
||||
tx.Raw(`SELECT id, quantity, price FROM baskets WHERE username = ? AND product_id = ?`,
|
||||
// Chercher uniquement un item normal (non-récompense) pour ce produit
|
||||
tx.Raw(`SELECT id, quantity, price FROM baskets WHERE username = ? AND product_id = ? AND is_reward = false`,
|
||||
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`,
|
||||
WHERE id = ? AND is_reward = false RETURNING id, username, product_id, quantity, price, is_reward, 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`,
|
||||
INSERT INTO baskets (username, product_id, quantity, price, is_reward, created_at)
|
||||
VALUES (?, ?, ?, ?, false, CURRENT_TIMESTAMP)
|
||||
RETURNING id, username, product_id, quantity, price, is_reward, created_at`,
|
||||
username, productID, quantity, priceResult.Price).Scan(&basket).Error
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -609,6 +609,51 @@ func (d *Database) CalculateAndAddPointsForCommandTx(tx *gorm.DB, commandID int,
|
||||
|
||||
log.Printf("🎉 [CalcPointsTx] SUCCÈS - %d points [%s] → %s", totalPoints, pointCategory, username)
|
||||
|
||||
// ✅ ÉTAPE 3: Déduire les points des récompenses reçues dans cette commande
|
||||
var rewardItems []struct {
|
||||
RewardPoolKey string `gorm:"column:reward_pool_key"`
|
||||
}
|
||||
if err := tx.Raw(`
|
||||
SELECT reward_pool_key FROM command_items
|
||||
WHERE command_id = ? AND is_reward = true AND reward_pool_key != ''
|
||||
`, commandID).Scan(&rewardItems).Error; err != nil {
|
||||
log.Printf("⚠️ [CalcPointsTx] Erreur query reward items: %v", err)
|
||||
}
|
||||
|
||||
for _, ri := range rewardItems {
|
||||
if settings.PointsReward == nil || settings.PointsReward.Threshold <= 0 {
|
||||
break
|
||||
}
|
||||
threshold := settings.PointsReward.Threshold
|
||||
poolKey := ri.RewardPoolKey
|
||||
// Déduire threshold points de points_extra[poolKey] (plancher à 0)
|
||||
if err := tx.Exec(`
|
||||
UPDATE clients
|
||||
SET points_extra = jsonb_set(
|
||||
COALESCE(points_extra, '{}'::jsonb),
|
||||
ARRAY[?],
|
||||
to_jsonb(GREATEST(0, COALESCE((points_extra->>?)::int, 0) - ?))
|
||||
), updated_at = CURRENT_TIMESTAMP
|
||||
WHERE username = ?
|
||||
`, poolKey, poolKey, threshold, username).Error; err != nil {
|
||||
log.Printf("⚠️ [CalcPointsTx] Erreur déduction points reward pool=%s: %v", poolKey, err)
|
||||
} else {
|
||||
log.Printf("🎁 [CalcPointsTx] Récompense reçue: -%d pts pool=%s → %s", threshold, poolKey, username)
|
||||
}
|
||||
// Décrémenter points_redeemed[poolKey] (plancher à 0)
|
||||
if err := tx.Exec(`
|
||||
UPDATE clients
|
||||
SET points_redeemed = jsonb_set(
|
||||
COALESCE(points_redeemed, '{}'::jsonb),
|
||||
ARRAY[?],
|
||||
to_jsonb(GREATEST(0, COALESCE((points_redeemed->>?)::int, 0) - 1))
|
||||
), updated_at = CURRENT_TIMESTAMP
|
||||
WHERE username = ?
|
||||
`, poolKey, poolKey, username).Error; err != nil {
|
||||
log.Printf("⚠️ [CalcPointsTx] Erreur décrément redeemed pool=%s: %v", poolKey, err)
|
||||
}
|
||||
}
|
||||
|
||||
return totalPoints, pointCategory, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,8 @@ func (d *Database) InsertCommandItemWithClientInfo(
|
||||
productID int,
|
||||
quantite float64,
|
||||
prix float64,
|
||||
isReward bool,
|
||||
rewardPoolKey string,
|
||||
clientUsername, clientNom, clientPrenom, clientTelephone, deliveryAddress string,
|
||||
) error {
|
||||
log.Printf("📝 [InsertCommandItemWithClientInfo] START - commandID=%d, produit=%s", commandID, produit)
|
||||
@@ -114,8 +116,11 @@ func (d *Database) InsertCommandItemWithClientInfo(
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validatePrix(prix); err != nil {
|
||||
return err
|
||||
// Les articles récompense ont prix=0, on saute la validation de prix pour eux
|
||||
if !isReward {
|
||||
if err := validatePrix(prix); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateUsername(clientUsername); err != nil {
|
||||
@@ -161,10 +166,12 @@ func (d *Database) InsertCommandItemWithClientInfo(
|
||||
err := d.GDB.Exec(`
|
||||
INSERT INTO command_items (
|
||||
command_id, produit, product_id, quantite, prix,
|
||||
is_reward, reward_pool_key,
|
||||
client_username, client_nom, client_prenom, client_telephone, delivery_address,
|
||||
status, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
|
||||
commandID, produit, productID, quantite, prix,
|
||||
isReward, rewardPoolKey,
|
||||
clientUsername, clientNom, clientPrenom, clientTelephone, deliveryAddress,
|
||||
).Error
|
||||
if err != nil {
|
||||
|
||||
@@ -45,14 +45,16 @@ func validateAddress(address string) error {
|
||||
}
|
||||
|
||||
type basketItem struct {
|
||||
ProductID int `gorm:"column:product_id"`
|
||||
Quantity float64 `gorm:"column:quantity"`
|
||||
Price float64 `gorm:"column:price"`
|
||||
ProductID int `gorm:"column:product_id"`
|
||||
Quantity float64 `gorm:"column:quantity"`
|
||||
Price float64 `gorm:"column:price"`
|
||||
IsReward bool `gorm:"column:is_reward"`
|
||||
RewardPoolKey string `gorm:"column:reward_pool_key"`
|
||||
}
|
||||
|
||||
func (d *Database) fetchBasketItems(username string) ([]basketItem, float64, error) {
|
||||
var items []basketItem
|
||||
if err := d.GDB.Table("baskets").Select("product_id, quantity, price").Where("username = ?", username).Scan(&items).Error; err != nil {
|
||||
if err := d.GDB.Table("baskets").Select("product_id, quantity, price, is_reward, reward_pool_key").Where("username = ?", username).Scan(&items).Error; err != nil {
|
||||
return nil, 0, fmt.Errorf("erreur récupération panier: %w", err)
|
||||
}
|
||||
total := 0.0
|
||||
@@ -122,11 +124,13 @@ func (d *Database) CreateCommand(username string) (*models.Command, error) {
|
||||
}
|
||||
|
||||
cmdItem := models.CommandItem{
|
||||
CommandID: commandID,
|
||||
Produit: productName,
|
||||
ProductID: item.ProductID,
|
||||
Quantity: item.Quantity,
|
||||
Price: item.Price,
|
||||
CommandID: commandID,
|
||||
Produit: productName,
|
||||
ProductID: item.ProductID,
|
||||
Quantity: item.Quantity,
|
||||
Price: item.Price,
|
||||
IsReward: item.IsReward,
|
||||
RewardPoolKey: item.RewardPoolKey,
|
||||
}
|
||||
if err := d.GDB.Create(&cmdItem).Error; err != nil {
|
||||
return nil, fmt.Errorf("erreur lors de l'insertion des items: %w", err)
|
||||
@@ -220,6 +224,8 @@ func (d *Database) CreateCommandWithAddress(username, deliveryAddress string) (*
|
||||
item.ProductID,
|
||||
item.Quantity,
|
||||
item.Price,
|
||||
item.IsReward,
|
||||
item.RewardPoolKey,
|
||||
username,
|
||||
clientNom,
|
||||
clientPrenom,
|
||||
|
||||
@@ -125,6 +125,19 @@ func InitDB() *Database {
|
||||
log.Fatalf("❌ Erreur migration baskets.is_reward: %v", err)
|
||||
}
|
||||
|
||||
// Migration: baskets.reward_pool_key — pool de points utilisé pour la récompense
|
||||
if _, err = database.Exec(`ALTER TABLE baskets ADD COLUMN IF NOT EXISTS reward_pool_key VARCHAR(100) NOT NULL DEFAULT ''`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration baskets.reward_pool_key: %v", err)
|
||||
}
|
||||
|
||||
// Migration: command_items.is_reward + reward_pool_key
|
||||
if _, err = database.Exec(`ALTER TABLE command_items ADD COLUMN IF NOT EXISTS is_reward BOOLEAN NOT NULL DEFAULT FALSE`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration command_items.is_reward: %v", err)
|
||||
}
|
||||
if _, err = database.Exec(`ALTER TABLE command_items ADD COLUMN IF NOT EXISTS reward_pool_key VARCHAR(100) NOT NULL DEFAULT ''`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration command_items.reward_pool_key: %v", err)
|
||||
}
|
||||
|
||||
// Migration: command_items.quantite INTEGER → NUMERIC(10,3) pour supporter les quantités fractionnaires
|
||||
if _, err = database.Exec(`
|
||||
DO $$
|
||||
|
||||
@@ -197,7 +197,7 @@ func ClaimMyReward(c *gin.Context) {
|
||||
var productName string
|
||||
if reward.RewardProductID > 0 && reward.RewardQuantity > 0 {
|
||||
qty := reward.RewardQuantity
|
||||
if item, addErr := database.AddRewardToBasket(username, reward.RewardProductID, qty); addErr == nil {
|
||||
if item, addErr := database.AddRewardToBasket(username, reward.RewardProductID, qty, req.PoolKey); addErr == nil {
|
||||
productAdded = true
|
||||
productName = item.ProductName
|
||||
log.Printf("✅ [CLAIM] Produit récompense id=%d (%.2f) ajouté au panier de %s", reward.RewardProductID, qty, username)
|
||||
|
||||
@@ -19,14 +19,16 @@ func (Command) TableName() string { return "commandes" }
|
||||
|
||||
// CommandItem représente un produit dans une commande
|
||||
type CommandItem struct {
|
||||
ID int `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
CommandID int `gorm:"column:command_id" json:"command_id"`
|
||||
Produit string `gorm:"column:produit" json:"produit"`
|
||||
ProductID int `gorm:"column:product_id" json:"product_id"`
|
||||
Quantity float64 `gorm:"column:quantite" json:"quantity"`
|
||||
Price float64 `gorm:"column:prix" json:"price"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
ID int `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
CommandID int `gorm:"column:command_id" json:"command_id"`
|
||||
Produit string `gorm:"column:produit" json:"produit"`
|
||||
ProductID int `gorm:"column:product_id" json:"product_id"`
|
||||
Quantity float64 `gorm:"column:quantite" json:"quantity"`
|
||||
Price float64 `gorm:"column:prix" json:"price"`
|
||||
IsReward bool `gorm:"column:is_reward" json:"is_reward"`
|
||||
RewardPoolKey string `gorm:"column:reward_pool_key" json:"reward_pool_key,omitempty"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
}
|
||||
|
||||
type CommandLog struct {
|
||||
|
||||
@@ -11,7 +11,8 @@ type Panier struct {
|
||||
Description string `json:"description"`
|
||||
Quantity float64 `json:"quantity"`
|
||||
Price float64 `json:"price"`
|
||||
IsReward bool `json:"is_reward"`
|
||||
IsReward bool `json:"is_reward"`
|
||||
RewardPoolKey string `json:"reward_pool_key,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user