chore: update id order

This commit is contained in:
2026-03-28 17:00:00 +01:00
parent 5380abe8ed
commit 3bf3f5e605
48 changed files with 2347 additions and 3693 deletions
+294 -371
View File
@@ -1,26 +1,32 @@
package db
import (
"database/sql"
"encoding/json"
"fmt"
"gestion/models"
"log"
"strings"
"time"
"gorm.io/gorm"
)
func (d *Database) CreateClient(client *models.Client) error {
query := `INSERT INTO clients (username, password, nom, prenom, telephone, command, amende, must_change_password, created_at)
VALUES ($1, $2, $3, $4, $5, 0, 0.0, $6, CURRENT_TIMESTAMP)
RETURNING id, created_at`
err := d.QueryRow(query, client.Username, client.Password, client.Nom, client.Prenom, client.Telephone, client.MustChangePassword).Scan(
&client.ID,
&client.CreatedAt,
)
var result struct {
ID int `gorm:"column:id"`
CreatedAt time.Time `gorm:"column:created_at"`
}
err := d.GDB.Raw(`
INSERT INTO clients (username, password, nom, prenom, telephone, command, amende, must_change_password, created_at)
VALUES (?, ?, ?, ?, ?, 0, 0.0, ?, CURRENT_TIMESTAMP)
RETURNING id, created_at`,
client.Username, client.Password, client.Nom, client.Prenom, client.Telephone, client.MustChangePassword,
).Scan(&result).Error
if err != nil {
return fmt.Errorf("erreur lors de la création du client: %w", err)
}
client.ID = result.ID
client.CreatedAt = result.CreatedAt
log.Printf("✅ Client créé avec succès: %s %s (ID: %d)", client.Prenom, client.Nom, client.ID)
return nil
@@ -28,110 +34,109 @@ func (d *Database) CreateClient(client *models.Client) error {
// GetClientByID récupère un client par son ID
func (d *Database) GetClientByID(id int) (*models.Client, error) {
var client models.Client
query := `SELECT id, username, password, nom, prenom, telephone, command, amende, COALESCE(points_extra, '{}'::jsonb), created_at
FROM clients WHERE id = $1`
var pointsExtraJSON []byte
err := d.QueryRow(query, id).Scan(
&client.ID,
&client.Username,
&client.Password,
&client.Nom,
&client.Prenom,
&client.Telephone,
&client.Command,
&client.Amende,
&pointsExtraJSON,
&client.CreatedAt,
)
if err == sql.ErrNoRows {
return nil, fmt.Errorf("client non trouvé")
var row struct {
ID int `gorm:"column:id"`
Username string `gorm:"column:username"`
Password string `gorm:"column:password"`
Nom string `gorm:"column:nom"`
Prenom string `gorm:"column:prenom"`
Telephone string `gorm:"column:telephone"`
Command int `gorm:"column:command"`
Amende float64 `gorm:"column:amende"`
PointsExtraJSON []byte `gorm:"column:points_extra"`
CreatedAt time.Time `gorm:"column:created_at"`
}
err := d.GDB.Raw(`
SELECT id, username, password, nom, prenom, telephone, command, amende,
COALESCE(points_extra, '{}'::jsonb) as points_extra, created_at
FROM clients WHERE id = ?`, id).Scan(&row).Error
if err != nil {
return nil, fmt.Errorf("erreur lors de la récupération du client: %w", err)
}
if len(pointsExtraJSON) > 0 {
json.Unmarshal(pointsExtraJSON, &client.PointsExtra)
if row.ID == 0 {
return nil, fmt.Errorf("client non trouvé")
}
return &client, nil
client := &models.Client{
ID: row.ID,
Username: row.Username,
Password: row.Password,
Nom: row.Nom,
Prenom: row.Prenom,
Telephone: row.Telephone,
Command: row.Command,
Amende: row.Amende,
CreatedAt: row.CreatedAt,
}
client.PointsExtra = map[string]int{}
if len(row.PointsExtraJSON) > 0 {
json.Unmarshal(row.PointsExtraJSON, &client.PointsExtra)
}
return client, nil
}
// GetAllClients récupère tous les clients
func (d *Database) GetAllClients() ([]*models.Client, error) {
query := `SELECT id, username, password, nom, prenom, telephone, command, amende, referral_balance, COALESCE(points_extra, '{}'::jsonb), created_at
FROM clients ORDER BY created_at DESC`
rows, err := d.Query(query)
var rows []struct {
ID int `gorm:"column:id"`
Username string `gorm:"column:username"`
Password string `gorm:"column:password"`
Nom string `gorm:"column:nom"`
Prenom string `gorm:"column:prenom"`
Telephone string `gorm:"column:telephone"`
Command int `gorm:"column:command"`
Amende float64 `gorm:"column:amende"`
ReferralBalance float64 `gorm:"column:referral_balance"`
PointsExtraJSON []byte `gorm:"column:points_extra"`
CreatedAt time.Time `gorm:"column:created_at"`
}
err := d.GDB.Raw(`
SELECT id, username, password, nom, prenom, telephone, command, amende, referral_balance,
COALESCE(points_extra, '{}'::jsonb) as points_extra, created_at
FROM clients ORDER BY created_at DESC`).Scan(&rows).Error
if err != nil {
return nil, fmt.Errorf("erreur lors de la récupération des clients: %w", err)
}
defer rows.Close()
var clients []*models.Client
for rows.Next() {
client := &models.Client{}
var pointsExtraJSON []byte
err := rows.Scan(
&client.ID,
&client.Username,
&client.Password,
&client.Nom,
&client.Prenom,
&client.Telephone,
&client.Command,
&client.Amende,
&client.ReferralBalance,
&pointsExtraJSON,
&client.CreatedAt,
)
if err != nil {
return nil, fmt.Errorf("erreur lors du scan du client: %w", err)
clients := make([]*models.Client, 0, len(rows))
for _, row := range rows {
client := &models.Client{
ID: row.ID,
Username: row.Username,
Password: row.Password,
Nom: row.Nom,
Prenom: row.Prenom,
Telephone: row.Telephone,
Command: row.Command,
Amende: row.Amende,
ReferralBalance: row.ReferralBalance,
CreatedAt: row.CreatedAt,
}
client.PointsExtra = map[string]int{}
if len(pointsExtraJSON) > 0 {
json.Unmarshal(pointsExtraJSON, &client.PointsExtra)
if len(row.PointsExtraJSON) > 0 {
json.Unmarshal(row.PointsExtraJSON, &client.PointsExtra)
}
clients = append(clients, client)
}
if err = rows.Err(); err != nil {
return nil, fmt.Errorf("erreur lors de l'itération des résultats: %w", err)
}
return clients, nil
}
// UpdateClient met à jour un client existant
func (d *Database) UpdateClient(client *models.Client) error {
query := `UPDATE clients
SET username = $1, password = $2, nom = $3, prenom = $4, telephone = $5,
command = $6, amende = $7
WHERE id = $8`
result, err := d.Exec(query,
client.Username,
client.Password,
client.Nom,
client.Prenom,
client.Telephone,
client.Command,
client.Amende,
client.ID,
result := d.GDB.Exec(`
UPDATE clients
SET username = ?, password = ?, nom = ?, prenom = ?, telephone = ?,
command = ?, amende = ?
WHERE id = ?`,
client.Username, client.Password, client.Nom, client.Prenom, client.Telephone,
client.Command, client.Amende, client.ID,
)
if err != nil {
return fmt.Errorf("erreur lors de la mise à jour du client: %w", err)
if result.Error != nil {
return fmt.Errorf("erreur lors de la mise à jour du client: %w", result.Error)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("erreur lors de la vérification des lignes affectées: %w", err)
}
if rowsAffected == 0 {
if result.RowsAffected == 0 {
return fmt.Errorf("client non trouvé")
}
@@ -142,19 +147,11 @@ func (d *Database) UpdateClient(client *models.Client) error {
func (d *Database) DeleteClient(id int) error {
_ = d.RevokeAllUserTokens(id, "client")
query := `DELETE FROM clients WHERE id = $1`
result, err := d.Exec(query, id)
if err != nil {
return fmt.Errorf("erreur lors de la suppression du client: %w", err)
result := d.GDB.Exec(`DELETE FROM clients WHERE id = ?`, id)
if result.Error != nil {
return fmt.Errorf("erreur lors de la suppression du client: %w", result.Error)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("erreur lors de la vérification des lignes affectées: %w", err)
}
if rowsAffected == 0 {
if result.RowsAffected == 0 {
return fmt.Errorf("client non trouvé")
}
@@ -163,19 +160,11 @@ func (d *Database) DeleteClient(id int) error {
// UpdateClientPassword met à jour le mot de passe d'un client
func (d *Database) UpdateClientPassword(clientID int, hashedPassword string) error {
query := `UPDATE clients SET password = $1 WHERE id = $2`
result, err := d.Exec(query, hashedPassword, clientID)
if err != nil {
return fmt.Errorf("erreur lors de la mise à jour du mot de passe: %w", err)
result := d.GDB.Exec(`UPDATE clients SET password = ? WHERE id = ?`, hashedPassword, clientID)
if result.Error != nil {
return fmt.Errorf("erreur lors de la mise à jour du mot de passe: %w", result.Error)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("erreur lors de la vérification: %w", err)
}
if rowsAffected == 0 {
if result.RowsAffected == 0 {
return fmt.Errorf("client non trouvé")
}
@@ -184,19 +173,13 @@ func (d *Database) UpdateClientPassword(clientID int, hashedPassword string) err
// UpdateClientPasswordAndClearFlag met à jour le mot de passe et remet must_change_password à false
func (d *Database) UpdateClientPasswordAndClearFlag(clientID int, hashedPassword string) error {
query := `UPDATE clients SET password = $1, must_change_password = FALSE, updated_at = CURRENT_TIMESTAMP WHERE id = $2`
result, err := d.Exec(query, hashedPassword, clientID)
if err != nil {
return fmt.Errorf("erreur lors de la mise à jour du mot de passe: %w", err)
result := d.GDB.Exec(`
UPDATE clients SET password = ?, must_change_password = FALSE, updated_at = CURRENT_TIMESTAMP
WHERE id = ?`, hashedPassword, clientID)
if result.Error != nil {
return fmt.Errorf("erreur lors de la mise à jour du mot de passe: %w", result.Error)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("erreur lors de la vérification: %w", err)
}
if rowsAffected == 0 {
if result.RowsAffected == 0 {
return fmt.Errorf("client non trouvé")
}
@@ -210,19 +193,18 @@ func (d *Database) GetClientStats(clientID int) (map[string]interface{}, error)
return nil, err
}
// Compter les commandes du client
var totalCommands, pendingCommands, completedCommands int
countQuery := `SELECT
COUNT(*) as total,
SUM(CASE WHEN status = 'pending' OR status = 'livre' THEN 1 ELSE 0 END) as pending,
SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END) as completed
FROM commandes WHERE username = $1`
err = d.QueryRow(countQuery, client.Username).Scan(&totalCommands, &pendingCommands, &completedCommands)
if err != nil {
var statsResult struct {
Total int `gorm:"column:total"`
Pending int `gorm:"column:pending"`
Completed int `gorm:"column:completed"`
}
if err := d.GDB.Raw(`
SELECT
COUNT(*) as total,
COALESCE(SUM(CASE WHEN status = 'pending' OR status = 'livre' THEN 1 ELSE 0 END), 0) as pending,
COALESCE(SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END), 0) as completed
FROM commandes WHERE username = ?`, client.Username).Scan(&statsResult).Error; err != nil {
log.Printf("⚠️ Erreur calcul stats: %v", err)
totalCommands, pendingCommands, completedCommands = 0, 0, 0
}
stats := map[string]interface{}{
@@ -231,9 +213,9 @@ func (d *Database) GetClientStats(clientID int) (map[string]interface{}, error)
"nom": client.Nom,
"prenom": client.Prenom,
"telephone": client.Telephone,
"total_commands": totalCommands,
"pending_commands": pendingCommands,
"completed_commands": completedCommands,
"total_commands": statsResult.Total,
"pending_commands": statsResult.Pending,
"completed_commands": statsResult.Completed,
"points_extra": client.PointsExtra,
"amende": client.Amende,
"member_since": client.CreatedAt,
@@ -243,23 +225,22 @@ func (d *Database) GetClientStats(clientID int) (map[string]interface{}, error)
}
func (d *Database) GetClientAmende(username string) (float64, error) {
var amende float64
query := `SELECT COALESCE(amende, 0) FROM clients WHERE username = $1`
err := d.QueryRow(query, username).Scan(&amende)
var result struct {
Amende float64 `gorm:"column:amende"`
}
err := d.GDB.Raw(`SELECT COALESCE(amende, 0) as amende FROM clients WHERE username = ?`, username).Scan(&result).Error
if err != nil {
log.Printf("❌ [GetClientAmende] Erreur pour %s: %v", username, err)
return 0, fmt.Errorf("erreur récupération pénalités: %w", err)
}
log.Printf("💰 [GetClientAmende] Client %s: %.2f points", username, amende)
return amende, nil
log.Printf("💰 [GetClientAmende] Client %s: %.2f points", username, result.Amende)
return result.Amende, nil
}
func (d *Database) PayClientPenalties(username string, amountPaid float64) error {
log.Printf("💳 [PayClientPenalties] Paiement de %.2f points pour %s", amountPaid, username)
// Vérifier le montant actuel
currentAmount, err := d.GetClientAmende(username)
if err != nil {
return err
@@ -273,21 +254,14 @@ func (d *Database) PayClientPenalties(username string, amountPaid float64) error
return fmt.Errorf("montant insuffisant: %.2f payé, %.2f requis", amountPaid, currentAmount)
}
query := `UPDATE clients
SET amende = 0, updated_at = CURRENT_TIMESTAMP
WHERE username = $1`
result, err := d.Exec(query, username)
if err != nil {
log.Printf("❌ [PayClientPenalties] Erreur UPDATE: %v", err)
return fmt.Errorf("erreur paiement pénalités: %w", err)
result := d.GDB.Exec(`
UPDATE clients SET amende = 0, updated_at = CURRENT_TIMESTAMP
WHERE username = ?`, username)
if result.Error != nil {
log.Printf("❌ [PayClientPenalties] Erreur UPDATE: %v", result.Error)
return fmt.Errorf("erreur paiement pénalités: %w", result.Error)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("erreur vérification: %w", err)
}
if rowsAffected == 0 {
if result.RowsAffected == 0 {
return fmt.Errorf("client non trouvé")
}
@@ -299,20 +273,11 @@ func (d *Database) PayClientPenalties(username string, amountPaid float64) error
// IncrementClientCommandCount incrémente le compteur de commandes du client
func (d *Database) IncrementClientCommandCount(username string) error {
query := `UPDATE clients
SET command = command + 1
WHERE username = $1`
result, err := d.Exec(query, username)
if err != nil {
return fmt.Errorf("erreur lors de l'incrémentation du compteur: %w", err)
result := d.GDB.Exec(`UPDATE clients SET command = command + 1 WHERE username = ?`, username)
if result.Error != nil {
return fmt.Errorf("erreur lors de l'incrémentation du compteur: %w", result.Error)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("erreur lors de la vérification: %w", err)
}
if rowsAffected == 0 {
if result.RowsAffected == 0 {
return fmt.Errorf("client non trouvé")
}
@@ -323,23 +288,19 @@ func (d *Database) AddClientPointsByCategory(username string, points int, poolKe
if poolKey == "" {
poolKey = "pool_0"
}
result, err := d.Exec(`
result := d.GDB.Exec(`
UPDATE clients
SET points_extra = jsonb_set(
COALESCE(points_extra, '{}'::jsonb),
ARRAY[$2],
to_jsonb(COALESCE((points_extra->>$2)::int, 0) + $3)
ARRAY[?],
to_jsonb(COALESCE((points_extra->>?)::int, 0) + ?)
), updated_at = CURRENT_TIMESTAMP
WHERE username = $1
`, username, poolKey, points)
if err != nil {
return fmt.Errorf("erreur lors de l'ajout de points: %w", err)
WHERE username = ?`,
poolKey, poolKey, points, username)
if result.Error != nil {
return fmt.Errorf("erreur lors de l'ajout de points: %w", result.Error)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("erreur lors de la vérification: %w", err)
}
if rowsAffected == 0 {
if result.RowsAffected == 0 {
return fmt.Errorf("client non trouvé")
}
log.Printf("✅ %d points (key=%s) ajoutés au client %s", points, poolKey, username)
@@ -347,101 +308,114 @@ func (d *Database) AddClientPointsByCategory(username string, points int, poolKe
}
func (d *Database) CalculateAndAddPointsForCommand(commandID int, username string) (int, error) {
tx, err := d.Begin()
if err != nil {
return 0, fmt.Errorf("erreur transaction: %w", err)
}
defer tx.Rollback()
points, _, err := d.CalculateAndAddPointsForCommandTx(tx, commandID, username)
var totalPoints int
err := d.GDB.Transaction(func(tx *gorm.DB) error {
points, _, err := d.CalculateAndAddPointsForCommandTx(tx, commandID, username)
if err != nil {
return err
}
totalPoints = points
return nil
})
if err != nil {
return 0, err
}
if err := tx.Commit(); err != nil {
return 0, fmt.Errorf("erreur commit: %w", err)
}
return points, nil
return totalPoints, nil
}
func (d *Database) GetClientByTelephone(telephone string) (*models.Client, error) {
client := &models.Client{}
query := `SELECT id, username, password, nom, prenom, telephone, command, amende, created_at
FROM clients WHERE telephone = $1`
err := d.QueryRow(query, telephone).Scan(
&client.ID,
&client.Username,
&client.Password,
&client.Nom,
&client.Prenom,
&client.Telephone,
&client.Command,
&client.Amende,
&client.CreatedAt,
)
if err == sql.ErrNoRows {
return nil, nil
var row struct {
ID int `gorm:"column:id"`
Username string `gorm:"column:username"`
Password string `gorm:"column:password"`
Nom string `gorm:"column:nom"`
Prenom string `gorm:"column:prenom"`
Telephone string `gorm:"column:telephone"`
Command int `gorm:"column:command"`
Amende float64 `gorm:"column:amende"`
CreatedAt time.Time `gorm:"column:created_at"`
}
err := d.GDB.Raw(`
SELECT id, username, password, nom, prenom, telephone, command, amende, created_at
FROM clients WHERE telephone = ?`, telephone).Scan(&row).Error
if err != nil {
return nil, fmt.Errorf("erreur lors de la récupération du client: %w", err)
}
if row.ID == 0 {
return nil, nil
}
return client, nil
return &models.Client{
ID: row.ID,
Username: row.Username,
Password: row.Password,
Nom: row.Nom,
Prenom: row.Prenom,
Telephone: row.Telephone,
Command: row.Command,
Amende: row.Amende,
CreatedAt: row.CreatedAt,
}, nil
}
// GetClientByUsername récupère un client par son username
func (d *Database) GetClientByUsername(username string) (*models.Client, error) {
client := &models.Client{}
var pointsExtraJSON []byte
query := `SELECT id, username, password, nom, prenom, telephone, command, amende, must_change_password, COALESCE(points_extra, '{}'::jsonb), created_at
FROM clients WHERE username = $1`
err := d.QueryRow(query, username).Scan(
&client.ID,
&client.Username,
&client.Password,
&client.Nom,
&client.Prenom,
&client.Telephone,
&client.Command,
&client.Amende,
&client.MustChangePassword,
&pointsExtraJSON,
&client.CreatedAt,
)
if err == sql.ErrNoRows {
return nil, nil
var row struct {
ID int `gorm:"column:id"`
Username string `gorm:"column:username"`
Password string `gorm:"column:password"`
Nom string `gorm:"column:nom"`
Prenom string `gorm:"column:prenom"`
Telephone string `gorm:"column:telephone"`
Command int `gorm:"column:command"`
Amende float64 `gorm:"column:amende"`
MustChangePassword bool `gorm:"column:must_change_password"`
PointsExtraJSON []byte `gorm:"column:points_extra"`
CreatedAt time.Time `gorm:"column:created_at"`
}
err := d.GDB.Raw(`
SELECT id, username, password, nom, prenom, telephone, command, amende,
must_change_password, COALESCE(points_extra, '{}'::jsonb) as points_extra, created_at
FROM clients WHERE username = ?`, username).Scan(&row).Error
if err != nil {
return nil, fmt.Errorf("erreur lors de la récupération du client: %w", err)
}
if row.ID == 0 {
return nil, nil
}
client := &models.Client{
ID: row.ID,
Username: row.Username,
Password: row.Password,
Nom: row.Nom,
Prenom: row.Prenom,
Telephone: row.Telephone,
Command: row.Command,
Amende: row.Amende,
MustChangePassword: row.MustChangePassword,
CreatedAt: row.CreatedAt,
}
client.PointsExtra = map[string]int{}
if len(pointsExtraJSON) > 0 {
json.Unmarshal(pointsExtraJSON, &client.PointsExtra)
if len(row.PointsExtraJSON) > 0 {
json.Unmarshal(row.PointsExtraJSON, &client.PointsExtra)
}
return client, nil
}
func (d *Database) GetClientPenaltiesInfo(username string) (map[string]interface{}, error) {
// Récupérer le montant des pénalités
amende, err := d.GetClientAmende(username)
if err != nil {
return nil, err
}
// Récupérer le nombre d'annulations
cancellationsCount, err := d.GetClientCancellationsCount(username)
if err != nil {
log.Printf("⚠️ [GetClientPenaltiesInfo] Erreur récup annulations: %v", err)
cancellationsCount = 0
}
// Récupérer l'historique d'annulations
cancellationHistory, err := d.GetClientCancellationHistory(username)
if err != nil {
log.Printf("⚠️ [GetClientPenaltiesInfo] Erreur récup historique: %v", err)
@@ -481,13 +455,10 @@ func (d *Database) CheckClientCanOrder(username string) (bool, float64, error) {
// extraPoolKey != "" → reset points_extra[extraPoolKey] uniquement
// extraPoolKey == "" (poolIdx=-1) → reset total points_extra
func (d *Database) ResetClientPoint(username string, poolIdx int, extraPoolKey string) error {
var query string
switch {
case extraPoolKey != "":
_, err := d.Exec(
`UPDATE clients SET points_extra = points_extra - $2, updated_at = CURRENT_TIMESTAMP WHERE username = $1`,
username, extraPoolKey,
)
if extraPoolKey != "" {
err := d.GDB.Exec(`
UPDATE clients SET points_extra = points_extra - ?, updated_at = CURRENT_TIMESTAMP
WHERE username = ?`, extraPoolKey, username).Error
if err != nil {
log.Printf("❌ [ResetClientPointAdmin] Erreur UPDATE extra: %v", err)
} else {
@@ -495,25 +466,20 @@ func (d *Database) ResetClientPoint(username string, poolIdx int, extraPoolKey s
Redis.Del(RedisCtx, cacheKey)
}
return err
default: // -1 ou poolIdx sans clé → reset total
query = `UPDATE clients SET points_extra = '{}'::jsonb, updated_at = CURRENT_TIMESTAMP WHERE username = $1`
}
result, err := d.Exec(query, username)
if err != nil {
log.Printf("❌ [ResetClientPointAdmin] Erreur UPDATE: %v", err)
return fmt.Errorf("erreur reset points: %w", err)
// -1 ou poolIdx sans clé → reset total
result := d.GDB.Exec(`
UPDATE clients SET points_extra = '{}'::jsonb, updated_at = CURRENT_TIMESTAMP
WHERE username = ?`, username)
if result.Error != nil {
log.Printf("❌ [ResetClientPointAdmin] Erreur UPDATE: %v", result.Error)
return fmt.Errorf("erreur reset points: %w", result.Error)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("erreur vérification: %w", err)
}
if rowsAffected == 0 {
if result.RowsAffected == 0 {
return fmt.Errorf("client non trouvé")
}
// Invalider le cache Redis du client
cacheKey := fmt.Sprintf("client:%s", username)
Redis.Del(RedisCtx, cacheKey)
@@ -525,33 +491,20 @@ func (d *Database) ResetClientPenalties(username string, resetCancellationsCount
var query string
if resetCancellationsCount {
query = `UPDATE clients
SET amende = 0,
cancellations_count = 0,
updated_at = CURRENT_TIMESTAMP
WHERE username = $1`
query = `UPDATE clients SET amende = 0, cancellations_count = 0, updated_at = CURRENT_TIMESTAMP WHERE username = ?`
} else {
query = `UPDATE clients
SET amende = 0,
updated_at = CURRENT_TIMESTAMP
WHERE username = $1`
query = `UPDATE clients SET amende = 0, updated_at = CURRENT_TIMESTAMP WHERE username = ?`
}
result, err := d.Exec(query, username)
if err != nil {
log.Printf("❌ [ResetClientPenalties] Erreur UPDATE: %v", err)
return fmt.Errorf("erreur reset pénalités: %w", err)
result := d.GDB.Exec(query, username)
if result.Error != nil {
log.Printf("❌ [ResetClientPenalties] Erreur UPDATE: %v", result.Error)
return fmt.Errorf("erreur reset pénalités: %w", result.Error)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("erreur vérification: %w", err)
}
if rowsAffected == 0 {
if result.RowsAffected == 0 {
return fmt.Errorf("client non trouvé")
}
// Invalider le cache Redis du client
cacheKey := fmt.Sprintf("client:%s", username)
Redis.Del(RedisCtx, cacheKey)
@@ -559,38 +512,29 @@ func (d *Database) ResetClientPenalties(username string, resetCancellationsCount
}
func (d *Database) GetAllClientsWithPenalties() ([]map[string]interface{}, error) {
query := `
var rows []struct {
Username string `gorm:"column:username"`
Amende float64 `gorm:"column:amende"`
CancellationsCount int `gorm:"column:cancellations_count"`
UpdatedAt interface{} `gorm:"column:updated_at"`
}
err := d.GDB.Raw(`
SELECT username, amende, COALESCE(cancellations_count, 0) as cancellations_count, updated_at
FROM clients
WHERE amende > 0
ORDER BY amende DESC
`
rows, err := d.Query(query)
ORDER BY amende DESC`).Scan(&rows).Error
if err != nil {
log.Printf("❌ [GetAllClientsWithPenalties] Erreur query: %v", err)
return nil, fmt.Errorf("erreur récupération clients: %w", err)
}
defer rows.Close()
var clients []map[string]interface{}
for rows.Next() {
var username string
var amende float64
var cancellationsCount int
var updatedAt interface{}
err := rows.Scan(&username, &amende, &cancellationsCount, &updatedAt)
if err != nil {
log.Printf("⚠️ [GetAllClientsWithPenalties] Erreur scan: %v", err)
continue
}
clients := make([]map[string]interface{}, 0, len(rows))
for _, row := range rows {
clients = append(clients, map[string]interface{}{
"username": username,
"total_penalty": amende,
"cancellations_count": cancellationsCount,
"last_updated": updatedAt,
"username": row.Username,
"total_penalty": row.Amende,
"cancellations_count": row.CancellationsCount,
"last_updated": row.UpdatedAt,
})
}
@@ -600,51 +544,42 @@ func (d *Database) GetAllClientsWithPenalties() ([]map[string]interface{}, error
}
func (d *Database) GetClientPenaltiesStats() (map[string]interface{}, error) {
query := `
var result struct {
ClientsWithPenalties int `gorm:"column:clients_with_penalties"`
TotalPenalties float64 `gorm:"column:total_penalties"`
AvgPenalty float64 `gorm:"column:avg_penalty"`
MaxPenalty float64 `gorm:"column:max_penalty"`
TotalClients int `gorm:"column:total_clients"`
}
err := d.GDB.Raw(`
SELECT
COUNT(CASE WHEN amende > 0 THEN 1 END) as clients_with_penalties,
COALESCE(SUM(amende), 0) as total_penalties,
COALESCE(AVG(amende), 0) as avg_penalty,
COALESCE(MAX(amende), 0) as max_penalty,
COUNT(*) as total_clients
FROM clients
`
var stats struct {
ClientsWithPenalties int
TotalPenalties float64
AvgPenalty float64
MaxPenalty float64
TotalClients int
}
err := d.QueryRow(query).Scan(
&stats.ClientsWithPenalties,
&stats.TotalPenalties,
&stats.AvgPenalty,
&stats.MaxPenalty,
&stats.TotalClients,
)
FROM clients`).Scan(&result).Error
if err != nil {
log.Printf("❌ [GetClientPenaltiesStats] Erreur: %v", err)
return nil, fmt.Errorf("erreur récupération stats: %w", err)
}
result := map[string]interface{}{
"clients_with_penalties": stats.ClientsWithPenalties,
"total_penalties": stats.TotalPenalties,
"average_penalty": stats.AvgPenalty,
"max_penalty": stats.MaxPenalty,
"total_clients": stats.TotalClients,
stats := map[string]interface{}{
"clients_with_penalties": result.ClientsWithPenalties,
"total_penalties": result.TotalPenalties,
"average_penalty": result.AvgPenalty,
"max_penalty": result.MaxPenalty,
"total_clients": result.TotalClients,
}
log.Printf("📊 [GetClientPenaltiesStats] Stats: %d/%d clients avec pénalités",
stats.ClientsWithPenalties, stats.TotalClients)
result.ClientsWithPenalties, result.TotalClients)
return result, nil
return stats, nil
}
func (d *Database) CalculateAndAddPointsForCommandTx(tx *sql.Tx, commandID int, username string) (int, string, error) {
func (d *Database) CalculateAndAddPointsForCommandTx(tx *gorm.DB, commandID int, username string) (int, string, error) {
log.Printf("💰 [CalcPointsTx] START - cmd=%d, user=%s", commandID, username)
// Charger les paramètres globaux
@@ -674,45 +609,34 @@ func (d *Database) CalculateAndAddPointsForCommandTx(tx *sql.Tx, commandID int,
}
// ✅ ÉTAPE 1: Récupérer tous les items de la commande avec leurs catégories
rows, err := tx.Query(`
var items []struct {
Quantite float64 `gorm:"column:quantite"`
Prix float64 `gorm:"column:prix"`
Category string `gorm:"column:category"`
}
if err := tx.Raw(`
SELECT ci.quantite, ci.prix, COALESCE(p.category, '') as category
FROM command_items ci
LEFT JOIN products p ON ci.product_id = p.id
WHERE ci.command_id = $1
`, commandID)
if err != nil {
WHERE ci.command_id = ?
`, commandID).Scan(&items).Error; err != nil {
log.Printf("❌ [CalcPointsTx] Erreur query items: %v", err)
return 0, "", fmt.Errorf("erreur récupération items: %w", err)
}
defer rows.Close()
var itemCount int
poolTotals := make([]float64, len(pools))
for rows.Next() {
var quantite, prix float64
var category string
if err := rows.Scan(&quantite, &prix, &category); err != nil {
log.Printf("❌ [CalcPointsTx] Erreur scan: %v", err)
return 0, "", fmt.Errorf("erreur lecture item: %w", err)
}
itemCount++
catLower := strings.ToLower(category)
if poolIdx, ok := catToPool[catLower]; ok {
poolTotals[poolIdx] += prix
}
}
if err = rows.Err(); err != nil {
log.Printf("❌ [CalcPointsTx] Erreur rows: %v", err)
return 0, "", fmt.Errorf("erreur itération items: %w", err)
}
if itemCount == 0 {
if len(items) == 0 {
log.Printf("⚠️ [CalcPointsTx] Aucun item trouvé pour cmd %d", commandID)
return 0, "", nil
}
poolTotals := make([]float64, len(pools))
for _, item := range items {
catLower := strings.ToLower(item.Category)
if poolIdx, ok := catToPool[catLower]; ok {
poolTotals[poolIdx] += item.Prix
}
}
for i, t := range poolTotals {
log.Printf("📊 [CalcPointsTx] Pool[%d] (%s): %.2f€", i, pools[i].Name, t)
}
@@ -747,16 +671,15 @@ func (d *Database) CalculateAndAddPointsForCommandTx(tx *sql.Tx, commandID int,
if poolPts[i] == 0 {
continue
}
_, err = tx.Exec(`
if err := tx.Exec(`
UPDATE clients
SET points_extra = jsonb_set(
COALESCE(points_extra, '{}'::jsonb),
ARRAY[$2],
to_jsonb(COALESCE((points_extra->>$2)::int, 0) + $3)
ARRAY[?],
to_jsonb(COALESCE((points_extra->>?)::int, 0) + ?)
), updated_at = CURRENT_TIMESTAMP
WHERE username = $1
`, username, pool.Key, poolPts[i])
if err != nil {
WHERE username = ?
`, pool.Key, pool.Key, poolPts[i], username).Error; err != nil {
log.Printf("❌ [CalcPointsTx] Erreur UPDATE points_extra pool[%d] (%s): %v", i, pool.Key, err)
return 0, "", fmt.Errorf("erreur mise à jour points pool[%d]: %w", i, err)
}
@@ -783,22 +706,22 @@ func (d *Database) CanUserAccessCommand(
// 🚚 Livreur : seulement commandes assignées
if role == "livreur" {
err := d.QueryRow(`
err := d.GDB.Raw(`
SELECT EXISTS(
SELECT 1 FROM commandes
WHERE id = $1 AND livreur_assign = $2
WHERE id = ? AND livreur_assign = ?
)
`, commandID, username).Scan(&exists)
`, commandID, username).Scan(&exists).Error
return exists, err
}
// 👤 User : seulement SES commandes
err := d.QueryRow(`
err := d.GDB.Raw(`
SELECT EXISTS(
SELECT 1 FROM commandes
WHERE id = $1 AND username = $2
WHERE id = ? AND username = ?
)
`, commandID, username).Scan(&exists)
`, commandID, username).Scan(&exists).Error
return exists, err
}