# Especificación CTC – Órdenes de Compra y Clasificación de Productos

19/10/2026 · Mikael

## Contexto y alcance

Control Tower Clientes (CTC) incorporará la gestión de órdenes de compra (OC/PO) y la solicitud de clasificación arancelaria de productos que no existen en Websimex. Prioridad: urgente, revisión el jueves 22/10/2026.

Hoy la solicitud de clasificación se diligencia a mano en el Excel "Informe búsqueda de productos por importador". El objetivo es que el sistema la genere o alimente automáticamente a partir de la OC, y que mida los tiempos contra el ANS de clasificación.

**Incluye:** carga de OC por plantilla, validación de producto en Websimex, solicitud de clasificación, bolsa de clasificación, alertas de producto nuevo y de Registro de Importación (RIM), trazabilidad de modalidad de transporte, vistas en Central / Torre de Control, medición de tiempos y parametrización de destinatarios por cliente en Central, que también cubre a los clientes sin CTC.

**Glosario:** CTC = Control Tower Clientes · RIM = Registro de Importación · OC/PO = orden de compra · VoBo = visto bueno para movimiento de mercancía · In House = encargado de supervisar y controlar la información de los clientes en CTC · Analista de Servicio al Cliente = gestiona en Central a los clientes que no tienen CTC.

## Flujo de trabajo esperado

Cada paso del flujo de clasificación queda soportado por un módulo de CTC (M1 a M7, indicado en cada paso).

```mermaid
flowchart TD
  A["Identificar material para clasificación<br/>(código no existe en Websimex · M1–M2)"] --> B["Solicitar información al proveedor<br/>(bolsa de clasificación · M3)"]
  B --> C["Recibir y validar información<br/>(documentos completos: inicia ANS · M7)"]
  C --> D["Realizar clasificación arancelaria<br/>(subpartida sugerida; se detiene ANS)"]
  D --> E["Identificar requisitos de importación<br/>(campo Requisito · M2)"]
  E --> F{"¿Requiere registro de importación?"}
  F -- Sí --> G["Montar / gestionar RIM<br/>(alerta RIM bloquea VoBo · M4)"]
  G --> H["Obtener RIM aprobado"]
  H --> I["Visto bueno para movimiento de mercancía<br/>(analista registra VoBo · M3)"]
  F -- No --> I
```

Si la clasificación exige registro de importación, el VoBo espera la aprobación del RIM; si no, pasa directo al visto bueno.

## Módulo 1 – Ingreso de OC: documento con IA, manual y plantilla Excel

Las OC ingresan a CTC por tres canales que deben producir el mismo registro:

- **Documento con IA:** carga del documento de la OC con lectura y extracción por IA (canal actual).
- **Manual:** digitación directa en el formulario de OC.
- **Plantilla Excel:** plantilla estandarizada y propia de CTC. El cliente la descarga del sistema, la diligencia y la carga en la aplicación.

Los campos de la plantilla se definieron a partir de los marcados en rojo en el reporte de OC de un cliente. Son los mismos que la IA debe extraer y que pide el formulario manual.

| Aspecto | Documento + IA | Manual | Plantilla Excel |
| --- | --- | --- | --- |
| Entrada | PDF o imagen de la OC | Formulario de OC | Archivo .xlsx con la plantilla CTC |
| Obtención de datos | Lectura y extracción por IA | Digitación del usuario | Lectura directa de columnas |
| Validación | Reglas comunes RN-1.1 a RN-1.5 + cotejo visual contra el documento | Reglas comunes RN-1.1 a RN-1.5 | Reglas comunes RN-1.1 a RN-1.5 (estructura, listas, productos, fechas y valores) |
| Revisión humana | Obligatoria en campos con baja confianza o vacíos | La hace el mismo usuario al digitar | Solo en filas con error |
| Resultado | Registro de OC único (mismo modelo de datos) | Registro de OC único (mismo modelo de datos) | Registro de OC único (mismo modelo de datos) |

**Reglas de consonancia entre canales**

- RN-1.6 Los tres canales escriben en el mismo modelo de datos de OC y aplican las mismas reglas de negocio: estructura, valores frente a las listas y productos, y consistencia de fechas y valores. Cada canal agrega las validaciones propias de su contexto (RN-1.8). El resultado es el mismo cualquiera que sea la fuente de ingreso: una misma OC produce el mismo registro, con las mismas líneas y los mismos productos.
- RN-1.7 La IA extrae exactamente los campos de la plantilla (los marcados en rojo); un campo que no encuentre queda vacío para revisión, nunca inferido.
- RN-1.8 Cada canal tiene su propia vista de validación, porque los contextos son distintos:
  - **Documento con IA:** antes de guardar, el usuario coteja campo por campo los datos extraídos contra el documento, en el formulario de cotejo, y los confirma o corrige. Así certifica visualmente que la extracción no trajo información errada.
  - **Plantilla Excel:** la información proviene del cliente y se presume correcta. La carga valida la estructura, los valores frente a las listas y productos, y la consistencia de fechas y valores, y muestra un reporte de errores por fila.
- RN-1.9 El canal de origen de cada OC lo determina el medio de creación: Extracción IA, Manual o Importación Excel. La OC guarda además:
  - **Archivo fuente:** cuando lo hay, la OC guarda la referencia en S3 al archivo usado. En la importación masiva, el archivo cargado se sube a S3 y todas las OC creadas con ese archivo quedan con su referencia.
  - **Acceso privado:** en este módulo, los archivos se suben a S3 como privados (el Excel importado, el documento leído por la IA y los adjuntos de la solicitud de clasificación). Solo se abren con una URL firmada de vigencia corta, que el CTC genera en el momento en que un usuario autorizado pide verlos.
  - **Correcciones:** si aplica, las hechas sobre lo extraído por la IA.
- RN-1.10 Después del ingreso, el flujo es idéntico: validación en Websimex (Módulo 2), bolsa de clasificación y alertas.

**Plantilla CTC de orden de compra**

CTC genera la plantilla con 24 columnas. Son los 20 campos marcados en rojo en el reporte de referencia, más otros cuatro:
- la posición de la línea, que se usa para ordenar los productos al mostrarlos;
- el país de procedencia;
- la fecha estimada de despacho (RN-1.3);
- la fecha de la orden (RN-1.3).

| # | Campo de la plantilla | Obligatorio | Tipo / validación | Diligenciamiento |
| --- | --- | --- | --- | --- |
| 1 | Cliente | Sí | Texto | Lista de compañías del grupo (CTC) o precargado y bloqueado (Central) |
| 2 | Proveedor | Sí | Texto | Lista de proveedores del cliente, tomada de los exportadores de Websimex, con opción de buscar en el maestro global (RN-1.19) |
| 3 | Orden de compra | Sí | Texto, conserva ceros | Libre |
| 4 | Posición | No | Entero > 0 | Libre. Ordena los productos al mostrarlos; no identifica la línea (RN-1.1) |
| 5 | Código SAP | No | Texto, conserva ceros | Es el código que se valida en Websimex (Módulo 2). Si viene vacío, se usa la descripción corta como código (RN-1.2) |
| 6 | Código producto | No | Texto, conserva ceros | Libre. Informativo: no se valida en Websimex |
| 7 | Descripción corta | Sí | Texto | Alimenta "Texto breve" de la solicitud de clasificación. Hace de código del producto cuando no viene el SAP (RN-1.2) |
| 8 | Fecha de entrega del proveedor | Sí | Fecha (dd/mm/aaaa) | Se guarda en `PO.fecha_entrega_proveedor` (campo nuevo). Es distinta de la fecha estimada de despacho (`PO.fecha_etd`). Criterio de orden por defecto (RN-1.3) |
| 9 | Cantidad ordenada | Sí | Número > 0 | Cantidad ordenada en la OC, no la pendiente |
| 10 | Precio unitario | No | Decimal, en moneda del documento | Si viene vacío, se calcula como valor total ÷ cantidad (RN-1.13) |
| 11 | Moneda | Sí | Código ISO 4217 | Lista |
| 12 | Valor total (moneda documento) | Sí | Decimal | Igual a cantidad × precio unitario, cuando el precio viene diligenciado |
| 13 | Valor total USD | No | Decimal | Libre |
| 14 | Incoterm | Sí | Catálogo Incoterms | Lista |
| 15 | Modalidad de transporte | Sí | Catálogo Websimex | Lista (RN-1.4) |
| 16 | País de origen | Sí | Código ISO de 2 letras | Lista ("Origen" en el borrador de la Hoja2) |
| 17 | País de procedencia | Sí | Código ISO de 2 letras | Lista ("Procedencia" en el borrador de la Hoja2) |
| 18 | Puerto / aeropuerto de origen | Sí | Código UN/LOCODE de 5 caracteres (p. ej. `CNSGP`) | Catálogo de puertos internacionales de Websimex (`ex_lugares_embar_arrib_inales`) |
| 19 | Condición de pago | No | Texto | Libre |
| 20 | Comprador interno | Sí | Texto | Lista de compradores activos del cliente, del catálogo en Central, con opción "Nuevo" (RN-8.7 y RN-8.8) |
| 21 | Usuario solicitante | No | Texto | Libre |
| 22 | Email del usuario | No | Email válido | Libre |
| 23 | Fecha estimada de despacho | Sí | Fecha (dd/mm/aaaa) | Se guarda en `PO.fecha_etd`. Distinta de la fecha de entrega del proveedor (RN-1.3) |
| 24 | Fecha de la orden | Sí | Fecha (dd/mm/aaaa) | Se guarda en `PO.fecha_po`. Es uno de los tres criterios de orden de las vistas (RN-1.3) |

- RN-1.11 La plantilla se descarga desde CTC antes de cada cargue; lleva encabezados protegidos, listas desplegables con los catálogos vigentes y una hoja de instrucciones.
- RN-1.12 Cada plantilla lleva un identificador de versión; el cargue rechaza archivos de una versión anterior o con columnas alteradas.
- RN-1.13 Cada valor se guarda en su campo: el precio unitario en el precio unitario y el valor total en el valor total.
  - Si la línea trae los dos, el valor total en moneda del documento debe coincidir con cantidad × precio unitario. Una diferencia mayor a 0,01 marca la fila con error.
  - Si la línea no trae precio unitario, porque el cliente solo comparte el total, el sistema lo calcula como valor total ÷ cantidad y marca la línea con "precio calculado".
- RN-1.14 Las columnas de códigos (OC, código SAP, código producto) tienen formato texto para no perder ceros a la izquierda.

**Fuentes de las listas**

Las listas de la plantilla se consultan en línea al generarla, así la plantilla descargada siempre trae los valores vigentes.

| Lista | Clientes con CTC | Clientes en Central |
| --- | --- | --- |
| Cliente | Tabla compania, relacionada con grupo_empresarial | Tabla importadores (conexión websimex), elegido antes del cargue |
| Proveedor | Websimex: exportadores asociados al importador (RN-1.19) | Websimex: exportadores asociados al importador (RN-1.19) |
| Comprador interno | Catálogo de compradores en Central (Módulo 8) | Catálogo de compradores en Central (Módulo 8) |
| Modalidad de transporte | Websimex (conexión websimex) | Websimex (conexión websimex) |
| Incoterm | Websimex (conexión websimex) | Websimex (conexión websimex) |
| Moneda | Websimex (conexión websimex) | Websimex (conexión websimex) |
| País de origen y país de procedencia | Websimex (conexión websimex) | Websimex (conexión websimex) |
| Puerto / aeropuerto de origen | Websimex: puertos internacionales (`ex_lugares_embar_arrib_inales`) | Websimex: puertos internacionales (`ex_lugares_embar_arrib_inales`) |

- RN-1.15 Ninguna lista se copia ni se mantiene a mano: se consulta en su fuente al generar la plantilla.
- RN-1.16 En CTC, la lista de clientes muestra solo las compañías asociadas al grupo empresarial del cliente que ingresa.
- RN-1.17 En Central, antes de iniciar el cargue el usuario busca y selecciona el importador en un buscador con texto predictivo; la plantilla se genera con ese cliente precargado y bloqueado, y el cargue rechaza filas de otro cliente.
- RN-1.18 Al cargar, los valores se validan de nuevo contra las mismas fuentes, por si cambiaron entre la descarga y el cargue.
- RN-1.19 En Websimex, el área de operaciones crea la mayoría de los exportadores sin importador asociado, para que sirvan a todos los importadores que les compran. Por eso la lista de proveedores de un cliente no puede salir solo del importador asignado al exportador. Se arma con los exportadores activos que cumplen al menos una de estas condiciones:
  - tienen asignado el importador del cliente;
  - aparecen en las facturas de las operaciones del cliente (`cabeza_facturas`);
  - están asociados a productos del cliente (`productos.ID_EXPORTADOR`).

  Si el proveedor no está en esa lista, el usuario lo busca en el maestro global de exportadores activos.
- RN-1.20 País de origen, país de procedencia, moneda, incoterm, modalidad de transporte, puerto de origen y proveedor se guardan en la OC con el código de Websimex, que es el maestro general para todos los importadores. Los ids de los maestros por cliente del CTC se siguen llenando cuando existe equivalencia, para no romper la V1 ni los reportes existentes.
- RN-1.21 Todas las OC se guardan en el mismo repositorio (`PO` y `po_detalle` en la base del CTC), sin importar si ingresan por CTC o por Central. Cada OC queda identificada por el importador de Websimex y por su sistema de origen (CTC o Central). Central crea sus OC a través del CTC, para aplicar el mismo validador, y las consulta directamente. Desde Central se ven las OC de ambos orígenes.
- RN-1.22 Si el proveedor no existe en Websimex, la OC se crea igual:
  - queda con el nombre recibido y la observación "Proveedor por crear";
  - se alerta al Analista de Servicio al Cliente para que lo cree en Websimex;
  - al crearlo, el analista lo asocia a la OC, que pasa a guardar su código de Websimex.

  Esta condición **no bloquea el VoBo**.
- RN-1.23 Los datos del producto de la OC se guardan en el maestro de materiales del CTC (`material`), no en la línea (`po_detalle`). La línea guarda solo lo propio de la OC: cantidad, valores, posición y unidad (decidido 22/10):
  - código de búsqueda (el SAP o, si no viene, la descripción corta, RN-1.2) → `material.codigo`; código SAP → `material.codigo_sap`; descripción corta → `material.nombre`; código producto (informativo) → `material.codigo_homologacion`, solo si está vacío (corregido 27/10: así están los datos, p. ej. en el grupo 27 846 de 851 materiales con SAP tienen `codigo` = SAP y 62 sin SAP tienen `codigo` = descripción, y es la llave con la que el validador consolida y Websimex busca);
  - el estado frente a Websimex (`EXISTE` / `NUEVO` / `NO_CODIFICADO`) y la solicitud de clasificación son del material. Al clasificarlo, todas las OC que lo tienen ven el cambio;
  - cada material pertenece a un importador: `material.id_importador` guarda el `compania.id_websimex` de la compañía dueña del producto, y el estado se evalúa contra los productos de Websimex de ese importador;
  - si el grupo empresarial tiene una sola compañía activa, `id_importador` se llena con su `id_websimex` sin preguntar;
  - si tiene varias, el formulario de materiales exige elegir la compañía (tarea 1.8). En la carga masiva, el importador sale de la compañía de la fila (tarea 1.4). Que una misma plantilla traiga dos compañías del mismo grupo es atípico, pero se resuelve igual: cada fila usa el `id_websimex` de su compañía;
  - **no se admiten materiales duplicados** (decidido 22/10). La primera capa es la aplicación, y la base es la segunda (índice único por grupo + importador + código + nombre, A.4 #16). La aplicación es más estricta que la base: el código identifica el producto, así que el nombre no se tiene en cuenta. Un material es duplicado si en el mismo grupo ya existe otro, activo o inactivo, con el mismo `codigo` (sin distinguir mayúsculas ni espacios sobrantes) y con el mismo `id_importador`, o con `id_importador` nulo. Un material anterior sin importador cuenta como duplicado, para que se complete en lugar de crear otro. Al editar se excluye el propio registro. Una sola verificación la usan el formulario de materiales y el importador masivo.

**Reglas de negocio**

- RN-1.1 Cada línea de la OC se identifica por PO + material, y las actualizaciones usan esa llave. La posición, cuando viene, se guarda y se usa para ordenar los productos al mostrar la información; no identifica la línea. Si un mismo material aparece en varias posiciones de una PO, se consolida en una sola línea. Es un caso excepcional, que en la práctica no debería ocurrir (regla aprobada el 21/10):
  - la cantidad es la suma de las cantidades;
  - el valor total es la suma de los valores totales;
  - el precio unitario es valor total ÷ cantidad;
  - la posición es la menor.
- RN-1.2 Si la línea no trae código SAP, la descripción corta hace de código del producto (aprobado 21/10):
  - Con ese valor se valida la existencia del producto en Websimex (`productos.CODIGO_PRODUCTO`) y se identifica el material de la línea.
  - Es una práctica que ya existe en Websimex: 45.658 productos activos tienen como código una descripción, por ejemplo "FILTRO AIRE SMC AF40-N04D-D NPT".
  - Si el producto no existe, la línea se marca "No Codificado" y va a la bolsa de clasificación.
  - Valores como "S/N", "N/A" o "-" cuentan como SAP vacío.
  - La comparación con Websimex se hace quitando los espacios sobrantes (al inicio, al final y dobles); no distingue mayúsculas.
  - En Websimex el código admite 50 caracteres. Si la descripción usada como código pasa de ese largo, la validación avisa que no podrá encontrarse en Websimex tal como viene.
- RN-1.3 La OC maneja dos fechas distintas (aclarado con operaciones, 21/10):
  - la **fecha de entrega del proveedor** (`PO.fecha_entrega_proveedor`, campo nuevo), que es obligatoria;
  - la **fecha estimada de despacho** (`PO.fecha_etd`, campo existente), que también es obligatoria (columna 23 de la plantilla).

  Las vistas de OC se pueden ordenar por tres campos: fecha de entrega del proveedor (orden por defecto, aprobado 21/10), fecha estimada de despacho y fecha de la orden (`PO.fecha_po`). En cualquiera de los tres órdenes, las OC sin esa fecha van al final.
- RN-1.4 La modalidad de transporte se valida contra el mismo catálogo de Websimex (ver Módulo 5). CABOTAJE (`0` en Websimex) se excluye de la OC, por dos razones (decidido 22/10):
  - es transporte entre puertos nacionales y no aplica a una OC de importación;
  - en el catálogo del CTC, el `0` es "NINGUNO", el valor por defecto de `PO.codigo_medio_trans`, y la V1 y los reportes mostrarían la OC así.

  También se excluyen CORREO (`5`), VÍAS NAVEGABLES INTERIORES (`8`) y VARIOS (`99`), que están inactivas en el catálogo del CTC. La OC admite: marítimo (`1`), férreo (`2`), terrestre (`3`), aéreo (`4`), multimodal (`6`), instalaciones físicas (`7`) y otro modo (`9`). Con la exclusión, los códigos que se guardan coinciden en los dos catálogos.
- RN-1.5 La carga rechaza el archivo si faltan columnas obligatorias y muestra un reporte por fila con los errores.

**Criterios de aceptación**

- [ ] Una plantilla válida crea todas sus líneas, consolidadas por PO + material, y muestra el total cargado.
- [ ] Un material repetido en dos posiciones de una PO queda en una sola línea, con la suma de cantidades y de valores.
- [ ] Una línea sin código SAP queda como "No Codificado".
- [ ] La lista de OC aparece ordenada por fecha de entrega del proveedor más próxima, y se puede cambiar el orden a fecha estimada de despacho o a fecha de la orden.
- [ ] La misma OC ingresada por documento con IA, de forma manual o por Excel produce registros idénticos.
- [ ] Un campo que la IA no encuentra queda vacío y bloquea el guardado hasta que el usuario lo complete.

## Módulo 2 – Validación en Websimex y Solicitud de Clasificación

Al cargar una OC, el sistema busca el código SAP de cada línea en los productos de Websimex del importador (`productos.CODIGO_PRODUCTO`, aprobado y activo). Si la línea no trae SAP, busca la descripción corta (RN-1.2). Si existe, la línea sigue su curso; si no existe, CTC crea o completa automáticamente una Solicitud de Clasificación con el formato que hoy se llena a mano.

**Campos de la solicitud (formato "Informe búsqueda de productos por importador")**

| Campo | Origen | Quién lo diligencia |
| --- | --- | --- |
| Ítem | Consecutivo automático | Sistema |
| Fecha de solicitud | Fecha de creación | Sistema |
| PO | Plantilla de OC | Sistema |
| Pedido | Consecutivo generado por el sistema (RN-2.8) | Sistema |
| Código producto | Plantilla de OC (o "No Codificado") | Sistema |
| Texto breve | Plantilla de OC | Sistema |
| Usuario | Usuario que cargó la OC | Sistema |
| Proveedor | Plantilla de OC | Sistema |
| Nombre comercial en español | Ficha del producto | Cliente / proveedor |
| Descripción | Ficha técnica estructurada (ver abajo) | Cliente / proveedor |
| Foto | Adjunto | Cliente / proveedor |
| Requisito | Ficha técnica, foto del producto y de la máquina destino | Cliente / proveedor |
| Subpartida sugerida | Resultado de la clasificación | Analista de clasificación |
| VoBo | Visto bueno para movimiento de mercancía | Analista de clasificación |

**Descripción estructurada:** se reemplaza el texto libre por campos separados: nombre del producto en español, marca, referencia, modelo, serial, materia constitutiva, principio de funcionamiento, uso o función, destino (máquina en la que se instalará) y función de la máquina.

**Reglas de negocio**

- RN-2.1 Varias líneas de una misma OC sin producto en Websimex se agrupan en una sola solicitud; la cantidad de ítems define el ANS (Módulo 7). Si a una OC ya cargada se le agregan productos:
  - el proceso que ya arrancó no se reinicia ni se modifica (estados, documentos y ANS siguen igual);
  - se alerta visualmente al In House de la adición, para su validación y seguimiento;
  - los productos agregados que no existen en Websimex arrancan una **solicitud nueva**, con sus propios estados y mediciones.
- RN-2.2 Los documentos requeridos se configuran por solicitud, porque no siempre son los mismos:
  - el analista los elige de un catálogo de tipos de documento, para cada producto, según su requerimiento específico;
  - la solicitud queda en estado "Documentos incompletos" hasta tener los campos del cliente y todos los documentos configurados.
- RN-2.3 Cuando todos los documentos configurados están cargados, la solicitud pasa automáticamente a "Documentos completos", y ahí empieza a contar el ANS.
- RN-2.4 La solicitud se puede exportar en el formato Excel actual mientras dura la transición.
- RN-2.5 La creación del producto en Websimex es manual: no hay integración. CTC y Central no escriben en Websimex; el Analista de Servicio al Cliente crea el producto directamente en ese sistema, en todos los casos (clientes con y sin CTC), luego de que la clasificación queda definida (estado "Clasificada").
- RN-2.6 Una vez creado, el Analista de Servicio al Cliente marca la solicitud como "Producto creado" e indica el código; el sistema verifica en Websimex (solo lectura) que el código exista antes de aceptar el cambio y lo asocia a las líneas de la OC.
- RN-2.7 Si el código no se encuentra en Websimex, el cambio se rechaza y la solicitud sigue pendiente de creación.
- RN-2.8 El pedido es un consecutivo que genera el sistema, con formato `AAAAMM` + 5 dígitos (11 caracteres; por ejemplo, `20261000001` es la primera OC de octubre de 2026). El consecutivo se reinicia cada mes. No viene en la plantilla.
  - Se genera **uno por OC**.
  - Es único y global para todos los clientes.
  - Se genera de forma segura frente a cargas simultáneas: dos cargas al mismo tiempo nunca reciben el mismo número.
  - Reemplaza al mecanismo actual, que se vuelve a implementar (ver A.4, punto 14).
- RN-2.9 El In House o el cliente diligencia la ficha del producto en CTC. El proveedor no tiene usuario en CTC: responde por correo y el analista adjunta su respuesta a la solicitud.

**Criterios de aceptación**

- [ ] Un código inexistente en Websimex genera la solicitud con los campos del sistema ya llenos.
- [ ] Un código existente no genera solicitud.
- [ ] La exportación reproduce las columnas del formato actual.

## Módulo 3 – Bolsa de clasificación

La bolsa es la bandeja de trabajo del analista: reúne todas las POs pendientes de clasificación, incluidas las "No Codificado", ordenadas por fecha de entrega del proveedor (RN-1.3).

- RN-3.1 La bolsa vive en Central, y sus datos (solicitudes, ítems, estados, documentos requeridos y suspensiones) se guardan en la base de CTI (conexión `cti`). El CTC crea las solicitudes al cargar la OC y el In House diligencia la ficha desde el CTC, escribiendo en esas mismas tablas.
- RN-3.2 Cada solicitud la ven quien la generó, el In House del cliente, el analista, la Dirección de Servicio al Cliente y el rol que se designe para clasificar. Esta visibilidad es parametrizable: se arranca con esa configuración inicial y se ajusta después sin bloquear el inicio de la operación.

**Estados de la solicitud:** Documentos incompletos → Documentos completos → En clasificación (⇄ Suspendida – esperando proveedor) → Clasificada → Producto creado → Pendiente RIM / En trámite RIM → RIM aprobado → VoBo otorgado.

**Funciones**

- Filtrar por cliente, proveedor, PO, estado y días restantes de ANS.
- El analista toma una solicitud, pide información al proveedor y registra subpartida, requisitos y VoBo.
- El analista envía la solicitud de información al proveedor desde la bolsa; queda registrada la fecha de envío y de respuesta.
- Semáforo por solicitud según días hábiles consumidos frente al ANS.

**Criterios de aceptación**

- [ ] Toda PO sin producto en Websimex aparece en la bolsa.
- [ ] Cada cambio de estado guarda usuario y fecha.

## Módulo 4 – Alertas

El sistema notifica en pantalla (y por correo, si se confirma) los eventos que bloquean el movimiento de la mercancía.

| Alerta | Se dispara cuando | Destinatario |
| --- | --- | --- |
| Producto nuevo | Un código de la OC no existe en Websimex | En pantalla y por correo. Si la OC entra por CTC: In House y Dirección de Servicio al Cliente. Si entra por Central: el analista que la cargó y la Dirección de Servicio al Cliente |
| Documentos incompletos | Pasan 3 días hábiles sin documentos, contados desde el día hábil siguiente al envío de la solicitud (sin sábados, domingos ni festivos de Colombia) | Cliente / proveedor |
| ANS en riesgo | Se consumió el 80 % de los días hábiles del ANS | Analista y Dirección de Servicio al Cliente |
| Adición de productos | A una OC ya cargada se le agregan productos (RN-2.1) | En pantalla, al In House |
| Producto por crear | La solicitud pasa a "Clasificada" | Analista de Servicio al Cliente |
| Proveedor por crear | La OC se carga con un proveedor que no existe en Websimex (RN-1.22). Es informativa, no bloquea el VoBo | Analista de Servicio al Cliente |
| Requiere RIM | La clasificación indica registro de importación | Coordinador de Registros de Importación (rol 47 de CTI) y cliente |
| RIM sin aprobar | La fecha de despacho se acerca y el RIM no está aprobado | Coordinador de Registros de Importación (rol 47 de CTI) y Torre de Control |

La alerta de RIM se refuerza: deja de ser solo informativa y bloquea el VoBo hasta registrar la aprobación del registro.

- RN-4.2 Indicadores de RIM. El sistema muestra al analista dos indicativos de que un producto requiere registro de importación:
  - el de la subpartida (`posicion_arancelaria.REQUIERE_REGISTRO` / `REGISTRO_IMPORTACION`);
  - el del producto (`productos.REQUIERE_REGISTRO_IMPORTACION`), que antes no se diligenciaba y ahora sí se diligencia en los productos nuevos.

  Con esos indicativos y la necesidad del cliente, el analista decide y diligencia la solicitud de creación del RIM en el módulo de RIM que ya existe en Central (`cti.solicitud_registros_importacion`). La solicitud de clasificación queda enlazada a la de RIM, y el VoBo espera su aprobación.
- RN-4.1 Los destinatarios de cada tipo de alerta se configuran. La tabla anterior es la configuración inicial. Después de iniciada la operación, el administrador puede agregar a cualquier alerta más personas o roles (por ejemplo, un rol de CTI) sin cambios de código.

## Módulo 5 – Modalidad de transporte y trazabilidad

La modalidad de transporte usa el mismo catálogo de Websimex, y cualquier cambio posterior a la carga queda registrado.

- RN-5.1 El campo solo acepta valores del catálogo de Websimex.
- RN-5.2 Cada cambio guarda: OC, valor anterior, valor nuevo, usuario, fecha y hora, y motivo (obligatorio). El motivo se elige de una lista corta y admite una observación libre, para poder medir por qué cambian las modalidades.
- RN-5.3 El historial se ve en el detalle de la OC y no se puede editar ni borrar.
- RN-5.4 Después de la carga, la modalidad la pueden cambiar el In House (en CTC) y el analista (en Central). El usuario cliente de solo consulta no puede cambiarla.
- RN-5.5 El catálogo de Websimex se aplica a las OC nuevas. Los valores históricos de `PO.codigo_medio_trans` no se convierten, porque sus códigos no significan lo mismo que en Websimex (por ejemplo, `0` es NINGUNO en el CTC y CABOTAJE en Websimex).

**Criterios de aceptación**

- [ ] Un valor fuera del catálogo se rechaza.
- [ ] Cambiar la modalidad sin motivo no se permite.
- [ ] El historial muestra todos los cambios en orden cronológico.

## Módulo 6 – Vistas en Central y Torre de Control

Las OC se consultan en la Central y se integran a la Torre de Control.

**Columnas mínimas de la vista de OC:** PO, pedido, proveedor, cliente, fecha de la orden, fecha de entrega del proveedor, fecha estimada de despacho, modalidad de transporte, estado de clasificación, estado de RIM, VoBo y días de ANS restantes.

- RN-6.1 La vista de OC muestra la fecha de entrega del proveedor y la fecha estimada de despacho (`PO.fecha_etd`) como columnas separadas.
- RN-6.2 El orden por defecto es fecha de entrega del proveedor más próxima. También se puede ordenar por fecha estimada de despacho o por fecha de la orden (RN-1.3).
- RN-6.3 El In House opera completamente desde el módulo de OC del CTC, con el alcance que le da su rol. No se construye una vista aparte de In House para la Torre de Control (confirmado 21/10).
- RN-6.4 La vista principal de OC muestra siempre la modalidad de transporte y permite filtrar por ella, con los valores del catálogo de Websimex; el filtro admite varias modalidades a la vez y se combina con los demás filtros.
- RN-6.5 La vista de OC existe en CTC y en Central. Las dos hacen lo mismo y comparten la conexión y los datos (`PO` y `po_detalle`), pero cada una sigue el estilo visual de su aplicación. Central lee las OC por importador.
- RN-6.6 La Torre de Control (Dashboard V2) muestra tarjetas resumen con enlace al listado de OC: OC con productos nuevos, solicitudes por estado, ANS en riesgo o vencido y RIM pendientes. No repite el listado completo.

**Criterios de aceptación**

- [ ] La columna de modalidad de transporte aparece en la vista principal de OC en CTC y en Central.
- [ ] Filtrar por una o varias modalidades muestra solo las OC con esas modalidades.
- [ ] Si la modalidad de una OC cambió, la vista y el filtro usan el valor vigente (Módulo 5).

## Módulo 7 – Medición de tiempos y ANS

La eficiencia se mide en días hábiles desde que el cliente entrega documentos completos hasta que la solicitud queda clasificada. El plazo depende de la cantidad de ítems de la solicitud.

| Cantidad de ítems | Días hábiles para clasificar |
| --- | --- |
| 1 – 3 | 2 |
| 4 – 5 | 3 |
| 6 – 10 | 5 |
| 11 – 15 | 6 |
| 16 o más | 8 (según cantidad y complejidad) |

**Reglas de cálculo**

- RN-7.1 El reloj inicia con el estado "Documentos completos" y se detiene en "Clasificada"; el tiempo medido descuenta los periodos de suspensión (RN-7.4).
- RN-7.2 Se excluyen sábados, domingos y festivos de Colombia.
- RN-7.3 Para 16 ítems o más, el plazo se puede ampliar con justificación registrada. La ampliación la autoriza la Dirección de Servicio al Cliente.
- RN-7.4 El reloj se suspende cuando el analista envía una solicitud de información al proveedor y se reactiva cuando se recibe la respuesta con la información solicitada.
- RN-7.5 Una respuesta incompleta no reactiva el reloj: el analista la marca como incompleta y la suspensión continúa.
- RN-7.6 Cada suspensión queda registrada con: solicitud, motivo, información pedida, usuario que suspende, fecha y hora de suspensión, fecha y hora de respuesta, usuario que reactiva y días hábiles suspendidos.
- RN-7.7 Una solicitud puede tener varias suspensiones; el historial es de solo lectura y se muestra en el detalle de la solicitud.
- RN-7.8 Los rangos de la tabla anterior son la configuración inicial. Se guardan en una tabla editable, de modo que se pueden ajustar sin cambios de código.
- RN-7.9 El reloj del ANS, las alertas por tiempo y los indicadores viven en Central, junto a la bolsa. La Torre de Control solo muestra el resumen (RN-6.6).

**Criterios de aceptación**

- [ ] Al enviar la solicitud al proveedor, el reloj se detiene y la solicitud pasa a "Suspendida – esperando proveedor".
- [ ] Al registrar la respuesta completa, el reloj continúa desde los días ya consumidos.
- [ ] El indicador de cumplimiento del ANS excluye los días suspendidos y el reporte los muestra por separado.

**Indicadores**

- % de solicitudes clasificadas dentro del ANS, por mes y por analista.
- Días hábiles en suspensión por proveedor y tiempo promedio por tramo: carga de OC → documentos completos → clasificada → RIM aprobado → VoBo.
- Solicitudes vencidas y en riesgo.

## Módulo 8 – Parametrización por cliente en Central

La Solicitud de Clasificación se envía al In House del cliente si usa CTC, o al Analista de Servicio al Cliente si no lo usa y se gestiona en Central; en ambos casos con copia a la Dirección de Servicio al Cliente. Estos destinatarios se parametrizan en Central, el sistema institucional de ASERCOL, para cubrir también a los clientes que no tienen CTC.

**Parámetros por cliente**

| Parámetro | Obligatorio | Descripción |
| --- | --- | --- |
| Cliente | Sí | Cliente de ASERCOL al que aplica la configuración |
| Usa CTC | Sí | Sí: la OC la carga el cliente en CTC · No: la carga ASERCOL en Central |
| Destinatario principal | Sí | **Con CTC:** el In House del cliente, tomado de `cti.inhouse_cliente`, la tabla que ya se administra en Central (pantalla `/inhouse_cliente`); admite más de uno. **Sin CTC:** el Analista de Servicio al Cliente, que es el `ID_EMPLEADO_OP` del DO en Websimex. Antes de que exista el DO, es el analista que cargó la OC |
| Copias adicionales | No | Otros correos en copia |
| Estado | Sí | Activo / inactivo |

La **Dirección de Servicio al Cliente** es un solo grupo de correos para todos los clientes. No se parametriza por cliente: se toma de la configuración de destinatarios de alertas (RN-4.1).

**Reglas de negocio**

- RN-8.1 Toda Solicitud de Clasificación se envía a los destinatarios parametrizados del cliente: In House (cliente con CTC) o Analista de Servicio al Cliente (cliente sin CTC) como destinatario, y Dirección de Servicio al Cliente en copia.
- RN-8.2 Si el cliente no tiene parametrización activa, la solicitud no se envía y el sistema alerta al administrador de Central.
- RN-8.3 Para clientes sin CTC, el personal de ASERCOL carga la OC en Central con la misma plantilla o el documento con IA, y se aplica el mismo flujo: validación en Websimex, bolsa de clasificación, alertas y ANS.
- RN-8.4 CTC y Central comparten la misma parametrización; no se configura dos veces.
- RN-8.5 La parametrización la editan el Analista, la Dirección de Servicio al Cliente y Sistemas (rol 1). Cada cambio guarda usuario, fecha, valor anterior y valor nuevo.

**Catálogo de compradores internos**

El comprador interno es el usuario del cliente que se usa para seguimiento, mediciones y control. Se parametriza por cliente en un catálogo maestro en Central, que CTC consume; así queda resuelto también para los clientes sin CTC.

| Campo | Obligatorio | Descripción |
| --- | --- | --- |
| Cliente | Sí | Compañía (CTC) o importador de websimex (Central) |
| Nombre | Sí | Nombre completo del comprador |
| Correo | Sí | Correo para seguimiento y notificaciones |
| Área | No | Área del cliente a la que pertenece |
| Estado | Sí | Activo · Inactivo · Por aprobar |

- RN-8.6 El catálogo maestro vive en Central; CTC lo consulta y no mantiene una copia propia.
- RN-8.7 En la plantilla, "Comprador interno" es una lista con los compradores activos del cliente elegido; el correo se toma del catálogo.
- RN-8.8 Si el comprador no está en la lista, el usuario elige "Nuevo" e ingresa nombre y correo; la fila se carga y el comprador queda "Por aprobar".
- RN-8.9 El In House (CTC) o el Analista de Servicio al Cliente (Central) aprueba o rechaza el comprador nuevo; al aprobarlo queda activo en el catálogo.
- RN-8.10 En el canal de documento con IA, el nombre extraído se compara con el catálogo del cliente; si no hay coincidencia clara, la línea queda en revisión (RN-1.8).
- RN-8.11 El catálogo se carga inicialmente con los compradores de las OC históricas de cada cliente. Hoy son campos dinámicos (`definicion_campos_po` / `adicionales_po`, grupos 27 y 2) que solo guardan el nombre. Por eso se cargan en estado "Por aprobar" y sin correo, y el In House completa el correo al aprobarlos.

**Criterios de aceptación**

- [ ] Una solicitud de un cliente con CTC llega al In House con copia a la Dirección de Servicio al Cliente.
- [ ] Una solicitud generada en Central para un cliente sin CTC sigue el mismo flujo y llega al Analista de Servicio al Cliente con copia a la Dirección de Servicio al Cliente.
- [ ] Un cliente sin parametrización bloquea el envío y genera la alerta al administrador.

## Pendientes por confirmar

- [x] Campos en rojo de la plantilla de orden de compra: definidos en la tabla del Módulo 1.
- [x] Destinatarios de la solicitud definidos en el Módulo 8. La alerta de producto nuevo va por correo al In House (OC cargada en CTC) o al analista que cargó la OC (Central), con copia a la Dirección de Servicio al Cliente (confirmado 21/10).
- [x] La creación del producto en Websimex es manual, sin integración (ver RN-2.5). La hace siempre el Analista de Servicio al Cliente; se hace luego de que la clasificación queda definida (confirmado).
- [x] Días sin documentos antes de alertar al cliente: 3 días hábiles desde el día hábil siguiente al envío de la solicitud (confirmado, Módulo 4).
- [x] Alcance de In House Expo en la Torre de Control.
- [x] Pausa del ANS durante la espera al proveedor: confirmada, ver RN-7.4 a RN-7.7.
- [x] Fuentes de todas las listas de la plantilla definidas en el Módulo 1.
- [x] "Procedencia" de la Hoja2 = país de procedencia y "Origen" = país de origen (confirmado).
- [x] "Comprador interno": parametrizado por cliente en catálogo maestro en Central (decidido, ver Módulo 8). El CTC no tiene tabla de compradores: hoy son campos dinámicos. La migración se definió en RN-8.11 (confirmado 21/10).
- [x] La columna Cantidad corresponde a la cantidad ordenada de la OC (confirmado).

## Análisis de brechas y plan de implementación

20/10/2026 · Análisis técnico sobre la rama `mayala`, con consultas de solo lectura a `asercolg_po` (CTC), `comex` (Websimex), `asercolg_central` y `asercolg_bitacora` (CTI).
21/10/2026 · Actualizado con las decisiones y aclaraciones del negocio. Todas las decisiones de A.3 y los vacíos de A.4 quedaron resueltos.

### A.1 Punto de partida

La base es Órdenes de Compra V2 ([SPEC_PurchaseOrderV2.MD](SPEC_PurchaseOrderV2.MD)), compuesta por tres piezas:

- **Listado:** el embudo de `PurchaseOrderV2`.
- **Formulario:** lectura del documento con Gemini, en `PurchaseOrderFormV2` y `app/Services/ExtraccionOrdenCompra/`.
- **Carga masiva:** la heredada de la V1, `PurchaseOrderMassImport`.

Esas piezas se aprovechan. En cambio, nada de lo que este spec pide sobre clasificación existe en ninguna de las cuatro bases.

El trabajo se reparte entre dos aplicaciones que comparten datos:

- **CTC** (este repositorio): ingreso de OC por los tres canales, validación en Websimex, creación de la solicitud, ficha del producto para el In House y su vista de OC.
- **Central** (proyecto `cia`): bolsa de clasificación, reloj del ANS, alertas por tiempo, indicadores, parametrización y su propia vista de OC.
  - `cia` corre Laravel 10 con PHP 8.2. La restricción de PHP 7.3 aplica solo al CTC.
  - Sus módulos nuevos siguen el patrón V2 de `cia`: módulo del shell, servicios `Acceso*` de permisos y specs en `cia/specs/`. La bolsa de clasificación se construye igual que la Bolsa RIM V2.
  - Ya tiene las conexiones `ctc`, `cti`, `central` y `websimex`. También tiene la pantalla de In House por cliente (`/inhouse_cliente`), el módulo de RIM (`app/Services/Rim/`) y un servicio de días hábiles (`app/Services/Fechas/DiasHabiles.php`).
- **Datos compartidos:**
  - las OC, en `asercolg_po`;
  - las solicitudes de clasificación, en la base de CTI (`asercolg_bitacora`);
  - la parametrización, en `asercolg_central`.

| Módulo | Cobertura actual | Brecha principal | Dónde se resuelve |
|---|---|---|---|
| M1 Ingreso | Parcial | La plantilla tiene 14 columnas con listas del CTC. La carga consolida por material, pero conserva el último precio. 9 de los 24 campos no tienen dónde guardarse | CTC (Central crea OC por API) |
| M2 Validación y solicitud | No existe | Al cargar, nadie valida el código SAP contra `productos` de Websimex. No hay solicitud de clasificación | CTC |
| M3 Bolsa | No existe | Exige una vista interna que cruza clientes | Central, con datos en CTI |
| M4 Alertas | Mínima | Hay correo (`AsercolMailing`), pero no hay alertas en pantalla, ni destinatarios configurables, ni comandos para este flujo | CTC (eventos) y Central (por tiempo) |
| M5 Modalidad | Parcial | `PO.codigo_medio_trans` está diligenciado en el 80 % de las órdenes, pero ni V1 ni V2 lo usan, sus códigos no coinciden con los de Websimex y no hay historial | CTC y Central |
| M6 Vistas | Parcial | V2 ordena solo por `fecha_po` y no tiene filtro de modalidad ni columnas de clasificación. Central no tiene vista de OC | CTC, Central y Dashboard V2 |
| M7 ANS | Solo infraestructura | `cia` ya calcula días hábiles con festivos de Colombia (`DiasHabiles`). Faltan estados, suspensiones, rangos editables e indicadores | Central |
| M8 Parametrización | No existe | Los compradores hoy son campos dinámicos. El In House por cliente ya existe en `cti.inhouse_cliente` | Central |

### A.2 Análisis por módulo

#### M1 – Ingreso de OC

**Lo que se reutiliza:**

- **Plantilla generada por el sistema** (`app/Exports/PlantillaPurchaseOrderImportacionExport.php`): las listas desplegables usan una hoja oculta y rangos con nombre. Es el patrón a extender.
- **Importador** (`app/Imports/PurchaseOrderImportacionMasivaImport.php`): valida por fila, guarda todo o nada en una transacción y reporta los errores como "Fila N: …".
- **Canal IA:** el contrato `ExtractorOrdenCompra`, la homologación en `HomologadorCatalogos` y el JSON crudo en `documentos.param_data`.

**Brechas frente a las reglas:**

| Requisito | Situación actual | Evidencia |
|---|---|---|
| RN-1.1 Una línea es PO + posición | El importador **suma las cantidades por PO + material** y actualiza por `id_po + id_material`. `po_detalle.posicion_pedido` existe, pero ni V1 ni V2 la escriben: está vacía en el 90 % de las líneas. **Decidido (21/10): se mantiene la llave PO + material.** La posición se diligencia y se usa solo para ordenar. La brecha que queda es que la consolidación actual conserva el último precio en lugar de sumar los valores, así que el total de la línea queda mal cuando las posiciones tienen precios distintos | `PurchaseOrderImportacionMasivaImport.php:472-489`, `:119-122` |
| RN-1.2 "No Codificado" | No existe. Al cargar, nadie verifica si el producto existe en `comex.productos` para el importador de la OC. **Confirmado (20/10):** que el importador cree o reactive el material en el maestro del CTC (`material`) es correcto y se conserva. La brecha es solo la validación contra Websimex, que marca la línea y la envía a la bolsa (M2) | `:427-449` |
| RN-1.6 Un solo validador | Hay dos validadores distintos: el del importador y el del formulario V2. **Aclarado (20/10):** lo que se comparte son las reglas de negocio: estructura, listas, producto en Websimex, consistencia de fechas y de valores. Cada canal agrega las validaciones propias de su contexto. El canal Manual (formulario V2) es un tercer canal y produce el mismo registro que la IA y el Excel | — |
| RN-1.8 Vista de validación común | **No es brecha (aclarado 20/10).** Cada canal conserva su pantalla porque los contextos son distintos. **IA:** el formulario de cotejo certifica visualmente, campo por campo contra el documento, que la extracción no trajo información errada. **Excel:** viene del cliente y su contenido se presume correcto; solo se valida la estructura, los valores frente a las listas y productos, y la consistencia de fechas y valores, con un reporte de errores por fila | — |
| RN-1.9 Canal de origen y archivo fuente | Solo se distingue de forma indirecta (`documentos.modo`). El Excel se borra después de cargarlo. **Aclarado (21/10):** el canal se determina por el medio de creación (Extracción IA, Manual, Importación Excel). El Excel importado se sube a S3 y cada OC creada guarda la referencia al archivo. **Decidido (21/10):** en este módulo los archivos son privados y se abren con URL firmada. Hoy `FileAWS` sube todo como `public-read` | `PurchaseOrderMassImport.php:73`, `FileAWS.php:110` |
| RN-1.11 / 1.12 / 1.14 | La plantilla no tiene versión, hoja de instrucciones ni protección, y no fuerza las columnas de código a texto. No hay ningún `setProtection` en el repo | — |
| RN-1.13 Valor total | **Defecto vigente:** el importador guarda en `valor_total` el precio unitario. Viene de cuando la información del cliente traía solo el total y no el precio unitario, lo que todavía puede pasar. **Aclarado (21/10):** en esta versión cada valor va en su campo. Si falta el precio unitario, se calcula como total ÷ cantidad (RN-1.13) | `:126-127`, `:137-138` |
| RN-1.15 / 1.18 Listas de Websimex | Todas las listas salen de los maestros por cliente del CTC (`pais`, `Supplier`, `incoterms`, `moneda`), y `PO` guarda sus ids. Esos maestros no sirven para homologar: en `pais`, el código `01` es Colombia en el grupo 1 y Estados Unidos en el grupo 3, y 44 países no tienen código. Además, los clientes sin CTC no tienen maestros en el CTC. **Resuelto (21/10):** la OC guarda el código de Websimex (RN-1.20), en un repositorio único para CTC y Central (RN-1.21) | `HomologadorCatalogos.php:32-115` |
| RN-1.3 Orden por fecha de entrega | V2 ordena solo por `fecha_po DESC`. **Aclarado con operaciones (21/10):** son dos fechas distintas. La fecha estimada de despacho es `PO.fecha_etd`, que ya existe. La fecha de entrega del proveedor no existe y requiere un campo nuevo, `fecha_entrega_proveedor`. Se ordena por los tres campos: entrega del proveedor (por defecto), despacho y fecha de la orden | `PurchaseOrderV2.php:537` |

**Campos de la plantilla frente al modelo de datos:**

| # | Campo | Dónde se guarda hoy | ¿Lo extrae la IA? | Acción |
|---|---|---|---|---|
| 1 | Cliente | `PO.id_compania` | No | — |
| 2 | Proveedor | `PO.IdSupplier` | Sí | `id_exportador` de Websimex (RN-1.19) |
| 3 | Orden de compra | `PO.po` | Sí | — |
| 4 | Posición | `po_detalle.posicion_pedido` (sin uso) | No | Guardarla para ordenar (RN-1.1) y agregarla al prompt |
| 5 | Código SAP | Solo en el maestro (`material.codigo_sap`) | No | Se queda en `material.codigo_sap`. Es dato del producto, no de la línea (RN-1.23) |
| 6 | Código producto | `material.codigo`, vía `id_material` | Sí | `material.codigo` pasa a ser el código de búsqueda (SAP o descripción); el código producto va a `material.codigo_homologacion` si está vacío (RN-1.23, corregido 27/10) |
| 7 | Descripción corta | No se guarda: la IA la lee y se descarta | Sí | `material.nombre` (RN-1.23) |
| 8 | Fecha de entrega del proveedor | **No existe.** La IA guarda `Delivery_Date` en `fecha_etd`, que es el despacho | Parcial | Columna nueva `fecha_entrega_proveedor` y corregir el mapeo (RN-1.3) |
| 9 | Cantidad ordenada | `cantidad_material` | Sí | — |
| 10 | Precio unitario | `valor_unitario`, **`double(22,2)`** | Sí | Ampliar los decimales: un precio calculado como total ÷ cantidad no cabe en 2 decimales sin perder precisión |
| 11 | Moneda | `PO.IdMoneda` (catálogo del CTC) | Sí | Código de Websimex (`monedas`, ISO) |
| 12 | Valor total | `valor_total` | Sí | Corregir el defecto de RN-1.13 |
| 13 | Valor total USD | `PO.vr_fob_usd`, **pero V2 guarda ahí el total en la moneda del documento** | No | Separar los dos valores |
| 14 | Incoterm | `PO.IdIncoterms` (catálogo del CTC, 18 filas) | Sí | Código de Websimex (`condiciones_entrega`) |
| 15 | Modalidad de transporte | `PO.codigo_medio_trans` (V1 y V2 no lo usan) | No | Ver M5 |
| 16 | País de origen | `PO.IdPaisOrigen` (catálogo del CTC por grupo, sin ISO) | No | Código de Websimex (`paises`) |
| 17 | País de procedencia | No existe | No | Columna nueva con el código de Websimex (`paises`) |
| 18 | Puerto / aeropuerto de origen | `PO.IdPuertoOrigen` | No | Código UN/LOCODE de Websimex (`ex_lugares_embar_arrib_inales`) y agregarlo al prompt |
| 19 | Condición de pago | No existe (campos dinámicos por cliente; la IA la manda a observaciones) | Parcial | Columna nueva |
| 20 | Comprador interno | No existe (campos dinámicos, ver M8) | No | Referencia al catálogo de compradores |
| 21 | Usuario solicitante | No existe | No | Columna nueva |
| 22 | Email del usuario | No existe | No | Columna nueva |
| 23 | Fecha estimada de despacho | `PO.fecha_etd` (diligenciada en 750 de 46.207 órdenes) | No (hoy la IA pone ahí `Delivery_Date`) | Columna obligatoria de la plantilla y agregarla al prompt (RN-1.3) |
| 24 | Fecha de la orden | `PO.fecha_po` | Sí (`Date`) | Columna obligatoria de la plantilla (agregada 21/10) |

#### M2 – Validación en Websimex y solicitud de clasificación

- **Tabla de referencia.** Es `comex.productos` (356.869 filas), con las columnas `CODIGO_PRODUCTO`, `ID_IMPORTADOR`, `APROBADO`, `ACTIVO`, `CODIGO_POSICION` y `REQUIERE_REGISTRO_IMPORTACION`.
  - El índice `(CODIGO_PRODUCTO, ID_IMPORTADOR)` no es único y hay ~12.700 combinaciones duplicadas.
  - Por eso "el producto existe" debe definirse como "hay al menos un registro aprobado y activo para ese importador".
- **Validación actual.**
  - Solo el comando `sync:detalle-ordencompra` hace la validación correcta: filtra por importador, aprobado y activo (`DetalleOrdenCompraCommand.php:46-83`). Corre únicamente para 10 grupos.
  - V2 valida contra `material` del CTC, no contra Websimex.
- **Hallazgo de aislamiento.**
  - `PurchaseOrderProductForm.php:59` y `app/Imports/ProductosImportacion.php:96` buscan en `productos` por código **sin filtrar `ID_IMPORTADOR`**.
  - Pueden mostrar el producto de otro cliente, y con él su estado de RIM. Se corrige en la Fase 0.
- **Solicitudes de clasificación.**
  - No existe ningún módulo, tabla ni export en `asercolg_po`, `asercolg_central` ni `asercolg_bitacora`.
  - Websimex tiene `solicitud_clasificacion` y `tarea_clasificacion_do`, pero están vacías y van por DO, no por producto. No se usan, y el CTC tampoco escribe en Websimex.
- **RN-2.6.** Verificar que el código creado exista es directo: se consulta `productos` por código e `ID_IMPORTADOR`.

#### M3 – Bolsa de clasificación

No existe, y exige algo que hoy ningún módulo del CTC hace: **una vista interna que reúne solicitudes de varios clientes**.

- La regla 1 de `CLAUDE.md` (todo filtra por `session('GrupoEmpresarial')`) no aplica tal cual.
- Hace falta un alcance "interno" explícito, resuelto en el servidor (decisión D2).
- **Decidido 21/10 (D1):** la bolsa vive en Central, que ya es la aplicación interna de ASERCOL, y sus datos en la base de CTI. El CTC solo crea solicitudes y muestra al In House las de su propio cliente.

Websimex ya sabe qué analista clasifica a qué cliente: `importadores.ID_EMPLEADO_CLASIFICADOR` y `empleados.ES_CLASIFICADOR`.

#### M4 – Alertas

- **Correo:**
  - Existe `App\Mail\AsercolMailing`, con la plantilla `emails.email` y los tipos genéricos 99 y 100.
  - Los envíos se registran en `correo_auditoria`; el patrón está en `Po/ProgramacionArribos.php:922-972`.
  - Con `QUEUE_CONNECTION=sync`, el envío corre dentro del request.
- **En pantalla:** no hay centro de notificaciones ni campana, solo toasts. `cti.notificaciones_internas` pertenece a otra aplicación.
- **Programadas:**
  - `Kernel.php` ya tiene el patrón de comandos que envían correo (por ejemplo `command:levantesindoc`).
  - En esos comandos los destinatarios están escritos en el código (`LevantesindocokCommand.php:45`). M8 resuelve eso para este flujo.
- **RIM:**
  - **Cómo se decide hoy "requiere RIM":** solo con `productos.REQUIERE_REGISTRO_IMPORTACION`, que está en NULL en el 98 % de los productos (348.313 de 356.869).
  - **Una señal mejor sin uso:** la subpartida trae `posicion_arancelaria.REQUIERE_REGISTRO` y `REGISTRO_IMPORTACION`, y ningún código del CTC las lee.
  - **"RIM aprobado" ya está resuelto** en `ConsultaSeguimientoRim::porProducto()`, con la condición `FECHA_APROBACION IS NOT NULL`.
  - **El trámite del RIM** vive en `cti.solicitud_registros_importacion` (4.526 filas) y se gestiona desde Central.
  - **Lo que falta:** no hay alerta de RIM por correo y el RIM no bloquea nada.
  - **Aclarado 21/10 (D5):** `productos.REQUIERE_REGISTRO_IMPORTACION` está vacío en los productos antiguos, pero ya se diligencia en los nuevos. Junto con el indicativo de la subpartida, alimenta la alerta, y el analista decide si crea la solicitud de RIM en el módulo de Central (RN-4.2).

#### M5 – Modalidad de transporte

- **Dato actual:** `PO.codigo_medio_trans char(2)` está diligenciado en 36.802 de 46.207 órdenes, por cargues legacy. V1 y V2 no lo leen ni lo escriben.
- **Catálogo de Websimex:** `medios_trasporte`, 11 filas, modelo `MedioTransporteWs`.
- **Los códigos no significan lo mismo** en el CTC y en Websimex: en el CTC `0` es NINGUNO y en Websimex `0` es CABOTAJE. Antes de tratar la columna como código de Websimex hay que verificar qué contiene.
- **No hay historial de cambios:**
  - `bitacora` solo la llenan triggers sobre `po_detalle`, y en actualización solo para el grupo 7.
  - Sobre la cabecera de `PO` no se registra nada.

#### M6 – Vistas en Central y Torre de Control

- **Lo que ya tiene V2:** paginación, filtros, chips y los parciales `_fila` y `_detalle`.
- **Lo que le falta a V2:**
  - Columnas: fecha de entrega del proveedor, fecha estimada de despacho, modalidad, estado de clasificación, RIM, VoBo y días de ANS.
  - El filtro de varias modalidades a la vez.
  - El orden por tres campos: fecha de entrega del proveedor (por defecto), fecha estimada de despacho y fecha de la orden.
- **Fecha de despacho:**
  - `estados_do.FECHA_DESPACHO` está vacía en el 100 % de las DO.
  - La fecha de despacho de la OC es `PO.fecha_etd` (despacho estimado), diligenciada hoy en solo 750 órdenes.
  - **Aclarado con operaciones (21/10):** la fecha de entrega del proveedor es otro dato, y va en un campo nuevo (`fecha_entrega_proveedor`).
- **Pedido:** la plantilla no lo trae (ver A.4).
- **Torre de Control:** hay dos versiones.
  - La legacy, `/rtorrecontrol`, son tarjetas armadas con SQL guardado en BD.
  - La nueva es el Dashboard V2, `/dashboard/v2`.
  - Ninguna tiene un punto de extensión para listados. La integración sería un componente nuevo dentro de `Dashboard/V2/Tablero`, o un enlace al módulo.
- **In House Expo:**
  - No existe en código, rutas ni menús.
  - **Aclarado 21/10 (RN-6.3):** el In House opera completamente desde el módulo de OC del CTC según su rol. No hace falta una vista aparte.
  - Exportaciones tampoco tiene una V2 de órdenes de compra.

#### M7 – Medición de tiempos y ANS

- **Calendario:**
  - En el CTC, `app/Support/CalendarioLaboral.php` (del Dashboard V2) calcula días hábiles. Sus festivos (`websystem.calendario`) se acaban el 31/12/2026.
  - **Actualizado 21/10:** el reloj del ANS vive en Central (RN-7.9), y `cia` ya tiene `app/Services/Fechas/DiasHabiles.php`.
    - Excluye fines de semana y calcula los festivos de Colombia por regla (Ley Emiliani), sin depender de ninguna tabla, así que no se vence.
    - Verificado: los 18 festivos de 2026 coinciden con el calendario oficial, y también calcula 2027.
  - Por eso el ANS usa `DiasHabiles` de `cia`. `CalendarioLaboral` no se lleva a Central ni se modifica.
  - El CTC no calcula el ANS: lee de la solicitud la fecha límite y los días consumidos que guarda Central.
- **Rangos del ANS:**
  - El catálogo de ANS del Dashboard (`indicadores`) mide sobre hitos del ERP, así que su motor no sirve para solicitudes.
  - **Decidido 21/10 (RN-7.8):** los rangos van en una tabla editable en la base de CTI, no en un archivo de configuración. El reloj vive en Central (RN-7.9).
- **Faltan:** los estados de la solicitud, las suspensiones y los indicadores.

#### M8 – Parametrización por cliente en Central

- **Central es otra aplicación:**
  - El proyecto sobre el que se ejecuta el plan es `cia` (Laravel 10, PHP 8.2), confirmado 21/10.
  - El CTC lee su base `asercolg_central` por la conexión `central` (14 modelos), pero no tiene ninguna pantalla de Central.
  - Todo M8, y la carga de OC para clientes sin CTC (RN-8.3), es trabajo en ese repositorio, o depende de decidir dónde vive (D1).
- **Destinatarios.** No hay parametrización por cliente. Lo que existe está disperso:
  - `cti.inhouse_cliente`, con 2 filas y administrado desde Central.
  - `mysql.responsable_comex`, con 38 filas.
  - `notificacion_etapas`.
  - Correos escritos en el código.
- **Roles.**
  - En el CTC no existen como rol: In House, Analista de Servicio al Cliente, Coordinador, Responsable de RIM ni Administrador de Central.
  - Lo más cercano:
    - en el CTC, el perfil 31 "Inhouse de Asercol" (3 usuarios) y el perfil 9 "Operativo Interno";
    - en CTI, los roles de `cti.roles` y `cti.cti_roles`: INHOUSE, COORDINADOR, DIR. SERVICIO AL CLIENTE.
  - El único indicador fiable de que un usuario es interno es `cti.usuarios.tipo_usuario`, al que se llega por `users.id_bitacora`.
  - `operacion = 'E'` es un permiso de edición, no un indicador de usuario interno.
- **Compradores (respuesta al pendiente abierto): el CTC no tiene tabla de compradores.**
  - Hoy el comprador es un campo dinámico por cliente, en `definicion_campos_po` + `adicionales_po`:
    - grupo 27, campo 363 "Nombre del Comprador", con 1.694 usos;
    - grupo 2, campos 366 y 380.
  - Solo guarda el nombre, sin correo. `comex.comprador` existe pero está vacía.
  - **Migración (RN-8.11):** los valores distintos de esos campos pasan al catálogo en estado "Por aprobar", sin correo. El In House completa el correo al aprobar.
  - Los reportes que leen `apo.valor_363` (`ReprteGlobalBrinsa.php:114`, `GlobalImportacion/Brinsa.php:245`) siguen funcionando mientras los dos esquemas convivan.

### A.3 Decisiones para la revisión del 22/10

| # | Decisión | Opciones | Recomendación |
|---|---|---|---|
| D1 | ¿Dónde vive el flujo de los clientes **sin** CTC (carga de OC y bolsa)? | (a) Reimplementarlo en Central. (b) Un solo motor en CTC, con un módulo interno al que Central enlaza | **(b).** RN-1.6 pide un solo validador, y dos repositorios con el mismo validador terminan desalineados. Central conserva la parametrización (M8). **Aprobado 21/10 para el ingreso de OC:** Central crea sus OC a través de una API del CTC y las consulta directamente (RN-1.21). Central ya tiene una conexión `ctc` a `asercolg_po` y modelos sobre `PO` y `po_detalle`. **Aprobado 21/10 para la bolsa:** vive en Central, con sus datos en la base de CTI (conexión `cti`, RN-3.1). Central y el CTC ya tienen conexión a esa base |
| D2 | ¿Qué solicitudes ven las vistas internas (bolsa y modo Central)? | Todos los clientes para cualquier usuario interno, o solo los importadores asignados al analista | **Aprobado 21/10 (RN-3.2):** las ve quien la generó, el In House, el analista, la Dirección de Servicio al Cliente y el rol designado para clasificar. Es parametrizable y no bloquea el inicio: se arranca con esta configuración |
| D3 | Las OC de clientes sin CTC no tienen `compania` ni `grupo_empresarial` | (a) Columna `PO.id_importador` (Websimex). (b) Un grupo empresarial "ASERCOL Central" con una compañía por importador | **Aprobado 21/10:** `PO.id_importador` va **siempre** lleno, como clave universal del cliente; con CTC se toma de `compania.id_websimex`. `id_grupo_empresarial` e `id_compania` ya admiten nulos, así que las OC de Central quedan fuera del alcance de cualquier CTC sin cambios en el aislamiento |
| D4 | ¿Cómo se agregan los campos nuevos a `PO` y `po_detalle`? | Columnas nuevas, o una tabla satélite | **Aprobado 21/10:** columnas nuevas en las tablas existentes, que admiten nulo. MariaDB 10.6 las agrega sin bloquear la tabla |
| D5 | ¿Cómo se determina "requiere RIM"? | Lo registra el analista, o lo deduce el sistema por subpartida | **Aprobado 21/10 (RN-4.2):** decide el analista. El sistema le muestra dos indicativos: el de la subpartida y el del producto (`productos.REQUIERE_REGISTRO_IMPORTACION`, que ya se diligencia en los productos nuevos). El analista diligencia la solicitud de RIM en el módulo que ya existe en Central, y esa solicitud se enlaza a la de clasificación |
| D6 | ¿Qué código se busca en Websimex: el código de producto o el código SAP? | — | **Aprobado 21/10: el código SAP**, contra `productos.CODIGO_PRODUCTO` del importador. El código de producto queda como dato informativo. Si no viene SAP, se busca la descripción corta (RN-1.2). Ver el riesgo R12 |
| D7 | Las listas salen de Websimex, pero `PO` guarda ids del CTC | Homologar por código, o guardar el código de Websimex | **Aprobado 21/10: guardar el código de Websimex** (RN-1.20). Los ids `Id*` del CTC se llenan cuando hay equivalencia, por compatibilidad. Si no la hay quedan nulos y la carga **no** se rechaza |
| D8 | ¿Dónde se guardan las OC de CTC y de Central? | Una tabla, dos tablas en la misma BD, o dos BD | **Aprobado 21/10: una sola tabla** (`PO` / `po_detalle`), con `id_importador` y `sistema_origen` (RN-1.21). Con dos tablas o dos BD, cada consulta de Central necesitaría un `UNION` y los esquemas se desalinearían |
| D9 | ¿De dónde sale la lista de proveedores? | Maestro `Supplier` del CTC, o `exportadores` de Websimex | **Aprobado 21/10: `exportadores` de Websimex.** El 64 % del maestro (8.630 de 13.406) no tiene importador asociado, porque operaciones los crea globales. La lista por cliente se arma con RN-1.19. En las facturas hay 14.386 pares importador–exportador en 1.670 importadores |
| D10 | ¿Dónde se guardan los datos del producto de la OC (códigos, descripción, estado frente a Websimex, solicitud)? | En la línea (`po_detalle`), en el maestro (`material`), o en una tabla material × importador | **Decidido 22/10: en `material`** (RN-1.23). `po_detalle` guarda solo lo propio de la línea. Como Websimex valida por importador y 10 grupos tienen más de un importador activo (el 45 tiene 35), cada material queda asociado a un importador con `material.id_importador` |

### A.4 Vacíos e inconsistencias del spec

1. ~~**Qué código se valida en Websimex.**~~ **Resuelto 21/10 (D6):** se busca el código SAP. Si la línea trae código de producto pero no SAP, queda "No Codificado".
2. ~~**"Pedido" no está en la plantilla.**~~ **Resuelto 21/10 (RN-2.8):** el pedido no viene en la plantilla porque lo genera el sistema, como año + mes + consecutivo (por ejemplo, `20261001`).
3. ~~**El puerto de origen como "código de 3 letras".**~~ **Resuelto 21/10:** se usa el catálogo de puertos internacionales de Websimex (`ex_lugares_embar_arrib_inales`, códigos UN/LOCODE de 5 caracteres, por ejemplo `CNSGP`).
4. ~~**Qué cuenta como "ítem" para el ANS (RN-2.1).**~~ **Resuelto por RN-1.1 (21/10):** como la línea es PO + material, cada línea es un producto distinto y el ítem es la línea.
5. ~~**Una OC cargada en dos tandas.**~~ **Resuelto 21/10 (RN-2.1):** el proceso que ya arrancó no se reinicia. Se alerta visualmente al In House de la adición, y los productos agregados que no existen en Websimex arrancan una solicitud nueva, con sus propios estados y mediciones.
6. ~~**El alcance de In House Expo no está escrito.**~~ **Resuelto 21/10 (RN-6.3):** el In House opera completamente desde el módulo de OC del CTC, con el alcance de su rol.
7. ~~**Coordinador y Responsable de RIM no están en la parametrización.**~~ **Resuelto 21/10:** el Responsable de RIM corresponde al rol 47 de CTI, "CORDINADOR REGISTROS DE IMPORTACION" (`cti.roles`). Los destinatarios son los usuarios activos con ese rol en `cti.usuarios`: hoy son 4, todos con correo. No se parametriza en M8.
8. ~~**¿La alerta de producto nuevo también va por correo?**~~ **Resuelto 21/10:** sí. Va por correo; los destinatarios dependen del sistema de origen (punto 15).
9. ~~**Qué significa "Documentos completos" (RN-2.2).**~~ **Resuelto 21/10:** los documentos requeridos los define el analista para cada producto, según su requerimiento específico.
10. ~~**Dónde diligencia la ficha el cliente o el proveedor.**~~ **Resuelto 21/10 (RN-2.9):** el In House o el cliente diligencia la ficha en CTC. El proveedor responde por correo y el analista adjunta la respuesta.
11. ~~**Proveedor que no existe en Websimex.**~~ **Resuelto 21/10 (RN-1.22):** la OC se crea con la observación "Proveedor por crear" y se alerta al Analista de Servicio al Cliente. No bloquea el VoBo.
12. ~~**Varias líneas "No Codificado" en una misma PO.**~~ **Resuelto 21/10 (RN-1.2):** sin SAP, la descripción corta hace de código. Cada descripción distinta es un material distinto, así que las líneas no se fusionan por error.
13. ~~**Largo de la descripción usada como código.**~~ **Resuelto 21/10 (RN-1.2):** se compara quitando los espacios sobrantes, y una descripción de más de 50 caracteres genera un aviso en la validación.
14. ~~**Formato del pedido (RN-2.8).**~~ **Resuelto 21/10:** se genera uno por OC, con una implementación nueva.
    - **Mecanismo actual:** `PO.pedido` ya existe y está lleno en 50.861 de 54.954 órdenes. Lo generan los importadores legacy (por ejemplo, `JPoImport.php:64-72`) así:
      - año + id del grupo empresarial + consecutivo, con `MAX(pedido) + 1` por grupo;
      - por ejemplo, `202680739` es 2026 + grupo 8 + 0739.
    - **Problemas del mecanismo actual:**
      - `MAX + 1` no es seguro con cargas simultáneas;
      - los números de distintos grupos se pisan cuando el consecutivo crece (el grupo 1 llega a `202620001`, que es donde arranca el grupo 2);
      - ya hay unos 600 valores repetidos (50.246 distintos), así que no se puede poner un índice `UNIQUE` sobre la columna.
    - **Formato aprobado (21/10):** `AAAAMM` + 5 dígitos, 11 caracteres en total. Por ejemplo, `20261000001` es la primera OC de octubre de 2026.
      - El consecutivo se reinicia cada mes.
      - Los valores legacy tienen 9 o 10 caracteres, así que por largo nunca pueden coincidir con los nuevos.
15. ~~**Analista encargado del pedido.**~~ **Resuelto 21/10:**
    - si la OC entra por CTC, la alerta de producto nuevo va al In House y a la Dirección de Servicio al Cliente;
    - si entra por Central, va al analista que la cargó y a la Dirección de Servicio al Cliente.
16. ~~**Material repetido entre compañías del mismo grupo (RN-1.23).**~~ **Resuelto 22/10, opción (a):** el índice único de `material` pasa de `(id_grupo_empresarial, codigo, nombre)` a `(id_grupo_empresarial, id_importador, codigo, nombre)`. Así, un mismo producto puede existir una vez por cada compañía del grupo.
    - MariaDB no compara los nulos en un índice único. Por eso los materiales con `id_importador` nulo (todos los anteriores, salvo los del grupo 27) quedan sin protección en la base.
    - La primera capa contra duplicados es la validación de la aplicación (RN-1.23), no la base.
    - Hoy hay 809 pares grupo + código repetidos con nombres distintos (medido 22/10). No se corrigen de forma masiva: el formulario los señala cuando se edita uno de ellos.

### A.5 Modelo de datos propuesto

Todo el DDL se entrega como scripts en `specs/sql/`, con el estilo vigente:

- idempotente;
- con un paso 0 de diagnóstico;
- con verificación al final;
- con un bloque de reversión.

No se ejecuta `migrate`.

**`asercolg_po` (CTC)**

| Objeto | Cambio |
|---|---|
| `PO` | Columnas nuevas: `id_importador` (siempre lleno, D3), `sistema_origen` (`CTC` / `CENTRAL`), `canal_origen` (`IA` / `MANUAL` / `EXCEL`, según el medio de creación, RN-1.9), `ruta_archivo_origen` (clave del objeto privado en S3: Excel importado o documento leído por la IA; vacía en el canal Manual). Se guarda la clave y no una URL, porque la URL firmada se genera cada vez que se abre el archivo, `fecha_entrega_proveedor` (date, RN-1.3), `condicion_pago`, `id_comprador`, `comprador_nombre` (nombre recibido mientras no exista el catálogo de Central; agregado 27/10), `usuario_solicitante` y `email_solicitante`. `proveedor_por_crear` (nombre recibido cuando el proveedor no existe en Websimex, RN-1.22; se limpia al asociar `id_exportador`). Columnas nuevas con códigos de Websimex (RN-1.20): `id_exportador`, `codigo_pais_origen`, `codigo_pais_procedencia`, `codigo_moneda`, `codigo_incoterm` y `codigo_puerto_origen` (UN/LOCODE). La columna `pedido` ya existe; cambia la forma en que se genera (RN-2.8). La modalidad usa la columna existente `codigo_medio_trans`, ahora con códigos de Websimex. `IdSupplier`, `IdPaisOrigen`, `IdMoneda` e `IdIncoterms` se conservan y se llenan solo si hay equivalencia. `vr_fob_usd` pasa a guardar solo valores en USD |
| `po_detalle` | Solo lo propio de la línea. Columna nueva: `precio_calculado` (tinyint, RN-1.13). `valor_unitario` pasa a `decimal(20,6)`. `posicion_pedido` ya existe y empieza a llenarse (RN-1.1). Los datos del producto van en `material` (RN-1.23) |
| `material` | Datos del producto de la OC (RN-1.23, decidido 22/10). Columnas nuevas: `estado_producto` (`EXISTE` / `NUEVO` / `NO_CODIFICADO`, evaluado contra los productos de Websimex del `id_importador`) e `id_solicitud_clasificacion` (última solicitud del material). Columnas que ya existen y se usan: `codigo_sap`, `codigo` (código producto tal como llegó), `nombre` (descripción corta) e `id_importador` (`compania.id_websimex`, hasta hoy lleno solo en el grupo 27). Los materiales existentes quedan con estado nulo: no se evalúan hacia atrás. El índice único `Índice 2` `(id_grupo_empresarial, codigo, nombre)` se reemplaza por `uk_material_importador` `(id_grupo_empresarial, id_importador, codigo, nombre)` (A.4 #16) |
| `po_modalidad_historial` | Tabla nueva: OC, valor anterior, valor nuevo, motivo (de una lista), observación, usuario y fecha. Solo admite `INSERT` (RN-5.2) |
| `po_motivo_cambio_modalidad` | Tabla nueva: lista corta de motivos de cambio de modalidad (RN-5.2) |
| `pedido_consecutivo` | Tabla nueva: contador por año + mes, global para todos los clientes. Se lee y se incrementa dentro de una transacción con bloqueo de fila (`SELECT … FOR UPDATE`), lo que garantiza que no haya colisiones (RN-2.8). No se puede poner un índice `UNIQUE` sobre `PO.pedido` porque los datos legacy ya traen unos 600 valores repetidos |
| `documentos` | Sin cambios. Los adjuntos de la ficha se guardan con un `id_tipo_documento` nuevo |

**`asercolg_bitacora` (CTI, conexión `cti`): solicitudes de clasificación (D1, RN-3.1)**

La bolsa vive en Central, pero sus datos se guardan aquí. El CTC (al cargar la OC y al diligenciar la ficha) y Central (bolsa) leen y escriben las mismas tablas. Las dos aplicaciones ya tienen conexión `cti`.

| Objeto | Cambio |
|---|---|
| `clasificacion_solicitud` | Tabla nueva, cabecera de la solicitud: OC (`PO.id`), pedido, importador, grupo, sistema de origen, estado, cantidad de ítems, plazo del ANS, fecha límite y días hábiles consumidos (los calcula `cia`), ampliación con su justificación, fechas de cada tramo, quién la generó y analista |
| `clasificacion_item` | Tabla nueva, ítem de la solicitud. Cada ítem es un material (`material.id`), no una línea de la OC (RN-1.23): código, texto breve (copias de `material` para listar sin cruzar bases), ficha estructurada (10 campos), subpartida sugerida, requisito, indicativos de RIM de la subpartida y del producto, enlace a la solicitud de RIM (`solicitud_registros_importacion`, RN-4.2), código creado en Websimex y VoBo |
| `clasificacion_tipo_documento` | Tabla nueva: catálogo de tipos de documento que el analista puede exigir en una solicitud (RN-2.2). Ejemplos: ficha técnica, foto del producto, foto de la máquina |
| `clasificacion_documento_requerido` | Tabla nueva: los documentos que el analista exige para cada ítem (RN-2.2), con el tipo de documento, si ya está cargado, la clave del archivo privado en S3, quién lo definió y la fecha |
| `clasificacion_estado_historial` | Tabla nueva: cada cambio de estado con estado anterior, estado nuevo, usuario y fecha. Solo admite `INSERT` |
| `clasificacion_suspension` | Tabla nueva con todos los datos de RN-7.6 |
| `clasificacion_ans_rango` | Tabla nueva: rangos de ítems y días hábiles del ANS, editable (RN-7.8). Se carga con la tabla del M7 |
| `clasificacion_ampliacion` | Tabla nueva: ampliaciones de plazo para 16 ítems o más, con días adicionales, justificación, quién la pidió y quién la autorizó (Dirección de Servicio al Cliente, RN-7.3) |
| `clasificacion_visibilidad` | Tabla nueva: quién ve las solicitudes (RN-3.2). Cada fila indica un tipo (creador, In House, analista, Dirección de Servicio al Cliente, rol de CTI o usuario) y su valor. Se carga con la configuración inicial |
| `ctc_alerta` | Tabla nueva para las alertas en pantalla: tipo, entidad, destinatario y si ya se leyó. Va en esta base porque la leen tanto el CTC (In House) como Central (analistas) |

**`asercolg_central` (Central, administrada desde su repositorio)**

| Objeto | Contenido |
|---|---|
| `clasificacion_param_cliente` | Importador, si usa CTC, copias adicionales y estado. El In House sale de `cti.inhouse_cliente` (existente), el Analista de Servicio al Cliente de `estados_do.ID_EMPLEADO_OP`, y la Dirección de Servicio al Cliente de `alerta_destinatario` (M8) |
| `comprador_interno` | Importador, nombre, correo, área y estado (Activo, Inactivo o Por aprobar) |
| `clasificacion_param_auditoria` | Los cambios que exige RN-8.5 |
| `alerta_destinatario` | Destinatarios configurables por tipo de alerta (RN-4.1). Cada fila indica el tipo de destinatario (usuario, rol de CTI o correo) y su valor. Se carga con la configuración inicial del M4 y la edita el administrador en Central |

Los historiales "no editables ni borrables" (RN-5.3, RN-7.7) se garantizan en la aplicación, que no tiene métodos para editarlos. Si se aprueba, se refuerza en BD con un usuario sin permisos `UPDATE` ni `DELETE` sobre esas tablas.

### A.6 Plan de trabajo

**Estado al 28/10/2026 (punto de retoma)**

- **Rama:** el 28/10 `feature/oc-clasificacion` (que incluye `feature/layout-tailwind`) se fusionó en `main`; punto de regreso: tag `main-antes-migracion-2026-10-28`. La tarea 1.7 va en `feature/oc-modalidad`, desde `main`. Corre sobre la migración: Laravel 10, PHP 8.3, Livewire 3 y el layout nuevo. La restricción de PHP 7.3 ya no aplica a este módulo, que se despliega con el servidor nuevo. Las pantallas nuevas usan el kit ui-ctc.
- **Commits del módulo:**
  - `fa339ba6`: Fase 0 y tareas 1.1–1.2.
  - `60c32980`: tarea 1.3.
  - `fc22d4fd`: tarea 1.8.
  - `131e7a10`: tareas 1.4 y 1.5.
  - `b3f73511`: tarea 1.6.
- **Hecho:**
  - Fase 0, salvo 0.7.
  - Fase 1 completa (1.1 a 1.8).
- **DDL (tarea 0.3), verificado el 28/10 con consultas de solo lectura:**
  - `oc_clasificacion_01_ctc.sql` aplicado, salvo el paso 2b: `po_detalle.valor_unitario` sigue en `DOUBLE(22,2)`.
  - El paso 3b se aplicó aparte, el mismo 28/10: existe `uk_material_importador` y ya no está `Índice 2`. El grupo 2 tiene dos compañías activas (1334 y 276), así que se puede probar un mismo material en las dos.
  - Sin el 2b, el precio unitario calculado (total ÷ cantidad) se guarda redondeado a 2 decimales. El valor total no se afecta.
  - `_02_cti.sql` y `_03_central.sql` no están aplicados. Bloquean la Fase 2 desde la tarea 2.2.
- **Pendiente de prueba (después del DDL, con el grupo 2):**
  - Carga real de la plantilla: crear las OC y volver a cargar el mismo archivo. Las líneas deben quedar iguales, no sumadas.
  - Subida a S3 y apertura por URL firmada (0.8).
  - Revisión visual de la plantilla en Excel de escritorio, en particular el desplegable de 82.043 puertos.
  - Formulario V2 (tarea 1.6): crear una OC a mano y otra desde un documento con IA, guardar dos veces y editar una OC anterior a la 1.6. Aplicar antes `oc_clasificacion_04_prompt_ia.sql`.
  - Modalidad (tarea 1.7): cambiarla con motivo en una OC guardada y ver el historial; volver a cargar por Excel esa OC con otra modalidad (debe conservarla y avisar).
- **Siguiente:** Fase 2, desde la tarea 2.1.
- **Decisiones del 27/10:**
  - `material.codigo` es el código de búsqueda: el SAP o, si no viene, la descripción (RN-1.23 corregida).
  - Columna nueva `PO.comprador_nombre` en el DDL, para guardar el comprador como texto hasta que exista el catálogo de Central (tarea 4.4).

Las fases se pueden desplegar por separado. Las estimaciones son días de desarrollo de una persona y no incluyen la validación del negocio. En las fases que tocan los dos repositorios se indica cuánto corresponde a cada uno.

**Fase 0 – Cimientos y correcciones (≈ 3 días, en el CTC)**

| # | Tarea | Depende de |
|---|---|---|
| 0.1 | ~~Cerrar las decisiones de A.3 y los vacíos de A.4~~ **Hecho 20–21/10** | — |
| 0.2 | **Avance 22/10:** `PO.codigo_medio_trans` ya usa los códigos de `medios_trasporte` de Websimex: `1` (23.313), vacío (15.329), `4` (8.483), `0` (6.353), `3` (1.481), `2` (3) y `9` (2). **Resuelto 22/10:** el `0` es el valor por defecto "NINGUNO" del catálogo del CTC (`asercolg_po.medios_trasporte`), no CABOTAJE. En el CTC, CABOTAJE es `98`, y en Websimex es `0`. Los códigos `1` a `9` y `99` coinciden en los dos catálogos. Diagnóstico de solo lectura: qué contiene realmente `PO.codigo_medio_trans`, solo para documentarlo, porque los históricos no se convierten (RN-5.5). Además, en cuántos clientes con CTC no hay equivalencia para las columnas `Id*` de compatibilidad | — |
| 0.3 | **Hecho 21/10:** `specs/sql/oc_clasificacion_01_ctc.sql`, `_02_cti.sql` y `_03_central.sql`. El 27/10 se agregó al script 01 la columna `PO.comprador_nombre`. **El 01 está aplicado salvo el paso 2b; faltan el 02 y el 03 (28/10, ver el estado arriba).** Escribir los scripts DDL (`specs/sql/oc_clasificacion_*.sql`) y que el DBA los aplique | 0.1 |
| 0.4 | ~~Calendario de festivos~~ **No aplica (21/10):** el ANS se calcula en `cia` con `DiasHabiles`, que calcula los festivos por regla y no se vence. `CalendarioLaboral` del CTC no se toca | — |
| 0.5 | **Hecho 21/10.** Corregir el aislamiento: filtrar por `ID_IMPORTADOR` en `PurchaseOrderProductForm.php:59` y `ProductosImportacion.php:96` | — |
| 0.6 | **Hecho 21/10.** Corregir el defecto de `valor_total` en el importador masivo. Cada valor va en su campo y, si falta el precio unitario, se calcula como total ÷ cantidad (RN-1.13). Las líneas históricas no se corrigen: no se puede saber si su `valor_total` es un precio o un total | — |
| 0.7 | Designar el rol de CTI para clasificar (`cti.roles`) y cargar la configuración inicial de visibilidad (`clasificacion_visibilidad`, RN-3.2). No bloquea el inicio: la configuración se ajusta después | 0.1 |
| 0.8 | **Hecho 21/10:** `app/Services/OrdenCompra/ArchivosOc.php`, `ArchivoOrdenCompraController` y ruta `oc-archivo-origen`. Desde el 27/10 lo usa el importador v2 (1.4) para guardar el Excel. Pendiente probar contra S3. `app/Services/OrdenCompra/ArchivosOc`: subida privada a S3 y URL firmada con `Storage::disk('s3')` (`visibility = private` y `temporaryUrl()`; `league/flysystem-aws-s3-v3` ya está instalado). Ruta del CTC con `auth` que verifica que el archivo sea del tenant del usuario antes de firmar. `FileAWS` **no se modifica**, porque lo usa toda la aplicación | — |

**Fase 1 – Ingreso unificado: M1 + M5 (≈ 9 días, en el CTC)**

| # | Tarea | Depende de |
|---|---|---|
| 1.1 | **Hecho 21/10:** `ValidadorLineaOc`, `ResultadoValidacionOc` y la interfaz `FuenteCatalogosOc`, probados con catálogos en memoria. Falta la fuente real de Websimex (1.2). `app/Services/OrdenCompra/ValidadorLineaOc`: núcleo de reglas de negocio común a los tres canales (IA, Manual y Excel) (RN-1.1 a RN-1.5, RN-1.13 y RN-1.18: estructura, listas, producto, fechas y valores). Cada canal suma encima sus validaciones propias (RN-1.8 aclarada) | 0.3 |
| 1.2 | **Hecho 22/10:** `CatalogosOc` (implementa `FuenteCatalogosOc` y entrega las listas de la plantilla), `EquivalenciasCtcOc` (ids de compatibilidad, filtrados por grupo) y `ProveedorAmbiguo`. Probados con lecturas contra Websimex y el CTC reales. Hallazgos que fijan las reglas:<br>• `monedas.ACTIVO` no sirve de filtro: USD, EUR y CNY lo tienen nulo. Valen los códigos ISO de 3 letras (169).<br>• `paises.CODIGO_PAIS` es el DIAN; el ISO de 2 letras está en `CODIGO_SIGLOXXI`. Para los ISO repetidos (BE, TW) gana el DIAN de 3 dígitos, que es el que usan las facturas.<br>• En puertos solo valen los UN/LOCODE de 5 caracteres; los más cortos son departamentos.<br>• Los nombres de exportador se repiten, incluso dentro de la lista de un mismo cliente. Por eso el proveedor se identifica por `ID_EXPORTADOR`: un nombre que corresponde a varios exportadores es error, no "Proveedor por crear".<br>• `Supplier.codigo` no es el ID de Websimex: `IdSupplier` se homologa por nombre. `IdPaisOrigen` exige que coincidan código y nombre.<br>• La lista de proveedores del cliente (RN-1.19) tarda ~1 s y se consulta una vez por carga.<br>Formato de las opciones de la plantilla: `CODIGO - NOMBRE`; la carga acepta también el código solo. Texto original de la tarea: `CatalogosOc`: listas consultadas en línea desde Websimex (`medios_trasporte`, `condiciones_entrega`, `monedas`, `paises`, puertos) y proveedores del cliente según RN-1.19 (`exportadores` + `cabeza_facturas` + `productos`). Devuelve códigos de Websimex y, si existe, el id equivalente del CTC para las columnas de compatibilidad | 0.2 |
| 1.3 | **Hecho 27/10:** `PlantillaOc` (versión `CTC-OC-2` en la propiedad `ctc_plantilla_oc` del libro; `PlantillaOc::encabezados()` es la lista que verifica el importador), `CatalogosOc::listaPuertos()`, `PlantillaOrdenCompraController` y ruta `oc-plantilla` (`impo/oc/plantilla`). Hojas Plantilla, Instrucciones y Listas (oculta); 2.000 filas; una validación por columna. Proveedor y puerto admiten valores fuera de la lista, con advertencia; el comprador queda como texto libre hasta la tarea 4.4. Con el grupo 2 se genera en ~6 s, con 190 MB de pico y 1,4 MB de archivo. El modo Central (`generar($clientes, true)`) precarga y bloquea el cliente. Falta enlazar la descarga desde la pantalla de carga v2 (1.4). **Decidido 22/10:** la lista de puertos (~82.000 UN/LOCODE, `CatalogosOc`) va completa en una hoja oculta de la plantilla, y la columna se valida contra ella. Las opciones de todas las listas usan el formato `CODIGO - NOMBRE` (tarea 1.2). Plantilla v2: 24 columnas, versión en las propiedades del libro, hoja de instrucciones, encabezados protegidos, columnas de código como texto y las listas de 1.2. La lista de proveedores admite escribir uno del maestro global, y la carga lo valida. En modo Central, el cliente va precargado y bloqueado | 1.2 |
| 1.4 | **Hecho 27/10 (sin probar la escritura: falta el DDL):** `ImportadorOc` (lectura de la hoja Plantilla sin cargar la de puertos, versión y encabezados, cliente por fila entre las compañías del grupo, validación por cliente, plan sin escrituras y luego S3 + transacción) y `MaterialesOc::registrar()`. La línea existente se reemplaza, no se suma: cargar dos veces el mismo archivo deja el mismo resultado. Una OC existente de otra compañía del grupo es error. `pedido` no se genera aquí (RN-2.8, tarea 2.3) ni el estado del producto (2.2). Probado en seco con el grupo 2: consolidación, línea sin SAP, proveedor por crear, cliente ajeno, OC con dos clientes, versión anterior, columnas alteradas y material sin compañía. Importador v2: una línea por PO + material, que es la llave de actualización. Si el material se repite, consolida sumando cantidades y valores totales y recalcula el precio unitario. Guarda la menor posición, que se usa para ordenar. Conserva la creación o reactivación del material en el maestro del CTC (RN-1.2, confirmado 20/10), y guarda en él el código SAP, el código producto y la descripción corta (`codigo_sap`, `codigo`, `nombre`). Llena `material.id_importador` con el `compania.id_websimex` de la compañía de la fila, y busca el material por grupo + importador + código. Si la plantilla trae dos compañías del mismo grupo, cada fila usa la suya. Antes de crear un material aplica la misma verificación de duplicados que el formulario (RN-1.23). Si encuentra un material anterior sin importador, del mismo grupo y código, lo reutiliza y le completa el `id_importador`, solo si el grupo tiene una sola compañía. Si tiene varias, reporta el error en la fila (RN-1.23). Sube el Excel como privado con `ArchivosOc` antes de procesarlo, en lugar de borrarlo, y escribe su clave en `ruta_archivo_origen` de cada OC creada, con `canal_origen = EXCEL`. Los canales IA y Manual registran su canal desde el formulario V2. El documento de la IA pasa también a `ArchivosOc`, y el visor del formulario V2 deja de usar la URL pública para usar la firmada | 1.1, 0.8 |
| 1.5 | **Hecho 27/10 (Excel):** componente `Importaciones\OrdenCompraCargaExcel` en la V2 (diálogo del kit ui-ctc, descarga de la plantilla, resumen y tablas de errores y avisos por fila; misma condición que el botón: no tercero y `operacion = E`). La parte de la IA se hizo con la 1.6. Reporte de errores del Excel por fila, con columna, valor recibido y motivo. El cotejo de la IA se mantiene en el formulario V2 y se extiende con los campos nuevos (RN-1.8 aclarada: cada canal tiene su propia pantalla) | 1.1 |
| 1.6 | **Hecho 28/10 (sin probar la escritura ni la lectura real):** el formulario V2 es el canal IA y el Manual, y produce el mismo registro que la carga Excel (RN-1.6). Campos de la plantilla CTC-OC-2 con los catálogos de Websimex (`CatalogosOc`); el puerto se escribe por código UN/LOCODE o se busca por nombre. Valida con `ValidadorLineaOc` y escribe con `RegistroOc`, la escritura que se separó del importador v2 y que ahora usan los dos. Diferencias propias del canal: no sobrescribe una OC por coincidencia de número (hay que abrirla), y al guardar inactiva las líneas que ya no están en el formulario. `canal_origen` = `IA` si hubo lectura, `MANUAL` si no. El documento se sube privado con `ArchivosOc` (`documentos.medio = S3-PRIVADO`, `ruta` = clave) y se abre por la ruta nueva `oc-documento` o por URL firmada; los documentos públicos anteriores siguen abriendo. Lo que la IA lee se traduce a códigos de Websimex; lo que no se traduce queda vacío y marcado para revisar. Las fechas solo se marcan para revisar si son ambiguas. La línea guarda la unidad (`po_detalle.unidad_medida`) y la posición. Un material que no existe se crea en el maestro al guardar, como en el Excel. Editar una OC anterior a la 1.6 exige completar los campos obligatorios nuevos. Prompt nuevo en `specs/sql/oc_clasificacion_04_prompt_ia.sql` (lo aplica el DBA; el código tolera los dos prompts). Corrección de paso en el validador: sin SAP, el código de búsqueda se recorta a 50 caracteres (largo de `material.codigo` y de Websimex), porque con `strict = false` la base lo truncaba y la siguiente carga no encontraba el material. Texto original: IA: agregar al prompt de `tipo_documento_global` los campos faltantes (posición, país de origen y de procedencia, puerto, modalidad, código SAP). Separar las dos fechas (RN-1.3): `Delivery_Date` pasa a `fecha_entrega_proveedor`, y se agrega al prompt la fecha estimada de despacho, que va a `fecha_etd`. Se quita la marca `revisar` fija: las fechas solo se marcan `revisar` si son ambiguas (confirmado 21/10). Guardar la unidad y la descripción de cada línea | 1.1 |
| 1.7 | **Hecho 28/10 (sin probar la escritura):** `ModalidadOc` (cambio con motivo de la lista y observación, en transacción, que inserta en `po_modalidad_historial` con `sistema_origen = CTC`; historial en orden cronológico; sin métodos para editarlo ni borrarlo) y los modelos `PoModalidadHistorial` y `PoMotivoCambioModalidad`. En el formulario V2, la modalidad de una OC guardada queda bloqueada y se cambia con "Cambiar"; el servidor rechaza cambiarla por la vía de guardar aunque se manipule el campo. El historial se ve en la misma página. **Decidido 28/10:** la cambia quien puede editar la OC (`operacion = E` y no tercero), no un rol aparte. Una OC con código legacy o vacío no tiene modalidad vigente (RN-5.5): el campo queda libre para completarla, sin historial. La carga Excel de una OC existente conserva su modalidad vigente y avisa en la fila (decidido 28/10). El cambio desde Central (analista) llega con la API de la tarea 4.6, con `sistema_origen = CENTRAL`. Texto original: Modalidad: edición por el In House (CTC) o el analista (Central), con motivo de lista y observación, registro en `po_modalidad_historial` y vista del historial (RN-5.2, RN-5.4). Sin conversión de valores históricos (RN-5.5) | 0.3 |
| 1.8 | **Hecho 27/10:** regla de duplicados en `app/Services/OrdenCompra/MaterialesOc::duplicado()` (la usa también el importador v2; desde la 1.4 lee el maestro del grupo una vez y aplica la regla en memoria). `ProductosImpoProcesar` carga y guarda el material filtrado por grupo (otro grupo → 404), asigna `id_grupo_empresarial` solo al crear, pide la compañía si hay más de una (y la verifica contra el grupo al guardar), llena `id_importador` y deja `idProducto`, `idGrupoEmpresarial` e `idUser` con `#[Locked]`. Probado con `Livewire::test` sobre el grupo 2, sin escrituras. Formulario de materiales: el componente Livewire `Configuraciones\ProductosImpoProcesar` (vista `livewire/configuraciones/productos-impo-procesar`), dentro de `/config/productos` (`productos-importaciones`). `MaterialController` ya no se usa para crear materiales y no se toca. El componente debe:<br>• exigir la compañía dueña del producto cuando el grupo tiene más de una compañía activa, y guardar su `id_websimex` en `material.id_importador`. Con una sola compañía, la toma sin preguntar. La lista sale de `compania` del grupo con `inactivo = 0`;<br>• antes de `save()`, al crear y al editar, rechazar duplicados con la regla de RN-1.23 (al editar se excluye el propio registro). El mensaje de validación indica el material existente, en lugar de dejar que la base responda con un error de índice. Hoy el componente solo valida largo mínimo de `codigo` y `nombre`;<br>• **corregir el aislamiento (CLAUDE.md §1):** `mount()` carga el material con `Materiales::where('id', $idProducto)` sin filtrar por `id_grupo_empresarial`, y `save()` sobrescribe `id_grupo_empresarial` con el grupo de la sesión. Con el id de otro cliente, un usuario puede ver ese material y, al guardar, pasarlo a su propio grupo. El material debe cargarse filtrado por el grupo de la sesión, y `id_grupo_empresarial` solo se asigna al crear | 0.3 |

**Fase 2 – Validación en Websimex, solicitud y bolsa: M2 + M3 (≈ 10 días: 4 en el CTC para 2.1 a 2.3, y 6 en Central para 2.4 a 2.7)**

| # | Tarea | Depende de |
|---|---|---|
| 2.1 | `ProductoWebsimex::existe($codigo, $idImportador)`: busca en `productos.CODIGO_PRODUCTO` del importador, aprobado y activo, solo lectura. El código es el SAP o, si no viene, la descripción corta (D6, RN-1.2) | — |
| 2.2 | Al guardar la OC, marcar cada material como `EXISTE`, `NUEVO` o `NO_CODIFICADO` (`material.estado_producto`, contra los productos de Websimex de su `id_importador`, RN-1.23) y crear en la base de CTI la solicitud agrupada por OC (RN-2.1, RN-3.1). Cada ítem es un material, que guarda la solicitud en `material.id_solicitud_clasificacion`. Un material que ya tiene una solicitud abierta no entra en otra (confirmado 22/10). Si la OC ya tenía un proceso en curso, no se toca: los productos agregados sin producto en Websimex crean una solicitud nueva, y se registra la alerta de adición para el In House | 1.4, 2.1 |
| 2.3 | Ficha estructurada con adjuntos privados (`ArchivosOc` y `documentos`). El analista define los documentos requeridos por producto, y la solicitud pasa a "Documentos completos" cuando están todos (RN-2.2, RN-2.3). El analista adjunta las respuestas que el proveedor envía por correo (RN-2.9). Generación del consecutivo de pedido (RN-2.8) | 2.2, 0.8 |
| 2.4 | En Central: bolsa con filtros, toma de solicitudes, máquina de estados con historial y visibilidad según `clasificacion_visibilidad` (RN-3.2). En el CTC: vista de solicitudes del propio cliente para el In House | 2.2, 0.7 |
| 2.5 | En Central: registro de subpartida y requisito. Muestra los dos indicativos de RIM (subpartida y producto) y permite crear la solicitud de RIM en el módulo existente (`solicitud_registros_importacion`), que queda enlazada al ítem (RN-4.2) | 2.4 |
| 2.6 | "Producto creado": verificar el código en Websimex y asociarlo al material (`id_material_websimex`, `subpartida`, `estado_producto = EXISTE`). Todas las OC que tienen el material lo ven a través de `po_detalle.id_material` (RN-2.6, RN-2.7, RN-1.23) | 2.1, 2.4 |
| 2.7 | Exportar en el formato "Informe búsqueda de productos por importador" (RN-2.4) | 2.2 |

**Fase 3 – ANS y alertas: M7 + M4 (≈ 8 días: 5 en Central para 3.1, 3.2, 3.4 y 3.5, y 3 en el CTC para 3.3 y 3.6)**

| # | Tarea | Depende de |
|---|---|---|
| 3.1 | En `cia`: clase `RelojAns` sobre `App\Services\Fechas\DiasHabiles`, que descuenta las suspensiones. Guarda en la solicitud la fecha límite y los días consumidos, para que el CTC los muestre sin calcular. Lee los rangos de `clasificacion_ans_rango` (RN-7.8) y suma las ampliaciones autorizadas por la Dirección de Servicio al Cliente (RN-7.3) | 2.4 |
| 3.2 | En Central, desde la bolsa: suspender, reactivar y marcar una respuesta como incompleta. Ampliación del plazo para 16 ítems o más | 3.1 |
| 3.3 | Alertas disparadas por eventos (producto nuevo, producto por crear, proveedor por crear, requiere RIM). Las de RIM van a los usuarios activos con rol 47 en `cti.usuarios`. La de producto nuevo va por correo según el sistema de origen: In House (CTC) o el analista que cargó la OC (Central), en ambos casos con la Dirección de Servicio al Cliente. La de proveedor incluye la acción del analista para asociar el exportador ya creado en Websimex (RN-1.22). Todas las alertas: se guardan en `ctc_alerta`, se envían con `AsercolMailing`, se registran en `correo_auditoria`, y sus destinatarios se leen de `alerta_destinatario` (RN-4.1) | 2.2, 4.7 |
| 3.4 | En Central: comando diario `clasificacion:alertas` para las alertas por tiempo: documentos incompletos, ANS en riesgo y RIM sin aprobar | 3.1 |
| 3.5 | En Central: bloquear el VoBo mientras la solicitud de RIM enlazada no esté aprobada (RN-4.2) | 2.5 |
| 3.6 | Indicador de alertas (campana) en el shell, para los usuarios que tengan alertas | 3.3 |

**Fase 4 – Parametrización en Central: M8 (≈ 8 días: 5 en Central para 4.1 a 4.3, 4.5 y 4.7, y 3 en el CTC para 4.4 y 4.6; en paralelo con las Fases 1 y 2)**

| # | Tarea | Depende de |
|---|---|---|
| 4.1 | Pantallas de parametrización por cliente, con auditoría de cambios. Reutilizan la pantalla existente `/inhouse_cliente` para el In House. Las editan el Analista, la Dirección de Servicio al Cliente y Sistemas (rol 1, RN-8.5) | 0.3 |
| 4.2 | Catálogo de compradores con flujo de aprobación | 0.3 |
| 4.3 | Carga inicial del catálogo desde `adicionales_po` (grupos 27 y 2), en estado "Por aprobar" y sin correo. El In House completa el correo al aprobar (RN-8.11) | 4.2 |
| 4.4 | En CTC: leer el catálogo (conexión `central`) en la plantilla y en la IA. La opción "Nuevo" crea el comprador en "Por aprobar" y el In House lo aprueba | 4.2, 1.3 |
| 4.5 | Bloquear el envío y alertar al administrador cuando el cliente no tiene parametrización activa (RN-8.2) | 4.1, 3.3 |
| 4.6 | En el CTC: API autenticada para que Central cree OC con el mismo validador (RN-1.21), con `sistema_origen = CENTRAL`. En Central: el listado consulta `PO` por `id_importador` con su conexión `ctc` existente | 1.1, 1.4 |
| 4.7 | En Central: pantalla de destinatarios por tipo de alerta (usuarios, roles de CTI o correos), cargada con la configuración inicial del M4 y con auditoría de cambios (RN-4.1) | 0.3 |

**Fase 5 – Vistas, Torre de Control e indicadores: M6 + M7 (≈ 7 días: 3 en el CTC para 5.1 y 5.4, y 4 en Central para 5.2 y 5.3)**

| # | Tarea | Depende de |
|---|---|---|
| 5.1 | `PurchaseOrderV2`: columnas nuevas, selector de orden por `fecha_entrega_proveedor` (por defecto), `fecha_etd` o `fecha_po` (en los tres, las órdenes sin fecha van al final), productos del detalle ordenados por posición (RN-1.1) y filtro de varias modalidades sobre el valor vigente | 1.7, 2.2 |
| 5.2 | En Central: vista de OC propia, con el estilo visual de Central y la misma funcionalidad que la del CTC (RN-6.5). Lee `PO` por importador con su conexión `ctc` y tiene buscador predictivo de importador (RN-1.17) | D1, 0.7 |
| 5.3 | En Central: indicadores de % de solicitudes en plazo por mes y por analista, días de suspensión por proveedor y tiempo promedio por tramo (RN-7.9) | 3.1 |
| 5.4 | En el Dashboard V2 de la Torre de Control: tarjetas resumen con enlace al listado. Muestran OC con productos nuevos, solicitudes por estado, ANS en riesgo o vencido y RIM pendientes (RN-6.6). El In House no requiere vista aparte (RN-6.3) | 5.1, 3.1 |

**Fase 6 – Piloto y verificación (≈ 3 días)**

- **Piloto:** un grupo con CTC (propuesta: el grupo 27, el mismo de V2) y un importador sin CTC.
- **Aislamiento:**
  - El cliente A no ve solicitudes, alertas ni compradores del cliente B.
  - La bolsa solo es accesible a perfiles internos. Se prueba invocando los métodos de Livewire a mano, como en V2.
- **ANS:** pruebas con fechas que crucen festivos y el fin de año.

**Ruta crítica y entrega mínima:**

- **Entrega mínima:** las Fases 0 → 1 → 2, ≈ 23 días. Son lo mínimo que tiene valor por sí solo: reemplaza el Excel manual y alimenta la bolsa.
- **Dependencias:**
  - Las Fases 3 y 5 dependen de la 2.
  - La Fase 4 corre en paralelo, pero bloquea la tarea 3.3 (destinatarios de alertas) y la lista de compradores de la plantilla. Mientras tanto, la plantilla acepta el comprador como texto libre.
  - En Central, la bolsa (2.4) necesita que el CTC ya cree solicitudes (2.2) y que existan las tablas en CTI (0.3).
- **Total:** ≈ 48 días de desarrollo: ≈ 26 en el CTC, ≈ 19 en Central (`cia`) y 3 de piloto.
- **Con dos personas** (una por repositorio), el trabajo de Central avanza en paralelo desde la Fase 0. La duración en calendario queda cerca de los ≈ 30 días que suma el CTC más el piloto.

### A.7 Riesgos

| # | Riesgo | Mitigación |
|---|---|---|
| R1 | El calendario de festivos del CTC se acaba el 31/12/2026 | **Mitigado 21/10:** el ANS se calcula en `cia` con `DiasHabiles`, que calcula los festivos por regla. El CTC no calcula ANS |
| R2 | Las solicitudes de todos los clientes están en una sola base (CTI). Un error de alcance en la vista del In House en el CTC expone las solicitudes de un cliente a otro | La vista de Central es interna y cruza clientes por diseño (RN-3.2). En el CTC, la vista del In House filtra siempre por los importadores de su grupo, en un solo método verificado en el servidor, como `$gruposVisibles` en V2 |
| R3 | Las OC nuevas sin equivalencia en los maestros del CTC quedan con `Id*` nulos, y la V1 y los reportes legacy no verán esos datos | La OC guarda el código de Websimex (D7), así que el dato no se pierde. El diagnóstico 0.2 mide cuántos clientes quedan afectados. Los reportes se migran a leer el código cuando se toquen |
| R4 | `productos` de Websimex tiene duplicados de código + importador | "Existe" significa al menos un registro aprobado y activo. Se muestra cuál registro se tomó |
| R5 | Dos repositorios (CTC y Central) avanzan en paralelo sobre las mismas tablas. Central ya escribe directamente en `po_detalle` (`ActualizacionesController`) | Un solo juego de DDL en `specs/sql/`. Central es dueño de sus tablas de parametrización y el CTC solo las lee. Las OC de Central entran solo por la API del CTC (4.6), nunca escribiendo en `PO` directamente. Las tablas de clasificación en CTI las escriben las dos aplicaciones: los cambios de estado pasan por una sola máquina de estados y quedan en `clasificacion_estado_historial` |
| R6 | Con `QUEUE_CONNECTION=sync`, los correos se envían dentro del request al cargar una OC con muchos productos nuevos | Una alerta agrupada por solicitud, no una por línea. Las alertas por tiempo van por comando |
| R7 | V1 y los reportes legacy siguen leyendo `po_detalle` consolidado | Solo V2 usa el importador v2. V1 conserva el suyo hasta que se retire. El formulario de productos de la V1 (`PurchaseOrderProductForm`) conserva su defecto de guardar el precio como total: se deja así hasta retirar la V1 (decidido 21/10) |
| R8 | El gemelo de exportaciones (regla 7 de `CLAUDE.md`) | Queda fuera de alcance. Se deja registrado |
| R9 | La fecha de despacho del ERP está vacía | Se usa `PO.fecha_etd`. Si el negocio quiere la fecha real de despacho, hay que definir otra fuente |
| R10 | Las URL firmadas vencen. Un enlace a S3 puesto en un correo de alerta deja de funcionar | Los correos enlazan a la ruta del CTC (0.8), que autentica y firma en el momento. Nunca llevan la URL de S3 |
| R11 | Los documentos de la V2 creados antes de este cambio quedaron públicos | **Fuera de alcance (decidido 21/10).** Se dejan como están, porque cambiar su visibilidad rompe los enlaces existentes. Las URL firmadas aplican solo a los archivos cargados desde la puesta en marcha de este módulo |
| R12 | En algunos clientes el `CODIGO_PRODUCTO` de Websimex no es el código SAP. Medido en los maestros del CTC (21/10): en brinsa (grupo 27), 523 de 846 SAP existen en Websimex. En brenntag (grupo 8), el `codigo_sap` es "S/N" en casi todos sus materiales y solo 1 de 182 existe en Websimex, mientras que 164 se encuentran por `codigo_homologacion`. Si el cliente no envía el SAP con el mismo código que Websimex, todas sus líneas quedarían como "No Codificado" o "Nuevo" | **Mitigado 21/10:** `CODIGO_PRODUCTO` es el campo que, por estándar, se usa para encontrar desde el CTC los productos en Websimex. La diferencia medida viene del maestro histórico del CTC (`material.codigo_sap`), no de la regla. Basta con que el cliente envíe en la plantilla el código con el que está creado en Websimex |
