mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-23 15:08:22 +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;
|
invoiceReference?: string;
|
||||||
invoiceAmount?: number;
|
invoiceAmount?: number;
|
||||||
supplierName?: string;
|
supplierName?: string;
|
||||||
|
dueDate?: string | null;
|
||||||
fromCache?: boolean;
|
fromCache?: boolean;
|
||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
@@ -396,7 +397,8 @@ export class InvoiceVerificationService {
|
|||||||
matchType: 'invoice_reference' as const,
|
matchType: 'invoice_reference' as const,
|
||||||
invoiceReference: matchResult.data.invoice_reference || invoiceReference,
|
invoiceReference: matchResult.data.invoice_reference || invoiceReference,
|
||||||
invoiceAmount: parseFloat(matchResult.data[group.nocodbAmountColumnName || 'amount'] || '0'),
|
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
|
// Sauvegarder en cache si succès
|
||||||
@@ -432,7 +434,8 @@ export class InvoiceVerificationService {
|
|||||||
matchType: 'bl_number' as const,
|
matchType: 'bl_number' as const,
|
||||||
invoiceReference: matchResult.data.invoice_reference || `BL_${invoiceReference}`,
|
invoiceReference: matchResult.data.invoice_reference || `BL_${invoiceReference}`,
|
||||||
invoiceAmount: parseFloat(matchResult.data[group.nocodbAmountColumnName || 'amount'] || '0'),
|
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
|
// Sauvegarder en cache si succès
|
||||||
@@ -599,6 +602,7 @@ export class InvoiceVerificationService {
|
|||||||
invoiceReference?: string;
|
invoiceReference?: string;
|
||||||
invoiceAmount?: number;
|
invoiceAmount?: number;
|
||||||
supplierName?: string;
|
supplierName?: string;
|
||||||
|
dueDate?: string | null;
|
||||||
fromCache?: boolean;
|
fromCache?: boolean;
|
||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
@@ -795,7 +799,8 @@ export class InvoiceVerificationService {
|
|||||||
matchType: 'bl_number' as const,
|
matchType: 'bl_number' as const,
|
||||||
invoiceReference: matchResult.data[group.invoiceColumnName || 'RefFacture'] || `BL_${blNumber}`,
|
invoiceReference: matchResult.data[group.invoiceColumnName || 'RefFacture'] || `BL_${blNumber}`,
|
||||||
invoiceAmount: parseFloat(matchResult.data[group.nocodbAmountColumnName || 'amount'] || '0'),
|
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
|
// 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);
|
const group = await storage.getGroup(groupId);
|
||||||
if (!group || !group.nocodbConfigId || !group.nocodbTableId) {
|
if (!group) {
|
||||||
return res.json({ schedules: [], message: 'Pas de configuration NocoDB pour ce groupe' });
|
return res.json({ schedules: [], message: 'Groupe non trouvé' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Récupérer la configuration NocoDB
|
console.log('📅 Récupération échéances depuis deliveries:', { groupId, groupName: group.name });
|
||||||
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
|
// Récupérer toutes les livraisons du groupe avec échéance
|
||||||
if (!group.nocodbDueDateColumnName || !group.invoiceColumnName || !group.nocodbAmountColumnName) {
|
const allDeliveries = await storage.getDeliveries();
|
||||||
return res.json({
|
const deliveriesWithDueDate = allDeliveries.filter(
|
||||||
schedules: [],
|
(d: any) => d.groupId === groupId && d.dueDate && d.invoiceReference
|
||||||
message: 'Configuration incomplète: colonnes date échéance, facture ou montant manquantes'
|
);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Récupérer tous les enregistrements de NocoDB
|
console.log(`📅 Trouvé ${deliveriesWithDueDate.length} livraisons avec échéance pour le groupe ${groupId}`);
|
||||||
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, {
|
// Récupérer tous les fournisseurs pour le mapping du mode de paiement
|
||||||
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();
|
const allSuppliers = await storage.getSuppliers();
|
||||||
|
const supplierMap = new Map(allSuppliers.map((s: any) => [s.id, s]));
|
||||||
// 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
|
// Formatter les échéances
|
||||||
const schedules = (data.list || [])
|
const schedules = deliveriesWithDueDate.map((delivery: any) => {
|
||||||
.map((record: any) => {
|
const supplier = supplierMap.get(delivery.supplierId);
|
||||||
const dueDate = record[group.nocodbDueDateColumnName!];
|
|
||||||
const invoiceRef = record[group.invoiceColumnName!];
|
return {
|
||||||
const amount = record[group.nocodbAmountColumnName!];
|
id: delivery.id,
|
||||||
const supplierName = record[group.nocodbSupplierColumnName || 'Fournisseur'];
|
invoiceReference: delivery.invoiceReference,
|
||||||
|
dueDate: delivery.dueDate,
|
||||||
if (!dueDate) return null;
|
amount: delivery.invoiceAmount ? parseFloat(delivery.invoiceAmount) : 0,
|
||||||
|
supplierName: supplier?.name || 'Fournisseur inconnu',
|
||||||
// Chercher le fournisseur correspondant avec normalisation
|
paymentMethod: supplier?.paymentMethod || null,
|
||||||
const normalizedSupplierName = normalizeName(supplierName || '');
|
groupId: group.id,
|
||||||
const supplier = supplierMap.get(normalizedSupplierName);
|
groupName: group.name
|
||||||
|
};
|
||||||
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 });
|
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);
|
const updatedDelivery = await storage.updateDelivery(id, data);
|
||||||
console.log('✅ Delivery updated successfully:', { id, updatedDelivery });
|
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
|
blAmount: decimal("bl_amount", { precision: 10, scale: 2 }), // Montant BL
|
||||||
invoiceReference: varchar("invoice_reference"), // Référence facture
|
invoiceReference: varchar("invoice_reference"), // Référence facture
|
||||||
invoiceAmount: decimal("invoice_amount", { precision: 10, scale: 2 }), // Montant 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é
|
reconciled: boolean("reconciled").default(false), // Rapprochement effectué
|
||||||
validatedAt: timestamp("validated_at"), // Date de validation de la livraison
|
validatedAt: timestamp("validated_at"), // Date de validation de la livraison
|
||||||
// Champs pour le contrôle des livraisons (fournisseurs nécessitant un contrôle)
|
// Champs pour le contrôle des livraisons (fournisseurs nécessitant un contrôle)
|
||||||
|
|||||||
Reference in new issue
Block a user