Some checks failed
CI / Lint (pull_request) Has been cancelled
CI / Schema map up to date (pull_request) Has been cancelled
CI / Forbidden patterns (9 checks) (pull_request) Has been cancelled
CI / Migrations apply (node-pg-migrate) (pull_request) Has been cancelled
Visual diff / Should run? (pull_request) Has been cancelled
CI / Unit tests (vitest) (pull_request) Has been cancelled
Convoy metrics gate / Require role-event telemetry on convoy PRs (pull_request) Has been cancelled
PR Health rollup / Aggregate gate status (pull_request) Has been cancelled
Preview smoke / Should run? (pull_request) Has been cancelled
Preview smoke / Playwright smoke (pull_request) Has been cancelled
Visual diff / Screenshot diff (pull_request) Has been cancelled
Add pricing schema, free API integrations (MTG bulk, TCGdex, Foolio), valuation and sort-by-price, card detail pricing UI, and nearby store discovery via home zip and OSM geo clients. Co-authored-by: Cursor <cursoragent@cursor.com>
56 lines
2.8 KiB
Markdown
56 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.
|