Make delivery validation require a mandatory delivery note number

Enforce the requirement for a delivery note number (blNumber) during delivery validation on both the delivery and calendar modules. This change updates the frontend validation schema to make blNumber a required field with a minimum length, modifies the backend logic to reject validations without a blNumber, and integrates a new modal component for handling this validation process within the Deliveries page.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 98f01874-e00c-4bbe-9d71-b0b338001848
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/98f01874-e00c-4bbe-9d71-b0b338001848/yaZkPAT
This commit is contained in:
michaelschal committed 2025-09-01 11:57:55 +00:00
1 parent 6dca340c45
commit c09388c0aa
3 files changed
+34 -60

No files matched your search

@@ -13,7 +13,7 @@ import { isUnauthorizedError } from "@/lib/authUtils";
import { Check, X } from "lucide-react";
const validateDeliverySchema = z.object({
blNumber: z.string().optional(),
blNumber: z.string().min(1, "Le numéro de bon de livraison est obligatoire"),
blAmount: z.string().optional(),
});
@@ -47,10 +47,8 @@ export default function ValidateDeliveryModal({
mutationFn: async (data: ValidateDeliveryForm) => {
const payload: any = {};
// Ajouter blNumber seulement s'il est fourni
if (data.blNumber && data.blNumber.trim() !== '') {
payload.blNumber = data.blNumber.trim();
}
// Le blNumber est maintenant obligatoire
payload.blNumber = data.blNumber.trim();
if (data.blAmount && data.blAmount.trim() !== '') {
payload.blAmount = parseFloat(data.blAmount);
@@ -137,10 +135,10 @@ export default function ValidateDeliveryModal({
name="blNumber"
render={({ field }) => (
<FormItem>
<FormLabel>N° Bon de Livraison</FormLabel>
<FormLabel>N° Bon de Livraison *</FormLabel>
<FormControl>
<Input
placeholder="Ex: BL-2024-001 (optionnel)"
placeholder="Ex: BL-2024-001"
{...field}
/>
</FormControl>
+21 -46
View File
@@ -29,6 +29,7 @@ import { safeFormat } from "@/lib/dateUtils";
import CreateDeliveryModal from "@/components/modals/CreateDeliveryModal";
import EditDeliveryModal from "@/components/modals/EditDeliveryModal";
import OrderDetailModal from "@/components/modals/OrderDetailModal";
import ValidateDeliveryModal from "@/components/modals/ValidateDeliveryModal";
import ConfirmDeleteModal from "@/components/modals/ConfirmDeleteModal";
import type { DeliveryWithRelations } from "@shared/schema";
@@ -67,8 +68,10 @@ export default function Deliveries() {
const [showEditModal, setShowEditModal] = useState(false);
const [showDetailModal, setShowDetailModal] = useState(false);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [showValidateModal, setShowValidateModal] = useState(false);
const [selectedDelivery, setSelectedDelivery] = useState<DeliveryWithRelations | null>(null);
const [deliveryToDelete, setDeliveryToDelete] = useState<DeliveryWithRelations | null>(null);
const [deliveryToValidate, setDeliveryToValidate] = useState<DeliveryWithRelations | null>(null);
// Construire l'URL pour l'historique complet sans filtrage par date
// CRITICAL FIX: Appliquer le filtrage par storeId pour TOUS les rôles, pas seulement admin
@@ -107,46 +110,6 @@ export default function Deliveries() {
const groups = Array.isArray(groupsData) ? groupsData : [];
const validateMutation = useMutation({
mutationFn: async (id: number) => {
await apiRequest(`/api/deliveries/${id}/validate`, "POST");
},
onSuccess: () => {
toast({
title: "Succès",
description: "Livraison validée avec succès",
});
// Invalider tous les caches liés aux livraisons
queryClient.invalidateQueries({ queryKey: ['/api/deliveries'] });
queryClient.invalidateQueries({ queryKey: [deliveriesUrl] });
// Invalider tous les caches BL/Rapprochement
queryClient.invalidateQueries({
predicate: (query) =>
query.queryKey[0] === '/api/deliveries/bl' ||
query.queryKey[0] === '/api/deliveries'
});
queryClient.invalidateQueries({ queryKey: ['/api/orders'] });
queryClient.invalidateQueries({ queryKey: ['/api/stats/monthly'] });
},
onError: (error) => {
if (isUnauthorizedError(error)) {
toast({
title: "Non autorisé",
description: "Vous êtes déconnecté. Reconnexion...",
variant: "destructive",
});
setTimeout(() => {
window.location.href = "/api/login";
}, 500);
return;
}
toast({
title: "Erreur",
description: "Impossible de valider la livraison",
variant: "destructive",
});
},
});
const deleteMutation = useMutation({
mutationFn: async (id: number) => {
@@ -231,10 +194,14 @@ export default function Deliveries() {
setShowEditModal(true);
};
const handleValidateDelivery = (id: number) => {
if (window.confirm("Êtes-vous sûr de vouloir valider cette livraison ?")) {
validateMutation.mutate(id);
}
const handleValidateDelivery = (delivery: DeliveryWithRelations) => {
setDeliveryToValidate(delivery);
setShowValidateModal(true);
};
const handleValidateModalClose = () => {
setShowValidateModal(false);
setDeliveryToValidate(null);
};
const handleDeleteDelivery = (delivery: DeliveryWithRelations) => {
@@ -448,9 +415,9 @@ export default function Deliveries() {
<Button
variant="ghost"
size="sm"
onClick={() => handleValidateDelivery(delivery.id)}
onClick={() => handleValidateDelivery(delivery)}
className="text-green-600 hover:text-green-700"
disabled={validateMutation.isPending}
disabled={false}
>
<Check className="w-4 h-4 border border-gray-300 rounded p-0.5" />
</Button>
@@ -513,6 +480,14 @@ export default function Deliveries() {
/>
)}
{showValidateModal && deliveryToValidate && (
<ValidateDeliveryModal
isOpen={showValidateModal}
onClose={handleValidateModalClose}
delivery={deliveryToValidate}
/>
)}
{/* Modal de confirmation de suppression */}
<ConfirmDeleteModal
isOpen={showDeleteModal}
+8 -7
View File
@@ -1113,13 +1113,14 @@ export async function registerRoutes(app: Express): Promise<Server> {
const { blNumber, blAmount } = req.body;
// BL data is optional - delivery can be validated without it
let blData: any = undefined;
if (blNumber) {
blData = { blNumber };
if (blAmount !== undefined && blAmount !== null && blAmount !== '') {
blData.blAmount = blAmount;
}
// Le numéro de BL est maintenant obligatoire pour valider une livraison
if (!blNumber || !blNumber.trim()) {
return res.status(400).json({ message: "Le numéro de bon de livraison est obligatoire pour valider une livraison" });
}
let blData: any = { blNumber: blNumber.trim() };
if (blAmount !== undefined && blAmount !== null && blAmount !== '') {
blData.blAmount = blAmount;
}
await storage.validateDelivery(id, blData);