UDT-011: Localización Temporal Argentina (infra transversal) #25
@@ -22,6 +22,7 @@ public sealed class LoginCommandHandler : ICommandHandler<LoginCommand, LoginRes
|
||||
private readonly IRolPermisoRepository _rolPermisoRepository;
|
||||
private readonly ISecurityEventLogger _security;
|
||||
private readonly ILogger<LoginCommandHandler> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public LoginCommandHandler(
|
||||
IUsuarioRepository repository,
|
||||
@@ -33,7 +34,8 @@ public sealed class LoginCommandHandler : ICommandHandler<LoginCommand, LoginRes
|
||||
AuthOptions authOptions,
|
||||
IRolPermisoRepository rolPermisoRepository,
|
||||
ISecurityEventLogger security,
|
||||
ILogger<LoginCommandHandler> logger)
|
||||
ILogger<LoginCommandHandler> logger,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_repository = repository;
|
||||
_hasher = hasher;
|
||||
@@ -45,6 +47,7 @@ public sealed class LoginCommandHandler : ICommandHandler<LoginCommand, LoginRes
|
||||
_rolPermisoRepository = rolPermisoRepository;
|
||||
_security = security;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<LoginResponseDto> Handle(LoginCommand command)
|
||||
@@ -81,7 +84,7 @@ public sealed class LoginCommandHandler : ICommandHandler<LoginCommand, LoginRes
|
||||
// Generate and persist refresh token — only the hash hits the DB
|
||||
var rawRefresh = _refreshGenerator.Generate();
|
||||
var hash = TokenHasher.Sha256Base64Url(rawRefresh);
|
||||
var now = DateTime.UtcNow;
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
var ttl = TimeSpan.FromDays(_authOptions.RefreshTokenDays);
|
||||
var entity = RefreshToken.IssueForNewFamily(
|
||||
usuario.Id, hash, now, ttl,
|
||||
|
||||
@@ -8,18 +8,24 @@ public sealed class LogoutCommandHandler : ICommandHandler<LogoutCommand, Logout
|
||||
{
|
||||
private readonly IRefreshTokenRepository _refreshRepo;
|
||||
private readonly ISecurityEventLogger _security;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public LogoutCommandHandler(IRefreshTokenRepository refreshRepo, ISecurityEventLogger security)
|
||||
public LogoutCommandHandler(
|
||||
IRefreshTokenRepository refreshRepo,
|
||||
ISecurityEventLogger security,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_refreshRepo = refreshRepo;
|
||||
_security = security;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<LogoutResponseDto> Handle(LogoutCommand command)
|
||||
{
|
||||
// Revoke all active tokens for the user across all families.
|
||||
// Idempotent: 0 rows affected is not an error.
|
||||
await _refreshRepo.RevokeAllActiveForUserAsync(command.UsuarioId, DateTime.UtcNow);
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
await _refreshRepo.RevokeAllActiveForUserAsync(command.UsuarioId, now);
|
||||
await _security.LogAsync("logout", "success", actorUserId: command.UsuarioId);
|
||||
return new LogoutResponseDto(true, "Sesión cerrada correctamente");
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ public sealed class RefreshCommandHandler : ICommandHandler<RefreshCommand, Refr
|
||||
private readonly IClientContext _clientCtx;
|
||||
private readonly AuthOptions _authOptions;
|
||||
private readonly ISecurityEventLogger _security;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public RefreshCommandHandler(
|
||||
IRefreshTokenRepository refreshRepo,
|
||||
@@ -25,7 +26,8 @@ public sealed class RefreshCommandHandler : ICommandHandler<RefreshCommand, Refr
|
||||
IRefreshTokenGenerator refreshGenerator,
|
||||
IClientContext clientCtx,
|
||||
AuthOptions authOptions,
|
||||
ISecurityEventLogger security)
|
||||
ISecurityEventLogger security,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_refreshRepo = refreshRepo;
|
||||
_usuarioRepo = usuarioRepo;
|
||||
@@ -34,6 +36,7 @@ public sealed class RefreshCommandHandler : ICommandHandler<RefreshCommand, Refr
|
||||
_clientCtx = clientCtx;
|
||||
_authOptions = authOptions;
|
||||
_security = security;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<RefreshResponseDto> Handle(RefreshCommand command)
|
||||
@@ -60,7 +63,7 @@ public sealed class RefreshCommandHandler : ICommandHandler<RefreshCommand, Refr
|
||||
if (stored is null)
|
||||
throw new InvalidRefreshTokenException();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
|
||||
// 4. Reuse detection: already revoked → chain revocation and throw
|
||||
if (stored.IsRevoked)
|
||||
|
||||
@@ -11,11 +11,13 @@ public sealed class CreateIngresosBrutosCommandHandler
|
||||
{
|
||||
private readonly IIngresosBrutosRepository _repo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public CreateIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit)
|
||||
public CreateIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit, TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<IngresosBrutosDto> Handle(CreateIngresosBrutosCommand command)
|
||||
@@ -51,7 +53,7 @@ public sealed class CreateIngresosBrutosCommandHandler
|
||||
vigenciaDesde: entity.VigenciaDesde,
|
||||
vigenciaHasta: entity.VigenciaHasta,
|
||||
predecesorId: entity.PredecesorId,
|
||||
fechaCreacion: DateTime.UtcNow,
|
||||
fechaCreacion: _timeProvider.GetUtcNow().UtcDateTime,
|
||||
fechaModificacion: null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ public sealed class DeactivateIngresosBrutosCommandHandler
|
||||
{
|
||||
private readonly IIngresosBrutosRepository _repo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public DeactivateIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit)
|
||||
public DeactivateIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit, TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<IngresosBrutosDto> Handle(DeactivateIngresosBrutosCommand command)
|
||||
@@ -41,6 +43,7 @@ public sealed class DeactivateIngresosBrutosCommandHandler
|
||||
|
||||
tx.Complete();
|
||||
|
||||
return IngresosBrutosMapper.ToDto(entity.Deactivate());
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
return IngresosBrutosMapper.ToDto(entity.Deactivate(now));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ public sealed class NuevaVersionIngresosBrutosCommandHandler
|
||||
{
|
||||
private readonly IIngresosBrutosRepository _repo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public NuevaVersionIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit)
|
||||
public NuevaVersionIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit, TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<NuevaVersionIibbResultDto> Handle(NuevaVersionIngresosBrutosCommand command)
|
||||
@@ -29,10 +31,13 @@ public sealed class NuevaVersionIngresosBrutosCommandHandler
|
||||
if (!predecesora.Activo || predecesora.VigenciaHasta is not null)
|
||||
throw new PredecesorYaCerradoException(command.PredecesoraId);
|
||||
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
|
||||
// Steps 3–4: domain validation + tuple creation (throws ArgumentException if vigencia invalid)
|
||||
var (predecesoraCerrada, nuevaVersion) = predecesora.NuevaVersion(
|
||||
command.NuevaAlicuota,
|
||||
command.VigenciaDesde);
|
||||
command.VigenciaDesde,
|
||||
now);
|
||||
|
||||
using var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -12,11 +12,13 @@ public sealed class ReactivateIngresosBrutosCommandHandler
|
||||
{
|
||||
private readonly IIngresosBrutosRepository _repo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public ReactivateIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit)
|
||||
public ReactivateIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit, TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<IngresosBrutosDto> Handle(ReactivateIngresosBrutosCommand command)
|
||||
@@ -41,6 +43,7 @@ public sealed class ReactivateIngresosBrutosCommandHandler
|
||||
|
||||
tx.Complete();
|
||||
|
||||
return IngresosBrutosMapper.ToDto(entity.Reactivate());
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
return IngresosBrutosMapper.ToDto(entity.Reactivate(now));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ public sealed class UpdateIngresosBrutosCommandHandler
|
||||
{
|
||||
private readonly IIngresosBrutosRepository _repo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public UpdateIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit)
|
||||
public UpdateIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit, TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<IngresosBrutosDto> Handle(UpdateIngresosBrutosCommand command)
|
||||
@@ -24,8 +26,9 @@ public sealed class UpdateIngresosBrutosCommandHandler
|
||||
var entity = await _repo.GetByIdAsync(command.Id)
|
||||
?? throw new IngresosBrutosNotFoundException(command.Id);
|
||||
|
||||
var updated = entity.WithDescripcion(command.Descripcion);
|
||||
updated = command.Activo ? updated.Reactivate() : updated.Deactivate();
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
var updated = entity.WithDescripcion(command.Descripcion, now);
|
||||
updated = command.Activo ? updated.Reactivate(now) : updated.Deactivate(now);
|
||||
|
||||
using var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -11,11 +11,13 @@ public sealed class DeactivateMedioCommandHandler : ICommandHandler<DeactivateMe
|
||||
{
|
||||
private readonly IMedioRepository _repo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public DeactivateMedioCommandHandler(IMedioRepository repo, IAuditLogger audit)
|
||||
public DeactivateMedioCommandHandler(IMedioRepository repo, IAuditLogger audit, TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<MedioStatusDto> Handle(DeactivateMedioCommand command)
|
||||
@@ -27,7 +29,8 @@ public sealed class DeactivateMedioCommandHandler : ICommandHandler<DeactivateMe
|
||||
if (!target.Activo)
|
||||
return new MedioStatusDto(target.Id, target.Codigo, target.Activo);
|
||||
|
||||
var updated = target.WithActivo(false);
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
var updated = target.WithActivo(false, now);
|
||||
|
||||
using var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -12,11 +12,13 @@ public sealed class ReactivateMedioCommandHandler : ICommandHandler<ReactivateMe
|
||||
{
|
||||
private readonly IMedioRepository _repo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public ReactivateMedioCommandHandler(IMedioRepository repo, IAuditLogger audit)
|
||||
public ReactivateMedioCommandHandler(IMedioRepository repo, IAuditLogger audit, TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<MedioStatusDto> Handle(ReactivateMedioCommand command)
|
||||
@@ -28,7 +30,8 @@ public sealed class ReactivateMedioCommandHandler : ICommandHandler<ReactivateMe
|
||||
if (target.Activo)
|
||||
return new MedioStatusDto(target.Id, target.Codigo, target.Activo);
|
||||
|
||||
var updated = target.WithActivo(true);
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
var updated = target.WithActivo(true, now);
|
||||
|
||||
using var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -11,11 +11,13 @@ public sealed class UpdateMedioCommandHandler : ICommandHandler<UpdateMedioComma
|
||||
{
|
||||
private readonly IMedioRepository _repo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public UpdateMedioCommandHandler(IMedioRepository repo, IAuditLogger audit)
|
||||
public UpdateMedioCommandHandler(IMedioRepository repo, IAuditLogger audit, TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<MedioUpdatedDto> Handle(UpdateMedioCommand command)
|
||||
@@ -23,7 +25,8 @@ public sealed class UpdateMedioCommandHandler : ICommandHandler<UpdateMedioComma
|
||||
var target = await _repo.GetByIdAsync(command.Id)
|
||||
?? throw new MedioNotFoundException(command.Id);
|
||||
|
||||
var updated = target.WithUpdatedProfile(command.Nombre, command.Tipo, command.PlataformaEmpresaId);
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
var updated = target.WithUpdatedProfile(command.Nombre, command.Tipo, command.PlataformaEmpresaId, now);
|
||||
|
||||
using var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -12,15 +12,18 @@ public sealed class DeactivatePuntoDeVentaCommandHandler : ICommandHandler<Deact
|
||||
private readonly IPuntoDeVentaRepository _repo;
|
||||
private readonly IMedioRepository _medioRepo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public DeactivatePuntoDeVentaCommandHandler(
|
||||
IPuntoDeVentaRepository repo,
|
||||
IMedioRepository medioRepo,
|
||||
IAuditLogger audit)
|
||||
IAuditLogger audit,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_medioRepo = medioRepo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<PuntoDeVentaStatusDto> Handle(DeactivatePuntoDeVentaCommand command)
|
||||
@@ -32,7 +35,8 @@ public sealed class DeactivatePuntoDeVentaCommandHandler : ICommandHandler<Deact
|
||||
if (!target.Activo)
|
||||
return new PuntoDeVentaStatusDto(target.Id, target.NumeroAFIP, target.Activo);
|
||||
|
||||
var updated = target.WithActivo(false);
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
var updated = target.WithActivo(false, now);
|
||||
|
||||
using var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -13,15 +13,18 @@ public sealed class ReactivatePuntoDeVentaCommandHandler : ICommandHandler<React
|
||||
private readonly IPuntoDeVentaRepository _repo;
|
||||
private readonly IMedioRepository _medioRepo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public ReactivatePuntoDeVentaCommandHandler(
|
||||
IPuntoDeVentaRepository repo,
|
||||
IMedioRepository medioRepo,
|
||||
IAuditLogger audit)
|
||||
IAuditLogger audit,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_medioRepo = medioRepo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<PuntoDeVentaStatusDto> Handle(ReactivatePuntoDeVentaCommand command)
|
||||
@@ -39,7 +42,8 @@ public sealed class ReactivatePuntoDeVentaCommandHandler : ICommandHandler<React
|
||||
if (target.Activo)
|
||||
return new PuntoDeVentaStatusDto(target.Id, target.NumeroAFIP, target.Activo);
|
||||
|
||||
var updated = target.WithActivo(true);
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
var updated = target.WithActivo(true, now);
|
||||
|
||||
using var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -12,15 +12,18 @@ public sealed class UpdatePuntoDeVentaCommandHandler : ICommandHandler<UpdatePun
|
||||
private readonly IPuntoDeVentaRepository _repo;
|
||||
private readonly IMedioRepository _medioRepo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public UpdatePuntoDeVentaCommandHandler(
|
||||
IPuntoDeVentaRepository repo,
|
||||
IMedioRepository medioRepo,
|
||||
IAuditLogger audit)
|
||||
IAuditLogger audit,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_medioRepo = medioRepo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<PuntoDeVentaUpdatedDto> Handle(UpdatePuntoDeVentaCommand command)
|
||||
@@ -39,7 +42,8 @@ public sealed class UpdatePuntoDeVentaCommandHandler : ICommandHandler<UpdatePun
|
||||
if (exists)
|
||||
throw new NumeroAFIPDuplicadoException(target.MedioId, command.NumeroAFIP);
|
||||
|
||||
var updated = target.WithUpdatedProfile(command.Nombre, command.NumeroAFIP, command.Descripcion);
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
var updated = target.WithUpdatedProfile(command.Nombre, command.NumeroAFIP, command.Descripcion, now);
|
||||
|
||||
using var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -12,12 +12,18 @@ public sealed class DeactivateSeccionCommandHandler : ICommandHandler<Deactivate
|
||||
private readonly ISeccionRepository _repo;
|
||||
private readonly IMedioRepository _medioRepo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public DeactivateSeccionCommandHandler(ISeccionRepository repo, IMedioRepository medioRepo, IAuditLogger audit)
|
||||
public DeactivateSeccionCommandHandler(
|
||||
ISeccionRepository repo,
|
||||
IMedioRepository medioRepo,
|
||||
IAuditLogger audit,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_medioRepo = medioRepo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<SeccionStatusDto> Handle(DeactivateSeccionCommand command)
|
||||
@@ -35,7 +41,8 @@ public sealed class DeactivateSeccionCommandHandler : ICommandHandler<Deactivate
|
||||
if (!target.Activo)
|
||||
return new SeccionStatusDto(target.Id, target.Codigo, target.Activo);
|
||||
|
||||
var updated = target.WithActivo(false);
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
var updated = target.WithActivo(false, now);
|
||||
|
||||
using var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -13,12 +13,18 @@ public sealed class ReactivateSeccionCommandHandler : ICommandHandler<Reactivate
|
||||
private readonly ISeccionRepository _repo;
|
||||
private readonly IMedioRepository _medioRepo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public ReactivateSeccionCommandHandler(ISeccionRepository repo, IMedioRepository medioRepo, IAuditLogger audit)
|
||||
public ReactivateSeccionCommandHandler(
|
||||
ISeccionRepository repo,
|
||||
IMedioRepository medioRepo,
|
||||
IAuditLogger audit,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_medioRepo = medioRepo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<SeccionStatusDto> Handle(ReactivateSeccionCommand command)
|
||||
@@ -36,7 +42,8 @@ public sealed class ReactivateSeccionCommandHandler : ICommandHandler<Reactivate
|
||||
if (target.Activo)
|
||||
return new SeccionStatusDto(target.Id, target.Codigo, target.Activo);
|
||||
|
||||
var updated = target.WithActivo(true);
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
var updated = target.WithActivo(true, now);
|
||||
|
||||
using var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -12,12 +12,18 @@ public sealed class UpdateSeccionCommandHandler : ICommandHandler<UpdateSeccionC
|
||||
private readonly ISeccionRepository _repo;
|
||||
private readonly IMedioRepository _medioRepo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public UpdateSeccionCommandHandler(ISeccionRepository repo, IMedioRepository medioRepo, IAuditLogger audit)
|
||||
public UpdateSeccionCommandHandler(
|
||||
ISeccionRepository repo,
|
||||
IMedioRepository medioRepo,
|
||||
IAuditLogger audit,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_medioRepo = medioRepo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<SeccionUpdatedDto> Handle(UpdateSeccionCommand command)
|
||||
@@ -31,7 +37,8 @@ public sealed class UpdateSeccionCommandHandler : ICommandHandler<UpdateSeccionC
|
||||
if (!medio.Activo)
|
||||
throw new MedioInactivoException(medio.Id);
|
||||
|
||||
var updated = target.WithUpdatedProfile(command.Nombre, command.Tipo);
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
var updated = target.WithUpdatedProfile(command.Nombre, command.Tipo, now);
|
||||
|
||||
using var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -12,11 +12,13 @@ public sealed class CreateTipoDeIvaCommandHandler : ICommandHandler<CreateTipoDe
|
||||
{
|
||||
private readonly ITipoDeIvaRepository _repo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public CreateTipoDeIvaCommandHandler(ITipoDeIvaRepository repo, IAuditLogger audit)
|
||||
public CreateTipoDeIvaCommandHandler(ITipoDeIvaRepository repo, IAuditLogger audit, TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<TipoDeIvaDto> Handle(CreateTipoDeIvaCommand command)
|
||||
@@ -55,7 +57,7 @@ public sealed class CreateTipoDeIvaCommandHandler : ICommandHandler<CreateTipoDe
|
||||
vigenciaDesde: entity.VigenciaDesde,
|
||||
vigenciaHasta: entity.VigenciaHasta,
|
||||
predecesorId: entity.PredecesorId,
|
||||
fechaCreacion: DateTime.UtcNow,
|
||||
fechaCreacion: _timeProvider.GetUtcNow().UtcDateTime,
|
||||
fechaModificacion: null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,13 @@ public sealed class DeactivateTipoDeIvaCommandHandler : ICommandHandler<Deactiva
|
||||
{
|
||||
private readonly ITipoDeIvaRepository _repo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public DeactivateTipoDeIvaCommandHandler(ITipoDeIvaRepository repo, IAuditLogger audit)
|
||||
public DeactivateTipoDeIvaCommandHandler(ITipoDeIvaRepository repo, IAuditLogger audit, TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<TipoDeIvaDto> Handle(DeactivateTipoDeIvaCommand command)
|
||||
@@ -41,6 +43,7 @@ public sealed class DeactivateTipoDeIvaCommandHandler : ICommandHandler<Deactiva
|
||||
|
||||
tx.Complete();
|
||||
|
||||
return TipoDeIvaMapper.ToDto(entity.Deactivate());
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
return TipoDeIvaMapper.ToDto(entity.Deactivate(now));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ public sealed class NuevaVersionTipoDeIvaCommandHandler
|
||||
{
|
||||
private readonly ITipoDeIvaRepository _repo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public NuevaVersionTipoDeIvaCommandHandler(ITipoDeIvaRepository repo, IAuditLogger audit)
|
||||
public NuevaVersionTipoDeIvaCommandHandler(ITipoDeIvaRepository repo, IAuditLogger audit, TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<NuevaVersionResultDto> Handle(NuevaVersionTipoDeIvaCommand command)
|
||||
@@ -29,10 +31,13 @@ public sealed class NuevaVersionTipoDeIvaCommandHandler
|
||||
if (!predecesora.Activo || predecesora.VigenciaHasta is not null)
|
||||
throw new PredecesorYaCerradoException(command.PredecesoraId);
|
||||
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
|
||||
// Steps 3–4: delegate validation + tuple creation to domain (throws ArgumentException on invalid vigencia)
|
||||
var (predecesoraCerrada, nuevaVersion) = predecesora.NuevaVersion(
|
||||
command.NuevoPorcentaje,
|
||||
command.VigenciaDesde);
|
||||
command.VigenciaDesde,
|
||||
now);
|
||||
|
||||
using var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -11,11 +11,13 @@ public sealed class ReactivateTipoDeIvaCommandHandler : ICommandHandler<Reactiva
|
||||
{
|
||||
private readonly ITipoDeIvaRepository _repo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public ReactivateTipoDeIvaCommandHandler(ITipoDeIvaRepository repo, IAuditLogger audit)
|
||||
public ReactivateTipoDeIvaCommandHandler(ITipoDeIvaRepository repo, IAuditLogger audit, TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<TipoDeIvaDto> Handle(ReactivateTipoDeIvaCommand command)
|
||||
@@ -41,6 +43,7 @@ public sealed class ReactivateTipoDeIvaCommandHandler : ICommandHandler<Reactiva
|
||||
|
||||
tx.Complete();
|
||||
|
||||
return TipoDeIvaMapper.ToDto(entity.Reactivate());
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
return TipoDeIvaMapper.ToDto(entity.Reactivate(now));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,13 @@ public sealed class UpdateTipoDeIvaCommandHandler : ICommandHandler<UpdateTipoDe
|
||||
{
|
||||
private readonly ITipoDeIvaRepository _repo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public UpdateTipoDeIvaCommandHandler(ITipoDeIvaRepository repo, IAuditLogger audit)
|
||||
public UpdateTipoDeIvaCommandHandler(ITipoDeIvaRepository repo, IAuditLogger audit, TimeProvider timeProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<TipoDeIvaDto> Handle(UpdateTipoDeIvaCommand command)
|
||||
@@ -23,15 +25,16 @@ public sealed class UpdateTipoDeIvaCommandHandler : ICommandHandler<UpdateTipoDe
|
||||
var entity = await _repo.GetByIdAsync(command.Id)
|
||||
?? throw new TipoDeIvaNotFoundException(command.Id);
|
||||
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
var updated = entity
|
||||
.WithCodigo(command.Codigo)
|
||||
.WithDescripcion(command.Descripcion)
|
||||
.WithAplicaIVA(command.AplicaIVA);
|
||||
.WithCodigo(command.Codigo, now)
|
||||
.WithDescripcion(command.Descripcion, now)
|
||||
.WithAplicaIVA(command.AplicaIVA, now);
|
||||
|
||||
// Apply Activo change if needed
|
||||
updated = command.Activo
|
||||
? updated.Reactivate()
|
||||
: updated.Deactivate();
|
||||
? updated.Reactivate(now)
|
||||
: updated.Deactivate(now);
|
||||
|
||||
using var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -13,15 +13,18 @@ public sealed class DeactivateUsuarioCommandHandler : ICommandHandler<Deactivate
|
||||
private readonly IUsuarioRepository _repository;
|
||||
private readonly IRefreshTokenRepository _refreshTokenRepository;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public DeactivateUsuarioCommandHandler(
|
||||
IUsuarioRepository repository,
|
||||
IRefreshTokenRepository refreshTokenRepository,
|
||||
IAuditLogger audit)
|
||||
IAuditLogger audit,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_repository = repository;
|
||||
_refreshTokenRepository = refreshTokenRepository;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<UsuarioDetailDto> Handle(DeactivateUsuarioCommand cmd)
|
||||
@@ -43,7 +46,7 @@ public sealed class DeactivateUsuarioCommandHandler : ICommandHandler<Deactivate
|
||||
throw new LastAdminLockoutException();
|
||||
|
||||
var fields = new UpdateUsuarioFields(target.Nombre, target.Apellido, target.Email, target.Rol, false);
|
||||
var now = DateTime.UtcNow;
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
|
||||
using (var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -18,17 +18,20 @@ public sealed class UpdateUsuarioPermisosOverridesCommandHandler
|
||||
private readonly IRolPermisoRepository _rolPermisoRepo;
|
||||
private readonly IPermisoRepository _permisoRepo;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public UpdateUsuarioPermisosOverridesCommandHandler(
|
||||
IUsuarioRepository usuarioRepo,
|
||||
IRolPermisoRepository rolPermisoRepo,
|
||||
IPermisoRepository permisoRepo,
|
||||
IAuditLogger audit)
|
||||
IAuditLogger audit,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_usuarioRepo = usuarioRepo;
|
||||
_rolPermisoRepo = rolPermisoRepo;
|
||||
_permisoRepo = permisoRepo;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<UsuarioPermisosDto> Handle(UpdateUsuarioPermisosOverridesCommand command)
|
||||
@@ -59,7 +62,8 @@ public sealed class UpdateUsuarioPermisosOverridesCommandHandler
|
||||
// 4. Persist — use WithPermisosJson to get updated FechaModificacion
|
||||
var newOverrides = new PermisosOverride(grant, deny);
|
||||
var previousOverrides = PermisosOverride.FromJson(usuario.PermisosJson);
|
||||
var updated = usuario.WithPermisosJson(newOverrides.ToJson());
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
var updated = usuario.WithPermisosJson(newOverrides.ToJson(), now);
|
||||
|
||||
using (var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -12,11 +12,16 @@ public sealed class ReactivateUsuarioCommandHandler : ICommandHandler<Reactivate
|
||||
{
|
||||
private readonly IUsuarioRepository _repository;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public ReactivateUsuarioCommandHandler(IUsuarioRepository repository, IAuditLogger audit)
|
||||
public ReactivateUsuarioCommandHandler(
|
||||
IUsuarioRepository repository,
|
||||
IAuditLogger audit,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_repository = repository;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<UsuarioDetailDto> Handle(ReactivateUsuarioCommand cmd)
|
||||
@@ -34,7 +39,7 @@ public sealed class ReactivateUsuarioCommandHandler : ICommandHandler<Reactivate
|
||||
}
|
||||
|
||||
var fields = new UpdateUsuarioFields(target.Nombre, target.Apellido, target.Email, target.Rol, true);
|
||||
var now = DateTime.UtcNow;
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
|
||||
using (var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
@@ -14,17 +14,20 @@ public sealed class ResetUsuarioPasswordCommandHandler : ICommandHandler<ResetUs
|
||||
private readonly IPasswordHasher _hasher;
|
||||
private readonly IRefreshTokenRepository _refreshTokenRepository;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public ResetUsuarioPasswordCommandHandler(
|
||||
IUsuarioRepository repository,
|
||||
IPasswordHasher hasher,
|
||||
IRefreshTokenRepository refreshTokenRepository,
|
||||
IAuditLogger audit)
|
||||
IAuditLogger audit,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_repository = repository;
|
||||
_hasher = hasher;
|
||||
_refreshTokenRepository = refreshTokenRepository;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<ResetUsuarioPasswordResponse> Handle(ResetUsuarioPasswordCommand cmd)
|
||||
@@ -45,8 +48,9 @@ public sealed class ResetUsuarioPasswordCommandHandler : ICommandHandler<ResetUs
|
||||
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted },
|
||||
TransactionScopeAsyncFlowOption.Enabled);
|
||||
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
await _repository.UpdatePasswordAsync(cmd.TargetId, hash, mustChangePassword: true);
|
||||
await _refreshTokenRepository.RevokeAllActiveForUserAsync(cmd.TargetId, DateTime.UtcNow);
|
||||
await _refreshTokenRepository.RevokeAllActiveForUserAsync(cmd.TargetId, now);
|
||||
|
||||
await _audit.LogAsync(
|
||||
action: "usuario.password_reset",
|
||||
|
||||
@@ -14,17 +14,20 @@ public sealed class UpdateUsuarioCommandHandler : ICommandHandler<UpdateUsuarioC
|
||||
private readonly IRolRepository _rolRepository;
|
||||
private readonly IRefreshTokenRepository _refreshTokenRepository;
|
||||
private readonly IAuditLogger _audit;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public UpdateUsuarioCommandHandler(
|
||||
IUsuarioRepository repository,
|
||||
IRolRepository rolRepository,
|
||||
IRefreshTokenRepository refreshTokenRepository,
|
||||
IAuditLogger audit)
|
||||
IAuditLogger audit,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_repository = repository;
|
||||
_rolRepository = rolRepository;
|
||||
_refreshTokenRepository = refreshTokenRepository;
|
||||
_audit = audit;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task<UsuarioDetailDto> Handle(UpdateUsuarioCommand cmd)
|
||||
@@ -52,7 +55,7 @@ public sealed class UpdateUsuarioCommandHandler : ICommandHandler<UpdateUsuarioC
|
||||
}
|
||||
|
||||
var fields = new UpdateUsuarioFields(cmd.Nombre, cmd.Apellido, cmd.Email, cmd.Rol, cmd.Activo);
|
||||
var now = DateTime.UtcNow;
|
||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||
|
||||
using (var tx = new TransactionScope(
|
||||
TransactionScopeOption.Required,
|
||||
|
||||
Reference in New Issue
Block a user