Checkpoint: Ajout de l'onglet "Paramétrage" dans la page Ventilation FreePro :
- Table DB freeproSettings (URL portail, credentials, fréquence, date antériorité, statut dernière récupération) - Service freeproAutoImport.ts : connexion HTTP au portail FreePro, téléchargement CSV, pipeline d'import - Procédures tRPC : getSettings, saveSettings, testConnection, forceImport - Job périodique en mémoire (daily/weekly/monthly) via setInterval - Frontend : wrapper Tabs (onglet 1 = Import & Historique, onglet 2 = Paramétrage) - Onglet Paramétrage : credentials, fréquence, date antériorité, bouton "Forcer récupération", statut - Tests unitaires : 8 tests passés
This commit is contained in:
@@ -86,11 +86,14 @@ import { startEmailImportService, stopEmailImportService, isEmailImportServiceRu
|
||||
import { startFolderImportService, stopFolderImportService, isFolderImportServiceRunning } from "./folderImportService";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { processFreeproExcel } from "./freeproService";
|
||||
import { runFreeproAutoImport, testFreeproConnection, startFreeproAutoJob, stopFreeproAutoJob, frequencyToMs } from "./freeproAutoImport";
|
||||
import {
|
||||
createFreeproImport,
|
||||
getFreeproImportsByUser,
|
||||
getFreeproImportWithLines,
|
||||
deleteFreeproImport,
|
||||
getFreeproSettings,
|
||||
upsertFreeproSettings,
|
||||
} from "./db";
|
||||
|
||||
// Admin-only procedure
|
||||
@@ -2293,6 +2296,78 @@ export const appRouter = router({
|
||||
return { base64, fileName };
|
||||
}),
|
||||
|
||||
/** Récupère les paramètres de connexion automatique FreePro */
|
||||
getSettings: protectedProcedure.query(async ({ ctx }) => {
|
||||
const s = await getFreeproSettings(ctx.user.id);
|
||||
// Ne pas exposer le mot de passe en clair
|
||||
if (s) {
|
||||
return {
|
||||
...s,
|
||||
loginPassword: s.loginPassword ? '••••••••' : null,
|
||||
hasPassword: !!s.loginPassword,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
|
||||
/** Sauvegarde les paramètres de connexion automatique FreePro */
|
||||
saveSettings: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
portalUrl: z.string().url().optional(),
|
||||
loginEmail: z.string().email().optional().or(z.literal('')),
|
||||
loginPassword: z.string().optional(), // vide = ne pas changer
|
||||
frequency: z.enum(['manual', 'daily', 'weekly', 'monthly']).optional(),
|
||||
maxAnteriority: z.number().nullable().optional(), // timestamp Unix en secondes
|
||||
autoEnabled: z.number().min(0).max(1).optional(),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const existing = await getFreeproSettings(ctx.user.id);
|
||||
const updateData: any = {};
|
||||
|
||||
if (input.portalUrl !== undefined) updateData.portalUrl = input.portalUrl;
|
||||
if (input.loginEmail !== undefined) updateData.loginEmail = input.loginEmail || null;
|
||||
// Ne mettre à jour le mot de passe que si une vraie valeur est fournie
|
||||
if (input.loginPassword && input.loginPassword !== '••••••••') {
|
||||
updateData.loginPassword = input.loginPassword;
|
||||
}
|
||||
if (input.frequency !== undefined) updateData.frequency = input.frequency;
|
||||
if (input.maxAnteriority !== undefined) updateData.maxAnteriority = input.maxAnteriority;
|
||||
if (input.autoEnabled !== undefined) updateData.autoEnabled = input.autoEnabled;
|
||||
|
||||
await upsertFreeproSettings(ctx.user.id, updateData);
|
||||
|
||||
// Gérer le job périodique
|
||||
const newSettings = await getFreeproSettings(ctx.user.id);
|
||||
if (newSettings?.autoEnabled && newSettings.frequency !== 'manual') {
|
||||
const ms = frequencyToMs(newSettings.frequency);
|
||||
if (ms > 0) startFreeproAutoJob(ctx.user.id, ms);
|
||||
} else {
|
||||
stopFreeproAutoJob(ctx.user.id);
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
}),
|
||||
|
||||
/** Teste la connexion au portail FreePro */
|
||||
testConnection: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
email: z.string().email(),
|
||||
password: z.string().min(1),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input }) => {
|
||||
return testFreeproConnection(input.email, input.password);
|
||||
}),
|
||||
|
||||
/** Force la récupération immédiate des factures FreePro */
|
||||
forceImport: protectedProcedure.mutation(async ({ ctx }) => {
|
||||
const result = await runFreeproAutoImport(ctx.user.id);
|
||||
return result;
|
||||
}),
|
||||
|
||||
/** Exporte la ventilation FreePro vers SharePoint */
|
||||
exportToSharePoint: protectedProcedure
|
||||
.input(z.object({ id: z.number(), pdfBase64: z.string().optional() }))
|
||||
|
||||
Reference in New Issue
Block a user