Files
clinicpro/docs/api/inventory.md
T
hamed 5c4976d65f feat: Implement secretary permissions enforcement across multiple resources
- Added SecretaryAccessChecker to manage resource access for secretaries.
- Integrated permission checks for payments, inventory, and tags in relevant controllers.
- Updated PaymentController and PaymentMethodController to enforce secretary permissions.
- Enhanced TenantTagController to check permissions for tag management actions.
- Introduced tests for secretary resource enforcement, ensuring proper access control.
- Updated DoctorSecretary entity to include inventory and tags permissions.
- Created a comprehensive audit document for secretary permissions coverage and enforcement.
- Fixed potential crashes in SecretaryDashboard when rendering without doctor data.
2026-07-23 16:36:35 +03:30

5.4 KiB

Inventory API

Prefix: /api/v1/inventory-*

دسترسی منشی: برای ROLE_SECRETARY روی منبع inventory اعمال می‌شود (SecretaryAccessChecker): GET→view, POST→create, PATCH→update, DELETE→delete؛ نبودِ مجوز → 403. پیش‌فرضِ منشی برای این منبع همه false است. جزئیات: secretary.md.

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 <= 0out_of_stock; stock <= alertThresholdlow_stock; else in_stock.


Items

GET /api/v1/inventory-items

List the tenant's items plus the four derived stat counters.

Response 200

{
  "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

{ "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

{
  "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

{ "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

{
  "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.