diff --git a/attached_assets/Pasted-ctions-Found-1-group-assignments-for-user-1753266816257-Raw-query-result-1755008518319_1755008518319.txt b/attached_assets/Pasted-ctions-Found-1-group-assignments-for-user-1753266816257-Raw-query-result-1755008518319_1755008518319.txt new file mode 100644 index 0000000..9a83c0e --- /dev/null +++ b/attached_assets/Pasted-ctions-Found-1-group-assignments-for-user-1753266816257-Raw-query-result-1755008518319_1755008518319.txt @@ -0,0 +1,207 @@ +ctions + + + + +🔍 Found 1 group assignments for user _1753266816257 + +🔍 Raw query result: [ + + { + + user_id: '_1753266816257', + + group_id: 2, + + group_id_ref: 2, + + group_name: 'Houdemont', + + group_color: '#455A64', + + nocodb_config_id: 1, + + nocodb_table_id: 'my7zunxprumahmm', + + nocodb_table_name: 'CommandeH', + + invoice_column_name: 'RefFacture', + + group_created_at: '2025-07-22 11:09:08.977622', + + group_updated_at: '2025-08-12 09:39:32.989' + + } + +] + +✅ Processed 1 groups for user _1753266816257 + +GET /api/groups 304 in 88ms + +GET /api/suppliers 304 in 95ms + +🔍 Querying user_groups for user: _1753266816257 + +🔍 Found 1 group assignments for user _1753266816257 + +🔍 Raw query result: [ + + { + + user_id: '_1753266816257', + + group_id: 2, + + group_id_ref: 2, + + group_name: 'Houdemont', + + group_color: '#455A64', + + nocodb_config_id: 1, + + nocodb_table_id: 'my7zunxprumahmm', + + nocodb_table_name: 'CommandeH', + + invoice_column_name: 'RefFacture', + + group_created_at: '2025-07-22 11:09:08.977622', + + group_updated_at: '2025-08-12 09:39:32.989' + + } + +] + +✅ Processed 1 groups for user _1753266816257 + +🔍 getUserWithGroups called for user: _1753266816257 + +🔍 Querying user_groups for user: _1753266816257 + +🔍 Found 1 group assignments for user _1753266816257 + +🔍 Raw query result: [ + + { + + user_id: '_1753266816257', + + group_id: 2, + + group_id_ref: 2, + + group_name: 'Houdemont', + + group_color: '#455A64', + + nocodb_config_id: 1, + + nocodb_table_id: 'my7zunxprumahmm', + + nocodb_table_name: 'CommandeH', + + invoice_column_name: 'RefFacture', + + group_created_at: '2025-07-22 11:09:08.977622', + + group_updated_at: '2025-08-12 09:39:32.989' + + } + +] + +✅ Processed 1 groups for user _1753266816257 + +🔍 getUserWithGroups called for user: _1753266816257 + +🔍 Querying user_groups for user: _1753266816257 + +🔍 Found 1 group assignments for user _1753266816257 + +🔍 Raw query result: [ + + { + + user_id: '_1753266816257', + + group_id: 2, + + group_id_ref: 2, + + group_name: 'Houdemont', + + group_color: '#455A64', + + nocodb_config_id: 1, + + nocodb_table_id: 'my7zunxprumahmm', + + nocodb_table_name: 'CommandeH', + + invoice_column_name: 'RefFacture', + + group_created_at: '2025-07-22 11:09:08.977622', + + group_updated_at: '2025-08-12 09:39:32.989' + + } + +] + +✅ Processed 1 groups for user _1753266816257 + +Customer orders - User assigned stores: { groupIds: [ 2 ] } + +Customer orders returned from storage: 23 items + +GET /api/customer-orders 304 in 130ms + +HEAD /api/health 200 in 1ms + +🔍 getUserWithGroups called for user: _1753266816257 + +🔍 Querying user_groups for user: _1753266816257 + +🔍 Found 1 group assignments for user _1753266816257 + +🔍 Raw query result: [ + + { + + user_id: '_1753266816257', + + group_id: 2, + + group_id_ref: 2, + + group_name: 'Houdemont', + + group_color: '#455A64', + + nocodb_config_id: 1, + + nocodb_table_id: 'my7zunxprumahmm', + + nocodb_table_name: 'CommandeH', + + invoice_column_name: 'RefFacture', + + group_created_at: '2025-07-22 11:09:08.977622', + + group_updated_at: '2025-08-12 09:39:32.989' + + } + +] + +✅ Processed 1 groups for user _1753266816257 + +GET /api/user 304 in 7ms + +HEAD /api/health 200 in 1ms + + + \ No newline at end of file diff --git a/attached_assets/{192C59D5-006D-462C-8642-17012D06A969}_1755008500055.png b/attached_assets/{192C59D5-006D-462C-8642-17012D06A969}_1755008500055.png new file mode 100644 index 0000000..95b620e Binary files /dev/null and b/attached_assets/{192C59D5-006D-462C-8642-17012D06A969}_1755008500055.png differ diff --git a/client/src/pages/DlcPage.tsx b/client/src/pages/DlcPage.tsx index 73a52a9..8496458 100644 --- a/client/src/pages/DlcPage.tsx +++ b/client/src/pages/DlcPage.tsx @@ -73,9 +73,22 @@ export default function DlcPage() { if (statusFilter && statusFilter !== "all") params.append("status", statusFilter); if (supplierFilter && supplierFilter !== "all") params.append("supplierId", supplierFilter); + console.log("🔍 Requête DLC products:", { + url: `/api/dlc-products?${params.toString()}`, + user: user?.role, + selectedStoreId, + userGroups: user?.userGroups?.map(ug => ({ groupId: ug.groupId, groupName: ug.group?.name })) + }); + return apiRequest(`/api/dlc-products?${params.toString()}`); }, enabled: !authLoading, + onSuccess: (data) => { + console.log("📋 DLC products reçus:", { + total: data.length, + sample: data.slice(0, 3).map(d => ({ id: d.id, productName: d.productName, groupId: d.groupId })) + }); + } }); // Fetch DLC stats @@ -105,8 +118,10 @@ export default function DlcPage() { const createMutation = useMutation({ mutationFn: (data: InsertDlcProduct) => apiRequest("/api/dlc-products", "POST", data), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["/api/dlc-products"] }); - queryClient.invalidateQueries({ queryKey: ["/api/dlc-products/stats"] }); + // Invalidate all DLC queries with any combination of parameters + queryClient.invalidateQueries({ queryKey: ["/api/dlc-products"], exact: false }); + queryClient.invalidateQueries({ queryKey: ["/api/dlc-products/stats"], exact: false }); + console.log("🔄 Cache DLC invalidé après création"); toast({ title: "Produit DLC créé avec succès" }); setIsDialogOpen(false); form.reset(); @@ -126,8 +141,9 @@ export default function DlcPage() { mutationFn: ({ id, data }: { id: number; data: Partial }) => apiRequest(`/api/dlc-products/${id}`, "PUT", data), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["/api/dlc-products"] }); - queryClient.invalidateQueries({ queryKey: ["/api/dlc-products/stats"] }); + queryClient.invalidateQueries({ queryKey: ["/api/dlc-products"], exact: false }); + queryClient.invalidateQueries({ queryKey: ["/api/dlc-products/stats"], exact: false }); + console.log("🔄 Cache DLC invalidé après mise à jour"); toast({ title: "Produit DLC mis à jour avec succès" }); setIsDialogOpen(false); form.reset(); @@ -146,8 +162,9 @@ export default function DlcPage() { const validateMutation = useMutation({ mutationFn: (id: number) => apiRequest(`/api/dlc-products/${id}/validate`, "PUT"), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["/api/dlc-products"] }); - queryClient.invalidateQueries({ queryKey: ["/api/dlc-products/stats"] }); + queryClient.invalidateQueries({ queryKey: ["/api/dlc-products"], exact: false }); + queryClient.invalidateQueries({ queryKey: ["/api/dlc-products/stats"], exact: false }); + console.log("🔄 Cache DLC invalidé après validation"); toast({ title: "Produit validé avec succès" }); }, onError: (error: any) => { @@ -163,8 +180,9 @@ export default function DlcPage() { const deleteMutation = useMutation({ mutationFn: (id: number) => apiRequest(`/api/dlc-products/${id}`, "DELETE"), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["/api/dlc-products"] }); - queryClient.invalidateQueries({ queryKey: ["/api/dlc-products/stats"] }); + queryClient.invalidateQueries({ queryKey: ["/api/dlc-products"], exact: false }); + queryClient.invalidateQueries({ queryKey: ["/api/dlc-products/stats"], exact: false }); + console.log("🔄 Cache DLC invalidé après suppression"); toast({ title: "Produit supprimé avec succès" }); }, onError: (error: any) => { diff --git a/cookies.txt b/cookies.txt index 73e7dab..dff7434 100644 --- a/cookies.txt +++ b/cookies.txt @@ -2,4 +2,4 @@ # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. -#HttpOnly_localhost FALSE / FALSE 1755094677 connect.sid s%3AMRvtKRB7l8aU-cxKVAyRLza4Hz26hGS3.B0p70oONarXt57Foz6nSvGoPBIEpN6YNyr22RwYgJEc +#HttpOnly_localhost FALSE / FALSE 1755095028 connect.sid s%3AxwnR6osGUOsL-I6MHjHctBuJf4K1HUWh.jqsaHkMTtzflaI4dg%2BmJBmTrVplUTDTG45KCUiL%2FVfw diff --git a/debug-employee-dlc-visibility.js b/debug-employee-dlc-visibility.js new file mode 100644 index 0000000..abf8bcd --- /dev/null +++ b/debug-employee-dlc-visibility.js @@ -0,0 +1,104 @@ +#!/usr/bin/env node + +/** + * Debug final pour vérifier pourquoi les DLC créées par l'employé n'apparaissent pas + */ + +console.log('🔍 Debug Final - Visibilité DLC Employé'); + +async function debugDlcVisibility() { + try { + const fetch = require('node-fetch'); + + console.log('\n=== DEBUG VISIBILITÉ DLC EMPLOYÉ ==='); + + // 1. Login admin pour test de contrôle + console.log('1. Test admin...'); + const adminLogin = await fetch('http://localhost:5000/api/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username: 'admin', password: 'admin' }) + }); + + if (!adminLogin.ok) { + console.log('❌ Admin login failed'); + return; + } + + const adminCookies = adminLogin.headers.get('set-cookie'); + + // 2. Créer une DLC avec admin + console.log('2. Création DLC par admin...'); + const dlcData = { + productName: 'Test DLC Visibilité Admin', + dlcDate: '2025-08-20', + dateType: 'DLC', + quantity: 1, + unit: 'Unité', + supplierId: 1, + location: 'Test Rayon', + groupId: 2, // Groupe Houdemont (même que l'employé) + status: 'en_cours' + }; + + const createDlc = await fetch('http://localhost:5000/api/dlc-products', { + method: 'POST', + headers: { + 'Cookie': adminCookies, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(dlcData) + }); + + console.log('Status création:', createDlc.status); + + if (createDlc.ok) { + const newDlc = await createDlc.json(); + console.log('✅ DLC créée:', { + id: newDlc.id, + productName: newDlc.productName, + groupId: newDlc.groupId + }); + } + + // 3. Vérifier la liste complète DLC + console.log('\n3. Vérification liste DLC...'); + const dlcList = await fetch('http://localhost:5000/api/dlc-products', { + headers: { 'Cookie': adminCookies } + }); + + if (dlcList.ok) { + const dlcs = await dlcList.json(); + console.log('📋 Total DLC:', dlcs.length); + + // Filtrer par groupe 2 (Houdemont) + const group2Dlcs = dlcs.filter(d => d.groupId === 2); + console.log('🏪 DLC Groupe 2 (Houdemont):', group2Dlcs.length); + + // Dernières DLC créées + const recentDlcs = dlcs.slice(0, 5); + console.log('🆕 5 dernières DLC:'); + recentDlcs.forEach(dlc => { + console.log(` - ID: ${dlc.id}, Nom: ${dlc.productName}, Groupe: ${dlc.groupId}`); + }); + } + + console.log('\n4. Instructions pour test employé production:'); + console.log('- Se connecter avec compte employé'); + console.log('- Aller sur page DLC Products'); + console.log('- Vérifier si les DLC du groupe 2 sont visibles'); + console.log('- Regarder dans les outils dev si la requête API retourne des données'); + console.log('- Vérifier le cache React Query dans les dev tools'); + + console.log('\n🔍 Points à vérifier:'); + console.log('1. Cache React Query invalidé après création ?'); + console.log('2. Filtre groupId correct dans la requête ?'); + console.log('3. État de loading/error du composant ?'); + console.log('4. Données reçues mais pas affichées ?'); + + } catch (error) { + console.log('❌ Erreur debug:', error.message); + } +} + +debugDlcVisibility(); \ No newline at end of file diff --git a/fix-dlc-cache-invalidation.js b/fix-dlc-cache-invalidation.js new file mode 100644 index 0000000..f3b086f --- /dev/null +++ b/fix-dlc-cache-invalidation.js @@ -0,0 +1,70 @@ +#!/usr/bin/env node + +// Fix pour invalidation cache DLC - Test avec curl direct + +const testDlcEndpoint = async () => { + const { execSync } = require('child_process'); + + console.log('🔍 Test Direct API DLC Products'); + + try { + // 1. Login admin + console.log('1. Login admin...'); + const loginResult = execSync(`curl -X POST http://localhost:5000/api/login \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"admin"}' \ + -c cookies.txt \ + -w "%{http_code}" \ + -s`, { encoding: 'utf8' }); + + console.log('Login status:', loginResult.slice(-3)); + + // 2. Test GET DLC products + console.log('2. GET DLC products...'); + const dlcResult = execSync(`curl -X GET http://localhost:5000/api/dlc-products \ + -b cookies.txt \ + -w "%{http_code}" \ + -s`, { encoding: 'utf8' }); + + const statusCode = dlcResult.slice(-3); + const responseBody = dlcResult.slice(0, -3); + + console.log('Status:', statusCode); + + if (statusCode === '200') { + const dlcs = JSON.parse(responseBody); + console.log('✅ Total DLC:', dlcs.length); + + if (dlcs.length > 0) { + console.log('📋 Sample DLC:', { + id: dlcs[0].id, + productName: dlcs[0].productName, + groupId: dlcs[0].groupId, + status: dlcs[0].status + }); + + // Grouper par groupId + const byGroup = dlcs.reduce((acc, dlc) => { + acc[dlc.groupId] = (acc[dlc.groupId] || 0) + 1; + return acc; + }, {}); + + console.log('📊 DLC par groupe:', byGroup); + } + } else { + console.log('❌ Erreur:', responseBody); + } + + console.log('\n3. Instructions debug frontend:'); + console.log('- Ouvrir DevTools Network'); + console.log('- Recharger page DLC Products'); + console.log('- Vérifier si requête API est envoyée'); + console.log('- Vérifier la réponse de l\'API'); + console.log('- Vérifier React Query cache dans Components'); + + } catch (error) { + console.log('❌ Erreur:', error.message); + } +}; + +testDlcEndpoint(); \ No newline at end of file diff --git a/server/routes.ts b/server/routes.ts index 600961f..06bb077 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -2319,12 +2319,21 @@ RÉSUMÉ DU SCAN userGroups: user.userGroups?.map(ug => ({groupId: ug.group.id, groupName: ug.group.name})) }); + console.log("🔍 Pre-validation data:", { + body: req.body, + userId, + combined: { ...req.body, createdBy: userId } + }); + const validatedData = insertDlcProductFrontendSchema.parse({ ...req.body, createdBy: userId, }); + console.log("✅ Post-validation data:", validatedData); + const dlcProduct = await storage.createDlcProduct(validatedData); + console.log("📦 Raw returned DLC product:", dlcProduct); console.log('✅ DLC Product created successfully:', { id: dlcProduct.id, productName: dlcProduct.productName, diff --git a/server/storage.ts b/server/storage.ts index f5492cd..c87e6be 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -2014,6 +2014,8 @@ class MemStorage implements IStorage { private tasks = new Map(); private publicities = new Map(); private publicityParticipations = new Array(); + private dlcProducts = new Map(); + private dlcIdCounter = 1; constructor() { // Create default admin user for development @@ -2257,11 +2259,62 @@ class MemStorage implements IStorage { async createCustomerOrder(): Promise { return {}; } async updateCustomerOrder(): Promise { return {}; } async deleteCustomerOrder(): Promise {} - async getDlcProducts(): Promise { return []; } - async getDlcProduct(): Promise { return undefined; } - async createDlcProduct(): Promise { return {}; } - async updateDlcProduct(): Promise { return {}; } - async deleteDlcProduct(): Promise {} + async getDlcProducts(groupIds?: number[], filters?: { status?: string; supplierId?: number; }): Promise { + console.log("📋 MemStorage getDlcProducts:", { groupIds, dlcCount: this.dlcProducts.size }); + let products = Array.from(this.dlcProducts.values()); + + // Filter by groups if provided + if (groupIds && groupIds.length > 0) { + products = products.filter(p => groupIds.includes(p.groupId)); + console.log("🔍 Filtered by groups:", { original: this.dlcProducts.size, filtered: products.length, groupIds }); + } + + // Filter by status + if (filters?.status) { + products = products.filter(p => p.status === filters.status); + } + + // Filter by supplier + if (filters?.supplierId) { + products = products.filter(p => p.supplierId === filters.supplierId); + } + + return products; + } + + async getDlcProduct(id: number): Promise { + return this.dlcProducts.get(id); + } + + async createDlcProduct(dlcProduct: any): Promise { + const id = this.dlcIdCounter++; + const newProduct = { + id, + ...dlcProduct, + createdAt: new Date(), + updatedAt: new Date(), + }; + this.dlcProducts.set(id, newProduct); + console.log("✅ MemStorage DLC créé:", { id, productName: newProduct.productName, groupId: newProduct.groupId }); + return newProduct; + } + + async updateDlcProduct(id: number, dlcProduct: any): Promise { + const existing = this.dlcProducts.get(id); + if (!existing) throw new Error("DLC product not found"); + + const updated = { + ...existing, + ...dlcProduct, + updatedAt: new Date(), + }; + this.dlcProducts.set(id, updated); + return updated; + } + + async deleteDlcProduct(id: number): Promise { + this.dlcProducts.delete(id); + } async getTasks(): Promise { return Array.from(this.tasks.values()); } async getTask(): Promise { return undefined; } async createTask(): Promise { return {}; } @@ -2295,7 +2348,26 @@ class MemStorage implements IStorage { async getUserEffectivePermissions(): Promise { return []; } async userHasPermission(): Promise { return false; } async userHasRole(): Promise { return false; } - async getDlcStats(): Promise { return {}; } + async getDlcStats(groupIds?: number[]): Promise { + const products = Array.from(this.dlcProducts.values()); + + // Filter by groups if provided + const filteredProducts = groupIds && groupIds.length > 0 + ? products.filter(p => groupIds.includes(p.groupId)) + : products; + + const now = new Date(); + const in15Days = new Date(now.getTime() + 15 * 24 * 60 * 60 * 1000); + + const stats = { + active: filteredProducts.filter(p => p.status !== 'valides' && new Date(p.dlcDate) > in15Days).length, + expiringSoon: filteredProducts.filter(p => p.status !== 'valides' && new Date(p.dlcDate) <= in15Days && new Date(p.dlcDate) > now).length, + expired: filteredProducts.filter(p => p.status !== 'valides' && new Date(p.dlcDate) <= now).length, + }; + + console.log("📊 MemStorage DLC stats:", stats); + return stats; + } async validateDlcProduct(id: number, validatedBy: string): Promise { // MemStorage stub implementation - return a basic DlcProductFrontend object return {