Aplicación para cargar comercios desde archivos CSV, procesarlos por fecha y separar en cuarentena los registros que incumplen las reglas de calidad.
flowchart LR
CSV[Archivo CSV] --> UI[Frontend React]
UI --> API[API ASP.NET Core]
API --> COM[(commerce)]
COM --> PROC[Procesamiento por fecha]
PROC -->|Válidos| COM
PROC -->|Inválidos| Q[(commerce_quarantine)]
Q --> LIST[Consulta paginada y filtros]
- El usuario selecciona un archivo
commerce_DDMMYYYY.csv. - El frontend valida el nombre y muestra una vista previa.
- La API guarda todos los registros en una transacción.
- El procesamiento evalúa únicamente la fecha seleccionada.
- Los registros inválidos se copian a cuarentena y se eliminan físicamente de la tabla principal dentro de la misma transacción.
- La cuarentena puede consultarse por páginas y filtrarse por texto, fecha o motivo.
Un comercio pasa a cuarentena cuando:
pc_nomcomredestá vacío.pc_numdocestá vacío.pc_numdoccontiene letras o caracteres especiales.
El archivo debe incluir las columnas pc_nomcomred, pc_numdoc y
pc_processdate. Pueden estar en cualquier orden y pueden existir columnas
adicionales.
Requisito: Docker Desktop con Docker Compose.
Desde la raíz del proyecto:
docker compose up --build -d
docker compose psServicios disponibles:
| Servicio | Dirección |
|---|---|
| Aplicación web | http://localhost:3000 |
| API | http://localhost:5048 |
| MySQL | localhost:3306 |
El frontend usa rutas relativas. Nginx recibe /api y /health y las redirige
internamente al contenedor de la API; no se requiere ningún archivo .env.
Si un puerto está ocupado, puede ajustarse solo para el comando de Docker, por
ejemplo: API_PORT=5050 FRONTEND_PORT=3001 docker compose up --build -d.
Para detener el entorno sin borrar los datos:
docker compose downPuede utilizarse MySQL mediante Docker:
docker compose up -d mysqlTambién es posible ejecutar el proyecto sin Docker. Para ello se requiere
MySQL Server 8; la versión 8.4 es la recomendada. Con el servidor iniciado y el
cliente mysql disponible, desde la raíz del proyecto se crea la base y el
usuario local mediante:
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS commerce_processor CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER IF NOT EXISTS 'commerce_app'@'localhost' IDENTIFIED BY 'commerce_password'; GRANT ALL PRIVILEGES ON commerce_processor.* TO 'commerce_app'@'localhost'; FLUSH PRIVILEGES;"Después se ejecutan los scripts en el siguiente orden:
mysql -u root -p commerce_processor < Backend/Database/init/tables/000_commerce_table.sql
mysql -u root -p commerce_processor < Backend/Database/init/tables/001_commerce_quarantine_table.sql
mysql -u root -p commerce_processor < Backend/Database/init/stored_procedures/000_sp_create_commerce.sql
mysql -u root -p commerce_processor < Backend/Database/init/stored_procedures/001_sp_process_commerce.sql
mysql -u root -p commerce_processor < Backend/Database/init/stored_procedures/002_sp_get_commerce_quarantine.sqlLa configuración de desarrollo del backend ya utiliza localhost:3306, la
base commerce_processor y el usuario indicado. Estas credenciales son solo
para desarrollo local. Si se utilizan valores diferentes, debe actualizarse
Backend/Backend.Api/appsettings.Development.json o sobrescribirse la cadena
ConnectionStrings__CommerceDatabase mediante una variable de entorno.
Requiere .NET SDK 10.
cd Backend/Backend.Api
dotnet restore
dotnet watch runLa API escucha en http://localhost:5048 mediante su perfil local.
Requiere Node.js 22 o superior y pnpm 10. Si pnpm no está habilitado, puede
activarse con corepack enable.
cd Frontend
pnpm install --frozen-lockfile
pnpm devVite sirve la aplicación en http://localhost:3000 y redirige las rutas de la
API al puerto local del backend.
POST /api/commerce/upload
Content-Type: multipart/form-dataEl formulario debe contener un campo file.
POST /api/commerce/process
Content-Type: application/json
{
"processDate": "2026-08-27"
}GET /api/commerce/quarantine?page=1&pageSize=10Filtros opcionales:
| Parámetro | Descripción |
|---|---|
page |
Página solicitada, desde 1 |
pageSize |
Registros por página, máximo 100 |
processDate |
Fecha de proceso en formato YYYY-MM-DD |
reason |
Motivo exacto de cuarentena |
search |
Texto en comercio, documento o motivo |
Ejemplo de respuesta:
{
"items": [],
"page": 1,
"pageSize": 10,
"totalRecords": 0,
"totalPages": 0
}El archivo samples/commerce_27082026.csv
contiene ocho registros. Al procesar 2026-08-27, seis registros de esa fecha
son evaluados y dos pasan a cuarentena:
Comercio Surpor el documentoABC123.Mercado del Vallepor el documento123-456.
Los dos registros con fecha 2026-08-28 no participan en ese procesamiento.
Para no incorporar reglas de negocio que no fueron definidas expresamente, se adoptaron las siguientes decisiones:
- La fecha
DDMMYYYYdel nombre se valida como una fecha calendario válida, pero no se exige que coincida conpc_processdateen todas las filas. Cada registro conserva su propia fecha y el procesamiento se ejecuta para la fecha seleccionada por el usuario. - La carga valida el nombre, la estructura y las fechas del CSV, pero no rechaza filas por nombre o documento vacío ni por documentos con caracteres no numéricos. Esas son reglas del procesamiento y los registros que las incumplen deben llegar a la cuarentena.
- No se aplica una regla de duplicados porque no se especificó una clave de negocio para determinar cuándo dos comercios representan el mismo registro.
- Un número de documento se considera válido cuando contiene únicamente dígitos. No se asumieron longitudes, tipos de identificación ni algoritmos de validación propios de un país.
- Las tres columnas requeridas pueden aparecer en cualquier orden y el archivo puede incluir columnas adicionales, que se ignoran durante la carga.
- Por criterio de diseño, las tablas incluyen
is_activey campos de auditoría terminados en_aty_by.is_activepermite representar el estado de un registro, mientras que los demás campos dejan preparada la trazabilidad de cuándo ocurrió una operación y quién la realizó. En esta entrega no existe autenticación, por lo que los campos_byutilizan un identificador de sistema; aun cuando no todos intervienen en el flujo actual, esta estructura puede ser útil si posteriormente se incorporan usuarios, auditoría o eliminación lógica.
Estas decisiones mantienen el comportamiento solicitado sin descartar datos por supuestos adicionales. Si el contexto de negocio definiera posteriormente una relación obligatoria entre la fecha del archivo y sus filas, una clave de duplicidad o reglas documentales más específicas, podrían añadirse como validaciones de entrada.
La pantalla de cuarentena presenta búsqueda, filtro por fecha, filtro por motivo y navegación entre páginas sin descargar todos los registros de la base.
Backend/
Backend.Api/ API, servicios y acceso a datos
Database/init/ tablas y procedimientos almacenados
Frontend/
src/ interfaz React
nginx.conf servidor web y proxy para Docker
samples/ CSV de demostración
compose.yaml MySQL, API y frontend
Las credenciales incluidas en Docker Compose son exclusivamente para desarrollo. En otro entorno deben reemplazarse mediante variables de configuración y no deben guardarse credenciales reales en el repositorio.
Los scripts de Database/init se ejecutan al crear el volumen de MySQL por
primera vez. Al iniciar el entorno completo, el servicio temporal
database-init vuelve a aplicar de forma segura las definiciones de tablas y
procedimientos, permitiendo actualizar un volumen existente sin eliminar sus
datos. docker compose down -v elimina el volumen y todos sus datos; debe
utilizarse únicamente cuando se quiera reiniciar la base local.
