# Tekshelf — API du tenant
## Intégration No Waste · Shelfy

Version 0.5 — 26 octobre 2026.

Documentation des appels utilisés pour suivre les Shelfy depuis No Waste, **en lecture seule**. Les choix encore ouverts sont regroupés dans la section « Points à confirmer ».

## 1. Objectif de l’intégration

Permettre à No Waste de suivre, pour une pharmacie équipée de deux ou trois Shelfy, l’état et l’évolution du remplissage de ces bacs, afin de déclencher ses propres alertes de don selon leur rotation. No Waste lit les données brutes de façon périodique et réalise sa propre analyse.

Le périmètre cible comprend :

- L’identification de la pharmacie et de ses bacs.
- La consultation de l’état courant d’un bac : produits présents, remplissage, poids et date de mise à jour.
- La consultation de l’historique de remplissage de chaque bac.
- La vérification de la fraîcheur des mesures.

Aucune écriture n’est ouverte au partenaire : ni création, ni modification, ni association de produit, ni maintenance.

### Nommage

| Où | Nom utilisé | Exemples |
|---|---|---|
| Ce que voit le client (écrans, libellés, cette documentation) | **Shelfy** | Portail, scanner, étiquette du bac, nom du point d’accès `SHELFY-xxxx`. |
| Le code et l’API | `promo_bin` | Type d’appareil et de meuble `promo_bin`, route `/api/promo-bins`, identité `PB-<numéro de série>`. |

Le texte de cette documentation dit donc « Shelfy » ; les chemins, types et identifiants cités entre accents graves restent les noms techniques `promo_bin`. Une exception : les réponses de la liste et du détail rangent le bac dans une propriété nommée `shelfy`.

## 2. Adresse d’API propre à chaque tenant

L’adresse de l’API est fournie par Tekshelf pour chaque tenant. Les chemins décrits dans cette documentation s’ajoutent à cette adresse.

Un tenant correspond à un périmètre client. Un filtre `storeId` permet de cibler un magasin dans les appels qui le proposent.

### 2.1 Configuration de connexion

| Information | Règle d’intégration |
|---|---|
| Origine de l’API | Adresse HTTPS propre au tenant, fournie par Tekshelf. |
| Environnement | Test ou production, explicitement identifié. |
| Accès partenaire | Jeton `Bearer` fourni par Tekshelf pour un utilisateur en lecture, limité aux magasins autorisés ; procédure d’obtention à compléter. |
| Magasin | Identifiant `storeId` (UUID) utilisé lorsque l’appel le demande. |
| Ressources | Identifiants Tekshelf des bacs (`deviceId`) et des produits ; à conserver avec le contexte du tenant. |

**Convention de cette documentation :** `TENANT_ORIGIN` désigne uniquement le protocole et l’hôte, sans barre finale ni suffixe `/api`.

Exemple d’assemblage, avec un domaine fictif non opérationnel :

```text
TENANT_ORIGIN = https://tenant-a.example.invalid
Chemin = /api/promo-bins?storeId={storeId}
URL appelée = TENANT_ORIGIN + chemin
```

Les adresses de test et de production se configurent séparément dans No Waste.

### 2.2 Gestion de plusieurs tenants par No Waste

No Waste conserve, pour chaque connexion client, l’origine du tenant, l’environnement, les accès associés et la correspondance avec les magasins. Une requête est toujours envoyée au tenant de la ressource concernée.

Les identifiants sont à considérer dans leur contexte : le stockage côté No Waste doit associer au minimum l’environnement, le tenant et l’identifiant de ressource. Une connexion accordée à un tenant n’autorise pas à lire un autre tenant.

Un `storeId` est un filtre et un contexte métier ; il ne remplace pas le contrôle des droits côté serveur. L’absence de filtre ne doit jamais ouvrir l’accès à des magasins non autorisés.

Aucune API centrale de découverte de tous les tenants n’est définie ici. Les adresses initiales sont fournies par Tekshelf ; leur provisionnement automatisé pourra être spécifié séparément si nécessaire.

## 3. Actions à permettre ou à exclure pour No Waste

Périmètre proposé pour No Waste : lecture seule.

| Action métier | Décision proposée | Conditions et limites |
| --- | --- | --- |
| Consulter les magasins autorisés | Autoriser | Uniquement dans le périmètre de la connexion partenaire. |
| Lister les Shelfy d’un magasin | Autoriser | `GET /api/promo-bins?storeId=…` : état courant, produits, remplissage et fraîcheur en un appel. |
| Consulter l’état d’un bac | Autoriser | Par identifiant ou par numéro de série. |
| Consulter l’historique de remplissage | Autoriser | `fill-history` : 30 jours au plus, un point par tranche de 15 min, 1 h ou 4 h selon la période. |
| Vérifier la fraîcheur des mesures | Autoriser | `acquisition` : dernière et prochaine mesure. |
| Rechercher ou consulter un produit | Autoriser | Catalogue accessible au tenant. |
| Créer, modifier ou supprimer un bac, un produit ou une association | Exclure | Hors périmètre : l’accès est en lecture seule. |
| Passer un bac en maintenance, le déplacer, l’installer | Exclure | Réservé à Tekshelf. |
| Enregistrer ou réécrire une mesure, une quantité ou l’historique | Exclure | Les résultats proviennent du système Tekshelf ; l’historique est préservé. |
| Modifier calibration, coefficients, firmware ou configuration matérielle | Réserver à Tekshelf | Hors gestion quotidienne du partenaire. |
| Créer des tenants, changer leurs domaines ou transférer du matériel | Réserver à Tekshelf | Administration de la plateforme. |
| Gérer comptes, rôles ou secrets de la plateforme | Réserver à Tekshelf | Aucun droit administrateur requis pour No Waste. |
| Recevoir les mises à jour automatiquement | Hors périmètre initial | Aucun webhook sortant : No Waste interroge l’API périodiquement. |

### 3.1 Droits d’accès

Les accès partenaires sont limités aux ressources des magasins autorisés. Un bac d’un autre magasin renvoie le même refus `403` qu’un droit manquant. Le compte du partenaire est un compte en lecture ; il ne doit pas porter de droit de gestion.

### 3.2 Périmètre de gestion

L’objectif est de couvrir le suivi quotidien du remplissage des bacs. L’administration technique de la plateforme et toute modification demeurent assurées par Tekshelf.

## 4. Comprendre les objets

| Terme utilisé dans cette documentation | Objet de l’API | Rôle |
|---|---|---|
| Pharmacie / magasin | `store` | Périmètre métier auquel rattacher les bacs et les droits d’accès. |
| Shelfy | `promo_bin`, identifié par `deviceId` et `serial` | Le bac promo équipé de son dispositif peseur ; lu via `/api/promo-bins`. |
| Produit du bac | `products[]` | Produits associés au bac : `productId`, `name`, `brand`, `ean`, `unitWeight`. |
| Référence 100 % | `weight.baseG` | Poids du bac plein, base du pourcentage de remplissage. |
| Indicateur de remplissage | `indicator` | `mode`, `units`, `percent` et `status`. |
| Mesure | point de `fill-history` | Poids et remplissage à une date ; voir la section 5.2. |

### Indicateur de remplissage

Chaque bac porte un `indicator` :

| Champ | Signification |
|---|---|
| `mode` | `units` (nombre d’unités, bac mono-produit au poids unitaire confirmé), `percent`, ou `pending` (pas encore de valeur affichable). |
| `units` | Nombre d’unités estimé, en mode `units` seulement. |
| `percent` | Remplissage par rapport à la référence 100 %. |
| `status` | État selon les seuils : avertissement à 30 %, critique à 10 %. |

La quantité en unités n’est donnée que pour un bac mono-produit : pour un bac mixte, seul le pourcentage de remplissage est fiable.

## 5. Référence technique

Les exemples utilisent des identifiants fictifs. Les schémas détaillés non renseignés sont signalés ; la procédure d’authentification et les données de test restent à fournir par Tekshelf.

### 5.1 Lister et consulter les bacs

| Méthode | Chemin | Utilisation |
|---|---|---|
| `GET` | `/api/promo-bins?storeId={storeId}` | Lister les bacs d’un magasin. |
| `GET` | `/api/promo-bins/{deviceId}` | Consulter un bac. |
| `GET` | `/api/promo-bins/by-serial/{serial}` | Retrouver un bac par son numéro de série. |

La liste exige `storeId` (sinon `400 INVALID_QUERY`) et renvoie `{ items, total }`. Chaque élément contient `shelfy` (identité, produits, indicateur de remplissage, état de mise en service), `acquisition` (fraîcheur) et `weight` (`weightG`, dernier poids stable, et `baseG`, référence 100 %).

### 5.2 Historique de remplissage

`GET /api/promo-bins/{deviceId}/fill-history`

| Paramètre | Règle |
|---|---|
| `hours` | De 12 à 720 (12 heures à 30 jours). |
| `days` | De 1 à 7. Ne pas combiner avec `hours` (`400 INVALID_QUERY`). |
| `page` | À partir de 1 (défaut 1). |
| `limit` | De 1 à 200 (défaut 200). |

Une période tient toujours en une page de 200 points au plus. Le pas d’un point dépend de la période :

| Période demandée | Un point toutes les |
|---|---|
| jusqu’à 48 h | 15 minutes |
| de 49 h à 192 h | 1 heure |
| au-delà de 192 h (30 jours au plus) | 4 heures |

La valeur d’un point est la dernière mesure de sa tranche.

Réponse :

| Champ | Signification |
|---|---|
| `from`, `to`, `hours`, `days`, `bucketS` | Fenêtre lue et durée d’une tranche, en secondes. |
| `unit` | `percent` si une référence 100 % est connue, sinon `grams`. |
| `reference` | `{ weightG, since }` : poids de référence et date de sa prise ; `null` si inconnue. |
| `thresholds` | `warningPercent` (30) et `criticalPercent` (10). |
| `dataSince` | Début des données exploitables : dernière modification des produits du bac. Aucune mesure antérieure n’est rendue. |
| `lastMeasureAt` | Date de la dernière mesure reçue. |
| `points[]` | `at`, `weightG`, `percent` (ou `null`), `restock`. |
| `page`, `limit`, `total`, `hasMore` | Pagination. |
| `truncated` | `true` si la fenêtre dépasse 50 000 mesures lues ; seules les plus récentes sont prises en compte. |

Règles d’interprétation :

- `percent` vaut `null` pour les mesures antérieures à la prise de la référence 100 %. Utiliser alors `weightG`.
- `restock` signale une hausse d’au moins 20 g entre deux mesures consécutives. C’est une détection par seuil, pas un événement de réassort enregistré.
- Un changement de produit repart d’une courbe vide : ne pas relier les points de part et d’autre de `dataSince`.

### 5.3 Fraîcheur des mesures

`GET /api/promo-bins/{deviceId}/acquisition`

| Champ | Signification |
|---|---|
| `lastAt` | Date de la dernière mesure. |
| `lastContactAt` | Dernier contact du bac. |
| `nextAt` | Prochaine mesure attendue. |
| `nextEstimated` | `true` si `nextAt` est une estimation. |
| `intervalS` | Intervalle de réveil, en secondes. |

### 5.4 Autres lectures

| Méthode | Chemin | Utilisation |
|---|---|---|
| `GET` | `/api/furnitures?storeId={storeId}&page=1&limit=100` | Lister les meubles d’une pharmacie ; les Shelfy ont le type `promo_bin`. |
| `GET` | `/api/furnitures/map?storeId={storeId}` | Vue enrichie des meubles et de leurs données de stock. |
| `GET` | `/api/furnitures/{furnitureId}` | Consulter un meuble. |
| `GET` | `/api/products?ean={code}` ou `?search={texte}` | Rechercher un produit (EAN, master CIP, CIP13 ou nom). |
| `GET` | `/api/products/{productId}` | Consulter un produit. |
| `GET` | `/api/devices/{deviceId}` | Consulter un dispositif. |
| `GET` | `/api/events?storeId={storeId}&deviceId={deviceId}` | Journal d’événements paginé. |

Le journal d’événements ne contient pas les mesures des Shelfy : pour le suivi du remplissage, utiliser l’historique de la section 5.2.

## 6. Parcours d’intégration à couvrir

### 6.1 Ouvrir l’espace bacs d’une pharmacie

1. Retrouver la connexion client No Waste : tenant, environnement et magasin.
2. Utiliser l’origine d’API propre à ce tenant et les accès associés.
3. Lister les bacs du magasin : `GET /api/promo-bins?storeId={storeId}`.
4. Afficher les produits, l’indicateur de remplissage et la date de dernière mesure.
5. Présenter distinctement une donnée valide, une donnée absente et une donnée ancienne.
6. Rafraîchir les données selon la cadence convenue avec Tekshelf.

L’ouverture de l’écran ne doit pas être présentée comme un réveil ou une nouvelle pesée du matériel.

### 6.2 Suivre l’évolution du remplissage

1. Lister les bacs du magasin (parcours 6.1).
2. Pour chaque bac, lire l’historique : `GET /api/promo-bins/{deviceId}/fill-history?hours=48`.
3. Dédoublonner les points reçus par leur date `at` : il n’existe pas de curseur, les fenêtres se recouvrent.
4. Calculer la rotation côté No Waste à partir de la baisse de `percent` (ou de `weightG` si `percent` est `null`).

Cadence recommandée : pour conserver le pas de 15 minutes, demander `hours=48` au moins une fois toutes les 48 heures (par exemple chaque jour). Une lecture sur 30 jours (`hours=720`) donne un point toutes les 4 heures.

### 6.3 Gérer une indisponibilité

No Waste conserve la dernière donnée connue avec sa date et signale son ancienneté. Une perte de connexion ne transforme pas un bac en rupture. Le bac se réveille à intervalles réguliers : l’absence de mesure récente n’est pas une rupture ; la fraîcheur se juge avec `lastMeasureAt` et `acquisition`. Après reprise, la relecture de l’historique permet de retrouver l’état courant.

## 7. Données et règles d’affichage

### 7.1 Informations à afficher

| Information | Règle d’affichage |
|---|---|
| Identité du bac | Identifiant, nom, numéro de série et magasin, interprétés dans le contexte du tenant. |
| Produits présentés | Produits associés au bac. |
| Poids | `weightG` en grammes (unité à confirmer) ; référence 100 % dans `baseG`. |
| Quantité | Indiquer qu’il s’agit d’une estimation dérivée du poids ; disponible seulement en mono-produit. |
| Remplissage | Pourcentage par rapport à la référence 100 % ; la référence peut évoluer. |
| Dernière mise à jour | `lastMeasureAt` (historique) ou `lastAt` (acquisition). |

### 7.2 Valeurs absentes et fraîcheur

- `null` ou absence de mesure signifie « inconnu » ; ce n’est pas équivalent à zéro.
- Une mesure ancienne peut rester visible avec sa date et une indication de fraîcheur.
- Les règles qui rendent une donnée « ancienne » dépendent de la cadence du bac (`intervalS`) et doivent être fixées avec Tekshelf.
- La consultation fréquente de l’API n’augmente pas la fréquence des relevés physiques.

## 8. Intégration des visuels dans No Waste

L’API de données et l’intégration de l’interface doivent être décrites séparément.

| Mode envisagé | Principe | À vérifier |
|---|---|---|
| Vue Tekshelf intégrée | No Waste affiche une vue hébergée par Tekshelf dans son interface. | Existence d’une vue intégrable, connexion transparente, droits limités à la pharmacie, navigation et adaptation mobile. |
| Interface développée par No Waste | No Waste construit les écrans à partir de l’API. | Schémas stables, règles d’affichage, états sans données. |

Le choix entre ces deux modes reste à arrêter.

## 9. Points à confirmer

| Sujet | Qui confirme ? | Question à régler |
|---|---|---|
| Accès API | Tekshelf | Fournir l’adresse et les accès de test, la procédure d’authentification et les schémas de réponse encore incomplets. |
| Unité du poids | Tekshelf | `weightG` est annoncé en grammes ; confirmer l’unité réellement servie. |
| Lecture incrémentale | Tekshelf | Un curseur sur la date de capture est-il nécessaire pour la relecture périodique de l’historique ? |
| Mesures brutes | Les deux | L’historique donne une mesure par tranche ; un accès aux mesures unitaires est-il requis ? |
| Cadence et limites | Les deux | Fréquence de lecture et limites d’usage. |
| Intégration des visuels | Les deux | Intégrer une vue Tekshelf ou construire les écrans dans No Waste ? |
| Nom de la propriété `shelfy` | Tekshelf | La règle de nommage place `promo_bin` dans le code et Shelfy côté client : la propriété `shelfy` des réponses est-elle conservée telle quelle ou renommée ? |
