Add payment schedule view with filtering and supplier payment methods

Adds a new payment schedule page to display due dates and amounts, incorporating supplier payment methods and date filtering.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 1e4433b5-47ab-464c-b663-fea2e53367dd
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: e9c65355-546d-41f7-885b-728c905f4497
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/1e4433b5-47ab-464c-b663-fea2e53367dd/9uIte90
This commit is contained in:
michaelschal committed 2025-11-03 16:02:12 +00:00
1 parent eeb9b5b34b
commit 6f298c4df7
7 files changed
+488 -5

No files matched your search

+126
View File
@@ -278,6 +278,132 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
// Route pour récupérer l'échéancier des paiements fournisseurs
app.get('/api/payment-schedule', isAuthenticated, async (req: any, res) => {
try {
const userId = req.user?.claims?.sub || req.user?.id;
if (!userId) {
return res.status(401).json({ error: 'Utilisateur non authentifié' });
}
const user = await storage.getUser(userId);
if (!user || (user.role !== 'admin' && user.role !== 'directeur')) {
return res.status(403).json({ error: 'Accès refusé - Admin ou Directeur uniquement' });
}
// Validation du groupId avec Zod
const groupIdSchema = z.coerce.number().int().positive();
const validation = groupIdSchema.safeParse(req.query.groupId);
if (!validation.success) {
return res.status(400).json({ error: 'groupId invalide - doit être un entier positif' });
}
const groupId = validation.data;
// Vérifier l'autorisation : directeur ne peut voir que ses groupes
if (user.role === 'directeur') {
const userGroupIds = await storage.getUserGroups(user.id);
if (!userGroupIds.includes(groupId)) {
return res.status(403).json({ error: 'Accès refusé - Vous ne pouvez accéder qu\'aux données de votre groupe' });
}
}
// Récupérer le groupe avec la configuration NocoDB
const group = await storage.getGroup(groupId);
if (!group || !group.nocodbConfigId || !group.nocodbTableId) {
return res.json({ schedules: [], message: 'Pas de configuration NocoDB pour ce groupe' });
}
// Récupérer la configuration NocoDB
const nocodbConfig = await storage.getNocodbConfig(group.nocodbConfigId);
if (!nocodbConfig) {
return res.json({ schedules: [], message: 'Configuration NocoDB non trouvée' });
}
// Vérifier que les colonnes nécessaires sont configurées
if (!group.nocodbDueDateColumnName || !group.invoiceColumnName || !group.nocodbAmountColumnName) {
return res.json({
schedules: [],
message: 'Configuration incomplète: colonnes date échéance, facture ou montant manquantes'
});
}
// Récupérer tous les enregistrements de NocoDB
const url = `${nocodbConfig.baseUrl}/api/v1/db/data/noco/${nocodbConfig.projectId}/${group.nocodbTableId}`;
console.log('📅 Récupération échéances NocoDB:', {
url,
groupId,
dueDateColumn: group.nocodbDueDateColumnName
});
const response = await fetch(url, {
method: "GET",
headers: {
"xc-token": nocodbConfig.apiToken,
"Content-Type": "application/json",
},
});
if (!response.ok) {
console.error('❌ Erreur NocoDB:', response.status, response.statusText);
return res.status(500).json({ error: 'Erreur lors de la récupération des données NocoDB' });
}
const data = await response.json();
// Récupérer tous les fournisseurs pour le mapping
const allSuppliers = await storage.getSuppliers();
// Fonction de normalisation pour comparer les noms de fournisseurs
const normalizeName = (name: string) => {
return name
.toLowerCase()
.trim()
.normalize("NFD") // Décompose les accents
.replace(/[\u0300-\u036f]/g, "") // Supprime les diacritiques
.replace(/\s+/g, ' '); // Normalise les espaces multiples
};
// Créer une map avec noms normalisés
const supplierMap = new Map(allSuppliers.map(s => [normalizeName(s.name), s]));
// Traiter les données et filtrer les enregistrements avec une date d'échéance
const schedules = (data.list || [])
.map((record: any) => {
const dueDate = record[group.nocodbDueDateColumnName!];
const invoiceRef = record[group.invoiceColumnName!];
const amount = record[group.nocodbAmountColumnName!];
const supplierName = record[group.nocodbSupplierColumnName || 'Fournisseur'];
if (!dueDate) return null;
// Chercher le fournisseur correspondant avec normalisation
const normalizedSupplierName = normalizeName(supplierName || '');
const supplier = supplierMap.get(normalizedSupplierName);
return {
id: record.Id || record.id,
invoiceReference: invoiceRef,
dueDate: dueDate,
amount: amount ? parseFloat(amount) : 0,
supplierName: supplierName,
paymentMethod: supplier?.paymentMethod || null,
groupId: group.id,
groupName: group.name
};
})
.filter((s: any) => s !== null);
res.json({ schedules });
} catch (error: any) {
console.error('❌ Erreur récupération échéances:', error);
res.status(500).json({ error: 'Erreur serveur', details: error.message });
}
});
// Route BAP pour envoi webhook n8n
app.post('/api/bap/send-webhook', isAuthenticated, async (req: any, res) => {
try {