diff --git a/.github/workflows/on-pr.yaml b/.github/workflows/on-pr.yaml
index 009150e8..8f4801c2 100644
--- a/.github/workflows/on-pr.yaml
+++ b/.github/workflows/on-pr.yaml
@@ -6,6 +6,7 @@ on:
- main
- staging
- develop
+ - MBI-1
jobs:
lint:
@@ -18,7 +19,7 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v4
with:
- node-version: "22.14"
+ node-version: '22.14'
- name: Install dependencies
run: npm install
@@ -35,7 +36,7 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v4
with:
- node-version: "22.14"
+ node-version: '22.14'
- name: Install dependencies
run: npm install
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..13566b81
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/aws.xml b/.idea/aws.xml
new file mode 100644
index 00000000..03f1bb6e
--- /dev/null
+++ b/.idea/aws.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..6ea3fa32
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 00000000..79ee123c
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 00000000..913be6d2
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ mysql.8
+ true
+ com.mysql.cj.jdbc.Driver
+ jdbc:mysql://altertex.cqns2oecgotm.us-east-1.rds.amazonaws.com:3306/Altertex
+ $ProjectFileDir$
+
+
+
\ No newline at end of file
diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml
new file mode 100644
index 00000000..47877842
--- /dev/null
+++ b/.idea/dictionaries/project.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 00000000..26a57286
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml
new file mode 100644
index 00000000..94763096
--- /dev/null
+++ b/.idea/material_theme_project_new.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/prettier.xml b/.idea/prettier.xml
new file mode 100644
index 00000000..60cfc994
--- /dev/null
+++ b/.idea/prettier.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..35eb1ddf
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/webResources.xml b/.idea/webResources.xml
new file mode 100644
index 00000000..2d5e98e8
--- /dev/null
+++ b/.idea/webResources.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Categorias/Controladores/actualizarCategoria.controller.js b/Categorias/Controladores/actualizarCategoria.controller.js
new file mode 100644
index 00000000..8138e24d
--- /dev/null
+++ b/Categorias/Controladores/actualizarCategoria.controller.js
@@ -0,0 +1,44 @@
+const { actualizarCategoria } = require('@altertex/cat/repos/repositorioActualizarCategorias');
+const MENSAJES = require('@altertex/util/const/mensajesCategorias');
+
+/**
+ * RF49 - Actualizar categoría de productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF49
+ *
+ * @param {express.Request} req
+ * @param {express.Response} res
+ * @returns {Promise}
+ */
+exports.actualizarCategoria = async (req, res) => {
+ try {
+ const { idCategoria } = req.params;
+ const { nombreCategoria, descripcion, productos } = req.body;
+
+ if (!idCategoria) {
+ return res.status(400).json(MENSAJES.CATEGORIA_NO_ENCONTRADA);
+ }
+
+ if (!nombreCategoria || typeof nombreCategoria !== 'string' || nombreCategoria.trim() === '') {
+ return res.status(400).json(MENSAJES.NOMBRE_CATEGORIA_INVALIDO);
+ }
+
+ if (!Array.isArray(productos)) {
+ return res.status(400).json({
+ codigo: 400,
+ mensaje: 'El campo productos debe ser un arreglo.',
+ });
+ }
+
+ if (descripcion && typeof descripcion !== 'string') {
+ return res.status(400).json(MENSAJES.DESCRIPCION_INVALIDA);
+ }
+
+ await actualizarCategoria({ idCategoria, nombreCategoria, descripcion, productos });
+
+ return res.status(200).json({
+ codigo: 200,
+ mensaje: 'Categoría actualizada correctamente.',
+ });
+ } catch {
+ return res.status(500).json(MENSAJES.ERROR_CREAR_CATEGORIA);
+ }
+};
\ No newline at end of file
diff --git a/Categorias/Controladores/consultarDetalleCategoria.controller.js b/Categorias/Controladores/consultarDetalleCategoria.controller.js
new file mode 100644
index 00000000..1bcd4163
--- /dev/null
+++ b/Categorias/Controladores/consultarDetalleCategoria.controller.js
@@ -0,0 +1,41 @@
+const repositorio = require('@altertex/cat/repos/repositorioLeerDetalleCategoria');
+const MENSAJES_CATEGORIAS = require('@altertex/util/const/mensajesCategorias');
+
+/**
+ * Consulta el detalle de una categoría de productos, incluyendo sus productos asociados.
+ *
+ * @function
+ * @async
+ * @param {Express.Request} req - Objeto de solicitud HTTP con `req.params.idCategoria`.
+ * @param {Express.Response} res - Objeto de respuesta HTTP para enviar el resultado.
+ *
+ * @returns {Promise} Devuelve una respuesta HTTP con el detalle de la categoría o un mensaje de error.
+ *
+ * @description
+ * Implementa el RF48: Leer categoría de productos.
+ * Si no se encuentra la categoría, devuelve código 404.
+ * Si ocurre un error inesperado, devuelve código 500.
+ *
+ * @see [RF48 - Documentación de requisitos](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF48)
+ */
+exports.consultarDetalleCategoria = async (req, res) => {
+ const idCategoria = parseInt(req.params.idCategoria);
+
+ try {
+ const resultado = await repositorio.leerDetalleCategoria(idCategoria);
+
+ if (!resultado) {
+ return res
+ .status(MENSAJES_CATEGORIAS.CATEGORIA_NO_ENCONTRADA.codigo)
+ .json({ mensaje: MENSAJES_CATEGORIAS.CATEGORIA_NO_ENCONTRADA.mensaje });
+ }
+
+ return res
+ .status(MENSAJES_CATEGORIAS.CATEGORIA_OBTENIDA.codigo)
+ .json({ mensaje: MENSAJES_CATEGORIAS.CATEGORIA_OBTENIDA.mensaje, categoria: resultado });
+ } catch {
+ return res
+ .status(MENSAJES_CATEGORIAS.ERROR_OBTENER_CATEGORIA.codigo)
+ .json({ mensaje: MENSAJES_CATEGORIAS.ERROR_OBTENER_CATEGORIA.mensaje });
+ }
+};
\ No newline at end of file
diff --git a/Categorias/Datos/Repositorios/repositorioActualizarCategorias.js b/Categorias/Datos/Repositorios/repositorioActualizarCategorias.js
new file mode 100644
index 00000000..b3c72b05
--- /dev/null
+++ b/Categorias/Datos/Repositorios/repositorioActualizarCategorias.js
@@ -0,0 +1,22 @@
+const correrQuery = require('@altertex/util/ser/correrQuery');
+const CONSULTAS = require('@altertex/util/const/consultasCategorias');
+
+/**
+ * Actualiza el nombre, descripción y productos de una categoría.
+ *
+ * @param {object} categoria - Objeto con los datos de la categoría.
+ * @param {number} categoria.idCategoria - ID de la categoría a actualizar.
+ * @param {string} categoria.nombreCategoria - Nuevo nombre de la categoría.
+ * @param {string} categoria.descripcion - Nueva descripción.
+ * @param {number[]} categoria.productos - IDs de productos asociados.
+ * @returns {Promise}
+ */
+exports.actualizarCategoria = async ({ idCategoria, nombreCategoria, descripcion, productos }) => {
+ await correrQuery(CONSULTAS.ACTUALIZAR_CATEGORIA, [nombreCategoria, descripcion, idCategoria]);
+ await correrQuery(CONSULTAS.ELIMINAR_PRODUCTOS_CATEGORIA, [idCategoria]);
+
+ if (productos && productos.length > 0) {
+ const valores = productos.map((idProd) => [idCategoria, idProd]);
+ await correrQuery(CONSULTAS.ASIGNAR_PRODUCTOS_A_CATEGORIA, [valores]);
+ }
+};
\ No newline at end of file
diff --git a/Categorias/Datos/Repositorios/repositorioLeerDetalleCategoria.js b/Categorias/Datos/Repositorios/repositorioLeerDetalleCategoria.js
new file mode 100644
index 00000000..63b0ca0e
--- /dev/null
+++ b/Categorias/Datos/Repositorios/repositorioLeerDetalleCategoria.js
@@ -0,0 +1,35 @@
+const correrQuery = require('@altertex/util/ser/correrQuery');
+const CONSULTAS = require('@altertex/util/const/consultasCategorias');
+
+/**
+ * Consulta el detalle de una categoría y sus productos asociados.
+ *
+ * @param {number} idCategoria - ID de la categoría a consultar.
+ * @returns {Promise} Objeto con la información de la categoría y sus productos, o null si no existe.
+ *
+ * @throws {Error} Si ocurre un error al ejecutar la consulta.
+ *
+ * @see [RF48 - Documentación de requisitos](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF48)
+ */
+exports.leerDetalleCategoria = async (idCategoria) => {
+ const query = CONSULTAS.LEER_DETALLE_CATEGORIA;
+ const resultados = await correrQuery(query, [idCategoria]);
+
+ if (!resultados || resultados.length === 0) return null;
+
+ const { nombreCategoria, descripcion } = resultados[0];
+
+ const productos = resultados
+ .filter(resul => resul.idProducto !== null)
+ .map(produc => ({
+ idProducto: produc.idProducto,
+ nombreComun: produc.nombreComun,
+ }));
+
+ return {
+ idCategoria,
+ nombreCategoria,
+ descripcion,
+ productos,
+ };
+};
\ No newline at end of file
diff --git a/Categorias/Rutas/RutasIndividuales/actualizarCategorias.routes.js b/Categorias/Rutas/RutasIndividuales/actualizarCategorias.routes.js
new file mode 100644
index 00000000..67956f7d
--- /dev/null
+++ b/Categorias/Rutas/RutasIndividuales/actualizarCategorias.routes.js
@@ -0,0 +1,72 @@
+/**
+ * RF49 - Actualizar categoría de productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF49
+ */
+
+const express = require('express');
+const ruteador = express.Router();
+const controlador = require('@altertex/cat/ctrl/actualizarCategoria.controller');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+
+/**
+ * @swagger
+ * /api/categorias/actualizar-categoria/{idCategoria}:
+ * put:
+ * summary: Actualiza una categoría y su lista de productos.
+ * description: Requiere autenticación y permisos adecuados. Valida que no se incluyan entradas maliciosas.
+ * tags:
+ * - Categorías
+ * security:
+ * - ApiKeyAuth: []
+ * - BearerAuth: []
+ * parameters:
+ * - in: path
+ * name: idCategoria
+ * required: true
+ * schema:
+ * type: integer
+ * description: ID de la categoría a actualizar
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * categoria:
+ * type: object
+ * properties:
+ * nombreCategoria:
+ * type: string
+ * descripcion:
+ * type: string
+ * productos:
+ * type: array
+ * items:
+ * type: object
+ * properties:
+ * idProducto:
+ * type: integer
+ * responses:
+ * 200:
+ * description: Categoría actualizada correctamente.
+ * 400:
+ * description: Datos inválidos o entrada maliciosa detectada.
+ * 500:
+ * description: Error interno al actualizar la categoría.
+ */
+ruteador.put(
+ `${RUTAS.CATEGORIAS.ACTUALIZAR}/:idCategoria`,
+ validarYSanitizar,
+
+ revisarApiKey(),
+ autorizarToken,
+ verificarPermisos(PERMISOS.ACTUALIZAR_CATEGORIA_PRODUCTOS),
+ controlador.actualizarCategoria
+);
+
+module.exports = ruteador;
\ No newline at end of file
diff --git a/Categorias/Rutas/RutasIndividuales/consultarDetalleCategoria.routes.js b/Categorias/Rutas/RutasIndividuales/consultarDetalleCategoria.routes.js
new file mode 100644
index 00000000..0b755eaf
--- /dev/null
+++ b/Categorias/Rutas/RutasIndividuales/consultarDetalleCategoria.routes.js
@@ -0,0 +1,22 @@
+/**
+ * RF48 Leer categoría de productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF48
+ */
+
+const express = require('express');
+const ruteador = express.Router();
+const controlador = require('@altertex/cat/ctrl/consultarDetalleCategoria.controller');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+
+ruteador.get(
+ `${RUTAS.CATEGORIAS.LEER}/:idCategoria`,
+ revisarApiKey(),
+ autorizarToken,
+ verificarPermisos(PERMISOS.LEER_CATEGORIA_PRODUCTOS),
+ controlador.consultarDetalleCategoria
+);
+
+module.exports = ruteador;
\ No newline at end of file
diff --git a/Categorias/Rutas/RutasIndividuales/consultarListaCategorias.routes.js b/Categorias/Rutas/RutasIndividuales/consultarListaCategorias.routes.js
index f9e482e1..e5b99c36 100644
--- a/Categorias/Rutas/RutasIndividuales/consultarListaCategorias.routes.js
+++ b/Categorias/Rutas/RutasIndividuales/consultarListaCategorias.routes.js
@@ -61,7 +61,7 @@
* mensaje:
* type: string
* example: "No se encontraron categorías registradas."
- * 500:
+ * 400:
* description: Error en el servidor al intentar obtener la lista de categorías.
* content:
* application/json:
diff --git a/Categorias/Rutas/indexCategorias.routes.js b/Categorias/Rutas/indexCategorias.routes.js
index 5555b343..d412692f 100644
--- a/Categorias/Rutas/indexCategorias.routes.js
+++ b/Categorias/Rutas/indexCategorias.routes.js
@@ -3,11 +3,15 @@ const ruteador = express.Router();
const rutasConsultarListaCategorias = require('@altertex/cat/rutasInd/consultarListaCategorias.routes');
const rutasCrearCategoria = require('@altertex/cat/rutasInd/crearCategoria.routes');
const rutasEliminarCategoria = require('@altertex/cat/rutasInd/eliminarCategoria.routes');
+const rutasLeerCategoria = require('@altertex/cat/rutasInd/consultarDetalleCategoria.routes');
+const rutasActualizarCategoria = require('@altertex/cat/rutasInd/actualizarCategorias.routes');
const RUTAS = require('@altertex/util/const/rutas');
ruteador.use(RUTAS.CATEGORIAS.BASE, rutasConsultarListaCategorias);
ruteador.use(RUTAS.CATEGORIAS.BASE, rutasCrearCategoria);
ruteador.use(RUTAS.CATEGORIAS.BASE, rutasEliminarCategoria);
+ruteador.use(RUTAS.CATEGORIAS.BASE, rutasLeerCategoria);
+ruteador.use(RUTAS.CATEGORIAS.BASE, rutasActualizarCategoria);
module.exports = ruteador;
diff --git a/Clientes/Rutas/RutasIndividuales/eliminarCliente.routes.js b/Clientes/Rutas/RutasIndividuales/eliminarCliente.routes.js
index 0e2f26cf..bd0e0b0d 100644
--- a/Clientes/Rutas/RutasIndividuales/eliminarCliente.routes.js
+++ b/Clientes/Rutas/RutasIndividuales/eliminarCliente.routes.js
@@ -28,11 +28,9 @@
* mensaje:
* type: string
* example: Cliente eliminado
- * 400:
- * description: No se puede eliminar el cliente debido a restricciones (ej. registros asociados, ID inválido).
* 404:
* description: No se encontró un cliente con el ID proporcionado.
- * 500:
+ * 400:
* description: Error interno al eliminar el cliente
*/
diff --git a/Clientes/Rutas/RutasIndividuales/leerCliente.routes.js b/Clientes/Rutas/RutasIndividuales/leerCliente.routes.js
index d5430bb1..803bae07 100644
--- a/Clientes/Rutas/RutasIndividuales/leerCliente.routes.js
+++ b/Clientes/Rutas/RutasIndividuales/leerCliente.routes.js
@@ -66,7 +66,7 @@
* mensaje:
* type: string
* example: "No se encontró un cliente con el ID proporcionado."
- * 500:
+ * 400:
* description: Error interno del servidor al consultar el cliente.
* content:
* application/json:
diff --git a/Configuracion/clienteRedis.js b/Configuracion/clienteRedis.js
index 5f5f3a76..f6ec29e3 100644
--- a/Configuracion/clienteRedis.js
+++ b/Configuracion/clienteRedis.js
@@ -7,4 +7,4 @@ const redis = createClient({
redis.on('error', (err) => console.error('Error Redis', err));
redis.connect();
-module.exports = redis;
\ No newline at end of file
+module.exports = redis;
\ No newline at end of file
diff --git a/Configuracion/rutasSwagger.js b/Configuracion/rutasSwagger.js
index 4b6d5deb..b7982bb0 100644
--- a/Configuracion/rutasSwagger.js
+++ b/Configuracion/rutasSwagger.js
@@ -49,6 +49,7 @@ module.exports = [
'./SetsProductos/Rutas/RutasIndividuales/consultarSetsProductos.routes.js',
'./SetsProductos/Rutas/RutasIndividuales/eliminarSetsProductos.routes.js',
+ './SetsProductos/Rutas/RutasIndividuales/crearSetsProductos.routes.js',
'./Usuarios/Rutas/RutasIndividuales/consultarListaUsuarios.routes.js',
'./Usuarios/Rutas/RutasIndividuales/crearUsuario.routes.js',
diff --git a/Cuotas/Controladores/actualizarSetCuotas.controller.js b/Cuotas/Controladores/actualizarSetCuotas.controller.js
new file mode 100644
index 00000000..7c5ae1da
--- /dev/null
+++ b/Cuotas/Controladores/actualizarSetCuotas.controller.js
@@ -0,0 +1,24 @@
+const repositorio = require('@altertex/cuota/repos/actualizarSetCuotasRepositorio');
+
+/**
+ * Controlador que gestiona la actualización de un set de cuotas.
+ *
+ * @param {object} req - Objeto de solicitud HTTP de Express.
+ * @param {object} res - Objeto de respuesta HTTP de Express.
+ * @returns {object} Respuesta JSON con el resultado de la operación.
+ */
+exports.actualizarSetCuotas = async (req, res) => {
+ try {
+ const { idCuotaSet, cambios } = req.body;
+
+ if (!idCuotaSet || !cambios) {
+ return res.status(400).json({ mensaje: 'Datos incompletos' });
+ }
+
+ await repositorio.actualizarSetCuotas(idCuotaSet, cambios);
+ return res.status(200).json({ mensaje: 'Set de cuotas actualizado correctamente' });
+
+ } catch (error) {
+ return res.status(500).json({ mensaje: error.message });
+ }
+};
diff --git a/Cuotas/Controladores/leerSetCuotas.controller.js b/Cuotas/Controladores/leerSetCuotas.controller.js
new file mode 100644
index 00000000..9c33418d
--- /dev/null
+++ b/Cuotas/Controladores/leerSetCuotas.controller.js
@@ -0,0 +1,44 @@
+const repositorio = require('@altertex/cuota/repos/leerSetCuotasRepositorio');
+const MENSAJES_CUOTAS = require('@altertex/util/const/mensajesCuotas');
+
+/**
+ * Lee un conjunto de cuotas desde la base de datos utilizando su ID.
+ *
+ * Valida el parámetro `idSetCuota` y obtiene la información del set de cuotas a través del repositorio.
+ * Si el set de cuotas no es encontrado o el parámetro es inválido, retorna un error.
+ *
+ * @param {Express.Request} req - La solicitud HTTP que contiene el `idSetCuota` en el cuerpo.
+ * @param {Express.Response} res - La respuesta HTTP para enviar el resultado al cliente.
+ * @returns {Promise} Responde con el set de cuotas encontrado o un mensaje de error.
+ *
+ * @see [RF33] Leer set cuotas(https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF33)
+ */
+exports.leerSetCuotas = async (req, res) => {
+ const idSetCuota = parseInt(req.body.idSetCuota);
+
+ if (isNaN(idSetCuota)) {
+ return res
+ .status(MENSAJES_CUOTAS.PARAMETROS_INVALIDOS.codigo)
+ .json({ mensaje: MENSAJES_CUOTAS.PARAMETROS_INVALIDOS.mensaje });
+ }
+
+ try {
+ const setCuota = await repositorio.obtenerSetCuotaPorId(idSetCuota);
+
+ if (!setCuota) {
+ return res
+ .status(MENSAJES_CUOTAS.SET_CUOTA_NO_ENCONTRADO.codigo)
+ .json({ mensaje: MENSAJES_CUOTAS.SET_CUOTA_NO_ENCONTRADO.mensaje });
+ }
+
+ return res.status(MENSAJES_CUOTAS.CONSULTA_EXITOSA.codigo).json({
+ mensaje: MENSAJES_CUOTAS.CONSULTA_EXITOSA.mensaje,
+ setCuota,
+ });
+ } catch (error) {
+ console.error('Error al consultar Set cuotas:', error);
+ return res
+ .status(MENSAJES_CUOTAS.ERROR_OBTENER_SET_CUOTA.codigo)
+ .json({ mensaje: MENSAJES_CUOTAS.ERROR_OBTENER_SET_CUOTA.mensaje });
+ }
+};
diff --git a/Cuotas/Datos/Repositorios/actualizarSetCuotasRepositorio.js b/Cuotas/Datos/Repositorios/actualizarSetCuotasRepositorio.js
new file mode 100644
index 00000000..158b6a66
--- /dev/null
+++ b/Cuotas/Datos/Repositorios/actualizarSetCuotasRepositorio.js
@@ -0,0 +1,44 @@
+const CONSULTAS_CUOTAS = require('@altertex/util/const/consultasCuotas');
+const correrQuery = require('@altertex/util/ser/correrQuery');
+
+/**
+ * Actualiza un set de cuotas en la base de datos.
+ *
+ * @async
+ * @param {number} idCuotaSet - ID del set de cuotas a actualizar.
+ * @param {object} cambios - Objeto con los cambios a aplicar.
+ * @param {string} cambios.nombre - Nombre actualizado del set de cuotas.
+ * @param {string} cambios.descripcion - Descripción del set de cuotas.
+ * @param {number} cambios.periodoRenovacion - Periodo de renovación en meses.
+ * @param {boolean} cambios.renovacionHabilitada - Si la renovación está habilitada.
+ * @param {Array} [cambios.productos=[]] - Lista de productos con límites.
+ * @param {number} cambios.productos[].idProducto - ID del producto asociado.
+ * @param {number} cambios.productos[].limite - Límite asignado al producto.
+ * @param {number} cambios.productos[].limiteActual - Límite actual del producto.
+ * @throws {Error} Si ocurre un error en la consulta a la base de datos.
+ */
+exports.actualizarSetCuotas = async (idCuotaSet, cambios) => {
+ const { nombre, descripcion, periodoRenovacion, renovacionHabilitada, productos = [] } = cambios;
+
+ await correrQuery(CONSULTAS_CUOTAS.ACTUALIZAR_CUOTA_SET, [
+ nombre,
+ descripcion,
+ periodoRenovacion,
+ renovacionHabilitada,
+ new Date(),
+ idCuotaSet
+ ]);
+
+ await correrQuery(CONSULTAS_CUOTAS.ELIMINAR_PRODUCTOS_CUOTA_SET, [idCuotaSet]);
+
+ for (const producto of productos) {
+ if (producto.idProducto && producto.idProducto > 0) {
+ await correrQuery(CONSULTAS_CUOTAS.INSERTAR_CUOTA_PRODUCTO_ACTUALIZAR, [
+ idCuotaSet,
+ producto.idProducto,
+ producto.limite || 0,
+ producto.limiteActual || 0
+ ]);
+ }
+ }
+};
diff --git a/Cuotas/Datos/Repositorios/leerSetCuotasRepositorio.js b/Cuotas/Datos/Repositorios/leerSetCuotasRepositorio.js
new file mode 100644
index 00000000..b939a7b6
--- /dev/null
+++ b/Cuotas/Datos/Repositorios/leerSetCuotasRepositorio.js
@@ -0,0 +1,55 @@
+const correrQuery = require('@altertex/util/ser/correrQuery');
+const CONSULTAS_CUOTAS = require('@altertex/util/const/consultasCuotas');
+
+/**
+ * Obtiene un conjunto de cuotas desde la base de datos mediante su ID.
+ *
+ * Ejecuta una consulta SQL y retorna el primer conjunto de cuotas encontrado o `null` si no existe.
+ *
+ * @param {number} idSetCuota - ID del set de cuotas a buscar.
+ * @returns {Promise} El conjunto de cuotas encontrado o `null` si no existe.
+ * @throws {Error} Si ocurre un error al ejecutar la consulta.
+ */
+exports.obtenerSetCuotaPorId = async (idSetCuota) => {
+
+ const query = CONSULTAS_CUOTAS.LEER_CUOTA_SET;
+ const queryCuotas = CONSULTAS_CUOTAS.LEER_CUOTA_SET_PRODUCTOS;
+
+ const resultado = await correrQuery(query, [idSetCuota]);
+
+ if (resultado.length === 0) return null;
+
+ const productosCuota = await correrQuery(queryCuotas, [idSetCuota]);
+
+ const productos = productosCuota.map((producto) => ({
+ idProducto: producto.idProducto,
+ nombre: producto.nombreComun,
+ nombreComun: producto.nombreComun,
+ cuota_valor: producto.cuota_valor,
+ limite_actual: producto.limite_actual,
+ valor: producto.cuota_valor,
+ limite: producto.cuota_valor,
+ limiteActual: producto.limite_actual
+ }));
+
+ const cuotas = productosCuota.map((producto) => ({
+ valor: producto.cuota_valor,
+ }));
+
+ const setCuota = {
+ idSetCuota: resultado[0].idCuotaSet,
+ idCuotaSet: resultado[0].idCuotaSet,
+
+ nombre: resultado[0].nombre,
+ descripcion: resultado[0].descripcion,
+
+ periodoRenovacion: resultado[0].periodoRenovacion,
+ renovacionHabilitada: resultado[0].renovacionHabilitada,
+ ultimaActualizacion: resultado[0].ultimaActualizacion,
+ productos,
+ cuotas,
+ };
+
+
+ return setCuota;
+};
\ No newline at end of file
diff --git a/Cuotas/Rutas/RutasIndividuales/actualizarSetCuotas.routes.js b/Cuotas/Rutas/RutasIndividuales/actualizarSetCuotas.routes.js
new file mode 100644
index 00000000..65860635
--- /dev/null
+++ b/Cuotas/Rutas/RutasIndividuales/actualizarSetCuotas.routes.js
@@ -0,0 +1,37 @@
+/**
+ * Ruta para actualizar un set de cuotas.
+ *
+ * Método: PUT
+ * Ruta: /api/cuotas/actualizar-set-cuotas (o la que defina `RUTAS.CUOTAS.ACTUALIZAR_SET_CUOTAS`)
+ *
+ * Middleware aplicados:
+ * - revisión de API Key
+ * - autorización por token JWT
+ * - verificación de permisos de usuario
+ *
+ * Permiso requerido: PERMISOS.ACTUALIZAR_SET_CUOTAS
+ *
+ * @module actualizarSetCuotas.routes
+ */
+
+
+
+const express = require('express');
+const controlador = require('@altertex/cuota/ctrl/actualizarSetCuotas.controller');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+const RUTAS = require('@altertex/util/const/rutas');
+const PERMISOS = require('@altertex/util/const/permisos');
+
+const ruteador = express.Router();
+
+ruteador.put(
+ RUTAS.CUOTAS.ACTUALIZAR_SET_CUOTAS,
+ revisarApiKey(),
+ autorizarToken,
+ verificarPermisos(PERMISOS.ACTUALIZAR_SET_CUOTAS),
+ controlador.actualizarSetCuotas
+);
+
+module.exports = ruteador;
diff --git a/Cuotas/Rutas/RutasIndividuales/leerSetCuotas.routes.js b/Cuotas/Rutas/RutasIndividuales/leerSetCuotas.routes.js
new file mode 100644
index 00000000..e5481465
--- /dev/null
+++ b/Cuotas/Rutas/RutasIndividuales/leerSetCuotas.routes.js
@@ -0,0 +1,67 @@
+//RF33 - LEER COUTA SET - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF33]
+const express = require('express');
+const ruteador = express.Router();
+const controlador = require('@altertex/cuota/ctrl/leerSetCuotas.controller');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+
+/**
+ * @swagger
+ * /api/cuotas/leer-set-cuotas:
+ * post:
+ * summary: Leer set de cuotas
+ * description: Obtiene el set de cuotas según los parámetros enviados.
+ * tags:
+ * - Cuotas
+ * security:
+ * - ApiKeyAuth: []
+ * - BearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * idSet:
+ * type: string
+ * description: ID del set de cuotas a consultar
+ * required:
+ * - idSet
+ * responses:
+ * '200':
+ * description: Set de cuotas obtenido correctamente
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * cuotas:
+ * type: array
+ * items:
+ * type: object
+ * # Define los campos de cada cuota aquí
+ * '401':
+ * description: No autorizado (API Key o Token inválido)
+ * '403':
+ * description: Permisos insuficientes
+ * '400':
+ * description: Error de validación o parámetros incorrectos
+ * '500':
+ * description: Error interno del servidor
+ */
+
+ruteador.post(
+ RUTAS.CUOTAS.LEER_SET_CUOTAS,
+ validarYSanitizar,
+ revisarApiKey(),
+ autorizarToken,
+ verificarPermisos(PERMISOS.LEER_SET_CUOTAS),
+ controlador.leerSetCuotas
+);
+
+module.exports = ruteador;
diff --git a/Cuotas/Rutas/indexCuotas.routes.js b/Cuotas/Rutas/indexCuotas.routes.js
index f8e91d1f..0c4056a8 100644
--- a/Cuotas/Rutas/indexCuotas.routes.js
+++ b/Cuotas/Rutas/indexCuotas.routes.js
@@ -1,17 +1,21 @@
-const express = require("express");
+const express = require('express');
const ruteador = express.Router();
-const rutaCrearCuota = require("@altertex/cuota/rutasInd/crearCuota.routes");
-const rutaObtenerOpcionesCuota = require("@altertex/cuota/rutasInd/obtenerOpcionesCuotas.routes");
-const rutasConsultarListaCuotas = require("@altertex/cuota/rutasInd/consultarCuotas.routes");
+const rutaCrearCuota = require('@altertex/cuota/rutasInd/crearCuota.routes');
+const rutaObtenerOpcionesCuota = require('@altertex/cuota/rutasInd/obtenerOpcionesCuotas.routes');
+const rutasConsultarListaCuotas = require('@altertex/cuota/rutasInd/consultarCuotas.routes');
const rutaEliminarSetCuotas = require('@altertex/cuota/rutasInd/eliminarSetCuotas.routes');
+const rutaLeerCuota = require('@altertex/cuota/rutasInd/leerSetCuotas.routes');
+const rutaActualizarSetCuotas = require('@altertex/cuota/rutasInd/actualizarSetCuotas.routes');
-const RUTAS = require("@altertex/util/const/rutas");
+const RUTAS = require('@altertex/util/const/rutas');
ruteador.use(RUTAS.CUOTAS.BASE, rutaCrearCuota);
ruteador.use(RUTAS.CUOTAS.BASE, rutaObtenerOpcionesCuota);
ruteador.use(RUTAS.CUOTAS.BASE, rutasConsultarListaCuotas);
-ruteador.use(RUTAS.CUOTAS.BASE, rutaEliminarSetCuotas)
+ruteador.use(RUTAS.CUOTAS.BASE, rutaEliminarSetCuotas);
+ruteador.use(RUTAS.CUOTAS.BASE, rutaLeerCuota);
+ruteador.use(RUTAS.CUOTAS.BASE, rutaActualizarSetCuotas);
module.exports = ruteador;
diff --git a/Empleados/Controladores/actualizarGrupoEmpleado.controller.js b/Empleados/Controladores/actualizarGrupoEmpleado.controller.js
new file mode 100644
index 00000000..3687ffcc
--- /dev/null
+++ b/Empleados/Controladores/actualizarGrupoEmpleado.controller.js
@@ -0,0 +1,39 @@
+const MENSAJES = require('@altertex/util/const/mensajesGrupoEmpleados');
+const repositorio = require('@altertex/emp/repos/repositorioActualizarGrupo');
+
+// RF[24] Actualiza grupo empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF24]
+
+/**
+ * Controlador HTTP para actualizar un grupo de empleados.
+ *
+ * Valida que el cuerpo de la solicitud contenga los datos necesarios,
+ * y delega la lógica de actualización al repositorio correspondiente.
+ *
+ * Respuestas posibles:
+ * - 400 si faltan datos en el cuerpo de la solicitud.
+ * - 200 si el grupo se actualiza correctamente.
+ * - 500 si ocurre un error en el proceso de actualización.
+ *
+ * @async
+ * @function actualizarGrupoEmpleados
+ * @param {Express.Request} req - Objeto de solicitud HTTP de Express.
+ * @param {Express.Response} res - Objeto de respuesta HTTP de Express.
+ * @returns {Promise} La respuesta HTTP con el estado y mensaje correspondiente.
+ */
+exports.actualizarGrupoEmpleados = async (req, res) => {
+ const datosActualizacion = req.body;
+ if (!datosActualizacion || Object.keys(datosActualizacion).length === 0) {
+ return res
+ .status(MENSAJES.FORMATO_INVALIDO_DATOS.codigo)
+ .json({ mensaje: MENSAJES.FORMATO_INVALIDO_DATOS.mensaje });
+ }
+ try {
+ await repositorio.actualizarGrupoEmpleados(datosActualizacion);
+
+ return res
+ .status(MENSAJES.GRUPO_ACTUALIZADO.codigo)
+ .json({ mensaje: MENSAJES.GRUPO_ACTUALIZADO.mensaje });
+ } catch (error) {
+ return res.status(MENSAJES.ERROR_ACTUALIZAR_GRUPOS.codigo).json({ mensaje: error.message });
+ }
+};
diff --git a/Empleados/Controladores/crearEmpleado.controller.js b/Empleados/Controladores/crearEmpleado.controller.js
new file mode 100644
index 00000000..4ecb4a14
--- /dev/null
+++ b/Empleados/Controladores/crearEmpleado.controller.js
@@ -0,0 +1,172 @@
+const bcrypt = require('bcryptjs');
+const repositorio = require('@altertex/emp/repos/repositorioCrearEmpleado');
+
+/**
+ * Controlador para crear un nuevo empleado.
+ * Este endpoint recibe un objeto con la información del nuevo empleado
+ * y usa su repositorio para insertar el nuevo registro en la base de datos.
+ *
+ * @function crearEmpleado
+ * @async
+ * @param {Array} req - Objeto de solicitud HTTP de Express.
+ * @param {Array} req.body - Cuerpo de la solicitud con los datos del nuevo empleado.
+ * @param {string} req.body[].nombreCompleto - Nombre completo del usuario.
+ * @param {string} req.body[].correoElectronico - Correo electrónico único del usuario.
+ * @param {string} req.body[].contrasenia - Contraseña en texto plano.
+ * @param {string} req.body[].numeroTelefono - Número de teléfono (10 dígitos).
+ * @param {string} req.body[].direccion - Dirección del usuario.
+ * @param {string} req.body[].fechaNacimiento - Fecha de nacimiento en formato YYYY-MM-DD.
+ * @param {string} req.body[].genero - Género del usuario.
+ * @param {boolean} req.body[].estatus - Estatus activo/inactivo del usuario.
+ * @param {number} req.body[].idRol - ID del rol asignado al usuario.
+ * @param {number|Array} req.body[].idCliente - ID(s) de cliente asociados.
+ * @param {string} req.body[].numeroEmergencia - Teléfono de emergencia del empleado.
+ * @param {string} req.body[].areaTrabajo - Área donde trabaja el empleado.
+ * @param {string} req.body[].posicion - Puesto del empleado.
+ * @param {number} req.body[].cantidadPuntos - Puntos iniciales del empleado.
+ * @param {string} req.body[].antiguedad - Fecha de ingreso (YYYY-MM-DD).
+ * @param {response} res - Objeto de respuesta HTTP.
+ * @returns {Promise}
+ *
+ * - 200 si el empleado se creó correctamente.
+ * - 400 si el cuerpo está vacío o no es un arreglo.
+ * - 500 si ocurre un error al crear el empleado.
+ */
+exports.crearEmpleado = async (req, res) => {
+ const {
+ nombreCompleto,
+ correoElectronico,
+ contrasenia,
+ numeroTelefono,
+ direccion,
+ fechaNacimiento,
+ genero,
+ estatus,
+ idRol,
+ idCliente,
+ numeroEmergencia,
+ areaTrabajo,
+ posicion,
+ cantidadPuntos,
+ antiguedad,
+ } = req.body;
+
+ // Validaciones críticas
+ if (!idCliente) {
+ return res.status(400).json({ mensaje: 'Cliente no seleccionado' });
+ }
+ if (
+ !nombreCompleto
+ || !correoElectronico
+ || !contrasenia
+ || !numeroTelefono
+ || !direccion
+ || !fechaNacimiento
+ || !genero
+ || estatus === undefined
+ || idRol === undefined
+ || !numeroEmergencia
+ || !areaTrabajo
+ || !posicion
+ || cantidadPuntos === undefined
+ || !antiguedad
+ ) {
+ return res.status(400).json({ mensaje: 'Faltan campos requeridos' });
+ }
+
+ // Validaciones de formato y longitud
+ if (nombreCompleto.length > 75) {
+ return res.status(400).json({ mensaje: 'El nombre es demasiado largo' });
+ }
+ if (!/^[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+$/.test(nombreCompleto)) {
+ return res.status(400).json({ mensaje: 'El nombre solo puede contener letras y espacios' });
+ }
+ if (correoElectronico.length > 75) {
+ return res.status(400).json({ mensaje: 'El correo es demasiado largo' });
+ }
+ const correoValido = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ if (!correoValido.test(correoElectronico)) {
+ return res.status(400).json({ mensaje: 'El correo electrónico no es válido' });
+ }
+ if (contrasenia.length > 75) {
+ return res.status(400).json({ mensaje: 'La contraseña es demasiado larga' });
+ }
+ const tieneCaracterEspecial = /[!@#$%^&*(),.?":{}|<>]/;
+ const tieneMayuscula = /[A-Z]/;
+ if (
+ contrasenia.length < 8
+ || !tieneCaracterEspecial.test(contrasenia)
+ || !tieneMayuscula.test(contrasenia)
+ ) {
+ return res
+ .status(400)
+ .json({
+ mensaje:
+ 'La contraseña es débil. Debe tener al menos 8 caracteres, una mayúscula y un caracter especial.',
+ });
+ }
+ if (direccion.length > 150) {
+ return res.status(400).json({ mensaje: 'La dirección es demasiado larga' });
+ }
+ if (posicion.length > 75) {
+ return res.status(400).json({ mensaje: 'La posición es demasiado larga' });
+ }
+ if (areaTrabajo.length > 75) {
+ return res.status(400).json({ mensaje: 'El área de trabajo es demasiado larga' });
+ }
+ if (genero.length > 20) {
+ return res.status(400).json({ mensaje: 'El género es demasiado largo' });
+ }
+ if (isNaN(numeroEmergencia)) {
+ return res.status(400).json({ mensaje: 'El número de emergencia no es válido' });
+ }
+ if (!/^\d+$/.test(String(cantidadPuntos)) || Number(cantidadPuntos) < 0) {
+ return res
+ .status(400)
+ .json({ mensaje: 'Los puntos deben ser un número entero mayor o igual a 0' });
+ }
+ const telefonoValido = /^\d{10}$/;
+ if (!telefonoValido.test(numeroTelefono)) {
+ return res
+ .status(400)
+ .json({ mensaje: 'El número de teléfono debe tener 10 dígitos numéricos' });
+ }
+ const fechaRegex = /^\d{4}-\d{2}-\d{2}$/;
+ if (!fechaRegex.test(fechaNacimiento) || isNaN(Date.parse(fechaNacimiento))) {
+ return res
+ .status(400)
+ .json({ mensaje: 'La fecha de nacimiento no tiene un formato válido (YYYY-MM-DD)' });
+ }
+ if (!fechaRegex.test(antiguedad) || isNaN(Date.parse(antiguedad))) {
+ return res
+ .status(400)
+ .json({ mensaje: 'La antigüedad no tiene un formato válido (YYYY-MM-DD)' });
+ }
+
+ try {
+ const contraseniaEncriptada = await bcrypt.hash(contrasenia, 10);
+ const resultado = await repositorio.crearEmpleado({
+ nombreCompleto,
+ correoElectronico,
+ contrasenia: contraseniaEncriptada,
+ numeroTelefono,
+ direccion,
+ fechaNacimiento,
+ genero,
+ estatus,
+ idRol,
+ idCliente,
+ numeroEmergencia,
+ areaTrabajo,
+ posicion,
+ cantidadPuntos,
+ antiguedad,
+ });
+ return res.status(201).json({
+ mensaje: 'Empleado creado exitosamente',
+ datos: resultado,
+ });
+ } catch (error) {
+ return res.status(400).json({ mensaje: error.message });
+ }
+};
diff --git a/Empleados/Controladores/exportarEmpleados.controller.js b/Empleados/Controladores/exportarEmpleados.controller.js
new file mode 100644
index 00000000..f3d3e4c3
--- /dev/null
+++ b/Empleados/Controladores/exportarEmpleados.controller.js
@@ -0,0 +1,73 @@
+const repositorio = require('@altertex/emp/repos/repositorioExportarEmpleado');
+const { Parser } = require('json2csv');
+const { format } = require('date-fns');
+const MENSAJES_EMPLEADOS = require('@altertex/util/const/mensajesEmpleados');
+
+/**
+ * Controlador para exportar empleados seleccionados de un cliente y retornar CSV como string en JSON.
+ *
+ * @async
+ * @function exportarEmpleados
+ * @param {Request} req
+ * @param {Response} res
+ * @returns {Response} JSON con mensaje + contenido CSV en texto plano
+ * @see [RF59 - Exportar Empleados](https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF59)
+ */
+exports.exportarEmpleados = async (req, res) => {
+ try {
+ const idCliente = parseInt(req.user.clienteSeleccionado);
+ const idsEmpleado = req.body.idsEmpleado;
+
+ if (!Array.isArray(idsEmpleado) || idsEmpleado.length === 0) {
+ return res.status(MENSAJES_EMPLEADOS.PARAMETROS_INVALIDOS.codigo).json({
+ mensaje: 'Debes seleccionar al menos un empleado para exportar.'
+ });
+ }
+
+ const idsSeleccionados = idsEmpleado.map(id => parseInt(id));
+ const empleados = await repositorio.obtenerEmpleadosExportacion(idCliente, idsSeleccionados);
+
+ if (!empleados || empleados.length === 0) {
+ return res.status(MENSAJES_EMPLEADOS.EMPLEADOS_NO_ENCONTRADOS.codigo).json({
+ mensaje: MENSAJES_EMPLEADOS.EMPLEADOS_NO_ENCONTRADOS.mensaje,
+ csv: ''
+ });
+ }
+
+ empleados.forEach(emp => {
+ emp.fechaNacimiento = format(new Date(emp.fechaNacimiento), 'dd/MM/yyyy');
+ emp.antiguedad = format(new Date(emp.antiguedad), 'dd/MM/yyyy');
+ });
+
+ const campos = [
+ { label: 'ID', value: 'idEmpleado' },
+ { label: 'Nombre completo', value: 'nombreCompleto' },
+ { label: 'Correo electrónico', value: 'correoElectronico' },
+ { label: 'Número de teléfono', value: 'numeroTelefono' },
+ { label: 'Dirección', value: 'direccion' },
+ { label: 'Fecha de nacimiento', value: 'fechaNacimiento' },
+ { label: 'Género', value: 'genero' },
+ { label: 'Estatus', value: 'estatus' },
+ { label: 'Número de emergencia', value: 'numeroEmergencia' },
+ { label: 'Área de trabajo', value: 'areaTrabajo' },
+ { label: 'Posición', value: 'posicion' },
+ { label: 'Cantidad de puntos', value: 'cantidadPuntos' },
+ { label: 'Antigüedad', value: 'antiguedad' }
+ ];
+
+ const parser = new Parser({ fields: campos });
+ const csv = parser.parse(empleados);
+ const csvConBOM = `\uFEFF${csv}`;
+
+ return res.status(MENSAJES_EMPLEADOS.LISTA_EMPLEADOS_EXPORTADA.codigo).json({
+ mensaje: MENSAJES_EMPLEADOS.LISTA_EMPLEADOS_EXPORTADA.mensaje,
+ csv: csvConBOM
+ });
+ } catch (error) {
+ console.error('Error al exportar empleados:', error);
+ return res.status(MENSAJES_EMPLEADOS.ERROR_EXPORTAR_EMPLEADOS.codigo).json({
+ mensaje: MENSAJES_EMPLEADOS.ERROR_EXPORTAR_EMPLEADOS.mensaje,
+ csv: ''
+ });
+ }
+};
\ No newline at end of file
diff --git a/Empleados/Datos/Repositorios/repositorioActualizarGrupo.js b/Empleados/Datos/Repositorios/repositorioActualizarGrupo.js
new file mode 100644
index 00000000..4f803667
--- /dev/null
+++ b/Empleados/Datos/Repositorios/repositorioActualizarGrupo.js
@@ -0,0 +1,111 @@
+const CONSULTAS = require('@altertex/util/const/consultasGrupoEmpleados');
+const correrQuery = require('@altertex/util/ser/correrQuery');
+const MENSAJES = require('@altertex/util/const/mensajesGrupoEmpleados');
+// RF[24] Actualiza grupo empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF24]
+
+/**
+ * Actualiza un grupo de empleados en la base de datos, incluyendo su nombre,
+ * descripción, empleados asociados y sets de productos relacionados.
+ *
+ * Esta función realiza:
+ * - La actualización del nombre y descripción del grupo.
+ * - La verificación y reemplazo de los empleados asociados al grupo.
+ * - La verificación y reemplazo de los sets de productos asociados al grupo.
+ *
+ * En caso de que los empleados o sets proporcionados no pertenezcan al mismo cliente,
+ * se lanza un error.
+ *
+ * Nota: Si empleados o setsDeProductos están vacíos, se eliminan todas las asociaciones existentes.
+ *
+ * @async
+ * @function actualizarGrupoEmpleados
+ * @param {object} datosActualizacion - Datos necesarios para actualizar el grupo.
+ * @param {number} datosActualizacion.idGrupoEmpleado - ID del grupo de empleados a actualizar.
+ * @param {string} datosActualizacion.nombre - Nuevo nombre del grupo.
+ * @param {string} datosActualizacion.descripcion - Nueva descripción del grupo.
+ * @param {number[]} datosActualizacion.empleados - Lista de IDs de empleados a asociar al grupo. Array vacío elimina todas las asociaciones.
+ * @param {number[]} datosActualizacion.setsDeProductos - Lista de IDs de sets de productos a asociar al grupo. Array vacío elimina todas las asociaciones.
+ * @throws {Error} Si ocurre algún error durante la actualización o verificación de empleados/sets.
+ */
+exports.actualizarGrupoEmpleados = async (datosActualizacion) => {
+ const { idGrupoEmpleado, nombre, descripcion, empleados, setsDeProductos } = datosActualizacion;
+
+ try {
+ // Actualizar nombre y descripción del grupo
+ await correrQuery(CONSULTAS.ACTUALIZAR_GRUPO_EMPLEADOS_NOMBRE_DESCRIPCION, [
+ nombre,
+ descripcion,
+ idGrupoEmpleado,
+ nombre,
+ descripcion,
+ ]);
+
+ // Manejar empleados (incluyendo arrays vacíos)
+ if (empleados !== undefined && Array.isArray(empleados)) {
+ if (empleados.length > 0) {
+ // Si hay empleados, verificar que pertenezcan al mismo cliente
+ const empleadosSTR = empleados.join(', ');
+ const valores = empleados
+ .map((empleadoId) => `(${empleadoId}, ${idGrupoEmpleado})`)
+ .join(', ');
+
+ const resultadoVerificacion = await correrQuery(
+ CONSULTAS.VERIFICAR_EMPLEADOS_CLIENTE.replace('__EMPLEADOS__', empleadosSTR),
+ [idGrupoEmpleado],
+ );
+
+ if (resultadoVerificacion[0].validos !== empleados.length) {
+ throw new Error(MENSAJES.ERROR_VERIFICACION_CLIENTE_EMPLEADO.mensaje);
+ }
+
+ // Eliminar empleados que no están en la nueva lista
+ await correrQuery(
+ CONSULTAS.ELIMINAR_EMPLEADOS_DE_GRUPO_BASE.replace('__ID__', idGrupoEmpleado).replace(
+ '__EMPLEADOS__',
+ empleadosSTR,
+ ),
+ );
+
+ // Agregar los nuevos empleados
+ await correrQuery(CONSULTAS.AGREGAR_EMPLEADOS_NUEVOS_BASE.replace('__VALORES__', valores));
+ } else {
+ // Si el array está vacío, eliminar todas las asociaciones de empleados
+ await correrQuery(CONSULTAS.ELIMINAR_TODOS_EMPLEADOS_DE_GRUPO, [idGrupoEmpleado]);
+ }
+ }
+
+ // Manejar sets de productos (incluyendo arrays vacíos)
+ if (setsDeProductos !== undefined && Array.isArray(setsDeProductos)) {
+ if (setsDeProductos.length > 0) {
+ // Si hay sets, verificar que pertenezcan al mismo cliente
+ const setsSTR = setsDeProductos.join(', ');
+ const valores = setsDeProductos.map((setId) => `(${setId}, ${idGrupoEmpleado})`).join(', ');
+
+ const resultadoVerificacion = await correrQuery(
+ CONSULTAS.VERIFICAR_SETS_CLIENTE.replace('__SETS__', setsSTR),
+ [idGrupoEmpleado],
+ );
+
+ if (resultadoVerificacion[0].validos !== setsDeProductos.length) {
+ throw new Error(MENSAJES.ERROR_VERIFICACION_CLIENTE_SET.mensaje);
+ }
+
+ // Eliminar sets que no están en la nueva lista
+ await correrQuery(
+ CONSULTAS.ELIMINAR_SETS_DE_GRUPO_BASE.replace('__ID__', idGrupoEmpleado).replace(
+ '__SETS__',
+ setsSTR,
+ ),
+ );
+
+ // Agregar los nuevos sets
+ await correrQuery(CONSULTAS.AGREGAR_SETS_NUEVOS_BASE.replace('__VALORES__', valores));
+ } else {
+ // Si el array está vacío, eliminar todas las asociaciones de sets
+ await correrQuery(CONSULTAS.ELIMINAR_TODOS_SETS_DE_GRUPO, [idGrupoEmpleado]);
+ }
+ }
+ } catch (error) {
+ throw new Error(error);
+ }
+};
\ No newline at end of file
diff --git a/Empleados/Datos/Repositorios/repositorioCrearEmpleado.js b/Empleados/Datos/Repositorios/repositorioCrearEmpleado.js
new file mode 100644
index 00000000..ed0ed8f2
--- /dev/null
+++ b/Empleados/Datos/Repositorios/repositorioCrearEmpleado.js
@@ -0,0 +1,156 @@
+const db = require('@altertex/util/bd/db');
+const ROL_PREDETERMINADO = 3; // ID del rol por defecto para empleados
+
+const CONSULTAS_EMPLEADOS = require('@altertex/util/const/consultasEmpleados');
+const CONSULTAS_IMPORTAR_EMPLEADOS = require('@altertex/util/const/consultasImportarEmpleados');
+
+//RF[16] Crear empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF16]
+
+/**
+ * Crea un nuevo empleado y su usuario asociado en la base de datos.
+ *
+ * @async
+ * @function crearEmpleado
+ * @param {object} empleado - Objeto con los datos del nuevo empleado.
+ * @param {string} empleado.nombreCompleto - Nombre completo del usuario.
+ * @param {string} empleado.correoElectronico - Correo electrónico único del usuario.
+ * @param {string} empleado.contrasena - Contraseña en texto plano (ya hasheada).
+ * @param {string} empleado.numeroTelefono - Teléfono del usuario (exactamente 10 dígitos).
+ * @param {string} empleado.direccion - Dirección del usuario.
+ * @param {string} empleado.fechaNacimiento - Fecha de nacimiento (YYYY-MM-DD).
+ * @param {string} empleado.genero - Género del usuario.
+ * @param {boolean} empleado.estatus - Estatus del usuario (true = activo, false = inactivo).
+ * @param {number} empleado.idRol - ID del rol asignado al usuario.
+ * @param {number} empleado.idCliente - ID del cliente asociado al usuario.
+ * @param {string} empleado.numeroEmergencia - Teléfono de emergencia del empleado.
+ * @param {string} empleado.areaTrabajo - Área de trabajo del empleado.
+ * @param {string} empleado.posicion - Puesto o cargo del empleado.
+ * @param {number} empleado.cantidadPuntos - Puntos acumulados del empleado.
+ * @param {string} empleado.antiguedad - Fecha de antigüedad/ingreso (YYYY-MM-DD).
+ * @throws {Error} Si el parámetro "empleado" no es un objeto válido o está vacío.
+ * @throws {Error} Si ocurre cualquier fallo durante la inserción en la transacción.
+ *
+ * @returns {Promise} Resuelve con un objeto que contiene el ID del nuevo empleado y su usuario.
+ */
+exports.crearEmpleado = async (empleado) => {
+ if (!empleado || typeof empleado !== 'object') {
+ throw new Error('No se recibió un empleado válido para importar.');
+ }
+
+ const conn = await db.getConnection();
+
+ try {
+ await conn.beginTransaction();
+
+ // Validar correo duplicado
+ const [correoExistente] = await conn.query(
+ CONSULTAS_IMPORTAR_EMPLEADOS.VALIDAR_CORREOS_DUPLICADOS,
+ [[empleado.correoElectronico]]
+ );
+ if (correoExistente.length > 0) {
+ throw new Error(`Correo ya registrado: ${empleado.correoElectronico}`);
+ }
+
+ // Validar fecha de nacimiento
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(empleado.fechaNacimiento)) {
+ throw new Error('Fecha de nacimiento inválida. Debe ser en formato YYYY-MM-DD.');
+ }
+ const fechaNacimiento = new Date(empleado.fechaNacimiento);
+ if (isNaN(fechaNacimiento.getTime())) {
+ throw new Error('Fecha de nacimiento inválida.');
+ }
+ const hoy = new Date();
+ if (fechaNacimiento > hoy) {
+ throw new Error('La fecha de nacimiento no puede ser futura.');
+ }
+ if (fechaNacimiento.getFullYear() < 1900) {
+ throw new Error('La fecha de nacimiento no puede ser anterior a 1900.');
+ }
+
+ // Validar que sea mayor o igual a 18 años
+ const edad = hoy.getFullYear() - fechaNacimiento.getFullYear();
+ const mes = hoy.getMonth() - fechaNacimiento.getMonth();
+ const dia = hoy.getDate() - fechaNacimiento.getDate();
+ let edadFinal = edad;
+ if (mes < 0 || (mes === 0 && dia < 0)) {
+ edadFinal -= 1;
+ }
+ if (edadFinal < 18) {
+ throw new Error('El empleado debe tener al menos 18 años.');
+ }
+
+ // Validar teléfono duplicado
+ const [telefonoExistente] = await conn.query(
+ CONSULTAS_IMPORTAR_EMPLEADOS.VALIDAR_TELEFONO_DUPLICADO,
+ [[empleado.numeroTelefono]]
+ );
+ if (telefonoExistente.length > 0) {
+ throw new Error(`Teléfono ya registrado: ${empleado.numeroTelefono}`);
+ }
+
+ //Validar antigüedad
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(empleado.antiguedad)) {
+ throw new Error('Antigüedad inválida. Debe ser en formato YYYY-MM-DD.');
+ }
+ const fechaAntiguedad = new Date(empleado.antiguedad);
+ if (isNaN(fechaAntiguedad.getTime())) {
+ throw new Error('Antigüedad inválida.');
+ }
+ if (fechaAntiguedad > hoy) {
+ throw new Error('La antigüedad no puede ser futura.');
+ }
+ if (fechaAntiguedad.getFullYear() < 1900) {
+ throw new Error('La antigüedad no puede ser anterior a 1900.');
+ }
+ // Validar que la antigüedad sea menor o igual a la fecha de nacimiento
+ if (fechaAntiguedad < fechaNacimiento) {
+ throw new Error('La antigüedad no puede ser anterior a la fecha de nacimiento.');
+ }
+ // Validar que la antigüedad sea menor o igual a la fecha actual
+ if (fechaAntiguedad > hoy) {
+ throw new Error('La antigüedad no puede ser posterior a la fecha actual.');
+ }
+
+ // Insertar usuario
+ const [resultadoUsuario] = await conn.query(CONSULTAS_EMPLEADOS.INSERTAR_USUARIO, [
+ empleado.nombreCompleto,
+ empleado.correoElectronico,
+ empleado.contrasenia,
+ empleado.numeroTelefono,
+ empleado.direccion,
+ empleado.fechaNacimiento,
+ empleado.genero,
+ empleado.estatus,
+ ]);
+ const idUsuario = resultadoUsuario.insertId;
+
+ // Insertar rol
+ await conn.query(CONSULTAS_EMPLEADOS.INSERTAR_ROL, [idUsuario, ROL_PREDETERMINADO]);
+
+ // Insertar asociación usuario-cliente
+ const listaClientes = Array.isArray(empleado.idCliente)
+ ? empleado.idCliente
+ : [empleado.idCliente];
+ for (const idCli of listaClientes) {
+ await conn.query(CONSULTAS_EMPLEADOS.INSERTAR_USUARIO_CLIENTE, [idUsuario, idCli]);
+ }
+
+ // Insertar empleado
+ await conn.query(CONSULTAS_EMPLEADOS.INSERTAR_EMPLEADO, [
+ idUsuario,
+ empleado.idCliente,
+ empleado.numeroEmergencia,
+ empleado.areaTrabajo,
+ empleado.posicion,
+ parseFloat(empleado.cantidadPuntos),
+ empleado.antiguedad,
+ ]);
+
+ await conn.commit();
+ } catch (err) {
+ await conn.rollback();
+ throw new Error(`${err.message}`);
+ } finally {
+ if (conn) conn.release();
+ }
+};
diff --git a/Empleados/Datos/Repositorios/repositorioExportarEmpleado.js b/Empleados/Datos/Repositorios/repositorioExportarEmpleado.js
new file mode 100644
index 00000000..f8d7f19a
--- /dev/null
+++ b/Empleados/Datos/Repositorios/repositorioExportarEmpleado.js
@@ -0,0 +1,17 @@
+const correrQuery = require('@altertex/util/ser/correrQuery');
+const CONSULTAS_EMPLEADOS = require('@altertex/util/const/consultasEmpleados');
+
+/**
+ * Consulta la lista de empleados seleccionados de un cliente para exportar en CSV.
+ *
+ * @param {number} idCliente
+ * @param {number[]} idsEmpleado
+ * @returns {Promise>}
+ *
+ * @see [RF59 - Documentación de requisitos](https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF59)
+ */
+exports.obtenerEmpleadosExportacion = (idCliente, idsEmpleado) => {
+ const placeholders = idsEmpleado.map(() => '?').join(', ');
+ const query = CONSULTAS_EMPLEADOS.OBTENER_DATOS_EXPORTACION.replace('__IDS__', placeholders);
+ return correrQuery(query, [idCliente, ...idsEmpleado]);
+};
\ No newline at end of file
diff --git a/Empleados/Datos/Repositorios/repositorioGrupoDeEmpleados.js b/Empleados/Datos/Repositorios/repositorioGrupoDeEmpleados.js
index 184fc061..c4975405 100644
--- a/Empleados/Datos/Repositorios/repositorioGrupoDeEmpleados.js
+++ b/Empleados/Datos/Repositorios/repositorioGrupoDeEmpleados.js
@@ -3,7 +3,7 @@
* @description
* Contiene funciones para consultar y validar grupos de empleados asociados a un cliente.
* Incluye lógica para obtener todos los grupos de un cliente y verificar duplicados por nombre.
- *
+ *
* RF22 - Consulta Lista de Grupo Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF22
*/
@@ -29,6 +29,7 @@ exports.obtenerGrupoDeEmpleados = async (idCliente) => {
try {
const gruposDeEmpleados = await correrQuery(query, [idCliente]);
+
return gruposDeEmpleados;
} catch {
return [];
@@ -55,7 +56,7 @@ exports.existeGrupoConNombre = (nombreGrupo, idCliente) => {
(err, resultados) => {
if (err) return reject(err);
resolve(resultados.length > 0);
- },
+ }
);
});
-};
\ No newline at end of file
+};
diff --git a/Empleados/Datos/Repositorios/repositorioImportarEmpleado.js b/Empleados/Datos/Repositorios/repositorioImportarEmpleado.js
index d66469fa..9a0741c1 100644
--- a/Empleados/Datos/Repositorios/repositorioImportarEmpleado.js
+++ b/Empleados/Datos/Repositorios/repositorioImportarEmpleado.js
@@ -1,5 +1,5 @@
const db = require('@altertex/util/bd/db');
-const DEFAULT_ROLE_ID = 3;
+const DEFAULT_ROLE_ID = 3; // ID del rol por defecto para empleados
const CONSULTAS_IMPORTAR_EMPLEADOS = require('@altertex/util/const/consultasImportarEmpleados');
/**
* Importa en bloque múltiples empleados, creando sus usuarios, asignando rol y vinculación con clientes.
@@ -40,29 +40,29 @@ exports.importarEmpleadosMasivo = async (empleados) => {
await conn.beginTransaction();
// 1) Validar correos duplicados en bloque
- const correos = empleados.map(elemento => elemento.correoElectronico);
+ const correos = empleados.map((elemento) => elemento.correoElectronico);
const [correosExistentes] = await conn.query(
CONSULTAS_IMPORTAR_EMPLEADOS.VALIDAR_CORREOS_DUPLICADOS,
[correos]
);
if (correosExistentes.length > 0) {
- const lista = correosExistentes.map(fila => fila.correoElectronico).join(', ');
+ const lista = correosExistentes.map((fila) => fila.correoElectronico).join(', ');
throw new Error(`Correos ya registrados: ${lista}`);
}
// 2) Validar teléfonos duplicados en bloque
- const telefonos = empleados.map(elemento => elemento.numeroTelefono);
+ const telefonos = empleados.map((elemento) => elemento.numeroTelefono);
const [telefonosExistentes] = await conn.query(
CONSULTAS_IMPORTAR_EMPLEADOS.VALIDAR_TELEFONO_DUPLICADO,
[telefonos]
);
if (telefonosExistentes.length > 0) {
- const lista = telefonosExistentes.map(fila => fila.numeroTelefono).join(', ');
+ const lista = telefonosExistentes.map((fila) => fila.numeroTelefono).join(', ');
throw new Error(`Teléfonos ya registrados: ${lista}`);
}
// 3) Bulk‐insert de usuarios
- const usuariosValues = empleados.map(elemento => [
+ const usuariosValues = empleados.map((elemento) => [
elemento.nombreCompleto,
elemento.correoElectronico,
elemento.contrasena,
@@ -70,81 +70,72 @@ exports.importarEmpleadosMasivo = async (empleados) => {
elemento.direccion,
elemento.fechaNacimiento,
elemento.genero,
- elemento.estatus
+ elemento.estatus,
]);
- await conn.query(
- CONSULTAS_IMPORTAR_EMPLEADOS.INSERTAR_USUARIO_EN_VOLUMEN,
- [usuariosValues]
- );
+ await conn.query(CONSULTAS_IMPORTAR_EMPLEADOS.INSERTAR_USUARIO_EN_VOLUMEN, [usuariosValues]);
// 4) Recuperar los IDs generados
- const [rowsUsuarios] = await conn.query(
- CONSULTAS_IMPORTAR_EMPLEADOS.OBTENER_ID_GENERADOS,
- [correos]
- );
+ const [rowsUsuarios] = await conn.query(CONSULTAS_IMPORTAR_EMPLEADOS.OBTENER_ID_GENERADOS, [
+ correos,
+ ]);
const idMap = rowsUsuarios.reduce((map, row) => {
map[row.correoElectronico] = row.idUsuario;
return map;
}, {});
// 5) Bulk‐insert de roles
- const rolValues = empleados.map(elemento => [
+ const rolValues = empleados.map((elemento) => [
idMap[elemento.correoElectronico],
- DEFAULT_ROLE_ID
+ DEFAULT_ROLE_ID,
]);
- await conn.query(
- CONSULTAS_IMPORTAR_EMPLEADOS.INSERTAR_ROLES_EN_VOLUMEN,
- [rolValues]
- );
+ await conn.query(CONSULTAS_IMPORTAR_EMPLEADOS.INSERTAR_ROLES_EN_VOLUMEN, [rolValues]);
// 6) Bulk‐insert de asociaciones usuario‐cliente
const clienteValues = [];
- empleados.forEach(elemento => {
+ empleados.forEach((elemento) => {
const idU = idMap[elemento.correoElectronico];
- const listaClientes = Array.isArray(elemento.idCliente) ? elemento.idCliente : [elemento.idCliente];
- listaClientes.forEach(idCli => clienteValues.push([idU, idCli]));
+ const listaClientes = Array.isArray(elemento.idCliente)
+ ? elemento.idCliente
+ : [elemento.idCliente];
+ listaClientes.forEach((idCli) => clienteValues.push([idU, idCli]));
});
- await conn.query(
- CONSULTAS_IMPORTAR_EMPLEADOS.INSERTAR_USUARIO_CLIENTE_EN_VOLUMEN,
- [clienteValues]
- );
+ await conn.query(CONSULTAS_IMPORTAR_EMPLEADOS.INSERTAR_USUARIO_CLIENTE_EN_VOLUMEN, [
+ clienteValues,
+ ]);
// 7) Bulk‐insert de empleados
- const empValues = empleados.map(elemento => [
+ const empValues = empleados.map((elemento) => [
idMap[elemento.correoElectronico],
elemento.idCliente,
elemento.numeroEmergencia,
elemento.areaTrabajo,
elemento.posicion,
parseFloat(elemento.cantidadPuntos),
- elemento.antiguedad
+ elemento.antiguedad,
]);
- await conn.query(
- CONSULTAS_IMPORTAR_EMPLEADOS.INSERTAR_EMPLEADOS_EN_VOLUMEN,
- [empValues]
- );
+ await conn.query(CONSULTAS_IMPORTAR_EMPLEADOS.INSERTAR_EMPLEADOS_EN_VOLUMEN, [empValues]);
await conn.commit();
} catch (err) {
await conn.rollback();
const mensajeOriginal = err.message || '';
- // Detectar error por entrada duplicada
- const entradaDuplicada = mensajeOriginal.match(/Duplicate entry '(.+)' for key '(.+)'/);
+ // Detectar error por entrada duplicada
+ const entradaDuplicada = mensajeOriginal.match(/Duplicate entry '(.+)' for key '(.+)'/);
- if (entradaDuplicada) {
- const valorDuplicado = entradaDuplicada[1];
- const campo = entradaDuplicada[2];
+ if (entradaDuplicada) {
+ const valorDuplicado = entradaDuplicada[1];
+ const campo = entradaDuplicada[2];
- let campoTraducido = campo;
- if (campo.includes('correoElectronico')) campoTraducido = 'correo electrónico';
- else if (campo.includes('telefono')) campoTraducido = 'número de teléfono';
+ let campoTraducido = campo;
+ if (campo.includes('correoElectronico')) campoTraducido = 'correo electrónico';
+ else if (campo.includes('telefono')) campoTraducido = 'número de teléfono';
- throw new Error(`La entrada ${campoTraducido} "${valorDuplicado}" esta duplicada`);
- }
+ throw new Error(`La entrada ${campoTraducido} "${valorDuplicado}" esta duplicada`);
+ }
- throw new Error(`Error en importación masiva: ${mensajeOriginal}`);
+ throw new Error(`Error en importación masiva: ${mensajeOriginal}`);
} finally {
if (conn) conn.release();
}
-};
\ No newline at end of file
+};
diff --git a/Empleados/Datos/Repositorios/repositorioLeerGrupoDeEmpleados.js b/Empleados/Datos/Repositorios/repositorioLeerGrupoDeEmpleados.js
index 9ff8be9a..54831892 100644
--- a/Empleados/Datos/Repositorios/repositorioLeerGrupoDeEmpleados.js
+++ b/Empleados/Datos/Repositorios/repositorioLeerGrupoDeEmpleados.js
@@ -19,13 +19,37 @@ exports.obtenerGrupoEmpleadosPorId = async (idGrupo) => {
if (resultado.length === 0) return null;
+ // ✅ Manejo seguro de setProductosActualizar cuando es null o contiene objetos con valores null
+ const setProductosOriginal = resultado[0].setProductosActualizar || [];
+
+ // Filtrar objetos que tienen id null (cuando no hay datos reales)
+ const setProductosValidos = setProductosOriginal.filter((obj) => obj.id !== null);
+
+ const setProductosFiltrados = Object.values(
+ setProductosValidos.reduce((acc, obj) => {
+ acc[obj.id] = obj; // sobrescribe si ya existe ese id
+ return acc;
+ }, {})
+ );
+
const grupoEmpleados = {
idGrupo: resultado[0].idGrupo,
nombre: resultado[0].nombre,
descripcion: resultado[0].descripcion,
setsProductos: resultado[0].setsProductos ? resultado[0].setsProductos.split(', ') : [],
+ idsSetProductos: resultado[0].idsSetProductos
+ ? resultado[0].idsSetProductos.split(', ').map(Number)
+ : [],
empleados: resultado[0].infoEmpleados ? resultado[0].infoEmpleados.split(' || ') : [],
+ idsEmpleados: resultado[0].idsEmpleados
+ ? resultado[0].idsEmpleados.split(',').map(Number)
+ : [],
+ empleadosActualizar: (resultado[0].empleadosActualizar || []).filter(
+ (obj) => obj.id !== null
+ ),
+ setProductosActualizar: setProductosFiltrados, // Ya no necesita validación adicional
};
+
return grupoEmpleados;
} catch (error) {
console.error('Error al obtener el grupo de empleados con id:', error);
diff --git a/Empleados/Rutas/RutasIndividuales/actualizarEmpleado.routes.js b/Empleados/Rutas/RutasIndividuales/actualizarEmpleado.routes.js
index 544300ab..5b1496aa 100644
--- a/Empleados/Rutas/RutasIndividuales/actualizarEmpleado.routes.js
+++ b/Empleados/Rutas/RutasIndividuales/actualizarEmpleado.routes.js
@@ -10,14 +10,13 @@ const revisarPermisos = require('@altertex/util/inter/verificarPermisos');
const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones');
-
//RF[19] Actualizar Empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF19]
/**
* @swagger
* /api/empleados/actualizar:
* put:
- * summary: Actualiza la información de un empleado.
+ * summary: Actualiza la información de uno o varios empleados.
* tags:
* - Empleados
* security:
@@ -28,115 +27,48 @@ const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones')
* content:
* application/json:
* schema:
- * oneOf:
- * - type: object
- * description: Información del empleado a actualizar directamente en el body
- * required:
- * - idEmpleado
- * - numeroEmergencia
- * - areaTrabajo
- * - posicion
- * - cantidadPuntos
- * - antiguedad
- * properties:
- * id:
- * type: integer
- * example: 50
- * idEmpleado:
- * type: integer
- * example: 50
- * idUsuario:
- * type: integer
- * example: 30
- * nombreCompleto:
- * type: string
- * example: "Angel Romero"
- * correoElectronico:
- * type: string
- * example: "aromero@google.com"
- * numeroEmergencia:
- * type: string
- * example: "9876543214"
- * areaTrabajo:
- * type: string
- * example: "Ventas"
- * posicion:
- * type: string
- * example: "Auxiliar"
- * cantidadPuntos:
- * type: integer
- * example: 2
- * antiguedad:
- * type: string
- * example: "2000-02-10"
- * - type: object
- * properties:
- * cambios:
- * oneOf:
- * - type: object
- * description: Objeto único con información del empleado
- * required:
- * - idEmpleado
- * - numeroEmergencia
- * - areaTrabajo
- * - posicion
- * - cantidadPuntos
- * - antiguedad
- * properties:
- * idEmpleado:
- * type: integer
- * example: 50
- * idUsuario:
- * type: integer
- * example: 30
- * numeroEmergencia:
- * type: string
- * example: "9876543214"
- * areaTrabajo:
- * type: string
- * example: "Ventas"
- * posicion:
- * type: string
- * example: "Auxiliar"
- * cantidadPuntos:
- * type: integer
- * example: 2
- * antiguedad:
- * type: string
- * example: "2000-02-10"
- * - type: array
- * description: Array de objetos con información de empleados
- * items:
- * type: object
- * required:
- * - idEmpleado
- * - numeroEmergencia
- * - areaTrabajo
- * - posicion
- * - cantidadPuntos
- * - antiguedad
- * properties:
- * idEmpleado:
- * type: integer
- * example: 50
- * idUsuario:
- * type: integer
- * example: 30
- * numeroEmergencia:
- * type: string
- * example: "9876543214"
- * areaTrabajo:
- * type: string
- * example: "Ventas"
- * posicion:
- * type: string
- * example: "Auxiliar"
- * cantidadPuntos:
- * type: integer
- * example: 2
- * antiguedad:
- * type: string
- * example: "2000-02-10"
+ * type: array
+ * description: Array de objetos con información de empleados a actualizar
+ * items:
+ * type: object
+ * required:
+ * - idEmpleado
+ * - numeroEmergencia
+ * - areaTrabajo
+ * - posicion
+ * - cantidadPuntos
+ * - antiguedad
+ * properties:
+ * id:
+ * type: integer
+ * example: 50
+ * idEmpleado:
+ * type: integer
+ * example: 50
+ * idUsuario:
+ * type: integer
+ * example: 30
+ * nombreCompleto:
+ * type: string
+ * example: "Angel Romero"
+ * correoElectronico:
+ * type: string
+ * example: "aromero@google.com"
+ * numeroEmergencia:
+ * type: string
+ * example: "9876543214"
+ * areaTrabajo:
+ * type: string
+ * example: "Ventas"
+ * posicion:
+ * type: string
+ * example: "Auxiliar"
+ * cantidadPuntos:
+ * type: integer
+ * example: 2
+ * antiguedad:
+ * type: string
+ * example: "2000-02-10"
* responses:
* 200:
* description: Información del empleado actualizada correctamente.
@@ -166,12 +98,12 @@ const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones')
* - lang: JavaScript
* label: cURL
* source: |
- * # Ejemplo enviando datos directamente
+ * # Ejemplo enviando un array de empleados
* curl -X PUT "https://tu-api.com/api/empleados/actualizar" \
* -H "x-api-key: TU_API_KEY" \
* -H "Authorization: Bearer TU_TOKEN" \
* -H "Content-Type: application/json" \
- * -d '{"id":50,"idUsuario":30,"nombreCompleto":"Angel Romero","correoElectronico":"aromero@google.com","numeroEmergencia":"9876543214","areaTrabajo":"Ventas","posicion":"Auxiliar","cantidadPuntos":2,"antiguedad":"2000-02-10","idEmpleado":50}'
+ * -d '[{"id":50,"idUsuario":30,"nombreCompleto":"Angel Romero","correoElectronico":"aromero@google.com","numeroEmergencia":"9876543214","areaTrabajo":"Ventas","posicion":"Auxiliar","cantidadPuntos":2,"antiguedad":"2000-02-10","idEmpleado":50}]'
*/
ruteador.put(
diff --git a/Empleados/Rutas/RutasIndividuales/actualizarGrupoEmpleados.routes.js b/Empleados/Rutas/RutasIndividuales/actualizarGrupoEmpleados.routes.js
new file mode 100644
index 00000000..d4c530bd
--- /dev/null
+++ b/Empleados/Rutas/RutasIndividuales/actualizarGrupoEmpleados.routes.js
@@ -0,0 +1,118 @@
+const express = require('express');
+const ruteador = express.Router();
+const RUTAS = require('@altertex/util/const/rutas');
+const PERMISOS = require('@altertex/util/const/permisos');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+const controlador = require('@altertex/emp/ctrl/actualizarGrupoEmpleado.controller');
+// RF[24] Actualiza grupo empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF24]
+
+/**
+ * @swagger
+ * /api/empleados/actualizar-grupo:
+ * put:
+ * summary: Actualiza un grupo de empleados
+ * description: Actualiza el nombre, la descripción, los empleados y los sets de productos asociados a un grupo de empleados existente.
+ * tags:
+ * - Grupos de Empleados
+ * security:
+ * - ApiKeyAuth: []
+ * - BearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - idGrupoEmpleado
+ * - nombre
+ * - descripcion
+ * - empleados
+ * - setsDeProductos
+ * properties:
+ * idGrupoEmpleado:
+ * type: integer
+ * example: 1
+ * nombre:
+ * type: string
+ * example: "Grupo Administrativo"
+ * descripcion:
+ * type: string
+ * example: "Grupo para empleados administrativos"
+ * empleados:
+ * type: array
+ * items:
+ * type: integer
+ * example: [101, 102, 103]
+ * setsDeProductos:
+ * type: array
+ * items:
+ * type: integer
+ * example: [201, 202]
+ * responses:
+ * 200:
+ * description: Se actualizó correctamente el grupo de empleados.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: Se actualizo correctamente el grupo de empleados.
+ * 400:
+ * description: Error de validación o datos inválidos.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * examples:
+ * Formato inválido:
+ * value:
+ * mensaje: No se obtuvieron los datos correctamente.
+ * Verificación de empleados fallida:
+ * value:
+ * mensaje: Algunos empleados no pertenecen al mismo cliente que el grupo.
+ * Verificación de sets fallida:
+ * value:
+ * mensaje: Algunos sets de productos no pertenecen al cliente de este grupo.
+ * Actualización fallida:
+ * value:
+ * mensaje: Error actualizando el grupo de empleados.
+ * 403:
+ * description: No tiene permisos para realizar esta acción.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: No tiene permiso para consultar grupos de empleados de este cliente.
+ * 500:
+ * description: Error interno del servidor.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: Ocurrió un error al actualizar el grupo de empleados.
+ */
+ruteador.put(
+ RUTAS.EMPLEADOS.ACTUALIZAR_GRUPO_EMPLEADO,
+ revisarApiKey(),
+ autorizarToken,
+ verificarPermisos(PERMISOS.ACTUALIZAR_GRUPO_EMPLEADOS),
+ validarYSanitizar,
+ controlador.actualizarGrupoEmpleados
+);
+
+module.exports = ruteador;
diff --git a/Empleados/Rutas/RutasIndividuales/crearEmpleado.routes.js b/Empleados/Rutas/RutasIndividuales/crearEmpleado.routes.js
new file mode 100644
index 00000000..f883d48f
--- /dev/null
+++ b/Empleados/Rutas/RutasIndividuales/crearEmpleado.routes.js
@@ -0,0 +1,204 @@
+//RF16 - Crear Empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF16]
+
+const express = require('express');
+const ruteador = express.Router();
+
+const controlador = require('@altertex/emp/ctrl/crearEmpleado.controller');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones');
+
+/**
+ * @swagger
+ * /api/empleados/crear:
+ * post:
+ * summary: Crea un nuevo empleado.
+ * tags:
+ * - Empleados
+ * security:
+ * - ApiKeyAuth: []
+ * - BearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * oneOf:
+ * - type: object
+ * description: Información del nuevo empleado a crear directamente en el body
+ * required:
+ * - idUsuario
+ * - idCliente
+ * - nombreCompleto
+ * - correoElectronico
+ * - numeroEmergencia
+ * - areaTrabajo
+ * - posicion
+ * - cantidadPuntos
+ * - antiguedad
+ * properties:
+ * idUsuario:
+ * type: integer
+ * example: 30
+ * idCliente:
+ * type: integer
+ * example: 10
+ * nombreCompleto:
+ * type: string
+ * example: "Carlos Pérez"
+ * correoElectronico:
+ * type: string
+ * example: "cperez@google.com"
+ * numeroEmergencia:
+ * type: string
+ * example: "9876543214"
+ * areaTrabajo:
+ * type: string
+ * example: "Producción"
+ * posicion:
+ * type: string
+ * example: "Operador"
+ * cantidadPuntos:
+ * type: integer
+ * example: 0
+ * antiguedad:
+ * type: string
+ * example: "2024-01-01"
+ * - type: object
+ * properties:
+ * empleados:
+ * oneOf:
+ * - type: object
+ * description: Array único con información del empleado
+ * required:
+ * - idUsuario
+ * - idCliente
+ * - nombreCompleto
+ * - correoElectronico
+ * - numeroEmergencia
+ * - areaTrabajo
+ * - posicion
+ * - cantidadPuntos
+ * - antiguedad
+ * properties:
+ * idUsuario:
+ * type: integer
+ * example: 30
+ * idCliente:
+ * type: integer
+ * example: 10
+ * nombreCompleto:
+ * type: string
+ * example: "Carlos Pérez"
+ * correoElectronico:
+ * type: string
+ * example: "cperez@google.com"
+ * numeroEmergencia:
+ * type: string
+ * example: "9876543214"
+ * areaTrabajo:
+ * type: string
+ * example: "Producción"
+ * posicion:
+ * type: string
+ * example: "Operador"
+ * cantidadPuntos:
+ * type: integer
+ * example: 0
+ * antiguedad:
+ * type: string
+ * example: "2024-01-01"
+ * - type: array
+ * description: Array de objetos con información de empleados
+ * items:
+ * type: object
+ * required:
+ * - idUsuario
+ * - idCliente
+ * - nombreCompleto
+ * - correoElectronico
+ * - numeroEmergencia
+ * - areaTrabajo
+ * - posicion
+ * - cantidadPuntos
+ * - antiguedad
+ * properties:
+ * idUsuario:
+ * type: integer
+ * example: 30
+ * idCliente:
+ * type: integer
+ * example: 10
+ * nombreCompleto:
+ * type: string
+ * example: "Carlos Pérez"
+ * correoElectronico:
+ * type: string
+ * example: "cperez@google.com"
+ * numeroEmergencia:
+ * type: string
+ * example: "9876543214"
+ * areaTrabajo:
+ * type: string
+ * example: "Producción"
+ * posicion:
+ * type: string
+ * example: "Operador"
+ * cantidadPuntos:
+ * type: integer
+ * example: 0
+ * antiguedad:
+ * type: string
+ * example: "2024-01-01"
+ * responses:
+ * 201:
+ * description: Empleado creado correctamente.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: "Empleado creado exitosamente."
+ * datos:
+ * type: array
+ * items:
+ * type: object
+ * 400:
+ * description: Error en los datos enviados o en la creación.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: "Error al crear empleado"
+ * x-codeSamples:
+ * - lang: JavaScript
+ * label: cURL
+ * source: |
+ * # Ejemplo enviando datos directamente
+ * curl -X POST "https://tu-api.com/api/empleados/crear" \
+ * -H "x-api-key: TU_API_KEY" \
+ * -H "Authorization: Bearer TU_TOKEN" \
+ * -H "Content-Type: application/json" \
+ * -d '{"idUsuario":30,"idCliente":10,"nombreCompleto":"Carlos Pérez","correoElectronico":"cperez@google.com","numeroEmergencia":"9876543214","areaTrabajo":"Producción","posicion":"Operador","cantidadPuntos":0,"antiguedad":"2024-01-01"}'
+ */
+
+ruteador.post(
+ RUTAS.EMPLEADOS.CREAR,
+ revisarApiKey(),
+ autorizarToken,
+ limitePeticionesDiarias,
+ validarYSanitizar,
+ verificarPermisos(PERMISOS.CREAR_EMPLEADO),
+ controlador.crearEmpleado
+);
+
+module.exports = ruteador;
diff --git a/Empleados/Rutas/RutasIndividuales/exportarEmpleados.routes.js b/Empleados/Rutas/RutasIndividuales/exportarEmpleados.routes.js
new file mode 100644
index 00000000..a51ccfe8
--- /dev/null
+++ b/Empleados/Rutas/RutasIndividuales/exportarEmpleados.routes.js
@@ -0,0 +1,77 @@
+//RF59 - Exportar Empleados- https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF59
+
+/**
+ * @swagger
+ * /api/empleados/exportar:
+ * post:
+ * summary: Exporta la lista completa de empleados en formato CSV.
+ * tags:
+ * - Empleados
+ * security:
+ * - ApiKeyAuth: []
+ * - BearerAuth: []
+ * parameters:
+ * - in: header
+ * name: x-api-key
+ * required: true
+ * schema:
+ * type: string
+ * description: Clave de API
+ * - in: header
+ * name: Authorization
+ * required: true
+ * schema:
+ * type: string
+ * description: Token JWT con formato "Bearer "
+ * responses:
+ * 200:
+ * description: Archivo CSV generado correctamente.
+ * content:
+ * text/csv:
+ * schema:
+ * type: string
+ * format: binary
+ * 204:
+ * description: No hay empleados para exportar.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: No hay empleados para exportar.
+ * 400:
+ * description: Error interno del servidor al exportar empleados.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: Error al exportar la lista de empleados.
+ */
+
+const express = require('express');
+const ruteador = express.Router();
+const controlador = require('@altertex/emp/ctrl/exportarEmpleados.controller');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones');
+
+ruteador.post(
+ RUTAS.EMPLEADOS.EXPORTAR_EMPLEADOS,
+ revisarApiKey(),
+ autorizarToken,
+ limitePeticionesDiarias,
+ validarYSanitizar,
+ verificarPermisos(PERMISOS.EXPORTAR_EMPLEADOS),
+ controlador.exportarEmpleados
+);
+
+module.exports = ruteador;
\ No newline at end of file
diff --git a/Empleados/Rutas/indexEmpleados.routes.js b/Empleados/Rutas/indexEmpleados.routes.js
index f9ff9fe4..7f39b6f5 100644
--- a/Empleados/Rutas/indexEmpleados.routes.js
+++ b/Empleados/Rutas/indexEmpleados.routes.js
@@ -1,14 +1,16 @@
const express = require('express');
const ruteador = express.Router();
const rutasConsultarListaGrupos = require('@altertex/emp/rutasInd/consultarListaGrupos.routes');
+const rutasCrearEmpleado = require('@altertex/emp/rutasInd/crearEmpleado.routes');
const rutasConsultarLista = require('@altertex/emp/rutasInd/consultarLista.routes');
const rutasEliminarGrupo = require('@altertex/emp/rutasInd/eliminarGrupoEmpleados.routes');
const rutasEliminarEmpleado = require('@altertex/emp/rutasInd/eliminarEmpleado.routes');
const rutasImportarEmpleados = require('@altertex/emp/rutasInd/importarEmpleados.routes');
+const rutasExportarEmpleados = require('@altertex/emp/rutasInd/exportarEmpleados.routes');
const rutasLeerGrupoEmpleados = require('@altertex/emp/rutasInd/leerGrupoEmpleados.routes');
const rutasCrearGrupo = require('@altertex/emp/rutasInd/crearGrupoEmpleados.routes');
-
const rutasActualizarEmpleado = require('@altertex/emp/rutasInd/actualizarEmpleado.routes');
+const rutasActualizarGrupo = require('@altertex/emp/rutasInd/actualizarGrupoEmpleados.routes');
const RUTAS = require('@altertex/util/const/rutas');
@@ -26,7 +28,12 @@ ruteador.use(RUTAS.EMPLEADOS.BASE, rutasImportarEmpleados);
ruteador.use(RUTAS.EMPLEADOS.BASE, rutasLeerGrupoEmpleados);
//RF21 - Crear Grupo de Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF21
ruteador.use(RUTAS.EMPLEADOS.BASE, rutasCrearGrupo);
-
//RF19 - Actualizar Empleado - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF19
ruteador.use(RUTAS.EMPLEADOS.BASE, rutasActualizarEmpleado);
+//RF24 - Actualizar Grupo de Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF24
+ruteador.use(RUTAS.EMPLEADOS.BASE, rutasActualizarGrupo);
+//RF59 - Exportar Empleados- https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF59
+ruteador.use(RUTAS.EMPLEADOS.BASE, rutasExportarEmpleados);
+//RF16 - Crear Empleado - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF16
+ruteador.use(RUTAS.EMPLEADOS.BASE, rutasCrearEmpleado);
module.exports = ruteador;
diff --git a/Eventos/Controladores/consultarEvento.controller.js b/Eventos/Controladores/consultarEvento.controller.js
index d08c37c8..b7503273 100644
--- a/Eventos/Controladores/consultarEvento.controller.js
+++ b/Eventos/Controladores/consultarEvento.controller.js
@@ -12,6 +12,7 @@ const MENSAJES_EVENTOS = require('@altertex/util/const/mensajesEventos');
* @returns {Promise} Responde con el evento encontrado o un mensaje de error.
*
* @see [RF38 Leer evento](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF38)
+ *
*/
exports.consultarEvento = async (req, res) => {
const idEvento = parseInt(req.body.idEvento);
diff --git a/Eventos/Controladores/crearEvento.controller.js b/Eventos/Controladores/crearEvento.controller.js
new file mode 100644
index 00000000..fa769be6
--- /dev/null
+++ b/Eventos/Controladores/crearEvento.controller.js
@@ -0,0 +1,74 @@
+// RF36 - Crear Evento - [https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF36]
+
+const repositorio = require('@altertex/eve/repos/repositorioCrearEvento');
+const MENSAJES_EVENTOS = require('@altertex/util/const/mensajesEventos');
+
+/**
+ * Controlador para crear un nuevo evento.
+ *
+ * @param {object} req - Objeto de solicitud de Express.
+ * @param {object} req.body - Cuerpo de la solicitud que contiene los datos del evento.
+ * @param {string} req.body.idCliente - ID del cliente asociado al evento.
+ * @param {string} req.body.nombre - Nombre del evento.
+ * @param {string} req.body.descripcion - Descripción del evento.
+ * @param {number} req.body.puntos - Puntos otorgados por el evento.
+ * @param {number} req.body.multiplicador - Multiplicador de puntos del evento.
+ * @param {string} req.body.periodoRenovacion - Periodo de renovación del evento.
+ * @param {boolean} req.body.renovacion - Indica si el evento se renueva automáticamente.
+ * @param {object} res - Objeto de respuesta de Express.
+ *
+ * @returns {object} - Respuesta JSON con el resultado de la operación.
+ */
+exports.crearEvento = async (req, res) => {
+ try {
+ const { idCliente, nombre, descripcion, puntos, multiplicador, periodoRenovacion, renovacion } = req.body;
+
+ // Validaciones básicas de campos requeridos (descripcion y periodoRenovacion son opcionales)
+ if (!idCliente || !nombre || !puntos || !multiplicador) {
+
+ return res.status(MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo).json({
+ codigo: MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo,
+ mensaje: MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.mensaje,
+ });
+ }
+
+ // Validar los datos de entrada
+ const idClienteNum = parseInt(idCliente, 10);
+ const puntosNum = parseFloat(puntos);
+ const multiplicadorNum = parseFloat(multiplicador);
+
+ // Validaciones de formato
+ if (isNaN(idClienteNum) || idClienteNum <= 0) {
+ return res.status(MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo).json({
+ codigo: MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo,
+ mensaje: 'El ID del cliente debe ser un número válido mayor a 0.',
+ });
+ }
+
+ const nuevoEvento = {
+ idCliente: idClienteNum,
+ nombre,
+ descripcion: descripcion && descripcion.trim() !== '' ? descripcion : null,
+ puntos: puntosNum,
+ multiplicador: multiplicadorNum,
+ periodoRenovacion: periodoRenovacion && periodoRenovacion.trim() !== '' ? periodoRenovacion : null,
+ renovacion: renovacion ? 1 : 0,
+ };
+
+ const resultado = await repositorio.crearEvento(nuevoEvento);
+
+ // Verificar si el evento fue creado exitosamente
+ return res.status(MENSAJES_EVENTOS.EVENTO_CREADO.codigo).json({
+ codigo: MENSAJES_EVENTOS.EVENTO_CREADO.codigo,
+ mensaje: MENSAJES_EVENTOS.EVENTO_CREADO.mensaje,
+ evento: resultado?.evento || resultado,
+ });
+
+ } catch (error) {
+ // Usar el mensaje personalizado del error en la respuesta
+ return res.status(MENSAJES_EVENTOS.ERROR_CREAR_EVENTO.codigo).json({
+ codigo: MENSAJES_EVENTOS.ERROR_CREAR_EVENTO.codigo,
+ mensaje: error.message || MENSAJES_EVENTOS.ERROR_CREAR_EVENTO.mensaje,
+ });
+ }
+};
diff --git a/Eventos/Datos/Repositorios/repositorioCrearEvento.js b/Eventos/Datos/Repositorios/repositorioCrearEvento.js
new file mode 100644
index 00000000..55d09b47
--- /dev/null
+++ b/Eventos/Datos/Repositorios/repositorioCrearEvento.js
@@ -0,0 +1,64 @@
+// RF36 - Crear Evento - [https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF36]
+
+const correrQuery = require('@altertex/util/ser/correrQuery');
+const CONSULTAS_EVENTOS = require('@altertex/util/const/consultasEventos');
+const MENSAJES_EVENTOS = require('@altertex/util/const/mensajesEventos');
+
+/**
+ * Crea un nuevo evento en la base de datos
+ * @function crearEvento
+ * @param {object} evento - Objeto que contiene los datos del evento a crear
+ * @param {number} evento.idCliente - ID del cliente asociado al evento
+ * @param {string} evento.nombre - Nombre del evento
+ * @param {string} evento.descripcion - Descripción del evento
+ * @param {number} evento.puntos - Puntos asociados al evento
+ * @param {number} evento.multiplicador - Multiplicador del evento
+ * @param {number} evento.periodoRenovacion - Periodo de renovación del evento
+ * @param {number} evento.renovacion - Renovación del evento
+ * @returns {object} - Resultado de la operación de creación
+ * @throws {Error} - Error personalizado según el problema encontrado
+ */
+exports.crearEvento = async ({
+ idCliente,
+ nombre,
+ descripcion,
+ puntos,
+ multiplicador,
+ periodoRenovacion,
+ renovacion,
+}) => {
+ try {
+ const query = CONSULTAS_EVENTOS.CREAR_EVENTO;
+
+ const resultado = await correrQuery(query, [
+ idCliente,
+ nombre,
+ descripcion,
+ puntos,
+ multiplicador,
+ periodoRenovacion,
+ renovacion,
+ ]);
+
+ // Si no hay resultado
+ if (!resultado) {
+ throw new Error(MENSAJES_EVENTOS.ERROR_INESPERADO.mensaje);
+ }
+
+ return resultado;
+ } catch (error) {
+ // Si es un error que ya hemos generado, lo lanzamos tal cual
+ const mensajesError = Object.values(MENSAJES_EVENTOS).map(msg => msg.mensaje);
+ if (mensajesError.includes(error.message)) {
+ throw error;
+ }
+
+ // Interpretamos posibles errores SQL - solo nos interesa si el cliente no existe
+ if (error.message.includes('foreign key constraint') || error.message.includes('FOREIGN KEY')) {
+ throw new Error(MENSAJES_EVENTOS.ERROR_CLIENTE_NO_EXISTE.mensaje);
+ }
+
+ // Si es otro tipo de error, lanzamos un error genérico
+ throw new Error(`${MENSAJES_EVENTOS.ERROR_INESPERADO.mensaje}: ${error.message}`);
+ }
+};
diff --git a/Eventos/Rutas/RutasIndividuales/consultarEvento.routes.js b/Eventos/Rutas/RutasIndividuales/consultarEvento.routes.js
index 42da867c..94653d09 100644
--- a/Eventos/Rutas/RutasIndividuales/consultarEvento.routes.js
+++ b/Eventos/Rutas/RutasIndividuales/consultarEvento.routes.js
@@ -8,7 +8,6 @@ const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones');
-
const PERMISOS = require('@altertex/util/const/permisos');
const RUTAS = require('@altertex/util/const/rutas');
diff --git a/Eventos/Rutas/RutasIndividuales/crearEvento.routes.js b/Eventos/Rutas/RutasIndividuales/crearEvento.routes.js
new file mode 100644
index 00000000..97feadf0
--- /dev/null
+++ b/Eventos/Rutas/RutasIndividuales/crearEvento.routes.js
@@ -0,0 +1,85 @@
+// RF36 - Crear Evento - [https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF36]
+const express = require('express');
+const ruteador = express.Router();
+const controlador = require('@altertex/eve/ctrl/crearEvento.controller');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+
+/**
+ * @swagger
+ * /api/eventos/crear:
+ * post:
+ * summary: Crea un nuevo evento.
+ * description: Este endpoint permite crear un nuevo evento en el sistema.
+ * tags: [Eventos]
+ * security:
+ * - ApiKeyAuth: []
+ * - BearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * idCliente:
+ * type: string
+ * description: ID del cliente asociado al evento.
+ * example: 101
+ * nombre:
+ * type: string
+ * description: Nombre del evento.
+ * example: Evento de prueba
+ * descripcion:
+ * type: string
+ * description: Descripción del evento.
+ * example: Este es un evento de prueba.
+ * puntos:
+ * type: number
+ * format: double
+ * description: Puntos otorgados por el evento.
+ * example: 10.5
+ * multiplicador:
+ * type: number
+ * format: double
+ * description: Multiplicador de puntos del evento.
+ * example: 1.5
+ * periodoRenovacion:
+ * type: string
+ * description: Periodo de renovación del evento.
+ * example: mensual
+ * renovacion:
+ * type: boolean
+ * description: Indica si el evento se renueva automáticamente.
+ * example: true
+ * responses:
+ * 200:
+ * description: Evento creado exitosamente.
+ * 400:
+ * description: Solicitud incorrecta.
+ * 401:
+ * description: No autorizado.
+ * 403:
+ * description: Prohibido.
+ * 404:
+ * description: No encontrado.
+ * 500:
+ * description: Error interno del servidor.
+ */
+ruteador.post(
+ RUTAS.EVENTOS.CREAR,
+ validarYSanitizar,
+ revisarApiKey(),
+ autorizarToken,
+ limitePeticionesDiarias,
+ verificarPermisos(PERMISOS.CREAR_EVENTO),
+ controlador.crearEvento
+);
+
+module.exports = ruteador;
diff --git a/Eventos/Rutas/indexEventos.routes.js b/Eventos/Rutas/indexEventos.routes.js
index e6c02353..e3aef2a4 100644
--- a/Eventos/Rutas/indexEventos.routes.js
+++ b/Eventos/Rutas/indexEventos.routes.js
@@ -1,5 +1,6 @@
const express = require('express');
const ruteador = express.Router();
+const crearEvento = require('@altertex/eve/rutasInd/crearEvento.routes');
const rutasConsultarListaEventos = require('@altertex/eve/rutasInd/consultarListaEventos.routes');
const rutasConsultarEvento = require('@altertex/eve/rutasInd/consultarEvento.routes');
const rutasEliminarEvento = require('@altertex/eve/rutasInd/eliminarEvento.routes');
@@ -11,6 +12,7 @@ const rutasEliminarEvento = require('@altertex/eve/rutasInd/eliminarEvento.route
const RUTAS = require('@altertex/util/const/rutas');
// Configuración de las rutas específicas para eventos
+ruteador.use(RUTAS.EVENTOS.BASE, crearEvento);
ruteador.use(RUTAS.EVENTOS.BASE, rutasConsultarListaEventos);
ruteador.use(RUTAS.EVENTOS.BASE, rutasEliminarEvento);
ruteador.use(RUTAS.EVENTOS.BASE, rutasConsultarEvento);
diff --git a/Pedidos/Controladores/actualizarPedido.controller.js b/Pedidos/Controladores/actualizarPedido.controller.js
new file mode 100644
index 00000000..299ddbd4
--- /dev/null
+++ b/Pedidos/Controladores/actualizarPedido.controller.js
@@ -0,0 +1,47 @@
+const MENSAJES = require('@altertex/util/const/mensajesPedidos');
+const repositorio = require('@altertex/pedidos/repos/repositorioActualizarPedido');
+
+/**
+ * RF[62] - Actualizar Pedido [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF62]
+ * Controlador para actualizar la información de uno o varios pedidos.
+ *
+ * Respuestas posibles:
+ * - 400 si faltan datos en el cuerpo de la solicitud.
+ * - 200 si el pedido se actualiza correctamente.
+ *
+ * @async
+ * @function actualizarGrupoEmpleados
+ * @param {Express.Request} req - Objeto de solicitud HTTP de Express.
+ * @param {Express.Response} res - Objeto de respuesta HTTP de Express.
+ * @returns {Promise} La respuesta HTTP con el estado y mensaje correspondiente.
+ */
+exports.actualizarPedido = async (req, res) => {
+ let datos;
+ if (req.body.idPedido) {
+ datos = [req.body];
+ } else if (req.body.cambios) {
+ datos = Array.isArray(req.body.cambios) ? req.body.cambios : [req.body.cambios];
+ } else {
+ return res.status(MENSAJES.ERROR_ACTUALIZAR_PEDIDO.codigo).json({
+ mensaje: MENSAJES.ERROR_ACTUALIZAR_PEDIDO.mensaje,
+ });
+ }
+
+ if (!datos || datos.length === 0) {
+ return res.status(MENSAJES.ERROR_ACTUALIZAR_PEDIDO.codigo).json({
+ mensaje: MENSAJES.ERROR_ACTUALIZAR_PEDIDO.mensaje,
+ });
+ }
+
+ try {
+ await repositorio.actualizarPedido(datos);
+ return res.status(MENSAJES.PEDIDO_ACTUALIZADO.codigo).json({
+ mensaje: MENSAJES.PEDIDO_ACTUALIZADO.mensaje,
+ datos,
+ });
+ } catch {
+ return res.status(MENSAJES.ERROR_ACTUALIZAR_PEDIDO.codigo).json({
+ mensaje: MENSAJES.ERROR_ACTUALIZAR_PEDIDO.mensaje,
+ });
+ }
+};
diff --git a/Pedidos/Datos/Repositorios/repositorioActualizarPedido.js b/Pedidos/Datos/Repositorios/repositorioActualizarPedido.js
new file mode 100644
index 00000000..002d898a
--- /dev/null
+++ b/Pedidos/Datos/Repositorios/repositorioActualizarPedido.js
@@ -0,0 +1,50 @@
+const correrQuery = require('@altertex/util/ser/correrQuery');
+const MENSAJES = require('@altertex/util/const/mensajesPedidos');
+const CONSULTAS = require('@altertex/util/const/consultasPedidos');
+
+//RF[62] - Actualizar Pedido - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF62]
+
+/**
+ * Actualiza uno o varios pedidos en la base de datos con nueva información
+ * de estado, precio total, ID de envío y ID de pago.
+ *
+ * Esta función realiza:
+ * - La validación de que se proporcionen datos para actualizar.
+ * - La actualización simultánea de múltiples pedidos usando Promise.all.
+ * - El manejo de errores durante el proceso de actualización.
+ *
+ * La función procesa cada pedido de forma paralela, actualizando sus campos
+ * mediante una consulta SQL preparada con los parámetros proporcionados.
+ *
+ * @async
+ * @function actualizarPedido
+ * @param {object[]} datos - Array de objetos con los datos de los pedidos a actualizar.
+ * @param {number} datos[].idPedido - ID único del pedido a actualizar.
+ * @param {string|number} datos[].estado - Nuevo estado del pedido.
+ * @param {number} datos[].precioTotal - Nuevo precio total del pedido.
+ * @param {number} datos[].idEnvio - ID del envío asociado al pedido.
+ * @param {number} datos[].idPago - ID del pago asociado al pedido.
+ * @throws {Error} 'Sin datos para actualizar.' - Si el array está vacío o no es válido.
+ * @throws {Error} Mensaje de error específico desde MENSAJES.ERROR_ACTUALIZAR_PEDIDO - Si ocurre algún error durante la actualización.
+ */
+exports.actualizarPedido = async (datos) => {
+ if (!Array.isArray(datos) || datos.length === 0) {
+ throw new Error('Sin datos para actualizar.');
+ }
+
+ try {
+ await Promise.all(
+ datos.map(({ idPedido, estado, precioTotal, idEnvio, idPago }) => {
+ return correrQuery(CONSULTAS.ACTUALIZAR_PEDIDO, [
+ estado,
+ precioTotal,
+ idPago,
+ idEnvio,
+ idPedido,
+ ]);
+ })
+ );
+ } catch {
+ throw new Error(MENSAJES.ERROR_ACTUALIZAR_PEDIDO.mensaje);
+ }
+};
diff --git a/Pedidos/Rutas/RutasIndividuales/actualizarPedidos.routes.js b/Pedidos/Rutas/RutasIndividuales/actualizarPedidos.routes.js
new file mode 100644
index 00000000..90ba4c2a
--- /dev/null
+++ b/Pedidos/Rutas/RutasIndividuales/actualizarPedidos.routes.js
@@ -0,0 +1,24 @@
+const express = require('express');
+const ruteador = express.Router();
+
+const controlador = require('@altertex/pedidos/ctrl/actualizarPedido.controller');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+const limitePeticiones = require('@altertex/util/inter/limitePeticiones');
+const RUTAS = require('@altertex/util/const/rutas');
+const PERMISOS = require('@altertex/util/const/permisos');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+
+// RF[62] - Actualizar Pedido
+ruteador.put(
+ RUTAS.PEDIDOS.ACTUALIZAR_PEDIDO,
+ revisarApiKey(),
+ autorizarToken,
+ limitePeticiones,
+ verificarPermisos(PERMISOS.ACTUALIZAR_PEDIDO),
+ validarYSanitizar,
+ controlador.actualizarPedido
+);
+
+module.exports = ruteador;
diff --git a/Pedidos/Rutas/indexPedidos.routes.js b/Pedidos/Rutas/indexPedidos.routes.js
index 83334c54..16647e25 100644
--- a/Pedidos/Rutas/indexPedidos.routes.js
+++ b/Pedidos/Rutas/indexPedidos.routes.js
@@ -2,9 +2,11 @@ const express = require('express');
const ruteador = express.Router();
const rutasObtenerPedidos = require('@altertex/pedidos/rutasInd/obtenerPedidos.routes');
const rutasEliminarPedido = require('@altertex/pedidos/rutasInd/eliminarPedidos.routes');
+const rutasActualizarPedidos = require('@altertex/pedidos/rutasInd/actualizarPedidos.routes');
const RUTAS = require('@altertex/util/const/rutas');
ruteador.use(RUTAS.PEDIDOS.BASE, rutasObtenerPedidos);
ruteador.use(RUTAS.PEDIDOS.BASE, rutasEliminarPedido);
+ruteador.use(RUTAS.PEDIDOS.BASE, rutasActualizarPedidos);
module.exports = ruteador;
diff --git a/Productos/Controladores/crearProducto.controller.js b/Productos/Controladores/crearProducto.controller.js
index 8852ef24..f78d11a0 100644
--- a/Productos/Controladores/crearProducto.controller.js
+++ b/Productos/Controladores/crearProducto.controller.js
@@ -103,11 +103,11 @@ exports.crearProducto = [
// Upload image processing remains unchanged
const urlImagenProductoPromise = imagenProducto
? enviarS3({
- Bucket: process.env.AWS_BUCKET_NAME,
- Key: `productos/${imagenProducto.originalname}`,
- Body: imagenProducto.buffer,
- ContentType: imagenProducto.mimetype,
- })
+ Bucket: process.env.AWS_BUCKET_NAME,
+ Key: `productos/${imagenProducto.originalname}`,
+ Body: imagenProducto.buffer,
+ ContentType: imagenProducto.mimetype,
+ })
: Promise.resolve(null);
// prettier-ignore
@@ -176,4 +176,4 @@ exports.crearProducto = [
if (conexion) conexion.release();
}
},
-];
\ No newline at end of file
+];
diff --git a/Productos/Controladores/eliminarProducto.controller.js b/Productos/Controladores/eliminarProducto.controller.js
index bbcdd56f..8e33cca8 100644
--- a/Productos/Controladores/eliminarProducto.controller.js
+++ b/Productos/Controladores/eliminarProducto.controller.js
@@ -1,6 +1,5 @@
// Importación de la función que elimina productos en el repositorio de datos.
const { eliminarProductos } = require('@altertex/pro/repos/productosRepositorio');
-const eliminarImagenS3 = require('@altertex/util/ser/eliminarImagenS3');
// Importación de las constantes de mensajes utilizados para respuestas del módulo de productos.
const {
@@ -30,7 +29,7 @@ const {
*/
const eliminarProductoController = async (req, res) => {
try {
- const { ids, imagenes } = req.body;
+ const { ids } = req.body;
// Validación de los IDs recibidos.
if (!Array.isArray(ids) || ids.length === 0) {
@@ -39,17 +38,8 @@ const eliminarProductoController = async (req, res) => {
mensaje: 'Debes proporcionar al menos un ID de producto para eliminar.',
});
}
-
- // Validación de las imágenes recibidas.
- if (Array.isArray(imagenes)) {
- imagenes.forEach((url) => {
- const parts = url.split('/');
- const filename = parts[parts.length - 1];
- eliminarImagenS3('productos/', filename);
- });
- }
// Se realiza la eliminación de los productos.
- const resultado = await eliminarProductos(ids, imagenes);
+ const resultado = await eliminarProductos(ids);
// Se responde dependiendo del éxito o fallo de la operación.
if (resultado) {
diff --git a/Productos/Controladores/exportarProductos.controller.js b/Productos/Controladores/exportarProductos.controller.js
new file mode 100644
index 00000000..ebdd243c
--- /dev/null
+++ b/Productos/Controladores/exportarProductos.controller.js
@@ -0,0 +1,49 @@
+const repositorio = require('@altertex/pro/repos/repositorioExportarProducto');
+const MENSAJES_PRODUCTOS = require('@altertex/util/const/mensajesProductos');
+
+/**
+ * Controlador para exportar productos seleccionados de un cliente y retornar Excel.
+ *
+ * @async
+ * @function exportarProductos
+ * @param {Request} req
+ * @param {Response} res
+ * @returns {Response} Archivo Excel con los productos exportados
+ * @see [RF58 - Exportar Productos](https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF58)
+ */
+exports.exportarProductos = async (req, res) => {
+ try {
+ const idCliente = parseInt(req.user.clienteSeleccionado);
+ const idsProducto = req.body.idsProducto;
+
+ if (!Array.isArray(idsProducto) || idsProducto.length === 0) {
+ return res.status(MENSAJES_PRODUCTOS.PARAMETROS_INVALIDOS.codigo).json({
+ mensaje: 'Debes seleccionar al menos un producto para exportar.',
+ });
+ }
+
+ const idsSeleccionados = idsProducto.map((id) => parseInt(id));
+ const productos = await repositorio.obtenerProductosExportacion(idCliente, idsSeleccionados);
+
+ if (!productos || productos.length === 0) {
+ return res.status(MENSAJES_PRODUCTOS.PRODUCTOS_NO_ENCONTRADOS.codigo).json({
+ mensaje: MENSAJES_PRODUCTOS.PRODUCTOS_NO_ENCONTRADOS.mensaje,
+ });
+ }
+
+ const buffer = await repositorio.generarArchivoExcel(productos);
+
+ res.setHeader('Content-Disposition', 'attachment; filename=productos.xlsx');
+ res.setHeader(
+ 'Content-Type',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+ );
+
+ return res.send(buffer);
+ } catch (error) {
+ console.error('Error al exportar productos:', error);
+ return res.status(MENSAJES_PRODUCTOS.ERROR_EXPORTACION.codigo).json({
+ mensaje: MENSAJES_PRODUCTOS.ERROR_EXPORTACION.mensaje,
+ });
+ }
+};
diff --git a/Productos/Controladores/importarProductos.controller.js b/Productos/Controladores/importarProductos.controller.js
new file mode 100644
index 00000000..35c59d7a
--- /dev/null
+++ b/Productos/Controladores/importarProductos.controller.js
@@ -0,0 +1,141 @@
+const validarVariante = require('@altertex/util/vali/validarVarianteImportar');
+const validarOpcionesImportar = require('@altertex/util/vali/validarOpcionesImportar');
+const repositorioCrearProducto = require('@altertex/pro/repos/repositorioCrearProducto');
+const repositorioCrearVariante = require('@altertex/pro/repos/repositorioCrearVariante');
+const repositorioCrearOpcion = require('@altertex/pro/repos/repositorioCrearOpcion');
+const db = require('@altertex/util/bd/db');
+const validarProductoImportado = require('@altertex/util/vali/validarProductoImportado');
+const { crearGeneradorSKUConsecutivo } = require('@altertex/util/inter/generarSKUAuto');
+const { proveedorExiste } = require('@altertex/pro/repos/repositorioValidarProveedor');
+
+
+/**
+ * Importa productos y sus variantes/opciones para un cliente.
+ *
+ * Espera en req.body un array de objetos con la forma:
+ * [
+ * {
+ * producto: { ... },
+ * variantes: [
+ * {
+ * ...,
+ * opciones: { ... }
+ * }
+ * ]
+ * }
+ * ]
+ *
+ * Valida cada producto, variante y opciones antes de insertar en la base de datos.
+ * Si hay errores en alguna fila, los acumula y los devuelve al finalizar.
+ *
+ * @async
+ * @function importarProductos
+ * @param {Express.Request} req - Request de Express, requiere req.user.clienteSeleccionado y req.body.
+ * @param { Express.Response} res - Response de Express.
+ * @returns {Promise} Devuelve un JSON con el resultado de la importación y los errores encontrados.
+ *
+ * @see RF[56] Leer producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF56]
+ */
+exports.importarProductos = async (req, res) => {
+ const idCliente = parseInt(req.user.clienteSeleccionado);
+ const productos = req.body;
+
+ const MENSAJE_PRODUCTOS_INVALIDOS = 'No se recibieron productos válidos.'
+ const MENSAJE_VARIANTES_INVALIDAS = 'No se recibieron productos válidos.'
+ const MENSAJE_ERRORES_ARCHIVO = 'Se encontraron errores en el archivo.'
+ const IMPORTACION_EXITOSA = 'Importación completada exitosamente.';
+ const ERROR_AL_IMPORTAR = 'Error al importar productos.';
+
+ if (!Array.isArray(productos) || productos.length === 0) {
+ return res.status(400).json({ mensaje: MENSAJE_PRODUCTOS_INVALIDOS});
+ }
+
+ const errores = [];
+ let conexion = null;
+
+ try {
+ conexion = await db.getConnection();
+ await conexion.beginTransaction();
+
+ for (let im = 0; im < productos.length; im += 1) {
+ const { producto, variantes } = productos[im];
+ const fila = im + 1;
+
+ const errorProducto = validarProductoImportado(producto);
+ if (errorProducto) {
+ errores.push({ fila, error: errorProducto.error });
+ continue;
+ }
+ if (producto.idProveedor !== null) {
+ const existe = await proveedorExiste(conexion, producto.idProveedor);
+ if (!existe) {
+ errores.push({ fila, error: `idProveedor ${producto.idProveedor} no existe en la base de datos.` });
+ continue;
+ }
+ }
+
+ if (!Array.isArray(variantes) || variantes.length === 0) {
+ errores.push({ fila, error: MENSAJE_VARIANTES_INVALIDAS });
+ continue;
+ }
+
+ for (const variante of variantes) {
+ const errorVariante = validarVariante(variante);
+ if (errorVariante) {
+ errores.push({ fila, error: errorVariante.error });
+ continue;
+ }
+
+ const errorOpciones = validarOpcionesImportar(variante.opciones);
+ if (errorOpciones) {
+ errores.push({ fila, error: errorOpciones.error });
+ continue;
+ }
+ }
+ }
+
+ if (errores.length > 0) {
+ await conexion.rollback();
+ return res.status(200).json({
+ mensaje: MENSAJE_ERRORES_ARCHIVO,
+ errores,
+ });
+ }
+ const generarSKUConsecutivo = crearGeneradorSKUConsecutivo();
+ for (let indice = 0; indice < productos.length; indice += 1) {
+ const { producto, variantes } = productos[indice];
+ const idProducto = await repositorioCrearProducto.crearProducto(idCliente, producto);
+
+ for (const variante of variantes) {
+ const idVariante = await repositorioCrearVariante.crearVariante(idProducto, variante);
+
+ const opcionesConSKU = variante.opciones.map(opcion => ({
+ ...opcion,
+ SKUautomatico: generarSKUConsecutivo(
+ producto.nombreComun,
+ variante.nombreVariante,
+ opcion.valorOpcion || 'SINVALOR'
+ )
+ }));
+
+ await repositorioCrearOpcion.crearOpcion(idVariante, opcionesConSKU);
+ }
+ }
+
+ await conexion.commit();
+
+ return res.status(200).json({
+ mensaje: IMPORTACION_EXITOSA,
+ errores: null,
+ });
+
+ } catch (err) {
+ if (conexion) await conexion.rollback();
+ return res.status(500).json({
+ mensaje: ERROR_AL_IMPORTAR,
+ error: err.message,
+ });
+ } finally {
+ if (conexion) conexion.release();
+ }
+};
diff --git a/Productos/Controladores/leerProducto.controller.js b/Productos/Controladores/leerProducto.controller.js
new file mode 100644
index 00000000..5303e6f6
--- /dev/null
+++ b/Productos/Controladores/leerProducto.controller.js
@@ -0,0 +1,38 @@
+const MENSAJES = require('@altertex/util/const/mensajesProductos');
+const repositorio = require('@altertex/pro/repos/repositorioLeerProducto');
+// RF[28] Leer producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF28]
+
+/**
+ * Controlador para leer la información de un producto específico.
+ *
+ * Este endpoint espera que el `idProducto` se encuentre en el cuerpo de la solicitud
+ * y que el `idCliente` esté disponible en el objeto `req.user.clienteSeleccionado`.
+ *
+ * @async
+ * @function leerProducto
+ * @param {Express.Request} req - Objeto de solicitud de Express.
+ * @param {object} req.body - Cuerpo de la solicitud.
+ * @param {string|number} req.body.idProducto - ID del producto a consultar.
+ * @param {object} req.user - Usuario autenticado con un cliente seleccionado.
+ * @param {string|number} req.user.clienteSeleccionado - ID del cliente asociado.
+ * @param {Express.Response} res - Objeto de respuesta de Express.
+ * @returns {Promise} Retorna una respuesta HTTP con la información del producto o un mensaje de error.
+ * @throws {Error} Lanza un error si `idProducto` no es válido o si ocurre un error al consultar el repositorio.
+ */
+exports.leerProducto = async (req, res) => {
+ const idProducto = req.query.idProducto;
+ const idCliente = req.user.clienteSeleccionado;
+
+ if (!idProducto) {
+ return res.status(MENSAJES.ID_INVALIDO.codigo).json({ mensaje: MENSAJES.ID_INVALIDO.mensaje });
+ }
+
+ try {
+ const infoProducto = await repositorio.leerProducto(idProducto, idCliente);
+ return res.status(MENSAJES.LEER_PRODUCTO_EXITO.codigo).json({
+ infoProducto,
+ });
+ } catch (error) {
+ return res.status(MENSAJES.ERROR_LEER_PRODUCTO.codigo).json({ mensaje: error.message });
+ }
+};
\ No newline at end of file
diff --git a/Productos/Datos/Repositorios/productosRepositorio.js b/Productos/Datos/Repositorios/productosRepositorio.js
index dc6012e4..5af72a09 100644
--- a/Productos/Datos/Repositorios/productosRepositorio.js
+++ b/Productos/Datos/Repositorios/productosRepositorio.js
@@ -1,6 +1,6 @@
// RF[30] Eliminar Producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF30]
const correrQuery = require('@altertex/util/ser/correrQuery');
-const { ELIMINAR_PRODUCTOS } = require('@altertex/util/const/consultasProductos');
+const consultas = require('@altertex/util/const/consultasProductos');
const extraerNombreArchivoS3 = require('@altertex/util/ser/extraerNombreArchivoS3');
const eliminarImagenS3 = require('@altertex/util/ser/eliminarImagenS3');
@@ -19,26 +19,26 @@ const eliminarImagenS3 = require('@altertex/util/ser/eliminarImagenS3');
*/
const eliminarProductos = async (ids) => {
try {
- // 1. Obtener imágenes de los productos antes de borrarlos
- const obtenerQuery = `
- SELECT i.urlImagen FROM producto p
- JOIN imagen_producto ip ON p.idProducto = ip.idProducto
- JOIN imagen i ON ip.idImagen = i.idImagen
- WHERE p.idProducto IN (${ids.map(() => '?').join(',')});
- `;
- const imagenes = await correrQuery(obtenerQuery, ids);
+ // 1. Obtener imágenes asociadas
+ const placeholders = ids.map(() => '?').join(',');
+ const queryObtenerImagenes = consultas.OBTENER_IMAGENES_POR_IDS.replace('(?)', `(${placeholders})`);
+ const imagenes = await correrQuery(queryObtenerImagenes, ids);
- // 2. Borrar las imágenes en S3
+ // 2. Eliminar imágenes válidas
for (const img of imagenes) {
- const nombreReal = extraerNombreArchivoS3(img.urlImagen);
- eliminarImagenS3('productos/', nombreReal);
+ if (img.urlImagen && !img.urlImagen.includes('placeholder')) {
+ const nombreReal = extraerNombreArchivoS3(img.urlImagen);
+ if (nombreReal) {
+ await eliminarImagenS3('productos/', nombreReal);
+ }
+ }
}
- // 3. Eliminar productos en base de datos
- const placeholders = ids.map(() => '?').join(',');
- const query = ELIMINAR_PRODUCTOS.replace('(?)', `(${placeholders})`);
- const resultado = await correrQuery(query, ids);
- return resultado.affectedRows > 0;
+ // 3. Eliminar productos
+ const queryEliminar = consultas.ELIMINAR_PRODUCTOS.replace('(?)', `(${placeholders})`);
+ const resultado = await correrQuery(queryEliminar, ids);
+
+ return resultado?.affectedRows > 0;
} catch {
return false;
}
@@ -46,4 +46,4 @@ const eliminarProductos = async (ids) => {
module.exports = {
eliminarProductos,
-};
+};
\ No newline at end of file
diff --git a/Productos/Datos/Repositorios/repositorioExportarProducto.js b/Productos/Datos/Repositorios/repositorioExportarProducto.js
new file mode 100644
index 00000000..69b7af2c
--- /dev/null
+++ b/Productos/Datos/Repositorios/repositorioExportarProducto.js
@@ -0,0 +1,135 @@
+const correrQuery = require('@altertex/util/ser/correrQuery');
+const CONSULTAS_PRODUCTOS = require('@altertex/util/const/consultasProductos');
+const excelJS = require('exceljs');
+
+/**
+ * Consulta la lista de productos seleccionados de un cliente para exportar en CSV.
+ *
+ * @param {number} idCliente
+ * @param {number[]} idsProducto
+ * @returns {Promise>}
+ *
+ * @see [RF58 - Documentación de requisitos](https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF58)
+ */
+exports.obtenerProductosExportacion = (idCliente, idsProducto) => {
+ const placeholders = idsProducto.map(() => '?').join(', ');
+ const query = CONSULTAS_PRODUCTOS.OBTENER_DATOS_EXPORTACION.replace('__IDS__', placeholders);
+ return correrQuery(query, [idCliente, ...idsProducto]);
+};
+
+/**
+ * Procesa y genera el archivo Excel con los productos exportados
+ * @param {Array} productos Lista de productos a exportar
+ * @returns {Promise} Buffer con el archivo Excel
+ */
+exports.generarArchivoExcel = async (productos) => {
+ const workbook = new excelJS.Workbook();
+
+ // Primera hoja - Información básica del producto
+ const hoja1 = workbook.addWorksheet('Información Producto');
+ hoja1.columns = [
+ { header: 'ID Producto', key: 'idProducto' },
+ { header: 'ID Proveedor', key: 'idProveedor' },
+ { header: 'Nombre Producto', key: 'nombreProducto' },
+ { header: 'Nombre Comercial', key: 'nombreComercial' },
+ { header: 'Descripción', key: 'descripcionProducto' },
+ { header: 'Tipo Producto', key: 'tipoProducto' },
+ { header: 'Marca', key: 'marca' },
+ { header: 'Modelo', key: 'modelo' },
+ { header: 'Costo', key: 'costo' },
+ { header: 'Precio Venta', key: 'precioVenta' },
+ { header: 'Precio Cliente', key: 'precioCliente' },
+ { header: 'Precio Puntos', key: 'precioPuntos' },
+ { header: 'Impuesto', key: 'impuesto' },
+ { header: 'Descuento', key: 'descuento' },
+ { header: 'Estado', key: 'estado' },
+ { header: 'Envío', key: 'envio' },
+ ];
+ hoja1.addRows(productos);
+
+ // Segunda hoja - Información desglosada de variantes
+ const hoja2 = workbook.addWorksheet('Variantes');
+ hoja2.columns = [
+ { header: 'ID Producto', key: 'idProducto' },
+ { header: 'Nombre Producto', key: 'nombreProducto' },
+ { header: 'Nombre Variante', key: 'nombreVariante' },
+ { header: 'Descripción Variante', key: 'descripcionVariante' },
+ ];
+
+ const variantesDesglosadas = procesarVariantes(productos);
+ hoja2.addRows(variantesDesglosadas);
+
+ // Tercera hoja - Información desglosada de opciones
+ const hoja3 = workbook.addWorksheet('Opciones');
+ hoja3.columns = [
+ { header: 'ID Producto', key: 'idProducto' },
+ { header: 'Nombre Producto', key: 'nombreProducto' },
+ { header: 'Nombre Variante', key: 'nombreVariante' },
+ { header: 'Valor Opción', key: 'valorOpcion' },
+ { header: 'SKU Comercial', key: 'skuComercial' },
+ { header: 'SKU Automático', key: 'skuAutomatico' },
+ { header: 'Cantidad', key: 'cantidad' },
+ ];
+
+ const opcionesDesglosadas = procesarOpciones(productos);
+ hoja3.addRows(opcionesDesglosadas);
+
+ return await workbook.xlsx.writeBuffer();
+};
+
+/**
+ * Procesa las variantes de los productos
+ * @param {Array} productos Lista de productos
+ * @returns {Array} Lista de variantes procesadas
+ */
+function procesarVariantes(productos) {
+ return productos.flatMap((producto) => {
+ const variantes = producto.variantes_opciones.split(' | ').map((variante) => {
+ const [datosVariante] = variante.split(',');
+ const [nombreVariante, descripcionVariante] = datosVariante.split('-');
+ return {
+ idProducto: producto.idProducto,
+ nombreProducto: producto.nombreProducto,
+ nombreVariante,
+ descripcionVariante,
+ };
+ });
+ return variantes;
+ });
+}
+
+/**
+ * Procesa las opciones de los productos
+ * @param {Array} productos Lista de productos
+ * @returns {Array} Lista de opciones procesadas
+ */
+function procesarOpciones(productos) {
+ return productos.flatMap((producto) => {
+ return producto.variantes_opciones.split(' | ').flatMap((variante) => {
+ const [datosVariante, ...opcionesParts] = variante.split(',');
+ const [nombreVariante] = datosVariante.split('-');
+ const opcionesCompletas = opcionesParts.join(',').trim();
+
+ if (!opcionesCompletas) return [];
+
+ return opcionesCompletas
+ .split(', ')
+ .filter((opcion) => opcion.trim())
+ .map((opcion) => {
+ const [valorOpcion, skuComercial, skuAutomatico, cantidad] = opcion
+ .split(':')
+ .map((valores) => valores.trim());
+
+ return {
+ idProducto: producto.idProducto,
+ nombreProducto: producto.nombreProducto,
+ nombreVariante,
+ valorOpcion,
+ skuComercial,
+ skuAutomatico,
+ cantidad,
+ };
+ });
+ });
+ });
+}
diff --git a/Productos/Datos/Repositorios/repositorioLeerProducto.js b/Productos/Datos/Repositorios/repositorioLeerProducto.js
new file mode 100644
index 00000000..7c306d02
--- /dev/null
+++ b/Productos/Datos/Repositorios/repositorioLeerProducto.js
@@ -0,0 +1,27 @@
+const CONSULTAS = require('@altertex/util/const/consultasProductos');
+const MENSAJES = require('@altertex/util/const/mensajesProductos');
+const correrQuery = require('@altertex/util/ser/correrQuery');
+
+/**
+ * Lee la información de un producto específico para un cliente dado.
+ *
+ * @async
+ * @function leerProducto
+ * @param {number|string} idProducto - El identificador del producto a consultar.
+ * @param {number|string} idCliente - El identificador del cliente que realiza la consulta.
+ * @returns {Promise} Retorna una promesa que resuelve con los datos del producto si se encuentra.
+ * @throws {Error} Lanza un error si el producto no se encuentra o si ocurre un error durante la consulta.
+ */
+exports.leerProducto = async (idProducto, idCliente) => {
+ try {
+ const resultado = await correrQuery(CONSULTAS.LEER_PRODUCTO, [idProducto, idCliente]);
+
+ if (resultado.length === 0) {
+ throw new Error(MENSAJES.PRODUCTO_NO_ENCONTRADO.mensaje);
+ }
+
+ return resultado;
+ } catch (error) {
+ throw new Error(error.message);
+ }
+};
diff --git a/Productos/Datos/Repositorios/repositorioValidarProveedor.js b/Productos/Datos/Repositorios/repositorioValidarProveedor.js
new file mode 100644
index 00000000..7886411c
--- /dev/null
+++ b/Productos/Datos/Repositorios/repositorioValidarProveedor.js
@@ -0,0 +1,19 @@
+const consultas = require('@altertex/util/const/consultasProveedores');
+
+
+/**
+ * Verifica si un proveedor existe en la base de datos.
+ * @param {object} conexion - La conexión a la base de datos.
+ * @param {number|null} idProveedor - El ID del proveedor a verificar.
+ * @returns {Promise} True si el proveedor existe o si idProveedor es null.
+ */
+async function proveedorExiste(conexion, idProveedor) {
+ if (idProveedor === null) return true; // Permitido por diseño
+ const [result] = await conexion.query(
+ consultas.VERIFICAR_EXISTE,
+ [idProveedor]
+ );
+ return result.length > 0;
+}
+
+module.exports = { proveedorExiste };
diff --git a/Productos/Rutas/RutasIndividuales/exportarProductos.routes.js b/Productos/Rutas/RutasIndividuales/exportarProductos.routes.js
new file mode 100644
index 00000000..5fde8376
--- /dev/null
+++ b/Productos/Rutas/RutasIndividuales/exportarProductos.routes.js
@@ -0,0 +1,77 @@
+//RF58 - Exportar Productos - https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF58
+
+/**
+ * @swagger
+ * /api/productos/exportar:
+ * post:
+ * summary: Exporta la lista completa de empleados en formato CSV.
+ * tags:
+ * - Productos
+ * security:
+ * - ApiKeyAuth: []
+ * - BearerAuth: []
+ * parameters:
+ * - in: header
+ * name: x-api-key
+ * required: true
+ * schema:
+ * type: string
+ * description: Clave de API
+ * - in: header
+ * name: Authorization
+ * required: true
+ * schema:
+ * type: string
+ * description: Token JWT con formato "Bearer "
+ * responses:
+ * 200:
+ * description: Archivo CSV generado correctamente.
+ * content:
+ * text/csv:
+ * schema:
+ * type: string
+ * format: binary
+ * 204:
+ * description: No hay productos para exportar.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: No hay productos para exportar.
+ * 400:
+ * description: Error interno del servidor al exportar productos.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: Error al exportar la lista de productos.
+ */
+
+const express = require('express');
+const ruteador = express.Router();
+const controlador = require('@altertex/pro/ctrl/exportarProductos.controller');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones');
+
+ruteador.post(
+ RUTAS.PRODUCTOS.EXPORTAR_PRODUCTOS,
+ revisarApiKey(),
+ autorizarToken,
+ limitePeticionesDiarias,
+ validarYSanitizar,
+ verificarPermisos(PERMISOS.EXPORTAR_PRODUCTOS),
+ controlador.exportarProductos
+);
+
+module.exports = ruteador;
diff --git a/Productos/Rutas/RutasIndividuales/importarProductos.routes.js b/Productos/Rutas/RutasIndividuales/importarProductos.routes.js
new file mode 100644
index 00000000..2c95b954
--- /dev/null
+++ b/Productos/Rutas/RutasIndividuales/importarProductos.routes.js
@@ -0,0 +1,191 @@
+// RF[56] Leer producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF56]
+const express = require('express');
+const ruteador = express.Router();
+const controlador = require('@altertex/pro/ctrl/importarProductos.controller');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+
+/**
+ * @swagger
+ * /api/productos/importar-productos:
+ * post:
+ * summary: Importa un lote de productos con variantes y opciones desde un archivo procesado.
+ * tags:
+ * - Productos
+ * security:
+ * - bearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: array
+ * items:
+ * type: object
+ * required:
+ * - producto
+ * - variantes
+ * properties:
+ * producto:
+ * type: object
+ * properties:
+ * idProveedor:
+ * type: integer
+ * nullable: true
+ * example: 12
+ * nombreComun:
+ * type: string
+ * example: Tornillo galvanizado
+ * nombreComercial:
+ * type: string
+ * nullable: true
+ * example: Tornillo FG 3/4
+ * descripcion:
+ * type: string
+ * nullable: true
+ * example: Tornillo de acero inoxidable
+ * marca:
+ * type: string
+ * nullable: true
+ * example: TRUPER
+ * modelo:
+ * type: string
+ * nullable: true
+ * example: M3X40
+ * tipoProducto:
+ * type: string
+ * nullable: true
+ * example: Ferretería
+ * costo:
+ * type: number
+ * example: 3.5
+ * precioVenta:
+ * type: number
+ * example: 5.0
+ * precioCliente:
+ * type: number
+ * example: 4.5
+ * precioPuntos:
+ * type: number
+ * example: 4.0
+ * impuesto:
+ * type: number
+ * example: 0.16
+ * descuento:
+ * type: number
+ * example: 0.1
+ * estado:
+ * type: integer
+ * enum: [0, 1]
+ * example: 1
+ * envio:
+ * type: integer
+ * enum: [0, 1]
+ * example: 1
+ * variantes:
+ * type: array
+ * items:
+ * type: object
+ * required:
+ * - nombreVariante
+ * - opciones
+ * properties:
+ * nombreVariante:
+ * type: string
+ * example: Color
+ * descripcion:
+ * type: string
+ * nullable: true
+ * example: Variantes por color
+ * opciones:
+ * type: array
+ * items:
+ * type: object
+ * required:
+ * - cantidad
+ * - valorOpcion
+ * - SKUautomatico
+ * - SKUcomercial
+ * - costoAdicional
+ * - descuento
+ * - estado
+ * properties:
+ * cantidad:
+ * type: integer
+ * example: 10
+ * valorOpcion:
+ * type: string
+ * example: Rojo
+ * SKUautomatico:
+ * type: string
+ * example: SKU-AUTO-1
+ * SKUcomercial:
+ * type: string
+ * example: SKU-ROJO-123
+ * costoAdicional:
+ * type: number
+ * example: 0.5
+ * descuento:
+ * type: number
+ * example: 10
+ * estado:
+ * type: integer
+ * enum: [0, 1]
+ * example: 1
+ * responses:
+ * 200:
+ * description: Importación completada exitosamente o con errores parciales.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: Importación completada exitosamente.
+ * errores:
+ * type: array
+ * items:
+ * type: object
+ * properties:
+ * fila:
+ * type: integer
+ * example: 3
+ * error:
+ * type: string
+ * example: Producto sin variantes válidas.
+ * 400:
+ * description: No se recibieron productos válidos.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: No se recibieron productos válidos.
+ * 401:
+ * description: No autorizado. Token inválido o faltante.
+ * 403:
+ * description: Acceso denegado por falta de permisos.
+ * 500:
+ * description: Error interno del servidor.
+ */
+
+ruteador.post(
+ RUTAS.PRODUCTOS.IMPORTAR,
+ revisarApiKey(),
+ autorizarToken,
+ limitePeticionesDiarias,
+ validarYSanitizar,
+ verificarPermisos(PERMISOS.IMPORTAR_PRODUCTOS),
+ controlador.importarProductos
+);
+
+module.exports = ruteador;
diff --git a/Productos/Rutas/RutasIndividuales/leerProductos.routes.js b/Productos/Rutas/RutasIndividuales/leerProductos.routes.js
new file mode 100644
index 00000000..d11e4424
--- /dev/null
+++ b/Productos/Rutas/RutasIndividuales/leerProductos.routes.js
@@ -0,0 +1,14 @@
+const express = require('express');
+const ruteador = express.Router();
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+const controlador = require('@altertex/pro/ctrl/leerProducto.controller');
+// RF[28] Leer producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF28]
+
+ruteador.get(RUTAS.PRODUCTOS.LEER, revisarApiKey(), autorizarToken, verificarPermisos(PERMISOS.LEER_PRODUCTO), validarYSanitizar, controlador.leerProducto);
+
+module.exports = ruteador;
\ No newline at end of file
diff --git a/Productos/Rutas/indexProductos.routes.js b/Productos/Rutas/indexProductos.routes.js
index 47a7202e..3f014e4e 100644
--- a/Productos/Rutas/indexProductos.routes.js
+++ b/Productos/Rutas/indexProductos.routes.js
@@ -1,8 +1,11 @@
const express = require('express');
const ruteador = express.Router();
const rutaConsultarLista = require('@altertex/pro/rutasInd/consultarProductos.routes');
-const rutaEliminar = require("@altertex/pro/rutasInd/eliminarProducto.routes");
+const rutaEliminar = require('@altertex/pro/rutasInd/eliminarProducto.routes');
const rutaCrearProducto = require('@altertex/pro/rutasInd/crearProducto.routes');
+const rutaImportarProductos = require('@altertex/pro/rutasInd/importarProductos.routes');
+const rutasLeerProducto = require('@altertex/pro/rutasInd/leerProductos.routes');
+const rutasExportarProductos = require('@altertex/pro/rutasInd/exportarProductos.routes');
const RUTAS = require('@altertex/util/const/rutas');
@@ -10,7 +13,13 @@ const RUTAS = require('@altertex/util/const/rutas');
ruteador.use(RUTAS.PRODUCTOS.BASE, rutaConsultarLista);
//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26
ruteador.use(RUTAS.PRODUCTOS.BASE, rutaCrearProducto);
-// RF[30] Eliminar Producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF30]
+// RF[30] Eliminar Producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF30]
ruteador.use(RUTAS.PRODUCTOS.BASE, rutaEliminar);
+// RF[28] Leer producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF28]
+ruteador.use(RUTAS.PRODUCTOS.BASE, rutasLeerProducto);
+// RF[56] Leer producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF56]
+ruteador.use(RUTAS.PRODUCTOS.BASE, rutaImportarProductos);
+// RF[58] Exportar Productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF58]
+ruteador.use(RUTAS.PRODUCTOS.BASE, rutasExportarProductos);
-module.exports = ruteador;
\ No newline at end of file
+module.exports = ruteador;
diff --git a/Roles/Controladores/actualizarRol.controller.js b/Roles/Controladores/actualizarRol.controller.js
new file mode 100644
index 00000000..0e129abb
--- /dev/null
+++ b/Roles/Controladores/actualizarRol.controller.js
@@ -0,0 +1,29 @@
+const MENSAJES = require('@altertex/util/const/mensajesRoles');
+const repositorio = require('@altertex/rol/repos/repositorioActualizarRol');
+
+/**
+ * Controlador para actualizar un rol.
+ *
+ * Este controlador recibe los datos del rol a actualizar desde el cuerpo de la solicitud
+ * y el cliente seleccionado desde el usuario autenticado. Valida los datos y delega
+ * la lógica de actualización al repositorio.
+ *
+ * @function
+ * @param {Express.Request} req - Objeto de solicitud de Express.
+ * @param {Express.Response} res - Objeto de respuesta de Express.
+ * @returns {Promise} - Retorna una respuesta HTTP con el resultado de la operación.
+ */
+exports.actualizarRol = async (req, res) => {
+ const datosActualizacion = req.body.datosRolActualizacion;
+
+ if (!datosActualizacion) {
+ return res.status(MENSAJES.PARAMETROS_INVALIDOS.codigo).json({ mensaje: MENSAJES.PARAMETROS_INVALIDOS.mensaje });
+ }
+
+ try {
+ await repositorio.actualizarRol(datosActualizacion);
+ return res.status(MENSAJES.ACTUALIZAR_ROL.codigo).json({ mensaje: MENSAJES.ACTUALIZAR_ROL.mensaje });
+ } catch (error) {
+ return res.status(400).json({ mensaje: error.message });
+ }
+};
\ No newline at end of file
diff --git a/Roles/Controladores/consultarDetalleRol.controller.js b/Roles/Controladores/consultarDetalleRol.controller.js
new file mode 100644
index 00000000..a402fe08
--- /dev/null
+++ b/Roles/Controladores/consultarDetalleRol.controller.js
@@ -0,0 +1,69 @@
+const repositorio = require('@altertex/rol/repos/repositorioDetalleRol');
+const MENSAJES_ROLES = require('@altertex/util/const/mensajesRoles');
+
+/**
+ * RF8 - Leer rol
+ * Documentación del requisito funcional:
+ * https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF8
+ *
+ * @function consultarDetalle
+ * @async
+ * @param {object} req - Objeto de solicitud HTTP (Request).
+ * @param {object} res - Objeto de respuesta HTTP (Response).
+ * @returns {Response} Respuesta HTTP con los detalles del rol solicitado.
+ *
+ * @description
+ * Este controlador obtiene el detalle de un rol: nombre, descripción,
+ * número de usuarios asociados y permisos relacionados.
+ */
+exports.consultarDetalle = async (req, res) => {
+ try {
+ const { idRol } = req.query;
+
+ // Validación: verificar que se proporcione un ID válido
+ if (!idRol || isNaN(Number(idRol))) {
+ return res
+ .status(MENSAJES_ROLES.PARAMETROS_INVALIDOS.codigo)
+ .json({ mensaje: MENSAJES_ROLES.PARAMETROS_INVALIDOS.mensaje });
+ }
+
+ // Se consulta al repositorio de roles para obtener el detalle por ID.
+ const resultado = await repositorio.obtenerDetalleRol(Number(idRol));
+
+ // Validación: si no se encontró el rol, se responde con mensaje de "sin resultados".
+ if (!resultado || resultado.length === 0) {
+ return res
+ .status(MENSAJES_ROLES.SIN_RESULTADOS.codigo)
+ .json({ mensaje: MENSAJES_ROLES.SIN_RESULTADOS.mensaje });
+ }
+
+ // Extrae los datos generales del rol desde la primera fila
+ const { nombreRol, descripcionRol, totalUsuarios } = resultado[0];
+
+ // Construye arreglo de permisos (omite si no tiene permisos)
+ const permisos = resultado
+ .filter(permiso => permiso.idPermiso !== null)
+ .map(permiso => ({
+ id: permiso.idPermiso,
+ nombre: permiso.nombrePermiso,
+ descripcion: permiso.descripcionPermiso,
+ }));
+
+ // Respuesta exitosa con datos
+ return res.status(MENSAJES_ROLES.CONSULTA_EXITOSA.codigo).json({
+ mensaje: MENSAJES_ROLES.CONSULTA_EXITOSA.mensaje,
+ rol: {
+ idRol: Number(idRol),
+ nombre: nombreRol,
+ descripcion: descripcionRol,
+ totalUsuarios,
+ permisos,
+ },
+ });
+ } catch {
+ // Error inesperado en el servidor
+ return res
+ .status(MENSAJES_ROLES.ERROR_CONSULTAR_ROLES.codigo)
+ .json({ mensaje: MENSAJES_ROLES.ERROR_CONSULTAR_ROLES.mensaje });
+ }
+};
\ No newline at end of file
diff --git a/Roles/Datos/Repositorios/repositorioActualizarRol.js b/Roles/Datos/Repositorios/repositorioActualizarRol.js
new file mode 100644
index 00000000..22e67561
--- /dev/null
+++ b/Roles/Datos/Repositorios/repositorioActualizarRol.js
@@ -0,0 +1,94 @@
+const MENSAJES = require('@altertex/util/const/mensajesRoles');
+const conexion = require('@altertex/util/bd/db');
+
+/**
+ * Actualiza un rol existente, incluyendo su nombre, descripción y permisos asociados.
+ *
+ * @param {object} datosActualizarRol - Datos necesarios para actualizar el rol.
+ * @param {object} datosActualizarRol.datosRol - Contiene el nombre, descripción y permisos del rol.
+ * @param {number} datosActualizarRol.idRol - ID del rol a actualizar.
+ * @throws {Error} Si hay parámetros inválidos o errores en la base de datos.
+ */
+exports.actualizarRol = async (datosActualizarRol) => {
+ const datosRol = datosActualizarRol.datosRol;
+ const idRol = datosActualizarRol.idRol;
+ const permisos = datosRol.permisos || [];
+
+ if (!datosActualizarRol || !idRol) {
+ throw new Error(MENSAJES.PARAMETROS_INVALIDOS.mensaje);
+ }
+
+ const conexionBD = await conexion.getConnection();
+
+ try {
+ await conexionBD.beginTransaction();
+
+ // Check for duplicate name only if name is being updated
+ if (datosRol.nombre !== null && datosRol.nombre !== undefined) {
+ const resultadoNombreDuplicado = await conexionBD.query(
+ 'SELECT idRol FROM rol WHERE nombre = ? AND idRol != ?',
+ [datosRol.nombre, idRol],
+ );
+
+ if (resultadoNombreDuplicado[0].length > 0) {
+ throw new Error(MENSAJES.ROL_EXISTENTE);
+ }
+ }
+
+ // Build dynamic UPDATE query based on which fields are provided
+ const camposActualizar = [];
+ const valoresActualizar = [];
+
+ if (datosRol.nombre !== null && datosRol.nombre !== undefined) {
+ camposActualizar.push('nombre = ?');
+ valoresActualizar.push(datosRol.nombre);
+ }
+
+ if (datosRol.descripcion !== null && datosRol.descripcion !== undefined) {
+ camposActualizar.push('descripcion = ?');
+ valoresActualizar.push(datosRol.descripcion);
+ }
+
+ // Only run UPDATE if there are fields to update
+ if (camposActualizar.length > 0) {
+ const consultaActualizar = `UPDATE rol
+ SET ${camposActualizar.join(', ')}
+ WHERE idRol = ?`;
+ valoresActualizar.push(idRol);
+
+ await conexionBD.query(consultaActualizar, valoresActualizar);
+ }
+
+ // Always handle permissions (delete old ones)
+ await conexionBD.query(
+ 'DELETE FROM rol_permiso WHERE idRol = ?',
+ [idRol],
+ );
+
+ // Insert new permissions if any
+ if (permisos.length > 0) {
+ const valores = permisos.map(idPermiso => [idRol, idPermiso]);
+ const marcadores = permisos.map(() => '(?, ?)').join(', ');
+ const consultaInsertar = `INSERT INTO rol_permiso (idRol, idPermiso)
+ VALUES ${marcadores}`;
+ const valoresAplanados = valores.flat();
+
+ await conexionBD.query(consultaInsertar, valoresAplanados);
+ }
+
+ await conexionBD.commit();
+
+ } catch (error) {
+ await conexionBD.rollback();
+ console.error('Error actualizando rol:', error);
+
+ if (error.message === MENSAJES.ROL_EXISTENTE
+ || error.message === MENSAJES.PARAMETROS_INVALIDOS.mensaje) {
+ throw new Error(error.message);
+ } else {
+ throw new Error('Ocurrio un error al actualizar rol.');
+ }
+ } finally {
+ conexionBD.release();
+ }
+};
\ No newline at end of file
diff --git a/Roles/Datos/Repositorios/repositorioDetalleRol.js b/Roles/Datos/Repositorios/repositorioDetalleRol.js
new file mode 100644
index 00000000..b950287a
--- /dev/null
+++ b/Roles/Datos/Repositorios/repositorioDetalleRol.js
@@ -0,0 +1,29 @@
+const correrQuery = require('@altertex/util/ser/correrQuery');
+const CONSULTAS_ROLES = require('@altertex/util/const/consultasRoles');
+/**
+ * RF8 - Leer detalle de un rol
+ * Documentación del requisito funcional:
+ * https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF8
+ *
+ * @async
+ * @function obtenerDetalleRol
+ * @param {number} idRol - ID del rol que se desea consultar.
+ * @returns {Promise>} Arreglo de objetos con datos del rol y sus permisos asociados.
+ *
+ * @throws {Error} Si ocurre un error en la consulta a la base de datos.
+ */
+exports.obtenerDetalleRol = async (idRol) => {
+ const query = CONSULTAS_ROLES.OBTENER_DETALLE_ROL;
+
+ try {
+ const resultado = await correrQuery(query, [idRol]);
+
+ if (!resultado || resultado.length === 0) {
+ throw new Error('Rol no encontrado');
+ }
+
+ return resultado;
+ } catch {
+ throw new Error('Error al consultar el detalle del rol');
+ }
+};
\ No newline at end of file
diff --git a/Roles/Rutas/RutasIndividuales/actualizarRol.routes.js b/Roles/Rutas/RutasIndividuales/actualizarRol.routes.js
new file mode 100644
index 00000000..76fe2262
--- /dev/null
+++ b/Roles/Rutas/RutasIndividuales/actualizarRol.routes.js
@@ -0,0 +1,15 @@
+const express = require('express');
+const ruteador = express.Router();
+const controlador = require('@altertex/rol/ctrl/actualizarRol.controller');
+
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones');
+const revisarPermisos = require('@altertex/util/inter/verificarPermisos');
+
+ruteador.put(RUTAS.ROLES.ACTUALIZAR, validarYSanitizar, revisarApiKey(), autorizarToken, limitePeticionesDiarias, revisarPermisos(PERMISOS.ACTUALIZAR_ROL), controlador.actualizarRol);
+
+module.exports = ruteador;
\ No newline at end of file
diff --git a/Roles/Rutas/RutasIndividuales/consultarDetalleRol.routes.js b/Roles/Rutas/RutasIndividuales/consultarDetalleRol.routes.js
new file mode 100644
index 00000000..027bbac2
--- /dev/null
+++ b/Roles/Rutas/RutasIndividuales/consultarDetalleRol.routes.js
@@ -0,0 +1,54 @@
+const express = require('express');
+const ruteador = express.Router();
+
+const controlador = require('@altertex/rol/ctrl/consultarDetalleRol.controller');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones');
+
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+
+/**
+ * RF## - Leer detalle de un rol - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF##
+ */
+
+/**
+ * @swagger
+ * /api/roles/leer:
+ * get:
+ * summary: Obtener detalle de un rol
+ * description: Devuelve nombre, descripción, cantidad de usuarios y permisos de un rol específico.
+ * tags: [Roles]
+ * security:
+ * - ApiKeyAuth: []
+ * parameters:
+ * - in: query
+ * name: idRol
+ * required: true
+ * schema:
+ * type: integer
+ * description: ID del rol a consultar.
+ * responses:
+ * 200:
+ * description: Detalle del rol obtenido exitosamente.
+ * 400:
+ * description: Parámetros inválidos.
+ * 404:
+ * description: Rol no encontrado.
+ * 500:
+ * description: Error interno del servidor.
+ */
+ruteador.get(
+ RUTAS.ROLES.LEER_ROL,
+ validarYSanitizar,
+ revisarApiKey(),
+ autorizarToken,
+ limitePeticionesDiarias,
+ verificarPermisos(PERMISOS.LEER_ROL),
+ controlador.consultarDetalle,
+);
+
+module.exports = ruteador;
\ No newline at end of file
diff --git a/Roles/Rutas/indexRoles.routes.js b/Roles/Rutas/indexRoles.routes.js
index 6ea28210..5f70fd94 100644
--- a/Roles/Rutas/indexRoles.routes.js
+++ b/Roles/Rutas/indexRoles.routes.js
@@ -21,6 +21,10 @@ const rutasObtenerOpcionesRol = require('@altertex/rol/rutasInd/obtenerOpcionesR
const rutasEliminarRol = require('@altertex/rol/rutasInd/eliminarRol.routes');
+const rutasConsultarDetalle = require('@altertex/rol/rutasInd/consultarDetalleRol.routes');
+
+const rutasActualizarRol = require('@altertex/rol/rutasInd/actualizarRol.routes');
+
// Importación del archivo de constantes donde están definidas las rutas base del sistema.
const RUTAS = require('@altertex/util/const/rutas');
@@ -39,5 +43,10 @@ ruteador.use(RUTAS.ROLES.BASE, rutasObtenerOpcionesRol);
ruteador.use(RUTAS.ROLES.BASE, rutasEliminarRol);
+ruteador.use(RUTAS.ROLES.BASE, rutasConsultarDetalle);
+
+ruteador.use(RUTAS.ROLES.BASE, rutasActualizarRol);
+
+
// Exporta el enrutador para ser utilizado en el archivo principal de rutas de la aplicación (por ejemplo: app.js).
-module.exports = ruteador;
+module.exports = ruteador;
\ No newline at end of file
diff --git a/SetsProductos/Controladores/actualizarSetsProductos.controller.js b/SetsProductos/Controladores/actualizarSetsProductos.controller.js
new file mode 100644
index 00000000..ae6d0fcf
--- /dev/null
+++ b/SetsProductos/Controladores/actualizarSetsProductos.controller.js
@@ -0,0 +1,53 @@
+const MENSAJES = require('@altertex/util/const/mensajesSetsProductos');
+const repositorio = require('@altertex/setspro/repos/repositorioActualizarSetsProductos');
+//RF[44] Actualizar set de productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF44]
+
+/**
+ * Controlador HTTP para actualizar un set de productos.
+ *
+ * Valida que el cuerpo de la solicitud contenga los datos necesarios,
+ * y delega la lógica de actualización al repositorio correspondiente.
+ *
+ * Respuestas posibles:
+ * - 400 si faltan datos en el cuerpo de la solicitud.
+ * - 200 si el set se actualiza correctamente.
+ * - 500 si ocurre un error en el proceso de actualización.
+ *
+ * @async
+ * @function actualizarSetProductos
+ * @param {Express.Request} req - Objeto de solicitud HTTP de Express.
+ * @param {Express.Response} res - Objeto de respuesta HTTP de Express.
+ * @returns {Promise} La respuesta HTTP con el estado y mensaje correspondiente.
+ */
+exports.actualizarSetProductos = async (req, res) => {
+ const datosActualizacion = req.body;
+ const cliente = req.user.clienteSeleccionado;
+
+ // Validación básica de datos requeridos
+ if (
+ !datosActualizacion
+ || !datosActualizacion.nombre
+ || !datosActualizacion.productos
+ || !Array.isArray(datosActualizacion.productos)
+ ) {
+ return res.status(MENSAJES.FORMATO_INVALIDO_DATOS.codigo).json({
+ mensaje: MENSAJES.FORMATO_INVALIDO_DATOS.mensaje,
+ detalles: 'Se requieren nombre y lista de productos',
+ });
+ }
+
+ try {
+ await repositorio.actualizarSetProductos(cliente, datosActualizacion);
+
+ return res.status(MENSAJES.SET_ACTUALIZADO.codigo).json({
+ mensaje: MENSAJES.SET_ACTUALIZADO.mensaje,
+ datos: datosActualizacion,
+ });
+ } catch (error) {
+ console.error('Error al actualizar set de productos:', error);
+
+ return res.status(MENSAJES.ERROR_ACTUALIZAR_SET.codigo).json({
+ mensaje: error.message,
+ });
+ }
+};
diff --git a/SetsProductos/Controladores/crearSetsProductos.controller.js b/SetsProductos/Controladores/crearSetsProductos.controller.js
new file mode 100644
index 00000000..96fc79a7
--- /dev/null
+++ b/SetsProductos/Controladores/crearSetsProductos.controller.js
@@ -0,0 +1,49 @@
+const MENSAJES_SETS_PRODUCTOS = require('@altertex/util/const/mensajesSetsProductos');
+const repositorio = require('@altertex/setspro/repos/repositorioCrearSetsProductos');
+
+/**
+ * Controlador de Express que maneja la creación de un nuevo set de productos para un cliente.
+ *
+ * Valida:
+ * - Que se haya enviado el cuerpo de la solicitud con los datos requeridos.
+ * - Que se hayan proporcionado todos los campos necesarios.
+ * - Que exista un cliente seleccionado en el usuario autenticado.
+ *
+ * Llama al repositorio para realizar la lógica de negocio y persistencia, y responde
+ * con el estado adecuado dependiendo del resultado.
+ *
+ * @async
+ * @function crearSetsProductos
+ * @param {Express.Request} req - Objeto de solicitud de Express.
+ * @param {Express.Response} res - Objeto de respuesta de Express.
+ *
+ * @returns {Promise} Devuelve una respuesta HTTP con el estado correspondiente.
+ *
+ * @throws {500} En caso de error inesperado en la creación del set.
+ */
+exports.crearSetsProductos = async (req, res) => {
+ const datos = req.body.nuevoSetsProductos;
+ const cliente = req.user.clienteSeleccionado;
+
+ if (!req.body.nuevoSetsProductos) {
+ return res.status(MENSAJES_SETS_PRODUCTOS.DATOS_INVALIDOS_ERROR.codigo).json({ mensaje: MENSAJES_SETS_PRODUCTOS.DATOS_INVALIDOS_ERROR.mensaje });
+ }
+
+
+ if (!datos.nombre || !datos.nombreVisible || !datos.descripcion || !datos.activo || !datos.idProductos) {
+ return res.status(MENSAJES_SETS_PRODUCTOS.DATOS_INVALIDOS_ERROR.codigo).json({ mensaje: MENSAJES_SETS_PRODUCTOS.DATOS_INVALIDOS_ERROR.mensaje });
+ }
+
+ if (!cliente) {
+ return res.status(MENSAJES_SETS_PRODUCTOS.CLIENTE_NO_SELECCIONADO.codigo).json({ mensaje: MENSAJES_SETS_PRODUCTOS.CLIENTE_NO_SELECCIONADO.mensaje });
+ }
+
+ try {
+ await repositorio.crearSetsProductos(cliente, datos);
+ return res.status(MENSAJES_SETS_PRODUCTOS.SETS_PRODUCTOS_CREADO_EXITO.codigo).json({ mensaje: MENSAJES_SETS_PRODUCTOS.SETS_PRODUCTOS_CREADO_EXITO.mensaje });
+ } catch (error) {
+ return res.status(500).json({ mensaje: error.message });
+ }
+
+
+};
\ No newline at end of file
diff --git a/SetsProductos/Datos/Repositorios/repositorioActualizarSetsProductos.js b/SetsProductos/Datos/Repositorios/repositorioActualizarSetsProductos.js
new file mode 100644
index 00000000..0e91e086
--- /dev/null
+++ b/SetsProductos/Datos/Repositorios/repositorioActualizarSetsProductos.js
@@ -0,0 +1,75 @@
+const correrQuery = require('@altertex/util/ser/correrQuery');
+const MENSAJES = require('@altertex/util/const/mensajesSetsProductos');
+const CONSULTAS = require('@altertex/util/const/consultasSetsProductos');
+
+// RF[44] Actualizar set de productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF44]
+
+// RF[44] Actualizar set de productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF44]
+
+/**
+ * Actualiza un set de productos con su información general y productos asociados.
+ *
+ * - Verifica que el nuevo nombre no esté duplicado para el cliente.
+ * - Actualiza los campos básicos del set: nombre, descripción y estado activo.
+ * - Si se especifica un arreglo de productos:
+ * - Elimina las asociaciones que ya no existen.
+ * - Agrega las nuevas asociaciones.
+ * - Si el arreglo está vacío, elimina todas las asociaciones.
+ *
+ * @async
+ * @function actualizarSetProductos
+ * @param {number} idCliente - ID del cliente propietario del set.
+ * @param {object} datos - Objeto con los datos para actualizar el set.
+ * @param {number} datos.idSetProducto - ID del set de productos a actualizar.
+ * @param {string} datos.nombre - Nombre interno del set.
+ * @param {string} datos.descripcion - Descripción del set.
+ * @param {boolean} datos.activo - Estado activo o inactivo del set.
+ * @param {number[]} datos.productos - Lista de IDs de productos asociados al set.
+ * Si es un arreglo vacío, se eliminarán todas las asociaciones.
+ * @throws {Error} Si ocurre un error durante la actualización o si el nombre está duplicado.
+ */
+exports.actualizarSetProductos = async (idCliente, datos) => {
+ const { idSetProducto, nombre, descripcion, activo, productos } = datos;
+
+ try {
+
+ const duplicados = await correrQuery(CONSULTAS.CONSULTAR_NOMBRE_DUPLICADO, [
+ idCliente,
+ idSetProducto,
+ nombre,
+ ]);
+
+ if (duplicados.length > 0) {
+ throw new Error(MENSAJES.ERROR_NOMBRE_NORMAL_DUPLICADO.mensaje);
+ }
+ // 1. Actualizar info básica del set
+ await correrQuery(CONSULTAS.ACTUALIZAR_SET_INFO, [nombre, descripcion, activo, idSetProducto]);
+
+ // 2. Manejar productos asociados al set
+ if (Array.isArray(productos)) {
+ if (productos.length > 0) {
+ // Verificar que los productos pertenezcan al cliente
+ const productosSTR = productos.join(', ');
+ const valores = productos
+ .map((idProducto) => `(${idSetProducto}, ${idProducto})`)
+ .join(', ');
+
+ // Eliminar asociaciones actuales que no estén en la nueva lista
+ await correrQuery(
+ CONSULTAS.ELIMINAR_PRODUCTOS_DEL_SET.replace('__ID__', idSetProducto).replace(
+ '__PRODUCTOS__',
+ productosSTR
+ )
+ );
+
+ // Agregar nuevos productos al set
+ await correrQuery(CONSULTAS.AGREGAR_PRODUCTOS_AL_SET.replace('__VALORES__', valores));
+ } else {
+ // Eliminar todas las asociaciones si el array está vacío
+ await correrQuery(CONSULTAS.ELIMINAR_TODOS_PRODUCTOS_DEL_SET, [idSetProducto]);
+ }
+ }
+ } catch (error) {
+ throw new Error(error.message || MENSAJES.ERROR_ACTUALIZAR_SET_PRODUCTOS.mensaje);
+ }
+};
diff --git a/SetsProductos/Datos/Repositorios/repositorioCrearSetsProductos.js b/SetsProductos/Datos/Repositorios/repositorioCrearSetsProductos.js
new file mode 100644
index 00000000..21623f34
--- /dev/null
+++ b/SetsProductos/Datos/Repositorios/repositorioCrearSetsProductos.js
@@ -0,0 +1,61 @@
+const MENSAJES_SETS_PRODUCTOS = require('@altertex/util/const/mensajesSetsProductos');
+const correrQuery = require('@altertex/util/ser/correrQuery');
+const CONSULTAS = require('@altertex/util/const/consultasSetsProductos');
+
+/**
+ * Crea un nuevo set de productos para un cliente específico, validando que:
+ * - El nombre y nombre visible del set no estén duplicados.
+ * - Todos los productos asociados existan.
+ *
+ * Si todo es válido, crea el set de productos y asigna los productos especificados al set.
+ *
+ * @async
+ * @function crearSetsProductos
+ * @param {number} idCliente - ID del cliente al que se le asignará el set de productos.
+ * @param {object} datosSetsProducto - Datos del set de productos a crear.
+ * @param {string} datosSetsProducto.nombre - Nombre interno del set.
+ * @param {string} datosSetsProducto.nombreVisible - Nombre visible del set.
+ * @param {string} [datosSetsProducto.descripcion] - Descripción del set (opcional).
+ * @param {boolean} datosSetsProducto.activo - Indicador de si el set estará activo.
+ * @param {number[]} datosSetsProducto.idProductos - IDs de los productos que se asociarán al set.
+ *
+ * @throws {Error} Si el nombre o nombre visible del set ya están en uso.
+ * @throws {Error} Si uno o más productos no existen en la base de datos.
+ * @throws {Error} Si ocurre un error inesperado durante la ejecución.
+ *
+ * @returns {Promise} No retorna un valor directamente, pero lanza errores si algo falla.
+ */
+exports.crearSetsProductos = async (idCliente, datosSetsProducto) => {
+ try {
+
+ const duplicados = await correrQuery(CONSULTAS.CONSULTAR_DUPLICADOS, [
+ idCliente,
+ datosSetsProducto.nombre,
+ datosSetsProducto.nombreVisible,
+ ]);
+
+ if (duplicados.length > 0) {
+ throw new Error(MENSAJES_SETS_PRODUCTOS.ERROR_NOMBRE_DUPLICADO.mensaje);
+ }
+
+ const ids = datosSetsProducto.idProductos;
+ const temporal = ids.map(() => '?').join(', ');
+ const queryProductos = CONSULTAS.CONSULTAR_PRODUCTOS_EXISTENTES.replace('__IDS__', temporal);
+ const productosExistentes = await correrQuery(queryProductos, ids);
+
+ if (productosExistentes.length !== ids.length) {
+ throw new Error(MENSAJES_SETS_PRODUCTOS.ERROR_PRODUCTOS_INVALIDOS.mensaje);
+ }
+
+ const resultado = await correrQuery(CONSULTAS.CREAR_SET_PRODUCTO, [idCliente, datosSetsProducto.nombre, datosSetsProducto.nombreVisible, datosSetsProducto.descripcion, datosSetsProducto.activo]);
+ const idSetProducto = resultado.insertId;
+
+
+ for (const producto of datosSetsProducto.idProductos) {
+ await correrQuery(CONSULTAS.ASIGNAR_PRODUCTO_SET_PRODUCTO, [producto, idSetProducto]);
+ }
+ } catch (error) {
+ throw new Error(error.message);
+ }
+
+};
\ No newline at end of file
diff --git a/SetsProductos/Rutas/RutasIndividuales/actualizarSetsProductos.routes.js b/SetsProductos/Rutas/RutasIndividuales/actualizarSetsProductos.routes.js
new file mode 100644
index 00000000..d2f5892b
--- /dev/null
+++ b/SetsProductos/Rutas/RutasIndividuales/actualizarSetsProductos.routes.js
@@ -0,0 +1,29 @@
+const express = require('express');
+const ruteador = express.Router();
+
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+const controlador = require('@altertex/setspro/ctrl/actualizarSetsProductos.controller');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const revisarPermisos = require('@altertex/util/inter/verificarPermisos');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones');
+
+//RF[19] Actualizar Empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF19]
+
+/**
+ * @swagger...
+ */
+
+ruteador.put(
+ RUTAS.SETS_PRODUCTOS.ACTUALIZAR,
+ revisarApiKey(),
+ validarYSanitizar,
+ autorizarToken,
+ limitePeticionesDiarias,
+ revisarPermisos(PERMISOS.ACTUALIZAR_SET_PRODUCTOS),
+ controlador.actualizarSetProductos
+);
+
+module.exports = ruteador;
diff --git a/SetsProductos/Rutas/RutasIndividuales/consultarSetsProductos.routes.js b/SetsProductos/Rutas/RutasIndividuales/consultarSetsProductos.routes.js
index 8ba2d771..2e7299fb 100644
--- a/SetsProductos/Rutas/RutasIndividuales/consultarSetsProductos.routes.js
+++ b/SetsProductos/Rutas/RutasIndividuales/consultarSetsProductos.routes.js
@@ -6,7 +6,6 @@ const autorizarToken = require('@altertex/util/inter/autorizarToken');
const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones');
-
const PERMISOS = require('@altertex/util/const/permisos');
const RUTAS = require('@altertex/util/const/rutas');
diff --git a/SetsProductos/Rutas/RutasIndividuales/crearSetsProductos.routes.js b/SetsProductos/Rutas/RutasIndividuales/crearSetsProductos.routes.js
new file mode 100644
index 00000000..22a05c6b
--- /dev/null
+++ b/SetsProductos/Rutas/RutasIndividuales/crearSetsProductos.routes.js
@@ -0,0 +1,89 @@
+const express = require('express');
+const ruteador = express.Router();
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const verificarPermisos = require('@altertex/util/inter/verificarPermisos');
+const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+
+const controlador = require('@altertex/setspro/ctrl/crearSetsProductos.controller');
+
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+
+/**
+ * @swagger
+ * /api/sets-productos/crear:
+ * post:
+ * summary: Crea un nuevo set de productos para un cliente autenticado.
+ * tags:
+ * - Sets de Productos
+ * security:
+ * - ApiKeyAuth: []
+ * - BearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * nuevoSetsProductos:
+ * type: object
+ * required:
+ * - nombre
+ * - nombreVisible
+ * - descripcion
+ * - activo
+ * - idProductos
+ * properties:
+ * nombre:
+ * type: string
+ * example: "combo-verano"
+ * nombreVisible:
+ * type: string
+ * example: "Combo de Verano"
+ * descripcion:
+ * type: string
+ * example: "Incluye productos para la temporada de verano"
+ * activo:
+ * type: boolean
+ * example: true
+ * idProductos:
+ * type: array
+ * items:
+ * type: integer
+ * example: [1, 2, 3]
+ * responses:
+ * 201:
+ * description: Set de productos creado exitosamente.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: "Set de productos creado correctamente."
+ * 400:
+ * description: Datos inválidos o faltantes en la solicitud.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: "Los datos enviados son inválidos."
+ * 401:
+ * description: Token de autenticación no válido o ausente.
+ * 403:
+ * description: Permisos insuficientes para crear sets de productos.
+ * 429:
+ * description: Límite diario de peticiones alcanzado.
+ * 500:
+ * description: Error interno del servidor al crear el set de productos.
+ */
+ruteador.post(RUTAS.SETS_PRODUCTOS.CREAR, validarYSanitizar, revisarApiKey(), autorizarToken, limitePeticionesDiarias, verificarPermisos(PERMISOS.CREAR_SET_PRODUCTOS), controlador.crearSetsProductos);
+
+module.exports = ruteador;
\ No newline at end of file
diff --git a/SetsProductos/Rutas/indexSetsProductos.routes.js b/SetsProductos/Rutas/indexSetsProductos.routes.js
index a30971e5..b1f8e1c2 100644
--- a/SetsProductos/Rutas/indexSetsProductos.routes.js
+++ b/SetsProductos/Rutas/indexSetsProductos.routes.js
@@ -2,6 +2,8 @@ const express = require('express');
const ruteador = express.Router();
const rutasConsultarSetsProductos = require('@altertex/setspro/rutasInd/consultarSetsProductos.routes');
const rutasEliminarSetsProductos = require('@altertex/setspro/rutasInd/eliminarSetsProductos.routes');
+const rutasCrearSetsProductos = require('@altertex/setspro/rutasInd/crearSetsProductos.routes');
+const rutasActualizarSetsProductos = require('@altertex/setspro/rutasInd/actualizarSetsProductos.routes');
const RUTAS = require('@altertex/util/const/rutas');
@@ -11,4 +13,9 @@ ruteador.use(RUTAS.SETS_PRODUCTOS.BASE, rutasConsultarSetsProductos);
//RF[45] Elimina set de productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF45]
ruteador.use(RUTAS.SETS_PRODUCTOS.BASE, rutasEliminarSetsProductos);
+ruteador.use(RUTAS.SETS_PRODUCTOS.BASE, rutasCrearSetsProductos);
+
+//RF[44] Actualizar set de productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF44]
+ruteador.use(RUTAS.SETS_PRODUCTOS.BASE, rutasActualizarSetsProductos);
+
module.exports = ruteador;
diff --git a/Usuarios/Controladores/actualizarUsuario.controller.js b/Usuarios/Controladores/actualizarUsuario.controller.js
new file mode 100644
index 00000000..e6538745
--- /dev/null
+++ b/Usuarios/Controladores/actualizarUsuario.controller.js
@@ -0,0 +1,45 @@
+const repositorio = require('@altertex/usu/repos/repositorioActualizarUsuario');
+const bcrypt = require('bcryptjs');
+
+//RF[4] Actualizar Usuario - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF4]
+
+/**
+ * Controlador para actualizar la información de un usuario.
+ *
+ * Este endpoint recibe un objeto con los cambios que se aplicarán
+ * sobre un usuario y usa su repositorio para hacer el cambio en la
+ * base de datos.
+ *
+ * @function actualizarUsuario
+ * @async
+ * @param {Express.Request} req - Objeto de solicitud HTTP de Express.
+ * @param {object} req.body - Cuerpo de la solicitud.
+ * @param {object|Array} req.body.cambios - Información del usuario a actualizar.
+ * @param {Express.Response} res - Objecto de respuesta HTTP de Express.
+ * @returns {Promise} Retorna una respuesta JSON indicando éxito o un error.
+ */
+exports.actualizarUsuario = async (req, res) => {
+ const cambios = req.body.cambios || req.body;
+
+ if (!cambios) {
+ return res.status(400).json({ mensaje: 'No se enviaron los datos del usuario' });
+ }
+
+ const datos = Array.isArray(cambios) ? cambios : [cambios];
+
+ if (!datos[0].idUsuario) {
+ return res.status(400).json({ mensaje: 'ID del usuario no proporcionado' });
+ }
+
+ if (datos[0].contrasenia) {
+ const contraseniaEncriptada = await bcrypt.hash(datos[0].contrasenia, 10);
+ datos[0].contrasenia = contraseniaEncriptada;
+ }
+
+ try {
+ await repositorio.actualizarUsuario(datos);
+ return res.status(200).json({ mensaje: 'Usuario actualizado correctamente' });
+ } catch (error) {
+ return res.status(400).json({ mensaje: error.message });
+ }
+};
diff --git a/Usuarios/Controladores/consultarListaUsuarios.controller.js b/Usuarios/Controladores/consultarListaUsuarios.controller.js
index 1a52225f..70e675df 100644
--- a/Usuarios/Controladores/consultarListaUsuarios.controller.js
+++ b/Usuarios/Controladores/consultarListaUsuarios.controller.js
@@ -26,9 +26,20 @@ exports.consultarListaUsuarios = async (req, res) => {
.json({ mensaje: MENSAJES_USUARIOS.USUARIOS_NO_ENCONTRADOS.mensaje });
}
+ const generoMap = {
+ masculino: 'Hombre',
+ femenino: 'Mujer',
+ otro: 'Otro',
+ };
+
+ const resultadosMapeados = resultados.map((usuario) => ({
+ ...usuario,
+ genero: generoMap[usuario.genero] || usuario.genero,
+ }));
+
return res.status(MENSAJES_USUARIOS.LISTA_USUARIOS_OBTENIDA.codigo).json({
mensaje: MENSAJES_USUARIOS.LISTA_USUARIOS_OBTENIDA.mensaje,
- listaUsuarios: resultados,
+ listaUsuarios: resultadosMapeados,
});
} catch {
return res
diff --git a/Usuarios/Controladores/eliminarUsuario.controller.js b/Usuarios/Controladores/eliminarUsuario.controller.js
index 7078f34a..7bf238c5 100644
--- a/Usuarios/Controladores/eliminarUsuario.controller.js
+++ b/Usuarios/Controladores/eliminarUsuario.controller.js
@@ -42,6 +42,12 @@ exports.eliminarUsuario = async (req, res) => {
}
const idsNumericos = idsUsuarios.map(Number);
+
+ if (idsNumericos.includes(idSolicitante)) {
+ return res.status(403).json({
+ mensaje: 'No puedes eliminar tu propio usuario.',
+ });
+ }
const [usuariosObjetivo] = await db.query(`
SELECT u.idUsuario, r.nombre AS rol
diff --git a/Usuarios/Datos/Repositorios/repositorioActualizarUsuario.js b/Usuarios/Datos/Repositorios/repositorioActualizarUsuario.js
new file mode 100644
index 00000000..8100363a
--- /dev/null
+++ b/Usuarios/Datos/Repositorios/repositorioActualizarUsuario.js
@@ -0,0 +1,104 @@
+const correrQuery = require('@altertex/util/ser/correrQuery');
+const MENSAJES = require('@altertex/util/const/mensajesUsuarios');
+const CONSULTAS_USUARIOS = require('@altertex/util/const/consultasUsuarios');
+
+//RF[4] Actualizar Usuario - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF4
+
+/**
+ * Repositorio para actualizar los datos de un usuario en la BD.
+ *
+ * Recorre un arreglo de objetos que contienen la información de cada usuario para
+ * actualizar su información.
+ *
+ * Utiliza un array de objetos con la información del usuario, y hace la
+ * consulta correspondiente a la base de datos.
+ *
+ * @function actualizarUsuario
+ * @async
+ * @param {Array<{ idUsuario: number, nombreCompleto: string, correoElectronico: string, telefono: string }>} datos - Lista de
+ * información del usuario a actualizar.
+ * @throws {Error} Si el arreglo está vacío o si ocurre un error en la base de datos.
+ * @returns {Promise} Promesa que se resuelve cuando todas las actualizaciones han sido ejecutadas.
+ */
+exports.actualizarUsuario = async (datos) => {
+ if (!Array.isArray(datos) || datos.length === 0) {
+ throw new Error(MENSAJES.ERROR_OBTENER_USUARIO.mensaje);
+ }
+
+ try {
+ for (const usuario of datos) {
+ const {
+ idUsuario,
+ correoElectronico,
+ nombreCompleto,
+ contrasenia,
+ numeroTelefono,
+ direccion,
+ fechaNacimiento,
+ idRol,
+ genero,
+ estatus,
+ cliente,
+ } = usuario;
+
+ const resultadoCorreo = await correrQuery(
+ CONSULTAS_USUARIOS.VALIDAR_CORREO_DUPLICADO_ACTUALIZACION,
+ [correoElectronico, idUsuario]
+ );
+
+ if (resultadoCorreo.length > 0) {
+ throw new Error(MENSAJES.USUARIO_YA_EXISTE.mensaje);
+ }
+
+ const conContrasena = contrasenia && contrasenia.trim() !== '';
+
+ if (conContrasena) {
+ await correrQuery(CONSULTAS_USUARIOS.ACTUALIZAR_DATOS_USUARIO, [
+ nombreCompleto,
+ correoElectronico,
+ contrasenia,
+ numeroTelefono,
+ direccion,
+ fechaNacimiento,
+ genero,
+ estatus,
+ idUsuario,
+ ]);
+ } else {
+ await correrQuery(CONSULTAS_USUARIOS.ACTUALIZAR_DATOS_USUARIO_SIN_CONTRASENA, [
+ nombreCompleto,
+ correoElectronico,
+ numeroTelefono,
+ direccion,
+ fechaNacimiento,
+ genero,
+ estatus,
+ idUsuario,
+ ]);
+ }
+
+ // Asociar cliente(s)
+ if (cliente) {
+ const clientes = Array.isArray(cliente) ? cliente : [cliente];
+
+ // Eliminar asociaciones anteriores
+ await correrQuery(CONSULTAS_USUARIOS.ELIMINAR_USUARIO_CLIENTE, [idUsuario]);
+
+ for (const idCliente of clientes) {
+ if (idCliente) {
+ await correrQuery(CONSULTAS_USUARIOS.ASOCIAR_USUARIO_A_CLIENTE, [
+ idUsuario,
+ idCliente,
+ ]);
+ }
+ }
+ }
+ await correrQuery(CONSULTAS_USUARIOS.ACTUALIZAR_ROL_USUARIO, [idRol, idUsuario]);
+ }
+ } catch (error) {
+ if (error.code === 'ER_TRUNCATED_WRONG_VALUE') {
+ throw new Error(MENSAJES.ERROR_FECHA_NO_VALIDA.mensaje);
+ }
+ throw new Error(error.message || MENSAJES.ERROR_ACTUALIZAR_USUARIO.mensaje);
+ }
+};
diff --git a/Usuarios/Rutas/RutasIndividuales/actualizarUsuario.routes.js b/Usuarios/Rutas/RutasIndividuales/actualizarUsuario.routes.js
new file mode 100644
index 00000000..d3239e98
--- /dev/null
+++ b/Usuarios/Rutas/RutasIndividuales/actualizarUsuario.routes.js
@@ -0,0 +1,227 @@
+const express = require('express');
+const ruteador = express.Router();
+const PERMISOS = require('@altertex/util/const/permisos');
+const RUTAS = require('@altertex/util/const/rutas');
+const controlador = require('@altertex/usu/ctrl/actualizarUsuario.controller');
+const revisarApiKey = require('@altertex/util/inter/revisarApiKey');
+const autorizarToken = require('@altertex/util/inter/autorizarToken');
+const revisarPermisos = require('@altertex/util/inter/verificarPermisos');
+const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar');
+const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones');
+
+//RF[4] Actualizar Usuario - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF4]
+
+/**
+ * @swagger
+ * /api/usuarios/actualizar:
+ * put:
+ * summary: Actualiza la información de un usuario.
+ * tags:
+ * - Usuarios
+ * security:
+ * - ApiKeyAuth: []
+ * - BearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * oneOf:
+ * - type: object
+ * description: Información del usuario a actualizar directamente
+ * required:
+ * - idUsuario
+ * - nombreCompleto
+ * - correoElectronico
+ * - contrasenia
+ * - numeroTelefono
+ * - direccion
+ * - fechaNacimiento
+ * - genero
+ * - estatus
+ * - idRol
+ * properties:
+ * idUsuario:
+ * type: integer
+ * example: 30
+ * nombreCompleto:
+ * type: string
+ * example: "Luis Hernández"
+ * correoElectronico:
+ * type: string
+ * example: "lhernandez@gmail.com"
+ * contrasenia:
+ * type: string
+ * example: "NuevaContraseniaSegura123"
+ * numeroTelefono:
+ * type: string
+ * example: "5551234567"
+ * direccion:
+ * type: string
+ * example: "Av. Revolución 123, CDMX"
+ * fechaNacimiento:
+ * type: string
+ * format: date
+ * example: "1995-06-15"
+ * genero:
+ * type: string
+ * enum: [Masculino, Femenino, Otro]
+ * example: "Masculino"
+ * estatus:
+ * type: boolean
+ * example: true
+ * idRol:
+ * type: integer
+ * example: 2
+ * - type: object
+ * properties:
+ * usuarios:
+ * oneOf:
+ * - type: object
+ * required:
+ * - idUsuario
+ * - nombreCompleto
+ * - correoElectronico
+ * - contrasenia
+ * - numeroTelefono
+ * - direccion
+ * - fechaNacimiento
+ * - genero
+ * - estatus
+ * - idRol
+ * properties:
+ * idUsuario:
+ * type: integer
+ * example: 30
+ * nombreCompleto:
+ * type: string
+ * example: "Luis Hernández"
+ * correoElectronico:
+ * type: string
+ * example: "lhernandez@gmail.com"
+ * contrasenia:
+ * type: string
+ * example: "NuevaContraseniaSegura123"
+ * numeroTelefono:
+ * type: string
+ * example: "5551234567"
+ * direccion:
+ * type: string
+ * example: "Av. Revolución 123, CDMX"
+ * fechaNacimiento:
+ * type: string
+ * format: date
+ * example: "1995-06-15"
+ * genero:
+ * type: string
+ * example: "Masculino"
+ * estatus:
+ * type: boolean
+ * example: true
+ * idRol:
+ * type: integer
+ * example: 2
+ * - type: array
+ * items:
+ * type: object
+ * required:
+ * - idUsuario
+ * - nombreCompleto
+ * - correoElectronico
+ * - contrasenia
+ * - numeroTelefono
+ * - direccion
+ * - fechaNacimiento
+ * - genero
+ * - estatus
+ * - idRol
+ * properties:
+ * idUsuario:
+ * type: integer
+ * example: 30
+ * nombreCompleto:
+ * type: string
+ * example: "Luis Hernández"
+ * correoElectronico:
+ * type: string
+ * example: "lhernandez@gmail.com"
+ * contrasenia:
+ * type: string
+ * example: "NuevaContraseniaSegura123"
+ * numeroTelefono:
+ * type: string
+ * example: "5551234567"
+ * direccion:
+ * type: string
+ * example: "Av. Revolución 123, CDMX"
+ * fechaNacimiento:
+ * type: string
+ * format: date
+ * example: "1995-06-15"
+ * genero:
+ * type: string
+ * example: "Masculino"
+ * estatus:
+ * type: boolean
+ * example: true
+ * idRol:
+ * type: integer
+ * example: 2
+ * responses:
+ * 200:
+ * description: Información del usuario actualizada correctamente.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: "Usuario actualizado con éxito."
+ * datos:
+ * type: array
+ * items:
+ * type: object
+ * 400:
+ * description: Error en los datos enviados o en la actualización.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * mensaje:
+ * type: string
+ * example: "Error al actualizar el usuario."
+ * x-codeSamples:
+ * - lang: JavaScript
+ * label: cURL
+ * source: |
+ * curl -X PUT "https://tu-api.com/api/usuarios/actualizar" \
+ * -H "x-api-key: TU_API_KEY" \
+ * -H "Authorization: Bearer TU_TOKEN" \
+ * -H "Content-Type: application/json" \
+ * -d '{
+ * "idUsuario": 30,
+ * "nombreCompleto": "Luis Hernández",
+ * "correoElectronico": "lhernandez@gmail.com",
+ * "contrasenia": "NuevaContraseniaSegura123",
+ * "numeroTelefono": "5551234567",
+ * "direccion": "Av. Revolución 123, CDMX",
+ * "fechaNacimiento": "1995-06-15",
+ * "genero": "Masculino",
+ * "estatus": true,
+ * "idRol": 2
+ * }'
+ */
+
+ruteador.put(
+ RUTAS.USUARIOS.ACTUALIZAR,
+ revisarApiKey(),
+ validarYSanitizar,
+ autorizarToken,
+ limitePeticionesDiarias,
+ revisarPermisos(PERMISOS.ACTUALIZAR_USUARIO),
+ controlador.actualizarUsuario
+);
+
+module.exports = ruteador;
diff --git a/Usuarios/Rutas/RutasIndividuales/leerUsuario.routes.js b/Usuarios/Rutas/RutasIndividuales/leerUsuario.routes.js
index 084d5f62..4485e114 100644
--- a/Usuarios/Rutas/RutasIndividuales/leerUsuario.routes.js
+++ b/Usuarios/Rutas/RutasIndividuales/leerUsuario.routes.js
@@ -85,7 +85,7 @@
* mensaje:
* type: string
* example: "No se encontró un usuario con el ID proporcionado."
- * 500:
+ * 400:
* description: Error interno del servidor al consultar el usuario.
* content:
* application/json:
diff --git a/Usuarios/Rutas/indexUsuarios.routes.js b/Usuarios/Rutas/indexUsuarios.routes.js
index 5dc5ed19..f6fa16d0 100644
--- a/Usuarios/Rutas/indexUsuarios.routes.js
+++ b/Usuarios/Rutas/indexUsuarios.routes.js
@@ -4,6 +4,7 @@ const rutasCrearUsuario = require('@altertex/usu/rutasInd/crearUsuario.routes');
const rutasLeerUsuario = require('@altertex/usu/rutasInd/leerUsuario.routes');
const rutasConsultarListaUsuarios = require('@altertex/usu/rutasInd/consultarListaUsuarios.routes');
const rutasEliminarUsuario = require('@altertex/usu/rutasInd/eliminarUsuario.routes');
+const rutasActualizarUsuario = require('@altertex/usu/rutasInd/actualizarUsuario.routes');
const RUTAS = require('@altertex/util/const/rutas');
@@ -11,5 +12,6 @@ ruteador.use(RUTAS.USUARIOS.BASE, rutasConsultarListaUsuarios);
ruteador.use(RUTAS.USUARIOS.BASE, rutasCrearUsuario);
ruteador.use(RUTAS.USUARIOS.BASE, rutasLeerUsuario);
ruteador.use(RUTAS.USUARIOS.BASE, rutasEliminarUsuario);
+ruteador.use(RUTAS.USUARIOS.BASE, rutasActualizarUsuario);
module.exports = ruteador;
diff --git a/Utilidades/Constantes/consultasCategorias.js b/Utilidades/Constantes/consultasCategorias.js
index 6890d77a..9d9f34c9 100644
--- a/Utilidades/Constantes/consultasCategorias.js
+++ b/Utilidades/Constantes/consultasCategorias.js
@@ -1,17 +1,16 @@
module.exports = {
OBTENER_CATEGORIAS_CON_PRODUCTOS: `
- SELECT c.idCategoria,
- c.nombreCategoria,
- c.descripcion,
- COUNT(p.idProducto) AS cantidadProductos,
- p.idCliente
- FROM categoria c
- JOIN
- categoria_producto cp ON c.idCategoria = cp.idCategoria
- JOIN
- producto p ON cp.idProducto = p.idProducto
- WHERE p.idCliente = ?
- GROUP BY c.idCategoria, c.nombreCategoria, c.descripcion, p.idCliente;
+ SELECT
+ c.idCategoria,
+ c.nombreCategoria,
+ c.descripcion,
+ COUNT(p.idProducto) AS cantidadProductos
+ FROM
+ categoria c
+ LEFT JOIN categoria_producto cp ON c.idCategoria = cp.idCategoria
+ LEFT JOIN producto p ON cp.idProducto = p.idProducto AND p.idCliente = ?
+ GROUP BY
+ c.idCategoria, c.nombreCategoria, c.descripcion;
`,
CREAR_CATEGORIAS: `
@@ -47,4 +46,32 @@ module.exports = {
FROM producto
WHERE idProducto IN (?);
`,
+
+ LEER_DETALLE_CATEGORIA: `
+ SELECT
+ c.idCategoria,
+ c.nombreCategoria,
+ c.descripcion,
+ p.idProducto,
+ p.nombreComun
+ FROM categoria c
+ LEFT JOIN categoria_producto cp ON c.idCategoria = cp.idCategoria
+ LEFT JOIN producto p ON cp.idProducto = p.idProducto
+ WHERE c.idCategoria = ?;
+ `,
+
+ ACTUALIZAR_CATEGORIA: `
+ UPDATE categoria
+ SET nombreCategoria = ?, descripcion = ?
+ WHERE idCategoria = ?;
+ `,
+
+ ELIMINAR_PRODUCTOS_CATEGORIA: `
+ DELETE FROM categoria_producto WHERE idCategoria = ?;
+ `,
+
+ ASIGNAR_PRODUCTOS_A_CATEGORIA: `
+ INSERT INTO categoria_producto (idCategoria, idProducto)
+ VALUES ?;
+ `,
};
diff --git a/Utilidades/Constantes/consultasCuotas.js b/Utilidades/Constantes/consultasCuotas.js
index be725c22..3308c3d1 100644
--- a/Utilidades/Constantes/consultasCuotas.js
+++ b/Utilidades/Constantes/consultasCuotas.js
@@ -40,6 +40,44 @@ module.exports = {
ELIMINAR_CUOTA_SET: `
DELETE FROM cuota_set WHERE idCuotaSet = ?;
`,
+ LEER_CUOTA_SET: `
+ SELECT
+ cs.idCuotaSet,
+ cs.nombre,
+ cs.descripcion,
+ cs.periodoRenovacion,
+ cs.renovacionHabilitada,
+ cs.ultimaActualizacion
+ FROM cuota_set cs
+ WHERE cs.idCuotaSet = ?;
+ `,
+
+ LEER_CUOTA_SET_PRODUCTOS:
+ `SELECT
+ p.idProducto,
+ p.nombreComun,
+ csp.limite AS cuota_valor,
+ csp.limite_actual
+ FROM cuota_set cs
+ JOIN cuota_set_producto csp
+ ON cs.idCuotaSet = csp.idCuotaSet
+ JOIN producto p
+ ON p.idProducto = csp.idProducto
+ WHERE cs.idCuotaSet = ?`,
+
+ ACTUALIZAR_CUOTA_SET: `
+ UPDATE cuota_set
+ SET nombre = ?, descripcion = ?, periodoRenovacion = ?, renovacionHabilitada = ?, ultimaActualizacion = ?
+ WHERE idCuotaSet = ?;
+ `,
+
+ INSERTAR_CUOTA_PRODUCTO_ACTUALIZAR: `
+ INSERT INTO cuota_set_producto (idCuotaSet, idProducto, limite, limite_actual)
+ VALUES (?, ?, ?, ?)
+ `,
+
+ ELIMINAR_PRODUCTOS_CUOTA_SET: `
+ DELETE FROM cuota_set_producto WHERE idCuotaSet = ?;
+ `,
-
};
diff --git a/Utilidades/Constantes/consultasEmpleados.js b/Utilidades/Constantes/consultasEmpleados.js
index 38e3b382..fa19ab29 100644
--- a/Utilidades/Constantes/consultasEmpleados.js
+++ b/Utilidades/Constantes/consultasEmpleados.js
@@ -24,4 +24,58 @@ module.exports = {
numeroEmergencia = ?, areaTrabajo = ?, posicion = ?,
cantidadPuntos = ?, antiguedad = ? WHERE idEmpleado = ?;
`,
+ OBTENER_DATOS_EXPORTACION: `
+ SELECT
+ e.idEmpleado,
+ u.nombreCompleto,
+ u.correoElectronico,
+ u.numeroTelefono,
+ u.direccion,
+ u.fechaNacimiento,
+ u.genero,
+ CASE
+ WHEN u.estatus = 1 THEN 'Activo'
+ WHEN u.estatus = 0 THEN 'Inactivo'
+ ELSE 'Desconocido'
+ END AS estatus,
+ e.numeroEmergencia,
+ e.areaTrabajo,
+ e.posicion,
+ e.cantidadPuntos,
+ e.antiguedad
+ FROM empleado e
+ JOIN usuario u ON e.idUsuario = u.idUsuario
+ WHERE e.idCliente = ? AND e.idEmpleado IN (__IDS__);
+ `,
+ OBTENER_ULTIMO_ID_EMPLEADO: `
+ SELECT idEmpleado FROM empleado ORDER BY idEmpleado DESC LIMIT 1;
+ `,
+ CONSULTAR_ID_VALIDO: `
+ SELECT
+ CASE
+ WHEN NOT EXISTS (SELECT 1 FROM usuarios WHERE idUsuario = ?)
+ THEN 'No hay ningún usuario registrado bajo este ID'
+ WHEN EXISTS (SELECT 1 FROM empleado WHERE idUsuario = ?)
+ THEN 'Este usuario ya está registrado como empleado, revisa de nuevo el ID a usar'
+ ELSE 'OK'
+ END AS resultado;
+ `,
+
+ INSERTAR_USUARIO: `
+ INSERT INTO usuario
+ (nombreCompleto, correoElectronico, contrasenia, numeroTelefono, direccion, fechaNacimiento, genero, estatus)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
+
+ `,
+
+ INSERTAR_ROL: `
+ INSERT INTO usuario_rol (idUsuario, idRol)
+ VALUES (?, ?)
+ `,
+
+ INSERTAR_USUARIO_CLIENTE: `
+ INSERT INTO usuario_cliente (idUsuario, idCliente)
+ VALUES (?, ?)
+ `,
+
};
diff --git a/Utilidades/Constantes/consultasEventos.js b/Utilidades/Constantes/consultasEventos.js
index c9d54bcc..022c9eca 100644
--- a/Utilidades/Constantes/consultasEventos.js
+++ b/Utilidades/Constantes/consultasEventos.js
@@ -1,4 +1,11 @@
module.exports = {
+ CREAR_EVENTO: `
+ INSERT INTO evento (idCliente, nombre, descripcion, puntos, multiplicador, periodoRenovacion, renovacion)
+ VALUES (?, ?, ?, ?, ?, ?, ?);
+ `,
+ VERIFICAR_CLIENTE: `
+ SELECT idCliente FROM cliente WHERE idCliente = ?;
+ `,
OBTENER_LISTA_EVENTOS: `
SELECT
e.idEvento,
diff --git a/Utilidades/Constantes/consultasGrupoEmpleados.js b/Utilidades/Constantes/consultasGrupoEmpleados.js
index a5919454..ffdd2b33 100644
--- a/Utilidades/Constantes/consultasGrupoEmpleados.js
+++ b/Utilidades/Constantes/consultasGrupoEmpleados.js
@@ -1,54 +1,145 @@
module.exports = {
OBTENER_LISTA: `
- SELECT
- ge.idGrupo,
- ge.nombre AS geNombre,
- ge.descripcion,
- COUNT(eg.idEmpleado) AS totalEmpleados
+ SELECT ge.idGrupo,
+ ge.nombre AS geNombre,
+ ge.descripcion,
+ COUNT(eg.idEmpleado) AS totalEmpleados
FROM grupo_empleado ge
- LEFT JOIN empleado_grupo eg ON ge.idGrupo = eg.idGrupo
+ LEFT JOIN empleado_grupo eg ON ge.idGrupo = eg.idGrupo
WHERE ge.idCliente = ?
GROUP BY ge.idGrupo;
- `,
+ `,
ELIMINAR_SET_PRODUCTO_GRUPO: `
- DELETE FROM set_producto_grupo_empleado WHERE idGrupo = ?;
+ DELETE
+ FROM set_producto_grupo_empleado
+ WHERE idGrupo = ?;
`,
ELIMINAR_GRUPO: `
- DELETE FROM grupo_empleado WHERE idGrupo = ?;
- `,
+ DELETE
+ FROM grupo_empleado
+ WHERE idGrupo = ?;
+ `,
ELIMINAR_EMPLEADO_GRUPO: `
- DELETE FROM empleado_grupo WHERE idGrupo = ?;
- `,
+ DELETE
+ FROM empleado_grupo
+ WHERE idGrupo = ?;
+ `,
LEER_GRUPO: `
- SELECT
- ge.idGrupo,
- ge.nombre AS nombre,
- ge.descripcion AS descripcion,
- IFNULL(GROUP_CONCAT(DISTINCT sp.nombre SEPARATOR ', '), 'Sin sets de productos asociados') AS setsProductos,
- IFNULL(GROUP_CONCAT(DISTINCT CONCAT(
- u.nombreCompleto, ' | ',
- u.correoElectronico, ' | ',
- e.areaTrabajo
- ) SEPARATOR ' || '), 'Sin empleados asociados') AS infoEmpleados
+ SELECT ge.idGrupo,
+ ge.nombre AS nombre,
+ ge.descripcion AS descripcion,
+
+ IFNULL(GROUP_CONCAT(DISTINCT sp.nombre SEPARATOR ', '), 'Sin sets de productos asociados') AS setsProductos,
+ IFNULL(GROUP_CONCAT(DISTINCT sp.idSetProducto SEPARATOR ','), '') AS idsSetProductos,
+
+ IFNULL(GROUP_CONCAT(DISTINCT CONCAT(
+ u.nombreCompleto, ' | ',
+ u.correoElectronico, ' | ',
+ e.areaTrabajo
+ ) SEPARATOR ' || '), 'Sin empleados asociados') AS infoEmpleados,
+
+ IFNULL(GROUP_CONCAT(DISTINCT e.idEmpleado SEPARATOR ','), '') AS idsEmpleados,
+ IFNULL(
+ JSON_ARRAYAGG(
+ JSON_OBJECT(
+ 'id', e.idEmpleado,
+ 'correo', u.correoElectronico,
+ 'nombre', u.nombreCompleto,
+ 'area', e.areaTrabajo
+ )
+ ),
+ JSON_ARRAY()
+ ) AS empleadosActualizar,
+ IFNULL(
+ JSON_ARRAYAGG(
+ JSON_OBJECT(
+ 'id', sp.idSetProducto,
+ 'nombreProducto', sp.nombre,
+ 'activo', sp.activo
+ )
+ ),
+ JSON_ARRAY()
+ ) AS setProductosActualizar
FROM grupo_empleado ge
- LEFT JOIN empleado_grupo eg ON ge.idGrupo = eg.idGrupo
- LEFT JOIN empleado e ON eg.idEmpleado = e.idEmpleado
- LEFT JOIN usuario u ON e.idUsuario = u.idUsuario
- LEFT JOIN set_producto_grupo_empleado spge ON ge.idGrupo = spge.idGrupo
- LEFT JOIN set_producto sp ON spge.idSetProducto = sp.idSetProducto
+ LEFT JOIN empleado_grupo eg ON ge.idGrupo = eg.idGrupo
+ LEFT JOIN empleado e ON eg.idEmpleado = e.idEmpleado
+ LEFT JOIN usuario u ON e.idUsuario = u.idUsuario
+ LEFT JOIN set_producto_grupo_empleado spge ON ge.idGrupo = spge.idGrupo
+ LEFT JOIN set_producto sp ON spge.idSetProducto = sp.idSetProducto
WHERE ge.idGrupo = ?
GROUP BY ge.idGrupo
ORDER BY ge.idGrupo;
- `,
+ `,
+ ACTUALIZAR_GRUPO_EMPLEADOS_NOMBRE_DESCRIPCION: `
+ UPDATE grupo_empleado
+ SET nombre = ?,
+ descripcion = ?
+ WHERE idGrupo = ?
+ AND (nombre != ? OR descripcion != ?);
+ `,
+ ELIMINAR_EMPLEADOS_DE_GRUPO_BASE: `
+ DELETE
+ FROM empleado_grupo
+ WHERE idGrupo = __ID__
+ AND idEmpleado NOT IN (__EMPLEADOS__);
+ `,
+ AGREGAR_EMPLEADOS_NUEVOS_BASE: `
+ INSERT
+ IGNORE INTO empleado_grupo (idEmpleado, idGrupo)
+ VALUES __VALORES__;
+ `,
+ VERIFICAR_EMPLEADOS_CLIENTE: `
+ SELECT COUNT(*) AS validos
+ FROM empleado e
+ JOIN grupo_empleado g ON g.idGrupo = ?
+ WHERE e.idEmpleado IN (__EMPLEADOS__)
+ AND e.idCliente = g.idCliente
+ `,
+ VERIFICAR_SETS_CLIENTE: `
+ SELECT COUNT(*) AS validos
+ FROM set_producto s
+ JOIN grupo_empleado g ON g.idGrupo = ?
+ WHERE s.idSetProducto IN (__SETS__)
+ AND s.idCliente = g.idCliente
+ `,
+
+ ELIMINAR_SETS_DE_GRUPO_BASE: `
+ DELETE
+ FROM set_producto_grupo_empleado
+ WHERE idGrupo = __ID__
+ AND idSetProducto NOT IN (__SETS__);
+ `,
+
+ AGREGAR_SETS_NUEVOS_BASE: `
+ INSERT
+ IGNORE INTO set_producto_grupo_empleado (idSetProducto, idGrupo)
+ VALUES __VALORES__;
+ `,
VALIDAR_NOMBRE_REPETIDO: `
- SELECT 1 FROM grupo_empleado
- WHERE idCliente = ? AND nombre = ? LIMIT 1
+ SELECT 1
+ FROM grupo_empleado
+ WHERE idCliente = ?
+ AND nombre = ? LIMIT 1
`,
CREAR_GRUPO: `
- INSERT INTO grupo_empleado (idCliente, nombre, descripcion) VALUES (?, ?, ?);
+ INSERT INTO grupo_empleado (idCliente, nombre, descripcion)
+ VALUES (?, ?, ?);
`,
ASIGNAR_EMPLEADO_A_GRUPO: `
- INSERT INTO empleado_grupo (idEmpleado, idGrupo) VALUES (?, ?);
- `
-};
+ INSERT INTO empleado_grupo (idEmpleado, idGrupo)
+ VALUES (?, ?);
+ `,
+
+ ELIMINAR_TODOS_EMPLEADOS_DE_GRUPO: `
+ DELETE
+ FROM empleado_grupo
+ WHERE idGrupo = ?;
+ `,
+
+ ELIMINAR_TODOS_SETS_DE_GRUPO: `
+ DELETE
+ FROM set_producto_grupo_empleado
+ WHERE idGrupo = ?;
+ `,
+};
\ No newline at end of file
diff --git a/Utilidades/Constantes/consultasImportarEmpleados.js b/Utilidades/Constantes/consultasImportarEmpleados.js
index 9701c024..abaded26 100644
--- a/Utilidades/Constantes/consultasImportarEmpleados.js
+++ b/Utilidades/Constantes/consultasImportarEmpleados.js
@@ -1,28 +1,27 @@
module.exports = {
-
- VALIDAR_CORREOS_DUPLICADOS: `
+ VALIDAR_CORREOS_DUPLICADOS: `
SELECT correoElectronico FROM usuario WHERE correoElectronico IN (?)
`,
- VALIDAR_TELEFONO_DUPLICADO: `
+ VALIDAR_TELEFONO_DUPLICADO: `
SELECT numeroTelefono FROM usuario WHERE numeroTelefono IN (?)
`,
- INSERTAR_USUARIO_EN_VOLUMEN:`
+ INSERTAR_USUARIO_EN_VOLUMEN: `
INSERT INTO usuario
(nombreCompleto, correoElectronico, contrasenia, numeroTelefono, direccion, fechaNacimiento, genero, estatus)
VALUES ?
`,
- OBTENER_ID_GENERADOS: `
+ OBTENER_ID_GENERADOS: `
SELECT idUsuario, correoElectronico FROM usuario WHERE correoElectronico IN (?)
`,
- INSERTAR_ROLES_EN_VOLUMEN:`
+ INSERTAR_ROLES_EN_VOLUMEN: `
INSERT INTO usuario_rol (idUsuario, idRol) VALUES ?
`,
- INSERTAR_USUARIO_CLIENTE_EN_VOLUMEN:`
+ INSERTAR_USUARIO_CLIENTE_EN_VOLUMEN: `
INSERT INTO usuario_cliente (idUsuario, idCliente) VALUES ?
`,
- INSERTAR_EMPLEADOS_EN_VOLUMEN:`
+ INSERTAR_EMPLEADOS_EN_VOLUMEN: `
INSERT INTO empleado
(idUsuario, idCliente, numeroEmergencia, areaTrabajo, posicion, cantidadPuntos, antiguedad)
VALUES ?
`,
-}
\ No newline at end of file
+};
diff --git a/Utilidades/Constantes/consultasPedidos.js b/Utilidades/Constantes/consultasPedidos.js
index a92f3cfc..1426a12d 100644
--- a/Utilidades/Constantes/consultasPedidos.js
+++ b/Utilidades/Constantes/consultasPedidos.js
@@ -34,4 +34,16 @@ module.exports = {
ELIMINAR_PEDIDO: `
DELETE FROM pedido
WHERE idPedido = ?;`,
+
+ ACTUALIZAR_PEDIDO: `
+ UPDATE pedido
+ JOIN pago ON pedido.idPago = pago.idPago
+ JOIN envio ON pedido.idEnvio = envio.idEnvio
+ SET
+ pedido.estado = ?,
+ pedido.precioTotal = ?,
+ pago.estatus = ?,
+ envio.estado = ?
+ WHERE pedido.idPedido = ?;
+ `,
};
diff --git a/Utilidades/Constantes/consultasProductos.js b/Utilidades/Constantes/consultasProductos.js
index 81b5df4c..b3526c61 100644
--- a/Utilidades/Constantes/consultasProductos.js
+++ b/Utilidades/Constantes/consultasProductos.js
@@ -2,28 +2,124 @@ module.exports = {
OBTENER_LISTA: `
SELECT p.idProducto, p.nombreComun, p.precioVenta, p.estado, i.urlImagen
FROM producto p
- JOIN imagen_producto ip ON p.idProducto = ip.idProducto
- JOIN imagen i ON ip.idImagen = i.idImagen
- WHERE i.tipoImagen = "Imagen Producto"
- AND p.idCliente = ?;
+ LEFT JOIN imagen_producto ip ON p.idProducto = ip.idProducto
+ LEFT JOIN imagen i ON ip.idImagen = i.idImagen AND i.tipoImagen = "Imagen Producto"
+ WHERE p.idCliente = ?;
`,
CREAR: `
- INSERT INTO producto (
- idCliente, idProveedor, nombreComun, nombreComercial, descripcion,
- marca, modelo, tipoProducto, precioPuntos, precioCliente,
- precioVenta, costo, impuesto, descuento, estado, envio
- )
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
- `,
+ INSERT INTO producto (idCliente, idProveedor, nombreComun, nombreComercial, descripcion,
+ marca, modelo, tipoProducto, precioPuntos, precioCliente,
+ precioVenta, costo, impuesto, descuento, estado, envio)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
+ `,
CREAR_IMAGEN_PRODUCTO: `
- INSERT INTO imagen_producto (idImagen, idProducto)
- VALUES (?, ?);
- `,
+ INSERT INTO imagen_producto (idImagen, idProducto)
+ VALUES (?, ?);
+ `,
CREAR_DATOS_ENVIO: `
- INSERT INTO datos_envio (idProducto, peso, longitud, ancho, altura, volumen, tipoPaquete)
- VALUES (?, ?, ?, ?, ?, ?,?);
- `,
+ INSERT INTO datos_envio (idProducto, peso, longitud, ancho, altura, volumen, tipoPaquete)
+ VALUES (?, ?, ?, ?, ?, ?, ?);
+ `,
ELIMINAR_PRODUCTOS:
"DELETE FROM producto WHERE idProducto IN (?)",
+
+ OBTENER_IMAGENES_POR_IDS: `
+ SELECT p.idProducto, i.urlImagen
+ FROM producto p
+ LEFT JOIN imagen_producto ip ON p.idProducto = ip.idProducto
+ LEFT JOIN imagen i ON ip.idImagen = i.idImagen
+ WHERE p.idProducto IN (?);
+ `,
+
+ LEER_PRODUCTO: `
+ SELECT JSON_OBJECT(
+ 'idProducto', p.idProducto,
+ 'idProveedor', p.idProveedor,
+ 'nombreComun', p.nombreComun,
+ 'nombreComercial', p.nombreComercial,
+ 'marca', p.marca,
+ 'modelo', p.modelo,
+ 'tipoProducto', p.tipoProducto,
+ 'precioPuntos', p.precioPuntos,
+ 'precioCliente', p.precioCliente,
+ 'precioVenta', p.precioVenta,
+ 'costo', p.costo,
+ 'impuesto', p.impuesto,
+ 'descuento', p.descuento,
+ 'estado', p.estado,
+ 'envio', p.envio,
+ 'nombreProveedor', pr.nombreCompania,
+ 'variantes', (SELECT JSON_ARRAYAGG(
+ JSON_OBJECT(
+ 'idVariante', v.idVariante,
+ 'nombreVariante', v.nombreVariante,
+ 'descripcion', v.descripcion,
+ 'opciones', (SELECT JSON_ARRAYAGG(
+ JSON_OBJECT(
+ 'cantidad', o.cantidad,
+ 'valorOpcion',
+ o.valorOpcion,
+ 'SKUautomatico',
+ o.SKUautomatico,
+ 'SKUcomercial',
+ o.SKUcomercial,
+ 'costoAdicional',
+ o.costoAdicional,
+ 'descuento', o.descuento,
+ 'estado', o.estado
+ )
+ )
+ FROM opcion o
+ WHERE o.idVariante = v.idVariante)
+ )
+ )
+ FROM variante v
+ WHERE v.idProducto = p.idProducto)
+ ) AS producto
+ FROM producto p
+ LEFT JOIN proveedor pr ON p.idProveedor = pr.idProveedor
+ WHERE p.idProducto = ?
+ AND p.idCliente = ?;
+ `,
+ OBTENER_DATOS_EXPORTACION: `
+ SELECT
+ p.idProducto,
+ p.idProveedor,
+ p.nombreComun AS nombreProducto,
+ p.nombreComercial,
+ p.descripcion AS descripcionProducto,
+ p.tipoProducto,
+ p.marca,
+ p.modelo,
+ p.costo,
+ p.precioVenta,
+ p.precioCliente,
+ p.precioPuntos,
+ p.impuesto,
+ p.descuento,
+ p.estado,
+ p.envio,
+ GROUP_CONCAT(
+ CONCAT(
+ v.nombreVariante, '-',
+ v.descripcion, ',',
+ (SELECT GROUP_CONCAT(
+ CONCAT(o.valorOpcion, ':', o.SKUcomercial, ':', o.SKUautomatico, ':', o.cantidad)
+ SEPARATOR ', '
+ )
+ FROM opcion o
+ WHERE o.idVariante = v.idVariante
+ )
+ )
+ SEPARATOR ' | '
+ ) AS variantes_opciones
+ FROM producto p
+ JOIN variante v ON v.idProducto = p.idProducto
+ WHERE p.idCliente = ? AND p.idProducto IN (__IDS__)
+ GROUP BY p.idProducto, p.idProveedor, p.nombreComun, p.nombreComercial,
+ p.descripcion, p.tipoProducto, p.marca, p.modelo, p.costo,
+ p.precioVenta, p.precioCliente, p.precioPuntos, p.impuesto,
+ p.descuento, p.estado, p.envio;
+ `,
};
diff --git a/Utilidades/Constantes/consultasProveedores.js b/Utilidades/Constantes/consultasProveedores.js
index 73fad2c5..95edd293 100644
--- a/Utilidades/Constantes/consultasProveedores.js
+++ b/Utilidades/Constantes/consultasProveedores.js
@@ -7,4 +7,8 @@ module.exports = {
INSERT INTO proveedor (idCliente, nombre, nombreCompania, telefonoContacto, direccion, codigoPostal, pais, estado)
VALUES (?, ?, ?, ?, ?, ?, ?, ?);
`,
-};
+ VERIFICAR_EXISTE:`
+ SELECT idProveedor FROM proveedor WHERE idProveedor = ? LIMIT 1
+ `
+}
+
diff --git a/Utilidades/Constantes/consultasRoles.js b/Utilidades/Constantes/consultasRoles.js
index 01d1fc37..3f6de048 100644
--- a/Utilidades/Constantes/consultasRoles.js
+++ b/Utilidades/Constantes/consultasRoles.js
@@ -28,38 +28,64 @@ module.exports = {
* Agrupa los resultados por `idRol` para consolidar la información por rol.
*/
OBTENER_LISTA: `
- SELECT r.idRol, r.nombre, r.descripcion, COUNT(ur.idUsuario) AS totalUsuarios
- FROM rol r
- LEFT JOIN usuario_rol ur ON r.idRol = ur.idRol
- GROUP BY r.idRol;
+ SELECT r.idRol, r.nombre, r.descripcion, COUNT(ur.idUsuario) AS totalUsuarios
+ FROM rol r
+ LEFT JOIN usuario_rol ur ON r.idRol = ur.idRol
+ GROUP BY r.idRol;
`,
VERIFICAR_NOMBRE_ROL: `
- SELECT idRol FROM rol WHERE nombre = ? LIMIT 1`,
+ SELECT idRol
+ FROM rol
+ WHERE nombre = ? LIMIT 1`,
VERIFICAR_PERMISO: `
- SELECT idPermiso FROM permiso WHERE idPermiso = ? LIMIT 1`,
+ SELECT idPermiso
+ FROM permiso
+ WHERE idPermiso = ? LIMIT 1`,
INSERTAR_ROL: `
- INSERT INTO rol (nombre, descripcion)
- VALUES (?, ?)`,
+ INSERT INTO rol (nombre, descripcion)
+ VALUES (?, ?)`,
INSERTAR_ROL_PERMISO: `
- INSERT INTO rol_permiso (idRol, idPermiso)
- VALUES (?, ?)`,
+ INSERT INTO rol_permiso (idRol, idPermiso)
+ VALUES (?, ?)`,
OBTENER_PERMISOS_POR_CLIENTE: `
- SELECT idPermiso AS id, nombre FROM permiso;
+ SELECT idPermiso AS id, nombre
+ FROM permiso;
`,
ELIMINAR_ROL: `
- DELETE FROM rol
- WHERE idRol IN (__IDS__);
+ DELETE
+ FROM rol
+ WHERE idRol IN (__IDS__);
`,
VALIDAR_ROL_SIN_USUARIOS: `
- SELECT COUNT(*) AS cantidad
- FROM usuario_rol
- WHERE idRol IN (__IDS__);
+ SELECT COUNT(*) AS cantidad
+ FROM usuario_rol
+ WHERE idRol IN (__IDS__);
+ `,
+ OBTENER_DETALLE_ROL: `
+ SELECT r.idRol,
+ r.nombre AS nombreRol,
+ r.descripcion AS descripcionRol,
+ (SELECT COUNT(*)
+ FROM usuario_rol ur
+ WHERE ur.idRol = r.idRol) AS totalUsuarios,
+ p.idPermiso,
+ p.nombre AS nombrePermiso,
+ p.descripcion AS descripcionPermiso
+ FROM rol r
+ LEFT JOIN rol_permiso rp ON r.idRol = rp.idRol
+ LEFT JOIN permiso p ON rp.idPermiso = p.idPermiso
+ WHERE r.idRol = ?;
`,
+ VERIFICAR_NOMBRE_DUPLICADO_ROL: `
+ select *
+ from rol
+ where nombre = ?;
+ `,
-};
+};
\ No newline at end of file
diff --git a/Utilidades/Constantes/consultasSetsProductos.js b/Utilidades/Constantes/consultasSetsProductos.js
index 741e9a16..07a20c80 100644
--- a/Utilidades/Constantes/consultasSetsProductos.js
+++ b/Utilidades/Constantes/consultasSetsProductos.js
@@ -1,30 +1,78 @@
module.exports = {
OBTENER_LISTA: `
- SELECT
- sp.idSetProducto,
- sp.nombre,
- sp.descripcion,
- sp.activo,
- GROUP_CONCAT(DISTINCT p.nombreComun SEPARATOR ', ') AS productos,
- GROUP_CONCAT(DISTINCT ge.nombre SEPARATOR ', ') AS grupos
- FROM set_producto sp
- LEFT JOIN producto_set_producto psp ON psp.idSetProducto = sp.idSetProducto
- LEFT JOIN producto p ON p.idProducto = psp.idProducto
- LEFT JOIN set_producto_grupo_empleado spge ON spge.idSetProducto = sp.idSetProducto
- LEFT JOIN grupo_empleado ge ON ge.idGrupo = spge.idGrupo
- WHERE sp.idCliente = ?
- GROUP BY sp.idSetProducto, sp.nombre, sp.descripcion, sp.activo;
- `,
+ SELECT sp.idSetProducto,
+ sp.nombre,
+ sp.descripcion,
+ sp.activo,
+ GROUP_CONCAT(DISTINCT p.nombreComun SEPARATOR ', ') AS productos,
+ GROUP_CONCAT(DISTINCT ge.nombre SEPARATOR ', ') AS grupos,
+ GROUP_CONCAT(p.idProducto SEPARATOR ', ') AS idsProductos
+ FROM set_producto sp
+ LEFT JOIN producto_set_producto psp ON psp.idSetProducto = sp.idSetProducto
+ LEFT JOIN producto p ON p.idProducto = psp.idProducto
+ LEFT JOIN set_producto_grupo_empleado spge ON spge.idSetProducto = sp.idSetProducto
+ LEFT JOIN grupo_empleado ge ON ge.idGrupo = spge.idGrupo
+ WHERE sp.idCliente = ?
+ GROUP BY sp.idSetProducto, sp.nombre, sp.descripcion, sp.activo;
+ `,
ELIMINAR_SET_PRODUCTOS_GRUPO_EMPLEADOS: `
- DELETE FROM set_producto_grupo_empleado
+ DELETE
+ FROM set_producto_grupo_empleado
WHERE idSetProducto = ?;
- `,
+ `,
ELIMINAR_PRODUCTOS_SET_PRODUCTOS: `
- DELETE FROM producto_set_producto
+ DELETE
+ FROM producto_set_producto
WHERE idSetProducto = ?;
- `,
+ `,
ELIMINAR_SET_PRODUCTOS: `
- DELETE FROM set_producto
+ DELETE
+ FROM set_producto
WHERE idSetProducto = ?;
- `,
+ `,
+ CREAR_SET_PRODUCTO: `
+ INSERT INTO set_producto (idCliente, nombre, nombreVisible, descripcion, activo)
+ VALUES (?, ?, ?, ?, ?);
+ `,
+ ASIGNAR_PRODUCTO_SET_PRODUCTO: `
+ INSERT INTO producto_set_producto (idProducto, idSetProducto)
+ VALUES (?, ?);
+ `,
+ CONSULTAR_DUPLICADOS: `
+ SELECT idSetProducto
+ FROM set_producto
+ WHERE idCliente = ?
+ AND (nombre = ? OR nombreVisible = ?);
+ `,
+ CONSULTAR_NOMBRE_DUPLICADO: `
+ SELECT idSetProducto
+ FROM set_producto
+ WHERE idCliente = ? and idSetProducto!= ?
+ AND (nombre = ?);
+ `,
+ CONSULTAR_PRODUCTOS_EXISTENTES: `
+ SELECT idProducto
+ FROM producto
+ WHERE idProducto IN (__IDS__);
+ `,
+ ACTUALIZAR_SET_INFO: `
+ UPDATE set_producto
+ SET nombre = ?, activo = ?, descripcion = ?
+ WHERE idSetProducto = ?
+ `,
+
+ ELIMINAR_PRODUCTOS_DEL_SET: `
+ DELETE FROM producto_set_producto
+ WHERE idSetProducto = __ID__ AND idProducto NOT IN (__PRODUCTOS__)
+ `,
+
+ AGREGAR_PRODUCTOS_AL_SET: `
+ INSERT IGNORE INTO producto_set_producto (idSetProducto, idProducto)
+ VALUES __VALORES__
+ `,
+
+ ELIMINAR_TODOS_PRODUCTOS_DEL_SET: `
+ DELETE FROM producto_set_producto
+ WHERE idSetProducto = ?
+ `,
};
diff --git a/Utilidades/Constantes/consultasUsuarios.js b/Utilidades/Constantes/consultasUsuarios.js
index 0f679e91..e2062013 100644
--- a/Utilidades/Constantes/consultasUsuarios.js
+++ b/Utilidades/Constantes/consultasUsuarios.js
@@ -40,6 +40,11 @@ module.exports = {
VALUES (?, ?);
`,
+ BORRAR_ASOCIACIONES_CLIENTE_USUARIO: `
+ DELETE FROM usuario_cliente
+ WHERE idUsuario = ?;
+ `,
+
ASOCIAR_USUARIO_A_CLIENTE: `
INSERT INTO usuario_cliente (idUsuario, idCliente)
VALUES (?, ?);
@@ -54,8 +59,9 @@ module.exports = {
u.fechaNacimiento,
u.genero,
u.estatus,
- r.nombre AS rol,
- uc.idCliente,
+ r.idRol AS rol,
+ r.nombre AS nombreRol,
+ uc.idCliente AS idCliente,
c.nombreComercial AS nombreCliente
FROM usuario u
LEFT JOIN usuario_rol ur ON u.idUsuario = ur.idUsuario
@@ -66,29 +72,60 @@ module.exports = {
`,
OBTENER_LISTA: `
- SELECT u.idUsuario,
- u.nombreCompleto AS nombre,
- r.nombre AS rol,
- c.nombreComercial AS cliente,
- u.estatus,
- u.correoElectronico AS correo,
- u.numeroTelefono AS telefono
- FROM usuario u
- LEFT JOIN usuario_rol ur ON u.idUsuario = ur.idUsuario
- LEFT JOIN rol r ON ur.idRol = r.idRol
- LEFT JOIN usuario_cliente uc ON u.idUsuario = uc.idUsuario
- LEFT JOIN cliente c ON uc.idCliente = c.idCliente
- WHERE u.idUsuario NOT IN (SELECT ur2.idUsuario
- FROM usuario_rol ur2
- WHERE ur2.idRol = 3);
-
- `,
+ SELECT u.idUsuario,
+ u.nombreCompleto AS nombre,
+ r.idRol AS rol,
+ c.nombreComercial AS cliente,
+ u.estatus,
+ u.correoElectronico AS correo,
+ u.numeroTelefono AS telefono
+ FROM usuario u
+ LEFT JOIN usuario_rol ur ON u.idUsuario = ur.idUsuario
+ LEFT JOIN rol r ON ur.idRol = r.idRol
+ LEFT JOIN usuario_cliente uc ON u.idUsuario = uc.idUsuario
+ LEFT JOIN cliente c ON uc.idCliente = c.idCliente
+ WHERE u.idUsuario NOT IN (SELECT ur2.idUsuario
+ FROM usuario_rol ur2
+ WHERE ur2.idRol = 3);
+`,
ELIMINAR_USUARIOS: `
DELETE
FROM usuario
WHERE idUsuario = (?);
`,
+
+ ACTUALIZAR_DATOS_USUARIO: `
+ UPDATE usuario SET
+ nombreCompleto = ?,
+ correoElectronico = ?,
+ contrasenia = ?,
+ numeroTelefono = ?,
+ direccion = ?,
+ fechaNacimiento = ?,
+ genero = ?,
+ estatus = ?
+ WHERE idUsuario = ?;
+ `,
+
+ ACTUALIZAR_DATOS_USUARIO_SIN_CONTRASENA: `
+ UPDATE usuario SET
+ nombreCompleto = ?,
+ correoElectronico = ?,
+ numeroTelefono = ?,
+ direccion = ?,
+ fechaNacimiento = ?,
+ genero = ?,
+ estatus = ?
+ WHERE idUsuario = ?;
+ `,
+
+ ACTUALIZAR_ROL_USUARIO: `
+ UPDATE usuario_rol SET
+ idRol = ?
+ WHERE idUsuario = ?;
+ `,
+
VALIDAR_CORREO: `
SELECT idUsuario
FROM usuario
@@ -183,6 +220,9 @@ module.exports = {
WHERE idUsuario IN (?)
AND puedeActivar2FA = true;
`,
-
-
+ VALIDAR_CORREO_DUPLICADO_ACTUALIZACION: `
+ SELECT idUsuario
+ FROM usuario
+ WHERE correoElectronico = ? AND idUsuario <> ?;
+ `,
};
diff --git a/Utilidades/Constantes/mensajesCategorias.js b/Utilidades/Constantes/mensajesCategorias.js
index 4b860b8e..3629a3f0 100644
--- a/Utilidades/Constantes/mensajesCategorias.js
+++ b/Utilidades/Constantes/mensajesCategorias.js
@@ -32,7 +32,7 @@ module.exports = {
},
NOMBRE_CATEGORIA_INVALIDO: {
codigo: 400,
- mensaje: 'El nombre de la categoría proporcionado no es válido.',
+ mensaje: 'El nombre de la categoría es obligatorio.',
},
CATEGORIA_YA_EXISTE: {
codigo: 400,
@@ -42,6 +42,10 @@ module.exports = {
codigo: 400,
mensaje: 'Los parámetros proporcionados no son válidos.',
},
+ ERROR_OBTENER_CATEGORIAS: {
+ codigo: 400,
+ mensaje: 'Ocurrió un error al obtener la lista de categorías.',
+ },
// 401 - No autorizado
CREDENCIALES_INVALIDAS: {
@@ -66,10 +70,6 @@ module.exports = {
codigo: 400,
mensaje: 'Ocurrió un error al intentar crear la categoría.',
},
- ERROR_OBTENER_CATEGORIAS: {
- codigo: 400,
- mensaje: 'Ocurrió un error al obtener la lista de categorías.',
- },
ERROR_OBTENER_CATEGORIA: {
codigo: 400,
mensaje: 'Ocurrió un error al obtener los datos de la categoría.',
@@ -82,4 +82,9 @@ module.exports = {
codigo: 400,
mensaje: 'El producto no existe en la base de datos',
},
+ DESCRIPCION_INVALIDA: {
+ codigo: 400,
+ mensaje: 'La descripción proporcionada no es válida.',
+ },
+
};
diff --git a/Utilidades/Constantes/mensajesClientes.js b/Utilidades/Constantes/mensajesClientes.js
index 1d4889f2..3e5ea4e5 100644
--- a/Utilidades/Constantes/mensajesClientes.js
+++ b/Utilidades/Constantes/mensajesClientes.js
@@ -44,6 +44,14 @@ module.exports = {
codigo: 400,
mensaje: 'El ID del cliente debe ser un número entero válido.',
},
+ ERROR_ELIMINAR_CLIENTE: {
+ codigo: 400,
+ mensaje: 'Ocurrió un error al eliminar el cliente.',
+ },
+ ERROR_CONSULTAR_CLIENTE: {
+ codigo: 400,
+ mensaje: 'Ocurrió un error al obtener la información del cliente.',
+ },
// 403 - Forbidden
ACCESO_NO_AUTORIZADO: {
@@ -62,10 +70,6 @@ module.exports = {
},
// 500 - Internal Server Error
- ERROR_CONSULTAR_CLIENTE: {
- codigo: 500,
- mensaje: 'Ocurrió un error al obtener la información del cliente.',
- },
ERROR_CONSULTAR_SISTEMA: {
codigo: 500,
mensaje: 'Ocurrió un error al obtener la información del sistema del cliente.',
@@ -74,10 +78,7 @@ module.exports = {
codigo: 500,
mensaje: 'Ocurrió un error al obtener la lista de clientes.',
},
- ERROR_ELIMINAR_CLIENTE: {
- codigo: 500,
- mensaje: 'Ocurrió un error al eliminar el cliente.',
- },
+
// Crear cliente
CAMPO_OBLIGATORIO: {
diff --git a/Utilidades/Constantes/mensajesCuotas.js b/Utilidades/Constantes/mensajesCuotas.js
index 9ee05ba0..9ac28661 100644
--- a/Utilidades/Constantes/mensajesCuotas.js
+++ b/Utilidades/Constantes/mensajesCuotas.js
@@ -2,45 +2,43 @@
module.exports = {
// crearCuota
- FORMATO_INVALIDO: "Formato de cuota set inválido",
- CREACION_EXITOSA: "Cuota set creado exitosamente",
- ERROR_CREACION: "Error creando cuota set",
+ FORMATO_INVALIDO: 'Formato de cuota set inválido',
+ CREACION_EXITOSA: 'Cuota set creado exitosamente',
+ ERROR_CREACION: 'Error creando cuota set',
// obtenerOpcionesCuotas
- FALTA_ID_CLIENTE: "No hay idCliente",
- OPCIONES_OBTENIDAS: "Opciones producto para cuota",
- ERROR_OBTENIENDO_OPCIONES: "Error obteniendo opciones",
+ FALTA_ID_CLIENTE: 'No hay idCliente',
+ OPCIONES_OBTENIDAS: 'Opciones producto para cuota',
+ ERROR_OBTENIENDO_OPCIONES: 'Error obteniendo opciones',
// validarCuotaSet
NOMBRE_REQUERIDO: 'El campo "nombre" es obligatorio.',
- PRODUCTOS_REQUERIDOS: "Debes enviar al menos un producto con su límite.",
- ID_PRODUCTO_INVALIDO: (pos) =>
- `El producto en la posición ${pos} no tiene un idProducto válido.`,
+ PRODUCTOS_REQUERIDOS: 'Debes enviar al menos un producto con su límite.',
+ ID_PRODUCTO_INVALIDO: (pos) => `El producto en la posición ${pos} no tiene un idProducto válido.`,
LIMITE_INVALIDO: (id) => `El producto "${id}" tiene un "limite" inválido.`,
- LIMITE_ACTUAL_INVALIDO: (id) =>
- `El producto "${id}" tiene un "limiteActual" inválido.`,
+ LIMITE_ACTUAL_INVALIDO: (id) => `El producto "${id}" tiene un "limiteActual" inválido.`,
- // consultarListaCuotas
+ // consultarListaCuotas
CONSULTA_EXITOSA: {
codigo: 200,
- mensaje: "Lista de sets de cuotas obtenida exitosamente.",
+ mensaje: 'Lista de sets de cuotas obtenida exitosamente.',
},
SIN_RESULTADOS: {
codigo: 204,
- mensaje: "No se encontraron sets de cuotas registrados para el cliente.",
+ mensaje: 'No se encontraron sets de cuotas registrados para el cliente.',
},
PARAMETROS_INVALIDOS: {
codigo: 400,
- mensaje: "Falta el ID del cliente para realizar la consulta.",
+ mensaje: 'Falta el ID del cliente para realizar la consulta.',
},
ERROR_CONSULTAR_CUOTAS: {
codigo: 500,
- mensaje: "Error al consultar los sets de cuotas.",
+ mensaje: 'Error al consultar los sets de cuotas.',
},
-
+
SET_CUOTA_NO_ENCONTRADO: {
codigo: 404,
mensaje: 'Set de cuotas no encontrado.',
@@ -55,5 +53,20 @@ module.exports = {
codigo: 500,
mensaje: 'Ocurrió un error al eliminar el set de productos.',
},
+
+ ERROR_OBTENER_SET_CUOTA: {
+ codigo: 500,
+ mensaje: 'Error interno al obtener el set de cuotas.',
+ },
+
+ ACTUALIZACION_EXITOSA: {
+ codigo: 200,
+ mensaje: 'Set de cuotas actualizado correctamente.'
+ },
+ ERROR_ACTUALIZACION: {
+ codigo: 500,
+ mensaje: 'No se pudo actualizar el set de cuotas.'
+ },
+
};
diff --git a/Utilidades/Constantes/mensajesEmpleados.js b/Utilidades/Constantes/mensajesEmpleados.js
index f67fd574..dcb3049b 100644
--- a/Utilidades/Constantes/mensajesEmpleados.js
+++ b/Utilidades/Constantes/mensajesEmpleados.js
@@ -1,5 +1,9 @@
module.exports = {
// 200 - OK
+ EXITO_CREAR: {
+ codigo: 200,
+ mensaje: 'Empleado agregado exitosamente.',
+ },
CONSULTA_EXITOSA: {
codigo: 200,
mensaje: 'Lista de empleados obtenida exitosamente.',
@@ -28,6 +32,18 @@ module.exports = {
codigo: 400,
mensaje: 'Error al actualizar',
},
+ ERROR_EXPORTAR_EMPLEADOS: {
+ codigo: 400,
+ mensaje: 'Error al exportar la lista de empleados.',
+ },
+ ERROR_CREAR: {
+ codigo: 400,
+ mensaje: 'Error al crear',
+ },
+ CAMPOS_REQUERIDOS: {
+ codigo: 400,
+ mensaje: 'Faltan campos requeridos',
+ },
// 403 - Forbidden
PERMISO_DENEGADO: {
@@ -50,6 +66,15 @@ module.exports = {
codigo: 200,
mensaje: 'Empleado(s) eliminado(s) correctamente.',
},
+ LISTA_EMPLEADOS_EXPORTADA: {
+ codigo: 200,
+ mensaje: 'Lista de empleados exportada exitosamente.',
+ },
+ // 204 - No hay datos
+ EMPLEADOS_NO_ENCONTRADOS: {
+ codigo: 204,
+ mensaje: 'No hay empleados para exportar.',
+ },
// 500 - Internal Server Error
ERROR_ELIMINAR_EMPLEADO: {
codigo: 500,
@@ -72,9 +97,12 @@ module.exports = {
codigo: 500,
mensaje: 'Ocurrió un error al crear el grupo de empleados.',
},
-
GRUPO_NOMBRE_REPETIDO: {
codigo: 'GRUPO_NOMBRE_REPETIDO',
mensaje: 'Ya existe un grupo con ese nombre.',
},
+ ERROR_CREACION: {
+ codigo: 500,
+ mensaje: 'Error al crear el empleado',
+ },
};
diff --git a/Utilidades/Constantes/mensajesEventos.js b/Utilidades/Constantes/mensajesEventos.js
index 60bb5ef7..78590e3b 100644
--- a/Utilidades/Constantes/mensajesEventos.js
+++ b/Utilidades/Constantes/mensajesEventos.js
@@ -1,23 +1,11 @@
module.exports = {
// 201 - Creado
- CATEGORIA_CREADA: {
- codigo: 201,
- mensaje: 'Categoría creada correctamente.',
- },
EVENTO_CREADO: {
codigo: 201,
mensaje: 'Evento creado correctamente.',
},
// 200 - OK
- CATEGORIA_OBTENIDA: {
- codigo: 200,
- mensaje: 'Información de la categoría obtenida exitosamente.',
- },
- LISTA_CATEGORIAS_OBTENIDA: {
- codigo: 200,
- mensaje: 'Lista de categorías obtenida exitosamente.',
- },
EVENTO_OBTENIDO: {
codigo: 200,
mensaje: 'Información del evento obtenida exitosamente.',
@@ -58,10 +46,6 @@ module.exports = {
codigo: 400,
mensaje: 'Los parámetros proporcionados no son válidos.',
},
- LIMITE_OFFSET_INVALIDOS: {
- codigo: 400,
- mensaje: 'Los valores de límite u offset deben ser números enteros positivos mayores a cero.',
- },
NOMBRE_EVENTO_INVALIDO: {
codigo: 400,
mensaje: 'El nombre del evento proporcionado no es válido.',
@@ -70,41 +54,19 @@ module.exports = {
codigo: 400,
mensaje: 'Ya existe un evento con ese nombre.',
},
-
- // 401 - No autorizado
- CREDENCIALES_INVALIDAS: {
- codigo: 401,
- mensaje: 'Credenciales inválidas para consultar categorías.',
- },
-
- // 403 - Acceso denegado
- ACCESO_DENEGADO: {
- codigo: 403,
- mensaje: 'No tiene permiso para realizar esta acción sobre categorías.',
+ // 400 - Error de cliente
+ ERROR_CLIENTE_NO_EXISTE: {
+ codigo: 400,
+ mensaje: 'El cliente especificado no existe en el sistema.',
},
+
// 404 - No encontrado
- CATEGORIA_NO_ENCONTRADA: {
- codigo: 404,
- mensaje: 'No se encontró una categoría con el ID proporcionado.',
- },
EVENTO_NO_ENCONTRADO: {
codigo: 404,
mensaje: 'No se encontró un evento con el ID proporcionado.',
},
// 500 - Error del servidor
- ERROR_CREAR_CATEGORIA: {
- codigo: 500,
- mensaje: 'Ocurrió un error al intentar crear la categoría.',
- },
- ERROR_OBTENER_CATEGORIAS: {
- codigo: 500,
- mensaje: 'Ocurrió un error al obtener la lista de categorías.',
- },
- ERROR_OBTENER_CATEGORIA: {
- codigo: 500,
- mensaje: 'Ocurrió un error al obtener los datos de la categoría.',
- },
ERROR_CREAR_EVENTO: {
codigo: 500,
mensaje: 'Ocurrió un error al intentar crear el evento.',
@@ -125,4 +87,12 @@ module.exports = {
codigo: 500,
mensaje: 'Ocurrió un error interno en el servidor.',
},
+ ERROR_DB_CONEXION: {
+ codigo: 500,
+ mensaje: 'Error de conexión con la base de datos.',
+ },
+ ERROR_INESPERADO: {
+ codigo: 500,
+ mensaje: 'Ocurrió un error inesperado al crear el evento.',
+ },
};
diff --git a/Utilidades/Constantes/mensajesGrupoEmpleados.js b/Utilidades/Constantes/mensajesGrupoEmpleados.js
index b1530e8b..1e3dd0ef 100644
--- a/Utilidades/Constantes/mensajesGrupoEmpleados.js
+++ b/Utilidades/Constantes/mensajesGrupoEmpleados.js
@@ -51,4 +51,24 @@ module.exports = {
codigo: 500,
mensaje: 'Ocurrió un error al obtener los datos del grupo de empleados.',
},
+ GRUPO_ACTUALIZADO: {
+ codigo: 200,
+ mensaje: 'Se actualizo correctamente el grupo de empleados.',
+ },
+ FORMATO_INVALIDO_DATOS: {
+ codigo: 400,
+ mensaje: 'No se obtuvieron los datos correctamente.',
+ },
+ ERROR_ACTUALIZAR_GRUPOS: {
+ codigo: 400,
+ mensaje: 'Error actualizando el grupo de empleados.s',
+ },
+ ERROR_VERIFICACION_CLIENTE_EMPLEADO: {
+ codigo: 400,
+ mensaje: 'Algunos empleados no pertenecen al mismo cliente que el grupo.',
+ },
+ ERROR_VERIFICACION_CLIENTE_SET: {
+ codigo: 400,
+ mensaje: 'Algunos sets de productos no pertenecen al cliente de este grupo.',
+ },
};
diff --git a/Utilidades/Constantes/mensajesPedidos.js b/Utilidades/Constantes/mensajesPedidos.js
index 8e21d51d..978727e6 100644
--- a/Utilidades/Constantes/mensajesPedidos.js
+++ b/Utilidades/Constantes/mensajesPedidos.js
@@ -32,4 +32,13 @@ module.exports = {
codigo: 500,
mensaje: 'Ocurrió un error al eliminar el pedido.',
},
+ PEDIDO_ACTUALIZADO: {
+ codigo: 200,
+ mensaje: 'Pedido actualizado correctamente.',
+ },
+ ERROR_ACTUALIZAR_PEDIDO: {
+ codigo: 400,
+ mensaje: 'Error al actualizar el pedido.',
+ },
+
};
diff --git a/Utilidades/Constantes/mensajesProductos.js b/Utilidades/Constantes/mensajesProductos.js
index 8f357e88..7f713977 100644
--- a/Utilidades/Constantes/mensajesProductos.js
+++ b/Utilidades/Constantes/mensajesProductos.js
@@ -14,6 +14,10 @@ module.exports = {
codigo: 204,
mensaje: 'No se encontraron productos registrados para el cliente.',
},
+ PRODUCTOS_NO_ENCONTRADOS: {
+ codigo: 204,
+ mensaje: 'No se encontraron productos para exportar.',
+ },
// 400 - Bad Request
PARAMETROS_INVALIDOS: {
@@ -63,12 +67,37 @@ module.exports = {
// 200 - OK
RESPUESTA_ELIMINAR_PRODUCTO_EXITOSA: {
codigo: 200,
- mensaje: "Producto eliminado exitosamente.",
+ mensaje: 'Producto eliminado exitosamente.',
},
-
+
// 500 - Internal Server Error
RESPUESTA_ERROR_GENERAL: {
codigo: 500,
- mensaje: "Ocurrió un error al procesar la solicitud.",
+ mensaje: 'Ocurrió un error al procesar la solicitud.',
+ },
+ //LEER PRODUCTO
+ ERROR_LEER_PRODUCTO: {
+ codigo: 400,
+ mensaje: 'Ocurrió un error al obtener la informacion del producto.',
+ },
+ LEER_PRODUCTO_EXITO: {
+ codigo: 200,
+ mensaje: 'Lista de productos consultada exitosamente.',
+ },
+ ID_INVALIDO: {
+ codigo: 400,
+ mensaje: 'No se proporciono el id del producto.',
+ },
+ ERROR_OBTENIENDO_INFORMACION: {
+ codigo: 400,
+ mensaje: 'Error obteniendo informacion del producto.',
+ },
+ PRODUCTO_NO_ENCONTRADO: {
+ codigo: 400,
+ mensaje: 'El producto solicitado no existe.',
+ },
+ ERROR_EXPORTACION: {
+ codigo: 400,
+ mensaje: 'Error al exportar la lista de productos.',
},
};
diff --git a/Utilidades/Constantes/mensajesRoles.js b/Utilidades/Constantes/mensajesRoles.js
index 3eef144f..d8ab3f05 100644
--- a/Utilidades/Constantes/mensajesRoles.js
+++ b/Utilidades/Constantes/mensajesRoles.js
@@ -76,9 +76,13 @@ module.exports = {
codigo: 400,
mensaje: 'Ocurrió un error al eliminar rol',
mensaje_no_existe: 'Ocurrió un error, rol no existe',
- mensaje_rol_asignado: 'No se puede eliminar el rol porque está asignado a uno o más usuarios.'
+ mensaje_rol_asignado: 'No se puede eliminar el rol porque está asignado a uno o más usuarios.',
},
+ ACTUALIZAR_ROL: {
+ codigo: 200,
+ mensaje: 'Se actualizo correctamente el rol.',
+ },
NOMBRE_OBLIGATORIO: 'El nombre del rol es obligatorio.',
PERMISOS_OBLIGATORIOS: 'Debes seleccionar al menos un permiso.',
diff --git a/Utilidades/Constantes/mensajesSetsProductos.js b/Utilidades/Constantes/mensajesSetsProductos.js
index f887f58c..3d8291c9 100644
--- a/Utilidades/Constantes/mensajesSetsProductos.js
+++ b/Utilidades/Constantes/mensajesSetsProductos.js
@@ -8,6 +8,10 @@ module.exports = {
codigo: 200,
mensaje: 'Set de productos eliminado correctamente.',
},
+ SET_PRODUCTOS_ACTUALIZADO: {
+ codigo: 200,
+ mensaje: 'Set de productos actualizado correctamente.',
+ },
// 204 - No Content
SIN_RESULTADOS: {
@@ -36,4 +40,56 @@ module.exports = {
codigo: 500,
mensaje: 'Ocurrió un error al eliminar el set de productos.',
},
+ DATOS_INVALIDOS_ERROR: {
+ codigo: 400,
+ mensaje: 'Formato de datos invalido.',
+ },
+ CLIENTE_NO_SELECCIONADO: {
+ codigo: 400,
+ mensaje: 'Cliente no seleccionado.',
+ },
+ SETS_PRODUCTOS_CREADO_EXITO: {
+ codigo: 200,
+ mensaje: 'Set de producto creado exitosamente.',
+ },
+ ERROR_NOMBRE_DUPLICADO: {
+ codigo: 400,
+ mensaje: 'Nombre o nombre visible duplicado.',
+ },
+ ERROR_NOMBRE_NORMAL_DUPLICADO: {
+ codigo: 400,
+ mensaje: 'Nombre duplicado.',
+ },
+ ERROR_PRODUCTOS_INVALIDOS: {
+ codigo: 400,
+ mensaje: 'Uno o más productos no existen en este cliente.',
+ },
+ ERROR_ACTUALIZAR_SET_PRODUCTOS: {
+ codigo: 500,
+ mensaje: 'Ocurrió un error al actualizar el set de productos',
+ },
+ FORMATO_INVALIDO_DATOS: {
+ codigo: 400,
+ mensaje: 'Formato de datos inválido para actualizar el set de productos',
+ },
+ SET_ACTUALIZADO: {
+ codigo: 200,
+ mensaje: 'Set de productos actualizado correctamente',
+ },
+ ERROR_ACTUALIZAR_SET: {
+ codigo: 500,
+ mensaje: 'Error interno al actualizar el set de productos',
+ },
+ SET_NO_ENCONTRADO: {
+ codigo: 404,
+ mensaje: 'El set de productos no fue encontrado',
+ },
+ PRODUCTOS_INVALIDOS: {
+ codigo: 400,
+ mensaje: 'La lista de productos contiene elementos inválidos',
+ },
+ PARAMETROS_INVALIDOS: {
+ codigo: 400,
+ mensaje: 'Parámetros inválidos',
+ },
};
diff --git a/Utilidades/Constantes/mensajesUsuarios.js b/Utilidades/Constantes/mensajesUsuarios.js
index ac2ddeee..9e01f97d 100644
--- a/Utilidades/Constantes/mensajesUsuarios.js
+++ b/Utilidades/Constantes/mensajesUsuarios.js
@@ -14,6 +14,10 @@ module.exports = {
codigo: 200,
mensaje: 'Lista de usuarios obtenida exitosamente.',
},
+ USUARIO_ACTUALIZADO: {
+ codigo: 200,
+ mensaje: 'Usuario actualizado correctamente.',
+ },
// 204 - Sin contenido
USUARIOS_NO_ENCONTRADOS: {
@@ -51,6 +55,10 @@ module.exports = {
codigo: 400,
mensaje: 'Los parámetros proporcionados no son válidos.',
},
+ ERROR_OBTENER_USUARIO: {
+ codigo: 400,
+ mensaje: 'Ocurrió un error al obtener los datos del usuario.',
+ },
// 401 - sin autorizacion
CREDENCIALES_INVALIDAS: {
@@ -79,12 +87,16 @@ module.exports = {
codigo: 500,
mensaje: 'Ocurrió un error al obtener la lista de usuarios.',
},
- ERROR_OBTENER_USUARIO: {
- codigo: 500,
- mensaje: 'Ocurrió un error al obtener los datos del usuario.',
- },
ERROR_ELIMINAR_USUARIO: {
codigo: 500,
mensaje: 'Ocurrió un error al intentar eliminar el usuario.',
},
+ ERROR_ACTUALIZAR_USUARIO: {
+ codigo: 500,
+ mensaje: 'Ocurrió un error al intentar actualizar el usuario.',
+ },
+ ERROR_FECHA_NO_VALIDA: {
+ codigo: 500,
+ mensaje: 'La fecha de nacimiento proporcionada no es válida.',
+ },
};
diff --git a/Utilidades/Constantes/rutas.js b/Utilidades/Constantes/rutas.js
index 92946212..66bf6d91 100644
--- a/Utilidades/Constantes/rutas.js
+++ b/Utilidades/Constantes/rutas.js
@@ -8,7 +8,7 @@ module.exports = {
CERRAR_SESION: '/cerrar-sesion',
USUARIO_AUTENTICADO: '/autenticar',
ACTIVAR_2FA: '/activar-2fa',
- VERIFICAR_2FA: '/verificar-2fa'
+ VERIFICAR_2FA: '/verificar-2fa',
},
USUARIOS: {
BASE: '/usuarios',
@@ -16,6 +16,7 @@ module.exports = {
CREAR: '/crear',
ELIMINAR_USUARIOS: '/eliminar-usuarios',
LEER: '/consultar-usuario',
+ ACTUALIZAR: '/actualizar-usuario',
},
CATEGORIAS: {
BASE: '/categorias',
@@ -25,7 +26,8 @@ module.exports = {
CONSULTAR_LISTA_USUARIOS: '/consultar-lista-usuarios',
CREAR: '/crear',
ELIMINAR_USUARIOS: '/eliminar-usuarios',
- LEER: '/consultar-usuario',
+ LEER: '/leer',
+ ACTUALIZAR: '/actualizar',
},
EVENTOS: {
BASE: '/eventos',
@@ -40,6 +42,9 @@ module.exports = {
CONSULTAR_LISTA: '/consultar-lista',
CREAR: '/crear',
ELIMINAR_PRODUCTO: '/eliminar',
+ IMPORTAR: '/importar',
+ LEER: '/leer-producto',
+ EXPORTAR_PRODUCTOS: '/exportar-productos',
},
PROVEEDORES: {
BASE: '/proveedores',
@@ -52,6 +57,7 @@ module.exports = {
CREAR: '/crear',
SUBIR_IMAGEN: '/subir-imagen',
ELIMINAR_SET_PRODUCTOS: '/eliminar',
+ ACTUALIZAR: '/actualizar',
},
CLIENTES: {
BASE: '/clientes',
@@ -64,14 +70,17 @@ module.exports = {
},
EMPLEADOS: {
BASE: '/empleados',
+ CREAR: '/crear',
CONSULTAR_LISTA: '/consultar-lista',
CONSULTAR_GRUPO: '/consultar-grupo',
ELIMINAR_GRUPO: '/eliminar-grupo',
ELIMINAR_EMPLEADO: '/eliminar',
IMPORTAR_EMPLEADOS: '/importar-empleados',
+ EXPORTAR_EMPLEADOS: '/exportar-empleados',
LEER_GRUPO: '/leer-grupo',
CREAR_GRUPO: '/crear-grupo',
ACTUALIZAR: '/actualizar',
+ ACTUALIZAR_GRUPO_EMPLEADO: '/actualizar-grupo',
},
CUOTAS: {
BASE: '/cuotas',
@@ -79,6 +88,8 @@ module.exports = {
OPCIONES: '/obtener-opciones',
CONSULTAR_LISTA: '/consultar-lista',
ELIMINAR_SET_CUOTAS: '/eliminar-set-cuotas',
+ LEER_SET_CUOTAS: '/leer-set-cuotas',
+ ACTUALIZAR_SET_CUOTAS: '/actualizar-set-cuotas',
},
ROLES: {
BASE: '/roles',
@@ -87,11 +98,14 @@ module.exports = {
OBTENER_OPCIONES: '/obtener-opciones',
CONFIRMAR_CREACION: '/confirmar-creacion',
ELIMINAR_ROL: '/eliminar',
+ LEER_ROL: '/leer',
+ ACTUALIZAR: '/actualizar-rol',
},
PEDIDOS: {
BASE: '/pedidos',
CONSULTAR_LISTA: '/consultar-lista',
ELIMINAR_PEDIDO: '/eliminar',
+ ACTUALIZAR_PEDIDO: '/actualizar-pedido',
},
PAGOS: {
BASE: '/pagos',
diff --git a/Utilidades/Intermediarios/Validaciones/validarOpcionesImportar.js b/Utilidades/Intermediarios/Validaciones/validarOpcionesImportar.js
new file mode 100644
index 00000000..69e7775e
--- /dev/null
+++ b/Utilidades/Intermediarios/Validaciones/validarOpcionesImportar.js
@@ -0,0 +1,81 @@
+//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26
+/**
+ * Valida un conjunto de opciones para un producto.
+ *
+ * Esta función valida cada opción dentro del arreglo de opciones pasado como argumento.
+ * Se asegura de que los valores de las propiedades cumplan con los tipos y rangos especificados,
+ * y retorna un objeto de error si algún valor es inválido.
+ *
+ * @param {Array} opciones - Un arreglo de objetos que representan las opciones de un producto.
+ * @param {number} opciones[].cantidad - La cantidad de la opción, que debe ser un número entero positivo o cero.
+ * @param {string} opciones[].valorOpcion - El valor de la opción, que debe ser una cadena de texto de máximo 100 caracteres.
+ * @param {string} [opciones[].SKUautomatico] - El SKU automático de la opción, que debe ser una cadena de texto de máximo 50 caracteres, si se proporciona.
+ * @param {string} [opciones[].SKUcomercial] - El SKU comercial de la opción, que debe ser una cadena de texto de máximo 50 caracteres, si se proporciona.
+ * @param {number} opciones[].costoAdicional - El costo adicional de la opción, que debe ser un número positivo o cero.
+ * @param {number} opciones[].descuento - El descuento de la opción, que debe ser un número entre 0 y 100.
+ * @param {number} opciones[].estado - El estado de la opción, que debe ser 1 (activo) o 0 (inactivo).
+ *
+ * @returns {object|null} Un objeto con una propiedad `error` si hay un error de validación, o `null` si todas las opciones son válidas.
+ * @example
+ * const opciones = [
+ * {
+ * cantidad: 5,
+ * valorOpcion: 'Tamaño M',
+ * SKUautomatico: 'SKU123',
+ * SKUcomercial: 'S123',
+ * costoAdicional: 15.50,
+ * descuento: 10,
+ * estado: 1
+ * }
+ * ];
+ *
+ * const resultado = validarOpciones(opciones);
+ * console.log(resultado); // null si todo está bien, o un objeto de error si algo es inválido
+ */
+module.exports = (opciones) => {
+ for (const opcion of opciones) {
+ // prettier-ignore
+ if (
+ typeof opcion.cantidad !== 'number'
+ || opcion.cantidad < 0
+ || !Number.isInteger(opcion.cantidad)
+ || opcion.cantidad % 1 !== 0
+ ) {
+ return { error: 'cantidad de la opción debe ser un número entero positivo o cero.' };
+ }
+
+ // prettier-ignore
+ if (
+ !opcion.valorOpcion
+ || typeof opcion.valorOpcion !== 'string'
+ || opcion.valorOpcion.length > 100
+ ) {
+ return {
+ error: 'valorOpcion es requerido y debe ser una cadena de texto de máximo 100 caracteres.',
+ };
+ }
+
+ // prettier-ignore
+ if (!opcion.SKUcomercial
+ || opcion.SKUcomercial == null
+ || typeof opcion.SKUcomercial !== 'string'
+ || opcion.SKUcomercial.length > 50
+ ) {
+ return { error: 'SKUcomercial debe ser una cadena de texto de máximo 50 caracteres.' };
+ }
+
+ if (typeof opcion.costoAdicional !== 'number' || opcion.costoAdicional < 0) {
+ return { error: 'costoAdicional debe ser un número positivo o cero.' };
+ }
+
+ if (typeof opcion.descuento !== 'number' || opcion.descuento < 0 || opcion.descuento > 100) {
+ return { error: 'descuento debe ser un número entre 0 y 100.' };
+ }
+
+ if (typeof opcion.estado !== 'number' || (opcion.estado !== 0 && opcion.estado !== 1)) {
+ return { error: 'estado debe ser 1 (activo) o 0 (inactivo).' };
+ }
+ }
+
+ return null;
+};
diff --git a/Utilidades/Intermediarios/Validaciones/validarProductoImportado.js b/Utilidades/Intermediarios/Validaciones/validarProductoImportado.js
new file mode 100644
index 00000000..1fd8e2b3
--- /dev/null
+++ b/Utilidades/Intermediarios/Validaciones/validarProductoImportado.js
@@ -0,0 +1,181 @@
+//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26
+/**
+ * Valida los campos de un producto.
+ *
+ * Esta función valida los diferentes campos de un objeto `producto` asegurándose de que cumplan con los tipos y restricciones especificados. Si algún campo no cumple con las condiciones, se devuelve un objeto de error con un mensaje específico. Si todos los campos son válidos, se retorna `null`.
+ *
+ * @param {object} producto - El objeto que representa un producto a validar.
+ * @param {number|null} producto.idProveedor - El ID del proveedor, debe ser un número entero positivo o `null`.
+ * @param {string} producto.nombreComun - El nombre común del producto, debe ser una cadena de texto de máximo 100 caracteres.
+ * @param {string|null} producto.nombreComercial - El nombre comercial del producto, debe ser una cadena de texto de máximo 150 caracteres o `null`.
+ * @param {string|null} producto.descripcion - Descripción del producto, debe ser una cadena de texto de máximo 1000 caracteres o `null`.
+ * @param {string|null} producto.marca - La marca del producto, debe ser una cadena de texto de máximo 100 caracteres o `null`.
+ * @param {string|null} producto.modelo - El modelo del producto, debe ser una cadena de texto de máximo 100 caracteres o `null`.
+ * @param {string|null} producto.tipoProducto - El tipo de producto, debe ser una cadena de texto de máximo 50 caracteres o `null`.
+ * @param {number|null} producto.precioPuntos - El precio en puntos, debe ser un número entero positivo o `null`.
+ * @param {number|null} producto.precioCliente - El precio para el cliente, debe ser un número mayor o igual a cero o `null`.
+ * @param {number|null} producto.precioVenta - El precio de venta, debe ser un número mayor o igual a cero o `null`.
+ * @param {number|null} producto.costo - El costo del producto, debe ser un número mayor o igual a cero o `null`.
+ * @param {number|null} producto.impuesto - El impuesto del producto, debe ser un número mayor o igual a cero o `null`.
+ * @param {number|null} producto.descuento - El descuento del producto, debe ser un número mayor o igual a cero o `null`.
+ * @param {number} producto.estado - El estado del producto, debe ser 0 (inactivo) o 1 (activo).
+ * @param {number} producto.envio - Indica si el envío está disponible, debe ser 0 (no disponible) o 1 (disponible).
+ *
+ * @returns {object|null} Un objeto con una propiedad `error` si hay un error de validación, o `null` si todos los campos son válidos.
+ * @example
+ * const producto = {
+ * idProveedor: 1,
+ * nombreComun: 'Producto X',
+ * nombreComercial: 'Producto X Comercial',
+ * descripcion: 'Descripción del producto',
+ * marca: 'Marca X',
+ * modelo: 'Modelo 123',
+ * tipoProducto: 'Tipo A',
+ * precioPuntos: 100,
+ * precioCliente: 200.50,
+ * precioVenta: 250,
+ * costo: 150,
+ * impuesto: 25,
+ * descuento: 10,
+ * estado: 1,
+ * envio: 1,
+ * };
+ *
+ * const resultado = validarProducto(producto);
+ * console.log(resultado); // null si todo está bien, o un objeto de error si algo es inválido
+ */
+// prettier-ignore
+module.exports = (producto) => {
+ if (
+ producto.idProveedor !== null
+ && (
+ typeof producto.idProveedor !== 'number'
+ || producto.idProveedor <= 0
+ || producto.idProveedor % 1 !== 0
+ )
+ ) {
+ return { error: 'idProveedor debe ser un número entero positivo o NULL.' };
+ }
+
+ if (
+ !producto.nombreComun
+ || typeof producto.nombreComun !== 'string'
+ || producto.nombreComun.length > 100
+ ) {
+ return {
+ error: 'nombreProducto es requerido, debe ser una cadena de texto y no exceder 100 caracteres.',
+ };
+ }
+
+ if (
+ !producto.nombreComercial
+ || typeof producto.nombreComercial !== 'string'
+ || producto.nombreComercial.length > 100
+ ) {
+ return {
+ error: 'nombreComercial es requerido, debe ser una cadena de texto y no exceder 100 caracteres.',
+ };
+ }
+
+ if (
+ producto.descripcion !== null
+ && (typeof producto.descripcion !== 'string' || producto.descripcion.length > 1000 || producto.descripcion.trim() === '')
+ ) {
+ return {
+ error: 'descripcion debe ser una cadena de texto y no exceder 1000 caracteres.',
+ };
+ }
+
+ if (
+ producto.marca !== null
+ && (typeof producto.marca !== 'string' || producto.marca.length > 100 || producto.marca.trim() === '')
+ ) {
+ return { error: 'marca debe ser una cadena de texto y no exceder 100 caracteres.' };
+ }
+
+ if (
+ producto.modelo !== null
+ && (typeof producto.modelo !== 'string' || producto.modelo.length > 100 || producto.modelo.trim() === '')
+ ) {
+ return { error: 'modelo debe ser una cadena de texto y no exceder 100 caracteres.' };
+ }
+
+ if (
+ producto.tipoProducto !== null
+ && (typeof producto.tipoProducto !== 'string' || producto.tipoProducto.length > 50 || producto.tipoProducto.trim() === '')
+ ) {
+ return {
+ error: 'tipoProducto debe ser una cadena de texto y no exceder 50 caracteres.',
+ };
+ }
+
+ if (
+ typeof producto.costo !== 'number'
+ || producto.costo < 0
+ || Number.isNaN(producto.costo)
+ ) {
+ return {
+ error: 'costo debe ser un número mayor o igual a cero',
+ };
+ }
+
+ if (
+ typeof producto.precioVenta !== 'number'
+ || producto.precioVenta < 0
+ || Number.isNaN(producto.precioVenta)
+ ) {
+ return {
+ error: 'precioVenta debe ser un número mayor o igual a cero',
+ };
+ }
+
+ if (
+ typeof producto.precioCliente !== 'number'
+ || producto.precioCliente < 0
+ || Number.isNaN(producto.precioCliente)
+ ) {
+ return {
+ error: 'precioCliente debe ser un número mayor o igual a cero',
+ };
+ }
+
+ if (
+ typeof producto.precioPuntos !== 'number'
+ || producto.precioPuntos < 0
+ || Number.isNaN(producto.precioPuntos)
+ ) {
+ return {
+ error: 'precioPuntos debe ser un número mayor o igual a cero',
+ };
+ }
+
+ if (
+ typeof producto.impuesto !== 'number'
+ || producto.impuesto < 0
+ || Number.isNaN(producto.impuesto)
+ ) {
+ return {
+ error: 'impuesto debe ser un número mayor o igual a cero',
+ };
+ }
+
+ if (
+ typeof producto.descuento !== 'number'
+ || producto.descuento < 0 || producto.descuento > 100
+ || Number.isNaN(producto.descuento)
+ ) {
+ return {
+ error: 'descuento debe ser un número mayor o igual a cero y menor o igual a 100',
+ };
+ }
+
+ if (producto.estado !== undefined && producto.estado !== 0 && producto.estado !== 1) {
+ return { error: 'estado debe ser 0 (inactivo) o 1 (activo).' };
+ }
+
+ if (producto.envio !== undefined && producto.envio !== 0 && producto.envio !== 1) {
+ return { error: 'envio debe ser 0 (no disponible) o 1 (disponible).' };
+ }
+
+ return null;
+};
diff --git a/Utilidades/Intermediarios/Validaciones/validarVariante.js b/Utilidades/Intermediarios/Validaciones/validarVariante.js
index b8646350..e5de5951 100644
--- a/Utilidades/Intermediarios/Validaciones/validarVariante.js
+++ b/Utilidades/Intermediarios/Validaciones/validarVariante.js
@@ -36,7 +36,7 @@ module.exports = (variante) => {
&& (typeof variante.descripcion !== 'string' || variante.descripcion.length > 1000)
) {
return {
- error: 'descripcion debe ser una cadena de texto o NULL y no exceder 1000 caracteres.',
+ error: 'descripcion debe ser una cadena de texto y no exceder 1000 caracteres.',
};
}
diff --git a/Utilidades/Intermediarios/Validaciones/validarVarianteImportar.js b/Utilidades/Intermediarios/Validaciones/validarVarianteImportar.js
new file mode 100644
index 00000000..a65542f0
--- /dev/null
+++ b/Utilidades/Intermediarios/Validaciones/validarVarianteImportar.js
@@ -0,0 +1,44 @@
+//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26
+/**
+ * Valida los campos de una variante.
+ *
+ * Esta función valida los diferentes campos de un objeto `variante` asegurándose de que cumplan con los tipos y restricciones especificadas. Si algún campo no cumple con las condiciones, se devuelve un objeto de error con un mensaje específico. Si todos los campos son válidos, se retorna `null`.
+ *
+ * @param {object} variante - El objeto que representa una variante a validar.
+ * @param {string} variante.nombreVariante - El nombre de la variante, debe ser una cadena de texto de máximo 100 caracteres.
+ * @param {string|null} variante.descripcion - La descripción de la variante, debe ser una cadena de texto de máximo 1000 caracteres o `null`.
+ *
+ * @returns {object|null} Un objeto con una propiedad `error` si hay un error de validación, o `null` si todos los campos son válidos.
+ * @example
+ * const variante = {
+ * nombreVariante: 'Tamaño L',
+ * descripcion: 'Variante para talla grande',
+ * };
+ *
+ * const resultado = validarVariante(variante);
+ * console.log(resultado); // null si todo está bien, o un objeto de error si algo es inválido
+ */
+// prettier-ignore
+module.exports = (variante) => {
+ if (
+ !variante.nombreVariante
+ || typeof variante.nombreVariante !== 'string'
+ || variante.nombreVariante.length > 100
+ ) {
+ return {
+ error:
+ 'nombreVariante es requerido, debe ser una cadena de texto y no exceder 100 caracteres.',
+ };
+ }
+
+ if (
+ variante.descripcion !== null
+ && (typeof variante.descripcion !== 'string' || variante.descripcion.length > 1000 || variante.descripcion.trim() === '')
+ ) {
+ return {
+ error: 'descripcionVariante debe ser una cadena de texto y no exceder 1000 caracteres.',
+ };
+ }
+
+ return null;
+};
diff --git a/Utilidades/Intermediarios/generarSKUAuto.js b/Utilidades/Intermediarios/generarSKUAuto.js
new file mode 100644
index 00000000..d8ffb993
--- /dev/null
+++ b/Utilidades/Intermediarios/generarSKUAuto.js
@@ -0,0 +1,66 @@
+/**
+ * Limpia el texto eliminando acentos, caracteres especiales y lo convierte a mayúsculas.
+ * @param {string} texto - El texto a limpiar.
+ * @returns {string} El texto limpio.
+ */
+const limpiarTexto = (texto) =>
+ (typeof texto === 'string' ? texto : '')
+ .normalize('NFD')
+ .replace(/[\u0300-\u036f]/g, '')
+ .replace(/[^a-zA-Z0-9\s]/g, '')
+ .toUpperCase();
+
+/**
+ * Obtiene un código basado en el texto proporcionado, usando la primera palabra que cumpla la longitud mínima.
+ * @param {string} texto - El texto del que se extraerá el código.
+ * @param {number} [longitud=3] - La longitud mínima del código a extraer.
+ * @returns {string} El código generado a partir del texto.
+ */
+const obtenerCodigo = (texto, longitud = 3) => {
+ const palabras = limpiarTexto(texto).split(' ');
+ for (const palabra of palabras) {
+ if (palabra.length >= longitud) return palabra.substring(0, longitud);
+ }
+ return limpiarTexto(texto).substring(0, longitud);
+};
+
+
+/**
+ * Genera un SKU basado en el nombre del producto, variante y valor de opción.
+ * @param {string} nombreProducto - El nombre del producto.
+ * @param {string} nombreVariante - El nombre de la variante.
+ * @param {string} valorOpcion - El valor de la opción.
+ * @returns {string} El SKU generado.
+ */
+const generarSKU = (nombreProducto, nombreVariante, valorOpcion) => {
+ try {
+ const prefijo = obtenerCodigo(nombreProducto);
+ const codigoVariante = obtenerCodigo(nombreVariante);
+ const codigoOpcion = obtenerCodigo(valorOpcion);
+ return `${prefijo}-${codigoVariante}-${codigoOpcion}`;
+ } catch {
+ return 'SKU-ERROR';
+ }
+};
+
+/**
+ * Crea una función generadora de SKUs consecutivos basada en los parámetros dados.
+ * @returns {function(string, string, string): string} Función que genera un SKU único e incremental.
+ */
+const crearGeneradorSKUConsecutivo = () => {
+ const contadorSKU = new Map();
+
+ return (nombreProducto, nombreVariante, valorOpcion) => {
+ const base = generarSKU(nombreProducto, nombreVariante, valorOpcion);
+ const actual = contadorSKU.get(base) || 0;
+ const siguiente = actual + 1;
+ contadorSKU.set(base, siguiente);
+
+ return `${base}-${String(siguiente).padStart(3, '0')}`;
+ };
+};
+
+module.exports = {
+ generarSKU, // solo base
+ crearGeneradorSKUConsecutivo // base + numeración incremental
+};
diff --git a/_tests_/Categorias/consultarListaCategorias.controller.test.js b/_tests_/Categorias/consultarListaCategorias.controller.test.js
new file mode 100644
index 00000000..c6e4b246
--- /dev/null
+++ b/_tests_/Categorias/consultarListaCategorias.controller.test.js
@@ -0,0 +1,89 @@
+/**
+ * RF[47] Consulta Lista de Categorías - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF47
+ * Mocks antes de importar el controlador
+ */
+jest.mock('@altertex/cat/repos/repositorioConsultarListaCategorias', () => ({
+ consultarListaCategorias: jest.fn(),
+}));
+
+// Importar después del mock
+const controlador = require('@altertex/cat/ctrl/consultarListaCategorias.controller');
+const repositorio = require('@altertex/cat/repos/repositorioConsultarListaCategorias');
+const MENSAJES_CATEGORIAS = require('@altertex/util/const/mensajesCategorias');
+
+describe('Controlador consultarListaCategorias', () => {
+ let req;
+ let res;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ req = {
+ user: {
+ clienteSeleccionado: '3',
+ },
+ };
+
+ res = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn(),
+ };
+
+ jest.spyOn(console, 'error').mockImplementation(() => {}); // Evita logs
+ });
+
+ // Escenario 1: No se encuentran categorías
+ test('Debe retornar mensaje cuando no hay categorías encontradas', async () => {
+ repositorio.consultarListaCategorias.mockResolvedValue([]);
+
+ await controlador.consultarListaCategorias(req, res);
+
+ expect(repositorio.consultarListaCategorias).toHaveBeenCalledWith(3);
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_CATEGORIAS.CATEGORIAS_NO_ENCONTRADAS.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_CATEGORIAS.CATEGORIAS_NO_ENCONTRADAS.mensaje,
+ });
+ });
+
+ // Escenario 2: Lista de categorías obtenida correctamente
+ test('Debe retornar la lista de categorías cuando se obtienen con éxito', async () => {
+ const mockCategorias = [
+ {
+ idCategoria: 1,
+ nombre: 'Camisas',
+ productos: ['Camisa A', 'Camisa B'],
+ },
+ {
+ idCategoria: 2,
+ nombre: 'Pantalones',
+ productos: ['Pantalón X'],
+ },
+ ];
+
+ repositorio.consultarListaCategorias.mockResolvedValue(mockCategorias);
+
+ await controlador.consultarListaCategorias(req, res);
+
+ expect(repositorio.consultarListaCategorias).toHaveBeenCalledWith(3);
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_CATEGORIAS.LISTA_CATEGORIAS_OBTENIDA.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_CATEGORIAS.LISTA_CATEGORIAS_OBTENIDA.mensaje,
+ listaCategoria: mockCategorias,
+ });
+ });
+
+ // Escenario 3: Error inesperado en el repositorio
+ test('Debe manejar errores si falla el repositorio', async () => {
+ repositorio.consultarListaCategorias.mockRejectedValue(
+ new Error('Error de base de datos')
+ );
+
+ await controlador.consultarListaCategorias(req, res);
+
+ expect(repositorio.consultarListaCategorias).toHaveBeenCalledWith(3);
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_CATEGORIAS.ERROR_OBTENER_CATEGORIAS.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_CATEGORIAS.ERROR_OBTENER_CATEGORIAS.mensaje,
+ });
+ });
+});
\ No newline at end of file
diff --git a/_tests_/Clientes/eliminarCliente.controller.test.js b/_tests_/Clientes/eliminarCliente.controller.test.js
new file mode 100644
index 00000000..22b47d57
--- /dev/null
+++ b/_tests_/Clientes/eliminarCliente.controller.test.js
@@ -0,0 +1,92 @@
+/**
+ * RF[15] Eliminar Cliente - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF15
+ * Mocks antes de importar el controlador
+ */
+jest.mock('@altertex/cli/repos/repositorioEliminarCliente', () => ({
+ eliminarClientePorId: jest.fn(),
+}));
+jest.mock('@altertex/util/ser/eliminarImagenS3', () => jest.fn());
+jest.mock('@altertex/util/ser/extraerNombreArchivoS3', () => jest.fn());
+jest.mock('@altertex/util/ser/correrQuery', () => jest.fn());
+
+const controlador = require('@altertex/cli/ctrl/eliminarCliente.controller');
+const repositorio = require('@altertex/cli/repos/repositorioEliminarCliente');
+const eliminarImagenS3 = require('@altertex/util/ser/eliminarImagenS3');
+const extraerNombreArchivoS3 = require('@altertex/util/ser/extraerNombreArchivoS3');
+const correrQuery = require('@altertex/util/ser/correrQuery');
+const MENSAJES_CLIENTES = require('@altertex/util/const/mensajesClientes');
+
+describe('Controlador eliminarCliente', () => {
+ let req;
+ let res;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ req = {
+ body: { idCliente: '3' },
+ };
+
+ res = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn(),
+ };
+
+ jest.spyOn(console, 'error').mockImplementation(() => {});
+ });
+
+ // Escenario 1: ID inválido
+ test('Debe retornar error si el ID de cliente es inválido', async () => {
+ req.body.idCliente = ' ';
+
+ await controlador.eliminarCliente(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.CLIENTE_INVALIDO.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_CLIENTES.CLIENTE_INVALIDO.mensaje,
+ });
+ });
+
+ // Escenario 2: Cliente no encontrado
+ test('Debe retornar error si el cliente no existe', async () => {
+ correrQuery.mockResolvedValue([]);
+ repositorio.eliminarClientePorId.mockResolvedValue({ affectedRows: 0 });
+
+ await controlador.eliminarCliente(req, res);
+
+ expect(repositorio.eliminarClientePorId).toHaveBeenCalledWith(3);
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.CLIENTE_NO_ENCONTRADO.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_CLIENTES.CLIENTE_NO_ENCONTRADO.mensaje,
+ });
+ });
+
+ // Escenario 3: Cliente eliminado correctamente con imagen
+ test('Debe eliminar al cliente y su imagen si existe', async () => {
+ correrQuery.mockResolvedValue([{ urlImagen: 'https://bucket.s3/cliente123.jpg' }]);
+ extraerNombreArchivoS3.mockReturnValue('cliente123.jpg');
+ repositorio.eliminarClientePorId.mockResolvedValue({ affectedRows: 1 });
+
+ await controlador.eliminarCliente(req, res);
+
+ expect(correrQuery).toHaveBeenCalled();
+ expect(extraerNombreArchivoS3).toHaveBeenCalledWith('https://bucket.s3/cliente123.jpg');
+ expect(eliminarImagenS3).toHaveBeenCalledWith('clientes/', 'cliente123.jpg');
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.CLIENTE_ELIMINADO.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_CLIENTES.CLIENTE_ELIMINADO.mensaje,
+ });
+ });
+
+ // Escenario 4: Error inesperado
+ test('Debe manejar errores inesperados en el try/catch', async () => {
+ correrQuery.mockRejectedValue(new Error('DB error'));
+
+ await controlador.eliminarCliente(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.ERROR_ELIMINAR_CLIENTE.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_CLIENTES.ERROR_ELIMINAR_CLIENTE.mensaje,
+ });
+ });
+});
diff --git a/_tests_/Clientes/leerCliente.controller.test.js b/_tests_/Clientes/leerCliente.controller.test.js
new file mode 100644
index 00000000..953b7318
--- /dev/null
+++ b/_tests_/Clientes/leerCliente.controller.test.js
@@ -0,0 +1,124 @@
+/**
+ * RF[13] Leer Cliente - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/rf13/
+ * Mocks antes de importar el controlador
+ */
+jest.mock('@altertex/cli/repos/repositorioLeerCliente', () => ({
+ obtenerClientePorId: jest.fn(),
+}));
+jest.mock('@altertex/util/ser/obtenerImagenCliente', () => jest.fn());
+
+const controlador = require('@altertex/cli/ctrl/leerCliente.controller');
+const repositorio = require('@altertex/cli/repos/repositorioLeerCliente');
+const obtenerImagenCliente = require('@altertex/util/ser/obtenerImagenCliente');
+const MENSAJES_CLIENTES = require('@altertex/util/const/mensajesClientes');
+
+describe('Controlador leerCliente', () => {
+ let req;
+ let res;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ req = {
+ body: { idCliente: '7' },
+ };
+
+ res = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn(),
+ };
+
+ jest.spyOn(console, 'error').mockImplementation(() => {});
+ });
+
+ // Escenario 1: ID inválido
+ test('Debe retornar error si el ID es inválido', async () => {
+ req.body.idCliente = ' ';
+
+ await controlador.leerCliente(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.PARAMETROS_INVALIDOS.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_CLIENTES.PARAMETROS_INVALIDOS.mensaje,
+ });
+ });
+
+ // Escenario 2: Cliente no encontrado
+ test('Debe retornar error si el cliente no existe', async () => {
+ repositorio.obtenerClientePorId.mockResolvedValue(null);
+
+ await controlador.leerCliente(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.CLIENTE_NO_ENCONTRADO.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_CLIENTES.CLIENTE_NO_ENCONTRADO.mensaje,
+ });
+ });
+
+ // Escenario 3: Cliente encontrado con imagen exitosa
+ test('Debe retornar cliente completo con imagen si todo funciona bien', async () => {
+ const mockCliente = {
+ idCliente: 7,
+ nombreLegal: 'Tech S.A.',
+ nombreVisible: 'Tech Store',
+ empleados: [],
+ usuariosAsignados: [],
+ numeroEmpleados: 10,
+ urlImagen: 'https://bucket.s3/cliente.jpg',
+ };
+
+ repositorio.obtenerClientePorId.mockResolvedValue(mockCliente);
+ obtenerImagenCliente.mockResolvedValue('https://signed-url.com/cliente.jpg');
+
+ await controlador.leerCliente(req, res);
+
+ expect(obtenerImagenCliente).toHaveBeenCalledWith(mockCliente.urlImagen);
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.CONSULTA_EXITOSA.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_CLIENTES.CONSULTA_EXITOSA.mensaje,
+ cliente: {
+ ...mockCliente,
+ imagenCliente: 'https://signed-url.com/cliente.jpg',
+ },
+ });
+ });
+
+ // Escenario 4: Cliente encontrado, pero falla obtenerImagenCliente
+ test('Debe retornar imagen placeholder si obtenerImagenCliente falla', async () => {
+ const mockCliente = {
+ idCliente: 7,
+ nombreLegal: 'Tech S.A.',
+ nombreVisible: 'Tech Store',
+ empleados: [],
+ usuariosAsignados: [],
+ numeroEmpleados: 10,
+ urlImagen: 'https://bucket.s3/cliente.jpg',
+ };
+
+ repositorio.obtenerClientePorId.mockResolvedValue(mockCliente);
+ obtenerImagenCliente.mockRejectedValue(new Error('Fallo S3'));
+
+ await controlador.leerCliente(req, res);
+
+ expect(obtenerImagenCliente).toHaveBeenCalledWith(mockCliente.urlImagen);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_CLIENTES.CONSULTA_EXITOSA.mensaje,
+ cliente: {
+ ...mockCliente,
+ imagenCliente: '/placeholder.png',
+ },
+ });
+ });
+
+ // Escenario 5: Error inesperado
+ test('Debe manejar error inesperado del repositorio', async () => {
+ repositorio.obtenerClientePorId.mockRejectedValue(new Error('DB error'));
+
+ await controlador.leerCliente(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.ERROR_CONSULTAR_CLIENTE.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_CLIENTES.ERROR_CONSULTAR_CLIENTE.mensaje,
+ });
+ });
+});
\ No newline at end of file
diff --git a/_tests_/Cuotas/Controladores/actualizarSetCuotas.controller.test.js b/_tests_/Cuotas/Controladores/actualizarSetCuotas.controller.test.js
new file mode 100644
index 00000000..ceb45d0d
--- /dev/null
+++ b/_tests_/Cuotas/Controladores/actualizarSetCuotas.controller.test.js
@@ -0,0 +1,242 @@
+/**
+ * Mocks antes de importar el controlador
+ */
+jest.mock('@altertex/cuota/repos/actualizarSetCuotasRepositorio', () => ({
+ actualizarSetCuotas: jest.fn(),
+}));
+
+const controladorActualizarSetCuotas = require('@altertex/cuota/ctrl/actualizarSetCuotas.controller');
+const repositorio = require('@altertex/cuota/repos/actualizarSetCuotasRepositorio');
+
+describe('Controlador de Actualizar Set de Cuotas', () => {
+ let req;
+ let res;
+
+ const dataMockCompleto = {
+ idCuotaSet: 123,
+ cambios: {
+ nombre: 'Plan Premium Actualizado',
+ descripcion: 'Plan premium con nuevas características',
+ periodoRenovacion: 12,
+ renovacionHabilitada: true,
+ productos: [
+ { idProducto: 1, limite: 200, limiteActual: 150 },
+ { idProducto: 2, limite: 100, limiteActual: 75 },
+ { idProducto: 3, limite: 50, limiteActual: 25 }
+ ]
+ }
+ };
+
+ const dataMockSinProductos = {
+ idCuotaSet: 456,
+ cambios: {
+ nombre: 'Plan Básico Actualizado',
+ descripcion: 'Plan básico sin productos',
+ periodoRenovacion: 1,
+ renovacionHabilitada: false,
+ productos: []
+ }
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ req = {
+ body: {},
+ };
+ res = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn(),
+ };
+ });
+
+ test('Debe actualizar un set de cuotas exitosamente con productos', async () => {
+ req.body = dataMockCompleto;
+ repositorio.actualizarSetCuotas.mockResolvedValue();
+
+ await controladorActualizarSetCuotas.actualizarSetCuotas(req, res);
+
+ expect(repositorio.actualizarSetCuotas).toHaveBeenCalledWith(
+ 123,
+ dataMockCompleto.cambios
+ );
+
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: 'Set de cuotas actualizado correctamente'
+ });
+ });
+
+ test('Debe actualizar un set de cuotas exitosamente sin productos', async () => {
+ req.body = dataMockSinProductos;
+ repositorio.actualizarSetCuotas.mockResolvedValue();
+
+ await controladorActualizarSetCuotas.actualizarSetCuotas(req, res);
+
+ expect(repositorio.actualizarSetCuotas).toHaveBeenCalledWith(
+ 456,
+ dataMockSinProductos.cambios
+ );
+
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: 'Set de cuotas actualizado correctamente'
+ });
+ });
+
+ test('Debe retornar error 400 cuando falta idCuotaSet', async () => {
+ req.body = {
+ cambios: dataMockCompleto.cambios
+ };
+
+ await controladorActualizarSetCuotas.actualizarSetCuotas(req, res);
+
+ expect(repositorio.actualizarSetCuotas).not.toHaveBeenCalled();
+ expect(res.status).toHaveBeenCalledWith(400);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: 'Datos incompletos'
+ });
+ });
+
+ test('Debe retornar error 400 cuando faltan cambios', async () => {
+ req.body = {
+ idCuotaSet: 123
+ };
+
+ await controladorActualizarSetCuotas.actualizarSetCuotas(req, res);
+
+ expect(repositorio.actualizarSetCuotas).not.toHaveBeenCalled();
+ expect(res.status).toHaveBeenCalledWith(400);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: 'Datos incompletos'
+ });
+ });
+
+ test('Debe retornar error 400 cuando idCuotaSet es null', async () => {
+ req.body = {
+ idCuotaSet: null,
+ cambios: dataMockCompleto.cambios
+ };
+
+ await controladorActualizarSetCuotas.actualizarSetCuotas(req, res);
+
+ expect(repositorio.actualizarSetCuotas).not.toHaveBeenCalled();
+ expect(res.status).toHaveBeenCalledWith(400);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: 'Datos incompletos'
+ });
+ });
+
+ test('Debe retornar error 400 cuando cambios es null', async () => {
+ req.body = {
+ idCuotaSet: 123,
+ cambios: null
+ };
+
+ await controladorActualizarSetCuotas.actualizarSetCuotas(req, res);
+
+ expect(repositorio.actualizarSetCuotas).not.toHaveBeenCalled();
+ expect(res.status).toHaveBeenCalledWith(400);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: 'Datos incompletos'
+ });
+ });
+
+ test('Debe retornar error 400 cuando el body está vacío', async () => {
+ req.body = {};
+
+ await controladorActualizarSetCuotas.actualizarSetCuotas(req, res);
+
+ expect(repositorio.actualizarSetCuotas).not.toHaveBeenCalled();
+ expect(res.status).toHaveBeenCalledWith(400);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: 'Datos incompletos'
+ });
+ });
+
+ test('Debe manejar errores del repositorio y retornar error 500', async () => {
+ req.body = dataMockCompleto;
+ const errorMensaje = 'Error de conexión a la base de datos';
+ repositorio.actualizarSetCuotas.mockRejectedValue(new Error(errorMensaje));
+
+ await controladorActualizarSetCuotas.actualizarSetCuotas(req, res);
+
+ expect(repositorio.actualizarSetCuotas).toHaveBeenCalledWith(
+ 123,
+ dataMockCompleto.cambios
+ );
+
+ expect(res.status).toHaveBeenCalledWith(500);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: errorMensaje
+ });
+ });
+
+ test('Debe manejar errores de validación SQL y retornar error 500', async () => {
+ req.body = dataMockCompleto;
+ const errorSQL = 'Constraint violation: Foreign key constraint fails';
+ repositorio.actualizarSetCuotas.mockRejectedValue(new Error(errorSQL));
+
+ await controladorActualizarSetCuotas.actualizarSetCuotas(req, res);
+
+ expect(repositorio.actualizarSetCuotas).toHaveBeenCalledWith(
+ 123,
+ dataMockCompleto.cambios
+ );
+
+ expect(res.status).toHaveBeenCalledWith(500);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: errorSQL
+ });
+ });
+
+ test('Debe actualizar correctamente con idCuotaSet como string numérico', async () => {
+ req.body = {
+ idCuotaSet: '789',
+ cambios: dataMockCompleto.cambios
+ };
+ repositorio.actualizarSetCuotas.mockResolvedValue();
+
+ await controladorActualizarSetCuotas.actualizarSetCuotas(req, res);
+
+ expect(repositorio.actualizarSetCuotas).toHaveBeenCalledWith(
+ '789',
+ dataMockCompleto.cambios
+ );
+
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: 'Set de cuotas actualizado correctamente'
+ });
+ });
+
+ test('Debe manejar productos con idProducto en 0 o negativos', async () => {
+ req.body = {
+ idCuotaSet: 123,
+ cambios: {
+ nombre: 'Test',
+ descripcion: 'Test desc',
+ periodoRenovacion: 1,
+ renovacionHabilitada: true,
+ productos: [
+ { idProducto: 0, limite: 100, limiteActual: 50 },
+ { idProducto: -1, limite: 200, limiteActual: 100 },
+ { idProducto: 1, limite: 300, limiteActual: 150 }
+ ]
+ }
+ };
+ repositorio.actualizarSetCuotas.mockResolvedValue();
+
+ await controladorActualizarSetCuotas.actualizarSetCuotas(req, res);
+
+ expect(repositorio.actualizarSetCuotas).toHaveBeenCalledWith(
+ 123,
+ req.body.cambios
+ );
+
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: 'Set de cuotas actualizado correctamente'
+ });
+ });
+});
\ No newline at end of file
diff --git a/_tests_/Empleados/exportarEmpleados.controller.test.js b/_tests_/Empleados/exportarEmpleados.controller.test.js
new file mode 100644
index 00000000..3dd61936
--- /dev/null
+++ b/_tests_/Empleados/exportarEmpleados.controller.test.js
@@ -0,0 +1,106 @@
+/**
+ * RF[59] Exportar Empleados - https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF59
+ * Mocks antes de importar el controlador
+ */
+jest.mock('@altertex/emp/repos/repositorioExportarEmpleado', () => ({
+ obtenerEmpleadosExportacion: jest.fn(),
+}));
+
+const controlador = require('@altertex/emp/ctrl/exportarEmpleados.controller');
+const repositorio = require('@altertex/emp/repos/repositorioExportarEmpleado');
+const MENSAJES_EMPLEADOS = require('@altertex/util/const/mensajesEmpleados');
+
+describe('Controlador exportarEmpleados', () => {
+ let req;
+ let res;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ req = {
+ user: { clienteSeleccionado: '5' },
+ body: { idsEmpleado: [1, 2] },
+ };
+
+ res = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn(),
+ };
+
+ jest.spyOn(console, 'error').mockImplementation(() => {});
+ });
+
+ // Escenario 1: No se envían IDs
+ test('Debe retornar error si no se envían empleados a exportar', async () => {
+ req.body.idsEmpleado = [];
+
+ await controlador.exportarEmpleados(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_EMPLEADOS.PARAMETROS_INVALIDOS.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: 'Debes seleccionar al menos un empleado para exportar.',
+ });
+ });
+
+ // Escenario 2: No se encuentran empleados en la base
+ test('Debe retornar mensaje si no hay empleados encontrados', async () => {
+ repositorio.obtenerEmpleadosExportacion.mockResolvedValue([]);
+
+ await controlador.exportarEmpleados(req, res);
+
+ expect(repositorio.obtenerEmpleadosExportacion).toHaveBeenCalledWith(5, [1, 2]);
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_EMPLEADOS.EMPLEADOS_NO_ENCONTRADOS.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_EMPLEADOS.EMPLEADOS_NO_ENCONTRADOS.mensaje,
+ csv: '',
+ });
+ });
+
+ // Escenario 3: Exportación exitosa
+ test('Debe retornar CSV con empleados exportados correctamente', async () => {
+ const empleadosMock = [
+ {
+ idEmpleado: 1,
+ nombreCompleto: 'Juan Pérez',
+ correoElectronico: 'juan@example.com',
+ numeroTelefono: '1234567890',
+ direccion: 'Calle Falsa 123',
+ fechaNacimiento: '1990-01-01',
+ genero: 'M',
+ estatus: 'Activo',
+ numeroEmergencia: '0987654321',
+ areaTrabajo: 'Ventas',
+ posicion: 'Ejecutivo',
+ cantidadPuntos: 200,
+ antiguedad: '2020-01-01',
+ },
+ ];
+
+ repositorio.obtenerEmpleadosExportacion.mockResolvedValue(empleadosMock);
+
+ await controlador.exportarEmpleados(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_EMPLEADOS.LISTA_EMPLEADOS_EXPORTADA.codigo);
+ const csvLlamado = res.json.mock.calls[0][0].csv;
+
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_EMPLEADOS.LISTA_EMPLEADOS_EXPORTADA.mensaje,
+ csv: expect.stringContaining('Juan Pérez'), // Verifica contenido parcial
+ });
+
+ expect(csvLlamado.startsWith('\uFEFF')).toBe(true); // CSV debe tener BOM
+ });
+
+ // Escenario 4: Error inesperado
+ test('Debe manejar errores del repositorio', async () => {
+ repositorio.obtenerEmpleadosExportacion.mockRejectedValue(new Error('Error de DB'));
+
+ await controlador.exportarEmpleados(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_EMPLEADOS.ERROR_EXPORTAR_EMPLEADOS.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_EMPLEADOS.ERROR_EXPORTAR_EMPLEADOS.mensaje,
+ csv: '',
+ });
+ });
+});
\ No newline at end of file
diff --git a/_tests_/Eventos/Controladores/crearEvento.controller.test.js b/_tests_/Eventos/Controladores/crearEvento.controller.test.js
new file mode 100644
index 00000000..68c0613d
--- /dev/null
+++ b/_tests_/Eventos/Controladores/crearEvento.controller.test.js
@@ -0,0 +1,185 @@
+// Mock del repositorio
+jest.mock('@altertex/eve/repos/repositorioCrearEvento', () => ({
+ crearEvento: jest.fn(),
+}));
+
+// Función a probar
+const { crearEvento } = require('@altertex/eve/ctrl/crearEvento.controller');
+const repositorio = require('@altertex/eve/repos/repositorioCrearEvento'); // Mock del repositorio
+const MENSAJES_EVENTOS = require('@altertex/util/const/mensajesEventos');
+
+// Pruebas
+describe('Controlador de Crear Evento', () => {
+ let req;
+ let res;
+
+ beforeEach(() => {
+ // Reset de los mocks
+ jest.clearAllMocks();
+
+ // Mock de req y res
+ req = {
+ body: {},
+ };
+
+ res = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn(),
+ cookie: jest.fn(),
+ };
+ });
+
+ test('Campos requeridos vacíos', async () => {
+ // Arrange - Solo faltan los campos requeridos
+ req.body = {
+ idCliente: '',
+ nombre: '',
+ descripcion: '', // Opcional - está bien que esté vacío
+ puntos: '',
+ multiplicador: '',
+ periodoRenovacion: '', // Opcional - está bien que esté vacío
+ renovacion: false,
+ };
+
+ // Act
+ await crearEvento(req, res);
+
+ // Assert
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ codigo: MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo,
+ mensaje: MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.mensaje,
+ });
+ });
+
+ test('Evento con campos opcionales vacíos', async () => {
+ // Arrange - Campos requeridos llenos, opcionales vacíos
+ req.body = {
+ idCliente: '1',
+ nombre: 'Evento de prueba',
+ descripcion: '', // Campo opcional vacío
+ puntos: '100',
+ multiplicador: '1.5',
+ periodoRenovacion: '', // Campo opcional vacío
+ renovacion: true,
+ };
+
+ const eventoCreado = {
+ id: 1,
+ nombre: 'Evento de prueba'
+ };
+
+ repositorio.crearEvento.mockResolvedValue({ evento: eventoCreado });
+
+ // Act
+ await crearEvento(req, res);
+
+ // Assert
+ expect(repositorio.crearEvento).toHaveBeenCalledWith({
+ idCliente: 1,
+ nombre: 'Evento de prueba',
+ descripcion: null, // Se convierte a null cuando está vacío
+ puntos: 100,
+ multiplicador: 1.5,
+ periodoRenovacion: null, // Se convierte a null cuando está vacío
+ renovacion: 1,
+ });
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_EVENTOS.EVENTO_CREADO.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ codigo: MENSAJES_EVENTOS.EVENTO_CREADO.codigo,
+ mensaje: MENSAJES_EVENTOS.EVENTO_CREADO.mensaje,
+ evento: eventoCreado,
+ });
+ });
+
+ test('Campos inválidos', async () => {
+ // Arrange
+ req.body = {
+ idCliente: 'abc',
+ nombre: 'Evento de prueba',
+ descripcion: 'Descripción del evento',
+ puntos: 'cien',
+ multiplicador: 'uno punto cinco',
+ periodoRenovacion: 'mensual',
+ renovacion: 'sí',
+ };
+
+ // Act
+ await crearEvento(req, res);
+
+ // Assert
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ codigo: MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo,
+ mensaje: 'El ID del cliente debe ser un número válido mayor a 0.',
+ });
+ });
+
+ test('Cliente inexistente (error del repositorio)', async () => {
+ // Arrange
+ req.body = {
+ idCliente: '999',
+ nombre: 'Evento de prueba',
+ descripcion: 'Descripción del evento',
+ puntos: '100',
+ multiplicador: '1.5',
+ periodoRenovacion: 'mensual',
+ renovacion: true,
+ };
+
+ // Simulamos un error específico del repositorio
+ repositorio.crearEvento.mockRejectedValue(new Error(MENSAJES_EVENTOS.ERROR_CLIENTE_NO_EXISTE.mensaje));
+
+ // Act
+ await crearEvento(req, res);
+
+ // Assert
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_EVENTOS.ERROR_CREAR_EVENTO.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ codigo: MENSAJES_EVENTOS.ERROR_CREAR_EVENTO.codigo,
+ mensaje: MENSAJES_EVENTOS.ERROR_CLIENTE_NO_EXISTE.mensaje,
+ });
+ });
+
+ test('Evento creado exitosamente', async () => {
+ // Arrange
+ req.body = {
+ idCliente: '1',
+ nombre: 'Evento de prueba',
+ descripcion: 'Descripción del evento',
+ puntos: '100',
+ multiplicador: '1.5',
+ periodoRenovacion: 'mensual',
+ renovacion: true,
+ };
+
+ const eventoCreado = {
+ id: 1,
+ nombre: 'Evento de prueba'
+ };
+
+ repositorio.crearEvento.mockResolvedValue({ evento: eventoCreado });
+
+ // Act
+ await crearEvento(req, res);
+
+ // Assert
+ expect(repositorio.crearEvento).toHaveBeenCalledWith({
+ idCliente: 1,
+ nombre: 'Evento de prueba',
+ descripcion: 'Descripción del evento',
+ puntos: 100,
+ multiplicador: 1.5,
+ periodoRenovacion: 'mensual',
+ renovacion: 1,
+ });
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_EVENTOS.EVENTO_CREADO.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ codigo: MENSAJES_EVENTOS.EVENTO_CREADO.codigo,
+ mensaje: MENSAJES_EVENTOS.EVENTO_CREADO.mensaje,
+ evento: eventoCreado,
+ });
+ });
+});
diff --git a/_tests_/SetsProductos/actualizarSetsProductos.controller.test.js b/_tests_/SetsProductos/actualizarSetsProductos.controller.test.js
new file mode 100644
index 00000000..3aea3b8d
--- /dev/null
+++ b/_tests_/SetsProductos/actualizarSetsProductos.controller.test.js
@@ -0,0 +1,83 @@
+const request = require('supertest');
+const express = require('express');
+const bodyParser = require('body-parser');
+const MENSAJES = require('@altertex/util/const/mensajesSetsProductos');
+
+jest.mock('@altertex/setspro/repos/repositorioActualizarSetsProductos', () => ({
+ actualizarSetProductos: jest.fn()
+}));
+
+const repositorio = require('@altertex/setspro/repos/repositorioActualizarSetsProductos');
+const controller = require('@altertex/setspro/ctrl/actualizarSetsProductos.controller');
+
+const app = express();
+app.use(bodyParser.json());
+
+// 👇 Middleware para simular usuario con cliente seleccionado
+app.use((req, res, next) => {
+ req.user = { clienteSeleccionado: 1 };
+ next();
+});
+
+app.put('/sets-productos', controller.actualizarSetProductos);
+
+describe('Controlador actualizarSetProductos', () => {
+ // Silenciar console.error durante las pruebas para evitar ruido en la salida
+ beforeAll(() => {
+ jest.spyOn(console, 'error').mockImplementation(() => {});
+ });
+
+ // Restaurar el comportamiento original de console.error
+ afterAll(() => {
+ console.error.mockRestore();
+ });
+
+ // Limpia los mocks después de cada test
+ afterEach(() => jest.clearAllMocks());
+
+ it('debe devolver 400 si faltan campos requeridos', async () => {
+ const res = await request(app).put('/sets-productos').send({
+ nombre: 'Set incompleto'
+ });
+
+ expect(res.statusCode).toBe(MENSAJES.FORMATO_INVALIDO_DATOS.codigo);
+ expect(res.body.mensaje).toBe(MENSAJES.FORMATO_INVALIDO_DATOS.mensaje);
+ expect(res.body.detalles).toMatch(/nombre y lista de productos/i);
+ });
+
+ it('debe devolver 200 si la actualización es exitosa', async () => {
+ repositorio.actualizarSetProductos.mockResolvedValue();
+
+ const datos = {
+ idSetProducto: 1,
+ nombre: 'Set Actualizado',
+ descripcion: 'Nueva descripción',
+ activo: true,
+ productos: [101, 102]
+ };
+
+ const res = await request(app).put('/sets-productos').send(datos);
+
+ expect(res.statusCode).toBe(MENSAJES.SET_ACTUALIZADO.codigo);
+ expect(res.body.mensaje).toBe(MENSAJES.SET_ACTUALIZADO.mensaje);
+ expect(res.body.datos).toEqual(datos);
+ expect(repositorio.actualizarSetProductos).toHaveBeenCalledWith(1, datos);
+ });
+
+ it('debe devolver 500 si el repositorio lanza un error', async () => {
+ repositorio.actualizarSetProductos.mockRejectedValue(new Error('Fallo DB'));
+
+ const datos = {
+ idSetProducto: 2,
+ nombre: 'Set con error',
+ descripcion: 'desc',
+ activo: false,
+ productos: []
+ };
+
+ const res = await request(app).put('/sets-productos').send(datos);
+
+ expect(res.statusCode).toBe(MENSAJES.ERROR_ACTUALIZAR_SET.codigo);
+ expect(res.body.mensaje).toBe('Fallo DB');
+ });
+});
diff --git a/_tests_/SetsProductos/consultarListaSetsProductos.controller.test.js b/_tests_/SetsProductos/consultarListaSetsProductos.controller.test.js
new file mode 100644
index 00000000..3c4d609a
--- /dev/null
+++ b/_tests_/SetsProductos/consultarListaSetsProductos.controller.test.js
@@ -0,0 +1,81 @@
+// Mocks antes de importar el controlador
+jest.mock('@altertex/setspro/repos/repositorioConsultarSetsProductos', () => ({
+ obtenerSetsProductos: jest.fn(),
+}));
+
+// Importaciones
+const controlador = require('@altertex/setspro/ctrl/consultarSetsProductos.controller');
+const repositorio = require('@altertex/setspro/repos/repositorioConsultarSetsProductos');
+const MENSAJES = require('@altertex/util/const/mensajesSetsProductos');
+
+describe('Controlador consultarLista (sets de productos)', () => {
+ let req;
+ let res;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ req = {
+ user: {
+ clienteSeleccionado: '101',
+ },
+ };
+
+ res = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn(),
+ };
+
+ jest.spyOn(console, 'error').mockImplementation(() => {}); // Silenciar errores
+ });
+
+ // Escenario 1: ID de cliente inválido
+ test('Debe retornar error si el ID del cliente no es válido', async () => {
+ req.user.clienteSeleccionado = undefined;
+
+ await controlador.consultarLista(req, res);
+ expect(res.status).toHaveBeenCalledWith(MENSAJES.PARAMETROS_INVALIDOS.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES.PARAMETROS_INVALIDOS.mensaje,
+ });
+ });
+
+ // Escenario 2: Lista de sets obtenida exitosamente
+ test('Debe retornar la lista de sets si la consulta es exitosa', async () => {
+ const mockSets = [
+ {
+ idSet: 1,
+ nombre: 'Set A',
+ productos: ['Producto 1', 'Producto 2'],
+ },
+ {
+ idSet: 2,
+ nombre: 'Set B',
+ productos: ['Producto 3'],
+ },
+ ];
+
+ repositorio.obtenerSetsProductos.mockResolvedValue(mockSets);
+
+ await controlador.consultarLista(req, res);
+
+ expect(repositorio.obtenerSetsProductos).toHaveBeenCalledWith(101);
+ expect(res.status).toHaveBeenCalledWith(MENSAJES.CONSULTA_EXITOSA.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES.CONSULTA_EXITOSA.mensaje,
+ setsProductos: mockSets,
+ });
+ });
+
+ // Escenario 3: Error inesperado en el repositorio
+ test('Debe manejar errores inesperados del repositorio', async () => {
+ repositorio.obtenerSetsProductos.mockRejectedValue(new Error('Error'));
+
+ await controlador.consultarLista(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES.ERROR_CONSULTAR_SETS_PRODUCTOS.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES.ERROR_CONSULTAR_SETS_PRODUCTOS.mensaje,
+ });
+ });
+});
diff --git a/_tests_/Usuarios/crearUsuario.controller.test.js b/_tests_/Usuarios/crearUsuario.controller.test.js
new file mode 100644
index 00000000..9e4496ed
--- /dev/null
+++ b/_tests_/Usuarios/crearUsuario.controller.test.js
@@ -0,0 +1,100 @@
+jest.mock('@altertex/usu/repos/repositorioCrearUsuario', () => ({
+ crearUsuarioConAsociaciones: jest.fn(),
+}));
+
+const repositorio = require('@altertex/usu/repos/repositorioCrearUsuario');
+const controlador = require('@altertex/usu/ctrl/crearUsuario.controller');
+const MENSAJES_USUARIOS = require('@altertex/util/const/mensajesUsuarios');
+
+describe('Controlador de Crear Usuario', () => {
+ let req;
+ let res;
+
+ const datosMock = {
+ nombreCompleto: 'María López',
+ correoElectronico: 'maria@correo.com',
+ contrasenia: 'Contrasenia1.',
+ numeroTelefono: '5512345678',
+ direccion: 'Calle Falsa 123',
+ fechaNacimiento: '1990-01-01',
+ genero: 'Femenino',
+ estatus: true,
+ idRol: 2,
+ idCliente: [1],
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ req = { body: { ...datosMock } };
+ res = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn(),
+ };
+ });
+
+ test('Debe crear un usuario exitosamente', async () => {
+ repositorio.crearUsuarioConAsociaciones.mockResolvedValue({ idUsuario: 101 });
+
+ await controlador.crearUsuario(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_USUARIOS.USUARIO_CREADO.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_USUARIOS.USUARIO_CREADO.mensaje,
+ idUsuario: 101,
+ });
+ expect(repositorio.crearUsuarioConAsociaciones).toHaveBeenCalled();
+ });
+
+ test('Debe rechazar si faltan campos requeridos', async () => {
+ req.body = { ...datosMock, nombreCompleto: undefined };
+
+ await controlador.crearUsuario(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(400);
+ expect(res.json).toHaveBeenCalledWith({ mensaje: 'Faltan campos requeridos' });
+ });
+
+ test('Debe rechazar correo inválido', async () => {
+ req.body.correoElectronico = 'correo-no-valido';
+
+ await controlador.crearUsuario(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_USUARIOS.CORREO_INVALIDO.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_USUARIOS.CORREO_INVALIDO.mensaje,
+ });
+ });
+
+ test('Debe rechazar contraseña débil (sin mayúscula)', async () => {
+ req.body.contrasenia = 'contrasenia$1';
+
+ await controlador.crearUsuario(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_USUARIOS.CONTRASENA_DEBIL.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: 'La contraseña debe contener al menos una letra mayúscula.',
+ });
+ });
+
+ test('Debe rechazar número telefónico inválido', async () => {
+ req.body.numeroTelefono = 'abc123';
+
+ await controlador.crearUsuario(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_USUARIOS.TELEFONO_INVALIDO.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_USUARIOS.TELEFONO_INVALIDO.mensaje,
+ });
+ });
+
+ test('Debe manejar error del repositorio', async () => {
+ repositorio.crearUsuarioConAsociaciones.mockRejectedValue(new Error('Fallo'));
+
+ await controlador.crearUsuario(req, res);
+
+ expect(res.status).toHaveBeenCalledWith(MENSAJES_USUARIOS.ERROR_CREAR_USUARIO.codigo);
+ expect(res.json).toHaveBeenCalledWith({
+ mensaje: MENSAJES_USUARIOS.ERROR_CREAR_USUARIO.mensaje,
+ });
+ });
+});
diff --git a/app.js b/app.js
index 1ad91006..c2ded97d 100644
--- a/app.js
+++ b/app.js
@@ -6,6 +6,7 @@ const express = require('express');
const cors = require('cors');
const cookieParser = require('cookie-parser');
const swaggerJSDoc = require('swagger-jsdoc');
+const csrf = require('csurf')
//Importaciones de configuracion
const corsOptions = require('@altertex/config/corsOptions');
@@ -35,10 +36,17 @@ const puerto = process.env.PORT || 5000;
//Configuracion de aplicacion express
const app = express();
-app.use(express.json({ limit: '5mb' }));
-app.use(express.urlencoded({ limit: '5mb', extended: true }));
+app.use(express.json({limit: '5mb'}));
+app.use(express.urlencoded({limit: '5mb', extended: true}));
app.use(cookieParser());
app.use(cors(corsOptions));
+const proteccionCsrf = csrf({
+ cookie: {
+ httpOnly: true,
+ secure: false,
+ sameSite: 'strict'
+ }
+})
cronCuotas.start();
@@ -57,14 +65,25 @@ app.use(RUTAS.API, rutasPedidos);
app.use(RUTAS.API, rutasEventos);
app.use(RUTAS.API, rutasPagos);
+app.use((req, res, next) => {
+ if (req.method === 'GET' && req.path === '/api/csrf-token') {
+ return next()
+ }
+ proteccionCsrf(req, res, next)
+})
+
+app.get('/api/csrf-token', proteccionCsrf, (req, res) => {
+ res.json({csrfToken: req.csrfToken()});
+});
+
app.get('/', async (req, res) => {
- return res
- .status(200)
- .json({ mensaje: `Ruta por default Proyecto Text&Lines en ambiente: ${process.env.NODE_ENV}` });
+ return res
+ .status(200)
+ .json({mensaje: `Ruta por default Proyecto Text&Lines en ambiente: ${process.env.NODE_ENV}`});
});
//Configuracion de swaggerUI
const swaggerSpec = swaggerJSDoc(opcionesSwagger);
app.use(RUTAS.API_DOCS, swaggerUI.serve, swaggerUI.setup(swaggerSpec));
app.listen(puerto, () =>
- console.log(`Servidor corriendo en puerto ${puerto} [${process.env.NODE_ENV}]`));
+ console.log(`Servidor corriendo en puerto ${puerto} [${process.env.NODE_ENV}]`));
diff --git a/dump.rdb b/dump.rdb
new file mode 100644
index 00000000..fa5c9e72
Binary files /dev/null and b/dump.rdb differ
diff --git a/jest.config.js b/jest.config.js
index ae7116a4..9162afb7 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -30,6 +30,22 @@ module.exports = {
'^@altertex/aut/datos/(.*)$': '/Autenticacion/Datos/$1',
'^@altertex/aut/(.*)$': '/Autenticacion/$1',
+ // Categorias module mappings
+ "^@altertex/cat/ctrl/(.*)$": "/Categorias/Controladores/$1",
+ "^@altertex/cat/repos/(.*)$": "/Categorias/Datos/Repositorios/$1",
+ "^@altertex/cat/rutasInd/(.*)$": "/Categorias/Rutas/RutasIndividuales/$1",
+ "^@altertex/cat/rutas/(.*)$": "/Categorias/Rutas/$1",
+ "^@altertex/cat/datos/(.*)$": "/Categorias/Datos/$1",
+ "^@altertex/cat/(.*)$": "/Categorias/$1",
+
+ // Clientes module mappings
+ "^@altertex/cli/ctrl/(.*)$": "/Clientes/Controladores/$1",
+ "^@altertex/cli/repos/(.*)$": "/Clientes/Datos/Repositorios/$1",
+ "^@altertex/cli/rutasInd/(.*)$": "/Clientes/Rutas/RutasIndividuales/$1",
+ "^@altertex/cli/rutas/(.*)$": "/Clientes/Rutas/$1",
+ "^@altertex/cli/datos/(.*)$": "/Clientes/Datos/$1",
+ "^@altertex/cli/(.*)$": "/Clientes/$1",
+
// Cuotas module mappings (added)
'^@altertex/cuota/ctrl/(.*)$': '/Cuotas/Controladores/$1',
'^@altertex/cuota/repos/(.*)$': '/Cuotas/Datos/Repositorios/$1',
@@ -60,7 +76,28 @@ module.exports = {
"^@altertex/emp/datos/(.*)$": "/Empleados/Datos/$1",
"^@altertex/emp/(.*)$": "/Empleados/$1",
+ // Eventos module mappings
+ "^@altertex/eve/ctrl/(.*)$": "/Eventos/Controladores/$1",
+ "^@altertex/eve/repos/(.*)$": "/Eventos/Datos/Repositorios/$1",
+ "^@altertex/eve/rutasInd/(.*)$": "/Eventos/Rutas/RutasIndividuales/$1",
+ "^@altertex/eve/rutas/(.*)$": "/Eventos/Rutas/$1",
+ "^@altertex/eve/datos/(.*)$": "/Eventos/Datos/$1",
+ "^@altertex/eve/(.*)$": "/Eventos/$1",
+
+ // Usuarios module mappings
+ '^@altertex/usu/ctrl/(.*)$': '/Usuarios/Controladores/$1',
+ '^@altertex/usu/repos/(.*)$': '/Usuarios/Datos/Repositorios/$1',
+ '^@altertex/usu/rutasInd/(.*)$': '/Usuarios/Rutas/RutasIndividuales/$1',
+ '^@altertex/usu/rutas/(.*)$': '/Usuarios/Rutas/$1',
+ '^@altertex/usu/datos/(.*)$': '/Usuarios/Datos/$1',
+ '^@altertex/usu/(.*)$': '/Usuarios/$1',
+
+ // Sets de productos
+ "^@altertex/setspro/ctrl/(.*)$": "/SetsProductos/Controladores/$1",
+ "^@altertex/setspro/repos/(.*)$": "/SetsProductos/Datos/Repositorios/$1",
+
// Generic mapping as fallback
'^@altertex/(.*)$': '/$1',
+
},
};
diff --git a/package-lock.json b/package-lock.json
index 23c6c96a..ce03fdc1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,10 +19,14 @@
"body-parser": "^1.20.3",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
+ "csurf": "^1.11.0",
+ "date-fns": "^4.1.0",
"dotenv": "^16.4.7",
+ "exceljs": "^4.4.0",
"express": "^4.21.2",
"helmet": "^8.1.0",
- "jest": "^29.7.0",
+ "i": "^0.3.7",
+ "json2csv": "^6.0.0-alpha.2",
"jsonwebtoken": "^9.0.2",
"module-alias": "^2.2.3",
"multer": "^1.4.5-lts.1",
@@ -42,13 +46,15 @@
"devDependencies": {
"@eslint/js": "^9.23.0",
"eslint": "^9.22.0",
- "eslint-plugin-jsdoc": "^50.6.11"
+ "eslint-plugin-jsdoc": "^50.6.11",
+ "jest": "^29.7.0"
}
},
"node_modules/@ampproject/remapping": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
"license": "Apache-2.0",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
@@ -1149,6 +1155,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.27.1",
@@ -1163,6 +1170,7 @@
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz",
"integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -1172,6 +1180,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz",
"integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.2.0",
@@ -1202,6 +1211,7 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -1219,12 +1229,14 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@babel/core/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -1234,6 +1246,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz",
"integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.27.1",
@@ -1250,6 +1263,7 @@
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
"integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.27.2",
@@ -1266,6 +1280,7 @@
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -1275,6 +1290,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/traverse": "^7.27.1",
@@ -1288,6 +1304,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz",
"integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.27.1",
@@ -1305,6 +1322,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
"integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -1314,6 +1332,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -1323,6 +1342,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -1332,6 +1352,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -1341,6 +1362,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz",
"integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.27.1",
@@ -1354,6 +1376,7 @@
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz",
"integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.27.1"
@@ -1369,6 +1392,7 @@
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
"integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
@@ -1381,6 +1405,7 @@
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
"integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
@@ -1393,6 +1418,7 @@
"version": "7.12.13",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
"integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.12.13"
@@ -1405,6 +1431,7 @@
"version": "7.14.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
"integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.14.5"
@@ -1420,6 +1447,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz",
"integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
@@ -1435,6 +1463,7 @@
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
"integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.10.4"
@@ -1447,6 +1476,7 @@
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
"integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
@@ -1459,6 +1489,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz",
"integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
@@ -1474,6 +1505,7 @@
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
"integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.10.4"
@@ -1486,6 +1518,7 @@
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
"integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
@@ -1498,6 +1531,7 @@
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
"integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.10.4"
@@ -1510,6 +1544,7 @@
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
"integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
@@ -1522,6 +1557,7 @@
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
"integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
@@ -1534,6 +1570,7 @@
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
"integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
@@ -1546,6 +1583,7 @@
"version": "7.14.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
"integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.14.5"
@@ -1561,6 +1599,7 @@
"version": "7.14.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
"integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.14.5"
@@ -1576,6 +1615,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz",
"integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
@@ -1612,6 +1652,7 @@
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
@@ -1626,6 +1667,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz",
"integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
@@ -1644,6 +1686,7 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -1661,6 +1704,7 @@
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -1670,12 +1714,14 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@babel/types": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz",
"integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
@@ -1689,6 +1735,7 @@
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@es-joy/jsdoccomment": {
@@ -1894,6 +1941,47 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@fast-csv/format": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz",
+ "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "^14.0.1",
+ "lodash.escaperegexp": "^4.1.2",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isequal": "^4.5.0",
+ "lodash.isfunction": "^3.0.9",
+ "lodash.isnil": "^4.0.0"
+ }
+ },
+ "node_modules/@fast-csv/format/node_modules/@types/node": {
+ "version": "14.18.63",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz",
+ "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==",
+ "license": "MIT"
+ },
+ "node_modules/@fast-csv/parse": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz",
+ "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "^14.0.1",
+ "lodash.escaperegexp": "^4.1.2",
+ "lodash.groupby": "^4.6.0",
+ "lodash.isfunction": "^3.0.9",
+ "lodash.isnil": "^4.0.0",
+ "lodash.isundefined": "^3.0.1",
+ "lodash.uniq": "^4.5.0"
+ }
+ },
+ "node_modules/@fast-csv/parse/node_modules/@types/node": {
+ "version": "14.18.63",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz",
+ "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==",
+ "license": "MIT"
+ },
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -1964,6 +2052,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
"integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"camelcase": "^5.3.1",
@@ -1980,6 +2069,7 @@
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"sprintf-js": "~1.0.2"
@@ -1989,6 +2079,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
@@ -2002,6 +2093,7 @@
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^1.0.7",
@@ -2015,6 +2107,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
@@ -2027,6 +2120,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
@@ -2042,6 +2136,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
@@ -2054,6 +2149,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -2063,12 +2159,14 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true,
"license": "BSD-3-Clause"
},
"node_modules/@istanbuljs/schema": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
"integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -2078,6 +2176,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz",
"integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/types": "^29.6.3",
@@ -2095,6 +2194,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz",
"integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/console": "^29.7.0",
@@ -2142,6 +2242,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
"integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/fake-timers": "^29.7.0",
@@ -2157,6 +2258,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz",
"integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"expect": "^29.7.0",
@@ -2170,6 +2272,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
"integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"jest-get-type": "^29.6.3"
@@ -2182,6 +2285,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
"integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/types": "^29.6.3",
@@ -2199,6 +2303,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
"integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/environment": "^29.7.0",
@@ -2214,6 +2319,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz",
"integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@bcoe/v8-coverage": "^0.2.3",
@@ -2257,6 +2363,7 @@
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
"integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@sinclair/typebox": "^0.27.8"
@@ -2269,6 +2376,7 @@
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz",
"integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.18",
@@ -2283,6 +2391,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz",
"integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/console": "^29.7.0",
@@ -2298,6 +2407,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz",
"integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/test-result": "^29.7.0",
@@ -2313,6 +2423,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
"integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/core": "^7.11.6",
@@ -2339,6 +2450,7 @@
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
"integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/schemas": "^29.6.3",
@@ -2356,6 +2468,7 @@
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
@@ -2370,6 +2483,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -2379,6 +2493,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -2388,12 +2503,14 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
@@ -3102,12 +3219,14 @@
"version": "0.27.8",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
"integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@sinonjs/commons": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
"integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
+ "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"type-detect": "4.0.8"
@@ -3117,6 +3236,7 @@
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
"integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
+ "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"@sinonjs/commons": "^3.0.0"
@@ -3840,14 +3960,19 @@
"node": ">=18.0.0"
}
},
+ "node_modules/@streamparser/json": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.6.tgz",
+ "integrity": "sha512-vL9EVn/v+OhZ+Wcs6O4iKE9EUpwHUqHmCtNUMWjqp+6dr85+XPOSGTEsqYNq1Vn04uk9SWlOVmx9J48ggJVT2Q=="
+ },
"node_modules/@swagger-api/apidom-ast": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-1.0.0-beta.37.tgz",
- "integrity": "sha512-PeFTmtt1TGd5lks48JWcTj3sIytifuQLXfN5xwbxhB98z1J/UvFUV7M+Vg6E/oJsvUaI7TyiaTcUAZgIal/57w==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-1.0.0-beta.39.tgz",
+ "integrity": "sha512-EWeSOtvI8XpbYMRkDyu4qAIlivhcplrskpau2cbrWfXGBjrqEtmHqWlbJ9xoXJbNshbIcZ0Z77QdxicimGjs0w==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-error": "^1.0.0-beta.37",
+ "@swagger-api/apidom-error": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
@@ -3855,54 +3980,54 @@
}
},
"node_modules/@swagger-api/apidom-core": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-1.0.0-beta.37.tgz",
- "integrity": "sha512-6ywaTg/95lZKV4c5cvPJ1vZWmUyN83G0qhe8VItsv8O7iFchaNRslgftoga03V6J8TGVlvAvWBQJ7WY/u2iUcQ==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-1.0.0-beta.39.tgz",
+ "integrity": "sha512-tYZSVA+uDFvBJmnP104d8Qb/mye8B6ykNviohHAngHsy8ElcOPzSi5GKwwmJgf3taWzipMqWNM0ch5KytbXTqw==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-ast": "^1.0.0-beta.37",
- "@swagger-api/apidom-error": "^1.0.0-beta.37",
+ "@swagger-api/apidom-ast": "^1.0.0-beta.39",
+ "@swagger-api/apidom-error": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"minim": "~0.23.8",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
- "short-unique-id": "=5.2.0",
+ "short-unique-id": "^5.3.2",
"ts-mixer": "^6.0.3"
}
},
"node_modules/@swagger-api/apidom-error": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-error/-/apidom-error-1.0.0-beta.37.tgz",
- "integrity": "sha512-goMIrcF02RFMW9uAfT0jWkp9gDNMOPqA2NJgb4bsEoBQL9uSXNnIMUEV3jF0fH4ZIanyqsar4ygznIKHC73Fdw==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-error/-/apidom-error-1.0.0-beta.39.tgz",
+ "integrity": "sha512-vQ3xQaRQGP9kNNBEDcFCmUd2PT9rCtYdkCyqYWZMxHBm5dXSBC/dQaC5VN1DbqQygE16fSQC+c5sqOrwg5d5WQ==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.20.7"
}
},
"node_modules/@swagger-api/apidom-json-pointer": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-1.0.0-beta.37.tgz",
- "integrity": "sha512-QZJScrxtG4J3oGdwOjV+X0bNdEh/Lggwy5zjnAFDajblRuqu6SADoIAEqrSsKPZzPwCuJ2N1haFpfJc6Z3c7Gg==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-1.0.0-beta.39.tgz",
+ "integrity": "sha512-gPDNT+MCs/B1XYuNpmnz0rOHQ0ssN9YjVDqeGkX61v03BLJUF/JZKMo3J3FA2mgKb6ap+kRHzpzw5PpHLwRKAw==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-error": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-error": "^1.0.0-beta.39",
"@swaggerexpert/json-pointer": "^2.10.1"
}
},
"node_modules/@swagger-api/apidom-ns-api-design-systems": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-1.0.0-beta.37.tgz",
- "integrity": "sha512-+/swYDxqNhjGxCdRG34hqmuTGNg/WCI+Je1CcmL+MlEvB2eiEAZ/KmhmvIES/P/6V1uPdFUsNOdQJ+oy8g7OJQ==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-1.0.0-beta.39.tgz",
+ "integrity": "sha512-MpdCb8KS3Tz1mGTrU0XC0Q2OcsrUWKB+buFPzLFOv0dU36ArARERX+Mz6aCJQ1CqnkFVM49uMe2NECO93ZR52w==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-error": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-error": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
@@ -3910,15 +4035,15 @@
}
},
"node_modules/@swagger-api/apidom-ns-arazzo-1": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-arazzo-1/-/apidom-ns-arazzo-1-1.0.0-beta.37.tgz",
- "integrity": "sha512-dYZckkAKMh/ptCsWcZEKcTkFhMjrRL6O9NqGmkWtjj6oqtHSV/syHvDG26mYPKM/wFjNacQh6mV12ADKk5so4A==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-arazzo-1/-/apidom-ns-arazzo-1-1.0.0-beta.39.tgz",
+ "integrity": "sha512-gIiZhlt51JxEZBAZ5PfHV1c73SMQJiwJX5DnazGehMO+ojR4HyLPFh1lc6mChMxPyPlRFOfqnmx/hmNcJ/XRiA==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-json-schema-2020-12": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-json-schema-2020-12": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
@@ -3926,15 +4051,15 @@
}
},
"node_modules/@swagger-api/apidom-ns-asyncapi-2": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-1.0.0-beta.37.tgz",
- "integrity": "sha512-I4N5VYaa4jtVYxIdsGbHe3mYmDhXvMShjhVFlIOrvmOpNE5C/AywUcN6zctlT9w8USrfMkDdc73eSvf2oNLDAw==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-1.0.0-beta.39.tgz",
+ "integrity": "sha512-Jtdo+6MgVhf8HynjRo5pIj+aYYICAQGwRkd0n0YtOhvvKoI0gWEMpcRkDbJrNcNYOHaSxMlQfotGlTCaMl7QJQ==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
@@ -3942,15 +4067,15 @@
}
},
"node_modules/@swagger-api/apidom-ns-json-schema-2019-09": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-2019-09/-/apidom-ns-json-schema-2019-09-1.0.0-beta.37.tgz",
- "integrity": "sha512-NCkbEiUILg83pl4KR4/YYsEk6QaAC4GajoFtq2/6cUktv3DgRRYx4P1A6UvOaszBQDG7PKgda3fP71CEwyd/lw==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-2019-09/-/apidom-ns-json-schema-2019-09-1.0.0-beta.39.tgz",
+ "integrity": "sha512-I/XP4zbrWAmnq2KWPtbb9DKLWgzYFovIiSQOyh47bJqbYgz64/IhoZb/uGihZojVVHSqeeJH9o6JOahqHQzKOw==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-error": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-error": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
@@ -3958,15 +4083,15 @@
}
},
"node_modules/@swagger-api/apidom-ns-json-schema-2020-12": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-2020-12/-/apidom-ns-json-schema-2020-12-1.0.0-beta.37.tgz",
- "integrity": "sha512-knPfySy8l9p1hDh2aNo8QgyLxWimeXo6SOzsw43Pv/1eoxn/9N/yCnwnRn4EcbupufFl17CsQNYS9avs2i3Few==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-2020-12/-/apidom-ns-json-schema-2020-12-1.0.0-beta.39.tgz",
+ "integrity": "sha512-9bpMp96fb76lOqeggtyCU457K/XBLyw3O9fxdVS3Tevhf8P3SJ6QpabmweRb6kFt4vI3+DiBschJGn0iqmlcXw==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-error": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-json-schema-2019-09": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-error": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-json-schema-2019-09": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
@@ -3974,14 +4099,14 @@
}
},
"node_modules/@swagger-api/apidom-ns-json-schema-draft-4": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-1.0.0-beta.37.tgz",
- "integrity": "sha512-33AKN2yeRx25Hl/ivK/R+Ni9Ih6V7XGI28AcQs9Ej+96FegRqdMKLypHOEEF7LZZHmqd2Ioij/GZJwH+t29MTg==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-1.0.0-beta.39.tgz",
+ "integrity": "sha512-F25tm/nwPl1rRnUHzaVw4SAeASodO60oAtWX+GF3K61WEx/Aao4Maldv3CQtAoUk8L0Ml0l1KZL00sgfikwqlw==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-ast": "^1.0.0-beta.37",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
+ "@swagger-api/apidom-ast": "^1.0.0-beta.39",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
@@ -3989,15 +4114,15 @@
}
},
"node_modules/@swagger-api/apidom-ns-json-schema-draft-6": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-1.0.0-beta.37.tgz",
- "integrity": "sha512-d5iR8Gbnm2YrWgXmT1Hbr2I66XlumC9073O5Cf170Jj5QUbIcAGlsIFDYmG+DvWQA0pPD4xONPyoEYezagivew==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-1.0.0-beta.39.tgz",
+ "integrity": "sha512-E2fQQHWIRtbM5C1m1EL95MQNDPL98mlgYomPQDDUEFbYrH3u9BQGAgpIu4KuYasKquyuhx9YXqS/jLRhMCRfAQ==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-error": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-error": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
@@ -4005,15 +4130,15 @@
}
},
"node_modules/@swagger-api/apidom-ns-json-schema-draft-7": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-1.0.0-beta.37.tgz",
- "integrity": "sha512-x3k/O0x2u3WrvU1ZhIO6DQ15nVuj9tARBQdDHJnGOK5PuD6Dh04+7pLYiOrjRyukLzHcKusoTgxKd7bFNjobww==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-1.0.0-beta.39.tgz",
+ "integrity": "sha512-mhzb7n3pm0yfYuM9bZowYMp6L61Cz+HrbjBowUIt5iOMMAATQd6x209pj81hnSmgHmEJCgv+8IO9dvweme698A==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-error": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-json-schema-draft-6": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-error": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-json-schema-draft-6": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
@@ -4021,16 +4146,16 @@
}
},
"node_modules/@swagger-api/apidom-ns-openapi-2": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-2/-/apidom-ns-openapi-2-1.0.0-beta.37.tgz",
- "integrity": "sha512-w+X20EbyMUDeRJH+k5gv0XRfZgrBNZD67OjQqwJhSDACa9DWbNryD878BMnZQVuHbZLDFdqRzJqz+UM9687/dQ==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-2/-/apidom-ns-openapi-2-1.0.0-beta.39.tgz",
+ "integrity": "sha512-/Cjggp0eAM1GfKeLu53sLjCV9lFVUMucFruXJnD1TWdCKv5S5JAKsGBASbchw8hvwrfx6sPHslzZFV+tZKbn2Q==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-error": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-error": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
@@ -4038,15 +4163,15 @@
}
},
"node_modules/@swagger-api/apidom-ns-openapi-3-0": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-1.0.0-beta.37.tgz",
- "integrity": "sha512-Hq1UwZ5J0XhWZU1a8XIg43xY03mKACO8u9SYIjkVwWvT5c3SK8lkf/ES3fpADExIyUIv9Z2wayIzvHTGMoEsWw==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-1.0.0-beta.39.tgz",
+ "integrity": "sha512-lvNlUtCmyHH8+52qOhgXXdzy4HEYA+t7xnFNvDb6dtP+epXCexux3uRs8+xEYBHo/WqUGzjdwd0qKFRgyP7Lrw==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-error": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-error": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
@@ -4054,17 +4179,17 @@
}
},
"node_modules/@swagger-api/apidom-ns-openapi-3-1": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-1.0.0-beta.37.tgz",
- "integrity": "sha512-ZhT7EvR4lVPvP541KiXtV2uGOF1/tWnVetpe4x1NLZTAXAXnWFfZVfPS2I7SPd8mavKB7Btk++Xjlv+O+82W+w==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-1.0.0-beta.39.tgz",
+ "integrity": "sha512-sXMJxTGL2F36Uyv9iqvPwvzsD5NJM/dJ52tUuiJP8h4RqXwjrOC86hqf1/Xk/rxgpZShhW4PNEqifvPq/Mto3w==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-ast": "^1.0.0-beta.37",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-json-pointer": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-json-schema-2020-12": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.37",
+ "@swagger-api/apidom-ast": "^1.0.0-beta.39",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-json-pointer": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-json-schema-2020-12": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
@@ -4072,112 +4197,112 @@
}
},
"node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-json": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-1.0.0-beta.37.tgz",
- "integrity": "sha512-PtDlmEY9cypb5KSrJfd2YBx0kBRxjliWmTcQ4PGSc2ZaYVnJDccsUPcnQ+rtY4s0PkYqUPVlSlxp08uurq34bA==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-1.0.0-beta.39.tgz",
+ "integrity": "sha512-jrPxZMvE6I2X8FUx4ri7VTMy2wTNOLLz+qXSx9sSXWULImqwdscvEwSVug8zdBQYMy8HXwt0wHpxlGLXBEmL8Q==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-beta.37",
- "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-beta.39",
+ "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0"
}
},
"node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-yaml": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-1.0.0-beta.37.tgz",
- "integrity": "sha512-z4miaZzBXCaKfZxK+boLYo6ZvpcNEx3e2+OP1Dh2JYPXYX/b26sT0jLfELbs30nOqVM+3WslfUhRi6AvQ/5XXg==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-1.0.0-beta.39.tgz",
+ "integrity": "sha512-JKyRYc4cBajPkIpO0YTJnxI+p8ubXfA+/1L8Fpq5kDPAI5Wh744iZ/scVHTgpgY8g+GbPqIoWB0ilQbEdlF5Sg==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-beta.37",
- "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-beta.39",
+ "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0"
}
},
"node_modules/@swagger-api/apidom-parser-adapter-arazzo-json-1": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-arazzo-json-1/-/apidom-parser-adapter-arazzo-json-1-1.0.0-beta.37.tgz",
- "integrity": "sha512-D8jGt31PS3pDE8j7qr+rFkHQcQUF7zHlLWRo+X0JjE6k+p03oaY0U+9A91+C5UUajS/fgDu0LqlKMXOuClMt0g==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-arazzo-json-1/-/apidom-parser-adapter-arazzo-json-1-1.0.0-beta.39.tgz",
+ "integrity": "sha512-qlQuj4jsEPpLRH4wpkMjbR3Id3qb4n/oerv4cKCi1TYJJphC7DG7QRS90tYjaAF7n2YA8HxUcEIu2+Y5QZKyMw==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-arazzo-1": "^1.0.0-beta.37",
- "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-arazzo-1": "^1.0.0-beta.39",
+ "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0"
}
},
"node_modules/@swagger-api/apidom-parser-adapter-arazzo-yaml-1": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-arazzo-yaml-1/-/apidom-parser-adapter-arazzo-yaml-1-1.0.0-beta.37.tgz",
- "integrity": "sha512-nSQZt3xWOURPBlVASeXb18lFstMMTRmVf/GIT1voBdvKsmluYK3HCVNgasCTaR375xpH+NOz9KbnufN2S1UKNg==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-arazzo-yaml-1/-/apidom-parser-adapter-arazzo-yaml-1-1.0.0-beta.39.tgz",
+ "integrity": "sha512-MUChnj6dJZRGDtIIVItIojzDNBEdan8KkuV+3U1l8bBA4eJQIq7yzHYk7fq6bl4Yd17HG0HT+1xckbUnj5Ay4A==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-arazzo-1": "^1.0.0-beta.37",
- "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-arazzo-1": "^1.0.0-beta.39",
+ "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0"
}
},
"node_modules/@swagger-api/apidom-parser-adapter-asyncapi-json-2": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-1.0.0-beta.37.tgz",
- "integrity": "sha512-kt8WF1tNylxQAXOTJ2YfZ3QGkm8jUsQ4izqrEnxNQwI8ag3jELpBogqhnM/NSS7aGJ47BNLg7Q/Ffi2Z075AJw==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-1.0.0-beta.39.tgz",
+ "integrity": "sha512-G+xIeYGetnCM3ylsWSwSyqCntpT6gt2Bv3f6hu/IonZxxCy2HqUl9JS41XF/cJHCoBchJU3G+bjOZXN22W3Xzw==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-beta.37",
- "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-beta.39",
+ "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0"
}
},
"node_modules/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-1.0.0-beta.37.tgz",
- "integrity": "sha512-F6xzrwMZGuKZ9YS09LNZby0CVhTMK8h2jDnCh8NamhGWG+BhMQpNwTv3KQfJh+YVZzE4NIN7LlsApTB4diM1qw==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-1.0.0-beta.39.tgz",
+ "integrity": "sha512-VPwlMRwtMQtPmEv6JXNefBBjAK/IxPsq9XWP/7kJZQ6CDp6ljHrMJDPAHZNundSL09xu7Wbz6KVGcpruUPiWmA==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-beta.37",
- "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-beta.39",
+ "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0"
}
},
"node_modules/@swagger-api/apidom-parser-adapter-json": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-1.0.0-beta.37.tgz",
- "integrity": "sha512-bTCiQjd5IGrqnHSefyoPC9wWuWouD0CRWyEFCb7YLfXEaLp+grK3BuxImtggvqPssmDdv5XsioVhKquZE5Hvzw==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-1.0.0-beta.39.tgz",
+ "integrity": "sha512-Rg2SgI06CDTdm3qs3A5pNvhonVxa2jOcwypxyhKngelIHaTuOPgaFA6qyCIvX0oIhwTcKcvV+5tPlGIR3vshpA==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-ast": "^1.0.0-beta.37",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-error": "^1.0.0-beta.37",
+ "@swagger-api/apidom-ast": "^1.0.0-beta.39",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-error": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0",
@@ -4199,112 +4324,112 @@
}
},
"node_modules/@swagger-api/apidom-parser-adapter-openapi-json-2": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-2/-/apidom-parser-adapter-openapi-json-2-1.0.0-beta.37.tgz",
- "integrity": "sha512-68gnZwSAC1eDAEcEQJSFlcJMEJBNkyHN1hWsfml+STwcnSxUOAe65A6kH2r970ekHzn4Ip8syk5WOPU46Q1epg==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-2/-/apidom-parser-adapter-openapi-json-2-1.0.0-beta.39.tgz",
+ "integrity": "sha512-DYB3jGcSnTu1ykbAKkMo550QW0BjnHlGxi1NaBbVYzdMfPYbBSQnDB3zYAwgakpoQx89OztFSlrNQ+3P8wuuSw==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-openapi-2": "^1.0.0-beta.37",
- "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-openapi-2": "^1.0.0-beta.39",
+ "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0"
}
},
"node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-0": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-1.0.0-beta.37.tgz",
- "integrity": "sha512-tTr43YkXnHhZMX6tbeH4SP2F6roK1b7BN0iZMCBUhD+gQiu4ZfhQBz8kJWkfTHtO1JB8sZoXUDQzY4PeITF25A==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-1.0.0-beta.39.tgz",
+ "integrity": "sha512-4ueWFNc3N4YZb7fTwsgrhWzdCo3TnZ7HgK5fPW3m0+Hm2wko2WYIUCIxU33Ef4DB+0Hd8y4Abjv8Mz0CCxRaeg==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.37",
- "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.39",
+ "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0"
}
},
"node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-1": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-1.0.0-beta.37.tgz",
- "integrity": "sha512-UGyVyh6ppl9SrPjDhbzRYzRGOClf+PisEWU2bdkNp5Ak4narqSppCCL65iGF/flasCDhODidBHJuJUY3Rx2rdg==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-1.0.0-beta.39.tgz",
+ "integrity": "sha512-8K/9J1CnQAwVqN+pvP9PH607WKA7WimNmZiyczgfnOgq93PUozNavrK97lwUUOQcZUmQra8pG2XrOrZZwd/M5w==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.37",
- "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.39",
+ "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0"
}
},
"node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-2": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-2/-/apidom-parser-adapter-openapi-yaml-2-1.0.0-beta.37.tgz",
- "integrity": "sha512-q6sqIfvXwq9VlwaXYznsaNYSj4MtAZhQGPvb7sWJvtPM/mi14uV08f3ZYCxjrDQg1kuElvEiXztcJPvnpfwL0A==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-2/-/apidom-parser-adapter-openapi-yaml-2-1.0.0-beta.39.tgz",
+ "integrity": "sha512-EfzVZrYTnwNGXUXIOrZkigQxdze+VdXxJWp55t3CWTy0CA7w9eM+PDpzHu7iqJgXqTOixMGy02Gzyv6N6sDj8A==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-openapi-2": "^1.0.0-beta.37",
- "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-openapi-2": "^1.0.0-beta.39",
+ "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0"
}
},
"node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-1.0.0-beta.37.tgz",
- "integrity": "sha512-CEdFueD/QKaWEp3d259Q7QkmqgxjYoaj1uj4tgm+9yErjIJQWUJWQmPMxDaGROglYYDfBw/qEwZJbKHAzze/Kw==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-1.0.0-beta.39.tgz",
+ "integrity": "sha512-ElHueuGdwB35VeZaJnmhZE3ILGE8F74ThJqgTbY+F2JcNo4O8cBkoCq9syw1pJ+l2JoAUErmxaTOR+zNA/wK+g==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.37",
- "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.39",
+ "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0"
}
},
"node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-1.0.0-beta.37.tgz",
- "integrity": "sha512-+bCYbidJaSqTkhVkDZZfTaMqc9F11XvSWFUjodXV0K9jRuDMKKY09uuOARVkUnEFZssUp7GSBpjaoXmjVJA+Dg==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-1.0.0-beta.39.tgz",
+ "integrity": "sha512-lNSXp+vGcsA/d/3ukXJeovAnO5oxcTJ5OvFBL84grJvK1C6E2v0AOfsMlUEipIRNhIHq3zYKpUnhFJyE13VqXA==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.37",
- "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.39",
+ "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0"
}
},
"node_modules/@swagger-api/apidom-parser-adapter-yaml-1-2": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-1.0.0-beta.37.tgz",
- "integrity": "sha512-aO7XosQpEBIEOVRXE/Um3sW2P/ZZLGgLYJhwHY2BcmPsflslGT3vqvZVXvFDHoqrfb3BDZY67IcbzMW04koE3Q==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-1.0.0-beta.39.tgz",
+ "integrity": "sha512-30Lhgkg2ZrHY7tQ5h9umjWWhy0Fqcoi28SXJ9vtdj1cLSnFvclaLe5ZGbXP3wdW4sXZO0As3+msL9tMwrUJ/7w==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-ast": "^1.0.0-beta.37",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-error": "^1.0.0-beta.37",
+ "@swagger-api/apidom-ast": "^1.0.0-beta.39",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-error": "^1.0.0-beta.39",
"@tree-sitter-grammars/tree-sitter-yaml": "=0.7.0",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
@@ -4346,16 +4471,16 @@
}
},
"node_modules/@swagger-api/apidom-reference": {
- "version": "1.0.0-beta.37",
- "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-1.0.0-beta.37.tgz",
- "integrity": "sha512-5rvC7oTgkcOGKPpBh3fskKV/meE9mSP8iaAP5b+HHoT72O24Me7AkVs0wiLthM3/NggEZDKjwfwG491fQHxotg==",
+ "version": "1.0.0-beta.39",
+ "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-1.0.0-beta.39.tgz",
+ "integrity": "sha512-PrV2/3Z6XGJPj4fv1JazY1dKjlnAg/BN22UQdUOzA5/A0TkfbImt8uVQuVzQSL2P8RA6G9TDsdpOalj80N47rw==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.26.10",
- "@swagger-api/apidom-core": "^1.0.0-beta.37",
- "@swagger-api/apidom-error": "^1.0.0-beta.37",
+ "@swagger-api/apidom-core": "^1.0.0-beta.39",
+ "@swagger-api/apidom-error": "^1.0.0-beta.39",
"@types/ramda": "~0.30.0",
- "axios": "^1.8.2",
+ "axios": "^1.9.0",
"minimatch": "^7.4.3",
"process": "^0.11.10",
"ramda": "~0.30.0",
@@ -4442,6 +4567,7 @@
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
"integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.20.7",
@@ -4455,6 +4581,7 @@
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
"integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.0.0"
@@ -4464,6 +4591,7 @@
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
"integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.1.0",
@@ -4474,6 +4602,7 @@
"version": "7.20.7",
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz",
"integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.20.7"
@@ -4490,6 +4619,7 @@
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
"integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
@@ -4508,12 +4638,14 @@
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
"integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@types/istanbul-lib-report": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz",
"integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/istanbul-lib-coverage": "*"
@@ -4523,6 +4655,7 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz",
"integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/istanbul-lib-report": "*"
@@ -4538,6 +4671,7 @@
"version": "22.15.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz",
"integrity": "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
@@ -4556,6 +4690,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@types/trusted-types": {
@@ -4587,6 +4722,7 @@
"version": "17.0.33",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
"integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/yargs-parser": "*"
@@ -4596,6 +4732,7 @@
"version": "21.0.3",
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
"integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
+ "dev": true,
"license": "MIT"
},
"node_modules/accepts": {
@@ -4688,6 +4825,7 @@
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
"integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"type-fest": "^0.21.3"
@@ -4748,6 +4886,59 @@
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
"license": "MIT"
},
+ "node_modules/archiver": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz",
+ "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^2.1.0",
+ "async": "^3.2.4",
+ "buffer-crc32": "^0.2.1",
+ "readable-stream": "^3.6.0",
+ "readdir-glob": "^1.1.2",
+ "tar-stream": "^2.2.0",
+ "zip-stream": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/archiver-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
+ "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.2.0",
+ "lazystream": "^1.0.0",
+ "lodash.defaults": "^4.2.0",
+ "lodash.difference": "^4.5.0",
+ "lodash.flatten": "^4.4.0",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.union": "^4.6.0",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/archiver/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/are-docs-informative": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz",
@@ -4879,6 +5070,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
"integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/transform": "^29.7.0",
@@ -4900,6 +5092,7 @@
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
"integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
+ "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
@@ -4916,6 +5109,7 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
"integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+ "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"@babel/core": "^7.12.3",
@@ -4932,6 +5126,7 @@
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -4941,6 +5136,7 @@
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
"integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.3.3",
@@ -4956,6 +5152,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz",
"integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/plugin-syntax-async-generators": "^7.8.4",
@@ -4982,6 +5179,7 @@
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz",
"integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"babel-plugin-jest-hoist": "^29.6.3",
@@ -5044,6 +5242,28 @@
"bcrypt": "bin/bcrypt"
}
},
+ "node_modules/big-integer": {
+ "version": "1.6.52",
+ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
+ "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==",
+ "license": "Unlicense",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/binary": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
+ "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==",
+ "license": "MIT",
+ "dependencies": {
+ "buffers": "~0.1.1",
+ "chainsaw": "~0.1.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -5056,6 +5276,55 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/bl/node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/bl/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/blessed": {
"version": "0.1.81",
"resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz",
@@ -5068,6 +5337,12 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/bluebird": {
+ "version": "3.4.7",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
+ "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==",
+ "license": "MIT"
+ },
"node_modules/bodec": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/bodec/-/bodec-0.1.0.tgz",
@@ -5130,6 +5405,7 @@
"version": "4.24.5",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz",
"integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==",
+ "dev": true,
"funding": [
{
"type": "opencollective",
@@ -5162,6 +5438,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
"integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+ "dev": true,
"license": "Apache-2.0",
"dependencies": {
"node-int64": "^0.4.0"
@@ -5178,6 +5455,15 @@
"isarray": "^1.0.0"
}
},
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
@@ -5190,6 +5476,23 @@
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"license": "MIT"
},
+ "node_modules/buffer-indexof-polyfill": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz",
+ "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/buffers": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
+ "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==",
+ "engines": {
+ "node": ">=0.2.0"
+ }
+ },
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@@ -5267,6 +5570,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -5285,6 +5589,7 @@
"version": "1.0.30001718",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
"integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==",
+ "dev": true,
"funding": [
{
"type": "opencollective",
@@ -5301,10 +5606,23 @@
],
"license": "CC-BY-4.0"
},
+ "node_modules/chainsaw": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
+ "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==",
+ "license": "MIT/X11",
+ "dependencies": {
+ "traverse": ">=0.3.0 <0.4"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
@@ -5321,6 +5639,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -5402,6 +5721,7 @@
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
"integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+ "dev": true,
"funding": [
{
"type": "github",
@@ -5417,6 +5737,7 @@
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
"integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
+ "dev": true,
"license": "MIT"
},
"node_modules/classnames": {
@@ -5453,6 +5774,7 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
@@ -5475,6 +5797,7 @@
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"iojs": ">= 1.0.0",
@@ -5485,6 +5808,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
"integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==",
+ "dev": true,
"license": "MIT"
},
"node_modules/color-convert": {
@@ -5552,6 +5876,35 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/compress-commons": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz",
+ "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "^0.2.13",
+ "crc32-stream": "^4.0.2",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/compress-commons/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -5598,6 +5951,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/cookie": {
@@ -5673,13 +6027,53 @@
"node": ">= 0.10"
}
},
- "node_modules/create-jest": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
- "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",
- "license": "MIT",
- "dependencies": {
- "@jest/types": "^29.6.3",
+ "node_modules/crc-32": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+ "license": "Apache-2.0",
+ "bin": {
+ "crc32": "bin/crc32.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/crc32-stream": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz",
+ "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "readable-stream": "^3.4.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/crc32-stream/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/create-jest": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
+ "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "^29.6.3",
"chalk": "^4.0.0",
"exit": "^0.1.2",
"graceful-fs": "^4.2.9",
@@ -5704,6 +6098,7 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
@@ -5714,12 +6109,93 @@
"node": ">= 8"
}
},
+ "node_modules/csrf": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/csrf/-/csrf-3.1.0.tgz",
+ "integrity": "sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w==",
+ "dependencies": {
+ "rndm": "1.2.0",
+ "tsscmp": "1.0.6",
+ "uid-safe": "2.1.5"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/css.escape": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
"integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
"license": "MIT"
},
+ "node_modules/csurf": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/csurf/-/csurf-1.11.0.tgz",
+ "integrity": "sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ==",
+ "deprecated": "This package is archived and no longer maintained. For support, visit https://github.com/expressjs/express/discussions",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "0.4.0",
+ "cookie-signature": "1.0.6",
+ "csrf": "3.1.0",
+ "http-errors": "~1.7.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/csurf/node_modules/cookie": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+ "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/csurf/node_modules/depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/csurf/node_modules/http-errors": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
+ "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
+ "dependencies": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.1.1",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/csurf/node_modules/setprototypeof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+ },
+ "node_modules/csurf/node_modules/statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/csurf/node_modules/toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
"node_modules/culvert": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/culvert/-/culvert-0.1.2.tgz",
@@ -5735,6 +6211,15 @@
"node": ">= 14"
}
},
+ "node_modules/date-fns": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
+ "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
"node_modules/dayjs": {
"version": "1.11.13",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
@@ -5763,6 +6248,7 @@
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz",
"integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==",
+ "dev": true,
"license": "MIT",
"peerDependencies": {
"babel-plugin-macros": "^3.1.0"
@@ -5870,6 +6356,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
"integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -5889,6 +6376,7 @@
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
"integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -5956,6 +6444,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/duplexer2": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+ "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "readable-stream": "^2.0.2"
+ }
+ },
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -5975,12 +6472,14 @@
"version": "1.5.152",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.152.tgz",
"integrity": "sha512-xBOfg/EBaIlVsHipHl2VdTPJRSvErNUaqW8ejTq5OlOlIYx1wOllCHsAvAIrr55jD1IYEfdR86miUEt8H5IeJg==",
+ "dev": true,
"license": "ISC"
},
"node_modules/emittery": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
"integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -6004,6 +6503,15 @@
"node": ">= 0.8"
}
},
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
"node_modules/enquirer": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
@@ -6020,6 +6528,7 @@
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"is-arrayish": "^0.2.1"
@@ -6074,6 +6583,7 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -6408,10 +6918,54 @@
"node": ">=18.0.0"
}
},
+ "node_modules/exceljs": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz",
+ "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==",
+ "license": "MIT",
+ "dependencies": {
+ "archiver": "^5.0.0",
+ "dayjs": "^1.8.34",
+ "fast-csv": "^4.3.1",
+ "jszip": "^3.10.1",
+ "readable-stream": "^3.6.0",
+ "saxes": "^5.0.1",
+ "tmp": "^0.2.0",
+ "unzipper": "^0.10.11",
+ "uuid": "^8.3.0"
+ },
+ "engines": {
+ "node": ">=8.3.0"
+ }
+ },
+ "node_modules/exceljs/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/exceljs/node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/execa": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"cross-spawn": "^7.0.3",
@@ -6435,6 +6989,7 @@
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
"integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+ "dev": true,
"engines": {
"node": ">= 0.8.0"
}
@@ -6443,6 +6998,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
"integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/expect-utils": "^29.7.0",
@@ -6535,6 +7091,19 @@
"follow-redirects": "^1.14.0"
}
},
+ "node_modules/fast-csv": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz",
+ "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==",
+ "license": "MIT",
+ "dependencies": {
+ "@fast-csv/format": "4.3.5",
+ "@fast-csv/parse": "4.3.6"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -6552,6 +7121,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/fast-levenshtein": {
@@ -6606,6 +7176,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
"integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
+ "dev": true,
"license": "Apache-2.0",
"dependencies": {
"bser": "2.1.1"
@@ -6800,6 +7371,12 @@
"node": ">= 0.6"
}
},
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "license": "MIT"
+ },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -6820,6 +7397,22 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
+ "node_modules/fstream": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
+ "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
+ "deprecated": "This package is no longer supported.",
+ "license": "ISC",
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "inherits": "~2.0.0",
+ "mkdirp": ">=0.5 0",
+ "rimraf": "2"
+ },
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -6842,6 +7435,7 @@
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -6884,6 +7478,7 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8.0.0"
@@ -6906,6 +7501,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -7149,6 +7745,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/http-errors": {
@@ -7243,11 +7840,20 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=10.17.0"
}
},
+ "node_modules/i": {
+ "version": "0.3.7",
+ "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz",
+ "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -7282,6 +7888,12 @@
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
"license": "ISC"
},
+ "node_modules/immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+ "license": "MIT"
+ },
"node_modules/immutable": {
"version": "3.8.2",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
@@ -7312,6 +7924,7 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
"integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"pkg-dir": "^4.2.0",
@@ -7331,6 +7944,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.8.19"
@@ -7440,6 +8054,7 @@
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/is-binary-path": {
@@ -7513,6 +8128,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
"integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -7602,6 +8218,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -7635,12 +8252,14 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
"license": "ISC"
},
"node_modules/istanbul-lib-coverage": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
"integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
+ "dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=8"
@@ -7650,6 +8269,7 @@
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
"integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
+ "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"@babel/core": "^7.23.9",
@@ -7666,6 +8286,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
"integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
+ "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"istanbul-lib-coverage": "^3.0.0",
@@ -7680,6 +8301,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
"integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
+ "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"debug": "^4.1.1",
@@ -7694,6 +8316,7 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -7711,12 +8334,14 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
"license": "MIT"
},
"node_modules/istanbul-reports": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
"integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
+ "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"html-escaper": "^2.0.0",
@@ -7730,6 +8355,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/core": "^29.7.0",
@@ -7756,6 +8382,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
"integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"execa": "^5.0.0",
@@ -7770,6 +8397,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz",
"integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/environment": "^29.7.0",
@@ -7801,6 +8429,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz",
"integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/core": "^29.7.0",
@@ -7834,6 +8463,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz",
"integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/core": "^7.11.6",
@@ -7879,6 +8509,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
"integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
@@ -7894,6 +8525,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz",
"integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"detect-newline": "^3.0.0"
@@ -7906,6 +8538,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz",
"integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/types": "^29.6.3",
@@ -7922,6 +8555,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz",
"integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/environment": "^29.7.0",
@@ -7939,6 +8573,7 @@
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz",
"integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -7948,6 +8583,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
"integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/types": "^29.6.3",
@@ -7973,6 +8609,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
"integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"jest-get-type": "^29.6.3",
@@ -7986,6 +8623,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
"integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
@@ -8001,6 +8639,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz",
"integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.12.13",
@@ -8021,6 +8660,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz",
"integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/types": "^29.6.3",
@@ -8035,6 +8675,7 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
"integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -8052,6 +8693,7 @@
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
"integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -8061,6 +8703,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz",
"integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
@@ -8081,6 +8724,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz",
"integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"jest-regex-util": "^29.6.3",
@@ -8094,6 +8738,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz",
"integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/console": "^29.7.0",
@@ -8126,6 +8771,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
"integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/environment": "^29.7.0",
@@ -8159,6 +8805,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
"integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/core": "^7.11.6",
@@ -8190,6 +8837,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
"integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/types": "^29.6.3",
@@ -8207,6 +8855,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz",
"integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/types": "^29.6.3",
@@ -8224,6 +8873,7 @@
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -8236,6 +8886,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz",
"integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/test-result": "^29.7.0",
@@ -8255,6 +8906,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
"integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*",
@@ -8270,6 +8922,7 @@
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
@@ -8346,6 +8999,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
@@ -8365,6 +9019,7 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true,
"license": "MIT"
},
"node_modules/json-schema-traverse": {
@@ -8388,10 +9043,36 @@
"license": "ISC",
"optional": true
},
+ "node_modules/json2csv": {
+ "version": "6.0.0-alpha.2",
+ "resolved": "https://registry.npmjs.org/json2csv/-/json2csv-6.0.0-alpha.2.tgz",
+ "integrity": "sha512-nJ3oP6QxN8z69IT1HmrJdfVxhU1kLTBVgMfRnNZc37YEY+jZ4nU27rBGxT4vaqM/KUCavLRhntmTuBFqZLBUcA==",
+ "dependencies": {
+ "@streamparser/json": "^0.0.6",
+ "commander": "^6.2.0",
+ "lodash.get": "^4.4.2"
+ },
+ "bin": {
+ "json2csv": "bin/json2csv.js"
+ },
+ "engines": {
+ "node": ">= 12",
+ "npm": ">= 6.13.0"
+ }
+ },
+ "node_modules/json2csv/node_modules/commander": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
+ "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
"license": "MIT",
"bin": {
"json5": "lib/cli.js"
@@ -8428,6 +9109,24 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
+ "node_modules/jszip": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+ "license": "(MIT OR GPL-3.0-or-later)",
+ "dependencies": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/jszip/node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "license": "(MIT AND Zlib)"
+ },
"node_modules/jwa": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
@@ -8463,6 +9162,7 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -8477,10 +9177,23 @@
"node": ">=0.2.0"
}
},
+ "node_modules/lazystream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
+ "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.6.3"
+ }
+ },
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
"integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -8500,12 +9213,28 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "immediate": "~3.0.5"
+ }
+ },
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
"license": "MIT"
},
+ "node_modules/listenercount": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz",
+ "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==",
+ "license": "ISC"
+ },
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -8534,6 +9263,30 @@
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"license": "MIT"
},
+ "node_modules/lodash.defaults": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+ "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.difference": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
+ "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.escaperegexp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
+ "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.flatten": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+ "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
+ "license": "MIT"
+ },
"node_modules/lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
@@ -8541,6 +9294,12 @@
"deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
"license": "MIT"
},
+ "node_modules/lodash.groupby": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz",
+ "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==",
+ "license": "MIT"
+ },
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@@ -8560,12 +9319,24 @@
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
"license": "MIT"
},
+ "node_modules/lodash.isfunction": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
+ "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==",
+ "license": "MIT"
+ },
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
"license": "MIT"
},
+ "node_modules/lodash.isnil": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz",
+ "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==",
+ "license": "MIT"
+ },
"node_modules/lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
@@ -8584,6 +9355,12 @@
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
"license": "MIT"
},
+ "node_modules/lodash.isundefined": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz",
+ "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==",
+ "license": "MIT"
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -8603,6 +9380,18 @@
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
"license": "MIT"
},
+ "node_modules/lodash.union": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
+ "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.uniq": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+ "license": "MIT"
+ },
"node_modules/long": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
@@ -8639,6 +9428,7 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"yallist": "^3.0.2"
@@ -8663,6 +9453,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
"integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"semver": "^7.5.3"
@@ -8678,6 +9469,7 @@
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
"integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
+ "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"tmpl": "1.0.5"
@@ -8714,6 +9506,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true,
"license": "MIT"
},
"node_modules/methods": {
@@ -8729,6 +9522,7 @@
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
@@ -8775,6 +9569,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -8951,6 +9746,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/needle": {
@@ -9108,12 +9904,14 @@
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
"integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/node-releases": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/nodemon": {
@@ -9201,6 +9999,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.0.0"
@@ -9280,6 +10079,7 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"mimic-fn": "^2.1.0"
@@ -9344,6 +10144,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"yocto-queue": "^0.1.0"
@@ -9486,6 +10287,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
@@ -9538,6 +10340,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -9559,6 +10362,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
@@ -9589,6 +10393,7 @@
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
"integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
@@ -9608,6 +10413,7 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"find-up": "^4.0.0"
@@ -9620,6 +10426,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
@@ -9633,6 +10440,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
@@ -9645,6 +10453,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
@@ -9660,6 +10469,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
@@ -9954,6 +10764,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jest/schemas": "^29.6.3",
@@ -9968,6 +10779,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -10013,6 +10825,7 @@
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
"integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"kleur": "^3.0.3",
@@ -10142,6 +10955,7 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
"integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==",
+ "dev": true,
"funding": [
{
"type": "individual",
@@ -10378,6 +11192,14 @@
"node": ">=4"
}
},
+ "node_modules/random-bytes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
+ "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -10498,6 +11320,7 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/react-redux": {
@@ -10573,6 +11396,36 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT"
},
+ "node_modules/readdir-glob": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
+ "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "minimatch": "^5.1.0"
+ }
+ },
+ "node_modules/readdir-glob/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/readdir-glob/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -10589,6 +11442,7 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/redis/-/redis-5.1.0.tgz",
"integrity": "sha512-5G5k9sYo5H5L0kd7UETiJZFTkIClH31fSmaEk2eU8E7mrmF0J1t6RqmFXOCJmSRlNd3QyvDUK/AWL9psbKaN0Q==",
+ "license": "MIT",
"dependencies": {
"@redis/bloom": "5.1.0",
"@redis/client": "5.1.0",
@@ -10767,6 +11621,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
"integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"resolve-from": "^5.0.0"
@@ -10779,6 +11634,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -10798,6 +11654,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz",
"integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -10812,6 +11669,24 @@
"node": ">=4"
}
},
+ "node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/rndm": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz",
+ "integrity": "sha512-fJhQQI5tLrQvYIYFpOnFinzv9dwmR7hRnUz1XqP3OJ1jIweTNOd6aTO4jwQSgcBSFUB+/KHJxuGneime+FdzOw=="
+ },
"node_modules/router": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
@@ -10956,6 +11831,18 @@
"integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==",
"license": "ISC"
},
+ "node_modules/saxes": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
+ "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
+ "license": "ISC",
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/scheduler": {
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
@@ -11086,6 +11973,12 @@
"node": ">= 0.4"
}
},
+ "node_modules/setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+ "license": "MIT"
+ },
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -11109,6 +12002,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
@@ -11121,6 +12015,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -11133,9 +12028,9 @@
"license": "BSD-2-Clause"
},
"node_modules/short-unique-id": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-5.2.0.tgz",
- "integrity": "sha512-cMGfwNyfDZ/nzJ2k2M+ClthBIh//GlZl1JEf47Uoa9XR11bz8Pa2T2wQO4bVrRdH48LrIDWJahQziKo3MjhsWg==",
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-5.3.2.tgz",
+ "integrity": "sha512-KRT/hufMSxXKEDSQujfVE0Faa/kZ51ihUcZQAcmP04t00DvPj7Ox5anHke1sJYUtzSuiT/Y5uyzg/W7bBEGhCg==",
"license": "Apache-2.0",
"bin": {
"short-unique-id": "bin/short-unique-id",
@@ -11236,12 +12131,14 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -11321,6 +12218,7 @@
"version": "0.5.13",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
"integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
@@ -11393,6 +12291,7 @@
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
"integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"escape-string-regexp": "^2.0.0"
@@ -11405,6 +12304,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
"integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -11470,6 +12370,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
"integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"char-regex": "^1.0.2",
@@ -11509,6 +12410,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -11518,6 +12420,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -11527,6 +12430,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -11640,18 +12544,18 @@
}
},
"node_modules/swagger-client": {
- "version": "3.35.0",
- "resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.35.0.tgz",
- "integrity": "sha512-AOs1GV0ucu7rNluT0tq0kSslEBvPhgIznwZnqs0fl+98MbpV4NtzbnHypRG1I93sS79Jj2bPtqhzujtnSS049w==",
+ "version": "3.35.3",
+ "resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.35.3.tgz",
+ "integrity": "sha512-4bO+dhBbasP485Ak67o46cWNVUnV0/92ypb2997bhvxTO2M+IuQZM1ilkN/7nSaiGuxDKJhkuL54I35PVI3AAw==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.22.15",
"@scarf/scarf": "=1.4.0",
- "@swagger-api/apidom-core": ">=1.0.0-beta.31 <1.0.0-rc.0",
- "@swagger-api/apidom-error": ">=1.0.0-beta.31 <1.0.0-rc.0",
- "@swagger-api/apidom-json-pointer": ">=1.0.0-beta.31 <1.0.0-rc.0",
- "@swagger-api/apidom-ns-openapi-3-1": ">=1.0.0-beta.31 <1.0.0-rc.0",
- "@swagger-api/apidom-reference": ">=1.0.0-beta.31 <1.0.0-rc.0",
+ "@swagger-api/apidom-core": ">=1.0.0-beta.39 <1.0.0-rc.0",
+ "@swagger-api/apidom-error": ">=1.0.0-beta.39 <1.0.0-rc.0",
+ "@swagger-api/apidom-json-pointer": ">=1.0.0-beta.39 <1.0.0-rc.0",
+ "@swagger-api/apidom-ns-openapi-3-1": ">=1.0.0-beta.39 <1.0.0-rc.0",
+ "@swagger-api/apidom-reference": ">=1.0.0-beta.39 <1.0.0-rc.0",
"@swaggerexpert/cookie": "^2.0.2",
"deepmerge": "~4.3.0",
"fast-json-patch": "^3.0.0-1",
@@ -11728,12 +12632,12 @@
}
},
"node_modules/swagger-ui": {
- "version": "5.21.0",
- "resolved": "https://registry.npmjs.org/swagger-ui/-/swagger-ui-5.21.0.tgz",
- "integrity": "sha512-zAY5P5nIWiYOuO0SWQk1x8/kL+pmarijO+oviWOp+SerfMpeokujYk6HknwEoeYNi4CtpO+kBj6Vm+8aswCBIA==",
+ "version": "5.22.0",
+ "resolved": "https://registry.npmjs.org/swagger-ui/-/swagger-ui-5.22.0.tgz",
+ "integrity": "sha512-xGc24TU6wgS5YWVcxwy90GUgqbHYvWvtvftjptfjXEMgetdvGWnI8CNRaYG3hG5bIl5Zt1rfai4NO2lBxAUswg==",
"license": "Apache-2.0",
"dependencies": {
- "@babel/runtime-corejs3": "^7.26.10",
+ "@babel/runtime-corejs3": "^7.27.1",
"@scarf/scarf": "=1.4.0",
"base64-js": "^1.5.1",
"classnames": "^2.5.1",
@@ -11763,7 +12667,7 @@
"reselect": "^5.1.1",
"serialize-error": "^8.1.0",
"sha.js": "^2.4.11",
- "swagger-client": "^3.34.4",
+ "swagger-client": "^3.35.3",
"url-parse": "^1.5.10",
"xml": "=1.0.1",
"xml-but-prettier": "^1.0.1",
@@ -11841,10 +12745,41 @@
"url": "https://www.buymeacoffee.com/systeminfo"
}
},
+ "node_modules/tar-stream": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^4.0.3",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tar-stream/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/test-exclude": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
"integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"@istanbuljs/schema": "^0.1.2",
@@ -11855,10 +12790,20 @@
"node": ">=8"
}
},
+ "node_modules/tmp": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
+ "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
"node_modules/tmpl": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
"integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
+ "dev": true,
"license": "BSD-3-Clause"
},
"node_modules/to-regex-range": {
@@ -11897,6 +12842,15 @@
"nodetouch": "bin/nodetouch.js"
}
},
+ "node_modules/traverse": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
+ "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==",
+ "license": "MIT/X11",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/tree-sitter": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.21.1.tgz",
@@ -11948,6 +12902,14 @@
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
+ "node_modules/tsscmp": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
+ "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==",
+ "engines": {
+ "node": ">=0.6.x"
+ }
+ },
"node_modules/tv4": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz",
@@ -11993,6 +12955,7 @@
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -12002,6 +12965,7 @@
"version": "0.21.3",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=10"
@@ -12038,6 +13002,17 @@
"ts-toolbelt": "^9.6.0"
}
},
+ "node_modules/uid-safe": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
+ "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
+ "dependencies": {
+ "random-bytes": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
@@ -12048,6 +13023,7 @@
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
"license": "MIT"
},
"node_modules/unpipe": {
@@ -12065,10 +13041,29 @@
"integrity": "sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==",
"license": "MIT"
},
+ "node_modules/unzipper": {
+ "version": "0.10.14",
+ "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz",
+ "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==",
+ "license": "MIT",
+ "dependencies": {
+ "big-integer": "^1.6.17",
+ "binary": "~0.3.0",
+ "bluebird": "~3.4.1",
+ "buffer-indexof-polyfill": "~1.0.0",
+ "duplexer2": "~0.1.4",
+ "fstream": "^1.0.12",
+ "graceful-fs": "^4.2.2",
+ "listenercount": "~1.0.1",
+ "readable-stream": "~2.3.6",
+ "setimmediate": "~1.0.4"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
"integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "dev": true,
"funding": [
{
"type": "opencollective",
@@ -12185,6 +13180,7 @@
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
"integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.12",
@@ -12241,6 +13237,7 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
"integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
+ "dev": true,
"license": "Apache-2.0",
"dependencies": {
"makeerror": "1.0.12"
@@ -12266,6 +13263,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
@@ -12318,6 +13316,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
@@ -12341,6 +13340,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
"integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"imurmurhash": "^0.1.4",
@@ -12408,6 +13408,12 @@
"node": ">=4.0"
}
},
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "license": "MIT"
+ },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@@ -12421,6 +13427,7 @@
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
"license": "ISC",
"engines": {
"node": ">=10"
@@ -12430,6 +13437,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
"license": "ISC"
},
"node_modules/yaml": {
@@ -12445,6 +13453,7 @@
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
@@ -12463,6 +13472,7 @@
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
@@ -12472,6 +13482,7 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -12516,6 +13527,55 @@
"integrity": "sha512-jEA1znR7b4C/NnaycInCU6h/d15ZzCd1jmsruqOKnZP6WXQSMH3W2GL+OXbkruslU4h+Tzuos0HdswzRUk/Vgg==",
"license": "Unlicense"
},
+ "node_modules/zip-stream": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz",
+ "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^3.0.4",
+ "compress-commons": "^4.1.2",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/zip-stream/node_modules/archiver-utils": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz",
+ "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^7.2.3",
+ "graceful-fs": "^4.2.0",
+ "lazystream": "^1.0.0",
+ "lodash.defaults": "^4.2.0",
+ "lodash.difference": "^4.5.0",
+ "lodash.flatten": "^4.4.0",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.union": "^4.6.0",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/zip-stream/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/zod": {
"version": "3.24.4",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz",
diff --git a/package.json b/package.json
index a503511e..f8296d10 100644
--- a/package.json
+++ b/package.json
@@ -23,10 +23,14 @@
"body-parser": "^1.20.3",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
+ "csurf": "^1.11.0",
+ "date-fns": "^4.1.0",
"dotenv": "^16.4.7",
+ "exceljs": "^4.4.0",
"express": "^4.21.2",
"helmet": "^8.1.0",
- "jest": "^29.7.0",
+ "i": "^0.3.7",
+ "json2csv": "^6.0.0-alpha.2",
"jsonwebtoken": "^9.0.2",
"module-alias": "^2.2.3",
"multer": "^1.4.5-lts.1",
@@ -46,7 +50,8 @@
"devDependencies": {
"@eslint/js": "^9.23.0",
"eslint": "^9.22.0",
- "eslint-plugin-jsdoc": "^50.6.11"
+ "eslint-plugin-jsdoc": "^50.6.11",
+ "jest": "^29.7.0"
},
"_moduleAliases": {
"@altertex/root": ".",