Aller au contenu principal

Système d'Import

Système complet pour importer des données depuis des fichiers CSV et Excel avec validation, mapping de champs, et import incrémental.

Vue d'ensemble​

Le système d'import permet de :

  • Importer des contacts, sessions, quotes et invoices depuis CSV/Excel
  • Auto-détecter le mapping des champs
  • Mapper manuellement les colonnes CSV vers les champs du CRM
  • Valider les données avant import
  • Mettre à jour les enregistrements existants (import incrémental)
  • Obtenir un rapport d'erreurs détaillé

Architecture​

Backend​

Service : ImportService​

  • Fichier : backend/src/import/import.service.ts
  • Responsabilités :
    • Parsing de fichiers CSV et Excel
    • Auto-détection du mapping des champs
    • Validation des lignes de données

Services d'Import Spécialisés​

  • ImportContactsService (backend/src/import/import-contacts.service.ts)

    • Import de contacts depuis des lignes parsées
    • Gestion de l'import incrémental (matched par email ou nom)
    • Mapping des données vers les DTOs
  • ImportSessionsService (backend/src/import/import-sessions.service.ts)

    • Import de sessions depuis des lignes parsées
    • Gestion de l'import incrémental (matched par titre et date de début)
    • Mapping des données vers les DTOs
  • ImportQuotesService (backend/src/import/import-quotes.service.ts)

    • Import de quotes depuis des lignes parsées
    • Gestion de l'import incrémental (matched par titre)
    • Mapping des données vers les DTOs
  • ImportInvoicesService (backend/src/import/import-invoices.service.ts)

    • Import d'invoices depuis des lignes parsées
    • Gestion de l'import incrémental (matched par titre)
    • Mapping des données vers les DTOs

Contrôleur : ImportController​

  • Fichier : backend/src/import/import.controller.ts
  • Endpoints :
    • POST /api/import/contacts - Importer des contacts
    • POST /api/import/sessions - Importer des sessions
    • POST /api/import/quotes - Importer des quotes
    • POST /api/import/invoices - Importer des invoices
    • POST /api/import/info - Informations sur le service d'import

Formats Supportés​

CSV​

  • Content-Type : text/csv ou application/csv
  • Extension : .csv
  • Limites : 10MB maximum

Excel​

  • Content-Type :
    • application/vnd.openxmlformats-officedocument.spreadsheetml.sheet (XLSX)
    • application/vnd.ms-excel (XLS)
  • Extensions : .xlsx, .xls
  • Limites : 10MB maximum

API Endpoints​

Importer des contacts​

Endpoint: POST /api/import/contacts

Request:

  • Method: POST
  • Content-Type: multipart/form-data
  • Body:
    • file: CSV ou Excel file (required)

Query Parameters:

  • updateExisting: true ou false - Mettre à jour les contacts existants (matched par email ou nom)
  • mapping: JSON string avec mapping personnalisé, ex: {"First Name":"first_name","Last Name":"last_name"}
  • rowActions (optionnel): JSON string des actions par ligne, ex: {"2":"create","3":"update","4":"skip"}
    • Actions supportées: create, update, skip
    • Permet une revue manuelle avant exécution finale.

Prévisualiser l'import des contacts (revue avant exécution)​

Endpoint: POST /api/import/contacts/preview

Request:

  • Method: POST
  • Content-Type: multipart/form-data
  • Body:
    • file: CSV ou Excel file (required)
  • Query Parameters:
    • mapping (optionnel): JSON string avec mapping personnalisé

Response:

{
"items": [
{
"line": 2,
"firstName": "Marie",
"lastName": "Dupont",
"reason": "Matching contact found in CRM",
"suggestedAction": "update",
"existingContactId": "uuid"
}
],
"summary": {
"create": 5,
"update": 2,
"skip": 1
},
"mapping": {},
"totalRows": 8
}

Response:

{
"imported": 10,
"skipped": 2,
"total": 12,
"errors": [
{
"line": 5,
"message": "Invalid email format",
"row": {
"First Name": "John",
"Last Name": "Doe",
"Email": "invalid-email"
}
}
],
"warnings": [],
"mapping": {
"First Name": "first_name",
"Last Name": "last_name",
"Email": "email"
},
"totalRows": 12
}

Importer des sessions​

Endpoint: POST /api/import/sessions

Request:

  • Method: POST
  • Content-Type: multipart/form-data
  • Body:
    • file: CSV ou Excel file (required)

Query Parameters:

  • updateExisting: true ou false - Mettre à jour les sessions existantes (matched par titre et date de début)
  • mapping: JSON string avec mapping personnalisé

Champs requis : title, location, contact_id Champs optionnels : start_date, end_date, notes, status

Importer des quotes​

Endpoint: POST /api/import/quotes

Request:

  • Method: POST
  • Content-Type: multipart/form-data
  • Body:
    • file: CSV ou Excel file (required)

Query Parameters:

  • updateExisting: true ou false - Mettre à jour les quotes existantes (matched par titre)
  • mapping: JSON string avec mapping personnalisé

Champs requis : title (et soit session_id soit contact_id) Champs optionnels : amount, status, valid_until, description, session_id, contact_id

Importer des invoices​

Endpoint: POST /api/import/invoices

Request:

  • Method: POST
  • Content-Type: multipart/form-data
  • Body:
    • file: CSV ou Excel file (required)

Query Parameters:

  • updateExisting: true ou false - Mettre à jour les invoices existantes (matched par titre)
  • mapping: JSON string avec mapping personnalisé

Champs requis : title (et soit session_id soit contact_id) Champs optionnels : amount, status, due_date, description, session_id, contact_id, quote_id

Informations sur l'import​

Endpoint: POST /api/import/info

Retourne des informations sur le service d'import, les entités supportées, les formats, et les mappings de champs.

Mapping de Champs​

Auto-Détection​

Le service détecte automatiquement le mapping des champs depuis les en-têtes CSV. Variations supportées :

Contacts​

  • First Name, firstname, first name → first_name
  • Last Name, lastname, last name → last_name
  • Email, email → email
  • Phone, phone_number, phone number → phone_number
  • Street, street → street
  • City, city → city
  • Zipcode, zip code, postal code → zipcode
  • Country, country → country
  • Notes, notes → notes
  • Contact Type, contact type, type → contact_type
  • Google Contacts CSV (compatibilité) :
    • Given Name → first_name
    • Family Name → last_name
    • Name → full_name (fallback si prénom/nom absents)
    • E-mail 1 - Value / E-mail 2 - Value → email
    • Phone 1 - Value / Phone 2 - Value → phone_number
    • Address 1 - Street → street
    • Address 1 - City → city
    • Address 1 - Postal Code → zipcode
    • Address 1 - Country → country

Sessions​

  • Title, title → title
  • Location, location → location
  • Contact ID, contact id, contact_id → contact_id
  • Start Date, start date, start_date → start_date
  • End Date, end date, end_date → end_date
  • Notes, notes → notes
  • Status, status → status

Quotes​

  • Title, title → title
  • Amount, amount → amount
  • Status, status → status
  • Valid Until, valid until, valid_until → valid_until
  • Description, description → description
  • Session ID, session id, session_id → session_id
  • Contact ID, contact id, contact_id → contact_id

Invoices​

  • Title, title → title
  • Amount, amount → amount
  • Status, status → status
  • Due Date, due date, due_date → due_date
  • Description, description → description
  • Session ID, session id, session_id → session_id
  • Contact ID, contact id, contact_id → contact_id
  • Quote ID, quote id, quote_id → quote_id

Mapping Personnalisé​

Vous pouvez fournir un mapping personnalisé comme string JSON :

{
"First Name": "first_name",
"Last Name": "last_name",
"Email Address": "email",
"Phone Number": "phone_number",
"Address": "street",
"City": "city",
"Postal Code": "zipcode",
"Country": "country",
"Notes": "notes",
"Type": "contact_type"
}

Import Incrémental​

Quand updateExisting=true, le service met à jour les enregistrements existants selon les critères suivants :

Contacts​

  1. Essaie de trouver les contacts existants par email (insensible à la casse)
  2. Si non trouvé, essaie de trouver par prénom + nom (insensible à la casse)
  3. Si trouvé, met à jour le contact existant
  4. Si non trouvé, crée un nouveau contact

Sessions​

  1. Essaie de trouver les sessions existantes par titre et date de début (insensible à la casse)
  2. Si trouvé, met à jour la session existante
  3. Si non trouvé, crée une nouvelle session

Quotes​

  1. Essaie de trouver les quotes existantes par titre (insensible à la casse)
  2. Si trouvé, met à jour la quote existante
  3. Si non trouvé, crée une nouvelle quote

Invoices​

  1. Essaie de trouver les invoices existantes par titre (insensible à la casse)
  2. Si trouvé, met à jour l'invoice existante
  3. Si non trouvé, crée une nouvelle invoice

Validation​

Le service valide :

Contacts​

  • Champs requis : first_name ou last_name doit être présent
  • Format email : Si fourni, l'email doit être valide

Sessions​

  • Champs requis : title, location, contact_id
  • Format de dates : Les dates doivent être au format ISO 8601

Quotes​

  • Champs requis : title (et soit session_id soit contact_id)
  • Types de données : Les montants doivent être des nombres positifs
  • Statuts valides : PENDING, SENT, ACCEPTED, REJECTED

Invoices​

  • Champs requis : title (et soit session_id soit contact_id)
  • Types de données : Les montants doivent être des nombres positifs
  • Statuts valides : DRAFT, SENT, PAID, OVERDUE

Général​

  • Longueurs de champs : Respecte les contraintes de la base de données
  • UUIDs : Les IDs de relations doivent être des UUIDs valides

Gestion des Erreurs​

Les erreurs sont rapportées avec :

  • Numéro de ligne : La ligne dans le fichier où l'erreur s'est produite
  • Message d'erreur : Description de l'erreur de validation
  • Données de la ligne : Les données réelles de la ligne qui ont causé l'erreur

Exemples d'Utilisation​

Import basique​

curl -X POST "http://localhost:3000/api/import/contacts" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@contacts.csv"

Import avec mise à jour des contacts existants​

curl -X POST "http://localhost:3000/api/import/contacts?updateExisting=true" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@contacts.csv"

Import avec mapping personnalisé​

curl -X POST "http://localhost:3000/api/import/contacts?mapping=%7B%22First%20Name%22%3A%22first_name%22%2C%22Last%20Name%22%3A%22last_name%22%7D" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@contacts.csv"

Exemples de fichiers CSV​

Contacts​

First Name,Last Name,Email,Phone,Street,City,Zipcode,Country
John,Doe,john@example.com,+1234567890,123 Main St,New York,10001,USA
Jane,Smith,jane@example.com,+0987654321,456 Oak Ave,Los Angeles,90001,USA

Sessions​

Title,Location,Contact ID,Start Date,End Date,Notes
Photoshoot Session,Studio A,uuid-contact-1,2025-01-15T10:00:00Z,2025-01-15T12:00:00Z,Portrait session
Wedding Photography,Venue B,uuid-contact-2,2025-02-20T14:00:00Z,2025-02-20T18:00:00Z,Full day coverage

Quotes​

Title,Amount,Status,Valid Until,Session ID,Contact ID
Quote #1,1500,PENDING,2025-02-01,uuid-session-1,uuid-contact-1
Quote #2,2500,SENT,2025-03-01,uuid-session-2,uuid-contact-2

Invoices​

Title,Amount,Status,Due Date,Session ID,Contact ID
Invoice #1,1500,DRAFT,2025-02-15,uuid-session-1,uuid-contact-1
Invoice #2,2500,SENT,2025-03-15,uuid-session-2,uuid-contact-2

Frontend​

Hooks d'Import​

useImportContacts​

import { useImportContacts } from "@/client/import/useImport";

const { import: importContacts, isLoading, result } = useImportContacts();

// Importer un fichier
importContacts(file, { updateExisting: true });

useImportSessions​

import { useImportSessions } from "@/client/import/useImport";

const { import: importSessions, isLoading, result } = useImportSessions();

// Importer un fichier
importSessions(file, { updateExisting: true });

useImportQuotes​

import { useImportQuotes } from "@/client/import/useImport";

const { import: importQuotes, isLoading, result } = useImportQuotes();

// Importer un fichier
importQuotes(file, { updateExisting: true });

useImportInvoices​

import { useImportInvoices } from "@/client/import/useImport";

const { import: importInvoices, isLoading, result } = useImportInvoices();

// Importer un fichier
importInvoices(file, { updateExisting: true });

Composants d'Import​

ImportButton (Composant réutilisable)​

import { ImportButton } from "@/components/import";
import { ExportEntityValues } from "@/_generated/types.gen";

// Bouton d'import pour contacts
<ImportButton entity={ExportEntityValues.CONTACTS} />

// Bouton d'import pour sessions
<ImportButton entity={ExportEntityValues.SESSIONS} />

// Bouton d'import pour quotes
<ImportButton entity={ExportEntityValues.QUOTES} />

// Bouton d'import pour invoices
<ImportButton entity={ExportEntityValues.INVOICES} />

Dialogs spécialisés​

import { 
ImportContactsDialog,
ImportSessionsDialog,
ImportQuotesDialog,
ImportInvoicesDialog
} from "@/components/import";

// Dialog pour contacts
<ImportContactsDialog
open={isOpen}
onOpenChange={setIsOpen}
/>

// Dialog pour sessions
<ImportSessionsDialog
open={isOpen}
onOpenChange={setIsOpen}
/>

// Dialog pour quotes
<ImportQuotesDialog
open={isOpen}
onOpenChange={setIsOpen}
/>

// Dialog pour invoices
<ImportInvoicesDialog
open={isOpen}
onOpenChange={setIsOpen}
/>

Intégration dans les pages​

Les boutons d'import sont intégrés dans toutes les pages de liste :

  • ContactsListPage - Import + Export
  • SessionsListPage - Import + Export
  • QuotesListPage - Import + Export
  • InvoicesListPage - Import + Export

Fichiers​

  • backend/src/import/import.service.ts - Service de parsing et validation
  • backend/src/import/import-contacts.service.ts - Service d'import contacts
  • backend/src/import/import-sessions.service.ts - Service d'import sessions
  • backend/src/import/import-quotes.service.ts - Service d'import quotes
  • backend/src/import/import-invoices.service.ts - Service d'import invoices
  • backend/src/import/import.controller.ts - Endpoints REST
  • backend/src/import/README.md - Documentation complète
  • frontend/src/client/import/useImport.ts - Hooks d'import (contacts, sessions, quotes, invoices)
  • frontend/src/components/import/ImportButton.tsx - Composant bouton réutilisable
  • frontend/src/components/import/ImportContactsDialog.tsx - Dialog d'import contacts
  • frontend/src/components/import/ImportSessionsDialog.tsx - Dialog d'import sessions
  • frontend/src/components/import/ImportQuotesDialog.tsx - Dialog d'import quotes
  • frontend/src/components/import/ImportInvoicesDialog.tsx - Dialog d'import invoices

Voir aussi​