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