diff --git a/backend/src/main/java/ca/bc/gov/app/dto/legacy/ForestClientContactDto.java b/backend/src/main/java/ca/bc/gov/app/dto/legacy/ForestClientContactDto.java index 44dc4dbec0..da75a6fa9c 100644 --- a/backend/src/main/java/ca/bc/gov/app/dto/legacy/ForestClientContactDto.java +++ b/backend/src/main/java/ca/bc/gov/app/dto/legacy/ForestClientContactDto.java @@ -9,6 +9,7 @@ public record ForestClientContactDto( String clientLocnCode, List locationCode, String contactCode, + String contactCodeDescription, String contactName, String businessPhone, String secondaryPhone, diff --git a/backend/src/main/java/ca/bc/gov/app/repository/client/ContactTypeCodeRepository.java b/backend/src/main/java/ca/bc/gov/app/repository/client/ContactTypeCodeRepository.java index ac46a506ab..ce35f94779 100644 --- a/backend/src/main/java/ca/bc/gov/app/repository/client/ContactTypeCodeRepository.java +++ b/backend/src/main/java/ca/bc/gov/app/repository/client/ContactTypeCodeRepository.java @@ -2,6 +2,7 @@ import ca.bc.gov.app.entity.client.ContactTypeCodeEntity; import java.time.LocalDate; +import java.util.Set; import org.springframework.data.domain.Pageable; import org.springframework.data.r2dbc.repository.Query; import org.springframework.data.repository.reactive.ReactiveCrudRepository; @@ -25,4 +26,6 @@ public interface ContactTypeCodeRepository Flux findActiveAt(LocalDate activeDate, Pageable pageable); Mono findByOrDescription(String id, String description); + + Flux findByContactTypeCodeIn(Set contactTypeCodes); } diff --git a/backend/src/main/java/ca/bc/gov/app/service/client/ClientCodeService.java b/backend/src/main/java/ca/bc/gov/app/service/client/ClientCodeService.java index 442d86cebf..9134db5dad 100644 --- a/backend/src/main/java/ca/bc/gov/app/service/client/ClientCodeService.java +++ b/backend/src/main/java/ca/bc/gov/app/service/client/ClientCodeService.java @@ -5,6 +5,7 @@ import ca.bc.gov.app.dto.client.CodeNameDto; import ca.bc.gov.app.dto.client.IdentificationTypeDto; import ca.bc.gov.app.entity.client.ClientTypeCodeEntity; +import ca.bc.gov.app.entity.client.ContactTypeCodeEntity; import ca.bc.gov.app.predicates.QueryPredicates; import ca.bc.gov.app.repository.client.ClientTypeCodeRepository; import ca.bc.gov.app.repository.client.ContactTypeCodeRepository; @@ -13,6 +14,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -155,5 +157,14 @@ public Mono getIdentificationTypeByCode(String idCode) { entity.getDescription())); } + public Mono> fetchContactTypesFromList(Set contactTypes){ + + return contactTypeCodeRepository + .findByContactTypeCodeIn(contactTypes) + .collectMap( + ContactTypeCodeEntity::getContactTypeCode, + ContactTypeCodeEntity::getDescription + ); + } } diff --git a/backend/src/main/java/ca/bc/gov/app/service/client/ClientService.java b/backend/src/main/java/ca/bc/gov/app/service/client/ClientService.java index dc7e7a2c3f..25380601f5 100644 --- a/backend/src/main/java/ca/bc/gov/app/service/client/ClientService.java +++ b/backend/src/main/java/ca/bc/gov/app/service/client/ClientService.java @@ -13,6 +13,7 @@ import ca.bc.gov.app.dto.client.ClientValueTextDto; import ca.bc.gov.app.dto.client.EmailRequestDto; import ca.bc.gov.app.dto.client.LegalTypeEnum; +import ca.bc.gov.app.dto.legacy.ForestClientContactDto; import ca.bc.gov.app.dto.legacy.ForestClientDetailsDto; import ca.bc.gov.app.dto.legacy.ForestClientDto; import ca.bc.gov.app.exception.ClientAlreadyExistException; @@ -28,15 +29,19 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -48,6 +53,7 @@ public class ClientService { private final ClientCountryProvinceService countryProvinceService; private final BcRegistryService bcRegistryService; + private final ClientCodeService codeService; private final ChesService chesService; private final ClientLegacyService legacyService; private final Predicate isMultiAddressEnabled; @@ -147,6 +153,7 @@ public Mono getClientDetailsByIncorporationNumber( public Mono getClientDetailsByClientNumber(String clientNumber) { return legacyService .searchByClientNumber(clientNumber) + .flatMap(this::populateContactTypes) .flatMap(forestClientDetailsDto -> Mono .just(forestClientDetailsDto) .filter(dto -> (StringUtils.isNotBlank(dto.corpRegnNmbr()))) @@ -158,6 +165,7 @@ public Mono getClientDetailsByClientNumber(String client .next() ) .flatMap(documentMono -> populateGoodStandingInd(forestClientDetailsDto, documentMono)) + .onErrorContinue(NoClientDataFound.class, (ex, obj) -> log.error("No data found on BC Registry for client number: {}", clientNumber) ) @@ -169,27 +177,6 @@ public Mono getClientDetailsByClientNumber(String client ); } - private Mono populateGoodStandingInd( - ForestClientDetailsDto forestClientDetailsDto, - BcRegistryDocumentDto document - ) { - Boolean goodStandingInd = document.business().goodStanding(); - String goodStanding = BooleanUtils.toString( - goodStandingInd, - "Y", - "N", - StringUtils.EMPTY - ); - - log.info("Setting goodStandingInd for client: {} to {}", - forestClientDetailsDto.clientNumber(), goodStanding); - - ForestClientDetailsDto updatedDetails = - forestClientDetailsDto.withGoodStandingInd(goodStanding); - - return Mono.just(updatedDetails); - } - /** * Searches the BC Registry API for {@link BcRegistryFacetSearchResultEntryDto} instances matching * the given value and maps them to {@link ClientLookUpDto} instances. @@ -268,6 +255,61 @@ public Mono triggerEmailDuplicatedClient( .then(); } + private Mono populateContactTypes( + ForestClientDetailsDto forestClientDetailsDto + ) { + + if (CollectionUtils.isEmpty(forestClientDetailsDto.contacts())) + return Mono.just(forestClientDetailsDto); + + Set contactCodes = + forestClientDetailsDto + .contacts() + .stream() + .filter(Objects::nonNull) + .map(ForestClientContactDto::contactCode) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toSet()); + + + return codeService + .fetchContactTypesFromList(contactCodes) + .map(map -> + forestClientDetailsDto + .withContacts( + forestClientDetailsDto + .contacts() + .stream() + .map(contact -> + contact.withContactCodeDescription(map.getOrDefault(contact.contactCode(), StringUtils.EMPTY)) + ) + .toList() + ) + ); + } + + + private Mono populateGoodStandingInd( + ForestClientDetailsDto forestClientDetailsDto, + BcRegistryDocumentDto document + ) { + Boolean goodStandingInd = document.business().goodStanding(); + String goodStanding = BooleanUtils.toString( + goodStandingInd, + "Y", + "N", + StringUtils.EMPTY + ); + + log.info("Setting goodStandingInd for client: {} to {}", + forestClientDetailsDto.clientNumber(), goodStanding); + + ForestClientDetailsDto updatedDetails = + forestClientDetailsDto.withGoodStandingInd(goodStanding); + + return Mono.just(updatedDetails); + } + private Function> buildDetails() { return document -> buildAddress( diff --git a/backend/src/test/java/ca/bc/gov/app/service/client/ClientServiceIntegrationTest.java b/backend/src/test/java/ca/bc/gov/app/service/client/ClientServiceIntegrationTest.java index 97ff8c610a..eb09c9ff3c 100644 --- a/backend/src/test/java/ca/bc/gov/app/service/client/ClientServiceIntegrationTest.java +++ b/backend/src/test/java/ca/bc/gov/app/service/client/ClientServiceIntegrationTest.java @@ -1,5 +1,7 @@ package ca.bc.gov.app.service.client; +import ca.bc.gov.app.dto.legacy.ForestClientContactDto; +import ca.bc.gov.app.dto.legacy.ForestClientLocationDto; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZonedDateTime; @@ -169,4 +171,350 @@ void testGetClientDetailsWithGoodStandingIndicator() { .verifyComplete(); } + @Test + @DisplayName("Search by incorporation number and fail due to no legal type") + void shouldSearchByIncorporationAndFail1(){ + + String corpRegnNmbr = "C00123456"; + String userId = "idir\\jwick"; + String businessId = "123456"; + LocalDateTime date = LocalDateTime.of(2021, 1, 1, 0, 0, 0); + + + BcRegistryOfficerDto mockOfficer = new BcRegistryOfficerDto( + "officer@email.com", + "John", + "Doe", + "D", + "123456", + "My Company Ltd.", + "Person"); + + BcRegistryAddressDto mockAddress = new BcRegistryAddressDto( + "City", + "Canada", + "BC", + "A1B2C3", + "Street", + "", + "", + ""); + + BcRegistryRoleDto mockRole = new BcRegistryRoleDto( + LocalDate.now().minusYears(1), + null, + "Owner"); + + BcRegistryPartyDto mockParty = new BcRegistryPartyDto( + mockAddress, + mockAddress, + mockOfficer, + List.of(mockRole)); + + BcRegistryAddressDto mockMailingAddress = mockAddress; + BcRegistryAddressDto mockDeliveryAddress = mockAddress; + BcRegistryBusinessAdressesDto mockBusinessOffice = new BcRegistryBusinessAdressesDto( + mockMailingAddress, + mockDeliveryAddress); + + BcRegistryAlternateNameDto mockAlternateName = new BcRegistryAlternateNameDto( + "EntityType", + corpRegnNmbr, + "Alternate Name", + ZonedDateTime.now(), + LocalDate.now()); + + BcRegistryBusinessDto mockBusinessDto = new BcRegistryBusinessDto( + List.of(mockAlternateName), + true, + false, + false, + false, + corpRegnNmbr, + "MY COMPANY LTD.", + "TOTS", + "Active"); + + BcRegistryOfficesDto mockOffices = new BcRegistryOfficesDto(mockBusinessOffice); + + BcRegistryDocumentDto mockDocumentDto = + new BcRegistryDocumentDto(mockBusinessDto, mockOffices, List.of(mockParty)); + + Mockito + .when(bcRegistryService + .requestDocumentData(corpRegnNmbr)) + .thenReturn(Flux.just(mockDocumentDto)); + + Mockito + .when(legacyService + .searchLegacy(corpRegnNmbr,"Alternate Name",userId,businessId)) + .thenReturn(Flux.empty()); + + service.getClientDetailsByIncorporationNumber(corpRegnNmbr, userId, businessId,"idir") + .as(StepVerifier::create) + .expectErrorMessage("406 NOT_ACCEPTABLE \"Unsupported Legal Type: TOTS\"") + .verify(); + + } + + @Test + @DisplayName("Search by client number and succeed") + void shouldSearchByClientNumberAndgetResult(){ + + String clientNumber = "00123456"; + String corpRegnNmbr = "C00123456"; + LocalDateTime date = LocalDateTime.of(2021, 1, 1, 0, 0, 0); + + ForestClientDetailsDto clientDto = new ForestClientDetailsDto( + clientNumber, + "MY COMPANY LTD.", + null, + null, + "ACT", + "Active", + "C", + "Corporation", + "ID", + "Client Identification", + "00123456", + "B", + corpRegnNmbr, + "MYCO", + "678", + "Test Client", + date, + "Admin", + null, + null, + null, + null, + null); + + + BcRegistryOfficerDto mockOfficer = new BcRegistryOfficerDto( + "officer@email.com", + "John", + "Doe", + "D", + "123456", + "My Company Ltd.", + "Person"); + + BcRegistryAddressDto mockAddress = new BcRegistryAddressDto( + "City", + "Canada", + "BC", + "A1B2C3", + "Street", + "", + "", + ""); + + BcRegistryRoleDto mockRole = new BcRegistryRoleDto( + LocalDate.now().minusYears(1), + null, + "Owner"); + + BcRegistryPartyDto mockParty = new BcRegistryPartyDto( + mockAddress, + mockAddress, + mockOfficer, + List.of(mockRole)); + + BcRegistryAddressDto mockMailingAddress = mockAddress; + BcRegistryAddressDto mockDeliveryAddress = mockAddress; + BcRegistryBusinessAdressesDto mockBusinessOffice = new BcRegistryBusinessAdressesDto( + mockMailingAddress, + mockDeliveryAddress); + + BcRegistryAlternateNameDto mockAlternateName = new BcRegistryAlternateNameDto( + "EntityType", + corpRegnNmbr, + "Alternate Name", + ZonedDateTime.now(), + LocalDate.now()); + + BcRegistryBusinessDto mockBusinessDto = new BcRegistryBusinessDto( + List.of(mockAlternateName), + true, + false, + false, + false, + corpRegnNmbr, + "MY COMPANY LTD.", + "LIC", + "Active"); + + BcRegistryOfficesDto mockOffices = new BcRegistryOfficesDto(mockBusinessOffice); + + BcRegistryDocumentDto mockDocumentDto = + new BcRegistryDocumentDto(mockBusinessDto, mockOffices, List.of(mockParty)); + + Mockito + .when(bcRegistryService + .requestDocumentData(corpRegnNmbr)) + .thenReturn(Flux.just(mockDocumentDto)); + + Mockito + .when(legacyService + .searchByClientNumber(clientNumber)) + .thenReturn(Mono.just(clientDto)); + + service.getClientDetailsByClientNumber(clientNumber) + .as(StepVerifier::create) + .expectNext(clientDto.withGoodStandingInd("Y")) + .verifyComplete(); + } + + @Test + @DisplayName("Search by client number and succeed including dependencies") + void shouldSearchByClientNumberAndgetResultWithContactAndLocation(){ + + String clientNumber = "00123456"; + String corpRegnNmbr = "C00123456"; + LocalDateTime date = LocalDateTime.of(2021, 1, 1, 0, 0, 0); + + ForestClientDetailsDto clientDto = new ForestClientDetailsDto( + clientNumber, + "MY COMPANY LTD.", + null, + null, + "ACT", + "Active", + "C", + "Corporation", + "ID", + "Client Identification", + "00123456", + "B", + corpRegnNmbr, + "MYCO", + "678", + "Test Client", + date, + "Admin", + null, + null, + List.of( + new ForestClientLocationDto( + clientNumber, + "00", + "Location", + "1234 Street", + null, + null, + "City", + "BC", + "A1B2C3", + "Canada", + "1234567890", + "0987654321", + "1234567890", + "0987654321", + "mail@notme.ca", + "N", + null, + null, + null, + "Admin", + "Admin", + 70L + ) + ), + List.of( + new ForestClientContactDto( + clientNumber, + "00", + List.of("00"), + "DI", + "Director", + "John Doe", + "1234567890", + "0987654321", + "1234567890", + "mail@nobody.ca", + "Admin", + "Admin", + 70L + ) + + ), + null); + + + BcRegistryOfficerDto mockOfficer = new BcRegistryOfficerDto( + "officer@email.com", + "John", + "Doe", + "D", + "123456", + "My Company Ltd.", + "Person"); + + BcRegistryAddressDto mockAddress = new BcRegistryAddressDto( + "City", + "Canada", + "BC", + "A1B2C3", + "Street", + "", + "", + ""); + + BcRegistryRoleDto mockRole = new BcRegistryRoleDto( + LocalDate.now().minusYears(1), + null, + "Owner"); + + BcRegistryPartyDto mockParty = new BcRegistryPartyDto( + mockAddress, + mockAddress, + mockOfficer, + List.of(mockRole)); + + BcRegistryAddressDto mockMailingAddress = mockAddress; + BcRegistryAddressDto mockDeliveryAddress = mockAddress; + BcRegistryBusinessAdressesDto mockBusinessOffice = new BcRegistryBusinessAdressesDto( + mockMailingAddress, + mockDeliveryAddress); + + BcRegistryAlternateNameDto mockAlternateName = new BcRegistryAlternateNameDto( + "EntityType", + corpRegnNmbr, + "Alternate Name", + ZonedDateTime.now(), + LocalDate.now()); + + BcRegistryBusinessDto mockBusinessDto = new BcRegistryBusinessDto( + List.of(mockAlternateName), + true, + false, + false, + false, + corpRegnNmbr, + "MY COMPANY LTD.", + "LIC", + "Active"); + + BcRegistryOfficesDto mockOffices = new BcRegistryOfficesDto(mockBusinessOffice); + + BcRegistryDocumentDto mockDocumentDto = + new BcRegistryDocumentDto(mockBusinessDto, mockOffices, List.of(mockParty)); + + Mockito + .when(bcRegistryService + .requestDocumentData(corpRegnNmbr)) + .thenReturn(Flux.just(mockDocumentDto)); + + Mockito + .when(legacyService + .searchByClientNumber(clientNumber)) + .thenReturn(Mono.just(clientDto)); + + service.getClientDetailsByClientNumber(clientNumber) + .as(StepVerifier::create) + .expectNext(clientDto.withGoodStandingInd("Y")) + .verifyComplete(); + } + } diff --git a/frontend/cypress/e2e/pages/SearchPage.cy.ts b/frontend/cypress/e2e/pages/SearchPage.cy.ts index efdb9a134d..b33ce17d4b 100644 --- a/frontend/cypress/e2e/pages/SearchPage.cy.ts +++ b/frontend/cypress/e2e/pages/SearchPage.cy.ts @@ -185,8 +185,7 @@ describe("Search Page", () => { cy.get("@windowOpen").should( "be.calledWith", `/clients/details/${clientNumber}`, - "_blank", - "noopener", + "_self", ); }); describe("and STAFF_CLIENT_DETAIL is turned off", () => { @@ -241,8 +240,7 @@ describe("Search Page", () => { cy.get("@windowOpen").should( "be.calledWith", `/clients/details/${clientNumber}`, - "_blank", - "noopener", + "_self", ); }); describe("and STAFF_CLIENT_DETAIL is turned off", () => { diff --git a/frontend/src/pages/ClientDetailsPage.vue b/frontend/src/pages/ClientDetailsPage.vue index 7ca19f0cca..2a8059b0e4 100644 --- a/frontend/src/pages/ClientDetailsPage.vue +++ b/frontend/src/pages/ClientDetailsPage.vue @@ -70,8 +70,8 @@ const formatCount = (count = 0) => { }; const formatAddress = (location: ClientLocation) => { - const { addressOne, city, provinceCode, countryDesc, postalCode } = location; - const list = [addressOne, city, provinceCode, countryDesc, postalCode]; + const { addressOne, city, province, country, postalCode } = location; + const list = [addressOne, city, province, country, postalCode]; return list.join(", "); }; diff --git a/frontend/src/pages/SearchPage.vue b/frontend/src/pages/SearchPage.vue index 24123ee364..e7bda5b2ee 100644 --- a/frontend/src/pages/SearchPage.vue +++ b/frontend/src/pages/SearchPage.vue @@ -142,7 +142,11 @@ const openClientDetails = (clientCode: string) => { const url = featureFlags.STAFF_CLIENT_DETAIL ? `/clients/details/${clientCode}` : `https://${greenDomain}/int/client/client02MaintenanceAction.do?bean.clientNumber=${clientCode}`; - window.open(url, "_blank", "noopener"); + + if (featureFlags.STAFF_CLIENT_DETAIL) + window.open(url, "_self"); + else + window.open(url, "_blank", "noopener"); } }; diff --git a/frontend/src/pages/client-details/ContactView.vue b/frontend/src/pages/client-details/ContactView.vue index a99e70bb68..59f4d19814 100644 --- a/frontend/src/pages/client-details/ContactView.vue +++ b/frontend/src/pages/client-details/ContactView.vue @@ -17,7 +17,7 @@ const faxNumber = computed(() => formatPhoneNumber(props.data.faxNumber));