chore(pm): vendor the Aramesh/PM platform under pm/ (squash of Aramesh/PM @ 91f3271)

The PM panel (PmPanelPage -> iframe /pm/) needs the project-management
platform served at /pm/ on the ERP domain. Source: org repo Aramesh/PM,
branch feature/project-management-platform. Update via:
git subtree pull --prefix=pm aramesh-pm feature/project-management-platform --squash
This commit is contained in:
MMD
2026-10-07 15:14:20 +03:30
87 changed files with 23922 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
# PM API Contract (v3) — Standalone Native Platform — پایه: /api/v1
پلتفرم مدیریت پروژه ساختمانی بومی (Standalone)، مستقل و بدون وابستگی به هیچ سیستم خارجی.
## Auth
- `POST /auth/login` `{username, password}` → `{data:{token, user:{id, username, display_name, role}}}`
- `GET /auth/dev-roles` (در حالت توسعه) → لیست نقش‌های آماده جهت ورود سریع
- `POST /auth/dev-login` `{role}` (در حالت توسعه) → دریافت سریع توکن نقش انتخابی
- همه endpointها (جز لاگین و فایل‌های عمومی) هدر `Authorization: Bearer <token>` می‌خواهند.
- خطاها: `{"error": {"code", "message", "field"?}}` — کدها: `VALIDATION_ERROR` (400)،
`UNAUTHORIZED` (401)، `FORBIDDEN` (403)، `NOT_FOUND` (404)، `CONFLICT` (409)،
`UPSTREAM_UNAVAILABLE` (503).
## تاریخ جلالی
- **همه تاریخ‌های قابل مشاهده در UI شمسی‌اند.** API هم ISO می‌پذیرد/می‌دهد و هم شمسی.
- هر تسک علاوه بر `start_date`/`due_date` (ISO) فیلدهای `start_date_jalali`/
`due_date_jalali` را هم برمی‌گرداند («1405/06/23»).
- `PATCH /tasks/{id}/dates` هر دو قالب را می‌پذیرد: `1405/06/23` یا `2026-09-14`
(سال ≥ 1700 میلادی، کمتر از آن شمسی تفسیر می‌شود). روز نامعتبر → 400.
- تبدیل‌ها در `backend/app/utils/jalali.py` و `frontend/src/lib/jalali.ts` انجام می‌شوند.
## Dashboard
- `GET /dashboard/today` → `{data:{summary:{problems,warnings,ok}, sections:{overdue,due_today,awaiting_qc,failed_qc,blocked,upcoming}}}`
- `awaiting_qc` = وضعیت‌های `pre_qc_pending|exec_qc_pending|ready`
- `failed_qc` = وضعیت‌های `pre_qc_failed|qc_failed_rework`
## Projects
- `GET /projects` → `[...{id, name, identifier, description, active, erp_project_id, created_at, stats:{total, awaiting_qc, failed_qc, overdue}}]`
- `POST /projects` → ساخت پروژه جدید
- `GET /projects/{id}` → جزئیات پروژه به همراه آمارهای تسک‌ها
- `PUT /projects/{id}` | `DELETE /projects/{id}`
- `GET /projects/{id}/gantt` → داده‌های گانت چارت پروژه
- `PATCH /projects/{id}/erp-mapping {erp_project_id}`
## Tasks
- `GET /tasks?project_id=&search=&qc_status=&status=&page=&page_size=`
- `POST /tasks` → ثبت فعالیت/تسک جدید
- `GET /tasks/{id}` → تسک + `relations` + `children` + **`qc`** + **`qc_definitions`** + `defects` + `open_defects`
- `PUT /tasks/{id}` → ویرایش اطلاعات تسک
- `DELETE /tasks/{id}` → حذف تسک
- `PATCH /tasks/{id}/progress {percentage: 0..100}`
- **قاعده ۱:** اگر `percentage > 0` و بازرسی پیش از اجرا تأیید نشده باشد → 400
«بازرسی پیش از اجرا تأیید نشده است (N سؤال باقی‌مانده)»
- **قاعده ۲:** اگر `percentage >= 100` و هر سؤال الزامی تأییدنشده/مردود باشد → 400
«تا زمانی که همه بازرسی‌های الزامی تأیید نشوند پیشرفت ۱۰۰٪ مجاز نیست (…)»
- `PATCH /tasks/{id}/dates {start_date?, due_date?}` (ISO یا شمسی)
- `POST /tasks/relations` | `DELETE /tasks/relations/{id}` → مدیریت روابط وابستگی تسک‌ها (FS, SS, FF, SF)
### شکل بلوک `qc` در جزئیات تسک
```json
{"pre": {"total":3,"passed":3,"failed":0,"pending":0,"na":0,"required_unresolved":0,"status":"passed"},
"exec": {"total":6,"passed":2,"failed":1,"pending":3,"na":0,"required_unresolved":4,"status":"pending"},
"all": { /* مجموع */ },
"status": "exec_qc_pending",
"can_start": true, "can_complete": false,
"block_reasons": ["تا زمانی که همه بازرسی‌های الزامی تأیید نشوند پیشرفت ۱۰۰٪ مجاز نیست (۴ سؤال باقی‌مانده، ۱ مورد مردود)"]}
```
### وضعیت‌های `qc_status` روی تسک
`none` · `pre_qc_pending` · `pre_qc_failed` · `ready` · `exec_qc_pending` ·
`in_progress` · `qc_failed_rework` · `final_passed`
## QC — تعریف (Definition)
### قالب‌های سراسری (قابل استفاده مجدد)
- `GET /qc/templates` → `[{id,name,activity_type,description,questions:[{id,phase,text,sort_order,required}]}]`
- `POST /qc/templates {name, activity_type?, description?, questions?:[…]}` → `{data:{id}}`
- `PATCH /qc/templates/{id} {name?,activity_type?,description?}`
- `DELETE /qc/templates/{id}`
- `POST /qc/templates/{id}/questions {phase,text,required?,sort_order?}`
- `PATCH /qc/questions/{qid} {text?,phase?,required?,sort_order?}`
- `DELETE /qc/questions/{qid}`
- `PUT /qc/templates/{id}/questions {questions:[…]}` — جایگزینی گروهی (برای ادیتور)
`phase` ∈ `"pre"` (پیش از اجرا) · `"during"` (هنگام اجرا) · `"after"` (پس از اجرا)
### تعریف مخصوص تسک (کپی مستقل — ویرایش آن قالب سراسری را تغییر نمی‌دهد)
- `GET /qc/tasks/{task_id}` → `{data:{task, definitions:[{id,name,source_template_id,created_at,questions:[{…,result,history_count}],summary}], readiness}}`
- `POST /qc/tasks/{task_id}/definition {name?, template_id?}` → **کپی عمیق** سؤالات قالب
- `PATCH /qc/definitions/{id} {name}` | `DELETE /qc/definitions/{id}`
- `POST /qc/definitions/{id}/questions {text,phase,required?,sort_order?}`
- `PATCH /qc/task-questions/{qid} {text?,phase?,required?,sort_order?}`
- `DELETE /qc/task-questions/{qid}`
## QC — اجرا (Execution)
- `PUT /qc/task-questions/{qid}/result {answer:"pass"|"fail"|"na", comment?}`
→ `{data:{question_id, answer, task_qc_status, readiness}}`
- پاسخ‌ها **append-only** هستند (تاریخچه می‌ماند؛ بازرسی مجدد پس از رفع عیب ممکن است)
- تعریف و اجرا کاملاً جدا هستند: هنگام ساخت/کپی سؤال هیچ پاس/ردی ثبت نمی‌شود.
- عیب‌ها:
- `POST /qc/defects {task_id, question_id?, title, description?, severity?, corrective_action?, photo_id?}`
- `POST /qc/defects/{id}/resolve {resolution_notes?}`
- عکس عیب: `POST /files` (multipart) → `{data:{id, url}}` سپس `photo_id`
## فایل‌ها
- `POST /files` multipart (jpg/png/webp تا ۱۰MB) → `{data:{id,url,filename}}`
- `GET /files/{id}`
## Materials / Resources (یکپارچگی ERP)
- `GET/POST /tasks/{task_id}/materials` · `PATCH/DELETE /materials/{req_id}`
- `GET /erp/materials?search=` · `GET /erp/projects`
- `POST /materials/{req_id}/reserve|purchase-request` · `GET /reservations` · `POST /reservations/{id}/release`
- `GET/PATCH /purchase-requests` · `POST /projects/{project_id}/erp-mapping`