mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-07 16:19:07 +00:00
fix(vendors): address directory review findings
This commit is contained in:
parent
d649940a8f
commit
8b3df46196
8 changed files with 255 additions and 79 deletions
|
|
@ -1,12 +1,15 @@
|
||||||
using Api.SeaHavenIndustries.Controllers;
|
using Api.SeaHavenIndustries.Controllers;
|
||||||
using Api.SeaHavenIndustries.DTOs;
|
using Api.SeaHavenIndustries.DTOs;
|
||||||
|
using Data.SeaHavenIndustries;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Moq;
|
using Moq;
|
||||||
using SeaHaven.Services.DTOs;
|
using SeaHaven.Services.DTOs;
|
||||||
using SeaHaven.Services.Interfaces;
|
using SeaHaven.Services.Interfaces;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
using System.Text.Json;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Api.SeaHavenIndustries.Tests;
|
namespace Api.SeaHavenIndustries.Tests;
|
||||||
|
|
@ -104,4 +107,63 @@ public class VendorControllerTests
|
||||||
It.Is<CreateVendorDTO>(dto => dto.IsActive == true),
|
It.Is<CreateVendorDTO>(dto => dto.IsActive == true),
|
||||||
"42"), Times.Once);
|
"42"), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(true, "Active Co", "Inactive Co")]
|
||||||
|
[InlineData(false, "Inactive Co", "Active Co")]
|
||||||
|
public async Task GetFacets_WithStatus_ReturnsOnlyMatchingVendors(
|
||||||
|
bool isActive,
|
||||||
|
string expected,
|
||||||
|
string excluded)
|
||||||
|
{
|
||||||
|
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||||
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
||||||
|
.Options;
|
||||||
|
await using var context = new ApplicationDbContext(options);
|
||||||
|
context.Vendors.AddRange(
|
||||||
|
new Vendor { CompanyName = "Active Co", IsActive = true, IsDeleted = false },
|
||||||
|
new Vendor { CompanyName = "Inactive Co", IsActive = false, IsDeleted = false });
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var controller = new VendorController(
|
||||||
|
Mock.Of<IVendorService>(),
|
||||||
|
context,
|
||||||
|
zipDistance: null!,
|
||||||
|
vendorTokens: null!,
|
||||||
|
config: null!);
|
||||||
|
|
||||||
|
var result = await controller.GetFacets(isActive);
|
||||||
|
var payload = JsonSerializer.Serialize(
|
||||||
|
result.Should().BeOfType<OkObjectResult>().Subject.Value);
|
||||||
|
|
||||||
|
payload.Should().Contain(expected);
|
||||||
|
payload.Should().NotContain(excluded);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetFacets_WithoutStatus_PreservesGlobalFacetLookup()
|
||||||
|
{
|
||||||
|
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||||
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
||||||
|
.Options;
|
||||||
|
await using var context = new ApplicationDbContext(options);
|
||||||
|
context.Vendors.AddRange(
|
||||||
|
new Vendor { CompanyName = "Active Co", IsActive = true, IsDeleted = false },
|
||||||
|
new Vendor { CompanyName = "Inactive Co", IsActive = false, IsDeleted = false });
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var controller = new VendorController(
|
||||||
|
Mock.Of<IVendorService>(),
|
||||||
|
context,
|
||||||
|
zipDistance: null!,
|
||||||
|
vendorTokens: null!,
|
||||||
|
config: null!);
|
||||||
|
|
||||||
|
var result = await controller.GetFacets();
|
||||||
|
var payload = JsonSerializer.Serialize(
|
||||||
|
result.Should().BeOfType<OkObjectResult>().Subject.Value);
|
||||||
|
|
||||||
|
payload.Should().Contain("Active Co");
|
||||||
|
payload.Should().Contain("Inactive Co");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,14 @@ public class VendorDataServiceTests
|
||||||
return new ApplicationDbContext(options);
|
return new ApplicationDbContext(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ApplicationDbContext NewSqlServerContext()
|
||||||
|
{
|
||||||
|
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||||
|
.UseSqlServer("Server=(local);Database=TranslationOnly;Trusted_Connection=True;TrustServerCertificate=True")
|
||||||
|
.Options;
|
||||||
|
return new ApplicationDbContext(options);
|
||||||
|
}
|
||||||
|
|
||||||
private static Vendor VendorWithJobs(
|
private static Vendor VendorWithJobs(
|
||||||
string company,
|
string company,
|
||||||
string city,
|
string city,
|
||||||
|
|
@ -181,4 +189,81 @@ public class VendorDataServiceTests
|
||||||
roofing.TotalCount.Should().Be(1);
|
roofing.TotalCount.Should().Be(1);
|
||||||
roofing.Items.Single().CompanyName.Should().Be("Middle");
|
roofing.Items.Single().CompanyName.Should().Be("Middle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetPagedAsync_CountsDistinctDirectAndLinkedWorkOrders()
|
||||||
|
{
|
||||||
|
await using var context = NewContext();
|
||||||
|
var vendor = new Vendor
|
||||||
|
{
|
||||||
|
CompanyName = "History Co",
|
||||||
|
IsActive = true,
|
||||||
|
IsDeleted = false,
|
||||||
|
Dispatches = new List<Dispatch>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
WorkOrderId = 100,
|
||||||
|
DispatchWorkOrders = new List<DispatchWorkOrder>
|
||||||
|
{
|
||||||
|
new() { WorkOrderId = 100 },
|
||||||
|
new() { WorkOrderId = 101 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new() { WorkOrderId = 101 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
context.Vendors.Add(vendor);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var service = new VendorDataService(context);
|
||||||
|
var result = await service.GetPagedAsync(1, 12, isActive: true);
|
||||||
|
|
||||||
|
result.Items.Single().TotalJobs.Should().Be(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetByIdAsync_ReturnsTrackedVendorWithoutLoadingDispatchGraph()
|
||||||
|
{
|
||||||
|
await using var context = NewContext();
|
||||||
|
var vendor = VendorWithJobs("Lean Co", "Chicago", "IL", "Plumbing", 2);
|
||||||
|
context.Vendors.Add(vendor);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
context.ChangeTracker.Clear();
|
||||||
|
|
||||||
|
var service = new VendorDataService(context);
|
||||||
|
var result = await service.GetByIdAsync(vendor.Id);
|
||||||
|
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
context.Entry(result!).State.Should().Be(EntityState.Unchanged);
|
||||||
|
context.Entry(result!).Collection(nameof(Vendor.Dispatches)).IsLoaded.Should().BeFalse();
|
||||||
|
result!.Dispatches.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("trade")]
|
||||||
|
[InlineData("location")]
|
||||||
|
[InlineData("job-bucket")]
|
||||||
|
public void BuildDirectoryQuery_TranslatesRepresentativeFiltersForSqlServer(string filter)
|
||||||
|
{
|
||||||
|
using var context = NewSqlServerContext();
|
||||||
|
var service = new VendorDataService(context);
|
||||||
|
|
||||||
|
var query = filter switch
|
||||||
|
{
|
||||||
|
"trade" => service.BuildDirectoryQuery(trades: new[] { "Plumbing" }),
|
||||||
|
"location" => service.BuildDirectoryQuery(locations: new[] { "Chicago, IL" }),
|
||||||
|
_ => service.BuildDirectoryQuery(jobBuckets: new[] { "50-99" })
|
||||||
|
};
|
||||||
|
|
||||||
|
var sql = query.ToQueryString();
|
||||||
|
|
||||||
|
sql.Should().Contain("SELECT");
|
||||||
|
if (filter == "trade")
|
||||||
|
sql.Should().Contain("TradeSpecialties");
|
||||||
|
if (filter == "location")
|
||||||
|
sql.Should().Contain("City");
|
||||||
|
if (filter == "job-bucket")
|
||||||
|
sql.Should().Contain("UNION");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ public class VendorServiceTests
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var data = new Mock<IVendorDataService>();
|
var data = new Mock<IVendorDataService>();
|
||||||
data.Setup(x => x.GetByIdAsync(11)).ReturnsAsync(vendor);
|
data.Setup(x => x.GetByIdWithDetailsAsync(11)).ReturnsAsync(vendor);
|
||||||
|
|
||||||
var result = await NewService(data).GetVendorByIdAsync(11);
|
var result = await NewService(data).GetVendorByIdAsync(11);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -282,11 +282,16 @@ namespace Api.SeaHavenIndustries.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("facets")]
|
[HttpGet("facets")]
|
||||||
public async Task<IActionResult> GetFacets()
|
public async Task<IActionResult> GetFacets([FromQuery] bool? isActive = null)
|
||||||
{
|
{
|
||||||
var vendors = await _db.Vendors
|
var query = _db.Vendors
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(v => v.IsDeleted == null || v.IsDeleted == false)
|
.Where(v => v.IsDeleted == null || v.IsDeleted == false);
|
||||||
|
|
||||||
|
if (isActive.HasValue)
|
||||||
|
query = query.Where(v => v.IsActive == isActive.Value);
|
||||||
|
|
||||||
|
var vendors = await query
|
||||||
.OrderByDescending(v => v.Id)
|
.OrderByDescending(v => v.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
namespace Data.SeaHavenIndustries
|
namespace Data.SeaHavenIndustries
|
||||||
{
|
{
|
||||||
public class Vendor : FullAuditEntity
|
public class Vendor : FullAuditEntity
|
||||||
|
|
@ -15,6 +17,8 @@ namespace Data.SeaHavenIndustries
|
||||||
public string? GoogleMapsUrl { get; set; }
|
public string? GoogleMapsUrl { get; set; }
|
||||||
public string? Notes { get; set; }
|
public string? Notes { get; set; }
|
||||||
public bool IsActive { get; set; } = true;
|
public bool IsActive { get; set; } = true;
|
||||||
|
[NotMapped]
|
||||||
|
public int TotalJobs { get; set; }
|
||||||
public ICollection<Dispatch>? Dispatches { get; set; }
|
public ICollection<Dispatch>? Dispatches { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,18 +17,16 @@ namespace SeaHaven.DataServices.Implementation
|
||||||
|
|
||||||
public async Task<Vendor?> GetByIdAsync(int id)
|
public async Task<Vendor?> GetByIdAsync(int id)
|
||||||
{
|
{
|
||||||
return await _context.Vendors
|
return await _context.Vendors.FirstOrDefaultAsync(v => v.Id == id);
|
||||||
.Include(v => v.Dispatches!)
|
|
||||||
.ThenInclude(dispatch => dispatch.DispatchWorkOrders)
|
|
||||||
.FirstOrDefaultAsync(v => v.Id == id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Vendor?> GetByIdWithDetailsAsync(int id)
|
public async Task<Vendor?> GetByIdWithDetailsAsync(int id)
|
||||||
{
|
{
|
||||||
return await _context.Vendors
|
return await ProjectWithTotalJobs(
|
||||||
.Include(v => v.Dispatches!)
|
_context.Vendors
|
||||||
.ThenInclude(dispatch => dispatch.DispatchWorkOrders)
|
.AsNoTracking()
|
||||||
.FirstOrDefaultAsync(v => v.Id == id);
|
.Where(v => v.Id == id))
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Vendor>> GetAllAsync()
|
public async Task<IEnumerable<Vendor>> GetAllAsync()
|
||||||
|
|
@ -46,24 +44,50 @@ namespace SeaHaven.DataServices.Implementation
|
||||||
IReadOnlyCollection<string>? locations = null,
|
IReadOnlyCollection<string>? locations = null,
|
||||||
IReadOnlyCollection<string>? jobBuckets = null)
|
IReadOnlyCollection<string>? jobBuckets = null)
|
||||||
{
|
{
|
||||||
search ??= "";
|
var projectedQuery = BuildDirectoryQuery(
|
||||||
|
search,
|
||||||
|
isActive,
|
||||||
|
companies,
|
||||||
|
trades,
|
||||||
|
locations,
|
||||||
|
jobBuckets);
|
||||||
|
|
||||||
var query = _context.Vendors.Where(v => v.IsDeleted == null || v.IsDeleted == false);
|
var totalCount = await projectedQuery.CountAsync();
|
||||||
|
var items = await projectedQuery
|
||||||
|
.OrderByDescending(v => v.Id)
|
||||||
|
.Skip((page - 1) * pageSize)
|
||||||
|
.Take(pageSize)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return (items, totalCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal IQueryable<Vendor> BuildDirectoryQuery(
|
||||||
|
string? search = null,
|
||||||
|
bool? isActive = true,
|
||||||
|
IReadOnlyCollection<string>? companies = null,
|
||||||
|
IReadOnlyCollection<string>? trades = null,
|
||||||
|
IReadOnlyCollection<string>? locations = null,
|
||||||
|
IReadOnlyCollection<string>? jobBuckets = null)
|
||||||
|
{
|
||||||
|
var query = _context.Vendors
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(v => v.IsDeleted == null || v.IsDeleted == false);
|
||||||
|
|
||||||
if (isActive.HasValue)
|
if (isActive.HasValue)
|
||||||
query = query.Where(v => v.IsActive == isActive.Value);
|
query = query.Where(v => v.IsActive == isActive.Value);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.ToLower();
|
var normalizedSearch = search.ToLower();
|
||||||
query = query.Where(v =>
|
query = query.Where(v =>
|
||||||
(v.CompanyName ?? "").ToLower().Contains(s) ||
|
(v.CompanyName ?? "").ToLower().Contains(normalizedSearch) ||
|
||||||
(v.ContactName ?? "").ToLower().Contains(s) ||
|
(v.ContactName ?? "").ToLower().Contains(normalizedSearch) ||
|
||||||
(v.Email ?? "").ToLower().Contains(s) ||
|
(v.Email ?? "").ToLower().Contains(normalizedSearch) ||
|
||||||
(v.TradeSpecialties ?? "").ToLower().Contains(s) ||
|
(v.TradeSpecialties ?? "").ToLower().Contains(normalizedSearch) ||
|
||||||
(v.City ?? "").ToLower().Contains(s) ||
|
(v.City ?? "").ToLower().Contains(normalizedSearch) ||
|
||||||
(v.State ?? "").ToLower().Contains(s) ||
|
(v.State ?? "").ToLower().Contains(normalizedSearch) ||
|
||||||
(v.Zip ?? "").ToLower().Contains(s));
|
(v.Zip ?? "").ToLower().Contains(normalizedSearch));
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectedCompanies = companies?
|
var selectedCompanies = companies?
|
||||||
|
|
@ -72,7 +96,8 @@ namespace SeaHaven.DataServices.Implementation
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToArray() ?? Array.Empty<string>();
|
.ToArray() ?? Array.Empty<string>();
|
||||||
if (selectedCompanies.Length > 0)
|
if (selectedCompanies.Length > 0)
|
||||||
query = query.Where(v => selectedCompanies.Contains((v.CompanyName ?? "").ToLower()));
|
query = query.Where(v =>
|
||||||
|
selectedCompanies.Contains((v.CompanyName ?? "").ToLower()));
|
||||||
|
|
||||||
var selectedTrades = trades?
|
var selectedTrades = trades?
|
||||||
.Where(value => !string.IsNullOrWhiteSpace(value))
|
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||||
|
|
@ -90,66 +115,56 @@ namespace SeaHaven.DataServices.Implementation
|
||||||
if (selectedLocations.Length > 0)
|
if (selectedLocations.Length > 0)
|
||||||
query = query.Where(BuildLocationPredicate(selectedLocations));
|
query = query.Where(BuildLocationPredicate(selectedLocations));
|
||||||
|
|
||||||
|
var result = ProjectWithTotalJobs(query);
|
||||||
var selectedJobBuckets = jobBuckets?
|
var selectedJobBuckets = jobBuckets?
|
||||||
.Where(value => !string.IsNullOrWhiteSpace(value))
|
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||||
.Select(value => value.Trim().ToLower())
|
.Select(value => value.Trim().ToLower())
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToArray() ?? Array.Empty<string>();
|
.ToArray() ?? Array.Empty<string>();
|
||||||
if (selectedJobBuckets.Length > 0)
|
if (selectedJobBuckets.Length == 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
var under50 = selectedJobBuckets.Contains("under-50");
|
||||||
|
var from50To99 = selectedJobBuckets.Contains("50-99");
|
||||||
|
var from100To149 = selectedJobBuckets.Contains("100-149");
|
||||||
|
var from150 = selectedJobBuckets.Contains("150-plus");
|
||||||
|
|
||||||
|
return result.Where(v =>
|
||||||
|
(under50 && v.TotalJobs < 50) ||
|
||||||
|
(from50To99 && v.TotalJobs >= 50 && v.TotalJobs <= 99) ||
|
||||||
|
(from100To149 && v.TotalJobs >= 100 && v.TotalJobs <= 149) ||
|
||||||
|
(from150 && v.TotalJobs >= 150));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQueryable<Vendor> ProjectWithTotalJobs(IQueryable<Vendor> query) =>
|
||||||
|
query.Select(v => new Vendor
|
||||||
{
|
{
|
||||||
var under50 = selectedJobBuckets.Contains("under-50");
|
Id = v.Id,
|
||||||
var from50To99 = selectedJobBuckets.Contains("50-99");
|
CompanyName = v.CompanyName,
|
||||||
var from100To149 = selectedJobBuckets.Contains("100-149");
|
ContactName = v.ContactName,
|
||||||
var from150 = selectedJobBuckets.Contains("150-plus");
|
Email = v.Email,
|
||||||
|
Phone = v.Phone,
|
||||||
var vendorsWithDispatches = await query
|
CompanyPhone = v.CompanyPhone,
|
||||||
.Include(v => v.Dispatches!)
|
Address = v.Address,
|
||||||
.ThenInclude(dispatch => dispatch.DispatchWorkOrders)
|
City = v.City,
|
||||||
.OrderByDescending(v => v.Id)
|
State = v.State,
|
||||||
.ToListAsync();
|
Zip = v.Zip,
|
||||||
var filtered = vendorsWithDispatches.Where(vendor =>
|
TradeSpecialties = v.TradeSpecialties,
|
||||||
{
|
GoogleMapsUrl = v.GoogleMapsUrl,
|
||||||
var totalJobs = CountDistinctWorkOrders(vendor);
|
Notes = v.Notes,
|
||||||
return (under50 && totalJobs < 50) ||
|
IsActive = v.IsActive,
|
||||||
(from50To99 && totalJobs is >= 50 and <= 99) ||
|
IsDeleted = v.IsDeleted,
|
||||||
(from100To149 && totalJobs is >= 100 and <= 149) ||
|
TotalJobs = _context.Dispatches
|
||||||
(from150 && totalJobs >= 150);
|
.Where(dispatch =>
|
||||||
}).ToList();
|
dispatch.VendorId == v.Id &&
|
||||||
|
dispatch.WorkOrderId.HasValue)
|
||||||
return (
|
.Select(dispatch => dispatch.WorkOrderId!.Value)
|
||||||
filtered.Skip((page - 1) * pageSize).Take(pageSize),
|
.Union(
|
||||||
filtered.Count);
|
_context.DispatchWorkOrders
|
||||||
}
|
.Where(link => link.Dispatch != null && link.Dispatch.VendorId == v.Id)
|
||||||
|
.Select(link => link.WorkOrderId))
|
||||||
var totalCount = await query.CountAsync();
|
.Count()
|
||||||
var items = await query
|
});
|
||||||
.Include(v => v.Dispatches!)
|
|
||||||
.ThenInclude(dispatch => dispatch.DispatchWorkOrders)
|
|
||||||
.OrderByDescending(v => v.Id)
|
|
||||||
.Skip((page - 1) * pageSize)
|
|
||||||
.Take(pageSize)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return (items, totalCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int CountDistinctWorkOrders(Vendor vendor)
|
|
||||||
{
|
|
||||||
if (vendor.Dispatches == null)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return vendor.Dispatches
|
|
||||||
.SelectMany(dispatch =>
|
|
||||||
{
|
|
||||||
var linked = dispatch.DispatchWorkOrders?.Select(item => item.WorkOrderId)
|
|
||||||
?? Enumerable.Empty<int>();
|
|
||||||
return dispatch.WorkOrderId.HasValue
|
|
||||||
? linked.Append(dispatch.WorkOrderId.Value)
|
|
||||||
: linked;
|
|
||||||
})
|
|
||||||
.Distinct()
|
|
||||||
.Count();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly MethodInfo StringConcatMethod =
|
private static readonly MethodInfo StringConcatMethod =
|
||||||
typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) })!;
|
typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) })!;
|
||||||
|
|
|
||||||
3
SeaHaven.DataServices/Properties/AssemblyInfo.cs
Normal file
3
SeaHaven.DataServices/Properties/AssemblyInfo.cs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Api.SeaHavenIndustries.Tests")]
|
||||||
|
|
@ -29,7 +29,7 @@ namespace SeaHaven.Services.Implementation
|
||||||
|
|
||||||
public async Task<VendorDTO?> GetVendorByIdAsync(int id)
|
public async Task<VendorDTO?> GetVendorByIdAsync(int id)
|
||||||
{
|
{
|
||||||
var vendor = await _vendorDataService.GetByIdAsync(id);
|
var vendor = await _vendorDataService.GetByIdWithDetailsAsync(id);
|
||||||
return vendor == null ? null : MapToDTO(vendor);
|
return vendor == null ? null : MapToDTO(vendor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,7 +180,9 @@ namespace SeaHaven.Services.Implementation
|
||||||
GoogleMapsUrl = vendor.GoogleMapsUrl,
|
GoogleMapsUrl = vendor.GoogleMapsUrl,
|
||||||
Notes = vendor.Notes,
|
Notes = vendor.Notes,
|
||||||
IsActive = vendor.IsActive,
|
IsActive = vendor.IsActive,
|
||||||
TotalJobs = CountDistinctWorkOrders(vendor)
|
TotalJobs = vendor.Dispatches == null
|
||||||
|
? vendor.TotalJobs
|
||||||
|
: CountDistinctWorkOrders(vendor)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue