Checkpoint: Bouton Relancer corrigé (automatismes uniquement, sans LLM). Système d'apprentissage complet : table invoiceLearnings, routes tRPC, déclenchement depuis InvoiceDetail, application lors des imports, page LearningSettings dans Configuration > Apprentissages IA.
This commit is contained in:
82
server/db.ts
82
server/db.ts
@@ -41,7 +41,10 @@ import {
|
||||
ServiceSignature,
|
||||
bapHistory,
|
||||
InsertBapHistory,
|
||||
BapHistory
|
||||
BapHistory,
|
||||
invoiceLearnings,
|
||||
InsertInvoiceLearning,
|
||||
InvoiceLearning
|
||||
} from "../drizzle/schema";
|
||||
import { ENV } from './_core/env';
|
||||
|
||||
@@ -811,3 +814,80 @@ export async function deleteBapHistoryEntry(id: number): Promise<void> {
|
||||
if (!db) return;
|
||||
await db.delete(bapHistory).where(eq(bapHistory.id, id));
|
||||
}
|
||||
|
||||
// ── Invoice Learnings (corrections manuelles apprises) ────────────────────────
|
||||
|
||||
/** Normalise le nom du fournisseur pour la clé de correspondance */
|
||||
export function normalizeSupplierId(supplierName: string): string {
|
||||
return supplierName.trim().toLowerCase().replace(/\s+/g, ' ');
|
||||
}
|
||||
|
||||
/** Récupère tous les apprentissages d'un utilisateur */
|
||||
export async function getLearningsByUser(userId: number): Promise<InvoiceLearning[]> {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
return db.select().from(invoiceLearnings).where(eq(invoiceLearnings.userId, userId));
|
||||
}
|
||||
|
||||
/** Récupère les apprentissages pour un fournisseur donné */
|
||||
export async function getLearningsBySupplier(userId: number, supplierName: string): Promise<InvoiceLearning[]> {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
const key = normalizeSupplierId(supplierName);
|
||||
return db.select().from(invoiceLearnings).where(
|
||||
and(eq(invoiceLearnings.userId, userId), eq(invoiceLearnings.supplierKey, key))
|
||||
);
|
||||
}
|
||||
|
||||
/** Enregistre ou met à jour un apprentissage (upsert par userId + supplierKey + fieldName) */
|
||||
export async function upsertLearning(data: {
|
||||
userId: number;
|
||||
supplierName: string;
|
||||
fieldName: string;
|
||||
originalValue?: string;
|
||||
correctedValue: string;
|
||||
}): Promise<void> {
|
||||
const db = await getDb();
|
||||
if (!db) return;
|
||||
const supplierKey = normalizeSupplierId(data.supplierName);
|
||||
// Chercher si une entrée existe déjà
|
||||
const existing = await db.select().from(invoiceLearnings).where(
|
||||
and(
|
||||
eq(invoiceLearnings.userId, data.userId),
|
||||
eq(invoiceLearnings.supplierKey, supplierKey),
|
||||
eq(invoiceLearnings.fieldName, data.fieldName)
|
||||
)
|
||||
);
|
||||
if (existing.length > 0) {
|
||||
await db.update(invoiceLearnings)
|
||||
.set({
|
||||
correctedValue: data.correctedValue,
|
||||
originalValue: data.originalValue ?? existing[0].originalValue,
|
||||
applyCount: (existing[0].applyCount || 1) + 1,
|
||||
})
|
||||
.where(eq(invoiceLearnings.id, existing[0].id));
|
||||
} else {
|
||||
await db.insert(invoiceLearnings).values({
|
||||
userId: data.userId,
|
||||
supplierKey,
|
||||
fieldName: data.fieldName,
|
||||
originalValue: data.originalValue,
|
||||
correctedValue: data.correctedValue,
|
||||
applyCount: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Supprime un apprentissage par ID */
|
||||
export async function deleteLearning(id: number): Promise<void> {
|
||||
const db = await getDb();
|
||||
if (!db) return;
|
||||
await db.delete(invoiceLearnings).where(eq(invoiceLearnings.id, id));
|
||||
}
|
||||
|
||||
/** Supprime tous les apprentissages d'un utilisateur */
|
||||
export async function deleteAllLearnings(userId: number): Promise<void> {
|
||||
const db = await getDb();
|
||||
if (!db) return;
|
||||
await db.delete(invoiceLearnings).where(eq(invoiceLearnings.userId, userId));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user