Skip to content

Theo dõi Chi phí

1. Tổng quan

Thuộc tínhGiá trị
StatusỔn định (chưa wire vào calculator)
Ownerpricing-team
ServiceCostService (src/services/management/cost.service.ts)
Controller / RouteCostController - /costs
Depends onschema 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áieffectiveFromeffectiveToMô tả
Hiện tạisetnullChi phí đang hoạt động - không giới hạn ngày kết thúc
Lịch sửsetsetChi phí quá khứ - phạm vi đóng
Đã lên lịchngày tương laiset hoặc nullThay đổ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.

http
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:

http
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

http
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ứcMô 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:

http
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:

json
{
  "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

http
GET /costs/product-variant/variant-laptop-001/current

Phản hồi: { "id": "cost-001", "amount": "50000", "effectiveTo": null }

Bước 2: Cập nhật Chi phí Hiện tại

http
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:

  1. Đóng chi phí cũ: effectiveTo = "2026-02-28T23:59:59.999Z" (1ms trước effectiveFrom mới)
  2. 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-003

Truy vấn Lịch sử Chi phí:

http
GET /costs?filter={"where":{"productVariantId":"variant-001"}}&order=effectiveFrom ASC

Phản hồi:

json
[
  {
    "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):

http
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:

json
{
  "statusCode": 409,
  "message": "[createCost] Cost dates overlap with existing records"
}

Phát hiện Trùng lặp:

Chi phí Hiện tạiChi phí MớiTrùng lặp?
1/1 → 31/31/2 → 30/4✅ CÓ
1/1 → 31/31/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

typescript
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:

http
PUT /costs/current
{
  "productVariantId": "variant-001",
  "amount": "55000",
  "effectiveFrom": "2026-03-01T00:00:00.000Z"
}

Tính toán Backend:

typescript
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:

json
[
  {
    "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

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.