# Rattacher chaque unité à sa commande B2B

En quittant cette page, vous saurez donner un territoire à vos distributeurs, faire scanner chaque unité par votre logisticien au moment de la préparation, et recevoir une alerte qui nomme la commande et le distributeur quand une unité est scannée hors de son territoire.

Source : https://docs.sealtrust.io/expedition-b2b/

---

Votre logisticien prépare vos commandes B2B. Chaque commande part chez un
distributeur qui a un territoire contractuel exclusif. Si une unité de cette
commande est ensuite scannée par un consommateur hors de ce territoire, vous
voulez savoir laquelle, de quelle commande, et chez quel distributeur elle
devait aller.

Pour cela, chaque unité est scannée une fois au moment où elle entre dans le
carton. Ce scan la rattache à la commande. Le parcours tient en quatre étapes.

Ce parcours demande l'offre Conformité + Identité, avec un QR code ou une puce
par unité. Sur l'offre Conformité, la création d'une commande et chaque scan
sont refusés avec le code `OFFER_EXCLUDES_IDENTITY`.

## 1. Donner un territoire à chaque distributeur

Dans la console, écran Distribution, onglet « Points de vente », créez le
distributeur avec le type « Distributeur ». Le champ « Territoire » reçoit ses
pays, en codes ISO à deux lettres séparés par des virgules, par exemple
`DE, AT`. Le code du point de vente, par exemple `DIST-DE`, est celui que votre
logisticien enverra.

Un distributeur sans territoire n'est pas une erreur : ses unités sont alors
vérifiées contre les pays autorisés de votre marque, comme avant.

Chaque création ou modification laisse une ligne dans le journal d'audit, avec
l'ancien et le nouveau territoire.

## 2. Ouvrir la commande

Une commande porte votre référence de commande, celle de votre ERP ou de votre
logisticien, et le distributeur. La même référence envoyée deux fois donne la
même commande : un logisticien qui rejoue un appel ne crée pas de doublon.

Trois façons de l'ouvrir, au choix.

- Par l'API, avec [`POST /partner/dispatch/shipments`](/reference/post-partner-dispatch-shipments/).
- Dans la console, onglet « Expéditions », bouton « Nouvelle expédition ».
- Par l'import CSV de l'étape 3, qui crée les commandes qu'il nomme.

## 3. Scanner chaque unité à la préparation

Chaque unité scannée entre dans la commande. Une unité ne peut être que dans
une seule commande active à la fois.

### Par l'API, depuis le système du logisticien

Le logisticien envoie les unités par paquets de 1000 au plus. Chaque unité
reçoit son propre résultat, et une erreur sur une unité n'empêche pas les
autres d'entrer. Le détail des résultats est dans
[`POST /partner/dispatch/shipments/{shipment_id}/units`](/reference/post-partner-dispatch-shipments-id-units/).

La clef d'API porte le droit `dispatch:write`. Créez-la dans la console,
Réglages, Développeurs, et ne cochez que ce droit : la clef ne pourra rien
faire d'autre.

```bash
# 1. Open the order
curl -X POST "https://api.sealtrust.io/v1/partner/dispatch/shipments" \
  -H "Authorization: Bearer st_live_0000000000000000000000000000000000000000000000" \
  -H "Content-Type: application/json" \
  -d '{"order_ref": "SO-2026-1042", "distributor_code": "DIST-DE"}'

# 2. Send the units scanned into the box, up to 1000 per call
curl -X POST "https://api.sealtrust.io/v1/partner/dispatch/shipments/318/units" \
  -H "Authorization: Bearer st_live_0000000000000000000000000000000000000000000000" \
  -H "Content-Type: application/json" \
  -d '{"units": ["H897RFWJ4972", "https://sealtrust.io/p/Y5T2VGGF2NP9"]}'

# 3. The order left the warehouse
curl -X POST "https://api.sealtrust.io/v1/partner/dispatch/shipments/318/close" \
  -H "Authorization: Bearer st_live_0000000000000000000000000000000000000000000000"
```

Les unités se lisent sous les formes que porte l'étiquette : le numéro de série
de 12 caractères, le lien `https://sealtrust.io/p/{serial}`, le lien GS1
Digital Link ou le lien du QR signé.

### Par la page de préparation de la console

Pour un petit volume, le préparateur ouvre la console sur son téléphone,
onglet « Expéditions », bouton « Page de préparation ». Il choisit la
commande, puis scanne :

- avec la caméra du téléphone ;
- avec une douchette, qui écrit le code dans le champ puis valide avec Entrée ;
- ou en tapant le numéro de série.

Chaque scan répond aussitôt : vert et un bip aigu quand l'unité est dans le
carton, rouge et un double bip grave sinon, avec la raison. Le compteur
d'unités dans le carton se met à jour à chaque scan.

### Par un fichier CSV

Si votre logisticien exporte des fichiers, importez-les dans l'onglet
« Expéditions », bouton « Importer un CSV ». Le fichier porte trois colonnes,
dans n'importe quel ordre, séparées par des virgules ou des points-virgules,
une ligne par unité :

```csv
order_ref,distributor_code,serial
SO-2026-1042,DIST-DE,H897RFWJ4972
SO-2026-1042,DIST-DE,Y5T2VGGF2NP9
SO-2026-1043,DIST-BE,K2M8Q0R4T6V1
```

| Colonne | Contenu |
| --- | --- |
| `order_ref` | votre référence de commande. Une commande absente est créée. |
| `distributor_code` | le code du distributeur. Une même commande ne nomme qu'un distributeur. |
| `serial` | le numéro de série de l'unité, ou son lien. |

Le fichier pèse 2 Mo au plus et porte 20000 lignes au plus. Nous lisons
l'UTF-8 comme le format CSV d'Excel en français (Windows-1252, points-virgules,
première ligne `sep=;` acceptée). Le résultat donne chaque ligne refusée avec
son numéro et sa raison.

Chaque ligne est jugée seule. Une ligne refusée n'empêche pas les autres :

| Raison | Ce qui la provoque |
| --- | --- |
| `missing_value` | une des trois colonnes est vide. |
| `invalid_value` | une référence de commande de plus de 100 caractères, un caractère de contrôle ou un octet illisible, ou une référence qui commence par `=`, `+`, `-` ou `@`, qu'un tableur exécuterait comme une formule. |
| `database_error` | la commande n'a pas pu être enregistrée. Les commandes déjà traitées du fichier restent enregistrées, les suivantes sont essayées. |

Les commandes sont enregistrées une par une, pas en bloc : un fichier de 2000
commandes dont une seule pose problème enregistre les 1999 autres. Chaque
import laisse une ligne dans le journal d'audit, y compris quand il s'arrête en
route.

### Une unité déjà dans une autre commande

Elle est refusée avec `already_allocated`, et la réponse nomme l'autre
commande. Deux façons de corriger : la retirer de l'autre commande, ou la
déplacer par l'API avec `reallocate` et un motif. L'ancienne ligne n'est
jamais effacée : elle est close avec la date, l'auteur et le motif, et le
journal d'audit garde le déplacement.

## 4. Recevoir l'alerte

Quand un consommateur scanne une unité rattachée à une commande, par le QR code
ou par la puce, nous comparons le pays du scan au territoire du distributeur.
Hors du territoire, vous recevez une alerte marché gris par les mêmes canaux
qu'avant : la console en temps réel, Slack ou Teams, et le webhook
`product.gray_market`.

Le message Slack ou Teams nomme la commande, le distributeur et son
territoire. Le webhook garde tous ses anciens champs et en reçoit de nouveaux.

```json
{
  "product_id": 4096,
  "product_name": "Eau de parfum 50 ml",
  "country": "FR",
  "city": "Lyon",
  "authorized_countries": ["FR", "DE", "AT", "BE"],
  "source": "qr",
  "nfc_auth_log_id": 88213,
  "retailer_id": null,
  "shipment_id": 318,
  "order_ref": "SO-2026-1042",
  "ship_date": "2026-10-09",
  "distributor": {"id": 77, "code": "DIST-DE", "name": "Acme Distribution GmbH"},
  "expected_territory": ["DE", "AT"],
  "territory_source": "distributor",
  "scan_country": "FR",
  "country_source": "ip"
}
```

| Champ | Ce qu'il dit |
| --- | --- |
| `order_ref`, `shipment_id`, `ship_date` | la commande de l'unité. `null` quand l'unité n'est dans aucune commande. |
| `distributor` | le distributeur, avec son code et son nom. `null` hors commande. |
| `expected_territory` | les pays où l'unité était attendue. |
| `authorized_countries` | les pays autorisés de votre marque, ou de son modèle, comme avant l'ajout des commandes. Pour une unité d'une commande, la zone vérifiée est `expected_territory`, pas celle-ci. |
| `territory_source` | `distributor` pour le territoire du distributeur, `authorized_countries` pour les pays de votre marque. |
| `scan_country` | le pays du scan. |
| `country_source` | d'où vient ce pays : `gps` (le téléphone), `ip` (le réseau), `relay` (la région du relais iCloud), `declared` (une vente déclarée par un point de vente). |

Un pays venu du réseau est une approximation : un consommateur en voyage, ou
derrière un réseau privé, peut apparaître ailleurs. Lisez `country_source`
avant d'appeler un distributeur.

La fiche de la commande, dans la console, montre pour chaque unité son dernier
scan consommateur et s'il est dans le territoire ou non. La fiche de chaque
produit montre sa commande et son distributeur.

## 5. Voir ce qui n'a jamais été scanné

La fiche de la commande dit combien de ses unités ont été scannées par un
consommateur depuis leur préparation, et combien ne l'ont jamais été
(« Jamais scannées »). Un scan fait avant la préparation, à l'entrepôt par
exemple, ne compte pas : il ne dit rien de l'endroit où l'unité a été vendue.
Seule une vérification réussie compte, jamais un échec ni la télémétrie d'une
lecture de puce.

Le lien « Voir sur la carte des scans » ouvre la [carte des
scans](/console/carte-des-scans/) filtrée sur cette commande : chaque unité
scannée y est placée, avec sa fiche (produit, lot, distributeur, territoire,
pays du scan, dans ou hors zone, d'où vient le lieu). Le panneau
« Couverture » de la carte donne les mêmes chiffres par distributeur, par
commande et par lot.
