# Fotos de productos

Una foto JPG o PNG de hasta 2 MB es opcional en la creación, edición y clonación de productos. Máximo 4000 píxeles por lado y 12 megapíxeles. Editar sin seleccionar otro archivo conserva la foto; «Quitar la foto actual» la desvincula del catálogo.

Las cargas se recodifican a JPEG, con un máximo de 1200 píxeles y una miniatura de 160 píxeles, en `web/uploads/productos/`. Los nombres son aleatorios y no se reutilizan. No borrar imágenes al reemplazarlas: las cotizaciones y los productos clonados pueden seguir utilizándolas.

La selección de un producto captura su foto en `costeo_estandar.foto`. Al guardar se traspasa a `detallecosteo.foto` y `detallecotizacion.foto`. Cambiar cantidades o clonar conserva esa versión; seleccionar otro producto toma su foto. Los documentos anteriores y las líneas manuales permanecen sin foto. Las rutas persistidas se resuelven en el servidor, no se aceptan desde el navegador.

Los listados muestran miniaturas y permiten ampliarlas en un visor, excluido de la navegación PJAX. El formulario usa una tarjeta con vista previa y un botón para seleccionar o cambiar la imagen. Las dos variantes del PDF de cotización y sus vistas previas incluyen la columna Foto. Las imágenes ausentes se muestran como «Sin foto» en pantalla y como «-» en el PDF.

## Instalación y comprobación

1. Guardar un checkpoint del código y las cuatro tablas.
2. Ejecutar `php commands/fotos-productos.php up`. Agrega únicamente `foto VARCHAR(255) NULL`; no rellena registros existentes. Es idempotente.
3. Instalar los archivos de esta modificación. Dar al usuario PHP acceso de escritura únicamente a `web/uploads/productos/` (en este servidor, `www-data`). Mantener el directorio fuera de cualquier ejecución de scripts.
4. Ejecutar `php commands/fotos-productos.php status`. La aplicación respeta el límite PHP existente de 2 MB.

## Pruebas

`php tests/regression/fotos-productos.php` crea una base temporal `rivtor_fotos_test_<identificador>`, copia únicamente el esquema y usa datos ficticios. Requiere permiso para crear/eliminar esa base. Nunca modifica los registros de la aplicación. Valida cargas, errores, las acciones de productos, fotos históricas, clonación, cambios de producto, migración y PDF. Elimina la base temporal y conserva artefactos en `/tmp`.

Con Playwright/Chromium disponibles: `python3 tests/regression/fotos-productos-browser.py /tmp/rivtor_fotos_test_<identificador>`. Usa el fragmento real de formulario generado por la prueba anterior y los scripts de la aplicación. Intercepta las solicitudes para verificar `FormData`; las acciones PHP se comprueban por separado.

## Volver al checkpoint

Consultar `.checkpoints/fotos-productos-20260927T191012Z/README.md`. La restauración habitual revierte sólo el código; conserva datos comerciales, columnas nuevas y archivos de fotos. Las columnas nullable son compatibles con el código anterior.

`php commands/fotos-productos.php down` permite retirar las columnas únicamente si no contienen referencias de fotos. Si ya se utilizó la función, conservar las columnas para no perder el historial.
