Files
mercadodevida/work/artifacts/F-138/implementer.md
2026-08-22 10:22:25 +02:00

32 lines
3.2 KiB
Markdown

# F-138 — Implementer evidence
## What
Closed the `GET /pricing/variants/:id` 404 race: `PricingService.seedVariantPrice` (pricing owns its table) is injected into `CreateProductVariant` and called best-effort right after variant creation. The single constructor covers all 3 call sites (product autovariant, lazy variant GET, explicit POST variant).
## Design recap (architect-approved)
- `PricingRepository` + `PricingService` gain `seedVariantPrice(variantId): Promise<void>`.
- `PgPricingRepository.seedVariantPrice`: `INSERT pricing_variant_prices(variant_id, net_unit_amount_cents, offer_cents, cost_cents, vat_rate) VALUES ($1, 0, NULL, NULL, 'general') ON CONFLICT (variant_id) DO NOTHING` — same column set as `setVariantPrice` (`currency`/`created_at`/`updated_at` via DDL defaults). Idempotent + parameterized.
- `CreateProductVariant` ctor += `pricing: PricingServicePort` (type-only import of pricing **public index** → R1-clean); after `variants.create`, best-effort `await this.pricing.seedVariantPrice(variant.id)` (try/catch: variant already persisted; seed failure must not fail/roll back variant creation).
- `catalog.routes.ts`: `CatalogRoutesDeps += pricing`; `new CreateProductVariant(repository, variants, deps.pricing)`.
- `build-app.ts`: hoist `const pricing = createPricingService(deps.pool)` above `registerCatalogRoutes` so it can be passed in; pricing routes + cart keep using it (unchanged).
## Files changed (source)
- `pricing/domain/ports.ts` — +`seedVariantPrice` on `PricingService` + `PricingRepository`.
- `pricing/application/pricing-service.ts` — +`seedVariantPrice` delegate.
- `pricing/infrastructure/pg-pricing-repository.ts` — +`seedVariantPrice` impl.
- `catalog/application/variant-use-cases.ts` — import `PricingServicePort`; ctor + execute seed (best-effort).
- `catalog/api/catalog.routes.ts``CatalogRoutesDeps += pricing: PricingServicePort`; ctor passes `deps.pricing`.
- `app/build-app.ts` — hoist `pricing` const; pass to `registerCatalogRoutes` deps.
## Tests
- **NEW** `catalog/tests/variant-use-cases.test.ts` (3 tests, runnable no DB): FakeProductRepository + FakeProductVariantRepository + FakePricingService — asserts seed-called-with-variant.id, no-seed-when-product-missing, seed-failure-doesn't-break-creation.
- `catalog.itest.ts` AC itest (skipIf !hasDb): POST `/products/:id/variants``pricing_variant_prices` row exists with `net=0/vat=general/currency=EUR`.
- Collateral (fake fakes updated to satisfy new port method): `pricing/tests/pricing-service.test.ts` repo() factory, `cart/tests/cart-service.test.ts` + `checkout/tests/checkout-service.test.ts` PricingServicePort fakes — added `seedVariantPrice: async () => undefined` no-op.
## Verification (fresh run)
- `npx tsc --noEmit`**0 errors** (strict, noUncheckedIndexedAccess).
- `npx vitest run` (targeted: variant/pricing/cart/checkout/product/build-app) → **16/16 pass**.
- `npm test`**209 passed | 57 skipped** (was 206 → +3 new; no regression in cart/checkout/pricing/build-app).
- `scripts/check-module-boundaries.mjs src`**0 NEW R1 violations**. (Pre-existing R1 on `security.routes.ts:14``log-broadcaster` is out of F-138 scope; git diff proves untouched.)
- `./scripts/verify.sh`**VERIFY_OK** (pre-close).