diff --git a/client/src/pages/BLReconciliation.tsx b/client/src/pages/BLReconciliation.tsx
index 43fc194..95ccfee 100644
--- a/client/src/pages/BLReconciliation.tsx
+++ b/client/src/pages/BLReconciliation.tsx
@@ -132,7 +132,17 @@ export default function BLReconciliation() {
// Fonction pour déclencher la vérification
const handleVerifyInvoice = (delivery: any) => {
- if (!delivery.group?.webhookUrl) {
+ console.log('🔍 Debug vérification:', {
+ delivery: delivery,
+ group: delivery.group,
+ webhookUrl: delivery.group?.webhookUrl,
+ nocodbTableName: delivery.group?.nocodbTableName,
+ invoiceColumnName: delivery.group?.invoiceColumnName,
+ nocodbConfigId: delivery.group?.nocodbConfigId,
+ supplier: delivery.supplier
+ });
+
+ if (!delivery.group?.nocodbTableName && !delivery.group?.nocodbConfigId) {
toast({
title: "Vérification non disponible",
description: "Ce magasin n'a pas de configuration NocoDB",
@@ -631,7 +641,7 @@ export default function BLReconciliation() {
{/* Icône de vérification de facture */}
- {delivery.group?.webhookUrl && (
+ {(delivery.group?.nocodbTableName || delivery.group?.nocodbConfigId) && (
{verifyingDeliveries.has(delivery.id) ? (
diff --git a/server/invoiceVerification.ts b/server/invoiceVerification.ts
index 99a3ab9..916c667 100644
--- a/server/invoiceVerification.ts
+++ b/server/invoiceVerification.ts
@@ -1,6 +1,6 @@
import { db } from './db.js';
-import { nocodbConfig, invoiceVerificationCache, groups } from '@shared/schema';
-import { eq, and, desc } from 'drizzle-orm';
+import { groups } from '@shared/schema';
+import { eq } from 'drizzle-orm';
interface InvoiceVerificationResult {
exists: boolean;
@@ -19,80 +19,17 @@ interface NocodbInvoice {
}
class InvoiceVerificationService {
- private async getNocodbConfig(): Promise
{
- const [config] = await db
- .select()
- .from(nocodbConfig)
- .where(eq(nocodbConfig.isActive, true))
- .limit(1);
-
- if (!config) {
- throw new Error('Aucune configuration NocoDB active trouvée');
- }
-
- return config;
+ private getNocodbConfigFromGroup(group: any): any {
+ // Configuration NocoDB stockée directement dans le groupe
+ // Vous devez renseigner ces valeurs dans votre table groups en production
+ return {
+ baseUrl: process.env.NOCODB_BASE_URL || 'https://your-nocodb-instance.com',
+ apiToken: process.env.NOCODB_API_TOKEN || '',
+ projectId: process.env.NOCODB_PROJECT_ID || ''
+ };
}
- private generateCacheKey(groupId: number, invoiceRef: string, supplierName: string): string {
- return `${groupId}:${invoiceRef}:${supplierName}`.toLowerCase();
- }
- private async getCachedResult(cacheKey: string): Promise {
- try {
- const [cached] = await db
- .select()
- .from(invoiceVerificationCache)
- .where(and(
- eq(invoiceVerificationCache.cacheKey, cacheKey),
- // Cache valide pour 24 heures
- eq(invoiceVerificationCache.expiresAt, new Date(Date.now() + 24 * 60 * 60 * 1000))
- ))
- .limit(1);
-
- if (cached && new Date() < new Date(cached.expiresAt)) {
- console.log('🎯 Cache hit pour:', cacheKey);
- return {
- exists: cached.exists,
- matchType: cached.matchType as any,
- invoiceReference: cached.invoiceReference,
- supplierMatch: !!cached.supplierName,
- cacheHit: true,
- apiCallTime: cached.apiCallTime || 0
- };
- }
- } catch (error) {
- console.error('❌ Erreur lors de la lecture du cache:', error);
- }
-
- return null;
- }
-
- private async setCachedResult(
- cacheKey: string,
- groupId: number,
- result: InvoiceVerificationResult,
- invoiceRef: string,
- supplierName: string
- ): Promise {
- try {
- await db.insert(invoiceVerificationCache).values({
- cacheKey,
- groupId,
- invoiceReference: invoiceRef,
- supplierName,
- exists: result.exists,
- matchType: result.matchType,
- errorMessage: result.errorMessage,
- cacheHit: false,
- apiCallTime: result.apiCallTime || 0,
- expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000) // 24h
- });
-
- console.log('💾 Résultat mis en cache:', cacheKey);
- } catch (error) {
- console.error('❌ Erreur lors de la mise en cache:', error);
- }
- }
private async searchInNocodb(
config: any,
@@ -153,32 +90,31 @@ class InvoiceVerificationService {
invoiceRef: string,
blNumber: string,
supplierName: string,
- groupId: number
+ groupId: number,
+ groupConfig?: any
): Promise {
const startTime = Date.now();
try {
- // 1. Vérifier le cache d'abord
- const cacheKey = this.generateCacheKey(groupId, invoiceRef, supplierName);
- const cachedResult = await this.getCachedResult(cacheKey);
- if (cachedResult) {
- return cachedResult;
- }
- // 2. Récupérer la configuration NocoDB
- const config = await this.getNocodbConfig();
-
- // 3. Récupérer les informations du groupe
- const [group] = await db
- .select()
- .from(groups)
- .where(eq(groups.id, groupId))
- .limit(1);
+ // 2. Utiliser la configuration du groupe passée en paramètre ou la récupérer
+ let group = groupConfig;
+ if (!group) {
+ const [groupFromDb] = await db
+ .select()
+ .from(groups)
+ .where(eq(groups.id, groupId))
+ .limit(1);
+ group = groupFromDb;
+ }
if (!group || !group.nocodbTableName) {
throw new Error('Configuration NocoDB manquante pour ce magasin');
}
+ // 3. Récupérer la configuration NocoDB à partir des variables d'environnement
+ const config = this.getNocodbConfigFromGroup(group);
+
let result: InvoiceVerificationResult = {
exists: false,
matchType: 'none',
@@ -255,8 +191,7 @@ class InvoiceVerificationService {
result.errorMessage = result.errorMessage || 'Aucune correspondance trouvée';
}
- // 6. Mettre en cache le résultat
- await this.setCachedResult(cacheKey, groupId, result, invoiceRef, supplierName);
+
return result;
diff --git a/server/routes.ts b/server/routes.ts
index 004fc76..47c3750 100644
--- a/server/routes.ts
+++ b/server/routes.ts
@@ -862,14 +862,30 @@ export async function registerRoutes(app: Express): Promise {
const { invoiceReference, blNumber } = req.body;
if (!delivery.supplier || !delivery.group) {
+ console.log('❌ Livraison manque informations:', {
+ deliveryId,
+ hasSupplier: !!delivery.supplier,
+ hasGroup: !!delivery.group,
+ delivery: delivery
+ });
return res.status(400).json({ message: "Delivery missing supplier or group information" });
}
+ console.log('🔍 Tentative de vérification facture:', {
+ deliveryId,
+ invoiceReference: invoiceReference || delivery.invoiceReference,
+ blNumber: blNumber || delivery.blNumber,
+ supplierName: delivery.supplier.name,
+ groupId: delivery.groupId,
+ groupData: delivery.group
+ });
+
const result = await invoiceVerificationService.verifyInvoiceReference(
invoiceReference || delivery.invoiceReference || '',
blNumber || delivery.blNumber || '',
delivery.supplier.name,
- delivery.groupId
+ delivery.groupId,
+ delivery.group
);
res.json(result);
diff --git a/server/storage.ts b/server/storage.ts
index 493d515..7480713 100644
--- a/server/storage.ts
+++ b/server/storage.ts
@@ -30,9 +30,8 @@ import {
type PublicityParticipation,
type InsertPublicityParticipation,
type PublicityWithRelations,
- nocodbConfig,
- type NocodbConfig,
- type InsertNocodbConfig,
+
+
type CustomerOrder,
type InsertCustomerOrder,
type CustomerOrderWithRelations,
diff --git a/shared/schema.ts b/shared/schema.ts
index 7f49911..12f0606 100644
--- a/shared/schema.ts
+++ b/shared/schema.ts
@@ -143,36 +143,7 @@ export const publicityParticipations = pgTable("publicity_participations", {
-// NocoDB configuration globale (une seule instance NocoDB partagée)
-export const nocodbConfig = pgTable("nocodb_config", {
- id: serial("id").primaryKey(),
- name: varchar("name").notNull(), // Nom de la configuration
- baseUrl: varchar("base_url").notNull(), // URL de l'instance NocoDB
- projectId: varchar("project_id").notNull(), // NocoDB Project ID
- apiToken: varchar("api_token").notNull(), // Personal API Token
- description: text("description"), // Description de la configuration
- isActive: boolean("is_active").default(true), // Configuration active ou non
- createdBy: varchar("created_by"),
- createdAt: timestamp("created_at").defaultNow(),
- updatedAt: timestamp("updated_at").defaultNow(),
-});
-// Cache de vérification des factures pour éviter les re-vérifications
-export const invoiceVerificationCache = pgTable("invoice_verification_cache", {
- id: serial("id").primaryKey(),
- cacheKey: varchar("cache_key", { length: 255 }).notNull(), // Clé unique de cache
- groupId: integer("group_id").notNull(), // ID du magasin
- invoiceReference: varchar("invoice_reference", { length: 255 }).notNull(), // Référence facture
- supplierName: varchar("supplier_name", { length: 255 }), // Nom du fournisseur
- exists: boolean("exists").notNull(), // Facture trouvée ou non
- matchType: varchar("match_type", { length: 50 }).notNull(), // Type de correspondance (invoice_ref, bl_number)
- errorMessage: text("error_message"), // Message d'erreur si applicable
- cacheHit: boolean("cache_hit").default(false), // Cache hit ou non
- apiCallTime: integer("api_call_time"), // Temps d'appel API en ms
- expiresAt: timestamp("expires_at").notNull(), // Date d'expiration du cache
- createdAt: timestamp("created_at").defaultNow(),
- updatedAt: timestamp("updated_at").defaultNow(),
-});
// Customer Orders (Commandes Client)
export const customerOrders = pgTable("customer_orders", {
@@ -271,10 +242,7 @@ export const groupsRelations = relations(groups, ({ one, many }) => ({
customerOrders: many(customerOrders),
dlcProducts: many(dlcProducts),
tasks: many(tasks),
- nocodbConfig: one(nocodbConfig, {
- fields: [groups.nocodbConfigId],
- references: [nocodbConfig.id],
- }),
+
}));
export const userGroupsRelations = relations(userGroups, ({ one }) => ({
@@ -455,17 +423,7 @@ export const insertPublicityParticipationSchema = createInsertSchema(publicityPa
-export const insertNocodbConfigSchema = createInsertSchema(nocodbConfig).omit({
- id: true,
- createdAt: true,
- updatedAt: true,
-});
-export const insertInvoiceVerificationCacheSchema = createInsertSchema(invoiceVerificationCache).omit({
- id: true,
- createdAt: true,
- updatedAt: true,
-});
export const insertCustomerOrderSchema = createInsertSchema(customerOrders).omit({
id: true,
@@ -531,8 +489,6 @@ export type Publicity = typeof publicities.$inferSelect;
export type InsertPublicity = z.infer;
export type PublicityParticipation = typeof publicityParticipations.$inferSelect;
export type InsertPublicityParticipation = z.infer;
-export type NocodbConfig = typeof nocodbConfig.$inferSelect;
-export type InsertNocodbConfig = z.infer;
export type CustomerOrder = typeof customerOrders.$inferSelect;
export type InsertCustomerOrder = z.infer;
export type DlcProduct = typeof dlcProducts.$inferSelect;