---
title: "Demo Account - Export / Import"
module: demo
type: functional
status: active
updated: 2026-07-06
---

# Demo Account - Export / Import

Ce module permet d'exporter les données d'un client existant vers un fichier XLSX, puis de les importer pour créer un nouveau compte client de démonstration.

## Cas d'usage

- Créer des comptes de démonstration à partir de données réelles anonymisées
- Dupliquer un client pour des tests
- Préparer des templates de données pré-remplies

---

## Commandes

### Export

Exporte toutes les données d'un client vers un fichier XLSX.

```bash
php artisan demo:export {source_idc} [--output=chemin]
```

**Arguments:**
| Argument | Description |
|----------|-------------|
| `source_idc` | ID du customer source à exporter |

**Options:**
| Option | Description |
|--------|-------------|
| `--output` | Chemin du fichier de sortie (défaut: `storage/app/exports/demo_YYYYMMDD_HHMMSS.xlsx`) |

**Exemple:**
```bash
# Exporter le client 999
php artisan demo:export 999

# Exporter vers un fichier spécifique
php artisan demo:export 999 --output=exports/template_hopital.xlsx
```

---

### Import

Importe les données depuis un fichier XLSX pour créer un nouveau client.

```bash
php artisan demo:import {file} [options]
```

**Arguments:**
| Argument | Description |
|----------|-------------|
| `file` | Chemin vers le fichier XLSX à importer |

**Options:**
| Option | Description |
|--------|-------------|
| `--target-idc` | ID du customer cible (requis pour import réel) |
| `--target-name` | Nom du customer cible (requis pour import réel) |
| `--dry-run` | Prévisualiser sans insérer les données |
| `--date-offset` | Décalage de dates en jours (défaut: auto) |
| `--skip-validation` | Ignorer la validation du fichier |
| `--force` | Forcer l'import même avec des avertissements |

**Exemples:**
```bash
# Prévisualisation (dry-run)
php artisan demo:import storage/app/exports/demo.xlsx --dry-run

# Import réel
php artisan demo:import storage/app/exports/demo.xlsx --target-idc=888 --target-name="Hôpital Demo"

# Import avec décalage de dates personnalisé (30 jours)
php artisan demo:import storage/app/exports/demo.xlsx --target-idc=888 --target-name="Demo" --date-offset=30

# Forcer l'import malgré les avertissements
php artisan demo:import storage/app/exports/demo.xlsx --target-idc=888 --target-name="Demo" --force
```

---

### Commandes d'instance (Hospital, Mairie, Stinglay)

```bash
php artisan demo:hospital {idc} {name} [options]
php artisan demo:mairie   {idc} {name} [options]
php artisan demo:stinglay {idc} {name} [options]
```

**Options:**
| Option | Description |
|--------|-------------|
| `--reset` | Purge toutes les données de l'idc avant import |
| `--merge` | Fusionne le template avec l'instance existante (mode idempotent) |
| `--dry-run` | Simule l'import sans rien écrire en DB |
| `--force-restore` | Ignore les tombstones et recrée les lignes seed supprimées par le PO (**nécessite --merge**) |

**Exemples:**
```bash
# Créer ou réinitialiser
php artisan demo:hospital 985 "Hôpital Providence Sud" --reset

# Mettre à jour un compte existant (merge idempotent)
php artisan demo:hospital 985 "Hôpital Providence Sud" --merge

# Merge en ignorant les suppressions PO (force-restore)
php artisan demo:hospital 985 "Hôpital Providence Sud" --merge --force-restore

# Prévisualiser un merge sans rien écrire
php artisan demo:hospital 985 "Hôpital Providence Sud" --merge --dry-run
```

---

## Mode merge (`--merge`)

Le mode merge permet de **remettre à jour un compte de démonstration existant** sans écraser les modifications apportées par le PO ou l'équipe commerciale.

### Stratégies de merge

Trois stratégies coexistent selon le type de données :

#### 1. SemiIdempotentMergeStrategy (tables configurables)
Pour les tables avec des colonnes SEED (données template) et des colonnes FREE (données PO). Le merge met à jour uniquement les colonnes SEED en préservant les colonnes FREE.

#### 2. NonIdempotentMergeStrategy (24 tables)
Pour les tables dont les données ne peuvent pas être réconciliées ligne par ligne (événements, interventions, équipements, tâches, projets...).

**Comportement :** delete ciblé des lignes seed (via `demo_seed_map`) + re-insert complet à chaque merge. Les lignes créées par le PO ne sont jamais touchées.

Tables concernées : `cyber_ev`, `customer_event`, `dsi_hw`, `dsi_lic`, `_com_interv`, `_com_contr`, `equipment_types`, `crm_aff`, `cust_suivi`, `dsi_hw_users`, `project_step`, `project_news`, `project_meeting`, `project_backlog`, `equipments`, `project_kpival`, `_com_task`, `_com_livrable`, `equipment_counters`, `project_crit`, `risk_mesure`, `risk_item`, `project_matrice`, `project_for`

#### 3. SkipIfExistsMergeStrategy (2 tables)
Pour les tables où la présence de données PO active indique une appropriation métier complète du module.

**Comportement :** si le tenant a des lignes actives (hors dtdel), les données seed sont entièrement ignorées pour cette table.

Tables concernées : `tickets`, `crm_miss`

---

## Tombstones

Un **tombstone** est une ligne seed supprimée par le PO (hard delete ou soft delete `dtdel`). Le système détecte ces suppressions avant chaque merge et les enregistre dans `demo_seed_map` avec `deleted_by_po=1`.

**Comportement par défaut (sans `--force-restore`) :**
- Les tombstones ne sont pas re-insérés lors du merge suivant.
- Des avertissements sont affichés en sortie de commande.

**Comportement avec `--force-restore` :**
- Les tombstones sont ignorés : toutes les lignes seed sont re-insérées comme si elles n'avaient jamais été supprimées.
- À utiliser quand le PO a fait une erreur de suppression ou après une réinitialisation partielle.

> **Note :** `--force-restore` ne fonctionne qu'avec `--merge`. Le SkipIfExistsMergeStrategy n'est **pas** affecté par `--force-restore` (protection des données PO actives).

---

## Format du fichier XLSX

### Onglet `_config`

Premier onglet obligatoire contenant les métadonnées:

| Clé | Valeur | Description |
|-----|--------|-------------|
| `version` | `1.0` | Version du format |
| `source_idc` | `999` | ID du client source |
| `export_date` | `2026-01-13 10:30:00` | Date d'export |
| `target_idc` | _(vide)_ | Rempli à l'import |
| `target_name` | _(vide)_ | Rempli à l'import |

### Onglets de tables

Chaque table a son propre onglet avec:

- **Ligne 1**: Noms des colonnes (correspondant aux colonnes DB)
- **Ligne 2**: Métadonnées (`PK`, `FK:table.col`, `DATE`, `R`=requis, `O`=optionnel)
- **Ligne 3+**: Données

### Convention des IDs temporaires

Les IDs sont remplacés par des identifiants temporaires au format:
```
TEMP_{TABLE}_{NNNN}
```

Exemples:
- `TEMP_CUSTOMER_0001`
- `TEMP_USERS_0042`
- `TEMP_PROJECT_0003`

Ces IDs permettent de maintenir les relations entre les enregistrements. À l'import, ils sont automatiquement remplacés par les vrais IDs générés.

---

## Tables supportées

L'import/export gère 45+ tables organisées par niveau de dépendance:

| Niveau | Tables |
|--------|--------|
| 0 | `customer` |
| 1 | `customer_addr`, `users`, `_customer_sub`, `customer_group`, `customer_event`, `crm_cust`, `customer_fac`, `dsi_param`, `dsi_param_po`, `customer_ask_system`, `supplier` |
| 2 | `customer_addr_bat`, `users_profile`, `dsi_hw`, `dsi_hwvm`, `dsi_con`, `dsi_app`, `dsi_lic`, `dsi_tel`, `dsi_planip`, `tickets`, `_com_interv`, `_com_contr`, `customer_act_san`, `customer_act_ms`, `_com_wiki`, `_com_know`, `project` |
| 3 | `customer_addr_bat_lo`, `project_team`, `project_step`, `project_role`, `project_news`, `project_danger`, `project_kpi`, `project_meeting`, `project_budget` |
| 4 | `customer_addr_bat_lo_loc`, `customer_addr_bat_lo_pi`, `project_kpival`, `_com_task`, `_com_acl`, `_term` |

---

## Décalage des dates

Pour les commandes d'instance (`demo:hospital`, `demo:mairie`, `demo:stinglay`), les dates sont **automatiquement fraîches** à chaque exécution : le template génère ses dates via `Carbon::now()` et la valeur `export_date` est positionnée à l'heure courante, ce qui donne un décalage effectif de 0 jours.

Pour les imports depuis fichier XLSX (`demo:import`), le décalage est calculé automatiquement à partir de la date d'export contenue dans l'onglet `_config`.

**Valeurs du paramètre `dateOffsetDays` (API interne) :**
| Valeur | Comportement |
|--------|-------------|
| `-1` | Auto : calcule le décalage depuis `export_date` (défaut pour `demo:import`) |
| `0` | Aucun décalage (dates importées telles quelles) |
| `N > 0` | Décalage forcé de N jours |

Pour désactiver le décalage sur un import fichier : `--date-offset=0`

---

## Données par défaut

Si un onglet est absent ou vide dans le fichier XLSX, des données par défaut peuvent être insérées pour certaines tables (ex: `project`). Ceci est configurable dans `DefaultDataConfig.php`.

---

## Transformations automatiques

À l'import, certaines données sont automatiquement transformées:

| Table | Transformation |
|-------|----------------|
| `customer` | SIRET unique généré, email modifié |
| `users` | Email rendu unique (format `email+demoXXX@domain`), mot de passe hashé |
| Toutes | Colonne `_id` mise à jour vers le `target_idc` |

---

## Validation

Avant l'import, le fichier est validé:

1. **Structure**: Présence de `_config`, colonnes requises
2. **Références**: Vérification que les FK pointent vers des IDs existants
3. **Données**: Colonnes obligatoires remplies

Les erreurs bloquent l'import. Les avertissements sont affichés mais n'empêchent pas l'import (sauf si `--force` n'est pas utilisé).

---

## Architecture technique

```
app/
├── Services/DemoAccount/
│   ├── DemoAccountService.php           # Façade principale
│   ├── Config/
│   │   ├── TableConfig.php              # PK, FK, dates par table
│   │   └── DefaultDataConfig.php        # Données par défaut
│   ├── Export/
│   │   └── DemoExporter.php
│   ├── Import/
│   │   ├── DemoImporter.php
│   │   └── Validators/XlsxValidator.php
│   └── Helpers/
│       ├── IdMappingHelper.php          # Mapping temp_id → real_id
│       └── DateOffsetHelper.php
├── Exports/DemoAccount/
│   ├── DemoAccountExport.php
│   └── Sheets/
│       ├── ConfigSheet.php
│       └── TableSheet.php
└── Console/Commands/DemoAccount/
    ├── ExportDemoCommand.php
    └── ImportDemoCommand.php
```

---

## Utilisation programmatique

```php
use App\Services\DemoAccount\DemoAccountService;

$service = new DemoAccountService();

// Export
$filePath = $service->export(999, 'exports/demo.xlsx');

// Validation
$result = $service->validate('storage/app/exports/demo.xlsx');
if (!$result->isValid) {
    foreach ($result->errors as $error) {
        echo $error;
    }
}

// Import
$result = $service->import(
    filePath: 'storage/app/exports/demo.xlsx',
    targetIdc: 888,
    targetName: 'Mon Client Demo',
    dryRun: false,
    dateOffsetDays: -1 // auto
);

if ($result->success) {
    echo "Import réussi: {$result->statistics['customer']['inserted']} customers créés";
}
```

---

## Troubleshooting

### Erreur "Duplicate entry for key 'PRIMARY'"
Le `target_idc` existe déjà. Choisissez un autre ID.

### Erreur "Column cannot be null"
Une colonne NOT NULL n'a pas de valeur dans le fichier. Vérifiez les données ou ajoutez une valeur par défaut dans `DefaultDataConfig.php`.

### Avertissements de références introuvables
Des FK pointent vers des enregistrements non exportés (utilisateurs supprimés, etc.). Ces références seront nulles à l'import.

### Décalage de dates incorrect
Utilisez `--date-offset=N` pour forcer un décalage spécifique en jours.

### Tombstones persistants après --force-restore
Si une ligne n'est toujours pas recréée après `--force-restore`, vérifier qu'elle n'est pas aussi filtrée par une collision PK (tables non auto-increment). Consulter les warnings de la commande.

---

## Voir aussi

- Scénarios de validation SQL (merge) : `docs/demo/demo-merge-tests.md`
- Documentation technique instances : `.claude/technical-docs/demo/demo-instances.md`
