# API de synchronisation reseaux

## Endpoint

```http
POST /api/network/sync
Authorization: Bearer net_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
```

Les cles sont generees depuis `/admin/networks/sync?id=ID`, affichees une seule fois, stockees hachees en base et revocables.

Limites par defaut :

- corps JSON : `1 MiB`
- objets agences + utilisateurs : `5000`
- appels par cle : `60` par heure

Ces limites sont configurables avec les variables `NETWORK_SYNC_*` de `.env`.

## Payload

```json
{
  "sync_mode": "partial",
  "locations": [
    {
      "external_id": "AGENCY-PARIS-01",
      "name": "Agence Paris Centre",
      "address": "10 rue Exemple",
      "postal_code": "75001",
      "city": "Paris",
      "phone": "+33102030405",
      "email": "paris@example.test",
      "status": "active",
      "shop_codes": ["PARIS-CENTRE"]
    }
  ],
  "users": [
    {
      "external_id": "USER-0042",
      "firstname": "Alice",
      "lastname": "Martin",
      "email": "alice.martin@example.test",
      "role": "ROLE_LOCATION_MANAGER",
      "status": "active",
      "location_external_id": "AGENCY-PARIS-01",
      "shop_codes": ["PARIS-CENTRE"]
    }
  ]
}
```

Le couple `network_id + external_id` est l'identifiant stable. L'email ne sert jamais seul a rattacher automatiquement un compte existant.

## Modes

`partial` met a jour uniquement les objets recus. Il convient aux mises a jour en continu.

Dans un objet `partial`, omettre `shop_codes` conserve le mapping existant ou herite des boutiques de l'agence. Envoyer explicitement `"shop_codes": []` retire les rattachements boutique concernes.

`full` exige les listes `locations` et `users`. Les objets actifs absents deviennent inactifs. Aucune ligne n'est supprimee physiquement.

Pour une synchronisation nocturne, envoyer un payload `full` apres extraction complete de l'ERP. Utiliser `partial` pour les corrections ou evenements en journee.

## Roles

Mappings par defaut :

| Role externe | Scope | Role plateforme |
| --- | --- | --- |
| `ROLE_NETWORK_ADMIN` | reseau | `network_manager` |
| `ROLE_LOCATION_MANAGER` | boutique | `agency_manager` |
| `ROLE_LOCATION_USER` | boutique | `agency_agent` |
| `ROLE_MARKETING` | reseau | `independent_agent` |
| `ROLE_READONLY` | boutique | `agency_agent` |

Les mappings peuvent etre surcharges par reseau dans `network_role_mappings`.

Exemple :

```sql
INSERT INTO network_role_mappings (network_id, external_role, target_scope, role_code)
VALUES (12, 'ROLE_CUSTOM_MANAGER', 'shop', 'agency_manager');
```

## Reponse reussie

```json
{
  "ok": true,
  "request_id": "f2c74a7df9448cd103188875cab4f224",
  "sync_log_id": 31,
  "network_id": 12,
  "sync_mode": "partial",
  "stats": {
    "received_locations": 1,
    "received_users": 1,
    "created_locations": 1,
    "updated_locations": 0,
    "deactivated_locations": 0,
    "created_users": 1,
    "updated_users": 0,
    "deactivated_users": 0,
    "errors": []
  }
}
```

## Erreurs

Format standard :

```json
{
  "ok": false,
  "request_id": "f2c74a7df9448cd103188875cab4f224",
  "error": {
    "code": "validation_error",
    "message": "users[0].email est invalide."
  }
}
```

Codes HTTP :

| HTTP | Code | Signification |
| --- | --- | --- |
| `400` | `invalid_json` | JSON illisible |
| `401` | `unauthorized` | Bearer absent, invalide ou revoque |
| `413` | `payload_too_large` | taille ou nombre d'objets depasse |
| `415` | `unsupported_media_type` | `Content-Type` incorrect |
| `422` | `validation_error` | payload incoherent ou reference inconnue |
| `429` | `rate_limited` | quota depasse |
| `500` | `internal_error` | erreur interne masquee |

Les reponses ne contiennent jamais de stacktrace, erreur SQL ou chemin systeme. Utiliser `request_id` pour retrouver la synchronisation ou l'erreur technique.

## Provisionnement

Lors d'une synchronisation utilisateur :

- le compte local est cree si le lien externe n'existe pas ;
- les informations, droits, agence et boutiques sont actualises ;
- un changement d'agence est conserve dans `network_user_location_history` ;
- un depart desactive le lien, les droits du reseau et le compte s'il ne reste aucun lien actif ;
- un retour reactive automatiquement le compte ;
- une collision email sans lien externe explicite bloque l'operation pour eviter un rattachement dangereux.

## Tables principales

- `network_locations`
- `network_location_shops`
- `network_users`
- `network_user_location_history`
- `network_role_mappings`
- `network_api_clients`
- `network_api_rate_limits`
- `network_sync_logs`

Le socle metier existant reste la source de verite pour les boutiques et leurs catalogues : `shops` correspond aux boutiques reseau, `user_shop_memberships` aux rattachements utilisateur-boutique et `shop_products` au catalogue distribue par boutique.
