This commit is contained in:
2026-05-15 11:15:30 +02:00
parent 775b9200f5
commit 3eba3e3a15
10 changed files with 71 additions and 20 deletions
+27 -13
View File
@@ -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
+10
View File
@@ -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
}
+8
View File
@@ -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
@@ -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 && (
<View style={styles.cancelReasonBox}>
{item.cancel_reason.startsWith("[Livreur:") && (
<View style={styles.livreurCancelBadge}>
<Ionicons name="bicycle-outline" size={12} color={colors.danger} />
<Text style={[styles.cancelReasonText, { color: colors.danger, fontWeight: "600" }]}>
{" "}Annulé par le livreur
</Text>
</View>
)}
<Text style={styles.cancelReasonText}>
Raison : {item.cancel_reason}
{item.cancel_reason.startsWith("[Livreur:")
? item.cancel_reason.replace(/^\[Livreur: [^\]]+\] /, "")
: item.cancel_reason}
</Text>
</View>
)}
+1
View File
@@ -22,6 +22,7 @@
"backgroundColor": "#000000"
},
"edgeToEdgeEnabled": true,
"softwareKeyboardLayoutMode": "pan",
"predictiveBackGestureEnabled": false,
"package": "com.uberstup.clientpanel",
"versionCode": 1,
+1 -1
View File
@@ -65,7 +65,7 @@ export default function Modal({
>
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === "ios" ? "padding" : "height"}
behavior="padding"
>
<View style={styles.overlay}>
<Animated.View
@@ -60,7 +60,7 @@ export default function ChangePasswordScreen() {
return (
<KeyboardAvoidingView
style={[styles.container, { backgroundColor: colors.bgPrimary }]}
behavior="height"
behavior="padding"
>
<ScrollView
contentContainerStyle={styles.scrollContent}
@@ -88,7 +88,7 @@ const LoginClient = () => {
return (
<KeyboardAvoidingView
style={[styles.container, { backgroundColor: colors.bgSecondary }]}
behavior="height"
behavior="padding"
>
<ScrollView
contentContainerStyle={styles.scrollContent}
+5 -2
View File
@@ -657,9 +657,12 @@ export default function CheckoutScreen() {
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === "ios" ? "padding" : undefined}
behavior="padding"
>
<ScrollView contentContainerStyle={styles.content}>
<ScrollView
contentContainerStyle={styles.content}
keyboardShouldPersistTaps="handled"
>
{/* Résumé commande */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>
+1 -1
View File
@@ -371,7 +371,7 @@ export default function ProfileScreen() {
}
return (
<KeyboardAvoidingView style={styles.container} behavior={Platform.OS === "ios" ? "padding" : undefined}>
<KeyboardAvoidingView style={styles.container} behavior="padding">
<ScrollView contentContainerStyle={styles.content} keyboardShouldPersistTaps="handled">
{/* Header */}