diff --git a/src/api/SIGCM2.Domain/Entities/IngresosBrutos.cs b/src/api/SIGCM2.Domain/Entities/IngresosBrutos.cs
index db35ce6..3a5e2db 100644
--- a/src/api/SIGCM2.Domain/Entities/IngresosBrutos.cs
+++ b/src/api/SIGCM2.Domain/Entities/IngresosBrutos.cs
@@ -95,9 +95,13 @@ public sealed class IngresosBrutos
///
/// Si la predecesora ya está cerrada (VigenciaHasta != null).
/// Si vigenciaDesde no es posterior a la predecesora, o nuevaAlicuota fuera de rango.
+ ///
+ /// Timestamp UTC provisto por el caller (Application layer via TimeProvider).
+ ///
public (IngresosBrutos predecesoraCerrada, IngresosBrutos nuevaVersion) NuevaVersion(
decimal nuevaAlicuota,
- DateOnly vigenciaDesde)
+ DateOnly vigenciaDesde,
+ DateTime now)
{
if (VigenciaHasta is not null)
throw new InvalidOperationException(
@@ -120,7 +124,7 @@ public sealed class IngresosBrutos
vigenciaHasta: vigenciaDesde.AddDays(-1),
predecesorId: PredecesorId,
fechaCreacion: FechaCreacion,
- fechaModificacion: DateTime.UtcNow);
+ fechaModificacion: now);
var nueva = ForCreation(
provincia: Provincia,
@@ -136,26 +140,26 @@ public sealed class IngresosBrutos
// ── Cosmetic mutators (NO WithAlicuota, NO WithProvincia) ─────────────────
/// Actualiza la descripción. Alicuota y Provincia permanecen inmutables.
- public IngresosBrutos WithDescripcion(string descripcion)
+ public IngresosBrutos WithDescripcion(string descripcion, DateTime now)
=> new(Id, Provincia, descripcion, Alicuota, Activo,
- VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, DateTime.UtcNow);
+ VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, now);
/// Retorna instancia con Activo=false.
- public IngresosBrutos Deactivate()
+ public IngresosBrutos Deactivate(DateTime now)
=> new(Id, Provincia, Descripcion, Alicuota, false,
- VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, DateTime.UtcNow);
+ VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, now);
/// Retorna instancia con Activo=true.
- public IngresosBrutos Reactivate()
+ public IngresosBrutos Reactivate(DateTime now)
=> new(Id, Provincia, Descripcion, Alicuota, true,
- VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, DateTime.UtcNow);
+ VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, now);
///
/// Cierra la vigencia seteando VigenciaHasta. Usado por el handler de NuevaVersion.
///
- public IngresosBrutos CerrarVigencia(DateOnly vigenciaHasta)
+ public IngresosBrutos CerrarVigencia(DateOnly vigenciaHasta, DateTime now)
=> new(Id, Provincia, Descripcion, Alicuota, Activo,
- VigenciaDesde, vigenciaHasta, PredecesorId, FechaCreacion, DateTime.UtcNow);
+ VigenciaDesde, vigenciaHasta, PredecesorId, FechaCreacion, now);
// ── Private helpers ───────────────────────────────────────────────────────
diff --git a/src/api/SIGCM2.Domain/Entities/Medio.cs b/src/api/SIGCM2.Domain/Entities/Medio.cs
index 6c7df79..25d3004 100644
--- a/src/api/SIGCM2.Domain/Entities/Medio.cs
+++ b/src/api/SIGCM2.Domain/Entities/Medio.cs
@@ -49,9 +49,9 @@ public sealed class Medio
///
/// Returns a new instance with updated fields. Codigo is immutable (use BD UQ to enforce).
- /// Sets FechaModificacion = UtcNow.
+ /// Caller is responsible for passing the current UTC timestamp via TimeProvider.GetUtcNow().UtcDateTime.
///
- public Medio WithUpdatedProfile(string nombre, TipoMedio tipo, int? plataformaEmpresaId)
+ public Medio WithUpdatedProfile(string nombre, TipoMedio tipo, int? plataformaEmpresaId, DateTime now)
=> new(
id: Id,
codigo: Codigo,
@@ -60,9 +60,9 @@ public sealed class Medio
plataformaEmpresaId: plataformaEmpresaId,
activo: Activo,
fechaCreacion: FechaCreacion,
- fechaModificacion: DateTime.UtcNow);
+ fechaModificacion: now);
- public Medio WithActivo(bool activo)
+ public Medio WithActivo(bool activo, DateTime now)
=> new(
id: Id,
codigo: Codigo,
@@ -71,5 +71,5 @@ public sealed class Medio
plataformaEmpresaId: PlataformaEmpresaId,
activo: activo,
fechaCreacion: FechaCreacion,
- fechaModificacion: DateTime.UtcNow);
+ fechaModificacion: now);
}
diff --git a/src/api/SIGCM2.Domain/Entities/PuntoDeVenta.cs b/src/api/SIGCM2.Domain/Entities/PuntoDeVenta.cs
index 87ec486..ba7572b 100644
--- a/src/api/SIGCM2.Domain/Entities/PuntoDeVenta.cs
+++ b/src/api/SIGCM2.Domain/Entities/PuntoDeVenta.cs
@@ -52,8 +52,9 @@ public sealed class PuntoDeVenta
///
/// Retorna una nueva instancia con nombre, numeroAFIP y descripcion actualizados.
/// MedioId es inmutable (enforce en BD).
+ /// Caller is responsible for passing the current UTC timestamp via TimeProvider.GetUtcNow().UtcDateTime.
///
- public PuntoDeVenta WithUpdatedProfile(string nombre, short numeroAFIP, string? descripcion)
+ public PuntoDeVenta WithUpdatedProfile(string nombre, short numeroAFIP, string? descripcion, DateTime now)
=> new(
id: Id,
medioId: MedioId,
@@ -62,9 +63,9 @@ public sealed class PuntoDeVenta
descripcion: descripcion,
activo: Activo,
fechaCreacion: FechaCreacion,
- fechaModificacion: DateTime.UtcNow);
+ fechaModificacion: now);
- public PuntoDeVenta WithActivo(bool activo)
+ public PuntoDeVenta WithActivo(bool activo, DateTime now)
=> new(
id: Id,
medioId: MedioId,
@@ -73,5 +74,5 @@ public sealed class PuntoDeVenta
descripcion: Descripcion,
activo: activo,
fechaCreacion: FechaCreacion,
- fechaModificacion: DateTime.UtcNow);
+ fechaModificacion: now);
}
diff --git a/src/api/SIGCM2.Domain/Entities/Seccion.cs b/src/api/SIGCM2.Domain/Entities/Seccion.cs
index f7d3e2f..115757b 100644
--- a/src/api/SIGCM2.Domain/Entities/Seccion.cs
+++ b/src/api/SIGCM2.Domain/Entities/Seccion.cs
@@ -46,9 +46,9 @@ public sealed class Seccion
///
/// Returns a new instance with updated fields. MedioId and Codigo are immutable.
- /// Sets FechaModificacion = UtcNow.
+ /// Caller is responsible for passing the current UTC timestamp via TimeProvider.GetUtcNow().UtcDateTime.
///
- public Seccion WithUpdatedProfile(string nombre, string tipo)
+ public Seccion WithUpdatedProfile(string nombre, string tipo, DateTime now)
=> new(
id: Id,
medioId: MedioId,
@@ -57,9 +57,9 @@ public sealed class Seccion
tipo: tipo,
activo: Activo,
fechaCreacion: FechaCreacion,
- fechaModificacion: DateTime.UtcNow);
+ fechaModificacion: now);
- public Seccion WithActivo(bool activo)
+ public Seccion WithActivo(bool activo, DateTime now)
=> new(
id: Id,
medioId: MedioId,
@@ -68,5 +68,5 @@ public sealed class Seccion
tipo: Tipo,
activo: activo,
fechaCreacion: FechaCreacion,
- fechaModificacion: DateTime.UtcNow);
+ fechaModificacion: now);
}
diff --git a/src/api/SIGCM2.Domain/Entities/TipoDeIva.cs b/src/api/SIGCM2.Domain/Entities/TipoDeIva.cs
index 3514bdc..9aab66f 100644
--- a/src/api/SIGCM2.Domain/Entities/TipoDeIva.cs
+++ b/src/api/SIGCM2.Domain/Entities/TipoDeIva.cs
@@ -106,9 +106,14 @@ public sealed class TipoDeIva
///
/// Si la predecesora ya está cerrada (VigenciaHasta != null).
/// Si vigenciaDesde no es posterior a la predecesora, o nuevoPorcentaje fuera de rango.
+ ///
+ /// Crea una nueva versión con el porcentaje actualizado.
+ /// Timestamp UTC provisto por el caller (Application layer via TimeProvider).
+ ///
public (TipoDeIva predecesoraCerrada, TipoDeIva nuevaVersion) NuevaVersion(
decimal nuevoPorcentaje,
- DateOnly vigenciaDesde)
+ DateOnly vigenciaDesde,
+ DateTime now)
{
if (VigenciaHasta is not null)
throw new InvalidOperationException(
@@ -132,7 +137,7 @@ public sealed class TipoDeIva
vigenciaHasta: vigenciaDesde.AddDays(-1),
predecesorId: PredecesorId,
fechaCreacion: FechaCreacion,
- fechaModificacion: DateTime.UtcNow);
+ fechaModificacion: now);
var nueva = ForCreation(
codigo: Codigo,
@@ -149,36 +154,36 @@ public sealed class TipoDeIva
// ── Cosmetic mutators (sealed With* — NOT WithPorcentaje) ─────────────────
/// Actualiza la descripción. Porcentaje y vigencias permanecen inmutables.
- public TipoDeIva WithDescripcion(string descripcion)
+ public TipoDeIva WithDescripcion(string descripcion, DateTime now)
=> new(Id, Codigo, descripcion, Porcentaje, AplicaIVA, Activo,
- VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, DateTime.UtcNow);
+ VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, now);
/// Actualiza el código. Porcentaje y vigencias permanecen inmutables.
- public TipoDeIva WithCodigo(string codigo)
+ public TipoDeIva WithCodigo(string codigo, DateTime now)
=> new(Id, codigo, Descripcion, Porcentaje, AplicaIVA, Activo,
- VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, DateTime.UtcNow);
+ VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, now);
/// Actualiza la bandera AplicaIVA. Porcentaje permanece inmutable.
- public TipoDeIva WithAplicaIVA(bool aplicaIVA)
+ public TipoDeIva WithAplicaIVA(bool aplicaIVA, DateTime now)
=> new(Id, Codigo, Descripcion, Porcentaje, aplicaIVA, Activo,
- VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, DateTime.UtcNow);
+ VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, now);
/// Retorna instancia con Activo=false.
- public TipoDeIva Deactivate()
+ public TipoDeIva Deactivate(DateTime now)
=> new(Id, Codigo, Descripcion, Porcentaje, AplicaIVA, false,
- VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, DateTime.UtcNow);
+ VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, now);
/// Retorna instancia con Activo=true.
- public TipoDeIva Reactivate()
+ public TipoDeIva Reactivate(DateTime now)
=> new(Id, Codigo, Descripcion, Porcentaje, AplicaIVA, true,
- VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, DateTime.UtcNow);
+ VigenciaDesde, VigenciaHasta, PredecesorId, FechaCreacion, now);
///
/// Cierra la vigencia seteando VigenciaHasta. Usado por el handler de NuevaVersion.
///
- public TipoDeIva CerrarVigencia(DateOnly vigenciaHasta)
+ public TipoDeIva CerrarVigencia(DateOnly vigenciaHasta, DateTime now)
=> new(Id, Codigo, Descripcion, Porcentaje, AplicaIVA, Activo,
- VigenciaDesde, vigenciaHasta, PredecesorId, FechaCreacion, DateTime.UtcNow);
+ VigenciaDesde, vigenciaHasta, PredecesorId, FechaCreacion, now);
// ── Private helpers ───────────────────────────────────────────────────────
diff --git a/src/api/SIGCM2.Domain/Entities/Usuario.cs b/src/api/SIGCM2.Domain/Entities/Usuario.cs
index deefe66..08d5966 100644
--- a/src/api/SIGCM2.Domain/Entities/Usuario.cs
+++ b/src/api/SIGCM2.Domain/Entities/Usuario.cs
@@ -76,9 +76,10 @@ public sealed class Usuario
///
/// Returns a new instance with updated profile fields.
- /// Sets FechaModificacion = UtcNow. Username and PasswordHash are immutable.
+ /// Caller is responsible for passing the current UTC timestamp via TimeProvider.GetUtcNow().UtcDateTime.
+ /// Username and PasswordHash are immutable.
///
- public Usuario WithUpdatedProfile(string nombre, string apellido, string? email, string rol, bool activo)
+ public Usuario WithUpdatedProfile(string nombre, string apellido, string? email, string rol, bool activo, DateTime now)
=> new(
id: Id,
username: Username,
@@ -89,15 +90,15 @@ public sealed class Usuario
rol: rol,
permisosJson: PermisosJson,
activo: activo,
- fechaModificacion: DateTime.UtcNow,
+ fechaModificacion: now,
ultimoLogin: UltimoLogin,
mustChangePassword: MustChangePassword);
///
/// Returns a new instance with a new password hash and mustChangePassword flag.
- /// Sets FechaModificacion = UtcNow.
+ /// Caller is responsible for passing the current UTC timestamp via TimeProvider.GetUtcNow().UtcDateTime.
///
- public Usuario WithNewPasswordHash(string hash, bool mustChangePassword)
+ public Usuario WithNewPasswordHash(string hash, bool mustChangePassword, DateTime now)
=> new(
id: Id,
username: Username,
@@ -108,15 +109,15 @@ public sealed class Usuario
rol: Rol,
permisosJson: PermisosJson,
activo: Activo,
- fechaModificacion: DateTime.UtcNow,
+ fechaModificacion: now,
ultimoLogin: UltimoLogin,
mustChangePassword: mustChangePassword);
///
/// Returns a new instance with only the MustChangePassword flag changed.
- /// Sets FechaModificacion = UtcNow.
+ /// Caller is responsible for passing the current UTC timestamp via TimeProvider.GetUtcNow().UtcDateTime.
///
- public Usuario WithMustChangePassword(bool value)
+ public Usuario WithMustChangePassword(bool value, DateTime now)
=> new(
id: Id,
username: Username,
@@ -127,16 +128,16 @@ public sealed class Usuario
rol: Rol,
permisosJson: PermisosJson,
activo: Activo,
- fechaModificacion: DateTime.UtcNow,
+ fechaModificacion: now,
ultimoLogin: UltimoLogin,
mustChangePassword: value);
///
/// UDT-009: Returns a new instance with PermisosJson replaced.
- /// Sets FechaModificacion = UtcNow.
+ /// Caller is responsible for passing the current UTC timestamp via TimeProvider.GetUtcNow().UtcDateTime.
/// Accepts raw JSON string so Domain stays free of Application dependencies.
///
- public Usuario WithPermisosJson(string permisosJson)
+ public Usuario WithPermisosJson(string permisosJson, DateTime now)
=> new(
id: Id,
username: Username,
@@ -147,7 +148,7 @@ public sealed class Usuario
rol: Rol,
permisosJson: permisosJson,
activo: Activo,
- fechaModificacion: DateTime.UtcNow,
+ fechaModificacion: now,
ultimoLogin: UltimoLogin,
mustChangePassword: MustChangePassword);