feat(catalog): manage global categories from settings
Categories are the taxonomy both services and resources select from, but
until now they could only be reached through the service catalog, so every
environment ended up with its own spelling of "whole body".
- Settings > Categories page: global CRUD plus the "includes" edge
- POST/GET/DELETE /api/v1/service-category/{uuid}/includes — a DAG, kept
separate from `parent` because "hand" sits under both "whole body" and
"upper limb"; a cycle is refused with 422
- PUT /api/v1/resource/{uuid}/categories — full replacement, and a category
from another environment is rejected explicitly since the uuid arrives in
the request body where TenantFilter does not reach
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -499,6 +499,62 @@ override فقط وقتی اعمال میشود که `branch_uuid` به `valid
|
||||
است (`outpatient`/`inpatient`) که روی خودِ `ServiceItem` هم نشسته. با `ServiceSection` هم
|
||||
فرق دارد — آن «بخش کلینیک» است، این تاکسونومی کاتالوگ. عمق حداکثر ۴ سطح.
|
||||
|
||||
دسته **سراسری** است: یک بار در «تنظیمات ← دستهبندیها» (`/admin/service-categories`) ساخته
|
||||
میشود و سرویس و منبع فقط از همان انتخاب میکنند. صفحهٔ سرویس و منبع عمداً امکان ساخت دسته
|
||||
ندارند، وگرنه هر کاربر «تمام بدن» خودش را با املای خودش میسازد.
|
||||
|
||||
### یال «شامل بودن» — `CatalogCategoryInclude`
|
||||
|
||||
جدا از `parent`. `parent` سلسلهمراتب نمایشی است و هر دسته فقط یک والد دارد؛ یال شاملبودن یک
|
||||
**DAG** است، چون «دست» هم زیر «تمام بدن» است و هم زیر «اندام فوقانی». موتور انتخاب سرویس از
|
||||
همین یالها استفاده میکند تا رزرو همزمان «لیزر تمام بدن» و «لیزر دست» را رد کند.
|
||||
|
||||
| متد | مسیر | مجوز |
|
||||
|---|---|---|
|
||||
| GET | `/api/v1/service-category/{uuid}/includes` | `appointment_settings.view` |
|
||||
| POST | `/api/v1/service-category/{uuid}/includes` | `appointment_settings.update` |
|
||||
| DELETE | `/api/v1/service-category/{uuid}/includes/{childUuid}` | `appointment_settings.update` |
|
||||
|
||||
**POST body:** `{ "child_category_uuid": "<uuid>" }` — الزامی.
|
||||
|
||||
```jsonc
|
||||
// POST /api/v1/service-category/{whole}/includes → 201 (تکرار همان یال → 200، نه خطا)
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"uuid": "f06c1e09-00d8-4f6f-b63b-c41efd5291f1",
|
||||
"parent_uuid": null,
|
||||
"name": "دست (doc)",
|
||||
"sort_order": 0,
|
||||
"active": true,
|
||||
"children": []
|
||||
}
|
||||
}
|
||||
|
||||
// GET → آرایهای از همان شکل
|
||||
// DELETE → { "success": true, "data": null }
|
||||
```
|
||||
|
||||
حلقه رد میشود — «تمام بدن شامل دست» و بعد «دست شامل تمام بدن» → **422**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"data": null,
|
||||
"errors": [{
|
||||
"code": "ERR_VALIDATION_001",
|
||||
"message": "«دست (doc)» از قبل زیرمجموعهٔ «تمام بدن (doc)» است؛ این دو نمیتوانند شامل هم باشند",
|
||||
"field": "child_category_uuid"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
خطاها: `404` دستهٔ نامعتبر · `422` نبودِ `child_category_uuid`، حلقه، یا دستهٔ محیط دیگر.
|
||||
|
||||
### دستهٔ منبع — `PUT /api/v1/resource/{uuid}/categories`
|
||||
|
||||
مستند کامل در [`resource.md`](resource.md).
|
||||
|
||||
## تستها
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user