chore: update
This commit is contained in:
@@ -432,8 +432,8 @@ func (d *Database) GetBasketItems(username string) ([]map[string]interface{}, er
|
||||
|
||||
var items []map[string]interface{}
|
||||
for rows.Next() {
|
||||
var productID, quantity int
|
||||
var price float64
|
||||
var productID int
|
||||
var quantity, price float64
|
||||
if err := rows.Scan(&productID, &quantity, &price); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -876,7 +876,7 @@ func (d *Database) CalculateAndAddPointsForCommandTx(tx *sql.Tx, commandID int,
|
||||
|
||||
type ItemPoints struct {
|
||||
Category string
|
||||
Quantite int
|
||||
Quantite float64
|
||||
Prix float64
|
||||
}
|
||||
|
||||
@@ -894,8 +894,11 @@ func (d *Database) CalculateAndAddPointsForCommandTx(tx *sql.Tx, commandID int,
|
||||
items = append(items, item)
|
||||
|
||||
// Cumuler par catégorie
|
||||
if item.Category == "zipette" {
|
||||
categoryLower := strings.ToLower(item.Category)
|
||||
if categoryLower == "zipette&co" || categoryLower == "zipette_co" {
|
||||
totalPrixZipette += item.Prix
|
||||
} else if categoryLower == "gros&semi" || categoryLower == "gros_semi" {
|
||||
// gros&semi → 0 points, on ne cumule pas
|
||||
} else {
|
||||
// weed_hash ou autres catégories
|
||||
totalPrixWeedHash += item.Prix
|
||||
@@ -915,10 +918,31 @@ func (d *Database) CalculateAndAddPointsForCommandTx(tx *sql.Tx, commandID int,
|
||||
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)
|
||||
// ✅ ÉTAPE 2: Calculer les points par catégorie avec le bon barème
|
||||
pointsWeedHash := 0
|
||||
switch {
|
||||
case totalPrixWeedHash >= 30 && totalPrixWeedHash <= 50:
|
||||
pointsWeedHash = 1
|
||||
case totalPrixWeedHash >= 60 && totalPrixWeedHash <= 150:
|
||||
pointsWeedHash = 2
|
||||
case totalPrixWeedHash >= 160 && totalPrixWeedHash <= 300:
|
||||
pointsWeedHash = 3
|
||||
case totalPrixWeedHash >= 310 && totalPrixWeedHash <= 400:
|
||||
pointsWeedHash = 5
|
||||
case totalPrixWeedHash >= 400:
|
||||
pointsWeedHash = 10
|
||||
}
|
||||
|
||||
pointsZipette := 0
|
||||
switch {
|
||||
case totalPrixZipette >= 30 && totalPrixZipette <= 100:
|
||||
pointsZipette = 1
|
||||
case totalPrixZipette >= 110 && totalPrixZipette <= 200:
|
||||
pointsZipette = 2
|
||||
case totalPrixZipette >= 210:
|
||||
pointsZipette = 3
|
||||
}
|
||||
|
||||
totalPoints := pointsWeedHash + pointsZipette
|
||||
|
||||
log.Printf("💰 [CalcPointsTx] Points calculés - weed_hash: %d, zipette: %d, total: %d",
|
||||
|
||||
@@ -31,7 +31,7 @@ func validateItemID(itemID int) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func validateQuantite(quantite int) error {
|
||||
func validateQuantite(quantite float64) error {
|
||||
if quantite <= 0 {
|
||||
return fmt.Errorf("quantité doit être > 0")
|
||||
}
|
||||
@@ -96,7 +96,8 @@ func validateItemStatus(status string) error {
|
||||
func (d *Database) InsertCommandItemWithClientInfo(
|
||||
commandID int,
|
||||
produit string,
|
||||
productID, quantite int,
|
||||
productID int,
|
||||
quantite float64,
|
||||
prix float64,
|
||||
clientUsername, clientNom, clientPrenom, clientTelephone, deliveryAddress string,
|
||||
) error {
|
||||
@@ -231,7 +232,8 @@ func (d *Database) GetCommandItems(commandID int) ([]map[string]interface{}, err
|
||||
var items []map[string]interface{}
|
||||
|
||||
for rows.Next() {
|
||||
var id, commandID, quantite int
|
||||
var id, commandID int
|
||||
var quantite float64
|
||||
var productID sql.NullInt64 // ✅ FIX: Utiliser NullInt64 pour gérer NULL
|
||||
var produit, clientUsername, clientNom, clientPrenom, clientTelephone string
|
||||
var deliveryAddress, status sql.NullString
|
||||
@@ -345,7 +347,8 @@ func (d *Database) GetCommandItemsByUsername(username string) ([]map[string]inte
|
||||
var items []map[string]interface{}
|
||||
|
||||
for rows.Next() {
|
||||
var id, commandID, quantite int
|
||||
var id, commandID int
|
||||
var quantite float64
|
||||
var productID sql.NullInt64 // ✅ FIX: NullInt64
|
||||
var produit, clientUsername, clientNom, clientPrenom, clientTelephone string
|
||||
var deliveryAddress, status sql.NullString
|
||||
|
||||
@@ -82,7 +82,7 @@ func (d *Database) CreateCommand(username string) (*models.Command, error) {
|
||||
|
||||
type BasketItem struct {
|
||||
ProductID int
|
||||
Quantity int
|
||||
Quantity float64
|
||||
Price float64
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ func (d *Database) CreateCommandWithAddress(username, deliveryAddress string) (*
|
||||
|
||||
type BasketItem struct {
|
||||
ProductID int
|
||||
Quantity int
|
||||
Quantity float64
|
||||
Price float64
|
||||
}
|
||||
|
||||
|
||||
@@ -76,11 +76,55 @@ func InitDB() *Database {
|
||||
log.Fatalf("❌ Erreur migration must_change_password: %v", err)
|
||||
}
|
||||
|
||||
// Migration: ajouter colonne push_token pour les notifications push
|
||||
// Migration: ajouter colonne push_token pour les notifications push (clients)
|
||||
if _, err = database.Exec(`ALTER TABLE clients ADD COLUMN IF NOT EXISTS push_token TEXT`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration push_token: %v", err)
|
||||
}
|
||||
|
||||
// Migration: ajouter colonne push_token pour les notifications push (livreurs/users)
|
||||
if _, err = database.Exec(`ALTER TABLE users ADD COLUMN IF NOT EXISTS push_token TEXT`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration push_token users: %v", err)
|
||||
}
|
||||
|
||||
// Migration: ajouter colonne unit pour l'unité de mesure des produits (kg, g, bag, l, cl, pcs, u)
|
||||
if _, err = database.Exec(`ALTER TABLE products ADD COLUMN IF NOT EXISTS unit VARCHAR(10) NOT NULL DEFAULT 'u'`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration unit products: %v", err)
|
||||
}
|
||||
|
||||
// Migration: baskets.quantity INTEGER → NUMERIC(10,3) pour supporter les quantités fractionnaires (ex: 0.5g)
|
||||
if _, err = database.Exec(`
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'baskets' AND column_name = 'quantity'
|
||||
AND data_type = 'integer'
|
||||
) THEN
|
||||
ALTER TABLE baskets ALTER COLUMN quantity TYPE NUMERIC(10,3) USING quantity::NUMERIC(10,3);
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration baskets.quantity: %v", err)
|
||||
}
|
||||
|
||||
// Migration: command_items.quantite INTEGER → NUMERIC(10,3) pour supporter les quantités fractionnaires
|
||||
if _, err = database.Exec(`
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'command_items' AND column_name = 'quantite'
|
||||
AND data_type = 'integer'
|
||||
) THEN
|
||||
ALTER TABLE command_items ALTER COLUMN quantite TYPE NUMERIC(10,3) USING quantite::NUMERIC(10,3);
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration command_items.quantite: %v", err)
|
||||
}
|
||||
|
||||
// Lancer le nettoyage périodique des tokens expirés
|
||||
go database.cleanExpiredTokensPeriodically()
|
||||
|
||||
@@ -151,6 +195,7 @@ func (db *Database) createTables() error {
|
||||
name VARCHAR(255) NOT NULL,
|
||||
category VARCHAR(100) NOT NULL,
|
||||
stock DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||
unit VARCHAR(10) NOT NULL DEFAULT 'u',
|
||||
description TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
|
||||
@@ -2,6 +2,7 @@ package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -36,11 +37,15 @@ func (d *Database) NotifyClient(username string, commandID int, notifType, messa
|
||||
}
|
||||
|
||||
func sendExpoPush(token, title, body string, commandID int, notifType string) {
|
||||
sendExpoPushWithChannel(token, title, body, commandID, notifType, "orders")
|
||||
}
|
||||
|
||||
func sendExpoPushWithChannel(token, title, body string, commandID int, notifType, channelID string) {
|
||||
payload := map[string]interface{}{
|
||||
"to": token,
|
||||
"title": title,
|
||||
"body": body,
|
||||
"channelId": "orders",
|
||||
"channelId": channelID,
|
||||
"data": map[string]interface{}{
|
||||
"command_id": commandID,
|
||||
"type": notifType,
|
||||
@@ -71,7 +76,59 @@ func sendExpoPush(token, title, body string, commandID int, notifType string) {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
log.Printf("✅ [EXPO_PUSH] Push envoyé à %s (status: %d)", token, resp.StatusCode)
|
||||
log.Printf("✅ [EXPO_PUSH] Push envoyé à %s (channel: %s, status: %d)", token, channelID, resp.StatusCode)
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// PUSH TOKEN - LIVREURS (table users)
|
||||
// ============================================
|
||||
|
||||
// SaveUserPushToken sauvegarde le token push d'un livreur dans la table users
|
||||
func (d *Database) SaveUserPushToken(username, token string) error {
|
||||
_, err := d.Exec(`UPDATE users SET push_token = $1 WHERE username = $2`, token, username)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetUserPushToken récupère le token push d'un livreur depuis la table users
|
||||
func (d *Database) GetUserPushToken(username string) (string, error) {
|
||||
var token sql.NullString
|
||||
err := d.QueryRow(`SELECT push_token FROM users WHERE username = $1`, username).Scan(&token)
|
||||
if err != nil || !token.Valid {
|
||||
return "", err
|
||||
}
|
||||
return token.String, nil
|
||||
}
|
||||
|
||||
// DeleteUserPushToken supprime le token push d'un livreur
|
||||
func (d *Database) DeleteUserPushToken(username string) error {
|
||||
_, err := d.Exec(`UPDATE users SET push_token = NULL WHERE username = $1`, username)
|
||||
return err
|
||||
}
|
||||
|
||||
// NotifyLivreur envoie une notification in-app (Redis) + push Expo à un livreur
|
||||
func (d *Database) NotifyLivreur(username string, commandID int, notifType, message string) error {
|
||||
notifKey := fmt.Sprintf("notifications:%s", username)
|
||||
|
||||
notification := map[string]interface{}{
|
||||
"command_id": commandID,
|
||||
"type": notifType,
|
||||
"message": message,
|
||||
"created_at": time.Now().Format(time.RFC3339),
|
||||
"read": false,
|
||||
}
|
||||
|
||||
notifJSON, _ := json.Marshal(notification)
|
||||
Redis.LPush(RedisCtx, notifKey, notifJSON)
|
||||
Redis.Expire(RedisCtx, notifKey, 7*24*time.Hour)
|
||||
|
||||
// Push notification si le livreur a un token enregistré
|
||||
pushToken, err := d.GetUserPushToken(username)
|
||||
if err == nil && pushToken != "" {
|
||||
go sendExpoPushWithChannel(pushToken, "Nouvelle commande assignée", message, commandID, notifType, "deliveries")
|
||||
}
|
||||
|
||||
log.Printf("📬 [LIVREUR_NOTIF] Notification envoyée à %s: %s", username, message)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddDeliveryRating ajoute une note pour un livreur
|
||||
|
||||
@@ -17,7 +17,8 @@ func (db *Database) CreateProduct(product interface{}) error {
|
||||
GetName() string
|
||||
GetCategory() string
|
||||
GetDescription() string
|
||||
GetStock() float64 // ← ajouter méthode pour le stock
|
||||
GetStock() float64
|
||||
GetUnit() string
|
||||
GetPrices() []models.ProductPrice
|
||||
SetID(int)
|
||||
SetCreatedAt(time.Time)
|
||||
@@ -45,15 +46,15 @@ func (db *Database) CreateProduct(product interface{}) error {
|
||||
log.Printf("📦 [DB CreateProduct] Nombre de prix: %d", len(p.GetPrices()))
|
||||
}
|
||||
|
||||
// Insérer le produit avec le stock
|
||||
query := `INSERT INTO products (name, category, description, stock, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, created_at, updated_at`
|
||||
// Insérer le produit avec le stock et l'unité
|
||||
query := `INSERT INTO products (name, category, description, stock, unit, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, created_at, updated_at`
|
||||
|
||||
now := time.Now()
|
||||
var productID int
|
||||
var createdAt, updatedAt time.Time
|
||||
|
||||
err := db.QueryRow(query, p.GetName(), p.GetCategory(), p.GetDescription(), p.GetStock(), now, now).
|
||||
err := db.QueryRow(query, p.GetName(), p.GetCategory(), p.GetDescription(), p.GetStock(), p.GetUnit(), now, now).
|
||||
Scan(&productID, &createdAt, &updatedAt)
|
||||
if err != nil {
|
||||
log.Printf("❌ [DB CreateProduct] Erreur INSERT: %v", err)
|
||||
@@ -93,10 +94,10 @@ func (d *Database) GetProductByID(id int) (models.Product, error) {
|
||||
|
||||
// ✅ AJOUTER stock dans le SELECT
|
||||
err := d.QueryRow(`
|
||||
SELECT id, name, category, description, stock, created_at, updated_at
|
||||
SELECT id, name, category, description, stock, unit, created_at, updated_at
|
||||
FROM products
|
||||
WHERE id=$1
|
||||
`, id).Scan(&p.ID, &p.Name, &p.Category, &p.Description, &p.Stock, &p.CreatedAt, &p.UpdatedAt)
|
||||
`, id).Scan(&p.ID, &p.Name, &p.Category, &p.Description, &p.Stock, &p.Unit, &p.CreatedAt, &p.UpdatedAt)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("❌ [GetProductByID] Erreur query: %v", err)
|
||||
@@ -122,7 +123,7 @@ func (d *Database) GetAllProducts() ([]models.Product, error) {
|
||||
log.Println("📦 [GetAllProducts] START")
|
||||
|
||||
rows, err := d.Query(`
|
||||
SELECT id, name, category, description, stock, created_at, updated_at
|
||||
SELECT id, name, category, description, stock, unit, created_at, updated_at
|
||||
FROM products
|
||||
ORDER BY id ASC
|
||||
`)
|
||||
@@ -135,7 +136,7 @@ func (d *Database) GetAllProducts() ([]models.Product, error) {
|
||||
var products []models.Product
|
||||
for rows.Next() {
|
||||
var p models.Product
|
||||
if err := rows.Scan(&p.ID, &p.Name, &p.Category, &p.Description, &p.Stock, &p.CreatedAt, &p.UpdatedAt); err != nil {
|
||||
if err := rows.Scan(&p.ID, &p.Name, &p.Category, &p.Description, &p.Stock, &p.Unit, &p.CreatedAt, &p.UpdatedAt); err != nil {
|
||||
log.Printf("❌ [GetAllProducts] Erreur scan: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
@@ -173,9 +174,8 @@ func (d *Database) GetAllProducts() ([]models.Product, error) {
|
||||
func (db *Database) GetProductsByCategory(category string) ([]models.Product, error) {
|
||||
log.Printf("📦 [GetProductsByCategory] START - Category=%s", category)
|
||||
|
||||
// ✅ AJOUTER stock dans le SELECT
|
||||
rows, err := db.Query(`
|
||||
SELECT id, name, category, description, stock, created_at, updated_at
|
||||
SELECT id, name, category, description, stock, unit, created_at, updated_at
|
||||
FROM products
|
||||
WHERE category = $1
|
||||
ORDER BY created_at DESC
|
||||
@@ -190,8 +190,7 @@ func (db *Database) GetProductsByCategory(category string) ([]models.Product, er
|
||||
var products []models.Product
|
||||
for rows.Next() {
|
||||
var p models.Product
|
||||
// ✅ AJOUTER &p.Stock dans le Scan
|
||||
if err := rows.Scan(&p.ID, &p.Name, &p.Category, &p.Description, &p.Stock, &p.CreatedAt, &p.UpdatedAt); err != nil {
|
||||
if err := rows.Scan(&p.ID, &p.Name, &p.Category, &p.Description, &p.Stock, &p.Unit, &p.CreatedAt, &p.UpdatedAt); err != nil {
|
||||
log.Printf("❌ [GetProductsByCategory] Erreur scan: %v", err)
|
||||
return nil, fmt.Errorf("erreur lors du scan d'un produit: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user