[CLAUDE] App+Api+FE: Kế thừa HĐ từ phiếu Duyệt NCC (Phase 4)

BE:
 - CreateContractFromEvaluationCommand: guard DaDuyet + SelectedSupplier
   + ContractId=null → tạo Contract draft mới với SupplierId/ProjectId/
   DepartmentId kế thừa từ PE. GiaTri = sum(details.thanhTienNganSach).
   DraftData = PE.PaymentTerms. Gen MaHopDong ngay + pin WorkflowDefinitionId
   theo ContractType user chọn. Log Changelog cả 2 bảng (Contract +
   PurchaseEvaluation), link 2 chiều PE.ContractId = contract.Id.
 - ListApprovedPurchaseEvaluationsQuery: DaDuyet + ContractId=null cho
   FE picker.
 - 2 endpoint mới:
   GET  /api/purchase-evaluations/approved-pending-contract
   POST /api/purchase-evaluations/{id}/create-contract

FE:
 - PeDetailTabs InfoTab: nếu Phase=DaDuyet && !ContractId && SelectedSupplierId
   → banner emerald + button "Tạo HĐ từ phiếu" → CreateContractDialog
   (pick ContractType dropdown 7 loại + TenHopDong + bypass CCM flag)
 - Sau khi tạo → navigate /contracts/{newId}
 - Mirror fe-user.

KHÔNG auto-map PE Details → Contract Details per-type (PE schema ≠ 7
ContractType details schemas — user điền lại sau). PE → Contract link
qua FK ContractId cho navigation + history.
This commit is contained in:
pqhuy1987
2026-04-23 16:58:41 +07:00
parent a737196b21
commit a385d70c2e
4 changed files with 341 additions and 20 deletions

View File

@ -149,8 +149,29 @@ public class PurchaseEvaluationsController(IMediator mediator) : ControllerBase
[HttpGet("{id:guid}/changelogs")]
public async Task<List<PurchaseEvaluationChangelogDto>> GetChangelogs(Guid id, CancellationToken ct)
=> await mediator.Send(new ListPurchaseEvaluationChangelogsQuery(id), ct);
// ========== Kế thừa HĐ ==========
// List phiếu đã DaDuyet chưa gen HĐ — dùng cho modal "Tạo HĐ từ phiếu"
[HttpGet("approved-pending-contract")]
public async Task<List<PurchaseEvaluationListItemDto>> ListApproved(CancellationToken ct)
=> await mediator.Send(new ListApprovedPurchaseEvaluationsQuery(), ct);
[HttpPost("{id:guid}/create-contract")]
public async Task<ActionResult<object>> CreateContractFromEvaluation(
Guid id, [FromBody] CreateContractFromEvaluationBody body, CancellationToken ct)
{
var contractId = await mediator.Send(new CreateContractFromEvaluationCommand(
id, body.ContractType, body.TenHopDong, body.BypassProcurementAndCCM), ct);
return Ok(new { contractId });
}
}
public record CreateContractFromEvaluationBody(
Domain.Contracts.ContractType ContractType,
string? TenHopDong,
bool BypassProcurementAndCCM = false);
public record TransitionPeBody(PurchaseEvaluationPhase TargetPhase, ApprovalDecision Decision, string? Comment);
public record AddSupplierBody(