# Validacion de lotes por almacen y doble libreta

Fecha: 2026-04-28
Tenant validado: `demo.enterfarmaplus.test`
Commit base: `727a34ca feat: validate warehouse pricing and lot stock controls`

## Sprint 1-2: tabla lote por almacen

Objetivo validado: la nueva tabla `item_lots_group_warehouse` representa cuanto stock de un lote existe en cada almacen, manteniendo la cantidad total legacy en `item_lots_group.quantity`.

Estructura validada:

- Tabla: `item_lots_group_warehouse`
- Columnas principales: `item_lot_group_id`, `warehouse_id`, `quantity`
- Indice unico: `ilgw_unique` sobre `item_lot_group_id + warehouse_id`
- Indice de consulta por almacen: `ilgw_wh_idx`

Prueba con dato QA temporal:

```text
Lote QA:
  Oficina  = 60
  Sucursal = 40

Total libreta nueva = 100
Total libreta vieja = 100
Resultado = OK, ambas libretas coinciden
```

Validaciones de modelo:

```text
quantityForWarehouse(Oficina) = 60
availableInWarehouse(Sucursal, 40) = true
```

Evidencia local generada:

```text
storage/playwright/qa-lot-warehouse-split-result.png
storage/playwright/qa-lot-warehouse-split-result.json
```

## Sprint 3: cada operacion actualiza ambas libretas

Objetivo validado: compras, ventas, traslados y anulaciones mantienen sincronizadas la libreta vieja y la libreta nueva.

La prueba fue transaccional y termino con rollback, por lo que no dejo datos QA permanentes.

### Compra

Compra de 50 unidades de Lote B a Oficina.

```text
Despues:
  Libreta vieja total = 50
  Oficina             = 50
  Sucursal            = 0
  Total libreta nueva = 50

Resultado = OK
```

### Venta

Venta de 10 unidades desde Sucursal, Lote A.

```text
Antes:
  Libreta vieja total = 100
  Oficina             = 60
  Sucursal            = 40
  Total libreta nueva = 100

Despues:
  Libreta vieja total = 90
  Oficina             = 60
  Sucursal            = 30
  Total libreta nueva = 90

Resultado = OK
```

### Traslado

Traslado de 20 unidades de Oficina a Sucursal, Lote A.

```text
Antes:
  Libreta vieja total = 100
  Oficina             = 60
  Sucursal            = 40
  Total libreta nueva = 100

Despues:
  Libreta vieja total = 100
  Oficina             = 40
  Sucursal            = 60
  Total libreta nueva = 100

Resultado = OK
```

El total legacy no cambia porque el stock solo se mueve entre almacenes.

### Anulacion de venta

Anulacion de una venta de 10 unidades que salio de Sucursal.

```text
Antes:
  Libreta vieja total = 90
  Oficina             = 60
  Sucursal            = 30
  Total libreta nueva = 90

Despues:
  Libreta vieja total = 100
  Oficina             = 60
  Sucursal            = 40
  Total libreta nueva = 100

Resultado = OK
```

Las unidades regresan a Sucursal, no a Oficina.

Evidencia local generada:

```text
storage/playwright/qa-sprint3-dual-ledger-result.png
storage/playwright/qa-sprint3-dual-ledger-result.json
```

## Pruebas ejecutadas

```text
npx playwright test tests/e2e/items-lots-summary.spec.js --config=playwright.config.js
Resultado: 4 passed

npx playwright test tests/e2e/lots-multiwarehouse.spec.js --config=playwright.config.js
Resultado: 6 skipped
```

## Conclusion

Sprint 1-2 esta validado en estructura, modelo y datos: la tabla nueva conserva el detalle por almacen y coincide con la cantidad total legacy.

Sprint 3 esta validado para los escenarios principales: compra, venta, traslado y anulacion de venta actualizan correctamente ambas libretas.

La prueba `lots-multiwarehouse.spec.js` sigue documentada como pendiente porque sus casos estan en `skip`; la validacion transaccional manual cubrio los numeros esperados de los ejemplos.

## Criterio operativo para produccion

Los desalineamientos detectados en `tenancy_demo` se consideran datos de prueba y no deben usarse como criterio final de salida a produccion.

Antes de pasar esta funcionalidad a produccion, validar contra el tenant real y con data real:

- `item_lots_group.quantity` debe coincidir con `SUM(item_lots_group_warehouse.quantity)` por lote.
- El stock por almacen debe coincidir con la distribucion de lotes por almacen.
- Compras, traslados, ventas, anulaciones, ajustes y devoluciones deben conservar ambas libretas alineadas.
- `php artisan lots:health-check --tenant=<tenant_real>` debe ejecutarse contra el tenant productivo y revisar divergencias lote por lote.
- No extrapolar resultados de `tenancy_demo` a produccion sin revalidar la data real.
