Backend Forms:
- Domain/Forms: ContractTemplate (FormCode, Name, ContractType, FileName, StoragePath, Format, FieldSpec JSON, IsActive) + ContractClause
- EF config voi unique FormCode + query filter IsDeleted
- DbSets + IApplicationDbContext update
- Migration AddForms (bang 14 total)
- Packages: DocumentFormat.OpenXml 3.x + ClosedXML 0.105+
- Application/Forms:
- IFormRenderer interface + RenderResult record
- FormFeatures.cs: List/Get/Render CQRS
- IWebHostEnvironmentLocator (abstract IWebHostEnvironment)
- Infrastructure/Forms:
- DocxRenderer: OpenXml-based placeholder {{field}} replace, handle split runs (gom text tat ca <w:t> trong paragraph, replace, gan lai text dau + clear rest)
- XlsxRenderer: ClosedXML cell value replace
- FormRenderer router theo format docx/xlsx
- Api:
- FormsController: GET /templates (filter type, onlyActive), GET /templates/{id}, POST /templates/{id}/render (return file)
- WebHostEnvironmentLocator impl
- DbInitializer SeedContractTemplatesAsync: seed 8 template metadata, IsActive=true chi khi file ton tai
Templates vat ly:
- Copy 5 .docx/.xlsx tu FORM/ sang wwwroot/templates/
- 3 .doc (FO-002.02/03/06) chua convert: IsActive=false (Word COM bi stuck luc test, can retry voi DisplayAlerts=0 hoac LibreOffice)
- scripts/convert-doc-to-docx.ps1 (Word COM automation)
Frontend fe-admin:
- types/forms.ts: ContractTemplate + ContractTypeLabel
- pages/forms/FormsPage.tsx: list templates + Render dialog (paste JSON data → download .docx/.xlsx)
- Route /forms them vao App.tsx
Bug fix:
- SpaceProcessingModeValues namespace: wrap EnumValue<> full path
- SaveAs2($path, 16) thay vi SaveAs([ref], [ref]) — PowerShell type issue
- Word COM stuck: kill process, skip .doc cho MVP
Docs (theo yeu cau user):
- docs/gotchas.md MOI: 17 pitfalls nhom theo tech stack / EF Core / OpenXml / JSON / dev workflow
- .claude/skills/form-engine/SKILL.md: placeholder → full spec (algorithm + code pointers + API + limitations)
- .claude/skills/permission-matrix/SKILL.md: placeholder → full spec (BE policy + FE guard + seed + pitfalls)
- docs/HANDOFF.md MOI: brief 5 phut cho session sau (run quickstart + where we are + next steps + file tree + gotchas ref)
- docs/STATUS.md: update cumulative stats + next up Phase 3
- docs/changelog/migration-todos.md: tick Phase 2 iteration 1 items + add iteration 2 list
- docs/changelog/sessions/2026-04-21-1200-phase2-form-engine.md: session log
- CLAUDE.md root: them reference den gotchas + HANDOFF
E2E verified:
- GET /api/forms/templates (onlyActive=false) → 8 templates
- POST /api/forms/templates/{FO-002.05}/render voi data dict → HTTP 200 + file .docx 482KB (Microsoft Word 2007+ OK)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.4 KiB
5.4 KiB
Session 2026-04-21 12:00 — Phase 2 Form Engine MVP
Dev: Claude (Opus 4.7)
Duration: ~1h
Base commit: 54d6c9b
Làm được
Chunk D — Forms domain + packages
- NuGet:
DocumentFormat.OpenXml 3.x+ClosedXML 0.105+(Infrastructure) - Domain:
Forms/ContractTemplate(FormCode, Name, ContractType, FileName, StoragePath, Format, FieldSpec JSON, IsActive),Forms/ContractClause(Code, Name, Content rich text, Version) - EF configs: unique index FormCode, query filter IsDeleted
- DbSets +
IApplicationDbContextupdate - Migration
AddForms
Chunk E — Renderer + Application + Controller
Application/Forms/Services/IFormRenderer+RenderResultrecordInfrastructure/Forms/DocxRenderer— OpenXml-based, xử lý placeholder bị split runs (gom text tất cả<w:t>trong paragraph → replace → gán lại vào text đầu)Infrastructure/Forms/XlsxRenderer— ClosedXML-based, replace cell value nếu là text chứa placeholderInfrastructure/Forms/FormRenderer— router theo format docx/xlsx- Register
IFormRendereras Singleton trong Infrastructure DI Application/Forms/FormFeatures.cs:ListContractTemplatesQuery(filter type + onlyActive)GetContractTemplateQueryRenderTemplateCommand+ Validator + Handler (resolve absolute path quaIWebHostEnvironmentLocator)
IWebHostEnvironmentLocatorinterface trong Application — abstractIWebHostEnvironment, impl ở Api layer (WebHostEnvironmentLocator)Api/Controllers/FormsController: GET templates, GET single, POST render (return file)
Chunk F — Convert + Seed + FE
scripts/convert-doc-to-docx.ps1— Word COM automation. Chạy thử bị stuck (hidden dialog) → killed process. 3 file.docchưa convert — đánh dấu IsActive=false trong seed.- Copy 5 file
.docx/.xlsxtừFORM/→wwwroot/templates/ DbInitializer.SeedContractTemplatesAsync— seed 8 template, check file exists để set IsActive- FE:
types/forms.ts(ContractTemplate + ContractTypeLabel),pages/forms/FormsPage.tsx— list + render button + Dialog điền JSON data → download file - Route
/formsadd vào App.tsx (menu Layout đã có path sẵn)
E2E verified
GET /api/forms/templates?onlyActive=false → 8 templates (5 active, 3 inactive)
POST /api/forms/templates/{fo-002.05-id}/render
body: {"benA_tenCongTy": "Solutions Construction", "giaTri": "150,000,000 VND", "ngayKy": "21/04/2026"}
→ HTTP 200, file .docx 482KB (Microsoft Word 2007+ format) — OK mở được bằng Word
TS check fe-admin pass.
Bug gặp + fix
| Bug | Fix |
|---|---|
SpaceProcessingModeValues namespace không tìm thấy |
Dùng full path + wrap EnumValue<> |
Word COM SaveAs([ref]) type conversion error |
Đổi sang SaveAs2($path, 16) |
| Word COM stuck (2 process, 164s CPU) | Kill process, fallback: skip .doc convert, đánh dấu IsActive=false cho 3 template tương ứng |
| Edit tool "File has not been read yet" sau system-reminder interrupt | Read lại rồi Write full file |
Docs updates trong session này
docs/gotchas.md(MỚI) — 17 bẫy đã gặp từ Phase 0 → 2, nhóm theo: tech stack constraints, EF Core, OpenXml/ClosedXML, System.Text.Json, file ops, dev workflow.claude/skills/form-engine/SKILL.md— update từ placeholder → full spec với code pointers, algorithm, API, limitations.claude/skills/permission-matrix/SKILL.md— update từ placeholder → full spec với BE policy + FE guard usage + pitfallsdocs/STATUS.md— mark Phase 2 MVP donedocs/changelog/migration-todos.md— tick Phase 2 items đã xong
Handoff cho session tiếp theo
Phase 2 còn lại (iteration 2)
- Convert 3 file
.doc(retry Word COM vớiDisplayAlerts=0+ set timeout) HOẶC dùng LibreOffice headless - Field spec JSON mỗi template — cho phép FE render dynamic form thay vì điền JSON tay
- Form builder FE: dynamic render từ fieldSpec → validation → preview → submit
- Support
{{#loop}}...{{/loop}}block (cho table hạng mục lặp ở FO-002.05, FO-002.07) - PDF convert via LibreOffice headless (hoặc Aspose nếu mua license)
- Admin upload template mới qua UI (POST multipart)
- ContractClause rich text editor (TipTap) cho admin edit FO-002.04
Phase 3 — Workflow (sắp tới)
Xem docs/flows/contract-approval-flow.md.
Các việc lớn:
- Entity
Contract+ContractApproval+ContractComment+ContractAttachment IContractWorkflowService.TransitionAsync()với state guard + role guardIContractCodeGeneratortheo RG-001 với transaction SERIALIZABLESlaExpiryJobhosted service auto-approve- Email + in-app notification service
- FE Inbox + Contract detail + timeline UI
Còn optional (không block Phase 3)
- Users management FE (tạo user + gán role)
- Roles CRUD
- fe-user menu động (hiện tại chưa sync với AuthContext menu pattern từ fe-admin)
Blocker
- ⏳ Gitea remote URL
Thông số sau Phase 2 MVP
- Git commits: 4 (từ scaffold) + sắp thêm 1
- Backend LOC: ~1900 (thêm ~400 cho Forms)
- DB tables: 14 (thêm ContractTemplates + ContractClauses)
- API endpoints: ~23 (thêm Forms 3)
- FE pages: 6 (thêm Forms)
- Templates vật lý: 5 .docx/.xlsx trong
wwwroot/templates/