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);
|
||
}
|
||
}
|