From d0a90483b39a6e4c1ac3d68a44f1db0b2949ddf8 Mon Sep 17 00:00:00 2001 From: Alexandre Brandizzi Date: Fri, 21 Aug 2026 09:32:58 -0300 Subject: [PATCH 1/3] fix(vendors): show canonical list contact data --- .../VendorDataServiceTests.cs | 103 ++++++++++++++++++ .../Implementation/VendorDataService.cs | 14 ++- 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/Api.SeaHavenIndustries.Tests/VendorDataServiceTests.cs b/Api.SeaHavenIndustries.Tests/VendorDataServiceTests.cs index 650c911..2bed953 100644 --- a/Api.SeaHavenIndustries.Tests/VendorDataServiceTests.cs +++ b/Api.SeaHavenIndustries.Tests/VendorDataServiceTests.cs @@ -364,6 +364,109 @@ public class VendorDataServiceTests items.Should().Contain(i => i.ContactName == "No Company Tech" && i.VendorId != null); } + [Fact] + public async Task GetDirectoryPagedAsync_UsesCanonicalCompanyLocationAndEmailFallback() + { + await using var context = NewContext(); + var company = new VendorCompany + { + Name = "Canonical Co", + NormalizedName = "canonical co", + Email = "dispatch@canonical.example", + City = "Chicago", + State = "IL" + }; + context.VendorCompanies.Add(company); + context.Vendors.AddRange( + new Vendor + { + CompanyName = "Canonical Co", + ContactName = "Company Fallback", + Email = " ", + City = "Stale City", + State = "TX", + IsActive = true, + IsDeleted = false, + Company = company + }, + new Vendor + { + CompanyName = "Canonical Co", + ContactName = "Technician Email", + Email = "technician@canonical.example", + IsActive = true, + IsDeleted = false, + Company = company + }, + new Vendor + { + CompanyName = "Legacy Co", + ContactName = "Unlinked Technician", + Email = "legacy@example.com", + City = "Austin", + State = "TX", + IsActive = true, + IsDeleted = false, + CompanyId = null + }); + await context.SaveChangesAsync(); + + var service = new VendorDataService(context); + var (items, totalCount) = await service.GetDirectoryPagedAsync(1, 50, isActive: true); + + totalCount.Should().Be(3); + var companyFallback = items.Single(i => i.ContactName == "Company Fallback"); + companyFallback.Email.Should().Be("dispatch@canonical.example"); + companyFallback.City.Should().Be("Chicago"); + companyFallback.State.Should().Be("IL"); + + var technicianEmail = items.Single(i => i.ContactName == "Technician Email"); + technicianEmail.Email.Should().Be("technician@canonical.example"); + technicianEmail.City.Should().Be("Chicago"); + technicianEmail.State.Should().Be("IL"); + + var legacy = items.Single(i => i.ContactName == "Unlinked Technician"); + legacy.Email.Should().Be("legacy@example.com"); + legacy.City.Should().Be("Austin"); + legacy.State.Should().Be("TX"); + } + + [Fact] + public async Task GetDirectoryPagedAsync_SearchAndLocationFilterUseCanonicalCompanyLocation() + { + await using var context = NewContext(); + var company = new VendorCompany + { + Name = "Searchable Co", + NormalizedName = "searchable co", + City = "Chicago", + State = "IL" + }; + context.VendorCompanies.Add(company); + context.Vendors.Add(new Vendor + { + CompanyName = "Searchable Co", + ContactName = "Pat", + City = null, + State = null, + IsActive = true, + IsDeleted = false, + Company = company + }); + await context.SaveChangesAsync(); + + var service = new VendorDataService(context); + var search = await service.GetDirectoryPagedAsync(1, 50, search: "chicago", isActive: true); + var location = await service.GetDirectoryPagedAsync( + 1, + 50, + isActive: true, + locations: new[] { "Chicago, IL" }); + + search.Items.Should().ContainSingle(i => i.ContactName == "Pat"); + location.Items.Should().ContainSingle(i => i.ContactName == "Pat"); + } + [Theory] [InlineData("trade")] [InlineData("location")] diff --git a/SeaHaven.DataServices/Implementation/VendorDataService.cs b/SeaHaven.DataServices/Implementation/VendorDataService.cs index 01a5be6..81f77ba 100644 --- a/SeaHaven.DataServices/Implementation/VendorDataService.cs +++ b/SeaHaven.DataServices/Implementation/VendorDataService.cs @@ -191,13 +191,21 @@ namespace SeaHaven.DataServices.Implementation CompanyId = v.CompanyId ?? 0, CompanyName = v.CompanyName, ContactName = v.ContactName, - Email = v.Email, + Email = v.Email != null && v.Email.Trim() != "" + ? v.Email + : v.Company != null + ? v.Company.Email + : v.Email, Phone = v.Phone, CompanyPhone = v.CompanyPhone, PreferredContact = v.PreferredContact, Address = v.Address, - City = v.City, - State = v.State, + City = v.Company != null && v.Company.City != null && v.Company.City.Trim() != "" + ? v.Company.City + : v.City, + State = v.Company != null && v.Company.State != null && v.Company.State.Trim() != "" + ? v.Company.State + : v.State, Zip = v.Zip, TradeSpecialties = v.TradeSpecialties, GoogleMapsUrl = v.GoogleMapsUrl, From 9c3da9b5e3e3150214f1fd0a264ca8fc96c4211b Mon Sep 17 00:00:00 2001 From: Alexandre Brandizzi Date: Fri, 21 Aug 2026 09:40:51 -0300 Subject: [PATCH 2/3] fix(vendors): keep location fallback atomic --- .../VendorDataServiceTests.cs | 17 +++++++++++------ .../Implementation/VendorDataService.cs | 8 ++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Api.SeaHavenIndustries.Tests/VendorDataServiceTests.cs b/Api.SeaHavenIndustries.Tests/VendorDataServiceTests.cs index 2bed953..a7d91f1 100644 --- a/Api.SeaHavenIndustries.Tests/VendorDataServiceTests.cs +++ b/Api.SeaHavenIndustries.Tests/VendorDataServiceTests.cs @@ -440,15 +440,15 @@ public class VendorDataServiceTests Name = "Searchable Co", NormalizedName = "searchable co", City = "Chicago", - State = "IL" + State = null }; context.VendorCompanies.Add(company); context.Vendors.Add(new Vendor { CompanyName = "Searchable Co", ContactName = "Pat", - City = null, - State = null, + City = "Stale City", + State = "TX", IsActive = true, IsDeleted = false, Company = company @@ -461,10 +461,15 @@ public class VendorDataServiceTests 1, 50, isActive: true, - locations: new[] { "Chicago, IL" }); + locations: new[] { "Chicago" }); - search.Items.Should().ContainSingle(i => i.ContactName == "Pat"); - location.Items.Should().ContainSingle(i => i.ContactName == "Pat"); + var searchRow = search.Items.Should().ContainSingle(i => i.ContactName == "Pat").Subject; + searchRow.City.Should().Be("Chicago"); + searchRow.State.Should().BeNull(); + + var locationRow = location.Items.Should().ContainSingle(i => i.ContactName == "Pat").Subject; + locationRow.City.Should().Be("Chicago"); + locationRow.State.Should().BeNull(); } [Theory] diff --git a/SeaHaven.DataServices/Implementation/VendorDataService.cs b/SeaHaven.DataServices/Implementation/VendorDataService.cs index 81f77ba..dc09d71 100644 --- a/SeaHaven.DataServices/Implementation/VendorDataService.cs +++ b/SeaHaven.DataServices/Implementation/VendorDataService.cs @@ -200,10 +200,14 @@ namespace SeaHaven.DataServices.Implementation CompanyPhone = v.CompanyPhone, PreferredContact = v.PreferredContact, Address = v.Address, - City = v.Company != null && v.Company.City != null && v.Company.City.Trim() != "" + City = v.Company != null && + ((v.Company.City != null && v.Company.City.Trim() != "") || + (v.Company.State != null && v.Company.State.Trim() != "")) ? v.Company.City : v.City, - State = v.Company != null && v.Company.State != null && v.Company.State.Trim() != "" + State = v.Company != null && + ((v.Company.City != null && v.Company.City.Trim() != "") || + (v.Company.State != null && v.Company.State.Trim() != "")) ? v.Company.State : v.State, Zip = v.Zip, From 98eb546738a0fa2dbd0c83c7b60a93630e30dccd Mon Sep 17 00:00:00 2001 From: Alexandre Brandizzi Date: Tue, 25 Aug 2026 05:38:14 -0300 Subject: [PATCH 3/3] chore(deps): upgrade AWS CDK library and CLI --- infra/cdk/package-lock.json | 16 ++++++++-------- infra/cdk/package.json | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/infra/cdk/package-lock.json b/infra/cdk/package-lock.json index 0ffb1ce..f09bb8f 100644 --- a/infra/cdk/package-lock.json +++ b/infra/cdk/package-lock.json @@ -8,12 +8,12 @@ "name": "shoc-backend-cdk", "version": "0.1.0", "dependencies": { - "aws-cdk-lib": "2.265.0", + "aws-cdk-lib": "2.266.0", "constructs": "10.8.1" }, "devDependencies": { "@types/node": "26.2.0", - "aws-cdk": "2.1137.0", + "aws-cdk": "2.1138.0", "typescript": "7.0.2" }, "engines": { @@ -419,9 +419,9 @@ } }, "node_modules/aws-cdk": { - "version": "2.1137.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1137.0.tgz", - "integrity": "sha512-SRy2df/5cqGvq8ncph1m93rWEh3RGbvP6uwElw+W0x135vZDzTpfbuq2L8oAHwK2yDt3RNS5sb7Phwb7xqQP2g==", + "version": "2.1138.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1138.0.tgz", + "integrity": "sha512-gZ5F8rmh+qc7ZNWsbaXYoV+p7jSYynRRg70s7FAn3VmzRaSkTE31ijpQHYroxCbDEtKSzgN63ORR/WuZmvXAwA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -432,9 +432,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.265.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.265.0.tgz", - "integrity": "sha512-Ur96d/ZW+gqAW4XtqKQp//D9O/jQvyRoXhAqoHGybpV03qjckGIQcLcGkLV68+jv03TX/rOXHCsa2IEFcCXa4w==", + "version": "2.266.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.266.0.tgz", + "integrity": "sha512-sBQU42pEc9ud3yeVU2En2euQRUhCg63eaJIPEVpBtE5aPhJjN3d9MkzQ9eYGLVXP3fnohUE54BiVOdUrkEDUbg==", "bundleDependencies": [ "@aws/cloudformation-validate", "@balena/dockerignore", diff --git a/infra/cdk/package.json b/infra/cdk/package.json index c79b316..7e07795 100644 --- a/infra/cdk/package.json +++ b/infra/cdk/package.json @@ -13,12 +13,12 @@ "deploy": "npm run build && cdk deploy" }, "dependencies": { - "aws-cdk-lib": "2.265.0", + "aws-cdk-lib": "2.266.0", "constructs": "10.8.1" }, "devDependencies": { "@types/node": "26.2.0", - "aws-cdk": "2.1137.0", + "aws-cdk": "2.1138.0", "typescript": "7.0.2" } }