Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
663ba4e
Feat: Implementar funcionalidad para actualizar productos y variantes
Rodrig0at Jun 6, 2025
7457525
Feat: AGREGUÉ LA NUEVAS QUERYS PARA PRODUCTO, VARIANTE Y OPCION E IMA…
Rodrig0at Jun 6, 2025
7d097a4
Feat: Agregar mensajes de respuesta para la actualización de productos
Rodrig0at Jun 6, 2025
bf6d634
Feat: Mejorar manejo de errores y mensajes en la actualización de pro…
Rodrig0at Jun 6, 2025
9c7e5e8
Feat: Agregar manejo de errores en la actualización de productos
Rodrig0at Jun 6, 2025
b2fe224
Feat: Agregar documentación Swagger para la actualización de producto…
Rodrig0at Jun 6, 2025
1524d71
Refactor: Simplificar la documentación de la función actualizarProduc…
Rodrig0at Jun 6, 2025
eea6fd9
Refactor: Eliminar importaciones innecesarias en consultasImagenes y …
Rodrig0at Jun 6, 2025
b0774d3
Fix: Corregir la importación del controlador en actualizarProducto.ro…
Rodrig0at Jun 6, 2025
2029906
Refactor: Mejorar la lógica de actualización de productos y manejo de…
Rodrig0at Jun 6, 2025
748ca6f
Merge branch 'MBI-1' of https://github.com/CodeAnd-Co/Backend-textile…
Rodrig0at Jun 6, 2025
2fc353f
Feature actualizar producto
Hiram10tec Jun 7, 2025
32ba180
Feat: Add product description to product queries
Rodrig0at Jun 7, 2025
13a6cd0
Merge branch 'feature/RA_US29_actualizar-producto' of https://github.…
Rodrig0at Jun 7, 2025
042973c
Merge branch 'MBI-1' of https://github.com/CodeAnd-Co/Backend-textile…
Rodrig0at Jun 7, 2025
8c56bc0
Refactor: ver si hay errores con cosole logs
Rodrig0at Jun 9, 2025
c141880
Merge branch 'MBI-1' of https://github.com/CodeAnd-Co/Backend-textile…
Rodrig0at Jun 9, 2025
ead432f
feat: editar producto sin tener que volver a subir todas las imágenes…
Rodrig0at Jun 12, 2025
bf9471f
feat: implementar función para obtener imágenes de productos por ID
Rodrig0at Jun 12, 2025
f3a3186
refactor: mejorar la estructura de código
Rodrig0at Jun 12, 2025
88db0ba
refactor: mejorar la legibilidad y estructura del código en el contro…
Rodrig0at Jun 12, 2025
b9ea417
refactor: simplificar la carga de imágenes en el controlador de actua…
Rodrig0at Jun 12, 2025
d914535
refactor: unificar estilo de comillas y mejorar formato en rutas de p…
Rodrig0at Jun 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions Clientes/Rutas/RutasIndividuales/consultarSistema.routes.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -63,7 +62,7 @@ ruteador.post(
RUTAS.CLIENTES.CONSULTAR_SISTEMA,
revisarApiKey(),
autorizarToken,
limitePeticionesDiarias,
limitePeticionesDiarias,
verificarPermisos(PERMISOS.CONSULTAR_SISTEMA_ADMINISTRATIVO),
controlador.consultarSistema
);
Expand Down
202 changes: 202 additions & 0 deletions Productos/Controladores/actualizarProducto.controller.js
Original file line number Diff line number Diff line change
@@ -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<void>} 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,
});
}
},
];
2 changes: 1 addition & 1 deletion Productos/Controladores/leerProducto.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ exports.leerProducto = async (req, res) => {
} catch (error) {
return res.status(MENSAJES.ERROR_LEER_PRODUCTO.codigo).json({ mensaje: error.message });
}
};
};
36 changes: 36 additions & 0 deletions Productos/Datos/Repositorios/repositorioActualizarProducto.js
Original file line number Diff line number Diff line change
@@ -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<boolean>} 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;
};
Original file line number Diff line number Diff line change
@@ -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<boolean>} 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;
};
24 changes: 24 additions & 0 deletions Productos/Datos/Repositorios/repositorioObtenerImagenesProducto.js
Original file line number Diff line number Diff line change
@@ -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<Array>} 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 [];
}
};
Loading