[CLAUDE] Contract: K7 SP-002 bridge KHKK->HD hoi-2-khoa + Line.ContractId + grant seeder (gate PASS-WITH-FLAGS 10, F-8 fixed)
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
BE: POST {id}/create-contract (Contracts.Create AND KeHoachKyKet.Read) + guard 4-ve IsActive/IsUserSelectable
+ Line.ContractId write-site DAU TIEN + list-DTO ContractIds + SeedContractCreateAccessAsync (Drafter/
Procurement/Admin upgrade-only, cau III default cho ratify). FE x2 app SHA-pair 3/3: nut bridge gate 2-khoa
+ dialog khuon-san + cay GD3/GD4 doc TAP contractIds (union pe.contractId legacy). Test +17 (12 bridge T1-T12
+ 5 authz) suite 639/0; ngoai-le CreateContract trong AuthorizePolicyRegressionTests (#85).
Gate F-8 fixed truoc commit: neo so-dong :2516 chet-luc-land -> neo ten ham (3 cho) + 13/13->12/13 theo do
prod + khai chu-dich CanRead + sua tro-nham-ham. No mang theo (0-blocker): MAJOR-race-2-request-dong-thoi
(chong-trung in-memory, phuong an unique/RowVersion = nhip sau) + DraftData-khong-carry + a11y/O(n.m) FE.
UAT-NOI-TRUOC: nut sang sau restart + user RE-LOGIN (menu cache); acceptance seeder log = 0 added + 2 upgraded.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@ -5,6 +5,7 @@ using SolutionErp.Application.Common.Models;
|
||||
using SolutionErp.Application.ContractSigningPlans;
|
||||
using SolutionErp.Application.ContractSigningPlans.Services;
|
||||
using SolutionErp.Domain.ContractSigningPlans;
|
||||
using SolutionErp.Domain.Contracts; // ContractType (cầu GĐ2 → GĐ3, K7)
|
||||
|
||||
namespace SolutionErp.Api.Controllers;
|
||||
|
||||
@ -213,6 +214,30 @@ public class ContractSigningPlansController(
|
||||
=> Ok(await workflow.TransitionAsync(id, body.Action, body.Comment,
|
||||
body.ApplyLevelFinalize ?? true, ct));
|
||||
|
||||
// ===================== Cầu GĐ2 → GĐ3 (K7 — S167) =====================
|
||||
// 🔴 [vá-1 spec-cụm-3 §②-1] AUTHZ = HỘI 2 KHOÁ, cả 2 vế đều phải chặn được:
|
||||
// vế 1 (class, dòng :28) `KeHoachKyKet.Read` — không được xem kế hoạch thì không được
|
||||
// lấy dòng của nó đi tạo HĐ;
|
||||
// vế 2 (action, ngay dưới) `Contracts.Create` — tài nguyên SINH RA là Contract ⇒ khoá phải
|
||||
// là khoá của thứ được tạo (gotcha #85:
|
||||
// gate bằng đúng key policy của endpoint).
|
||||
// ASP.NET Core cộng dồn [Authorize] class + action theo AND ⇒ thiếu 1 trong 2 = 403.
|
||||
// ⚠️ `Contracts.Create` trước S167 KHÔNG role nào có (seeder `SeedAllRolesReviewReadPermissionsAsync`
|
||||
// đặt `CanCreate = isPe` ⇒ false cho key `Contracts` — grep tên hàm, ĐỪNG neo số dòng: file này
|
||||
// dài ra là số thối [gate-K7 F-8 bắt neo cũ sai ngay lúc land]); `SeedContractCreateAccessAsync` cấp cho
|
||||
// Drafter + Procurement + Admin — grant đó ăn @restart, KHÔNG phải deploy FE.
|
||||
// ⚠️ KHÔNG động tới `POST /api/contracts` (`ContractsController` `[Authorize]` trần, #82 treo
|
||||
// chủ đích): siết ở đây là siết CỬA MỚI, không phải sửa cửa cũ.
|
||||
[HttpPost("{id:guid}/create-contract")]
|
||||
[Authorize(Policy = "Contracts.Create")]
|
||||
public async Task<ActionResult<CreateContractFromSigningPlanResult>> CreateContract(
|
||||
Guid id, [FromBody] CreateContractFromSigningPlanBody body, CancellationToken ct)
|
||||
{
|
||||
var result = await mediator.Send(new CreateContractFromSigningPlanCommand(
|
||||
id, body.LineIds ?? [], body.ContractType, body.ApprovalWorkflowId, body.TenHopDong), ct);
|
||||
return Created($"/api/contracts/{result.ContractId}", result);
|
||||
}
|
||||
|
||||
// ========================= Body records =========================
|
||||
// PUT header: KHÔNG mang `Id` trong body (id lấy từ route) — tránh 2 nguồn sự thật.
|
||||
// Mọi field nullable = null-safe: client không gửi ⇒ giữ giá trị cũ (#73).
|
||||
@ -231,4 +256,13 @@ public class ContractSigningPlansController(
|
||||
string Action,
|
||||
string? Comment = null,
|
||||
bool? ApplyLevelFinalize = null);
|
||||
|
||||
// [K7 S167] Cầu → HĐ. `PlanId` lấy từ route (không nhận trong body — 1 nguồn sự thật).
|
||||
// `LineIds` nullable ở BODY để client gửi thiếu ra 400 của validator ("chọn ít nhất 1 dòng")
|
||||
// thay vì 500 NullReference ở tầng dưới.
|
||||
public record CreateContractFromSigningPlanBody(
|
||||
List<Guid>? LineIds,
|
||||
ContractType ContractType,
|
||||
Guid ApprovalWorkflowId,
|
||||
string? TenHopDong = null);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user