# CLAUDE.md — AI Agent Context > **Full content:** [`docs/CLAUDE.md`](docs/CLAUDE.md) --- **SOLUTION_ERP** — Hệ thống Quản lý Hợp đồng Nhà cung cấp / Thầu phụ / Tổ đội + **Phiếu Duyệt NCC tiền-HĐ** cho Công ty TNHH Xây dựng Solutions. Kiến trúc: **.NET 10 Clean Architecture + 2 React FE (admin + user) + SQL Server + IIS**. ## 🚀 BẮT ĐẦU SESSION — 5 file đọc trước tiên ``` 1. docs/STATUS.md ← Snapshot HIỆN TẠI (phase nào, việc gì đang làm — PE module CÒN CHỈNH NHIỀU) 2. docs/HANDOFF.md ← Brief 5 phút: session trước làm gì + cảnh báo session tiếp 3. docs/PROJECT-MAP.md ← Bản đồ toàn cảnh 4. docs/changelog/migration-todos.md ← Atomic tasks theo phase (Phase 7 PE refinement mới) 5. docs/workflow-contract.md ← ⭐ State machine 9 phase HĐ — base pattern cho PE workflow ``` ## ⚡ Quick Rules ### Backend — `.NET 10` Clean Architecture - Solution: `SolutionErp.slnx` ở root, projects ở `src/Backend/` - **4 layer:** `Api → Application ← Domain` + `Infrastructure → Application` - Pattern: **CQRS + MediatR**, **FluentValidation**, **AutoMapper** - Repository qua `IApplicationDbContext` interface (Application layer) - Auth: **JWT Bearer + ASP.NET Identity** - DB: **SQL Server** (LocalDB dev / SQL Server prod), **EF Core 10 Code-First migrations** - Error handling: `GlobalExceptionMiddleware` map exception → HTTP status - Commit scope tech stack: `Api` · `App` · `Domain` · `Infra` ### Frontend — 2 app React 18 + Vite + TS + shadcn/ui + TanStack Query - `fe-admin/` (port **8082**) · `fe-user/` (port **8080**) - Vite proxy `/api → http://localhost:5443` (SolutionErp.Api) - **Named export**, không default export (trừ `App`) - shadcn/ui copy-paste, **duplicate giữa 2 app là CÓ CHỦ ĐÍCH** (mỗi app UX riêng) - Auth context: `solution-erp-admin-token` / `solution-erp-user-token` ở `localStorage` - TanStack Query cho data fetching - **Node pin `>=20`** trong `engines`; CI pin `20.x` qua `.nvmrc` (bài học NamGroup — KHÔNG dùng Node latest trên CI) - UI **100% tiếng Việt** ### Database conventions - Schema: `dbo` (single schema) - Table: **PascalCase tiếng Anh** (Contracts, Suppliers, Projects, ContractApprovals, PurchaseEvaluations, ...) - PK: `Id` (Guid), FK: `{Entity}Id` - Audit fields: `CreatedAt`, `UpdatedAt`, `CreatedBy`, `UpdatedBy` (`BaseEntity`) - Soft delete: `IsDeleted`, `DeletedAt`, `DeletedBy` (`AuditableEntity`) - Migrations: `dotnet ef migrations add --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api` - **Hiện có 45 migration → 92 bảng** (Phase 10 COMPLETE + Phase 11 P11-A/B/C — Mig 34-42 HRM/Office/WorkflowApps/Attendance + Contract V2 (32-33) + WireWorkflowApps V2 (41) + LeaveBalance (42) + Holiday filtered-unique (43, S45) + Vehicle/Driver catalog (44, S51) + HRM-catalog filtered-unique 3× (45, S51 gotcha #57). V2 schema history S29-era bên dưới giữ nguyên — Mig 32+33 Plan B Contract V2 cookie-cutter mirror PE Mig 22-26 (S29). Mig 26 `AddPeLevelOpinionsForV2`: bảng mới `PurchaseEvaluationLevelOpinions` UNIQUE composite (PEId, LevelId), FK Cascade Pe + Restrict Level. Section 5 "Ý kiến cấp duyệt" V2 dynamic theo workflow đã pin: forEach Step (Phòng) → forEach Level (Cấp) → forEach NV → 1 OpinionBox. Service `ApproveV2Async` UPSERT auto khi NV duyệt — Q1=1B (sync gắn với Duyệt, KHÔNG form input rời). SignedByUserId track signer thật, FE banner "Admin duyệt thay" khi !== ApproverUserId. Comment empty → "(duyệt — không ý kiến)" placeholder. Phiếu V1 legacy fallback Mig 15 4 box readOnly (data history). Mig 25 `AddIsUserSelectableToApprovalWorkflows`: ALTER `ApprovalWorkflows` +`IsUserSelectable bit` (admin pin/unpin workflow nào cho user pick lúc create phiếu, multi-select độc lập IsActive). Backfill `WHERE IsActive=1 SET 1` giữ behavior cũ. Designer +badge "Cho user chọn" + button Ghim/Bỏ ghim. Workspace filter dropdown chỉ workflows `IsUserSelectable=true`. Mig 22-24 V2 schema (Session 17): `ApprovalWorkflows`/Steps/Levels — Quy trình > Bước (Phòng) > Cấp (N NV cụ thể qua ApproverUserId, OR-of-N cùng cấp). PE.ApprovalWorkflowId pin V2. PE.CurrentApprovalLevelOrder track. State machine 5 trạng thái: Nháp / Đã gửi duyệt / Trả lại (Phase riêng TraLai=98) / Từ chối / Đã duyệt. PE Service V2 wire match `actor.Id == ApproverUserId`. Contract V2 ĐÃ WIRE (Mig 32+33 Plan B S29 — cookie-cutter mirror PE V2: `ApproveV2Async` + `ContractLevelOpinions` UPSERT + Workspace V2 Select dropdown). Mig 21 V1 flat workflow vẫn live cho phiếu cũ.) ### Modules | Module | Namespace | Migration | Trạng thái | |---|---|---|---| | Contract (HĐ) | `Domain/Contracts/` | 1-11 | Feature-complete (7 ContractType × 9 phase) | | PurchaseEvaluation (Duyệt NCC tiền-HĐ) | `Domain/PurchaseEvaluations/` | 12, 13, 15 | Feature-complete — chỉ Export PDF còn pending (không quan trọng) | | Budget (Ngân sách dự án) | `Domain/Budgets/` | 14 | **Feature-complete** — BE + FE 3-panel + integration với PE/HD | | Master (Supplier/Project/Department) | `Domain/Master/` | 2, 10 | Feature-complete | | Identity (User/Role/Permission/MenuItem) | `Domain/Identity/` | 1, 3, 11 | Feature-complete (30 demo user — 16 sample + 14 Solutions thật) | | Forms (Template + Clause) | `Domain/Forms/` | 4 | Feature-complete | | Notifications | `Domain/Notifications/` | 6 | In-app + SignalR OK, email SMTP TODO | | **Tests** | `tests/SolutionErp.{Domain,Infrastructure}.Tests/` | — | **186 test pass** (58 Domain + 128 Infra) — CI gate + path filter docs-only skip | ### Commit convention ``` [CLAUDE] : ``` **Scope:** `Contract` · `PurchaseEvaluation` · `Budget` · `Form` · `Workflow` · `Supplier` · `Auth` · `Admin` · `Api` · `App` · `Domain` · `Infra` · `FE-Admin` · `FE-User` · `Tests` · `Docs` · `CICD` · `Scripts` · `Skill` ## 🧪 Tests (Phase 9 — Session 9 +6) ``` tests/ ├── SolutionErp.Domain.Tests/ (58 test — Domain policy: Workflow / PE / Budget) └── SolutionErp.Infrastructure.Tests/ (128 test) ├── Common/ (SqliteDbFixture + TestApplicationDbContext + IdentityFixture S9) ├── Services/ (17 codegen + 6 PE 2-stage approval S9) └── Application/ (6 test - PeWorkflowDefinition versioning) ``` **186 unit test pass** (58 Domain + 128 Infra). CI gate + path filter live. (S51: +5 `HrmConfigFilteredUniqueTests` — gotcha #57 extended to 3 HRM catalog (LeaveType/Shift/OtPolicy Mig 45) + Vehicle/Driver catalog Mig 44. EXT Department/Supplier/Project → worktree Mig 46.) ```bash dotnet test SolutionErp.slnx # chạy cả 2 test project ``` **Quy tắc timing test:** feature mới = test-after (UAT ổn → viết). Bug fix = test-before BẮT BUỘC (reproduce → fix). Critical algorithm (codegen/guard/financial/security) = test-before merge. Spec change = update test cũ + code chung commit. Skip: DTO mapping, CRUD master, FE snapshot. Detail `docs/rules.md §7`. ### CI/CD pipeline (3 fix lớn 29/04) - ✅ Manual checkout bypass github.com (fix gotcha #39 TCP timeout) - ✅ Path filter docs-only skip — `paths-ignore` (gotcha #41) - ⏸️ npm cache (gotcha #40 — failed, rolled back) **Tốc độ:** code commit ~3 phút / docs-only commit **0s** (skip). ## 🛠️ Skills (.claude/skills/) — 6 skill PHẢI dùng khi task khớp | Domain (3) | Ops (3) | |---|---| | `contract-workflow` — state machine + versioned WF | `dependency-audit-erp` — npm/dotnet CVE scan | | `form-engine` — render docx/xlsx + PDF | `ef-core-migration` — EF migration + 3-file rule | | `permission-matrix` — role × menu × CRUD | `iis-deploy-runbook` — 3 site IIS + win-acme + runner | **Audit định kỳ:** đầu mỗi tháng — combined skill + doc drift audit theo `docs/rules.md §6.4 + §9.4`. Cron `solution-erp-skill-audit-monthly` fire 9:00 ngày 1. Lần kế: **2026-07-01** (S44 đã chạy 06-01). Quy tắc: - KHÔNG bulk-clone repo skill 3rd party. Chỉ thêm skill PROJECT-SPECIFIC. Xem `docs/rules.md §9` đầy đủ. - KHÔNG rewrite toàn bộ MD định kỳ. Chỉ compact + patch drift. Xem `docs/rules.md §6.4` đầy đủ. ## 📖 Tài liệu quan trọng | File | Nội dung | |---|---| | [`docs/STATUS.md`](docs/STATUS.md) | **🔥 Current state** — đọc đầu tiên | | [`docs/HANDOFF.md`](docs/HANDOFF.md) | Brief 5 phút: session trước làm gì + next tasks | | [`docs/rules.md`](docs/rules.md) | ⭐ Coding conventions (BE Clean Arch, FE React, DB, Git, Docs) | | [`docs/architecture.md`](docs/architecture.md) | ⭐ Layered architecture + request lifecycle + deployment | | [`docs/PROJECT-MAP.md`](docs/PROJECT-MAP.md) | Bản đồ tổng quan | | [`docs/changelog/migration-todos.md`](docs/changelog/migration-todos.md) | Roadmap 5 phase + atomic tasks | | [`docs/CLAUDE.md`](docs/CLAUDE.md) | Full context — tech stack chi tiết | | [`docs/workflow-contract.md`](docs/workflow-contract.md) | State machine 9 phase HĐ + role matrix | | [`docs/forms-spec.md`](docs/forms-spec.md) | Catalog 8 form + quy định mã HĐ RG-001 | | [`docs/database/database-guide.md`](docs/database/database-guide.md) | DB conventions + migration workflow + cheatsheet | | [`docs/database/schema-diagram.md`](docs/database/schema-diagram.md) | ⭐ ERD + luồng DB + data flow 92 table (+ §11 PE + §12 Budget + §13 PEDeptOpinions + §14 Contract V2 LevelOpinions; §16+ Mig 32-45 pending) | | [`docs/flows/README.md`](docs/flows/README.md) | Index 6 flow (auth, permission, contract, form, SLA) | | [`docs/gotchas.md`](docs/gotchas.md) | ⭐ 57 bẫy đã gặp — đọc trước khi debug tương tự | | [`.claude/skills/`](.claude/skills/README.md) | 6 skill: contract-workflow, form-engine, permission-matrix, dependency-audit-erp, ef-core-migration, iis-deploy-runbook | | [`docs/guides/vps-setup.md`](docs/guides/vps-setup.md) | ⭐ Master runbook deploy VPS shared với VIETREPORT | ## ⚠️ Kết thúc session 1. Update `docs/STATUS.md` (`In Progress` → `Recently Done`) 2. Tick checklist tương ứng trong `docs/changelog/migration-todos.md` 3. Tạo session log `docs/changelog/sessions/YYYY-MM-DD-HHMM-{topic}.md` nếu đáng ghi 4. Commit `[CLAUDE] : ` 5. ⚠️ **Update `SolutionErp.slnx`** nếu có `.cs/.csproj` mới > Bỏ qua nếu chỉ trả lời câu hỏi, không sửa file nào.