Files
solution-erp/docs/rules.md
pqhuy1987 fe7ad8e4a3 [CLAUDE] Phase4: Report MVP + Docs Consolidation (rules, architecture, schema-diagram)
Backend Report:
- Application/Reports/Dtos/DashboardStatsDto: 5 KPI + PhaseCount + SupplierCount + ProjectCount + MonthlyValue
- Application/Reports/Queries/GetDashboardStats handler: total/active/overdue/published this month/totalValueActive + byPhase + top 5 NCC/du an + 12 thang monthly (fill zero khi thang empty)
- Application/Reports/Services/IContractExcelExporter interface
- Infrastructure/Reports/ContractExcelExporter: ClosedXML workbook 10 cot, header style bold+blue, number format #,##0, formula SUM, auto-fit, freeze header
- Application/Reports/Commands/ExportContractsToExcelCommand: filter phase/supplier/project/date range
- Api/Controllers/ReportsController: GET /reports/dashboard, GET /reports/contracts/export
- DI register IContractExcelExporter (Scoped)

Frontend fe-admin:
- types/reports.ts: DashboardStats type
- components/BarChart.tsx: generic horizontal bar chart — chi Tailwind, khong thu vien ngoai
- pages/DashboardPage.tsx REWRITE: 5 KPI card (FileText/TrendingUp/AlertTriangle/CheckCircle2/Coins) + by-phase bar + monthly 12-month chart + top 5 NCC + top 5 du an + skeleton loader
- pages/ReportsPage.tsx MOI: filter phase/fromDate/toDate → export Excel button
- Route /reports vao App.tsx

E2E verified:
- GET /api/reports/dashboard → 200 voi day du KPI + monthly fill 12 thang
- GET /api/reports/contracts/export → 200 xlsx 7229 bytes (Microsoft Excel 2007+)

Docs consolidation (theo yeu cau user):
- docs/rules.md MOI: 9 section coding conventions (ngon ngu UI/code/DB/docs, BE Clean Arch, CQRS+MediatR, Validation FluentValidation, Error handling, Async, Entity rules, DI, Package pinning, FE React/TS erasableSyntaxOnly, path alias, TanStack Query, Permission guard, Toast+error, DB convention, Git commit format, Docs structure, Testing, Security)
- docs/architecture.md MOI: layered overview ASCII art, request lifecycle (1 POST/api/contracts qua 10 step), workflow state machine 9 phase, permission model, data flow sequence diagram 4 actor (Drafter/Manager/CCM/BOD/HRA), deployment architecture Phase 5, skill library, non-functional table
- docs/database/schema-diagram.md MOI: full ERD 19 table mermaid + data flow diagram + vong doi 1 HD (create → 7 transition → gen ma → publish) + index strategy table + relationship cardinality + soft delete behavior + SQL queries (inbox/dashboard/gen ma) + migration history
- docs/gotchas.md UPDATE: 17 → 26 pitfalls, them section "Claude Code harness quirks" (Edit File not read, DI build pass nhung runtime fail) + "Contract workflow" (ma HD gen 2 lan, BE-FE NEXT_PHASES sync, race condition) + "Permission matrix" (cache real-time, MenuKey typo)
- docs/STATUS.md: Phase 4 MVP done, docs entry points section liet ke het, next Phase 5 Production
- docs/HANDOFF.md: phase table them Phase 4 row, file tree update voi Reports, test points day du, git state commit 7
- docs/changelog/migration-todos.md: tick Phase 4 MVP items + them iteration 2 list
- docs/changelog/sessions/2026-04-21-1430-phase4-report.md: session log voi thong so cumulative (BE 3100 LOC, 30 docs)
- CLAUDE.md root: update Tai lieu quan trong section them rules.md, architecture.md, schema-diagram.md, .claude/skills (13 links now)

Bug fix:
- TS unused import ContractPhaseLabel trong DashboardPage
- DI thieu register IContractExcelExporter — build pass but runtime would fail (added)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 12:42:46 +07:00

9.6 KiB

Rules — Coding Conventions SOLUTION_ERP

Nguồn duy nhất cho rules. Bất cứ commit nào vi phạm cần justify trong message.

1. Ngôn ngữ

Thành phần Ngôn ngữ
UI FE 100% tiếng Việt
Code (.cs/.ts) English (tên biến/hàm/class)
Comment code Tiếng Anh hoặc Việt — miễn rõ
Tên DB table + column English PascalCase (Contracts, SupplierId)
Business label Tiếng Việt (vd "Đang soạn thảo" cho phase 2)
Commit message English hoặc Việt — preferred English
Doc MD (docs/) Tiếng Việt (audience là người Việt)

2. Backend — .NET 10

2.1 Clean Architecture dependency rule

Api → Application ← Domain
      ↑
Infrastructure ──┘
  • Domain: pure, chỉ dùng Microsoft.Extensions.Identity.Stores (cần cho IdentityUser) — không depend ASP.NET Core framework
  • Application: depend Domain; CQRS handlers + interfaces + DTOs + validators
  • Infrastructure: depend Application + Domain; impl interfaces (EF, JWT, rendering, services)
  • Api: depend Application + Infrastructure; Controllers + middleware + composition root

2.2 CQRS + MediatR pattern

  • 1 feature = 1 file {Verb}{Entity}Command.cs / {Verb}{Entity}Query.cs
  • File chứa: Command record + Validator + Handler (cùng 1 file cho compact)
  • Dài thì tách: {Feature}/Commands/{Create}/...

Command naming:

Create{Entity}Command      // tạo mới
Update{Entity}Command      // update full
Patch{Entity}FieldCommand  // update 1 field
Delete{Entity}Command      // soft delete (default)
{Action}{Entity}Command    // action domain-specific (vd TransitionContract)

Query naming:

Get{Entity}Query            // single by Id
List{Entity}sQuery          // list với paging
{Entity}{Purpose}Query      // custom query (vd GetMyInboxQuery)

2.3 Validation

  • FluentValidation (KHÔNG dùng DataAnnotation)
  • Validator = class trong cùng file command: {Command}Validator : AbstractValidator<{Command}>
  • Pipeline: ValidationBehavior auto-chạy trước Handler — throw ValidationException → 400 ProblemDetails

2.4 Error handling

  • KHÔNG try-catch ở Controller
  • Throw domain exception: NotFoundException, ForbiddenException, UnauthorizedException, ConflictException, ValidationException
  • GlobalExceptionMiddleware map exception → HTTP status + ProblemDetails JSON
  • Logging: Serilog structured, không Console.WriteLine

2.5 Async/await

  • Tất cả I/O (DB, HTTP, file) PHẢI async
  • Truyền CancellationToken mọi async call
  • Tên method async: suffix Async (trừ ASP.NET action signature)

2.6 Entity rules

  • Mọi entity mới extend BaseEntity (Id Guid + audit) hoặc AuditableEntity (+ soft delete)
  • PK: Id type Guid, default Guid.NewGuid() trong constructor
  • FK: {Entity}Id type Guid
  • Enum: HasConversion<int>() trong EF config
  • String: luôn HasMaxLength(n) trong EF config — không để nvarchar(max) trừ khi là JSON/rich text
  • Money: decimal + HasPrecision(18, 2)
  • DateTime: luôn UTC, lấy từ IDateTime.UtcNow (không DateTime.Now)
  • Query filter soft delete: HasQueryFilter(x => !x.IsDeleted) cho AuditableEntity

2.7 DI registration

  • Singleton: stateless services (DateTimeService, FormRenderer)
  • Scoped: per-request services (JwtTokenService, ContractWorkflowService, DbContext)
  • Transient: lightweight utility (hiếm dùng)

2.8 Package version pinning

  • KHÔNG dùng * hoặc latest — luôn pin version cụ thể
  • Check compatibility với .NET 10 trước khi add. Gotchas đã biết:
    • MediatR 14 → lỗi IMediator DI, pin 12.4.1
    • Swashbuckle 10 → conflict OpenApi 2, pin 6.9.0
  • Xem gotchas.md cho full list

3. Frontend — React 19 + Vite 8 + TS 6

3.1 Named export, không default export

// ✅
export function SuppliersPage() { }
export const MyConstant = 1

// ❌
export default function SuppliersPage() { }

// Exception: App.tsx dùng default export (Vite convention)

3.2 erasableSyntaxOnly (Vite 8)

KHÔNG dùng enum — dùng const-object pattern:

// ❌
export enum SupplierType { NhaCungCap = 1 }

// ✅
export const SupplierType = { NhaCungCap: 1, NhaThauPhu: 2 } as const
export type SupplierType = typeof SupplierType[keyof typeof SupplierType]

3.3 Path alias

  • Dùng @/ cho absolute import: import { api } from '@/lib/api'
  • Config ở vite.config.ts + tsconfig.app.json (chỉ paths, không baseUrl)

3.4 Data fetching

  • TanStack Query cho mọi API call (KHÔNG useState + useEffect thủ công)
  • useQuery cho GET, useMutation cho POST/PUT/DELETE
  • invalidateQueries sau mutation thành công

3.5 API calls

  • Qua api instance (src/lib/api.ts) — đã wire axios interceptor JWT + 401 redirect
  • Base URL: /api (Vite proxy → :5443)

3.6 Permission guard

<PermissionGuard menuKey="Contracts" action="Update">
  <Button>Sửa</Button>
</PermissionGuard>

// Hook
const { can } = usePermission()
if (!can('Contracts', 'Update')) return null

Nhớ: FE guard chỉ là UX. BE policy [Authorize(Policy = "Contracts.Update")] là source of truth.

3.7 Component naming

  • PascalCase cho file: SuppliersPage.tsx, DataTable.tsx
  • kebab-case cho utility: api.ts, cn.ts
  • React component = PascalCase
  • Hook: prefix use, ví dụ usePermission

3.8 Toast + error

  • Dùng sonner (đã wire <Toaster> ở App)
  • toast.success('...'), toast.error(getErrorMessage(err))
  • Error helper: src/lib/apiError.ts extract từ ProblemDetails

3.9 Duplicate giữa 2 FE

CÓ CHỦ ĐÍCH. Mỗi app (fe-admin + fe-user) là standalone — copy shared code giữa 2 app thay vì extract ra package chung. Lý do: 2 app có UX rất khác (admin mission-critical, user workflow-heavy), evolution độc lập.

Khi share code: cp file từ app này sang app kia. Đồng bộ tay khi có breaking change.

4. Database

4.1 Convention

Item Rule
Schema 1 schema duy nhất: dbo
Table PascalCase tiếng Anh, plural: Contracts, Suppliers
Column PascalCase: FullName, CreatedAt
PK Iduniqueidentifier
FK {Entity}Iduniqueidentifier
Index IX_{Table}_{Col}
Unique UX_{Table}_{Col}

Full: xem database/database-guide.md.

4.2 Migration

  • Một commit = 1 migration (trừ khi nhiều change nhỏ trong cùng feature)
  • Naming PascalCase: AddMasterData, AddContractsWorkflow
  • Commit đủ 3 file: {Name}.cs, {Name}.Designer.cs, ApplicationDbContextModelSnapshot.cs

5. Git & commits

5.1 Branch

  • main — deploy branch
  • Feature branch (nếu nhiều người): feature/phase{N}-{feature}

5.2 Commit format

[CLAUDE] <scope>: <imperative message>

<body optional — chi tiết what + why>

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Scope: Contract · Form · Workflow · Supplier · Auth · Admin · Api · App · Domain · Infra · FE-Admin · FE-User · Docs · CICD · Scripts · Report · Dashboard

5.3 Một commit nên

  • Build pass (BE + FE)
  • Không leave WIP nửa chừng (trừ khi commit message nói rõ)
  • Test E2E với endpoint mới (curl trong session log)

5.4 Không commit

  • .env, appsettings.*.Local.json
  • node_modules/, bin/, obj/
  • wwwroot/uploads/ (user files)
  • SolutionErp_Design database (tạm cho EF CLI)

6. Docs

6.1 Khi nào viết session log

  • Session có commit thay đổi code → PHẢI tạo docs/changelog/sessions/YYYY-MM-DD-HHMM-{topic}.md
  • Session chỉ Q&A, không đụng file → không cần

6.2 Session log format

# Session YYYY-MM-DD HH:MM — <topic>

**Dev:** Claude / Copilot / <name>
**Duration:** ~Nh
**Base commit:** <sha>

## Làm được

### Chunk X — <name>
- bullet list deliverable

## E2E verified

<curl output hoặc screenshot>

## Bug gặp + fix

| Bug | Fix |

## Docs updates

## Handoff

## Thông số cumulative

6.3 Update khi thêm feature

7. Testing (hiện chưa có test tự động)

Phase 1-4 — manual test:

  • E2E qua curl/Postman trong mỗi session log
  • Build + TS check mỗi commit

Phase 5 — tự động (chưa làm):

  • Unit test: xUnit cho BE, Vitest cho FE
  • Integration test: TestContainer SQL Server cho BE
  • E2E test: Playwright cho FE

8. Security baseline

  • HTTPS everywhere prod
  • JWT Secret trong user-secrets / env var (không commit appsettings)
  • Password hash: PBKDF2 (Identity default)
  • CORS whitelist chỉ 2 FE origin
  • SQL injection: EF Core parameterized (không raw SQL trừ khi phải)
  • Audit log: mọi ghi phải log qua ContractApproval hoặc AuditLog (future)
  • Rate limit: /api/auth/login 5 req/min/IP (chưa implement — Phase 5)

9. Liên quan