feat(#25): Gate B pricing and thickness band (TV-8, TV-9)

TV-8 pricing:
- computeRecommendedPrice(bdft, pricePerBdft) returns bdft × price/bdft rounded half-up to 2 decimals
  0.96 bdft × 12.50/bdft → 12.00
- computeSlabPrice handles three paths:
  1. Has rule, no override → recommended price, source 'recommendation'
  2. Has rule, override → override stored (150.00), source 'override'
  3. No rule, manual → manual stored (99.99), recommendedPrice null

TV-9 thickness band:
- findThicknessBand(thicknessIn, ranges) rounds to 1/8" then matches [min, max) half-open ranges
- 1" → 1"–1 1/2" (rounds up, label from synced Woo attribute, not hardcoded)

Test output (all 29 tests green, 6 new):
- tv8-pricing.test.ts: 6 passed (recommended, override, no-rule, rounding)
- tv9-thickness-band.test.ts: 6 passed (1"→1"–1 1/2", 0.75"→3/4"–1", 0.875"→3/4"–1", 1.5"→1 1/2"–2", null for out-of-range, rounding to 1/8")
This commit is contained in:
grokbot_ty
2026-09-07 06:33:24 +00:00
committed by grokbot_ty
parent f7040f5947
commit 34dbfa5d7d
7 changed files with 286 additions and 0 deletions
@@ -0,0 +1,11 @@
/** TECH-SPEC §8 TV-9 mock taxonomy fixture for thickness attribute. */
import type { ThicknessRange } from "../../../lib/pipeline/thicknessBand.js";
/** Mock Woo thickness attribute terms (synced from store). */
export function mockThicknessRanges(): ThicknessRange[] {
return [
{ wooId: 101, label: '3/4"–1"', minIn: 0.75, maxIn: 1.0 },
{ wooId: 102, label: '1"–1 1/2"', minIn: 1.0, maxIn: 1.5 },
{ wooId: 103, label: '1 1/2"–2"', minIn: 1.5, maxIn: 2.0 },
];
}