Fix Selectores de Fechas Reporte Existencia de Papel.

Se agrega Total a Liquidar para la E/S de Canillitas.
This commit is contained in:
2025-06-13 13:23:05 -03:00
parent b04a3b99bf
commit cec471b4b1
6 changed files with 106 additions and 72 deletions

View File

@@ -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+437b1e88641aca176cc46d68cee7c28d48eb88db")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+b04a3b99bf2a45834787bea2220e2af89715eff2")]
[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")]

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"C9goqBDGh4B0L1HpPwpJHjfbRNoIuzqnU7zFMHk1LhM=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["ZZivTt9Zh03vN/jzywHdSIjldJk\u002BW/DgTu7TlHDqhsY=","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=","flM0K9XRNNYylZG0CGbM3aCgbKpYSYF47xY41qCjtsY="],"CachedAssets":{},"CachedCopyCandidates":{}} {"GlobalPropertiesHash":"C9goqBDGh4B0L1HpPwpJHjfbRNoIuzqnU7zFMHk1LhM=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["ZZivTt9Zh03vN/jzywHdSIjldJk\u002BW/DgTu7TlHDqhsY=","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=","xAoI9GEquVEPBtI2g76y50iH7F\u002B5S2DDD/lAn\u002BmYZVM="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"w3MBbMV9Msh0YEq9AW/8s16bzXJ93T9lMVXKPm/r6es=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["ZZivTt9Zh03vN/jzywHdSIjldJk\u002BW/DgTu7TlHDqhsY=","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=","flM0K9XRNNYylZG0CGbM3aCgbKpYSYF47xY41qCjtsY="],"CachedAssets":{},"CachedCopyCandidates":{}} {"GlobalPropertiesHash":"w3MBbMV9Msh0YEq9AW/8s16bzXJ93T9lMVXKPm/r6es=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["ZZivTt9Zh03vN/jzywHdSIjldJk\u002BW/DgTu7TlHDqhsY=","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=","xAoI9GEquVEPBtI2g76y50iH7F\u002B5S2DDD/lAn\u002BmYZVM="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect, useCallback, useMemo } from 'react'; // << Añadido useMemo
import { import {
Box, Typography, TextField, Button, Paper, IconButton, Menu, MenuItem, Chip, Box, Typography, TextField, Button, Paper, IconButton, Menu, MenuItem, Chip,
Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TablePagination, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TablePagination,
@@ -33,9 +33,9 @@ type TipoDestinatarioFiltro = 'canillitas' | 'accionistas';
const GestionarEntradasSalidasCanillaPage: React.FC = () => { const GestionarEntradasSalidasCanillaPage: React.FC = () => {
const [movimientos, setMovimientos] = useState<EntradaSalidaCanillaDto[]>([]); const [movimientos, setMovimientos] = useState<EntradaSalidaCanillaDto[]>([]);
const [loading, setLoading] = useState(true); // Para carga principal de movimientos const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); // Error general o de carga const [error, setError] = useState<string | null>(null);
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null); // Para errores de modal/API const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
const [filtroFecha, setFiltroFecha] = useState<string>(new Date().toISOString().split('T')[0]); const [filtroFecha, setFiltroFecha] = useState<string>(new Date().toISOString().split('T')[0]);
const [filtroIdPublicacion, setFiltroIdPublicacion] = useState<number | string>(''); const [filtroIdPublicacion, setFiltroIdPublicacion] = useState<number | string>('');
@@ -52,7 +52,7 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
const [prefillModalData, setPrefillModalData] = useState<{ const [prefillModalData, setPrefillModalData] = useState<{
fecha?: string; fecha?: string;
idCanilla?: number | string; idCanilla?: number | string;
nombreCanilla?: string; // << AÑADIDO PARA PASAR AL MODAL nombreCanilla?: string;
idPublicacion?: number | string; idPublicacion?: number | string;
} | null>(null); } | null>(null);
@@ -82,34 +82,34 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
useEffect(() => { useEffect(() => {
const fetchPublicaciones = async () => { const fetchPublicaciones = async () => {
setLoadingFiltersDropdown(true); // Mover al inicio de la carga de pubs setLoadingFiltersDropdown(true);
try { try {
const pubsData = await publicacionService.getPublicacionesForDropdown(true); const pubsData = await publicacionService.getPublicacionesForDropdown(true);
setPublicaciones(pubsData); setPublicaciones(pubsData);
} catch (err) { } catch (err) {
console.error("Error cargando publicaciones para filtro:",err); console.error("Error cargando publicaciones para filtro:",err);
setError("Error al cargar publicaciones."); // Usar error general setError("Error al cargar publicaciones.");
} finally { } finally {
// No poner setLoadingFiltersDropdown(false) aquí, esperar a que ambas cargas terminen // No setLoadingFiltersDropdown(false) a, esperar a la otra carga
} }
}; };
fetchPublicaciones(); fetchPublicaciones();
}, []); }, []);
const fetchDestinatariosParaDropdown = useCallback(async () => { const fetchDestinatariosParaDropdown = useCallback(async () => {
setLoadingFiltersDropdown(true); // Poner al inicio de esta carga también setLoadingFiltersDropdown(true);
setFiltroIdCanillitaSeleccionado(''); setFiltroIdCanillitaSeleccionado('');
setDestinatariosDropdown([]); setDestinatariosDropdown([]);
setError(null); // Limpiar errores de carga de dropdowns previos setError(null);
try { try {
const esAccionistaFilter = filtroTipoDestinatario === 'accionistas'; const esAccionistaFilter = filtroTipoDestinatario === 'accionistas';
const data = await canillaService.getAllCanillas(undefined, undefined, true, esAccionistaFilter); const data = await canillaService.getAllCanillas(undefined, undefined, true, esAccionistaFilter);
setDestinatariosDropdown(data); setDestinatariosDropdown(data);
} catch (err) { } catch (err) {
console.error("Error cargando destinatarios para filtro:", err); console.error("Error cargando destinatarios para filtro:", err);
setError("Error al cargar canillitas/accionistas."); // Usar error general setError("Error al cargar canillitas/accionistas.");
} finally { } finally {
setLoadingFiltersDropdown(false); // Poner al final de AMBAS cargas de dropdown setLoadingFiltersDropdown(false);
} }
}, [filtroTipoDestinatario]); }, [filtroTipoDestinatario]);
@@ -153,9 +153,9 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
cargarMovimientos(); cargarMovimientos();
} else { } else {
setMovimientos([]); setMovimientos([]);
if (loading) setLoading(false); // Asegurar que no se quede en loading si los filtros se limpian if (loading) setLoading(false);
} }
}, [cargarMovimientos, filtroFecha, filtroIdCanillitaSeleccionado]); // `cargarMovimientos` ya tiene sus dependencias }, [cargarMovimientos, filtroFecha, filtroIdCanillitaSeleccionado]);
const handleOpenModal = (item?: EntradaSalidaCanillaDto) => { const handleOpenModal = (item?: EntradaSalidaCanillaDto) => {
@@ -172,7 +172,6 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
setEditingMovimiento(item); setEditingMovimiento(item);
setPrefillModalData(null); setPrefillModalData(null);
} else { } else {
// --- CAMBIO: Obtener nombre del canillita seleccionado para prefill ---
const canillitaSeleccionado = destinatariosDropdown.find( const canillitaSeleccionado = destinatariosDropdown.find(
c => c.idCanilla === Number(filtroIdCanillitaSeleccionado) c => c.idCanilla === Number(filtroIdCanillitaSeleccionado)
); );
@@ -180,7 +179,7 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
setPrefillModalData({ setPrefillModalData({
fecha: filtroFecha, fecha: filtroFecha,
idCanilla: filtroIdCanillitaSeleccionado, idCanilla: filtroIdCanillitaSeleccionado,
nombreCanilla: canillitaSeleccionado?.nomApe, // << AÑADIR NOMBRE nombreCanilla: canillitaSeleccionado?.nomApe,
idPublicacion: filtroIdPublicacion idPublicacion: filtroIdPublicacion
}); });
} }
@@ -188,7 +187,6 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
setModalOpen(true); setModalOpen(true);
}; };
// ... handleDelete, handleMenuOpen, handleMenuClose, handleSelectRowForLiquidar, handleSelectAllForLiquidar, handleOpenLiquidarDialog, handleCloseLiquidarDialog sin cambios ...
const handleDelete = async (idParte: number) => { const handleDelete = async (idParte: number) => {
if (window.confirm(`¿Seguro de eliminar este movimiento (ID: ${idParte})?`)) { if (window.confirm(`¿Seguro de eliminar este movimiento (ID: ${idParte})?`)) {
setApiErrorMessage(null); setApiErrorMessage(null);
@@ -233,9 +231,8 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
const handleConfirmLiquidar = async () => { const handleConfirmLiquidar = async () => {
if (selectedIdsParaLiquidar.size === 0) { /* ... */ return; } if (selectedIdsParaLiquidar.size === 0) { return; }
if (!fechaLiquidacionDialog) { /* ... */ return; } if (!fechaLiquidacionDialog) { return; }
// ... (validación de fecha sin cambios)
const fechaLiquidacionDate = new Date(fechaLiquidacionDialog + 'T00:00:00Z'); const fechaLiquidacionDate = new Date(fechaLiquidacionDialog + 'T00:00:00Z');
let fechaMovimientoMasReciente: Date | null = null; let fechaMovimientoMasReciente: Date | null = null;
selectedIdsParaLiquidar.forEach(idParte => { selectedIdsParaLiquidar.forEach(idParte => {
@@ -251,7 +248,6 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
setApiErrorMessage(`La fecha de liquidación (${fechaLiquidacionDate.toLocaleDateString('es-AR', {timeZone: 'UTC'})}) no puede ser inferior a la fecha del movimiento más reciente a liquidar (${(fechaMovimientoMasReciente as Date).toLocaleDateString('es-AR', {timeZone: 'UTC'})}).`); setApiErrorMessage(`La fecha de liquidación (${fechaLiquidacionDate.toLocaleDateString('es-AR', {timeZone: 'UTC'})}) no puede ser inferior a la fecha del movimiento más reciente a liquidar (${(fechaMovimientoMasReciente as Date).toLocaleDateString('es-AR', {timeZone: 'UTC'})}).`);
return; return;
} }
setApiErrorMessage(null); setApiErrorMessage(null);
setLoading(true); setLoading(true);
const liquidarDto: LiquidarMovimientosCanillaRequestDto = { const liquidarDto: LiquidarMovimientosCanillaRequestDto = {
@@ -262,17 +258,18 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
await entradaSalidaCanillaService.liquidarMovimientos(liquidarDto); await entradaSalidaCanillaService.liquidarMovimientos(liquidarDto);
setOpenLiquidarDialog(false); setOpenLiquidarDialog(false);
const primerIdParteLiquidado = Array.from(selectedIdsParaLiquidar)[0]; const primerIdParteLiquidado = Array.from(selectedIdsParaLiquidar)[0];
// Necesitamos encontrar el movimiento en la lista ANTES de recargar
const movimientoParaTicket = movimientos.find(m => m.idParte === primerIdParteLiquidado); const movimientoParaTicket = movimientos.find(m => m.idParte === primerIdParteLiquidado);
await cargarMovimientos(); await cargarMovimientos(); // Recargar la lista para reflejar el estado liquidado
// --- CAMBIO: NO IMPRIMIR TICKET SI ES ACCIONISTA --- // Usar la fecha del movimiento original para el ticket
if (movimientoParaTicket && !movimientoParaTicket.canillaEsAccionista) { if (movimientoParaTicket && !movimientoParaTicket.canillaEsAccionista) {
console.log("Liquidación exitosa, generando ticket para canillita NO accionista:", movimientoParaTicket.idCanilla); console.log("Liquidación exitosa, generando ticket para canillita NO accionista:", movimientoParaTicket.idCanilla);
await handleImprimirTicketLiquidacion( await handleImprimirTicketLiquidacion(
movimientoParaTicket.idCanilla, movimientoParaTicket.idCanilla,
fechaLiquidacionDialog, movimientoParaTicket.fecha, // Usar la fecha del movimiento
false // esAccionista = false false
); );
} else if (movimientoParaTicket && movimientoParaTicket.canillaEsAccionista) { } else if (movimientoParaTicket && movimientoParaTicket.canillaEsAccionista) {
console.log("Liquidación exitosa para accionista. No se genera ticket automáticamente."); console.log("Liquidación exitosa para accionista. No se genera ticket automáticamente.");
@@ -288,7 +285,6 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
}; };
const handleModalEditSubmit = async (data: UpdateEntradaSalidaCanillaDto, idParte: number) => { const handleModalEditSubmit = async (data: UpdateEntradaSalidaCanillaDto, idParte: number) => {
// ... (sin cambios)
setApiErrorMessage(null); setApiErrorMessage(null);
try { try {
await entradaSalidaCanillaService.updateEntradaSalidaCanilla(idParte, data); await entradaSalidaCanillaService.updateEntradaSalidaCanilla(idParte, data);
@@ -311,7 +307,6 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
const handleImprimirTicketLiquidacion = useCallback(async ( const handleImprimirTicketLiquidacion = useCallback(async (
idCanilla: number, fecha: string, esAccionista: boolean idCanilla: number, fecha: string, esAccionista: boolean
) => { ) => {
// ... (sin cambios)
setLoadingTicketPdf(true); setLoadingTicketPdf(true);
setApiErrorMessage(null); setApiErrorMessage(null);
try { try {
@@ -340,11 +335,14 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
}; };
const displayData = movimientos.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage); const displayData = movimientos.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
if (!loading && !puedeVer && !loadingFiltersDropdown && movimientos.length === 0 && !filtroFecha && !filtroIdCanillitaSeleccionado ) { // Modificado para solo mostrar si no hay filtros y no puede ver const totalARendirVisible = useMemo(() =>
displayData.filter(m => !m.liquidado).reduce((sum, item) => sum + item.montoARendir, 0)
, [displayData]);
if (!loading && !puedeVer && !loadingFiltersDropdown && movimientos.length === 0 && !filtroFecha && !filtroIdCanillitaSeleccionado ) {
return <Box sx={{ p: 2 }}><Alert severity="error">{error || "Acceso denegado."}</Alert></Box>; return <Box sx={{ p: 2 }}><Alert severity="error">{error || "Acceso denegado."}</Alert></Box>;
} }
const numSelectedToLiquidate = selectedIdsParaLiquidar.size; const numSelectedToLiquidate = selectedIdsParaLiquidar.size;
const numNotLiquidatedOnPage = displayData.filter(m => !m.liquidado).length; const numNotLiquidatedOnPage = displayData.filter(m => !m.liquidado).length;
@@ -354,11 +352,12 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
<Paper sx={{ p: 2, mb: 2 }}> <Paper sx={{ p: 2, mb: 2 }}>
<Typography variant="h6" gutterBottom>Filtros <FilterListIcon fontSize="small" /></Typography> <Typography variant="h6" gutterBottom>Filtros <FilterListIcon fontSize="small" /></Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 2, alignItems: 'center', mb: 2 }}> <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 2, alignItems: 'center', mb: 2 }}>
<TextField label="Fecha" type="date" size="small" value={filtroFecha} {/* ... (Filtros sin cambios) ... */}
<TextField label="Fecha" type="date" size="small" value={filtroFecha}
onChange={(e) => setFiltroFecha(e.target.value)} onChange={(e) => setFiltroFecha(e.target.value)}
InputLabelProps={{ shrink: true }} sx={{ minWidth: 170 }} InputLabelProps={{ shrink: true }} sx={{ minWidth: 170 }}
required required
error={!filtroFecha} // Se marca error si está vacío error={!filtroFecha}
helperText={!filtroFecha ? "Fecha es obligatoria" : ""} helperText={!filtroFecha ? "Fecha es obligatoria" : ""}
/> />
<ToggleButtonGroup <ToggleButtonGroup
@@ -398,14 +397,13 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
</Select> </Select>
</FormControl> </FormControl>
</Box> </Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap:2 }}>
{/* --- CAMBIO: DESHABILITAR BOTÓN SI FILTROS OBLIGATORIOS NO ESTÁN --- */}
{puedeCrear && ( {puedeCrear && (
<Button <Button
variant="contained" variant="contained"
startIcon={<AddIcon />} startIcon={<AddIcon />}
onClick={() => handleOpenModal()} onClick={() => handleOpenModal()}
disabled={!filtroFecha || !filtroIdCanillitaSeleccionado} // <<-- AÑADIDO disabled={!filtroFecha || !filtroIdCanillitaSeleccionado}
> >
Registrar Movimiento Registrar Movimiento
</Button> </Button>
@@ -417,26 +415,32 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
)} )}
</Box> </Box>
</Paper> </Paper>
{!filtroFecha && <Alert severity="info" sx={{my:1}}>Por favor, seleccione una fecha.</Alert>} {!filtroFecha && <Alert severity="info" sx={{my:1}}>Por favor, seleccione una fecha.</Alert>}
{filtroFecha && !filtroIdCanillitaSeleccionado && <Alert severity="info" sx={{my:1}}>Por favor, seleccione un {filtroTipoDestinatario === 'canillitas' ? 'canillita' : 'accionista'}.</Alert>} {filtroFecha && !filtroIdCanillitaSeleccionado && <Alert severity="info" sx={{my:1}}>Por favor, seleccione un {filtroTipoDestinatario === 'canillitas' ? 'canillita' : 'accionista'}.</Alert>}
{loading && <Box sx={{ display: 'flex', justifyContent: 'center', my: 2 }}><CircularProgress /></Box>} {loading && <Box sx={{ display: 'flex', justifyContent: 'center', my: 2 }}><CircularProgress /></Box>}
{/* Mostrar error general si no hay error de API específico y no está cargando filtros */} {error && !loading && !apiErrorMessage && <Alert severity="error" sx={{ my: 2 }}>{error}</Alert>}
{error && !loading && !apiErrorMessage && !loadingFiltersDropdown && <Alert severity="error" sx={{ my: 2 }}>{error}</Alert>}
{apiErrorMessage && <Alert severity="error" sx={{ my: 2 }}>{apiErrorMessage}</Alert>} {apiErrorMessage && <Alert severity="error" sx={{ my: 2 }}>{apiErrorMessage}</Alert>}
{loadingTicketPdf && {loadingTicketPdf && ( <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', my: 2 }}> <CircularProgress size={20} sx={{ mr: 1 }} /> <Typography variant="body2">Cargando ticket...</Typography> </Box> )}
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', my: 2 }}>
<CircularProgress size={20} sx={{ mr: 1 }} />
<Typography variant="body2">Cargando ticket...</Typography>
</Box>
}
{!loading && movimientos.length > 0 && (
<Paper sx={{ p: 1.5, mb: 2, mt:1, backgroundColor: 'grey.100' }}>
<Box sx={{display: 'flex', justifyContent: 'flex-end', alignItems: 'center'}}>
<Typography variant="subtitle1" sx={{mr:2}}>
Total a Liquidar:
</Typography>
<Typography variant="h6" sx={{fontWeight: 'bold', color: 'error.main'}}>
{totalARendirVisible.toLocaleString('es-AR', { style: 'currency', currency: 'ARS' })}
</Typography>
</Box>
</Paper>
)}
{!loading && !error && puedeVer && filtroFecha && filtroIdCanillitaSeleccionado && ( {!loading && !error && puedeVer && filtroFecha && filtroIdCanillitaSeleccionado && (
// ... (Tabla y Paginación sin cambios) <TableContainer component={Paper}>
<TableContainer component={Paper}> <Table size="small">
<Table size="small">
<TableHead> <TableHead>
<TableRow> <TableRow>
{puedeLiquidar && ( {puedeLiquidar && (
@@ -527,20 +531,22 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
</TableContainer> </TableContainer>
)} )}
<Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleMenuClose}> <Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleMenuClose}>
{puedeModificar && selectedRow && !selectedRow.liquidado && ( {puedeModificar && selectedRow && !selectedRow.liquidado && (
<MenuItem onClick={() => { handleOpenModal(selectedRow); handleMenuClose(); }}><EditIcon fontSize="small" sx={{ mr: 1 }} /> Modificar</MenuItem>)} <MenuItem onClick={() => { handleOpenModal(selectedRow); handleMenuClose(); }}><EditIcon fontSize="small" sx={{ mr: 1 }} /> Modificar</MenuItem>)}
{/* --- CAMBIO: MOSTRAR REIMPRIMIR TICKET SIEMPRE SI ESTÁ LIQUIDADO --- */}
{selectedRow && selectedRow.liquidado && puedeLiquidar && ( // Usar puedeLiquidar para consistencia {selectedRow && selectedRow.liquidado && puedeLiquidar && (
<MenuItem <MenuItem
onClick={() => { onClick={() => {
if (selectedRow) { if (selectedRow) {
// Usar siempre selectedRow.fecha, que es la fecha original del movimiento
handleImprimirTicketLiquidacion( handleImprimirTicketLiquidacion(
selectedRow.idCanilla, selectedRow.idCanilla,
selectedRow.fechaLiquidado || selectedRow.fecha, selectedRow.fecha, // Usar siempre la fecha del movimiento
selectedRow.canillaEsAccionista // Pasar si es accionista selectedRow.canillaEsAccionista
); );
} }
handleMenuClose();
}} }}
disabled={loadingTicketPdf} disabled={loadingTicketPdf}
> >
@@ -552,8 +558,9 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
{selectedRow && ( {selectedRow && (
((!selectedRow.liquidado && puedeEliminar) || (selectedRow.liquidado && puedeEliminarLiquidados)) ((!selectedRow.liquidado && puedeEliminar) || (selectedRow.liquidado && puedeEliminarLiquidados))
) && ( ) && (
<MenuItem onClick={() => { if (selectedRow) handleDelete(selectedRow.idParte); }}> <MenuItem onClick={() => {if (selectedRow) handleDelete(selectedRow.idParte);}}>
<DeleteIcon fontSize="small" sx={{ mr: 1 }} /> Eliminar <DeleteIcon fontSize="small" sx={{ mr: 1 }} />
Eliminar
</MenuItem> </MenuItem>
)} )}
</Menu> </Menu>
@@ -561,14 +568,13 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
<EntradaSalidaCanillaFormModal <EntradaSalidaCanillaFormModal
open={modalOpen} open={modalOpen}
onClose={handleCloseModal} onClose={handleCloseModal}
onSubmit={handleModalEditSubmit} // Este onSubmit es solo para edición onSubmit={handleModalEditSubmit}
initialData={editingMovimiento} initialData={editingMovimiento}
prefillData={prefillModalData} prefillData={prefillModalData}
errorMessage={apiErrorMessage} errorMessage={apiErrorMessage}
clearErrorMessage={() => setApiErrorMessage(null)} clearErrorMessage={() => setApiErrorMessage(null)}
/> />
{/* ... (Dialog de Liquidación sin cambios) ... */}
<Dialog open={openLiquidarDialog} onClose={handleCloseLiquidarDialog}> <Dialog open={openLiquidarDialog} onClose={handleCloseLiquidarDialog}>
<DialogTitle>Confirmar Liquidación</DialogTitle> <DialogTitle>Confirmar Liquidación</DialogTitle>
<DialogContent> <DialogContent>

View File

@@ -1,4 +1,3 @@
// src/pages/Reportes/ReporteExistenciaPapelPage.tsx
import React, { useState, useCallback } from 'react'; import React, { useState, useCallback } from 'react';
import { import {
Box, Box,

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { 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 { PlantaDropdownDto } from '../../models/dtos/Impresion/PlantaDropdownDto'; import type { PlantaDropdownDto } from '../../models/dtos/Impresion/PlantaDropdownDto';
import plantaService from '../../services/Impresion/plantaService'; import plantaService from '../../services/Impresion/plantaService';
@@ -12,10 +12,10 @@ interface SeleccionaReporteExistenciaPapelProps {
fechaHasta: string; fechaHasta: string;
idPlanta?: number | null; idPlanta?: number | null;
consolidado: boolean; consolidado: boolean;
}) => Promise<void>; // La función que realmente llama al servicio y maneja los datos }) => Promise<void>;
onCancel: () => void; // Para cerrar el modal/componente onCancel?: () => void; // onCancel sigue acá por si lo necesito en otros selectores
isLoading?: boolean; // Para mostrar estado de carga desde el padre isLoading?: boolean;
apiErrorMessage?: string | null; // Para mostrar errores de API desde el padre apiErrorMessage?: string | null;
} }
const SeleccionaReporteExistenciaPapel: React.FC<SeleccionaReporteExistenciaPapelProps> = ({ const SeleccionaReporteExistenciaPapel: React.FC<SeleccionaReporteExistenciaPapelProps> = ({
@@ -24,7 +24,12 @@ const SeleccionaReporteExistenciaPapel: React.FC<SeleccionaReporteExistenciaPape
apiErrorMessage apiErrorMessage
}) => { }) => {
const [fechaDesde, setFechaDesde] = useState<string>(new Date().toISOString().split('T')[0]); const [fechaDesde, setFechaDesde] = useState<string>(new Date().toISOString().split('T')[0]);
const [fechaHasta, setFechaHasta] = useState<string>(new Date().toISOString().split('T')[0]); const [fechaHasta, setFechaHasta] = useState<string>(() => {
// Inicializar fechaHasta al día siguiente por defecto
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
return tomorrow.toISOString().split('T')[0];
});
const [idPlanta, setIdPlanta] = useState<number | string>(''); const [idPlanta, setIdPlanta] = useState<number | string>('');
const [consolidado, setConsolidado] = useState<boolean>(false); const [consolidado, setConsolidado] = useState<boolean>(false);
@@ -49,18 +54,39 @@ const SeleccionaReporteExistenciaPapel: React.FC<SeleccionaReporteExistenciaPape
}, []); }, []);
useEffect(() => { useEffect(() => {
// Si se marca consolidado, limpiar y deshabilitar la selección de planta
if (consolidado) { if (consolidado) {
setIdPlanta(''); setIdPlanta('');
} }
}, [consolidado]); }, [consolidado]);
const handleFechaDesdeChange = (newFechaDesde: string) => {
setFechaDesde(newFechaDesde);
// Limpiar errores
setLocalErrors(p => ({ ...p, fechaDesde: null, fechaHasta: null }));
// Si la nueva fechaDesde es igual o posterior a la fechaHasta, ajustar fechaHasta
if (newFechaDesde && fechaHasta && new Date(newFechaDesde) >= new Date(fechaHasta)) {
const dDesde = new Date(newFechaDesde + 'T00:00:00'); // Usar T00:00:00 para evitar problemas de zona horaria
dDesde.setDate(dDesde.getDate() + 1);
setFechaHasta(dDesde.toISOString().split('T')[0]);
}
};
// Función para obtener la fecha mínima permitida para fechaHasta
const getMinFechaHasta = () => {
if (!fechaDesde) return undefined;
const minDate = new Date(fechaDesde + 'T00:00:00');
minDate.setDate(minDate.getDate() + 1); // El mínimo es el día SIGUIENTE a fechaDesde
return minDate.toISOString().split('T')[0];
};
const validate = (): boolean => { const validate = (): boolean => {
const errors: { [key: string]: string | null } = {}; const errors: { [key: string]: string | null } = {};
if (!fechaDesde) errors.fechaDesde = 'Fecha Desde es obligatoria.'; if (!fechaDesde) errors.fechaDesde = 'Fecha Desde es obligatoria.';
if (!fechaHasta) errors.fechaHasta = 'Fecha Hasta es obligatoria.'; if (!fechaHasta) errors.fechaHasta = 'Fecha Hasta es obligatoria.';
if (fechaDesde && fechaHasta && new Date(fechaDesde) > new Date(fechaHasta)) {
errors.fechaHasta = 'Fecha Hasta no puede ser anterior a Fecha Desde.'; if (fechaDesde && fechaHasta && new Date(fechaHasta) <= new Date(fechaDesde)) {
errors.fechaHasta = 'Fecha Hasta debe ser posterior a Fecha Desde.';
} }
if (!consolidado && !idPlanta) { if (!consolidado && !idPlanta) {
errors.idPlanta = 'Seleccione una planta si no es consolidado.'; errors.idPlanta = 'Seleccione una planta si no es consolidado.';
@@ -88,7 +114,7 @@ const SeleccionaReporteExistenciaPapel: React.FC<SeleccionaReporteExistenciaPape
label="Fecha Desde" label="Fecha Desde"
type="date" type="date"
value={fechaDesde} value={fechaDesde}
onChange={(e) => { setFechaDesde(e.target.value); setLocalErrors(p => ({ ...p, fechaDesde: null, fechaHasta: null })); }} onChange={(e) => handleFechaDesdeChange(e.target.value)}
margin="normal" margin="normal"
fullWidth fullWidth
required required
@@ -109,6 +135,9 @@ const SeleccionaReporteExistenciaPapel: React.FC<SeleccionaReporteExistenciaPape
helperText={localErrors.fechaHasta} helperText={localErrors.fechaHasta}
disabled={isLoading} disabled={isLoading}
InputLabelProps={{ shrink: true }} InputLabelProps={{ shrink: true }}
inputProps={{
min: getMinFechaHasta()
}}
/> />
<FormControlLabel <FormControlLabel
control={ control={
@@ -126,7 +155,7 @@ const SeleccionaReporteExistenciaPapel: React.FC<SeleccionaReporteExistenciaPape
<Select <Select
labelId="planta-select-label" labelId="planta-select-label"
label="Planta" label="Planta"
value={consolidado ? '' : idPlanta} // Limpiar selección si es consolidado value={consolidado ? '' : idPlanta}
onChange={(e) => { setIdPlanta(e.target.value as number); setLocalErrors(p => ({ ...p, idPlanta: null })); }} onChange={(e) => { setIdPlanta(e.target.value as number); setLocalErrors(p => ({ ...p, idPlanta: null })); }}
> >
<MenuItem value="" disabled><em>{consolidado ? 'N/A (Consolidado)' : 'Seleccione una planta'}</em></MenuItem> <MenuItem value="" disabled><em>{consolidado ? 'N/A (Consolidado)' : 'Seleccione una planta'}</em></MenuItem>