[CLAUDE] App+Api: 7 Details CRUD endpoints + Changelogs query
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m37s

## DTOs (Application/Contracts/Dtos/ContractDetailDtos.cs)

- 7 typed DTOs (ThauPhuDetailDto, GiaoKhoanDetailDto, NhaCungCapDetailDto,
  DichVuDetailDto, MuaBanDetailDto, NguyenTacNccDetailDto,
  NguyenTacDvDetailDto) — schema 1-1 với Domain entities
- ContractDetailsBundleDto — wrapper trả về theo Type, chỉ 1 list có data
  (FE đọc field tương ứng, tránh polymorphic deserialize phức tạp)
- ContractChangelogDto — full audit entry

## CQRS (Application/Contracts/ContractDetailsFeatures.cs)

- GetContractDetailsQuery — load bundle, switch theo Contract.Type chỉ
  query bảng tương ứng (avoid 7 query waste)
- 7 AddXxxDetailCommand handlers — typed payload + EnsureContractType
  guard (throw ConflictException nếu Contract.Type sai)
- DeleteContractDetailCommand generic — dispatch xóa theo Type, log change
- Tất cả handler call IChangelogService.LogDetailChangeAsync với summary
  human-readable (vd "Thêm hạng mục: Đào móng")

## CQRS (Application/Contracts/ContractChangelogFeatures.cs)

- ListContractChangelogsQuery (read-only) — desc CreatedAt, default top 200

## Controller (Api/Controllers/ContractsController.cs)

8 endpoints mới:
- GET /api/contracts/{id}/details → bundle theo Type
- POST /api/contracts/{id}/details/{thau-phu|giao-khoan|nha-cung-cap|
  dich-vu|mua-ban|nguyen-tac-ncc|nguyen-tac-dv} → 7 typed POST
- DELETE /api/contracts/{id}/details/{detailId} → generic delete
- GET /api/contracts/{id}/changelogs → list audit entries

Build: dotnet pass (0 error)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-04-23 10:16:18 +07:00
parent 71c035d31e
commit e6844553a4
4 changed files with 485 additions and 0 deletions

View File

@ -0,0 +1,47 @@
using SolutionErp.Domain.Contracts;
namespace SolutionErp.Application.Contracts.Dtos;
// 7 typed DTOs cho 7 ContractType-specific Details bảng. Map 1-1 với entity
// trong Domain/Contracts/Details/. Generic ContractDetailItem dùng cho
// list-mixed view (vd Lịch sử changelog reference).
public record ThauPhuDetailDto(Guid Id, int Order, string HangMuc, string DonViTinh, decimal KhoiLuong, decimal DonGia, decimal ThanhTien, DateTime? ThoiGianHoanThanh, string? GhiChu);
public record GiaoKhoanDetailDto(Guid Id, int Order, string MaCongViec, string TenCongViec, string DonViTinh, decimal KhoiLuong, decimal DonGia, decimal ThanhTien, DateTime? ThoiGianHoanThanh, string? YeuCauKyThuat, string? GhiChu);
public record NhaCungCapDetailDto(Guid Id, int Order, string MaSP, string TenSP, string? ThongSoKyThuat, string DonViTinh, decimal SoLuong, decimal DonGia, decimal ThanhTien, DateTime? ThoiGianGiao, string? XuatXu, string? GhiChu);
public record DichVuDetailDto(Guid Id, int Order, string MaDichVu, string TenDichVu, string? MoTa, string DonViTinh, decimal ThoiGian, decimal DonGia, decimal ThanhTien, DateTime? TuNgay, DateTime? DenNgay, string? GhiChu);
public record MuaBanDetailDto(Guid Id, int Order, string MaSP, string TenSP, string? MoTa, string DonViTinh, decimal SoLuong, decimal DonGia, decimal ThueVAT, decimal ThanhTien, string? XuatXu, string? GhiChu);
public record NguyenTacNccDetailDto(Guid Id, int Order, string NhomSP, string TenSP, string DonViTinh, decimal DonGiaToiThieu, decimal DonGiaToiDa, string? DieuKienGiaoHang, string? DieuKienThanhToan, string? GhiChu);
public record NguyenTacDvDetailDto(Guid Id, int Order, string LoaiDichVu, string TenDichVu, string DonViTinh, decimal DonGiaToiThieu, decimal DonGiaToiDa, string? PhamViDichVu, string? SLA, string? GhiChu);
// Wrapper trả về theo Type — FE đọc field tương ứng (chỉ 1 list có data,
// 6 list khác empty). Tránh dùng object/dynamic — TS strict mode.
public record ContractDetailsBundleDto(
ContractType Type,
List<ThauPhuDetailDto> ThauPhu,
List<GiaoKhoanDetailDto> GiaoKhoan,
List<NhaCungCapDetailDto> NhaCungCap,
List<DichVuDetailDto> DichVu,
List<MuaBanDetailDto> MuaBan,
List<NguyenTacNccDetailDto> NguyenTacNcc,
List<NguyenTacDvDetailDto> NguyenTacDv);
// Changelog DTO cho FE Lịch sử tab
public record ContractChangelogDto(
Guid Id,
ChangelogEntityType EntityType,
Guid? EntityId,
ChangelogAction Action,
ContractPhase? PhaseAtChange,
Guid? UserId,
string? UserName,
string? Summary,
string? FieldChangesJson,
string? ContextNote,
DateTime CreatedAt);