feat(adm-009): TipoDeIva + IngresosBrutos handlers, DTOs, DI registration

This commit is contained in:
2026-04-17 18:09:52 -03:00
parent 2cd25e1036
commit bd0c4deea7
47 changed files with 1134 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
namespace SIGCM2.Application.IngresosBrutos.Update;
/// <summary>
/// Updates only cosmetic fields: Descripcion, Activo.
/// Alicuota and Provincia are NOT part of this command — they are immutable.
/// </summary>
public sealed record UpdateIngresosBrutosCommand(
int Id,
string Descripcion,
bool Activo);

View File

@@ -0,0 +1,51 @@
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.Update;
public sealed class UpdateIngresosBrutosCommandHandler
: ICommandHandler<UpdateIngresosBrutosCommand, IngresosBrutosDto>
{
private readonly IIngresosBrutosRepository _repo;
private readonly IAuditLogger _audit;
public UpdateIngresosBrutosCommandHandler(IIngresosBrutosRepository repo, IAuditLogger audit)
{
_repo = repo;
_audit = audit;
}
public async Task<IngresosBrutosDto> Handle(UpdateIngresosBrutosCommand command)
{
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();
using var tx = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted },
TransactionScopeAsyncFlowOption.Enabled);
await _repo.UpdateCosmeticoAsync(command.Id, command.Descripcion, command.Activo);
await _audit.LogAsync(
action: "ingresos_brutos.update",
targetType: "IngresosBrutos",
targetId: command.Id.ToString(),
metadata: new
{
before = new { entity.Descripcion, entity.Activo },
after = new { command.Descripcion, command.Activo },
});
tx.Complete();
return IngresosBrutosMapper.ToDto(updated);
}
}

View File

@@ -0,0 +1,19 @@
using FluentValidation;
namespace SIGCM2.Application.IngresosBrutos.Update;
public sealed class UpdateIngresosBrutosCommandValidator : AbstractValidator<UpdateIngresosBrutosCommand>
{
private const int DescripcionMaxLength = 255;
public UpdateIngresosBrutosCommandValidator()
{
RuleFor(x => x.Id)
.GreaterThan(0).WithMessage("El id debe ser mayor a 0.");
RuleFor(x => x.Descripcion)
.NotEmpty().WithMessage("La descripción es requerida.")
.MaximumLength(DescripcionMaxLength)
.WithMessage($"La descripción no puede superar los {DescripcionMaxLength} caracteres.");
}
}