This commit is contained in:
@@ -19,12 +19,19 @@ type Product struct {
|
||||
func (Product) TableName() string { return "products" }
|
||||
|
||||
type ProductPrice struct {
|
||||
ID int `json:"id" gorm:"primaryKey;autoIncrement"`
|
||||
ProductID int `json:"product_id" gorm:"column:product_id;index"`
|
||||
Quantity float64 `json:"quantity" gorm:"column:quantity" binding:"required"`
|
||||
Price float64 `json:"price" gorm:"column:price" binding:"required"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
|
||||
ActivePrice bool `json:"active_price" gorm:"column:active_price;default:true"`
|
||||
ID int `json:"id" gorm:"primaryKey;autoIncrement"`
|
||||
ProductID int `json:"product_id" gorm:"column:product_id;index"`
|
||||
Quantity float64 `json:"quantity" gorm:"column:quantity" binding:"required"`
|
||||
Price float64 `json:"price" gorm:"column:price" binding:"required"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
|
||||
// Pas de tag gorm "default:true" ici : GORM omet de l'INSERT tout champ
|
||||
// dont la valeur Go est la valeur zéro (false) s'il porte un tag
|
||||
// "default", laissant Postgres appliquer sa propre valeur par défaut
|
||||
// (TRUE) à la place — un prix explicitement désactivé (false) revenait
|
||||
// donc toujours actif après un Create(). La colonne a déjà son défaut
|
||||
// TRUE posé au niveau SQL (db_init.go), ce tag Go était redondant et
|
||||
// seulement source du bug.
|
||||
ActivePrice bool `json:"active_price" gorm:"column:active_price"`
|
||||
}
|
||||
|
||||
func (ProductPrice) TableName() string { return "product_prices" }
|
||||
|
||||
@@ -278,6 +278,55 @@ func TestUpdateProduct_UpdatesStockWhenProvided(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// productPriceActiveFlags relit active_price par palier de quantité pour un
|
||||
// produit, directement en base.
|
||||
func productPriceActiveFlags(t *testing.T, productID int) map[float64]bool {
|
||||
t.Helper()
|
||||
var rows []struct {
|
||||
Quantity float64 `gorm:"column:quantity"`
|
||||
ActivePrice bool `gorm:"column:active_price"`
|
||||
}
|
||||
if err := testDB.GDB.Raw(
|
||||
`SELECT quantity, active_price FROM product_prices WHERE product_id = ?`, productID,
|
||||
).Scan(&rows).Error; err != nil {
|
||||
t.Fatalf("lecture active_price: %v", err)
|
||||
}
|
||||
out := make(map[float64]bool, len(rows))
|
||||
for _, r := range rows {
|
||||
out[r.Quantity] = r.ActivePrice
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Un prix explicitement désactivé (active_price=false) doit rester désactivé
|
||||
// après UpdateProduct — piège classique de GORM : un champ bool à sa valeur
|
||||
// zéro (false) avec un tag gorm "default" est omis de l'INSERT, laissant la
|
||||
// base appliquer son propre défaut (TRUE) à la place. Voir le commentaire sur
|
||||
// ProductPrice.ActivePrice dans models/product.go.
|
||||
func TestUpdateProduct_PersistsInactivePriceFlag(t *testing.T) {
|
||||
cleanupStockTestData(t)
|
||||
cat := newTestCategory(t, "CatInactive")
|
||||
productID := newTestProduct(t, "UPInactive", 5)
|
||||
|
||||
body := []byte(fmt.Sprintf(
|
||||
`{"name":"UPInactive","category":%q,"description":"d2","unit":"g","stock":10,"prices":[{"quantity":1,"price":5,"active_price":false},{"quantity":5,"price":20,"active_price":true}]}`,
|
||||
cat,
|
||||
))
|
||||
c, rec := updateProductJSONContext("admin", body, productID)
|
||||
handlers.UpdateProduct(c)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("UpdateProduct valide doit réussir: got=%d body=%s", rec.Code, rec.Body.String())
|
||||
}
|
||||
|
||||
flags := productPriceActiveFlags(t, productID)
|
||||
if flags[1] != false {
|
||||
t.Errorf("palier qty=1 doit rester désactivé après UpdateProduct: got active_price=%v", flags[1])
|
||||
}
|
||||
if flags[5] != true {
|
||||
t.Errorf("palier qty=5 doit rester actif après UpdateProduct: got active_price=%v", flags[5])
|
||||
}
|
||||
}
|
||||
|
||||
// ── DeleteProduct ────────────────────────────────────────────────────────
|
||||
|
||||
func TestDeleteProduct_RemovesProductWithoutMedia(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user