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 namefirst_name
  • Last Name, lastname, last namelast_name
  • Email, emailemail
  • Phone, phone_number, phone numberphone_number
  • Street, streetstreet
  • City, citycity
  • Zipcode, zip code, postal codezipcode
  • Country, countrycountry
  • Notes, notesnotes
  • Contact Type, contact type, typecontact_type
  • Google Contacts CSV (compatibilité) :
    • Given Namefirst_name
    • Family Namelast_name
    • Namefull_name (fallback si prénom/nom absents)
    • E-mail 1 - Value / E-mail 2 - Valueemail
    • Phone 1 - Value / Phone 2 - Valuephone_number
    • Address 1 - Streetstreet
    • Address 1 - Citycity
    • Address 1 - Postal Codezipcode
    • Address 1 - Countrycountry

Sessions

  • Title, titletitle
  • Location, locationlocation
  • Contact ID, contact id, contact_idcontact_id
  • Start Date, start date, start_datestart_date
  • End Date, end date, end_dateend_date
  • Notes, notesnotes
  • Status, statusstatus

Quotes

  • Title, titletitle
  • Amount, amountamount
  • Status, statusstatus
  • Valid Until, valid until, valid_untilvalid_until
  • Description, descriptiondescription
  • Session ID, session id, session_idsession_id
  • Contact ID, contact id, contact_idcontact_id

Invoices

  • Title, titletitle
  • Amount, amountamount
  • Status, statusstatus
  • Due Date, due date, due_datedue_date
  • Description, descriptiondescription
  • Session ID, session id, session_idsession_id
  • Contact ID, contact id, contact_idcontact_id
  • Quote ID, quote id, quote_idquote_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