Session 5 (29/04) — 6 commit feature + 1 chốt MD này. ==== Stats sau session 5 ==== - 52 DB tables (+1 PEDeptOpinions) - 15 migrations (+`AddPurchaseEvaluationDepartmentOpinions`) - ~128 API endpoints (+4) - ~31 FE pages (+5 Budget + 1 PeWorkflowsPage) - 71 unit test pass (54 Domain + 17 Infra) — CI gate live, fail → no deploy - ~13050 BE LOC (+1300) - 30 demo user, 38 gotchas, 6 skill (no change) ==== MD touched ==== - STATUS.md: header Phase 8 + 6 row Recently Done session 5 + cumulative cột S5 + In Progress S6 (Hard blockers + Optional polish + Tests Phase 3-5 + Ops) - HANDOFF.md: TL;DR 6 milestone S5 + Cảnh báo S6 (CI test gate workflow mới) + Priority 0 S6 (UAT + Ops focus) + Phase status table cập nhật - migration-todos.md: Phase 8 done với A/B/C/D/E (FE Budget / PE-HD integration / PE WF Designer / Ý kiến 4 PB / Tests Phase 1-2) + Phase 9 active (UAT + Ops + carry over) - architecture.md: §11 Testing strategy mới (test pyramid bottom-heavy + stack + CI gate + phased priority + quy tắc bổ sung mỗi feature) - database/schema-diagram.md: Migration 15 row + total 52 tables + §13 PE Department Opinion (1 bảng UNIQUE PEId+Kind + Upsert behavior + SQL DDL) - ef-core-migration SKILL: migration 15 entry + 52 tables total + Phase 8 update note - CLAUDE.md (root): modules table + Tests row + scope `Tests` + Tests section mới + count update 15/52 - docs/CLAUDE.md: 7 module bullet + ERD 52 bảng + Roadmap Phase 8 done + Phase 9 active S6 - memory project_solution_erp.md: Phase 8 summary + Session 6 priority + workflow user mới (dotnet test → commit → push) - session log 2026-04-29-chot-session-5-budget-fe-pe-tests.md (NEW — 10+ section detail) ==== Verify ==== - dotnet test SolutionErp.slnx → 71 pass / 2s - git status clean sau commit này Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 KiB
name, description, when-to-use
| name | description | when-to-use | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| ef-core-migration | Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có 15 migration sẵn (Init → AddPurchaseEvaluationDepartmentOpinions). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ. |
|
EF Core Migration — SOLUTION_ERP
Context: .NET 10 + EF Core 10 + SQL Server. DbContext:
ApplicationDbContextởInfrastructure/Persistence/. Startup:SolutionErp.Api.
Migration history (15 migration hiện có)
| # | Name | Tables added / changed |
|---|---|---|
| 1 | Init |
7 Identity tables (AspNetUsers/Roles/UserRoles/...) |
| 2 | AddMasterData |
Suppliers, Projects, Departments |
| 3 | AddPermissions |
MenuItems, Permissions |
| 4 | AddForms |
ContractTemplates, ContractClauses |
| 5 | AddContractsWorkflow |
Contracts, ContractApprovals, ContractComments, ContractAttachments, ContractCodeSequences |
| 6 | AddNotifications |
Notifications |
| 7 | AddWorkflowTypeAssignments |
WorkflowTypeAssignments (legacy admin override) |
| 8 | AddVersionedWorkflows |
WorkflowDefinitions, WorkflowSteps, WorkflowStepApprovers + Contracts.WorkflowDefinitionId FK |
| 9 | AddContractDetailsAndChangelog |
7 per-type Details (ThauPhu/GiaoKhoan/NhaCungCap/DichVu/MuaBan/NguyenTacNcc/NguyenTacDv) + ContractChangelogs |
| 10 | AddMasterCatalogs |
UnitsOfMeasure, MaterialItems, ServiceItems, WorkItems |
| 11 | AddRoleShortNameAndUserDepartment |
+Role.ShortName + User.DepartmentId/Position (cột thêm, không bảng mới) |
| 12 | AddPurchaseEvaluations |
10 bảng module Duyệt NCC: PurchaseEvaluations + Suppliers + Details + Quotes + Approvals + Changelogs + Attachments + WorkflowDefinitions/Steps/StepApprovers |
| 13 | AddPurchaseEvaluationCodeSequences |
1 bảng PurchaseEvaluationCodeSequences (Prefix PK, LastSeq) — atomic sequence cho MaPhieu format PE/{YYYY}/{A|B}/{Seq:D3} |
| 14 | AddBudgets |
4 bảng module Ngân sách: Budgets + BudgetDetails + BudgetApprovals + BudgetChangelogs. + nullable FK index Contracts.BudgetId & PurchaseEvaluations.BudgetId |
| 15 | AddPurchaseEvaluationDepartmentOpinions |
1 bảng PurchaseEvaluationDepartmentOpinions (UNIQUE PEId+Kind, max 1 row mỗi PeDepartmentKind per phiếu — Phê duyệt/Ccm/MuaHang/SmPm) |
Total: 52 bảng dbo + __EFMigrationsHistory. Xem docs/database/schema-diagram.md ERD đầy đủ.
Phase 7 pending:
AddPePaymentTermFields— táchPurchaseEvaluations.PaymentTermsJSON thành 6 column riêngAddPeDepartmentOpinions— thêm 4 field Ý kiến phòng ban (Phê duyệt/CCM/MuaHàng/SM-PM) vào header hoặc table riêng
Phase 8 pending (Budget refinement):
AddBudgetCodeSequences— atomic sequence khi format MaNganSach chốt (mirror Contract/PE pattern, hiện Random.Shared)AddBudgetVersionedWorkflow— nếu Solutions cần admin config UI (3 bảngBudgetWorkflowDefinitions/Steps/StepApprovers+Budgets.WorkflowDefinitionId?)
Phase 8 update (2026-04-29 Session 5):
- Migration 15
AddPurchaseEvaluationDepartmentOpinions— 1 bảng riêng (UNIQUE PEId+Kind), max 4 row mỗi phiếu cho 4 phòng ban (Phê duyệt/Ccm/MuaHang/SmPm). UPDATE in-place khi user đổi ý, audit qua Changelog. - Tests Phase 1-2 + CI gate live:
tests/SolutionErp.Domain.Tests/(54 test policy state machine) +tests/SolutionErp.Infrastructure.Tests/(17 test code generator). Total 71 test pass / ~2s. CI fail → no deploy. - Migration verify pattern thêm: chạy
dotnet testlocal trước mỗi migration commit để chống regression policy/codegen.
Phase 7 update (2026-04-28):
- Migration 14
AddBudgets— 4 bảng (Budgets/Details/Approvals/Changelogs) + 2 cộtBudgetId?thêm vào Contracts & PurchaseEvaluations. Workflow hardcodedBudgetPolicy.Default(chưa versioned). - 14 demo user thật
@solutions.com.vnquaSeedDemoUsersAsyncreconcile (PRO 5 + CCM 7 + ISO 1 + CEO 1).
Phase 6 update (2026-04-24):
- Enum
PurchaseEvaluationAttachmentPurpose.ComparisonTable = 4mới (file bảng so sánh tổng). Int column, không cần migration. BackfillUserEmailDomainAsynctrong DbInitializer — rename in-place 4 field Email/NormalizedEmail/UserName/NormalizedUserName. Idempotent.
Commands (chạy từ root repo)
Thêm migration mới
# Sau khi chỉnh Domain entity + EF config + DbContext DbSet
dotnet ef migrations add <MigrationName> `
--project src/Backend/SolutionErp.Infrastructure `
--startup-project src/Backend/SolutionErp.Api `
--output-dir Persistence/Migrations
Naming convention: PascalCase, verb prefix:
Add<Thing>— thêm entity / bảngUpdate<Thing>— thay đổi schemaRemove<Thing>— xóa column / tableRename<Thing>— đổi tên
Apply migration to DB
# Dev: LocalDB SolutionErp_Dev
dotnet ef database update `
--project src/Backend/SolutionErp.Infrastructure `
--startup-project src/Backend/SolutionErp.Api
# Hoặc đơn giản: chạy API → DbInitializer tự Migrate
dotnet run --project src/Backend/SolutionErp.Api
Revert 1 migration (rollback)
# Rollback về migration trước đó (ví dụ về sau AddWorkflowTypeAssignments)
dotnet ef database update AddWorkflowTypeAssignments `
--project src/Backend/SolutionErp.Infrastructure `
--startup-project src/Backend/SolutionErp.Api
# Xóa file migration (3 file, xem dưới)
dotnet ef migrations remove `
--project src/Backend/SolutionErp.Infrastructure `
--startup-project src/Backend/SolutionErp.Api
Gen SQL script (review trước khi apply prod)
# Script từ 1 migration → latest
dotnet ef migrations script AddWorkflowTypeAssignments `
--project src/Backend/SolutionErp.Infrastructure `
--startup-project src/Backend/SolutionErp.Api `
--output tmp/migration-7-to-8.sql
# Idempotent (check if exists trước mỗi operation)
dotnet ef migrations script --idempotent `
--project src/Backend/SolutionErp.Infrastructure `
--startup-project src/Backend/SolutionErp.Api
3-file rule (BẮT BUỘC commit đủ)
Mỗi migrations add sinh 3 file — phải commit đủ:
src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/
├── {Timestamp}_{Name}.cs ← migration logic (Up/Down)
├── {Timestamp}_{Name}.Designer.cs ← model snapshot tại thời điểm migration
└── ApplicationDbContextModelSnapshot.cs ← current model state (được overwrite mỗi migration)
Gotcha #17: Thiếu ModelSnapshot.cs → migrations add kế tiếp sẽ sinh duplicate columns. Thiếu Designer.cs → không revert được.
Pitfalls thường gặp
P1 — DesignTime DbContext resolve fail
Triệu chứng: dotnet ef migrations add → Unable to resolve service for type 'DbContextOptions<ApplicationDbContext>'.
Nguyên nhân: EF tooling chạy đứng ngoài runtime DI, cần factory riêng.
Fix: Đã có src/Backend/SolutionErp.Infrastructure/Persistence/DesignTimeDbContextFactory.cs. Nếu connection string thay đổi → update factory, không chỉ appsettings.json.
P2 — Table rename / column rename gen ra DROP + CREATE
Triệu chứng: Migration sinh DropColumn + AddColumn thay vì RenameColumn → mất data.
Fix: Sửa migration thủ công sang migrationBuilder.RenameColumn(...). Hoặc dùng [Column("newname")] attribute để EF tự detect rename.
P3 — Query filter (soft delete) khi thêm FK
Triệu chứng: Warning The entity type 'X' has a global query filter but referencing entity 'Y' doesn't. This may lead to inconsistent results.
Fix: Entity reference cũng phải có query filter:
builder.HasQueryFilter(x => !x.IsDeleted);
Hoặc dùng .IgnoreQueryFilters() cho query cần bypass.
P4 — FK cascade/restrict khác với expectation
Triệu chứng: Migration apply OK nhưng DELETE parent → cascade sang child (hoặc ngược lại restrict block).
Fix: Explicit config trong IEntityTypeConfiguration<T>:
builder.HasOne(x => x.Contract)
.WithMany(c => c.Approvals)
.HasForeignKey(x => x.ContractId)
.OnDelete(DeleteBehavior.Cascade); // hoặc Restrict, SetNull
Case study quan trọng: Contracts.WorkflowDefinitionId → WorkflowDefinitions.Id PHẢI dùng Restrict để protect HĐ cũ khi admin archive version (xem workflow-contract.md invariants).
P5 — Nullable reference type gen column NOT NULL
Triệu chứng: Property public string? Description → migration gen NOT NULL.
Fix: Check builder.HasQueryFilter chỗ config, hoặc explicit builder.Property(x => x.Description).IsRequired(false).
P6 — AddVersionedWorkflows duplicate seed
Case study hiện tại: Nếu thêm migration mới sau AddVersionedWorkflows, cẩn thận KHÔNG trigger DbInitializer.SeedWorkflowDefinitionsAsync 2 lần. Check if (!db.WorkflowDefinitions.Any()) trong DbInitializer.
Workflow khi thêm entity mới (example: add AuditLog)
1. Domain/AuditLogs/AuditLog.cs — tạo entity (BaseEntity hoặc AuditableEntity)
2. Infrastructure/Persistence/Configurations/AuditLogConfiguration.cs
- ToTable("AuditLogs")
- Unique index trên (EntityType, EntityId) nếu cần
3. Infrastructure/Persistence/ApplicationDbContext.cs
- public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
4. Application/Common/Interfaces/IApplicationDbContext.cs
- DbSet<AuditLog> AuditLogs { get; }
5. dotnet ef migrations add AddAuditLogs
6. Review file .cs sinh ra → OK
7. dotnet ef database update → LocalDB apply
8. Test: dotnet run → check bảng được tạo
9. Commit [CLAUDE] Domain+Infra: add AuditLog entity + migration
10. ⚠️ UPDATE docs/database/schema-diagram.md (ERD + migration table)
11. UPDATE docs/STATUS.md nếu là work lớn
Apply prod (VPS)
# Trên VPS, sau khi deploy code:
# Option 1: auto migrate — DbInitializer.MigrateAsync() chạy khi API startup
# Option 2: manual script
dotnet ef migrations script <LastApplied> --idempotent --output migrate.sql
# Review migrate.sql → chạy qua sqlcmd:
sqlcmd -S .\SQLEXPRESS -d SolutionErp -U vrapp -P <pw> -i migrate.sql
Prod safety:
- Backup trước:
scripts/backup-sql.ps1 - Dry-run idempotent script local trước
- Test rollback plan: script from-N-to-M-1 sẵn
Code pointers
src/Backend/SolutionErp.Infrastructure/Persistence/ApplicationDbContext.cs— 24 DbSetsrc/Backend/SolutionErp.Infrastructure/Persistence/DesignTimeDbContextFactory.cs— EF tooling factorysrc/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs— seed + warn + migrate runtimesrc/Backend/SolutionErp.Infrastructure/Persistence/Configurations/— 24 IEntityTypeConfigurationsrc/Backend/SolutionErp.Application/Common/Interfaces/IApplicationDbContext.cs— interface Application layer
Related
docs/database/database-guide.md— conventions + migration workflow chi tiếtdocs/database/schema-diagram.md— ERD 24 bảngdocs/gotchas.md#7, #17 — migration pitfalls