All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m53s
User request: 'review cap nhat va tai cau truc lai MD sao cho phu hop voi hien tai, cac phan thua va da dieu chinh co the bo ra luon hoac cap nhat lai'. Cleanup highlights: 1. Archive 2 file Phase 0 raw dump → docs/_archive/ (forms-spec-raw 657 line + workflow-raw 62 line). Update link reference 2 file goc. 2. Compact migration-todos.md 386 → 114 line (-71%). Collapse Phase 0-5 + Tier 3 + Sessions detailed thanh 1 bang summary. Detail xem session logs. Phase 6 iter 1+2 + Phase 7 active checklist. 3. Compact STATUS.md In Progress: bo ~17 row ✅ done (giu chỉ 5+ task pending: 3 PE feature gap + 4 optional polish + 2 Ops). Recently Done table giu day du history. 4. Update flows/README — tat ca 6 flow ✅ Implemented + them PE row reference architecture.md §9. 5. Update docs/CLAUDE.md — project layout co PurchaseEvaluations, _archive, skills 6 (3 dom + 3 ops). Roadmap them Phase 6 ✅ + Phase 7 WIP. Lien he them prod URL solutions.com.vn + SSH config + login admin. 6. Skill ef-core-migration: 13 migration label. Net delta: -800 line docs (chu yeu archive + collapse migration-todos).
274 lines
13 KiB
Markdown
274 lines
13 KiB
Markdown
# Workflow — Quy trình trình ký Hợp đồng TP/NCC/Tổ đội
|
||
|
||
> **Nguồn:** `QUY_TRINH/QT TRINH KY HOP DONG TP-NCC.docx`
|
||
> **Raw dump:** [`_archive/workflow-raw.md`](_archive/workflow-raw.md) — Phase 0 (archived)
|
||
> **Phase 3 deliverable:** Implement state machine + role guard + SLA timer + notification
|
||
|
||
## 1. Phạm vi
|
||
|
||
Áp dụng cho Hợp đồng/Phụ lục HĐ ký với: **Thầu phụ (NTP)** · **Nhà cung cấp (NCC)** · **Tổ đội (TĐ)**. Tham chiếu ISO 9001.
|
||
|
||
## 2. Glossary (viết tắt)
|
||
|
||
| Mã | Nghĩa | Role trong system |
|
||
|---|---|---|
|
||
| **BOD** | Ban Giám đốc | `Director` |
|
||
| **NĐUQ** | Người được ủy quyền | `AuthorizedSigner` |
|
||
| **CCM** | Phòng Kiểm soát Chi phí | `CostControl` |
|
||
| **PRO** | Phòng Cung ứng | `Procurement` |
|
||
| **FIN** | Phòng Tài chính | `Finance` |
|
||
| **ACT** | Phòng Kế toán | `Accounting` |
|
||
| **EQU** | Phòng Thiết bị | `Equipment` |
|
||
| **HRA** | Nhân sự - Hành chính (đóng dấu) | `HR_Admin` |
|
||
| **PB** | Phòng ban | `Department` |
|
||
| **BCH CT** | Ban chỉ huy công trường | `SiteCommand` |
|
||
| **PD** | Giám đốc Thi công | `ProjectDirector` |
|
||
| **PM** | Giám đốc Dự án | `ProjectManager` |
|
||
| **TPB** | Trưởng Phòng ban | `DeptManager` |
|
||
| **TBP** | Trưởng Bộ phận | `SectionLeader` |
|
||
| **QS/NV.PB** | QS công trường / Nhân viên PB | `Drafter` (người soạn) |
|
||
| **NTP/NCC/TĐ** | Đối tác ký HĐ | Partner (external — không login) |
|
||
|
||
## 3. State Machine (9 phase)
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> DangChon: Tạo mới
|
||
DangChon --> DangSoanThao: Chọn NCC xong (chuyển Drafter)
|
||
DangSoanThao --> DangGopY: Gửi email góp ý (7d)
|
||
DangGopY --> DangDamPhan: Nhận xong comment (7d)
|
||
DangDamPhan --> DangInKy: Thỏa thuận xong (7d)
|
||
DangInKy --> DangKiemTraCCM: Đã ký nháy QS/PD/TPB, chuyển CCM (1d)
|
||
DangKiemTraCCM --> DangTrinhKy: CCM ký nháy xong (3d)
|
||
DangTrinhKy --> DangDongDau: BOD/NĐUQ ký duyệt (1d)
|
||
DangDongDau --> DaPhatHanh: HRA đóng dấu xong
|
||
DaPhatHanh --> [*]: PB scan + gửi bản gốc + lưu Filing
|
||
|
||
DangSoanThao --> TuChoi: Cancel
|
||
DangGopY --> DangSoanThao: Revise
|
||
DangKiemTraCCM --> DangSoanThao: CCM reject
|
||
DangTrinhKy --> DangSoanThao: BOD reject
|
||
TuChoi --> [*]
|
||
```
|
||
|
||
## 4. Chi tiết từng phase
|
||
|
||
| # | Phase (state) | Role thực hiện | Input | Output | SLA | Form liên quan |
|
||
|---|---|---|---|---|---|---|
|
||
| 1 | `DangChon` — Lựa chọn NTP/NCC | `PB` / `BCH CT` | Yêu cầu công việc | Chốt đối tác | — | (theo Quy trình Cung ứng SOL-PRO-SP-001) |
|
||
| 2 | `DangSoanThao` — Soạn thảo HĐ | `Drafter` (QS/NV.PB) + `TBP`/`TPB` check | Template HĐ | Dự thảo HĐ | **7 ngày** | FO-002.02/.03/.04/.05/.06 (chọn loại phù hợp) |
|
||
| 3 | `DangGopY` — Góp ý nội dung | `PD`/`PM`/`PRO`/`CCM`/`FIN`/`ACT`/`EQU` | Dự thảo | Comment (email → hệ thống) | **7 ngày** | (đính kèm comment thread) |
|
||
| 4 | `DangDamPhan` — Đàm phán | `Drafter` + `TBP`/`TPB`/`PD`/`PM` | Comment | Bản cuối thỏa thuận | **7 ngày** | — |
|
||
| 5 | `DangInKy` — In + đối tác ký | `Drafter` + **NTP/NCC/TĐ** (external) + `PD`/`PM`/`TPB` ký nháy | Bản cuối | 2 mặt, có chữ ký đối tác + ký nháy nội bộ | **1 ngày** | FO-002.01 (cover approval) |
|
||
| 6 | `DangKiemTraCCM` — CCM kiểm tra | `CCM` (`TP.CCM` ký nháy) | HĐ đã ký nháy | HĐ CCM approved | **3 ngày** | FO-002.01 |
|
||
| 7 | `DangTrinhKy` — BOD ký duyệt | `BOD` hoặc `NĐUQ` | HĐ CCM approved | HĐ ký duyệt | **1 ngày** | FO-002.01 |
|
||
| 8 | `DangDongDau` — Đóng dấu | `HRA`/`ISO` | HĐ đã ký | HĐ có dấu | — | — |
|
||
| 9 | `DaPhatHanh` — Phát hành + lưu trữ | `PB`/`BCH CT` + `CCM` | HĐ có dấu | Scan + bản gốc gửi NCC + lưu Filing System | — | — |
|
||
|
||
**Tổng SLA:** ~19 ngày (phase 2-7) cho 1 HĐ hoàn chỉnh.
|
||
|
||
## 5. Role × Phase Matrix (quyền xem/thao tác)
|
||
|
||
Ký hiệu: `R` = read, `W` = write/update draft, `A` = approve (chuyển phase tiếp), `-` = không có quyền.
|
||
|
||
| Role \ Phase | 1.Chọn | 2.Soạn | 3.GópÝ | 4.ĐàmPhán | 5.InKý | 6.CCMCheck | 7.BODKý | 8.ĐóngDấu | 9.PhátHành |
|
||
|---|---|---|---|---|---|---|---|---|---|
|
||
| **Drafter** (QS/NV.PB) | R | W,A | R | W,A | W,A | R | R | R | W |
|
||
| **TBP/TPB** | R | R,A | R,A | R,A | R,A | R | R | R | R |
|
||
| **PD/PM** | R | R | W,A | R,A | R,A | R | R | R | R |
|
||
| **PRO/EQU/FIN/ACT** | A | R | W,A | R | - | - | - | - | R |
|
||
| **CCM** | - | R | W,A | R | R | W,A | R | R | W |
|
||
| **BOD/NĐUQ** | - | - | - | - | - | - | A | - | R |
|
||
| **HRA** | - | - | - | - | - | - | - | W,A | R |
|
||
| **Admin** (system) | R | R | R | R | R | R | R | R | R (+ override) |
|
||
|
||
**Guard rule:**
|
||
- Đặc biệt: HĐ với **Chủ đầu tư** → có thể **bypass** phase 3 + 6 (không cần PRO/CCM góp ý + kiểm tra). Cờ `BypassProcurementAndCCM: bool` ở entity HĐ.
|
||
- Quá SLA mỗi phase không action → **auto-approve** (chuyển phase tiếp). Gửi notification warning khi còn 2h.
|
||
- Nếu cần kéo dài → bộ phận kiểm tra phải comment vào "Ý kiến" trước khi SLA hết.
|
||
|
||
## 6. Notification triggers
|
||
|
||
| Event | Người nhận | Kênh |
|
||
|---|---|---|
|
||
| Chuyển `DangSoanThao` → `DangGopY` | Tất cả role góp ý (PD/PM/PRO/CCM/FIN/ACT) | email + in-app |
|
||
| Chuyển `DangKiemTraCCM` | `CCM` | email + in-app |
|
||
| Chuyển `DangTrinhKy` | `BOD` + `NĐUQ` | email + in-app (high priority) |
|
||
| Quá SLA 80% thời gian | Role đang giữ phase | in-app warning |
|
||
| Quá SLA → auto-approve | Drafter + role giữ phase | email + in-app (log audit) |
|
||
| Reject (quay về `DangSoanThao`) | Drafter | email + in-app |
|
||
|
||
## 7. Data model implication (cho Phase 3 + Tier 3 versioned)
|
||
|
||
```csharp
|
||
// Domain
|
||
public enum ContractPhase {
|
||
DangChon = 1,
|
||
DangSoanThao,
|
||
DangGopY,
|
||
DangDamPhan,
|
||
DangInKy,
|
||
DangKiemTraCCM,
|
||
DangTrinhKy,
|
||
DangDongDau,
|
||
DaPhatHanh,
|
||
TuChoi
|
||
}
|
||
|
||
public class Contract : AuditableEntity {
|
||
public Guid Id { get; set; }
|
||
public string? MaHopDong { get; set; } // tự gen theo RG-001
|
||
public ContractType Type { get; set; } // HDTP, HDGK, NCC, HDDV...
|
||
public ContractPhase Phase { get; set; }
|
||
public Guid SupplierId { get; set; }
|
||
public Guid ProjectId { get; set; }
|
||
public decimal GiaTri { get; set; }
|
||
public bool BypassProcurementAndCCM { get; set; }
|
||
public DateTime? SlaDeadline { get; set; }
|
||
public bool SlaWarningSent { get; set; }
|
||
|
||
// Tier 3: pin policy version at create-time cho immutability
|
||
public Guid? WorkflowDefinitionId { get; set; }
|
||
|
||
public List<ContractComment> Comments { get; set; }
|
||
public List<ContractApproval> Approvals { get; set; }
|
||
public List<ContractAttachment> Attachments { get; set; }
|
||
}
|
||
|
||
public class ContractApproval {
|
||
public Guid ContractId { get; set; }
|
||
public ContractPhase FromPhase { get; set; }
|
||
public ContractPhase ToPhase { get; set; }
|
||
public Guid? ApproverUserId { get; set; } // null = system (SLA auto)
|
||
public DateTime? ApprovedAt { get; set; }
|
||
public ApprovalDecision Decision { get; set; } // Pending | Approve | Reject | AutoApprove
|
||
public string? Comment { get; set; }
|
||
}
|
||
|
||
// ==================== Tier 3: versioned workflow ====================
|
||
|
||
public class WorkflowDefinition : AuditableEntity {
|
||
public Guid Id { get; set; }
|
||
public string Code { get; set; } = ""; // "QT-MB", "QT-TP", "QT-NCC", ...
|
||
public int Version { get; set; } // 1, 2, 3, ... auto-increment per Code
|
||
public bool IsActive { get; set; } // chỉ 1 = true per ContractType
|
||
public ContractType ContractType { get; set; }
|
||
public string Name { get; set; } = ""; // "Quy trình Mua bán v02"
|
||
public string? Description { get; set; }
|
||
|
||
public List<WorkflowStep> Steps { get; set; } = new();
|
||
}
|
||
|
||
public class WorkflowStep {
|
||
public Guid Id { get; set; }
|
||
public Guid WorkflowDefinitionId { get; set; }
|
||
public int Order { get; set; } // thứ tự step trong định nghĩa
|
||
public ContractPhase Phase { get; set; } // target phase
|
||
public string Name { get; set; } = ""; // "Kiểm tra CCM"
|
||
public int SlaDays { get; set; } // SLA ngày cho phase này
|
||
|
||
public List<WorkflowStepApprover> Approvers { get; set; } = new();
|
||
}
|
||
|
||
public class WorkflowStepApprover {
|
||
public Guid Id { get; set; }
|
||
public Guid WorkflowStepId { get; set; }
|
||
public ApproverKind Kind { get; set; } // Role | User
|
||
public string AssignmentValue { get; set; } = ""; // RoleName hoặc UserId Guid string
|
||
}
|
||
|
||
public enum ApproverKind { Role = 1, User = 2 }
|
||
```
|
||
|
||
**Service chính:**
|
||
- `IContractWorkflowService.TransitionAsync(contractId, targetPhase, userId, comment)` — resolve policy, check guard, update state, tạo approval, emit notification
|
||
- `IContractCodeGenerator.GenerateAsync(projectId, type, supplierId)` — SERIALIZABLE transaction tránh race
|
||
- `SlaExpiryJob : BackgroundService` — 15min, auto-approve quá hạn với Decision=AutoApprove
|
||
- `IRealtimeNotifier` (SignalR impl) — push vào group User-{Id} khi Notification created
|
||
|
||
## 7bis. Policy resolution — versioned workflow
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant User as Actor
|
||
participant API as ContractsController
|
||
participant WF as ContractWorkflowService
|
||
participant DB as WorkflowDefinitions
|
||
|
||
User->>API: POST /contracts/{id}/transitions {targetPhase}
|
||
API->>WF: TransitionAsync(id, targetPhase, userId, comment)
|
||
|
||
alt Contract.WorkflowDefinitionId != null (Tier 3 pinned)
|
||
WF->>DB: Include(Steps.Approvers).First(Id == wfId)
|
||
DB-->>WF: def
|
||
WF->>WF: policy = Registry.FromDefinition(def)
|
||
else Admin override in WorkflowTypeAssignments
|
||
WF->>DB: Find(ContractType == c.Type)
|
||
DB-->>WF: override
|
||
WF->>WF: policy = Registry.ByName(override.PolicyName)
|
||
else Legacy fallback
|
||
WF->>WF: policy = Registry.For(c.Type) // hardcoded Standard/SkipCcm
|
||
end
|
||
|
||
WF->>WF: check (from, to) ∈ policy.Transitions
|
||
WF->>WF: check actor.Roles ∩ allowedRoles != ∅
|
||
WF->>DB: UPDATE Phase + INSERT ContractApproval + INSERT Notifications
|
||
WF-->>API: 200
|
||
```
|
||
|
||
## 7ter. Admin designer flow (tạo version mới)
|
||
|
||
```
|
||
Admin → /system/workflows → click type "HĐ Mua bán"
|
||
→ /system/workflows/MuaBan
|
||
→ thấy active version QT-MB-v01 + history
|
||
→ click "Tạo phiên bản mới" → Designer modal (có thể Clone từ v01)
|
||
- Code: QT-MB (auto-fill)
|
||
- Version: v02 (auto-compute max+1)
|
||
- Name + Description
|
||
- Steps (repeatable):
|
||
[Order 1] Phase=2 (DangSoanThao) SLA=7 days Approvers: +Role Drafter, +Role DeptManager
|
||
[Order 2] Phase=3 (DangGopY) SLA=7 days Approvers: +Role ProjectManager, +User {userId}
|
||
...
|
||
- Save → POST /api/workflows
|
||
BE: auto Version=max+1, deactivate QT-MB-v01.IsActive=0, insert v02.IsActive=1, atomic
|
||
→ trở về /system/workflows/MuaBan → v02 active, v01 archived "N HĐ còn chạy"
|
||
→ HĐ cũ pin v01 vẫn chạy v01 (Contract.WorkflowDefinitionId không đổi)
|
||
→ HĐ mới tạo sau đây sẽ pin v02
|
||
```
|
||
|
||
## 8. Business rules summary
|
||
|
||
1. **Một role chỉ có 1 phase active tại 1 thời điểm** cho 1 HĐ.
|
||
2. **Auto-approve nếu quá SLA** — phải log `Decision=AutoApprove` rõ ràng trong `ContractApproval`.
|
||
3. **Reject → quay về `DangSoanThao`** — Drafter nhận lại, toàn bộ approval trước đó bị invalidate (kept as history).
|
||
4. **Không cho xóa HĐ** đã qua phase 5 (`DangInKy`) — chỉ soft delete.
|
||
5. **Mã HĐ** gen theo `forms-spec.md § RG-001` — chỉ gen khi transition sang phase `DangDongDau` (8).
|
||
6. **Audit log** — mọi transition đều insert `ContractApprovals` row với actor + timestamp + phase before/after.
|
||
7. **Versioned workflow** — `Contract.WorkflowDefinitionId` pin tại create-time, **không update sau đó**. Admin tạo version mới ảnh hưởng HĐ tương lai, HĐ cũ giữ version cũ.
|
||
8. **Chỉ 1 active version per ContractType** — enforce qua business logic trong `CreateWorkflowDefinitionCommand` (atomic deactivate + insert).
|
||
|
||
## 9. Code pointers (Tier 3)
|
||
|
||
**Domain:**
|
||
- `Domain/Contracts/WorkflowDefinition.cs`
|
||
- `Domain/Contracts/WorkflowStep.cs`
|
||
- `Domain/Contracts/WorkflowStepApprover.cs` (+ `ApproverKind` enum)
|
||
- `Domain/Contracts/WorkflowPolicy.cs` (record + `WorkflowPolicies.Standard/SkipCcm` + `WorkflowPolicyRegistry.FromDefinition` + `ForContract`)
|
||
|
||
**Application:**
|
||
- `Application/Contracts/WorkflowAdminFeatures.cs`:
|
||
- `GetWorkflowAdminOverviewQuery` — landing per-type + active + history
|
||
- `CreateWorkflowDefinitionCommand` — auto Version + atomic deactivate old
|
||
|
||
**Infrastructure:**
|
||
- `Infrastructure/Services/ContractWorkflowService.cs` — `LoadPolicyAsync(contractId)` resolution order
|
||
|
||
**Api:**
|
||
- `Api/Controllers/WorkflowsController.cs` — GET overview, GET per-type, POST create-version
|
||
|
||
**FE-Admin:**
|
||
- `fe-admin/src/pages/system/WorkflowsPage.tsx` — URL-driven, landing + per-type
|
||
- `fe-admin/src/components/workflow/WorkflowDesigner.tsx` — modal Steps + Approvers
|
||
- `fe-admin/src/components/workflow/DefinitionCard.tsx` — active + history card
|