diff --git a/client/src/pages/BLReconciliation.tsx b/client/src/pages/BLReconciliation.tsx index e765774..95ccfee 100644 --- a/client/src/pages/BLReconciliation.tsx +++ b/client/src/pages/BLReconciliation.tsx @@ -137,10 +137,12 @@ export default function BLReconciliation() { 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?.webhookUrl) { + if (!delivery.group?.nocodbTableName && !delivery.group?.nocodbConfigId) { toast({ title: "Vérification non disponible", description: "Ce magasin n'a pas de configuration NocoDB", @@ -639,7 +641,7 @@ export default function BLReconciliation() { {/* Icône de vérification de facture */} - {(delivery.group?.webhookUrl || delivery.group?.nocodbTableName) && ( + {(delivery.group?.nocodbTableName || delivery.group?.nocodbConfigId) && (
{verifyingDeliveries.has(delivery.id) ? ( diff --git a/server/invoiceVerification.ts b/server/invoiceVerification.ts index 7aadf11..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,93 +19,17 @@ interface NocodbInvoice { } class InvoiceVerificationService { - private async getNocodbConfig(): Promise { - try { - const [config] = await db - .select() - .from(nocodbConfig) - .where(eq(nocodbConfig.isActive, true)) - .limit(1); - - if (!config) { - console.log('⚠️ Aucune configuration NocoDB active trouvée dans la table nocodb_config'); - throw new Error('Aucune configuration NocoDB active trouvée'); - } - - console.log('✅ Configuration NocoDB trouvée:', { - id: config.id, - name: config.name, - baseUrl: config.baseUrl, - projectId: config.projectId - }); - - return config; - } catch (error) { - console.error('❌ Erreur lors de la récupération de la configuration NocoDB:', error); - throw error; - } + 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, @@ -166,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', @@ -268,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 475de76..47c3750 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -884,7 +884,8 @@ export async function registerRoutes(app: Express): Promise { 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 9acf2c4..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 }) => ({ @@ -453,17 +421,7 @@ export const insertPublicityParticipationSchema = createInsertSchema(publicityPa createdAt: true, }); -export const insertNocodbConfigSchema = createInsertSchema(nocodbConfig).omit({ - id: true, - createdAt: true, - updatedAt: true, -}); -export const insertInvoiceVerificationCacheSchema = createInsertSchema(invoiceVerificationCache).omit({ - id: true, - createdAt: true, - updatedAt: 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;