# Inventory API > **Prefix:** `/api/v1/inventory-*` Per-tenant (doctor/clinic) consumable-stock management: **items** and **packages** (bundles of items). Every row is scoped to the caller's resolved entity (`doctor` / `clinic`), exactly like Tenant Tags — a tenant only ever sees and mutates its own inventory. Prices are stored and returned in **Rial** (integer). **Permission (all routes):** `IS_AUTHENTICATED_FULLY` (roles `doctor`, `clinic`; `secretary` resolves to its active clinic/doctor context). Item `status` is **derived, never stored**: `stock <= 0` → `out_of_stock`; `stock <= alertThreshold` → `low_stock`; else `in_stock`. --- ## Items ### GET `/api/v1/inventory-items` List the tenant's items plus the four derived stat counters. #### Response `200` ```json { "success": true, "data": { "items": [ { "uuid": "…", "name": "دستکش جراحی", "consumable": "جراحی", "category": "لوازم مصرفی و تزریقات", "unit": "عدد", "price": 250000, "stock": 150, "alertThreshold": 20, "status": "in_stock" } ], "stats": { "total": 1, "low": 0, "inStock": 1, "outOfStock": 0 } } } ``` ### GET `/api/v1/inventory-categories` Distinct non-empty `category` values **actually in use** by the tenant — powers the filter dropdown. For the full list of allowed categories use `inventory-meta`. #### Response `200` ```json { "success": true, "data": ["دارو", "لوازم آزمایشگاهی"] } ``` ### GET `/api/v1/inventory-meta` Backend-owned option lists for the item form. **Single source of truth** — the admin never hardcodes units/categories. Values are plain Persian strings stored as-is; the create/update endpoints validate against these lists. #### Response `200` ```json { "success": true, "data": { "units": ["عدد", "بسته", "…", "سی‌سی", "میلی‌لیتر", "…"], "categories": ["دارو", "لوازم مصرفی و تزریقات", "…", "سایر"] } } ``` ### POST `/api/v1/inventory-item` Create an item. #### Body | Field | Type | Required | Notes | |-------|------|----------|-------| | `name` | string | ✅ | نام کالا | | `consumable` | string | ❌ | «مصرفی» — یادداشت آزاد (سازگاری عقب‌رو) | | `category` | string | ❌ | دسته‌بندی؛ باید یکی از `inventory-meta.categories` باشد. خالی → `null`. فرم ادمین آن را الزامی می‌کند | | `unit` | string | ❌ | باید یکی از `inventory-meta.units` باشد. خالی → پیش‌فرض `عدد` | | `price` | integer | ❌ | Rial, Default `0` | | `stock` | integer | ❌ | Default `0` | | `alertThreshold` | integer | ❌ | Default `0` | #### Response `201` ```json { "success": true, "data": { "uuid": "…", "name": "دستکش جراحی", "status": "in_stock", "...": "..." } } ``` #### Errors | Status | Code | Cause | |--------|------|-------| | `422` | `ERR_VALIDATION_001` | `name` خالی است (field `name`) | | `422` | `ERR_VALIDATION_001` | `unit` خارج از لیست مجاز (field `unit`) | | `422` | `ERR_VALIDATION_001` | `category` خارج از لیست مجاز (field `category`) | | `403` | `ERR_FORBIDDEN_001` | پروفایل tenant یافت نشد | ### PATCH `/api/v1/inventory-item/{uuid}` Partial update. Any of the create fields may be sent. Returns `200` with the item, `404 ERR_NOT_FOUND_001` if the item does not belong to the caller, `422` on empty `name`. ### DELETE `/api/v1/inventory-item/{uuid}` Delete an item. `200` with `{ "message": "کالا حذف شد" }`, or `404` if not owned. Deleting an item also removes it from any package lines (FK `ON DELETE CASCADE`). --- ## Packages A package's `total` (Rial) and `available` (boolean) are **derived at read time** from its component items — `available` is `true` only if every component item has `stock >= amount`. ### GET `/api/v1/inventory-packages` #### Response `200` ```json { "success": true, "data": [ { "uuid": "…", "title": "پکیج شماره یک", "items": [ { "itemUuid": "…", "name": "ژل", "unit": "سی‌سی", "price": 1200000, "amount": 2 } ], "total": 2400000, "available": true } ] } ``` ### POST `/api/v1/inventory-package` #### Body | Field | Type | Required | Notes | |-------|------|----------|-------| | `title` | string | ✅ | نام پکیج | | `items` | array | ❌ | `[{ "itemUuid": "…", "amount": 2 }]` — references not owned by the caller are silently skipped | #### Response `201` — the serialized package (same shape as list). Errors: `422 ERR_VALIDATION_001` (empty `title`), `403 ERR_FORBIDDEN_001`. ### PATCH `/api/v1/inventory-package/{uuid}` Partial update. `title` renames; sending `items` **replaces** all component lines. `200` with the package, `404 ERR_NOT_FOUND_001` if not owned, `422` on empty `title`. ### DELETE `/api/v1/inventory-package/{uuid}` `200` with `{ "message": "پکیج حذف شد" }`, or `404` if not owned.