970 lines
27 KiB
Go
970 lines
27 KiB
Go
package db
|
||
|
||
import (
|
||
"database/sql"
|
||
"fmt"
|
||
"gestion/models"
|
||
"log"
|
||
"strings"
|
||
)
|
||
|
||
func (d *Database) CreateClient(client *models.Client) error {
|
||
query := `INSERT INTO clients (username, password, nom, prenom, telephone, command, point, point_zipette, amende, created_at)
|
||
VALUES ($1, $2, $3, $4, $5, 0, 0, 0, 0.0, CURRENT_TIMESTAMP)
|
||
RETURNING id, created_at`
|
||
|
||
err := d.QueryRow(query, client.Username, client.Password, client.Nom, client.Prenom, client.Telephone).Scan(
|
||
&client.ID,
|
||
&client.CreatedAt,
|
||
)
|
||
if err != nil {
|
||
return fmt.Errorf("erreur lors de la création du client: %w", err)
|
||
}
|
||
|
||
log.Printf("✅ Client créé avec succès: %s %s (ID: %d)", client.Prenom, client.Nom, client.ID)
|
||
return nil
|
||
}
|
||
|
||
// 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, point, point_zipette, amende, created_at
|
||
FROM clients WHERE id = $1`
|
||
|
||
err := d.QueryRow(query, id).Scan(
|
||
&client.ID,
|
||
&client.Username,
|
||
&client.Password,
|
||
&client.Nom,
|
||
&client.Prenom,
|
||
&client.Telephone,
|
||
&client.Command,
|
||
&client.Point,
|
||
&client.PointZipette,
|
||
&client.Amende,
|
||
&client.CreatedAt,
|
||
)
|
||
|
||
if err == sql.ErrNoRows {
|
||
return nil, fmt.Errorf("client non trouvé")
|
||
}
|
||
if err != nil {
|
||
return nil, fmt.Errorf("erreur lors de la récupération du client: %w", err)
|
||
}
|
||
|
||
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, point, point_zipette, amende, created_at
|
||
FROM clients ORDER BY created_at DESC`
|
||
|
||
rows, err := d.Query(query)
|
||
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{}
|
||
err := rows.Scan(
|
||
&client.ID,
|
||
&client.Username,
|
||
&client.Password,
|
||
&client.Nom,
|
||
&client.Prenom,
|
||
&client.Telephone,
|
||
&client.Command,
|
||
&client.Point,
|
||
&client.PointZipette,
|
||
&client.Amende,
|
||
&client.CreatedAt,
|
||
)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("erreur lors du scan du client: %w", err)
|
||
}
|
||
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, point = $7, point_zipette = $8, amende = $9
|
||
WHERE id = $10`
|
||
|
||
result, err := d.Exec(query,
|
||
client.Username,
|
||
client.Password,
|
||
client.Nom,
|
||
client.Prenom,
|
||
client.Telephone,
|
||
client.Command,
|
||
client.Point,
|
||
client.PointZipette,
|
||
client.Amende,
|
||
client.ID,
|
||
)
|
||
if err != nil {
|
||
return fmt.Errorf("erreur lors de la mise à jour du client: %w", err)
|
||
}
|
||
|
||
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 {
|
||
return fmt.Errorf("client non trouvé")
|
||
}
|
||
|
||
log.Printf("✅ Client mis à jour: %s (ID: %d)", client.Username, client.ID)
|
||
return nil
|
||
}
|
||
|
||
// DeleteClient supprime un client
|
||
func (d *Database) DeleteClient(id int) error {
|
||
// ✅ MODIFIÉ : Supprimer tous les tokens du client avec le user_type "client"
|
||
_ = 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)
|
||
}
|
||
|
||
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 {
|
||
return fmt.Errorf("client non trouvé")
|
||
}
|
||
|
||
log.Printf("✅ Client supprimé (ID: %d)", id)
|
||
return nil
|
||
}
|
||
|
||
// 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)
|
||
}
|
||
|
||
rowsAffected, err := result.RowsAffected()
|
||
if err != nil {
|
||
return fmt.Errorf("erreur lors de la vérification: %w", err)
|
||
}
|
||
|
||
if rowsAffected == 0 {
|
||
return fmt.Errorf("client non trouvé")
|
||
}
|
||
|
||
log.Printf("✅ Mot de passe client mis à jour (ID: %d)", clientID)
|
||
return nil
|
||
}
|
||
|
||
// GetClientStats récupère les statistiques d'un client
|
||
func (d *Database) GetClientStats(clientID int) (map[string]interface{}, error) {
|
||
client, err := d.GetClientByID(clientID)
|
||
if err != nil {
|
||
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 = 'support' 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 {
|
||
log.Printf("⚠️ Erreur calcul stats: %v", err)
|
||
totalCommands, pendingCommands, completedCommands = 0, 0, 0
|
||
}
|
||
|
||
stats := map[string]interface{}{
|
||
"id": clientID,
|
||
"username": client.Username,
|
||
"nom": client.Nom,
|
||
"prenom": client.Prenom,
|
||
"telephone": client.Telephone,
|
||
"total_commands": totalCommands,
|
||
"pending_commands": pendingCommands,
|
||
"completed_commands": completedCommands,
|
||
"points": client.Point,
|
||
"points_zipette": client.PointZipette,
|
||
"amende": client.Amende,
|
||
"member_since": client.CreatedAt,
|
||
}
|
||
|
||
return stats, nil
|
||
}
|
||
|
||
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)
|
||
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
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
if currentAmount <= 0 {
|
||
return fmt.Errorf("aucune pénalité à payer")
|
||
}
|
||
|
||
if amountPaid < currentAmount {
|
||
return fmt.Errorf("montant insuffisant: %.2f payé, %.2f requis", amountPaid, currentAmount)
|
||
}
|
||
|
||
// Réinitialiser les pénalités
|
||
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)
|
||
}
|
||
|
||
rowsAffected, err := result.RowsAffected()
|
||
if err != nil {
|
||
return fmt.Errorf("erreur vérification: %w", err)
|
||
}
|
||
if rowsAffected == 0 {
|
||
return fmt.Errorf("client non trouvé")
|
||
}
|
||
|
||
log.Printf("✅ [PayClientPenalties] Pénalités réglées pour %s", username)
|
||
|
||
// Invalider le cache Redis du client
|
||
cacheKey := fmt.Sprintf("client:%s", username)
|
||
Redis.Del(RedisCtx, cacheKey)
|
||
|
||
return nil
|
||
}
|
||
|
||
// 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)
|
||
}
|
||
|
||
rowsAffected, err := result.RowsAffected()
|
||
if err != nil {
|
||
return fmt.Errorf("erreur lors de la vérification: %w", err)
|
||
}
|
||
if rowsAffected == 0 {
|
||
return fmt.Errorf("client non trouvé")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// ✅ NOUVELLE FONCTION: Ajouter des points selon la catégorie
|
||
func (d *Database) AddClientPointsByCategory(username string, points int, category string) error {
|
||
var query string
|
||
|
||
if category == "zipette&co" {
|
||
query = `UPDATE clients
|
||
SET point_zipette = point_zipette + $1
|
||
WHERE username = $2`
|
||
log.Printf("🎁 [ADD_POINTS] Ajout de %d points ZIPETTE à %s", points, username)
|
||
} else {
|
||
query = `UPDATE clients
|
||
SET point = point + $1
|
||
WHERE username = $2`
|
||
log.Printf("🎁 [ADD_POINTS] Ajout de %d points WEED/HASH à %s", points, username)
|
||
}
|
||
|
||
result, err := d.Exec(query, points, username)
|
||
if err != nil {
|
||
return fmt.Errorf("erreur lors de l'ajout de points: %w", err)
|
||
}
|
||
|
||
rowsAffected, err := result.RowsAffected()
|
||
if err != nil {
|
||
return fmt.Errorf("erreur lors de la vérification: %w", err)
|
||
}
|
||
if rowsAffected == 0 {
|
||
return fmt.Errorf("client non trouvé")
|
||
}
|
||
|
||
log.Printf("✅ %d points (%s) ajoutés au client %s (EN DB)", points, category, username)
|
||
return nil
|
||
}
|
||
|
||
// ✅ ANCIENNE FONCTION CONSERVÉE POUR COMPATIBILITÉ (utilise weed/hash par défaut)
|
||
func (d *Database) AddClientPoints(username string, points int) error {
|
||
return d.AddClientPointsByCategory(username, points, "weed_hash")
|
||
}
|
||
|
||
func (d *Database) GetClientByTelephone(telephone string) (*models.Client, error) {
|
||
client := &models.Client{}
|
||
query := `SELECT id, username, password, nom, prenom, telephone, command, point, point_zipette, 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.Point,
|
||
&client.PointZipette,
|
||
&client.Amende,
|
||
&client.CreatedAt,
|
||
)
|
||
|
||
if err == sql.ErrNoRows {
|
||
return nil, nil
|
||
}
|
||
if err != nil {
|
||
return nil, fmt.Errorf("erreur lors de la récupération du client: %w", err)
|
||
}
|
||
|
||
return client, nil
|
||
}
|
||
|
||
// GetClientByUsername récupère un client par son username
|
||
func (d *Database) GetClientByUsername(username string) (*models.Client, error) {
|
||
client := &models.Client{}
|
||
query := `SELECT id, username, password, nom, prenom, telephone, command, point, point_zipette, amende, 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.Point,
|
||
&client.PointZipette,
|
||
&client.Amende,
|
||
&client.CreatedAt,
|
||
)
|
||
|
||
if err == sql.ErrNoRows {
|
||
return nil, nil
|
||
}
|
||
if err != nil {
|
||
return nil, fmt.Errorf("erreur lors de la récupération du client: %w", err)
|
||
}
|
||
|
||
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)
|
||
cancellationHistory = map[string]interface{}{
|
||
"cancellations_count": cancellationsCount,
|
||
"next_penalty": 20,
|
||
}
|
||
}
|
||
|
||
info := map[string]interface{}{
|
||
"username": username,
|
||
"total_penalty": amende,
|
||
"cancellations_count": cancellationsCount,
|
||
"cancellation_history": cancellationHistory,
|
||
"has_penalties": amende > 0,
|
||
}
|
||
|
||
return info, nil
|
||
}
|
||
|
||
// CheckClientCanOrder vérifie si un client peut passer commande (pas de pénalités impayées)
|
||
func (d *Database) CheckClientCanOrder(username string) (bool, float64, error) {
|
||
amende, err := d.GetClientAmende(username)
|
||
if err != nil {
|
||
return false, 0, err
|
||
}
|
||
|
||
if amende > 0 {
|
||
log.Printf("⚠️ [CheckClientCanOrder] Client %s bloqué: %.2f points de pénalités", username, amende)
|
||
return false, amende, fmt.Errorf("pénalités impayées: %.2f points", amende)
|
||
}
|
||
|
||
return true, 0, nil
|
||
}
|
||
|
||
func (d *Database) ResetClientPoint(username string, resetCancellationsPoint bool) error {
|
||
var query string
|
||
if resetCancellationsPoint {
|
||
query = `UPDATE clients SET point = 0, point_zipette = 0, updated_at = CURRENT_TIMESTAMP WHERE username = $1`
|
||
} else {
|
||
query = `UPDATE clients SET point = 0, 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)
|
||
}
|
||
|
||
rowsAffected, err := result.RowsAffected()
|
||
if err != nil {
|
||
return fmt.Errorf("erreur vérification: %w", err)
|
||
}
|
||
if rowsAffected == 0 {
|
||
return fmt.Errorf("client non trouvé")
|
||
}
|
||
|
||
// Invalider le cache Redis du client
|
||
cacheKey := fmt.Sprintf("client:%s", username)
|
||
Redis.Del(RedisCtx, cacheKey)
|
||
|
||
return nil
|
||
}
|
||
|
||
func (d *Database) ResetClientPenalties(username string, resetCancellationsCount bool) error {
|
||
log.Printf("🔄 [ResetClientPenalties] Reset pour %s (reset_count=%v)", username, resetCancellationsCount)
|
||
|
||
var query string
|
||
if resetCancellationsCount {
|
||
query = `UPDATE clients
|
||
SET amende = 0,
|
||
cancellations_count = 0,
|
||
updated_at = CURRENT_TIMESTAMP
|
||
WHERE username = $1`
|
||
} else {
|
||
query = `UPDATE clients
|
||
SET amende = 0,
|
||
updated_at = CURRENT_TIMESTAMP
|
||
WHERE username = $1`
|
||
}
|
||
|
||
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)
|
||
}
|
||
|
||
rowsAffected, err := result.RowsAffected()
|
||
if err != nil {
|
||
return fmt.Errorf("erreur vérification: %w", err)
|
||
}
|
||
if rowsAffected == 0 {
|
||
return fmt.Errorf("client non trouvé")
|
||
}
|
||
|
||
// Invalider le cache Redis du client
|
||
cacheKey := fmt.Sprintf("client:%s", username)
|
||
Redis.Del(RedisCtx, cacheKey)
|
||
|
||
return nil
|
||
}
|
||
|
||
func (d *Database) GetAllClientsWithPenalties() ([]map[string]interface{}, error) {
|
||
query := `
|
||
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)
|
||
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 = append(clients, map[string]interface{}{
|
||
"username": username,
|
||
"total_penalty": amende,
|
||
"cancellations_count": cancellationsCount,
|
||
"last_updated": updatedAt,
|
||
})
|
||
}
|
||
|
||
log.Printf("📊 [GetAllClientsWithPenalties] %d clients avec pénalités", len(clients))
|
||
|
||
return clients, nil
|
||
}
|
||
|
||
func (d *Database) GetClientPenaltiesStats() (map[string]interface{}, error) {
|
||
query := `
|
||
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,
|
||
)
|
||
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,
|
||
}
|
||
|
||
log.Printf("📊 [GetClientPenaltiesStats] Stats: %d/%d clients avec pénalités",
|
||
stats.ClientsWithPenalties, stats.TotalClients)
|
||
|
||
return result, nil
|
||
}
|
||
|
||
// ✅ FONCTION MODIFIÉE: Calculer les points sans cumuler entre catégories
|
||
// À remplacer dans db/clients.go à partir de la ligne 498
|
||
|
||
// À remplacer dans db/clients.go
|
||
|
||
func (d *Database) CalculatePointsForCommand(commandID int) (int, string, error) {
|
||
query := `
|
||
SELECT p.category, ci.prix, ci.quantite
|
||
FROM command_items ci
|
||
JOIN products p ON ci.product_id = p.id
|
||
WHERE ci.command_id = $1
|
||
`
|
||
|
||
rows, err := d.Query(query, commandID)
|
||
if err != nil {
|
||
return 0, "", fmt.Errorf("erreur récupération items: %w", err)
|
||
}
|
||
defer rows.Close()
|
||
|
||
zipetteTotal := 0.0
|
||
weedTotal := 0.0
|
||
grosSemiTotal := 0.0
|
||
|
||
for rows.Next() {
|
||
var category string
|
||
var prix float64
|
||
var quantite int
|
||
|
||
if err := rows.Scan(&category, &prix, &quantite); err != nil {
|
||
log.Printf("⚠️ Erreur scan item: %v", err)
|
||
continue
|
||
}
|
||
|
||
itemTotal := prix // Prix déjà calculé pour la quantité
|
||
categoryLower := strings.ToLower(category)
|
||
|
||
if categoryLower == "zipette&co" || categoryLower == "zipette_co" {
|
||
zipetteTotal += itemTotal
|
||
} else if categoryLower == "gros&semi" || categoryLower == "gros_semi" {
|
||
grosSemiTotal += itemTotal
|
||
} else {
|
||
weedTotal += itemTotal
|
||
}
|
||
}
|
||
|
||
log.Printf("💰 [CALC_POINTS] Cmd %d - Zipette: %.2f€, Weed: %.2f€, GrosSemi: %.2f€",
|
||
commandID, zipetteTotal, weedTotal, grosSemiTotal)
|
||
|
||
// ✅ CALCUL POINTS WEED&HASH
|
||
weedPoints := 0
|
||
if weedTotal > 0 {
|
||
switch {
|
||
case weedTotal >= 30 && weedTotal <= 50:
|
||
weedPoints = 1
|
||
case weedTotal >= 60 && weedTotal <= 150:
|
||
weedPoints = 2
|
||
case weedTotal >= 160 && weedTotal <= 300:
|
||
weedPoints = 3
|
||
case weedTotal >= 310 && weedTotal <= 400:
|
||
weedPoints = 5
|
||
case weedTotal >= 400:
|
||
weedPoints = 10
|
||
}
|
||
if weedPoints > 0 {
|
||
log.Printf("🎁 [CALC_POINTS] Weed: %.2f€ → %d points", weedTotal, weedPoints)
|
||
}
|
||
}
|
||
|
||
// ✅ CALCUL POINTS ZIPETTE&CO
|
||
zipettePoints := 0
|
||
if zipetteTotal > 0 {
|
||
switch {
|
||
case zipetteTotal >= 30 && zipetteTotal <= 100:
|
||
zipettePoints = 1
|
||
case zipetteTotal >= 110 && zipetteTotal <= 200:
|
||
zipettePoints = 2
|
||
case zipetteTotal >= 210:
|
||
zipettePoints = 3
|
||
}
|
||
if zipettePoints > 0 {
|
||
log.Printf("🎁 [CALC_POINTS] Zipette: %.2f€ → %d points", zipetteTotal, zipettePoints)
|
||
}
|
||
}
|
||
|
||
totalPoints := zipettePoints + weedPoints
|
||
|
||
if totalPoints == 0 {
|
||
if grosSemiTotal > 0 && zipetteTotal == 0 && weedTotal == 0 {
|
||
log.Printf("ℹ️ [CALC_POINTS] Cmd %d - Catégorie GROS&SEMI uniquement (%.2f€) → 0 points",
|
||
commandID, grosSemiTotal)
|
||
return 0, "gros&semi", nil
|
||
}
|
||
log.Printf("ℹ️ [CALC_POINTS] Cmd %d - Aucun montant éligible aux points", commandID)
|
||
return 0, "unknown", nil
|
||
}
|
||
|
||
var dominantCategory string
|
||
if zipetteTotal >= weedTotal {
|
||
dominantCategory = "zipette&co"
|
||
} else {
|
||
dominantCategory = "weed&hash"
|
||
}
|
||
|
||
log.Printf("✅ [CALC_POINTS] Cmd %d - Total: %d points (Zipette: %d, Weed: %d)",
|
||
commandID, totalPoints, zipettePoints, weedPoints)
|
||
|
||
return totalPoints, dominantCategory, nil
|
||
}
|
||
|
||
// ✅ FONCTION CORRIGÉE: Calculer et ajouter les points séparément avec le BON BARÈME
|
||
func (d *Database) CalculateAndAddPointsForCommand(commandID int, username string) (int, error) {
|
||
query := `
|
||
SELECT p.category, ci.prix, ci.quantite
|
||
FROM command_items ci
|
||
JOIN products p ON ci.product_id = p.id
|
||
WHERE ci.command_id = $1
|
||
`
|
||
|
||
rows, err := d.Query(query, commandID)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("erreur récupération items: %w", err)
|
||
}
|
||
defer rows.Close()
|
||
|
||
zipetteTotal := 0.0
|
||
weedTotal := 0.0
|
||
grosSemiTotal := 0.0
|
||
|
||
for rows.Next() {
|
||
var category string
|
||
var prix float64
|
||
var quantite int
|
||
|
||
if err := rows.Scan(&category, &prix, &quantite); err != nil {
|
||
log.Printf("⚠️ Erreur scan item: %v", err)
|
||
continue
|
||
}
|
||
|
||
// ✅ prix contient déjà le total pour la quantité
|
||
itemTotal := prix
|
||
categoryLower := strings.ToLower(category)
|
||
|
||
if categoryLower == "zipette&co" || categoryLower == "zipette_co" {
|
||
zipetteTotal += itemTotal
|
||
} else if categoryLower == "gros&semi" || categoryLower == "gros_semi" {
|
||
grosSemiTotal += itemTotal
|
||
} else {
|
||
weedTotal += itemTotal
|
||
}
|
||
}
|
||
|
||
log.Printf("💰 [CALC_POINTS] Cmd %d - Zipette: %.2f€, Weed: %.2f€, GrosSemi: %.2f€",
|
||
commandID, zipetteTotal, weedTotal, grosSemiTotal)
|
||
|
||
// ✅ CALCUL POINTS WEED&HASH - NOUVEAU BARÈME
|
||
// De 30 à 50€ -> 1 point
|
||
// De 60 à 150€ -> 2 points
|
||
// De 160 à 300€ -> 3 points
|
||
// De 310 à 400€ -> 5 points
|
||
// 400€ et + -> 10 points
|
||
weedPoints := 0
|
||
if weedTotal > 0 {
|
||
switch {
|
||
case weedTotal >= 30 && weedTotal <= 50:
|
||
weedPoints = 1
|
||
case weedTotal >= 60 && weedTotal <= 150:
|
||
weedPoints = 2
|
||
case weedTotal >= 160 && weedTotal <= 300:
|
||
weedPoints = 3
|
||
case weedTotal >= 310 && weedTotal <= 400:
|
||
weedPoints = 5
|
||
case weedTotal >= 400:
|
||
weedPoints = 10
|
||
}
|
||
if weedPoints > 0 {
|
||
log.Printf("🎁 [CALC_POINTS] Weed: %.2f€ → %d points", weedTotal, weedPoints)
|
||
}
|
||
}
|
||
|
||
// ✅ CALCUL POINTS ZIPETTE&CO - NOUVEAU BARÈME
|
||
// De 30 à 100€ -> 1 point
|
||
// De 110 à 200€ -> 2 points (je suppose que c'est 110 et non 1100)
|
||
// De 210€ et + -> 3 points
|
||
zipettePoints := 0
|
||
if zipetteTotal > 0 {
|
||
switch {
|
||
case zipetteTotal >= 30 && zipetteTotal <= 100:
|
||
zipettePoints = 1
|
||
case zipetteTotal >= 110 && zipetteTotal <= 200:
|
||
zipettePoints = 2
|
||
case zipetteTotal >= 210:
|
||
zipettePoints = 3
|
||
}
|
||
if zipettePoints > 0 {
|
||
log.Printf("🎁 [CALC_POINTS] Zipette: %.2f€ → %d points", zipetteTotal, zipettePoints)
|
||
}
|
||
}
|
||
|
||
totalPoints := 0
|
||
|
||
// ✅ AJOUTER LES POINTS SÉPARÉMENT PAR CATÉGORIE
|
||
if zipettePoints > 0 {
|
||
if err := d.AddClientPointsByCategory(username, zipettePoints, "zipette&co"); err != nil {
|
||
log.Printf("❌ Erreur ajout points Zipette: %v", err)
|
||
} else {
|
||
totalPoints += zipettePoints
|
||
log.Printf("✅ %d points ZIPETTE ajoutés à %s", zipettePoints, username)
|
||
}
|
||
}
|
||
|
||
if weedPoints > 0 {
|
||
if err := d.AddClientPointsByCategory(username, weedPoints, "weed&hash"); err != nil {
|
||
log.Printf("❌ Erreur ajout points Weed: %v", err)
|
||
} else {
|
||
totalPoints += weedPoints
|
||
log.Printf("✅ %d points WEED ajoutés à %s", weedPoints, username)
|
||
}
|
||
}
|
||
|
||
if totalPoints == 0 {
|
||
if grosSemiTotal > 0 && zipetteTotal == 0 && weedTotal == 0 {
|
||
log.Printf("ℹ️ [CALC_POINTS] Cmd %d - Catégorie GROS&SEMI uniquement → 0 points", commandID)
|
||
} else {
|
||
log.Printf("ℹ️ [CALC_POINTS] Cmd %d - Aucun point éligible (Weed: %.2f€, Zipette: %.2f€)",
|
||
commandID, weedTotal, zipetteTotal)
|
||
}
|
||
}
|
||
|
||
log.Printf("✅ [CALC_POINTS] Cmd %d - Total: %d points (Zipette: %d, Weed: %d)",
|
||
commandID, totalPoints, zipettePoints, weedPoints)
|
||
|
||
return totalPoints, nil
|
||
}
|
||
|
||
func (d *Database) CalculateAndAddPointsForCommandTx(tx *sql.Tx, commandID int, username string) (int, error) {
|
||
log.Printf("💰 [CalcPointsTx] START - cmd=%d, user=%s", commandID, username)
|
||
|
||
// ✅ ÉTAPE 1: Récupérer tous les items de la commande avec leurs catégories
|
||
query := `
|
||
SELECT ci.quantite, ci.prix, COALESCE(p.category, 'weed_hash') as category
|
||
FROM command_items ci
|
||
LEFT JOIN products p ON ci.product_id = p.id
|
||
WHERE ci.command_id = $1
|
||
`
|
||
|
||
rows, err := tx.Query(query, commandID)
|
||
if err != nil {
|
||
log.Printf("❌ [CalcPointsTx] Erreur query items: %v", err)
|
||
return 0, fmt.Errorf("erreur récupération items: %w", err)
|
||
}
|
||
defer rows.Close()
|
||
|
||
type ItemPoints struct {
|
||
Category string
|
||
Quantite int
|
||
Prix float64
|
||
}
|
||
|
||
var items []ItemPoints
|
||
totalPrixWeedHash := 0.0
|
||
totalPrixZipette := 0.0
|
||
|
||
for rows.Next() {
|
||
var item ItemPoints
|
||
if err := rows.Scan(&item.Quantite, &item.Prix, &item.Category); err != nil {
|
||
log.Printf("❌ [CalcPointsTx] Erreur scan: %v", err)
|
||
return 0, fmt.Errorf("erreur lecture item: %w", err)
|
||
}
|
||
|
||
items = append(items, item)
|
||
|
||
// Cumuler par catégorie
|
||
if item.Category == "zipette" {
|
||
totalPrixZipette += item.Prix
|
||
} else {
|
||
// weed_hash ou autres catégories
|
||
totalPrixWeedHash += item.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 len(items) == 0 {
|
||
log.Printf("⚠️ [CalcPointsTx] Aucun item trouvé pour cmd %d", commandID)
|
||
return 0, nil
|
||
}
|
||
|
||
log.Printf("📊 [CalcPointsTx] %d items - weed_hash: %.2f€, zipette: %.2f€",
|
||
len(items), totalPrixWeedHash, totalPrixZipette)
|
||
|
||
// ✅ ÉTAPE 2: Calculer les points par catégorie
|
||
// Règle: 1 point par tranche de 10€
|
||
pointsWeedHash := int(totalPrixWeedHash / 10.0)
|
||
pointsZipette := int(totalPrixZipette / 10.0)
|
||
totalPoints := pointsWeedHash + pointsZipette
|
||
|
||
log.Printf("💰 [CalcPointsTx] Points calculés - weed_hash: %d, zipette: %d, total: %d",
|
||
pointsWeedHash, pointsZipette, totalPoints)
|
||
|
||
// ✅ ÉTAPE 3: Mettre à jour les points du client (dans la transaction)
|
||
if pointsWeedHash > 0 || pointsZipette > 0 {
|
||
updateQuery := `
|
||
UPDATE clients
|
||
SET
|
||
point = point + $1,
|
||
point_zipette = point_zipette + $2,
|
||
updated_at = CURRENT_TIMESTAMP
|
||
WHERE username = $3
|
||
`
|
||
|
||
result, err := tx.Exec(updateQuery, pointsWeedHash, pointsZipette, username)
|
||
if err != nil {
|
||
log.Printf("❌ [CalcPointsTx] Erreur UPDATE points: %v", err)
|
||
return 0, fmt.Errorf("erreur mise à jour points: %w", err)
|
||
}
|
||
|
||
rows, _ := result.RowsAffected()
|
||
if rows == 0 {
|
||
log.Printf("⚠️ [CalcPointsTx] Client %s non trouvé", username)
|
||
return 0, fmt.Errorf("client non trouvé")
|
||
}
|
||
|
||
log.Printf("✅ [CalcPointsTx] Points ajoutés: +%d weed_hash, +%d zipette pour %s",
|
||
pointsWeedHash, pointsZipette, username)
|
||
}
|
||
|
||
log.Printf("🎉 [CalcPointsTx] SUCCÈS - Total %d points attribués", totalPoints)
|
||
|
||
return totalPoints, nil
|
||
}
|
||
|
||
func (d *Database) CanUserAccessCommand(
|
||
commandID int,
|
||
username string,
|
||
role string,
|
||
) (bool, error) {
|
||
|
||
// 👑 Admin : accès total
|
||
if role == "admin" {
|
||
return true, nil
|
||
}
|
||
|
||
var exists bool
|
||
|
||
// 🚚 Livreur : seulement commandes assignées
|
||
if role == "livreur" {
|
||
err := d.QueryRow(`
|
||
SELECT EXISTS(
|
||
SELECT 1 FROM commandes
|
||
WHERE id = $1 AND livreur_assign = $2
|
||
)
|
||
`, commandID, username).Scan(&exists)
|
||
return exists, err
|
||
}
|
||
|
||
// 👤 User : seulement SES commandes
|
||
err := d.QueryRow(`
|
||
SELECT EXISTS(
|
||
SELECT 1 FROM commandes
|
||
WHERE id = $1 AND username = $2
|
||
)
|
||
`, commandID, username).Scan(&exists)
|
||
|
||
return exists, err
|
||
}
|