[CLAUDE] Backout: Chunk D — K5 cleanup F2 zombie endpoint + UsersPage column + DTO field
Reviewer K2 Major #1: PATCH /api/users/{id}/allow-skip-final endpoint Admin tick = NoOp swallow silent (K1 sentinel → confusion UX). Full backout Plan D S22 stack: BE drop (7 files): - UsersController.cs: DELETE PATCH /allow-skip-final endpoint + SetAllowDrafterSkipToFinalBody record - UserFeatures.cs: DELETE SetUserAllowDrafterSkipToFinalCommand + Handler + UserDto.AllowDrafterSkipToFinal field + list/get DTO mapping sentinel-false references - ApprovalWorkflow.cs: REWRITE stale narrative line 78-80 (Reviewer Major #2 Mig 31 semantic) + docstring AllowApproverSkipToFinal line 108 clean stale Users storage ref - PurchaseEvaluationFeatures.cs: REWRITE Command DTO comment line 401 (Reviewer Minor #3) - ApprovalWorkflowConfiguration.cs: APPEND Mig 31 narrative line 22-24 (Reviewer Minor #4) + clean storage move comment line 87 - ApprovalWorkflowV2AdminFeatures.cs: clean DTO comment line 58 stale "F2 xuống User table" - IPurchaseEvaluationWorkflowService.cs + PurchaseEvaluationDtos.cs: clean stale "storage Users.AllowDrafterSkipToFinal" comments FE Admin drop (2 files): - UsersPage.tsx: DELETE "Skip cuối" column + FastForward badge + FastForward import + allowSkipMut mutation hook + FastForward toggle button - types/users.ts: DELETE allowDrafterSkipToFinal field fe-user KHÔNG đụng (no UsersPage admin-only; K6 sẽ handle Workspace Drafter checkbox). FE Designer page KHÔNG đụng (K3 done; 2 stale comment leftover deferred K6). Plan K refactor F2 storage Users → Levels (Mig 31) complete cumulative cleanup. Pattern reusable: post-refactor full cleanup (BE endpoint + Command + DTO + FE column + types + stale narratives) atomic 1 commit thay vì leak zombie state. Verify: - dotnet build production projects 0 err (2 pre-existing DocxRenderer warn) - npm build fe-admin 0 TS err (no new warning) - Grep AllowDrafterSkipToFinal + allow-skip-final + allowDrafterSkipToFinal zero results across src/Backend (excl Migrations history) + fe-admin/src Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -85,21 +85,13 @@ public class UsersController(IMediator mediator) : ControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// Mig 29 F2 per-Drafter: admin toggle AllowDrafterSkipToFinal cho user. Khi
|
||||
// true, Drafter có thể tick "Gửi thẳng Cấp cuối" trong PE Workspace để bay
|
||||
// thẳng tới Cấp cuối workflow. Mặc định false.
|
||||
[HttpPatch("{id:guid}/allow-skip-final")]
|
||||
[Authorize(Policy = "Users.Update")]
|
||||
public async Task<IActionResult> SetAllowDrafterSkipToFinal(
|
||||
Guid id, [FromBody] SetAllowDrafterSkipToFinalBody body, CancellationToken ct)
|
||||
{
|
||||
await mediator.Send(new SetUserAllowDrafterSkipToFinalCommand(id, body.AllowDrafterSkipToFinal), ct);
|
||||
return NoContent();
|
||||
}
|
||||
// Mig 31 (S23 t1 Plan K Chunk D) — DELETED PATCH F2 endpoint per-Drafter.
|
||||
// F2 semantic + storage refactor sang ApprovalWorkflowLevels per-Approver
|
||||
// slot (AllowApproverSkipToFinal, admin opt-in qua Workflow Designer).
|
||||
// Plan K backout zombie endpoint NoOp.
|
||||
}
|
||||
|
||||
public record AssignRolesBody(List<string> Roles);
|
||||
public record ResetPasswordBody(string NewPassword);
|
||||
public record SetBypassReviewBody(bool CanBypassReview);
|
||||
public record SetPositionLevelBody(int? PositionLevel);
|
||||
public record SetAllowDrafterSkipToFinalBody(bool AllowDrafterSkipToFinal);
|
||||
|
||||
@ -55,9 +55,10 @@ public record AwDefinitionDto(
|
||||
string? Description,
|
||||
bool IsActive,
|
||||
bool IsUserSelectable,
|
||||
// Mig 29 (S21 t5) — 6 advanced options đã MOVE per-NV: 5 flag (F1+F3) xuống
|
||||
// AwLevelDto (per slot Approver), F2 AllowDrafterSkipToFinal xuống User table
|
||||
// (per-Drafter user). Workflow-level Mig 28 dropped.
|
||||
// Mig 29 (S21 t5) — 6 advanced options đã MOVE per-NV: F1+F3 xuống AwLevelDto
|
||||
// (per slot Approver). Workflow-level Mig 28 dropped.
|
||||
// Mig 31 (S23 t1) — F2 cũng refactor xuống Level slot (AllowApproverSkipToFinal
|
||||
// trong AwLevelDto) per-Approver scope ChoDuyet — KHÔNG còn per-Drafter user scope.
|
||||
DateTime? ActivatedAt,
|
||||
DateTime CreatedAt,
|
||||
List<AwStepDto> Steps);
|
||||
|
||||
@ -84,7 +84,7 @@ public record PurchaseEvaluationChangelogDto(
|
||||
// Mig 30 (S22+5) — F4 +AllowApproverEditBudget cho Section "Điều chỉnh ngân sách".
|
||||
// Mig 31 (S23 t1) — F2 refactor sang Approver scope ChoDuyet: +AllowApproverSkipToFinal
|
||||
// cho phép Approver duyệt thẳng Cấp cuối (admin opt-in per slot). Storage cũ
|
||||
// Users.AllowDrafterSkipToFinal đã drop, semantic Drafter-from-Nháp deprecated.
|
||||
// trên Users table đã drop, semantic Drafter-from-Nháp deprecated.
|
||||
public record ApprovalWorkflowOptionsDto(
|
||||
bool AllowReturnOneLevel,
|
||||
bool AllowReturnOneStep,
|
||||
|
||||
@ -398,7 +398,8 @@ public record TransitionPurchaseEvaluationCommand(
|
||||
// Mig 28 (S21 t4) — F1 mode Trả lại (optional, null = default Drafter)
|
||||
WorkflowReturnMode? ReturnMode = null,
|
||||
Guid? ReturnTargetUserId = null,
|
||||
// F2 — Drafter skip thẳng Cấp cuối khi trình duyệt (optional, default false)
|
||||
// F2 — Approver skip thẳng Cấp cuối lúc duyệt ChoDuyet (Mig 31 admin opt-in
|
||||
// per slot, AllowApproverSkipToFinal). Default false.
|
||||
bool SkipToFinal = false) : IRequest;
|
||||
|
||||
public class TransitionPurchaseEvaluationCommandValidator : AbstractValidator<TransitionPurchaseEvaluationCommand>
|
||||
|
||||
@ -16,7 +16,7 @@ public interface IPurchaseEvaluationWorkflowService
|
||||
// - skipToFinal: F2 Approver during ChoDuyet duyệt thẳng Cấp cuối → set Phase=DaDuyet
|
||||
// terminal trực tiếp, clear pointer. Mig 31 (S23 t1) refactor sang Approver scope:
|
||||
// matchingLevel.AllowApproverSkipToFinal phải true (admin opt-in per slot).
|
||||
// Semantic cũ Drafter-from-Nháp đã deprecated, storage Users.AllowDrafterSkipToFinal dropped.
|
||||
// Semantic cũ Drafter-from-Nháp đã deprecated + storage cũ trên Users table đã drop.
|
||||
Task TransitionAsync(
|
||||
PurchaseEvaluation evaluation,
|
||||
PurchaseEvaluationPhase targetPhase,
|
||||
|
||||
@ -22,8 +22,7 @@ public record UserDto(
|
||||
string? DepartmentName,
|
||||
string? Position,
|
||||
bool CanBypassReview,
|
||||
int? PositionLevel, // Mig 18 — 1=NV, 2=PP, 3=TP. Null cho admin/system/external user.
|
||||
bool AllowDrafterSkipToFinal); // Mig 29 — F2 per-Drafter: cho phép Drafter gửi thẳng Cấp cuối khi tạo PE.
|
||||
int? PositionLevel); // Mig 18 — 1=NV, 2=PP, 3=TP. Null cho admin/system/external user.
|
||||
|
||||
// ========== LIST ==========
|
||||
public record ListUsersQuery : PagedRequest, IRequest<PagedResult<UserDto>>;
|
||||
@ -62,9 +61,7 @@ public class ListUsersQueryHandler(UserManager<User> userManager, IApplicationDb
|
||||
var roles = await userManager.GetRolesAsync(u);
|
||||
var isLocked = u.LockoutEnd.HasValue && u.LockoutEnd.Value.UtcDateTime > now;
|
||||
string? deptName = u.DepartmentId is { } did && deptNames.TryGetValue(did, out var dn) ? dn : null;
|
||||
// Mig 31 (S23 t1 K1) — F2 storage moved to ApprovalWorkflowLevels.
|
||||
// DTO field kept transiently (K2 sẽ refactor DTO + drop field). Sentinel false.
|
||||
items.Add(new UserDto(u.Id, u.Email!, u.FullName, u.IsActive, isLocked, u.CreatedAt, roles.ToList(), u.DepartmentId, deptName, u.Position, u.CanBypassReview, (int?)u.PositionLevel, false));
|
||||
items.Add(new UserDto(u.Id, u.Email!, u.FullName, u.IsActive, isLocked, u.CreatedAt, roles.ToList(), u.DepartmentId, deptName, u.Position, u.CanBypassReview, (int?)u.PositionLevel));
|
||||
}
|
||||
|
||||
return new PagedResult<UserDto>(items, total, request.Page, request.PageSize);
|
||||
@ -86,9 +83,7 @@ public class GetUserQueryHandler(UserManager<User> userManager, IApplicationDbCo
|
||||
string? deptName = null;
|
||||
if (u.DepartmentId is { } did)
|
||||
deptName = await db.Departments.AsNoTracking().Where(d => d.Id == did).Select(d => d.Name).FirstOrDefaultAsync(ct);
|
||||
// Mig 31 (S23 t1 K1) — F2 storage moved to ApprovalWorkflowLevels.
|
||||
// DTO field kept transiently (K2 sẽ refactor DTO + drop field). Sentinel false.
|
||||
return new UserDto(u.Id, u.Email!, u.FullName, u.IsActive, isLocked, u.CreatedAt, roles.ToList(), u.DepartmentId, deptName, u.Position, u.CanBypassReview, (int?)u.PositionLevel, false);
|
||||
return new UserDto(u.Id, u.Email!, u.FullName, u.IsActive, isLocked, u.CreatedAt, roles.ToList(), u.DepartmentId, deptName, u.Position, u.CanBypassReview, (int?)u.PositionLevel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,23 +323,7 @@ public class SetUserPositionLevelCommandHandler(UserManager<User> userManager)
|
||||
}
|
||||
}
|
||||
|
||||
// ========== SET ALLOW DRAFTER SKIP TO FINAL (Mig 29 — F2 per-Drafter) ==========
|
||||
// Admin toggle AllowDrafterSkipToFinal cho 1 user. Khi true, user (Drafter) được
|
||||
// dùng checkbox "Gửi thẳng Cấp cuối" trong PE Workspace để skip toàn bộ Bước/Cấp
|
||||
// trung gian và bay thẳng tới Cấp cuối. Mặc định false (an toàn — Drafter phải
|
||||
// tuần tự qua mọi Bước/Cấp theo workflow).
|
||||
public record SetUserAllowDrafterSkipToFinalCommand(Guid Id, bool AllowDrafterSkipToFinal) : IRequest;
|
||||
|
||||
public class SetUserAllowDrafterSkipToFinalCommandHandler(UserManager<User> userManager)
|
||||
: IRequestHandler<SetUserAllowDrafterSkipToFinalCommand>
|
||||
{
|
||||
public async Task Handle(SetUserAllowDrafterSkipToFinalCommand request, CancellationToken ct)
|
||||
{
|
||||
// Mig 31 (S23 t1 K1) — F2 storage moved to ApprovalWorkflowLevels.AllowApproverSkipToFinal
|
||||
// (per-Approver slot, set via Workflow Designer). Command kept transiently NoOp until K2
|
||||
// sẽ replace với SetApprovalWorkflowLevelAllowApproverSkipToFinalCommand + FE remove toggle.
|
||||
_ = await userManager.FindByIdAsync(request.Id.ToString())
|
||||
?? throw new NotFoundException("User", request.Id);
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
// Mig 31 (S23 t1 Plan K Chunk D) — DELETED F2 Command + Handler per-Drafter.
|
||||
// F2 semantic + storage refactor sang ApprovalWorkflowLevels (per-Approver
|
||||
// slot, admin opt-in qua Workflow Designer). Plan K backout zombie Command.
|
||||
// Replaced bởi Workflow Designer per-Level toggle trong ApprovalWorkflowV2AdminFeatures.
|
||||
|
||||
@ -75,8 +75,9 @@ public class ApprovalWorkflowLevel : BaseEntity
|
||||
// tick stick per Level row (KHÔNG còn workflow-level cũ Mig 28).
|
||||
//
|
||||
// F1 (4 mode Trả lại) + F3 (Edit Section 2) = quyền của Approver Level.
|
||||
// F2 (Drafter skip) đã move sang Users.AllowDrafterSkipToFinal (per-Drafter
|
||||
// user — không liên quan slot Approver).
|
||||
// F2 đã move xuống Level slot này (Mig 31 — xem prop AllowApproverSkipToFinal).
|
||||
// Semantic mới: Approver during ChoDuyet skip thẳng Cấp cuối (admin opt-in
|
||||
// per slot, KHÔNG còn per-Drafter user scope cũ).
|
||||
//
|
||||
// Backfill Mig 29: copy từ workflow-level Allow* cũ → all Levels của workflow.
|
||||
// Default backward compat: AllowReturnToDrafter=true (S17 fallback). 4 flag
|
||||
@ -104,12 +105,12 @@ public class ApprovalWorkflowLevel : BaseEntity
|
||||
/// Nháp/Trả lại — flag này CHỈ mở thêm scope cho Approver ChoDuyet.
|
||||
public bool AllowApproverEditBudget { get; set; }
|
||||
|
||||
/// F2 (Mig 31 — S23 t1 Plan K) — REFACTOR semantic + storage từ
|
||||
/// `Users.AllowDrafterSkipToFinal`. Cho phép NV slot này (khi đang duyệt
|
||||
/// ChoDuyet) Approve skip thẳng Cấp cuối, bỏ qua mọi Bước/Cấp trung gian
|
||||
/// còn lại. Default false (admin opt-in per slot). Mirror F3+F4 admin opt-in
|
||||
/// per-Approver pattern (Mig 29 + Mig 30) reinforced 3× cumulative.
|
||||
/// NO BACKFILL — 4 prod user lose value cũ per bro Option A.
|
||||
/// F2 (Mig 31 — S23 t1 Plan K) — REFACTOR semantic + storage từ per-Drafter
|
||||
/// (cũ Mig 29 ở Users table, đã DROP) sang per-Approver slot. Cho phép NV slot
|
||||
/// này (khi đang duyệt ChoDuyet) Approve skip thẳng Cấp cuối, bỏ qua mọi
|
||||
/// Bước/Cấp trung gian còn lại. Default false (admin opt-in per slot).
|
||||
/// Mirror F3+F4 admin opt-in per-Approver pattern (Mig 29 + Mig 30)
|
||||
/// reinforced 3× cumulative. NO BACKFILL — 4 prod user lose value cũ per bro Option A.
|
||||
public bool AllowApproverSkipToFinal { get; set; }
|
||||
|
||||
public ApprovalWorkflowStep? Step { get; set; }
|
||||
|
||||
@ -22,6 +22,9 @@ public class ApprovalWorkflowConfiguration : IEntityTypeConfiguration<ApprovalWo
|
||||
// Mig 28 cũ 6 column Allow* đã DROP trong Mig 29 (S21 t5) — refactor sang
|
||||
// per-NV (Level table cho F1+F3, Users table cho F2). Backfill bulk SQL
|
||||
// preserve config admin từ S21 t4 trước khi drop.
|
||||
// + Mig 31 (S23 t1) F2 refactor sang Approver scope per-Level slot —
|
||||
// AllowApproverSkipToFinal trên Level table (admin opt-in per slot,
|
||||
// KHÔNG còn per-Drafter user scope cũ Mig 29).
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,7 +87,7 @@ public class ApprovalWorkflowLevelConfiguration : IEntityTypeConfiguration<Appro
|
||||
|
||||
// Mig 31 (S23 t1 Plan K Chunk A) — F2 per-NV REFACTOR: cho phép Approver
|
||||
// slot này skip thẳng Cấp cuối lúc đang duyệt ChoDuyet. Default false
|
||||
// (admin opt-in). Semantic + storage move từ Users.AllowDrafterSkipToFinal.
|
||||
// (admin opt-in). Semantic + storage cũ ở Users table per-Drafter đã drop.
|
||||
e.Property(x => x.AllowApproverSkipToFinal).HasDefaultValue(false);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user