diff --git a/VALIDATION-ERROR-FIX.md b/VALIDATION-ERROR-FIX.md new file mode 100644 index 0000000..db12d27 --- /dev/null +++ b/VALIDATION-ERROR-FIX.md @@ -0,0 +1,106 @@ +# Fix Urgent - Erreurs de Validation Zod + +## ❌ Problème Identifié +**Erreur 500 :** ZodError lors de PUT /api/deliveries/:id + +### Erreurs Specifiques +1. **blAmount** : Expected string, received number +2. **validatedAt** : Expected date, received string + +## 🔍 Analyse du Problème + +### Schema Drizzle (shared/schema.ts) +```typescript +// Dans deliveries table: +blAmount: decimal("bl_amount", { precision: 10, scale: 2 }) // → Zod attend string +invoiceAmount: decimal("invoice_amount", { precision: 10, scale: 2 }) // → Zod attend string +validatedAt: timestamp("validated_at") // → Zod attend Date object +``` + +### Données Envoyées depuis Frontend +```javascript +// ❌ Problématique +{ + blAmount: 3397.86, // number → should be string + invoiceAmount: null, // OK + validatedAt: '2025-08-12T11:42:39.679Z' // string → should be Date +} +``` + +## ✅ Solutions Appliquées + +### 1. **Transformation côté Serveur** (server/routes.ts) +```typescript +// Avant validation Zod, transformer les types +const transformedData = { ...req.body }; + +// Convertir montants number → string +if (transformedData.blAmount !== undefined && transformedData.blAmount !== null) { + transformedData.blAmount = transformedData.blAmount.toString(); +} +if (transformedData.invoiceAmount !== undefined && transformedData.invoiceAmount !== null) { + transformedData.invoiceAmount = transformedData.invoiceAmount.toString(); +} + +// Convertir validatedAt string → Date +if (transformedData.validatedAt && typeof transformedData.validatedAt === 'string') { + transformedData.validatedAt = new Date(transformedData.validatedAt); +} + +const data = insertDeliverySchema.partial().parse(transformedData); +``` + +### 2. **Correction côté Frontend** (ReconciliationModal.tsx) +```typescript +// Avant +validatedAt: new Date() // ❌ Date object → string lors JSON.stringify + +// Après +validatedAt: new Date().toISOString() // ✅ string → convertie en Date côté serveur +``` + +### 3. **Montants toujours en string** (ReconciliationModal.tsx) +```typescript +// Déjà corrigé précédemment +blAmount: formData.blAmount ? formData.blAmount.toString() : null, +invoiceAmount: formData.invoiceAmount ? formData.invoiceAmount.toString() : null, +``` + +## 🧪 Test de Validation + +### Payload Attendu Maintenant +```javascript +// ✅ Correct après transformation +{ + blNumber: 'LD2250800571', + blAmount: '3397.86', // string ✅ + invoiceReference: null, + invoiceAmount: null, + reconciled: true, + validatedAt: new Date('2025-08-12T11:42:39.679Z') // Date object ✅ +} +``` + +### Tests à Effectuer +1. **Enregistrer données BL** : blAmount doit passer en string +2. **Valider rapprochement** : validatedAt doit passer en Date +3. **Enregistrer montant facture** : invoiceAmount doit passer en string +4. **Vérifier console** : Plus d'erreurs ZodError 500 + +## 🔄 Flux de Données Complet + +``` +Frontend Form → JSON.stringify → HTTP Request → Server Transform → Zod Validation → Database + ↓ ↓ ↓ ↓ ↓ ↓ +{number} → "3397.86" → "3397.86" → "3397.86" → ✅ → DECIMAL +{Date} → "2025-..." → "2025-..." → Date(...) → ✅ → TIMESTAMP +``` + +## 📊 Status + +- ✅ Transformation automatique côté serveur +- ✅ Frontend envoie validatedAt en ISO string +- ✅ Montants forcés en string côté client +- 🔄 **À tester en production** + +**L'erreur 500 ZodError devrait être résolue !** \ No newline at end of file diff --git a/attached_assets/{E6E79F46-7D13-4B31-859C-5957C0D12A79}_1754998968767.png b/attached_assets/{E6E79F46-7D13-4B31-859C-5957C0D12A79}_1754998968767.png new file mode 100644 index 0000000..f190dc6 Binary files /dev/null and b/attached_assets/{E6E79F46-7D13-4B31-859C-5957C0D12A79}_1754998968767.png differ diff --git a/client/src/components/modals/ReconciliationModal.tsx b/client/src/components/modals/ReconciliationModal.tsx index 702138e..0ee5fbf 100644 --- a/client/src/components/modals/ReconciliationModal.tsx +++ b/client/src/components/modals/ReconciliationModal.tsx @@ -90,9 +90,9 @@ export default function ReconciliationModal({ updateDeliveryMutation.mutate({ blNumber: formData.blNumber.trim(), - blAmount: formData.blAmount ? parseFloat(formData.blAmount) : null, + blAmount: formData.blAmount ? formData.blAmount.toString() : null, invoiceReference: formData.invoiceReference.trim() || null, - invoiceAmount: formData.invoiceAmount ? parseFloat(formData.invoiceAmount) : null, + invoiceAmount: formData.invoiceAmount ? formData.invoiceAmount.toString() : null, }); }; @@ -108,9 +108,9 @@ export default function ReconciliationModal({ updateDeliveryMutation.mutate({ blNumber: formData.blNumber.trim(), - blAmount: formData.blAmount ? parseFloat(formData.blAmount) : null, + blAmount: formData.blAmount ? formData.blAmount.toString() : null, invoiceReference: formData.invoiceReference.trim() || null, - invoiceAmount: formData.invoiceAmount ? parseFloat(formData.invoiceAmount) : null, + invoiceAmount: formData.invoiceAmount ? formData.invoiceAmount.toString() : null, reconciled: true, validatedAt: new Date().toISOString() }); diff --git a/server/routes.ts b/server/routes.ts index dc7414f..c2667cb 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -690,7 +690,24 @@ export async function registerRoutes(app: Express): Promise { } console.log('🔄 Updating delivery:', { id, data: req.body, user: user.id }); - const data = insertDeliverySchema.partial().parse(req.body); + + // Transform data types before validation + const transformedData = { ...req.body }; + + // Convert decimal amounts to strings (schema expects string for decimal fields) + if (transformedData.blAmount !== undefined && transformedData.blAmount !== null) { + transformedData.blAmount = transformedData.blAmount.toString(); + } + if (transformedData.invoiceAmount !== undefined && transformedData.invoiceAmount !== null) { + transformedData.invoiceAmount = transformedData.invoiceAmount.toString(); + } + + // Convert validatedAt to Date object (schema expects Date for timestamp fields) + if (transformedData.validatedAt && typeof transformedData.validatedAt === 'string') { + transformedData.validatedAt = new Date(transformedData.validatedAt); + } + + const data = insertDeliverySchema.partial().parse(transformedData); const updatedDelivery = await storage.updateDelivery(id, data); console.log('✅ Delivery updated successfully:', { id, updatedDelivery }); @@ -721,7 +738,7 @@ export async function registerRoutes(app: Express): Promise { // Auto-valider le rapprochement await storage.updateDelivery(id, { reconciled: true, - validatedAt: new Date() + validatedAt: new Date().toISOString() }); console.log(`✅ Auto-reconciliation: Delivery #${id} automatically validated for supplier ${supplier.name}`);