# Arsenal Nexora 1.7.0 - Collections + Campaign Manager

## Objetivo

Añadir una capa operativa para agrupar configuraciones del catálogo y programar rotaciones comerciales por reino sin modificar la compra pública, Warehouse, seriales ni encoder DbVersion 3.

## Colecciones

Nueva sección:

`AdminCP -> Arsenal Nexora -> Colecciones`

Una colección es una agrupación administrativa reutilizable de configuraciones existentes. No cambia categoría, precio, stock, publicación ni bytes del ítem.

- cada colección pertenece al reino operativo actual;
- una configuración puede pertenecer a varias colecciones;
- se pueden seleccionar hasta 2.000 configuraciones por colección;
- la interfaz permite buscar, filtrar por categoría, seleccionar visibles y limpiar selección;
- eliminar una configuración del catálogo limpia automáticamente su membresía mediante FK `ON DELETE CASCADE`;
- una colección no puede eliminarse mientras participa en una campaña `scheduled` o `active`.

Tablas nuevas por reino:

- `NEXORA_ITEMSHOP_COLLECTIONS`
- `NEXORA_ITEMSHOP_COLLECTION_ITEMS`

## Campaign Manager

Nueva sección:

`AdminCP -> Arsenal Nexora -> Campañas`

Una campaña utiliza una colección y un rango horario local. Puede aplicar al iniciar:

- publicar configuraciones;
- marcar configuraciones como destacadas;
- aplicar un descuento porcentual entre 1% y 95% sobre el precio normal.

Por defecto, al terminar restaura exactamente el estado previo de los campos que modificó.

### Snapshot y restauración

Al iniciar, Arsenal bloquea las filas objetivo y guarda en `NEXORA_ITEMSHOP_CAMPAIGNS.snapshot`:

- ID y nombre de cada configuración afectada;
- campos modificados;
- estado `before`;
- estado `after`;
- guards operativos, como `price_wc` cuando la campaña aplica descuento.

Al finalizar:

1. vuelve a bloquear las filas;
2. compara el estado actual contra `after` y los guards;
3. restaura únicamente las filas que siguen coincidiendo;
4. si una fila fue editada, eliminada o cambió su precio base durante la campaña, no la sobrescribe y la registra como conflicto.

No existe rollback de compras ni de entregas históricas.

## Prevención de campañas superpuestas

Arsenal bloquea campañas cuyo horario se solapa cuando las colecciones comparten al menos una configuración. La validación ocurre al guardar y se repite al iniciar, por si la membresía de una colección cambió después de programarla.

Esto evita que dos snapshots intenten restaurar el mismo producto en orden ambiguo.

## Promociones

El descuento se calcula desde `price_wc` de cada configuración al iniciar la campaña.

La campaña escribe:

- `sale_price_wc` calculado;
- `sale_start` = inicio efectivo;
- `sale_end` = fin programado.

Si se usa `Iniciar ahora`, un inicio futuro se adelanta al momento real de ejecución. El precio normal nunca se modifica.

## Automatización

Nuevo cron:

`includes/cron/itemshop_campaigns.php`

`Instalar / validar tablas` registra o actualiza automáticamente la tarea:

`Arsenal Campaign Manager`

con intervalo de 60 segundos.

El runner de cron existente ya selecciona Legado y Seasonal por separado, por lo que las campañas se procesan dentro del contexto operativo de cada reino.

El AdminCP también permite:

- `Procesar ahora`;
- `Iniciar ahora` una campaña programada;
- `Finalizar ahora` una campaña activa;
- cancelar una campaña programada sin tocar catálogo.

## Tabla de campañas

Nueva tabla por reino:

`NEXORA_ITEMSHOP_CAMPAIGNS`

Estados utilizados:

- `scheduled`
- `active`
- `completed`
- `cancelled`

Se conservan `affected`, `restored_count`, `conflict_count`, `last_error`, `started_at` y `ended_at` para trazabilidad administrativa.

## Seguridad y aislamiento

- CSRF central de AdminCP;
- toda colección/campaña opera contra el realm activo;
- IDs de catálogo se normalizan server-side;
- las fechas se convierten de timezone local a UTC antes de persistir;
- cron realm-aware;
- snapshots dentro de transacciones SQL;
- `UPDLOCK,HOLDLOCK` al iniciar y finalizar campañas;
- protección contra solapamiento de campañas;
- auditoría `itemshop.collection_save`, `itemshop.collection_membership`, `itemshop.collection_delete`, `itemshop.campaign_save`, `itemshop.campaign_start`, `itemshop.campaign_end` y `itemshop.campaign_cancel`.

## Sin cambios

Arsenal 1.7.0 no cambia:

- `purchase()`;
- `encodeItem()`;
- `saveItem()`;
- `deleteItem()`;
- `itemshop.js` público;
- `item-catalog.json`;
- `item-options.json`;
- Warehouse;
- seriales;
- órdenes históricas.
