[CLAUDE] PE-Workflow: Chunk A — Mig 28 +6 Allow* column ApprovalWorkflow (F1+F2+F3 advanced options)

Domain `ApprovalWorkflow` (Mig 22 — Session 17) thêm 6 boolean cấu hình "Cấu
hình nâng cao" cho admin Designer (F1 trả lại modes + F2 skip cấp cuối + F3
approver edit Section 2):

- AllowReturnOneLevel       (default false) — F1 mode 1 lùi 1 Cấp peer review
- AllowReturnOneStep        (default false) — F1 mode 2 lùi 1 Bước
- AllowReturnToAssignee     (default false) — F1 mode 3 pick runtime từ NV đã duyệt
- AllowReturnToDrafter      (default TRUE)  — F1 mode 4 backward compat S17 fallback
- AllowDrafterSkipToFinal   (default false) — F2 Drafter trình thẳng Cấp cuối
- AllowApproverEditDetails  (default false) — F3 Approver edit HangMuc/NCC/Báo giá

Default backward compat S17: AllowReturnToDrafter=true → mọi workflow cũ chạy
đúng "Trả về Drafter" Phase=TraLai. 5 flag còn lại default false → admin
opt-in per workflow để audit nghiêm.

Mig 28 `AddAdvancedOptionsToApprovalWorkflows`:
- AddColumn × 6 bit NOT NULL DEFAULT 0/1 (3-file rule complete + Designer + Snapshot)
- Apply LocalDB SolutionErp_Dev (runtime) + SolutionErp_Design (ef tooling)

EF config ApprovalWorkflowConfiguration thêm 6 HasDefaultValue match Mig 28
default (backfill rows cũ + ef snapshot consistency).

3 mode Trả lại mới giữ Phase=ChoDuyet, chỉ lùi pointer (peer review chain
sequential). Mode Drafter giữ Phase=TraLai + clear pointer như S17. Behavior
implement trong Chunk B (Service.TransitionAsync extend branches).

Verify:
- dotnet ef migrations add success (no compile error)
- 3-file rule complete: 28 mig × 2 + Snapshot = 57 file Migrations dir
- LocalDB Dev + Design both apply success

Pending Chunk B: BE Service branches + handlers + Controller body extend.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-13 18:46:01 +07:00
parent 6d30ba42d1
commit 0294693a4a
5 changed files with 4096 additions and 0 deletions

View File

@ -34,6 +34,46 @@ public class ApprovalWorkflow : BaseEntity
// khi tạo version mới (mirror IsActive default), admin có thể unstick. // khi tạo version mới (mirror IsActive default), admin có thể unstick.
public bool IsUserSelectable { get; set; } public bool IsUserSelectable { get; set; }
// ===== Mig 28 (Session 21 turn 4) — 6 advanced options per workflow =====
// Cấu hình "Cấu hình nâng cao" trong Admin Designer. User eOffice render
// dropdown/checkbox theo flag enabled. 4 flag Return* = mode Trả lại (F1).
// 1 flag Skip = Drafter trình thẳng Cấp cuối (F2). 1 flag EditDetails =
// Approver chỉnh Section 2 (F3).
//
// Default backward compat S17: AllowReturnToDrafter=true (mọi workflow cũ
// chạy đúng — fallback "Trả về Drafter" như Session 17 spec). 5 flag còn
// lại default false — admin opt-in per workflow để audit nghiêm.
/// F1 mode 1 — Cho phép Approver Trả lại 1 Cấp trước (lùi pointer trong
/// cùng Step). Phiếu GIỮ Phase=ChoDuyet (peer review chain).
public bool AllowReturnOneLevel { get; set; }
/// F1 mode 2 — Cho phép Approver Trả lại 1 Bước trước (lùi sang Step trước,
/// set level = max của step đó). Phiếu GIỮ Phase=ChoDuyet.
public bool AllowReturnOneStep { get; set; }
/// F1 mode 3 — Cho phép Approver Trả lại Người chỉ định (pick runtime từ
/// list NV ĐÃ DUYỆT trong PeLevelOpinions). Phiếu GIỮ Phase=ChoDuyet, set
/// Step/Level = vị trí của user pick trong workflow.
public bool AllowReturnToAssignee { get; set; }
/// F1 mode 4 — Cho phép Approver Trả lại Người soạn thảo (Drafter). Phiếu
/// đi vào Phase=TraLai, clear pointer (như Session 17 spec). Default TRUE
/// để backward compat — admin có thể unstick force peer review only.
public bool AllowReturnToDrafter { get; set; } = true;
/// F2 — Cho phép Drafter gửi thẳng Cấp cuối (skip mọi Bước/Cấp trung gian).
/// UI eOffice trình duyệt thêm dropdown 2 option ("Gửi tuần tự" default vs
/// "Gửi thẳng Cấp cuối"). BE set CurrentWorkflowStepIndex=maxStep,
/// CurrentApprovalLevelOrder=maxLevel. Audit changelog "Drafter skip C1..N".
public bool AllowDrafterSkipToFinal { get; set; }
/// F3 — Cho phép Approver chỉnh sửa Section 2 (Hạng mục + NCC + Báo giá)
/// khi phase=ChoDuyet + actor match CurrentLevel.ApproverUserId. KHÔNG đụng
/// PE Header (TenGoiThau/Project/Budget). KHÔNG reset workflow. Audit ghi
/// PurchaseEvaluationChangelog cho mỗi field/row thay đổi.
public bool AllowApproverEditDetails { get; set; }
public List<ApprovalWorkflowStep> Steps { get; set; } = new(); public List<ApprovalWorkflowStep> Steps { get; set; } = new();
} }

View File

@ -18,6 +18,15 @@ public class ApprovalWorkflowConfiguration : IEntityTypeConfiguration<ApprovalWo
e.HasIndex(x => new { x.Code, x.Version }).IsUnique(); e.HasIndex(x => new { x.Code, x.Version }).IsUnique();
e.HasIndex(x => new { x.ApplicableType, x.IsActive }); 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);
} }
} }

View File

@ -0,0 +1,84 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace SolutionErp.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddAdvancedOptionsToApprovalWorkflows : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
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);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
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");
}
}
}

View File

@ -134,6 +134,36 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
b.Property<DateTime?>("ActivatedAt") b.Property<DateTime?>("ActivatedAt")
.HasColumnType("datetime2"); .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") b.Property<int>("ApplicableType")
.HasColumnType("int"); .HasColumnType("int");