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 contactsPOST /api/import/sessions- Importer des sessionsPOST /api/import/quotes- Importer des quotesPOST /api/import/invoices- Importer des invoicesPOST /api/import/info- Informations sur le service d'import
Formats Supportés
CSV
- Content-Type :
text/csvouapplication/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:trueoufalse- 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.
- Actions supportées:
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:trueoufalse- 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:trueoufalse- 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:trueoufalse- 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_nameLast Name,lastname,last name→last_nameEmail,email→emailPhone,phone_number,phone number→phone_numberStreet,street→streetCity,city→cityZipcode,zip code,postal code→zipcodeCountry,country→countryNotes,notes→notesContact Type,contact type,type→contact_type- Google Contacts CSV (compatibilité) :
Given Name→first_nameFamily Name→last_nameName→full_name(fallback si prénom/nom absents)E-mail 1 - Value/E-mail 2 - Value→emailPhone 1 - Value/Phone 2 - Value→phone_numberAddress 1 - Street→streetAddress 1 - City→cityAddress 1 - Postal Code→zipcodeAddress 1 - Country→country
Sessions
Title,title→titleLocation,location→locationContact ID,contact id,contact_id→contact_idStart Date,start date,start_date→start_dateEnd Date,end date,end_date→end_dateNotes,notes→notesStatus,status→status
Quotes
Title,title→titleAmount,amount→amountStatus,status→statusValid Until,valid until,valid_until→valid_untilDescription,description→descriptionSession ID,session id,session_id→session_idContact ID,contact id,contact_id→contact_id
Invoices
Title,title→titleAmount,amount→amountStatus,status→statusDue Date,due date,due_date→due_dateDescription,description→descriptionSession ID,session id,session_id→session_idContact ID,contact id,contact_id→contact_idQuote 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
- Essaie de trouver les contacts existants par email (insensible à la casse)
- Si non trouvé, essaie de trouver par prénom + nom (insensible à la casse)
- Si trouvé, met à jour le contact existant
- Si non trouvé, crée un nouveau contact
Sessions
- Essaie de trouver les sessions existantes par titre et date de début (insensible à la casse)
- Si trouvé, met à jour la session existante
- Si non trouvé, crée une nouvelle session
Quotes
- Essaie de trouver les quotes existantes par titre (insensible à la casse)
- Si trouvé, met à jour la quote existante
- Si non trouvé, crée une nouvelle quote
Invoices
- Essaie de trouver les invoices existantes par titre (insensible à la casse)
- Si trouvé, met à jour l'invoice existante
- Si non trouvé, crée une nouvelle invoice
Validation
Le service valide :
Contacts
- Champs requis :
first_nameoulast_namedoit ê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 soitsession_idsoitcontact_id) - Types de données : Les montants doivent être des nombres positifs
- Statuts valides :
PENDING,SENT,ACCEPTED,REJECTED
Invoices
- Champs requis :
title(et soitsession_idsoitcontact_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 + ExportSessionsListPage- Import + ExportQuotesListPage- Import + ExportInvoicesListPage- Import + Export
Fichiers
backend/src/import/import.service.ts- Service de parsing et validationbackend/src/import/import-contacts.service.ts- Service d'import contactsbackend/src/import/import-sessions.service.ts- Service d'import sessionsbackend/src/import/import-quotes.service.ts- Service d'import quotesbackend/src/import/import-invoices.service.ts- Service d'import invoicesbackend/src/import/import.controller.ts- Endpoints RESTbackend/src/import/README.md- Documentation complètefrontend/src/client/import/useImport.ts- Hooks d'import (contacts, sessions, quotes, invoices)frontend/src/components/import/ImportButton.tsx- Composant bouton réutilisablefrontend/src/components/import/ImportContactsDialog.tsx- Dialog d'import contactsfrontend/src/components/import/ImportSessionsDialog.tsx- Dialog d'import sessionsfrontend/src/components/import/ImportQuotesDialog.tsx- Dialog d'import quotesfrontend/src/components/import/ImportInvoicesDialog.tsx- Dialog d'import invoices
Voir aussi
- Export System - Documentation du système d'export
- Scheduled Exports - Documentation des exports planifiés
- Export Templates - Documentation des templates d'export