diff --git a/backend/gestion/db/db_command_items.go b/backend/gestion/db/db_command_items.go index b40004dc..bc321059 100644 --- a/backend/gestion/db/db_command_items.go +++ b/backend/gestion/db/db_command_items.go @@ -328,29 +328,43 @@ func (d *Database) DeleteCommandItem(commandID, itemID int) error { var cmdStatus string d.GDB.Raw(`SELECT status FROM commandes WHERE id = ?`, commandID).Scan(&cmdStatus) - // Supprimer l'item - if err := d.GDB.Exec(`DELETE FROM command_items WHERE id = ?`, itemID).Error; err != nil { + noRestoreStatuses := []string{"cancelled", "approved", "livre"} + restoreStock := result.ProductID != 0 && !slices.Contains(noRestoreStatuses, cmdStatus) + + tx := d.GDB.Begin() + if tx.Error != nil { + return fmt.Errorf("erreur démarrage transaction: %w", tx.Error) + } + + if err := tx.Exec(`DELETE FROM command_items WHERE id = ?`, itemID).Error; err != nil { + tx.Rollback() log.Printf("❌ Erreur DELETE command_items: %v", err) return fmt.Errorf("erreur suppression item: %w", err) } - // Recalculer le total de la commande - if err := d.GDB.Exec( + if err := tx.Exec( `UPDATE commandes SET total_prix = GREATEST(0, total_prix - ?) WHERE id = ?`, result.Prix*result.Quantite, commandID, ).Error; err != nil { - log.Printf("⚠️ [DeleteCommandItem] Erreur maj total commande: %v", err) + tx.Rollback() + log.Printf("❌ [DeleteCommandItem] Erreur maj total commande: %v", err) + return fmt.Errorf("erreur mise à jour total commande: %w", err) } - // Restaurer le stock si la commande n'est pas déjà terminée - noRestoreStatuses := []string{"cancelled", "approved", "livre"} - if result.ProductID != 0 && !slices.Contains(noRestoreStatuses, cmdStatus) { - if err := d.GDB.Exec(`UPDATE products SET stock = stock + ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, - result.Quantite, result.ProductID).Error; err != nil { - log.Printf("⚠️ [DeleteCommandItem] Erreur restauration stock: %v", err) - } else { - log.Printf("✅ [DeleteCommandItem] Stock restauré: +%.3f pour produit %d", result.Quantite, result.ProductID) + if restoreStock { + if err := tx.Exec( + `UPDATE products SET stock = stock + ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, + result.Quantite, result.ProductID, + ).Error; err != nil { + tx.Rollback() + log.Printf("❌ [DeleteCommandItem] Erreur restauration stock: %v", err) + return fmt.Errorf("erreur restauration stock: %w", err) } + log.Printf("✅ [DeleteCommandItem] Stock restauré: +%.3f pour produit %d", result.Quantite, result.ProductID) + } + + if err := tx.Commit().Error; err != nil { + return fmt.Errorf("erreur commit transaction: %w", err) } return nil diff --git a/backend/gestion/db/db_commands.go b/backend/gestion/db/db_commands.go index 6952b8ed..f6911757 100644 --- a/backend/gestion/db/db_commands.go +++ b/backend/gestion/db/db_commands.go @@ -903,3 +903,13 @@ func (d *Database) ApproveDeliveryAtomicByStaff(commandID int, staffUsername str return totalPoints, pointCategory, clientUsernameOut, nil } + +func (d *Database) SetCommandCancelReason(commandID int, reason string) error { + if len(reason) > 500 { + reason = reason[:500] + } + return d.GDB.Exec( + `UPDATE commandes SET cancel_reason = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, + reason, commandID, + ).Error +} diff --git a/backend/gestion/handlers/deleviry.go b/backend/gestion/handlers/deleviry.go index 4cf150a4..0e0091d2 100644 --- a/backend/gestion/handlers/deleviry.go +++ b/backend/gestion/handlers/deleviry.go @@ -263,6 +263,14 @@ func UpdateDeliveryStatus(c *gin.Context) { return } + if req.Status == "cancelled" { + cancelMsg := req.Notes + if cancelMsg == "" { + cancelMsg = "Annulé par le livreur" + } + database.SetCommandCancelReason(commandID, fmt.Sprintf("[Livreur: %s] %s", usernameStr, cancelMsg)) + } + // ✅ SI PASSAGE EN "EN_ROUTE" → CALCULER ET DÉFINIR L'ETA var etaMinutes int var etaMessage string diff --git a/frontend-admin/src/screens/admin/OrdersScreen.tsx b/frontend-admin/src/screens/admin/OrdersScreen.tsx index d0b660d1..7ca2b0a3 100644 --- a/frontend-admin/src/screens/admin/OrdersScreen.tsx +++ b/frontend-admin/src/screens/admin/OrdersScreen.tsx @@ -443,6 +443,11 @@ export default function OrdersScreen() { color: colors.textSecondary, fontSize: fontSize.xs, }, + livreurCancelBadge: { + flexDirection: "row", + alignItems: "center", + marginBottom: spacing.xs, + }, proposedAddressBox: { backgroundColor: colors.bgPrimary, borderRadius: borderRadius.sm, @@ -662,8 +667,18 @@ export default function OrdersScreen() { {/* Raison annulation */} {isCancelled && !!item.cancel_reason && ( + {item.cancel_reason.startsWith("[Livreur:") && ( + + + + {" "}Annulé par le livreur + + + )} - Raison : {item.cancel_reason} + {item.cancel_reason.startsWith("[Livreur:") + ? item.cancel_reason.replace(/^\[Livreur: [^\]]+\] /, "") + : item.cancel_reason} )} diff --git a/mobile/app.json b/mobile/app.json index 32300143..7ed673a5 100644 --- a/mobile/app.json +++ b/mobile/app.json @@ -22,6 +22,7 @@ "backgroundColor": "#000000" }, "edgeToEdgeEnabled": true, + "softwareKeyboardLayoutMode": "pan", "predictiveBackGestureEnabled": false, "package": "com.uberstup.clientpanel", "versionCode": 1, diff --git a/mobile/src/components/ui/Modal.tsx b/mobile/src/components/ui/Modal.tsx index f0abc142..a2ee957b 100644 --- a/mobile/src/components/ui/Modal.tsx +++ b/mobile/src/components/ui/Modal.tsx @@ -65,7 +65,7 @@ export default function Modal({ > { return ( - + {/* Résumé commande */} diff --git a/mobile/src/screens/client/ProfileScreen.tsx b/mobile/src/screens/client/ProfileScreen.tsx index 49c84997..f20fb1e4 100644 --- a/mobile/src/screens/client/ProfileScreen.tsx +++ b/mobile/src/screens/client/ProfileScreen.tsx @@ -371,7 +371,7 @@ export default function ProfileScreen() { } return ( - + {/* Header */}