Server Actions — Import
Source : src/app/actions/import.ts
processImportCSV(formData, accountId, format)
Parse et importe un fichier CSV dans un compte. Point d'entrée unique pour tous les formats supportés.
export type CsvFormat = "BOURSO_OPS" | "BOURSO_PEA" | "REVOLUT" | "CSV_TR";
export type ImportResult =
| { success: true; imported: number; ignored: number; snapshots: number; format: CsvFormat }
| { success: false; error: string };
export async function processImportCSV(
formData: FormData,
accountId: string,
format: CsvFormat
): Promise<ImportResult>
Pré-conditions :
- Session valide (
auth()) accountIdappartient à l'utilisateur courantformDatacontient un champfile(objetFile)
Formats supportés
format | Banque | Parser |
|---|---|---|
BOURSO_OPS | BoursoBank — opérations courantes | src/lib/parsers/bourso.ts |
BOURSO_PEA | BoursoBank — PEA | src/lib/parsers/bourso-pea.ts |
REVOLUT | Revolut | src/lib/parsers/revolut.ts |
CSV_TR | Trade Republic | src/lib/parsers/trade-republic.ts |
Le format est détecté automatiquement si non spécifié via src/lib/parsers/detect.ts.
Pipeline d'import
L'action exécute séquentiellement :
1. Parsing CSV → transactions + snapshots
Le parser retourne { transactions[], snapshots[], format }. Chaque transaction reçoit un importHash déterministe calculé depuis ses champs significatifs.
2. Déduplication des transactions
await prisma.transaction.createMany({
data: transactions,
skipDuplicates: true, // ignoré si importHash déjà présent
});
ignored = transactions.length - imported indique le nombre de doublons.
3. Upsert des snapshots
Chaque snapshot est upsert sur (accountId, date) sauf si un snapshot MANUAL existe déjà pour ce jour — celui-ci n'est jamais écrasé. Voir BR-SNAP-003.
4. Détection des transferts internes
Si au moins une transaction a été importée, un scan de détection automatique est lancé :
- Cherche dans les autres comptes de l'utilisateur une transaction de montant opposé dans une fenêtre de ±2 jours.
- Si trouvée : les deux transactions sont marquées
isInternal = trueet liées viacounterpartId(transaction atomique).
Voir BR-TX-003.
5. Revalidation
revalidatePath("/accounts"), revalidatePath("/accounts/<id>"), revalidatePath("/dashboard").
Retour
// Succès
{ success: true, imported: 42, ignored: 3, snapshots: 5, format: "BOURSO_OPS" }
// Échec
{ success: false, error: "Format CSV non reconnu" }
| Champ | Description |
|---|---|
imported | Nombre de nouvelles transactions créées |
ignored | Doublons ignorés (importHash déjà présent) |
snapshots | Snapshots créés ou mis à jour |
format | Format détecté par le parser |
Appel client (exemple)
const formData = new FormData();
formData.append("file", file);
const result = await processImportCSV(formData, accountId, "BOURSO_OPS");
if (!result.success) {
toast.error(result.error);
} else {
toast.success(`${result.imported} transactions importées`);
}