feat: Implementación módulos Empresas, Plantas, Tipos y Estados Bobina
Backend API: - Implementado CRUD completo para Empresas (DE001-DE004): - EmpresaRepository, EmpresaService, EmpresasController. - Lógica de creación/eliminación de saldos iniciales en EmpresaService. - Transacciones y registro en tablas _H. - Verificación de permisos específicos. - Implementado CRUD completo para Plantas de Impresión (IP001-IP004): - PlantaRepository, PlantaService, PlantasController. - Transacciones y registro en tablas _H. - Verificación de permisos. - Implementado CRUD completo para Tipos de Bobina (IB006-IB009): - TipoBobinaRepository, TipoBobinaService, TiposBobinaController. - Transacciones y registro en tablas _H. - Verificación de permisos. - Implementado CRUD completo para Estados de Bobina (IB010-IB013): - EstadoBobinaRepository, EstadoBobinaService, EstadosBobinaController. - Transacciones y registro en tablas _H. - Verificación de permisos. Frontend React: - Módulo Empresas: - empresaService.ts para interactuar con la API. - EmpresaFormModal.tsx para crear/editar empresas. - GestionarEmpresasPage.tsx con tabla, filtro, paginación y menú de acciones. - Integración con el hook usePermissions para control de acceso. - Módulo Plantas de Impresión: - plantaService.ts. - PlantaFormModal.tsx. - GestionarPlantasPage.tsx con tabla, filtro, paginación y acciones. - Integración con usePermissions. - Módulo Tipos de Bobina: - tipoBobinaService.ts. - TipoBobinaFormModal.tsx. - GestionarTiposBobinaPage.tsx con tabla, filtro, paginación y acciones. - Integración con usePermissions. - Módulo Estados de Bobina: - estadoBobinaService.ts. - EstadoBobinaFormModal.tsx. - GestionarEstadosBobinaPage.tsx con tabla, filtro, paginación y acciones. - Integración con usePermissions. - Navegación: - Añadidas sub-pestañas y rutas para los nuevos módulos dentro de "Distribución" (Empresas) e "Impresión" (Plantas, Tipos Bobina, Estados Bobina). - Creado ImpresionIndexPage.tsx para la navegación interna del módulo de Impresión. Correcciones: - Corregido el uso de CommitAsync/RollbackAsync a Commit/Rollback síncronos en PlantaService.cs debido a que IDbTransaction no los soporta asíncronamente.
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
using GestionIntegral.Api.Dtos.Impresion; // Para los DTOs
|
||||
using GestionIntegral.Api.Services.Impresion; // Para IPlantaService
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GestionIntegral.Api.Controllers.Impresion
|
||||
{
|
||||
[Route("api/[controller]")] // Ruta base: /api/plantas
|
||||
[ApiController]
|
||||
[Authorize] // Requiere autenticación para todos los endpoints
|
||||
public class PlantasController : ControllerBase
|
||||
{
|
||||
private readonly IPlantaService _plantaService;
|
||||
private readonly ILogger<PlantasController> _logger;
|
||||
|
||||
public PlantasController(IPlantaService plantaService, ILogger<PlantasController> logger)
|
||||
{
|
||||
_plantaService = plantaService ?? throw new ArgumentNullException(nameof(plantaService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
// --- Helper para verificar permisos ---
|
||||
private bool TienePermiso(string codAccRequerido)
|
||||
{
|
||||
if (User.IsInRole("SuperAdmin")) return true;
|
||||
return User.HasClaim(c => c.Type == "permission" && c.Value == codAccRequerido);
|
||||
}
|
||||
|
||||
// --- Helper para obtener User ID ---
|
||||
private int? GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub");
|
||||
if (int.TryParse(userIdClaim, out int userId))
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
_logger.LogWarning("No se pudo obtener el UserId del token JWT en PlantasController.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- Endpoints CRUD ---
|
||||
|
||||
// GET: api/plantas
|
||||
// Permiso: IP001 (Ver Plantas)
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(IEnumerable<PlantaDto>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public async Task<IActionResult> GetAllPlantas([FromQuery] string? nombre, [FromQuery] string? detalle)
|
||||
{
|
||||
if (!TienePermiso("IP001"))
|
||||
{
|
||||
_logger.LogWarning("Acceso denegado a GetAllPlantas para el usuario {UserId}", GetCurrentUserId() ?? 0);
|
||||
return Forbid();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var plantas = await _plantaService.ObtenerTodasAsync(nombre, detalle);
|
||||
return Ok(plantas);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error al obtener todas las Plantas. Filtros: Nombre={Nombre}, Detalle={Detalle}", nombre, detalle);
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, "Error interno al obtener las plantas.");
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/plantas/{id}
|
||||
// Permiso: IP001 (Ver Plantas)
|
||||
[HttpGet("{id:int}", Name = "GetPlantaById")]
|
||||
[ProducesResponseType(typeof(PlantaDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public async Task<IActionResult> GetPlantaById(int id)
|
||||
{
|
||||
if (!TienePermiso("IP001")) return Forbid();
|
||||
|
||||
try
|
||||
{
|
||||
var planta = await _plantaService.ObtenerPorIdAsync(id);
|
||||
if (planta == null)
|
||||
{
|
||||
return NotFound(new { message = $"Planta con ID {id} no encontrada." });
|
||||
}
|
||||
return Ok(planta);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error al obtener Planta por ID: {Id}", id);
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, "Error interno al obtener la planta.");
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/plantas
|
||||
// Permiso: IP002 (Agregar Plantas)
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(PlantaDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public async Task<IActionResult> CreatePlanta([FromBody] CreatePlantaDto createDto)
|
||||
{
|
||||
if (!TienePermiso("IP002")) return Forbid();
|
||||
|
||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||
|
||||
var idUsuario = GetCurrentUserId();
|
||||
if (idUsuario == null) return Unauthorized("No se pudo obtener el ID del usuario del token.");
|
||||
|
||||
try
|
||||
{
|
||||
var (plantaCreada, error) = await _plantaService.CrearAsync(createDto, idUsuario.Value);
|
||||
|
||||
if (error != null) return BadRequest(new { message = error });
|
||||
if (plantaCreada == null) return StatusCode(StatusCodes.Status500InternalServerError, "Error al crear la planta.");
|
||||
|
||||
return CreatedAtRoute("GetPlantaById", new { id = plantaCreada.IdPlanta }, plantaCreada);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error al crear Planta. Nombre: {Nombre} por Usuario ID: {UsuarioId}", createDto.Nombre, idUsuario);
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, "Error interno al crear la planta.");
|
||||
}
|
||||
}
|
||||
|
||||
// PUT: api/plantas/{id}
|
||||
// Permiso: IP003 (Modificar Plantas)
|
||||
[HttpPut("{id:int}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public async Task<IActionResult> UpdatePlanta(int id, [FromBody] UpdatePlantaDto updateDto)
|
||||
{
|
||||
if (!TienePermiso("IP003")) return Forbid();
|
||||
|
||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||
|
||||
var idUsuario = GetCurrentUserId();
|
||||
if (idUsuario == null) return Unauthorized("No se pudo obtener el ID del usuario del token.");
|
||||
|
||||
try
|
||||
{
|
||||
var (exito, error) = await _plantaService.ActualizarAsync(id, updateDto, idUsuario.Value);
|
||||
|
||||
if (!exito)
|
||||
{
|
||||
if (error == "Planta no encontrada.") return NotFound(new { message = error });
|
||||
return BadRequest(new { message = error });
|
||||
}
|
||||
return NoContent();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error al actualizar Planta ID: {Id} por Usuario ID: {UsuarioId}", id, idUsuario);
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, "Error interno al actualizar la planta.");
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE: api/plantas/{id}
|
||||
// Permiso: IP004 (Eliminar Plantas)
|
||||
[HttpDelete("{id:int}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)] // Si está en uso
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public async Task<IActionResult> DeletePlanta(int id)
|
||||
{
|
||||
if (!TienePermiso("IP004")) return Forbid();
|
||||
|
||||
var idUsuario = GetCurrentUserId();
|
||||
if (idUsuario == null) return Unauthorized("No se pudo obtener el ID del usuario del token.");
|
||||
|
||||
try
|
||||
{
|
||||
var (exito, error) = await _plantaService.EliminarAsync(id, idUsuario.Value);
|
||||
|
||||
if (!exito)
|
||||
{
|
||||
if (error == "Planta no encontrada.") return NotFound(new { message = error });
|
||||
return BadRequest(new { message = error }); // Ej: "En uso"
|
||||
}
|
||||
return NoContent();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error al eliminar Planta ID: {Id} por Usuario ID: {UsuarioId}", id, idUsuario);
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, "Error interno al eliminar la planta.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user