From 32a60b4476e5a16f912ee25f7bcd705e66f3da42 Mon Sep 17 00:00:00 2001 From: Xor290 Date: Mon, 10 Aug 2026 11:27:11 +0200 Subject: [PATCH] fix: sql request --- backend/gestion/db/db_stat.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/backend/gestion/db/db_stat.go b/backend/gestion/db/db_stat.go index 9c283d8b..f02e09ab 100644 --- a/backend/gestion/db/db_stat.go +++ b/backend/gestion/db/db_stat.go @@ -37,11 +37,14 @@ func (d *Database) ReadResetAt(key string) time.Time { // ── Construction des clauses WHERE (filtrage par reset) ──────────────────── -// statusFilterClause construit " [AND created_at >= ?]" et renvoie -// la clause ainsi que les arguments à binder, dans l'ordre. -func statusFilterClause(baseStatus string, resetAt time.Time) (string, []interface{}) { +// statusFilterClause construit " [AND >= ?]" et +// renvoie la clause ainsi que les arguments à binder, dans l'ordre. +// dateColumn doit être qualifié par l'alias de table (ex: "c.created_at") dès +// que la requête appelante fait une jointure où plusieurs tables possèdent une +// colonne created_at, sous peine d'erreur Postgres "ambiguous column". +func statusFilterClause(baseStatus string, resetAt time.Time, dateColumn string) (string, []interface{}) { if !resetAt.IsZero() { - return baseStatus + " AND created_at >= ?", []interface{}{resetAt.Format(time.RFC3339)} + return baseStatus + " AND " + dateColumn + " >= ?", []interface{}{resetAt.Format(time.RFC3339)} } return baseStatus, nil } @@ -92,7 +95,7 @@ func (d *Database) LoadAdminStatsFilters() AdminStatsFilters { // ── Commandes par jour de la semaine (non annulées) ───────────────────────── func (d *Database) OrderPerDaysPerWeeks(wdRows *[]models.WeekdayRow, resetAt time.Time) error { - where, args := statusFilterClause("status != 'cancelled'", resetAt) + where, args := statusFilterClause("status != 'cancelled'", resetAt, "created_at") query := ` SELECT EXTRACT(DOW FROM created_at)::int AS dow, COUNT(*) AS count FROM commandes @@ -106,7 +109,7 @@ func (d *Database) OrderPerDaysPerWeeks(wdRows *[]models.WeekdayRow, resetAt tim // ── Commandes par jour sur 30 jours ────────────────────────────────────────── func (d *Database) OrdersByDayLast30(dayRows *[]models.DayRow, resetAt time.Time) error { - where, args := statusFilterClause("status != 'cancelled'", resetAt) + where, args := statusFilterClause("status != 'cancelled'", resetAt, "created_at") query := ` SELECT DATE(created_at) AS day, COUNT(*) AS count FROM commandes @@ -121,7 +124,7 @@ func (d *Database) OrdersByDayLast30(dayRows *[]models.DayRow, resetAt time.Time // ── Revenus par jour sur 30 jours (commandes approuvées) ───────────────────── func (d *Database) RevenueByDayLast30(dayRevRows *[]models.DayRevenueRow, resetAt time.Time) error { - where, args := statusFilterClause("status = 'approved'", resetAt) + where, args := statusFilterClause("status = 'approved'", resetAt, "created_at") query := ` SELECT DATE(created_at) AS day, COALESCE(SUM(total_prix - COALESCE(referral_used, 0)), 0) AS revenue FROM commandes @@ -146,7 +149,7 @@ func (d *Database) StatsByDayForMonth(rows *[]DailyMonthStatRow, monthStart time start := time.Date(monthStart.Year(), monthStart.Month(), 1, 0, 0, 0, 0, monthStart.Location()) end := start.AddDate(0, 1, 0) - where, whereArgs := statusFilterClause("status != 'cancelled'", resetAt) + where, whereArgs := statusFilterClause("status != 'cancelled'", resetAt, "created_at") query := ` SELECT @@ -195,7 +198,7 @@ func (d *Database) StatsByDayForMonth(rows *[]DailyMonthStatRow, monthStart time // cohérent avec TotalRevenue/RevenueByDayLast30, pour ne pas compter comme // "revenu" une commande encore en cours qui pourrait être annulée). func (d *Database) OrdersAndRevenueByHour(hourRows *[]models.HourRow, resetAt time.Time) error { - where, args := statusFilterClause("status != 'cancelled'", resetAt) + where, args := statusFilterClause("status != 'cancelled'", resetAt, "created_at") query := ` SELECT EXTRACT(HOUR FROM created_at)::int AS hour, @@ -215,7 +218,7 @@ func (d *Database) OrdersAndRevenueByHour(hourRows *[]models.HourRow, resetAt ti // commandes reflètent l'activité (non annulées), le revenu ne compte que les // commandes approuvées (revenu confirmé, cohérent avec le résumé global). func (d *Database) TopProducts(prodRows *[]models.ProductRow, resetAt time.Time, limit int) error { - where, args := statusFilterClause("c.status != 'cancelled'", resetAt) + where, args := statusFilterClause("c.status != 'cancelled'", resetAt, "c.created_at") args = append(args, limit) query := ` SELECT @@ -245,7 +248,7 @@ func (d *Database) TopProducts(prodRows *[]models.ProductRow, resetAt time.Time, // QuantityBreakdown : quantité/nombre de commandes reflètent l'activité (non // annulées), le revenu ne compte que les commandes approuvées (revenu confirmé). func (d *Database) QuantityBreakdown(qtyRows *[]models.QuantityBreakdownRow, resetAt time.Time) error { - where, args := statusFilterClause("c.status != 'cancelled'", resetAt) + where, args := statusFilterClause("c.status != 'cancelled'", resetAt, "c.created_at") query := ` SELECT ci.product_id, @@ -351,7 +354,7 @@ func (d *Database) DailyOrdersCount() (int64, error) { // TotalOrders renvoie le nombre total de commandes filtré par le reset "commandes". func (d *Database) TotalOrders(resetAt time.Time) (int64, error) { - where, args := statusFilterClause("status != 'cancelled'", resetAt) + where, args := statusFilterClause("status != 'cancelled'", resetAt, "created_at") var total int64 err := d.GDB.Raw(`SELECT COUNT(*) FROM commandes WHERE `+where, args...).Scan(&total).Error return total, err @@ -359,7 +362,7 @@ func (d *Database) TotalOrders(resetAt time.Time) (int64, error) { // TotalRevenue renvoie le revenu total (commandes approuvées) filtré par le reset "revenus". func (d *Database) TotalRevenue(resetAt time.Time) (float64, error) { - where, args := statusFilterClause("status = 'approved'", resetAt) + where, args := statusFilterClause("status = 'approved'", resetAt, "created_at") var total float64 err := d.GDB.Raw(`SELECT COALESCE(SUM(total_prix - COALESCE(referral_used, 0)), 0) FROM commandes WHERE `+where, args...). Scan(&total).Error @@ -368,7 +371,7 @@ func (d *Database) TotalRevenue(resetAt time.Time) (float64, error) { // ActiveDaysLast30 renvoie le nombre de jours distincts ayant eu au moins une commande sur 30 jours. func (d *Database) ActiveDaysLast30(resetAt time.Time) (int64, error) { - where, args := statusFilterClause("status != 'cancelled'", resetAt) + where, args := statusFilterClause("status != 'cancelled'", resetAt, "created_at") var activeDays int64 query := ` SELECT COUNT(DISTINCT DATE(created_at)) @@ -380,7 +383,7 @@ func (d *Database) ActiveDaysLast30(resetAt time.Time) (int64, error) { // OrdersCountLast30 renvoie le nombre de commandes sur les 30 derniers jours. func (d *Database) OrdersCountLast30(resetAt time.Time) (int64, error) { - where, args := statusFilterClause("status != 'cancelled'", resetAt) + where, args := statusFilterClause("status != 'cancelled'", resetAt, "created_at") var count int64 query := ` SELECT COUNT(*) FROM commandes