diff --git a/Clientes/Rutas/RutasIndividuales/consultarSistema.routes.js b/Clientes/Rutas/RutasIndividuales/consultarSistema.routes.js index 304c0c1..5284953 100644 --- a/Clientes/Rutas/RutasIndividuales/consultarSistema.routes.js +++ b/Clientes/Rutas/RutasIndividuales/consultarSistema.routes.js @@ -1,14 +1,13 @@ -const express = require("express"); +const express = require('express'); const ruteador = express.Router(); -const controlador = require("@altertex/cli/ctrl/consultarSistema.controller"); -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/cli/ctrl/consultarSistema.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 PERMISOS = require("@altertex/util/const/permisos"); -const RUTAS = require("@altertex/util/const/rutas"); +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); /** * @swagger @@ -63,7 +62,7 @@ ruteador.post( RUTAS.CLIENTES.CONSULTAR_SISTEMA, revisarApiKey(), autorizarToken, - limitePeticionesDiarias, + limitePeticionesDiarias, verificarPermisos(PERMISOS.CONSULTAR_SISTEMA_ADMINISTRATIVO), controlador.consultarSistema ); diff --git a/Productos/Controladores/actualizarProducto.controller.js b/Productos/Controladores/actualizarProducto.controller.js new file mode 100644 index 0000000..0a95cf6 --- /dev/null +++ b/Productos/Controladores/actualizarProducto.controller.js @@ -0,0 +1,202 @@ +// RF29 Actualizar Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF29 +const multer = require('multer'); +const upload = multer({ storage: multer.memoryStorage() }); +const validarProducto = require('@altertex/util/vali/validarProducto'); +const repositorioActualizarProducto = require('@altertex/pro/repos/repositorioActualizarProducto'); +const repositorioActualizarProductoSinImagenes = require('@altertex/pro/repos/repositorioActualizarProductoSinImagenes'); +const repositorioCrearOpcion = require('@altertex/pro/repos/repositorioCrearOpcion'); +const repositorioCrearVariante = require('@altertex/pro/repos/repositorioCrearVariante'); +const repositorioProductoImagen = require('@altertex/pro/repos/repositorioProductoImagen'); +const repositorioVarianteImagen = require('@altertex/pro/repos/repositorioVarianteImagen'); +const repositorioObtenerImagenes = require('@altertex/pro/repos/repositorioObtenerImagenesProducto'); +const validarVariante = require('@altertex/util/vali/validarVariante'); +const validarOpciones = require('@altertex/util/vali/validarOpciones'); +const enviarS3 = require('@altertex/util/ser/enviarS3'); +const MENSAJES_PRODUCTOS = require('@altertex/util/const/mensajesProductos'); +const db = require('@altertex/util/bd/db'); + +/** + * Controlador para actualizar un producto existente. + * + * @param {Express.Request} req - Objeto de solicitud HTTP. + * @param {Express.Response} res - Objeto de respuesta HTTP. + * @returns {Promise} Respuesta HTTP con el estado de la operación. + */ +// prettier-ignore +exports.actualizarProducto = [ + upload.fields([ + { name: 'imagenProducto', maxCount: 1 }, + { name: 'imagenesVariante', maxCount: 100 }, + ]), + + async (req, res) => { + let conexion; + + try { + console.log(req.body); + console.log(req.files); + // Extracción y parsing de datos + const { idProducto } = req.body; + const producto = JSON.parse(req.body.producto); + const variantes = JSON.parse(req.body.variantes); + const mapaImagenes = JSON.parse(req.body.mapaImagenes); + const imagenProducto = req.files.imagenProducto ? req.files.imagenProducto[0] : null; + console.log('imagen producto: ', imagenProducto); + const imagenesVariante = req.files.imagenesVariante || []; + + // Validaciones iniciales + if (!idProducto || isNaN(parseInt(idProducto)) || !producto) { + return res.status(MENSAJES_PRODUCTOS.PARAMETROS_INVALIDOS.codigo).json({ + mensaje: MENSAJES_PRODUCTOS.PARAMETROS_INVALIDOS.mensaje, + }); + } // Validación del producto + const productoParaValidar = { ...producto }; + delete productoParaValidar.idProveedor; + const errorValidacion = validarProducto(productoParaValidar); + if (errorValidacion) { + return res.status(MENSAJES_PRODUCTOS.PARAMETROS_INVALIDOS.codigo).json({ + mensaje: errorValidacion.error, + }); + } // Comprobar si hay nuevas imágenes + const tieneNuevasImagenes = !!imagenProducto + || (imagenesVariante && imagenesVariante.length > 0); + + // Iniciar transacción de base de datos + conexion = await db.getConnection(); + await conexion.beginTransaction(); + // Actualizar producto - elegir el repositorio adecuado según si hay o no nuevas imágenes + // Si no hay imágenes nuevas, usamos el repositorio que no elimina imágenes existentes + const actualizado = tieneNuevasImagenes + ? await repositorioActualizarProducto.actualizarProducto(idProducto, producto) + : await repositorioActualizarProductoSinImagenes.actualizarProductoSinImagenes(idProducto, producto); + if (!actualizado) { + await conexion.rollback(); + return res.status(MENSAJES_PRODUCTOS.PRODUCTO_NO_ENCONTRADO_ACTUALIZACION.codigo).json({ + mensaje: MENSAJES_PRODUCTOS.PRODUCTO_NO_ENCONTRADO_ACTUALIZACION.mensaje, + }); + } + + // Procesar variantes + const varianteIdMap = {}; + const variantesPromises = variantes.map(async (variante) => { + // Validar variante + const errorVariante = validarVariante({ + nombreVariante: variante.nombreVariante, + descripcion: variante.descripcion, + }); + if (errorVariante) { + throw new Error(errorVariante.error); + } + + // Crear variante + const idVariante = await repositorioCrearVariante.crearVariante(idProducto, variante); + if (!idVariante) { + throw new Error('Error al crear variante'); + } + + // Mapear ID de variante + varianteIdMap[variante.identificador] = { + id: idVariante, + nombre: variante.nombreVariante, + }; + + // Validar y crear opciones + const errorOpciones = validarOpciones(variante.opciones); + if (errorOpciones) { + throw new Error(errorOpciones.error); + } + + await repositorioCrearOpcion.crearOpcion(idVariante, variante.opciones); + }); + + await Promise.all(variantesPromises); + await conexion.commit(); + conexion.release(); // Procesar imágenes en S3 solo si hay nuevas imágenes + if (tieneNuevasImagenes) { + const urlImagenProductoPromise = imagenProducto + ? enviarS3({ + Bucket: process.env.AWS_BUCKET_NAME, + Key: `productos/${imagenProducto.originalname}`, + Body: imagenProducto.buffer, + ContentType: imagenProducto.mimetype, + }) + : Promise.resolve(null); + + const urlImagenVariantePromises = imagenesVariante.map((imagenVariante) => + enviarS3({ + Bucket: process.env.AWS_BUCKET_NAME, + Key: `productos/${imagenVariante.originalname}`, + Body: imagenVariante.buffer, + ContentType: imagenVariante.mimetype, + })); + + const [urlImagenProducto, ...urlImagenVariantes] = await Promise.all([ + urlImagenProductoPromise, + ...urlImagenVariantePromises, + ]); + // Verificar que las imágenes se subieron correctamente (solo si hay imágenes nuevas) + if ( + (imagenProducto && !urlImagenProducto) + || (imagenesVariante.length > 0 && urlImagenVariantes.includes(null)) + ) { + throw new Error('Error al subir imágenes al servidor'); + } + } // Guardar imágenes solo si hay nuevas + if (tieneNuevasImagenes) { + // Guardar imagen del producto en base de datos + if (imagenProducto) { + await repositorioProductoImagen.crearImagen( + idProducto, + imagenProducto.originalname, + producto.nombreComun + ); + } + + // Guardar imágenes de variantes en base de datos + if (imagenesVariante.length > 0) { + const imagenesVariantePromises = imagenesVariante.map(async (imagen, index) => { + const { idVariante: tempIdVariante } = mapaImagenes[index]; + const varianteInfo = varianteIdMap[tempIdVariante]; + + if (!varianteInfo) { + throw new Error(`Variante con ID temporal ${tempIdVariante} no encontrada`); + } + + await repositorioVarianteImagen.crearImagen( + varianteInfo.id, + imagen.originalname, + varianteInfo.nombre + ); + }); + + await Promise.all(imagenesVariantePromises); + } + } // Construir la respuesta de éxito + const respuesta = { + mensaje: MENSAJES_PRODUCTOS.ACTUALIZACION_EXITOSA.mensaje, + actualizacionImagenes: tieneNuevasImagenes, + }; + + // Obtener las imágenes actuales del producto solo si se subieron nuevas imágenes + if (tieneNuevasImagenes) { + const imagenes = await repositorioObtenerImagenes.obtenerImagenesProducto(idProducto); + respuesta.imagenes = imagenes; + } + + // Respuesta exitosa + return res.status(MENSAJES_PRODUCTOS.ACTUALIZACION_EXITOSA.codigo).json(respuesta); + } catch (error) { + // Manejo de errores + if (conexion) { + await conexion.rollback(); + conexion.release(); + } + + console.error('Error al actualizar producto extendido:', error); + return res.status(MENSAJES_PRODUCTOS.ERROR_ACTUALIZAR_PRODUCTO.codigo).json({ + mensaje: MENSAJES_PRODUCTOS.ERROR_ACTUALIZAR_PRODUCTO.mensaje, + error: error.message, + }); + } + }, +]; diff --git a/Productos/Controladores/leerProducto.controller.js b/Productos/Controladores/leerProducto.controller.js index 5303e6f..752bd30 100644 --- a/Productos/Controladores/leerProducto.controller.js +++ b/Productos/Controladores/leerProducto.controller.js @@ -35,4 +35,4 @@ exports.leerProducto = async (req, res) => { } 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/repositorioActualizarProducto.js b/Productos/Datos/Repositorios/repositorioActualizarProducto.js new file mode 100644 index 0000000..2c94097 --- /dev/null +++ b/Productos/Datos/Repositorios/repositorioActualizarProducto.js @@ -0,0 +1,36 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const consultas = require('@altertex/util/const/consultasProductos'); + +/** + * Actualiza un producto existente en la base de datos. + * + * @param {number} idProducto - ID del producto a actualizar. + * @param {object} producto - Objeto con la nueva información del producto. + * @returns {Promise} Verdadero si se actualizó correctamente. + */ +exports.actualizarProducto = async (idProducto, producto) => { + const params = [ + producto.nombreComun, + producto.nombreComercial, + producto.descripcion, + producto.marca, + producto.modelo, + producto.tipoProducto, + producto.precioPuntos, + producto.precioCliente, + producto.precioVenta, + producto.costo, + producto.impuesto, + producto.descuento, + producto.estado, + producto.envio, + idProducto, + ]; + + const resultado = await correrQuery(consultas.ACTUALIZAR, params); + await correrQuery(consultas.ELIMINAR_OPCIONES, [idProducto]); + await correrQuery(consultas.ELIMINAR_VARIANTES, [idProducto]); + await correrQuery(consultas.ELIMINAR_IMAGEN_PRODUCTO, [idProducto]); + await correrQuery(consultas.ELIMINAR_IMAGEN_VARIANTES, [idProducto]); + return resultado.affectedRows > 0; +}; \ No newline at end of file diff --git a/Productos/Datos/Repositorios/repositorioActualizarProductoSinImagenes.js b/Productos/Datos/Repositorios/repositorioActualizarProductoSinImagenes.js new file mode 100644 index 0000000..38b8c5f --- /dev/null +++ b/Productos/Datos/Repositorios/repositorioActualizarProductoSinImagenes.js @@ -0,0 +1,38 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const consultas = require('@altertex/util/const/consultasProductos'); + +/** + * Actualiza un producto existente en la base de datos sin afectar las imágenes. + * + * Esta función es una versión modificada de actualizarProducto que no elimina las imágenes existentes, + * permitiendo actualizar solo la información del producto y las variantes/opciones. + * + * @param {number} idProducto - ID del producto a actualizar. + * @param {object} producto - Objeto con la nueva información del producto. + * @returns {Promise} Verdadero si se actualizó correctamente. + */ +exports.actualizarProductoSinImagenes = async (idProducto, producto) => { + const params = [ + producto.nombreComun, + producto.nombreComercial, + producto.descripcion, + producto.marca, + producto.modelo, + producto.tipoProducto, + producto.precioPuntos, + producto.precioCliente, + producto.precioVenta, + producto.costo, + producto.impuesto, + producto.descuento, + producto.estado, + producto.envio, + idProducto, + ]; + + const resultado = await correrQuery(consultas.ACTUALIZAR, params); + // Eliminar opciones y variantes pero no las imágenes + await correrQuery(consultas.ELIMINAR_OPCIONES, [idProducto]); + await correrQuery(consultas.ELIMINAR_VARIANTES, [idProducto]); + return resultado.affectedRows > 0; +}; diff --git a/Productos/Datos/Repositorios/repositorioObtenerImagenesProducto.js b/Productos/Datos/Repositorios/repositorioObtenerImagenesProducto.js new file mode 100644 index 0000000..11e5261 --- /dev/null +++ b/Productos/Datos/Repositorios/repositorioObtenerImagenesProducto.js @@ -0,0 +1,24 @@ +// Repositorio para obtener imágenes de productos +const correrQuery = require('@altertex/util/ser/correrQuery'); +const consultas = require('@altertex/util/const/consultasProductos'); + +/** + * Obtiene todas las imágenes asociadas a un producto específico. + * + * @async + * @function obtenerImagenesProducto + * @param {number|string} idProducto - ID del producto del cual se quieren obtener las imágenes. + * @returns {Promise} Lista de imágenes asociadas al producto. + */ +exports.obtenerImagenesProducto = async (idProducto) => { + try { + // Usar la consulta existente para obtener imágenes por ID + const query = consultas.OBTENER_IMAGENES_POR_IDS.replace('(?)', '?'); + const imagenes = await correrQuery(query, [idProducto]); + + return imagenes || []; + } catch (error) { + console.error('Error al obtener imágenes del producto:', error); + return []; + } +}; diff --git a/Productos/Rutas/RutasIndividuales/actualizarProducto.routes.js b/Productos/Rutas/RutasIndividuales/actualizarProducto.routes.js new file mode 100644 index 0000000..207fd86 --- /dev/null +++ b/Productos/Rutas/RutasIndividuales/actualizarProducto.routes.js @@ -0,0 +1,218 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/pro/ctrl/actualizarProducto.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 + * /productos/actualizar: + * post: + * tags: + * - Productos + * summary: Actualizar un producto existente + * description: | + * Permite actualizar un producto existente con sus variantes, opciones e imágenes. + * Requiere autenticación, permisos específicos y está sujeto a límites de peticiones diarias. + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * multipart/form-data: + * schema: + * type: object + * required: + * - idProducto + * - producto + * - variantes + * - mapaImagenes + * properties: + * idProducto: + * type: string + * description: ID único del producto a actualizar + * example: "123" + * producto: + * type: string + * description: Objeto JSON stringificado con los datos del producto + * example: '{"nombreComun":"Camiseta Básica","descripcion":"Camiseta 100% algodón","precio":25.99,"categoria":"Ropa","marca":"AlterTex"}' + * variantes: + * type: string + * description: Array JSON stringificado con las variantes del producto + * example: '[{"identificador":"var1","nombreVariante":"Talla M","descripcion":"Talla mediana","opciones":{"color":"azul","talla":"M"}}]' + * mapaImagenes: + * type: string + * description: Array JSON stringificado que mapea imágenes con variantes + * example: '[{"idVariante":"var1","indiceImagen":0}]' + * imagenProducto: + * type: string + * format: binary + * description: Imagen principal del producto (opcional) + * imagenesVariante: + * type: array + * items: + * type: string + * format: binary + * description: Imágenes de las variantes del producto (máximo 100) + * responses: + * 200: + * description: Producto actualizado exitosamente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Producto actualizado exitosamente" + * 400: + * description: Parámetros inválidos o error de validación + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Los parámetros proporcionados son inválidos" + * 401: + * description: No autorizado - Token inválido o faltante + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Token de acceso inválido" + * 403: + * description: Permisos insuficientes + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "No tienes permisos para actualizar productos" + * 404: + * description: Producto no encontrado + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Producto no encontrado para actualización" + * 429: + * description: Límite de peticiones diarias excedido + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Límite de peticiones diarias excedido" + * 500: + * description: Error interno del servidor + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Error interno al actualizar el producto" + * error: + * type: string + * example: "Descripción detallada del error" + * + * components: + * securitySchemes: + * ApiKeyAuth: + * type: apiKey + * in: header + * name: X-API-Key + * description: API Key requerida para acceder al endpoint + * BearerAuth: + * type: http + * scheme: bearer + * bearerFormat: JWT + * description: Token JWT para autenticación de usuario + * schemas: + * ProductoBase: + * type: object + * properties: + * nombreComun: + * type: string + * description: Nombre común del producto + * example: "Camiseta Básica" + * descripcion: + * type: string + * description: Descripción detallada del producto + * example: "Camiseta 100% algodón, cómoda y duradera" + * precio: + * type: number + * format: float + * description: Precio del producto + * example: 25.99 + * categoria: + * type: string + * description: Categoría del producto + * example: "Ropa" + * marca: + * type: string + * description: Marca del producto + * example: "AlterTex" + * VarianteProducto: + * type: object + * properties: + * identificador: + * type: string + * description: Identificador único temporal de la variante + * example: "var1" + * nombreVariante: + * type: string + * description: Nombre de la variante + * example: "Talla M - Color Azul" + * descripcion: + * type: string + * description: Descripción de la variante + * example: "Talla mediana en color azul" + * opciones: + * type: object + * description: Opciones específicas de la variante + * example: {"color": "azul", "talla": "M"} + * MapaImagen: + * type: object + * properties: + * idVariante: + * type: string + * description: ID temporal de la variante asociada a la imagen + * example: "var1" + * indiceImagen: + * type: integer + * description: Índice de la imagen en el array de imágenes + * example: 0 + */ + + +ruteador.post( + RUTAS.PRODUCTOS.ACTUALIZAR, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + validarYSanitizar, + verificarPermisos(PERMISOS.ACTUALIZAR_PRODUCTO), + controlador.actualizarProducto +); + +module.exports = ruteador; \ No newline at end of file diff --git a/Productos/Rutas/RutasIndividuales/leerProductos.routes.js b/Productos/Rutas/RutasIndividuales/leerProductos.routes.js index d11e442..1eeca7a 100644 --- a/Productos/Rutas/RutasIndividuales/leerProductos.routes.js +++ b/Productos/Rutas/RutasIndividuales/leerProductos.routes.js @@ -9,6 +9,13 @@ 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); +ruteador.get( + RUTAS.PRODUCTOS.LEER, + revisarApiKey(), + autorizarToken, + verificarPermisos(PERMISOS.LEER_PRODUCTO), + validarYSanitizar, + controlador.leerProducto +); -module.exports = ruteador; \ No newline at end of file +module.exports = ruteador; diff --git a/Productos/Rutas/indexProductos.routes.js b/Productos/Rutas/indexProductos.routes.js index 3f014e4..5f8ffc4 100644 --- a/Productos/Rutas/indexProductos.routes.js +++ b/Productos/Rutas/indexProductos.routes.js @@ -6,7 +6,7 @@ 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 rutasActualizarProducto = require('@altertex/pro/rutasInd/actualizarProducto.routes'); const RUTAS = require('@altertex/util/const/rutas'); //RF27 Consulta Lista de Productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF27 @@ -21,5 +21,7 @@ ruteador.use(RUTAS.PRODUCTOS.BASE, rutasLeerProducto); 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); +// RF[29] Actualizar Producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF29] +ruteador.use(RUTAS.PRODUCTOS.BASE, rutasActualizarProducto); module.exports = ruteador; diff --git a/Utilidades/Constantes/consultasImagenes.js b/Utilidades/Constantes/consultasImagenes.js index 0c559a6..0c3b8f0 100644 --- a/Utilidades/Constantes/consultasImagenes.js +++ b/Utilidades/Constantes/consultasImagenes.js @@ -3,4 +3,9 @@ module.exports = { INSERT INTO imagen(urlImagen, tipoImagen, textoAlternativo) VALUES (?, ?, ?); `, + ACTUALIZAR: ` + UPDATE imagen + SET urlImagen = ?, tipoImagen = ?, textoAlternativo = ? + WHERE idImagen = ?; + `, }; diff --git a/Utilidades/Constantes/consultasOpciones.js b/Utilidades/Constantes/consultasOpciones.js index 825ebfe..ac41ac9 100644 --- a/Utilidades/Constantes/consultasOpciones.js +++ b/Utilidades/Constantes/consultasOpciones.js @@ -3,4 +3,9 @@ module.exports = { INSERT INTO opcion (idVariante, cantidad, valorOpcion, SKUautomatico, SKUcomercial, costoAdicional, descuento, estado) VALUES (?, ?, ?, ?, ?, ?, ?, ?); `, + ACTUALIZAR: ` + UPDATE opcion + SET idVariante = ?, cantidad = ?, valorOpcion = ?, SKUcomercial = ?, costoAdicional = ?, descuento = ?, estado = ? + WHERE idOpcion = ?; + `, }; diff --git a/Utilidades/Constantes/consultasProductos.js b/Utilidades/Constantes/consultasProductos.js index b3526c6..06ed28c 100644 --- a/Utilidades/Constantes/consultasProductos.js +++ b/Utilidades/Constantes/consultasProductos.js @@ -1,4 +1,33 @@ module.exports = { + ACTUALIZAR:` + UPDATE producto + SET nombreComun = ?, nombreComercial = ?, descripcion = ?, + marca = ?, modelo = ?, tipoProducto = ?, precioPuntos = ?, precioCliente = ?, + precioVenta = ?, costo = ?, impuesto = ?, descuento = ?, estado = ?, envio = ? + WHERE idProducto = ?; + `, + + ELIMINAR_OPCIONES: ` + DELETE opcion + FROM opcion + INNER JOIN variante ON opcion.idVariante = variante.idVariante + WHERE variante.idProducto = ?; + `, + + ELIMINAR_VARIANTES: ` + DELETE FROM variante + WHERE idProducto = ?; + `, + ELIMINAR_IMAGEN_PRODUCTO: ` + DELETE FROM imagen_producto + WHERE idProducto = ?; + `, + ELIMINAR_IMAGEN_VARIANTES: ` + DELETE imagen_variante + FROM imagen_variante + INNER JOIN variante ON imagen_variante.idVariante = variante.idVariante + WHERE variante.idProducto = ?; + `, OBTENER_LISTA: ` SELECT p.idProducto, p.nombreComun, p.precioVenta, p.estado, i.urlImagen FROM producto p @@ -49,6 +78,7 @@ module.exports = { 'descuento', p.descuento, 'estado', p.estado, 'envio', p.envio, + 'descripcion', p.descripcion, 'nombreProveedor', pr.nombreCompania, 'variantes', (SELECT JSON_ARRAYAGG( JSON_OBJECT( @@ -57,6 +87,7 @@ module.exports = { 'descripcion', v.descripcion, 'opciones', (SELECT JSON_ARRAYAGG( JSON_OBJECT( + 'idOpcion', o.idOpcion, 'cantidad', o.cantidad, 'valorOpcion', o.valorOpcion, diff --git a/Utilidades/Constantes/consultasVariantes.js b/Utilidades/Constantes/consultasVariantes.js index b9ebc21..b7288c9 100644 --- a/Utilidades/Constantes/consultasVariantes.js +++ b/Utilidades/Constantes/consultasVariantes.js @@ -7,4 +7,14 @@ module.exports = { INSERT INTO imagen_variante (idImagen, idVariante) VALUES (?, ?); `, + ACTUALIZAR: ` + UPDATE variante + SET idProducto = ?, nombreVariante = ?, descripcion = ? + WHERE idVariante = ?; + `, + ACTUALIZAR_IMAGEN_VARIANTE: ` + UPDATE imagen_variante + SET idImagen = ? + WHERE idVariante = ?; + `, }; diff --git a/Utilidades/Constantes/mensajesProductos.js b/Utilidades/Constantes/mensajesProductos.js index 7f71397..28db914 100644 --- a/Utilidades/Constantes/mensajesProductos.js +++ b/Utilidades/Constantes/mensajesProductos.js @@ -8,6 +8,10 @@ module.exports = { codigo: 200, mensaje: 'Producto creado correctamente.', }, + ACTUALIZACION_EXITOSA: { + codigo: 200, + mensaje: 'Producto actualizado exitosamente.', + }, // 204 - No Content SIN_RESULTADOS: { @@ -28,6 +32,11 @@ module.exports = { codigo: 400, mensaje: 'Los valores de límite u offset deben ser números positivos.', }, + ERROR_PARAMETROS_ACTUALIZACION: { + codigo: 400, + mensaje: + 'Los parámetros proporcionados para la actualización no son válidos o están incompletos.', + }, // 403 - Forbidden PERMISO_DENEGADO: { @@ -35,6 +44,12 @@ module.exports = { mensaje: 'No tiene permiso para consultar productos de este cliente.', }, + // 404 - Not Found + PRODUCTO_NO_ENCONTRADO_ACTUALIZACION: { + codigo: 404, + mensaje: 'El producto que intenta actualizar no existe.', + }, + // 500 - Internal Server Error ERROR_CONSULTAR_PRODUCTOS: { codigo: 500, @@ -63,6 +78,10 @@ module.exports = { mensaje: 'Ocurrió un error al asociar la imagen con la variante. Verifique los datos de las imágenes de variantes.', }, + ERROR_ACTUALIZAR_PRODUCTO: { + codigo: 500, + mensaje: 'Ocurrió un error al actualizar el producto. Por favor, intente nuevamente más tarde.', + }, // 200 - OK RESPUESTA_ELIMINAR_PRODUCTO_EXITOSA: { diff --git a/Utilidades/Constantes/rutas.js b/Utilidades/Constantes/rutas.js index c9ba52b..1487f7b 100644 --- a/Utilidades/Constantes/rutas.js +++ b/Utilidades/Constantes/rutas.js @@ -44,6 +44,7 @@ module.exports = { IMPORTAR: '/importar', LEER: '/leer-producto', EXPORTAR_PRODUCTOS: '/exportar-productos', + ACTUALIZAR: '/actualizar', }, PROVEEDORES: { BASE: '/proveedores', diff --git a/Utilidades/Intermediarios/Validaciones/validarProducto.js b/Utilidades/Intermediarios/Validaciones/validarProducto.js index 5738d07..b38dcc1 100644 --- a/Utilidades/Intermediarios/Validaciones/validarProducto.js +++ b/Utilidades/Intermediarios/Validaciones/validarProducto.js @@ -46,13 +46,15 @@ */ // prettier-ignore module.exports = (producto) => { - if ( - producto.idProveedor !== null - && (typeof producto.idProveedor !== 'number' - || producto.idProveedor <= 0 - || !Number.isInteger(producto.idProveedor)) - ) { - return { error: 'idProveedor debe ser un número entero positivo o NULL.' }; + if ('idProveedor' in producto) { + if ( + producto.idProveedor !== null + && (typeof producto.idProveedor !== 'number' + || producto.idProveedor <= 0 + || !Number.isInteger(producto.idProveedor)) + ) { + return { error: 'idProveedor debe ser un número entero positivo o NULL.' }; + } } if (