diff --git a/Controllers/LinesController.cs b/Controllers/LinesController.cs index 4f1ce34..6b870d4 100644 --- a/Controllers/LinesController.cs +++ b/Controllers/LinesController.cs @@ -105,6 +105,7 @@ namespace line_gestao_api.Controllers [FromQuery] string? search, [FromQuery] string? additionalMode, [FromQuery] string? additionalServices, + [FromQuery] bool includeAssignedReservaInAll = false, [FromQuery] int page = 1, [FromQuery] int pageSize = 10) { @@ -128,7 +129,9 @@ namespace line_gestao_api.Controllers } if (!reservaFilter) - query = ExcludeReservaContext(query); + query = includeAssignedReservaInAll + ? ExcludeOnlyReservaStockContext(query) + : ExcludeReservaContext(query); query = ApplyAdditionalFilters(query, additionalMode, additionalServices); @@ -209,7 +212,8 @@ namespace line_gestao_api.Controllers [FromQuery] string? skil, [FromQuery] string? reservaMode, [FromQuery] string? additionalMode, - [FromQuery] string? additionalServices) + [FromQuery] string? additionalServices, + [FromQuery] bool includeAssignedReservaInAll = false) { var query = _db.MobileLines.AsNoTracking(); var reservaFilter = false; @@ -227,7 +231,9 @@ namespace line_gestao_api.Controllers } if (!reservaFilter) - query = ExcludeReservaContext(query); + query = includeAssignedReservaInAll + ? ExcludeOnlyReservaStockContext(query) + : ExcludeReservaContext(query); query = ApplyAdditionalFilters(query, additionalMode, additionalServices); @@ -398,6 +404,7 @@ namespace line_gestao_api.Controllers [FromQuery] string? operadora, [FromQuery] string? additionalMode, [FromQuery] string? additionalServices, + [FromQuery] bool includeAssignedReservaInAll = false, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, [FromQuery] string? sortBy = "item", @@ -422,7 +429,9 @@ namespace line_gestao_api.Controllers } if (!reservaFilter) - q = ExcludeReservaContext(q); + q = includeAssignedReservaInAll + ? ExcludeOnlyReservaStockContext(q) + : ExcludeReservaContext(q); q = ApplyAdditionalFilters(q, additionalMode, additionalServices); q = OperadoraContaResolver.ApplyOperadoraFilter(q, operadora); @@ -5494,6 +5503,19 @@ namespace line_gestao_api.Controllers (x.Cliente ?? "").Trim().ToUpper() != "RESERVA"); } + private static IQueryable ExcludeOnlyReservaStockContext(IQueryable query) + { + return query.Where(x => + !EF.Functions.ILike((x.Cliente ?? "").Trim(), "RESERVA") && + !( + (x.Cliente ?? "").Trim() == "" && + ( + EF.Functions.ILike((x.Usuario ?? "").Trim(), "RESERVA") || + EF.Functions.ILike((x.Skil ?? "").Trim(), "RESERVA") + ) + )); + } + private static IQueryable ApplyAdditionalFilters( IQueryable query, string? additionalMode, diff --git a/line-gestao-api.Tests/LinesControllerTests.cs b/line-gestao-api.Tests/LinesControllerTests.cs new file mode 100644 index 0000000..f673634 --- /dev/null +++ b/line-gestao-api.Tests/LinesControllerTests.cs @@ -0,0 +1,188 @@ +using line_gestao_api.Controllers; +using line_gestao_api.Data; +using line_gestao_api.Dtos; +using line_gestao_api.Models; +using line_gestao_api.Services; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.FileProviders; +using Xunit; + +namespace line_gestao_api.Tests +{ + public class LinesControllerTests + { + [Fact] + public async Task GetAll_WithAssignedReservaInAll_IncludesAssignedReserveAndExcludesStock() + { + var tenantId = Guid.NewGuid(); + var provider = new TestTenantProvider(tenantId); + await using var db = BuildContext(provider); + + db.MobileLines.AddRange( + new MobileLine + { + TenantId = tenantId, + Item = 1, + Cliente = "CLIENTE A", + Usuario = "USUARIO A", + Skil = "PESSOA JURIDICA", + Linha = "11111111111", + Status = "ATIVO" + }, + new MobileLine + { + TenantId = tenantId, + Item = 2, + Cliente = "AVANCO DISTRIBUIDORA", + Usuario = "RESERVA", + Skil = "RESERVA", + Linha = "22222222222", + Status = "ATIVO" + }, + new MobileLine + { + TenantId = tenantId, + Item = 3, + Cliente = "RESERVA", + Usuario = "RESERVA", + Skil = "RESERVA", + Linha = "33333333333", + Status = "ATIVO" + }); + + await db.SaveChangesAsync(); + + var controller = CreateController(db, provider); + var result = await controller.GetAll(includeAssignedReservaInAll: true, pageSize: 50); + + var ok = Assert.IsType(result.Result); + var payload = Assert.IsType>(ok.Value); + + Assert.Equal(2, payload.Total); + Assert.Contains(payload.Items, x => x.Linha == "11111111111"); + Assert.Contains(payload.Items, x => x.Linha == "22222222222"); + Assert.DoesNotContain(payload.Items, x => x.Linha == "33333333333"); + } + + [Fact] + public async Task GetAll_WithoutAssignedReservaInAll_KeepsAssignedReserveOutOfAll() + { + var tenantId = Guid.NewGuid(); + var provider = new TestTenantProvider(tenantId); + await using var db = BuildContext(provider); + + db.MobileLines.AddRange( + new MobileLine + { + TenantId = tenantId, + Item = 1, + Cliente = "CLIENTE A", + Usuario = "USUARIO A", + Skil = "PESSOA JURIDICA", + Linha = "11111111111", + Status = "ATIVO" + }, + new MobileLine + { + TenantId = tenantId, + Item = 2, + Cliente = "AVANCO DISTRIBUIDORA", + Usuario = "RESERVA", + Skil = "RESERVA", + Linha = "22222222222", + Status = "ATIVO" + }, + new MobileLine + { + TenantId = tenantId, + Item = 3, + Cliente = "RESERVA", + Usuario = "RESERVA", + Skil = "RESERVA", + Linha = "33333333333", + Status = "ATIVO" + }); + + await db.SaveChangesAsync(); + + var controller = CreateController(db, provider); + var result = await controller.GetAll(pageSize: 50); + + var ok = Assert.IsType(result.Result); + var payload = Assert.IsType>(ok.Value); + + Assert.Single(payload.Items); + Assert.Equal(1, payload.Total); + Assert.Contains(payload.Items, x => x.Linha == "11111111111"); + Assert.DoesNotContain(payload.Items, x => x.Linha == "22222222222"); + Assert.DoesNotContain(payload.Items, x => x.Linha == "33333333333"); + } + + private static AppDbContext BuildContext(TestTenantProvider provider) + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(Guid.NewGuid().ToString()) + .Options; + + return new AppDbContext(options, provider, new NullAuditLogBuilder()); + } + + private static LinesController CreateController(AppDbContext db, TestTenantProvider provider) + { + return new LinesController( + db, + provider, + new NullVigenciaNotificationSyncService(), + null!, + null!, + null!, + new TestWebHostEnvironment()); + } + + private sealed class TestTenantProvider : ITenantProvider + { + public TestTenantProvider(Guid tenantId) + { + TenantId = tenantId; + } + + public Guid? ActorTenantId => TenantId; + + public Guid? TenantId { get; private set; } + + public bool HasGlobalViewAccess => false; + + public void SetTenantId(Guid? tenantId) + { + TenantId = tenantId; + } + } + + private sealed class NullAuditLogBuilder : IAuditLogBuilder + { + public List BuildAuditLogs(Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker changeTracker) + { + return new List(); + } + } + + private sealed class NullVigenciaNotificationSyncService : IVigenciaNotificationSyncService + { + public Task SyncCurrentTenantAsync(CancellationToken cancellationToken = default) => Task.CompletedTask; + + public Task SyncTenantAsync(Guid tenantId, CancellationToken cancellationToken = default) => Task.CompletedTask; + } + + private sealed class TestWebHostEnvironment : IWebHostEnvironment + { + public string EnvironmentName { get; set; } = "Tests"; + public string ApplicationName { get; set; } = "line-gestao-api.Tests"; + public string WebRootPath { get; set; } = Path.GetTempPath(); + public IFileProvider WebRootFileProvider { get; set; } = new NullFileProvider(); + public string ContentRootPath { get; set; } = Path.GetTempPath(); + public IFileProvider ContentRootFileProvider { get; set; } = new NullFileProvider(); + } + } +}