Theo dõi Chi phí
1. Tổng quan
| Thuộc tính | Giá trị |
|---|---|
| Status | Ổn định (chưa wire vào calculator) |
| Owner | pricing-team |
| Service | CostService (src/services/management/cost.service.ts) |
| Controller / Route | CostController - /costs |
| Depends on | schema Cost (@nx/core) |
Theo dõi chi phí biến thể sản phẩm theo thời gian. Mỗi bản ghi có một phạm vi ngày hiệu lực; chỉ một chi phí mở (hiện tại) mỗi variant. getCurrentCost / getEffectiveCost hardcode principalType: 'ProductVariant'.
Danh mục service và identity card: xem Pricing Service index. Tham chiếu REST: trực tiếp tại
/v1/api/pricing/doc/openapi.json.
2. Mô hình Dữ liệu
2.1. Trạng thái Phạm vi Ngày
| Trạng thái | effectiveFrom | effectiveTo | Mô tả |
|---|---|---|---|
| Hiện tại | set | null | Chi phí đang hoạt động - không giới hạn ngày kết thúc |
| Lịch sử | set | set | Chi phí quá khứ - phạm vi đóng |
| Đã lên lịch | ngày tương lai | set hoặc null | Thay đổi chi phí trong tương lai |
3. Thao tác Chi phí
3.1. Tạo Chi phí
Kiểm tra không có chi phí hiện có nào trùng lặp với phạm vi ngày mới. Trả về 409 Conflict nếu phát hiện trùng lặp.
POST /costs
Content-Type: application/json
Authorization: Bearer <token>
{
"productVariantId": "variant-123",
"amount": "50000",
"effectiveFrom": "2026-01-01T00:00:00Z",
"note": "New supplier pricing"
}3.2. Cập nhật Chi phí Hiện tại
Thay thế chi phí đang hoạt động hiện tại bằng cách đóng chi phí cũ và tạo mục mới:
PUT /costs/current
Content-Type: application/json
Authorization: Bearer <token>
{
"productVariantId": "variant-123",
"amount": "55000",
"effectiveFrom": "2026-03-01T00:00:00Z",
"note": "Supplier price increase"
}3.3. Lấy Chi phí Hiện tại
GET /costs/product-variant/variant-123/current
Authorization: Bearer <token>Trả về chi phí có effectiveTo = null, hoặc null nếu không có chi phí đang hoạt động.
4. Kiểm tra Trùng lặp Ngày
Trước khi tạo hoặc cập nhật chi phí, hệ thống kiểm tra trùng lặp phạm vi ngày:
- Phạm vi có giới hạn: Kiểm tra nếu bất kỳ phạm vi ngày chi phí hiện có nào giao với phạm vi mới
- Phạm vi mở: Kiểm tra nếu đã tồn tại chi phí mở khác
- Loại trừ khi cập nhật: Loại trừ bản ghi hiện tại khi kiểm tra cho thao tác cập nhật
Phát hiện trùng lặp -> lỗi 409 Conflict.
5. Thao tác Repository
| Phương thức | Mô tả |
|---|---|
findCurrentCost() | Tìm chi phí có effectiveTo = null |
findEffectiveCost() | Tìm chi phí đang hoạt động tại một ngày cho trước |
findCostHistory() | Truy xuất bản ghi chi phí trong phạm vi ngày |
hasOverlappingDates() | Kiểm tra trùng lặp phạm vi ngày (với tùy chọn loại trừ) |
6. API Controller
CostController mount CRUD chuẩn cộng hai route tuỳ chỉnh: PUT /costs/current (đóng cái hiện tại + mở cái mới) và GET /costs/product-variant/{id}/current. Tham chiếu request/response đầy đủ được render trực tiếp từ /v1/api/pricing/doc/openapi.json - không bảo trì thủ công ở đây.
7. Ví dụ Thực tế
7.1. Kịch bản 1: Tạo Chi phí Ban đầu
Quy tắc: "Đặt chi phí ban đầu cho biến thể sản phẩm mới là 50,000đ bắt đầu từ 2026-01-01"
Yêu cầu:
POST /costs
Content-Type: application/json
{
"productVariantId": "variant-laptop-001",
"amount": "50000",
"effectiveFrom": "2026-01-01T00:00:00Z",
"effectiveTo": null,
"note": "Initial supplier pricing from TechVendor Ltd"
}Phản hồi:
{
"id": "cost-001",
"productVariantId": "variant-laptop-001",
"amount": "50000",
"effectiveFrom": "2026-01-01T00:00:00.000Z",
"effectiveTo": null,
"note": "Initial supplier pricing from TechVendor Ltd",
"createdAt": "2026-01-01T08:00:00Z"
}Kết quả: Chi phí này hiện là chi phí hiện tại đang hoạt động (effectiveTo = null).
7.2. Kịch bản 2: Thay thế Chi phí Hiện tại
Quy tắc: "Nhà cung cấp tăng giá lên 55,000đ có hiệu lực từ 2026-03-01"
Bước 1: Lấy Chi phí Hiện tại
GET /costs/product-variant/variant-laptop-001/currentPhản hồi: { "id": "cost-001", "amount": "50000", "effectiveTo": null }
Bước 2: Cập nhật Chi phí Hiện tại
PUT /costs/current
Content-Type: application/json
{
"productVariantId": "variant-laptop-001",
"amount": "55000",
"effectiveFrom": "2026-03-01T00:00:00Z",
"note": "Supplier price increase - Q1 adjustment"
}Thao tác Backend:
- Đóng chi phí cũ:
effectiveTo = "2026-02-28T23:59:59.999Z"(1ms trước effectiveFrom mới) - Tạo chi phí mới:
amount = "55000",effectiveTo = null
Kết quả: Lịch sử chi phí được giữ lại, chi phí mới có hiệu lực từ 2026-03-01.
7.3. Kịch bản 3: Dòng Thời gian Lịch sử Chi phí
Quy tắc: "Theo dõi thay đổi chi phí qua 6 tháng: Tháng 1 (50k) → Tháng 3 (55k) → Tháng 5 (52k)"
Dòng thời gian:
2026-01-01 2026-03-01 2026-05-01 (ongoing)
| | | |
|---- 50,000 -----|---- 55,000 -----|---- 52,000 -----|→
cost-001 cost-002 cost-003Truy vấn Lịch sử Chi phí:
GET /costs?filter={"where":{"productVariantId":"variant-001"}}&order=effectiveFrom ASCPhản hồi:
[
{
"id": "cost-001",
"amount": "50000",
"effectiveFrom": "2026-01-01T00:00:00Z",
"effectiveTo": "2026-02-28T23:59:59.999Z",
"note": "Q1 pricing"
},
{
"id": "cost-002",
"amount": "55000",
"effectiveFrom": "2026-03-01T00:00:00Z",
"effectiveTo": "2026-04-30T23:59:59.999Z",
"note": "Supplier increase"
},
{
"id": "cost-003",
"amount": "52000",
"effectiveFrom": "2026-05-01T00:00:00Z",
"effectiveTo": null,
"note": "Negotiated discount"
}
]7.4. Kịch bản 4: Lỗi Trùng lặp Ngày
Quy tắc: "Ngăn chặn tạo bản ghi chi phí trùng lặp"
Trạng thái Hiện tại: Chi phí tồn tại từ 2026-01-01 đến 2026-03-31
Yêu cầu Không hợp lệ (trùng lặp):
POST /costs
{
"productVariantId": "variant-001",
"amount": "48000",
"effectiveFrom": "2026-02-01T00:00:00Z",
"effectiveTo": "2026-04-30T23:59:59Z"
}Phản hồi Lỗi:
{
"statusCode": 409,
"message": "[createCost] Cost dates overlap with existing records"
}Phát hiện Trùng lặp:
| Chi phí Hiện tại | Chi phí Mới | Trùng lặp? |
|---|---|---|
| 1/1 → 31/3 | 1/2 → 30/4 | ✅ CÓ |
| 1/1 → 31/3 | 1/4 → 31/5 | ❌ KHÔNG |
7.5. Kịch bản 5: Lấy Chi phí Hiệu lực tại Ngày Cụ thể
Quy tắc: "Truy xuất chi phí đang hoạt động vào một ngày lịch sử cụ thể"
Truy vấn: Chi phí vào ngày 2026-02-15
const cost = await costService.getEffectiveCost({
productVariantId: 'variant-001',
effectiveDate: new Date('2026-02-15T10:00:00Z')
});Kết quả: Trả về cost-001 với amount 50000 (hoạt động từ 1/1 đến 28/2).
7.6. Kịch bản 6: Chuyển đổi Chi phí với Ranh giới Ngày Chính xác
Quy tắc: "Đảm bảo chuyển đổi chi phí liền mạch không có khoảng trống"
Cập nhật Chi phí Hiện tại:
PUT /costs/current
{
"productVariantId": "variant-001",
"amount": "55000",
"effectiveFrom": "2026-03-01T00:00:00.000Z"
}Tính toán Backend:
const oldEffectiveTo = dayjs('2026-03-01T00:00:00.000Z')
.subtract(1, 'millisecond')
.toISOString();
// Result: "2026-02-28T23:59:59.999Z"Trạng thái Cuối cùng:
[
{
"id": "cost-001",
"effectiveTo": "2026-02-28T23:59:59.999Z"
},
{
"id": "cost-002",
"effectiveFrom": "2026-03-01T00:00:00.000Z",
"effectiveTo": null
}
]Kết quả: Không có khoảng trống, không trùng lặp - liên tục hoàn hảo.
8. Tài liệu Liên quan
- Pricing Service - Tổng quan package
- Fare System - Tính fare
- Tax System - Tính thuế
- Promotions - Hệ thống khuyến mãi