57 lines
2.8 KiB
Markdown
57 lines
2.8 KiB
Markdown
|
|
---
|
|||
|
|
convoy: pricing-online-local
|
|||
|
|
brief_number: 2
|
|||
|
|
depends_on: [1]
|
|||
|
|
recommended_model: composer-2.5
|
|||
|
|
model_tier: standard
|
|||
|
|
files:
|
|||
|
|
- lib/pricing/card-price.js
|
|||
|
|
- lib/pricing/price-history.js
|
|||
|
|
- lib/pricing/collection-valuation.js
|
|||
|
|
- lib/pricing/outbound-links.js
|
|||
|
|
- test/lib/pricing/card-price.test.js
|
|||
|
|
- test/lib/pricing/price-history.test.js
|
|||
|
|
- test/lib/pricing/collection-valuation.test.js
|
|||
|
|
cross_brief_commitments:
|
|||
|
|
- brief: 3
|
|||
|
|
description: |
|
|||
|
|
Briefs 3–5 import `recordPriceSnapshots` from `lib/pricing/price-history.js`
|
|||
|
|
and set `prices_updated_at` after successful price writes. They must call
|
|||
|
|
the shared helpers rather than duplicating snapshot SQL.
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Brief 2: Pricing foundation lib
|
|||
|
|
|
|||
|
|
## Goal (1 sentence)
|
|||
|
|
|
|||
|
|
Create shared pricing helpers for foil-aware unit prices, daily history snapshots, collection valuation SQL, and outbound buy links.
|
|||
|
|
|
|||
|
|
## Files in scope (do not edit anything else)
|
|||
|
|
- `lib/pricing/card-price.js`
|
|||
|
|
- `lib/pricing/price-history.js`
|
|||
|
|
- `lib/pricing/collection-valuation.js`
|
|||
|
|
- `lib/pricing/outbound-links.js`
|
|||
|
|
- `test/lib/pricing/card-price.test.js`
|
|||
|
|
- `test/lib/pricing/price-history.test.js`
|
|||
|
|
- `test/lib/pricing/collection-valuation.test.js`
|
|||
|
|
|
|||
|
|
## Conventions to follow
|
|||
|
|
- ESM (`import`/`export`); DB via `import { sql } from '../sql.js'` (relative from `lib/pricing/`).
|
|||
|
|
- `card-price.js`: export `resolveUnitPrice({ currentPrice, marketPrice, isFoil })` — nonfoil prefers `current_price`, foil prefers `market_price`; export `PRICE_STALE_HOURS = 24`.
|
|||
|
|
- `collection-valuation.js`: export `effectiveUnitPriceExpr({ cardAlias = 'c', userCardAlias = 'uc' })` returning SQL `CASE WHEN uc.is_foil THEN c.market_price ELSE COALESCE(c.current_price, c.market_price) END`; export `listUnitPriceExpr({ cardAlias = 'c' })` → `COALESCE(c.current_price, c.market_price)`.
|
|||
|
|
- `price-history.js`: export `recordPriceSnapshots(rows)` where each row is `{ cardId, source, priceType, currency, amount }`; upsert on daily UNIQUE key.
|
|||
|
|
- `outbound-links.js`: extract TCGPlayer + eBay URL logic currently inline in `CardDetailView.js` (game-aware).
|
|||
|
|
- Mirror test style from `test/lib/collection-vocabulary.test.js`.
|
|||
|
|
|
|||
|
|
## Acceptance criteria
|
|||
|
|
- [ ] `resolveUnitPrice` returns foil vs nonfoil correctly for MTG-shaped data
|
|||
|
|
- [ ] `recordPriceSnapshots` is idempotent for same card/day/source/type
|
|||
|
|
- [ ] `effectiveUnitPriceExpr` produces valid SQL fragment (unit-tested via known inputs or integration mock)
|
|||
|
|
- [ ] `buildTcgplayerUrl(card)` and `buildEbayUrl(card)` exported
|
|||
|
|
- [ ] All three test files pass (`npm run test:run`)
|
|||
|
|
- [ ] No scope expansion
|
|||
|
|
|
|||
|
|
## Rationale (≤3 sentences)
|
|||
|
|
|
|||
|
|
Centralizes pricing semantics so bulk sync, valuation fix, and APIs cannot drift. The foil bug (`market_price` = Scryfall `usd_foil`) is fixed once at the helper layer. Snapshot writer gives Briefs 3–5 a single hook.
|