# SATABIN - Endurecimiento V0

## Regla de oro

SATABIN es una plataforma de Business Intelligence territorial / geomarketing que toma datos comerciales ordinarios de una empresa, los cruza con territorio y mercado, y devuelve oportunidades comerciales cuantificadas y explicables.

La V0 debe ser coherente con esa definicion: no prometer GIS productivo, mercado externo real ni competencia automatica hasta que existan fuentes, geocodificacion y persistencia espacial reales.

## Estado honesto

### Se endurece ahora

- Landing con definicion fija.
- App local navegable.
- Parametros de alcance: Argentina, provincia, localidad, industria, familia y subproducto.
- Catalogo de oferta.
- Estructura comercial: sucursal, vendedor, distribuidor, representante, punto de venta, servicio y casa central.
- Importacion CSV, TSV, TXT, pegado desde Excel y XLSX con API local.
- Mapeo de columnas y perfiles reutilizables.
- Calidad de datos.
- Preparacion GIS declarada.
- Diagnostico con ranking, brecha, oportunidad, cobertura, riesgos y accion.
- Brief ejecutivo imprimible.
- Control de coherencia y prueba de cierre.

### Queda estimado con limite

- Opportunity Engine heuristico.
- Mercado objetivo estimado.
- Mapa conceptual.
- Preparacion GIS sin proveedor productivo.
- Solapamiento comercial interno.
- SATABIN Ask guiado.

Estas piezas se pueden mostrar solo si quedan explicadas como hipotesis, estimacion o lectura inicial.

### Se posterga para SaaS real

- Login productivo.
- Multiempresa y roles.
- PostgreSQL/PostGIS operativo.
- Geocodificacion real con calidad de match.
- Coordenadas persistidas.
- Poligonos, buffers y consultas espaciales reales.
- Capas externas de mercado.
- Competencia territorial automatica.
- SATABIN Ask avanzado sobre dataset real.

## Riesgos principales

1. Que el mapa parezca GIS real cuando todavia es conceptual.
2. Que la oportunidad estimada se interprete como potencial validado por mercado externo.
3. Que el dashboard parezca mas maduro que la logica interna.
4. Que archivos reales de ERP/CRM rompan el mapeo automatico.
5. Que productos, canales o territorios no mapeados sesguen la decision.

## Proxima prueba obligatoria

Antes de seguir agregando pantallas, probar el flujo completo con al menos tres archivos reales o realistas:

- un CSV simple;
- un Excel XLSX exportado desde sistema;
- un archivo con encabezados raros o columnas incompletas.

Para cada prueba hay que registrar:

- columnas detectadas;
- mapeo propuesto;
- campos criticos faltantes;
- calidad de datos;
- preparacion GIS;
- ranking generado;
- riesgos declarados;
- ajustes necesarios.

## Prueba automatica agregada

Se agrego `backend/test/import-samples.mjs`.

Estado actual:

- 01 CSV coma: aprobado.
- 02 CSV punto y coma: aprobado.
- 03 TSV Excel: aprobado.
- 04 TXT pipe: aprobado.
- 05 CSV con encabezado desplazado: aprobado.
- 07 muestra publica argentina con separador `#`: aprobado.

Cobertura de la prueba:

- deteccion de separador;
- soporte para separador `#`;
- cantidad minima de filas;
- mapeo de zona/localidad;
- mapeo de ventas;
- filas utilizables.

Limite de la prueba:

Estos archivos son muestras controladas. La V0 todavia necesita prueba con exportaciones reales de ERP/CRM para validar encabezados raros, columnas incompletas, montos con formatos mixtos y bases grandes.

## Muestra publica argentina encontrada en web

Se encontro una muestra publica de ventas argentinas en GitHub Gist:

- Fuente: https://gist.github.com/ahcamachod/d2efadda3f3833d65da750c64705ac42
- Titulo declarado: "Informe de ventas de tienda por departamentos en Argentina."
- Estructura visible: `fecha_pedido`, `fecha_envio`, `modo_envio`, `nombre_cliente`, `segmento_cliente`, `ciudad`, `provincia`, `region`, `departamento`, `tipo_producto`, `ventas`, `ganancia`, entre otras columnas.
- Separador observado: `#`.

Decision tomada:

- No se copio el dataset completo.
- Se creo una muestra local compatible con esa estructura para probar SATABIN sin depender de descarga externa.
- Se agrego soporte de separador `#` al backend y a la app offline.
- Se agrego `samples/07_public_argentina_sales_hash.csv`.

Estado actual:

- Aprobado dentro de `npm run test:v0`.

Limite de la prueba:

No es un archivo ERP oficial ni prueba datos privados reales. Es una muestra publica de ventas argentinas util como aproximacion externa para columnas, geografia, producto y separador no habitual. Sigue faltando export real de ERP/CRM.

## Prueba automatica de importacion con caso sintetico dificil

Se agrego `backend/test/import-edge-cases.mjs` y `samples/06_synthetic_erp_edge_case.csv`.

Origen:

- Sintetico, creado por SATABIN para probar casos borde.
- No fue encontrado en internet.
- No debe presentarse como archivo ERP real.

Estado actual:

- Aprobado.

Cobertura de la prueba:

- detecta punto y coma en export tipo ERP;
- ignora dos filas informativas antes del encabezado;
- mapea aliases frecuentes como `cliente_nombre`, `prov`, `ciudad_cliente`, `vendedor_nombre`, `tipo_cliente`, `linea_producto`, `subfamilia`, `neto gravado`, `fecha comprobante` y `domicilio cliente`;
- conserva columnas con nombres repetidos sin pisar valores;
- interpreta importes argentinos con `$ 1.234.567,89`;
- interpreta formato internacional `1,234,567.89`;
- interpreta negativos contables con parentesis, por ejemplo `($ 125.000,00)`.

Limite de la prueba:

Sigue siendo un archivo controlado e inventado. No garantiza que cualquier ERP/CRM entre sin intervencion humana. La V0 debe mantener confirmacion de mapeo por usuario y registrar ajustes por sistema real.

## Alineacion de importacion en app offline

Se actualizo `app.html` para que la app estatica, cuando se abre sin backend local, use criterios de importacion equivalentes al backend.

Se agrego `backend/test/app-static-sanity.mjs`.

Estado actual:

- Aprobado dentro de `npm run test:v0`.

Cobertura de la prueba:

- valida sintaxis del JavaScript inline de la app;
- valida presencia de aliases ERP;
- valida proteccion contra encabezados repetidos;
- valida soporte de formatos numericos mixtos;
- valida soporte de negativos contables.

Limite de la prueba:

No reemplaza una prueba visual completa en navegador ni una carga manual end-to-end. Solo evita que la app offline quede por detras del backend en criterios basicos de importacion.

## Prueba automatica del Opportunity Engine

Se agrego `backend/test/opportunity-engine.mjs`.

Estado actual:

- Aprobado.

Cobertura de la prueba:

- excluye zonas sin ventas positivas;
- calcula clientes, vendedores, mercado estimado, brecha, cobertura, preparacion GIS, score y oportunidad;
- devuelve resultado ordenado por score descendente;
- adjunta explicacion trazable por zona;
- genera recomendacion vinculada a la zona.

Limite de la prueba:

El motor sigue siendo heuristico. El test valida estabilidad logica basica, no valida mercado real, competencia, elasticidad comercial, potencial externo ni causalidad. La salida debe tratarse como hipotesis territorial cuantificada hasta conectar fuentes externas y calibrar con datos reales.

## Prueba automatica de preparacion GIS

Se agrego `backend/test/geocoder.mjs`.

Estado actual:

- Aprobado.

Cobertura de la prueba:

- clasifica domicilio con calle como `street`;
- clasifica localidad sin domicilio como `locality`;
- clasifica registros sin domicilio ni localidad como `failed`;
- devuelve coordenadas aproximadas para `street` y `locality`;
- resume total, utilizables, porcentaje utilizable, street, locality y failed;
- declara que falta proveedor real para precision final.

Limite de la prueba:

No valida geocodificacion real, precision rooftop, normalizacion postal, altura exacta, codigos postales, geometrias, buffers ni PostGIS. La V0 solo puede hablar de preparacion GIS y lectura territorial inicial.

## Prueba automatica de capa de mercado estimada

Se agrego `backend/test/market-layers.mjs`.

Estado actual:

- Aprobado.

Cobertura de la prueba:

- valida que la fuente sea `satabin-local-seed`;
- valida que el modo sea `estimated`;
- valida que la descripcion declare que debe reemplazarse por fuentes externas reales;
- encuentra zonas aunque vengan sin acento;
- adjunta senal de mercado cuando la zona existe;
- deja `marketLayer: null` cuando la zona no tiene senal;
- agrega explicacion trazable con fuente y modo.

Limite de la prueba:

Esta capa no es mercado externo real. Solo prueba que SATABIN puede adjuntar una senal estimada y declarar su origen. Antes de uso productivo se necesitan fuentes reales, licencias, actualizacion, trazabilidad y criterios por industria.

## Prueba automatica de trazabilidad local

Se agrego `backend/test/audit-trace.mjs`.

Estado actual:

- Aprobado.

Cobertura de la prueba:

- crea proyecto local;
- crea importacion;
- guarda corrida de geocodificacion;
- guarda corrida de oportunidad;
- registra eventos `project.created`, `import.created`, `geocoding.completed` y `opportunity.completed`;
- valida que la oportunidad registre cantidad de zonas y zona prioritaria;
- usa store temporal para no ensuciar datos de trabajo.

Limite de la prueba:

Valida trazabilidad local en JSON. No reemplaza auditoria productiva con usuarios, roles, tenant isolation, base SQL, logs inmutables ni controles de seguridad SaaS.

## Prueba automatica de semilla de seguridad

Se agrego `backend/test/security-seed.mjs`.

Estado actual:

- Aprobado.

Cobertura de la prueba:

- valida que la semilla este desactivada;
- valida que el usuario `satabin` este deshabilitado;
- valida algoritmo `pbkdf2-sha256`;
- valida cantidad minima de iteraciones;
- valida formato de salt y hash;
- valida ausencia de `password` y `plainPassword`;
- valida que no quede la clave en claro;
- valida criterio de captcha: no usar captcha visible de entrada.

Limite de la prueba:

No implementa login. No valida sesiones, cookies, CSRF, roles, rate limiting, bloqueo por intentos, reset de clave, 2FA, auditoria SQL ni aislamiento real de tenants. Solo evita activar accidentalmente una semilla insegura antes del VPS.

## Validacion V0 unificada

Se agrego `npm run test:v0` en `backend/package.json`.

Estado actual:

- Aprobado.

Que corre:

- chequeo de sintaxis de backend y tests;
- smoke test del flujo basico;
- importacion de muestras CSV, TSV, TXT y encabezado desplazado;
- importacion de caso sintetico dificil;
- importacion de muestra argentina con separador `#`;
- sanity check de app offline;
- Opportunity Engine;
- preparacion GIS;
- capa de mercado estimada;
- trazabilidad local;
- semilla de seguridad desactivada.

Resultado de referencia:

- filas smoke: 5;
- calidad smoke: 100;
- geocodificacion smoke: 100;
- zona principal smoke: Cordoba Norte;
- importacion: 5/5 muestras aprobadas;
- seguridad: usuario semilla desactivado, PBKDF2 y sin clave plana.

Limite de la prueba:

Esta validacion todavia no reemplaza prueba con archivos reales de ERP/CRM, prueba visual en navegador, carga/performance, PostGIS real, proveedor de geocodificacion, login productivo, multiempresa ni despliegue. Sirve para evitar que la V0 local se rompa mientras seguimos endureciendo.

## Parametrizacion del Opportunity Engine

Se actualizo `backend/src/opportunity-engine.mjs`.

Estado actual:

- Aprobado dentro de `npm run test:v0`.

Que se corrigio:

- el motor ya no calcula solo sobre todas las filas disponibles;
- puede respetar alcance por provincia;
- puede respetar alcance por localidad;
- puede filtrar por familia de productos;
- puede filtrar por subproducto;
- incorpora estructura comercial declarada para ajustar cobertura;
- separa `marketCoverage` de `commercialCoverage`;
- registra unidades/canales de cobertura usados en la explicacion.

Prueba agregada:

- una corrida global mantiene ranking y explicacion por zona;
- una corrida acotada a Cordoba + Norte + Alimento balanceado + Linea ganadera devuelve solo la zona esperada;
- dos unidades de cobertura declaradas elevan la cobertura comercial frente a la penetracion de cartera.

Limite de la prueba:

La cobertura ajustada todavia es heuristica. No reemplaza asignacion real de vendedores, rutas, sucursales, distribuidores, puntos de venta o servicios desde una base operacional completa. Sirve para que SATABIN sea coherente con parametros de negocio y no diagnostique todo como si fuera una sola cartera plana.
