diff --git a/cookies.txt b/cookies.txt index 0feb356..02f39c6 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 1755334812 connect.sid s%3AIUsQcAbYjJ2Yu9GRwhXwQt22rGvvMGRo.zTBgX64O5zq1nh16Yc0umuKKQnBAy0xAn4BkA5iErOQ +#HttpOnly_localhost FALSE / FALSE 1755415501 connect.sid s%3Akq9rv9dYrk7ObCvKdTPAwZUL7UaWGfqL.x5cl2uDVboBhiWzQE1pp4349NXCs5x%2ByI8OHzfVl8%2Bw diff --git a/cookies_manager1.txt b/cookies_manager1.txt new file mode 100644 index 0000000..14a2b42 --- /dev/null +++ b/cookies_manager1.txt @@ -0,0 +1,5 @@ +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + +#HttpOnly_localhost FALSE / FALSE 1755415612 connect.sid s%3ALA_C3VgRvKbgl1oG2suDWpO0NfwuqOpQ.%2B1GRLR0jeREW8LP6YP5YNnQJG0UtzOoYylg2R3Vf4Hg diff --git a/cookies_manager2.txt b/cookies_manager2.txt new file mode 100644 index 0000000..d56163c --- /dev/null +++ b/cookies_manager2.txt @@ -0,0 +1,5 @@ +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + +#HttpOnly_localhost FALSE / FALSE 1755415612 connect.sid s%3ARzj7n77KbzyUgygzi1pqOCY7ToJBSeT7.A2HiKInsuth99WAyYy%2Bc2ZnZePEr3rtLet6qccJCXN0 diff --git a/server/localAuth.ts b/server/localAuth.ts index 69f4aa1..34dfab4 100644 --- a/server/localAuth.ts +++ b/server/localAuth.ts @@ -25,11 +25,19 @@ export async function hashPassword(password: string) { async function comparePasswords(supplied: string, stored: string) { console.log('🔐 comparePasswords:', { supplied: 'HIDDEN', stored: stored?.substring(0, 20) + '...', hasFormat: stored?.includes('.') }); - // Simple password comparison for development (admin.salt format) + // Simple password comparison for development users if (stored === 'admin.salt' && supplied === 'admin') { console.log('🔐 Development auth: admin password accepted'); return true; } + if (stored === 'manager1.salt' && supplied === 'manager1') { + console.log('🔐 Development auth: manager1 password accepted'); + return true; + } + if (stored === 'manager2.salt' && supplied === 'manager2') { + console.log('🔐 Development auth: manager2 password accepted'); + return true; + } // Vérifier le format du mot de passe stocké pour production if (!stored || !stored.includes('.')) { diff --git a/server/routes.ts b/server/routes.ts index 4bf8ca1..4e06e67 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -1395,6 +1395,75 @@ export async function registerRoutes(app: Express): Promise { } }); + // DEBUG ROUTE for production diagnosis + app.get('/api/debug/customer-orders-raw', isAuthenticated, async (req: any, res) => { + try { + const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id); + if (!user || user.role !== 'admin') { + return res.status(403).json({ message: "Admin access required" }); + } + + console.log('🔍 [DEBUG] Raw customer orders analysis started'); + + // Get all customer orders without filtering + const allOrders = await storage.getCustomerOrders(); + console.log('🔍 [DEBUG] Total orders in DB:', allOrders.length); + + // Group by store and analyze dates + const analysis = allOrders.reduce((acc, order) => { + const groupId = order.groupId; + const groupName = order.group?.name || 'Unknown'; + + if (!acc[groupId]) { + acc[groupId] = { + groupId, + groupName, + orders: [], + count: 0, + latestDate: null, + oldestDate: null + }; + } + + acc[groupId].orders.push({ + id: order.id, + customerName: order.customerName, + createdAt: order.createdAt, + status: order.status + }); + acc[groupId].count++; + + const orderDate = new Date(order.createdAt); + if (!acc[groupId].latestDate || orderDate > new Date(acc[groupId].latestDate)) { + acc[groupId].latestDate = order.createdAt; + } + if (!acc[groupId].oldestDate || orderDate < new Date(acc[groupId].oldestDate)) { + acc[groupId].oldestDate = order.createdAt; + } + + return acc; + }, {} as Record); + + console.log('🔍 [DEBUG] Analysis by group:', analysis); + + res.json({ + totalOrders: allOrders.length, + analysis: Object.values(analysis), + sampleOrders: allOrders.slice(0, 10).map(order => ({ + id: order.id, + groupId: order.groupId, + groupName: order.group?.name, + customerName: order.customerName, + createdAt: order.createdAt, + status: order.status + })) + }); + } catch (error) { + console.error("Error in debug route:", error); + res.status(500).json({ error: error.message }); + } + }); + // Customer Orders routes app.get('/api/customer-orders', isAuthenticated, async (req: any, res) => { try { @@ -1417,9 +1486,31 @@ export async function registerRoutes(app: Express): Promise { } } - console.log('Customer Orders API called with:', { groupIds, userRole: user.role }); + console.log('🔍 [PRODUCTION DEBUG] Customer Orders API called with:', { + groupIds, + userRole: user.role, + userId: user.id, + userGroups: user.userGroups?.map(ug => ({ groupId: ug.groupId, groupName: ug.group?.name })) + }); const customerOrders = await storage.getCustomerOrders(groupIds); - console.log('Customer Orders returned:', customerOrders.length, 'items'); + console.log('🔍 [PRODUCTION DEBUG] Customer Orders returned:', customerOrders.length, 'items'); + + // Debug: Group orders by groupId to see distribution + const ordersByGroup = customerOrders.reduce((acc, order) => { + const groupId = order.groupId || 'null'; + if (!acc[groupId]) acc[groupId] = []; + acc[groupId].push(order); + return acc; + }, {} as Record); + + console.log('🔍 [PRODUCTION DEBUG] Orders distribution by group:', + Object.entries(ordersByGroup).map(([groupId, orders]) => ({ + groupId, + count: orders.length, + latestDate: orders[0]?.createdAt, + groupName: orders[0]?.group?.name + })) + ); res.json(customerOrders); } catch (error) { console.error("Error fetching customer orders:", error); diff --git a/server/storage.ts b/server/storage.ts index 8440673..bbec28c 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -1324,6 +1324,8 @@ export class DatabaseStorage implements IStorage { // Real implementations for production operations async getCustomerOrders(groupIds?: number[]): Promise { + console.log('🔍 [PRODUCTION DEBUG] getCustomerOrders called with groupIds:', groupIds); + let query = db .select({ customerOrder: customerOrders, @@ -1336,15 +1338,34 @@ export class DatabaseStorage implements IStorage { if (groupIds && groupIds.length > 0) { query = query.where(inArray(customerOrders.groupId, groupIds)); + console.log('🔍 [PRODUCTION DEBUG] Filtering by groupIds:', groupIds); } - const results = await query.orderBy(desc(customerOrders.createdAt)); - return results.map((row: any) => ({ - ...row.customerOrder, - supplier: row.supplier, - group: row.group, - creator: row.creator || { id: row.customerOrder.createdBy, username: 'unknown' } - })); + try { + const results = await query.orderBy(desc(customerOrders.createdAt)); + console.log('🔍 [PRODUCTION DEBUG] Raw SQL results count:', results.length); + + // Debug: Log some sample data + if (results.length > 0) { + console.log('🔍 [PRODUCTION DEBUG] Sample results (first 3):'); + results.slice(0, 3).forEach((row, idx) => { + console.log(` [${idx}] Order ID: ${row.customerOrder?.id}, GroupId: ${row.customerOrder?.groupId}, Customer: ${row.customerOrder?.customerName}, Created: ${row.customerOrder?.createdAt}`); + }); + } + + const mappedResults = results.map((row: any) => ({ + ...row.customerOrder, + supplier: row.supplier, + group: row.group, + creator: row.creator || { id: row.customerOrder.createdBy, username: 'unknown' } + })); + + console.log('🔍 [PRODUCTION DEBUG] Final mapped results count:', mappedResults.length); + return mappedResults; + } catch (error) { + console.error('❌ [PRODUCTION ERROR] getCustomerOrders failed:', error); + throw error; + } } async getCustomerOrder(id: number): Promise { @@ -1863,6 +1884,7 @@ export class MemStorage implements IStorage { private tasks = new Map(); private savTickets = new Map(); private savTicketHistories = new Map(); + private passwords = new Map(); private idCounters = { group: 1, @@ -1892,13 +1914,14 @@ export class MemStorage implements IStorage { firstName: 'Admin', lastName: 'User', profileImageUrl: null, - password: 'admin', // In real app, this would be hashed + password: 'admin.salt', // Development format with salt role: 'admin', passwordChanged: false, createdAt: new Date(), updatedAt: new Date(), }; this.users.set(adminUser.id, adminUser); + this.passwords.set('admin', { password: 'admin.salt', hasFormat: true }); // Initialize group counter this.idCounters.group = 2; @@ -1907,39 +1930,124 @@ export class MemStorage implements IStorage { this.idCounters.supplier = 2; // Create test groups - const testGroup: Group = { + const testGroup1: Group = { id: 1, - name: 'Magasin Test', + name: 'Magasin 1', color: '#3B82F6', nocodbConfigId: 1, // Référence vers la config NocoDB de test - nocodbTableName: 'invoices', + nocodbTableName: 'invoices_store1', nocodbTableId: 'mrr733dfb8wtt9b', // ID réel de la table NocoDB invoiceColumnName: 'invoice_reference', nocodbBlColumnName: 'bl_number', nocodbAmountColumnName: 'amount', nocodbSupplierColumnName: 'supplier', - webhookUrl: 'https://webhook.test/invoice-upload', + webhookUrl: 'https://webhook.test/invoice-upload-store1', createdAt: new Date(), updatedAt: new Date(), }; - this.groups.set(testGroup.id, testGroup); - this.idCounters.group = 2; + + const testGroup2: Group = { + id: 2, + name: 'Magasin 2', + color: '#10B981', + nocodbConfigId: 1, // Même config NocoDB + nocodbTableName: 'invoices_store2', + nocodbTableId: 'store2table123', // Différent ID de table + invoiceColumnName: 'invoice_reference', + nocodbBlColumnName: 'bl_number', + nocodbAmountColumnName: 'amount', + nocodbSupplierColumnName: 'supplier', + webhookUrl: 'https://webhook.test/invoice-upload-store2', + createdAt: new Date(), + updatedAt: new Date(), + }; + + this.groups.set(testGroup1.id, testGroup1); + this.groups.set(testGroup2.id, testGroup2); + this.idCounters.group = 3; - // Create test supplier - const testSupplier: Supplier = { + // Create test suppliers + const testSupplier1: Supplier = { id: 1, - name: 'Fournisseur Test', - contact: 'test@supplier.com', + name: 'Fournisseur Magasin 1', + contact: 'magasin1@supplier.com', phone: '0123456789', - hasDlc: false, + hasDlc: true, + automaticReconciliation: true, + createdAt: new Date(), + updatedAt: new Date(), + }; + + const testSupplier2: Supplier = { + id: 2, + name: 'Fournisseur Magasin 2', + contact: 'magasin2@supplier.com', + phone: '0987654321', + hasDlc: true, automaticReconciliation: false, createdAt: new Date(), updatedAt: new Date(), }; - this.suppliers.set(testSupplier.id, testSupplier); - this.idCounters.supplier = 2; + + this.suppliers.set(testSupplier1.id, testSupplier1); + this.suppliers.set(testSupplier2.id, testSupplier2); + this.idCounters.supplier = 3; + + // Create additional test users for different stores + const manager1User: User = { + id: 'manager1', + username: 'manager1', + email: 'manager1@magasin1.com', + name: 'Manager Magasin 1', + firstName: 'Manager', + lastName: 'Magasin 1', + profileImageUrl: null, + password: 'manager1.salt', + role: 'manager', + passwordChanged: false, + createdAt: new Date(), + updatedAt: new Date(), + }; + this.users.set(manager1User.id, manager1User); + this.passwords.set('manager1', { password: 'manager1.salt', hasFormat: true }); + + const manager2User: User = { + id: 'manager2', + username: 'manager2', + email: 'manager2@magasin2.com', + name: 'Manager Magasin 2', + firstName: 'Manager', + lastName: 'Magasin 2', + profileImageUrl: null, + password: 'manager2.salt', + role: 'manager', + passwordChanged: false, + createdAt: new Date(), + updatedAt: new Date(), + }; + this.users.set(manager2User.id, manager2User); + this.passwords.set('manager2', { password: 'manager2.salt', hasFormat: true }); + + // Set up user-group associations + this.userGroups.set('admin', [ + { userId: 'admin', groupId: 1 }, + { userId: 'admin', groupId: 2 } + ]); + + this.userGroups.set('manager1', [ + { userId: 'manager1', groupId: 1 } + ]); + + this.userGroups.set('manager2', [ + { userId: 'manager2', groupId: 2 } + ]); console.log('✅ MemStorage initialized with admin user (admin/admin)'); + console.log('🔧 Test users created:', { + admin: 'Access to both stores', + manager1: 'Access to Magasin 1 only', + manager2: 'Access to Magasin 2 only' + }); } // User operations @@ -2014,6 +2122,16 @@ export class MemStorage implements IStorage { async deleteUser(id: string): Promise { this.users.delete(id); this.userGroups.delete(id); + this.passwords.delete(id); + } + + async validatePassword(userId: string, password: string): Promise { + const storedPassword = this.passwords.get(userId); + if (!storedPassword) return false; + + // For development, we use simple string comparison + // In production, this would use bcrypt comparison + return storedPassword.password === password; } // Group operations @@ -2683,7 +2801,9 @@ export class MemStorage implements IStorage { } private initializeExtraTestData() { - // Add test customer orders + console.log('🔧 Initialisation des données de test des commandes clients...'); + + // Add test customer orders for Store 1 (groupId: 1) - Recent dates const testOrder1: CustomerOrder = { id: 1, supplierId: 1, @@ -2702,8 +2822,8 @@ export class MemStorage implements IStorage { isPromotionalPrice: false, customerNotified: false, notes: 'Livraison urgente demandée', - createdAt: new Date('2025-01-15'), - updatedAt: new Date('2025-01-15'), + createdAt: new Date('2025-08-15'), + updatedAt: new Date('2025-08-15'), }; const testOrder2: CustomerOrder = { @@ -2724,13 +2844,114 @@ export class MemStorage implements IStorage { isPromotionalPrice: true, customerNotified: true, notes: null, - createdAt: new Date('2025-01-10'), - updatedAt: new Date('2025-01-12'), + createdAt: new Date('2025-08-10'), + updatedAt: new Date('2025-08-12'), + }; + + // Add test customer orders for Store 2 (groupId: 2) - Recent dates + const testOrder3: CustomerOrder = { + id: 3, + supplierId: 2, + groupId: 2, + createdBy: 'admin', + orderTaker: 'Manager Store 2', + customerName: 'Jean Durand', + customerPhone: '0147258369', + customerEmail: 'jean.durand@email.fr', + productDesignation: 'Canapé 3 places', + productReference: 'CAN-001', + gencode: null, + quantity: 1, + deposit: '300.00' as any, + status: 'Commande passée', + isPromotionalPrice: false, + customerNotified: true, + notes: 'Client préfère livraison matin', + createdAt: new Date('2025-08-14'), + updatedAt: new Date('2025-08-14'), + }; + + const testOrder4: CustomerOrder = { + id: 4, + supplierId: 2, + groupId: 2, + createdBy: 'admin', + orderTaker: 'Manager Store 2', + customerName: 'Sophie Moreau', + customerPhone: '0612345678', + customerEmail: null, + productDesignation: 'Étagère murale', + productReference: null, + gencode: null, + quantity: 3, + deposit: '75.00' as any, + status: 'En attente de Commande', + isPromotionalPrice: true, + customerNotified: false, + notes: 'Prix promotionnel -20%', + createdAt: new Date('2025-08-16'), + updatedAt: new Date('2025-08-16'), + }; + + // Add more recent orders for Store 1 to show variety + const testOrder5: CustomerOrder = { + id: 5, + supplierId: 1, + groupId: 1, + createdBy: 'admin', + orderTaker: 'Admin', + customerName: 'Michel Blanc', + customerPhone: '0188776655', + customerEmail: 'michel.blanc@test.fr', + productDesignation: 'Set de jardin complet', + productReference: 'JARDIN-SET-01', + gencode: null, + quantity: 1, + deposit: '250.00' as any, + status: 'Livrée', + isPromotionalPrice: false, + customerNotified: true, + notes: 'Commande livrée avec succès', + createdAt: new Date('2025-08-09'), + updatedAt: new Date('2025-08-11'), + }; + + const testOrder6: CustomerOrder = { + id: 6, + supplierId: 1, + groupId: 1, + createdBy: 'admin', + orderTaker: 'Admin', + customerName: 'Isabelle Roux', + customerPhone: '0199887766', + customerEmail: null, + productDesignation: 'Barbecue électrique', + productReference: null, + gencode: null, + quantity: 1, + deposit: '120.00' as any, + status: 'En attente de Commande', + isPromotionalPrice: false, + customerNotified: false, + notes: 'À confirmer avec le client', + createdAt: new Date('2025-08-16'), + updatedAt: new Date('2025-08-16'), }; this.customerOrders.set(1, testOrder1); this.customerOrders.set(2, testOrder2); - this.idCounters.customerOrder = 3; + this.customerOrders.set(3, testOrder3); + this.customerOrders.set(4, testOrder4); + this.customerOrders.set(5, testOrder5); + this.customerOrders.set(6, testOrder6); + this.idCounters.customerOrder = 7; + + console.log('✅ Données de test initialisées:', { + totalOrders: 6, + store1Orders: [1, 2, 5, 6], + store2Orders: [3, 4], + recentDates: '2025-08-09 à 2025-08-16' + }); // Add test DLC products const testDlc1: DlcProduct = {