From d69da5ff4c068a49410af0bc0841e9bdbdc66dba Mon Sep 17 00:00:00 2001 From: dmolinari Date: Sat, 18 Apr 2026 10:12:17 -0300 Subject: [PATCH] =?UTF-8?q?feat(udt-011):=20T400.10=20=E2=80=94=20inject?= =?UTF-8?q?=20TimeProvider=20into=20all=20Application=20handlers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All command handlers that call domain mutators now inject TimeProvider via constructor and use _timeProvider.GetUtcNow().UtcDateTime as the explicit 'now' argument. Replaces previous direct DateTime.UtcNow usage. --- .../Auth/Login/LoginCommandHandler.cs | 7 +++++-- .../Auth/Logout/LogoutCommandHandler.cs | 10 ++++++++-- .../Auth/Refresh/RefreshCommandHandler.cs | 7 +++++-- .../Create/CreateIngresosBrutosCommandHandler.cs | 6 ++++-- .../DeactivateIngresosBrutosCommandHandler.cs | 7 +++++-- .../NuevaVersionIngresosBrutosCommandHandler.cs | 9 +++++++-- .../ReactivateIngresosBrutosCommandHandler.cs | 7 +++++-- .../Update/UpdateIngresosBrutosCommandHandler.cs | 9 ++++++--- .../Deactivate/DeactivateMedioCommandHandler.cs | 7 +++++-- .../Reactivate/ReactivateMedioCommandHandler.cs | 7 +++++-- .../Medios/Update/UpdateMedioCommandHandler.cs | 7 +++++-- .../DeactivatePuntoDeVentaCommandHandler.cs | 8 ++++++-- .../ReactivatePuntoDeVentaCommandHandler.cs | 8 ++++++-- .../Update/UpdatePuntoDeVentaCommandHandler.cs | 8 ++++++-- .../Deactivate/DeactivateSeccionCommandHandler.cs | 11 +++++++++-- .../Reactivate/ReactivateSeccionCommandHandler.cs | 11 +++++++++-- .../Update/UpdateSeccionCommandHandler.cs | 11 +++++++++-- .../Create/CreateTipoDeIvaCommandHandler.cs | 6 ++++-- .../DeactivateTipoDeIvaCommandHandler.cs | 7 +++++-- .../NuevaVersionTipoDeIvaCommandHandler.cs | 9 +++++++-- .../ReactivateTipoDeIvaCommandHandler.cs | 7 +++++-- .../Update/UpdateTipoDeIvaCommandHandler.cs | 15 +++++++++------ .../Deactivate/DeactivateUsuarioCommandHandler.cs | 7 +++++-- ...pdateUsuarioPermisosOverridesCommandHandler.cs | 8 ++++++-- .../Reactivate/ReactivateUsuarioCommandHandler.cs | 9 +++++++-- .../ResetUsuarioPasswordCommandHandler.cs | 8 ++++++-- .../Update/UpdateUsuarioCommandHandler.cs | 7 +++++-- 27 files changed, 164 insertions(+), 59 deletions(-) diff --git a/src/api/SIGCM2.Application/Auth/Login/LoginCommandHandler.cs b/src/api/SIGCM2.Application/Auth/Login/LoginCommandHandler.cs index e74a07f..d1e8459 100644 --- a/src/api/SIGCM2.Application/Auth/Login/LoginCommandHandler.cs +++ b/src/api/SIGCM2.Application/Auth/Login/LoginCommandHandler.cs @@ -22,6 +22,7 @@ public sealed class LoginCommandHandler : ICommandHandler _logger; + private readonly TimeProvider _timeProvider; public LoginCommandHandler( IUsuarioRepository repository, @@ -33,7 +34,8 @@ public sealed class LoginCommandHandler : ICommandHandler logger) + ILogger logger, + TimeProvider timeProvider) { _repository = repository; _hasher = hasher; @@ -45,6 +47,7 @@ public sealed class LoginCommandHandler : ICommandHandler Handle(LoginCommand command) @@ -81,7 +84,7 @@ public sealed class LoginCommandHandler : ICommandHandler 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"); } diff --git a/src/api/SIGCM2.Application/Auth/Refresh/RefreshCommandHandler.cs b/src/api/SIGCM2.Application/Auth/Refresh/RefreshCommandHandler.cs index b9dbbaa..b9d56e5 100644 --- a/src/api/SIGCM2.Application/Auth/Refresh/RefreshCommandHandler.cs +++ b/src/api/SIGCM2.Application/Auth/Refresh/RefreshCommandHandler.cs @@ -17,6 +17,7 @@ public sealed class RefreshCommandHandler : ICommandHandler Handle(RefreshCommand command) @@ -60,7 +63,7 @@ public sealed class RefreshCommandHandler : ICommandHandler 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)); } } diff --git a/src/api/SIGCM2.Application/IngresosBrutos/Deactivate/DeactivateIngresosBrutosCommandHandler.cs b/src/api/SIGCM2.Application/IngresosBrutos/Deactivate/DeactivateIngresosBrutosCommandHandler.cs index d78b769..b21b179 100644 --- a/src/api/SIGCM2.Application/IngresosBrutos/Deactivate/DeactivateIngresosBrutosCommandHandler.cs +++ b/src/api/SIGCM2.Application/IngresosBrutos/Deactivate/DeactivateIngresosBrutosCommandHandler.cs @@ -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 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)); } } diff --git a/src/api/SIGCM2.Application/IngresosBrutos/NuevaVersion/NuevaVersionIngresosBrutosCommandHandler.cs b/src/api/SIGCM2.Application/IngresosBrutos/NuevaVersion/NuevaVersionIngresosBrutosCommandHandler.cs index 741b93b..6131c1c 100644 --- a/src/api/SIGCM2.Application/IngresosBrutos/NuevaVersion/NuevaVersionIngresosBrutosCommandHandler.cs +++ b/src/api/SIGCM2.Application/IngresosBrutos/NuevaVersion/NuevaVersionIngresosBrutosCommandHandler.cs @@ -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 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, diff --git a/src/api/SIGCM2.Application/IngresosBrutos/Reactivate/ReactivateIngresosBrutosCommandHandler.cs b/src/api/SIGCM2.Application/IngresosBrutos/Reactivate/ReactivateIngresosBrutosCommandHandler.cs index 4b96bd3..c203c7e 100644 --- a/src/api/SIGCM2.Application/IngresosBrutos/Reactivate/ReactivateIngresosBrutosCommandHandler.cs +++ b/src/api/SIGCM2.Application/IngresosBrutos/Reactivate/ReactivateIngresosBrutosCommandHandler.cs @@ -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 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)); } } diff --git a/src/api/SIGCM2.Application/IngresosBrutos/Update/UpdateIngresosBrutosCommandHandler.cs b/src/api/SIGCM2.Application/IngresosBrutos/Update/UpdateIngresosBrutosCommandHandler.cs index d87e4c3..4b0ef12 100644 --- a/src/api/SIGCM2.Application/IngresosBrutos/Update/UpdateIngresosBrutosCommandHandler.cs +++ b/src/api/SIGCM2.Application/IngresosBrutos/Update/UpdateIngresosBrutosCommandHandler.cs @@ -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 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, diff --git a/src/api/SIGCM2.Application/Medios/Deactivate/DeactivateMedioCommandHandler.cs b/src/api/SIGCM2.Application/Medios/Deactivate/DeactivateMedioCommandHandler.cs index 7b87564..92dacc1 100644 --- a/src/api/SIGCM2.Application/Medios/Deactivate/DeactivateMedioCommandHandler.cs +++ b/src/api/SIGCM2.Application/Medios/Deactivate/DeactivateMedioCommandHandler.cs @@ -11,11 +11,13 @@ public sealed class DeactivateMedioCommandHandler : ICommandHandler Handle(DeactivateMedioCommand command) @@ -27,7 +29,8 @@ public sealed class DeactivateMedioCommandHandler : ICommandHandler Handle(ReactivateMedioCommand command) @@ -28,7 +30,8 @@ public sealed class ReactivateMedioCommandHandler : ICommandHandler Handle(UpdateMedioCommand command) @@ -23,7 +25,8 @@ public sealed class UpdateMedioCommandHandler : ICommandHandler Handle(DeactivatePuntoDeVentaCommand command) @@ -32,7 +35,8 @@ public sealed class DeactivatePuntoDeVentaCommandHandler : ICommandHandler Handle(ReactivatePuntoDeVentaCommand command) @@ -39,7 +42,8 @@ public sealed class ReactivatePuntoDeVentaCommandHandler : ICommandHandler Handle(UpdatePuntoDeVentaCommand command) @@ -39,7 +42,8 @@ public sealed class UpdatePuntoDeVentaCommandHandler : ICommandHandler Handle(DeactivateSeccionCommand command) @@ -35,7 +41,8 @@ public sealed class DeactivateSeccionCommandHandler : ICommandHandler Handle(ReactivateSeccionCommand command) @@ -36,7 +42,8 @@ public sealed class ReactivateSeccionCommandHandler : ICommandHandler Handle(UpdateSeccionCommand command) @@ -31,7 +37,8 @@ public sealed class UpdateSeccionCommandHandler : ICommandHandler Handle(CreateTipoDeIvaCommand command) @@ -55,7 +57,7 @@ public sealed class CreateTipoDeIvaCommandHandler : ICommandHandler Handle(DeactivateTipoDeIvaCommand command) @@ -41,6 +43,7 @@ public sealed class DeactivateTipoDeIvaCommandHandler : ICommandHandler 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, diff --git a/src/api/SIGCM2.Application/TiposDeIva/Reactivate/ReactivateTipoDeIvaCommandHandler.cs b/src/api/SIGCM2.Application/TiposDeIva/Reactivate/ReactivateTipoDeIvaCommandHandler.cs index 2a2ab9a..7a617b7 100644 --- a/src/api/SIGCM2.Application/TiposDeIva/Reactivate/ReactivateTipoDeIvaCommandHandler.cs +++ b/src/api/SIGCM2.Application/TiposDeIva/Reactivate/ReactivateTipoDeIvaCommandHandler.cs @@ -11,11 +11,13 @@ public sealed class ReactivateTipoDeIvaCommandHandler : ICommandHandler Handle(ReactivateTipoDeIvaCommand command) @@ -41,6 +43,7 @@ public sealed class ReactivateTipoDeIvaCommandHandler : ICommandHandler Handle(UpdateTipoDeIvaCommand command) @@ -23,15 +25,16 @@ public sealed class UpdateTipoDeIvaCommandHandler : ICommandHandler Handle(DeactivateUsuarioCommand cmd) @@ -43,7 +46,7 @@ public sealed class DeactivateUsuarioCommandHandler : ICommandHandler 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, diff --git a/src/api/SIGCM2.Application/Usuarios/Reactivate/ReactivateUsuarioCommandHandler.cs b/src/api/SIGCM2.Application/Usuarios/Reactivate/ReactivateUsuarioCommandHandler.cs index c89a473..4c73270 100644 --- a/src/api/SIGCM2.Application/Usuarios/Reactivate/ReactivateUsuarioCommandHandler.cs +++ b/src/api/SIGCM2.Application/Usuarios/Reactivate/ReactivateUsuarioCommandHandler.cs @@ -12,11 +12,16 @@ public sealed class ReactivateUsuarioCommandHandler : ICommandHandler Handle(ReactivateUsuarioCommand cmd) @@ -34,7 +39,7 @@ public sealed class ReactivateUsuarioCommandHandler : ICommandHandler Handle(ResetUsuarioPasswordCommand cmd) @@ -45,8 +48,9 @@ public sealed class ResetUsuarioPasswordCommandHandler : ICommandHandler Handle(UpdateUsuarioCommand cmd) @@ -52,7 +55,7 @@ public sealed class UpdateUsuarioCommandHandler : ICommandHandler