# F-100 — Generación automática de SKU desde el título

## Backend
- `src/modules/catalog/domain/sku.ts` (helper puro):
  - `generateSkuFromTitle(title)`: normaliza mayúsculas, diacríticos, separadores; produce `MV-<SLUG>` (max 100 chars).
  - `uniqueSku(base, existing)`: añade `-2`, `-3`, … hasta encontrar uno libre; truncando a 100 chars.
- `src/modules/catalog/tests/sku.test.ts` (9 tests): normalización, acentos, `&`, hyphens, longitud, mayúsculas, lowercase collisions, truncado.
- `src/modules/catalog/domain/ports.ts` + `pg-variant-repository.ts`: nuevo `listAllSkus()` para reutilizar en la creación.
- `src/modules/catalog/api/catalog.routes.ts`:
  - `POST /products/sku:generate` (admin): recibe `{ title }` y devuelve `{ sku }` único.
  - `POST /products`: la variante por defecto ahora se crea con `generateSkuFromTitle(product.name)` + `uniqueSku` en vez de `SKU-MV-${uuid}`.
  - `GET /products/:id/variants` (lazy migration): igualmente usa SKU derivado del título.

## Admin UI (apps/admin)
- `lib/api-client.ts`: `productsApi.generateSku(title)`.
- `features/products/components/sections/PriceStockSection.tsx`:
  - Nuevo bloque "SKU" con input editable (validación `^[A-Za-z0-9-]+$`), botón `↻` para regenerar sugerencia.
  - Guardado en blur/Enter; mensajes: `✓` / `SKU duplicado` / `SKU inválido`.

## Evidencia
- `npm run typecheck` (backend) OK.
- `npm test`: 169 passed / 0 failed (9 nuevos).
- `apps/admin tsc --noEmit` OK.
- Smoke: `POST /products/sku:generate { "title": "Aceite de Oliva Virgen Extra" }` → `{ "sku": "MV-ACEITE-DE-OLIVA-VIRGEN-EXTRA" }`.
