72 lines
2.6 KiB
C#
72 lines
2.6 KiB
C#
|
|
using System.Transactions;
|
|||
|
|
using SIGCM2.Application.Abstractions;
|
|||
|
|
using SIGCM2.Application.Abstractions.Persistence;
|
|||
|
|
using SIGCM2.Application.Audit;
|
|||
|
|
using SIGCM2.Application.IngresosBrutos.Dtos;
|
|||
|
|
using SIGCM2.Domain.Exceptions;
|
|||
|
|
|
|||
|
|
namespace SIGCM2.Application.IngresosBrutos.NuevaVersion;
|
|||
|
|
|
|||
|
|
public sealed class NuevaVersionIngresosBrutosCommandHandler
|
|||
|
|
: ICommandHandler<NuevaVersionIngresosBrutosCommand, NuevaVersionIibbResultDto>
|
|||
|
|
{
|
|||
|
|
private readonly IIngresosBrutosRepository _repo;
|
|||
|
|
private readonly IAuditLogger _audit;
|
|||
|
|
|
|||
|
|
public NuevaVersionIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit)
|
|||
|
|
{
|
|||
|
|
_repo = repo;
|
|||
|
|
_audit = audit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task<NuevaVersionIibbResultDto> Handle(NuevaVersionIngresosBrutosCommand command)
|
|||
|
|
{
|
|||
|
|
// Step 1: load predecesora
|
|||
|
|
var predecesora = await _repo.GetByIdAsync(command.PredecesoraId)
|
|||
|
|
?? throw new IngresosBrutosNotFoundException(command.PredecesoraId);
|
|||
|
|
|
|||
|
|
// Step 2: guard — predecesora must be open and active
|
|||
|
|
if (!predecesora.Activo || predecesora.VigenciaHasta is not null)
|
|||
|
|
throw new PredecesorYaCerradoException(command.PredecesoraId);
|
|||
|
|
|
|||
|
|
// Steps 3–4: domain validation + tuple creation (throws ArgumentException if vigencia invalid)
|
|||
|
|
var (predecesoraCerrada, nuevaVersion) = predecesora.NuevaVersion(
|
|||
|
|
command.NuevaAlicuota,
|
|||
|
|
command.VigenciaDesde);
|
|||
|
|
|
|||
|
|
using var tx = new TransactionScope(
|
|||
|
|
TransactionScopeOption.Required,
|
|||
|
|
new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted },
|
|||
|
|
TransactionScopeAsyncFlowOption.Enabled);
|
|||
|
|
|
|||
|
|
// Step 5: optimistic close — race guard
|
|||
|
|
var closed = await _repo.UpdateCierreVigenciaAsync(
|
|||
|
|
command.PredecesoraId,
|
|||
|
|
predecesoraCerrada.VigenciaHasta!.Value);
|
|||
|
|
|
|||
|
|
if (!closed)
|
|||
|
|
throw new PredecesorYaCerradoException(command.PredecesoraId);
|
|||
|
|
|
|||
|
|
// Step 6: insert new version
|
|||
|
|
var nuevoId = await _repo.InsertAsync(nuevaVersion);
|
|||
|
|
|
|||
|
|
// Step 7: audit (fail-closed)
|
|||
|
|
await _audit.LogAsync(
|
|||
|
|
action: "ingresos_brutos.nueva_version",
|
|||
|
|
targetType: "IngresosBrutos",
|
|||
|
|
targetId: nuevoId.ToString(),
|
|||
|
|
metadata: new
|
|||
|
|
{
|
|||
|
|
predecesoraId = command.PredecesoraId,
|
|||
|
|
nuevoId,
|
|||
|
|
alicuotaNueva = command.NuevaAlicuota,
|
|||
|
|
vigenciaDesde = command.VigenciaDesde,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Step 8: commit
|
|||
|
|
tx.Complete();
|
|||
|
|
|
|||
|
|
return new NuevaVersionIibbResultDto(command.PredecesoraId, nuevoId);
|
|||
|
|
}
|
|||
|
|
}
|