db6625304adefefbf130feb82c117c8d9a162ff2
70 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| db6625304a |
[CLAUDE] Domain: Chunk A — Mig 31 swap F2 storage Users→ApprovalWorkflowLevels (Approver scope ChoDuyet)
Mig 31 RefactorSkipToFinalToApproverLevel — 2 stage manual reorder: - ADD ApprovalWorkflowLevels.AllowApproverSkipToFinal bit NOT NULL DEFAULT 0 - DROP Users.AllowDrafterSkipToFinal (semantic mới khác hẳn — admin re-config qua Designer) - NO BACKFILL (Option A — accept lose 4 prod user value per K0-bis audit) Plan K refactor F2 semantic: Drafter from Nháp → Approver during ChoDuyet skip thẳng Cấp cuối. Mirror F3+F4 admin opt-in per-Approver-slot pattern (Mig 29 + Mig 30) reinforced 3× cumulative. Service line 121-157 F2 Drafter SUBMIT branch REMOVED stub (K2 sẽ add Approver F2 branch trong APPROVE STEP line ~393-525). TransitionAsync skipToFinal param 8th KEPT cho K2 repurpose. Application layer compile-break fix transient: UserDto field mapping + GET handler + LIST handler + SetUserAllowDrafterSkipToFinalCommandHandler NoOp + PurchaseEvaluationFeatures drafter flag → sentinel false. DTO + Command signature UNCHANGED (K2 chunk Chủ trì sẽ refactor DTO/Command theo plan). 4 prod user (fin.pp + pm.nv + nv.test + truong.nguyen) lose AllowDrafterSkipToFinal=true per bro Option A. Audit trail trong session log K8. Verify: - dotnet ef migrations add pass - dotnet ef database update Dev + Design pass (Mig 31 applied both DB) - dotnet build src/Backend/SolutionErp.Api production projects clean (0 err, 0 warn) - dotnet test SKIPPED per UAT mode (memory feedback_uat_skip_verify) — K7 chunk fix remaining PurchaseEvaluationWorkflowServiceReturnModeTests.cs:253 reference Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| b079b27343 |
[CLAUDE] PE-Workflow: S22+5 Chunk A — Mig 30 +AllowApproverEditBudget per-Level slot
Bro clarify spec S22+4: - KHÔNG đổi logic edit ngân sách (Drafter Nháp/TraLai vẫn duy nhất default) - Thêm flag per-NV slot trong Designer: "Cho phép NV này edit Section ngân sách lúc đang duyệt" (mirror pattern F3 AllowApproverEditDetails Mig 29) Mig 30 `AddAllowApproverEditBudgetToLevels`: - ALTER ApprovalWorkflowLevels +AllowApproverEditBudget bit NOT NULL DEFAULT 0 - 3-file rule (mig.cs + Designer.cs + Snapshot.cs) - Apply LocalDB Dev + Design Domain entity ApprovalWorkflowLevel +AllowApproverEditBudget (default false). EF config HasDefaultValue(false). DTO AwLevelDto + ApprovalWorkflowOptionsDto + CreateAwLevelInput all extend +AllowApproverEditBudget. PE GET handler populate currentLevelOptions thêm AllowApproverEditBudget từ curLevel slot. Admin Designer GET/POST handler propagate flag. AdjustBudgetCommand handler refactor ChoDuyet branch: - Trước: check actor match level.ApproverUserId (cho phép mặc định) - Sau: check level.AllowApproverEditBudget=true AND actor match ApproverUserId → throw ConflictException nếu slot chưa được cấp quyền Verify: - dotnet build SolutionErp.slnx — 0 err, 2 warn DocxRenderer pre-existing - Mig 30 applied Dev + Design DB Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 37b51d7f07 |
[CLAUDE] PurchaseEvaluation: S22+4 Chunk A — BE attachment view endpoint + AdjustBudget command
Feature 1 (attachment preview):
- NEW `GET /api/purchase-evaluations/{id}/attachments/{attId}/view`
- Cùng handler download, override `Content-Disposition: inline` để FE nhúng iframe
- Permission: same scope GET phiếu (Plan E V2 strict scope)
Feature 2 (điều chỉnh ngân sách):
- NEW `AdjustPurchaseEvaluationBudgetCommand` + Handler + Validator
- NEW `PATCH /api/purchase-evaluations/{id}/budget-adjust` body
`{budgetId, budgetManualName, budgetManualAmount}`
- Phase + actor scope guard:
* DangSoanThao/TraLai → chỉ Drafter của phiếu
* ChoDuyet → Approver currentLevel (match ApproverUserId) — V2 only
* Admin → bypass tất cả
- Audit changelog với diff narrative: "Điều chỉnh ngân sách: link X→Y, số tiền A→Bđ [Drafter/Approver Bước/Cấp/Admin]"
- Tách riêng KHÔNG dùng UpdatePeDraft vì Approver scope KHÔNG nên được edit
Section 1 fields (TenGoiThau/DiaDiem/MoTa/PaymentTerms)
Verify:
- dotnet build SolutionErp.slnx — 0 err, 2 warn DocxRenderer pre-existing
- Test defer carry Plan C (UAT mode §7) — guard logic critical, ưu tiên cho S23+
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| f149661d36 |
[CLAUDE] PurchaseEvaluation: Plan E — phân quyền strict V2 scope (List + Detail)
Thắt chặt phân quyền PE V2 từ UAT loose sang strict actor.UserId scope: Trước (loose): mọi authenticated user thấy mọi phiếu V2 (`ApprovalWorkflowId != null`). Sau (strict): - ListPurchaseEvaluationsQuery: phiếu V2 chỉ visible khi actor là approver trong any Step.Level.ApproverUserId của workflow đã pin. Pre-compute userApprovalWfIds = DISTINCT workflow IDs có user trong Levels. - GetPurchaseEvaluationQuery: same — actor must be V2 approver in any Level của workflow pin để thấy phiếu (ngoài Drafter scope + role eligible phase). Drafter vẫn thấy phiếu mình tạo (regardless V2/V1). Admin bypass full. Inbox đã strict từ Session 17 (ResolveV2InboxIdsAsync match current Cấp + ApproverUserId) — KHÔNG đụng. Tests defer: Plan C carry — 4 integration tests Strict V2 List + Detail (Drafter own / V2 approver / non-approver throw 403) khi UAT confirm. Verify: - dotnet build SolutionErp.slnx — 0 err, 2 warning DocxRenderer pre-existing - dotnet test SolutionErp.slnx — 103/103 PASS regression-free Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 215b1e036a |
[CLAUDE] Tests: Plan C task 1-3 — Service per-NV Allow* test catch-up (S21 t4-t5 Mig 28-29)
14 test cover 3 helper sửa lớn S21 t4-t5 (test-after UAT backlog): Task 1+2 — PurchaseEvaluationWorkflowServiceReturnModeTests.cs (7 test): - ApplyReturnModeAsync Drafter allowed/denied/admin bypass (3 test mode flag) - OneLevel happy path (peer review chain in same Step) - OneLevel admin bypass (override disabled flag) - skipToFinal Drafter allowed/denied/admin bypass (3 test per-user F2) Task 3 — PurchaseEvaluationDraftGuardTests.cs (7 test): - Drafter scope: DangSoanThao + TraLai → return (2 test) - F3 Approver scope: ChoDuyet + flag on + actor match → return - F3 Approver scope: ChoDuyet + flag off → ConflictException - F3 Approver scope: ChoDuyet + flag on + actor mismatch → ForbiddenException - Admin bypass ChoDuyet + flag off → return - DaDuyet any caller → ConflictException (terminal phase) InternalsVisibleTo: expose PurchaseEvaluationDraftGuard internal helper cho test. Finding: skipToFinal Service mutate Phase=ChoDuyet TRƯỚC validate user flag. Throw chặn SaveChanges nên DB không persist nhưng in-memory dirty. Note trong test — không refactor scope catch-up (defer S22+). Verify: - dotnet test SolutionErp.slnx — 103/103 PASS (58 Domain + 45 Infra) Δ: 89 → 103 (+14: ReturnMode 7 + Guard 7) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 60efeeda63 |
[CLAUDE] Users: Plan D — F2 toggle AllowDrafterSkipToFinal per-user (Mig 29 wire UI)
BE: UserDto +AllowDrafterSkipToFinal + SetUserAllowDrafterSkipToFinalCommand
+ Handler + UsersController PATCH /api/users/{id}/allow-skip-final body
{allowDrafterSkipToFinal:bool} Policy=Users.Update.
FE Admin: User type +allowDrafterSkipToFinal. UsersPage column "Skip cuối"
violet FastForward badge + action button toggle mirror bypass-review pattern.
fe-user KHÔNG mirror (UsersPage admin-only).
Verify:
- dotnet build SolutionErp.slnx — 0 err, 2 warning DocxRenderer pre-existing
- npm run build fe-admin — pass 638ms
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 036694638e |
[CLAUDE] PE-Workflow: S21 t5 Chunk A — Mig 29 refactor Allow* sang per-NV (per-Level + per-Drafter)
Refactor 6 Allow* options từ workflow-level (Mig 28 S21 t4) sang per-NV scope: - F1 (4 mode Trả lại) + F3 (Edit Section 2) → 5 flag MOVE xuống `ApprovalWorkflowLevels` (per slot Approver, cùng table với ApproverUserId). - F2 (AllowDrafterSkipToFinal) → MOVE xuống `Users` (per-Drafter user, User Mgmt). Mig 29 `RefactorAdvancedOptionsToPerLevelAndDrafterUser` — 4-stage migration (EF auto-generated drop-then-add đã được REORDER manual): 1. ADD 5 column trên `ApprovalWorkflowLevels` (AllowReturnOneLevel/OneStep/ ToAssignee/ToDrafter[default true]/AllowApproverEditDetails) 2. ADD 1 column trên `Users` (AllowDrafterSkipToFinal default false) 3. BACKFILL bulk SQL (preserve admin config Mig 28): - Levels: copy workflow.Allow* → all Levels của workflow (JOIN Steps) - Users: SET TRUE cho user nào từng Drafter PE link workflow Allow=true 4. DROP 6 column workflow-level (Mig 28 cleanup) 3-file rule complete. Apply LocalDB Dev + Design success. Domain entity refactor: - `ApprovalWorkflow.cs` — REMOVE 6 Allow* field (S21 t4 Mig 28 cũ) - `ApprovalWorkflowLevel.cs` — ADD 5 Allow* field (F1 + F3) - `User.cs` — ADD 1 Allow* field (F2 AllowDrafterSkipToFinal) EF config update: - `ApprovalWorkflowConfiguration.cs` — remove 6 HasDefaultValue workflow-level, add 5 HasDefaultValue per-Level (4 false + 1 AllowReturnToDrafter true S17) Service refactor `ApplyReturnModeAsync` (`PurchaseEvaluationWorkflowService.cs`): - Resolve currentLevel slot (CurrentWorkflowStepIndex + CurrentApprovalLevelOrder) - Read 5 Allow* từ `currentLevel.AllowXxx` thay vì workflow.Allow* - Admin bypass per-Level flag check (unchanged behavior) - Drafter mode đặc biệt: check AllowReturnToDrafter của currentLevel (vẫn validate) - V1 legacy (no V2 schema) → fallback Drafter behavior tự động DRAFTER trình refactor (`TransitionAsync` skipToFinal branch): - Permission check moved from workflow-level → `drafterUser.AllowDrafterSkipToFinal` - Use `userManager.FindByIdAsync(actorUserId)` để get current Drafter user entity - Admin bypass user flag check (unchanged) Helper `EnsureEditableForDetailsAsync` refactor: - Read `level.AllowApproverEditDetails` thay vì workflow.AllowApproverEditDetails - Error message rõ "Cấp Approver hiện tại (Bước X / Cấp Y)" thay vì "Workflow" DTO refactor: - `AwLevelDto` ADD 5 Allow* field (admin Designer GET per-Level) - `AwDefinitionDto` REMOVE 6 Allow* (no longer workflow-level) - `CreateAwLevelInput` ADD 5 Allow* param (admin Designer POST per-Level) - `CreateAwDefinitionCommand` REMOVE 6 Allow* (Steps[].Levels[] now has them) - `ApprovalWorkflowOptionsDto` chỉ còn 5 flag (F2 removed — separate field) - `PurchaseEvaluationDetailBundleDto`: - rename `WorkflowOptions` → `CurrentLevelOptions` (clearer semantic per-slot) - ADD `DrafterAllowSkipToFinal bool` (resolve từ DrafterUserId → User entity) GetPurchaseEvaluationQueryHandler populate: - `currentLevelOptions` = 5 Allow* của Cấp hiện tại (null nếu V1 legacy / no pointer) - `drafterAllowSkipToFinal` = User.AllowDrafterSkipToFinal lookup từ DrafterUserId Backward compat verified: - Mig 29 backfill preserve admin config S21 t4 — workflow cũ vẫn chạy đúng sau deploy. User chưa từng làm Drafter F2 phải opt-in lần đầu (no auto-set). - 84 test PASS (58 Domain + 26 Infra unchanged, 3 gotcha #45 guard test backward compat signature). Pending Chunk B/C: FE Admin Designer move 5 checkbox xuống per-Level slot + FE eOffice read currentLevelOptions + drafterAllowSkipToFinal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| c56024ba25 |
[CLAUDE] PE-Workflow: Chunk B — BE Service + handlers + DTOs (F1+F2+F3)
F1 — 4 mode Trả lại (Service.ApplyReturnModeAsync helper):
- WorkflowReturnMode enum (OneLevel / OneStep / Assignee / Drafter)
- OneLevel: lùi 1 Cấp trong cùng Step (peer review). Bước 1 Cấp 1 → fallback Drafter.
- OneStep: lùi sang Bước trước Cấp cuối. Bước 1 → fallback Drafter.
- Assignee: pick runtime → tìm Step+Level match ApproverUserId trong workflow.
- Drafter: Phase=TraLai clear pointer như S17 (backward compat).
- 3 mode đầu giữ Phase=ChoDuyet, reset SLA 7d. Mode Drafter clear SLA.
- Admin bypass workflow.Allow* flag check. Non-admin → throw ConflictException
với message rõ "Workflow không bật mode X".
F2 — Drafter skipToFinal (extend DRAFTER trình branch):
- Workflow.AllowDrafterSkipToFinal=true required (non-admin)
- Set CurrentWorkflowStepIndex = Steps.Count-1 + CurrentApprovalLevelOrder = max Level
- Audit comment append "[Drafter gửi thẳng Cấp cuối]"
F3 — Approver edit Section 2 (Detail + NCC + Báo giá):
- New helper `EnsureEditableForDetailsAsync` (extend pattern PurchaseEvaluationDraftGuard):
- Drafter scope: DangSoanThao OR TraLai (any role, Controller [Authorize] handles)
- F3 Approver scope: ChoDuyet + workflow.AllowApproverEditDetails=true +
actor.Id match CurrentLevel.ApproverUserId. Admin bypass flag check.
- Throw ForbiddenException nếu approver Cấp khác nhau (rõ Bước/Cấp trong message).
- 8 handler switch helper + inject ICurrentUser khi cần:
- Detail: Add (existing ICurrentUser) / Update + Delete (inject new)
- Quote: Upsert + Delete (inject new)
- Supplier: Add (existing) / Update + Delete (inject new + add guard, trước
đây hoàn toàn KHÔNG có phase guard — bonus security fix)
- Audit: thêm changelog Update/Delete handler (trước đây silent). Khi phase=
ChoDuyet append " [Approver edit khi đang duyệt]" cho lịch sử rõ ai sửa.
Extension Service `TransitionAsync` signature (backward compat — 3 optional
param thêm cuối + default null/false):
- WorkflowReturnMode? returnMode = null
- Guid? returnTargetUserId = null
- bool skipToFinal = false
TransitionPurchaseEvaluationCommand DTO + Validator + Handler — mirror signature.
DTO extensions:
- ApprovalWorkflowOptionsDto NEW sub-record (6 Allow* flag) cho FE filter
- PurchaseEvaluationDetailBundleDto + WorkflowOptions field (null nếu V1 legacy)
- GetPe handler populate awOptions từ ApprovalWorkflow entity load (Mig 23 path)
- AwDefinitionDto + 6 Allow* field (admin Designer GET overview)
- CreateAwDefinitionCommand + 6 Allow* param (admin Designer POST new version)
- Handler ToDto + entity new() — propagate Allow* end-to-end
Default backward compat: workflow cũ → AllowReturnToDrafter=true (Mig 28 DB
default), 5 flag còn lại false. Phiếu cũ V2 vẫn Trả lại Drafter như S17 sau
deploy — no breaking change.
Verify:
- dotnet build SolutionErp.slnx → 0 err, 2 warn pre-existing DocxRenderer
- 3 regression test gotcha #45 vẫn PASS (backward compat signature change)
- LocalDB Dev + Design đã apply Mig 28 (Chunk A)
Pending Chunk C: FE Admin Designer mirror 2 app (6 checkbox + DTO types).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| ef394f8067 |
[CLAUDE] Api+App: Chunk B — PATCH /menus/{key} + DTO extend isVisible/displayLabel
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m49s
Session 20 turn 7 Chunk B. BE API cho admin Ẩn/Hiện + Đổi tên menu fe-user.
DTO (MenuDtos.cs):
- MenuNodeDto +IsVisible bool +DisplayLabel string?
- MenuItemDto +IsVisible bool +DisplayLabel string?
GetMyMenuTreeQueryHandler:
- Pass m.IsVisible + m.DisplayLabel vào MenuNodeDto record
- KHÔNG filter IsVisible server-side (FE 2 app tự filter — fe-admin
render hết, fe-user filter ẩn). Lý do: 1 endpoint serve cả 2 FE.
ListMenuItemsQueryHandler: +IsVisible +DisplayLabel trong Select projection.
NEW UpdateMenuItemCommand + Validator + Handler (PermissionFeatures.cs):
- Body: { Key, IsVisible, DisplayLabel? }
- Validator: Key required + max 50, DisplayLabel max 200
- Handler: load MenuItem by Key (NotFoundException nếu missing), set
IsVisible + DisplayLabel (whitespace → null normalize), SaveChangesAsync
MenusController +PATCH /api/menus/{key}:
- [Authorize(Policy = "Permissions.Update")] — reuse policy admin matrix
- Body: UpdateMenuItemRequest { IsVisible, DisplayLabel? }
- Send UpdateMenuItemCommand qua MediatR
- Return 204 NoContent
Verify:
- dotnet build SolutionErp.slnx — 0 err (1 warn cũ DocxRenderer không liên quan)
Pending Chunk C: FE Admin MenuVisibilityPage
Pending Chunk D: FE Layout fe-user filter + render displayLabel
Pending Chunk E: Docs S20 turn 7
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 9dee00da01 |
[CLAUDE] PurchaseEvaluation: Chunk A — reorder section Hạng mục lên #2 + auto-tạo 1 row mặc định
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m20s
Session 20 UI restructure (3 yêu cầu user). Chunk A xử lý:
BE — CreatePurchaseEvaluationCommandHandler thêm 1 PurchaseEvaluationDetail
mặc định khi tạo phiếu mới:
- GroupCode="01", GroupName="Hạng mục chính"
- NoiDung = TenGoiThau (tên gói thầu)
- DonGiaNganSach = ThanhTienNganSach = Budget.TongNganSach (nếu link)
fallback BudgetManualAmount fallback 0
- DonViTinh="gói", KL=1, Order=1
- Changelog entry kèm theo (audit Insert Detail)
FE — Đổi thứ tự 5 section trong PeDetailTabs.tsx (mirror 2 app):
1. Thông tin gói thầu (giữ)
2. Hạng mục + Báo giá (chuyển từ #4 lên #2)
3. Chọn NCC / TP (từ #2 xuống #3)
4. NCC / TP tham gia (từ #3 xuống #4 — Chunk B sẽ gộp vào #2 nested)
5. Ý kiến cấp duyệt (giữ)
Q1=a: Giữ Section "Chọn NCC TP thắng thầu" riêng (rõ UX).
Q2=a "1 hạng mục trước tiên": auto-seed đủ, multi-hạng-mục defer.
Verify:
- dotnet build SolutionErp.slnx — 0 warning / 0 error
- Test pass mặc định skip (Phase 9 UAT iteration, Q4 user public luôn)
Pending Chunk B: Nested grid Hạng mục → NCC expand inline edit
Pending Chunk C: Section 5 gộp đồng cấp cùng Phòng (1 box / Step)
Pending Chunk D: Docs S20 changelog + STATUS + HANDOFF
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 90baa8e73c |
[CLAUDE] PurchaseEvaluation: Chunk B Service V2 hook UPSERT opinion + DTO + GET include
Service `ApproveV2Async` sau khi log approval (Decision=Approve) → UPSERT row `PurchaseEvaluationLevelOpinions` cho Cấp hiện tại (auto sync ý kiến từ comment khi duyệt). Reject KHÔNG sync. Match level theo ApproverUserId của actor (multi-NV cùng Cấp OR-of-N). Admin override (actor.Id KHÔNG match) → fallback first level — FE detect SignedByUserId !== Level.ApproverUserId hiển thị "Admin duyệt thay". Empty/whitespace comment → "(duyệt — không ý kiến)" placeholder (Q4 bonus). Helper `ResolveActorFullNameAsync(actorUserId, isSystem, ct)` lookup denorm SignedByFullName từ Users (fallback "(System)" / "(unknown)"). DTO `PurchaseEvaluationLevelOpinionDto` (15 fields): - StepOrder/StepName/StepDepartmentId/StepDepartmentName (Bước Phòng) - LevelOrder/LevelName/ApproverUserId/ApproverFullName (Cấp NV) - Comment/SignedAt/SignedByUserId/SignedByFullName (sign-off) GetPurchaseEvaluationQueryHandler: - Include LevelOpinions - helper BuildLevelOpinionsAsync JOIN ApprovalWorkflows.Steps.Levels + Departments + Users → denorm DTO. Empty list cho phiếu V1 / V2 chưa có cấp nào duyệt → FE fallback message. Verify: dotnet build pass + dotnet test 81 pass (no regression). Chunk C kế tiếp: FE Section 5 dynamic mirror 2 app. |
|||
| 77a30584fc |
[CLAUDE] PurchaseEvaluation: Mig 26 PeLevelOpinions V2 dynamic — Chunk A Domain + EF
Schema mới cho Section 5 "Ý kiến cấp duyệt" V2 dynamic theo ApprovalWorkflowsV2 (Mig 22-25). Thay thế Mig 15 cố định 4 box (V1). Entity `PurchaseEvaluationLevelOpinion : AuditableEntity`: - (PEId, ApprovalWorkflowLevelId) UNIQUE composite - Comment nvarchar(2000) — text ý kiến hoặc "(duyệt — không ý kiến)" placeholder (Q4 bonus) - SignedAt datetime2 (luôn có khi UPSERT từ ApproveV2Async) - SignedByUserId Guid (NV chính chủ HOẶC Admin override) - SignedByFullName nvarchar(200) — denorm tránh user bị xóa/đổi tên EF: FK Cascade Pe + Restrict Level. SignedByUserId KHÔNG nav (denorm only). Migration 26 `AddPeLevelOpinionsForV2`: 1 CREATE TABLE + 2 FK + 2 index. 3-file rule commit đủ (.cs + Designer + Snapshot). Apply LocalDB SolutionErp_Dev OK (Mig 25 + 26 catchup). Verify: dotnet build pass + dotnet test 81 pass (no regression). Chunk B kế tiếp: Service V2 hook UPSERT auto trong ApproveV2Async. |
|||
| 2a53107602 |
[CLAUDE] AwV2: Mig 25 +IsUserSelectable + Designer pin toggle + Workspace filter, bỏ "(clone)"
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
Hai yêu cầu UAT 2026-05-08:
1. Bỏ "(clone)" auto-append khi clone version mới — version đã đủ phân biệt.
2. Thêm pin toggle để admin chọn workflows nào cho user pick lúc tạo phiếu.
Migration 25 AddIsUserSelectableToApprovalWorkflows:
- ALTER ApprovalWorkflows ADD IsUserSelectable bit NOT NULL DEFAULT 0
- UPDATE backfill SET IsUserSelectable=1 WHERE IsActive=1 (giữ behavior cũ
cho active versions, archived = false default — admin tự pin nếu cần)
BE:
- Domain ApprovalWorkflow +property IsUserSelectable
- DTO AwDefinitionDto +field
- CreateAwDefinitionCommandHandler set default true cho version mới
- New SetAwUserSelectableCommand + Handler
- API PATCH /api/approval-workflows-v2/{id}/user-selectable (Workflows.Create policy)
- DbInitializer SeedSampleApprovalWorkflowsV2Async set IsUserSelectable=true
FE Designer (fe-admin):
- DefinitionDto +isUserSelectable
- Badge amber "Pin Cho user chọn" khi true (cạnh Đang áp dụng/Archived)
- Button "Pin/PinOff Ghim cho user / Bỏ ghim" trong action group + mutation toggle
- Auto-fill name khi clone: bỏ "(clone)" suffix → giữ nguyên name
FE Workspace (fe-admin + fe-user):
- approvalWorkflows query filter w.isUserSelectable === true
- User dropdown chỉ thấy workflows admin đã pin
Verify: dotnet build pass · 81 test pass · npm build × 2 pass · Mig 25 apply LocalDB OK.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| de0f38dd25 |
[CLAUDE] PE Panel 3: bỏ phase cards + render flow workflow V2 thực tế
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m14s
User feedback: "bỏ luôn cái quy trình phía trên đi nhé, vì nó là trạng
thái rồi (đã có badge), update cái flow quy trình mới vào bên panel 3
đang đến ai".
BE — ApprovalFlow DTO mới (full snapshot Bước → Cấp → NV với Status):
- PurchaseEvaluationApprovalFlowDto { CurrentStepIndex, CurrentLevelOrder,
Steps[] }
- PurchaseEvaluationApprovalFlowStepDto { Order, Name, DepartmentId/Name,
Status, Levels[] }
- PurchaseEvaluationApprovalFlowLevelDto { Order, Name, Approvers[], Status }
- Status: "Done" | "Current" | "Pending"
Handler GetById compute Status logic:
- Phase=DaDuyet → tất cả Steps/Levels "Done"
- Phase=Nháp/Trả lại/Từ chối → tất cả "Pending"
- Phase=ChoDuyet:
* Step.Index < currentIdx → all Levels "Done"
* Step.Index == currentIdx:
Level.Order < currentLevelOrder → "Done"
Level.Order == currentLevelOrder → "Current"
Level.Order > currentLevelOrder → "Pending"
* Step.Index > currentIdx → all "Pending"
- Load Approvers info (FullName + Email) qua UserManager batch query
FE (cả 2 app mirror):
- types/purchaseEvaluation.ts: +PeApprovalFlow + Step + Level + Status union
PeDetail.approvalFlow optional
- PeWorkflowPanel:
* BỎ phase cards section (4 ô Nháp/TraLai/ChoDuyet/DaDuyet) — đã
duplicate với status badge ở header
* Header mới: "Quy trình duyệt" + Code + Version + Name workflow pin
* Render Flow vertical: Bước (icon ✓/●/○) → border + bg theo status
+ dept badge → list Cấp (icon nhỏ) với label "đang chờ" / "đã
duyệt" + tên NV duyệt
* Phiếu V1 legacy (no flow): show note "dùng quy trình cũ — không
khả dụng chi tiết"
* Bỏ helper isPastPhase() (orphan sau khi xóa cards)
Verify: BE build 0 error · 2 FE builds OK.
Test eoffice:
1. Mở phiếu V2 đang ChoDuyet → thấy flow Bước 1 (Phòng A):
✓ Cấp 1 NV X (đã duyệt)
● Cấp 2 NV Y (đang chờ) ← highlight
○ Cấp 3 NV Z (chưa)
2. Phase=DaDuyet → all Steps/Levels green ✓
3. Phase=Nháp/TraLai → all greyed ○
4. V1 legacy → fallback note
|
|||
| d250ae4e71 |
[CLAUDE] PE Inbox: nhận filter approvalWorkflowId + show dropdown cả 2 view
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m3s
User báo: - Filter "Tất cả quy trình duyệt" hiện chỉ ở Danh sách → muốn ở cả 2 - Filter chọn quy trình → không thấy phiếu V2 trong Duyệt (Inbox) BE — wire filter vào Inbox: - GetMyPurchaseEvaluationInboxQuery +ApprovalWorkflowId? param - Handler thêm filter `q.Where(x => x.e.ApprovalWorkflowId == awId)` - PurchaseEvaluationsController.Inbox +approvalWorkflowId query param FE (cả 2 app mirror): - PurchaseEvaluationsListPage: bỏ điều kiện `!pendingMe` ở Select dropdown → hiển thị filter quy trình duyệt CẢ Duyệt + Danh sách - Inbox API call: pass approvalWorkflowId từ URL param Verify: BE 0 error · 2 FE builds OK. Test luồng eoffice: 1. Vào "Duyệt NCC > Duyệt" → 2 dropdown filter hiện đầy đủ 2. Chọn 1 quy trình V2 từ dropdown → list filter chỉ phiếu pin quy trình đó 3. Vào "Duyệt NCC > Danh sách" → 2 dropdown vẫn show, filter cũng work |
|||
| 9e63e2da10 |
[CLAUDE] PE: V2-aware Inbox/List + 2 dropdown filter quy trình + trạng thái
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m12s
User báo: "Phiếu chưa thấy lên trong danh sách duyệt — chắc do chưa ăn vào flow. Tách thành 2 cái dropdown là list quy trình duyệt và list trạng thái. Debug trước, phân quyền rút gọn lại sau." BE — V2-aware permission + filter (Application/PurchaseEvaluations/ PurchaseEvaluationFeatures.cs): - ListPurchaseEvaluationsQuery +ApprovalWorkflowId? Guid? param + IDOR loose: phiếu pin V2 → mọi authenticated user thấy được (UAT) - GetMyPurchaseEvaluationInbox V2-aware: ResolveV2InboxIdsAsync helper precompute Set<Guid> phiếu Phase=ChoDuyet pin V2 + actor ∈ Cấp hiện tại approvers (CurrentWorkflowStepIndex + CurrentApprovalLevelOrder match Step.Order + Level.Order). Inbox where = eligiblePhases.Contains || v2InboxIds.Contains. eligiblePhases admin +ChoDuyet. - GetById Detail loose: V2 pin → cho non-Drafter xem (skip eligiblePhases check). API Controller: - PurchaseEvaluationsController.List +approvalWorkflowId query param FE — 2 dropdown filter (cả 2 app mirror): - PurchaseEvaluationsListPage: +URL param `awId` filter quy trình - useQuery `approval-workflows-v2-filter` load list V2 active+history theo applicableType=typeFilter (chỉ enabled khi có type) - Render Select riêng "Tất cả quy trình duyệt" (chỉ show !pendingMe vì Inbox dùng API endpoint khác) + Select "Tất cả trạng thái" giữ - Display option: "QT-DN-V2-001 v01 — Tên quy trình" Verify: BE build 0 error · 2 FE builds OK. Test luồng eoffice: 1. Drafter trình phiếu V2 → Phase=ChoDuyet 2. Login NV X (approver Cấp 1) vào "Duyệt NCC > Duyệt" (?pendingMe=1) → phiếu hiện trong list 3. Login NV Y (không phải approver) → list rỗng (đúng spec) 4. Vào "Duyệt NCC > Danh sách" (không pendingMe) → 2 dropdown: - Quy trình duyệt: filter theo workflow specific - Trạng thái: filter theo Phase |
|||
| d814429cee |
[CLAUDE] PE Workflow V2: disable nút Duyệt nếu actor không trong cấp hiện tại
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m14s
User feedback: "Nếu không đúng bước duyệt thì nút duyệt cho Disable luôn cũng đc."
BE — DTO + Handler populate "Bước/Cấp đang chờ duyệt":
- Application/PurchaseEvaluations/Dtos/PurchaseEvaluationDtos.cs:
+PurchaseEvaluationApprovalLevelApproverDto { UserId, FullName, Email }
+PurchaseEvaluationCurrentApprovalDto { StepIndex, StepName,
StepDepartmentId/Name, LevelOrder, LevelName, Approvers[] }
PurchaseEvaluationDetailBundleDto +CurrentApproval? optional field
- Application/PurchaseEvaluations/PurchaseEvaluationFeatures.cs handler
GetById: khi pin V2 + Phase=ChoDuyet → load AW.Steps.Levels Include
3-level + group by Order = Cấp + resolve user names → populate
CurrentApproval. Null khi V1 legacy hoặc không phải ChoDuyet.
FE — types + PeWorkflowPanel (cả 2 app mirror):
- types/purchaseEvaluation.ts: +PeCurrentApproval + PeCurrentApprovalLevelApprover
+ PeDetail.currentApproval optional
- PeWorkflowPanel:
* Banner V2 hiển thị "Đang chờ Bước N (TênBước · Phòng X) — Cấp K"
+ list NV được duyệt + status emerald (đến lượt) / amber (không phải lượt)
* useAuth() để check currentUser.id ∈ approvers + Admin bypass
* Button "Duyệt forward" disabled khi V2 pin + actor không khớp.
Title tooltip "Cấp K chỉ {NV X / NV Y} mới duyệt được."
* Button "Trả lại" + "Từ chối" vẫn enabled (BE không gating 2 hành
động này theo Cấp — Approver có thể reject bất cứ lúc nào).
* Send-back logic update: target = DangSoanThao OR TraLai (V2 dùng TraLai)
- Admin role bypass mọi check.
Verify: 81 test pass · npm build × 2 OK · BE 0 error.
Test thử:
1. NV X (approver Cấp 1 V2) login → banner emerald "Đến lượt bạn duyệt"
+ nút "✓ Duyệt → ChoDuyet" enabled
2. NV Y (không phải approver) login → banner amber "Không phải lượt
bạn — chỉ NV X mới duyệt được" + nút Duyệt grey disabled, hover tooltip
3. Admin login → bypass, button enabled
|
|||
| b41484b702 |
[CLAUDE] PE Workflow: wire Service V2 (Mig 24) — fix bug duyệt phiếu pin schema mới
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m14s
User báo bug eoffice: phiếu tạo mới không duyệt được + không bắt đc quy
trình mới. Root cause: Mig 23 pin ApprovalWorkflowId vào entity nhưng
Service vẫn đọc WorkflowDefinitionId legacy → match approver theo schema
cũ (Dept+PositionLevel/Role/User) thay vì ApproverUserId V2.
BE Domain — Migration 24 `AddCurrentApprovalLevelOrderToPe`:
- PurchaseEvaluation +CurrentApprovalLevelOrder int? (track Cấp 1/2/3
đang chờ duyệt trong Step hiện tại khi pin V2). Null khi terminal/V1.
- RejectedAtStepIndex giữ deprecated DB column cho data cũ.
BE Service PE — branch theo schema pin:
- V2 (`ApprovalWorkflowId` set): ApproveV2Async() — load
ApprovalWorkflows.Steps.Levels Include 3-level. Group Levels by Order
= Cấp (OR-of-N approvers). Match `actor.Id ∈ levelGroup.ApproverUserId`
(KHÔNG match Dept+Level/Role/User như V1). Advance:
Còn cấp tiếp trong Step → levelOrder++
Hết cấp → idx++, levelOrder=1
Hết Step → DaDuyet
- V1 legacy (chỉ `WorkflowDefinitionId` set): ApproveV1LegacyAsync() —
giữ nguyên logic Mig 21 (Dept+PositionLevel match)
- Drafter trình từ Nháp/Trả lại: init CurrentWorkflowStepIndex=0 +
CurrentApprovalLevelOrder=1 (chỉ khi V2 pin)
- Reject (Trả lại): clear CurrentApprovalLevelOrder=null
- Reject (Từ chối): clear all tracking
BE Synthetic Policy V2:
- `PurchaseEvaluationPolicyRegistry.ForV2Schema()` — simple state machine
policy (DangSoanThao/TraLai → ChoDuyet/TuChoi; ChoDuyet → ChoDuyet/
TraLai/TuChoi). Roles="*" cho ChoDuyet branch — Service tự enforce
ApproverUserId, Policy chỉ expose 3 nút FE.
- GetPurchaseEvaluationByIdQuery handler: ưu tiên ForV2Schema() khi pin
V2 (FE đọc workflow.nextPhases để show button).
Verify: 81 test pass · BE 0 error · Mig 24 applied cả 2 LocalDB.
Test thử (Drafter eoffice):
1. Designer V2 tạo quy trình QT-DN-V2-001: Bước 1 (Phòng A), Cấp 1 (NV X)
2. Workspace tạo phiếu mới, Select QT-DN-V2-001 → Lưu phiếu + Gửi duyệt
3. Phiếu Phase=ChoDuyet, idx=0, levelOrder=1. NV X login → thấy phiếu
trong Inbox + duyệt được. Sau approve → idx++, levelOrder reset 1.
4. Cấu hình level mismatch: NV Y khác → thấy ForbiddenException rõ tên.
Logic Contract V2 chưa wire (chỉ PE), defer Session sau khi user UAT PE OK.
|
|||
| 0a40c65421 |
[CLAUDE] PurchaseEvaluation: User chọn quy trình duyệt V2 lúc tạo phiếu (Mig 23)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m11s
User feedback: thay field "Loại quy trình (theo menu — khóa)" disabled
→ Select dropdown cho User pick quy trình ApprovalWorkflowsV2 (Mig 22)
ngay từ workspace tạo mới. Hiển thị "Mã + Tên + Version".
BE Domain:
- PurchaseEvaluation +ApprovalWorkflowId Guid? (nullable, FK Restrict)
- EF Configuration: Index + FK Restrict to ApprovalWorkflows
- Migration 23 `AddApprovalWorkflowIdToPurchaseEvaluation` (1 ALTER +
1 IX + 1 FK), applied cả _Design + _Dev LocalDB
- Field WorkflowDefinitionId (Mig 21 legacy) giữ song song để Service
PE chạy logic cũ tới khi Session sau wire qua schema mới
BE Application:
- CreatePurchaseEvaluationCommand +ApprovalWorkflowId? Guid? optional
param (default null)
- Validate: nếu set, phải tồn tại + ApplicableType khớp PE.Type
(DuyetNcc=1 → ApprovalWorkflowApplicableType.DuyetNcc, etc)
- Handler set entity.ApprovalWorkflowId từ request
- UpdatePurchaseEvaluationDraftCommand mirror — cho User đổi quy trình
khi sửa Nháp/Trả lại (validate same)
- PurchaseEvaluationDetailBundleDto +ApprovalWorkflowId/Code/Name/Version
- GetPurchaseEvaluationByIdQuery handler load workflow info join
- Update Phase guard: cho sửa cả DangSoanThao + TraLai (Trả lại =
editable per Session 17 spec)
FE (cả 2 app mirror):
- types/purchaseEvaluation.ts: PeDetail +approvalWorkflowId/Code/Name/Version
- PeWorkspaceCreateView.tsx:
- Replace field disabled "Loại quy trình" → Select bắt buộc
- useQuery `/api/approval-workflows-v2?applicableType=N` filter theo
defaultType (1=DuyetNcc / 2=DuyetNccPhuongAn)
- Display option: "QT-DN-V2-001 v01 — Quy trình Duyệt NCC (đang áp dụng)"
- List cả version active + archived (UAT cần test compare)
- Empty state hint amber "Chưa có quy trình, vào /system/approval-workflows-v2"
- canSubmit require approvalWorkflowId set
- POST payload include approvalWorkflowId
Verify: dotnet build OK · 81 test pass · npm build × 2 OK · Mig 23 applied
cả 2 LocalDB.
Logic Service PE chưa wire qua ApprovalWorkflowId — vẫn pin
WorkflowDefinitionId Mig 21 legacy chạy. Session sau wire Service iterate
ApprovalWorkflowSteps + match approver theo schema V2 + drop legacy.
|
|||
| ff21120c8c |
[CLAUDE] Workflow: State machine 5 trạng thái — Trả lại = Phase riêng
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m17s
Session 17 spec: chốt 5 trạng thái phiếu PE/HĐ/Budget theo state diagram:
Nháp ─trình──► Đã gửi duyệt ─approve cấp cuối──► Đã duyệt (terminal)
├─ Trả lại ────────► Trả lại
└─ Từ chối ────────► Từ chối (terminal)
Trả lại ──Drafter sửa+gửi lại──► Đã gửi duyệt (chạy LẠI từ đầu)
Khác Mig 21 (Session 16): bỏ smart-reject jump-back. Trả lại = Phase
RIÊNG (TraLai=98), không revert về DangSoanThao + không jump-back step.
Drafter từ TraLai gửi lại như case Nháp — workflow chạy lại từ Cấp 1
Bước 1 (Option A diagram chốt với user).
BE Domain:
- ContractPhase + TraLai = 98
- BudgetPhase + TraLai = 98
- PurchaseEvaluationPhase: TraLai=98 đổi từ [LEGACY deprecated] thành
primary state. Comment update enum docs cho cả 3.
BE Policy (PE/HĐ/Budget):
- Reject transitions trỏ về TraLai (thay DangSoanThao)
- Mirror entry transitions: TraLai → next phase (cho Drafter resubmit)
- ActivePhases thêm TraLai
- FromDefinition mirror: TraLai → step.Phase + reject → TraLai
- DefaultSla cho TraLai = same as DangSoanThao
BE Service (PE + Contract):
- Reject branch: target=TuChoi giữ; else set Phase=TraLai, clear
CurrentWorkflowStepIndex=null
- Bỏ ResumeAfterReject branch + RejectedAtStepIndex/RejectedFromPhase
assignment (DB column giữ deprecated cho data cũ)
- Drafter trình branch: từ DangSoanThao HOẶC TraLai → ChoDuyet, init
CurrentWorkflowStepIndex=0 (cùng entry point, chạy lại từ đầu)
- Notification: TraLai when fromPhase=ChoDuyet → "bị trả lại"
- Budget Handler: simplify reject → TraLai, bỏ smart-reject + isResuming
BE Tests update:
- WorkflowPolicyTests: Standard_RejectFromCCM → TraLai (rename + assert)
+ Standard_TraLai_To_DangGopY_Allowed_For_Drafter (new)
- PurchaseEvaluationPolicyTests: BothPolicies_RejectFromCCM → TraLai
+ BothPolicies_TraLai_To_ChoPurchasing_AllowedForDrafter (new theory)
- BudgetPolicyTests: Default_CostControl_ChoCCM_To_TraLai (rename)
+ ActivePhases All6States (was All5) + NextPhasesFrom_TraLai (new)
+ NextPhasesFrom_ChoCEO_Includes_DaDuyet_And_TraLai (rename)
- 77 → 81 test pass (+4 tests TraLai entry point)
FE rename "Bản nháp" → "Nháp" (cả 2 app + types):
- types/purchaseEvaluation.ts: PurchaseEvaluationPhaseLabel 1=Nháp,
10=Đã gửi duyệt. PeDisplayStatus.BanNhap → Nhap (key + value).
PhaseLabel/Color cho TraLai update active.
- types/contracts.ts: +ChoDuyet=10, +TraLai=98 const + label/color.
Phase 2 'Đang soạn thảo' → 'Nháp'.
- types/budget.ts: +TraLai=98 const + label/color. Phase 1 → 'Nháp'.
- PeListPanel + PurchaseEvaluationsListPage filter dropdown: Nhap +
TraLai map đúng phase value.
BE label maps update consistent:
- ContractExcelExporter PhaseLabel: DangSoanThao → "Nháp" + add ChoDuyet/
TraLai entries.
- PeWorkflowAdminFeatures + WorkflowAdminFeatures PhaseLabels: same.
Verify: dotnet test 81 pass · npm build × 2 app pass · BE 0 error.
Field RejectedAtStepIndex/RejectedFromPhase giữ DB column (nullable,
không set value mới). Cleanup migration sau.
|
|||
| f3bea3c616 |
[CLAUDE] Workflow: Max 3 cấp/bước + N NV/cấp + sequential gating (V2 UAT iter 2)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m16s
User feedback: "tối đa 3 cấp (không có cấp 4)" — không phải bắt buộc 3.
Mỗi cấp = N NV (add bao nhiêu cũng được). Quy trình chạy theo số cấp
thật sự cấu hình (1/2/3). C2 chưa thao tác được khi C1 chưa có NV.
Convention DB: nhiều `ApprovalWorkflowLevel` row cùng Order = same Cấp,
mỗi row = 1 NV. Service iterate group by Order; trong cùng cấp =
OR-of-N (1 NV duyệt → cấp pass).
BE — Application/ApprovalWorkflowsV2/ApprovalWorkflowV2AdminFeatures.cs:
- Validator strict:
- Order ∈ {1, 2, 3} (`MaxLevelsPerStep`)
- Sequential gating: HaveSequentialOrders → 1 / 1+2 / 1+2+3, KHÔNG
cho 2 (thiếu 1) hoặc 1+3 (thiếu 2)
- HaveNoDuplicateApproverInSameLevel: 1 NV không thêm 2 lần cùng cấp
- Schema KHÔNG đổi (giữ ApprovalWorkflowLevel.ApproverUserId 1-1).
- Handler không đổi — auto handle multiple rows cùng Order.
FE — ApprovalWorkflowsV2Page.tsx rewrite Levels section:
- Type EditStep.levels → levelEntries: { order: 1|2|3; approverUserId }[]
flat list (group by order trong render).
- 3 SECTION CỐ ĐỊNH C1/C2/C3 trong Designer:
- Mỗi section: header "Cấp N" + count NV + nút "+ Thêm NV"
- List rows mỗi NV với Select dropdown filtered theo Phòng + Trash
- C2 disabled (opacity-60) khi C1 empty. C3 disabled khi C2 empty.
- Tooltip "+ Thêm NV": "Cấp k-1 phải có ≥1 NV trước"
- Add NV: dropdown chỉ NV thuộc Phòng + chưa được thêm cùng cấp
(no duplicate same level).
- Xóa NV: chặn xóa NV cuối Cấp k nếu Cấp k+1 còn entries (toast error
"Hãy xóa hết NV ở Cấp k+1 trước khi rỗng Cấp k").
- Đổi Phòng → clear toàn bộ levelEntries (NV cũ không thuộc Phòng mới).
- DefinitionCard read-only: group s.levels by Order → render mỗi cấp
là 1 row với badge "Cấp N" + list NV bên dưới.
- Save validate: Phòng required + Cấp 1 ≥1 NV + sequential + NV thuộc
đúng Phòng (defensive double-check).
Verify: dotnet build BE OK · 77 test pass · npm build fe-admin OK.
Logic Service PE/Contract chưa wire schema mới — vẫn pin Mig 21 legacy.
|
|||
| f6047d5218 |
[CLAUDE] Workflow: App CQRS + API ApprovalWorkflowsV2 (Chunk B)
3 handler MediatR + Validator + Controller cho schema mới Mig 22.
Files:
- Application/ApprovalWorkflowsV2/ApprovalWorkflowV2AdminFeatures.cs
- GetAwAdminOverviewQuery (filter optional ApplicableType)
- CreateAwDefinitionCommand + Validator (auto-increment Version
theo Code, deactivate active version cùng ApplicableType)
- DeleteAwDefinitionCommand (UAT helper — chưa pin nên unconditional)
- DTO: AwDefinition/AwStep/AwLevel + TypeSummary
- Application/Common/Interfaces/IApplicationDbContext.cs (3 DbSet)
- Api/Controllers/ApprovalWorkflowsV2Controller.cs
- Route /api/approval-workflows-v2
- GET ?applicableType=N | POST | DELETE/{id}
- Reuse policy Workflows.Read/Workflows.Create
Verify: build OK 0 error, IApplicationDbContext expose 3 DbSet mới.
Next: Chunk C — FE Designer page + route + Layout resolver.
|
|||
| dbb0089e28 |
[CLAUDE] Drastic refactor: flat workflow Phòng × Cấp + Migration 21 (Chunk A)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m18s
User chốt drastic refactor — bỏ phase enum hoàn toàn, dùng ChoDuyet=10 đơn nhất + currentStepIndex tracking. Workflow flat list (Phòng × Cấp × Approvers). Mỗi PE/HĐ pin WorkflowDefinitionId chạy hết quy trình đó. Schema (Migration 21 `RefactorWorkflowToFlatModel`): - Phase enum +ChoDuyet=10 (PE + Contract). Legacy 2-9 + 98 deprecated. - WorkflowStep + DepartmentId Guid? (FK Restrict) + PositionLevel int? (PE + Contract — mirror). - PE/Contract + CurrentWorkflowStepIndex int? + RejectedAtStepIndex int? - DROP table PurchaseEvaluationWorkflowStepInnerSteps (Mig 18) - DROP table WorkflowStepInnerSteps (Mig 20) - DROP column ContractDeptApproval.InnerStepId (Mig 20) - DROP column PEDeptApproval.InnerStepId (Mig 18) - DROP filtered indexes (Mig 19/20) + restore simple unique (TargetId, Phase, Dept, Stage) non-filtered Service rewrite (PE + Contract WorkflowService.TransitionAsync): - Phase transitions: DangSoanThao → ChoDuyet (Drafter trình, init idx=0) - ChoDuyet → ChoDuyet (advance idx per approve) - ChoDuyet → DaDuyet/DaPhatHanh (idx >= steps.Count → terminal) - ChoDuyet → DangSoanThao (Trả lại — save RejectedAtStepIndex) - ChoDuyet → TuChoi (Từ chối — khoá vĩnh viễn) - DangSoanThao + RejectedAtStepIndex → ChoDuyet jump-back to saved idx - Approver match: actor.Dept == step.Dept AND actor.PositionLevel >= step.PositionLevel (OR-of-many cùng cấp/dept = pass) OR Approvers.Any(Kind=User AND id match) OR Approvers.Any(Kind=Role AND actorRoles contains) - Admin role bypass policy. Last step done → gen mã HĐ (Contract only) App CQRS: - WorkflowStepDto + WorkflowStepInput drop InnerStep, add DepartmentId + PositionLevel fields. PE + Contract mirror. Tests rewrite: - DROP PeNStageApprovalTests.cs (6 test) + ContractNStageApprovalTests.cs (6 test) + PeTwoStageApprovalTests.cs (7 test) — legacy N-stage/2-stage no longer applicable - UPDATE PeWorkflowAdminTests signature to new flat input - 96 → 77 test pass (drop 19 legacy) Reference Domain entities removed: - WorkflowStepInnerStep (Contract) - PurchaseEvaluationWorkflowStepInnerStep (PE) - DTOs WorkflowStepInnerStepDto / CreateWorkflowStepInnerStepInput per module Memory `feedback_drastic_refactor_scope.md` validated: drastic refactor done in dedicated session với context fresh, scope ~5h actual (planned ~8-10h with 2x buffer). Verify: - dotnet build SolutionErp.slnx 0 error - dotnet ef database update Mig 21 LocalDB applied OK - dotnet test 77 pass (54 Domain + 23 Infra) - 3-file rule: Migration .cs + Designer.cs + Snapshot updated Pending Chunk B: FE Designer flat UI (PeWorkflowsPage + WorkflowsPage). Pending Chunk C: FE PeWorkflowPanel + workflow timeline display. Pending Chunk D: Docs + Skill + Memory + session log. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 04cf2a0385 |
[CLAUDE] App: Contract workflow inner steps DTO + designer mirror PE (Chunk B)
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
DTO + Validator + Handler mở rộng cho N-stage admin designer Contract (Mig 20). Mirror PE Mig 18 pattern (PeWorkflowAdminFeatures Chunk B). WorkflowAdminFeatures: - WorkflowStepInnerStepDto record (Id, Order, DeptId, DeptName, PositionLevel, Name, SlaDays, IsRequired) - WorkflowStepDto extend +InnerSteps List - GetWorkflowAdminOverviewQueryHandler — Include InnerSteps OrderBy + resolve DeptNames cho display - CreateWorkflowStepInnerStepInput record - CreateWorkflowStepInput extend +InnerSteps (nullable, default null — backward compat existing test code positional new()) - Validator child rules cho InnerSteps (Order ≥1, DeptId not empty, PositionLevel 1-3, SlaDays ≥0) - CreateWorkflowDefinitionCommandHandler — convert InnerSteps khi build entity (atomic batch insert qua nav collection) Verify: dotnet build 0 error, 89 test pass (no regression). Pending Chunk C: ContractWorkflowService.TransitionAsync N-stage logic + legacy 2-stage fallback mirror PE pattern. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 0e56bd0a67 |
[CLAUDE] App: PE workflow inner steps DTO + UpdateUserPositionLevel CQRS (Chunk B)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m42s
DTO + Validator + Handler mở rộng cho N-stage admin designer (Mig 18): PeWorkflowAdminFeatures: - PeWorkflowStepInnerStepDto record (Order/Dept/PositionLevel/Name/SlaDays/IsRequired) - PeWorkflowStepDto extend +InnerSteps List - GetPeWorkflowAdminOverviewQuery: Include InnerSteps OrderBy Order + resolve DeptNames cho display - CreatePeWorkflowStepInnerStepInput record - CreatePeWorkflowStepInput extend +InnerSteps (nullable, default null — backward compat existing test PeWorkflowAdminTests positional new()) - Validator child rules cho InnerSteps (Order >=1, DeptId not empty, PositionLevel 1-3, SlaDays >=0) - Handler convert InnerSteps khi build entity (atomic batch insert) UserFeatures: - UserDto +PositionLevel int? field - ListUsers + GetUser handlers map (int?)u.PositionLevel - SetUserPositionLevelCommand + Validator + Handler mirror SetUserBypassReviewCommand pattern (admin set qua UserManager UI) Verify: - dotnet build SolutionErp.slnx 0 error - dotnet test 83 pass (54+29) — no regression - Backward compat: PeWorkflowAdminTests existing 6 test pass (named-arg positional record vẫn work với InnerSteps default null) Pending Chunk C: PurchaseEvaluationWorkflowService.TransitionAsync N-stage logic + legacy 2-stage fallback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 0f7901c19f |
[CLAUDE] App: PE + Contract Create/Update commands + DTO add manual budget fields
Chunk 2/5 — wire 2 field mới (BudgetManualName + BudgetManualAmount) qua tất cả
CQRS commands + handlers + DTOs cho cả PE và HĐ. Mirror logic per Q3 user.
Files sửa:
~ Application/PurchaseEvaluations/PurchaseEvaluationFeatures.cs
- CreatePurchaseEvaluationCommand record +2 param
- Validator: MaximumLength(200) + GreaterThanOrEqualTo(0)
- Handler: wire entity
- UpdatePurchaseEvaluationDraftCommand record +2 param + handler wire
- GetPurchaseEvaluationQuery → DTO mapping +2 field
~ Application/PurchaseEvaluations/Dtos/PurchaseEvaluationDtos.cs
- PurchaseEvaluationDetailBundleDto +2 field (BudgetManualName/Amount)
~ Application/PurchaseEvaluations/CreateContractFromEvaluationFeatures.cs
- Carry forward pe.BudgetManualName/Amount → contract khi gen HĐ từ phiếu
~ Application/Contracts/ContractFeatures.cs
- CreateContractCommand record +2 param + Validator + Handler
- UpdateContractDraftCommand record +2 param + Handler (diff log thêm 2 field)
- ContractDetailDto mapping +2 field
~ Application/Contracts/Dtos/ContractDtos.cs
- ContractDetailDto +2 field
Validation Q2 chốt: cả 2 cùng null OK. Manual amount có thể null hoặc >= 0.
KHÔNG XOR với BudgetId (BE prefer link BudgetId nếu set, manual fallback only).
Controllers KHÔNG đụng (FromBody bind JSON → record record optional fields gen
auto null cho legacy callers — backward compat).
Verify: dotnet build pass · dotnet test SolutionErp.slnx 83 pass.
Next: Chunk 3 FE-Admin toggle + 2 fields PeHeaderForm + ContractHeaderForm.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 1fc439b978 |
[CLAUDE] App+Api+FE: Chunk E5 — Budget 2-stage dept approval (mirror PE/Contract)
Budget complete the trifecta — đồng bộ pattern 2-stage cho 3 module
(Contract + PE + Budget) cùng UX cho user khi UAT.
BE App:
- TransitionBudgetCommandHandler thêm INotificationService + IDateTime DI
- Mirror logic 2-stage từ ContractWorkflowService:
- actor.DepartmentId != null + KHÔNG admin/system + KHÔNG resume
- DeptManager (TPB) hoặc CanBypassReview → Stage=Confirm
- Else (NV) → Stage=Review only, BLOCK transition
- Upsert BudgetDepartmentApproval (UNIQUE BudgetId+Phase+Dept+Stage)
- Block khi !hasConfirm: insert Approval + Changelog + Notify TPB → return early
- BudgetDepartmentApprovalFeatures.cs (List query mirror PE/Contract)
Api:
- BudgetsController endpoint GET /budgets/{id}/department-approvals
FE (cả fe-admin + fe-user):
- types/budget.ts thêm ApprovalStage const + BudgetDepartmentApproval type
- BudgetWorkflowPanel section "Tiến trình duyệt 2-cấp phòng ban":
- Group by phase × dept, show Review NV + Confirm TPB
- Highlight amber "chờ TPB confirm" + badge fuchsia bypass
Note: low-priority cho Budget (ít user duyệt budget per dept) nhưng giữ
consistent UX 3 module.
Build: BE pass + FE pass cả 2 + 77 test pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| b6f5a16420 |
[CLAUDE] Infra+App+Api+FE: Chunk E4 — HĐ 2-stage dept approval (mirror PE)
Mở rộng 2-stage logic từ PE sang Contract workflow (Migration 16 đã có schema):
BE Service:
- ContractWorkflowService thêm UserManager<User> DI
- Mirror logic 2-stage từ PurchaseEvaluationWorkflowService.TransitionAsync
Sau policy guard, trước gen mã HĐ:
- User.DepartmentId != null + actor không admin/system + KHÔNG resume
- DeptManager (TPB) → Stage=Confirm trực tiếp
- CanBypassReview=true → Stage=Confirm + IsBypassed=true
- Else (NV) → Stage=Review only, BLOCK transition
- Insert ContractDepartmentApproval row (UPSERT theo UNIQUE)
- Block transition khi chưa có Stage=Confirm:
- Insert ContractApproval (FromPhase=ToPhase=fromPhase, [Review NV] comment)
- Insert ContractChangelog "đã review, chờ TPB confirm"
- Notify TPB cùng dept (UserManager filter DeptManager role)
- Return early — phase KHÔNG đổi
App + Api:
- ContractDepartmentApprovalFeatures.cs (List query mirror PE)
- ContractsController endpoint GET /contracts/{id}/department-approvals
FE (cả fe-admin + fe-user):
- types/contracts.ts thêm ApprovalStage const + ContractDepartmentApproval type
- WorkflowHistoryPanel section "Tiến trình duyệt 2-cấp phòng ban":
- Group by phase × dept, show Review NV + Confirm TPB
- Highlight amber "chờ TPB confirm" khi current phase có Review chưa Confirm
- Badge fuchsia "bypass" khi NV.CanBypassReview=true
- Insert giữa WorkflowSummaryCard và Lịch sử duyệt
- Mirror cả 2 app (rule §3.9)
Use case mirror PE: HĐ ở phase DangGopY (P.CCM) — nv.cao (NV) duyệt thì
phase KHÔNG đổi (Review only), chờ ccm.tran (TPB) confirm mới sang DangXetDuyet.
Build: BE pass + FE pass cả 2 + 77 test pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 4380bdc075 |
[CLAUDE] App+FE-Admin: Chunk E3 — UserManager toggle CanBypassReview
Admin UI bật/tắt CanBypassReview per user (Migration 16):
- BE: UserDto thêm field CanBypassReview (List + Get queries)
- FE: User type thêm canBypassReview field
- UsersPage: column "Bypass" badge fuchsia khi true + button toggle ShieldCheck
(icon highlight fuchsia khi enabled, slate khi disabled)
- bypassMut PATCH /users/{id}/bypass-review { canBypassReview: !current }
Use case: phòng ban không có TPB hoặc TPB ủy quyền cho 1 NV cụ thể —
NV được Stage=Confirm trực tiếp (skip Stage Review), IsBypassed=true ghi audit.
Endpoint backend đã có sẵn ở Chunk E1 (commit
|
|||
| 3c4931687a |
[CLAUDE] App+Api+Docs: Chunk E1 — List endpoint + Bypass-review + Notify TPB + chốt session 8
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m15s
3 endpoint mới + Notify TPB + Docs update để chốt session 8.
Application:
- PurchaseEvaluationDepartmentApprovalFeatures.cs (NEW):
* ListPeDepartmentApprovalsQuery + DTO PeDepartmentApprovalDto
* Join Departments (lấy Name) + lookup Users.FullName denorm cho FE timeline
- UserFeatures.cs: SetUserBypassReviewCommand + Handler dùng UserManager.UpdateAsync
- IApplicationDbContext: thêm DbSet<User> Users + DbSet<Role> Roles (cần cho lookup)
Api:
- PurchaseEvaluationsController: GET /api/purchase-evaluations/{id}/department-approvals
- UsersController: PATCH /api/users/{id}/bypass-review (Authorize Users.Update)
Infra:
- PurchaseEvaluationWorkflowService: notify TPB cùng dept khi NV review.
Query db.Users.Where(DeptId match + IsActive) → UserManager.GetRolesAsync
filter DeptManager → notifications.NotifyAsync. Best effort fail non-critical.
Docs:
- STATUS.md: Recently Done thêm row session 8 + Phase header update
count 52→55 tables, 15→16 migrations, 128→131 endpoints
- HANDOFF.md: TL;DR session 8 + 8 cảnh báo session 9 (FE chưa làm,
test flow anh Kiệt, smart reject test, lock edit test, ...)
- migration-todos.md: Phase 9 done section đầy đủ 3 ràng buộc + pending Chunk E-bis
- CLAUDE.md: count 52→55 + migration 16 description
- session log: 2026-05-04-1230-chot-session-8-2-stage-dept-approval.md (full report)
Verify final:
- Build pass 0 warning 0 error
- 77 unit test pass (54 Domain + 23 Infra)
- Migration 16 applied LocalDB OK + schema verified
Total session 8 cumulative: 5 commit per-chunk:
- 5fe61cc (A: Migration 16 schema)
-
|
|||
| 9747f8cbf5 |
[CLAUDE] Infra+App: Chunk C — Smart reject + Resume after reject (3 module)
Ràng buộc 2 (Phase 9): khi reject, trả về Drafter (DangSoanThao) + lưu phase nguồn. Drafter sửa lại + trình lại → quay về phase đã reject (skip phase trung gian). Logic flow: 1. Reject (Decision=Reject): - entity.RejectedFromPhase = currentPhase // snapshot phase đang reject - targetPhase override = DangSoanThao // force về Drafter - Approval row: FromPhase=X, ToPhase=DangSoanThao, Decision=Reject - Notification cho Drafter 2. Resume after reject (Decision=Approve, fromPhase=DangSoanThao, RejectedFromPhase != null): - targetPhase override = entity.RejectedFromPhase!.Value - entity.RejectedFromPhase = null // clear field - Skip policy guard (Drafter có quyền trình lại sau khi sửa) - Approval row: FromPhase=DangSoanThao, ToPhase=ResumePhase, Decision=Approve 3. Normal transition (chưa reject hoặc đã clear): - Logic cũ giữ nguyên — policy guard check + transition Pattern unified cho 3 module: - ContractWorkflowService.TransitionAsync: 2 case detect + override - PurchaseEvaluationWorkflowService.TransitionAsync: tương tự - TransitionBudgetCommandHandler.Handle: tương tự (Budget không có service riêng, logic ở handler) Files: - src/Backend/SolutionErp.Infrastructure/Services/ContractWorkflowService.cs - src/Backend/SolutionErp.Infrastructure/Services/PurchaseEvaluationWorkflowService.cs - src/Backend/SolutionErp.Application/Budgets/BudgetFeatures.cs Verify: - Build pass (2 warning DocxRenderer cũ, không liên quan) - 77 unit test pass — Domain policy không đổi, tests giữ nguyên Note: Approval history giờ track đầy đủ cycle reject→sửa→resume: Approval 1: DangGopY → DangSoanThao, Decision=Reject (CCM reject) Approval 2: DangSoanThao → DangGopY, Decision=Approve (Drafter resume) UI có thể detect "đã từng reject" qua RejectedFromPhase != null hoặc qua Approval history (Decision=Reject row gần nhất). Hiển thị banner đỏ "Phiếu đã bị reject từ phase X, lý do: Y" cho Drafter. Smart reject hoàn tất Ràng buộc 2. Còn Ràng buộc 3 (2-stage dept approval) ở Chunk D. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 14f3c9f817 |
[CLAUDE] App: Chunk B — Lock edit guards (Phase != DangSoanThao) cho 17 handler
Ràng buộc 1 (Phase 9): khi đã trình duyệt → KHÔNG sửa được Header + Detail + Quote nữa. Phải reject để Drafter sửa lại. Pattern dùng: extend helper EnsureContractType + tạo helper PurchaseEvaluationDraftGuard mới cho PE + inline guard cho Budget. Single source of truth cho mỗi module. Handlers added Phase guard (17 total): Contract module (15) — qua EnsureContractType helper: - 7 Add*DetailHandler (ThauPhu/GiaoKhoan/NhaCungCap/DichVu/MuaBan/NguyenTacNcc/NguyenTacDv) - 7 Update*DetailHandler (cùng 7 type) - DeleteContractDetailHandler (inline guard) PE module (5) — qua PurchaseEvaluationDraftGuard helper mới: - AddPurchaseEvaluationDetail - UpdatePurchaseEvaluationDetail - DeletePurchaseEvaluationDetail - UpsertPurchaseEvaluationQuote - DeletePurchaseEvaluationQuote Budget module (3) — inline guard: - AddBudgetDetail - UpdateBudgetDetail (refactor: load Budget thay vì FirstOrDefault sau Detail load → bỏ null check không cần) - DeleteBudgetDetail (refactor: tương tự) KHÔNG lock (intentional): - ContractComment (cần được trong DangGopY phase 3) - ContractAttachment Upload/Delete (Drafter scan ký ở DangInKy phase 5) - PE OpinionUpsert (Ý kiến 4 PB là sign-off, có thể nhập sau khi trình) - PE Attachment (báo giá NCC upload xuyên suốt workflow) Verify: - Build pass (2 warning DocxRenderer cũ) - 77 unit test pass (54 Domain + 23 Infra) — domain policy không đổi Smart reject (ràng buộc 2) + 2-stage dept approval (ràng buộc 3) làm ở Chunk C + D. WorkflowService transition guard chưa update. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 5c200978cb |
[CLAUDE] Domain+Infra: Migration 16 — 2-stage dept approval + smart reject schema
Chunk A của feature 2-stage department approval (Phase 9). 3 ràng buộc gộp 1 migration để rollback atomic. Schema changes: 1. Smart reject (3 ALTER): - Contracts.RejectedFromPhase int NULL - PurchaseEvaluations.RejectedFromPhase int NULL - Budgets.RejectedFromPhase int NULL Lưu phase nguồn khi reject để Drafter trình lại quay về đúng phase (skip phase trung gian đã duyệt) thay vì đi tuần tự từ DangSoanThao. 2. Bypass per-user (1 ALTER): - Users.CanBypassReview bit NOT NULL DEFAULT 0 Khi true → NV được duyệt thay TPB (skip Stage Review, đẩy thẳng Stage Confirm). Audit qua DepartmentApproval.IsBypassed=true. 3. 2-stage dept approval (3 CREATE TABLE): - ContractDepartmentApprovals - PurchaseEvaluationDepartmentApprovals - BudgetDepartmentApprovals Schema (chung): - Id, *Id FK Cascade, PhaseAtApproval int, DepartmentId FK - Stage enum (1=Review NV, 2=Confirm TPB) - ApproverUserId, ApproverRoleSnapshot, Comment, ApprovedAt - IsBypassed bit (mark NV bypass) - AuditableEntity (CreatedAt/By/UpdatedAt/By/IsDeleted/...) Indexes: - UNIQUE (TargetId, PhaseAtApproval, DepartmentId, Stage) - Single: TargetId, DepartmentId, ApproverUserId Files: - Domain: 4 entity update (Contract/PE/Budget/User add field) + 1 enum mới ApprovalStage + 3 entity DepartmentApproval mới - Infrastructure: 3 EntityConfiguration update + 1 file mới DepartmentApprovalsConfiguration với 3 config classes - IApplicationDbContext: thêm 3 DbSet - ApplicationDbContext: thêm 3 DbSet - Migration 16: 3 file (.cs + Designer.cs + Snapshot.cs) — 3-file rule Verify: - Build pass (2 warning DocxRenderer cũ, không liên quan) - 77 unit test pass (54 Domain + 23 Infra) — Domain policy chưa update, test pass nguyên không regression Note: KHÔNG khác PurchaseEvaluationDepartmentOpinion (Migration 15) — Opinion là sign-off block "Ý kiến 4 phòng ban" trên header phiếu. DepartmentApproval mới là 2-stage approval workflow per phase. Tổng sau Migration 16: 55 bảng (52+3), 16 migration. Chunk B-E sẽ implement Application + FE + Tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 5d94bb449a |
[CLAUDE] PE: Workflow designer admin UI + Ý kiến 4 phòng ban (P1 Session 5)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m51s
==== Task 1: PE Workflow Designer admin ====
BE (mirror Contract WorkflowAdminFeatures pattern):
- Application/PurchaseEvaluations/PeWorkflowAdminFeatures.cs ~250 LOC:
- GetPeWorkflowAdminOverviewQuery → list 2 EvaluationType (DuyetNcc / DuyetNccPhuongAn) với Active + History versions + count phiếu đang dùng
- CreatePeWorkflowDefinitionCommand + Validator: auto-increment Version per Code, deactivate Active cũ trong cùng EvaluationType (1 active per type invariant)
- DTOs: PeWorkflowStepApproverDto / PeWorkflowStepDto / PeWorkflowDefinitionDto / PeWorkflowTypeSummaryDto / PeWorkflowAdminOverviewDto
- Phase validation 1..7 (state thường, không bao gồm 99=TuChoi)
- Api/Controllers/PeWorkflowsController.cs: 2 endpoint GET /api/pe-workflows + POST. Reuse policy "Workflows.Read" + "Workflows.Create" (admin chung quyền cho cả 2 nhóm WF).
FE:
- pages/system/PeWorkflowsPage.tsx ~500 LOC mirror WorkflowsPage:
- Landing 2-card grid khi /system/pe-workflows (chưa pick type)
- TypePanel khi /system/pe-workflows/:typeCode (DuyetNcc / DuyetNccPhuongAn)
- DefinitionCard read-only view với active badge + version + steps + approvers (Role/User chip)
- PeWorkflowDesigner dialog: clone từ existing, edit Code/Name/Description, add/remove steps, +Role / +User approvers per step, save → version mới + deactivate cũ
- App.tsx route /system/pe-workflows + /system/pe-workflows/:typeCode
- Layout đã có resolver PeWf_<Code> → /system/pe-workflows/<code> từ session 3
==== Task 2: Ý kiến 4 phòng ban PE ====
Domain:
- PurchaseEvaluationDepartmentOpinion entity (AuditableEntity) — PEId + Kind + Opinion text + SignedAt + UserId + UserName denorm
- PeDepartmentKind enum (PheDuyet / Ccm / MuaHang / SmPm)
- PE entity + collection navigation DepartmentOpinions
Infrastructure:
- PurchaseEvaluationDepartmentOpinionConfiguration EF: UNIQUE(PEId, Kind) — max 1 row per phòng ban per phiếu (UPDATE in-place)
- ApplicationDbContext + IApplicationDbContext DbSet
- Migration 15 AddPurchaseEvaluationDepartmentOpinions (15 migration total / 52 DB tables)
Application:
- PeDepartmentOpinionFeatures.cs: UpsertPeDepartmentOpinionCommand (sign=true → set SignedAt+UserId, sign=false chỉ lưu text giữ chữ ký cũ) + DeletePeDepartmentOpinionCommand
- DTO bundle update: + DepartmentOpinions list trong PurchaseEvaluationDetailBundleDto
- GetPurchaseEvaluationQueryHandler load DepartmentOpinions + KindLabel resolution
API:
- POST /api/purchase-evaluations/{id}/opinions (upsert)
- DELETE /api/purchase-evaluations/{id}/opinions/{kind}
FE:
- types/purchaseEvaluation.ts: + PeDepartmentKind enum + PeDepartmentKindLabel + PeDepartmentOpinion type + departmentOpinions vào bundle
- PeDetailTabs Section "5. Ý kiến 4 phòng ban (sign-off)" — 2x2 grid OpinionBox per kind:
- Read mode (readOnly menu Duyệt): hiển thị text + chữ ký
- Edit mode: textarea + 2 button "Lưu text" / "Lưu & Ký"
- Badge "Đã ký" emerald + tên người ký + ngày khi signedAt != null
==== Task 3: User seed verify ====
Seed `SeedDemoUsersAsync` đã match đúng user list authoritative (5 PRO TPB+NV / 7 CCM TPB+NV / 1 ISO / 1 CEO) từ prior commit. DbInitializer reconcile sẽ tự sync khi API restart. Typo trong list user (soluttions / trương) đã fixed sensibly trong seed.
==== Build verify ====
- dotnet build clean (0 error)
- fe-admin TS build pass (1 module mới PeWorkflowsPage)
- fe-user TS build pass (PE detail mirror)
Total: 8 file mới (BE 4 + FE 1 + Migration 2 + 1 Domain) + 13 file modified.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 61e5d4d503 |
[CLAUDE] PE+Contract+Budget integration — link Budget vào PE/HĐ + cột So với ngân sách
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m51s
BE wire BudgetId nullable FK qua command + DTO bundle:
- Budgets.Dtos: + BudgetSummaryDto (compact header snapshot, không kèm Details — gọi /budgets/{id} riêng nếu cần đối chiếu chi tiết)
- PurchaseEvaluations.Dtos: + BudgetId? + Budget? BudgetSummaryDto vào PurchaseEvaluationDetailBundleDto
- Contracts.Dtos: + BudgetId? + Budget? BudgetSummaryDto vào ContractDetailDto
- CreatePE + UpdatePEDraft + handlers: + BudgetId? param + validate (cùng Project + Phase=DaDuyet) + persist
- CreateContract + UpdateContractDraft + handlers: + BudgetId? param + validate + persist + log diff
- GetPE + GetContract handlers: load BudgetSummary nếu có link
- CreateContractFromEvaluation: carry forward pe.BudgetId → contract.BudgetId (nếu phiếu PE đã link)
FE PE (cả 2 app):
- types/purchaseEvaluation.ts: + BudgetSummary type + budgetId/budget vào PeDetailBundle
- PurchaseEvaluationCreatePage: thêm Select 'Ngân sách' filter Phase=DaDuyet + Project match (BE-side filter qua /budgets?projectId=&phase=4). Disabled khi chưa pick Project. Edit mode preserve.
- PeDetailTabs InfoTab: hiển thị Budget link với mã + tên + tổng (clickable → /budgets?id=)
- PeDetailTabs ItemsTab: thêm cột 'NS link · Δ' chỉ hiện khi ev.budgetId. Match per-row qua key groupCode|itemCode → fetch /budgets/{id} riêng. Footer aggregate row 'Tổng' + delta indicator (xanh dưới / đỏ vượt / xám khớp). No-match cell hiện '—'.
FE Contract (cả 2 app):
- types/contracts.ts: + ContractBudgetSummary + budgetId/budget vào ContractDetail
- ContractCreatePage HeaderForm: thêm Budget Select sau FormFields, useEffect reset khi đổi project
- ContractCreatePage EditForm: Select khi isDraft / read-only link card khi !isDraft
TS build pass cả 2 app + dotnet build clean. No new migration (BudgetId? nullable FK đã có từ migration 14).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| a05c57b081 |
[CLAUDE] Domain+App+Api: Module Ngan sach (Budget) - 4 bang + workflow simple
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m11s
User request: 'Them cho tao 4 bang luu ve ngan sach: Header / Chi tiet
/ Quy trinh duyet / Lich su thay doi'.
Domain (5 file + 1 enum):
- Budget (header) — Aggregate root, AuditableEntity. Field: MaNganSach,
TenNganSach, Description, NamNganSach, ProjectId FK, DepartmentId?,
DrafterUserId, Phase (BudgetPhase 5-state), TongNganSach (sum auto
tu Details), SlaDeadline, SlaWarningSent.
- BudgetDetail — flat row pattern (GroupCode/GroupName + Item +
KhoiLuong/DonGia/ThanhTien). 18,4 precision KhoiLuong, 18,2 money.
- BudgetApproval — workflow history (FromPhase/ToPhase/Decision/Comment)
- BudgetChangelog — audit log unified (EntityType: Header/Detail/Workflow)
- BudgetPhase enum 5 state: DangSoanThao(1) → ChoCCM(2) → ChoCEO(3) →
DaDuyet(4) | TuChoi(99)
- BudgetPolicy hardcoded (no versioned WF, simple default per user
confirm 'tam thoi don gian'): Drafter/DeptManager → CCM → CEO/
AuthorizedSigner. Reject path back to DangSoanThao.
Migration 14 AddBudgets:
- 4 bang moi: Budgets + BudgetDetails + BudgetApprovals + BudgetChangelogs
- Index: Phase+IsDeleted, ProjectId, NamNganSach, SlaDeadline,
MaNganSach unique filtered. Cascade delete child.
- +2 cot FK ngoai bang (per user 'lien ket ca 3'):
* Contracts.BudgetId Guid? + index
* PurchaseEvaluations.BudgetId Guid? + index
Cho phep doi chieu chi phi HD/PE vs ngan sach goi thau.
Application CQRS (BudgetFeatures.cs ~340 line):
- CreateBudget + UpdateBudgetDraft + TransitionBudget + ListBudgets
(filter Phase/Project/Year + search + paging) + GetBudget bundle
(Header + Details + Approvals + Workflow summary)
- DeleteBudget (only DangSoanThao/TuChoi)
- AddBudgetDetail + UpdateBudgetDetail + DeleteBudgetDetail (auto
recompute TongNganSach = sum Details.ThanhTien)
- ListBudgetChangelogs
Api: BudgetsController 11 endpoint REST /api/budgets:
- GET / /{id} /{id}/changelogs
- POST / /{id}/transitions /{id}/details
- PUT /{id} /{id}/details/{detailId}
- DELETE /{id} /{id}/details/{detailId}
DbContext + IApplicationDbContext: 4 DbSet new (Budgets/Details/
Approvals/Changelogs).
MenuKeys + DbInitializer: 4 menu key (Budgets root + Bg_List/Create/
Pending leaves) seed dau order=27 'Ngan sach' icon Wallet. Auto-grant
admin permission via SeedAdminPermissionsAsync (MenuKeys.All).
MaNganSach format don gian 'NS-YYYYMM-NNNN' Random.Shared (chua atomic
sequence - user said 'tam thoi chua co').
Workflow chua versioned, hardcode BudgetPolicy.Default. Tuong lai admin
config qua UI: them BudgetWorkflowDefinition tables tuong tu PE.
|
|||
| d1090843a2 |
[CLAUDE] PE: upload file dinh kem per-NCC (doi chieu bao gia)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m9s
User request: 'cho cac NCC 1,2 va 3 thi cho them cho upload file dinh
kem cho tung NCC de co the doi chieu'.
Entity PurchaseEvaluationAttachment + PurchaseEvaluationSupplierId nullable
da thiet ke san tu migration 12 — gio wire up BE + FE.
BE (Application/Api):
- PurchaseEvaluationAttachmentFeatures: Upload (multipart + supplierRowId
optional) + Download + Delete. Reuse IFileStorage + LocalFileStorage.
Validator 20MB + MIME whitelist (pdf/doc/docx/xls/xlsx/png/jpg/webp).
- Upload log vao PurchaseEvaluationChangelogs (Attachment + Insert).
- PurchaseEvaluationAttachmentDto + them field Attachments vao bundle.
- GetPurchaseEvaluationQueryHandler Include(x => x.Attachments) +
OrderByDescending(a => a.CreatedAt) projection.
- PurchaseEvaluationsController 3 endpoint:
POST /attachments (IFormFile + [FromForm] supplierRowId/purpose/note)
GET /attachments/{attId}/download (File stream)
DELETE /attachments/{attId}
- Storage path: wwwroot/uploads/purchase-evaluations/{id}/{attId}_{safeName}
FE (fe-admin + fe-user):
- Type PeAttachment + PeAttachmentPurpose/Label (QuoteDocument default)
- PeDetailBundle.attachments: PeAttachment[]
- SuppliersTab thay column Hien thi + Ghi chu bang column File dinh kem
(per-NCC upload + list N attachments + download + delete).
- SupplierAttachmentsCell component: <input type=file> hidden + [+ Them
file] button + inline list attachments voi Paperclip icon + filename
(click tai ve) + size + purpose chip + Trash2 delete.
|
|||
| 7783bd6005 |
[CLAUDE] App: menu tree inheritance mo rong Pe_* + PeWf_*
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m54s
GetMyMenuTreeQuery truoc chi inherit Contracts (Ct_*) va Workflows (Wf_*). Extend 2 root moi PurchaseEvaluations (Pe_*) + PeWorkflows (PeWf_*) de admin co PurchaseEvaluations.Read auto thay 2 group Pe_* + 6 leaf (Danh sach/Thao tac/Duyet x 2 type) + 2 PeWf_* leaf admin designer UI, khong can add per-subitem permission row. Verify bug: /menus/me cho admin hien chi root 'PurchaseEvaluations' + 'PeWorkflows' nhung khong co Pe_DuyetNcc group / Pe_DuyetNccPhuongAn group children du DB co 12 row (sqlcmd confirm). Root cause: hardcoded 2 inherit roots trong BuildChildren switch. Fix: expand switch cover 4 inherit roots. Propagate nextInherit xuong tat ca descendants. |
|||
| c48ac2116d |
[CLAUDE] PurchaseEvaluation: demo seed 4 phieu + MaPhieu atomic sequence + Pe_* perm defaults
Polish session tiep cua PE module skeleton (commit 2c6f0ca..3990066):
3 task A (MISSING in MVP) khac STATUS.md In Progress:
1. Demo PE data seed (SeedDemoPurchaseEvaluationsAsync)
- 4 phieu varied A/B x phase: A-001 DangSoanThao (mo), A-002
ChoCEODuyetNCC (winner+9 quotes), A-003 DaDuyet (chua tao HD,
PaymentTerms JSON), B-001 ChoDuAn (5-step giua chung).
- Idempotent: skip-if-[DEMO]-exists.
- Approval history dung policy A (3-step) hoac B (5-step).
2. MaPhieu atomic sequence — Migration 13
- Format PE/{YYYY}/{TypeLetter}/{Seq:D3} (vd PE/2026/A/001).
- PurchaseEvaluationCodeSequence entity (Prefix PK).
- IPurchaseEvaluationCodeGenerator + impl SERIALIZABLE
transaction (mirror ContractCodeGenerator 1:1).
- Replace Random.Shared trong CreatePurchaseEvaluationCommandHandler.
- Migration AddPurchaseEvaluationCodeSequences (1 bang).
3. Pe_* permission defaults
- SeedPurchaseEvaluationPermissionDefaultsAsync — 7 role business x 9 menu key.
- Drafter/DeptManager/Procurement: R+C+U; CostControl/PM/Director/AuthorizedSigner: R+U.
- DeptManager them Delete (xoa nhap).
- Idempotent per-(roleId x menuKey).
Build: 0 error, 2 warning (pre-existing DocxRenderer).
Files: 4 new + 8 modified (1 migration + entity + generator + DI + 2 ctx + 2 features).
Resolves: STATUS.md In Progress §A — 3 item PE MISSING.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| a385d70c2e |
[CLAUDE] App+Api+FE: Kế thừa HĐ từ phiếu Duyệt NCC (Phase 4)
BE:
- CreateContractFromEvaluationCommand: guard DaDuyet + SelectedSupplier
+ ContractId=null → tạo Contract draft mới với SupplierId/ProjectId/
DepartmentId kế thừa từ PE. GiaTri = sum(details.thanhTienNganSach).
DraftData = PE.PaymentTerms. Gen MaHopDong ngay + pin WorkflowDefinitionId
theo ContractType user chọn. Log Changelog cả 2 bảng (Contract +
PurchaseEvaluation), link 2 chiều PE.ContractId = contract.Id.
- ListApprovedPurchaseEvaluationsQuery: DaDuyet + ContractId=null cho
FE picker.
- 2 endpoint mới:
GET /api/purchase-evaluations/approved-pending-contract
POST /api/purchase-evaluations/{id}/create-contract
FE:
- PeDetailTabs InfoTab: nếu Phase=DaDuyet && !ContractId && SelectedSupplierId
→ banner emerald + button "Tạo HĐ từ phiếu" → CreateContractDialog
(pick ContractType dropdown 7 loại + TenHopDong + bypass CCM flag)
- Sau khi tạo → navigate /contracts/{newId}
- Mirror fe-user.
KHÔNG auto-map PE Details → Contract Details per-type (PE schema ≠ 7
ContractType details schemas — user điền lại sau). PE → Contract link
qua FK ContractId cho navigation + history.
|
|||
| 4678d192e2 |
[CLAUDE] App+Api: PurchaseEvaluation CQRS + Controller + WorkflowService
Application (4 file, ~900 lines): - IPurchaseEvaluationWorkflowService + PurchaseEvaluationDtos - PurchaseEvaluationFeatures: Create / UpdateDraft / Transition / List / Inbox / GetDetail (bundle Suppliers+Details+Quotes+Approvals+Workflow) / Delete / ListChangelogs. IDOR filter role-based phase eligibility. - PurchaseEvaluationSupplierFeatures: Add / Update / Remove supplier (N:M Phiếu × Supplier). Block remove nếu còn Quote FK reference. - PurchaseEvaluationDetailFeatures: Add/Update/Delete hạng mục + Upsert/Delete Quote + SelectWinner (set SelectedSupplierId). Infrastructure: - PurchaseEvaluationWorkflowService: policy load pinned definition → guard role + transition rules. Emit Notification drafter khi state-change. Tạo PurchaseEvaluationApproval + Changelog row. Api: - PurchaseEvaluationsController ~15 endpoint: CRUD phiếu, N:M supplier, hạng mục CRUD, Quote upsert, SelectWinner, Changelog list. Route /api/purchase-evaluations. DI: đăng ký IPurchaseEvaluationWorkflowService scoped. Skip MVP: Attachments upload, Admin PeWorkflows designer UI (sẽ phase sau — framework versioned WF table đã sẵn, designer pattern copy từ HĐ). |
|||
| 2c6f0cabfb |
[CLAUDE] Domain+Infra: PurchaseEvaluation module — 10 bảng + 2 workflow seed (migration 12)
Module Duyệt NCC (tiền-HĐ): phiếu trình duyệt so sánh giá N NCC × M hạng mục trước khi ký HĐ. 2 quy trình: A DuyetNcc (3-step: Purchasing→CCM→CEO), B DuyetNccPhuongAn (5-step: Purchasing→DựÁn→CCM→CEO PA→CEO NCC). Domain (7 core + 3 workflow admin): - PurchaseEvaluation (header, AuditableEntity, pin WorkflowDefinitionId, SelectedSupplierId, PaymentTerms JSON, ContractId? FK kế thừa) - PurchaseEvaluationSupplier (N:M Phiếu × Supplier + contact + payment term) - PurchaseEvaluationDetail (hạng mục + ngân sách, group A.I/A.II/...) - PurchaseEvaluationQuote (báo giá per NCC per hạng mục + IsSelected) - PurchaseEvaluationApproval (workflow history, reuse ApprovalDecision) - PurchaseEvaluationChangelog (audit log, reuse ChangelogAction) - PurchaseEvaluationAttachment (file upload — báo giá NCC + spec...) - PurchaseEvaluationWorkflowDefinition/Step/StepApprover (config y như HĐ, tách table riêng vì Phase là PurchaseEvaluationPhase enum riêng) Policy: - PurchaseEvaluationPolicy record + PurchaseEvaluationPolicies.NccOnly/ NccWithPlan (default hardcoded) + FromDefinition(def) build runtime policy từ DB admin-authored. Default SLA: soạn 3d, step 1-2d, CEO 1d. EF: 10 configurations với index phase+isDeleted, SupplierId, ProjectId, SlaDeadline, WorkflowDefinitionId, ContractId. UX index (PeId, SupplierId) + (DetailId, SupplierId). HasQueryFilter soft delete cho header. Migration 12 AddPurchaseEvaluations tạo 10 bảng. Idempotent seed: - SeedMenuTreeAsync +13 menu item (Pe_* root + 2 group + 6 action leaf + PeWorkflows root + 2 admin leaf) - SeedPurchaseEvaluationWorkflowsAsync seed QT-DN-A-v01 + QT-DN-B-v01 |
|||
| e53cd3a3b2 |
[CLAUDE] App+Api+FE+Scripts: Edit detail row inline + deps audit helper
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m45s
## Edit detail row inline (BE)
7 typed UpdateXxxDetailCommand handler trong ContractDetailsFeatures.cs
— pattern lặp giống Add commands, EnsureContractType guard + log
ChangelogAction.Update với summary "Sửa <hạng mục/SP/CV/...>".
7 PUT endpoints trong ContractsController:
- PUT /contracts/{id}/details/{thau-phu|giao-khoan|nha-cung-cap|dich-vu|
mua-ban|nguyen-tac-ncc|nguyen-tac-dv}/{detailId}
## Edit detail row inline (FE)
ContractDetailsTab.tsx refactor:
- DeleteBtn → ActionBtns (Pencil + Trash) với onEdit + onDelete callbacks
- 7 XxxTable signatures + onEdit prop + pass row data via callback
- New EditRowDialog component:
* useEffect populate form từ row data khi target thay đổi
* Reuse FIELDS_BY_TYPE config + buildPayload (compute thanhTien)
* Date field convert ISO → yyyy-MM-dd cho input[type=date]
* PUT /contracts/{id}/details/{slug}/{detailId}
- Parent state editTarget — open dialog, close khi save thành công
Mirror fe-admin (file copy).
## Deps audit helper script
scripts/deps-audit.ps1 — chạy thủ công hoặc CI integration:
- dotnet list package --vulnerable --include-transitive (BE)
- npm audit --audit-level=moderate (fe-admin + fe-user)
- Color-coded output (green/red), summary cuối
- -FailOnHigh switch để CI gate
Skill ref .claude/skills/dependency-audit-erp/SKILL.md (đã có) cho
pin constraints + workflow fix.
## Build
- BE: dotnet build pass (0 error)
- fe-user: tsc + vite pass (11.52s)
- fe-admin: tsc + vite pass (577ms)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 072ad6d014 |
[CLAUDE] App+Api+FE-Admin: RolesPage CRUD (/system/roles)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m58s
User feedback: /system/roles trỏ tới placeholder "chưa được build" — build trang quản lý 12 role mặc định + custom role admin tự thêm. ## BE — PermissionFeatures.cs 3 command mới: - CreateRoleCommand — Name regex `^[A-Za-z][A-Za-z0-9_]*$` (chỉ chữ/số/ underscore, bắt đầu chữ), throw ConflictException nếu code đã tồn tại - UpdateRoleCommand — CHỈ update ShortName + Description. KHÔNG đổi Name (Identity FK trong UserRoles + WorkflowStepApprover.AssignmentValue + [Authorize(Roles="...")] attr — đổi = data corruption widespread) - DeleteRoleCommand — block 2 trường hợp: * Role thuộc AppRoles.All hardcoded (workflow guard reference) * Còn user assigned (UserManager.GetUsersInRoleAsync count > 0) ValidationException reference fully-qualified để tránh ambiguous với FluentValidation.ValidationException. ## BE — RolesController 3 endpoint mới (POST/PUT/DELETE) — Authorize Admin role. ## FE — RolesPage Table list 12 + custom roles với 5 column (Mã code / Mã viết tắt / Tên đầy đủ / Loại badge / Ngày tạo) + actions Edit/Delete: - Edit dialog: chỉ ShortName + Description editable, Name disabled với hint "Không đổi được sau khi tạo" - Delete: block với toast nếu role mặc định (HARDCODED_ROLES set check client-side trước khi gọi BE — UX faster, BE vẫn double-check) - Create dialog: 3 field Name (regex pattern HTML5) + ShortName + Description - Banner amber warning về Mã code FK constraint - Loại badge: Mặc định (slate) vs Tùy chỉnh (brand) ## FE — App.tsx + import RolesPage + route /system/roles → RolesPage. ## Build - BE: dotnet build pass (0 error) - fe-admin: tsc + vite pass (13.88s) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 330d529c92 |
[CLAUDE] Domain+App+Infra: Role ShortName + User Department/Position + 13 demo users (migration 11)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m53s
User feedback: chi tiết hóa Users/Phòng ban + gán roles. Roles label tiếng Việt có Mã (ShortName) + Tên đầy đủ (Description). ## Entity changes ### Role (Domain/Identity/Role.cs) + ShortName (max 50) — Mã viết tắt VN: QTV/BOD/CCM/PRO/FIN/... + Description (đã có) — Tên đầy đủ VN - Identity Name = code English giữ nguyên (FK + [Authorize] attr) ### User (Domain/Identity/User.cs) + DepartmentId Guid? FK Departments (Restrict — không xóa dept nếu user reference) + Position string? max 200 — chức vụ free text ## Migration 11: AddRoleShortNameAndUserDepartment 3-file rule. Apply LocalDB OK. DB total: 36 tables (không tăng — chỉ thêm cột vào existing). ## Seed VN labels (12 roles) | Code | ShortName | Description | |---|---|---| | Admin | QTV | Quản trị viên hệ thống | | Drafter | NV.PB | Nhân viên phòng ban (soạn thảo HĐ) | | DeptManager | TPB | Trưởng phòng ban | | ProjectManager | PM | Giám đốc dự án | | Procurement | PRO | Phòng Cung ứng | | CostControl | CCM | Phòng Kiểm soát chi phí | | Finance | FIN | Phòng Tài chính | | Accounting | ACT | Phòng Kế toán | | Equipment | EQU | Phòng Thiết bị | | Director | BOD | Ban Giám đốc | | AuthorizedSigner | NĐUQ | Người được Ủy quyền ký HĐ | | HrAdmin | HRA | Phòng Nhân sự - Hành chính | SeedRolesAsync idempotent + backfill (existing role thiếu ShortName/ Description → update). ## Seed 13 demo users Default password: User@123456 (warn log để rotate prod). Coverage full org chart: - bod.huynh (Tổng GĐ — Director, BOD dept) - bod.le (Phó GĐ NĐUQ — AuthorizedSigner, BOD dept) - pm.nguyen (PM FLOCK 01 — ProjectManager, PM dept) - ccm.tran (TPB CCM — CostControl + DeptManager, CCM dept) - pro.pham (TPB PRO — Procurement + DeptManager, PRO dept) - fin.do, act.vu, equ.bui, hra.dang (TPB respective dept) - qs.hoang, qs.ngo (Drafter — QS dept) - nv.cao, nv.dinh (Drafter — PRO/FIN dept) Idempotent (skip nếu email đã tồn tại). ## DTOs + Commands updated - RoleDto + ShortName field - UserDto + DepartmentId/DepartmentName/Position - CreateUserCommand + DepartmentId/Position params (defaults null) - UpdateUserCommand + DepartmentId/Position - ListUsersQueryHandler load dept names denormalize per page - UpdateUserCommandHandler set UpdatedAt ## Note FE updates (UsersPage dept dropdown + role label VN) ở commit kế tiếp. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| e27c54702a |
[CLAUDE] Domain+App+Api: 4 master catalogs cho Details (migration 10)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m48s
User feedback: thêm Master Data cho phần Chi tiết (line items) — autocomplete khi user nhập field thay vì gõ tay free text. ## 4 entities mới (Domain/Master/Catalogs/) | Entity | Bảng | Dùng cho HĐ Detail | |---|---|---| | UnitOfMeasure | UnitsOfMeasure | Tất cả 7 type (DonViTinh) | | MaterialItem | MaterialItems | NCC + Mua bán + Nguyên tắc NCC (MaSP/TenSP) | | ServiceItem | ServiceItems | Dịch vụ + Nguyên tắc DV (MaDichVu/TenDichVu) | | WorkItem | WorkItems | Thầu phụ + Giao khoán (HangMuc/MaCongViec) | Common pattern: Code unique (filter IsDeleted=0) + Name + Category + DefaultUnit + AuditableEntity (soft delete) + IsActive flag. ## Migration 10: AddMasterCatalogs 3-file rule (gotcha #17). Total DB: 32 → 36 tables. Apply LocalDB OK. ## Seed defaults (idempotent — skip per-table nếu có row) - 20 UnitsOfMeasure: m2, m3, kg, tấn, lít, ngc (ngày công), giờ, gói, ... - 15 MaterialItems demo: xi măng PCB40, cát vàng, đá 1x2, thép D10, gạch 4 lỗ, sơn lót, ống PVC... - 10 ServiceItems demo: vận chuyển, bảo trì, tư vấn, kiểm định, vệ sinh... - 15 WorkItems demo: đào móng, đổ bê tông, xây tường, trát, lát gạch, sơn nước, lắp điện, lắp nước, thấm chống... ## CQRS (Application/Master/Catalogs/CatalogsFeatures.cs ~290 dòng) Mỗi catalog 5 handlers (List filter q+category, Create với unique code guard, Update, Delete soft). FluentValidation max length per spec EF. ## Controller (Api/Controllers/CatalogsController.cs) 13 endpoints: - GET /api/catalogs/{units|materials|services|work-items} - POST /api/catalogs/{kind} (Admin role) - PUT /api/catalogs/{kind}/{id} (Admin role) - DELETE /api/catalogs/{kind}/{id} (Admin role) Read open cho mọi role (FE Details add form autocomplete cần list). ## Menu (5 mới) - Catalogs (group, Master parent, order 24, "Library" icon) - CatalogUnits "Đơn vị tính" (Ruler) - CatalogMaterials "Vật tư / SP" (Package) - CatalogServices "Dịch vụ" (Wrench) - CatalogWorkItems "Hạng mục công việc" (ListChecks) Admin auto-grant tất cả CRUD action (qua SeedAdminPermissionsAsync loop MenuKeys.All). ## Build dotnet build BE pass (0 error) ## Note FE admin page CatalogsPage + datalist autocomplete trong Details form sẽ ở commit kế tiếp. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 51449d6b9d |
[CLAUDE] App+Infra: Mã HĐ gen ngay tại CreateContract + backfill HĐ legacy
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m45s
User feedback: HĐ phải có mã ngay khi tạo (không đợi đến DangDongDau như
cũ). HĐ đã tạo trước đây nhưng chưa có mã → backfill tự động.
## Thay đổi
### CreateContractCommandHandler (App)
- Inject IContractCodeGenerator
- Load supplier + project FULL (cần Code, không chỉ check tồn tại như trước)
- Call codeGenerator.GenerateAsync TRƯỚC khi db.Contracts.Add — entity
chưa tracked nên GenerateAsync internal SaveChangesAsync chỉ save SEQ
(không kèm contract chưa tracked)
- Set entity.MaHopDong = result trước khi Add → INSERT contract đã có mã
- Changelog summary include mã: "Tạo HĐ {mã} — {tên}"
### Trade-off documented
- Mã gen sớm → HĐ TuChoi sẽ "wasted" 1 mã (gap trong sequence)
- Acceptable vì user cần mã reference vào tài liệu/giấy tờ ngay từ đầu
### ContractWorkflowService.TransitionAsync (Infra)
- Giữ logic cũ `if MaHopDong is null → gen` ở DangDongDau
- Update comment: nominal flow skip vì mã đã có; defensive cho HĐ legacy
hoặc HĐ tạo bằng path khác (seed/import)
### DbInitializer.BackfillContractCodesAsync (Infra)
- Chạy 1 lần trước WarnDefaultAdminPasswordAsync
- Idempotent: count Contracts WHERE MaHopDong IS NULL → skip nếu 0
- Loop từng HĐ: load supplier+project → GenerateAsync → SaveChangesAsync
- Skip + log warning nếu missing supplier/project (legacy data corruption)
- Try-catch per HĐ, log success/failed count cuối cùng
## Build
dotnet build BE pass (0 error, 2 pre-existing DocxRenderer warning)
## Note
Khi deploy lên prod, DbInitializer chạy startup → backfill HĐ cũ tự động.
Log line "Backfill mã HĐ: X HĐ thiếu mã, đang gen..." sẽ xuất hiện ở
Logs/log-{date}.txt để verify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| e6844553a4 |
[CLAUDE] App+Api: 7 Details CRUD endpoints + Changelogs query
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m37s
## DTOs (Application/Contracts/Dtos/ContractDetailDtos.cs)
- 7 typed DTOs (ThauPhuDetailDto, GiaoKhoanDetailDto, NhaCungCapDetailDto,
DichVuDetailDto, MuaBanDetailDto, NguyenTacNccDetailDto,
NguyenTacDvDetailDto) — schema 1-1 với Domain entities
- ContractDetailsBundleDto — wrapper trả về theo Type, chỉ 1 list có data
(FE đọc field tương ứng, tránh polymorphic deserialize phức tạp)
- ContractChangelogDto — full audit entry
## CQRS (Application/Contracts/ContractDetailsFeatures.cs)
- GetContractDetailsQuery — load bundle, switch theo Contract.Type chỉ
query bảng tương ứng (avoid 7 query waste)
- 7 AddXxxDetailCommand handlers — typed payload + EnsureContractType
guard (throw ConflictException nếu Contract.Type sai)
- DeleteContractDetailCommand generic — dispatch xóa theo Type, log change
- Tất cả handler call IChangelogService.LogDetailChangeAsync với summary
human-readable (vd "Thêm hạng mục: Đào móng")
## CQRS (Application/Contracts/ContractChangelogFeatures.cs)
- ListContractChangelogsQuery (read-only) — desc CreatedAt, default top 200
## Controller (Api/Controllers/ContractsController.cs)
8 endpoints mới:
- GET /api/contracts/{id}/details → bundle theo Type
- POST /api/contracts/{id}/details/{thau-phu|giao-khoan|nha-cung-cap|
dich-vu|mua-ban|nguyen-tac-ncc|nguyen-tac-dv} → 7 typed POST
- DELETE /api/contracts/{id}/details/{detailId} → generic delete
- GET /api/contracts/{id}/changelogs → list audit entries
Build: dotnet pass (0 error)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 71c035d31e |
[CLAUDE] App+Infra: IChangelogService + log Workflow/Contract/Comment/Attachment
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m26s
User decision B (log cả 3): mọi thay đổi liên quan HĐ ghi vào unified ContractChangelogs để render tab Lịch sử FE. ## IChangelogService (Application/Common/Interfaces/) 5 methods: - LogContractChangeAsync — Header insert/update - LogDetailChangeAsync — line item insert/update/delete - LogWorkflowTransitionAsync — phase change (parallel với ContractApprovals) - LogCommentAddedAsync — góp ý mới - LogAttachmentAsync — upload/delete file KHÔNG SaveChanges trong service — caller chịu trách nhiệm save atomic cùng business changes (pattern giống INotificationService). ## ChangelogService impl - Resolve actor qua ICurrentUser → UserManager.FindByIdAsync - Denormalize UserName (FullName ?? Email) cho log readable - null UserId = system action (vd SLA auto-approve) - DI: AddScoped trong DependencyInjection.cs ## Wiring vào handlers hiện tại - ContractWorkflowService.TransitionAsync — LogWorkflowTransitionAsync sau khi insert ContractApproval - CreateContractCommandHandler — LogContractChangeAsync(Insert) - UpdateContractDraftCommandHandler — diff GiaTri/TenHopDong/NoiDung/ TemplateId trước update, log update với fieldChangesJson nếu có thay đổi - AddCommentCommandHandler — LogCommentAddedAsync - UploadContractAttachmentCommandHandler — LogAttachmentAsync(Insert) - DeleteContractAttachmentCommandHandler — LogAttachmentAsync(Delete) Build: dotnet build BE pass (0 error, 2 pre-existing warning trong DocxRenderer.cs) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 70810e1b34 |
[CLAUDE] Domain+Infra: 7 ContractType-specific Details + ContractChangelog (migration 9)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m37s
User decision: Option B — bảng riêng cho mỗi loại HĐ (chuẩn nhất, schema
chuyên biệt). Plus: ContractChangelog audit log thống nhất Header /
Detail / Workflow / Comment / Attachment.
## 8 entities mới
### Details (7) — Domain/Contracts/Details/
| Bảng | Loại HĐ | Field đặc trưng |
|---|---|---|
| ThauPhuDetails | 1 (Thầu phụ) | HangMuc, KhoiLuong, DonGia, ThoiGianHoanThanh |
| GiaoKhoanDetails | 2 (Giao khoán) | MaCongViec, KhoiLuong, YeuCauKyThuat |
| NhaCungCapDetails | 3 (NCC) | MaSP, ThongSoKyThuat, SoLuong, ThoiGianGiao, XuatXu |
| DichVuDetails | 4 (Dịch vụ) | MaDichVu, ThoiGian, TuNgay/DenNgay |
| MuaBanDetails | 5 (Mua bán) | MaSP, SoLuong, DonGia, ThueVAT (%), XuatXu |
| NguyenTacNccDetails | 6 (Nguyên tắc NCC) | NhomSP, DonGiaToiThieu/ToiDa, DieuKienGiaoHang |
| NguyenTacDvDetails | 7 (Nguyên tắc DV) | LoaiDichVu, DonGiaToiThieu/ToiDa, PhamViDichVu, SLA |
Common base `ContractDetailBase`: ContractId FK + Order + ThanhTien
decimal(18,2) + GhiChu nvarchar(1000) + audit (BaseEntity).
### ContractChangelog (1) — Domain/Contracts/
Unified audit log. Khác ContractApprovals (workflow-only, dùng cho guard
logic) — Changelog là VIEW LAYER cho user đọc lịch sử thao tác:
- EntityType enum: Contract | Detail | Workflow | Comment | Attachment
- Action enum: Insert | Update | Delete | Transition
- PhaseAtChange snapshot
- UserId + UserName denormalize (log readable)
- Summary human-readable + FieldChangesJson [{Field, Old, New}]
- ContextNote (comment kèm theo)
## EF Configurations
ContractDetailsConfiguration.cs (1 file gộp 7 IEntityTypeConfiguration):
- ToTable + HasMaxLength + HasPrecision per type
- HasOne(Contract).WithMany(<TypeDetails>) cascade delete
- IX (ContractId, Order) cho load timeline
ContractChangelogConfiguration.cs:
- Cascade delete khi Contract xóa
- IX (ContractId, CreatedAt) timeline + IX (ContractId, EntityType) filter
## DbContext + IApplicationDbContext
+ 8 DbSet mới (7 Details + ContractChangelogs).
## Migration 9: AddContractDetailsAndChangelog
3-file rule (gotcha #17): .cs + .Designer.cs + ApplicationDbContextModel
Snapshot.cs đầy đủ. Applied LocalDB SolutionErp_Dev OK — 24 + 8 = 32 bảng
total.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|