Cost Tracking
1. Overview
| Property | Value |
|---|---|
| Status | Stable (not yet wired into calculators) |
| Owner | pricing-team |
| Service | CostService (src/services/management/cost.service.ts) |
| Controller / Route | CostController — /costs |
| Depends on | Cost 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
| State | effectiveFrom | effectiveTo | Description |
|---|---|---|---|
| Current | set | null | Active cost — open-ended |
| Historical | set | set | Past cost — closed range |
| Scheduled | future date | set or null | Future 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.
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:
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
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
| Method | Description |
|---|---|
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:
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:
{
"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
GET /costs/product-variant/variant-laptop-001/currentResponse: { "id": "cost-001", "amount": "50000", "effectiveTo": null }
Step 2: Update Current Cost
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:
- Close old cost:
effectiveTo = "2026-02-28T23:59:59.999Z"(1ms before new effectiveFrom) - 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-003Query Cost History:
GET /costs?filter={"where":{"productVariantId":"variant-001"}}&order=effectiveFrom ASCResponse:
[
{
"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):
POST /costs
{
"productVariantId": "variant-001",
"amount": "48000",
"effectiveFrom": "2026-02-01T00:00:00Z",
"effectiveTo": "2026-04-30T23:59:59Z"
}Error Response:
{
"statusCode": 409,
"message": "[createCost] Cost dates overlap with existing records"
}Overlap Detection:
| Existing | New | Overlap? |
|---|---|---|
| Jan 1 → Mar 31 | Feb 1 → Apr 30 | ✅ YES |
| Jan 1 → Mar 31 | Apr 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
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:
PUT /costs/current
{
"productVariantId": "variant-001",
"amount": "55000",
"effectiveFrom": "2026-03-01T00:00:00.000Z"
}Backend Calculation:
const oldEffectiveTo = dayjs('2026-03-01T00:00:00.000Z')
.subtract(1, 'millisecond')
.toISOString();
// Result: "2026-02-28T23:59:59.999Z"Final State:
[
{
"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.
8. Related Documentation
- Pricing Service — Package overview
- Fare System — Fare calculation
- Tax System — Tax calculation
- Promotions — Promotion system