[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>
This commit is contained in:
@ -28,12 +28,14 @@ public record AwLevelDto(
|
|||||||
string? ApproverUserName,
|
string? ApproverUserName,
|
||||||
string? ApproverEmail,
|
string? ApproverEmail,
|
||||||
// Mig 29 (S21 t5) — 5 advanced options per slot Approver (F1 mode Trả lại
|
// Mig 29 (S21 t5) — 5 advanced options per slot Approver (F1 mode Trả lại
|
||||||
// + F3 Edit Section 2). Mỗi NV trong workflow có quyền riêng.
|
// + F3 Edit Section 2). Mig 30 (S22+5) — F4 +AllowApproverEditBudget.
|
||||||
|
// Mỗi NV trong workflow có quyền riêng.
|
||||||
bool AllowReturnOneLevel,
|
bool AllowReturnOneLevel,
|
||||||
bool AllowReturnOneStep,
|
bool AllowReturnOneStep,
|
||||||
bool AllowReturnToAssignee,
|
bool AllowReturnToAssignee,
|
||||||
bool AllowReturnToDrafter,
|
bool AllowReturnToDrafter,
|
||||||
bool AllowApproverEditDetails);
|
bool AllowApproverEditDetails,
|
||||||
|
bool AllowApproverEditBudget);
|
||||||
|
|
||||||
public record AwStepDto(
|
public record AwStepDto(
|
||||||
Guid Id,
|
Guid Id,
|
||||||
@ -152,7 +154,7 @@ public class GetAwAdminOverviewQueryHandler(
|
|||||||
// Mig 29 (S21 t5) — 5 Allow* flag per slot Level
|
// Mig 29 (S21 t5) — 5 Allow* flag per slot Level
|
||||||
return new AwLevelDto(l.Id, l.Order, l.Name, l.ApproverUserId, info.FullName, info.Email,
|
return new AwLevelDto(l.Id, l.Order, l.Name, l.ApproverUserId, info.FullName, info.Email,
|
||||||
l.AllowReturnOneLevel, l.AllowReturnOneStep, l.AllowReturnToAssignee,
|
l.AllowReturnOneLevel, l.AllowReturnOneStep, l.AllowReturnToAssignee,
|
||||||
l.AllowReturnToDrafter, l.AllowApproverEditDetails);
|
l.AllowReturnToDrafter, l.AllowApproverEditDetails, l.AllowApproverEditBudget);
|
||||||
}).ToList()
|
}).ToList()
|
||||||
)).ToList());
|
)).ToList());
|
||||||
|
|
||||||
@ -184,12 +186,13 @@ public record CreateAwLevelInput(
|
|||||||
Guid ApproverUserId,
|
Guid ApproverUserId,
|
||||||
// Mig 29 (S21 t5) — 5 Allow* options per slot. Admin Designer tick per
|
// Mig 29 (S21 t5) — 5 Allow* options per slot. Admin Designer tick per
|
||||||
// Level row. Default backward compat: AllowReturnToDrafter=true, 4 còn lại
|
// Level row. Default backward compat: AllowReturnToDrafter=true, 4 còn lại
|
||||||
// false (admin opt-in từng slot).
|
// false (admin opt-in từng slot). Mig 30 (S22+5) — F4 AllowApproverEditBudget.
|
||||||
bool AllowReturnOneLevel = false,
|
bool AllowReturnOneLevel = false,
|
||||||
bool AllowReturnOneStep = false,
|
bool AllowReturnOneStep = false,
|
||||||
bool AllowReturnToAssignee = false,
|
bool AllowReturnToAssignee = false,
|
||||||
bool AllowReturnToDrafter = true,
|
bool AllowReturnToDrafter = true,
|
||||||
bool AllowApproverEditDetails = false);
|
bool AllowApproverEditDetails = false,
|
||||||
|
bool AllowApproverEditBudget = false);
|
||||||
|
|
||||||
public record CreateAwStepInput(
|
public record CreateAwStepInput(
|
||||||
int Order,
|
int Order,
|
||||||
@ -315,6 +318,7 @@ public class CreateAwDefinitionCommandHandler(IApplicationDbContext db)
|
|||||||
AllowReturnToAssignee = l.AllowReturnToAssignee,
|
AllowReturnToAssignee = l.AllowReturnToAssignee,
|
||||||
AllowReturnToDrafter = l.AllowReturnToDrafter,
|
AllowReturnToDrafter = l.AllowReturnToDrafter,
|
||||||
AllowApproverEditDetails = l.AllowApproverEditDetails,
|
AllowApproverEditDetails = l.AllowApproverEditDetails,
|
||||||
|
AllowApproverEditBudget = l.AllowApproverEditBudget,
|
||||||
}).ToList(),
|
}).ToList(),
|
||||||
})
|
})
|
||||||
.ToList(),
|
.ToList(),
|
||||||
|
|||||||
@ -82,12 +82,14 @@ public record PurchaseEvaluationChangelogDto(
|
|||||||
// FE eOffice filter Trả lại dropdown + Edit Section 2 enabled theo flag của
|
// FE eOffice filter Trả lại dropdown + Edit Section 2 enabled theo flag của
|
||||||
// Cấp hiện tại NV đang duyệt. Null nếu phiếu V1 legacy hoặc không ChoDuyet.
|
// Cấp hiện tại NV đang duyệt. Null nếu phiếu V1 legacy hoặc không ChoDuyet.
|
||||||
// F2 (Drafter skip) đã move sang `PeDetailBundleDto.DrafterAllowSkipToFinal`.
|
// F2 (Drafter skip) đã move sang `PeDetailBundleDto.DrafterAllowSkipToFinal`.
|
||||||
|
// Mig 30 (S22+5) — F4 +AllowApproverEditBudget cho Section "Điều chỉnh ngân sách".
|
||||||
public record ApprovalWorkflowOptionsDto(
|
public record ApprovalWorkflowOptionsDto(
|
||||||
bool AllowReturnOneLevel,
|
bool AllowReturnOneLevel,
|
||||||
bool AllowReturnOneStep,
|
bool AllowReturnOneStep,
|
||||||
bool AllowReturnToAssignee,
|
bool AllowReturnToAssignee,
|
||||||
bool AllowReturnToDrafter,
|
bool AllowReturnToDrafter,
|
||||||
bool AllowApproverEditDetails);
|
bool AllowApproverEditDetails,
|
||||||
|
bool AllowApproverEditBudget);
|
||||||
|
|
||||||
public record PurchaseEvaluationWorkflowSummaryDto(
|
public record PurchaseEvaluationWorkflowSummaryDto(
|
||||||
string PolicyName,
|
string PolicyName,
|
||||||
|
|||||||
@ -290,7 +290,9 @@ public class AdjustPurchaseEvaluationBudgetCommandHandler(
|
|||||||
}
|
}
|
||||||
else if (entity.Phase == PurchaseEvaluationPhase.ChoDuyet)
|
else if (entity.Phase == PurchaseEvaluationPhase.ChoDuyet)
|
||||||
{
|
{
|
||||||
// Approver scope — actor phải là ApproverUserId của currentLevel
|
// F4 (Mig 30 — S22+5) — Approver scope chỉ accept khi
|
||||||
|
// currentLevel.AllowApproverEditBudget=true (admin Designer tick
|
||||||
|
// per slot) AND actor match ApproverUserId.
|
||||||
if (entity.ApprovalWorkflowId is not Guid awId)
|
if (entity.ApprovalWorkflowId is not Guid awId)
|
||||||
throw new ConflictException("Phiếu V1 legacy không hỗ trợ điều chỉnh ngân sách lúc đang duyệt.");
|
throw new ConflictException("Phiếu V1 legacy không hỗ trợ điều chỉnh ngân sách lúc đang duyệt.");
|
||||||
if (entity.CurrentWorkflowStepIndex is not int csi || entity.CurrentApprovalLevelOrder is not int curLvl)
|
if (entity.CurrentWorkflowStepIndex is not int csi || entity.CurrentApprovalLevelOrder is not int curLvl)
|
||||||
@ -307,7 +309,14 @@ public class AdjustPurchaseEvaluationBudgetCommandHandler(
|
|||||||
throw new ConflictException("Pointer step out of range — schema lỗi.");
|
throw new ConflictException("Pointer step out of range — schema lỗi.");
|
||||||
var step = stepsOrdered[csi];
|
var step = stepsOrdered[csi];
|
||||||
var level = step.Levels.FirstOrDefault(l => l.Order == curLvl);
|
var level = step.Levels.FirstOrDefault(l => l.Order == curLvl);
|
||||||
if (level?.ApproverUserId != actorId)
|
if (level is null)
|
||||||
|
throw new ConflictException("Cấp duyệt không tìm thấy — schema lỗi.");
|
||||||
|
if (!level.AllowApproverEditBudget)
|
||||||
|
throw new ConflictException(
|
||||||
|
$"Cấp Approver hiện tại (Bước {step.Order} / Cấp {curLvl}) " +
|
||||||
|
"không được cấp quyền chỉnh sửa Section ngân sách. " +
|
||||||
|
"Liên hệ Admin Designer cấp quyền slot.");
|
||||||
|
if (level.ApproverUserId != actorId)
|
||||||
throw new ForbiddenException(
|
throw new ForbiddenException(
|
||||||
$"Chỉ NV phụ trách Bước {step.Order} / Cấp {curLvl} mới được điều chỉnh ngân sách lúc đang duyệt.");
|
$"Chỉ NV phụ trách Bước {step.Order} / Cấp {curLvl} mới được điều chỉnh ngân sách lúc đang duyệt.");
|
||||||
actorTag = $"[Approver Bước {step.Order}/Cấp {curLvl}]";
|
actorTag = $"[Approver Bước {step.Order}/Cấp {curLvl}]";
|
||||||
@ -761,7 +770,8 @@ public class GetPurchaseEvaluationQueryHandler(
|
|||||||
curLevel.AllowReturnOneStep,
|
curLevel.AllowReturnOneStep,
|
||||||
curLevel.AllowReturnToAssignee,
|
curLevel.AllowReturnToAssignee,
|
||||||
curLevel.AllowReturnToDrafter,
|
curLevel.AllowReturnToDrafter,
|
||||||
curLevel.AllowApproverEditDetails);
|
curLevel.AllowApproverEditDetails,
|
||||||
|
curLevel.AllowApproverEditBudget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -98,5 +98,11 @@ public class ApprovalWorkflowLevel : BaseEntity
|
|||||||
/// duyệt. KHÔNG đụng PE Header, KHÔNG reset workflow.
|
/// duyệt. KHÔNG đụng PE Header, KHÔNG reset workflow.
|
||||||
public bool AllowApproverEditDetails { get; set; }
|
public bool AllowApproverEditDetails { get; set; }
|
||||||
|
|
||||||
|
/// F4 (Mig 30 — S22+5) — Cho phép NV này edit Section "Điều chỉnh ngân sách"
|
||||||
|
/// (BudgetId / BudgetManualName / BudgetManualAmount) lúc đang duyệt. Default
|
||||||
|
/// false (admin opt-in per slot). Logic edit ngân sách giữ nguyên Drafter
|
||||||
|
/// Nháp/Trả lại — flag này CHỈ mở thêm scope cho Approver ChoDuyet.
|
||||||
|
public bool AllowApproverEditBudget { get; set; }
|
||||||
|
|
||||||
public ApprovalWorkflowStep? Step { get; set; }
|
public ApprovalWorkflowStep? Step { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -77,5 +77,9 @@ public class ApprovalWorkflowLevelConfiguration : IEntityTypeConfiguration<Appro
|
|||||||
e.Property(x => x.AllowReturnToAssignee).HasDefaultValue(false);
|
e.Property(x => x.AllowReturnToAssignee).HasDefaultValue(false);
|
||||||
e.Property(x => x.AllowReturnToDrafter).HasDefaultValue(true);
|
e.Property(x => x.AllowReturnToDrafter).HasDefaultValue(true);
|
||||||
e.Property(x => x.AllowApproverEditDetails).HasDefaultValue(false);
|
e.Property(x => x.AllowApproverEditDetails).HasDefaultValue(false);
|
||||||
|
|
||||||
|
// Mig 30 (S22+5) — F4 per-NV: cho phép edit Section "Điều chỉnh ngân sách"
|
||||||
|
// lúc đang duyệt. Default false (admin opt-in).
|
||||||
|
e.Property(x => x.AllowApproverEditBudget).HasDefaultValue(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddAllowApproverEditBudgetToLevels : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "AllowApproverEditBudget",
|
||||||
|
table: "ApprovalWorkflowLevels",
|
||||||
|
type: "bit",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "AllowApproverEditBudget",
|
||||||
|
table: "ApprovalWorkflowLevels");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -188,6 +188,11 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
|||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowApproverEditBudget")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(false);
|
||||||
|
|
||||||
b.Property<bool>("AllowApproverEditDetails")
|
b.Property<bool>("AllowApproverEditDetails")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("bit")
|
.HasColumnType("bit")
|
||||||
|
|||||||
Reference in New Issue
Block a user