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 b7eda00585..44dc4dbec0 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 @@ -1,8 +1,13 @@ package ca.bc.gov.app.dto.legacy; +import java.util.List; +import lombok.With; + +@With public record ForestClientContactDto( String clientNumber, String clientLocnCode, + List locationCode, String contactCode, String contactName, String businessPhone, diff --git a/backend/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionService.java b/backend/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionService.java index 14923cfca6..2a688f501f 100644 --- a/backend/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionService.java +++ b/backend/src/main/java/ca/bc/gov/app/service/client/ClientSubmissionService.java @@ -531,7 +531,7 @@ private void cleanMatchers(SubmissionMatchDetailEntity entity) { }); } - private Mono saveAndAssociateContact( + private Flux saveAndAssociateContact( List locations, ClientContactDto contact, Integer submissionId, @@ -544,14 +544,18 @@ private Mono saveAndAssociateContact( .map(contactEntity -> contactEntity.withSubmissionId(submissionId)) .map(contactEntity -> contactEntity.withUserId(contact.index() == 0 ? userId : null)) .flatMap(submissionContactRepository::save) - .map(contactEntity -> - SubmissionLocationContactEntity - .builder() - .submissionLocationId(getLocationIdByName(locations, contact)) - .submissionContactId(contactEntity.getSubmissionContactId()) - .build() - ) - .flatMap(submissionLocationContactRepository::save); + .flatMapMany(contactEntity -> + Flux + .fromIterable(contact.locationNames()) + .map(locationName -> + SubmissionLocationContactEntity + .builder() + .submissionLocationId(getLocationIdByName(locations, locationName.text())) + .submissionContactId(contactEntity.getSubmissionContactId()) + .build() + ) + .flatMap(submissionLocationContactRepository::save) + ); } private Mono> saveAddresses( diff --git a/backend/src/main/java/ca/bc/gov/app/util/ClientMapper.java b/backend/src/main/java/ca/bc/gov/app/util/ClientMapper.java index 653cdfa69f..fbb0cdc42f 100644 --- a/backend/src/main/java/ca/bc/gov/app/util/ClientMapper.java +++ b/backend/src/main/java/ca/bc/gov/app/util/ClientMapper.java @@ -18,6 +18,9 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ClientMapper { + public static final String FIRST_NAME = "firstName"; + public static final String LAST_NAME = "lastName"; + /** * Maps a {@link ClientBusinessInformationDto} object to a {@link SubmissionDetailEntity} object, * using the specified submission ID. @@ -160,6 +163,19 @@ public static Integer getLocationIdByName( .orElse(0); } + public static Integer getLocationIdByName( + List locations, + String locationName + ) { + return + locations + .stream() + .filter(location -> locationName.equalsIgnoreCase(location.getName())) + .map(SubmissionLocationEntity::getSubmissionLocationId) + .findFirst() + .orElse(0); + } + public static Map parseName(String displayName, String provider) { String[] nameParts = displayName.contains(",") @@ -168,13 +184,13 @@ public static Map parseName(String displayName, String provider) if ("IDIR".equalsIgnoreCase(provider) && nameParts.length >= 2) { - String firstName = nameParts[1].replaceAll("\\s+\\w+:\\w+$", StringUtils.EMPTY).trim(); + String firstName = nameParts[1].replaceAll("\\s+[^\\s:]+:[^\\s:]+$", StringUtils.EMPTY).trim(); String lastName = nameParts[0].trim(); return Map.of( - "firstName", firstName.split(" ")[0].trim(), - "lastName", + FIRST_NAME, firstName.split(" ")[0].trim(), + LAST_NAME, Stream.concat( Stream @@ -187,14 +203,14 @@ public static Map parseName(String displayName, String provider) } else if (nameParts.length >= 2) { return Map.of( - "firstName", nameParts[0].trim(), - "lastName", String.join(" ", Arrays.copyOfRange(nameParts, 1, nameParts.length)) + FIRST_NAME, nameParts[0].trim(), + LAST_NAME, String.join(" ", Arrays.copyOfRange(nameParts, 1, nameParts.length)) ); } return Map.of( - "firstName", nameParts[0], - "lastName", "" + FIRST_NAME, nameParts[0], + LAST_NAME, "" ); } diff --git a/frontend/src/pages/ClientDetailsPage.vue b/frontend/src/pages/ClientDetailsPage.vue index 607698ffb9..106c3047fa 100644 --- a/frontend/src/pages/ClientDetailsPage.vue +++ b/frontend/src/pages/ClientDetailsPage.vue @@ -117,7 +117,7 @@ const formatLocations = ( const associatedLocationsRecord = computed(() => { const result: Record = {}; sortedContacts.value?.forEach((contact) => { - result[contact.contactCode] = formatLocations(contact.clientLocnCode); + result[contact.contactCode] = formatLocations(contact.locationCode); }); return result; }); diff --git a/frontend/stub/__files/response-clients-details-G.json b/frontend/stub/__files/response-clients-details-G.json index e0d0f62308..44cb360af8 100644 --- a/frontend/stub/__files/response-clients-details-G.json +++ b/frontend/stub/__files/response-clients-details-G.json @@ -13,7 +13,7 @@ "wcbFirmNumber": "{{randomValue length=7 type='NUMERIC'}}", "contacts": [ { - "clientLocnCode": ["00"], + "locationCode": ["00"], "contactCode": "00", "contactName": "Cheryl Bibby", "contactTypeCode": "BL", @@ -24,7 +24,7 @@ "emailAddress": "cheryl@ktb.com" }, { - "clientLocnCode": ["01", "02"], + "locationCode": ["01", "02"], "contactCode": "01", "contactName": "Edward Burns", "contactTypeCode": "DI", @@ -35,7 +35,7 @@ "emailAddress": "burns@ktb.com" }, { - "clientLocnCode": ["02"], + "locationCode": ["02"], "contactCode": "02", "contactName": "Christoffer Stewart", "contactTypeCode": "BL", diff --git a/frontend/stub/__files/response-clients-details-S.json b/frontend/stub/__files/response-clients-details-S.json index 6ba72e1d90..eb87ef8742 100644 --- a/frontend/stub/__files/response-clients-details-S.json +++ b/frontend/stub/__files/response-clients-details-S.json @@ -18,7 +18,7 @@ "clientComment": "Email from Michael Scott to request any letters for sec deposits be mailed to 3000, 28th St, Scranton", "wcbFirmNumber": "{{randomValue length=7 type='NUMERIC'}}", "contacts": [{ - "clientLocnCode": ["00"], + "locationCode": ["00"], "contactCode": "00", "contactName": "Cheryl Bibby", "contactTypeCode": "BL", diff --git a/legacy/src/main/java/ca/bc/gov/app/dto/ForestClientContactDto.java b/legacy/src/main/java/ca/bc/gov/app/dto/ForestClientContactDto.java index 9fd3cee960..0cc2b7225e 100644 --- a/legacy/src/main/java/ca/bc/gov/app/dto/ForestClientContactDto.java +++ b/legacy/src/main/java/ca/bc/gov/app/dto/ForestClientContactDto.java @@ -1,11 +1,13 @@ package ca.bc.gov.app.dto; +import java.util.List; import lombok.With; @With public record ForestClientContactDto( String clientNumber, String clientLocnCode, + List locationCode, String contactCode, String contactName, String businessPhone, diff --git a/legacy/src/main/java/ca/bc/gov/app/mappers/AbstractForestClientMapper.java b/legacy/src/main/java/ca/bc/gov/app/mappers/AbstractForestClientMapper.java index 9004b9dcbf..15e5ec29c8 100644 --- a/legacy/src/main/java/ca/bc/gov/app/mappers/AbstractForestClientMapper.java +++ b/legacy/src/main/java/ca/bc/gov/app/mappers/AbstractForestClientMapper.java @@ -2,6 +2,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.List; import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.mapstruct.InheritInverseConfiguration; @@ -49,4 +50,9 @@ default LocalDateTime toLocalDateTime(LocalDate date) { return date == null ? null : date.atStartOfDay(); } + @Named("AddToListQualifier") + default List addToList(String value) { + return value != null ? List.of(value) : List.of(); + } + } diff --git a/legacy/src/main/java/ca/bc/gov/app/mappers/ForestClientContactMapper.java b/legacy/src/main/java/ca/bc/gov/app/mappers/ForestClientContactMapper.java index 9794f646df..83eb0af085 100644 --- a/legacy/src/main/java/ca/bc/gov/app/mappers/ForestClientContactMapper.java +++ b/legacy/src/main/java/ca/bc/gov/app/mappers/ForestClientContactMapper.java @@ -22,6 +22,11 @@ public interface ForestClientContactMapper extends source = "cellPhone", target = "secondaryPhone" ) + @Mapping( + source = "clientLocnCode", + target = "locationCode", + qualifiedByName = "AddToListQualifier" + ) ForestClientContactDto toDto(ForestClientContactEntity entity); @Override diff --git a/legacy/src/main/java/ca/bc/gov/app/service/ClientSearchService.java b/legacy/src/main/java/ca/bc/gov/app/service/ClientSearchService.java index 7d262524f9..dbdfdb16ea 100644 --- a/legacy/src/main/java/ca/bc/gov/app/service/ClientSearchService.java +++ b/legacy/src/main/java/ca/bc/gov/app/service/ClientSearchService.java @@ -25,6 +25,7 @@ import io.micrometer.observation.annotation.Observed; import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.Comparator; import java.util.Optional; import java.util.stream.Collectors; @@ -63,10 +64,8 @@ public class ClientSearchService { private final ForestClientContactRepository contactRepository; private final ForestClientLocationRepository locationRepository; private final AbstractForestClientMapper forestClientMapper; - private final - AbstractForestClientMapper locationMapper; - private final - AbstractForestClientMapper contactMapper; + private final AbstractForestClientMapper locationMapper; + private final AbstractForestClientMapper contactMapper; private final R2dbcEntityTemplate template; private final ForestClientConfiguration configuration; @@ -82,10 +81,8 @@ public class ClientSearchService { * @param companyName The company name of the client to be searched for. * @return A Flux stream of ForestClientDto objects that match the search criteria. */ - public Flux findByRegistrationNumberOrCompanyName( - String registrationNumber, - String companyName - ) { + public Flux findByRegistrationNumberOrCompanyName(String registrationNumber, + String companyName) { if (StringUtils.isAllBlank(registrationNumber, companyName)) { log.error("Missing required parameter to search for registration number or company name"); @@ -95,30 +92,19 @@ public Flux findByRegistrationNumberOrCompanyName( log.info("Searching for registration number: {} or company name: {}", registrationNumber, companyName); - return - forestClientRepository - .findClientByIncorporationOrName(registrationNumber, companyName) - .doOnNext( - dto -> log.info( - "Found client with registration name {} or company name {} as {} {}", - registrationNumber, companyName, - dto.getClientNumber(), dto.getClientName())) - .switchIfEmpty( - Flux - .from(Mono.justOrEmpty(Optional.ofNullable(companyName))) - .filter(StringUtils::isNotBlank) - .flatMap(name -> - doingBusinessAsRepository - .findByDoingBusinessAsName(name.toUpperCase()) - .doOnNext(dto -> log.info("Found client doing business as: {} {}", - dto.getClientNumber(), dto.getDoingBusinessAsName())) - .map(ClientDoingBusinessAsEntity::getClientNumber) - .flatMap(forestClientRepository::findByClientNumber) - ) - ) - .map(forestClientMapper::toDto) - .distinct(ForestClientDto::clientNumber) - .sort(Comparator.comparing(ForestClientDto::clientNumber)); + return forestClientRepository.findClientByIncorporationOrName(registrationNumber, companyName) + .doOnNext( + dto -> log.info("Found client with registration name {} or company name {} as {} {}", + registrationNumber, companyName, dto.getClientNumber(), dto.getClientName())) + .switchIfEmpty(Flux.from(Mono.justOrEmpty(Optional.ofNullable(companyName))) + .filter(StringUtils::isNotBlank).flatMap( + name -> doingBusinessAsRepository.findByDoingBusinessAsName(name.toUpperCase()) + .doOnNext(dto -> log.info("Found client doing business as: {} {}", + dto.getClientNumber(), dto.getDoingBusinessAsName())) + .map(ClientDoingBusinessAsEntity::getClientNumber) + .flatMap(forestClientRepository::findByClientNumber))) + .map(forestClientMapper::toDto).distinct(ForestClientDto::clientNumber) + .sort(Comparator.comparing(ForestClientDto::clientNumber)); } /** @@ -134,13 +120,8 @@ public Flux findByRegistrationNumberOrCompanyName( * @param identification The identification of the client to be searched for. Optional. * @return A Flux stream of ForestClientDto objects that match the search criteria. */ - public Flux findByIndividual( - String firstName, - String lastName, - LocalDate dob, - String identification, - boolean fuzzy - ) { + public Flux findByIndividual(String firstName, String lastName, LocalDate dob, + String identification, boolean fuzzy) { if (StringUtils.isAnyBlank(firstName, lastName) || dob == null) { log.error("Missing required parameter to search for individual"); @@ -151,38 +132,25 @@ public Flux findByIndividual( StringUtils.defaultString(identification)); if (StringUtils.isBlank(identification) && fuzzy) { - return clientRepository - .findByIndividualFuzzy( - String.format("%s %s", firstName, lastName), - dob.atStartOfDay() - ) - .map(forestClientMapper::toDto) - .distinct(ForestClientDto::clientNumber) - .sort(Comparator.comparing(ForestClientDto::clientNumber)) - .doOnNext(dto -> log.info("Found individual matching {} {} {} as {} {}", - firstName, lastName, dob, - dto.clientNumber(), dto.clientName()) - ); + return clientRepository.findByIndividualFuzzy(String.format("%s %s", firstName, lastName), + dob.atStartOfDay()).map(forestClientMapper::toDto).distinct(ForestClientDto::clientNumber) + .sort(Comparator.comparing(ForestClientDto::clientNumber)).doOnNext( + dto -> log.info("Found individual matching {} {} {} as {} {}", firstName, lastName, + dob, dto.clientNumber(), dto.clientName())); } - Criteria queryCriteria = where("legalFirstName").is(firstName).ignoreCase(true) - .and(CLIENT_NAME).is(lastName).ignoreCase(true) - .and("birthdate").is(dob.atStartOfDay()) - .and("clientTypeCode").is("I").ignoreCase(true); + Criteria queryCriteria = where("legalFirstName").is(firstName).ignoreCase(true).and(CLIENT_NAME) + .is(lastName).ignoreCase(true).and("birthdate").is(dob.atStartOfDay()).and("clientTypeCode") + .is("I").ignoreCase(true); if (StringUtils.isNotBlank(identification)) { - queryCriteria = queryCriteria - .and(CLIENT_IDENTIFICATION) - .is(identification) - .ignoreCase(true); + queryCriteria = queryCriteria.and(CLIENT_IDENTIFICATION).is(identification).ignoreCase(true); } - return searchClientByQuery(queryCriteria, ForestClientEntity.class) - .map(forestClientMapper::toDto) - .doOnNext( - dto -> log.info("Found individual matching {} {} {} {} as {} {}", - firstName, lastName, dob, StringUtils.defaultString(identification), - dto.clientNumber(), dto.clientName())); + return searchClientByQuery(queryCriteria, ForestClientEntity.class).map( + forestClientMapper::toDto).doOnNext( + dto -> log.info("Found individual matching {} {} {} {} as {} {}", firstName, lastName, dob, + StringUtils.defaultString(identification), dto.clientNumber(), dto.clientName())); } /** @@ -201,15 +169,11 @@ public Flux matchBy(String companyName) { return Flux.error(new MissingRequiredParameterException("companyName")); } - return - forestClientRepository - .matchBy(companyName) - .map(forestClientMapper::toDto) - .distinct(ForestClientDto::clientNumber) - .sort(Comparator.comparing(ForestClientDto::clientNumber)) - .doOnNext(dto -> log.info("Found match for {} as {} {}", - companyName, - dto.clientNumber(), dto.clientName())); + return forestClientRepository.matchBy(companyName).map(forestClientMapper::toDto) + .distinct(ForestClientDto::clientNumber) + .sort(Comparator.comparing(ForestClientDto::clientNumber)).doOnNext( + dto -> log.info("Found match for {} as {} {}", companyName, dto.clientNumber(), + dto.clientName())); } /** @@ -232,15 +196,11 @@ public Flux findByIdAndLastName(String clientId, String lastNam Criteria queryCriteria = where(CLIENT_IDENTIFICATION).is(clientId).ignoreCase(true) .and(CLIENT_NAME).is(lastName).ignoreCase(true); - return searchClientByQuery(queryCriteria, ForestClientEntity.class) - .map(forestClientMapper::toDto) - .distinct(ForestClientDto::clientNumber) - .sort(Comparator.comparing(ForestClientDto::clientNumber)) - .doOnNext( - dto -> log.info("Found client with clientId {} and lastName {} as {} {}", - clientId, lastName, - dto.clientNumber(), dto.clientName()) - ); + return searchClientByQuery(queryCriteria, ForestClientEntity.class).map( + forestClientMapper::toDto).distinct(ForestClientDto::clientNumber) + .sort(Comparator.comparing(ForestClientDto::clientNumber)).doOnNext( + dto -> log.info("Found client with clientId {} and lastName {} as {} {}", clientId, + lastName, dto.clientNumber(), dto.clientName())); } /** @@ -262,18 +222,14 @@ public Flux findByIdentification(String idType, String identifi } Criteria queryCriteria = where("clientIdTypeCode").is(idType).ignoreCase(true) - .and(CLIENT_IDENTIFICATION).is(identification).ignoreCase(true) - .and("clientTypeCode").is("I").ignoreCase(true); + .and(CLIENT_IDENTIFICATION).is(identification).ignoreCase(true).and("clientTypeCode") + .is("I").ignoreCase(true); - return searchClientByQuery(queryCriteria, ForestClientEntity.class) - .map(forestClientMapper::toDto) - .distinct(ForestClientDto::clientNumber) - .sort(Comparator.comparing(ForestClientDto::clientNumber)) - .doOnNext( - dto -> log.info("Found client with clientId {} {} as {} {}", - idType, identification, - dto.clientNumber(), dto.clientName()) - ); + return searchClientByQuery(queryCriteria, ForestClientEntity.class).map( + forestClientMapper::toDto).distinct(ForestClientDto::clientNumber) + .sort(Comparator.comparing(ForestClientDto::clientNumber)).doOnNext( + dto -> log.info("Found client with clientId {} {} as {} {}", idType, identification, + dto.clientNumber(), dto.clientName())); } /** @@ -293,35 +249,19 @@ public Flux findByGeneralEmail(String email) { Criteria queryCriteria = where("emailAddress").is(email).ignoreCase(true); - Flux locations = - searchClientByQuery(queryCriteria, ForestClientLocationEntity.class) - .flatMap( - entity -> searchClientByQuery( - where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is(entity.getClientNumber()), - ForestClientEntity.class - ) - ) - .map(forestClientMapper::toDto); - Flux contacts = - searchClientByQuery(queryCriteria, ForestClientContactEntity.class) - .flatMap( - entity -> searchClientByQuery( - where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is(entity.getClientNumber()), - ForestClientEntity.class - ) - ) - .map(forestClientMapper::toDto); - - return - Flux - .concat(locations, contacts) - .distinct(ForestClientDto::clientNumber) - .sort(Comparator.comparing(ForestClientDto::clientNumber)) - .doOnNext( - dto -> log.info("Found client with email {} as {} {}", - email, - dto.clientNumber(), dto.clientName()) - ); + Flux locations = searchClientByQuery(queryCriteria, + ForestClientLocationEntity.class).flatMap(entity -> searchClientByQuery( + where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is(entity.getClientNumber()), + ForestClientEntity.class)).map(forestClientMapper::toDto); + Flux contacts = searchClientByQuery(queryCriteria, + ForestClientContactEntity.class).flatMap(entity -> searchClientByQuery( + where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is(entity.getClientNumber()), + ForestClientEntity.class)).map(forestClientMapper::toDto); + + return Flux.concat(locations, contacts).distinct(ForestClientDto::clientNumber) + .sort(Comparator.comparing(ForestClientDto::clientNumber)).doOnNext( + dto -> log.info("Found client with email {} as {} {}", email, dto.clientNumber(), + dto.clientName())); } /** @@ -333,54 +273,30 @@ public Flux findByGeneralEmail(String email) { * @param phoneNumber The phone number of the client to be searched for. * @return A Flux stream of ForestClientDto objects that match the search criteria. */ - public Flux findByGeneralPhoneNumber( - String phoneNumber - ) { + public Flux findByGeneralPhoneNumber(String phoneNumber) { if (StringUtils.isBlank(phoneNumber)) { return Flux.error(new MissingRequiredParameterException("phoneNumber")); } - Criteria queryCriteria = where("businessPhone").is(phoneNumber) - .or("cellPhone").is(phoneNumber) + Criteria queryCriteria = where("businessPhone").is(phoneNumber).or("cellPhone").is(phoneNumber) .or("faxNumber").is(phoneNumber); - Flux locations = - searchClientByQuery( - queryCriteria.or("homePhone").is(phoneNumber), - ForestClientLocationEntity.class - ) - .flatMap( - entity -> searchClientByQuery( - where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is(entity.getClientNumber()), - ForestClientEntity.class - ) - ) - .map(forestClientMapper::toDto); - - Flux contacts = - searchClientByQuery( - queryCriteria, - ForestClientContactEntity.class - ) - .flatMap( - entity -> searchClientByQuery( - where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is(entity.getClientNumber()), - ForestClientEntity.class - ) - ) - .map(forestClientMapper::toDto); - - return - Flux - .concat(locations, contacts) - .distinct(ForestClientDto::clientNumber) - .sort(Comparator.comparing(ForestClientDto::clientNumber)) - .doOnNext( - dto -> log.info("Found client with phone number {} as {} {}", - phoneNumber, - dto.clientNumber(), dto.clientName()) - ); + Flux locations = searchClientByQuery( + queryCriteria.or("homePhone").is(phoneNumber), ForestClientLocationEntity.class).flatMap( + entity -> searchClientByQuery( + where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is(entity.getClientNumber()), + ForestClientEntity.class)).map(forestClientMapper::toDto); + + Flux contacts = searchClientByQuery(queryCriteria, + ForestClientContactEntity.class).flatMap(entity -> searchClientByQuery( + where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is(entity.getClientNumber()), + ForestClientEntity.class)).map(forestClientMapper::toDto); + + return Flux.concat(locations, contacts).distinct(ForestClientDto::clientNumber) + .sort(Comparator.comparing(ForestClientDto::clientNumber)).doOnNext( + dto -> log.info("Found client with phone number {} as {} {}", phoneNumber, + dto.clientNumber(), dto.clientName())); } /** @@ -398,29 +314,14 @@ public Flux findByEntireAddress(AddressSearchDto address) { return Flux.error(new MissingRequiredParameterException("address")); } - return - locationRepository - .matchaddress( - address.address(), - address.postalCode(), - address.city(), - address.province(), - address.country() - ) - .flatMap(entity -> - searchClientByQuery( - where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is(entity.getClientNumber()), - ForestClientEntity.class - ) - ) - .map(forestClientMapper::toDto) - .distinct(ForestClientDto::clientNumber) - .sort(Comparator.comparing(ForestClientDto::clientNumber)) - .doOnNext( - dto -> log.info("Found client with address {} as [{}] {}", - address, - dto.clientNumber(), dto.clientName()) - ); + return locationRepository.matchaddress(address.address(), address.postalCode(), address.city(), + address.province(), address.country()).flatMap(entity -> searchClientByQuery( + where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is(entity.getClientNumber()), + ForestClientEntity.class)).map(forestClientMapper::toDto) + .distinct(ForestClientDto::clientNumber) + .sort(Comparator.comparing(ForestClientDto::clientNumber)).doOnNext( + dto -> log.info("Found client with address {} as [{}] {}", address, dto.clientNumber(), + dto.clientName())); } /** @@ -443,30 +344,16 @@ public Flux findByContact(ContactSearchDto contact) { } String name = Stream.of(contact.firstName(), contact.middleName(), contact.lastName()) - .filter(StringUtils::isNotBlank) - .collect(Collectors.joining(" ")); - - return contactRepository.matchByExpanded( - name, - contact.email(), - contact.phone(), - contact.phone2(), - contact.fax() - ) - .flatMap(entity -> - searchClientByQuery( - where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is(entity.getClientNumber()), - ForestClientEntity.class - ) - ) - .map(forestClientMapper::toDto) + .filter(StringUtils::isNotBlank).collect(Collectors.joining(" ")); + + return contactRepository.matchByExpanded(name, contact.email(), contact.phone(), + contact.phone2(), contact.fax()).flatMap(entity -> searchClientByQuery( + where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is(entity.getClientNumber()), + ForestClientEntity.class)).map(forestClientMapper::toDto) .distinct(ForestClientDto::clientNumber) - .sort(Comparator.comparing(ForestClientDto::clientNumber)) - .doOnNext( - dto -> log.info("Found client with contact {} as [{}] {}", - contact, - dto.clientNumber(), dto.clientName()) - ); + .sort(Comparator.comparing(ForestClientDto::clientNumber)).doOnNext( + dto -> log.info("Found client with contact {} as [{}] {}", contact, dto.clientNumber(), + dto.clientName())); } /** @@ -485,15 +372,11 @@ public Flux findByAcronym(String acronym) { Criteria queryCriteria = where("clientAcronym").is(acronym).ignoreCase(true); - return searchClientByQuery(queryCriteria, ForestClientEntity.class) - .map(forestClientMapper::toDto) - .distinct(ForestClientDto::clientNumber) - .sort(Comparator.comparing(ForestClientDto::clientNumber)) - .doOnNext( - dto -> log.info("Found client with acronym {} as {} {}", - acronym, - dto.clientNumber(), dto.clientName()) - ); + return searchClientByQuery(queryCriteria, ForestClientEntity.class).map( + forestClientMapper::toDto).distinct(ForestClientDto::clientNumber) + .sort(Comparator.comparing(ForestClientDto::clientNumber)).doOnNext( + dto -> log.info("Found client with acronym {} as {} {}", acronym, dto.clientNumber(), + dto.clientName())); } /** @@ -511,34 +394,16 @@ public Flux findByDoingBusinessAs(String doingBusinessAs, boole return Flux.error(new MissingRequiredParameterException("doingBusinessAs")); } - return - Mono - .just(isFuzzy) - .filter(fuzzy -> fuzzy) - .flatMapMany(fuzzy -> doingBusinessAsRepository.matchBy(doingBusinessAs)) - .switchIfEmpty( - searchClientByQuery( - where("doingBusinessAsName") - .is(doingBusinessAs) - .ignoreCase(true), - ClientDoingBusinessAsEntity.class - ) - ) - .flatMap(entity -> - searchClientByQuery( - where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is( - entity.getClientNumber()), - ForestClientEntity.class - ) - ) - .map(forestClientMapper::toDto) - .distinct(ForestClientDto::clientNumber) - .sort(Comparator.comparing(ForestClientDto::clientNumber)) - .doOnNext( - dto -> log.info("Found client with doing business as {} as [{}] {}", - doingBusinessAs, - dto.clientNumber(), dto.clientName()) - ); + return Mono.just(isFuzzy).filter(fuzzy -> fuzzy) + .flatMapMany(fuzzy -> doingBusinessAsRepository.matchBy(doingBusinessAs)).switchIfEmpty( + searchClientByQuery(where("doingBusinessAsName").is(doingBusinessAs).ignoreCase(true), + ClientDoingBusinessAsEntity.class)).flatMap(entity -> searchClientByQuery( + where(ApplicationConstants.CLIENT_NUMBER_LITERAL).is(entity.getClientNumber()), + ForestClientEntity.class)).map(forestClientMapper::toDto) + .distinct(ForestClientDto::clientNumber) + .sort(Comparator.comparing(ForestClientDto::clientNumber)).doOnNext( + dto -> log.info("Found client with doing business as {} as [{}] {}", doingBusinessAs, + dto.clientNumber(), dto.clientName())); } /** @@ -557,15 +422,11 @@ public Flux findByClientName(String clientName) { Criteria queryCriteria = where(CLIENT_NAME).is(clientName).ignoreCase(true); - return searchClientByQuery(queryCriteria, ForestClientEntity.class) - .map(forestClientMapper::toDto) - .distinct(ForestClientDto::clientNumber) - .sort(Comparator.comparing(ForestClientDto::clientNumber)) - .doOnNext( - dto -> log.info("Found client with name {} as {} {}", - clientName, - dto.clientNumber(), dto.clientName()) - ); + return searchClientByQuery(queryCriteria, ForestClientEntity.class).map( + forestClientMapper::toDto).distinct(ForestClientDto::clientNumber) + .sort(Comparator.comparing(ForestClientDto::clientNumber)).doOnNext( + dto -> log.info("Found client with name {} as {} {}", clientName, dto.clientNumber(), + dto.clientName())); } /** @@ -583,33 +444,21 @@ public Mono findByClientNumber(String clientNumber) { return Mono.error(new MissingRequiredParameterException("clientNumber")); } - return forestClientRepository.findDetailsByClientNumber(clientNumber) - .flatMap(dto -> - locationRepository - .findAllByClientNumber(clientNumber) - .map(locationMapper::toDto) - .collectList() - .map(dto::withAddresses) - .defaultIfEmpty(dto) - ) - .flatMap(dto -> - contactRepository - .findAllByClientNumber(clientNumber) - .map(contactMapper::toDto) + return forestClientRepository.findDetailsByClientNumber(clientNumber).flatMap( + dto -> locationRepository.findAllByClientNumber(clientNumber).map(locationMapper::toDto) + .collectList().map(dto::withAddresses).defaultIfEmpty(dto)).flatMap( + dto -> contactRepository.findAllByClientNumber(clientNumber).map(contactMapper::toDto) .collectList() - .map(dto::withContacts) - .defaultIfEmpty(dto) - ) - .switchIfEmpty( - Mono.error( - new NoValueFoundException("Client with number: " + clientNumber) - ) - ) - .doOnNext( - dto -> log.info("Found client with client number {}", - clientNumber, - dto.clientNumber()) - ); + // sometimes we will have duplicated contacts with different locationCode values. We need to remove duplicates and merge the contents of the locationCode field + .map(contacts -> new ArrayList<>(contacts.stream().collect( + Collectors.toMap(ForestClientContactDto::contactName, contact -> contact, + (contact1, contact2) -> contact1.withLocationCode( + Stream.concat(contact1.locationCode().stream(), + contact2.locationCode().stream()).distinct().toList()))).values())) + .map(dto::withContacts).defaultIfEmpty(dto)) + .switchIfEmpty(Mono.error(new NoValueFoundException("Client with number: " + clientNumber))) + .doOnNext(dto -> log.info("Found client with client number {}", clientNumber, + dto.clientNumber())); } /** @@ -620,26 +469,19 @@ public Mono findByClientNumber(String clientNumber) { * @param value the predictive search value * @param page the pagination information * @return a Flux containing pairs of PredictiveSearchResultDto objects and the total count of - * matching clients + * matching clients */ public Flux> complexSearch(String value, Pageable page) { // This condition is for predictive search, and we will stop here if no query param is provided if (StringUtils.isBlank(value)) { return Flux.error(new MissingRequiredParameterException("value")); } - return - forestClientRepository - .countByPredictiveSearch(value.toUpperCase()) - .flatMapMany(count -> - forestClientRepository - .findByPredictiveSearch(value.toUpperCase(), page.getPageSize(), - page.getOffset()) - .doOnNext( - dto -> log.info("Found complex search for value {} as {} {} with score {}", - value, dto.clientNumber(), dto.clientFullName(), dto.score()) - ) - .map(dto -> Pair.of(dto, count)) - ); + return forestClientRepository.countByPredictiveSearch(value.toUpperCase()).flatMapMany( + count -> forestClientRepository.findByPredictiveSearch(value.toUpperCase(), + page.getPageSize(), page.getOffset()).doOnNext( + dto -> log.info("Found complex search for value {} as {} {} with score {}", value, + dto.clientNumber(), dto.clientFullName(), dto.score())) + .map(dto -> Pair.of(dto, count))); } /** @@ -648,27 +490,15 @@ public Flux> complexSearch(String value, P * * @param page the pagination information * @return a Flux containing pairs of PredictiveSearchResultDto objects and the total count of - * matching clients + * matching clients */ public Flux> latestEntries(Pageable page) { - return - forestClientRepository - .countByEmptyFullSearch( - LocalDateTime - .now() - .minus(configuration.getData().getPredictiveCap()) - ) - .flatMapMany(count -> - forestClientRepository - .findByEmptyFullSearch( - page.getPageSize(), - page.getOffset(), - LocalDateTime.now().minus(configuration.getData().getPredictiveCap()) - ) - .doOnNext(dto -> log.info("Found complex empty search as {} {} with score {}", - dto.clientNumber(), dto.clientFullName(), dto.score())) - .map(dto -> Pair.of(dto, count)) - ); + return forestClientRepository.countByEmptyFullSearch( + LocalDateTime.now().minus(configuration.getData().getPredictiveCap())).flatMapMany( + count -> forestClientRepository.findByEmptyFullSearch(page.getPageSize(), page.getOffset(), + LocalDateTime.now().minus(configuration.getData().getPredictiveCap())).doOnNext( + dto -> log.info("Found complex empty search as {} {} with score {}", dto.clientNumber(), + dto.clientFullName(), dto.score())).map(dto -> Pair.of(dto, count))); } /** @@ -684,22 +514,15 @@ public Flux> latestEntries(Pageable page) * @param entityClass The class of the entity to be retrieved. * @return A Flux stream of the entityClass objects. */ - private Flux searchClientByQuery( - final Criteria queryCriteria, - final Class entityClass - ) { + private Flux searchClientByQuery(final Criteria queryCriteria, + final Class entityClass) { // Create a query based on the query criteria. Query searchQuery = Query.query(queryCriteria); log.info("Searching for clients with query {}", queryCriteria); - return template - .select( - searchQuery - .with(PageRequest.of(0, 1000)) - .sort(Sort.by(Sort.Order.asc(ApplicationConstants.CLIENT_NUMBER_LITERAL))), - entityClass - ) + return template.select(searchQuery.with(PageRequest.of(0, 1000)) + .sort(Sort.by(Sort.Order.asc(ApplicationConstants.CLIENT_NUMBER_LITERAL))), entityClass) .doOnNext(client -> log.info("Found client for query {}", queryCriteria)); } diff --git a/legacy/src/test/java/ca/bc/gov/app/controller/ClientContactControllerIntegrationTest.java b/legacy/src/test/java/ca/bc/gov/app/controller/ClientContactControllerIntegrationTest.java index 3d6b33dad0..b08a5ff777 100644 --- a/legacy/src/test/java/ca/bc/gov/app/controller/ClientContactControllerIntegrationTest.java +++ b/legacy/src/test/java/ca/bc/gov/app/controller/ClientContactControllerIntegrationTest.java @@ -3,6 +3,7 @@ import ca.bc.gov.app.dto.ForestClientContactDto; import ca.bc.gov.app.extensions.AbstractTestContainerIntegrationTest; +import java.util.List; import java.util.Map; import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; @@ -40,6 +41,7 @@ void shouldSaveLocation(String clientNumber) { new ForestClientContactDto( clientNumber, "00", + List.of("00"), "BL", "James Baxter", "2502502555", diff --git a/legacy/src/test/java/ca/bc/gov/app/mappers/ForestClientContactMapperTest.java b/legacy/src/test/java/ca/bc/gov/app/mappers/ForestClientContactMapperTest.java index fb22f446b7..8141b7fef7 100644 --- a/legacy/src/test/java/ca/bc/gov/app/mappers/ForestClientContactMapperTest.java +++ b/legacy/src/test/java/ca/bc/gov/app/mappers/ForestClientContactMapperTest.java @@ -6,6 +6,7 @@ import ca.bc.gov.app.dto.ForestClientContactDto; import ca.bc.gov.app.entity.ForestClientContactEntity; import java.time.LocalDateTime; +import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mapstruct.factory.Mappers; @@ -20,6 +21,7 @@ class ForestClientContactMapperTest { ForestClientContactDto dto = new ForestClientContactDto( "00000001", "00", + List.of("00"), "BL", "James Baxter", "2502502550",