[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>
This commit is contained in:
@ -19,14 +19,9 @@ public class ApprovalWorkflowConfiguration : IEntityTypeConfiguration<ApprovalWo
|
||||
e.HasIndex(x => new { x.Code, x.Version }).IsUnique();
|
||||
e.HasIndex(x => new { x.ApplicableType, x.IsActive });
|
||||
|
||||
// Mig 28 — 6 advanced options. 5 default false (admin opt-in). 1
|
||||
// AllowReturnToDrafter default true (backward compat S17 fallback).
|
||||
e.Property(x => x.AllowReturnOneLevel).HasDefaultValue(false);
|
||||
e.Property(x => x.AllowReturnOneStep).HasDefaultValue(false);
|
||||
e.Property(x => x.AllowReturnToAssignee).HasDefaultValue(false);
|
||||
e.Property(x => x.AllowReturnToDrafter).HasDefaultValue(true);
|
||||
e.Property(x => x.AllowDrafterSkipToFinal).HasDefaultValue(false);
|
||||
e.Property(x => x.AllowApproverEditDetails).HasDefaultValue(false);
|
||||
// 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.
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,5 +69,13 @@ public class ApprovalWorkflowLevelConfiguration : IEntityTypeConfiguration<Appro
|
||||
|
||||
e.HasIndex(x => new { x.ApprovalWorkflowStepId, x.Order });
|
||||
e.HasIndex(x => x.ApproverUserId);
|
||||
|
||||
// Mig 29 (S21 t5) — 5 per-NV advanced options. 4 default false (admin
|
||||
// opt-in). 1 AllowReturnToDrafter default true (backward compat S17).
|
||||
e.Property(x => x.AllowReturnOneLevel).HasDefaultValue(false);
|
||||
e.Property(x => x.AllowReturnOneStep).HasDefaultValue(false);
|
||||
e.Property(x => x.AllowReturnToAssignee).HasDefaultValue(false);
|
||||
e.Property(x => x.AllowReturnToDrafter).HasDefaultValue(true);
|
||||
e.Property(x => x.AllowApproverEditDetails).HasDefaultValue(false);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,196 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class RefactorAdvancedOptionsToPerLevelAndDrafterUser : Migration
|
||||
{
|
||||
// Mig 29 (S21 t5) — Refactor Allow* options từ workflow-level (Mig 28
|
||||
// S21 t4) sang per-NV:
|
||||
// - F1 (4 mode Trả lại) + F3 (Edit Section 2) = 5 flag move xuống
|
||||
// ApprovalWorkflowLevels (per slot Approver).
|
||||
// - F2 (AllowDrafterSkipToFinal) move xuống Users (per-Drafter user).
|
||||
//
|
||||
// Migration order: ADD columns mới TRƯỚC → BACKFILL bulk SQL từ workflow
|
||||
// → DROP columns workflow-level. Preserve admin config S21 t4 (Mig 28).
|
||||
//
|
||||
// EF auto-generated order (drop-then-add) đã được reorder manual.
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// ===== Stage 1: ADD 5 column trên ApprovalWorkflowLevels (per slot) =====
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowReturnOneLevel",
|
||||
table: "ApprovalWorkflowLevels",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowReturnOneStep",
|
||||
table: "ApprovalWorkflowLevels",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowReturnToAssignee",
|
||||
table: "ApprovalWorkflowLevels",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowReturnToDrafter",
|
||||
table: "ApprovalWorkflowLevels",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: true);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowApproverEditDetails",
|
||||
table: "ApprovalWorkflowLevels",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
// ===== Stage 2: ADD 1 column trên Users (per-Drafter F2) =====
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowDrafterSkipToFinal",
|
||||
table: "Users",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
// ===== Stage 3: BACKFILL bulk từ workflow-level (Mig 28) =====
|
||||
// Copy 5 F1+F3 flag từ workflow → all Levels của workflow đó.
|
||||
// SQL Server compatible (UPDATE ... FROM ... JOIN ...).
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE l SET
|
||||
l.AllowReturnOneLevel = w.AllowReturnOneLevel,
|
||||
l.AllowReturnOneStep = w.AllowReturnOneStep,
|
||||
l.AllowReturnToAssignee = w.AllowReturnToAssignee,
|
||||
l.AllowReturnToDrafter = w.AllowReturnToDrafter,
|
||||
l.AllowApproverEditDetails = w.AllowApproverEditDetails
|
||||
FROM ApprovalWorkflowLevels l
|
||||
INNER JOIN ApprovalWorkflowSteps s ON s.Id = l.ApprovalWorkflowStepId
|
||||
INNER JOIN ApprovalWorkflows w ON w.Id = s.ApprovalWorkflowId;
|
||||
");
|
||||
|
||||
// Backfill Users.AllowDrafterSkipToFinal: set TRUE cho user nào
|
||||
// từng làm Drafter PE pin workflow có AllowDrafterSkipToFinal=true.
|
||||
// Conservative: preserve admin config Mig 28 cho user thực tế dùng,
|
||||
// các user khác giữ false (admin opt-in lần đầu).
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE u SET u.AllowDrafterSkipToFinal = 1
|
||||
FROM Users u
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
FROM PurchaseEvaluations pe
|
||||
INNER JOIN ApprovalWorkflows w ON w.Id = pe.ApprovalWorkflowId
|
||||
WHERE pe.DrafterUserId = u.Id
|
||||
AND w.AllowDrafterSkipToFinal = 1
|
||||
);
|
||||
");
|
||||
|
||||
// ===== Stage 4: DROP 6 column workflow-level (Mig 28 cleanup) =====
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowApproverEditDetails",
|
||||
table: "ApprovalWorkflows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowDrafterSkipToFinal",
|
||||
table: "ApprovalWorkflows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowReturnOneLevel",
|
||||
table: "ApprovalWorkflows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowReturnOneStep",
|
||||
table: "ApprovalWorkflows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowReturnToAssignee",
|
||||
table: "ApprovalWorkflows");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowReturnToDrafter",
|
||||
table: "ApprovalWorkflows");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// Rollback: re-add 6 column workflow-level + drop 5 Level + 1 User.
|
||||
// No reverse backfill (data loss accepted khi rollback).
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowApproverEditDetails",
|
||||
table: "ApprovalWorkflows",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowDrafterSkipToFinal",
|
||||
table: "ApprovalWorkflows",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowReturnOneLevel",
|
||||
table: "ApprovalWorkflows",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowReturnOneStep",
|
||||
table: "ApprovalWorkflows",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowReturnToAssignee",
|
||||
table: "ApprovalWorkflows",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "AllowReturnToDrafter",
|
||||
table: "ApprovalWorkflows",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: true);
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowDrafterSkipToFinal",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowApproverEditDetails",
|
||||
table: "ApprovalWorkflowLevels");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowReturnOneLevel",
|
||||
table: "ApprovalWorkflowLevels");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowReturnOneStep",
|
||||
table: "ApprovalWorkflowLevels");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowReturnToAssignee",
|
||||
table: "ApprovalWorkflowLevels");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AllowReturnToDrafter",
|
||||
table: "ApprovalWorkflowLevels");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,36 +134,6 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
b.Property<DateTime?>("ActivatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<bool>("AllowApproverEditDetails")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("AllowDrafterSkipToFinal")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("AllowReturnOneLevel")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("AllowReturnOneStep")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("AllowReturnToAssignee")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("AllowReturnToDrafter")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<int>("ApplicableType")
|
||||
.HasColumnType("int");
|
||||
|
||||
@ -218,6 +188,31 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<bool>("AllowApproverEditDetails")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("AllowReturnOneLevel")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("AllowReturnOneStep")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("AllowReturnToAssignee")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("AllowReturnToDrafter")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<Guid>("ApprovalWorkflowStepId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
@ -1945,6 +1940,9 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("AllowDrafterSkipToFinal")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("CanBypassReview")
|
||||
.HasColumnType("bit");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user