Skip to content

Cost Tracking

1. Overview

PropertyValue
StatusStable (not yet wired into calculators)
Ownerpricing-team
ServiceCostService (src/services/management/cost.service.ts)
Controller / RouteCostController/costs
Depends onCost schema (@nx/core)

Tracks product variant costs over time. Each record has an effective date range; only one cost is open-ended (current) per variant. getCurrentCost / getEffectiveCost hardcode principalType: 'ProductVariant'.

Service catalogue and identity card: see Pricing Service index. REST reference: live at /v1/api/pricing/doc/openapi.json.

2. Data Model

2.1. Date Range States

StateeffectiveFromeffectiveToDescription
CurrentsetnullActive cost — open-ended
HistoricalsetsetPast cost — closed range
Scheduledfuture dateset or nullFuture cost change

3. Cost Operations

3.1. Create Cost

Validates that no existing cost overlaps with the new date range. Throws 409 Conflict if overlap detected.

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. Update Current Cost

Replaces the current active cost by closing the old one and creating a new entry:

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. Get Current Cost

http
GET /costs/product-variant/variant-123/current
Authorization: Bearer <token>

Returns the cost where effectiveTo = null, or null if no active cost exists.

4. Date Overlap Validation

Before creating or updating a cost, the system checks for date range overlaps:

  • Bounded range: Check if any existing cost's date range intersects with the new range
  • Open-ended range: Check if another open-ended cost exists
  • Update exclusion: Excludes the current record when checking for update operations

Overlap detected → 409 Conflict error.

5. Repository Operations

MethodDescription
findCurrentCost()Find cost where effectiveTo = null
findEffectiveCost()Find cost active on a given date
findCostHistory()Retrieve cost records within a date range
hasOverlappingDates()Check for date range overlaps (with optional exclude)

6. Controller API

CostController mounts standard CRUD plus two custom routes: PUT /costs/current (close current + open new) and GET /costs/product-variant/{id}/current. Full request/response reference is rendered live from /v1/api/pricing/doc/openapi.json — not hand-maintained here.


7. Practical Examples

7.1. Scenario 1: Initial Cost Creation

Business Rule: "Set initial cost for a new product variant at 50,000 VND starting January 1, 2026"

Request:

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"
}

Response:

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"
}

Result: This cost is now the current active cost (effectiveTo = null).


7.2. Scenario 2: Current Cost Replacement

Business Rule: "Supplier increases price to 55,000 VND effective March 1, 2026"

Step 1: Get Current Cost

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

Response: { "id": "cost-001", "amount": "50000", "effectiveTo": null }

Step 2: Update Current Cost

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"
}

Backend Operations:

  1. Close old cost: effectiveTo = "2026-02-28T23:59:59.999Z" (1ms before new effectiveFrom)
  2. Create new cost: amount = "55000", effectiveTo = null

Result: Cost history preserved, new cost becomes active on March 1, 2026.


7.3. Scenario 3: Cost History Timeline

Business Rule: "Track cost changes over 6 months: Jan (50k) → Mar (55k) → May (52k)"

Timeline:

2026-01-01        2026-03-01        2026-05-01        (ongoing)
    |                 |                 |                 |
    |---- 50,000 -----|---- 55,000 -----|---- 52,000 -----|→
  cost-001          cost-002          cost-003

Query Cost History:

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

Response:

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. Scenario 4: Date Overlap Error

Business Rule: "Prevent creating overlapping cost records"

Current State: Cost exists from 2026-01-01 to 2026-03-31

Invalid Request (overlaps):

http
POST /costs
{
  "productVariantId": "variant-001",
  "amount": "48000",
  "effectiveFrom": "2026-02-01T00:00:00Z",
  "effectiveTo": "2026-04-30T23:59:59Z"
}

Error Response:

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

Overlap Detection:

ExistingNewOverlap?
Jan 1 → Mar 31Feb 1 → Apr 30✅ YES
Jan 1 → Mar 31Apr 1 → May 31❌ NO

7.5. Scenario 5: Get Effective Cost at Specific Date

Business Rule: "Retrieve the cost active on a specific historical date"

Query: Cost on February 15, 2026

typescript
const cost = await costService.getEffectiveCost({
  productVariantId: 'variant-001',
  effectiveDate: new Date('2026-02-15T10:00:00Z')
});

Result: Returns cost-001 with amount 50000 (active from Jan 1 to Feb 28).


7.6. Scenario 6: Cost Transition with Exact Date Boundary

Business Rule: "Ensure seamless cost transition with no gaps"

Update Current Cost:

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

Backend Calculation:

typescript
const oldEffectiveTo = dayjs('2026-03-01T00:00:00.000Z')
  .subtract(1, 'millisecond')
  .toISOString();
// Result: "2026-02-28T23:59:59.999Z"

Final State:

json
[
  {
    "id": "cost-001",
    "effectiveTo": "2026-02-28T23:59:59.999Z"
  },
  {
    "id": "cost-002",
    "effectiveFrom": "2026-03-01T00:00:00.000Z",
    "effectiveTo": null
  }
]

Result: No gap, no overlap — perfect continuity.


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