# Cargar tus activos

Esta página está escrita sobre todo para el **agente de IA** que hace la carga. Si eres una persona, basta con que conectes tu agente, adjuntes tu Excel y le digas "Cárgame mi lista de activos en zensset".

La carga en lote (herramienta `asset_upsert`, operación `bulk_load`) la puede hacer un **Propietario** o un **Administrador** del sitio. Los activos quedan habilitados directamente, sin pasar por las reglas de aprobación, y el historial registra que fueron una carga inicial y quién la hizo.

Todas las herramientas de zensset reciben `{ operation, items: [ ... ] }`: un ítem por cosa que leer o cambiar.

## 1. Qué pide zensset por activo

| Campo | Obligatorio | De dónde sale |
|---|---|---|
| `ref` | Recomendado | El número de fila del Excel. zensset lo devuelve tal cual en el resultado, para que sepas qué fila falló. |
| `code` | Sí | Código único en el sitio. Del Excel: Código, Tag, ID, N.º de equipo. |
| `name` | Sí | Del Excel: Equipo, Nombre, Descripción corta. |
| `classification_id` | Sí | De `reference_list_query` (lista `classification`). Se deduce (ver punto 2). |
| `taxonomy_level` | Sí | `equipment_unit`, `subunit` o `maintainable_item` (ver punto 2). |
| `parent_code` | No | El código del activo padre, para componentes (ver punto 3). |
| `functional_group_code` | No | El grupo del padre del que cuelga el hijo. Si no viene, zensset usa "Componentes". |
| `location_id` | Recomendado | De `location_query`. Sin ubicación, el activo queda "Sin asignar". Solo para activos sin padre: un hijo toma la ubicación de su padre. |
| `brand_name` o `brand_id` | No | Uno de los dos (si mandas ambos, se usa `brand_id`). `brand_name` se busca sin importar mayúsculas ni tildes, y si no existe se crea. Las marcas actuales están en `reference_list_query` (lista `brand`). |
| `description`, `model`, `serial_number`, `manufactured_year`, `commissioned_on`, `responsible_user_id` | No | Recomendados cuando el Excel los trae. |
| `duplicate_reason` | No | Solo para reenviar una fila que el `dry_run` marcó como `duplicate_serial` (ver punto 6). |

## 2. Clasificación y nivel

- Lee la lista real del sitio con `reference_list_query` (lista `classification`). No inventes clasificaciones.
- Elige por el nombre y la descripción de cada activo. Agrupa: "estas 40 son bombas centrífugas".
- Si hay dos candidatas razonables, **pregunta** al usuario.
- Si falta una clasificación que el usuario necesita, propón crearla con `reference_list_upsert` (lista `classification`).
- Nivel: `equipment_unit` para equipos completos; `subunit` o `maintainable_item` para los que tienen padre.

## 3. Jerarquía

El Excel puede traer la jerarquía de muchas formas: una columna "Pertenece a" con el nombre del padre, códigos con un prefijo común, filas indentadas u hojas separadas.

- Reconócela y resuelve cada padre a su **código**: el de otra fila del mismo Excel o el de un activo que ya existe en el sitio (`asset_query`, operaciones `get_by_code` o `search`).
- Muestra el árbol propuesto al usuario y pregunta por los padres que no encuentres o que sean ambiguos (dos activos con el mismo nombre).
- Pasa el código del padre en `parent_code`. Dentro de un lote no importa el orden: zensset crea los padres antes que los hijos. Entre lotes, carga primero los padres.
- El hijo cuelga del padre a través de un grupo funcional del padre: el que indiques en `functional_group_code`, o "Componentes" si no indicas ninguno.
- Un hijo toma la ubicación de su padre: no le pases `location_id`.
- Si un padre falla, sus hijos también fallan con `parent_failed`.

## 4. Columnas típicas

| En el Excel | Campo |
|---|---|
| Código, Tag, ID, Código SAP | `code` |
| Equipo, Nombre, Descripción | `name` (y el resto en `description`) |
| Área, Sección, Línea, Ubicación | `location_id` |
| Marca, Fabricante | `brand_name` (si no existe, se crea) |
| Modelo | `model` |
| Serie, N.º de serie | `serial_number` |
| Año, Año de fabricación | `manufactured_year` |
| Pertenece a, Equipo padre | `parent_code` (resuelto a código) |

## 5. El protocolo, en este orden

1. Lee el Excel y resume qué trae: cuántas filas, qué columnas, la calidad de los datos.
2. Propón el mapeo columna → campo, y di qué columnas no se usan y por qué.
3. Recomienda columnas del Excel que conviene cargar aunque no sean obligatorias.
4. Propón la clasificación por grupo de activos y la jerarquía; pregunta lo dudoso.
5. Si el Excel trae áreas que no existen como ubicaciones, propón crearlas (`location_upsert`) y espera el visto bueno.
6. Corre `asset_upsert` con `operation: "bulk_load"` y un ítem `{ rows, dry_run }` en modo de prueba (`dry_run: true`, que es el valor por defecto) y muéstrale al usuario el resultado: qué entraría (`would_create`), el árbol (`tree`), las marcas nuevas (`brands_created`) y los errores.
7. Corrige con el usuario y carga de verdad con `dry_run: false`, en lotes de hasta 100 filas. Si una carga real deja filas sin entrar, reenvía **solo esas filas**, no el archivo completo: las que ya entraron fallarían como `duplicate_code` y arrastrarían a sus hijos.
8. Reporta el total cargado y las filas que no entraron, con su motivo.

## 6. Duplicados

- **Código repetido** (`duplicate_code`): ya existe en el sitio o está dos veces en el lote. No se sobrescribe: pregunta al usuario qué hacer.
- **Serie parecida** (`duplicate_serial`): ya hay un activo con la misma marca, clasificación y serie. Muéstralo al usuario; si confirma que es otro equipo, reenvía la fila con `duplicate_reason`.

## 7. Errores y qué hacer

Cada fila que no entra trae un `error_code` y un mensaje en español. Las demás filas del lote entran igual.

| `error_code` | Qué pasó | Qué hacer |
|---|---|---|
| `missing_required` | Falta `code`, `name`, `classification_id` o `taxonomy_level`. | Completa el campo con el usuario. |
| `duplicate_code` | El código ya existe en el sitio o está dos veces en el lote. | Pregunta al usuario: otro código, o no cargar la fila. |
| `duplicate_serial` | Hay un activo con la misma marca, clasificación y serie. | Muéstralo; si es otro equipo, reenvía con `duplicate_reason`. |
| `invalid_classification` | La clasificación no existe, está inactiva o no se puede elegir. | Elige otra de `reference_list_query` (lista `classification`). |
| `invalid_taxonomy_level` | El nivel no es `equipment_unit`, `subunit` ni `maintainable_item`. | Corrige el nivel. |
| `brand_not_found` | El `brand_id` no existe. | Usa `reference_list_query` (lista `brand`) o manda `brand_name`. |
| `location_not_found` | La ubicación no es de este sitio. | Revisa `location_query` o créala antes. |
| `location_with_parent` | La fila tiene padre y también `location_id`. | Quita `location_id`: el hijo toma la ubicación del padre. |
| `parent_not_found` | El padre no está en el lote ni en el sitio. | Revisa el código del padre con el usuario. |
| `parent_failed` | El padre falló en este lote (el mensaje dice en qué fila). | Corrige el padre y reenvía esas filas. Si el padre falló con `duplicate_code` porque ya se cargó antes, reenvía solo los hijos: zensset encuentra al padre en el sitio. |
| `hierarchy_cycle` | La cadena de padres vuelve a la misma fila. | Revisa los `parent_code`. |
| `functional_group_not_found` | El grupo funcional no existe. | Omite `functional_group_code` para usar "Componentes". |
| `invalid_row` | Otro error; el mensaje dice cuál. | Léelo y corrige la fila. |

## 8. Qué no hacer

- No inventes códigos ni clasificaciones.
- No cargues sin mostrar antes el resultado de `dry_run`.
- No elijas clasificación ni padre sin preguntar cuando hay ambigüedad.