From 30988c8be3c975f1040d4ad1f5c6d12b982ce45b Mon Sep 17 00:00:00 2001
From: michaelschal <35957947-michaelschal@users.noreply.replit.com>
Date: Mon, 8 Sep 2025 18:15:54 +0000
Subject: [PATCH 1/4] Add endpoint to verify credit note invoices using NocoDB
integration
Add a POST route '/api/avoirs/:id/verify-invoice' to the backend for verifying credit note invoices. This endpoint integrates with NocoDB, checks user permissions and group associations, and uses the invoiceVerificationService to validate invoices based on a provided reference.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9fcf4b21-eb0c-4e53-a567-e2ce4a6ad869
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/9fcf4b21-eb0c-4e53-a567-e2ce4a6ad869/GlyTlJp
---
server/routes.ts | 78 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/server/routes.ts b/server/routes.ts
index 320b74d..ac2d8f4 100644
--- a/server/routes.ts
+++ b/server/routes.ts
@@ -2600,6 +2600,84 @@ export async function registerRoutes(app: Express): Promise {
}
});
+ // Route de vérification de facture NocoDB pour les avoirs
+ app.post('/api/avoirs/:id/verify-invoice', isAuthenticated, async (req: any, res) => {
+ try {
+ const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
+ if (!user) {
+ return res.status(404).json({ message: "User not found" });
+ }
+
+ const avoirId = parseInt(req.params.id);
+ const avoir = await storage.getAvoir(avoirId);
+
+ if (!avoir) {
+ return res.status(404).json({ message: "Avoir not found" });
+ }
+
+ // Check permissions
+ if (!hasPermission(user.role, 'avoirs', 'view')) {
+ return res.status(403).json({ message: "Insufficient permissions" });
+ }
+
+ // Only admin have access to all avoirs, others must be in the same group
+ if (user.role !== 'admin') {
+ const userGroupIds = user.userGroups?.map((ug: any) => ug.groupId) || [];
+ if (!userGroupIds.includes(avoir.groupId)) {
+ console.log('đ« Access denied - User groups check:', {
+ userId: user.id,
+ userRole: user.role,
+ userGroupIds,
+ avoirGroupId: avoir.groupId,
+ avoirSupplier: avoir.supplier?.name
+ });
+ return res.status(403).json({ message: "Access denied to this group" });
+ }
+ }
+
+ const { invoiceReference, forceRefresh } = req.body;
+
+ if (!avoir.supplier || !avoir.group) {
+ console.log('â Avoir manque informations:', {
+ avoirId,
+ hasSupplier: !!avoir.supplier,
+ hasGroup: !!avoir.group
+ });
+ return res.status(400).json({ message: "Avoir missing supplier or group information" });
+ }
+
+ // Vérifier que la référence facture est présente
+ if (!invoiceReference || !invoiceReference.trim()) {
+ return res.status(400).json({ message: "Référence de facture requise" });
+ }
+
+ console.log('đ VĂ©rification facture avoir:', {
+ avoirId,
+ invoiceReference,
+ supplier: avoir.supplier?.name,
+ group: avoir.group?.name,
+ groupId: avoir.groupId
+ });
+
+ // Vérifier par référence de facture uniquement
+ const result = await invoiceVerificationService.verifyInvoice(
+ invoiceReference,
+ avoir.groupId,
+ forceRefresh || false,
+ false // Les avoirs ne sont pas "réconciliés" comme les livraisons
+ );
+
+ console.log('â
Résultat vérification avoir:', result);
+ res.json(result);
+ } catch (error) {
+ console.error("Error verifying avoir invoice:", error);
+ res.status(500).json({
+ message: "Failed to verify avoir invoice",
+ error: error instanceof Error ? error.message : 'Unknown error'
+ });
+ }
+ });
+
// Avoir status update routes
app.put('/api/avoirs/:id/webhook-status', isAuthenticated, async (req: any, res) => {
try {
From 73a7bcba16586710cce4323aa0716d91d17a8787 Mon Sep 17 00:00:00 2001
From: michaelschal <35957947-michaelschal@users.noreply.replit.com>
Date: Mon, 8 Sep 2025 18:17:17 +0000
Subject: [PATCH 2/4] Add invoice verification feature to improve financial
reconciliation
Implement a new API endpoint and frontend logic to verify invoice references for 'avoirs', including error handling and status updates. Adjust permission checks in routes.ts to use 'deliveries' module logic.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9fcf4b21-eb0c-4e53-a567-e2ce4a6ad869
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/9fcf4b21-eb0c-4e53-a567-e2ce4a6ad869/GlyTlJp
---
client/src/pages/Avoirs.tsx | 123 +++++++++++++++++++++++++++++++++++-
server/routes.ts | 4 +-
2 files changed, 124 insertions(+), 3 deletions(-)
diff --git a/client/src/pages/Avoirs.tsx b/client/src/pages/Avoirs.tsx
index 1c06e76..da27be0 100644
--- a/client/src/pages/Avoirs.tsx
+++ b/client/src/pages/Avoirs.tsx
@@ -1,6 +1,6 @@
import { useState } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
-import { Plus, Search, FileText, CheckCircle, AlertCircle, Clock, Edit, Trash2, UserCheck, Send, Upload } from "lucide-react";
+import { Plus, Search, FileText, CheckCircle, AlertCircle, Clock, Edit, Trash2, UserCheck, Send, Upload, XCircle } from "lucide-react";
import { useStore } from "@/components/Layout";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -111,6 +111,10 @@ export default function Avoirs() {
const [showWaitingModal, setShowWaitingModal] = useState(false);
const [processingSeconds, setProcessingSeconds] = useState(0);
const [processingTimeout, setProcessingTimeout] = useState(null);
+
+ // Ătats pour le systĂšme de vĂ©rification de facture
+ const [avoirVerificationResults, setAvoirVerificationResults] = useState>({});
+ const [verifyingAvoirs, setVerifyingAvoirs] = useState>(new Set());
// Utiliser le contexte global du magasin
const { selectedStoreId } = useStore();
const { toast } = useToast();
@@ -328,6 +332,123 @@ export default function Avoirs() {
setIsDeleteDialogOpen(true);
};
+ // đ FONCTION DE VĂRIFICATION DE FACTURE (comme rapprochement)
+ const verifyAvoirInvoiceMutation = useMutation({
+ mutationFn: async ({ avoirId, invoiceReference, forceRefresh }: { avoirId: number; invoiceReference?: string; forceRefresh?: boolean }) => {
+ try {
+ const result = await apiRequest(`/api/avoirs/${avoirId}/verify-invoice`, 'POST', {
+ invoiceReference,
+ forceRefresh: forceRefresh || false
+ });
+ return result;
+ } catch (error: any) {
+ console.error('Erreur API vérification avoir:', error);
+ throw new Error(error.message || 'Erreur de vérification');
+ }
+ },
+ onSuccess: (result, variables) => {
+ setAvoirVerificationResults(prev => ({
+ ...prev,
+ [variables.avoirId]: result
+ }));
+
+ setVerifyingAvoirs(prev => {
+ const newSet = new Set(prev);
+ newSet.delete(variables.avoirId);
+ return newSet;
+ });
+ },
+ onError: (error, variables) => {
+ console.error('Erreur vérification facture avoir:', error);
+ setAvoirVerificationResults(prev => ({
+ ...prev,
+ [variables.avoirId]: {
+ exists: false,
+ matchType: 'none',
+ errorMessage: error instanceof Error ? error.message : 'Erreur inconnue'
+ }
+ }));
+
+ setVerifyingAvoirs(prev => {
+ const newSet = new Set(prev);
+ newSet.delete(variables.avoirId);
+ return newSet;
+ });
+
+ toast({
+ title: "Erreur de vérification",
+ description: error instanceof Error ? error.message : 'Erreur inconnue',
+ variant: "destructive",
+ });
+ }
+ });
+
+ // Fonction pour déclencher la vérification d'un avoir
+ const handleVerifyAvoirInvoice = (avoir: Avoir, forceRefresh: boolean = false) => {
+ const hasInvoiceRef = avoir.invoiceReference?.trim();
+
+ if (!hasInvoiceRef) {
+ toast({
+ title: "Référence manquante",
+ description: "Veuillez saisir une référence de facture avant la vérification",
+ variant: "destructive",
+ });
+ return;
+ }
+
+ if (!avoir.group?.nocodbTableName && !avoir.group?.nocodbConfigId) {
+ toast({
+ title: "Vérification non disponible",
+ description: "Ce magasin n'a pas de configuration NocoDB",
+ variant: "destructive",
+ });
+ return;
+ }
+
+ console.log('đ DĂ©clenchement vĂ©rification avoir:', {
+ avoirId: avoir.id,
+ invoiceReference: avoir.invoiceReference,
+ supplier: avoir.supplier?.name,
+ group: avoir.group?.name
+ });
+
+ setVerifyingAvoirs(prev => new Set(prev).add(avoir.id));
+
+ verifyAvoirInvoiceMutation.mutate({
+ avoirId: avoir.id,
+ invoiceReference: avoir.invoiceReference,
+ forceRefresh
+ });
+ };
+
+ // Fonction pour vérifier tous les avoirs avec une référence facture
+ const handleVerifyAllAvoirInvoices = () => {
+ const avoirsToVerify = avoirs.filter(avoir =>
+ avoir.invoiceReference?.trim() &&
+ (avoir.group?.nocodbTableName || avoir.group?.nocodbConfigId)
+ );
+
+ if (avoirsToVerify.length === 0) {
+ toast({
+ title: "Aucun avoir à vérifier",
+ description: "Aucun avoir avec référence de facture trouvé",
+ });
+ return;
+ }
+
+ avoirsToVerify.forEach((avoir, index) => {
+ // Délai échelonné pour éviter la surcharge
+ setTimeout(() => {
+ handleVerifyAvoirInvoice(avoir, true); // Force refresh pour tous
+ }, index * 200); // 200ms entre chaque vérification
+ });
+
+ toast({
+ title: "Vérification lancée",
+ description: `Vérification de ${avoirsToVerify.length} avoir(s) en cours...`,
+ });
+ };
+
// đ„ FONCTIONS WEBHOOK MODAL (comme rapprochement)
const handleFileChange = (event: React.ChangeEvent) => {
const file = event.target.files?.[0];
diff --git a/server/routes.ts b/server/routes.ts
index ac2d8f4..cc5dfb2 100644
--- a/server/routes.ts
+++ b/server/routes.ts
@@ -2615,8 +2615,8 @@ export async function registerRoutes(app: Express): Promise {
return res.status(404).json({ message: "Avoir not found" });
}
- // Check permissions
- if (!hasPermission(user.role, 'avoirs', 'view')) {
+ // Check permissions (using 'deliveries' module for similar logic)
+ if (!hasPermission(user.role, 'deliveries', 'view')) {
return res.status(403).json({ message: "Insufficient permissions" });
}
From e650822170b9b8f1b8be01bc2bcdc70c5fcc6544 Mon Sep 17 00:00:00 2001
From: michaelschal <35957947-michaelschal@users.noreply.replit.com>
Date: Mon, 8 Sep 2025 18:19:33 +0000
Subject: [PATCH 3/4] Add invoice verification feature for user task management
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Update Avoir and Group interfaces to include Nocodb fields. Implement invoice verification functionality on the Avoirs page, adding a "Vérifier tous" button and individual verification status indicators for each avoir with an invoice reference.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9fcf4b21-eb0c-4e53-a567-e2ce4a6ad869
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/9fcf4b21-eb0c-4e53-a567-e2ce4a6ad869/ECMHcEr
---
client/src/pages/Avoirs.tsx | 61 +++++++++++++++++++++++++++++++++----
1 file changed, 55 insertions(+), 6 deletions(-)
diff --git a/client/src/pages/Avoirs.tsx b/client/src/pages/Avoirs.tsx
index da27be0..62fe7b9 100644
--- a/client/src/pages/Avoirs.tsx
+++ b/client/src/pages/Avoirs.tsx
@@ -47,6 +47,8 @@ interface Avoir {
id: number;
name: string;
color: string;
+ nocodbTableName?: string;
+ nocodbConfigId?: number;
};
creator: {
id: string;
@@ -65,6 +67,8 @@ interface Group {
id: number;
name: string;
color: string;
+ nocodbTableName?: string;
+ nocodbConfigId?: number;
}
// Schema for form validation
@@ -646,13 +650,30 @@ export default function Avoirs() {
-