feat(adm-009): TipoDeIva + IngresosBrutos handlers, DTOs, DI registration
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user