Skip to content

Theo dõi Chi phí

1. Tổng quan

Hệ thống theo dõi chi phí quản lý chi phí biến thể sản phẩm theo thời gian. Mỗi bản ghi chi phí có phạm vi ngày hiệu lực, cho phép theo dõi lịch sử chi phí và lên lịch thay đổi chi phí. Chỉ có một chi phí có thể đang hoạt động (không giới hạn ngày kết thúc) tại một thời điểm cho mỗi biến thể sản phẩm.

Source: src/services/management/cost.service.tsController: src/controllers/cost.controller.tsRoute: /costs

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

Phương thứcĐường dẫnMô tả
POST/costsTạo chi phí
GET/costsDanh sách chi phí
GET/costs/:idLấy chi phí theo ID
PUT/costs/:idCập nhật chi phí
DELETE/costs/:idXóa chi phí
PUT/costs/currentThay thế chi phí đang hoạt động hiện tại
GET/costs/product-variant/:variantId/currentLấy chi phí đang hoạt động hiện tại

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ừ 1/1/2026"

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": "Giá nhà cung cấp ban đầu từ 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": "Giá nhà cung cấp ban đầu từ 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ừ 1/3/2026"

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": "Tăng giá nhà cung cấp - Điều chỉnh Q1"
}

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ừ 1/3/2026.


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        (tiếp tục)
    |                 |                 |                 |
    |---- 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": "Giá Q1"
  },
  {
    "id": "cost-002",
    "amount": "55000",
    "effectiveFrom": "2026-03-01T00:00:00Z",
    "effectiveTo": "2026-04-30T23:59:59.999Z",
    "note": "Tăng giá nhà cung cấp"
  },
  {
    "id": "cost-003",
    "amount": "52000",
    "effectiveFrom": "2026-05-01T00:00:00Z",
    "effectiveTo": null,
    "note": "Giảm giá thương lượng"
  }
]

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ừ 1/1/2026 đến 31/3/2026

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 15/2/2026

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();
// Kết quả: "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.