399 lines
13 KiB
Go
399 lines
13 KiB
Go
package db
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"gestion/models"
|
|
"strconv"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// DefaultDeliverySchedule retourne un planning de livraison par défaut (tous les jours, 9h-20h)
|
|
func DefaultDeliverySchedule() models.DeliverySchedule {
|
|
day := models.DaySchedule{Enabled: true, OpenTime: "09:00", CloseTime: "20:00"}
|
|
return models.DeliverySchedule{
|
|
Monday: day, Tuesday: day, Wednesday: day, Thursday: day,
|
|
Friday: day, Saturday: day, Sunday: day,
|
|
}
|
|
}
|
|
|
|
// CalcPointsFromTiers retourne le nombre de points correspondant au total selon les paliers
|
|
func CalcPointsFromTiers(total float64, tiers []models.PointsTier) int {
|
|
for _, t := range tiers {
|
|
if total >= t.Min && (t.Max == 0 || total <= t.Max) {
|
|
return t.Points
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// DefaultSettings retourne les paramètres par défaut
|
|
func DefaultSettings() models.AppSettings {
|
|
return models.AppSettings{
|
|
PenaltiesEnabled: true,
|
|
ShowAmendeScore: true,
|
|
PenaltyTiers: []models.PenaltyTier{
|
|
{MinCancel: 0, Amount: 20},
|
|
{MinCancel: 1, Amount: 50},
|
|
{MinCancel: 2, Amount: 100},
|
|
{MinCancel: 3, Amount: 150},
|
|
},
|
|
PointsEnabled: true,
|
|
ReferralEnabled: true,
|
|
ReferralAmount: 0,
|
|
PointsPools: []models.PointsPool{
|
|
{
|
|
Key: "pool_0",
|
|
Name: "Pool 1",
|
|
Categories: []string{},
|
|
Tiers: []models.PointsTier{
|
|
{Min: 30, Max: 50, Points: 1},
|
|
{Min: 60, Max: 150, Points: 2},
|
|
{Min: 160, Max: 300, Points: 3},
|
|
{Min: 310, Max: 400, Points: 5},
|
|
{Min: 401, Max: 0, Points: 10},
|
|
},
|
|
},
|
|
{
|
|
Key: "pool_1",
|
|
Name: "Pool 2",
|
|
Categories: []string{},
|
|
Tiers: []models.PointsTier{
|
|
{Min: 30, Max: 100, Points: 1},
|
|
{Min: 110, Max: 200, Points: 2},
|
|
{Min: 210, Max: 0, Points: 3},
|
|
},
|
|
},
|
|
},
|
|
ShopName: "Milieu-Nantais",
|
|
ContactTelegram: "MLN44LA",
|
|
DeliveryMode: models.DeliveryModeConfig{
|
|
Mode: "single",
|
|
CategoryRoutes: []models.CategoryRoute{},
|
|
},
|
|
AdminColorPrimary: "#7c3aed",
|
|
AdminColorSecondary: "#000000",
|
|
AdminColorSuccess: "#4ade80",
|
|
AdminColorDanger: "#ef4444",
|
|
AdminColorWarning: "#f59e0b",
|
|
ClientColorPrimary: "#7c3aed",
|
|
ClientColorSecondary: "#000000",
|
|
ClientColorSuccess: "#4ade80",
|
|
ClientColorDanger: "#ef4444",
|
|
ClientColorWarning: "#f59e0b",
|
|
ClientTitleGradientFrom: "#a78bfa",
|
|
ClientTitleGradientTo: "#22d3ee",
|
|
DeliverySchedule: DefaultDeliverySchedule(),
|
|
PostalZones: []models.PostalZone{
|
|
{Name: "Zone 30€", MinAmount: 30, Codes: []string{"44000", "44100", "44200", "44300"}},
|
|
{Name: "Zone 50€", MinAmount: 50, Codes: []string{
|
|
"44400", "44880", "44120", "44230", "44115",
|
|
"44980", "44470", "44240", "44700", "44800", "44340", "44620", "44830",
|
|
}},
|
|
{Name: "Zone 100€", MinAmount: 100, Codes: []string{
|
|
"44860", "44220", "44118", "44710", "44690", "44119",
|
|
}},
|
|
},
|
|
Telegram2FAEnabled: false,
|
|
}
|
|
}
|
|
|
|
// GetSettings récupère les paramètres depuis la DB
|
|
func (d *Database) GetSettings() (models.AppSettings, error) {
|
|
settings := DefaultSettings()
|
|
|
|
var rows []struct {
|
|
Key string `gorm:"column:key"`
|
|
Value string `gorm:"column:value"`
|
|
}
|
|
if err := d.GDB.Table("app_settings").Select("key, value").Scan(&rows).Error; err != nil {
|
|
return settings, fmt.Errorf("erreur lecture settings: %w", err)
|
|
}
|
|
|
|
for _, row := range rows {
|
|
switch row.Key {
|
|
case "penalties_enabled":
|
|
settings.PenaltiesEnabled = row.Value == "true"
|
|
case "penalty_tiers":
|
|
var tiers []models.PenaltyTier
|
|
if err := json.Unmarshal([]byte(row.Value), &tiers); err == nil {
|
|
settings.PenaltyTiers = tiers
|
|
}
|
|
case "show_amende_score":
|
|
settings.ShowAmendeScore = row.Value == "true"
|
|
case "points_enabled":
|
|
settings.PointsEnabled = row.Value == "true"
|
|
case "points_pools":
|
|
var pools []models.PointsPool
|
|
if err := json.Unmarshal([]byte(row.Value), &pools); err == nil {
|
|
settings.PointsPools = pools
|
|
}
|
|
case "points_reward":
|
|
// row.Value peut valoir la chaîne littérale "null" (récompense
|
|
// désactivée puis sauvegardée : json.Marshal(nil *PointsReward)
|
|
// produit "null"). json.Unmarshal d'un null JSON dans une valeur
|
|
// non-pointeur est un no-op sans erreur (voir doc encoding/json),
|
|
// donc sans ce garde-fou &reward pointerait vers une struct vide
|
|
// mais non-nil, et la récompense réapparaîtrait activée.
|
|
if row.Value != "null" && row.Value != "" {
|
|
var reward models.PointsReward
|
|
if err := json.Unmarshal([]byte(row.Value), &reward); err == nil {
|
|
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 "free_gifts_enabled":
|
|
settings.FreeGiftsEnabled = row.Value == "true"
|
|
case "free_gifts":
|
|
var freeGifts []models.CategoryFreeGiftConfig
|
|
if err := json.Unmarshal([]byte(row.Value), &freeGifts); err == nil {
|
|
settings.FreeGifts = freeGifts
|
|
}
|
|
case "referral_enabled":
|
|
settings.ReferralEnabled = row.Value == "true"
|
|
case "referral_amount":
|
|
if v, err := strconv.ParseFloat(row.Value, 64); err == nil {
|
|
settings.ReferralAmount = v
|
|
}
|
|
case "crypto_payment_enabled":
|
|
settings.CryptoPaymentEnabled = row.Value == "true"
|
|
case "crypto_only":
|
|
settings.CryptoOnly = row.Value == "true"
|
|
case "nowpayments_api_key":
|
|
settings.NowPaymentsAPIKey = row.Value
|
|
case "nowpayments_ipn_secret":
|
|
settings.NowPaymentsIPNSecret = row.Value
|
|
case "nowpayments_currencies":
|
|
var currencies []string
|
|
if err := json.Unmarshal([]byte(row.Value), ¤cies); err == nil {
|
|
settings.NowPaymentsCurrencies = currencies
|
|
}
|
|
case "delivery_schedule":
|
|
var sched models.DeliverySchedule
|
|
if err := json.Unmarshal([]byte(row.Value), &sched); err == nil {
|
|
settings.DeliverySchedule = sched
|
|
}
|
|
case "postal_zones":
|
|
var zones []models.PostalZone
|
|
if err := json.Unmarshal([]byte(row.Value), &zones); err == nil {
|
|
settings.PostalZones = zones
|
|
}
|
|
case "contact_telegram":
|
|
settings.ContactTelegram = row.Value
|
|
case "telegram_bot_token":
|
|
settings.TelegramBotToken = row.Value
|
|
case "telegram_bot_username":
|
|
settings.TelegramBotUsername = row.Value
|
|
case "telegram_notifications_enabled":
|
|
settings.TelegramNotificationsEnabled = row.Value == "true"
|
|
case "delivery_mode":
|
|
var mode models.DeliveryModeConfig
|
|
if err := json.Unmarshal([]byte(row.Value), &mode); err == nil {
|
|
settings.DeliveryMode = mode
|
|
}
|
|
case "telegram_2fa_enabled":
|
|
settings.Telegram2FAEnabled = row.Value == "true"
|
|
case "shop_name":
|
|
settings.ShopName = row.Value
|
|
case "admin_color_primary":
|
|
settings.AdminColorPrimary = row.Value
|
|
case "admin_color_secondary":
|
|
settings.AdminColorSecondary = row.Value
|
|
case "admin_color_success":
|
|
settings.AdminColorSuccess = row.Value
|
|
case "admin_color_danger":
|
|
settings.AdminColorDanger = row.Value
|
|
case "admin_color_warning":
|
|
settings.AdminColorWarning = row.Value
|
|
case "client_color_primary":
|
|
settings.ClientColorPrimary = row.Value
|
|
case "client_color_secondary":
|
|
settings.ClientColorSecondary = row.Value
|
|
case "client_color_success":
|
|
settings.ClientColorSuccess = row.Value
|
|
case "client_color_danger":
|
|
settings.ClientColorDanger = row.Value
|
|
case "client_color_warning":
|
|
settings.ClientColorWarning = row.Value
|
|
case "client_title_gradient_from":
|
|
settings.ClientTitleGradientFrom = row.Value
|
|
case "client_title_gradient_to":
|
|
settings.ClientTitleGradientTo = row.Value
|
|
}
|
|
}
|
|
return settings, nil
|
|
}
|
|
|
|
// UpdateSettings sauvegarde les paramètres dans la DB
|
|
func (d *Database) UpdateSettings(s models.AppSettings) error {
|
|
boolStr := func(b bool) string {
|
|
if b {
|
|
return "true"
|
|
}
|
|
return "false"
|
|
}
|
|
|
|
if s.PenaltyTiers == nil {
|
|
s.PenaltyTiers = []models.PenaltyTier{}
|
|
}
|
|
tiersJSON, err := json.Marshal(s.PenaltyTiers)
|
|
if err != nil {
|
|
return fmt.Errorf("erreur sérialisation penalty_tiers: %w", err)
|
|
}
|
|
|
|
if s.PointsPools == nil {
|
|
s.PointsPools = []models.PointsPool{}
|
|
}
|
|
for i := range s.PointsPools {
|
|
if s.PointsPools[i].Categories == nil {
|
|
s.PointsPools[i].Categories = []string{}
|
|
}
|
|
if s.PointsPools[i].Tiers == nil {
|
|
s.PointsPools[i].Tiers = []models.PointsTier{}
|
|
}
|
|
}
|
|
|
|
poolsJSON, err := json.Marshal(s.PointsPools)
|
|
if err != nil {
|
|
return fmt.Errorf("erreur sérialisation pools: %w", err)
|
|
}
|
|
|
|
if s.PointsReward != nil {
|
|
for i := range s.PointsReward.CategoryConfigs {
|
|
if s.PointsReward.CategoryConfigs[i].Products == nil {
|
|
s.PointsReward.CategoryConfigs[i].Products = []models.RewardProductQuantity{}
|
|
}
|
|
}
|
|
}
|
|
rewardJSON, err := json.Marshal(s.PointsReward)
|
|
if err != nil {
|
|
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.FreeGifts == nil {
|
|
s.FreeGifts = []models.CategoryFreeGiftConfig{}
|
|
}
|
|
for i := range s.FreeGifts {
|
|
if s.FreeGifts[i].Tiers == nil {
|
|
s.FreeGifts[i].Tiers = []models.FreeGiftTier{}
|
|
}
|
|
if s.FreeGifts[i].Products == nil {
|
|
s.FreeGifts[i].Products = []models.FreeGiftProductQuantity{}
|
|
}
|
|
for j := range s.FreeGifts[i].Products {
|
|
if s.FreeGifts[i].Products[j].Tiers == nil {
|
|
s.FreeGifts[i].Products[j].Tiers = []models.FreeGiftTier{}
|
|
}
|
|
}
|
|
}
|
|
freeGiftsJSON, err := json.Marshal(s.FreeGifts)
|
|
if err != nil {
|
|
return fmt.Errorf("erreur sérialisation free_gifts: %w", err)
|
|
}
|
|
|
|
if s.NowPaymentsCurrencies == nil {
|
|
s.NowPaymentsCurrencies = []string{}
|
|
}
|
|
currenciesJSON, err := json.Marshal(s.NowPaymentsCurrencies)
|
|
if err != nil {
|
|
return fmt.Errorf("erreur sérialisation nowpayments_currencies: %w", err)
|
|
}
|
|
|
|
schedJSON, err := json.Marshal(s.DeliverySchedule)
|
|
if err != nil {
|
|
return fmt.Errorf("erreur sérialisation delivery_schedule: %w", err)
|
|
}
|
|
|
|
if s.PostalZones == nil {
|
|
s.PostalZones = []models.PostalZone{}
|
|
}
|
|
zonesJSON, err := json.Marshal(s.PostalZones)
|
|
if err != nil {
|
|
return fmt.Errorf("erreur sérialisation postal_zones: %w", err)
|
|
}
|
|
|
|
if s.DeliveryMode.CategoryRoutes == nil {
|
|
s.DeliveryMode.CategoryRoutes = []models.CategoryRoute{}
|
|
}
|
|
deliveryModeJSON, err := json.Marshal(s.DeliveryMode)
|
|
if err != nil {
|
|
return fmt.Errorf("erreur sérialisation delivery_mode: %w", err)
|
|
}
|
|
|
|
if s.ContactTelegram == "" {
|
|
s.ContactTelegram = "MLN44LA"
|
|
}
|
|
pairs := [][2]string{
|
|
{"penalties_enabled", boolStr(s.PenaltiesEnabled)},
|
|
{"penalty_tiers", string(tiersJSON)},
|
|
{"show_amende_score", boolStr(s.ShowAmendeScore)},
|
|
{"points_enabled", boolStr(s.PointsEnabled)},
|
|
{"points_pools", string(poolsJSON)},
|
|
{"points_reward", string(rewardJSON)},
|
|
{"promotions_enabled", boolStr(s.PromotionsEnabled)},
|
|
{"promotions", string(promotionsJSON)},
|
|
{"free_gifts_enabled", boolStr(s.FreeGiftsEnabled)},
|
|
{"free_gifts", string(freeGiftsJSON)},
|
|
{"referral_enabled", boolStr(s.ReferralEnabled)},
|
|
{"referral_amount", strconv.FormatFloat(s.ReferralAmount, 'f', 2, 64)},
|
|
{"crypto_payment_enabled", boolStr(s.CryptoPaymentEnabled)},
|
|
{"crypto_only", boolStr(s.CryptoOnly)},
|
|
{"nowpayments_api_key", s.NowPaymentsAPIKey},
|
|
{"nowpayments_ipn_secret", s.NowPaymentsIPNSecret},
|
|
{"nowpayments_currencies", string(currenciesJSON)},
|
|
{"delivery_schedule", string(schedJSON)},
|
|
{"postal_zones", string(zonesJSON)},
|
|
{"telegram_bot_token", s.TelegramBotToken},
|
|
{"telegram_bot_username", s.TelegramBotUsername},
|
|
{"telegram_notifications_enabled", boolStr(s.TelegramNotificationsEnabled)},
|
|
{"telegram_2fa_enabled", boolStr(s.Telegram2FAEnabled)},
|
|
{"delivery_mode", string(deliveryModeJSON)},
|
|
{"shop_name", s.ShopName},
|
|
{"contact_telegram", s.ContactTelegram},
|
|
{"admin_color_primary", s.AdminColorPrimary},
|
|
{"admin_color_secondary", s.AdminColorSecondary},
|
|
{"admin_color_success", s.AdminColorSuccess},
|
|
{"admin_color_danger", s.AdminColorDanger},
|
|
{"admin_color_warning", s.AdminColorWarning},
|
|
{"client_color_primary", s.ClientColorPrimary},
|
|
{"client_color_secondary", s.ClientColorSecondary},
|
|
{"client_color_success", s.ClientColorSuccess},
|
|
{"client_color_danger", s.ClientColorDanger},
|
|
{"client_color_warning", s.ClientColorWarning},
|
|
{"client_title_gradient_from", s.ClientTitleGradientFrom},
|
|
{"client_title_gradient_to", s.ClientTitleGradientTo},
|
|
}
|
|
|
|
upsert := `INSERT INTO app_settings (key, value) VALUES (?, ?)
|
|
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value`
|
|
|
|
return d.GDB.Transaction(func(tx *gorm.DB) error {
|
|
for _, p := range pairs {
|
|
if err := tx.Exec(upsert, p[0], p[1]).Error; err != nil {
|
|
return fmt.Errorf("erreur upsert %s: %w", p[0], err)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|