[CLAUDE] Docs: database-guide + 6 flow diagrams
docs/database/database-guide.md: - Conventions (naming, data types, audit fields, soft delete) - Schema hien tai (Identity tables sau migration Init) + seed 12 role + admin - Schema planned: Phase 1 dot 2 (Supplier/Project/Department + Permission Matrix) - Schema planned: Phase 3 (Contract + Approval + Comment + Attachment + Template + Clause + CodeSequence) - Mermaid ERD cho tung phase - Migration workflow (create/apply/revert) - Index strategy + unique indexes - Backup/restore SQL - Common pitfalls + SQL cheatsheet docs/flows/ — 6 flow documentation: - README.md: index - auth-flow.md: login/refresh/me/logout (IMPLEMENTED, sequence + edge cases + security checklist) - permission-flow.md: Phase 1 dot 2 - Role x MenuKey x CRUD resolution + FE guard + BE policy - contract-creation-flow.md: Phase 2 - Drafter flow chon template -> fill -> preview -> save draft - contract-approval-flow.md: Phase 3 - state machine 9 phase chi tiet + reject flow + timeline UI - form-render-flow.md: Phase 2 - OpenXml + ClosedXML + LibreOffice PDF convert - sla-expiry-flow.md: Phase 3 - BackgroundService auto-approve qua SLA + warning notify Update references: - CLAUDE.md (root): them 2 row Tai lieu quan trong - docs/CLAUDE.md: update project layout voi flows/ + database/ - docs/STATUS.md: log docs addition - docs/changelog/migration-todos.md: tick Phase 0 docs items Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
235
docs/flows/contract-creation-flow.md
Normal file
235
docs/flows/contract-creation-flow.md
Normal file
@ -0,0 +1,235 @@
|
||||
# Contract Creation Flow
|
||||
|
||||
> **Status:** 📝 Planned (Phase 2)
|
||||
> **Actors:** Drafter (QS/NV.PB)
|
||||
> **Entry:** User mở `/contracts/new` ở fe-user
|
||||
|
||||
## 1. Tổng quan
|
||||
|
||||
Drafter chọn loại HĐ → chọn template form → điền field động → preview → lưu draft. Sau đó có thể tiếp tục soạn hoặc submit lên phase `DangGopY` (xem [`contract-approval-flow.md`](contract-approval-flow.md)).
|
||||
|
||||
## 2. Sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor D as Drafter
|
||||
participant FE as fe-user<br/>/contracts/new
|
||||
participant API as ContractsController
|
||||
participant CMD as CreateContractCommandHandler
|
||||
participant TMPL as FormsController<br/>.GetTemplate
|
||||
participant REN as IFormRenderer
|
||||
participant DB
|
||||
participant FS as File Storage<br/>(wwwroot/uploads)
|
||||
|
||||
D->>FE: Click "Tạo HĐ mới"
|
||||
|
||||
D->>FE: Step 1 — Chọn loại HĐ<br/>(ContractType dropdown)
|
||||
FE->>API: GET /api/forms/templates?type={contractType}
|
||||
API->>DB: SELECT ContractTemplates WHERE Type = ? AND IsActive = 1
|
||||
API-->>FE: Template list<br/>[{id, formCode, name, fieldSpec}]
|
||||
|
||||
D->>FE: Step 2 — Chọn template (vd FO-002.05 Giao khoán)
|
||||
FE->>TMPL: GET /api/forms/templates/{id}
|
||||
TMPL->>DB: SELECT template + FieldSpec JSON
|
||||
TMPL-->>FE: fieldSpec (array of {name, type, required, ...})
|
||||
|
||||
FE->>FE: Dynamic form builder render form<br/>theo fieldSpec
|
||||
|
||||
D->>FE: Step 3 — Điền field (NCC, dự án,<br/>giá trị, hạng mục, nghiệm thu…)
|
||||
|
||||
D->>FE: Click "Preview"
|
||||
FE->>API: POST /api/forms/render<br/>{templateId, data}
|
||||
API->>REN: DocxRenderer.Render(template, data)
|
||||
REN->>REN: Load .docx template<br/>replace placeholder {{field}}<br/>with values
|
||||
REN->>FS: Write temp file uploads/preview/{guid}.docx
|
||||
REN-->>API: (bytes + temp path)
|
||||
API-->>FE: 200 file blob (inline hoặc Content-Disposition: inline)
|
||||
FE->>FE: Display PDF preview<br/>(convert server-side via LibreOffice)
|
||||
|
||||
D->>FE: Step 4 — Click "Lưu draft"
|
||||
FE->>API: POST /api/contracts<br/>{type, supplierId, projectId,<br/>templateId, draftData}
|
||||
API->>CMD: Send(CreateContractCommand)
|
||||
CMD->>CMD: Validate (FluentValidation)<br/>supplier exists, project active...
|
||||
CMD->>DB: INSERT Contracts (Phase=DangSoanThao,<br/>DraftData=JSON.serialize)
|
||||
CMD-->>API: ContractId
|
||||
API-->>FE: 201 Created + Location header
|
||||
|
||||
FE->>D: Redirect /contracts/{id}<br/>toast "Đã lưu draft"
|
||||
```
|
||||
|
||||
## 3. API chi tiết
|
||||
|
||||
### `GET /api/forms/templates?type=HopDongGiaoKhoan`
|
||||
|
||||
Response:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "a0b1-...",
|
||||
"formCode": "FO-002.05",
|
||||
"name": "Hợp đồng Giao khoán",
|
||||
"contractType": 2
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### `GET /api/forms/templates/{id}`
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"id": "a0b1-...",
|
||||
"formCode": "FO-002.05",
|
||||
"name": "Hợp đồng Giao khoán",
|
||||
"fieldSpec": {
|
||||
"sections": [
|
||||
{
|
||||
"title": "Thông tin hai bên",
|
||||
"fields": [
|
||||
{ "name": "benA_tenCongTy", "label": "Tên Bên A", "type": "string", "required": true },
|
||||
{ "name": "benA_diaChi", "label": "Địa chỉ Bên A", "type": "text" },
|
||||
{ "name": "benA_maSoThue", "label": "MST Bên A", "type": "string", "pattern": "^[0-9]{10,13}$" },
|
||||
{ "name": "benB_supplierId", "label": "NCC (Bên B)", "type": "reference", "table": "Suppliers" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Giá trị hợp đồng",
|
||||
"fields": [
|
||||
{ "name": "giaTri", "label": "Giá trị (VND)", "type": "decimal", "required": true, "min": 0 },
|
||||
{ "name": "ngayKy", "label": "Ngày ký dự kiến", "type": "date" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/forms/render`
|
||||
|
||||
Request:
|
||||
```json
|
||||
{
|
||||
"templateId": "a0b1-...",
|
||||
"data": {
|
||||
"benA_tenCongTy": "Công ty TNHH Xây dựng Solutions",
|
||||
"benA_diaChi": "123 Đường ABC, TP.HCM",
|
||||
"benB_supplierId": "d9e2-...",
|
||||
"giaTri": 150000000,
|
||||
"ngayKy": "2026-05-10"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response: binary stream `.docx` (or `.pdf` nếu convert).
|
||||
|
||||
### `POST /api/contracts`
|
||||
|
||||
Request:
|
||||
```json
|
||||
{
|
||||
"type": 2,
|
||||
"supplierId": "d9e2-...",
|
||||
"projectId": "p7a1-...",
|
||||
"departmentId": "dp3b-...",
|
||||
"templateId": "a0b1-...",
|
||||
"giaTri": 150000000,
|
||||
"draftData": { /* full field values */ }
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"id": "c5d6-...",
|
||||
"phase": "DangSoanThao",
|
||||
"createdAt": "2026-04-21T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Validation rules (FluentValidation)
|
||||
|
||||
```csharp
|
||||
public class CreateContractCommandValidator : AbstractValidator<CreateContractCommand>
|
||||
{
|
||||
public CreateContractCommandValidator(IApplicationDbContext db)
|
||||
{
|
||||
RuleFor(x => x.Type).IsInEnum();
|
||||
RuleFor(x => x.SupplierId).NotEmpty().MustAsync(SupplierExists);
|
||||
RuleFor(x => x.ProjectId).NotEmpty().MustAsync(ProjectActive);
|
||||
RuleFor(x => x.TemplateId).NotEmpty().MustAsync(TemplateActive);
|
||||
RuleFor(x => x.GiaTri).GreaterThan(0);
|
||||
RuleFor(x => x.DraftData).NotNull();
|
||||
// Template field validation — đọc FieldSpec + validate draftData match
|
||||
RuleFor(x => x).CustomAsync(async (cmd, ctx, ct) =>
|
||||
{
|
||||
var tpl = await db.ContractTemplates.FindAsync(cmd.TemplateId, ct);
|
||||
var spec = JsonSerializer.Deserialize<FormFieldSpec>(tpl.FieldSpec);
|
||||
// check required fields present in draftData
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. Side effects
|
||||
|
||||
| Action | Side effect |
|
||||
|---|---|
|
||||
| `POST /contracts` | INSERT `Contracts` (Phase=`DangSoanThao`) |
|
||||
| | AuditInterceptor: set `CreatedAt`, `CreatedBy` = Drafter.Id |
|
||||
| | (Phase 3) Emit `ContractCreatedEvent` domain event |
|
||||
| `POST /forms/render` preview | Write temp file `wwwroot/uploads/preview/{guid}.docx` (auto cleanup sau 1h) |
|
||||
| `POST /forms/render` với `persist=true` | Save final file vào `ContractAttachments` với `Purpose='Export'` |
|
||||
|
||||
## 6. UI wireframe (fe-user)
|
||||
|
||||
```
|
||||
/contracts/new
|
||||
┌────────────────────────────────────────────────────┐
|
||||
│ Tạo hợp đồng mới │
|
||||
├────────────────────────────────────────────────────┤
|
||||
│ Bước 1/4: Chọn loại hợp đồng │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ Trọn gói │ │ Giao │ │ Mua bán │ ... │
|
||||
│ │ NC+VT │ │ khoán │ │ │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||
├────────────────────────────────────────────────────┤
|
||||
│ Bước 2/4: Chọn template │
|
||||
│ ○ FO-002.02 Trọn gói nhân công + vật tư │
|
||||
│ ● FO-002.05 Giao khoán ← đã chọn │
|
||||
├────────────────────────────────────────────────────┤
|
||||
│ Bước 3/4: Điền thông tin │
|
||||
│ [Thông tin 2 bên] │
|
||||
│ Tên Bên A: [Công ty TNHH ...] │
|
||||
│ NCC (Bên B): [Autocomplete → chọn NCC] │
|
||||
│ [Giá trị HĐ] │
|
||||
│ Giá trị: [150,000,000 VND] │
|
||||
│ ... │
|
||||
├────────────────────────────────────────────────────┤
|
||||
│ Bước 4/4: Preview + Lưu │
|
||||
│ [PDF preview inline] │
|
||||
│ [← Quay lại] [💾 Lưu draft] [🚀 Submit góp ý] │
|
||||
└────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 7. Edge cases
|
||||
|
||||
| Case | Xử lý |
|
||||
|---|---|
|
||||
| NCC chưa có trong DB | FE modal "Tạo NCC mới" inline → callback back vào form |
|
||||
| Template bị deactivate khi đang soạn | Warning + cho phép tiếp tục với template cũ |
|
||||
| Draft bị trùng (user nhấn Save 2 lần) | Idempotency key header — return existing contract |
|
||||
| User mất kết nối khi preview | FE retry + show spinner; nếu fail 3 lần → disable button + show cached data |
|
||||
| Giá trị HĐ vượt ngân sách dự án | Warning (không block) — sẽ check lại ở phase CCM Review |
|
||||
| Template thiếu field bắt buộc | FormValidator block Save + highlight field đỏ |
|
||||
|
||||
## 8. Performance
|
||||
|
||||
- Template load 1 lần + cache TanStack Query `{queryKey: ['template', id], staleTime: 10min}`
|
||||
- Preview render: có thể tốn 1-3s cho .docx lớn → show spinner + allow cancel
|
||||
- Save draft < 500ms (chỉ INSERT 1 row)
|
||||
|
||||
## 9. Liên quan
|
||||
|
||||
- [`form-render-flow.md`](form-render-flow.md) — chi tiết render engine
|
||||
- [`contract-approval-flow.md`](contract-approval-flow.md) — sau Save, phase tiếp theo
|
||||
- [`../forms-spec.md`](../forms-spec.md) — spec 8 form
|
||||
Reference in New Issue
Block a user