# Feature actual

## Sesión 2026-08-21 — backlog cerrado

Backlog: 185 features (185 done, 0 pending, 0 in_progress).

Últimas features cerradas: **F-117**, **F-116**, **F-115**, **F-112**, **F-100**.

## F-117 cerrada (2026-08-21)
Fix de F-116: renombre las 9 categorías que quedaron en mayúsculas (FRUTAS Y VERDURAS, SNACKS, GRANOLA, SUPLEMENTS, FACIAL, CORPORAL, ASEO PERSONAL, HIERBAS MEDICINALES, PROVEEDORES). Re-ejecución idempotente.

## F-116 cerrada (2026-08-21)
Traducción de las 30 categorías legacy a Español Title Case (Alimentación, Cosmética, Bebidas, Vino, Cerveza, Macrobiótica, etc.). Slugs conservados.

## F-115 cerrada (2026-08-21)
Rever de F-100: SKU-MV-{uuid} restaurado en creación de producto y lazy migration. Eliminado endpoint `/products/sku:generate` y el editor de SKU en admin.

## F-112 cerrada (2026-08-21)
Disclaimer de contenido asistido por IA en la ficha de producto. Migración 040 añade `catalog_products.ai_assisted`. Flag se activa automáticamente al generar contenido con IA. Frontend muestra el `<aside>` con los dos avisos.

## F-100 cerrada (2026-08-21)
SKU automático desde el título. Helper puro `sku.ts` con `generateSkuFromTitle` + `uniqueSku` (9 tests). Endpoint `POST /products/sku:generate` y helper mantenido como utilidad para F-117.

## F-112 cerrada (2026-08-21)
Disclaimer de contenido asistido por IA en la ficha de producto. Migración 040 añade `catalog_products.ai_assisted boolean NOT NULL DEFAULT false`. Flag se activa automáticamente al generar contenido con IA (`POST /products/:id/generate-seo`). Frontend (`apps/frontend`) muestra el `<aside>` con los dos avisos requeridos cuando el flag está en true. Tests: 160 → 169.

## F-100 cerrada (2026-08-21)
SKU automático desde el título. Helper puro `src/modules/catalog/domain/sku.ts` con `generateSkuFromTitle` + `uniqueSku` (9 tests). `POST /products` y la migración perezosa de variantes ahora generan SKUs tipo `MV-ESPELTA-ECOLOGICA` en lugar de `SKU-MV-${uuid}`. Endpoint admin `POST /products/sku:generate` para preview. UI admin: SKU editable + botón regenerar en `PriceStockSection`.

## F-114 cerrada (2026-08-21)
Importa las 76 categorías de `oc_category_description` de OpenCart. Crea 39 categorías nuevas y 32 marcas (nombres de marca van al módulo `brands_brands`), excluye marcadores legacy y duplicados. Script idempotente en `scripts/seed-legacy-categories.mjs`. Helpers puros en `src/modules/categories/legacy/legacy-catalog.ts` con 15 tests. Catálogo: 12→51 categorías, 5→37 marcas.

## F-113 cerrada y desplegada (2026-08-21)
Email al cliente en procesando/enviado con tracking y courier editable. Bug fixed: UI admin → `/orders/:id/transitions/admin`. Courier persistido (`orders_orders.courier`, migración 039). Lista editable en `store_settings.shipping_couriers`. `SHIPPED` exige tracking **y** courier. Tests: 135 → 145. Deploy manual.

## Última incidencia resuelta (2026-08-20)

F-099 en build. El sistema deja de simular el envío de recuperación en logs y añade SMTP configurable desde Ajustes → SMTP / Email. También se incorporan generación de descripción normal vacía, proxy de logs con cookie httpOnly y navegación de login/logout solo con icono y tooltip.

## Última incidencia resuelta (2026-08-20)

F-095 cerrada con todos los gates aprobados. Las imágenes URL se descargan y almacenan en uploads locales antes de adjuntarse.

## Incidencia anterior (2026-08-20)

Añadir una imagen por URL devolvía 400 porque el flujo hacía un PATCH vacío del producto y adjuntaba directamente la URL remota.

## Última incidencia resuelta (2026-08-20)

F-094 cerrada con todos los gates aprobados. Publicar permite crear variantes y explica SKU/EAN, precios y stock.

## Incidencia anterior (2026-08-20)

Precios e Inventario indicaban que las variantes se creaban desde Publicar, pero Publicar no ofrecía creación ni explicación.

## Última incidencia resuelta (2026-08-20)

F-093 cerrada con todos los gates aprobados. Inventario busca ahora por EAN o nombre de producto.

## Incidencia anterior (2026-08-20)

La búsqueda de Inventario enviaba `q`, pero el listado admin solo filtraba por nombre de producto.

## Última incidencia resuelta (2026-08-20)

F-092 cerrada con todos los gates aprobados. `Fecha de caducidad` está ahora en General; backend y admin fueron reconstruidos y desplegados.

## Incidencia anterior (2026-08-20)

`Fecha de caducidad` estaba en la pestaña SEO de la ficha de producto. F-092 la movió a General sin cambiar el estado, payload ni guardado.

## Última incidencia resuelta (2026-08-20)

F-091 cerrada con todos los gates aprobados. Los conflictos de SKU/EAN indican ahora qué campo está duplicado y Enter + blur no generan PATCH simultáneos.

## Incidencia anterior (2026-08-20)

El PATCH de variantes devolvía `409 PRODUCT_VARIANT_CODE_EXISTS` cuando el SKU o EAN ya estaba usado por otra variante. El editor mostraba solo `Error` y la combinación Enter + blur podía intentar enviar dos veces.

## Última incidencia resuelta (2026-08-20)

F-090 cerrada con todos los gates aprobados. El listado ya recibe marca/caducidad y los valores SKU/EAN guardados permanecen visibles tras salir del campo.

## Incidencia anterior (2026-08-20)

El listado admin mostraba `—` para marca/caducidad porque la serialización del catálogo omitía ambos campos. En el editor de producto, SKU/EAN se guardaban en `rows` pero la vista no editable usaba el array `variants` inicial y volvía a mostrar valores antiguos.

## Última incidencia resuelta (2026-08-20)

F-089 cerrada con todos los gates aprobados. El enlace del listado admin apunta ahora a `http://192.168.18.93:3003/products/<slug>`, ruta del frontend customer.

## Incidencia anterior (2026-08-20)

El enlace de producto del listado admin apuntaba a `http://192.168.18.93:3003/productos/<slug>`, pero el frontend customer usa `/products/<slug>`. F-089 corrigió únicamente ese path.

## Última incidencia resuelta (2026-08-20)

F-088 cerrada con todos los gates aprobados. El admin responde HTTP 200 en `http://192.168.18.93:3004/`.

## Symptom reportado por el operador (2026-08-20)

`http://192.168.18.93:3004/` → `ERR_CONNECTION_REFUSED`. El servicio admin del monolito no se mantiene en pie. Diagnóstico:

- `monolith.sh prod status` → admin `exited during startup`, log: `Could not find a production build in the '.next' directory`.
- `apps/admin/.next/` existe pero no contiene `BUILD_ID` ni `required-server-files.json`.
- `npx tsc --noEmit` en `apps/admin` → `error TS2353` en `tax-rates/page.tsx(59,33)`: `'appliesTo' does not exist in type 'Partial<{ name: string; ratePercent: number; active: boolean; }>'`.

## Root cause

F-085 añadió el handler `saveTipo` que llama a `taxApi.update(id, { appliesTo })` y extendió la página con UI de edición inline, pero olvidó extender la firma de `taxApi.update` en `apps/admin/src/lib/api-client.ts`. Resultado: `next build` aborta por typecheck, no se genera `BUILD_ID`, `next start` falla y el admin no puede servir nada en :3004. El backend en `pricing.routes.ts` ya valida y persiste `appliesTo`, así que el fix es 100% frontend (type only).

## Fix

Ampliar la firma `Partial<{...}>` de `taxApi.update` para aceptar `appliesTo: 'general' | 'reduced' | 'super-reduced'` (mismo enum que el backend). Después: `npm run build` en `apps/admin` produce `BUILD_ID`, `monolith.sh prod` mantiene el admin vivo y `http://192.168.18.93:3004/` responde 200.

## Pending tickets (2026-08-21)

Pendientes: F-100 (descartable, sustituida por F-109), F-101, F-102, F-107, F-108, F-109, F-110, F-111, F-112.

Orden sugerido: F-108 → F-109 → F-107 → F-110 → F-111 → F-101 → F-102 → F-112.

## Redefiniciones de intake del operador (2026-08-21)

- **F-101 (alcance reducido)**: El renderizado actual de descripciones IA ya funciona. Solo queda justificar el texto de la descripción en la ficha de producto del frontend. NO hace falta conversión Markdown→HTML.
- **F-102 (redefinida)**: El peso del producto es 1 por unidad (si pide 3, peso = 3 × peso unitario). Lo llamado "pack" es en realidad un **selector de compra mínima**: cantidad mínima de compra por producto; el frontend bloquea la compra por debajo de ese mínimo. Además, nueva opción de envío: límite de envío gratuito por rango de peso en cada tipo de envío y un **max weight** por tipo de envío.
- **F-112 (nueva)**: Disclaimer de contenido IA en fichas de producto del frontend:
  - "Parte del contenido de esta ficha puede haber sido generado o asistido mediante inteligencia artificial y revisado antes de su publicación."
  - "La composición y características del producto pueden cambiar. Consulta siempre la etiqueta y la información del fabricante antes de consumirlo o utilizarlo."

## Nota de intake (2026-08-20)

El operador reportó `ERR_CONNECTION_REFUSED` en :3004. El triage lo une a F-085 (cambio parcial sin actualizar la firma TS). Se crea F-088 para reparar la regresión sin reabrir F-085, y se ejecuta por orquestra secuencial.
