Comenzando la implementación final de permisos y depuración. Se sigue...
This commit is contained in:
@@ -38,6 +38,7 @@ namespace GestionIntegral.Api.Controllers.Distribucion
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET: api/publicaciones
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType(typeof(IEnumerable<PublicacionDto>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(IEnumerable<PublicacionDto>), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
@@ -48,6 +49,26 @@ namespace GestionIntegral.Api.Controllers.Distribucion
|
|||||||
return Ok(publicaciones);
|
return Ok(publicaciones);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET: api/publicaciones/dropdown
|
||||||
|
[HttpGet("dropdown")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<PublicacionDropdownDto>), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||||
|
// No se verifica permiso DP001, solo requiere autenticación general ([Authorize] del controlador)
|
||||||
|
public async Task<IActionResult> GetPublicacionesForDropdown([FromQuery] bool soloHabilitadas = true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var publicaciones = await _publicacionService.ObtenerParaDropdownAsync(soloHabilitadas);
|
||||||
|
return Ok(publicaciones);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error al obtener publicaciones para dropdown.");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError, "Error interno al obtener publicaciones para selección.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET: api/publicaciones/{id}
|
||||||
[HttpGet("{id:int}", Name = "GetPublicacionById")]
|
[HttpGet("{id:int}", Name = "GetPublicacionById")]
|
||||||
[ProducesResponseType(typeof(PublicacionDto), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(PublicacionDto), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
@@ -60,6 +81,7 @@ namespace GestionIntegral.Api.Controllers.Distribucion
|
|||||||
return Ok(publicacion);
|
return Ok(publicacion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// POST: api/publicaciones
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ProducesResponseType(typeof(PublicacionDto), StatusCodes.Status201Created)]
|
[ProducesResponseType(typeof(PublicacionDto), StatusCodes.Status201Created)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
@@ -77,6 +99,7 @@ namespace GestionIntegral.Api.Controllers.Distribucion
|
|||||||
return CreatedAtRoute("GetPublicacionById", new { id = dto.IdPublicacion }, dto);
|
return CreatedAtRoute("GetPublicacionById", new { id = dto.IdPublicacion }, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PUT: api/publicaciones/{id}
|
||||||
[HttpPut("{id:int}")]
|
[HttpPut("{id:int}")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
@@ -98,6 +121,7 @@ namespace GestionIntegral.Api.Controllers.Distribucion
|
|||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DELETE: api/publicaciones/{id}
|
||||||
[HttpDelete("{id:int}")]
|
[HttpDelete("{id:int}")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
@@ -118,7 +142,7 @@ namespace GestionIntegral.Api.Controllers.Distribucion
|
|||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Endpoint para obtener las configuraciones de días para una publicación
|
// Endpoint para obtener la configuración de días de publicación para una publicación específica
|
||||||
[HttpGet("{idPublicacion:int}/dias-semana")]
|
[HttpGet("{idPublicacion:int}/dias-semana")]
|
||||||
[ProducesResponseType(typeof(IEnumerable<PublicacionDiaSemanaDto>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(IEnumerable<PublicacionDiaSemanaDto>), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
|
|||||||
@@ -71,6 +71,25 @@ namespace GestionIntegral.Api.Controllers.Impresion
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET: api/plantas/dropdown
|
||||||
|
[HttpGet("dropdown")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<PlantaDropdownDto>), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||||
|
// NO chequeo TienePermiso("IP001")(requiere autenticación)
|
||||||
|
public async Task<IActionResult> GetPlantasForDropdown()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var plantas = await _plantaService.ObtenerParaDropdownAsync();
|
||||||
|
return Ok(plantas);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error al obtener plantas para dropdown.");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError, "Error interno al obtener plantas para selección.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GET: api/plantas/{id}
|
// GET: api/plantas/{id}
|
||||||
// Permiso: IP001 (Ver Plantas)
|
// Permiso: IP001 (Ver Plantas)
|
||||||
[HttpGet("{id:int}", Name = "GetPlantaById")]
|
[HttpGet("{id:int}", Name = "GetPlantaById")]
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace GestionIntegral.Api.Dtos.Distribucion
|
||||||
|
{
|
||||||
|
public class PublicacionDropdownDto
|
||||||
|
{
|
||||||
|
public int IdPublicacion { get; set; }
|
||||||
|
public string Nombre { get; set; } = string.Empty;
|
||||||
|
public bool Habilitada { get; set; } // Simplificamos a bool, el backend manejará el default si es null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace GestionIntegral.Api.Dtos.Impresion
|
||||||
|
{
|
||||||
|
public class PlantaDropdownDto
|
||||||
|
{
|
||||||
|
public int IdPlanta { get; set; }
|
||||||
|
public required string Nombre { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,5 +14,6 @@ namespace GestionIntegral.Api.Services.Distribucion
|
|||||||
Task<IEnumerable<PublicacionDiaSemanaDto>> ObtenerConfiguracionDiasAsync(int idPublicacion);
|
Task<IEnumerable<PublicacionDiaSemanaDto>> ObtenerConfiguracionDiasAsync(int idPublicacion);
|
||||||
Task<IEnumerable<PublicacionDto>> ObtenerPublicacionesPorDiaSemanaAsync(byte diaSemana); // Devolvemos el DTO completo
|
Task<IEnumerable<PublicacionDto>> ObtenerPublicacionesPorDiaSemanaAsync(byte diaSemana); // Devolvemos el DTO completo
|
||||||
Task<(bool Exito, string? Error)> ActualizarConfiguracionDiasAsync(int idPublicacion, UpdatePublicacionDiasSemanaRequestDto requestDto, int idUsuario);
|
Task<(bool Exito, string? Error)> ActualizarConfiguracionDiasAsync(int idPublicacion, UpdatePublicacionDiasSemanaRequestDto requestDto, int idUsuario);
|
||||||
|
Task<IEnumerable<PublicacionDropdownDto>> ObtenerParaDropdownAsync(bool soloHabilitadas = true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,6 +75,22 @@ namespace GestionIntegral.Api.Services.Distribucion
|
|||||||
return MapToDto(data);
|
return MapToDto(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<PublicacionDropdownDto>> ObtenerParaDropdownAsync(bool soloHabilitadas = true)
|
||||||
|
{
|
||||||
|
var data = await _publicacionRepository.GetAllAsync(null, null, soloHabilitadas ? (bool?)true : null);
|
||||||
|
|
||||||
|
return data
|
||||||
|
.Where(p => p.Publicacion != null) // Asegurar que la publicación no sea null
|
||||||
|
.Select(d => new PublicacionDropdownDto
|
||||||
|
{
|
||||||
|
IdPublicacion = d.Publicacion!.IdPublicacion, // Usar ! si estás seguro que no es null después del Where
|
||||||
|
Nombre = d.Publicacion!.Nombre,
|
||||||
|
Habilitada = d.Publicacion!.Habilitada ?? true // Si necesitas filtrar por esto
|
||||||
|
})
|
||||||
|
.OrderBy(p => p.Nombre)
|
||||||
|
.ToList(); // O ToListAsync si el método del repo es async y devuelve IQueryable
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<(PublicacionDto? Publicacion, string? Error)> CrearAsync(CreatePublicacionDto createDto, int idUsuario)
|
public async Task<(PublicacionDto? Publicacion, string? Error)> CrearAsync(CreatePublicacionDto createDto, int idUsuario)
|
||||||
{
|
{
|
||||||
if (await _empresaRepository.GetByIdAsync(createDto.IdEmpresa) == null)
|
if (await _empresaRepository.GetByIdAsync(createDto.IdEmpresa) == null)
|
||||||
|
|||||||
@@ -11,5 +11,6 @@ namespace GestionIntegral.Api.Services.Impresion
|
|||||||
Task<(PlantaDto? Planta, string? Error)> CrearAsync(CreatePlantaDto createDto, int idUsuario);
|
Task<(PlantaDto? Planta, string? Error)> CrearAsync(CreatePlantaDto createDto, int idUsuario);
|
||||||
Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdatePlantaDto updateDto, int idUsuario);
|
Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdatePlantaDto updateDto, int idUsuario);
|
||||||
Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario);
|
Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario);
|
||||||
|
Task<IEnumerable<PlantaDropdownDto>> ObtenerParaDropdownAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,8 +114,8 @@ namespace GestionIntegral.Api.Services.Impresion
|
|||||||
};
|
};
|
||||||
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
using var connection = _connectionFactory.CreateConnection();
|
||||||
if (connection is System.Data.Common.DbConnection dbConnection) { await dbConnection.OpenAsync(); } else { connection.Open(); }
|
if (connection is System.Data.Common.DbConnection dbConnection) { await dbConnection.OpenAsync(); } else { connection.Open(); }
|
||||||
using var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
|
using var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -133,14 +133,14 @@ namespace GestionIntegral.Api.Services.Impresion
|
|||||||
}
|
}
|
||||||
catch (KeyNotFoundException knfex) // Captura específica si el repo la lanza
|
catch (KeyNotFoundException knfex) // Captura específica si el repo la lanza
|
||||||
{
|
{
|
||||||
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en ActualizarAsync Planta."); }
|
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en ActualizarAsync Planta."); }
|
||||||
_logger.LogWarning(knfex, "Intento de actualizar Planta ID: {Id} no encontrada.", id);
|
_logger.LogWarning(knfex, "Intento de actualizar Planta ID: {Id} no encontrada.", id);
|
||||||
return (false, "Planta no encontrada.");
|
return (false, "Planta no encontrada.");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en ActualizarAsync Planta."); }
|
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en ActualizarAsync Planta."); }
|
||||||
_logger.LogError(ex, "Error en transacción ActualizarAsync para Planta ID: {Id}", id);
|
_logger.LogError(ex, "Error en transacción ActualizarAsync para Planta ID: {Id}", id);
|
||||||
return (false, $"Error interno al actualizar la planta: {ex.Message}");
|
return (false, $"Error interno al actualizar la planta: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,33 +153,42 @@ namespace GestionIntegral.Api.Services.Impresion
|
|||||||
}
|
}
|
||||||
|
|
||||||
using var connection = _connectionFactory.CreateConnection();
|
using var connection = _connectionFactory.CreateConnection();
|
||||||
if (connection is System.Data.Common.DbConnection dbConnection) { await dbConnection.OpenAsync(); } else { connection.Open(); }
|
if (connection is System.Data.Common.DbConnection dbConnection) { await dbConnection.OpenAsync(); } else { connection.Open(); }
|
||||||
using var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
|
using var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var eliminado = await _plantaRepository.DeleteAsync(id, idUsuario, transaction);
|
var eliminado = await _plantaRepository.DeleteAsync(id, idUsuario, transaction);
|
||||||
if (!eliminado)
|
if (!eliminado)
|
||||||
{
|
{
|
||||||
throw new DataException("La operación de eliminación no afectó ninguna fila.");
|
throw new DataException("La operación de eliminación no afectó ninguna fila.");
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction.Commit(); // <--- CORREGIDO: Commit síncrono
|
transaction.Commit(); // <--- CORREGIDO: Commit síncrono
|
||||||
_logger.LogInformation("Planta ID {IdPlanta} eliminada exitosamente por Usuario ID {IdUsuario}.", id, idUsuario);
|
_logger.LogInformation("Planta ID {IdPlanta} eliminada exitosamente por Usuario ID {IdUsuario}.", id, idUsuario);
|
||||||
return (true, null);
|
return (true, null);
|
||||||
}
|
}
|
||||||
catch (KeyNotFoundException knfex) // Captura específica si el repo la lanza
|
catch (KeyNotFoundException knfex) // Captura específica si el repo la lanza
|
||||||
{
|
{
|
||||||
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en EliminarAsync Planta."); }
|
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en EliminarAsync Planta."); }
|
||||||
_logger.LogWarning(knfex, "Intento de eliminar Planta ID: {Id} no encontrada.", id);
|
_logger.LogWarning(knfex, "Intento de eliminar Planta ID: {Id} no encontrada.", id);
|
||||||
return (false, "Planta no encontrada.");
|
return (false, "Planta no encontrada.");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en EliminarAsync Planta."); }
|
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en EliminarAsync Planta."); }
|
||||||
_logger.LogError(ex, "Error en transacción EliminarAsync para Planta ID: {Id}", id);
|
_logger.LogError(ex, "Error en transacción EliminarAsync para Planta ID: {Id}", id);
|
||||||
return (false, $"Error interno al eliminar la planta: {ex.Message}");
|
return (false, $"Error interno al eliminar la planta: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<PlantaDropdownDto>> ObtenerParaDropdownAsync()
|
||||||
|
{
|
||||||
|
var plantas = await _plantaRepository.GetAllAsync(null, null);
|
||||||
|
return plantas
|
||||||
|
.OrderBy(p => p.Nombre)
|
||||||
|
.Select(p => new PlantaDropdownDto { IdPlanta = p.IdPlanta, Nombre = p.Nombre })
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("GestionIntegral.Api")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("GestionIntegral.Api")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+99532b03f191d55f42e738a90b97f9f1e0dc1a9c")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+062cc05fd00a484e43f8b4ff022e53ac49670a78")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("GestionIntegral.Api")]
|
[assembly: System.Reflection.AssemblyProductAttribute("GestionIntegral.Api")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("GestionIntegral.Api")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("GestionIntegral.Api")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"C9goqBDGh4B0L1HpPwpJHjfbRNoIuzqnU7zFMHk1LhM=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["V/5slELlkFDzZ8iiVKV8Jt0Ia8AL5AZxPCWo9apx5lQ=","bxlPVWHR7EivQofjz9PzA8dMpKpZqCfOZ\u002BHD\u002Bf1Ew9Y=","\u002BzMwu5DIAA49kPmSydn2WMzj\u002Bdcf0MC3YakKoR6HwYg=","FUb20tYUiusFv5/KhAPdh2OB4ArUWiGApXbQJdx8tX0=","pTWqrhLBwEeWg1GsRlTKzfOAnT1JEklZ8F1/EYlc1Nk=","Hu0oNH4YYNcbnR5Ts4qd5yzC5j5JbY2kEDXces8V1vs=","TKMARE0bLM2dm9NOqxxWztnuqao5IvCh24TEHCtht6I=","84UEEMEbmmNwHVXD5Iw3dtKHTZC0Zqbk3rIRO\u002BxOq4o=","qfTzsJ\u002B5ilLyrc6EhNm61KkSH37yRi85MtgW1\u002BUD2Vo=","4ayt/JAApEOfr0yjg9szkYMPzSs6x2k3QEwmrK5RZVY=","d0weYwKWe3mH5R2BURuNLkAyytO/viA6zivv9AcIBtQ=","Ssyx6SvSGgWMOzhc9pQpk6f6\u002BmVbKQNKeDJbvVA2tjs=","FSqDybxILZmKXw160ANhj76usnM83geRrbPvJxr89OA=","k3qzLxTWHeeJhAuWKMdta6j24bmJ9BMRMjuFEEVCRu0=","x/sHyso3gy4zVCu3ljpnTYCqu8IGZNRok1JoXiabIP8=","fdI2RZZ9M9QOVHCYU5cE\u002BgVVuT7ssRbMzdXvX8rHofc=","8ePFhqKT0OT9nEg3b5T7COC81U\u002BQBcf\u002BindBGyMy6z0=","/ghcduGmSd1I25YtYli\u002BqxF0xuscxc4cTDkbEC6XYVA=","/a3YEu0oBUeA5Qr2VMdppqLuz4CQPWJt2JfBl2dtUwA=","jEO/q4IO3UFTWxlyFwRr7kbGWcTIiS\u002BClxx3kahX/Fk=","4iYOCKYvhsROdGkA1hINVBejb6r8IkwFj9SNMKub3DM=","CeDswsZIn5a7t\u002BKeHJA222yhFvDVVEW1ky98Xxnxebc=","50j34YXOc950QSqaQBMtgezD3tV5mWWR9c5qZcYQoz4=","W/aX9jIKpjNEVoGrU6RXFOY8SDJVT6XB4Rg4QCaeQkQ=","16IbB\u002B3zYHZvsWbCQK6hBFmKJ6Z28SecBn2jm8R3w8I=","COJtHNQqycTJqXkFv2hhpLUT\u002B/AD4IWyQlmxkUVQPNk=","cp6a5bdvkLnUn3x47KQODzPycnx57RmWO\u002B9q8MuoGQo=","oKZRNhIQRaZrETEa3L6JiwIp0\u002BmjzJo193EWBoCuVUg=","sjwbCAEQX51sEWhYVGBihWUNBxniUKZALVJIGK\u002BYgsk=","A4m4kVcox60bvdkJ1CswoZADAT70WPcs4TAKdpMoUjM=","zSzyOuNcK0NQJLwK8Yg4sH4EflX7RPf65Fl2CZUWIGs=","hUWSz0FL2Jxxt3J4cPvysP9naCA2/Cxeo8sJx8s2hvs="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"C9goqBDGh4B0L1HpPwpJHjfbRNoIuzqnU7zFMHk1LhM=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["V/5slELlkFDzZ8iiVKV8Jt0Ia8AL5AZxPCWo9apx5lQ=","bxlPVWHR7EivQofjz9PzA8dMpKpZqCfOZ\u002BHD\u002Bf1Ew9Y=","\u002BzMwu5DIAA49kPmSydn2WMzj\u002Bdcf0MC3YakKoR6HwYg=","FUb20tYUiusFv5/KhAPdh2OB4ArUWiGApXbQJdx8tX0=","pTWqrhLBwEeWg1GsRlTKzfOAnT1JEklZ8F1/EYlc1Nk=","Hu0oNH4YYNcbnR5Ts4qd5yzC5j5JbY2kEDXces8V1vs=","TKMARE0bLM2dm9NOqxxWztnuqao5IvCh24TEHCtht6I=","84UEEMEbmmNwHVXD5Iw3dtKHTZC0Zqbk3rIRO\u002BxOq4o=","qfTzsJ\u002B5ilLyrc6EhNm61KkSH37yRi85MtgW1\u002BUD2Vo=","4ayt/JAApEOfr0yjg9szkYMPzSs6x2k3QEwmrK5RZVY=","d0weYwKWe3mH5R2BURuNLkAyytO/viA6zivv9AcIBtQ=","Ssyx6SvSGgWMOzhc9pQpk6f6\u002BmVbKQNKeDJbvVA2tjs=","FSqDybxILZmKXw160ANhj76usnM83geRrbPvJxr89OA=","k3qzLxTWHeeJhAuWKMdta6j24bmJ9BMRMjuFEEVCRu0=","x/sHyso3gy4zVCu3ljpnTYCqu8IGZNRok1JoXiabIP8=","fdI2RZZ9M9QOVHCYU5cE\u002BgVVuT7ssRbMzdXvX8rHofc=","8ePFhqKT0OT9nEg3b5T7COC81U\u002BQBcf\u002BindBGyMy6z0=","/ghcduGmSd1I25YtYli\u002BqxF0xuscxc4cTDkbEC6XYVA=","/a3YEu0oBUeA5Qr2VMdppqLuz4CQPWJt2JfBl2dtUwA=","jEO/q4IO3UFTWxlyFwRr7kbGWcTIiS\u002BClxx3kahX/Fk=","4iYOCKYvhsROdGkA1hINVBejb6r8IkwFj9SNMKub3DM=","CeDswsZIn5a7t\u002BKeHJA222yhFvDVVEW1ky98Xxnxebc=","50j34YXOc950QSqaQBMtgezD3tV5mWWR9c5qZcYQoz4=","W/aX9jIKpjNEVoGrU6RXFOY8SDJVT6XB4Rg4QCaeQkQ=","16IbB\u002B3zYHZvsWbCQK6hBFmKJ6Z28SecBn2jm8R3w8I=","COJtHNQqycTJqXkFv2hhpLUT\u002B/AD4IWyQlmxkUVQPNk=","cp6a5bdvkLnUn3x47KQODzPycnx57RmWO\u002B9q8MuoGQo=","oKZRNhIQRaZrETEa3L6JiwIp0\u002BmjzJo193EWBoCuVUg=","sjwbCAEQX51sEWhYVGBihWUNBxniUKZALVJIGK\u002BYgsk=","A4m4kVcox60bvdkJ1CswoZADAT70WPcs4TAKdpMoUjM=","zSzyOuNcK0NQJLwK8Yg4sH4EflX7RPf65Fl2CZUWIGs=","1I2C2FVhJyFRbvyuGXnropbTYN\u002BqpCoTcHfxWbfWF10="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"w3MBbMV9Msh0YEq9AW/8s16bzXJ93T9lMVXKPm/r6es=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["V/5slELlkFDzZ8iiVKV8Jt0Ia8AL5AZxPCWo9apx5lQ=","bxlPVWHR7EivQofjz9PzA8dMpKpZqCfOZ\u002BHD\u002Bf1Ew9Y=","\u002BzMwu5DIAA49kPmSydn2WMzj\u002Bdcf0MC3YakKoR6HwYg=","FUb20tYUiusFv5/KhAPdh2OB4ArUWiGApXbQJdx8tX0=","pTWqrhLBwEeWg1GsRlTKzfOAnT1JEklZ8F1/EYlc1Nk=","Hu0oNH4YYNcbnR5Ts4qd5yzC5j5JbY2kEDXces8V1vs=","TKMARE0bLM2dm9NOqxxWztnuqao5IvCh24TEHCtht6I=","84UEEMEbmmNwHVXD5Iw3dtKHTZC0Zqbk3rIRO\u002BxOq4o=","qfTzsJ\u002B5ilLyrc6EhNm61KkSH37yRi85MtgW1\u002BUD2Vo=","4ayt/JAApEOfr0yjg9szkYMPzSs6x2k3QEwmrK5RZVY=","d0weYwKWe3mH5R2BURuNLkAyytO/viA6zivv9AcIBtQ=","Ssyx6SvSGgWMOzhc9pQpk6f6\u002BmVbKQNKeDJbvVA2tjs=","FSqDybxILZmKXw160ANhj76usnM83geRrbPvJxr89OA=","k3qzLxTWHeeJhAuWKMdta6j24bmJ9BMRMjuFEEVCRu0=","x/sHyso3gy4zVCu3ljpnTYCqu8IGZNRok1JoXiabIP8=","fdI2RZZ9M9QOVHCYU5cE\u002BgVVuT7ssRbMzdXvX8rHofc=","8ePFhqKT0OT9nEg3b5T7COC81U\u002BQBcf\u002BindBGyMy6z0=","/ghcduGmSd1I25YtYli\u002BqxF0xuscxc4cTDkbEC6XYVA=","/a3YEu0oBUeA5Qr2VMdppqLuz4CQPWJt2JfBl2dtUwA=","jEO/q4IO3UFTWxlyFwRr7kbGWcTIiS\u002BClxx3kahX/Fk=","4iYOCKYvhsROdGkA1hINVBejb6r8IkwFj9SNMKub3DM=","CeDswsZIn5a7t\u002BKeHJA222yhFvDVVEW1ky98Xxnxebc=","50j34YXOc950QSqaQBMtgezD3tV5mWWR9c5qZcYQoz4=","W/aX9jIKpjNEVoGrU6RXFOY8SDJVT6XB4Rg4QCaeQkQ=","16IbB\u002B3zYHZvsWbCQK6hBFmKJ6Z28SecBn2jm8R3w8I=","COJtHNQqycTJqXkFv2hhpLUT\u002B/AD4IWyQlmxkUVQPNk=","cp6a5bdvkLnUn3x47KQODzPycnx57RmWO\u002B9q8MuoGQo=","oKZRNhIQRaZrETEa3L6JiwIp0\u002BmjzJo193EWBoCuVUg=","sjwbCAEQX51sEWhYVGBihWUNBxniUKZALVJIGK\u002BYgsk=","A4m4kVcox60bvdkJ1CswoZADAT70WPcs4TAKdpMoUjM=","zSzyOuNcK0NQJLwK8Yg4sH4EflX7RPf65Fl2CZUWIGs=","hUWSz0FL2Jxxt3J4cPvysP9naCA2/Cxeo8sJx8s2hvs="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"w3MBbMV9Msh0YEq9AW/8s16bzXJ93T9lMVXKPm/r6es=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["V/5slELlkFDzZ8iiVKV8Jt0Ia8AL5AZxPCWo9apx5lQ=","bxlPVWHR7EivQofjz9PzA8dMpKpZqCfOZ\u002BHD\u002Bf1Ew9Y=","\u002BzMwu5DIAA49kPmSydn2WMzj\u002Bdcf0MC3YakKoR6HwYg=","FUb20tYUiusFv5/KhAPdh2OB4ArUWiGApXbQJdx8tX0=","pTWqrhLBwEeWg1GsRlTKzfOAnT1JEklZ8F1/EYlc1Nk=","Hu0oNH4YYNcbnR5Ts4qd5yzC5j5JbY2kEDXces8V1vs=","TKMARE0bLM2dm9NOqxxWztnuqao5IvCh24TEHCtht6I=","84UEEMEbmmNwHVXD5Iw3dtKHTZC0Zqbk3rIRO\u002BxOq4o=","qfTzsJ\u002B5ilLyrc6EhNm61KkSH37yRi85MtgW1\u002BUD2Vo=","4ayt/JAApEOfr0yjg9szkYMPzSs6x2k3QEwmrK5RZVY=","d0weYwKWe3mH5R2BURuNLkAyytO/viA6zivv9AcIBtQ=","Ssyx6SvSGgWMOzhc9pQpk6f6\u002BmVbKQNKeDJbvVA2tjs=","FSqDybxILZmKXw160ANhj76usnM83geRrbPvJxr89OA=","k3qzLxTWHeeJhAuWKMdta6j24bmJ9BMRMjuFEEVCRu0=","x/sHyso3gy4zVCu3ljpnTYCqu8IGZNRok1JoXiabIP8=","fdI2RZZ9M9QOVHCYU5cE\u002BgVVuT7ssRbMzdXvX8rHofc=","8ePFhqKT0OT9nEg3b5T7COC81U\u002BQBcf\u002BindBGyMy6z0=","/ghcduGmSd1I25YtYli\u002BqxF0xuscxc4cTDkbEC6XYVA=","/a3YEu0oBUeA5Qr2VMdppqLuz4CQPWJt2JfBl2dtUwA=","jEO/q4IO3UFTWxlyFwRr7kbGWcTIiS\u002BClxx3kahX/Fk=","4iYOCKYvhsROdGkA1hINVBejb6r8IkwFj9SNMKub3DM=","CeDswsZIn5a7t\u002BKeHJA222yhFvDVVEW1ky98Xxnxebc=","50j34YXOc950QSqaQBMtgezD3tV5mWWR9c5qZcYQoz4=","W/aX9jIKpjNEVoGrU6RXFOY8SDJVT6XB4Rg4QCaeQkQ=","16IbB\u002B3zYHZvsWbCQK6hBFmKJ6Z28SecBn2jm8R3w8I=","COJtHNQqycTJqXkFv2hhpLUT\u002B/AD4IWyQlmxkUVQPNk=","cp6a5bdvkLnUn3x47KQODzPycnx57RmWO\u002B9q8MuoGQo=","oKZRNhIQRaZrETEa3L6JiwIp0\u002BmjzJo193EWBoCuVUg=","sjwbCAEQX51sEWhYVGBihWUNBxniUKZALVJIGK\u002BYgsk=","A4m4kVcox60bvdkJ1CswoZADAT70WPcs4TAKdpMoUjM=","zSzyOuNcK0NQJLwK8Yg4sH4EflX7RPf65Fl2CZUWIGs=","1I2C2FVhJyFRbvyuGXnropbTYN\u002BqpCoTcHfxWbfWF10="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export interface PublicacionDropdownDto {
|
||||||
|
idPublicacion: number;
|
||||||
|
nombre: string;
|
||||||
|
}
|
||||||
4
Frontend/src/models/dtos/Impresion/PlantaDropdownDto.ts
Normal file
4
Frontend/src/models/dtos/Impresion/PlantaDropdownDto.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export interface PlantaDropdownDto {
|
||||||
|
idPlanta: number;
|
||||||
|
nombre: string;
|
||||||
|
}
|
||||||
@@ -51,7 +51,7 @@ const GestionarNotasCDPage: React.FC = () => {
|
|||||||
const [editingNota, setEditingNota] = useState<NotaCreditoDebitoDto | null>(null);
|
const [editingNota, setEditingNota] = useState<NotaCreditoDebitoDto | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(10);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [selectedRow, setSelectedRow] = useState<NotaCreditoDebitoDto | null>(null);
|
const [selectedRow, setSelectedRow] = useState<NotaCreditoDebitoDto | null>(null);
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ const GestionarNotasCDPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[10, 25, 50]} component="div" count={notas.length}
|
rowsPerPageOptions={[25, 50, 100]} component="div" count={notas.length}
|
||||||
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
||||||
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const GestionarPagosDistribuidorPage: React.FC = () => {
|
|||||||
const [editingPago, setEditingPago] = useState<PagoDistribuidorDto | null>(null);
|
const [editingPago, setEditingPago] = useState<PagoDistribuidorDto | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(10);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [selectedRow, setSelectedRow] = useState<PagoDistribuidorDto | null>(null);
|
const [selectedRow, setSelectedRow] = useState<PagoDistribuidorDto | null>(null);
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ const GestionarPagosDistribuidorPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[10, 25, 50]} component="div" count={pagos.length}
|
rowsPerPageOptions={[25, 50, 100]} component="div" count={pagos.length}
|
||||||
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
||||||
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ const GestionarTiposPagoPage: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setRowsPerPage(parseInt(event.target.value, 10));
|
setRowsPerPage(parseInt(event.target.value, 25));
|
||||||
setPage(0);
|
setPage(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ const GestionarTiposPagoPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25]}
|
rowsPerPageOptions={[25, 50, 100]}
|
||||||
component="div"
|
component="div"
|
||||||
count={tiposPago.length}
|
count={tiposPago.length}
|
||||||
rowsPerPage={rowsPerPage}
|
rowsPerPage={rowsPerPage}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ const GestionarCanillitasPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
setLoading(true); setError(null); setApiErrorMessage(null);
|
setLoading(true); setError(null); setApiErrorMessage(null);
|
||||||
try {
|
try {
|
||||||
const legajoNum = filtroLegajo ? parseInt(filtroLegajo, 10) : undefined;
|
const legajoNum = filtroLegajo ? parseInt(filtroLegajo, 25) : undefined;
|
||||||
if (filtroLegajo && isNaN(legajoNum!)) {
|
if (filtroLegajo && isNaN(legajoNum!)) {
|
||||||
setApiErrorMessage("Legajo debe ser un número.");
|
setApiErrorMessage("Legajo debe ser un número.");
|
||||||
setCanillitas([]); // Limpiar resultados si el filtro es inválido
|
setCanillitas([]); // Limpiar resultados si el filtro es inválido
|
||||||
@@ -194,7 +194,7 @@ const GestionarCanillitasPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25]} component="div" count={canillitas.length}
|
rowsPerPageOptions={[25, 50, 100]} component="div" count={canillitas.length}
|
||||||
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
||||||
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const GestionarControlDevolucionesPage: React.FC = () => {
|
|||||||
const [editingControl, setEditingControl] = useState<ControlDevolucionesDto | null>(null);
|
const [editingControl, setEditingControl] = useState<ControlDevolucionesDto | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(10);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [selectedRow, setSelectedRow] = useState<ControlDevolucionesDto | null>(null);
|
const [selectedRow, setSelectedRow] = useState<ControlDevolucionesDto | null>(null);
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ const GestionarControlDevolucionesPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[10, 25, 50]} component="div" count={controles.length}
|
rowsPerPageOptions={[25, 50, 100]} component="div" count={controles.length}
|
||||||
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
||||||
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const GestionarDistribuidoresPage: React.FC = () => {
|
|||||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(5);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [selectedDistribuidorRow, setSelectedDistribuidorRow] = useState<DistribuidorDto | null>(null);
|
const [selectedDistribuidorRow, setSelectedDistribuidorRow] = useState<DistribuidorDto | null>(null);
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@ const GestionarDistribuidoresPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25]} component="div" count={distribuidores.length}
|
rowsPerPageOptions={[25, 50, 100]} component="div" count={distribuidores.length}
|
||||||
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
||||||
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const GestionarEmpresasPage: React.FC = () => {
|
|||||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null); // Para errores del modal (Create/Update/Delete)
|
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null); // Para errores del modal (Create/Update/Delete)
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(5);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
|
|
||||||
// Para el menú contextual de acciones por fila
|
// Para el menú contextual de acciones por fila
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
@@ -227,7 +227,7 @@ const GestionarEmpresasPage: React.FC = () => {
|
|||||||
</Table>
|
</Table>
|
||||||
{/* Paginación */}
|
{/* Paginación */}
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25]}
|
rowsPerPageOptions={[25, 50, 100]}
|
||||||
component="div"
|
component="div"
|
||||||
count={empresas.length}
|
count={empresas.length}
|
||||||
rowsPerPage={rowsPerPage}
|
rowsPerPage={rowsPerPage}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
|
|||||||
const [editingMovimiento, setEditingMovimiento] = useState<EntradaSalidaCanillaDto | null>(null);
|
const [editingMovimiento, setEditingMovimiento] = useState<EntradaSalidaCanillaDto | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(10);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [selectedRow, setSelectedRow] = useState<EntradaSalidaCanillaDto | null>(null);
|
const [selectedRow, setSelectedRow] = useState<EntradaSalidaCanillaDto | null>(null);
|
||||||
const [selectedIdsParaLiquidar, setSelectedIdsParaLiquidar] = useState<Set<number>>(new Set());
|
const [selectedIdsParaLiquidar, setSelectedIdsParaLiquidar] = useState<Set<number>>(new Set());
|
||||||
@@ -452,7 +452,7 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[10, 25, 50, 100]} component="div" count={movimientos.length}
|
rowsPerPageOptions={[25, 50, 100]} component="div" count={movimientos.length}
|
||||||
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
||||||
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const GestionarEntradasSalidasDistPage: React.FC = () => {
|
|||||||
const [editingMovimiento, setEditingMovimiento] = useState<EntradaSalidaDistDto | null>(null);
|
const [editingMovimiento, setEditingMovimiento] = useState<EntradaSalidaDistDto | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(10);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [selectedRow, setSelectedRow] = useState<EntradaSalidaDistDto | null>(null);
|
const [selectedRow, setSelectedRow] = useState<EntradaSalidaDistDto | null>(null);
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ const GestionarEntradasSalidasDistPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[10, 25, 50]} component="div" count={movimientos.length}
|
rowsPerPageOptions={[25, 50, 100]} component="div" count={movimientos.length}
|
||||||
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
||||||
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const GestionarOtrosDestinosPage: React.FC = () => {
|
|||||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(5);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [selectedDestinoRow, setSelectedDestinoRow] = useState<OtroDestinoDto | null>(null);
|
const [selectedDestinoRow, setSelectedDestinoRow] = useState<OtroDestinoDto | null>(null);
|
||||||
@@ -193,7 +193,7 @@ const GestionarOtrosDestinosPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25]}
|
rowsPerPageOptions={[25, 50, 100]}
|
||||||
component="div"
|
component="div"
|
||||||
count={otrosDestinos.length}
|
count={otrosDestinos.length}
|
||||||
rowsPerPage={rowsPerPage}
|
rowsPerPage={rowsPerPage}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const GestionarPublicacionesPage: React.FC = () => {
|
|||||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(5);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [selectedPublicacionRow, setSelectedPublicacionRow] = useState<PublicacionDto | null>(null);
|
const [selectedPublicacionRow, setSelectedPublicacionRow] = useState<PublicacionDto | null>(null);
|
||||||
|
|
||||||
@@ -284,7 +284,7 @@ const GestionarPublicacionesPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25]} component="div" count={publicaciones.length}
|
rowsPerPageOptions={[25, 50, 100]} component="div" count={publicaciones.length}
|
||||||
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
||||||
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const GestionarSalidasOtrosDestinosPage: React.FC = () => {
|
|||||||
const [editingSalida, setEditingSalida] = useState<SalidaOtroDestinoDto | null>(null);
|
const [editingSalida, setEditingSalida] = useState<SalidaOtroDestinoDto | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(10);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [selectedRow, setSelectedRow] = useState<SalidaOtroDestinoDto | null>(null);
|
const [selectedRow, setSelectedRow] = useState<SalidaOtroDestinoDto | null>(null);
|
||||||
|
|
||||||
@@ -224,7 +224,7 @@ const GestionarSalidasOtrosDestinosPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25]} component="div" count={salidas.length}
|
rowsPerPageOptions={[25, 50, 100]} component="div" count={salidas.length}
|
||||||
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
||||||
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const GestionarZonasPage: React.FC = () => {
|
|||||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(5);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [selectedZonaRow, setSelectedZonaRow] = useState<ZonaDto | null>(null);
|
const [selectedZonaRow, setSelectedZonaRow] = useState<ZonaDto | null>(null);
|
||||||
@@ -201,7 +201,7 @@ const GestionarZonasPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25]}
|
rowsPerPageOptions={[25, 50, 100]}
|
||||||
component="div"
|
component="div"
|
||||||
count={zonas.length}
|
count={zonas.length}
|
||||||
rowsPerPage={rowsPerPage}
|
rowsPerPage={rowsPerPage}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const GestionarEstadosBobinaPage: React.FC = () => {
|
|||||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(5);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [selectedEstadoRow, setSelectedEstadoRow] = useState<EstadoBobinaDto | null>(null);
|
const [selectedEstadoRow, setSelectedEstadoRow] = useState<EstadoBobinaDto | null>(null);
|
||||||
@@ -209,7 +209,7 @@ const GestionarEstadosBobinaPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25]}
|
rowsPerPageOptions={[25, 50, 100]}
|
||||||
component="div"
|
component="div"
|
||||||
count={estadosBobina.length}
|
count={estadosBobina.length}
|
||||||
rowsPerPage={rowsPerPage}
|
rowsPerPage={rowsPerPage}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const GestionarPlantasPage: React.FC = () => {
|
|||||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(5);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [selectedPlantaRow, setSelectedPlantaRow] = useState<PlantaDto | null>(null);
|
const [selectedPlantaRow, setSelectedPlantaRow] = useState<PlantaDto | null>(null);
|
||||||
@@ -220,7 +220,7 @@ const GestionarPlantasPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25]}
|
rowsPerPageOptions={[25, 50, 100]}
|
||||||
component="div"
|
component="div"
|
||||||
count={plantas.length}
|
count={plantas.length}
|
||||||
rowsPerPage={rowsPerPage}
|
rowsPerPage={rowsPerPage}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ const GestionarStockBobinasPage: React.FC = () => {
|
|||||||
const [selectedBobina, setSelectedBobina] = useState<StockBobinaDto | null>(null);
|
const [selectedBobina, setSelectedBobina] = useState<StockBobinaDto | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(10);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
|
|
||||||
const { tienePermiso, isSuperAdmin } = usePermissions();
|
const { tienePermiso, isSuperAdmin } = usePermissions();
|
||||||
@@ -258,7 +258,7 @@ const GestionarStockBobinasPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25, 50]} component="div" count={stock.length}
|
rowsPerPageOptions={[25, 50, 100]} component="div" count={stock.length}
|
||||||
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
||||||
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const GestionarTiposBobinaPage: React.FC = () => {
|
|||||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(5);
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [selectedTipoBobinaRow, setSelectedTipoBobinaRow] = useState<TipoBobinaDto | null>(null);
|
const [selectedTipoBobinaRow, setSelectedTipoBobinaRow] = useState<TipoBobinaDto | null>(null);
|
||||||
@@ -208,7 +208,7 @@ const GestionarTiposBobinaPage: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25]}
|
rowsPerPageOptions={[25, 50, 100]}
|
||||||
component="div"
|
component="div"
|
||||||
count={tiposBobina.length}
|
count={tiposBobina.length}
|
||||||
rowsPerPage={rowsPerPage}
|
rowsPerPage={rowsPerPage}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import { Outlet, useNavigate, useLocation } from 'react-router-dom';
|
|||||||
|
|
||||||
// Define las sub-pestañas del módulo Impresión
|
// Define las sub-pestañas del módulo Impresión
|
||||||
const impresionSubModules = [
|
const impresionSubModules = [
|
||||||
{ label: 'Plantas', path: 'plantas' },
|
|
||||||
{ label: 'Tipos Bobina', path: 'tipos-bobina' },
|
|
||||||
{ label: 'Estados Bobina', path: 'estados-bobina' },
|
|
||||||
{ label: 'Stock Bobinas', path: 'stock-bobinas' },
|
{ label: 'Stock Bobinas', path: 'stock-bobinas' },
|
||||||
{ label: 'Tiradas', path: 'tiradas' },
|
{ label: 'Tiradas', path: 'tiradas' },
|
||||||
|
{ label: 'Plantas', path: 'plantas' },
|
||||||
|
{ label: 'Tipos Bobina', path: 'tipos-bobina' },
|
||||||
|
{ label: 'Estados Bobina', path: 'estados-bobina' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const ImpresionIndexPage: React.FC = () => {
|
const ImpresionIndexPage: React.FC = () => {
|
||||||
@@ -17,26 +17,40 @@ const ImpresionIndexPage: React.FC = () => {
|
|||||||
const [selectedSubTab, setSelectedSubTab] = useState<number | false>(false);
|
const [selectedSubTab, setSelectedSubTab] = useState<number | false>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentBasePath = '/impresion'; // Ruta base de este módulo
|
const currentBasePath = '/impresion';
|
||||||
const subPath = location.pathname.startsWith(currentBasePath + '/')
|
const defaultSubPath = 'stock-bobinas'; // Define tu sub-ruta por defecto aquí
|
||||||
? location.pathname.substring(currentBasePath.length + 1)
|
|
||||||
: (location.pathname === currentBasePath ? impresionSubModules[0]?.path : undefined);
|
const pathParts = location.pathname.split('/');
|
||||||
|
const currentSubPathSegment = pathParts[2]; // /impresion -> pathParts[1] es 'impresion', pathParts[2] sería la sub-ruta
|
||||||
const activeTabIndex = impresionSubModules.findIndex(
|
|
||||||
(subModule) => subModule.path === subPath
|
let activeTabIndex = -1;
|
||||||
);
|
|
||||||
|
if (currentSubPathSegment) {
|
||||||
if (activeTabIndex !== -1) {
|
activeTabIndex = impresionSubModules.findIndex(
|
||||||
setSelectedSubTab(activeTabIndex);
|
(subModule) => subModule.path === currentSubPathSegment
|
||||||
} else {
|
);
|
||||||
if (location.pathname === currentBasePath && impresionSubModules.length > 0) {
|
|
||||||
navigate(impresionSubModules[0].path, { replace: true });
|
|
||||||
setSelectedSubTab(0);
|
|
||||||
} else {
|
|
||||||
setSelectedSubTab(false);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, [location.pathname, navigate]);
|
if (activeTabIndex !== -1) {
|
||||||
|
setSelectedSubTab(activeTabIndex);
|
||||||
|
} else {
|
||||||
|
// Si estamos en la ruta base /impresion o una subruta no reconocida
|
||||||
|
if (location.pathname === currentBasePath || (location.pathname.startsWith(currentBasePath) && activeTabIndex === -1) ) {
|
||||||
|
const defaultTabIndex = impresionSubModules.findIndex(sm => sm.path === defaultSubPath);
|
||||||
|
if (defaultTabIndex !== -1) {
|
||||||
|
navigate(`${currentBasePath}/${defaultSubPath}`, { replace: true });
|
||||||
|
setSelectedSubTab(defaultTabIndex);
|
||||||
|
} else if (impresionSubModules.length > 0) { // Fallback al primero si el default no existe
|
||||||
|
navigate(`${currentBasePath}/${impresionSubModules[0].path}`, { replace: true });
|
||||||
|
setSelectedSubTab(0);
|
||||||
|
} else {
|
||||||
|
setSelectedSubTab(false); // No hay sub-módulos
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setSelectedSubTab(false); // No es una ruta del módulo contable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [location.pathname, navigate]);
|
||||||
|
|
||||||
const handleSubTabChange = (_event: React.SyntheticEvent, newValue: number) => {
|
const handleSubTabChange = (_event: React.SyntheticEvent, newValue: number) => {
|
||||||
setSelectedSubTab(newValue);
|
setSelectedSubTab(newValue);
|
||||||
|
|||||||
@@ -3,17 +3,18 @@ import {
|
|||||||
Box, Typography, Paper, CircularProgress, Alert, Button
|
Box, Typography, Paper, CircularProgress, Alert, Button
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { DataGrid, type GridColDef, GridFooterContainer, GridFooter } from '@mui/x-data-grid';
|
import { DataGrid, type GridColDef, GridFooterContainer, GridFooter } from '@mui/x-data-grid';
|
||||||
import type {Theme} from '@mui/material/styles';
|
import type { Theme } from '@mui/material/styles';
|
||||||
import { esES } from '@mui/x-data-grid/locales';
|
import { esES } from '@mui/x-data-grid/locales';
|
||||||
import reportesService from '../../services/Reportes/reportesService';
|
import reportesService from '../../services/Reportes/reportesService';
|
||||||
import type { ComparativaConsumoBobinasDto } from '../../models/dtos/Reportes/ComparativaConsumoBobinasDto';
|
import type { ComparativaConsumoBobinasDto } from '../../models/dtos/Reportes/ComparativaConsumoBobinasDto';
|
||||||
import SeleccionaReporteComparativaConsumoBobinas from './SeleccionaReporteComparativaConsumoBobinas';
|
import SeleccionaReporteComparativaConsumoBobinas from './SeleccionaReporteComparativaConsumoBobinas';
|
||||||
|
import { usePermissions } from '../../hooks/usePermissions';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
// Interfaz extendida para DataGrid
|
// Interfaz extendida para DataGrid
|
||||||
interface ComparativaConsumoBobinasDataGridDto extends ComparativaConsumoBobinasDto {
|
interface ComparativaConsumoBobinasDataGridDto extends ComparativaConsumoBobinasDto {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReporteComparativaConsumoBobinasPage: React.FC = () => {
|
const ReporteComparativaConsumoBobinasPage: React.FC = () => {
|
||||||
@@ -28,9 +29,11 @@ const ReporteComparativaConsumoBobinasPage: React.FC = () => {
|
|||||||
fechaInicioMesB: string; fechaFinMesB: string;
|
fechaInicioMesB: string; fechaFinMesB: string;
|
||||||
idPlanta?: number | null; consolidado: boolean;
|
idPlanta?: number | null; consolidado: boolean;
|
||||||
nombrePlanta?: string;
|
nombrePlanta?: string;
|
||||||
mesA?: string;
|
mesA?: string;
|
||||||
mesB?: string;
|
mesB?: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
const { tienePermiso, isSuperAdmin } = usePermissions();
|
||||||
|
const puedeVerReporte = isSuperAdmin || tienePermiso("RR007");
|
||||||
|
|
||||||
const numberLocaleFormatter = (value: number | null | undefined) =>
|
const numberLocaleFormatter = (value: number | null | undefined) =>
|
||||||
value != null ? Number(value).toLocaleString('es-AR') : '';
|
value != null ? Number(value).toLocaleString('es-AR') : '';
|
||||||
@@ -40,6 +43,11 @@ const ReporteComparativaConsumoBobinasPage: React.FC = () => {
|
|||||||
fechaInicioMesB: string; fechaFinMesB: string;
|
fechaInicioMesB: string; fechaFinMesB: string;
|
||||||
idPlanta?: number | null; consolidado: boolean;
|
idPlanta?: number | null; consolidado: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
if (!puedeVerReporte) {
|
||||||
|
setError("No tiene permiso para generar este reporte.");
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
setApiErrorParams(null);
|
setApiErrorParams(null);
|
||||||
@@ -47,23 +55,23 @@ const ReporteComparativaConsumoBobinasPage: React.FC = () => {
|
|||||||
|
|
||||||
let plantaNombre = "Consolidado";
|
let plantaNombre = "Consolidado";
|
||||||
if (!params.consolidado && params.idPlanta) {
|
if (!params.consolidado && params.idPlanta) {
|
||||||
const plantaService = (await import('../../services/Impresion/plantaService')).default;
|
const plantaService = (await import('../../services/Impresion/plantaService')).default;
|
||||||
const plantaData = await plantaService.getPlantaById(params.idPlanta);
|
const plantaData = await plantaService.getPlantaById(params.idPlanta);
|
||||||
plantaNombre = plantaData?.nombre ?? "N/A";
|
plantaNombre = plantaData?.nombre ?? "N/A";
|
||||||
}
|
}
|
||||||
// Formatear nombres de meses para el PDF
|
// Formatear nombres de meses para el PDF
|
||||||
const formatMonthYear = (dateString: string) => {
|
const formatMonthYear = (dateString: string) => {
|
||||||
const date = new Date(dateString + 'T00:00:00'); // Asegurar que se parsea como local
|
const date = new Date(dateString + 'T00:00:00'); // Asegurar que se parsea como local
|
||||||
return date.toLocaleDateString('es-AR', { month: 'long', year: 'numeric', timeZone: 'UTC' });
|
return date.toLocaleDateString('es-AR', { month: 'long', year: 'numeric', timeZone: 'UTC' });
|
||||||
};
|
};
|
||||||
|
|
||||||
setCurrentParams({
|
setCurrentParams({
|
||||||
...params,
|
...params,
|
||||||
nombrePlanta: plantaNombre,
|
nombrePlanta: plantaNombre,
|
||||||
mesA: formatMonthYear(params.fechaInicioMesA),
|
mesA: formatMonthYear(params.fechaInicioMesA),
|
||||||
mesB: formatMonthYear(params.fechaInicioMesB)
|
mesB: formatMonthYear(params.fechaInicioMesB)
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await reportesService.getComparativaConsumoBobinas(params);
|
const data = await reportesService.getComparativaConsumoBobinas(params);
|
||||||
const dataWithIds = data.map((item, index) => ({ ...item, id: `${item.tipoBobina}-${index}` }));
|
const dataWithIds = data.map((item, index) => ({ ...item, id: `${item.tipoBobina}-${index}` }));
|
||||||
@@ -106,23 +114,23 @@ const ReporteComparativaConsumoBobinasPage: React.FC = () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const totales = dataToExport.reduce((acc, row) => {
|
const totales = dataToExport.reduce((acc, row) => {
|
||||||
acc.cantA += Number(row["Cant. Mes A"]); acc.cantB += Number(row["Cant. Mes B"]);
|
acc.cantA += Number(row["Cant. Mes A"]); acc.cantB += Number(row["Cant. Mes B"]);
|
||||||
acc.difCant += Number(row["Dif. Cant."]); acc.kgA += Number(row["Kg Mes A"]);
|
acc.difCant += Number(row["Dif. Cant."]); acc.kgA += Number(row["Kg Mes A"]);
|
||||||
acc.kgB += Number(row["Kg Mes B"]); acc.difKg += Number(row["Dif. Kg"]);
|
acc.kgB += Number(row["Kg Mes B"]); acc.difKg += Number(row["Dif. Kg"]);
|
||||||
return acc;
|
return acc;
|
||||||
}, { cantA:0, cantB:0, difCant:0, kgA:0, kgB:0, difKg:0 });
|
}, { cantA: 0, cantB: 0, difCant: 0, kgA: 0, kgB: 0, difKg: 0 });
|
||||||
|
|
||||||
dataToExport.push({
|
dataToExport.push({
|
||||||
"Tipo Bobina": "TOTALES", "Cant. Mes A": totales.cantA, "Cant. Mes B": totales.cantB,
|
"Tipo Bobina": "TOTALES", "Cant. Mes A": totales.cantA, "Cant. Mes B": totales.cantB,
|
||||||
"Dif. Cant.": totales.difCant, "Kg Mes A": totales.kgA, "Kg Mes B": totales.kgB,
|
"Dif. Cant.": totales.difCant, "Kg Mes A": totales.kgA, "Kg Mes B": totales.kgB,
|
||||||
"Dif. Kg": totales.difKg,
|
"Dif. Kg": totales.difKg,
|
||||||
});
|
});
|
||||||
|
|
||||||
const ws = XLSX.utils.json_to_sheet(dataToExport);
|
const ws = XLSX.utils.json_to_sheet(dataToExport);
|
||||||
const headers = Object.keys(dataToExport[0] || {});
|
const headers = Object.keys(dataToExport[0] || {});
|
||||||
ws['!cols'] = headers.map(h => {
|
ws['!cols'] = headers.map(h => {
|
||||||
const maxLen = Math.max(...dataToExport.map(row => (row as any)[h]?.toString().length ?? 0), h.length);
|
const maxLen = Math.max(...dataToExport.map(row => (row as any)[h]?.toString().length ?? 0), h.length);
|
||||||
return { wch: maxLen + 2 };
|
return { wch: maxLen + 2 };
|
||||||
});
|
});
|
||||||
ws['!freeze'] = { xSplit: 0, ySplit: 1 };
|
ws['!freeze'] = { xSplit: 0, ySplit: 1 };
|
||||||
const wb = XLSX.utils.book_new();
|
const wb = XLSX.utils.book_new();
|
||||||
@@ -192,88 +200,91 @@ const ReporteComparativaConsumoBobinasPage: React.FC = () => {
|
|||||||
if (!totalesGenerales) return null;
|
if (!totalesGenerales) return null;
|
||||||
|
|
||||||
const getCellStyle = (field: (typeof columns)[number]['field'] | 'label', isLabel: boolean = false) => {
|
const getCellStyle = (field: (typeof columns)[number]['field'] | 'label', isLabel: boolean = false) => {
|
||||||
const colConfig = columns.find(c => c.field === field);
|
const colConfig = columns.find(c => c.field === field);
|
||||||
// Ajustar anchos para los totales para que sean más compactos
|
// Ajustar anchos para los totales para que sean más compactos
|
||||||
let targetWidth: number | string = 'auto';
|
let targetWidth: number | string = 'auto';
|
||||||
let targetMinWidth: number | string = 'auto';
|
let targetMinWidth: number | string = 'auto';
|
||||||
|
|
||||||
if (isLabel) {
|
if (isLabel) {
|
||||||
targetWidth = colConfig?.width ? Math.max(80, colConfig.width * 0.5) : 100; // Más corto para "TOTALES:"
|
targetWidth = colConfig?.width ? Math.max(80, colConfig.width * 0.5) : 100; // Más corto para "TOTALES:"
|
||||||
targetMinWidth = 80;
|
targetMinWidth = 80;
|
||||||
} else if (colConfig) {
|
} else if (colConfig) {
|
||||||
targetWidth = colConfig.width ? Math.max(70, colConfig.width * 0.75) : 90; // 75% del ancho de columna, mínimo 70
|
targetWidth = colConfig.width ? Math.max(70, colConfig.width * 0.75) : 90; // 75% del ancho de columna, mínimo 70
|
||||||
targetMinWidth = 70;
|
targetMinWidth = 70;
|
||||||
}
|
}
|
||||||
|
|
||||||
const style: React.CSSProperties = {
|
|
||||||
minWidth: targetMinWidth,
|
|
||||||
width: targetWidth,
|
|
||||||
textAlign: isLabel ? 'left' : (colConfig?.align || 'right') as 'left' | 'right' | 'center',
|
|
||||||
paddingRight: isLabel ? 1 : (field === 'diferenciaKilosUtilizados' ? 0 : 1), // pr en theme units
|
|
||||||
fontWeight: 'bold',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Aplicar el separador si es la columna 'kilosUtilizadosMesA'
|
const style: React.CSSProperties = {
|
||||||
if (field === 'kilosUtilizadosMesA') {
|
minWidth: targetMinWidth,
|
||||||
style.borderLeft = `2px solid grey`; // O theme.palette.divider
|
width: targetWidth,
|
||||||
style.paddingLeft = '8px'; // Espacio después del separador
|
textAlign: isLabel ? 'left' : (colConfig?.align || 'right') as 'left' | 'right' | 'center',
|
||||||
}
|
paddingRight: isLabel ? 1 : (field === 'diferenciaKilosUtilizados' ? 0 : 1), // pr en theme units
|
||||||
return style;
|
fontWeight: 'bold',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Aplicar el separador si es la columna 'kilosUtilizadosMesA'
|
||||||
|
if (field === 'kilosUtilizadosMesA') {
|
||||||
|
style.borderLeft = `2px solid grey`; // O theme.palette.divider
|
||||||
|
style.paddingLeft = '8px'; // Espacio después del separador
|
||||||
|
}
|
||||||
|
return style;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridFooterContainer sx={{
|
<GridFooterContainer sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
borderTop: (theme) => `1px solid ${theme.palette.divider}`,
|
borderTop: (theme) => `1px solid ${theme.palette.divider}`,
|
||||||
minHeight: '52px',
|
minHeight: '52px',
|
||||||
|
}}>
|
||||||
|
<Box sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
flexShrink: 0,
|
||||||
|
overflow: 'hidden',
|
||||||
|
px: 1,
|
||||||
}}>
|
}}>
|
||||||
<Box sx={{
|
<GridFooter
|
||||||
display: 'flex',
|
sx={{
|
||||||
alignItems: 'center',
|
borderTop: 'none',
|
||||||
flexShrink: 0,
|
width: 'auto',
|
||||||
overflow: 'hidden',
|
'& .MuiToolbar-root': {
|
||||||
px:1,
|
paddingLeft: 0,
|
||||||
}}>
|
paddingRight: 0,
|
||||||
<GridFooter
|
},
|
||||||
sx={{
|
'& .MuiDataGrid-selectedRowCount': { display: 'none' },
|
||||||
borderTop: 'none',
|
}}
|
||||||
width: 'auto',
|
/>
|
||||||
'& .MuiToolbar-root': {
|
</Box>
|
||||||
paddingLeft: 0,
|
|
||||||
paddingRight: 0,
|
|
||||||
},
|
|
||||||
'& .MuiDataGrid-selectedRowCount': { display: 'none' },
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
overflowX: 'auto',
|
overflowX: 'auto',
|
||||||
px:1,
|
px: 1,
|
||||||
flexShrink: 1,
|
flexShrink: 1,
|
||||||
}}>
|
}}>
|
||||||
<Typography variant="subtitle2" sx={getCellStyle('label', true)}>TOTALES:</Typography>
|
<Typography variant="subtitle2" sx={getCellStyle('label', true)}>TOTALES:</Typography>
|
||||||
<Typography variant="subtitle2" sx={getCellStyle('bobinasUtilizadasMesA')}>{numberLocaleFormatter(totalesGenerales.bobinasUtilizadasMesA)}</Typography>
|
<Typography variant="subtitle2" sx={getCellStyle('bobinasUtilizadasMesA')}>{numberLocaleFormatter(totalesGenerales.bobinasUtilizadasMesA)}</Typography>
|
||||||
<Typography variant="subtitle2" sx={getCellStyle('bobinasUtilizadasMesB')}>{numberLocaleFormatter(totalesGenerales.bobinasUtilizadasMesB)}</Typography>
|
<Typography variant="subtitle2" sx={getCellStyle('bobinasUtilizadasMesB')}>{numberLocaleFormatter(totalesGenerales.bobinasUtilizadasMesB)}</Typography>
|
||||||
<Typography variant="subtitle2" sx={getCellStyle('diferenciaBobinasUtilizadas')}>{numberLocaleFormatter(totalesGenerales.diferenciaBobinasUtilizadas)}</Typography>
|
<Typography variant="subtitle2" sx={getCellStyle('diferenciaBobinasUtilizadas')}>{numberLocaleFormatter(totalesGenerales.diferenciaBobinasUtilizadas)}</Typography>
|
||||||
<Typography variant="subtitle2" sx={getCellStyle('kilosUtilizadosMesA')}>{numberLocaleFormatter(totalesGenerales.kilosUtilizadosMesA)}</Typography>
|
<Typography variant="subtitle2" sx={getCellStyle('kilosUtilizadosMesA')}>{numberLocaleFormatter(totalesGenerales.kilosUtilizadosMesA)}</Typography>
|
||||||
<Typography variant="subtitle2" sx={getCellStyle('kilosUtilizadosMesB')}>{numberLocaleFormatter(totalesGenerales.kilosUtilizadosMesB)}</Typography>
|
<Typography variant="subtitle2" sx={getCellStyle('kilosUtilizadosMesB')}>{numberLocaleFormatter(totalesGenerales.kilosUtilizadosMesB)}</Typography>
|
||||||
<Typography variant="subtitle2" sx={getCellStyle('diferenciaKilosUtilizados')}>{numberLocaleFormatter(totalesGenerales.diferenciaKilosUtilizados)}</Typography>
|
<Typography variant="subtitle2" sx={getCellStyle('diferenciaKilosUtilizados')}>{numberLocaleFormatter(totalesGenerales.diferenciaKilosUtilizados)}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</GridFooterContainer>
|
</GridFooterContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if (showParamSelector) {
|
if (showParamSelector) {
|
||||||
|
if (!loading && !puedeVerReporte) {
|
||||||
|
return <Alert severity="error" sx={{ m: 2 }}>No tiene permiso para acceder a este reporte.</Alert>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
||||||
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
||||||
@@ -293,7 +304,7 @@ const ReporteComparativaConsumoBobinasPage: React.FC = () => {
|
|||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2, flexWrap: 'wrap', gap: 1 }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2, flexWrap: 'wrap', gap: 1 }}>
|
||||||
<Typography variant="h5">Reporte: Comparativa Consumo de Bobinas</Typography>
|
<Typography variant="h5">Reporte: Comparativa Consumo de Bobinas</Typography>
|
||||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||||
<Button onClick={handleGenerarYAbrirPdf} variant="contained" disabled={loadingPdf || reportData.length === 0 || !!error} size="small">
|
<Button onClick={handleGenerarYAbrirPdf} variant="contained" disabled={loadingPdf || reportData.length === 0 || !!error} size="small">
|
||||||
{loadingPdf ? <CircularProgress size={20} color="inherit" /> : "Abrir PDF"}
|
{loadingPdf ? <CircularProgress size={20} color="inherit" /> : "Abrir PDF"}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleExportToExcel} variant="outlined" disabled={reportData.length === 0 || !!error} size="small">
|
<Button onClick={handleExportToExcel} variant="outlined" disabled={reportData.length === 0 || !!error} size="small">
|
||||||
@@ -305,16 +316,16 @@ const ReporteComparativaConsumoBobinasPage: React.FC = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{loading && <Box sx={{ textAlign: 'center', my:2 }}><CircularProgress /></Box>}
|
{loading && <Box sx={{ textAlign: 'center', my: 2 }}><CircularProgress /></Box>}
|
||||||
{error && !loading && <Alert severity="info" sx={{ my: 2 }}>{error}</Alert>}
|
{error && !loading && <Alert severity="info" sx={{ my: 2 }}>{error}</Alert>}
|
||||||
|
|
||||||
{!loading && !error && reportData.length > 0 && (
|
{!loading && !error && reportData.length > 0 && (
|
||||||
<Paper sx={{
|
<Paper sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
mt: 2,
|
mt: 2,
|
||||||
'& .separator-left': {
|
'& .separator-left': {
|
||||||
borderLeft: (theme: Theme) => `2px solid ${theme.palette.divider}`,
|
borderLeft: (theme: Theme) => `2px solid ${theme.palette.divider}`,
|
||||||
},
|
},
|
||||||
}}>
|
}}>
|
||||||
<DataGrid
|
<DataGrid
|
||||||
rows={rows}
|
rows={rows}
|
||||||
@@ -325,16 +336,16 @@ const ReporteComparativaConsumoBobinasPage: React.FC = () => {
|
|||||||
sx={{ height: 'calc(100vh - 300px)' }} // Ajusta esta altura según sea necesario
|
sx={{ height: 'calc(100vh - 300px)' }} // Ajusta esta altura según sea necesario
|
||||||
initialState={{
|
initialState={{
|
||||||
pagination: {
|
pagination: {
|
||||||
paginationModel: { pageSize: 10, page: 0 },
|
paginationModel: { pageSize: 10, page: 0 },
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
pageSizeOptions={[5, 10, 25, 50]}
|
pageSizeOptions={[5, 10, 25, 50]}
|
||||||
disableRowSelectionOnClick
|
disableRowSelectionOnClick
|
||||||
// hideFooterSelectedRowCount // Ya se maneja en CustomFooter
|
// hideFooterSelectedRowCount // Ya se maneja en CustomFooter
|
||||||
/>
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
)}
|
)}
|
||||||
{!loading && !error && reportData.length === 0 && currentParams && (<Typography sx={{mt:2, fontStyle:'italic'}}>No se encontraron datos para los criterios seleccionados.</Typography>)}
|
{!loading && !error && reportData.length === 0 && currentParams && (<Typography sx={{ mt: 2, fontStyle: 'italic' }}>No se encontraron datos para los criterios seleccionados.</Typography>)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { esES } from '@mui/x-data-grid/locales';
|
|||||||
import reportesService from '../../services/Reportes/reportesService';
|
import reportesService from '../../services/Reportes/reportesService';
|
||||||
import type { ConsumoBobinasPublicacionDto } from '../../models/dtos/Reportes/ConsumoBobinasPublicacionDto';
|
import type { ConsumoBobinasPublicacionDto } from '../../models/dtos/Reportes/ConsumoBobinasPublicacionDto';
|
||||||
import SeleccionaReporteConsumoBobinasPublicacion from './SeleccionaReporteConsumoBobinasPublicacion';
|
import SeleccionaReporteConsumoBobinasPublicacion from './SeleccionaReporteConsumoBobinasPublicacion';
|
||||||
|
import { usePermissions } from '../../hooks/usePermissions';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
@@ -27,17 +28,24 @@ const ReporteConsumoBobinasPublicacionPage: React.FC = () => {
|
|||||||
fechaHasta: string;
|
fechaHasta: string;
|
||||||
nombrePublicacion?: string; // Mantenido para nombre de archivo, no afecta al título DataGrid
|
nombrePublicacion?: string; // Mantenido para nombre de archivo, no afecta al título DataGrid
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
const { tienePermiso, isSuperAdmin } = usePermissions();
|
||||||
|
const puedeVerReporte = isSuperAdmin || tienePermiso("RR007");
|
||||||
|
|
||||||
const handleGenerarReporte = useCallback(async (params: {
|
const handleGenerarReporte = useCallback(async (params: {
|
||||||
fechaDesde: string;
|
fechaDesde: string;
|
||||||
fechaHasta: string;
|
fechaHasta: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
if (!puedeVerReporte) {
|
||||||
|
setError("No tiene permiso para generar este reporte.");
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
setApiErrorParams(null);
|
setApiErrorParams(null);
|
||||||
setCurrentParams(params);
|
setCurrentParams(params);
|
||||||
setReportData([]);
|
setReportData([]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await reportesService.getConsumoBobinasPorPublicacion(params);
|
const data = await reportesService.getConsumoBobinasPorPublicacion(params);
|
||||||
const dataWithIds = data.map((item, index) => ({ ...item, id: `${item.nombrePlanta}-${item.nombrePublicacion}-${index}` }));
|
const dataWithIds = data.map((item, index) => ({ ...item, id: `${item.nombrePlanta}-${item.nombrePublicacion}-${index}` }));
|
||||||
@@ -66,7 +74,7 @@ const ReporteConsumoBobinasPublicacionPage: React.FC = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleExportToExcel = useCallback(() => {
|
const handleExportToExcel = useCallback(() => {
|
||||||
if (reportData.length === 0) {
|
if (reportData.length === 0) {
|
||||||
alert("No hay datos para exportar.");
|
alert("No hay datos para exportar.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -76,23 +84,23 @@ const ReporteConsumoBobinasPublicacionPage: React.FC = () => {
|
|||||||
"Total Kilos": rest.totalKilos,
|
"Total Kilos": rest.totalKilos,
|
||||||
"Cantidad Bobinas": rest.cantidadBobinas,
|
"Cantidad Bobinas": rest.cantidadBobinas,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const totalGeneralKilos = reportData.reduce((sum, item) => sum + item.totalKilos, 0);
|
const totalGeneralKilos = reportData.reduce((sum, item) => sum + item.totalKilos, 0);
|
||||||
const totalGeneralBobinas = reportData.reduce((sum, item) => sum + item.cantidadBobinas, 0);
|
const totalGeneralBobinas = reportData.reduce((sum, item) => sum + item.cantidadBobinas, 0);
|
||||||
|
|
||||||
dataToExport.push({
|
dataToExport.push({
|
||||||
"Planta": "TOTAL GENERAL",
|
"Planta": "TOTAL GENERAL",
|
||||||
"Publicación": "", // Celda vacía para alineación
|
"Publicación": "", // Celda vacía para alineación
|
||||||
"Total Kilos": totalGeneralKilos,
|
"Total Kilos": totalGeneralKilos,
|
||||||
"Cantidad Bobinas": totalGeneralBobinas,
|
"Cantidad Bobinas": totalGeneralBobinas,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const ws = XLSX.utils.json_to_sheet(dataToExport);
|
const ws = XLSX.utils.json_to_sheet(dataToExport);
|
||||||
const headers = Object.keys(dataToExport[0] || {});
|
const headers = Object.keys(dataToExport[0] || {});
|
||||||
ws['!cols'] = headers.map(h => {
|
ws['!cols'] = headers.map(h => {
|
||||||
const maxLen = Math.max(...dataToExport.map(row => (row as any)[h]?.toString().length ?? 0), h.length);
|
const maxLen = Math.max(...dataToExport.map(row => (row as any)[h]?.toString().length ?? 0), h.length);
|
||||||
return { wch: maxLen + 2 };
|
return { wch: maxLen + 2 };
|
||||||
});
|
});
|
||||||
ws['!freeze'] = { xSplit: 0, ySplit: 1 };
|
ws['!freeze'] = { xSplit: 0, ySplit: 1 };
|
||||||
|
|
||||||
@@ -157,21 +165,21 @@ const ReporteConsumoBobinasPublicacionPage: React.FC = () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const rows = useMemo(() => reportData, [reportData]);
|
const rows = useMemo(() => reportData, [reportData]);
|
||||||
|
|
||||||
const totalGeneralKilos = useMemo(() =>
|
const totalGeneralKilos = useMemo(() =>
|
||||||
reportData.reduce((sum, item) => sum + item.totalKilos, 0),
|
reportData.reduce((sum, item) => sum + item.totalKilos, 0),
|
||||||
[reportData]);
|
[reportData]);
|
||||||
const totalGeneralBobinas = useMemo(() =>
|
const totalGeneralBobinas = useMemo(() =>
|
||||||
reportData.reduce((sum, item) => sum + item.cantidadBobinas, 0),
|
reportData.reduce((sum, item) => sum + item.cantidadBobinas, 0),
|
||||||
[reportData]);
|
[reportData]);
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
const CustomFooter = () => {
|
const CustomFooter = () => {
|
||||||
// Podrías añadir una condición para no renderizar si no hay datos/totales
|
// Podrías añadir una condición para no renderizar si no hay datos/totales
|
||||||
// if (totalGeneralKilos === 0 && totalGeneralBobinas === 0) return null;
|
// if (totalGeneralKilos === 0 && totalGeneralBobinas === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridFooterContainer sx={{
|
<GridFooterContainer sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between', // Separa la paginación (izquierda) de los totales (derecha)
|
justifyContent: 'space-between', // Separa la paginación (izquierda) de los totales (derecha)
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -179,54 +187,57 @@ const CustomFooter = () => {
|
|||||||
borderTop: (theme) => `1px solid ${theme.palette.divider}`,
|
borderTop: (theme) => `1px solid ${theme.palette.divider}`,
|
||||||
minHeight: '52px', // Altura estándar para el footer
|
minHeight: '52px', // Altura estándar para el footer
|
||||||
// No es necesario p: aquí si los hijos lo manejan o el GridFooterContainer lo aplica por defecto
|
// No es necesario p: aquí si los hijos lo manejan o el GridFooterContainer lo aplica por defecto
|
||||||
}}>
|
}}>
|
||||||
{/* Box para la paginación estándar */}
|
{/* Box para la paginación estándar */}
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
flexShrink: 0, // Evita que este box se encoja si los totales son anchos
|
flexShrink: 0, // Evita que este box se encoja si los totales son anchos
|
||||||
overflow: 'hidden', // Para asegurar que no desborde si el contenido interno es muy ancho
|
overflow: 'hidden', // Para asegurar que no desborde si el contenido interno es muy ancho
|
||||||
px: 1, // Padding horizontal para el contenedor de la paginación
|
px: 1, // Padding horizontal para el contenedor de la paginación
|
||||||
// Considera un flexGrow o un width/maxWidth si necesitas más control sobre el espacio de la paginación
|
// Considera un flexGrow o un width/maxWidth si necesitas más control sobre el espacio de la paginación
|
||||||
// Ejemplo: flexGrow: 1, maxWidth: 'calc(100% - 250px)' (para dejar espacio a los totales)
|
// Ejemplo: flexGrow: 1, maxWidth: 'calc(100% - 250px)' (para dejar espacio a los totales)
|
||||||
}}>
|
}}>
|
||||||
<GridFooter
|
<GridFooter
|
||||||
sx={{
|
sx={{
|
||||||
borderTop: 'none', // Quitar el borde superior del GridFooter interno
|
borderTop: 'none', // Quitar el borde superior del GridFooter interno
|
||||||
width: 'auto', // Permite que el GridFooter se ajuste a su contenido (paginador)
|
width: 'auto', // Permite que el GridFooter se ajuste a su contenido (paginador)
|
||||||
'& .MuiToolbar-root': { // Ajustar padding del toolbar de paginación
|
'& .MuiToolbar-root': { // Ajustar padding del toolbar de paginación
|
||||||
paddingLeft: 0, // O un valor pequeño si es necesario
|
paddingLeft: 0, // O un valor pequeño si es necesario
|
||||||
paddingRight: 0,
|
paddingRight: 0,
|
||||||
},
|
},
|
||||||
// Mantenemos oculto el contador de filas seleccionadas si no lo queremos
|
// Mantenemos oculto el contador de filas seleccionadas si no lo queremos
|
||||||
'& .MuiDataGrid-selectedRowCount': { display: 'none' },
|
'& .MuiDataGrid-selectedRowCount': { display: 'none' },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Box para los totales personalizados */}
|
{/* Box para los totales personalizados */}
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
whiteSpace: 'nowrap', // Evita que los totales hagan salto de línea
|
whiteSpace: 'nowrap', // Evita que los totales hagan salto de línea
|
||||||
overflowX: 'auto', // Scroll DENTRO de este Box si los totales son muy anchos
|
overflowX: 'auto', // Scroll DENTRO de este Box si los totales son muy anchos
|
||||||
px: 2, // Padding horizontal para el contenedor de los totales (ajusta pr:2 de tu ejemplo)
|
px: 2, // Padding horizontal para el contenedor de los totales (ajusta pr:2 de tu ejemplo)
|
||||||
flexShrink: 1, // Permitir que este contenedor se encoja si la paginación necesita más espacio
|
flexShrink: 1, // Permitir que este contenedor se encoja si la paginación necesita más espacio
|
||||||
}}>
|
}}>
|
||||||
<Typography variant="subtitle2" sx={{ mr: 1, fontWeight: 'bold' }}>TOTALES:</Typography>
|
<Typography variant="subtitle2" sx={{ mr: 1, fontWeight: 'bold' }}>TOTALES:</Typography>
|
||||||
<Typography variant="subtitle2" sx={{ mr: 2, minWidth: '80px', textAlign: 'right', fontWeight: 'bold' }}>
|
<Typography variant="subtitle2" sx={{ mr: 2, minWidth: '80px', textAlign: 'right', fontWeight: 'bold' }}>
|
||||||
Kg: {totalGeneralKilos.toLocaleString('es-AR')}
|
Kg: {totalGeneralKilos.toLocaleString('es-AR')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="subtitle2" sx={{ minWidth: '80px', textAlign: 'right', fontWeight: 'bold' }}>
|
<Typography variant="subtitle2" sx={{ minWidth: '80px', textAlign: 'right', fontWeight: 'bold' }}>
|
||||||
Cant: {totalGeneralBobinas.toLocaleString('es-AR')}
|
Cant: {totalGeneralBobinas.toLocaleString('es-AR')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</GridFooterContainer>
|
</GridFooterContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (showParamSelector) {
|
if (showParamSelector) {
|
||||||
|
if (!loading && !puedeVerReporte) {
|
||||||
|
return <Alert severity="error" sx={{ m: 2 }}>No tiene permiso para acceder a este reporte.</Alert>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
||||||
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
||||||
@@ -258,17 +269,17 @@ const CustomFooter = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{loading && <Box sx={{ textAlign: 'center', my:2 }}><CircularProgress /></Box>}
|
{loading && <Box sx={{ textAlign: 'center', my: 2 }}><CircularProgress /></Box>}
|
||||||
{error && !loading && <Alert severity="error" sx={{ my: 2 }}>{error}</Alert>}
|
{error && !loading && <Alert severity="error" sx={{ my: 2 }}>{error}</Alert>}
|
||||||
|
|
||||||
{!loading && !error && reportData.length > 0 && (
|
{!loading && !error && reportData.length > 0 && (
|
||||||
<Paper sx={{
|
<Paper sx={{
|
||||||
height: 'calc(100vh - 380px)', // Ajustar altura para dar espacio al footer
|
height: 'calc(100vh - 380px)', // Ajustar altura para dar espacio al footer
|
||||||
width: '100%',
|
width: '100%',
|
||||||
mt: 2,
|
mt: 2,
|
||||||
'& .MuiDataGrid-footerContainer': { // Asegurar que el contenedor del footer tenga suficiente espacio
|
'& .MuiDataGrid-footerContainer': { // Asegurar que el contenedor del footer tenga suficiente espacio
|
||||||
minHeight: '52px', // o el alto que necesite tu CustomFooter
|
minHeight: '52px', // o el alto que necesite tu CustomFooter
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<DataGrid
|
<DataGrid
|
||||||
rows={rows}
|
rows={rows}
|
||||||
@@ -276,13 +287,13 @@ const CustomFooter = () => {
|
|||||||
localeText={esES.components.MuiDataGrid.defaultProps.localeText}
|
localeText={esES.components.MuiDataGrid.defaultProps.localeText}
|
||||||
slots={{ footer: CustomFooter }}
|
slots={{ footer: CustomFooter }}
|
||||||
density="compact"
|
density="compact"
|
||||||
// pageSizeOptions={[10, 25, 50]} // Descomentar si deseas que el usuario cambie el tamaño de página
|
// pageSizeOptions={[10, 25, 50]} // Descomentar si deseas que el usuario cambie el tamaño de página
|
||||||
// initialState={{
|
// initialState={{
|
||||||
// pagination: {
|
// pagination: {
|
||||||
// paginationModel: { pageSize: 25, page: 0 },
|
// paginationModel: { pageSize: 25, page: 0 },
|
||||||
// },
|
// },
|
||||||
// }}
|
// }}
|
||||||
// autoHeight // Si se usa autoHeight, el `height` del Paper no aplicará scroll a la tabla
|
// autoHeight // Si se usa autoHeight, el `height` del Paper no aplicará scroll a la tabla
|
||||||
/>
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
import reportesService from '../../services/Reportes/reportesService';
|
import reportesService from '../../services/Reportes/reportesService';
|
||||||
import type { ConsumoBobinasSeccionDto } from '../../models/dtos/Reportes/ConsumoBobinasSeccionDto';
|
import type { ConsumoBobinasSeccionDto } from '../../models/dtos/Reportes/ConsumoBobinasSeccionDto';
|
||||||
import SeleccionaReporteConsumoBobinasSeccion from './SeleccionaReporteConsumoBobinasSeccion';
|
import SeleccionaReporteConsumoBobinasSeccion from './SeleccionaReporteConsumoBobinasSeccion';
|
||||||
|
import { usePermissions } from '../../hooks/usePermissions';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
@@ -40,6 +41,8 @@ const ReporteConsumoBobinasSeccionPage: React.FC = () => {
|
|||||||
consolidado: boolean;
|
consolidado: boolean;
|
||||||
nombrePlanta?: string; // Para el PDF
|
nombrePlanta?: string; // Para el PDF
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
const { tienePermiso, isSuperAdmin } = usePermissions();
|
||||||
|
const puedeVerReporte = isSuperAdmin || tienePermiso("RR007");
|
||||||
|
|
||||||
const handleGenerarReporte = useCallback(async (params: {
|
const handleGenerarReporte = useCallback(async (params: {
|
||||||
fechaDesde: string;
|
fechaDesde: string;
|
||||||
@@ -47,6 +50,11 @@ const ReporteConsumoBobinasSeccionPage: React.FC = () => {
|
|||||||
idPlanta?: number | null;
|
idPlanta?: number | null;
|
||||||
consolidado: boolean;
|
consolidado: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
if (!puedeVerReporte) {
|
||||||
|
setError("No tiene permiso para generar este reporte.");
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
setApiErrorParams(null);
|
setApiErrorParams(null);
|
||||||
@@ -54,12 +62,12 @@ const ReporteConsumoBobinasSeccionPage: React.FC = () => {
|
|||||||
|
|
||||||
let plantaNombre = "Consolidado";
|
let plantaNombre = "Consolidado";
|
||||||
if (!params.consolidado && params.idPlanta) {
|
if (!params.consolidado && params.idPlanta) {
|
||||||
const plantaService = (await import('../../services/Impresion/plantaService')).default;
|
const plantaService = (await import('../../services/Impresion/plantaService')).default;
|
||||||
const plantaData = await plantaService.getPlantaById(params.idPlanta);
|
const plantaData = await plantaService.getPlantaById(params.idPlanta);
|
||||||
plantaNombre = plantaData?.nombre ?? "N/A";
|
plantaNombre = plantaData?.nombre ?? "N/A";
|
||||||
}
|
}
|
||||||
setCurrentParams({...params, nombrePlanta: plantaNombre});
|
setCurrentParams({ ...params, nombrePlanta: plantaNombre });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await reportesService.getConsumoBobinasSeccion(params);
|
const data = await reportesService.getConsumoBobinasSeccion(params);
|
||||||
setReportData(data);
|
setReportData(data);
|
||||||
@@ -104,19 +112,19 @@ const ReporteConsumoBobinasSeccionPage: React.FC = () => {
|
|||||||
const totalGeneralKilos = reportData.reduce((sum, item) => sum + item.totalKilos, 0);
|
const totalGeneralKilos = reportData.reduce((sum, item) => sum + item.totalKilos, 0);
|
||||||
|
|
||||||
dataToExport.push({ // Fila de totales
|
dataToExport.push({ // Fila de totales
|
||||||
"Publicación": "TOTAL GENERAL",
|
"Publicación": "TOTAL GENERAL",
|
||||||
"Sección": "",
|
"Sección": "",
|
||||||
"Tipo Bobina": "",
|
"Tipo Bobina": "",
|
||||||
"Cantidad Bobinas": totalGeneralBobinas,
|
"Cantidad Bobinas": totalGeneralBobinas,
|
||||||
"Total Kilos": totalGeneralKilos,
|
"Total Kilos": totalGeneralKilos,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const ws = XLSX.utils.json_to_sheet(dataToExport);
|
const ws = XLSX.utils.json_to_sheet(dataToExport);
|
||||||
const headers = Object.keys(dataToExport[0] || {});
|
const headers = Object.keys(dataToExport[0] || {});
|
||||||
ws['!cols'] = headers.map(h => {
|
ws['!cols'] = headers.map(h => {
|
||||||
const maxLen = Math.max(...dataToExport.map(row => (row as any)[h]?.toString().length ?? 0), h.length);
|
const maxLen = Math.max(...dataToExport.map(row => (row as any)[h]?.toString().length ?? 0), h.length);
|
||||||
return { wch: maxLen + 2 };
|
return { wch: maxLen + 2 };
|
||||||
});
|
});
|
||||||
ws['!freeze'] = { xSplit: 0, ySplit: 1 };
|
ws['!freeze'] = { xSplit: 0, ySplit: 1 };
|
||||||
|
|
||||||
@@ -161,6 +169,9 @@ const ReporteConsumoBobinasSeccionPage: React.FC = () => {
|
|||||||
const totalGeneralKilos = reportData.reduce((sum, item) => sum + item.totalKilos, 0);
|
const totalGeneralKilos = reportData.reduce((sum, item) => sum + item.totalKilos, 0);
|
||||||
|
|
||||||
if (showParamSelector) {
|
if (showParamSelector) {
|
||||||
|
if (!loading && !puedeVerReporte) {
|
||||||
|
return <Alert severity="error" sx={{ m: 2 }}>No tiene permiso para acceder a este reporte.</Alert>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
||||||
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
||||||
@@ -231,11 +242,11 @@ const ReporteConsumoBobinasSeccionPage: React.FC = () => {
|
|||||||
<TableCell align="right">{bob.totalKilos.toLocaleString('es-AR')}</TableCell>
|
<TableCell align="right">{bob.totalKilos.toLocaleString('es-AR')}</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
{/* Fila de totales por sección */}
|
{/* Fila de totales por sección */}
|
||||||
<TableRow sx={{ backgroundColor: 'rgba(0, 0, 0, 0.02)'}}>
|
<TableRow sx={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}>
|
||||||
<TableCell colSpan={3} align="right" sx={{ fontWeight: 'bold', fontStyle:'italic' }}>Total Sección ({secKey}):</TableCell>
|
<TableCell colSpan={3} align="right" sx={{ fontWeight: 'bold', fontStyle: 'italic' }}>Total Sección ({secKey}):</TableCell>
|
||||||
<TableCell align="right" sx={{ fontWeight: 'bold', fontStyle:'italic' }}>{totalBobinasSeccion.toLocaleString('es-AR')}</TableCell>
|
<TableCell align="right" sx={{ fontWeight: 'bold', fontStyle: 'italic' }}>{totalBobinasSeccion.toLocaleString('es-AR')}</TableCell>
|
||||||
<TableCell align="right" sx={{ fontWeight: 'bold', fontStyle:'italic' }}>{totalKilosSeccion.toLocaleString('es-AR')}</TableCell>
|
<TableCell align="right" sx={{ fontWeight: 'bold', fontStyle: 'italic' }}>{totalKilosSeccion.toLocaleString('es-AR')}</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
@@ -244,11 +255,11 @@ const ReporteConsumoBobinasSeccionPage: React.FC = () => {
|
|||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow sx={{backgroundColor: 'grey.300'}}>
|
<TableRow sx={{ backgroundColor: 'grey.300' }}>
|
||||||
<TableCell colSpan={3} align="right" sx={{fontWeight: 'bold', fontSize: '1.1rem'}}>TOTAL GENERAL:</TableCell>
|
<TableCell colSpan={3} align="right" sx={{ fontWeight: 'bold', fontSize: '1.1rem' }}>TOTAL GENERAL:</TableCell>
|
||||||
<TableCell align="right" sx={{fontWeight: 'bold', fontSize: '1.1rem'}}>{totalGeneralBobinas.toLocaleString('es-AR')}</TableCell>
|
<TableCell align="right" sx={{ fontWeight: 'bold', fontSize: '1.1rem' }}>{totalGeneralBobinas.toLocaleString('es-AR')}</TableCell>
|
||||||
<TableCell align="right" sx={{fontWeight: 'bold', fontSize: '1.1rem'}}>{totalGeneralKilos.toLocaleString('es-AR')}</TableCell>
|
<TableCell align="right" sx={{ fontWeight: 'bold', fontSize: '1.1rem' }}>{totalGeneralKilos.toLocaleString('es-AR')}</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableFooter>
|
</TableFooter>
|
||||||
</Table>
|
</Table>
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
import reportesService from '../../services/Reportes/reportesService';
|
import reportesService from '../../services/Reportes/reportesService';
|
||||||
import type { ExistenciaPapelDto } from '../../models/dtos/Reportes/ExistenciaPapelDto';
|
import type { ExistenciaPapelDto } from '../../models/dtos/Reportes/ExistenciaPapelDto';
|
||||||
import SeleccionaReporteExistenciaPapel from './SeleccionaReporteExistenciaPapel';
|
import SeleccionaReporteExistenciaPapel from './SeleccionaReporteExistenciaPapel';
|
||||||
|
import { usePermissions } from '../../hooks/usePermissions';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
@@ -27,6 +28,8 @@ const ReporteExistenciaPapelPage: React.FC = () => {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [apiErrorParams, setApiErrorParams] = useState<string | null>(null);
|
const [apiErrorParams, setApiErrorParams] = useState<string | null>(null);
|
||||||
const [showParamSelector, setShowParamSelector] = useState(true);
|
const [showParamSelector, setShowParamSelector] = useState(true);
|
||||||
|
const { tienePermiso, isSuperAdmin } = usePermissions();
|
||||||
|
const puedeVerReporte = isSuperAdmin || tienePermiso("RR005");
|
||||||
const [currentParams, setCurrentParams] = useState<{
|
const [currentParams, setCurrentParams] = useState<{
|
||||||
fechaDesde: string;
|
fechaDesde: string;
|
||||||
fechaHasta: string;
|
fechaHasta: string;
|
||||||
@@ -40,6 +43,11 @@ const ReporteExistenciaPapelPage: React.FC = () => {
|
|||||||
idPlanta?: number | null;
|
idPlanta?: number | null;
|
||||||
consolidado: boolean;
|
consolidado: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
if (!puedeVerReporte) {
|
||||||
|
setError("No tiene permiso para generar este reporte.");
|
||||||
|
setLoading(false); // Asegurarse que loading no quede en true
|
||||||
|
return;
|
||||||
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
setApiErrorParams(null);
|
setApiErrorParams(null);
|
||||||
@@ -71,83 +79,83 @@ const ReporteExistenciaPapelPage: React.FC = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleExportToExcel = useCallback(() => {
|
const handleExportToExcel = useCallback(() => {
|
||||||
if (reportData.length === 0) {
|
if (reportData.length === 0) {
|
||||||
alert("No hay datos para exportar.");
|
alert("No hay datos para exportar.");
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// 1) Data inicial formateada
|
|
||||||
const dataToExport: Record<string, any>[] = reportData.map(item => {
|
|
||||||
let fechaString = '-';
|
|
||||||
if (item.fechaEstimacionFinStock) {
|
|
||||||
const d = new Date(item.fechaEstimacionFinStock);
|
|
||||||
if (!isNaN(d.getTime())) {
|
|
||||||
fechaString = d.toLocaleDateString('es-AR', { timeZone: 'UTC' });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {
|
|
||||||
"Tipo Bobina": item.tipoBobina,
|
|
||||||
"Bobinas Stock": item.bobinasEnStock ?? 0,
|
|
||||||
"Kg Stock": item.totalKilosEnStock ?? 0,
|
|
||||||
"Consumo Acum. (Kg)": item.consumoAcumulado ?? 0,
|
|
||||||
"Días Disp. (Prom.)": item.promedioDiasDisponibles != null
|
|
||||||
? Math.round(item.promedioDiasDisponibles)
|
|
||||||
: '-',
|
|
||||||
"Fecha Est. Fin Stock": fechaString,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// 2) Cálculo de totales
|
// 1) Data inicial formateada
|
||||||
const totales = dataToExport.reduce(
|
const dataToExport: Record<string, any>[] = reportData.map(item => {
|
||||||
(acc, row) => {
|
let fechaString = '-';
|
||||||
acc.bobinas += Number(row["Bobinas Stock"]);
|
if (item.fechaEstimacionFinStock) {
|
||||||
acc.kilos += Number(row["Kg Stock"]);
|
const d = new Date(item.fechaEstimacionFinStock);
|
||||||
acc.consumo += Number(row["Consumo Acum. (Kg)"]);
|
if (!isNaN(d.getTime())) {
|
||||||
return acc;
|
fechaString = d.toLocaleDateString('es-AR', { timeZone: 'UTC' });
|
||||||
},
|
}
|
||||||
{ bobinas: 0, kilos: 0, consumo: 0 }
|
}
|
||||||
);
|
return {
|
||||||
|
"Tipo Bobina": item.tipoBobina,
|
||||||
|
"Bobinas Stock": item.bobinasEnStock ?? 0,
|
||||||
|
"Kg Stock": item.totalKilosEnStock ?? 0,
|
||||||
|
"Consumo Acum. (Kg)": item.consumoAcumulado ?? 0,
|
||||||
|
"Días Disp. (Prom.)": item.promedioDiasDisponibles != null
|
||||||
|
? Math.round(item.promedioDiasDisponibles)
|
||||||
|
: '-',
|
||||||
|
"Fecha Est. Fin Stock": fechaString,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
// 3) Insertamos la fila de totales
|
// 2) Cálculo de totales
|
||||||
dataToExport.push({
|
const totales = dataToExport.reduce(
|
||||||
"Tipo Bobina": "Totales",
|
(acc, row) => {
|
||||||
"Bobinas Stock": totales.bobinas,
|
acc.bobinas += Number(row["Bobinas Stock"]);
|
||||||
"Kg Stock": totales.kilos,
|
acc.kilos += Number(row["Kg Stock"]);
|
||||||
"Consumo Acum. (Kg)": totales.consumo,
|
acc.consumo += Number(row["Consumo Acum. (Kg)"]);
|
||||||
"Días Disp. (Prom.)": '-', // o lo que prefieras
|
return acc;
|
||||||
"Fecha Est. Fin Stock": '-' // vacío o guión
|
},
|
||||||
});
|
{ bobinas: 0, kilos: 0, consumo: 0 }
|
||||||
|
);
|
||||||
|
|
||||||
// 4) Creamos la hoja
|
// 3) Insertamos la fila de totales
|
||||||
const ws = XLSX.utils.json_to_sheet(dataToExport);
|
dataToExport.push({
|
||||||
|
"Tipo Bobina": "Totales",
|
||||||
|
"Bobinas Stock": totales.bobinas,
|
||||||
|
"Kg Stock": totales.kilos,
|
||||||
|
"Consumo Acum. (Kg)": totales.consumo,
|
||||||
|
"Días Disp. (Prom.)": '-', // o lo que prefieras
|
||||||
|
"Fecha Est. Fin Stock": '-' // vacío o guión
|
||||||
|
});
|
||||||
|
|
||||||
// 5) Auto‐anchos
|
// 4) Creamos la hoja
|
||||||
const headers = Object.keys(dataToExport[0]);
|
const ws = XLSX.utils.json_to_sheet(dataToExport);
|
||||||
ws['!cols'] = headers.map(h => {
|
|
||||||
const maxLen = dataToExport.reduce((prev, row) => {
|
|
||||||
const cell = row[h]?.toString() ?? '';
|
|
||||||
return Math.max(prev, cell.length);
|
|
||||||
}, h.length);
|
|
||||||
return { wch: maxLen + 2 };
|
|
||||||
});
|
|
||||||
|
|
||||||
// 6) Congelamos la primera fila
|
// 5) Auto‐anchos
|
||||||
ws['!freeze'] = { xSplit: 0, ySplit: 1 };
|
const headers = Object.keys(dataToExport[0]);
|
||||||
|
ws['!cols'] = headers.map(h => {
|
||||||
|
const maxLen = dataToExport.reduce((prev, row) => {
|
||||||
|
const cell = row[h]?.toString() ?? '';
|
||||||
|
return Math.max(prev, cell.length);
|
||||||
|
}, h.length);
|
||||||
|
return { wch: maxLen + 2 };
|
||||||
|
});
|
||||||
|
|
||||||
// 7) Libro y guardado
|
// 6) Congelamos la primera fila
|
||||||
const wb = XLSX.utils.book_new();
|
ws['!freeze'] = { xSplit: 0, ySplit: 1 };
|
||||||
XLSX.utils.book_append_sheet(wb, ws, "ExistenciaPapel");
|
|
||||||
|
|
||||||
let fileName = "ReporteExistenciaPapel";
|
// 7) Libro y guardado
|
||||||
if (currentParams) {
|
const wb = XLSX.utils.book_new();
|
||||||
fileName += `_${currentParams.fechaDesde}_a_${currentParams.fechaHasta}`;
|
XLSX.utils.book_append_sheet(wb, ws, "ExistenciaPapel");
|
||||||
if (currentParams.consolidado) fileName += "_Consolidado";
|
|
||||||
else if (currentParams.idPlanta) fileName += `_Planta${currentParams.idPlanta}`;
|
|
||||||
}
|
|
||||||
fileName += ".xlsx";
|
|
||||||
|
|
||||||
XLSX.writeFile(wb, fileName);
|
let fileName = "ReporteExistenciaPapel";
|
||||||
}, [reportData, currentParams]);
|
if (currentParams) {
|
||||||
|
fileName += `_${currentParams.fechaDesde}_a_${currentParams.fechaHasta}`;
|
||||||
|
if (currentParams.consolidado) fileName += "_Consolidado";
|
||||||
|
else if (currentParams.idPlanta) fileName += `_Planta${currentParams.idPlanta}`;
|
||||||
|
}
|
||||||
|
fileName += ".xlsx";
|
||||||
|
|
||||||
|
XLSX.writeFile(wb, fileName);
|
||||||
|
}, [reportData, currentParams]);
|
||||||
|
|
||||||
const handleGenerarYAbrirPdf = useCallback(async () => {
|
const handleGenerarYAbrirPdf = useCallback(async () => {
|
||||||
if (!currentParams) {
|
if (!currentParams) {
|
||||||
@@ -175,6 +183,9 @@ const ReporteExistenciaPapelPage: React.FC = () => {
|
|||||||
}, [currentParams]);
|
}, [currentParams]);
|
||||||
|
|
||||||
if (showParamSelector) {
|
if (showParamSelector) {
|
||||||
|
if (!loading && !puedeVerReporte) {
|
||||||
|
return <Alert severity="error" sx={{ m: 2 }}>No tiene permiso para acceder a este reporte.</Alert>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
||||||
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
||||||
|
|||||||
@@ -5,20 +5,19 @@ import {
|
|||||||
import { DataGrid, type GridColDef, GridFooterContainer, GridFooter } from '@mui/x-data-grid';
|
import { DataGrid, type GridColDef, GridFooterContainer, GridFooter } from '@mui/x-data-grid';
|
||||||
import { esES } from '@mui/x-data-grid/locales';
|
import { esES } from '@mui/x-data-grid/locales';
|
||||||
import reportesService from '../../services/Reportes/reportesService';
|
import reportesService from '../../services/Reportes/reportesService';
|
||||||
// Corregir importaciones de DTOs
|
|
||||||
import type { MovimientoBobinasPorEstadoResponseDto } from '../../models/dtos/Reportes/MovimientoBobinasPorEstadoResponseDto';
|
import type { MovimientoBobinasPorEstadoResponseDto } from '../../models/dtos/Reportes/MovimientoBobinasPorEstadoResponseDto';
|
||||||
import type { MovimientoBobinaEstadoDetalleDto } from '../../models/dtos/Reportes/MovimientoBobinaEstadoDetalleDto';
|
import type { MovimientoBobinaEstadoDetalleDto } from '../../models/dtos/Reportes/MovimientoBobinaEstadoDetalleDto';
|
||||||
import type { MovimientoBobinaEstadoTotalDto } from '../../models/dtos/Reportes/MovimientoBobinaEstadoTotalDto';
|
import type { MovimientoBobinaEstadoTotalDto } from '../../models/dtos/Reportes/MovimientoBobinaEstadoTotalDto';
|
||||||
|
import { usePermissions } from '../../hooks/usePermissions';
|
||||||
import SeleccionaReporteMovimientoBobinasEstado from './SeleccionaReporteMovimientoBobinasEstado';
|
import SeleccionaReporteMovimientoBobinasEstado from './SeleccionaReporteMovimientoBobinasEstado';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
// Interfaces extendidas para DataGrid con 'id'
|
// Interfaces extendidas para DataGrid con 'id'
|
||||||
interface DetalleMovimientoDataGrid extends MovimientoBobinaEstadoDetalleDto { // Usar el DTO correcto
|
interface DetalleMovimientoDataGrid extends MovimientoBobinaEstadoDetalleDto {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
interface TotalPorEstadoDataGrid extends MovimientoBobinaEstadoTotalDto { // Usar el DTO correcto
|
interface TotalPorEstadoDataGrid extends MovimientoBobinaEstadoTotalDto {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,10 +34,12 @@ const ReporteMovimientoBobinasEstadoPage: React.FC = () => {
|
|||||||
idPlanta: number;
|
idPlanta: number;
|
||||||
nombrePlanta?: string;
|
nombrePlanta?: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
const { tienePermiso, isSuperAdmin } = usePermissions();
|
||||||
|
const puedeVerReporte = isSuperAdmin || tienePermiso("RR006");
|
||||||
|
|
||||||
const numberLocaleFormatter = (value: number | null | undefined) =>
|
const numberLocaleFormatter = (value: number | null | undefined) =>
|
||||||
value != null ? Number(value).toLocaleString('es-AR') : '';
|
value != null ? Number(value).toLocaleString('es-AR') : '';
|
||||||
|
|
||||||
const dateLocaleFormatter = (value: string | null | undefined) =>
|
const dateLocaleFormatter = (value: string | null | undefined) =>
|
||||||
value ? new Date(value).toLocaleDateString('es-AR', { timeZone: 'UTC' }) : '-';
|
value ? new Date(value).toLocaleDateString('es-AR', { timeZone: 'UTC' }) : '-';
|
||||||
|
|
||||||
@@ -48,13 +49,18 @@ const ReporteMovimientoBobinasEstadoPage: React.FC = () => {
|
|||||||
fechaHasta: string;
|
fechaHasta: string;
|
||||||
idPlanta: number;
|
idPlanta: number;
|
||||||
}) => {
|
}) => {
|
||||||
|
if (!puedeVerReporte) {
|
||||||
|
setError("No tiene permiso para generar este reporte.");
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
setApiErrorParams(null);
|
setApiErrorParams(null);
|
||||||
setCurrentParams(params);
|
setCurrentParams(params);
|
||||||
try {
|
try {
|
||||||
const data = await reportesService.getMovimientoBobinasEstado(params);
|
const data = await reportesService.getMovimientoBobinasEstado(params);
|
||||||
|
|
||||||
const processedData: MovimientoBobinasPorEstadoResponseDto = {
|
const processedData: MovimientoBobinasPorEstadoResponseDto = {
|
||||||
detalle: data.detalle?.map((item, index) => ({ ...item, id: `detalle-${index}-${item.numeroRemito}-${item.tipoBobina}` })) || [],
|
detalle: data.detalle?.map((item, index) => ({ ...item, id: `detalle-${index}-${item.numeroRemito}-${item.tipoBobina}` })) || [],
|
||||||
totales: data.totales?.map((item, index) => ({ ...item, id: `total-${index}-${item.tipoMovimiento}` })) || []
|
totales: data.totales?.map((item, index) => ({ ...item, id: `total-${index}-${item.tipoMovimiento}` })) || []
|
||||||
@@ -176,35 +182,38 @@ const ReporteMovimientoBobinasEstadoPage: React.FC = () => {
|
|||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
const CustomFooterDetalle = () => (
|
const CustomFooterDetalle = () => (
|
||||||
<GridFooterContainer sx={{
|
<GridFooterContainer sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
borderTop: (theme) => `1px solid ${theme.palette.divider}`,
|
borderTop: (theme) => `1px solid ${theme.palette.divider}`,
|
||||||
minHeight: '52px',
|
minHeight: '52px',
|
||||||
}}>
|
}}>
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
flexGrow:1,
|
flexGrow: 1,
|
||||||
display:'flex',
|
display: 'flex',
|
||||||
justifyContent:'flex-start',
|
justifyContent: 'flex-start',
|
||||||
}}>
|
}}>
|
||||||
<GridFooter
|
<GridFooter
|
||||||
sx={{
|
sx={{
|
||||||
borderTop: 'none',
|
borderTop: 'none',
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
'& .MuiToolbar-root': {
|
'& .MuiToolbar-root': {
|
||||||
paddingLeft: (theme) => theme.spacing(1),
|
paddingLeft: (theme) => theme.spacing(1),
|
||||||
paddingRight: (theme) => theme.spacing(1),
|
paddingRight: (theme) => theme.spacing(1),
|
||||||
},
|
},
|
||||||
'& .MuiDataGrid-selectedRowCount': { display: 'none' },
|
'& .MuiDataGrid-selectedRowCount': { display: 'none' },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</GridFooterContainer>
|
</GridFooterContainer>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
if (showParamSelector) {
|
if (showParamSelector) {
|
||||||
|
if (!loading && !puedeVerReporte) { // Si no tiene permiso Y no está cargando, muestra error
|
||||||
|
return <Alert severity="error" sx={{ m: 2 }}>No tiene permiso para acceder a este reporte.</Alert>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
||||||
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
||||||
@@ -218,7 +227,7 @@ const ReporteMovimientoBobinasEstadoPage: React.FC = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ p: 2 }}>
|
<Box sx={{ p: 2 }}>
|
||||||
@@ -247,12 +256,12 @@ const ReporteMovimientoBobinasEstadoPage: React.FC = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{loading && <Box sx={{ textAlign: 'center', my:2 }}><CircularProgress /></Box>}
|
{loading && <Box sx={{ textAlign: 'center', my: 2 }}><CircularProgress /></Box>}
|
||||||
{error && !loading && <Alert severity="info" sx={{ my: 2 }}>{error}</Alert>}
|
{error && !loading && <Alert severity="info" sx={{ my: 2 }}>{error}</Alert>}
|
||||||
|
|
||||||
{!loading && !error && reportData && (
|
{!loading && !error && reportData && (
|
||||||
<>
|
<>
|
||||||
<Typography variant="h6" gutterBottom sx={{ mt: 3 }}>
|
<Typography variant="h6" gutterBottom sx={{ mt: 3 }}>
|
||||||
Detalle de Movimientos
|
Detalle de Movimientos
|
||||||
</Typography>
|
</Typography>
|
||||||
{rowsDetalle.length > 0 ? (
|
{rowsDetalle.length > 0 ? (
|
||||||
@@ -264,7 +273,7 @@ const ReporteMovimientoBobinasEstadoPage: React.FC = () => {
|
|||||||
density="compact"
|
density="compact"
|
||||||
sx={{ height: 'calc(100vh - 350px)' }}
|
sx={{ height: 'calc(100vh - 350px)' }}
|
||||||
slots={{ footer: CustomFooterDetalle }}
|
slots={{ footer: CustomFooterDetalle }}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
) : (
|
) : (
|
||||||
@@ -287,11 +296,11 @@ const ReporteMovimientoBobinasEstadoPage: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
) : (
|
) : (
|
||||||
<Typography sx={{fontStyle: 'italic'}}>No hay totales por estado para mostrar.</Typography>
|
<Typography sx={{ fontStyle: 'italic' }}>No hay totales por estado para mostrar.</Typography>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{!loading && !error && !reportData && currentParams && (<Typography sx={{mt: 2, fontStyle: 'italic'}}>No se encontraron datos para los criterios seleccionados.</Typography>)}
|
{!loading && !error && !reportData && currentParams && (<Typography sx={{ mt: 2, fontStyle: 'italic' }}>No se encontraron datos para los criterios seleccionados.</Typography>)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { DataGrid, type GridColDef, GridFooterContainer, GridFooter } from '@mui
|
|||||||
import { esES } from '@mui/x-data-grid/locales'; // Para localización
|
import { esES } from '@mui/x-data-grid/locales'; // Para localización
|
||||||
import reportesService from '../../services/Reportes/reportesService';
|
import reportesService from '../../services/Reportes/reportesService';
|
||||||
import type { MovimientoBobinasDto } from '../../models/dtos/Reportes/MovimientoBobinasDto';
|
import type { MovimientoBobinasDto } from '../../models/dtos/Reportes/MovimientoBobinasDto';
|
||||||
|
import { usePermissions } from '../../hooks/usePermissions';
|
||||||
import SeleccionaReporteMovimientoBobinas from './SeleccionaReporteMovimientoBobinas';
|
import SeleccionaReporteMovimientoBobinas from './SeleccionaReporteMovimientoBobinas';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
@@ -16,11 +17,11 @@ interface MovimientoBobinasDataGridDto extends MovimientoBobinasDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ReporteMovimientoBobinasPage: React.FC = () => {
|
const ReporteMovimientoBobinasPage: React.FC = () => {
|
||||||
const [reportData, setReportData] = useState<MovimientoBobinasDataGridDto[]>([]); // Usar el tipo extendido
|
const [reportData, setReportData] = useState<MovimientoBobinasDataGridDto[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false); // Loading para generar reporte
|
||||||
const [loadingPdf, setLoadingPdf] = useState(false);
|
const [loadingPdf, setLoadingPdf] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null); // Error al generar reporte
|
||||||
const [apiErrorParams, setApiErrorParams] = useState<string | null>(null);
|
const [apiErrorParams, setApiErrorParams] = useState<string | null>(null); // Error específico del selector
|
||||||
const [showParamSelector, setShowParamSelector] = useState(true);
|
const [showParamSelector, setShowParamSelector] = useState(true);
|
||||||
const [currentParams, setCurrentParams] = useState<{
|
const [currentParams, setCurrentParams] = useState<{
|
||||||
fechaDesde: string;
|
fechaDesde: string;
|
||||||
@@ -29,6 +30,9 @@ const ReporteMovimientoBobinasPage: React.FC = () => {
|
|||||||
nombrePlanta?: string;
|
nombrePlanta?: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
|
const { tienePermiso, isSuperAdmin } = usePermissions(); // << OBTENER PERMISOS
|
||||||
|
const puedeVerReporte = isSuperAdmin || tienePermiso("RR006"); // << PERMISO ESPECÍFICO
|
||||||
|
|
||||||
const numberLocaleFormatter = (value: number | null | undefined) =>
|
const numberLocaleFormatter = (value: number | null | undefined) =>
|
||||||
value != null ? Number(value).toLocaleString('es-AR') : '';
|
value != null ? Number(value).toLocaleString('es-AR') : '';
|
||||||
|
|
||||||
@@ -37,20 +41,29 @@ const ReporteMovimientoBobinasPage: React.FC = () => {
|
|||||||
fechaHasta: string;
|
fechaHasta: string;
|
||||||
idPlanta: number;
|
idPlanta: number;
|
||||||
}) => {
|
}) => {
|
||||||
|
if (!puedeVerReporte) { // << CHEQUEO DE PERMISO
|
||||||
|
setError("No tiene permiso para generar este reporte.");
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
setApiErrorParams(null);
|
setApiErrorParams(null);
|
||||||
// Opcional: Obtener nombre de la planta
|
|
||||||
// const plantaService = (await import('../../services/Maestros/plantaService')).default;
|
let plantaNombre = `Planta ${params.idPlanta}`; // Default
|
||||||
// const plantaData = await plantaService.getPlantaById(params.idPlanta);
|
try {
|
||||||
// setCurrentParams({...params, nombrePlanta: plantaData?.nombre});
|
const plantaService = (await import('../../services/Impresion/plantaService')).default;
|
||||||
setCurrentParams(params);
|
const plantaData = await plantaService.getPlantaById(params.idPlanta);
|
||||||
|
if (plantaData) plantaNombre = plantaData.nombre;
|
||||||
|
} catch (e) { console.warn("No se pudo obtener nombre de planta para el reporte", e)}
|
||||||
|
|
||||||
|
setCurrentParams({...params, nombrePlanta: plantaNombre});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await reportesService.getMovimientoBobinas(params);
|
const data = await reportesService.getMovimientoBobinas(params);
|
||||||
// Añadir 'id' único a cada fila para DataGrid
|
|
||||||
const dataWithIds = data.map((item, index) => ({
|
const dataWithIds = data.map((item, index) => ({
|
||||||
...item,
|
...item,
|
||||||
id: `${item.tipoBobina}-${index}` // Asumiendo que tipoBobina es único por reporte o combinar con index
|
id: `${item.tipoBobina}-${index}`
|
||||||
}));
|
}));
|
||||||
setReportData(dataWithIds);
|
setReportData(dataWithIds);
|
||||||
if (dataWithIds.length === 0) {
|
if (dataWithIds.length === 0) {
|
||||||
@@ -61,12 +74,12 @@ const ReporteMovimientoBobinasPage: React.FC = () => {
|
|||||||
const message = axios.isAxiosError(err) && err.response?.data?.message
|
const message = axios.isAxiosError(err) && err.response?.data?.message
|
||||||
? err.response.data.message
|
? err.response.data.message
|
||||||
: 'Ocurrió un error al generar el reporte.';
|
: 'Ocurrió un error al generar el reporte.';
|
||||||
setApiErrorParams(message);
|
setApiErrorParams(message); // Error para el selector
|
||||||
setReportData([]);
|
setReportData([]);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [puedeVerReporte]); // << AÑADIR puedeVerReporte
|
||||||
|
|
||||||
const handleVolverAParametros = useCallback(() => {
|
const handleVolverAParametros = useCallback(() => {
|
||||||
setShowParamSelector(true);
|
setShowParamSelector(true);
|
||||||
@@ -291,7 +304,12 @@ const ReporteMovimientoBobinasPage: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --- Lógica de Renderizado Principal ---
|
||||||
|
// Primero, el chequeo de permiso para el selector
|
||||||
if (showParamSelector) {
|
if (showParamSelector) {
|
||||||
|
if (!loading && !puedeVerReporte) { // Si está cargando, no mostrar nada aún
|
||||||
|
return <Alert severity="error" sx={{ m: 2 }}>No tiene permiso para acceder a este reporte.</Alert>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
||||||
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
||||||
@@ -299,13 +317,25 @@ const ReporteMovimientoBobinasPage: React.FC = () => {
|
|||||||
onGenerarReporte={handleGenerarReporte}
|
onGenerarReporte={handleGenerarReporte}
|
||||||
onCancel={handleVolverAParametros}
|
onCancel={handleVolverAParametros}
|
||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
apiErrorMessage={apiErrorParams}
|
apiErrorMessage={apiErrorParams} // Pasar el error del selector
|
||||||
/>
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Si ya pasó el selector y no tiene permiso (ej. cambio de roles mientras ve la pantalla)
|
||||||
|
if (!loading && !puedeVerReporte && !showParamSelector) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ p: 2 }}>
|
||||||
|
<Alert severity="error" sx={{ m: 2 }}>No tiene permiso para ver este reporte.</Alert>
|
||||||
|
<Button onClick={handleVolverAParametros} variant="outlined" color="secondary" size="small">
|
||||||
|
Volver
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ p: 2 }}>
|
<Box sx={{ p: 2 }}>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2, flexWrap: 'wrap', gap: 1 }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2, flexWrap: 'wrap', gap: 1 }}>
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ import { esES } from '@mui/x-data-grid/locales';
|
|||||||
import reportesService from '../../services/Reportes/reportesService';
|
import reportesService from '../../services/Reportes/reportesService';
|
||||||
import type { TiradasPublicacionesSeccionesDto } from '../../models/dtos/Reportes/TiradasPublicacionesSeccionesDto';
|
import type { TiradasPublicacionesSeccionesDto } from '../../models/dtos/Reportes/TiradasPublicacionesSeccionesDto';
|
||||||
import SeleccionaReporteTiradasPublicacionesSecciones from './SeleccionaReporteTiradasPublicacionesSecciones';
|
import SeleccionaReporteTiradasPublicacionesSecciones from './SeleccionaReporteTiradasPublicacionesSecciones';
|
||||||
|
import { usePermissions } from '../../hooks/usePermissions';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
// Interfaz extendida para DataGrid
|
// Interfaz extendida para DataGrid
|
||||||
interface TiradasPublicacionesSeccionesDataGridDto extends TiradasPublicacionesSeccionesDto {
|
interface TiradasPublicacionesSeccionesDataGridDto extends TiradasPublicacionesSeccionesDto {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReporteTiradasPublicacionesSeccionesPage: React.FC = () => {
|
const ReporteTiradasPublicacionesSeccionesPage: React.FC = () => {
|
||||||
@@ -32,17 +33,24 @@ const ReporteTiradasPublicacionesSeccionesPage: React.FC = () => {
|
|||||||
nombrePlanta?: string;
|
nombrePlanta?: string;
|
||||||
mesAnioParaNombreArchivo?: string;
|
mesAnioParaNombreArchivo?: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
const { tienePermiso, isSuperAdmin } = usePermissions();
|
||||||
|
const puedeVerReporte = isSuperAdmin || tienePermiso("RR008");
|
||||||
|
|
||||||
const numberLocaleFormatter = (value: number | null | undefined, fractionDigits: number = 0) =>
|
const numberLocaleFormatter = (value: number | null | undefined, fractionDigits: number = 0) =>
|
||||||
value != null ? Number(value).toLocaleString('es-AR', { minimumFractionDigits: fractionDigits, maximumFractionDigits: fractionDigits }) : '';
|
value != null ? Number(value).toLocaleString('es-AR', { minimumFractionDigits: fractionDigits, maximumFractionDigits: fractionDigits }) : '';
|
||||||
|
|
||||||
const handleGenerarReporte = useCallback(async (params: {
|
const handleGenerarReporte = useCallback(async (params: {
|
||||||
idPublicacion: number;
|
idPublicacion: number;
|
||||||
fechaDesde: string;
|
fechaDesde: string;
|
||||||
fechaHasta: string;
|
fechaHasta: string;
|
||||||
idPlanta?: number | null;
|
idPlanta?: number | null;
|
||||||
consolidado: boolean;
|
consolidado: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
if (!puedeVerReporte) {
|
||||||
|
setError("No tiene permiso para generar este reporte.");
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
setApiErrorParams(null);
|
setApiErrorParams(null);
|
||||||
@@ -52,9 +60,9 @@ consolidado: boolean;
|
|||||||
const pubData = await pubService.getPublicacionById(params.idPublicacion);
|
const pubData = await pubService.getPublicacionById(params.idPublicacion);
|
||||||
let plantaNombre = "Consolidado";
|
let plantaNombre = "Consolidado";
|
||||||
if (!params.consolidado && params.idPlanta) {
|
if (!params.consolidado && params.idPlanta) {
|
||||||
const plantaService = (await import('../../services/Impresion/plantaService')).default;
|
const plantaService = (await import('../../services/Impresion/plantaService')).default;
|
||||||
const plantaData = await plantaService.getPlantaById(params.idPlanta);
|
const plantaData = await plantaService.getPlantaById(params.idPlanta);
|
||||||
plantaNombre = plantaData?.nombre ?? "N/A";
|
plantaNombre = plantaData?.nombre ?? "N/A";
|
||||||
}
|
}
|
||||||
// Formatear mes y año para el título/nombre de archivo
|
// Formatear mes y año para el título/nombre de archivo
|
||||||
// const fechaDesdeObj = new Date(params.fechaDesde + 'T00:00:00'); // Asegurar que es local
|
// const fechaDesdeObj = new Date(params.fechaDesde + 'T00:00:00'); // Asegurar que es local
|
||||||
@@ -65,8 +73,8 @@ consolidado: boolean;
|
|||||||
const mesAnioNombre = `${dateParts[1]}/${dateParts[0]}`;
|
const mesAnioNombre = `${dateParts[1]}/${dateParts[0]}`;
|
||||||
|
|
||||||
|
|
||||||
setCurrentParams({...params, nombrePublicacion: pubData?.nombre, nombrePlanta: plantaNombre, mesAnioParaNombreArchivo: mesAnioNombre});
|
setCurrentParams({ ...params, nombrePublicacion: pubData?.nombre, nombrePlanta: plantaNombre, mesAnioParaNombreArchivo: mesAnioNombre });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await reportesService.getTiradasPublicacionesSecciones(params);
|
const data = await reportesService.getTiradasPublicacionesSecciones(params);
|
||||||
const dataWithIds = data.map((item, index) => ({ ...item, id: `${item.nombreSeccion}-${index}` }));
|
const dataWithIds = data.map((item, index) => ({ ...item, id: `${item.nombreSeccion}-${index}` }));
|
||||||
@@ -109,20 +117,20 @@ consolidado: boolean;
|
|||||||
|
|
||||||
// Totales para Excel
|
// Totales para Excel
|
||||||
const totales = {
|
const totales = {
|
||||||
"Nombre Sección": "Totales",
|
"Nombre Sección": "Totales",
|
||||||
"Páginas Impresas": reportData.reduce((sum, item) => sum + item.totalPaginasImpresas, 0),
|
"Páginas Impresas": reportData.reduce((sum, item) => sum + item.totalPaginasImpresas, 0),
|
||||||
"Total Ediciones": reportData.reduce((sum, item) => sum + item.cantidadTiradas, 0),
|
"Total Ediciones": reportData.reduce((sum, item) => sum + item.cantidadTiradas, 0),
|
||||||
"Pág. Por Edición (Promedio)": reportData.reduce((sum, item) => sum + item.totalPaginasEjemplares, 0), // Suma de promedios para el total, como en el PDF
|
"Pág. Por Edición (Promedio)": reportData.reduce((sum, item) => sum + item.totalPaginasEjemplares, 0), // Suma de promedios para el total, como en el PDF
|
||||||
"Total Ejemplares": reportData.reduce((sum, item) => sum + item.totalEjemplares, 0),
|
"Total Ejemplares": reportData.reduce((sum, item) => sum + item.totalEjemplares, 0),
|
||||||
"Pág. Ejemplar (Promedio)": reportData.reduce((sum, item) => sum + item.promedioPaginasPorEjemplar, 0), // Suma de promedios para el total
|
"Pág. Ejemplar (Promedio)": reportData.reduce((sum, item) => sum + item.promedioPaginasPorEjemplar, 0), // Suma de promedios para el total
|
||||||
};
|
};
|
||||||
dataToExport.push(totales);
|
dataToExport.push(totales);
|
||||||
|
|
||||||
const ws = XLSX.utils.json_to_sheet(dataToExport);
|
const ws = XLSX.utils.json_to_sheet(dataToExport);
|
||||||
const headers = Object.keys(dataToExport[0] || {});
|
const headers = Object.keys(dataToExport[0] || {});
|
||||||
ws['!cols'] = headers.map(h => {
|
ws['!cols'] = headers.map(h => {
|
||||||
const maxLen = Math.max(...dataToExport.map(row => (row as any)[h]?.toString().length ?? 0), h.length);
|
const maxLen = Math.max(...dataToExport.map(row => (row as any)[h]?.toString().length ?? 0), h.length);
|
||||||
return { wch: maxLen + 2 };
|
return { wch: maxLen + 2 };
|
||||||
});
|
});
|
||||||
ws['!freeze'] = { xSplit: 0, ySplit: 1 };
|
ws['!freeze'] = { xSplit: 0, ySplit: 1 };
|
||||||
|
|
||||||
@@ -192,57 +200,61 @@ consolidado: boolean;
|
|||||||
if (!totalesGenerales) return null;
|
if (!totalesGenerales) return null;
|
||||||
|
|
||||||
const getCellStyle = (field: (typeof columns)[number]['field'] | 'label', isLabel: boolean = false) => {
|
const getCellStyle = (field: (typeof columns)[number]['field'] | 'label', isLabel: boolean = false) => {
|
||||||
const colConfig = columns.find(c => c.field === field);
|
const colConfig = columns.find(c => c.field === field);
|
||||||
let targetWidth: number | string = 'auto';
|
let targetWidth: number | string = 'auto';
|
||||||
let targetMinWidth: number | string = 'auto';
|
let targetMinWidth: number | string = 'auto';
|
||||||
|
|
||||||
if (isLabel) {
|
if (isLabel) {
|
||||||
targetWidth = colConfig?.width ? Math.max(100, colConfig.width * 0.6) : 150;
|
targetWidth = colConfig?.width ? Math.max(100, colConfig.width * 0.6) : 150;
|
||||||
targetMinWidth = 100;
|
targetMinWidth = 100;
|
||||||
} else if (colConfig) {
|
} else if (colConfig) {
|
||||||
targetWidth = colConfig.width ? Math.max(80, colConfig.width * 0.8) : 100;
|
targetWidth = colConfig.width ? Math.max(80, colConfig.width * 0.8) : 100;
|
||||||
targetMinWidth = 80;
|
targetMinWidth = 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
minWidth: targetMinWidth,
|
minWidth: targetMinWidth,
|
||||||
width: targetWidth,
|
width: targetWidth,
|
||||||
textAlign: isLabel ? 'left' : (colConfig?.align || 'right') as 'left' | 'right' | 'center',
|
textAlign: isLabel ? 'left' : (colConfig?.align || 'right') as 'left' | 'right' | 'center',
|
||||||
pr: isLabel ? 1 : (field === 'promedioPaginasPorEjemplar' ? 0 : 1),
|
pr: isLabel ? 1 : (field === 'promedioPaginasPorEjemplar' ? 0 : 1),
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridFooterContainer sx={{
|
<GridFooterContainer sx={{
|
||||||
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
|
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
|
||||||
width: '100%', borderTop: (theme) => `1px solid ${theme.palette.divider}`, minHeight: '52px',
|
width: '100%', borderTop: (theme) => `1px solid ${theme.palette.divider}`, minHeight: '52px',
|
||||||
|
}}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', flexShrink: 0, overflow: 'hidden', px: 1 }}>
|
||||||
|
<GridFooter
|
||||||
|
sx={{
|
||||||
|
borderTop: 'none', width: 'auto',
|
||||||
|
'& .MuiToolbar-root': { paddingLeft: 0, paddingRight: 0, },
|
||||||
|
'& .MuiDataGrid-selectedRowCount': { display: 'none' },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{
|
||||||
|
display: 'flex', alignItems: 'center', fontWeight: 'bold',
|
||||||
|
whiteSpace: 'nowrap', overflowX: 'auto', px: 1, flexShrink: 1,
|
||||||
}}>
|
}}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', flexShrink: 0, overflow: 'hidden', px:1 }}>
|
<Typography variant="subtitle2" sx={getCellStyle('label', true)}>Totales</Typography> {/* Cambiado de TOTALES: a Totales */}
|
||||||
<GridFooter
|
<Typography variant="subtitle2" sx={getCellStyle('totalPaginasImpresas')}>{numberLocaleFormatter(totalesGenerales.totalPaginasImpresas)}</Typography>
|
||||||
sx={{ borderTop: 'none', width: 'auto',
|
<Typography variant="subtitle2" sx={getCellStyle('cantidadTiradas')}>{numberLocaleFormatter(totalesGenerales.cantidadTiradas)}</Typography>
|
||||||
'& .MuiToolbar-root': { paddingLeft: 0, paddingRight: 0, },
|
<Typography variant="subtitle2" sx={getCellStyle('totalPaginasEjemplares')}>{numberLocaleFormatter(totalesGenerales.totalPaginasEjemplares)}</Typography>
|
||||||
'& .MuiDataGrid-selectedRowCount': { display: 'none' },
|
<Typography variant="subtitle2" sx={getCellStyle('totalEjemplares')}>{numberLocaleFormatter(totalesGenerales.totalEjemplares)}</Typography>
|
||||||
}}
|
<Typography variant="subtitle2" sx={getCellStyle('promedioPaginasPorEjemplar')}>{numberLocaleFormatter(totalesGenerales.promedioPaginasPorEjemplar)}</Typography>
|
||||||
/>
|
</Box>
|
||||||
</Box>
|
</GridFooterContainer>
|
||||||
<Box sx={{
|
|
||||||
display: 'flex', alignItems: 'center', fontWeight: 'bold',
|
|
||||||
whiteSpace: 'nowrap', overflowX: 'auto', px:1, flexShrink: 1,
|
|
||||||
}}>
|
|
||||||
<Typography variant="subtitle2" sx={getCellStyle('label', true)}>Totales</Typography> {/* Cambiado de TOTALES: a Totales */}
|
|
||||||
<Typography variant="subtitle2" sx={getCellStyle('totalPaginasImpresas')}>{numberLocaleFormatter(totalesGenerales.totalPaginasImpresas)}</Typography>
|
|
||||||
<Typography variant="subtitle2" sx={getCellStyle('cantidadTiradas')}>{numberLocaleFormatter(totalesGenerales.cantidadTiradas)}</Typography>
|
|
||||||
<Typography variant="subtitle2" sx={getCellStyle('totalPaginasEjemplares')}>{numberLocaleFormatter(totalesGenerales.totalPaginasEjemplares)}</Typography>
|
|
||||||
<Typography variant="subtitle2" sx={getCellStyle('totalEjemplares')}>{numberLocaleFormatter(totalesGenerales.totalEjemplares)}</Typography>
|
|
||||||
<Typography variant="subtitle2" sx={getCellStyle('promedioPaginasPorEjemplar')}>{numberLocaleFormatter(totalesGenerales.promedioPaginasPorEjemplar)}</Typography>
|
|
||||||
</Box>
|
|
||||||
</GridFooterContainer>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (showParamSelector) {
|
if (showParamSelector) {
|
||||||
|
if (!loading && !puedeVerReporte) {
|
||||||
|
return <Alert severity="error" sx={{ m: 2 }}>No tiene permiso para acceder a este reporte.</Alert>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
|
||||||
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
|
||||||
@@ -274,12 +286,12 @@ consolidado: boolean;
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="subtitle1" gutterBottom>
|
<Typography variant="subtitle1" gutterBottom>
|
||||||
Publicación: {currentParams?.nombrePublicacion} |
|
Publicación: {currentParams?.nombrePublicacion} |
|
||||||
Planta: {currentParams?.nombrePlanta} |
|
Planta: {currentParams?.nombrePlanta} |
|
||||||
Mes Consultado: {currentParams?.mesAnioParaNombreArchivo}
|
Mes Consultado: {currentParams?.mesAnioParaNombreArchivo}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{loading && <Box sx={{ textAlign: 'center', my:2 }}><CircularProgress /></Box>}
|
{loading && <Box sx={{ textAlign: 'center', my: 2 }}><CircularProgress /></Box>}
|
||||||
{error && !loading && <Alert severity="info" sx={{ my: 2 }}>{error}</Alert>}
|
{error && !loading && <Alert severity="info" sx={{ my: 2 }}>{error}</Alert>}
|
||||||
|
|
||||||
{!loading && !error && reportData.length > 0 && (
|
{!loading && !error && reportData.length > 0 && (
|
||||||
@@ -293,15 +305,15 @@ consolidado: boolean;
|
|||||||
sx={{ height: 'calc(100vh - 320px)' }} // Ajustar altura según sea necesario
|
sx={{ height: 'calc(100vh - 320px)' }} // Ajustar altura según sea necesario
|
||||||
initialState={{
|
initialState={{
|
||||||
pagination: {
|
pagination: {
|
||||||
paginationModel: { pageSize: 100, page: 0 },
|
paginationModel: { pageSize: 100, page: 0 },
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
pageSizeOptions={[25, 50 , 100]}
|
pageSizeOptions={[25, 50, 100]}
|
||||||
disableRowSelectionOnClick
|
disableRowSelectionOnClick
|
||||||
/>
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
)}
|
)}
|
||||||
{!loading && !error && reportData.length === 0 && currentParams && (<Typography sx={{mt:2, fontStyle:'italic'}}>No se encontraron datos para los criterios seleccionados.</Typography>)}
|
{!loading && !error && reportData.length === 0 && currentParams && (<Typography sx={{ mt: 2, fontStyle: 'italic' }}>No se encontraron datos para los criterios seleccionados.</Typography>)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
Box, Typography, TextField, Button, CircularProgress, Alert,
|
Box, Typography, TextField, Button, CircularProgress, Alert,
|
||||||
FormControl, InputLabel, Select, MenuItem, FormControlLabel, Checkbox
|
FormControl, InputLabel, Select, MenuItem, FormControlLabel, Checkbox
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import type { PlantaDto } from '../../models/dtos/Impresion/PlantaDto';
|
import type { PlantaDropdownDto } from '../../models/dtos/Impresion/PlantaDropdownDto';
|
||||||
import plantaService from '../../services/Impresion/plantaService';
|
import plantaService from '../../services/Impresion/plantaService';
|
||||||
|
|
||||||
interface SeleccionaReporteComparativaConsumoBobinasProps {
|
interface SeleccionaReporteComparativaConsumoBobinasProps {
|
||||||
@@ -35,7 +35,7 @@ const SeleccionaReporteComparativaConsumoBobinas: React.FC<SeleccionaReporteComp
|
|||||||
const [idPlanta, setIdPlanta] = useState<number | string>('');
|
const [idPlanta, setIdPlanta] = useState<number | string>('');
|
||||||
const [consolidado, setConsolidado] = useState<boolean>(false);
|
const [consolidado, setConsolidado] = useState<boolean>(false);
|
||||||
|
|
||||||
const [plantas, setPlantas] = useState<PlantaDto[]>([]);
|
const [plantas, setPlantas] = useState<PlantaDropdownDto[]>([]);
|
||||||
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
||||||
const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({});
|
const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({});
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ const SeleccionaReporteComparativaConsumoBobinas: React.FC<SeleccionaReporteComp
|
|||||||
const fetchPlantas = async () => {
|
const fetchPlantas = async () => {
|
||||||
setLoadingDropdowns(true);
|
setLoadingDropdowns(true);
|
||||||
try {
|
try {
|
||||||
const plantasData = await plantaService.getAllPlantas();
|
const plantasData = await plantaService.getPlantasForDropdown();
|
||||||
setPlantas(plantasData);
|
setPlantas(plantasData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error al cargar plantas:", error);
|
console.error("Error al cargar plantas:", error);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
Box, Typography, TextField, Button, CircularProgress, Alert,
|
Box, Typography, TextField, Button, CircularProgress, Alert,
|
||||||
FormControl, InputLabel, Select, MenuItem, FormControlLabel, Checkbox
|
FormControl, InputLabel, Select, MenuItem, FormControlLabel, Checkbox
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import type { PlantaDto } from '../../models/dtos/Impresion/PlantaDto';
|
import type { PlantaDropdownDto } from '../../models/dtos/Impresion/PlantaDropdownDto';
|
||||||
import plantaService from '../../services/Impresion/plantaService';
|
import plantaService from '../../services/Impresion/plantaService';
|
||||||
|
|
||||||
interface SeleccionaReporteConsumoBobinasSeccionProps {
|
interface SeleccionaReporteConsumoBobinasSeccionProps {
|
||||||
@@ -29,7 +29,7 @@ const SeleccionaReporteConsumoBobinasSeccion: React.FC<SeleccionaReporteConsumoB
|
|||||||
const [idPlanta, setIdPlanta] = useState<number | string>('');
|
const [idPlanta, setIdPlanta] = useState<number | string>('');
|
||||||
const [consolidado, setConsolidado] = useState<boolean>(false);
|
const [consolidado, setConsolidado] = useState<boolean>(false);
|
||||||
|
|
||||||
const [plantas, setPlantas] = useState<PlantaDto[]>([]);
|
const [plantas, setPlantas] = useState<PlantaDropdownDto[]>([]);
|
||||||
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
||||||
const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({});
|
const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({});
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ const SeleccionaReporteConsumoBobinasSeccion: React.FC<SeleccionaReporteConsumoB
|
|||||||
const fetchPlantas = async () => {
|
const fetchPlantas = async () => {
|
||||||
setLoadingDropdowns(true);
|
setLoadingDropdowns(true);
|
||||||
try {
|
try {
|
||||||
const plantasData = await plantaService.getAllPlantas();
|
const plantasData = await plantaService.getPlantasForDropdown();
|
||||||
setPlantas(plantasData);
|
setPlantas(plantasData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error al cargar plantas:", error);
|
console.error("Error al cargar plantas:", error);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
Box, Typography, TextField, Button, CircularProgress, Alert,
|
Box, Typography, TextField, Button, CircularProgress, Alert,
|
||||||
FormControl, InputLabel, Select, MenuItem, Checkbox, FormControlLabel
|
FormControl, InputLabel, Select, MenuItem, Checkbox, FormControlLabel
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import type { PlantaDto } from '../../models/dtos/Impresion/PlantaDto';
|
import type { PlantaDropdownDto } from '../../models/dtos/Impresion/PlantaDropdownDto';
|
||||||
import plantaService from '../../services/Impresion/plantaService';
|
import plantaService from '../../services/Impresion/plantaService';
|
||||||
|
|
||||||
interface SeleccionaReporteExistenciaPapelProps {
|
interface SeleccionaReporteExistenciaPapelProps {
|
||||||
@@ -28,7 +28,7 @@ const SeleccionaReporteExistenciaPapel: React.FC<SeleccionaReporteExistenciaPape
|
|||||||
const [idPlanta, setIdPlanta] = useState<number | string>('');
|
const [idPlanta, setIdPlanta] = useState<number | string>('');
|
||||||
const [consolidado, setConsolidado] = useState<boolean>(false);
|
const [consolidado, setConsolidado] = useState<boolean>(false);
|
||||||
|
|
||||||
const [plantas, setPlantas] = useState<PlantaDto[]>([]);
|
const [plantas, setPlantas] = useState<PlantaDropdownDto[]>([]);
|
||||||
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
||||||
const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({});
|
const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({});
|
||||||
|
|
||||||
@@ -36,10 +36,10 @@ const SeleccionaReporteExistenciaPapel: React.FC<SeleccionaReporteExistenciaPape
|
|||||||
const fetchPlantas = async () => {
|
const fetchPlantas = async () => {
|
||||||
setLoadingDropdowns(true);
|
setLoadingDropdowns(true);
|
||||||
try {
|
try {
|
||||||
const plantasData = await plantaService.getAllPlantas();
|
const plantasData = await plantaService.getPlantasForDropdown();
|
||||||
setPlantas(plantasData);
|
setPlantas(plantasData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error al cargar plantas:", error);
|
console.error("Error al cargar plantas para dropdown:", error);
|
||||||
setLocalErrors(prev => ({ ...prev, dropdowns: 'Error al cargar plantas.' }));
|
setLocalErrors(prev => ({ ...prev, dropdowns: 'Error al cargar plantas.' }));
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingDropdowns(false);
|
setLoadingDropdowns(false);
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
Box, Typography, TextField, Button, CircularProgress, Alert,
|
Box, Typography, TextField, Button, CircularProgress, Alert,
|
||||||
FormControl, InputLabel, Select, MenuItem
|
FormControl, InputLabel, Select, MenuItem
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import type { PlantaDto } from '../../models/dtos/Impresion/PlantaDto'; // Asumo que ya tienes este DTO
|
import type { PlantaDropdownDto } from '../../models/dtos/Impresion/PlantaDropdownDto';
|
||||||
import plantaService from '../../services/Impresion/plantaService'; // Asumo que ya tienes este servicio
|
import plantaService from '../../services/Impresion/plantaService';
|
||||||
|
|
||||||
interface SeleccionaReporteMovimientoBobinasProps {
|
interface SeleccionaReporteMovimientoBobinasProps {
|
||||||
onGenerarReporte: (params: {
|
onGenerarReporte: (params: {
|
||||||
@@ -26,7 +26,7 @@ const SeleccionaReporteMovimientoBobinas: React.FC<SeleccionaReporteMovimientoBo
|
|||||||
const [fechaHasta, setFechaHasta] = useState<string>(new Date().toISOString().split('T')[0]);
|
const [fechaHasta, setFechaHasta] = useState<string>(new Date().toISOString().split('T')[0]);
|
||||||
const [idPlanta, setIdPlanta] = useState<number | string>(''); // Puede ser string inicialmente por el MenuItem vacío
|
const [idPlanta, setIdPlanta] = useState<number | string>(''); // Puede ser string inicialmente por el MenuItem vacío
|
||||||
|
|
||||||
const [plantas, setPlantas] = useState<PlantaDto[]>([]);
|
const [plantas, setPlantas] = useState<PlantaDropdownDto[]>([]);
|
||||||
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
||||||
const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({});
|
const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({});
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ const SeleccionaReporteMovimientoBobinas: React.FC<SeleccionaReporteMovimientoBo
|
|||||||
const fetchPlantas = async () => {
|
const fetchPlantas = async () => {
|
||||||
setLoadingDropdowns(true);
|
setLoadingDropdowns(true);
|
||||||
try {
|
try {
|
||||||
const plantasData = await plantaService.getAllPlantas(); // Asumiendo que esto devuelve todas
|
const plantasData = await plantaService.getPlantasForDropdown();
|
||||||
setPlantas(plantasData);
|
setPlantas(plantasData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error al cargar plantas:", error);
|
console.error("Error al cargar plantas:", error);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
Box, Typography, TextField, Button, CircularProgress, Alert,
|
Box, Typography, TextField, Button, CircularProgress, Alert,
|
||||||
FormControl, InputLabel, Select, MenuItem
|
FormControl, InputLabel, Select, MenuItem
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import type { PlantaDto } from '../../models/dtos/Impresion/PlantaDto';
|
import type { PlantaDropdownDto } from '../../models/dtos/Impresion/PlantaDropdownDto';
|
||||||
import plantaService from '../../services/Impresion/plantaService';
|
import plantaService from '../../services/Impresion/plantaService';
|
||||||
|
|
||||||
interface SeleccionaReporteMovimientoBobinasEstadoProps {
|
interface SeleccionaReporteMovimientoBobinasEstadoProps {
|
||||||
@@ -26,7 +26,7 @@ const SeleccionaReporteMovimientoBobinasEstado: React.FC<SeleccionaReporteMovimi
|
|||||||
const [fechaHasta, setFechaHasta] = useState<string>(new Date().toISOString().split('T')[0]);
|
const [fechaHasta, setFechaHasta] = useState<string>(new Date().toISOString().split('T')[0]);
|
||||||
const [idPlanta, setIdPlanta] = useState<number | string>('');
|
const [idPlanta, setIdPlanta] = useState<number | string>('');
|
||||||
|
|
||||||
const [plantas, setPlantas] = useState<PlantaDto[]>([]);
|
const [plantas, setPlantas] = useState<PlantaDropdownDto[]>([]);
|
||||||
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
||||||
const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({});
|
const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({});
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ const SeleccionaReporteMovimientoBobinasEstado: React.FC<SeleccionaReporteMovimi
|
|||||||
const fetchPlantas = async () => {
|
const fetchPlantas = async () => {
|
||||||
setLoadingDropdowns(true);
|
setLoadingDropdowns(true);
|
||||||
try {
|
try {
|
||||||
const plantasData = await plantaService.getAllPlantas();
|
const plantasData = await plantaService.getPlantasForDropdown();
|
||||||
setPlantas(plantasData);
|
setPlantas(plantasData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error al cargar plantas:", error);
|
console.error("Error al cargar plantas:", error);
|
||||||
@@ -65,7 +65,7 @@ const SeleccionaReporteMovimientoBobinasEstado: React.FC<SeleccionaReporteMovimi
|
|||||||
onGenerarReporte({
|
onGenerarReporte({
|
||||||
fechaDesde,
|
fechaDesde,
|
||||||
fechaHasta,
|
fechaHasta,
|
||||||
idPlanta: Number(idPlanta)
|
idPlanta: Number(idPlanta) // Asegurarse de que es un número
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import {
|
|||||||
Box, Typography, TextField, Button, CircularProgress, Alert,
|
Box, Typography, TextField, Button, CircularProgress, Alert,
|
||||||
FormControl, InputLabel, Select, MenuItem, FormControlLabel, Checkbox
|
FormControl, InputLabel, Select, MenuItem, FormControlLabel, Checkbox
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import type { PublicacionDto } from '../../models/dtos/Distribucion/PublicacionDto';
|
import type { PublicacionDropdownDto } from '../../models/dtos/Distribucion/PublicacionDropdownDto';
|
||||||
import publicacionService from '../../services/Distribucion/publicacionService';
|
import publicacionService from '../../services/Distribucion/publicacionService';
|
||||||
import type { PlantaDto } from '../../models/dtos/Impresion/PlantaDto';
|
import type { PlantaDropdownDto } from '../../models/dtos/Impresion/PlantaDropdownDto';
|
||||||
import plantaService from '../../services/Impresion/plantaService';
|
import plantaService from '../../services/Impresion/plantaService';
|
||||||
|
|
||||||
interface SeleccionaReporteTiradasPublicacionesSeccionesProps {
|
interface SeleccionaReporteTiradasPublicacionesSeccionesProps {
|
||||||
@@ -31,8 +31,8 @@ const SeleccionaReporteTiradasPublicacionesSecciones: React.FC<SeleccionaReporte
|
|||||||
const [idPlanta, setIdPlanta] = useState<number | string>('');
|
const [idPlanta, setIdPlanta] = useState<number | string>('');
|
||||||
const [consolidado, setConsolidado] = useState<boolean>(false);
|
const [consolidado, setConsolidado] = useState<boolean>(false);
|
||||||
|
|
||||||
const [publicaciones, setPublicaciones] = useState<PublicacionDto[]>([]);
|
const [publicaciones, setPublicaciones] = useState<PublicacionDropdownDto[]>([]);
|
||||||
const [plantas, setPlantas] = useState<PlantaDto[]>([]);
|
const [plantas, setPlantas] = useState<PlantaDropdownDto[]>([]);
|
||||||
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
||||||
const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({});
|
const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({});
|
||||||
|
|
||||||
@@ -41,8 +41,8 @@ const SeleccionaReporteTiradasPublicacionesSecciones: React.FC<SeleccionaReporte
|
|||||||
setLoadingDropdowns(true);
|
setLoadingDropdowns(true);
|
||||||
try {
|
try {
|
||||||
const [pubData, plantaData] = await Promise.all([
|
const [pubData, plantaData] = await Promise.all([
|
||||||
publicacionService.getAllPublicaciones(undefined, undefined, true),
|
publicacionService.getPublicacionesForDropdown(true),
|
||||||
plantaService.getAllPlantas()
|
plantaService.getPlantasForDropdown()
|
||||||
]);
|
]);
|
||||||
setPublicaciones(pubData.map(p => p));
|
setPublicaciones(pubData.map(p => p));
|
||||||
setPlantas(plantaData);
|
setPlantas(plantaData);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { CreatePublicacionDto } from '../../models/dtos/Distribucion/Create
|
|||||||
import type { UpdatePublicacionDto } from '../../models/dtos/Distribucion/UpdatePublicacionDto';
|
import type { UpdatePublicacionDto } from '../../models/dtos/Distribucion/UpdatePublicacionDto';
|
||||||
import type { PublicacionDiaSemanaDto } from '../../models/dtos/Distribucion/PublicacionDiaSemanaDto';
|
import type { PublicacionDiaSemanaDto } from '../../models/dtos/Distribucion/PublicacionDiaSemanaDto';
|
||||||
import type { UpdatePublicacionDiasSemanaRequestDto } from '../../models/dtos/Distribucion/UpdatePublicacionDiasSemanaRequestDto';
|
import type { UpdatePublicacionDiasSemanaRequestDto } from '../../models/dtos/Distribucion/UpdatePublicacionDiasSemanaRequestDto';
|
||||||
|
import type { PublicacionDropdownDto } from '../../models/dtos/Distribucion/PublicacionDropdownDto';
|
||||||
|
|
||||||
const getAllPublicaciones = async (
|
const getAllPublicaciones = async (
|
||||||
nombreFilter?: string,
|
nombreFilter?: string,
|
||||||
@@ -51,6 +52,11 @@ const getPublicacionesPorDiaSemana = async (diaSemana: number): Promise<Publicac
|
|||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getPublicacionesForDropdown = async (soloHabilitadas: boolean = true): Promise<PublicacionDropdownDto[]> => { // << NUEVA FUNCIÓN
|
||||||
|
const response = await apiClient.get<PublicacionDropdownDto[]>('/publicaciones/dropdown', { params: { soloHabilitadas } });
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
const publicacionService = {
|
const publicacionService = {
|
||||||
getAllPublicaciones,
|
getAllPublicaciones,
|
||||||
getPublicacionById,
|
getPublicacionById,
|
||||||
@@ -59,7 +65,8 @@ const publicacionService = {
|
|||||||
deletePublicacion,
|
deletePublicacion,
|
||||||
getConfiguracionDiasPublicacion,
|
getConfiguracionDiasPublicacion,
|
||||||
updateConfiguracionDiasPublicacion,
|
updateConfiguracionDiasPublicacion,
|
||||||
getPublicacionesPorDiaSemana
|
getPublicacionesPorDiaSemana,
|
||||||
|
getPublicacionesForDropdown,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default publicacionService;
|
export default publicacionService;
|
||||||
@@ -2,6 +2,7 @@ import apiClient from '../apiClient';
|
|||||||
import type { PlantaDto } from '../../models/dtos/Impresion/PlantaDto';
|
import type { PlantaDto } from '../../models/dtos/Impresion/PlantaDto';
|
||||||
import type { CreatePlantaDto } from '../../models/dtos/Impresion/CreatePlantaDto';
|
import type { CreatePlantaDto } from '../../models/dtos/Impresion/CreatePlantaDto';
|
||||||
import type { UpdatePlantaDto } from '../../models/dtos/Impresion/UpdatePlantaDto';
|
import type { UpdatePlantaDto } from '../../models/dtos/Impresion/UpdatePlantaDto';
|
||||||
|
import type { PlantaDropdownDto } from '../../models/dtos/Impresion/PlantaDropdownDto';
|
||||||
|
|
||||||
const getAllPlantas = async (nombreFilter?: string, detalleFilter?: string): Promise<PlantaDto[]> => {
|
const getAllPlantas = async (nombreFilter?: string, detalleFilter?: string): Promise<PlantaDto[]> => {
|
||||||
const params: Record<string, string> = {};
|
const params: Record<string, string> = {};
|
||||||
@@ -35,12 +36,18 @@ const deletePlanta = async (id: number): Promise<void> => {
|
|||||||
await apiClient.delete(`/plantas/${id}`);
|
await apiClient.delete(`/plantas/${id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getPlantasForDropdown = async (): Promise<PlantaDropdownDto[]> => { // << NUEVA FUNCIÓN
|
||||||
|
const response = await apiClient.get<PlantaDropdownDto[]>('/plantas/dropdown');
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
const plantaService = {
|
const plantaService = {
|
||||||
getAllPlantas,
|
getAllPlantas,
|
||||||
getPlantaById,
|
getPlantaById,
|
||||||
createPlanta,
|
createPlanta,
|
||||||
updatePlanta,
|
updatePlanta,
|
||||||
deletePlanta,
|
deletePlanta,
|
||||||
|
getPlantasForDropdown,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default plantaService;
|
export default plantaService;
|
||||||
Reference in New Issue
Block a user