mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
Add due date tracking to invoice verification and delivery data
Updates InvoiceVerificationService to include dueDate, modifies routes.ts to fetch deliveries with due dates from storage instead of NocoDB, and adds dueDate to the deliveries schema in shared/schema.ts. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 1e4433b5-47ab-464c-b663-fea2e53367dd Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: 45962ece-1470-4431-936b-016ef39720d8 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/1e4433b5-47ab-464c-b663-fea2e53367dd/G9gVBac
This commit is contained in:
1 parent
84edfbfedd
commit
09c873eb06
3 files changed
+69
-81
No files matched your search
@@ -242,6 +242,7 @@ export class InvoiceVerificationService {
|
||||
invoiceReference?: string;
|
||||
invoiceAmount?: number;
|
||||
supplierName?: string;
|
||||
dueDate?: string | null;
|
||||
fromCache?: boolean;
|
||||
}> {
|
||||
try {
|
||||
@@ -396,7 +397,8 @@ export class InvoiceVerificationService {
|
||||
matchType: 'invoice_reference' as const,
|
||||
invoiceReference: matchResult.data.invoice_reference || invoiceReference,
|
||||
invoiceAmount: parseFloat(matchResult.data[group.nocodbAmountColumnName || 'amount'] || '0'),
|
||||
supplierName: matchResult.data[group.nocodbSupplierColumnName || 'supplier'] || 'Inconnu'
|
||||
supplierName: matchResult.data[group.nocodbSupplierColumnName || 'supplier'] || 'Inconnu',
|
||||
dueDate: matchResult.data[group.nocodbDueDateColumnName || 'Echeance'] || null
|
||||
};
|
||||
|
||||
// Sauvegarder en cache si succès
|
||||
@@ -432,7 +434,8 @@ export class InvoiceVerificationService {
|
||||
matchType: 'bl_number' as const,
|
||||
invoiceReference: matchResult.data.invoice_reference || `BL_${invoiceReference}`,
|
||||
invoiceAmount: parseFloat(matchResult.data[group.nocodbAmountColumnName || 'amount'] || '0'),
|
||||
supplierName: matchResult.data[group.nocodbSupplierColumnName || 'supplier'] || 'Inconnu'
|
||||
supplierName: matchResult.data[group.nocodbSupplierColumnName || 'supplier'] || 'Inconnu',
|
||||
dueDate: matchResult.data[group.nocodbDueDateColumnName || 'Echeance'] || null
|
||||
};
|
||||
|
||||
// Sauvegarder en cache si succès
|
||||
@@ -599,6 +602,7 @@ export class InvoiceVerificationService {
|
||||
invoiceReference?: string;
|
||||
invoiceAmount?: number;
|
||||
supplierName?: string;
|
||||
dueDate?: string | null;
|
||||
fromCache?: boolean;
|
||||
}> {
|
||||
try {
|
||||
@@ -795,7 +799,8 @@ export class InvoiceVerificationService {
|
||||
matchType: 'bl_number' as const,
|
||||
invoiceReference: matchResult.data[group.invoiceColumnName || 'RefFacture'] || `BL_${blNumber}`,
|
||||
invoiceAmount: parseFloat(matchResult.data[group.nocodbAmountColumnName || 'amount'] || '0'),
|
||||
supplierName: foundSupplier || supplierName
|
||||
supplierName: foundSupplier || supplierName,
|
||||
dueDate: matchResult.data[group.nocodbDueDateColumnName || 'Echeance'] || null
|
||||
};
|
||||
|
||||
// Sauvegarder en cache si succès
|
||||
|
||||
+60
-78
@@ -309,92 +309,41 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer le groupe avec la configuration NocoDB
|
||||
// Récupérer le groupe
|
||||
const group = await storage.getGroup(groupId);
|
||||
if (!group || !group.nocodbConfigId || !group.nocodbTableId) {
|
||||
return res.json({ schedules: [], message: 'Pas de configuration NocoDB pour ce groupe' });
|
||||
if (!group) {
|
||||
return res.json({ schedules: [], message: 'Groupe non trouvé' });
|
||||
}
|
||||
|
||||
// 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' });
|
||||
}
|
||||
console.log('📅 Récupération échéances depuis deliveries:', { groupId, groupName: group.name });
|
||||
|
||||
// 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 toutes les livraisons du groupe avec échéance
|
||||
const allDeliveries = await storage.getDeliveries();
|
||||
const deliveriesWithDueDate = allDeliveries.filter(
|
||||
(d: any) => d.groupId === groupId && d.dueDate && d.invoiceReference
|
||||
);
|
||||
|
||||
// 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
|
||||
});
|
||||
console.log(`📅 Trouvé ${deliveriesWithDueDate.length} livraisons avec échéance pour le groupe ${groupId}`);
|
||||
|
||||
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
|
||||
// Récupérer tous les fournisseurs pour le mapping du mode de paiement
|
||||
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]));
|
||||
const supplierMap = new Map(allSuppliers.map((s: any) => [s.id, 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);
|
||||
// Formatter les échéances
|
||||
const schedules = deliveriesWithDueDate.map((delivery: any) => {
|
||||
const supplier = supplierMap.get(delivery.supplierId);
|
||||
|
||||
return {
|
||||
id: delivery.id,
|
||||
invoiceReference: delivery.invoiceReference,
|
||||
dueDate: delivery.dueDate,
|
||||
amount: delivery.invoiceAmount ? parseFloat(delivery.invoiceAmount) : 0,
|
||||
supplierName: supplier?.name || 'Fournisseur inconnu',
|
||||
paymentMethod: supplier?.paymentMethod || null,
|
||||
groupId: group.id,
|
||||
groupName: group.name
|
||||
};
|
||||
});
|
||||
|
||||
res.json({ schedules });
|
||||
|
||||
@@ -1374,6 +1323,39 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
}
|
||||
}
|
||||
|
||||
// GESTION ÉCHÉANCE : Si la référence facture change, mettre à jour la date d'échéance
|
||||
if (data.invoiceReference !== undefined && data.invoiceReference !== delivery.invoiceReference) {
|
||||
if (data.invoiceReference && data.invoiceReference.trim()) {
|
||||
// Nouvelle référence facture : reverifier dans NocoDB pour récupérer l'échéance
|
||||
try {
|
||||
const { InvoiceVerificationService } = await import('./invoiceVerification.js');
|
||||
const verificationService = new InvoiceVerificationService();
|
||||
const result = await verificationService.verifyInvoice(
|
||||
data.invoiceReference,
|
||||
delivery.groupId,
|
||||
true, // forceRefresh
|
||||
delivery.reconciled || false
|
||||
);
|
||||
|
||||
if (result.exists && result.dueDate) {
|
||||
data.dueDate = result.dueDate;
|
||||
console.log(`📅 Date d'échéance récupérée depuis NocoDB: ${result.dueDate}`);
|
||||
} else {
|
||||
data.dueDate = null;
|
||||
console.log(`📅 Aucune date d'échéance trouvée dans NocoDB`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur récupération échéance:', error);
|
||||
// Ne pas bloquer la mise à jour, juste ne pas avoir d'échéance
|
||||
data.dueDate = null;
|
||||
}
|
||||
} else {
|
||||
// Référence facture vidée : vider aussi l'échéance
|
||||
data.dueDate = null;
|
||||
console.log(`📅 Référence facture vidée, échéance également vidée`);
|
||||
}
|
||||
}
|
||||
|
||||
const updatedDelivery = await storage.updateDelivery(id, data);
|
||||
console.log('✅ Delivery updated successfully:', { id, updatedDelivery });
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ export const deliveries = pgTable("deliveries", {
|
||||
blAmount: decimal("bl_amount", { precision: 10, scale: 2 }), // Montant BL
|
||||
invoiceReference: varchar("invoice_reference"), // Référence facture
|
||||
invoiceAmount: decimal("invoice_amount", { precision: 10, scale: 2 }), // Montant facture
|
||||
dueDate: date("due_date"), // Date d'échéance de paiement (récupérée depuis NocoDB)
|
||||
reconciled: boolean("reconciled").default(false), // Rapprochement effectué
|
||||
validatedAt: timestamp("validated_at"), // Date de validation de la livraison
|
||||
// Champs pour le contrôle des livraisons (fournisseurs nécessitant un contrôle)
|
||||
|
||||
Reference in new issue
Block a user