feat: Implementación de Secciones, Recargos, Porc. Pago Dist. y backend E/S Dist.
Backend API:
- Recargos por Zona (`dist_RecargoZona`):
- CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Endpoints anidados bajo `/publicaciones/{idPublicacion}/recargos`.
- Lógica de negocio para vigencias (cierre/reapertura de períodos).
- Auditoría en `dist_RecargoZona_H`.
- Porcentajes de Pago Distribuidores (`dist_PorcPago`):
- CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Endpoints anidados bajo `/publicaciones/{idPublicacion}/porcentajespago`.
- Lógica de negocio para vigencias.
- Auditoría en `dist_PorcPago_H`.
- Porcentajes/Montos Pago Canillitas (`dist_PorcMonPagoCanilla`):
- CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Endpoints anidados bajo `/publicaciones/{idPublicacion}/porcentajesmoncanilla`.
- Lógica de negocio para vigencias.
- Auditoría en `dist_PorcMonPagoCanilla_H`.
- Secciones de Publicación (`dist_dtPubliSecciones`):
- CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Endpoints anidados bajo `/publicaciones/{idPublicacion}/secciones`.
- Auditoría en `dist_dtPubliSecciones_H`.
- Entradas/Salidas Distribuidores (`dist_EntradasSalidas`):
- Implementado backend (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Lógica para determinar precios/recargos/porcentajes aplicables.
- Cálculo de monto y afectación de saldos de distribuidores en `cue_Saldos`.
- Auditoría en `dist_EntradasSalidas_H`.
- Correcciones de Mapeo Dapper:
- Aplicados alias explícitos en repositorios de RecargoZona, PorcPago, PorcMonCanilla, PubliSeccion,
Canilla, Distribuidor y Precio para asegurar mapeo correcto de IDs y columnas.
Frontend React:
- Recargos por Zona:
- `recargoZonaService.ts`.
- `RecargoZonaFormModal.tsx` para crear/editar períodos de recargos.
- `GestionarRecargosPublicacionPage.tsx` para listar y gestionar recargos por publicación.
- Porcentajes de Pago Distribuidores:
- `porcPagoService.ts`.
- `PorcPagoFormModal.tsx`.
- `GestionarPorcentajesPagoPage.tsx`.
- Porcentajes/Montos Pago Canillitas:
- `porcMonCanillaService.ts`.
- `PorcMonCanillaFormModal.tsx`.
- `GestionarPorcMonCanillaPage.tsx`.
- Secciones de Publicación:
- `publiSeccionService.ts`.
- `PubliSeccionFormModal.tsx`.
- `GestionarSeccionesPublicacionPage.tsx`.
- Navegación:
- Actualizadas rutas y menús para acceder a la gestión de recargos, porcentajes (dist. y canillita) y secciones desde la vista de una publicación.
- Layout:
- Uso consistente de `Box` con Flexbox en lugar de `Grid` en nuevos modales y páginas para evitar errores de tipo.
This commit is contained in:
@@ -0,0 +1,355 @@
|
||||
using GestionIntegral.Api.Data;
|
||||
using GestionIntegral.Api.Data.Repositories.Contables;
|
||||
using GestionIntegral.Api.Data.Repositories.Distribucion;
|
||||
using GestionIntegral.Api.Dtos.Distribucion;
|
||||
using GestionIntegral.Api.Models.Distribucion;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GestionIntegral.Api.Services.Distribucion
|
||||
{
|
||||
public class EntradaSalidaDistService : IEntradaSalidaDistService
|
||||
{
|
||||
private readonly IEntradaSalidaDistRepository _esRepository;
|
||||
private readonly IPublicacionRepository _publicacionRepository;
|
||||
private readonly IDistribuidorRepository _distribuidorRepository;
|
||||
private readonly IPrecioRepository _precioRepository;
|
||||
private readonly IRecargoZonaRepository _recargoZonaRepository;
|
||||
private readonly IPorcPagoRepository _porcPagoRepository;
|
||||
private readonly ISaldoRepository _saldoRepository;
|
||||
private readonly IEmpresaRepository _empresaRepository; // Para obtener IdEmpresa de la publicación
|
||||
private readonly DbConnectionFactory _connectionFactory;
|
||||
private readonly ILogger<EntradaSalidaDistService> _logger;
|
||||
|
||||
public EntradaSalidaDistService(
|
||||
IEntradaSalidaDistRepository esRepository,
|
||||
IPublicacionRepository publicacionRepository,
|
||||
IDistribuidorRepository distribuidorRepository,
|
||||
IPrecioRepository precioRepository,
|
||||
IRecargoZonaRepository recargoZonaRepository,
|
||||
IPorcPagoRepository porcPagoRepository,
|
||||
ISaldoRepository saldoRepository,
|
||||
IEmpresaRepository empresaRepository,
|
||||
DbConnectionFactory connectionFactory,
|
||||
ILogger<EntradaSalidaDistService> logger)
|
||||
{
|
||||
_esRepository = esRepository;
|
||||
_publicacionRepository = publicacionRepository;
|
||||
_distribuidorRepository = distribuidorRepository;
|
||||
_precioRepository = precioRepository;
|
||||
_recargoZonaRepository = recargoZonaRepository;
|
||||
_porcPagoRepository = porcPagoRepository;
|
||||
_saldoRepository = saldoRepository;
|
||||
_empresaRepository = empresaRepository;
|
||||
_connectionFactory = connectionFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private async Task<EntradaSalidaDistDto> MapToDto(EntradaSalidaDist es)
|
||||
{
|
||||
if (es == null) return null!;
|
||||
|
||||
var publicacionData = await _publicacionRepository.GetByIdAsync(es.IdPublicacion);
|
||||
var distribuidorData = await _distribuidorRepository.GetByIdAsync(es.IdDistribuidor);
|
||||
|
||||
decimal montoCalculado = await CalcularMontoMovimiento(es.IdPublicacion, es.IdDistribuidor, es.Fecha, es.Cantidad, es.TipoMovimiento,
|
||||
es.IdPrecio, es.IdRecargo, es.IdPorcentaje, distribuidorData.Distribuidor?.IdZona);
|
||||
|
||||
|
||||
return new EntradaSalidaDistDto
|
||||
{
|
||||
IdParte = es.IdParte,
|
||||
IdPublicacion = es.IdPublicacion,
|
||||
NombrePublicacion = publicacionData.Publicacion?.Nombre ?? "N/A",
|
||||
IdEmpresaPublicacion = publicacionData.Publicacion?.IdEmpresa ?? 0,
|
||||
NombreEmpresaPublicacion = publicacionData.NombreEmpresa ?? "N/A",
|
||||
IdDistribuidor = es.IdDistribuidor,
|
||||
NombreDistribuidor = distribuidorData.Distribuidor?.Nombre ?? "N/A",
|
||||
Fecha = es.Fecha.ToString("yyyy-MM-dd"),
|
||||
TipoMovimiento = es.TipoMovimiento,
|
||||
Cantidad = es.Cantidad,
|
||||
Remito = es.Remito,
|
||||
Observacion = es.Observacion,
|
||||
MontoCalculado = montoCalculado
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<decimal> CalcularMontoMovimiento(int idPublicacion, int idDistribuidor, DateTime fecha, int cantidad, string tipoMovimiento,
|
||||
int idPrecio, int idRecargo, int idPorcentaje, int? idZonaDistribuidor)
|
||||
{
|
||||
if (tipoMovimiento == "Entrada") return 0; // Las entradas (devoluciones) no generan "debe" inmediato, ajustan el saldo.
|
||||
|
||||
var precioConfig = await _precioRepository.GetByIdAsync(idPrecio);
|
||||
if (precioConfig == null) throw new InvalidOperationException("Configuración de precio no encontrada para el movimiento.");
|
||||
|
||||
decimal precioDia = 0;
|
||||
DayOfWeek diaSemana = fecha.DayOfWeek;
|
||||
switch (diaSemana)
|
||||
{
|
||||
case DayOfWeek.Monday: precioDia = precioConfig.Lunes ?? 0; break;
|
||||
case DayOfWeek.Tuesday: precioDia = precioConfig.Martes ?? 0; break;
|
||||
case DayOfWeek.Wednesday: precioDia = precioConfig.Miercoles ?? 0; break;
|
||||
case DayOfWeek.Thursday: precioDia = precioConfig.Jueves ?? 0; break;
|
||||
case DayOfWeek.Friday: precioDia = precioConfig.Viernes ?? 0; break;
|
||||
case DayOfWeek.Saturday: precioDia = precioConfig.Sabado ?? 0; break;
|
||||
case DayOfWeek.Sunday: precioDia = precioConfig.Domingo ?? 0; break;
|
||||
}
|
||||
|
||||
decimal valorRecargo = 0;
|
||||
if (idRecargo > 0 && idZonaDistribuidor.HasValue) // El recargo se aplica por la zona del distribuidor
|
||||
{
|
||||
// Necesitamos encontrar el recargo activo para la publicación, la zona del distribuidor y la fecha
|
||||
var recargoConfig = await _recargoZonaRepository.GetActiveByPublicacionZonaAndDateAsync(idPublicacion, idZonaDistribuidor.Value, fecha);
|
||||
if (recargoConfig != null)
|
||||
{
|
||||
valorRecargo = recargoConfig.Valor;
|
||||
}
|
||||
}
|
||||
|
||||
decimal precioConRecargo = precioDia + valorRecargo;
|
||||
decimal montoBase = precioConRecargo * cantidad;
|
||||
|
||||
if (idPorcentaje > 0)
|
||||
{
|
||||
var porcConfig = await _porcPagoRepository.GetByIdAsync(idPorcentaje);
|
||||
if (porcConfig != null)
|
||||
{
|
||||
return (montoBase / 100) * porcConfig.Porcentaje;
|
||||
}
|
||||
}
|
||||
return montoBase; // Si no hay porcentaje, se factura el 100% del precio con recargo
|
||||
}
|
||||
|
||||
|
||||
public async Task<IEnumerable<EntradaSalidaDistDto>> ObtenerTodosAsync(DateTime? fechaDesde, DateTime? fechaHasta, int? idPublicacion, int? idDistribuidor, string? tipoMovimiento)
|
||||
{
|
||||
var movimientos = await _esRepository.GetAllAsync(fechaDesde, fechaHasta, idPublicacion, idDistribuidor, tipoMovimiento);
|
||||
var dtos = new List<EntradaSalidaDistDto>();
|
||||
foreach (var mov in movimientos)
|
||||
{
|
||||
dtos.Add(await MapToDto(mov));
|
||||
}
|
||||
return dtos;
|
||||
}
|
||||
|
||||
public async Task<EntradaSalidaDistDto?> ObtenerPorIdAsync(int idParte)
|
||||
{
|
||||
var movimiento = await _esRepository.GetByIdAsync(idParte);
|
||||
return movimiento == null ? null : await MapToDto(movimiento);
|
||||
}
|
||||
|
||||
public async Task<(EntradaSalidaDistDto? Movimiento, string? Error)> CrearMovimientoAsync(CreateEntradaSalidaDistDto createDto, int idUsuario)
|
||||
{
|
||||
var publicacion = await _publicacionRepository.GetByIdSimpleAsync(createDto.IdPublicacion);
|
||||
if (publicacion == null || publicacion.Habilitada != true) return (null, "Publicación no válida o no habilitada.");
|
||||
|
||||
var distribuidor = await _distribuidorRepository.GetByIdSimpleAsync(createDto.IdDistribuidor);
|
||||
if (distribuidor == null) return (null, "Distribuidor no válido.");
|
||||
|
||||
if (await _esRepository.ExistsByRemitoAndTipoForPublicacionAsync(createDto.Remito, createDto.TipoMovimiento, createDto.IdPublicacion))
|
||||
{
|
||||
return (null, $"Ya existe un movimiento de '{createDto.TipoMovimiento}' con el remito N°{createDto.Remito} para esta publicación.");
|
||||
}
|
||||
|
||||
// Determinar IDs de Precio, Recargo y Porcentaje activos en la fecha del movimiento
|
||||
var precioActivo = await _precioRepository.GetActiveByPublicacionAndDateAsync(createDto.IdPublicacion, createDto.Fecha.Date);
|
||||
if (precioActivo == null) return (null, $"No hay un precio definido para la publicación '{publicacion.Nombre}' en la fecha {createDto.Fecha:dd/MM/yyyy}.");
|
||||
|
||||
RecargoZona? recargoActivo = null;
|
||||
if (distribuidor.IdZona.HasValue)
|
||||
{
|
||||
recargoActivo = await _recargoZonaRepository.GetActiveByPublicacionZonaAndDateAsync(createDto.IdPublicacion, distribuidor.IdZona.Value, createDto.Fecha.Date);
|
||||
}
|
||||
|
||||
var porcPagoActivo = await _porcPagoRepository.GetActiveByPublicacionDistribuidorAndDateAsync(createDto.IdPublicacion, createDto.IdDistribuidor, createDto.Fecha.Date);
|
||||
|
||||
var nuevoES = new EntradaSalidaDist
|
||||
{
|
||||
IdPublicacion = createDto.IdPublicacion,
|
||||
IdDistribuidor = createDto.IdDistribuidor,
|
||||
Fecha = createDto.Fecha.Date,
|
||||
TipoMovimiento = createDto.TipoMovimiento,
|
||||
Cantidad = createDto.Cantidad,
|
||||
Remito = createDto.Remito,
|
||||
Observacion = createDto.Observacion,
|
||||
IdPrecio = precioActivo.IdPrecio,
|
||||
IdRecargo = recargoActivo?.IdRecargo ?? 0, // 0 si no aplica
|
||||
IdPorcentaje = porcPagoActivo?.IdPorcentaje ?? 0 // 0 si no aplica
|
||||
};
|
||||
|
||||
using var connection = _connectionFactory.CreateConnection();
|
||||
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
||||
using var transaction = connection.BeginTransaction();
|
||||
try
|
||||
{
|
||||
var esCreada = await _esRepository.CreateAsync(nuevoES, idUsuario, transaction);
|
||||
if (esCreada == null) throw new DataException("Error al registrar el movimiento.");
|
||||
|
||||
// Afectar Saldo
|
||||
decimal montoAfectacion = await CalcularMontoMovimiento(
|
||||
esCreada.IdPublicacion, esCreada.IdDistribuidor, esCreada.Fecha, esCreada.Cantidad, esCreada.TipoMovimiento,
|
||||
esCreada.IdPrecio, esCreada.IdRecargo, esCreada.IdPorcentaje, distribuidor.IdZona);
|
||||
|
||||
// Si es Salida, montoAfectacion es positivo (aumenta deuda). Si es Entrada, es 0 por CalcularMontoMovimiento,
|
||||
// pero para el saldo, una Entrada (devolución) debería restar del saldo deudor.
|
||||
// Por lo tanto, el monto real a restar del saldo si es Entrada sería el valor de esos ejemplares devueltos.
|
||||
if(esCreada.TipoMovimiento == "Entrada")
|
||||
{
|
||||
// Recalcular como si fuera salida para obtener el valor a acreditar/restar del saldo
|
||||
montoAfectacion = await CalcularMontoMovimiento(
|
||||
esCreada.IdPublicacion, esCreada.IdDistribuidor, esCreada.Fecha, esCreada.Cantidad, "Salida", // Forzar tipo Salida para cálculo de valor
|
||||
esCreada.IdPrecio, esCreada.IdRecargo, esCreada.IdPorcentaje, distribuidor.IdZona);
|
||||
montoAfectacion *= -1; // Hacerlo negativo para restar del saldo
|
||||
}
|
||||
|
||||
|
||||
bool saldoActualizado = await _saldoRepository.ModificarSaldoAsync("Distribuidores", esCreada.IdDistribuidor, publicacion.IdEmpresa, montoAfectacion, transaction);
|
||||
if (!saldoActualizado) throw new DataException("Error al actualizar el saldo del distribuidor.");
|
||||
|
||||
transaction.Commit();
|
||||
_logger.LogInformation("Movimiento ID {Id} creado y saldo afectado por Usuario ID {UserId}.", esCreada.IdParte, idUsuario);
|
||||
return (await MapToDto(esCreada), null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
_logger.LogError(ex, "Error CrearMovimientoAsync para Pub ID {IdPub}", createDto.IdPublicacion);
|
||||
return (null, $"Error interno: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool Exito, string? Error)> ActualizarMovimientoAsync(int idParte, UpdateEntradaSalidaDistDto updateDto, int idUsuario)
|
||||
{
|
||||
// La actualización de un movimiento que afecta saldos es compleja.
|
||||
// Si cambia la cantidad, el monto original y el nuevo deben calcularse,
|
||||
// y la diferencia debe aplicarse al saldo.
|
||||
// Por ahora, este DTO solo permite cambiar Cantidad y Observacion.
|
||||
// Cambiar otros campos como Fecha, Publicacion, Distribuidor implicaría recalcular todo
|
||||
// y posiblemente anular el movimiento original y crear uno nuevo.
|
||||
|
||||
using var connection = _connectionFactory.CreateConnection();
|
||||
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
||||
using var transaction = connection.BeginTransaction();
|
||||
try
|
||||
{
|
||||
var esExistente = await _esRepository.GetByIdAsync(idParte);
|
||||
if (esExistente == null) return (false, "Movimiento no encontrado.");
|
||||
|
||||
var publicacion = await _publicacionRepository.GetByIdSimpleAsync(esExistente.IdPublicacion);
|
||||
if (publicacion == null) return (false, "Publicación asociada no encontrada."); // Muy raro
|
||||
var distribuidor = await _distribuidorRepository.GetByIdSimpleAsync(esExistente.IdDistribuidor);
|
||||
if (distribuidor == null) return (false, "Distribuidor asociado no encontrado.");
|
||||
|
||||
|
||||
// 1. Calcular monto del movimiento original (antes de la actualización)
|
||||
decimal montoOriginal = await CalcularMontoMovimiento(
|
||||
esExistente.IdPublicacion, esExistente.IdDistribuidor, esExistente.Fecha, esExistente.Cantidad, esExistente.TipoMovimiento,
|
||||
esExistente.IdPrecio, esExistente.IdRecargo, esExistente.IdPorcentaje, distribuidor.IdZona);
|
||||
if(esExistente.TipoMovimiento == "Entrada") montoOriginal *= -1; // Para revertir
|
||||
|
||||
// 2. Actualizar la entidad en la BD (esto también guarda en historial)
|
||||
var esParaActualizar = new EntradaSalidaDist
|
||||
{
|
||||
IdParte = esExistente.IdParte,
|
||||
IdPublicacion = esExistente.IdPublicacion, // No cambia
|
||||
IdDistribuidor = esExistente.IdDistribuidor, // No cambia
|
||||
Fecha = esExistente.Fecha, // No cambia
|
||||
TipoMovimiento = esExistente.TipoMovimiento, // No cambia
|
||||
Cantidad = updateDto.Cantidad, // Nuevo valor
|
||||
Remito = esExistente.Remito, // No cambia
|
||||
Observacion = updateDto.Observacion, // Nuevo valor
|
||||
IdPrecio = esExistente.IdPrecio, // No cambia
|
||||
IdRecargo = esExistente.IdRecargo, // No cambia
|
||||
IdPorcentaje = esExistente.IdPorcentaje // No cambia
|
||||
};
|
||||
var actualizado = await _esRepository.UpdateAsync(esParaActualizar, idUsuario, transaction);
|
||||
if (!actualizado) throw new DataException("Error al actualizar el movimiento.");
|
||||
|
||||
// 3. Calcular monto del movimiento nuevo (después de la actualización)
|
||||
decimal montoNuevo = await CalcularMontoMovimiento(
|
||||
esParaActualizar.IdPublicacion, esParaActualizar.IdDistribuidor, esParaActualizar.Fecha, esParaActualizar.Cantidad, esParaActualizar.TipoMovimiento,
|
||||
esParaActualizar.IdPrecio, esParaActualizar.IdRecargo, esParaActualizar.IdPorcentaje, distribuidor.IdZona);
|
||||
if(esParaActualizar.TipoMovimiento == "Entrada") montoNuevo *= -1;
|
||||
|
||||
|
||||
// 4. Ajustar saldo con la diferencia
|
||||
decimal diferenciaAjusteSaldo = montoNuevo - montoOriginal;
|
||||
if (diferenciaAjusteSaldo != 0)
|
||||
{
|
||||
bool saldoActualizado = await _saldoRepository.ModificarSaldoAsync("Distribuidores", esExistente.IdDistribuidor, publicacion.IdEmpresa, diferenciaAjusteSaldo, transaction);
|
||||
if (!saldoActualizado) throw new DataException("Error al ajustar el saldo del distribuidor tras la actualización.");
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
_logger.LogInformation("Movimiento ID {Id} actualizado y saldo ajustado por Usuario ID {UserId}.", idParte, idUsuario);
|
||||
return (true, null);
|
||||
}
|
||||
catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Movimiento no encontrado."); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
_logger.LogError(ex, "Error ActualizarMovimientoAsync ID: {Id}", idParte);
|
||||
return (false, $"Error interno: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool Exito, string? Error)> EliminarMovimientoAsync(int idParte, int idUsuario)
|
||||
{
|
||||
using var connection = _connectionFactory.CreateConnection();
|
||||
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
||||
using var transaction = connection.BeginTransaction();
|
||||
try
|
||||
{
|
||||
var esExistente = await _esRepository.GetByIdAsync(idParte);
|
||||
if (esExistente == null) return (false, "Movimiento no encontrado.");
|
||||
|
||||
var publicacion = await _publicacionRepository.GetByIdSimpleAsync(esExistente.IdPublicacion);
|
||||
if (publicacion == null) return (false, "Publicación asociada no encontrada.");
|
||||
var distribuidor = await _distribuidorRepository.GetByIdSimpleAsync(esExistente.IdDistribuidor);
|
||||
if (distribuidor == null) return (false, "Distribuidor asociado no encontrado.");
|
||||
|
||||
// 1. Calcular el monto del movimiento a eliminar para revertir el saldo
|
||||
decimal montoReversion = await CalcularMontoMovimiento(
|
||||
esExistente.IdPublicacion, esExistente.IdDistribuidor, esExistente.Fecha, esExistente.Cantidad, esExistente.TipoMovimiento,
|
||||
esExistente.IdPrecio, esExistente.IdRecargo, esExistente.IdPorcentaje, distribuidor.IdZona);
|
||||
|
||||
// Si es Salida, el monto es positivo, al revertir restamos.
|
||||
// Si es Entrada, el monto es 0 (por Calcular...), pero su efecto en saldo fue negativo, al revertir sumamos el valor.
|
||||
if(esExistente.TipoMovimiento == "Salida") {
|
||||
montoReversion *= -1; // Para restar del saldo lo que se sumó
|
||||
} else if (esExistente.TipoMovimiento == "Entrada") {
|
||||
// Recalcular el valor como si fuera salida para saber cuánto se restó del saldo
|
||||
montoReversion = await CalcularMontoMovimiento(
|
||||
esExistente.IdPublicacion, esExistente.IdDistribuidor, esExistente.Fecha, esExistente.Cantidad, "Salida",
|
||||
esExistente.IdPrecio, esExistente.IdRecargo, esExistente.IdPorcentaje, distribuidor.IdZona);
|
||||
// No se multiplica por -1 aquí, porque el saldo ya lo tiene restado, al eliminar revertimos sumando.
|
||||
}
|
||||
|
||||
|
||||
// 2. Eliminar el registro (esto también guarda en historial)
|
||||
var eliminado = await _esRepository.DeleteAsync(idParte, idUsuario, transaction);
|
||||
if (!eliminado) throw new DataException("Error al eliminar el movimiento.");
|
||||
|
||||
// 3. Ajustar Saldo
|
||||
bool saldoActualizado = await _saldoRepository.ModificarSaldoAsync("Distribuidores", esExistente.IdDistribuidor, publicacion.IdEmpresa, montoReversion, transaction);
|
||||
if (!saldoActualizado) throw new DataException("Error al revertir el saldo del distribuidor tras la eliminación.");
|
||||
|
||||
transaction.Commit();
|
||||
_logger.LogInformation("Movimiento ID {Id} eliminado y saldo revertido por Usuario ID {UserId}.", idParte, idUsuario);
|
||||
return (true, null);
|
||||
}
|
||||
catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Movimiento no encontrado."); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
_logger.LogError(ex, "Error EliminarMovimientoAsync ID: {Id}", idParte);
|
||||
return (false, $"Error interno: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user