Fix: Captura y Muestra del Error Por Recibo Duplicado
All checks were successful
Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 2m2s
All checks were successful
Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 2m2s
This commit is contained in:
@@ -16,7 +16,7 @@ namespace GestionIntegral.Api.Data.Repositories.Contables
|
|||||||
Task<PagoDistribuidor?> CreateAsync(PagoDistribuidor nuevoPago, int idUsuario, IDbTransaction transaction);
|
Task<PagoDistribuidor?> CreateAsync(PagoDistribuidor nuevoPago, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> UpdateAsync(PagoDistribuidor pagoAActualizar, int idUsuario, IDbTransaction transaction);
|
Task<bool> UpdateAsync(PagoDistribuidor pagoAActualizar, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> DeleteAsync(int idPago, int idUsuario, IDbTransaction transaction);
|
Task<bool> DeleteAsync(int idPago, int idUsuario, IDbTransaction transaction);
|
||||||
Task<bool> ExistsByReciboAndTipoMovimientoAsync(int recibo, string tipoMovimiento, int? excludeIdPago = null);
|
Task<PagoDistribuidor?> GetByReciboAndTipoMovimientoAsync(int recibo, string tipoMovimiento, int? excludeIdPago = null);
|
||||||
Task<IEnumerable<(PagoDistribuidorHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
|
Task<IEnumerable<(PagoDistribuidorHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
|
||||||
DateTime? fechaDesde, DateTime? fechaHasta,
|
DateTime? fechaDesde, DateTime? fechaHasta,
|
||||||
int? idUsuarioModifico, string? tipoModificacion,
|
int? idUsuarioModifico, string? tipoModificacion,
|
||||||
|
|||||||
@@ -70,9 +70,10 @@ namespace GestionIntegral.Api.Data.Repositories.Contables
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ExistsByReciboAndTipoMovimientoAsync(int recibo, string tipoMovimiento, int? excludeIdPago = null)
|
public async Task<PagoDistribuidor?> GetByReciboAndTipoMovimientoAsync(int recibo, string tipoMovimiento, int? excludeIdPago = null)
|
||||||
{
|
{
|
||||||
var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.cue_PagosDistribuidor WHERE Recibo = @ReciboParam AND TipoMovimiento = @TipoMovParam");
|
var sqlBuilder = new StringBuilder(SelectQueryBase()); // Reutiliza la consulta base
|
||||||
|
sqlBuilder.Append(" WHERE Recibo = @ReciboParam AND TipoMovimiento = @TipoMovParam");
|
||||||
var parameters = new DynamicParameters();
|
var parameters = new DynamicParameters();
|
||||||
parameters.Add("ReciboParam", recibo);
|
parameters.Add("ReciboParam", recibo);
|
||||||
parameters.Add("TipoMovParam", tipoMovimiento);
|
parameters.Add("TipoMovParam", tipoMovimiento);
|
||||||
@@ -85,12 +86,12 @@ namespace GestionIntegral.Api.Data.Repositories.Contables
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var connection = _cf.CreateConnection();
|
using var connection = _cf.CreateConnection();
|
||||||
return await connection.ExecuteScalarAsync<bool>(sqlBuilder.ToString(), parameters);
|
return await connection.QuerySingleOrDefaultAsync<PagoDistribuidor>(sqlBuilder.ToString(), parameters);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_log.LogError(ex, "Error en ExistsByReciboAndTipoMovimientoAsync. Recibo: {Recibo}, Tipo: {Tipo}", recibo, tipoMovimiento);
|
_log.LogError(ex, "Error en GetByReciboAndTipoMovimientoAsync. Recibo: {Recibo}, Tipo: {Tipo}", recibo, tipoMovimiento);
|
||||||
return true; // Asumir que existe en caso de error para prevenir duplicados
|
throw; // Relanzar para que el servicio lo maneje
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,8 +93,18 @@ namespace GestionIntegral.Api.Services.Contables
|
|||||||
return (null, "Tipo de pago no válido.");
|
return (null, "Tipo de pago no válido.");
|
||||||
if (await _empresaRepo.GetByIdAsync(createDto.IdEmpresa) == null)
|
if (await _empresaRepo.GetByIdAsync(createDto.IdEmpresa) == null)
|
||||||
return (null, "Empresa no válida.");
|
return (null, "Empresa no válida.");
|
||||||
if (await _pagoRepo.ExistsByReciboAndTipoMovimientoAsync(createDto.Recibo, createDto.TipoMovimiento))
|
var pagoExistente = await _pagoRepo.GetByReciboAndTipoMovimientoAsync(createDto.Recibo, createDto.TipoMovimiento);
|
||||||
return (null, $"Ya existe un pago '{createDto.TipoMovimiento}' con el número de recibo '{createDto.Recibo}'.");
|
if (pagoExistente != null)
|
||||||
|
{
|
||||||
|
// Si encontramos un duplicado, obtenemos los detalles para el mensaje de error
|
||||||
|
var distribuidor = await _distribuidorRepo.GetByIdSimpleAsync(pagoExistente.IdDistribuidor);
|
||||||
|
var empresa = await _empresaRepo.GetByIdAsync(pagoExistente.IdEmpresa);
|
||||||
|
|
||||||
|
string mensajeError = $"El recibo N° {createDto.Recibo} ya fue registrado como '{pagoExistente.TipoMovimiento}' el {pagoExistente.Fecha:dd/MM/yyyy} " +
|
||||||
|
$"para el distribuidor '{distribuidor?.Nombre ?? "Desconocido"}' en la empresa '{empresa?.Nombre ?? "Desconocida"}'.";
|
||||||
|
|
||||||
|
return (null, mensajeError);
|
||||||
|
}
|
||||||
|
|
||||||
var nuevoPago = new PagoDistribuidor
|
var nuevoPago = new PagoDistribuidor
|
||||||
{
|
{
|
||||||
@@ -275,25 +285,25 @@ namespace GestionIntegral.Api.Services.Contables
|
|||||||
DateTime? fechaDesde, DateTime? fechaHasta,
|
DateTime? fechaDesde, DateTime? fechaHasta,
|
||||||
int? idUsuarioModifico, string? tipoModificacion,
|
int? idUsuarioModifico, string? tipoModificacion,
|
||||||
int? idPagoAfectado)
|
int? idPagoAfectado)
|
||||||
{
|
|
||||||
var historialData = await _pagoRepo.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPagoAfectado);
|
|
||||||
|
|
||||||
return historialData.Select(h => new PagoDistribuidorHistorialDto
|
|
||||||
{
|
{
|
||||||
Id_Pago = h.Historial.Id_Pago,
|
var historialData = await _pagoRepo.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPagoAfectado);
|
||||||
Id_Distribuidor = h.Historial.Id_Distribuidor,
|
|
||||||
Fecha = h.Historial.Fecha,
|
return historialData.Select(h => new PagoDistribuidorHistorialDto
|
||||||
TipoMovimiento = h.Historial.TipoMovimiento,
|
{
|
||||||
Recibo = h.Historial.Recibo,
|
Id_Pago = h.Historial.Id_Pago,
|
||||||
Monto = h.Historial.Monto,
|
Id_Distribuidor = h.Historial.Id_Distribuidor,
|
||||||
Id_TipoPago = h.Historial.Id_TipoPago,
|
Fecha = h.Historial.Fecha,
|
||||||
Detalle = h.Historial.Detalle,
|
TipoMovimiento = h.Historial.TipoMovimiento,
|
||||||
Id_Empresa = h.Historial.Id_Empresa,
|
Recibo = h.Historial.Recibo,
|
||||||
Id_Usuario = h.Historial.Id_Usuario,
|
Monto = h.Historial.Monto,
|
||||||
NombreUsuarioModifico = h.NombreUsuarioModifico,
|
Id_TipoPago = h.Historial.Id_TipoPago,
|
||||||
FechaMod = h.Historial.FechaMod,
|
Detalle = h.Historial.Detalle,
|
||||||
TipoMod = h.Historial.TipoMod
|
Id_Empresa = h.Historial.Id_Empresa,
|
||||||
}).ToList();
|
Id_Usuario = h.Historial.Id_Usuario,
|
||||||
}
|
NombreUsuarioModifico = h.NombreUsuarioModifico,
|
||||||
|
FechaMod = h.Historial.FechaMod,
|
||||||
|
TipoMod = h.Historial.TipoMod
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,6 +64,7 @@ const PagoDistribuidorFormModal: React.FC<PagoDistribuidorFormModalProps> = ({
|
|||||||
const isEditing = Boolean(initialData);
|
const isEditing = Boolean(initialData);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Esta función se encarga de cargar los datos de los dropdowns.
|
||||||
const fetchDropdownData = async () => {
|
const fetchDropdownData = async () => {
|
||||||
setLoadingDropdowns(true);
|
setLoadingDropdowns(true);
|
||||||
try {
|
try {
|
||||||
@@ -133,7 +134,9 @@ const PagoDistribuidorFormModal: React.FC<PagoDistribuidorFormModalProps> = ({
|
|||||||
idTipoPago: Number(idTipoPago),
|
idTipoPago: Number(idTipoPago),
|
||||||
detalle: detalle || undefined,
|
detalle: detalle || undefined,
|
||||||
};
|
};
|
||||||
|
// << INICIO DE LA CORRECCIÓN >>
|
||||||
await onSubmit(dataToSubmit, initialData.idPago);
|
await onSubmit(dataToSubmit, initialData.idPago);
|
||||||
|
// << FIN DE LA CORRECCIÓN >>
|
||||||
} else {
|
} else {
|
||||||
const dataToSubmit: CreatePagoDistribuidorDto = {
|
const dataToSubmit: CreatePagoDistribuidorDto = {
|
||||||
idDistribuidor: Number(idDistribuidor),
|
idDistribuidor: Number(idDistribuidor),
|
||||||
@@ -147,7 +150,9 @@ const PagoDistribuidorFormModal: React.FC<PagoDistribuidorFormModalProps> = ({
|
|||||||
};
|
};
|
||||||
await onSubmit(dataToSubmit);
|
await onSubmit(dataToSubmit);
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose();
|
onClose();
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("Error en submit de PagoDistribuidorFormModal:", error);
|
console.error("Error en submit de PagoDistribuidorFormModal:", error);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
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,
|
||||||
CircularProgress, Alert, FormControl, InputLabel, Select, Tooltip
|
CircularProgress, Alert, FormControl, InputLabel, Select, Tooltip
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||||
@@ -28,11 +28,11 @@ const GestionarPagosDistribuidorPage: React.FC = () => {
|
|||||||
const [pagos, setPagos] = useState<PagoDistribuidorDto[]>([]);
|
const [pagos, setPagos] = useState<PagoDistribuidorDto[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
const [pageApiErrorMessage, setPageApiErrorMessage] = useState<string | null>(null);
|
||||||
|
const [modalApiErrorMessage, setModalApiErrorMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
// Filtros
|
const [filtroFechaDesde, setFiltroFechaDesde] = useState<string>(new Date().toISOString().split('T')[0]);
|
||||||
const [filtroFechaDesde, setFiltroFechaDesde] = useState<string>(new Date().toISOString().split('T')[0]); //useState('');
|
const [filtroFechaHasta, setFiltroFechaHasta] = useState<string>(new Date().toISOString().split('T')[0]);
|
||||||
const [filtroFechaHasta, setFiltroFechaHasta] = useState<string>(new Date().toISOString().split('T')[0]);//useState('');
|
|
||||||
const [filtroIdDistribuidor, setFiltroIdDistribuidor] = useState<number | string>('');
|
const [filtroIdDistribuidor, setFiltroIdDistribuidor] = useState<number | string>('');
|
||||||
const [filtroIdEmpresa, setFiltroIdEmpresa] = useState<number | string>('');
|
const [filtroIdEmpresa, setFiltroIdEmpresa] = useState<number | string>('');
|
||||||
const [filtroTipoMov, setFiltroTipoMov] = useState<'Recibido' | 'Realizado' | ''>('');
|
const [filtroTipoMov, setFiltroTipoMov] = useState<'Recibido' | 'Realizado' | ''>('');
|
||||||
@@ -50,7 +50,6 @@ const GestionarPagosDistribuidorPage: React.FC = () => {
|
|||||||
const [selectedRow, setSelectedRow] = useState<PagoDistribuidorDto | null>(null);
|
const [selectedRow, setSelectedRow] = useState<PagoDistribuidorDto | null>(null);
|
||||||
|
|
||||||
const { tienePermiso, isSuperAdmin } = usePermissions();
|
const { tienePermiso, isSuperAdmin } = usePermissions();
|
||||||
// Permisos CP001 (Ver), CP002 (Crear), CP003 (Modificar), CP004 (Eliminar)
|
|
||||||
const puedeVer = isSuperAdmin || tienePermiso("CP001");
|
const puedeVer = isSuperAdmin || tienePermiso("CP001");
|
||||||
const puedeCrear = isSuperAdmin || tienePermiso("CP002");
|
const puedeCrear = isSuperAdmin || tienePermiso("CP002");
|
||||||
const puedeModificar = isSuperAdmin || tienePermiso("CP003");
|
const puedeModificar = isSuperAdmin || tienePermiso("CP003");
|
||||||
@@ -59,21 +58,27 @@ const GestionarPagosDistribuidorPage: React.FC = () => {
|
|||||||
const fetchFiltersDropdownData = useCallback(async () => {
|
const fetchFiltersDropdownData = useCallback(async () => {
|
||||||
setLoadingFiltersDropdown(true);
|
setLoadingFiltersDropdown(true);
|
||||||
try {
|
try {
|
||||||
const [distData, empData] = await Promise.all([
|
const [distData, empData] = await Promise.all([
|
||||||
distribuidorService.getAllDistribuidores(),
|
distribuidorService.getAllDistribuidores(),
|
||||||
empresaService.getAllEmpresas()
|
empresaService.getAllEmpresas()
|
||||||
]);
|
]);
|
||||||
setDistribuidores(distData);
|
setDistribuidores(distData);
|
||||||
setEmpresas(empData);
|
setEmpresas(empData);
|
||||||
} catch (err) { console.error(err); setError("Error al cargar opciones de filtro.");
|
} catch (err) {
|
||||||
|
console.error(err); setError("Error al cargar opciones de filtro.");
|
||||||
} finally { setLoadingFiltersDropdown(false); }
|
} finally { setLoadingFiltersDropdown(false); }
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => { fetchFiltersDropdownData(); }, [fetchFiltersDropdownData]);
|
useEffect(() => { fetchFiltersDropdownData(); }, [fetchFiltersDropdownData]);
|
||||||
|
|
||||||
|
const clearModalApiErrorMessage = useCallback(() => {
|
||||||
|
setModalApiErrorMessage(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
const cargarPagos = useCallback(async () => {
|
const cargarPagos = useCallback(async () => {
|
||||||
if (!puedeVer) { setError("No tiene permiso."); setLoading(false); return; }
|
if (!puedeVer) { setError("No tiene permiso."); setLoading(false); return; }
|
||||||
setLoading(true); setError(null); setApiErrorMessage(null);
|
setLoading(true); setError(null); setPageApiErrorMessage(null);
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
fechaDesde: filtroFechaDesde || null, fechaHasta: filtroFechaHasta || null,
|
fechaDesde: filtroFechaDesde || null, fechaHasta: filtroFechaHasta || null,
|
||||||
@@ -83,19 +88,20 @@ const GestionarPagosDistribuidorPage: React.FC = () => {
|
|||||||
};
|
};
|
||||||
const data = await pagoDistribuidorService.getAllPagosDistribuidor(params);
|
const data = await pagoDistribuidorService.getAllPagosDistribuidor(params);
|
||||||
setPagos(data);
|
setPagos(data);
|
||||||
} catch (err) { console.error(err); setError('Error al cargar los pagos.');
|
} catch (err) {
|
||||||
|
console.error(err); setError('Error al cargar los pagos.');
|
||||||
} finally { setLoading(false); }
|
} finally { setLoading(false); }
|
||||||
}, [puedeVer, filtroFechaDesde, filtroFechaHasta, filtroIdDistribuidor, filtroIdEmpresa, filtroTipoMov]);
|
}, [puedeVer, filtroFechaDesde, filtroFechaHasta, filtroIdDistribuidor, filtroIdEmpresa, filtroTipoMov]);
|
||||||
|
|
||||||
useEffect(() => { cargarPagos(); }, [cargarPagos]);
|
useEffect(() => { cargarPagos(); }, [cargarPagos]);
|
||||||
|
|
||||||
const handleOpenModal = (item?: PagoDistribuidorDto) => {
|
const handleOpenModal = (item?: PagoDistribuidorDto) => {
|
||||||
setEditingPago(item || null); setApiErrorMessage(null); setModalOpen(true);
|
setEditingPago(item || null); setModalApiErrorMessage(null); setModalOpen(true);
|
||||||
};
|
};
|
||||||
const handleCloseModal = () => { setModalOpen(false); setEditingPago(null); };
|
const handleCloseModal = () => { setModalOpen(false); setEditingPago(null); };
|
||||||
|
|
||||||
const handleSubmitModal = async (data: CreatePagoDistribuidorDto | UpdatePagoDistribuidorDto, idPago?: number) => {
|
const handleSubmitModal = async (data: CreatePagoDistribuidorDto | UpdatePagoDistribuidorDto, idPago?: number) => {
|
||||||
setApiErrorMessage(null);
|
setModalApiErrorMessage(null);
|
||||||
try {
|
try {
|
||||||
if (idPago && editingPago) {
|
if (idPago && editingPago) {
|
||||||
await pagoDistribuidorService.updatePagoDistribuidor(idPago, data as UpdatePagoDistribuidorDto);
|
await pagoDistribuidorService.updatePagoDistribuidor(idPago, data as UpdatePagoDistribuidorDto);
|
||||||
@@ -105,15 +111,19 @@ const GestionarPagosDistribuidorPage: React.FC = () => {
|
|||||||
cargarPagos();
|
cargarPagos();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
const message = axios.isAxiosError(err) && err.response?.data?.message ? err.response.data.message : 'Error al guardar el pago.';
|
const message = axios.isAxiosError(err) && err.response?.data?.message ? err.response.data.message : 'Error al guardar el pago.';
|
||||||
setApiErrorMessage(message); throw err;
|
setModalApiErrorMessage(message);
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (idPago: number) => {
|
const handleDelete = async (idPago: number) => {
|
||||||
if (window.confirm(`¿Seguro de eliminar este pago (ID: ${idPago})? Esta acción revertirá el impacto en el saldo.`)) {
|
if (window.confirm(`¿Seguro de eliminar este pago (ID: ${idPago})? Esta acción revertirá el impacto en el saldo.`)) {
|
||||||
setApiErrorMessage(null);
|
setPageApiErrorMessage(null);
|
||||||
try { await pagoDistribuidorService.deletePagoDistribuidor(idPago); cargarPagos(); }
|
try { await pagoDistribuidorService.deletePagoDistribuidor(idPago); cargarPagos(); }
|
||||||
catch (err: any) { const msg = axios.isAxiosError(err) && err.response?.data?.message ? err.response.data.message : 'Error al eliminar.'; setApiErrorMessage(msg); }
|
catch (err: any) {
|
||||||
|
const msg = axios.isAxiosError(err) && err.response?.data?.message ? err.response.data.message : 'Error al eliminar.';
|
||||||
|
setPageApiErrorMessage(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
handleMenuClose();
|
handleMenuClose();
|
||||||
};
|
};
|
||||||
@@ -130,18 +140,11 @@ const GestionarPagosDistribuidorPage: React.FC = () => {
|
|||||||
const displayData = pagos.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
|
const displayData = pagos.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
|
||||||
const formatDate = (dateString?: string | null): string => {
|
const formatDate = (dateString?: string | null): string => {
|
||||||
if (!dateString) return '-';
|
if (!dateString) return '-';
|
||||||
// La fecha llega como "YYYY-MM-DD" o "YYYY-MM-DDTHH:mm:ss"
|
|
||||||
// Tomamos solo la parte de la fecha para evitar problemas de zona horaria.
|
|
||||||
const datePart = dateString.split('T')[0];
|
const datePart = dateString.split('T')[0];
|
||||||
const parts = datePart.split('-');
|
const parts = datePart.split('-');
|
||||||
|
|
||||||
// Aseguramos que el formato sea el esperado antes de reordenar
|
|
||||||
if (parts.length === 3) {
|
if (parts.length === 3) {
|
||||||
// parts[0] = YYYY, parts[1] = MM, parts[2] = DD
|
return `${parts[2]}/${parts[1]}/${parts[0]}`;
|
||||||
return `${parts[2]}/${parts[1]}/${parts[0]}`; // Formato DD/MM/YYYY
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si el formato no es el esperado, devolvemos la parte de la fecha tal como vino.
|
|
||||||
return datePart;
|
return datePart;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,93 +154,96 @@ const GestionarPagosDistribuidorPage: React.FC = () => {
|
|||||||
<Box sx={{ p: 1 }}>
|
<Box sx={{ p: 1 }}>
|
||||||
<Typography variant="h5" gutterBottom>Pagos de Distribuidores</Typography>
|
<Typography variant="h5" gutterBottom>Pagos de Distribuidores</Typography>
|
||||||
<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 Desde" type="date" size="small" value={filtroFechaDesde} onChange={(e) => setFiltroFechaDesde(e.target.value)} InputLabelProps={{ shrink: true }} sx={{minWidth: 170}}/>
|
<TextField label="Fecha Desde" type="date" size="small" value={filtroFechaDesde} onChange={(e) => setFiltroFechaDesde(e.target.value)} InputLabelProps={{ shrink: true }} sx={{ minWidth: 170 }} />
|
||||||
<TextField label="Fecha Hasta" type="date" size="small" value={filtroFechaHasta} onChange={(e) => setFiltroFechaHasta(e.target.value)} InputLabelProps={{ shrink: true }} sx={{minWidth: 170}}/>
|
<TextField label="Fecha Hasta" type="date" size="small" value={filtroFechaHasta} onChange={(e) => setFiltroFechaHasta(e.target.value)} InputLabelProps={{ shrink: true }} sx={{ minWidth: 170 }} />
|
||||||
<FormControl size="small" sx={{minWidth: 200, flexGrow: 1}} disabled={loadingFiltersDropdown}>
|
<FormControl size="small" sx={{ minWidth: 200, flexGrow: 1 }} disabled={loadingFiltersDropdown}>
|
||||||
<InputLabel>Distribuidor</InputLabel>
|
<InputLabel>Distribuidor</InputLabel>
|
||||||
<Select value={filtroIdDistribuidor} label="Distribuidor" onChange={(e) => setFiltroIdDistribuidor(e.target.value as number | string)}>
|
<Select value={filtroIdDistribuidor} label="Distribuidor" onChange={(e) => setFiltroIdDistribuidor(e.target.value as number | string)}>
|
||||||
<MenuItem value=""><em>Todos</em></MenuItem>
|
<MenuItem value=""><em>Todos</em></MenuItem>
|
||||||
{distribuidores.map(d => <MenuItem key={d.idDistribuidor} value={d.idDistribuidor}>{d.nombre}</MenuItem>)}
|
{distribuidores.map(d => <MenuItem key={d.idDistribuidor} value={d.idDistribuidor}>{d.nombre}</MenuItem>)}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl size="small" sx={{minWidth: 200, flexGrow: 1}} disabled={loadingFiltersDropdown}>
|
<FormControl size="small" sx={{ minWidth: 200, flexGrow: 1 }} disabled={loadingFiltersDropdown}>
|
||||||
<InputLabel>Empresa (Saldo)</InputLabel>
|
<InputLabel>Empresa (Saldo)</InputLabel>
|
||||||
<Select value={filtroIdEmpresa} label="Empresa (Saldo)" onChange={(e) => setFiltroIdEmpresa(e.target.value as number | string)}>
|
<Select value={filtroIdEmpresa} label="Empresa (Saldo)" onChange={(e) => setFiltroIdEmpresa(e.target.value as number | string)}>
|
||||||
<MenuItem value=""><em>Todas</em></MenuItem>
|
<MenuItem value=""><em>Todas</em></MenuItem>
|
||||||
{empresas.map(e => <MenuItem key={e.idEmpresa} value={e.idEmpresa}>{e.nombre}</MenuItem>)}
|
{empresas.map(e => <MenuItem key={e.idEmpresa} value={e.idEmpresa}>{e.nombre}</MenuItem>)}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl size="small" sx={{minWidth: 150, flexGrow: 1}}>
|
<FormControl size="small" sx={{ minWidth: 150, flexGrow: 1 }}>
|
||||||
<InputLabel>Tipo Mov.</InputLabel>
|
<InputLabel>Tipo Mov.</InputLabel>
|
||||||
<Select value={filtroTipoMov} label="Tipo Mov." onChange={(e) => setFiltroTipoMov(e.target.value as 'Recibido' | 'Realizado' | '')}>
|
<Select value={filtroTipoMov} label="Tipo Mov." onChange={(e) => setFiltroTipoMov(e.target.value as 'Recibido' | 'Realizado' | '')}>
|
||||||
<MenuItem value=""><em>Todos</em></MenuItem>
|
<MenuItem value=""><em>Todos</em></MenuItem>
|
||||||
<MenuItem value="Recibido">Recibido</MenuItem>
|
<MenuItem value="Recibido">Recibido</MenuItem>
|
||||||
<MenuItem value="Realizado">Realizado</MenuItem>
|
<MenuItem value="Realizado">Realizado</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Box>
|
</Box>
|
||||||
{puedeCrear && (<Button variant="contained" startIcon={<AddIcon />} onClick={() => handleOpenModal()}>Registrar Pago</Button>)}
|
{puedeCrear && (<Button variant="contained" startIcon={<AddIcon />} onClick={() => handleOpenModal()}>Registrar Pago</Button>)}
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{loading && <Box sx={{ display: 'flex', justifyContent: 'center', my: 2 }}><CircularProgress /></Box>}
|
{loading && <Box sx={{ display: 'flex', justifyContent: '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>}
|
||||||
{apiErrorMessage && <Alert severity="error" sx={{my: 2}}>{apiErrorMessage}</Alert>}
|
{pageApiErrorMessage && <Alert severity="error" sx={{ my: 2 }}>{pageApiErrorMessage}</Alert>}
|
||||||
|
|
||||||
{!loading && !error && puedeVer && (
|
{!loading && !error && puedeVer && (
|
||||||
<TableContainer component={Paper}>
|
<TableContainer component={Paper}>
|
||||||
<Table size="small">
|
<Table size="small">
|
||||||
<TableHead><TableRow>
|
<TableHead><TableRow>
|
||||||
<TableCell>Fecha</TableCell><TableCell>Distribuidor</TableCell><TableCell>Empresa (Saldo)</TableCell>
|
<TableCell>Fecha</TableCell><TableCell>Distribuidor</TableCell><TableCell>Empresa (Saldo)</TableCell>
|
||||||
<TableCell>Tipo Mov.</TableCell><TableCell>Recibo N°</TableCell>
|
<TableCell>Tipo Mov.</TableCell><TableCell>Recibo N°</TableCell>
|
||||||
<TableCell align="right">Monto</TableCell><TableCell>Tipo Pago</TableCell>
|
<TableCell align="right">Monto</TableCell><TableCell>Tipo Pago</TableCell>
|
||||||
<TableCell>Detalle</TableCell>
|
<TableCell>Detalle</TableCell>
|
||||||
{(puedeModificar || puedeEliminar) && <TableCell align="right">Acciones</TableCell>}
|
{(puedeModificar || puedeEliminar) && <TableCell align="right">Acciones</TableCell>}
|
||||||
</TableRow></TableHead>
|
</TableRow></TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{displayData.length === 0 ? (
|
{displayData.length === 0 ? (
|
||||||
<TableRow><TableCell colSpan={puedeModificar || puedeEliminar ? 9 : 8} align="center">No se encontraron pagos.</TableCell></TableRow>
|
<TableRow><TableCell colSpan={puedeModificar || puedeEliminar ? 9 : 8} align="center">No se encontraron pagos.</TableCell></TableRow>
|
||||||
) : (
|
) : (
|
||||||
displayData.map((p) => (
|
displayData.map((p) => (
|
||||||
<TableRow key={p.idPago} hover>
|
<TableRow key={p.idPago} hover>
|
||||||
<TableCell>{formatDate(p.fecha)}</TableCell><TableCell>{p.nombreDistribuidor}</TableCell>
|
<TableCell>{formatDate(p.fecha)}</TableCell><TableCell>{p.nombreDistribuidor}</TableCell>
|
||||||
<TableCell>{p.nombreEmpresa}</TableCell>
|
<TableCell>{p.nombreEmpresa}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Chip label={p.tipoMovimiento} color={p.tipoMovimiento === 'Recibido' ? 'success' : 'warning'} size="small"/>
|
<Chip label={p.tipoMovimiento} color={p.tipoMovimiento === 'Recibido' ? 'success' : 'warning'} size="small" />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{p.recibo}</TableCell>
|
<TableCell>{p.recibo}</TableCell>
|
||||||
<TableCell align="right">${p.monto.toFixed(2)}</TableCell>
|
<TableCell align="right">${p.monto.toFixed(2)}</TableCell>
|
||||||
<TableCell>{p.nombreTipoPago}</TableCell>
|
<TableCell>{p.nombreTipoPago}</TableCell>
|
||||||
<TableCell><Tooltip title={p.detalle || ''}><Box sx={{maxWidth: 150, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}}>{p.detalle || '-'}</Box></Tooltip></TableCell>
|
<TableCell><Tooltip title={p.detalle || ''}><Box sx={{ maxWidth: 150, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.detalle || '-'}</Box></Tooltip></TableCell>
|
||||||
{(puedeModificar || puedeEliminar) && (
|
{(puedeModificar || puedeEliminar) && (
|
||||||
<TableCell align="right">
|
<TableCell align="right">
|
||||||
<IconButton onClick={(e) => handleMenuOpen(e, p)} disabled={!puedeModificar && !puedeEliminar}><MoreVertIcon /></IconButton>
|
<IconButton onClick={(e) => handleMenuOpen(e, p)} disabled={!puedeModificar && !puedeEliminar}><MoreVertIcon /></IconButton>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)))}
|
)))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[25, 50, 100]} 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:"
|
||||||
/>
|
/>
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleMenuClose}>
|
<Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleMenuClose}>
|
||||||
{puedeModificar && selectedRow && (
|
{puedeModificar && selectedRow && (
|
||||||
<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>)}
|
||||||
{puedeEliminar && selectedRow && (
|
{puedeEliminar && selectedRow && (
|
||||||
<MenuItem onClick={() => handleDelete(selectedRow.idPago)}><DeleteIcon fontSize="small" sx={{mr:1}}/> Eliminar</MenuItem>)}
|
<MenuItem onClick={() => handleDelete(selectedRow.idPago)}><DeleteIcon fontSize="small" sx={{ mr: 1 }} /> Eliminar</MenuItem>)}
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
||||||
<PagoDistribuidorFormModal
|
<PagoDistribuidorFormModal
|
||||||
open={modalOpen} onClose={handleCloseModal} onSubmit={handleSubmitModal}
|
open={modalOpen}
|
||||||
initialData={editingPago} errorMessage={apiErrorMessage}
|
onClose={handleCloseModal}
|
||||||
clearErrorMessage={() => setApiErrorMessage(null)}
|
onSubmit={handleSubmitModal}
|
||||||
|
initialData={editingPago}
|
||||||
|
errorMessage={modalApiErrorMessage}
|
||||||
|
clearErrorMessage={clearModalApiErrorMessage}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user