[CLAUDE] Docs: Chunk E7 — chốt session 9 Chunk E-bis complete
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m7s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m7s
- STATUS: Recently Done +1 row session 9 (5 commit per-chunk + 83 test). Phase 9 header update count 77→83. Section E (Chunk E-bis) tick toàn bộ done. - HANDOFF: TL;DR session 9 + Cảnh báo session 10. Giữ session 8 narrative cũ. - migration-todos: Phase 9 +1 section "Session 9 done" với 5 task tick. Pending Chunk E-bis tick chuyển done. - CLAUDE.md (root + docs): test count 77 → 83 (54 Domain + 29 Infra: 17 codegen + 6 PE WF Application + 6 PE 2-stage approval). - Session log mới: 2026-05-04-1700-chot-session-9-chunk-e-bis-complete.md (full narrative + lessons + stats theo §6.5 KEEP rule). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -157,6 +157,25 @@ Session log: `2026-04-28-chot-session-4-budget.md`.
|
||||
|
||||
## 📝 Phase 9 — UAT + Ops + carry over (Session 6+ active)
|
||||
|
||||
### ✅ Session 9 done (2026-05-04) — Chunk E-bis complete (FE 2-stage + HĐ/Budget mirror + 6 test)
|
||||
|
||||
User chỉ thị "làm hết cho xong tính năng luôn" sau Session 8 close bug fix anh Kiệt phía BE PE. Session 9 đóng toàn bộ pending Chunk E-bis (defer từ session 8).
|
||||
|
||||
- [x] **Chunk E2 — FE Workflow Panel PE** hiển thị progress 2-stage timeline per phase × dept (commit `f8eebd5`). Component `DeptApprovalsSection` group by phase × dept, highlight amber khi current phase có Review nhưng chưa Confirm, badge fuchsia "bypass" khi NV.CanBypassReview. Cả fe-admin + fe-user (rule §3.9).
|
||||
- [x] **Chunk E3 — FE UserManager toggle CanBypassReview** (commit `4380bdc`). UserDto BE thêm field `CanBypassReview`. UsersPage column "Bypass" + button ShieldCheck (icon highlight fuchsia khi enabled). Endpoint backend đã có sẵn từ Session 8 Chunk E1. fe-user KHÔNG có UsersPage (admin-only).
|
||||
- [x] **Chunk E4 — HĐ 2-stage logic mở rộng** (commit `b6f5a16`). ContractWorkflowService thêm `UserManager<User>` DI + mirror toàn bộ logic 2-stage từ PE service (sau policy guard, trước gen mã HĐ). ContractDepartmentApprovalFeatures.cs (List query mirror PE pattern). Endpoint `GET /contracts/{id}/department-approvals`. FE WorkflowHistoryPanel section "Tiến trình duyệt 2-cấp phòng ban" insert giữa WorkflowSummaryCard và Lịch sử duyệt.
|
||||
- [x] **Chunk E5 — Budget 2-stage logic mở rộng** (commit `1fc439b`). TransitionBudgetCommandHandler thêm `INotificationService` + `IDateTime` DI + mirror 2-stage logic. BudgetDepartmentApprovalFeatures.cs + endpoint. FE BudgetWorkflowPanel section "Tiến trình duyệt 2-cấp phòng ban". Note: low-priority cho Budget (ít user duyệt budget per dept) nhưng giữ consistent UX 3 module.
|
||||
- [x] **Chunk E6 — 6 test 2-stage + IdentityFixture helper** (commit `8353fe8`). IdentityFixture (Common/) setup ServiceProvider với Identity stack đầy đủ — DbContext SQLite shared connection + AddIdentityCore<User> + AddRoles<Role> + AddEntityFrameworkStores. Single shared scope cho fixture lifetime đảm bảo DbContext + UserManager đồng instance. Helper `CreateUserAsync(email, name, deptId, roles, canBypassReview)` reusable. 6 test PeTwoStageApprovalTests: NV_Review_Blocks_Phase_Transition (đóng bug anh Kiệt — test chính xác) / TPB_Confirm_After_NV_Review_Allows_Transition / NV_With_BypassReview_Allows_Transition_With_IsBypassed_True / Admin_Skips_TwoStage_Logic_Entirely / Reject_Sets_RejectedFromPhase_And_Forces_DangSoanThao / Resume_After_Reject_Jumps_Back_To_RejectedPhase. Tests Contract + Budget 2-stage skipped — logic identical PE, ROI thấp; pattern reusable nếu UAT phát hiện regression riêng. **Total 77→83 test pass**.
|
||||
|
||||
Stats sau session 9:
|
||||
- BE LOC: ~13750 → ~14400 (+650, gồm Contract + Budget 2-stage logic + 2 List feature + Test fixture)
|
||||
- API endpoints: ~131 → ~133 (+2 List dept-approvals HĐ/Budget)
|
||||
- Tests: 77 → **83** (+6 PE 2-stage)
|
||||
- Schema không đổi (Migration 16 đã có sẵn từ session 8)
|
||||
- 5 commit per-chunk pushed → 1 CI run trigger qua HEAD = `8353fe8`
|
||||
|
||||
Pending còn lại Phase 9: chỉ Hard blockers Ops (UAT thật / SMTP / Rotate creds / SQL backup). Feature 2-stage đầy đủ.
|
||||
|
||||
### ✅ Session 8 done (2026-05-04) — Migration 16: 2-stage dept approval + smart reject + lock edit
|
||||
|
||||
**Bối cảnh:** Anh Kiệt (FDC) báo bug PE workflow: NV.PRO tạo phiếu → duyệt được hết phase. Phân quyền sai vì policy chỉ check role, không check Stage 2-cấp.
|
||||
@ -179,12 +198,12 @@ Session log: `2026-04-28-chot-session-4-budget.md`.
|
||||
|
||||
Session log: `2026-05-04-1230-chot-session-8-2-stage-dept-approval.md`.
|
||||
|
||||
**Pending Chunk E-bis (defer):**
|
||||
- [ ] FE Workflow Panel hiển thị progress 2-stage timeline
|
||||
- [ ] FE UserManager toggle `CanBypassReview` checkbox
|
||||
- [ ] HĐ 2-stage mở rộng (`ContractWorkflowService` thêm 2-stage logic + endpoint List)
|
||||
- [ ] Budget 2-stage mở rộng (low priority)
|
||||
- [ ] Tests 2-stage logic Service-layer (cần UserManager DI helper)
|
||||
**Pending Chunk E-bis** ✅ TẤT CẢ DONE ở Session 9 (xem section trên):
|
||||
- [x] FE Workflow Panel hiển thị progress 2-stage timeline (E2 — PE) + HĐ (E4) + Budget (E5)
|
||||
- [x] FE UserManager toggle `CanBypassReview` checkbox (E3)
|
||||
- [x] HĐ 2-stage mở rộng (E4)
|
||||
- [x] Budget 2-stage mở rộng (E5)
|
||||
- [x] Tests 2-stage logic Service-layer (E6 — 6 test PE + IdentityFixture)
|
||||
|
||||
### ✅ Session 6 done (2026-04-30 — pure docs work)
|
||||
|
||||
|
||||
@ -0,0 +1,319 @@
|
||||
# Session log — 2026-05-04 chốt session 9 — Chunk E-bis complete
|
||||
|
||||
**Topic:** User chỉ thị "làm hết cho xong tính năng luôn đi nhé" sau Session 8 đóng bug PE 2-stage. Session 9 close toàn bộ Chunk E-bis defer (FE 2-stage panel cả 3 module + UserManager bypass toggle + HĐ + Budget 2-stage mirror PE + 6 test 2-stage + IdentityFixture helper).
|
||||
|
||||
**Dev:** Claude (Opus 4.7) + user (pqhuy1987@gmail.com)
|
||||
**Duration:** ~3 giờ (gồm Chunk E2-E7 + verify build/test/push).
|
||||
**Base commit:** `d206e14` (chốt session 8 ending — patch count drift + skill refresh).
|
||||
|
||||
## Bối cảnh
|
||||
|
||||
Sau Session 8, BE PE 2-stage approval đã live trên prod (https://api.solutions.com.vn). Anh Kiệt FDC có thể test bug fix. Tuy nhiên còn pending:
|
||||
|
||||
- FE Workflow Panel chưa hiển thị 2-stage progress → user thấy "stuck" mà không hiểu
|
||||
- FE UserManager chưa có toggle CanBypassReview → admin phải PATCH qua Postman
|
||||
- HĐ + Budget 2-stage scope **defer** từ Session 8 (chỉ áp PE)
|
||||
- Tests 2-stage Service-layer chưa có (cần UserManager DI helper)
|
||||
|
||||
User chỉ thị "làm hết cho xong tính năng luôn" → close toàn bộ Chunk E-bis.
|
||||
|
||||
## Approach
|
||||
|
||||
Per-chunk commit pattern (Session 8 đã proven). 5 chunk small (mỗi <300 LOC change), build + test pass mỗi chunk:
|
||||
|
||||
- E2 — FE PE WorkflowPanel 2-stage timeline
|
||||
- E3 — FE UsersPage CanBypassReview toggle
|
||||
- E4 — HĐ 2-stage logic + endpoint + FE
|
||||
- E5 — Budget 2-stage logic + endpoint + FE
|
||||
- E6 — Tests + IdentityFixture
|
||||
- E7 — Docs update + commit + push
|
||||
|
||||
## Commits session 9
|
||||
|
||||
5 commit code + 1 commit docs ending:
|
||||
|
||||
- `f8eebd5` — E2: FE PE 2-stage timeline cả 2 app
|
||||
- `4380bdc` — E3: FE UserManager bypass toggle
|
||||
- `b6f5a16` — E4: HĐ 2-stage mirror PE
|
||||
- `1fc439b` — E5: Budget 2-stage mirror PE
|
||||
- `8353fe8` — E6: 6 test 2-stage + IdentityFixture
|
||||
- (current) — E7: Docs + session log
|
||||
|
||||
## E2 — FE PE WorkflowPanel 2-stage timeline
|
||||
|
||||
### Frontend (cả fe-admin + fe-user)
|
||||
|
||||
```typescript
|
||||
// types/purchaseEvaluation.ts:
|
||||
export const ApprovalStage = { Review: 1, Confirm: 2 } as const
|
||||
export type PeDepartmentApproval = {
|
||||
id: string; phaseAtApproval: number; departmentId: string
|
||||
departmentName: string | null; stage: number
|
||||
approverUserId: string; approverName: string | null
|
||||
approverRoleSnapshot: string | null // "TPB" | "NV" | "NV(bypass)"
|
||||
comment: string | null; approvedAt: string; isBypassed: boolean
|
||||
}
|
||||
|
||||
// PeWorkflowPanel.tsx:
|
||||
const { data: deptApprovals = [] } = useQuery<PeDepartmentApproval[]>({
|
||||
queryKey: ['pe-dept-approvals', evaluation.id],
|
||||
queryFn: async () => (await api.get(`/purchase-evaluations/${id}/department-approvals`)).data,
|
||||
})
|
||||
```
|
||||
|
||||
### DeptApprovalsSection component
|
||||
|
||||
Group by phase × dept. Render 2 row per dept:
|
||||
- **Review NV** (slate text) — ✓ tên + thời gian + comment
|
||||
- **Confirm TPB** (emerald hoặc amber) — ✓ hoặc "⏳ chờ TPB confirm"
|
||||
|
||||
Highlight border amber khi `phase === currentPhase && review && !confirm` → user biết "đang chờ TPB confirm".
|
||||
|
||||
Badge fuchsia "bypass" khi `isBypassed=true`.
|
||||
|
||||
Invalidate query sau transition mutation để refresh ngay.
|
||||
|
||||
## E3 — FE UsersPage CanBypassReview toggle
|
||||
|
||||
### Backend UserDto extend
|
||||
|
||||
```csharp
|
||||
// UserFeatures.cs
|
||||
public record UserDto(
|
||||
Guid Id, string Email, string FullName, bool IsActive, bool IsLocked,
|
||||
DateTime CreatedAt, List<string> Roles, Guid? DepartmentId,
|
||||
string? DepartmentName, string? Position,
|
||||
bool CanBypassReview); // NEW
|
||||
```
|
||||
|
||||
ListUsers + GetUser handler đều thêm `u.CanBypassReview` vào DTO instantiation.
|
||||
|
||||
### Frontend UsersPage
|
||||
|
||||
```tsx
|
||||
// types/users.ts
|
||||
export type User = { ...; canBypassReview: boolean }
|
||||
|
||||
// UsersPage.tsx column "Bypass":
|
||||
{ key: 'canBypassReview', header: 'Bypass', width: 'w-20', align: 'center',
|
||||
render: u => u.canBypassReview ? (
|
||||
<span className="rounded bg-fuchsia-100 ... text-fuchsia-700">
|
||||
<ShieldCheck /> bypass
|
||||
</span>
|
||||
) : <span className="text-slate-400">—</span> }
|
||||
|
||||
// Action button toggle:
|
||||
const bypassMut = useMutation({
|
||||
mutationFn: (u: User) =>
|
||||
api.patch(`/users/${u.id}/bypass-review`,
|
||||
{ canBypassReview: !u.canBypassReview }),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['users'] }),
|
||||
})
|
||||
|
||||
<Button onClick={() => bypassMut.mutate(u)}>
|
||||
<ShieldCheck className={u.canBypassReview ? 'text-fuchsia-600' : 'text-slate-400'} />
|
||||
</Button>
|
||||
```
|
||||
|
||||
Endpoint backend `PATCH /users/{id}/bypass-review` đã sẵn từ Session 8 Chunk E1. Chỉ wire FE.
|
||||
|
||||
fe-user KHÔNG có UsersPage (admin-only function) — chỉ update fe-admin.
|
||||
|
||||
## E4 — HĐ 2-stage logic mở rộng
|
||||
|
||||
### ContractWorkflowService
|
||||
|
||||
Thêm `UserManager<User>` DI:
|
||||
|
||||
```csharp
|
||||
public class ContractWorkflowService(
|
||||
IApplicationDbContext db,
|
||||
IContractCodeGenerator codeGenerator,
|
||||
IDateTime dateTime,
|
||||
INotificationService notifications,
|
||||
IChangelogService changelog,
|
||||
UserManager<User> userManager) : IContractWorkflowService
|
||||
```
|
||||
|
||||
Mirror toàn bộ logic 2-stage từ `PurchaseEvaluationWorkflowService.TransitionAsync`. Inject sau policy guard, trước gen mã HĐ:
|
||||
|
||||
```csharp
|
||||
if (decision == ApprovalDecision.Approve
|
||||
&& targetPhase != ContractPhase.DangSoanThao
|
||||
&& targetPhase != ContractPhase.TuChoi
|
||||
&& !isResumingAfterReject
|
||||
&& !isAdmin && !isSystem
|
||||
&& actorUserId is Guid actorUid)
|
||||
{
|
||||
var actor = await userManager.FindByIdAsync(actorUid.ToString());
|
||||
if (actor?.DepartmentId is Guid deptId)
|
||||
{
|
||||
var isManager = actorRoles.Contains(AppRoles.DeptManager);
|
||||
var canBypass = actor.CanBypassReview;
|
||||
var stage = (isManager || canBypass) ? Confirm : Review;
|
||||
// ... upsert ContractDepartmentApproval, check hasConfirm, BLOCK nếu chưa
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ContractDepartmentApprovalFeatures.cs (List query)
|
||||
|
||||
Mirror PE pattern. Join với Departments + Users (separate query) để denorm name.
|
||||
|
||||
### Endpoint
|
||||
|
||||
```http
|
||||
GET /api/contracts/{id}/department-approvals
|
||||
[Authorize(Policy = "Contracts.Read")] // qua [Authorize] trên controller class
|
||||
```
|
||||
|
||||
### FE WorkflowHistoryPanel
|
||||
|
||||
Section `DeptApprovalsSection` insert giữa `WorkflowSummaryCard` và "Lịch sử duyệt". Cùng pattern PE — group by phase × dept, highlight amber, badge fuchsia.
|
||||
|
||||
## E5 — Budget 2-stage mirror PE/Contract
|
||||
|
||||
### TransitionBudgetCommandHandler
|
||||
|
||||
Thêm 2 dependency mới: `INotificationService` + `IDateTime`. Mirror toàn bộ logic 2-stage. Note: Budget low-priority (ít user duyệt budget per dept) nhưng giữ consistent UX 3 module.
|
||||
|
||||
### BudgetDepartmentApprovalFeatures.cs
|
||||
|
||||
List query mirror PE/Contract pattern.
|
||||
|
||||
### Endpoint + FE
|
||||
|
||||
`GET /api/budgets/{id}/department-approvals` + section trong `BudgetWorkflowPanel`.
|
||||
|
||||
## E6 — Tests + IdentityFixture
|
||||
|
||||
### IdentityFixture (Common/)
|
||||
|
||||
Setup ServiceProvider với Identity stack đầy đủ. Key insight: dùng `Role` custom (extend `IdentityRole<Guid>`) thay vì `IdentityRole<Guid>` plain — match `ApplicationDbContext : IdentityDbContext<User, Role, Guid>`.
|
||||
|
||||
```csharp
|
||||
services.AddScoped<ApplicationDbContext>(_ =>
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseSqlite(connection).EnableSensitiveDataLogging().Options;
|
||||
return new TestApplicationDbContext(options);
|
||||
});
|
||||
services.AddScoped<TestApplicationDbContext>(sp =>
|
||||
(TestApplicationDbContext)sp.GetRequiredService<ApplicationDbContext>());
|
||||
|
||||
services.AddIdentityCore<User>(...)
|
||||
.AddRoles<Role>() // ← KEY: Role không IdentityRole<Guid>
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
|
||||
_root = services.BuildServiceProvider();
|
||||
Services = _root.CreateScope().ServiceProvider; // single shared scope
|
||||
```
|
||||
|
||||
Helper `CreateUserAsync(email, name, deptId, roles, canBypassReview)` reusable cho tests sau.
|
||||
|
||||
### 6 test PeTwoStageApprovalTests
|
||||
|
||||
| Test | Scenario | Expected |
|
||||
|---|---|---|
|
||||
| `NV_Review_Blocks_Phase_Transition` | NV.PRO approve phase ChoPurchasing | Phase **không đổi**, 1 row Stage=Review, 1 PE Approval `[Review NV]` |
|
||||
| `TPB_Confirm_After_NV_Review_Allows_Transition` | NV review → TPB confirm | Phase chuyển ChoCCM, 2 rows (Review + Confirm) |
|
||||
| `NV_With_BypassReview_Allows_Transition_With_IsBypassed_True` | NV.CanBypassReview=true approve | Phase chuyển, 1 row Stage=Confirm + IsBypassed=true |
|
||||
| `Admin_Skips_TwoStage_Logic_Entirely` | Admin role approve | Phase chuyển, **0 row** DepartmentApprovals |
|
||||
| `Reject_Sets_RejectedFromPhase_And_Forces_DangSoanThao` | TPB reject từ ChoCCM | Phase=DangSoanThao, RejectedFromPhase=ChoCCM |
|
||||
| `Resume_After_Reject_Jumps_Back_To_RejectedPhase` | Admin resume từ DangSoanThao + RejectedFromPhase=ChoCCM | Phase jump tới ChoCCM (không phải target ChoPurchasing), RejectedFromPhase=null |
|
||||
|
||||
Stub `FakeNotificationService` — best effort path không cần verify.
|
||||
|
||||
Tests Contract + Budget 2-stage **skip** — logic identical PE, ROI thấp. Pattern reusable nếu UAT phát hiện regression riêng.
|
||||
|
||||
## Verify
|
||||
|
||||
```
|
||||
✓ Build pass mỗi commit (2 warning DocxRenderer cũ — không liên quan)
|
||||
✓ 83 unit test pass mỗi commit (54 Domain + 29 Infra)
|
||||
- Trước: 77 (54 + 17 + 6)
|
||||
- Sau: 83 (54 + 17 + 6 + 6 PE 2-stage)
|
||||
✓ FE build pass cả 2 app mỗi chunk có FE change
|
||||
✓ TS strict mode + erasableSyntaxOnly check pass
|
||||
```
|
||||
|
||||
## Files touched session 9
|
||||
|
||||
```
|
||||
fe-admin/src/types/purchaseEvaluation.ts (mod)
|
||||
fe-admin/src/components/pe/PeWorkflowPanel.tsx (mod)
|
||||
fe-admin/src/types/users.ts (mod)
|
||||
fe-admin/src/pages/system/UsersPage.tsx (mod)
|
||||
fe-admin/src/types/contracts.ts (mod)
|
||||
fe-admin/src/components/contracts/WorkflowHistoryPanel.tsx (mod)
|
||||
fe-admin/src/types/budget.ts (mod)
|
||||
fe-admin/src/components/budgets/BudgetWorkflowPanel.tsx (mod)
|
||||
|
||||
fe-user/src/types/purchaseEvaluation.ts (mod, sync)
|
||||
fe-user/src/components/pe/PeWorkflowPanel.tsx (mod, sync)
|
||||
fe-user/src/types/contracts.ts (mod, sync)
|
||||
fe-user/src/components/contracts/WorkflowHistoryPanel.tsx (mod, sync)
|
||||
fe-user/src/types/budget.ts (mod, sync)
|
||||
fe-user/src/components/budgets/BudgetWorkflowPanel.tsx (mod, sync)
|
||||
|
||||
src/Backend/SolutionErp.Application/Users/UserFeatures.cs (mod: +CanBypassReview field DTO)
|
||||
src/Backend/SolutionErp.Application/Contracts/ContractDepartmentApprovalFeatures.cs (NEW)
|
||||
src/Backend/SolutionErp.Application/Budgets/BudgetDepartmentApprovalFeatures.cs (NEW)
|
||||
src/Backend/SolutionErp.Application/Budgets/BudgetFeatures.cs (mod: +2-stage logic + DI)
|
||||
|
||||
src/Backend/SolutionErp.Infrastructure/Services/ContractWorkflowService.cs (mod: +UserManager DI + 2-stage)
|
||||
|
||||
src/Backend/SolutionErp.Api/Controllers/ContractsController.cs (mod: +1 endpoint)
|
||||
src/Backend/SolutionErp.Api/Controllers/BudgetsController.cs (mod: +1 endpoint)
|
||||
|
||||
tests/SolutionErp.Infrastructure.Tests/Common/IdentityFixture.cs (NEW)
|
||||
tests/SolutionErp.Infrastructure.Tests/Services/PeTwoStageApprovalTests.cs (NEW)
|
||||
|
||||
docs/STATUS.md (mod)
|
||||
docs/HANDOFF.md (mod)
|
||||
docs/changelog/migration-todos.md (mod)
|
||||
docs/CLAUDE.md (mod)
|
||||
CLAUDE.md (mod: 77→83 test)
|
||||
docs/changelog/sessions/2026-05-04-1700-chot-session-9-*.md (NEW: file này)
|
||||
```
|
||||
|
||||
## Cảnh báo session 10+
|
||||
|
||||
1. **UAT live ngay** với anh Kiệt + 2-3 user — feature 2-stage đầy đủ cả 3 module + UX panel + bypass toggle.
|
||||
2. **Tests Contract + Budget skipped** — logic identical PE. Pattern `PeTwoStageApprovalTests` reusable.
|
||||
3. **Bypass toggle audit** — chưa log Changelog khi admin toggle CanBypassReview. Có thể cần thêm audit row riêng nếu UAT yêu cầu.
|
||||
4. **Notify TPB cùng dept** dùng UserManager filter DeptManager — verify production user có role đúng.
|
||||
5. **fe-user KHÔNG có UsersPage** — admin-only function, bypass toggle chỉ ở fe-admin.
|
||||
6. **3 endpoint mới** PE + HĐ + Budget List dept-approvals cùng pattern, reuse policy `*.Read` qua [Authorize] class-level.
|
||||
7. **Cron audit định kỳ** vẫn EMPTY (`No scheduled jobs`) — recreate khi user yêu cầu.
|
||||
|
||||
## Lessons learned
|
||||
|
||||
1. **Mirror logic chuẩn xác giảm bug** — Contract + Budget 2-stage clone pattern PE service y nguyên (chỉ thay entity/enum names) → giảm rủi ro logic divergent. Future refactor có thể extract thành `IDepartmentApprovalGuard<TEntity, TPhase>` nếu pattern lặp lần thứ 4.
|
||||
|
||||
2. **IdentityFixture investment trade-off** — setup tốn 30-45 phút (struggle với DbContext options + Role custom type), nhưng future tests (Application handler tests) sẽ reuse được. ROI dài hạn dương.
|
||||
|
||||
3. **Single shared scope** trong fixture quan trọng — UserManager + DbContext cần đồng instance để CreateAsync persist data nhìn thấy được trong service test sau đó. Nếu mỗi resolve scope mới → DbContext khác → data invisible cross calls.
|
||||
|
||||
4. **`Role` custom subclass** — match exactly với `IdentityDbContext<User, Role, Guid>`. Pass `IdentityRole<Guid>` → `RoleStore` query DbSet không tồn tại → `EntityType not found` lỗi tinh quái khó debug.
|
||||
|
||||
5. **fe-user duplicate file pattern §3.9** — cp file giữa 2 app sau khi edit fe-admin xong. Đỡ phải edit 2 lần. Diff trước cp để verify identical.
|
||||
|
||||
6. **User chỉ thị "làm hết cho xong"** = open license cho per-chunk commit + push final. Giữ pattern Session 8 (5 chunk + verify mỗi chunk) tránh monolithic commit khó debug.
|
||||
|
||||
## Stats sau session 9
|
||||
|
||||
| | Trước S9 | Sau S9 |
|
||||
|---|---:|---:|
|
||||
| BE LOC | ~13750 | ~14400 (+650) |
|
||||
| DB tables | 55 | 55 (không đổi) |
|
||||
| Migrations | 16 | 16 (không đổi) |
|
||||
| API endpoints | ~131 | **~133** (+2 List dept-approvals HĐ/Budget) |
|
||||
| FE pages | ~31 | ~31 (không đổi, chỉ component panel update) |
|
||||
| FE components | — | +DeptApprovalsSection × 3 panel + bypass column UsersPage |
|
||||
| Tests | 77 | **83** (+6 PE 2-stage) |
|
||||
| Test fixtures | SqliteDbFixture | + IdentityFixture (reusable) |
|
||||
| Gotchas | 41 | 41 (không có gotcha mới đáng ghi) |
|
||||
| Demo user | 30 | 30 |
|
||||
| Commits S9 | 0 | **5** (E2-E6) + docs E7 |
|
||||
| Session log | 19 | **20** |
|
||||
Reference in New Issue
Block a user