package db import ( "encoding/json" "fmt" ) // DaySchedule représente les horaires de livraison pour un jour de la semaine type DaySchedule struct { Enabled bool `json:"enabled"` OpenTime string `json:"open_time"` // ex: "09:00" CloseTime string `json:"close_time"` // ex: "20:00" } // DeliverySchedule représente les horaires de livraison pour chaque jour type DeliverySchedule struct { Monday DaySchedule `json:"monday"` Tuesday DaySchedule `json:"tuesday"` Wednesday DaySchedule `json:"wednesday"` Thursday DaySchedule `json:"thursday"` Friday DaySchedule `json:"friday"` Saturday DaySchedule `json:"saturday"` Sunday DaySchedule `json:"sunday"` } // DefaultDeliverySchedule retourne un planning de livraison par défaut (tous les jours, 9h-20h) func DefaultDeliverySchedule() DeliverySchedule { day := DaySchedule{Enabled: true, OpenTime: "09:00", CloseTime: "20:00"} return DeliverySchedule{ Monday: day, Tuesday: day, Wednesday: day, Thursday: day, Friday: day, Saturday: day, Sunday: day, } } // PostalZone représente une zone de livraison avec un minimum de commande type PostalZone struct { Name string `json:"name"` MinAmount float64 `json:"min_amount"` Codes []string `json:"codes"` } // PointsTier représente un palier du barème de points // Si Max == 0, il n'y a pas de borne supérieure (illimité) type PointsTier struct { Min float64 `json:"min"` Max float64 `json:"max"` // 0 = illimité Points int `json:"points"` } // CalcPointsFromTiers retourne le nombre de points correspondant au total selon les paliers func CalcPointsFromTiers(total float64, tiers []PointsTier) int { for _, t := range tiers { if total >= t.Min && (t.Max == 0 || total <= t.Max) { return t.Points } } return 0 } // AppSettings contient les paramètres globaux de l'application type AppSettings struct { PenaltiesEnabled bool `json:"penalties_enabled"` ShowAmendeScore bool `json:"show_amende_score"` // afficher le score d'amendes aux clients/cabine PointsEnabled bool `json:"points_enabled"` // afficher/activer le système de points PointsCategoriesWeed []string `json:"points_categories_weed"` // catégories → pool point (weed) PointsCategoriesZipette []string `json:"points_categories_zipette"` // catégories → pool point_zipette PointsCategoriesTotal []string `json:"points_categories_total"` // catégories → pool total (point, sans séparation) PointsSeparated bool `json:"points_separated"` // true = weed/zipette séparés, false = tout dans point // Barème de points par paliers configurables PointsWeedTiers []PointsTier `json:"points_weed_tiers"` PointsZipetteTiers []PointsTier `json:"points_zipette_tiers"` PointsTotalTiers []PointsTier `json:"points_total_tiers"` ReferralEnabled bool `json:"referral_enabled"` // activer/désactiver le système de parrainage DeliverySchedule DeliverySchedule `json:"delivery_schedule"` // horaires de livraison par jour PostalZones []PostalZone `json:"postal_zones"` // zones de livraison avec minimum de commande } // DefaultSettings retourne les paramètres par défaut func DefaultSettings() AppSettings { return AppSettings{ PenaltiesEnabled: true, ShowAmendeScore: true, PointsEnabled: true, ReferralEnabled: true, PointsCategoriesWeed: []string{}, PointsCategoriesZipette: []string{}, PointsCategoriesTotal: []string{}, PointsSeparated: true, PointsWeedTiers: []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}, }, PointsZipetteTiers: []PointsTier{ {Min: 30, Max: 100, Points: 1}, {Min: 110, Max: 200, Points: 2}, {Min: 210, Max: 0, Points: 3}, }, PointsTotalTiers: []PointsTier{}, DeliverySchedule: DefaultDeliverySchedule(), PostalZones: []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", }}, }, } } // GetSettings récupère les paramètres depuis la DB func (d *Database) GetSettings() (AppSettings, error) { settings := DefaultSettings() rows, err := d.Query(`SELECT key, value FROM app_settings`) if err != nil { return settings, fmt.Errorf("erreur lecture settings: %w", err) } defer rows.Close() for rows.Next() { var key, value string if err := rows.Scan(&key, &value); err != nil { continue } switch key { case "penalties_enabled": settings.PenaltiesEnabled = value == "true" case "show_amende_score": settings.ShowAmendeScore = value == "true" case "points_enabled": settings.PointsEnabled = value == "true" case "points_categories_weed": var cats []string if err := json.Unmarshal([]byte(value), &cats); err == nil { settings.PointsCategoriesWeed = cats } case "points_categories_zipette": var cats []string if err := json.Unmarshal([]byte(value), &cats); err == nil { settings.PointsCategoriesZipette = cats } case "points_categories_total": var cats []string if err := json.Unmarshal([]byte(value), &cats); err == nil { settings.PointsCategoriesTotal = cats } case "points_separated": settings.PointsSeparated = value == "true" case "points_weed_tiers": var tiers []PointsTier if err := json.Unmarshal([]byte(value), &tiers); err == nil { settings.PointsWeedTiers = tiers } case "points_zipette_tiers": var tiers []PointsTier if err := json.Unmarshal([]byte(value), &tiers); err == nil { settings.PointsZipetteTiers = tiers } case "points_total_tiers": var tiers []PointsTier if err := json.Unmarshal([]byte(value), &tiers); err == nil { settings.PointsTotalTiers = tiers } case "referral_enabled": settings.ReferralEnabled = value == "true" case "delivery_schedule": var sched DeliverySchedule if err := json.Unmarshal([]byte(value), &sched); err == nil { settings.DeliverySchedule = sched } case "postal_zones": var zones []PostalZone if err := json.Unmarshal([]byte(value), &zones); err == nil { settings.PostalZones = zones } } } return settings, rows.Err() } // UpdateSettings sauvegarde les paramètres dans la DB func (d *Database) UpdateSettings(s AppSettings) error { boolStr := func(b bool) string { if b { return "true" } return "false" } if s.PointsCategoriesWeed == nil { s.PointsCategoriesWeed = []string{} } if s.PointsCategoriesZipette == nil { s.PointsCategoriesZipette = []string{} } if s.PointsCategoriesTotal == nil { s.PointsCategoriesTotal = []string{} } weedJSON, err := json.Marshal(s.PointsCategoriesWeed) if err != nil { return fmt.Errorf("erreur sérialisation weed: %w", err) } zipetteJSON, err := json.Marshal(s.PointsCategoriesZipette) if err != nil { return fmt.Errorf("erreur sérialisation zipette: %w", err) } totalJSON, err := json.Marshal(s.PointsCategoriesTotal) if err != nil { return fmt.Errorf("erreur sérialisation total: %w", err) } tx, err := d.Begin() if err != nil { return err } defer tx.Rollback() upsert := `INSERT INTO app_settings (key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value` if s.PointsWeedTiers == nil { s.PointsWeedTiers = []PointsTier{} } if s.PointsZipetteTiers == nil { s.PointsZipetteTiers = []PointsTier{} } if s.PointsTotalTiers == nil { s.PointsTotalTiers = []PointsTier{} } weedTiersJSON, err := json.Marshal(s.PointsWeedTiers) if err != nil { return fmt.Errorf("erreur sérialisation weed tiers: %w", err) } zipetteTiersJSON, err := json.Marshal(s.PointsZipetteTiers) if err != nil { return fmt.Errorf("erreur sérialisation zipette tiers: %w", err) } totalTiersJSON, err := json.Marshal(s.PointsTotalTiers) if err != nil { return fmt.Errorf("erreur sérialisation total tiers: %w", err) } pairs := [][2]string{ {"penalties_enabled", boolStr(s.PenaltiesEnabled)}, {"show_amende_score", boolStr(s.ShowAmendeScore)}, {"points_enabled", boolStr(s.PointsEnabled)}, {"points_categories_weed", string(weedJSON)}, {"points_categories_zipette", string(zipetteJSON)}, {"points_categories_total", string(totalJSON)}, {"points_separated", boolStr(s.PointsSeparated)}, {"points_weed_tiers", string(weedTiersJSON)}, {"points_zipette_tiers", string(zipetteTiersJSON)}, {"points_total_tiers", string(totalTiersJSON)}, {"referral_enabled", boolStr(s.ReferralEnabled)}, } schedJSON, err := json.Marshal(s.DeliverySchedule) if err != nil { return fmt.Errorf("erreur sérialisation delivery_schedule: %w", err) } pairs = append(pairs, [2]string{"delivery_schedule", string(schedJSON)}) if s.PostalZones == nil { s.PostalZones = []PostalZone{} } zonesJSON, err := json.Marshal(s.PostalZones) if err != nil { return fmt.Errorf("erreur sérialisation postal_zones: %w", err) } pairs = append(pairs, [2]string{"postal_zones", string(zonesJSON)}) for _, p := range pairs { if _, err = tx.Exec(upsert, p[0], p[1]); err != nil { return fmt.Errorf("erreur upsert %s: %w", p[0], err) } } return tx.Commit() }