Aller au contenu principal

Produits (Administration)

Base URL : /api/v1/admin/org/products

CRUD complet des produits et gestion des mouvements de stock. Pour la consultation publique, voir Produits (catalogue).


GET /admin/org/products​

Liste admin des produits (actifs et inactifs).

Permissions : product.read

ParamTypeDéfautDescription
searchstring—Recherche (nom, code)
category_iduuid—Filtrer par catégorie
site_iduuid—Filtrer par site
is_activestring—"true" / "false"
limitinteger20Éléments par page
offsetinteger0Décalage

Réponse (200) :

{
"items": [
{
"id": "uuid",
"category_id": "uuid",
"code": "CAF",
"name": "Café",
"slug_date": null,
"meal_type": null,
"description": null,
"price_cents": 1000,
"price": 1000,
"price_formatted": "1 000 FCFA",
"stock_quantity": 50,
"is_active": true,
"available_days": [1, 2, 3, 4, 5],
"site_id": "uuid",
"reservation_deadline_days_before": null,
"reservation_deadline_time": null,
"image_path": null,
"low_stock_threshold": 10,
"created_at": "2025-01-10T10:00:00Z",
"updated_at": "2025-01-10T10:00:00Z",
"currency": "XOF"
}
],
"total": 42,
"summary": {
"total": 42,
"available": 38,
"low_stock": 3,
"out_of_stock": 1
},
"currency": "XOF"
}

GET /admin/org/products/{id}​

Détail d'un produit. Permissions : product.read Réponse : objet produit (même forme qu'un élément de liste).


POST /admin/org/products​

Crée un produit.

Permissions : product.create

ChampTypeRequisDescription
category_iduuidOuiCatégorie
namestringOuiNom
price_centsintegerOuiPrix
codestringNonCode
slug_datedateNonDate associée
meal_typestringNonVegetarian, Meat, Fish
descriptionstringNonDescription
stock_quantityintegerNonStock initial
is_activebooleanNonActif
available_daysinteger[]NonJours (1=lundi … 7=dimanche)
site_iduuidNonSite
reservation_deadline_days_beforeintegerNonDélai réservation (jours)
reservation_deadline_timetimeNonHeure limite réservation
image_pathstringNonChemin image
low_stock_thresholdintegerNonSeuil stock bas (défaut org)

Réponse (201) : { "message": "Produit créé", "product": {...} }


PUT /admin/org/products/{id}​

Met à jour un produit.

Permissions : product.update

Reprend les mêmes champs que la création, tous optionnels.

Sémantique de mise à jour

Certains champs (code, slug_date, meal_type, description, site_id, reservation_deadline_*, image_path) sont écrasés inconditionnellement : les omettre les efface (met à null). Les autres (name, price_cents, stock_quantity, is_active, available_days, low_stock_threshold, category_id) ne sont mis à jour que s'ils sont fournis. Ce n'est pas un vrai PATCH.

Réponse (200) : { "message": "Produit mis à jour", "product": {...} }


DELETE /admin/org/products/{id}​

Permissions : product.delete Réponse (200) : { "message": "Produit supprimé" }


Mouvements de stock​

Chaque mouvement est tracé dans stock_movements avec un type, un delta, un motif et l'acteur.

POST /admin/org/products/{id}/adjust-stock​

Ajustement manuel (inventaire).

Permissions : stock.adjust

{ "delta": -5, "reason": "Inventaire mensuel" }

delta peut être positif ou négatif. Le stock résultant ne peut pas être négatif.

Réponse (200) : { "message": "Stock ajusté" } — type correction.

POST /admin/org/products/{id}/purchase​

Réapprovisionnement (achat fournisseur).

Permissions : stock.purchase

{
"delta": 50,
"reason": "Commande fournisseur",
"unit_cost_cents": 500,
"expires_at": "2025-07-01T00:00:00Z"
}

delta doit être > 0. Champs optionnels : unit_cost_cents, batch_id, expires_at.

Réponse (200) : { "message": "Réapprovisionnement enregistré" } — type purchase.

POST /admin/org/products/{id}/production​

Production interne (plats préparés).

Permissions : stock.produce

{ "delta": 20, "reason": "Production du jour" }

delta doit être > 0.

Réponse (200) : { "message": "Production enregistrée" } — type production.

POST /admin/org/products/{id}/transfer​

Transfert entre sites.

Permissions : stock.transfer

{ "delta": 10, "reason": "Réappro intersite" }

delta peut être positif ou négatif (négatif vérifié contre le stock).

Réponse (200) : { "message": "Transfert enregistré" } — type transfer.

GET /admin/org/products/{id}/movements​

Historique des mouvements d'un produit.

Permissions : stock.read

ParamTypeDéfaut
limitinteger20
offsetinteger0
movement_typestring—

Réponse (200) :

{
"items": [
{
"id": "uuid",
"product_id": "uuid",
"product_name": "Café",
"product_code": "CAF",
"delta": 50,
"movement_type": "purchase",
"reason": "Commande fournisseur",
"performed_by": "uuid",
"performed_by_name": "Jean Mbeki",
"unit_cost_cents": 500,
"batch_id": null,
"expires_at": null,
"site_id": "uuid",
"site_name": "Buvette Principale",
"pos_id": null,
"pos_name": null,
"created_at": "2025-06-21T10:00:00Z"
}
],
"total": 1
}

Types de mouvement : purchase, production, sale, return, waste, expiry, transfer, correction, reservation.

GET /admin/org/movements​

Mouvements de tous les produits de l'organisation.

Permissions : stock.read

ParamTypeDescription
product_iduuidFiltrer par produit
movement_typestringFiltrer par type
date_fromdateDébut de plage
date_todateFin de plage
site_iduuidFiltrer par site
pos_iduuidFiltrer par point de vente
limit / offsetintegerPagination

Réponse (200) : { "items": [...], "total": n } (mêmes champs que ci-dessus).


POST /admin/org/products/import​

Importe des produits en lot (corps JSON, max 500).

Permissions : product.create

{
"products": [
{
"category_id": "uuid",
"name": "Thé vert",
"price_cents": 800,
"stock_quantity": 100,
"is_active": true
}
]
}

Réponse (200) : { "message": "1 produit importé", "created": 1, "products": [...], "currency": "XOF" }


Permissions​

PermissionDescription
product.read / product.create / product.update / product.deleteCRUD produit
stock.readLecture des mouvements
stock.adjust / stock.purchase / stock.produce / stock.transferMouvements de stock
product.manageSuper-permission couvrant stock.* et category.*

Voir aussi​