diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/BusinessPartnerService.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/BusinessPartnerService.kt index b74896cf9..d12e1f84b 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/BusinessPartnerService.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/BusinessPartnerService.kt @@ -31,14 +31,12 @@ import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType import org.eclipse.tractusx.bpdm.gate.api.model.request.BusinessPartnerInputRequest import org.eclipse.tractusx.bpdm.gate.api.model.response.BusinessPartnerInputDto import org.eclipse.tractusx.bpdm.gate.api.model.response.BusinessPartnerOutputDto -import org.eclipse.tractusx.bpdm.gate.config.PoolConfigProperties import org.eclipse.tractusx.bpdm.gate.entity.ChangelogEntry import org.eclipse.tractusx.bpdm.gate.entity.generic.* import org.eclipse.tractusx.bpdm.gate.exception.BpdmMissingStageException import org.eclipse.tractusx.bpdm.gate.repository.ChangelogRepository import org.eclipse.tractusx.bpdm.gate.repository.SharingStateRepository import org.eclipse.tractusx.bpdm.gate.repository.generic.BusinessPartnerRepository -import org.eclipse.tractusx.bpdm.pool.api.client.PoolApiClient import org.eclipse.tractusx.orchestrator.api.client.OrchestrationApiClient import org.eclipse.tractusx.orchestrator.api.model.BusinessPartnerGenericDto import org.eclipse.tractusx.orchestrator.api.model.TaskCreateRequest @@ -56,11 +54,7 @@ class BusinessPartnerService( private val sharingStateService: SharingStateService, private val changelogRepository: ChangelogRepository, private val orchestrationApiClient: OrchestrationApiClient, - private val orchestratorMappings: OrchestratorMappings, - private val sharingStateRepository: SharingStateRepository, - private val poolClient: PoolApiClient, - private val syncRecordService: SyncRecordService, - private val poolConfigProperties: PoolConfigProperties + private val sharingStateRepository: SharingStateRepository ) { private val logger = KotlinLogging.logger { } @@ -103,15 +97,15 @@ class BusinessPartnerService( val externalIds = entityCandidates.map { it.externalId } assertInputStageExists(externalIds) - val validatedEntities = filterUpdateCandidates(entityCandidates, StageType.Output) + val changedBusinessPartners = filterUpdateCandidates(entityCandidates, StageType.Output) - val resolutionResults = resolveCandidatesForStage(validatedEntities, StageType.Output) + val resolutionResults = resolveCandidatesForStage(changedBusinessPartners, StageType.Output) saveChangelog(resolutionResults) - val partners = resolutionResults.map { it.businessPartner } + val changedPartners = resolutionResults.map { it.businessPartner } - val successRequests = partners.map { + val successRequests = entityCandidates.map { SharingStateService.SuccessRequest( SharingStateService.SharingStateIdentifierDto(it.externalId, BusinessPartnerType.GENERIC), it.bpnA!! @@ -119,7 +113,7 @@ class BusinessPartnerService( } sharingStateService.setSuccess(successRequests) - return businessPartnerRepository.saveAll(partners) + return businessPartnerRepository.saveAll(changedPartners) } private fun getBusinessPartners(pageRequest: PageRequest, externalIds: Collection?, stage: StageType): Page { diff --git a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/controller/SiteController.kt b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/controller/SiteController.kt index 72abf772d..b7dbd11e0 100644 --- a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/controller/SiteController.kt +++ b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/controller/SiteController.kt @@ -22,7 +22,9 @@ package org.eclipse.tractusx.bpdm.pool.controller import org.eclipse.tractusx.bpdm.common.dto.PageDto import org.eclipse.tractusx.bpdm.common.dto.PaginationRequest import org.eclipse.tractusx.bpdm.pool.api.PoolSiteApi -import org.eclipse.tractusx.bpdm.pool.api.model.request.* +import org.eclipse.tractusx.bpdm.pool.api.model.request.SiteBpnSearchRequest +import org.eclipse.tractusx.bpdm.pool.api.model.request.SitePartnerCreateRequest +import org.eclipse.tractusx.bpdm.pool.api.model.request.SitePartnerUpdateRequest import org.eclipse.tractusx.bpdm.pool.api.model.response.* import org.eclipse.tractusx.bpdm.pool.service.AddressService import org.eclipse.tractusx.bpdm.pool.service.BusinessPartnerBuildService @@ -65,7 +67,7 @@ class SiteController( override fun createSite( requests: Collection ): SitePartnerCreateResponseWrapper { - return businessPartnerBuildService.createSites(requests) + return businessPartnerBuildService.createSitesWithMainAddress(requests) } @PreAuthorize("hasAuthority(@poolSecurityConfigProperties.getChangePoolPartnerDataAsRole())") diff --git a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/BusinessPartnerBuildService.kt b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/BusinessPartnerBuildService.kt index 1ef80d5be..45ecf8acc 100644 --- a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/BusinessPartnerBuildService.kt +++ b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/BusinessPartnerBuildService.kt @@ -24,7 +24,9 @@ import org.eclipse.tractusx.bpdm.common.dto.* import org.eclipse.tractusx.bpdm.common.exception.BpdmNotFoundException import org.eclipse.tractusx.bpdm.common.util.replace import org.eclipse.tractusx.bpdm.pool.api.model.ChangelogType +import org.eclipse.tractusx.bpdm.pool.api.model.ConfidenceCriteriaDto import org.eclipse.tractusx.bpdm.pool.api.model.LogisticAddressDto +import org.eclipse.tractusx.bpdm.pool.api.model.SiteStateDto import org.eclipse.tractusx.bpdm.pool.api.model.request.* import org.eclipse.tractusx.bpdm.pool.api.model.response.* import org.eclipse.tractusx.bpdm.pool.dto.AddressMetadataDto @@ -52,7 +54,8 @@ class BusinessPartnerBuildService( private val changelogService: PartnerChangelogService, private val siteRepository: SiteRepository, private val logisticAddressRepository: LogisticAddressRepository, - private val requestValidationService: RequestValidationService + private val requestValidationService: RequestValidationService, + private val businessPartnerEquivalenceService: BusinessPartnerEquivalenceService ) { private val logger = KotlinLogging.logger { } @@ -64,7 +67,6 @@ class BusinessPartnerBuildService( fun createLegalEntities(requests: Collection): LegalEntityPartnerCreateResponseWrapper { logger.info { "Create ${requests.size} new legal entities" } - val errorsByRequest = requestValidationService.validateLegalEntitiesToCreateFromController(requests) val errors = errorsByRequest.flatMap { it.value } val validRequests = requests.filterNot { errorsByRequest.containsKey(it) } @@ -100,8 +102,36 @@ class BusinessPartnerBuildService( return LegalEntityPartnerCreateResponseWrapper(legalEntityResponse, errors) } + fun createSitesWithLegalAddressAsMain(requests: Collection): SitePartnerCreateResponseWrapper { + logger.info { "Create ${requests.size} new sites with legal address as site main address" } + + val legalEntities = legalEntityRepository.findDistinctByBpnIn(requests.map { it.bpnLParent }) + val legalEntitiesByBpn = legalEntities.associateBy { it.bpn } + + val bpnSs = bpnIssuingService.issueSiteBpns(requests.size) + + val createdSites = requests.zip(bpnSs).map { (siteRequest, bpnS) -> + val legalEntityParent = + legalEntitiesByBpn[siteRequest.bpnLParent] ?: throw BpdmValidationException("Parent ${siteRequest.bpnLParent} not found for site to create") + createSite(siteRequest, bpnS, legalEntityParent) + .apply { mainAddress = legalEntityParent.legalAddress } + .apply { mainAddress.site = this } + } + + changelogService.createChangelogEntries(createdSites.map { + ChangelogEntryCreateRequest(it.bpn, ChangelogType.CREATE, BusinessPartnerType.SITE) + }) + + siteRepository.saveAll(createdSites) + + val siteResponse = createdSites.mapIndexed { index, site -> site.toUpsertDto(index.toString()) } + + return SitePartnerCreateResponseWrapper(siteResponse, emptyList()) + + } + @Transactional - fun createSites(requests: Collection): SitePartnerCreateResponseWrapper { + fun createSitesWithMainAddress(requests: Collection): SitePartnerCreateResponseWrapper { logger.info { "Create ${requests.size} new sites" } val errorsByRequest = requestValidationService.validateSitesToCreateFromController(requests) @@ -188,23 +218,37 @@ class BusinessPartnerBuildService( val bpnsToFetch = validRequests.map { it.bpnl } val legalEntities = legalEntityRepository.findDistinctByBpnIn(bpnsToFetch) businessPartnerFetchService.fetchDependenciesWithLegalAddress(legalEntities) - - changelogService.createChangelogEntries(legalEntities.map { - ChangelogEntryCreateRequest(it.bpn, ChangelogType.UPDATE, BusinessPartnerType.LEGAL_ENTITY) - }) - changelogService.createChangelogEntries(legalEntities.map { - ChangelogEntryCreateRequest(it.legalAddress.bpn, ChangelogType.UPDATE, BusinessPartnerType.ADDRESS) - }) - val requestsByBpn = validRequests.associateBy { it.bpnl } - val updatedLegalEntities = legalEntities.map { legalEntity -> - val request = requestsByBpn[legalEntity.bpn]!! - updateLegalEntity(legalEntity, request.legalEntity, request.legalEntity.legalName, legalEntityMetadataMap) - updateLogisticAddress(legalEntity.legalAddress, request.legalAddress, addressMetadataMap) - legalEntityRepository.save(legalEntity) + + val legalEntityRequestPairs = legalEntities.map { legalEntity -> Pair(legalEntity, requestsByBpn[legalEntity.bpn]!!) } + legalEntityRequestPairs.forEach { (legalEntity, request) -> + if (!businessPartnerEquivalenceService.isEquivalent(request)) { + updateLegalEntity(legalEntity, request.legalEntity, request.legalEntity.legalName, legalEntityMetadataMap) + updateLogisticAddress(legalEntity.legalAddress, request.legalAddress, addressMetadataMap) + legalEntityRepository.save(legalEntity) + + changelogService.createChangelogEntries( + listOf( + ChangelogEntryCreateRequest( + legalEntity.bpn, + ChangelogType.UPDATE, + BusinessPartnerType.LEGAL_ENTITY + ) + ) + ) + changelogService.createChangelogEntries( + listOf( + ChangelogEntryCreateRequest( + legalEntity.legalAddress.bpn, + ChangelogType.UPDATE, + BusinessPartnerType.ADDRESS + ) + ) + ) + } } - val legalEntityResponses = updatedLegalEntities.map { it.toUpsertDto(it.bpn) } + val legalEntityResponses = legalEntityRequestPairs.map { (legalEntity, request) -> legalEntity.toUpsertDto(request.bpnl) } return LegalEntityPartnerUpdateResponseWrapper(legalEntityResponses, errors) } @@ -221,23 +265,29 @@ class BusinessPartnerBuildService( val bpnsToFetch = validRequests.map { it.bpns } val sites = siteRepository.findDistinctByBpnIn(bpnsToFetch) - - changelogService.createChangelogEntries(sites.map { - ChangelogEntryCreateRequest(it.bpn, ChangelogType.UPDATE, BusinessPartnerType.SITE) - }) - changelogService.createChangelogEntries(sites.map { - ChangelogEntryCreateRequest(it.mainAddress.bpn, ChangelogType.UPDATE, BusinessPartnerType.ADDRESS) - }) - val requestByBpnMap = validRequests.associateBy { it.bpns } - val updatedSites = sites.map { - val request = requestByBpnMap[it.bpn]!! - updateSite(it, request.site) - updateLogisticAddress(it.mainAddress, request.site.mainAddress, addressMetadataMap) - siteRepository.save(it) + + val siteRequestPairs = sites.map { site -> Pair(site, requestByBpnMap[site.bpn]!!) } + siteRequestPairs.forEach { (site, request) -> + if (!businessPartnerEquivalenceService.isEquivalent(request)) { + updateSite(site, request.site) + updateLogisticAddress(site.mainAddress, request.site.mainAddress, addressMetadataMap) + siteRepository.save(site) + + changelogService.createChangelogEntries(listOf(ChangelogEntryCreateRequest(site.bpn, ChangelogType.UPDATE, BusinessPartnerType.SITE))) + changelogService.createChangelogEntries( + listOf( + ChangelogEntryCreateRequest( + site.mainAddress.bpn, + ChangelogType.UPDATE, + BusinessPartnerType.ADDRESS + ) + ) + ) + } } - val siteResponses = updatedSites.map { it.toUpsertDto(it.bpn) } + val siteResponses = siteRequestPairs.map { (site, request) -> site.toUpsertDto(request.bpns) } return SitePartnerUpdateResponseWrapper(siteResponses, errors) } @@ -252,16 +302,17 @@ class BusinessPartnerBuildService( val addresses = logisticAddressRepository.findDistinctByBpnIn(validRequests.map { it.bpna }) val metadataMap = metadataService.getMetadata(validRequests.map { it.address }).toMapping() - changelogService.createChangelogEntries(addresses.map { - ChangelogEntryCreateRequest(it.bpn, ChangelogType.UPDATE, BusinessPartnerType.ADDRESS) - }) + val addressRequestPairs = addresses.sortedBy { it.bpn }.zip(requests.sortedBy { it.bpna }) + addressRequestPairs.forEach { (address, request) -> + if (!businessPartnerEquivalenceService.isEquivalent(request)) { + updateLogisticAddress(address, request.address, metadataMap) + logisticAddressRepository.save(address) - val requestsByBpn = validRequests.associateBy { it.bpna } - val updatedAddresses = addresses.map { address -> - updateLogisticAddress(address, requestsByBpn[address.bpn]!!.address, metadataMap) - logisticAddressRepository.save(address) + changelogService.createChangelogEntries(listOf(ChangelogEntryCreateRequest(address.bpn, ChangelogType.UPDATE, BusinessPartnerType.ADDRESS))) + } } - val addressResponses = updatedAddresses.map { it.toDto() } + + val addressResponses = addresses.map { it.toDto() } return AddressPartnerUpdateResponseWrapper(addressResponses, errors) } @@ -393,6 +444,13 @@ class BusinessPartnerBuildService( val regions: Map ) + data class SiteCreateRequestWithLegalAddressAsMain( + override val name: String, + override val states: Collection, + override val confidenceCriteria: ConfidenceCriteriaDto, + val bpnLParent: String + ) : IBaseSiteDto + companion object { fun createCurrentnessTimestamp(): Instant { @@ -526,6 +584,7 @@ class BusinessPartnerBuildService( throw BpdmValidationException(TaskStepBuildService.CleaningError.LEGAL_NAME_IS_NULL.message) } + legalEntity.currentness = createCurrentnessTimestamp() legalEntity.legalName = Name(value = legalName, shortName = legalEntityDto.legalShortName) @@ -606,6 +665,4 @@ class BusinessPartnerBuildService( ) } - - } \ No newline at end of file diff --git a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/BusinessPartnerEquivalenceService.kt b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/BusinessPartnerEquivalenceService.kt index 548e55770..002d9a7ab 100644 --- a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/BusinessPartnerEquivalenceService.kt +++ b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/BusinessPartnerEquivalenceService.kt @@ -19,192 +19,336 @@ package org.eclipse.tractusx.bpdm.pool.service -import org.eclipse.tractusx.bpdm.pool.entity.* +import com.neovisionaries.i18n.CountryCode +import org.eclipse.tractusx.bpdm.common.dto.* +import org.eclipse.tractusx.bpdm.common.model.BusinessStateType +import org.eclipse.tractusx.bpdm.common.model.ClassificationType +import org.eclipse.tractusx.bpdm.common.model.DeliveryServiceType +import org.eclipse.tractusx.bpdm.pool.api.model.ConfidenceCriteriaDto +import org.eclipse.tractusx.bpdm.pool.api.model.LogisticAddressDto +import org.eclipse.tractusx.bpdm.pool.api.model.request.AddressPartnerUpdateRequest +import org.eclipse.tractusx.bpdm.pool.api.model.request.LegalEntityPartnerUpdateRequest +import org.eclipse.tractusx.bpdm.pool.api.model.request.SitePartnerUpdateRequest +import org.eclipse.tractusx.bpdm.pool.entity.ConfidenceCriteria +import org.eclipse.tractusx.bpdm.pool.entity.LegalEntity +import org.eclipse.tractusx.bpdm.pool.entity.LogisticAddress +import org.eclipse.tractusx.bpdm.pool.entity.Site +import org.eclipse.tractusx.bpdm.pool.exception.BpdmValidationException +import org.eclipse.tractusx.bpdm.pool.repository.LegalEntityRepository +import org.eclipse.tractusx.bpdm.pool.repository.LogisticAddressRepository +import org.eclipse.tractusx.bpdm.pool.repository.SiteRepository import org.springframework.stereotype.Service +import java.time.LocalDateTime +import java.util.* @Service -class BusinessPartnerEquivalenceService { - fun isEquivalent(original: LegalEntity, updated: LegalEntity): Boolean = - original.bpn != updated.bpn || - isEquivalent(original.legalForm, updated.legalForm) || - isEquivalentLegalEntityIdentifier(original.identifiers, updated.identifiers) || - isEquivalentLegalEntityState(original.states, updated.states) || - isEquivalentLogisticAddress(original.addresses, updated.addresses) || - isEquivalent(original.sites, updated.sites) || - isEquivalentLegalEntityClassification(original.classifications, updated.classifications) || - isEquivalent(original.legalName, updated.legalName) || - isEquivalent(original.legalAddress, updated.legalAddress) - - - private fun isEquivalentLegalEntityIdentifier( - originalSet: MutableSet, - updatedSet: MutableSet - ): Boolean { - return isEquivalent(originalSet, updatedSet) { entity1, entity2 -> - isEquivalent(entity1, entity2) - } - } - - private fun isEquivalent(original: LegalEntityIdentifier, updated: LegalEntityIdentifier): Boolean = - original.value != updated.value || - isEquivalent(original.type, updated.type) || - original.issuingBody != updated.issuingBody +class BusinessPartnerEquivalenceService( + private val legalEntityRepository: LegalEntityRepository, + private val siteRepository: SiteRepository, + private val logisticAddressRepository: LogisticAddressRepository +) { - private fun isEquivalent(original: IdentifierType, updated: IdentifierType): Boolean = - original.technicalKey != updated.technicalKey || - original.name != updated.name || - original.businessPartnerType.name != updated.businessPartnerType.name + fun isEquivalent(updateDto: LegalEntityPartnerUpdateRequest): Boolean { + val entity = legalEntityRepository.findByBpn(updateDto.bpnl) + ?: throw BpdmValidationException(TaskStepBuildService.CleaningError.INVALID_LEGAL_ENTITY_BPN.message) - - private fun isEquivalentLegalEntityState( - originalSet: MutableSet, - updatedSet: MutableSet - ): Boolean { - return isEquivalent(originalSet, updatedSet) { entity1, entity2 -> - isEquivalent(entity1, entity2) - } + return toEquivalenceDto(updateDto) == toEquivalenceDto(entity) } - private fun isEquivalent(original: LegalEntityState, updated: LegalEntityState): Boolean = - original.validFrom != updated.validFrom || - original.validTo != updated.validTo || - original.type != updated.type + fun isEquivalent(updateDto: SitePartnerUpdateRequest): Boolean { + val entity = + siteRepository.findByBpn(updateDto.bpns) ?: throw BpdmValidationException(TaskStepBuildService.CleaningError.INVALID_LEGAL_ENTITY_BPN.message) - - private fun isEquivalentLegalEntityClassification( - originalSet: MutableSet, - updatedSet: MutableSet - ): Boolean { - return isEquivalent(originalSet, updatedSet) { entity1, entity2 -> - isEquivalent(entity1, entity2) - } + return toEquivalenceDto(updateDto) == toEquivalenceDto(entity) } + fun isEquivalent(updateDto: AddressPartnerUpdateRequest): Boolean { + val entity = logisticAddressRepository.findByBpn(updateDto.bpna) + ?: throw BpdmValidationException(TaskStepBuildService.CleaningError.INVALID_LEGAL_ENTITY_BPN.message) - private fun isEquivalent(original: LegalEntityClassification, updated: LegalEntityClassification): Boolean { - return ( - original.value != updated.value || - original.code != updated.code || - original.type != updated.type - ) + return toEquivalenceDto(updateDto) == toEquivalenceDto(entity) } - private fun isEquivalent(original: Name, updated: Name): Boolean = - original.value != updated.value || - original.shortName != updated.shortName - - private fun isEquivalent(original: LegalForm?, updated: LegalForm?): Boolean = - original?.name != updated?.name || - original?.technicalKey != updated?.technicalKey || - original?.abbreviation != updated?.abbreviation - - - private fun isEquivalentLogisticAddress( - originalSet: MutableSet, - updatedSet: MutableSet - ): Boolean { - return isEquivalent(originalSet, updatedSet) { entity1, entity2 -> - isEquivalent(entity1, entity2) + private fun toEquivalenceDto(legalEntity: LegalEntity) = + with(legalEntity) { + LegalEntityEquivalenceDto( + legalForm = legalForm?.technicalKey, + legalName = legalName.value, + legalShortName = legalName.shortName, + identifiers = identifiers.map { IdentifierEquivalenceDto(it.value, it.type.technicalKey) }.toSortedSet(compareBy { it.value }), + states = states.map { StateEquivalenceDto(it.validFrom, it.validTo, it.type) }.toSortedSet(compareBy { it.validFrom }), + classifications = classifications.map { ClassificationEquivalenceDto(it.code, it.value, it.type) }.toSortedSet(compareBy { it.value }), + confidenceCriteria = toEquivalenceDto(confidenceCriteria), + legalAddress = toEquivalenceDto(legalEntity.legalAddress) + ) } - } - - fun isEquivalent(original: LogisticAddress, updated: LogisticAddress): Boolean = - original.bpn != updated.bpn || - original.name != updated.name || - isEquivalent(original.physicalPostalAddress, updated.physicalPostalAddress) || - isEquivalent(original.alternativePostalAddress, updated.alternativePostalAddress) + private fun toEquivalenceDto(request: LegalEntityPartnerUpdateRequest) = + with(request.legalEntity) { + LegalEntityEquivalenceDto( + legalForm = legalForm, + legalName = legalName, + legalShortName = legalShortName, + identifiers = identifiers.map { IdentifierEquivalenceDto(it.value, it.type) }.toSortedSet(compareBy { it.value }), + states = states.map { StateEquivalenceDto(it.validFrom, it.validTo, it.type) }.toSortedSet(compareBy { it.validFrom }), + classifications = classifications.map { ClassificationEquivalenceDto(it.code, it.value, it.type) }.toSortedSet(compareBy { it.value }), + confidenceCriteria = toEquivalenceDto(confidenceCriteria), + legalAddress = toEquivalenceDto(request.legalAddress) + ) + } - fun isEquivalentLogisticAddressSite(original: LogisticAddress, updated: LogisticAddress): Boolean = - original.name != updated.name || - isEquivalent(original.physicalPostalAddress, updated.physicalPostalAddress) || - isEquivalent(original.alternativePostalAddress, updated.alternativePostalAddress) + private fun toEquivalenceDto(site: Site) = + with(site) { + SiteEquivalenceDto( + name = name, + states = states.map { StateEquivalenceDto(it.validFrom, it.validTo, it.type) }.toSortedSet(compareBy { it.validFrom }), + confidenceCriteria = toEquivalenceDto(confidenceCriteria), + mainAddress = toEquivalenceDto(mainAddress) + ) + } - private fun isEquivalent( - originalSet: MutableSet, - updatedSet: MutableSet - ): Boolean { - return isEquivalent(originalSet, updatedSet) { entity1, entity2 -> - isEquivalent(entity1, entity2) + private fun toEquivalenceDto(request: SitePartnerUpdateRequest) = + with(request.site) { + SiteEquivalenceDto( + name = name, + states = states.map { StateEquivalenceDto(it.validFrom, it.validTo, it.type) }.toSortedSet(compareBy { it.validFrom }), + confidenceCriteria = toEquivalenceDto(confidenceCriteria), + mainAddress = toEquivalenceDto(mainAddress) + ) } - } - fun isEquivalent(original: Site, updated: Site): Boolean = - original.bpn != updated.bpn || - original.name != updated.name || - isEquivalentLogisticAddressSite(original.mainAddress, updated.mainAddress) - - private fun isEquivalent( - original: AlternativePostalAddress?, - updated: AlternativePostalAddress? - ): Boolean = - isEquivalent(original?.geographicCoordinates, updated?.geographicCoordinates) || - original?.country != updated?.country || - isEquivalent(original?.administrativeAreaLevel1, updated?.administrativeAreaLevel1) || - original?.postCode != updated?.postCode || - original?.city != updated?.city || - original?.deliveryServiceType != updated?.deliveryServiceType || - original?.deliveryServiceNumber != updated?.deliveryServiceNumber || - original?.deliveryServiceQualifier != updated?.deliveryServiceQualifier - - - private fun isEquivalent(original: GeographicCoordinate?, updated: GeographicCoordinate?): Boolean = - original?.latitude != updated?.latitude || - original?.longitude != updated?.longitude || - original?.altitude != updated?.altitude - - private fun isEquivalent(original: Region?, updated: Region?): Boolean = - original?.countryCode != updated?.countryCode || - original?.regionCode != updated?.regionCode || - original?.regionName != updated?.regionName - - - private fun isEquivalent( - original: PhysicalPostalAddress, - updated: PhysicalPostalAddress - ): Boolean = - original.country.name != updated.country.name || - original.administrativeAreaLevel1 != updated.administrativeAreaLevel1 || - original.administrativeAreaLevel2 != updated.administrativeAreaLevel2 || - original.administrativeAreaLevel3 != updated.administrativeAreaLevel3 || - original.administrativeAreaLevel4 != updated.administrativeAreaLevel4 || - original.postCode != updated.postCode || - original.city != updated.city || - original.districtLevel1 != updated.districtLevel1 || - original.districtLevel2 != updated.districtLevel2 || - original.companyPostCode != updated.companyPostCode || - original.industrialZone != updated.industrialZone || - original.building != updated.building || - original.floor != updated.floor || - original.door != updated.door || - isEquivalent(original.geographicCoordinates, updated.geographicCoordinates) || - isEquivalent(original.street, updated.street) - - private fun isEquivalent(original: Street?, updated: Street?): Boolean = - original?.name != updated?.name || - original?.houseNumber != updated?.houseNumber || - original?.milestone != updated?.milestone || - original?.direction != updated?.direction - - private fun isEquivalent( - originalSet: MutableSet, - updatedSet: MutableSet, - comparator: (T, T) -> Boolean - ): Boolean { - if (originalSet.size != updatedSet.size) { - return true + private fun toEquivalenceDto(logisticAddress: LogisticAddress) = + LogisticAddressEquivalenceDto( + name = logisticAddress.name, + states = logisticAddress.states.map { StateEquivalenceDto(it.validFrom, it.validTo, it.type) }.toSortedSet(compareBy { it.validFrom }), + identifiers = logisticAddress.identifiers.map { IdentifierEquivalenceDto(it.value, it.type.technicalKey) }.toSortedSet(compareBy { it.value }), + physicalPostalAddress = with(logisticAddress.physicalPostalAddress) { + PhysicalAddressEquivalenceDto( + geographicCoordinates = with(geographicCoordinates) { this?.let { GeoCoordinateDto(longitude, latitude, altitude) } }, + country = country, + administrativeAreaLevel1 = administrativeAreaLevel1?.regionCode, + administrativeAreaLevel2 = administrativeAreaLevel2, + administrativeAreaLevel3 = administrativeAreaLevel3, + postalCode = postCode, + city = city, + district = districtLevel1, + companyPostalCode = companyPostCode, + industrialZone = industrialZone, + building = building, + floor = floor, + door = door, + street = with(street) { + this?.let { + StreetEquivalenceDto( + name, + houseNumber, + houseNumberSupplement, + milestone, + direction, + namePrefix, + additionalNamePrefix, + nameSuffix, + additionalNameSuffix + ) + } + } + ) + }, + alternativePostalAddress = with(logisticAddress.alternativePostalAddress) { + this?.let { + AlternativeEquivalenceDto( + geographicCoordinates = geographicCoordinates?.let { with(geographicCoordinates) { GeoCoordinateDto(longitude, latitude, altitude) } }, + country = country, + administrativeAreaLevel1 = administrativeAreaLevel1?.regionCode, + postalCode = postCode, + city = city, + deliveryServiceType = deliveryServiceType, + deliveryServiceQualifier = deliveryServiceQualifier, + deliveryServiceNumber = deliveryServiceNumber + ) + } + }, + confidenceCriteria = toEquivalenceDto(logisticAddress.confidenceCriteria) + ) + + private fun toEquivalenceDto(request: AddressPartnerUpdateRequest) = + toEquivalenceDto(request.address) + + + private fun toEquivalenceDto(logisticAddress: LogisticAddressDto) = + with(logisticAddress) { + LogisticAddressEquivalenceDto( + name = logisticAddress.name, + states = states.map { StateEquivalenceDto(it.validFrom, it.validTo, it.type) }.toSortedSet(compareBy { it.validFrom }), + identifiers = identifiers.map { IdentifierEquivalenceDto(it.value, it.type) }.toSortedSet(compareBy { it.value }), + physicalPostalAddress = with(physicalPostalAddress) { + PhysicalAddressEquivalenceDto( + geographicCoordinates = with(geographicCoordinates) { this?.let { GeoCoordinateDto(longitude, latitude, altitude) } }, + country = country, + administrativeAreaLevel1 = administrativeAreaLevel1, + administrativeAreaLevel2 = administrativeAreaLevel2, + administrativeAreaLevel3 = administrativeAreaLevel3, + postalCode = postalCode, + city = city, + district = district, + companyPostalCode = companyPostalCode, + industrialZone = industrialZone, + building = building, + floor = floor, + door = door, + street = with(street) { + this?.let { + StreetEquivalenceDto( + name, + houseNumber, + houseNumberSupplement, + milestone, + direction, + namePrefix, + additionalNamePrefix, + nameSuffix, + additionalNameSuffix + ) + } + } + ) + }, + alternativePostalAddress = with(logisticAddress.alternativePostalAddress) { + this?.let { + AlternativeEquivalenceDto( + geographicCoordinates = with(geographicCoordinates) { this?.let { GeoCoordinateDto(longitude, latitude, altitude) } }, + country = country, + administrativeAreaLevel1 = administrativeAreaLevel1, + postalCode = postalCode, + city = city, + deliveryServiceType = deliveryServiceType, + deliveryServiceQualifier = deliveryServiceQualifier, + deliveryServiceNumber = deliveryServiceNumber + ) + } + }, + confidenceCriteria = toEquivalenceDto(confidenceCriteria) + ) } - for (originalEntity in originalSet) { - val updatedEntity = updatedSet.find { comparator(it, originalEntity) } + private fun toEquivalenceDto(confidenceCriteriaDto: ConfidenceCriteriaDto) = + with(confidenceCriteriaDto) { + ConfidenceCriteriaEquivalenceDto( + sharedByOwner, + checkedByExternalDataSource, + numberOfBusinessPartners, + lastConfidenceCheckAt, + nextConfidenceCheckAt, + confidenceLevel + ) + } - if (updatedEntity == null || comparator(originalEntity, updatedEntity)) { - return true - } + private fun toEquivalenceDto(confidenceCriteria: ConfidenceCriteria) = + with(confidenceCriteria) { + ConfidenceCriteriaEquivalenceDto( + sharedByOwner, + checkedByExternalDataSource, + numberOfBusinessPartners, + lastConfidenceCheckAt, + nextConfidenceCheckAt, + confidenceLevel + ) } - return false + data class LegalEntityEquivalenceDto( + override val legalForm: String?, + val legalName: String?, + override val legalShortName: String?, + override val identifiers: SortedSet, + override val states: SortedSet, + override val classifications: SortedSet, + override val confidenceCriteria: ConfidenceCriteriaEquivalenceDto?, + val legalAddress: LogisticAddressEquivalenceDto? + ) : IBaseLegalEntityDto + + data class SiteEquivalenceDto( + override val name: String?, + override val states: Collection, + override val confidenceCriteria: ConfidenceCriteriaEquivalenceDto?, + val mainAddress: LogisticAddressEquivalenceDto + ) : IBaseSiteDto + + data class LogisticAddressEquivalenceDto( + val name: String?, + override val states: Collection, + override val identifiers: Collection, + override val physicalPostalAddress: PhysicalAddressEquivalenceDto?, + override val alternativePostalAddress: AlternativeEquivalenceDto?, + override val confidenceCriteria: ConfidenceCriteriaEquivalenceDto? + ) : IBaseLogisticAddressDto + + data class IdentifierEquivalenceDto( + override val value: String, + override val type: String, + ) : IAddressIdentifierDto, ILegalEntityIdentifierDto { + override val issuingBody: String? = null } + + data class StateEquivalenceDto( + override val validFrom: LocalDateTime?, + override val validTo: LocalDateTime?, + override val type: BusinessStateType, + ) : IAddressStateDto, ILegalEntityStateDto, ISiteStateDto + + data class ClassificationEquivalenceDto( + override val code: String?, + override val value: String?, + override val type: ClassificationType + ) : ILegalEntityClassificationDto + + data class PhysicalAddressEquivalenceDto( + override val geographicCoordinates: GeoCoordinateDto?, + override val country: CountryCode?, + override val administrativeAreaLevel1: String?, + override val administrativeAreaLevel2: String?, + override val administrativeAreaLevel3: String?, + override val postalCode: String?, + override val city: String?, + override val district: String?, + override val street: StreetEquivalenceDto?, + override val companyPostalCode: String?, + override val industrialZone: String?, + override val building: String?, + override val floor: String?, + override val door: String? + ) : IBasePhysicalPostalAddressDto + + data class AlternativeEquivalenceDto( + override val geographicCoordinates: GeoCoordinateDto?, + override val country: CountryCode?, + override val administrativeAreaLevel1: String?, + override val postalCode: String?, + override val city: String?, + override val deliveryServiceType: DeliveryServiceType?, + override val deliveryServiceQualifier: String?, + override val deliveryServiceNumber: String? + ) : IBaseAlternativePostalAddressDto + + data class StreetEquivalenceDto( + override val name: String?, + override val houseNumber: String?, + override val houseNumberSupplement: String?, + override val milestone: String?, + override val direction: String?, + override val namePrefix: String?, + override val additionalNamePrefix: String?, + override val nameSuffix: String?, + override val additionalNameSuffix: String? + ) : IStreetDetailedDto + + data class ConfidenceCriteriaEquivalenceDto( + override val sharedByOwner: Boolean?, + override val checkedByExternalDataSource: Boolean?, + override val numberOfBusinessPartners: Int?, + override val lastConfidenceCheckAt: LocalDateTime?, + override val nextConfidenceCheckAt: LocalDateTime?, + override val confidenceLevel: Int? + ) : IConfidenceCriteriaDto } \ No newline at end of file diff --git a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepBuildService.kt b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepBuildService.kt index d2bd8046e..ff559fc53 100644 --- a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepBuildService.kt +++ b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepBuildService.kt @@ -20,29 +20,37 @@ package org.eclipse.tractusx.bpdm.pool.service import jakarta.transaction.Transactional -import org.eclipse.tractusx.bpdm.common.dto.AddressType -import org.eclipse.tractusx.bpdm.common.dto.BusinessPartnerType -import org.eclipse.tractusx.bpdm.pool.api.model.ChangelogType -import org.eclipse.tractusx.bpdm.pool.dto.AddressMetadataDto -import org.eclipse.tractusx.bpdm.pool.dto.ChangelogEntryCreateRequest -import org.eclipse.tractusx.bpdm.pool.dto.LegalEntityMetadataDto -import org.eclipse.tractusx.bpdm.pool.entity.* +import org.eclipse.tractusx.bpdm.common.dto.GeoCoordinateDto +import org.eclipse.tractusx.bpdm.pool.api.model.AddressIdentifierDto +import org.eclipse.tractusx.bpdm.pool.api.model.AddressStateDto +import org.eclipse.tractusx.bpdm.pool.api.model.LegalEntityClassificationDto +import org.eclipse.tractusx.bpdm.pool.api.model.LegalEntityIdentifierDto +import org.eclipse.tractusx.bpdm.pool.api.model.LegalEntityStateDto +import org.eclipse.tractusx.bpdm.pool.api.model.SiteStateDto +import org.eclipse.tractusx.bpdm.pool.api.model.StreetDto +import org.eclipse.tractusx.bpdm.pool.api.model.request.* +import org.eclipse.tractusx.bpdm.pool.api.model.response.ErrorCode +import org.eclipse.tractusx.bpdm.pool.api.model.response.ErrorInfo import org.eclipse.tractusx.bpdm.pool.exception.BpdmValidationException -import org.eclipse.tractusx.bpdm.pool.repository.LegalEntityRepository -import org.eclipse.tractusx.bpdm.pool.repository.LogisticAddressRepository -import org.eclipse.tractusx.bpdm.pool.repository.SiteRepository import org.eclipse.tractusx.orchestrator.api.model.* +import org.eclipse.tractusx.orchestrator.api.model.AlternativePostalAddressDto +import org.eclipse.tractusx.orchestrator.api.model.ConfidenceCriteriaDto +import org.eclipse.tractusx.orchestrator.api.model.LegalEntityDto +import org.eclipse.tractusx.orchestrator.api.model.LogisticAddressDto +import org.eclipse.tractusx.orchestrator.api.model.PhysicalPostalAddressDto +import org.eclipse.tractusx.orchestrator.api.model.SiteDto import org.springframework.stereotype.Service +import org.eclipse.tractusx.bpdm.pool.api.model.AlternativePostalAddressDto as AlternativePostalAddressPoolDto +import org.eclipse.tractusx.bpdm.pool.api.model.ConfidenceCriteriaDto as ConfidenceCriteriaPoolDto +import org.eclipse.tractusx.bpdm.pool.api.model.LegalEntityDto as LegalEntityPoolDto +import org.eclipse.tractusx.bpdm.pool.api.model.LogisticAddressDto as LogisticAddressPoolDto +import org.eclipse.tractusx.bpdm.pool.api.model.PhysicalPostalAddressDto as PhysicalPostalAddressPoolDto +import org.eclipse.tractusx.bpdm.pool.api.model.SiteDto as SitePoolDto + @Service class TaskStepBuildService( - private val metadataService: MetadataService, - private val bpnIssuingService: BpnIssuingService, - private val changelogService: PartnerChangelogService, - private val legalEntityRepository: LegalEntityRepository, - private val logisticAddressRepository: LogisticAddressRepository, - private val siteRepository: SiteRepository, - private val businessPartnerEquivalenceService: BusinessPartnerEquivalenceService, + private val businessPartnerBuildService: BusinessPartnerBuildService ) { enum class CleaningError(val message: String) { @@ -50,342 +58,400 @@ class TaskStepBuildService( COUNTRY_CITY_IS_NULL("Country or city in physicalAddress is null"), LEGAL_ENTITY_IS_NULL("Legal entity or BpnL Reference is null"), LEGAL_ADDRESS_IS_NULL("Legal Address is null"), - LOGISTIC_ADDRESS_IS_NULL("Logistic Address or Physical Address is null"), PHYSICAL_ADDRESS_IS_NULL("Physical Address is null"), ALTERNATIVE_ADDRESS_DATA_IS_NULL("Country or city or deliveryServiceType or deliveryServiceNumber in alternativeAddress is null"), MAINE_ADDRESS_IS_NULL("Main address is null"), BPNS_IS_NULL("BpnS Reference is null"), BPNA_IS_NULL("BpnA Reference is null"), SITE_NAME_IS_NULL("Site name is null"), - INVALID_LOGISTIC_ADDRESS_BPN("Invalid Logistic Address BPN"), INVALID_LEGAL_ENTITY_BPN("Invalid legal entity BPN"), - INVALID_SITE_BPN("Invalid site BPN"), - CLASSIFICATION_TYPE_IS_NULL("Classification type is null") + CLASSIFICATION_TYPE_IS_NULL("Classification type is null"), + PHYSICAL_ADDRESS_COUNTRY_MISSING("Physical Address has no country"), + PHYSICAL_ADDRESS_CITY_MISSING("Physical Address has no city"), + ALTERNATIVE_ADDRESS_COUNTRY_MISSING("Alternative Address has no country"), + ALTERNATIVE_ADDRESS_CITY_MISSING("Alternative Address has no city"), + ALTERNATIVE_ADDRESS_DELIVERY_SERVICE_TYPE_MISSING("Alternative Address has no delivery service type"), + ALTERNATIVE_ADDRESS_DELIVERY_SERVICE_NUMBER_MISSING("Alternative Address has no delivery service number"), + ADDRESS_CONFIDENCE_CRITERIA_MISSING("Logistic address is missing confidence criteria"), + SITE_CONFIDENCE_CRITERIA_MISSING("Site is missing confidence criteria"), + SITE_NAME_MISSING("Site has no name"), + LEGAL_ENTITY_CONFIDENCE_CRITERIA_MISSING("Legal Entity has no confidence criteria"), + MAIN_ADDRESS_BPN_REFERENCE_MISSING("The BpnA Reference of the site main address is missing"), + LEGAL_ADDRESS_BPN_REFERENCE_MISSING("The BpnA Reference of the legal address is missing"), } @Transactional fun upsertBusinessPartner(taskEntry: TaskStepReservationEntryDto, taskEntryBpnMapping: TaskEntryBpnMapping): TaskStepResultEntryDto { - val businessPartnerDto = taskEntry.businessPartner - var siteResult: SiteDto? = null - var addressResult: LogisticAddressDto? = null - - val legalEntity = upsertLegalEntity(businessPartnerDto.legalEntity, taskEntryBpnMapping) - var genericBpnA = legalEntity.legalAddress.bpn - - var siteEntity: Site? = null - if (businessPartnerDto.site != null) { - siteEntity = upsertSite(businessPartnerDto.site, legalEntity, taskEntryBpnMapping, businessPartnerDto) - siteResult = businessPartnerDto.site!!.copy( - bpnSReference = BpnReferenceDto(referenceValue = siteEntity.bpn, referenceType = BpnReferenceType.Bpn), - mainAddress = businessPartnerDto.site!!.mainAddress!!.copy( - bpnAReference = BpnReferenceDto(referenceValue = siteEntity.mainAddress.bpn, referenceType = BpnReferenceType.Bpn) - ) + + val legalEntityResult = upsertLegalEntity(businessPartnerDto.legalEntity, taskEntryBpnMapping) + + val siteResult = if (legalEntityResult.errors.isEmpty() && businessPartnerDto.site != null) { + upsertSite( + businessPartnerDto.site, + legalEntityResult.legalEntityBpn!!, + businessPartnerDto.legalEntity?.legalAddress?.bpnAReference, + taskEntryBpnMapping ) - genericBpnA = siteEntity.mainAddress.bpn + } else { + null } - val addressEntity: LogisticAddress? - if (businessPartnerDto.address != null) { - addressEntity = upsertLogisticAddress(businessPartnerDto.address, legalEntity, siteEntity, taskEntryBpnMapping) - addressResult = businessPartnerDto.address!!.copy( - bpnAReference = BpnReferenceDto(referenceValue = addressEntity.bpn, referenceType = BpnReferenceType.Bpn) + + val addressResult = if (legalEntityResult.errors.isEmpty() && siteResult?.errors?.isEmpty() != false && businessPartnerDto.address != null) + upsertLogisticAddress(businessPartnerDto.address, legalEntityResult.legalEntityBpn!!, siteResult?.siteBpn, taskEntryBpnMapping) + else + null + + val taskErrors = legalEntityResult.errors + .plus(siteResult?.errors ?: emptyList()) + .plus(addressResult?.errors ?: emptyList()) + .map { TaskErrorDto(TaskErrorType.Unspecified, it.message) } + + val bpna = addressResult?.addressBpn ?: siteResult?.mainAddressBpn ?: legalEntityResult.legalAddressBpn + val genericWithBpn = with(businessPartnerDto.generic) { + copy( + legalEntity = this.legalEntity.copy(legalEntityBpn = legalEntityResult.legalEntityBpn), + site = this.site.copy(siteBpn = siteResult?.siteBpn), + address = this.address.copy(addressBpn = bpna) ) - genericBpnA = addressEntity.bpn + } + + val legalEntityWithBpn = with(businessPartnerDto.legalEntity) { + this?.let { + copy( + bpnLReference = toBpnReference(legalEntityResult.legalEntityBpn), + legalAddress = legalAddress!!.copy(bpnAReference = toBpnReference(legalEntityResult.legalAddressBpn)) + ) + } + } + + val siteWithBpn = with(businessPartnerDto.site) { + this?.let { + copy( + bpnSReference = toBpnReference(siteResult?.siteBpn), + mainAddress = mainAddress!!.copy(bpnAReference = toBpnReference(siteResult?.mainAddressBpn)) + ) + } + } + + val addressWithBpn = with(businessPartnerDto.address) { + this?.let { + copy(bpnAReference = toBpnReference(addressResult?.addressBpn)) + } } return TaskStepResultEntryDto( taskId = taskEntry.taskId, businessPartner = BusinessPartnerFullDto( - generic = with(businessPartnerDto.generic) { - copy( - legalEntity = this.legalEntity.copy(legalEntityBpn = legalEntity.bpn), - site = this.site.copy(siteBpn = siteEntity?.bpn), - address = this.address.copy(addressBpn = genericBpnA) - ) - }, - legalEntity = businessPartnerDto.legalEntity!!.copy( - bpnLReference = BpnReferenceDto(referenceValue = legalEntity.bpn, referenceType = BpnReferenceType.Bpn), - legalAddress = businessPartnerDto.legalEntity!!.legalAddress!!.copy( - bpnAReference = BpnReferenceDto(referenceValue = legalEntity.legalAddress.bpn, referenceType = BpnReferenceType.Bpn) - ) - ), - site = siteResult, - address = addressResult - ) + generic = genericWithBpn, + legalEntity = legalEntityWithBpn, + site = siteWithBpn, + address = addressWithBpn + ), + errors = taskErrors ) } - private fun upsertLogisticAddress( - addressDto: LogisticAddressDto?, - legalEntity: LegalEntity, - siteEntity: Site?, - taskEntryBpnMapping: TaskEntryBpnMapping - ): LogisticAddress { - val bpnAReference = addressDto?.bpnAReference ?: throw BpdmValidationException(CleaningError.BPNA_IS_NULL.message) - val bpn = taskEntryBpnMapping.getBpn(bpnAReference) - var hasChanges = false - val upsertAddress = if (bpn == null) { - val bpnA = bpnIssuingService.issueAddressBpns(1).single() - taskEntryBpnMapping.addMapping(bpnAReference, bpnA) - createLogisticAddressInternal(addressDto, bpnA) - } else { - val addressMetadataMap = metadataService.getMetadata(listOf(addressDto)).toMapping() - val updateAddress = logisticAddressRepository.findByBpn(bpn) - - if (updateAddress != null) { - val newAddress = createLogisticAddressInternal(addressDto, updateAddress.bpn) - updateLogisticAddress(newAddress, addressDto, addressMetadataMap) - hasChanges = businessPartnerEquivalenceService.isEquivalent(updateAddress, newAddress) - updateLogisticAddress(updateAddress, addressDto, addressMetadataMap) - } else { - throw BpdmValidationException(CleaningError.INVALID_LOGISTIC_ADDRESS_BPN.message) - } - updateAddress - } - if (siteEntity != null) { - upsertAddress.site = siteEntity + fun upsertLegalEntity( + legalEntityDto: LegalEntityDto?, taskEntryBpnMapping: TaskEntryBpnMapping + ): LegalEntityUpsertResponse { + val bpnLReference = legalEntityDto?.bpnLReference ?: throw BpdmValidationException(CleaningError.LEGAL_ENTITY_IS_NULL.message) + + val bpn = taskEntryBpnMapping.getBpn(bpnLReference) + val bpnWithError = if (bpn == null) { + createLegalEntity(legalEntityDto, taskEntryBpnMapping) } else { - upsertAddress.legalEntity = legalEntity + updateLegalEntity(bpn, legalEntityDto) } - if (hasChanges || bpn == null) { - logisticAddressRepository.save(upsertAddress) - } + return bpnWithError + } + private fun createLegalEntity( + legalEntityDto: LegalEntityDto, taskEntryBpnMapping: TaskEntryBpnMapping + ): LegalEntityUpsertResponse { + val legalAddress = legalEntityDto.legalAddress ?: throw BpdmValidationException(CleaningError.LEGAL_ADDRESS_IS_NULL.message) + val bpnAReference = + legalEntityDto.legalAddress?.bpnAReference ?: throw BpdmValidationException(CleaningError.LEGAL_ADDRESS_BPN_REFERENCE_MISSING.message) - val changelogType = - if (bpn == null) ChangelogType.CREATE else ChangelogType.UPDATE - if (hasChanges || bpn == null) { - changelogService.createChangelogEntries( - listOf( - ChangelogEntryCreateRequest(upsertAddress.bpn, changelogType, BusinessPartnerType.ADDRESS) - ) - ) - } + val createRequest = LegalEntityPartnerCreateRequest( + legalEntity = toPoolDto(legalEntityDto), + legalAddress = toPoolDto(legalAddress), + index = "" + ) + val result = businessPartnerBuildService.createLegalEntities(listOf(createRequest)) + val legalEntityResult = result.entities.firstOrNull() + val bpnL = legalEntityResult?.legalEntity?.bpnl + val legalAddressBpnA = legalEntityResult?.legalAddress?.bpna + bpnL?.run { taskEntryBpnMapping.addMapping(legalEntityDto.bpnLReference!!, bpnL) } + legalAddressBpnA?.run { taskEntryBpnMapping.addMapping(bpnAReference, legalAddressBpnA) } - return upsertAddress + return LegalEntityUpsertResponse(bpnL, legalAddressBpnA, result.errors) } - private fun createLogisticAddress(addressDto: LogisticAddressDto?, taskEntryBpnMapping: TaskEntryBpnMapping): LogisticAddress { - - val bpnAReference = addressDto?.bpnAReference - val bpnA = bpnIssuingService.issueAddressBpns(1).single() + private fun updateLegalEntity( + bpnL: String, + legalEntityDto: LegalEntityDto + ): LegalEntityUpsertResponse { + val legalAddress = legalEntityDto.legalAddress ?: throw BpdmValidationException(CleaningError.LEGAL_ADDRESS_IS_NULL.message) - if (bpnAReference != null && bpnAReference.referenceType == BpnReferenceType.BpnRequestIdentifier) { - taskEntryBpnMapping.addMapping(bpnAReference, bpnA) - } - val newAddress = createLogisticAddressInternal(addressDto, bpnA) - changelogService.createChangelogEntries( - listOf( - ChangelogEntryCreateRequest(newAddress.bpn, ChangelogType.CREATE, BusinessPartnerType.ADDRESS) - ) + val updateRequest = LegalEntityPartnerUpdateRequest( + bpnl = bpnL, + legalEntity = toPoolDto(legalEntityDto), + legalAddress = toPoolDto(legalAddress) ) - - return newAddress + val result = businessPartnerBuildService.updateLegalEntities(listOf(updateRequest)) + val legalEntityResult = result.entities.firstOrNull() + return LegalEntityUpsertResponse(legalEntityResult?.legalEntity?.bpnl, legalEntityResult?.legalAddress?.bpna, result.errors) } - private fun createLogisticAddressInternal( - dto: LogisticAddressDto?, - bpn: String - ): LogisticAddress { + private fun upsertSite( + siteDto: SiteDto?, + legalEntityBpn: String, + legalAddressReference: BpnReferenceDto?, + taskEntryBpnMapping: TaskEntryBpnMapping + ): SiteUpsertResponse { + val bpnSReference = siteDto?.bpnSReference ?: throw BpdmValidationException(CleaningError.BPNS_IS_NULL.message) + val bpn = taskEntryBpnMapping.getBpn(bpnSReference) - if (dto?.physicalPostalAddress == null) { - throw BpdmValidationException(CleaningError.LOGISTIC_ADDRESS_IS_NULL.message) + val upsertSite = if (bpn == null) { + createSite(siteDto, legalEntityBpn, legalAddressReference, taskEntryBpnMapping) + } else { + updateSite(bpn, siteDto) } - - val addressMetadataMap = metadataService.getMetadata(listOf(dto)).toMapping() - val address = LogisticAddress( - bpn = bpn, - legalEntity = null, - site = null, - physicalPostalAddress = BusinessPartnerBuildService.createPhysicalAddress(dto.physicalPostalAddress!!, addressMetadataMap.regions), - alternativePostalAddress = dto.alternativePostalAddress?.let { - BusinessPartnerBuildService.createAlternativeAddress( - it, - addressMetadataMap.regions - ) - }, - name = dto.name, - confidenceCriteria = BusinessPartnerBuildService.createConfidenceCriteria(dto.confidenceCriteria!!) - ) - updateAddressIdentifiersAndStates(address, dto, addressMetadataMap.idTypes) - - return address + return upsertSite } - private fun updateLogisticAddress(address: LogisticAddress, dto: LogisticAddressDto, metadataMap: BusinessPartnerBuildService.AddressMetadataMapping) { - - if (dto.physicalPostalAddress == null) { - - throw BpdmValidationException(CleaningError.PHYSICAL_ADDRESS_IS_NULL.message) + private fun createSite( + siteDto: SiteDto, + legalEntityBpn: String, + legalAddressReference: BpnReferenceDto?, + taskEntryBpnMapping: TaskEntryBpnMapping + ): SiteUpsertResponse { + val mainAddressReference = siteDto.mainAddress?.bpnAReference ?: throw BpdmValidationException(CleaningError.MAIN_ADDRESS_BPN_REFERENCE_MISSING.message) + + val result = if (mainAddressReference.referenceValue == legalAddressReference?.referenceValue) { + val createRequest = BusinessPartnerBuildService.SiteCreateRequestWithLegalAddressAsMain( + name = siteDto.name ?: throw BpdmValidationException(CleaningError.SITE_NAME_MISSING.message), + states = siteDto.states.map { SiteStateDto(it.validFrom, it.validTo, it.type) }, + confidenceCriteria = siteDto.confidenceCriteria?.let { toPoolDto(it) } + ?: throw BpdmValidationException(CleaningError.SITE_CONFIDENCE_CRITERIA_MISSING.message), + bpnLParent = legalEntityBpn + ) + businessPartnerBuildService.createSitesWithLegalAddressAsMain(listOf(createRequest)) + } else { + val createRequest = SitePartnerCreateRequest( + bpnlParent = legalEntityBpn, + site = toPoolDto(siteDto), + index = "" + ) + businessPartnerBuildService.createSitesWithMainAddress(listOf(createRequest)) } - address.name = dto.name - address.physicalPostalAddress = BusinessPartnerBuildService.createPhysicalAddress(dto.physicalPostalAddress!!, metadataMap.regions) - address.alternativePostalAddress = dto.alternativePostalAddress?.let { BusinessPartnerBuildService.createAlternativeAddress(it, metadataMap.regions) } - address.confidenceCriteria = BusinessPartnerBuildService.createConfidenceCriteria(dto.confidenceCriteria!!) + val siteResult = result.entities.firstOrNull() + val bpnS = siteResult?.site?.bpns + val mainAddressBpnA = siteResult?.mainAddress?.bpna - updateAddressIdentifiersAndStates(address, dto, metadataMap.idTypes) - } + bpnS?.run { taskEntryBpnMapping.addMapping(siteDto.bpnSReference!!, bpnS) } + mainAddressBpnA?.run { taskEntryBpnMapping.addMapping(mainAddressReference, mainAddressBpnA) } - private fun updateAddressIdentifiersAndStates( - address: LogisticAddress, - dto: LogisticAddressDto, - idTypes: Map - ) { - address.identifiers.apply { - clear() - addAll(dto.identifiers.map { - AddressIdentifier( - value = it.value, - type = idTypes[it.type]!!, - address = address - ) - }) - } - address.states.apply { - clear() - addAll(dto.states.map { - AddressState( - validFrom = it.validFrom, - validTo = it.validTo, - type = it.type, - address = address - ) - }) - } + return SiteUpsertResponse(bpnS, mainAddressBpnA, result.errors) } - fun upsertLegalEntity( - legalEntityDto: LegalEntityDto?, taskEntryBpnMapping: TaskEntryBpnMapping - ): LegalEntity { + private fun updateSite( + bpnS: String, + siteDto: SiteDto + ): SiteUpsertResponse { + val updateRequest = SitePartnerUpdateRequest( + bpns = bpnS, + site = toPoolDto(siteDto) + ) + val result = businessPartnerBuildService.updateSites(listOf(updateRequest)) + val siteResult = result.entities.firstOrNull() - val bpnLReference = legalEntityDto?.bpnLReference ?: throw BpdmValidationException(CleaningError.LEGAL_ENTITY_IS_NULL.message) - val legalAddress = legalEntityDto.legalAddress ?: throw BpdmValidationException(CleaningError.LEGAL_ADDRESS_IS_NULL.message) - val legalEntityMetadataMap = metadataService.getMetadata(listOf(legalEntityDto)).toMapping() + return SiteUpsertResponse(siteResult?.site?.bpns, siteResult?.mainAddress?.bpna, result.errors) + } - val bpn = taskEntryBpnMapping.getBpn(bpnLReference) - var hasChanges = false - val upsertLe = if (bpn == null) { - val bpnL = bpnIssuingService.issueLegalEntityBpns(1).single() - taskEntryBpnMapping.addMapping(bpnLReference, bpnL) - val createdLe = BusinessPartnerBuildService.createLegalEntity(legalEntityDto, bpnL, legalEntityDto.legalName, legalEntityMetadataMap) - val address = createLogisticAddress(legalAddress, taskEntryBpnMapping) - createdLe.legalAddress = address - address.legalEntity = createdLe - createdLe + private fun upsertLogisticAddress( + addressDto: LogisticAddressDto?, + legalEntityBpn: String, + siteBpn: String?, + taskEntryBpnMapping: TaskEntryBpnMapping + ): AddressUpsertResponse { + val bpnAReference = addressDto?.bpnAReference ?: throw BpdmValidationException(CleaningError.BPNA_IS_NULL.message) + val bpn = taskEntryBpnMapping.getBpn(bpnAReference) + + val upsertAddress = if (bpn == null) { + createLogisticAddress(addressDto, legalEntityBpn, siteBpn, taskEntryBpnMapping) } else { + updateLogisticAddress(bpn, addressDto) + } - val updateLe = legalEntityRepository.findByBpn(bpn) - if (updateLe != null) { - if (legalEntityDto.hasChanged == true) { + return upsertAddress + } - val createdLe = - BusinessPartnerBuildService.createLegalEntity(legalEntityDto, updateLe.bpn, legalEntityDto.legalName, legalEntityMetadataMap) - val address = createLogisticAddress(legalAddress, taskEntryBpnMapping) - createdLe.legalAddress = address - address.legalEntity = createdLe - hasChanges = businessPartnerEquivalenceService.isEquivalent(updateLe, createdLe) + private fun createLogisticAddress( + addressDto: LogisticAddressDto, + legalEntityBpn: String, + siteBpn: String?, + taskEntryBpnMapping: TaskEntryBpnMapping + ): AddressUpsertResponse { + val addressCreateRequest = AddressPartnerCreateRequest( + bpnParent = siteBpn ?: legalEntityBpn, + index = "", + address = toPoolDto(addressDto) + ) + val result = businessPartnerBuildService.createAddresses(listOf(addressCreateRequest)) + val bpna = result.entities.firstOrNull()?.address?.bpna + bpna?.run { taskEntryBpnMapping.addMapping(addressDto.bpnAReference!!, bpna) } - BusinessPartnerBuildService.updateLegalEntity(updateLe, legalEntityDto, legalEntityDto.legalName, legalEntityMetadataMap) - val addressMetadataMap = metadataService.getMetadata(listOf(legalAddress)).toMapping() - updateLogisticAddress(updateLe.legalAddress, legalAddress, addressMetadataMap) - } - } else { - throw BpdmValidationException(CleaningError.INVALID_LEGAL_ENTITY_BPN.message) - } - updateLe - } - val changelogType = - if (bpn == null) ChangelogType.CREATE else ChangelogType.UPDATE - - if (hasChanges || bpn == null) { - legalEntityRepository.save(upsertLe) - changelogService.createChangelogEntries( - listOf( - ChangelogEntryCreateRequest(upsertLe.bpn, changelogType, BusinessPartnerType.LEGAL_ENTITY) - ) - ) - } - return upsertLe + return AddressUpsertResponse(bpna, result.errors) } - private fun upsertSite( - siteDto: SiteDto?, - legalEntity: LegalEntity, - taskEntryBpnMapping: TaskEntryBpnMapping, - genericBusinessPartner: BusinessPartnerFullDto - ): Site { + private fun updateLogisticAddress( + bpnA: String, + addressDto: LogisticAddressDto + ): AddressUpsertResponse { + val addressUpdateRequest = AddressPartnerUpdateRequest( + bpna = bpnA, + address = toPoolDto(addressDto) + ) + val result = businessPartnerBuildService.updateAddresses(listOf(addressUpdateRequest)) - val bpnSReference = siteDto?.bpnSReference ?: throw BpdmValidationException(CleaningError.BPNS_IS_NULL.message) - val mainAddress = siteDto.mainAddress ?: throw BpdmValidationException(CleaningError.MAINE_ADDRESS_IS_NULL.message) + return AddressUpsertResponse(result.entities.firstOrNull()?.bpna, result.errors) + } - val bpn = taskEntryBpnMapping.getBpn(bpnSReference) - val changelogType = if (bpn == null) ChangelogType.CREATE else ChangelogType.UPDATE - var hasChanges = false - val upsertSite = if (bpn == null) { - val bpnS = bpnIssuingService.issueSiteBpns(1).single() - val createSite = BusinessPartnerBuildService.createSite(siteDto, bpnS, legalEntity) - taskEntryBpnMapping.addMapping(bpnSReference, bpnS) + private fun toPoolDto(legalEntity: LegalEntityDto) = + with(legalEntity) { + LegalEntityPoolDto( + legalName = legalName ?: throw BpdmValidationException(CleaningError.LEGAL_NAME_IS_NULL.message), + legalShortName = legalShortName, + legalForm = legalForm, + identifiers = identifiers.map { LegalEntityIdentifierDto(it.value, it.type, it.issuingBody) }, + states = states.map { LegalEntityStateDto(it.validFrom, it.validTo, it.type) }, + classifications = classifications.map { LegalEntityClassificationDto(it.type, it.code, it.value) }, + confidenceCriteria = confidenceCriteria?.let { toPoolDto(it) } + ?: throw BpdmValidationException(CleaningError.LEGAL_ENTITY_CONFIDENCE_CRITERIA_MISSING.message), + ) + } - val siteMainAddress = - if (genericBusinessPartner.generic.address.addressType == AddressType.LegalAndSiteMainAddress) - legalEntity.legalAddress - else - createLogisticAddress(mainAddress, taskEntryBpnMapping) + private fun toPoolDto(site: SiteDto) = + with(site) { + SitePoolDto( + name = name ?: throw BpdmValidationException(CleaningError.SITE_NAME_MISSING.message), + states = states.map { SiteStateDto(it.validFrom, it.validTo, it.type) }, + mainAddress = mainAddress?.let { toPoolDto(it) } ?: throw BpdmValidationException(CleaningError.MAINE_ADDRESS_IS_NULL.message), + confidenceCriteria = confidenceCriteria?.let { toPoolDto(it) } + ?: throw BpdmValidationException(CleaningError.SITE_CONFIDENCE_CRITERIA_MISSING.message) + ) + } - createSite.mainAddress = siteMainAddress - siteMainAddress.site = createSite - createSite - } else { + private fun toPoolDto(logisticAddress: LogisticAddressDto) = + with(logisticAddress) { + LogisticAddressPoolDto( + name = name, + states = states.map { AddressStateDto(it.validFrom, it.validTo, it.type) }, + identifiers = identifiers.map { AddressIdentifierDto(it.value, it.type) }, + physicalPostalAddress = physicalPostalAddress?.let { toPoolDto(it) } + ?: throw BpdmValidationException(CleaningError.PHYSICAL_ADDRESS_IS_NULL.message), + alternativePostalAddress = alternativePostalAddress?.let { toPoolDto(it) }, + confidenceCriteria = with(confidenceCriteria) { + this?.let { + ConfidenceCriteriaPoolDto( + sharedByOwner, + checkedByExternalDataSource, + numberOfBusinessPartners, + lastConfidenceCheckAt, + nextConfidenceCheckAt, + confidenceLevel + ) + } + } ?: throw BpdmValidationException(CleaningError.ADDRESS_CONFIDENCE_CRITERIA_MISSING.message) + ) + } - val updateSite = siteRepository.findByBpn(bpn) - if (updateSite != null) { - if (siteDto.hasChanged == true) { - val createSite = BusinessPartnerBuildService.createSite(siteDto, updateSite.bpn, legalEntity) - val siteMainAddress = - if (genericBusinessPartner.generic.address.addressType == AddressType.LegalAndSiteMainAddress) - legalEntity.legalAddress - else - createLogisticAddress(mainAddress, taskEntryBpnMapping) - createSite.mainAddress = siteMainAddress - siteMainAddress.site = createSite - hasChanges = businessPartnerEquivalenceService.isEquivalent(updateSite, createSite) - - BusinessPartnerBuildService.updateSite(updateSite, siteDto) - val addressMetadataMap = metadataService.getMetadata(listOf(mainAddress)).toMapping() - updateLogisticAddress(updateSite.mainAddress, mainAddress, addressMetadataMap) + private fun toPoolDto(physicalPostalAddressDto: PhysicalPostalAddressDto) = + with(physicalPostalAddressDto) { + PhysicalPostalAddressPoolDto( + geographicCoordinates = with(geographicCoordinates) { this?.let { GeoCoordinateDto(longitude, latitude, altitude) } }, + country = country ?: throw BpdmValidationException(CleaningError.PHYSICAL_ADDRESS_COUNTRY_MISSING.message), + administrativeAreaLevel1 = administrativeAreaLevel1, + administrativeAreaLevel2 = administrativeAreaLevel2, + administrativeAreaLevel3 = administrativeAreaLevel3, + postalCode = postalCode, + city = city ?: throw BpdmValidationException(CleaningError.PHYSICAL_ADDRESS_CITY_MISSING.message), + district = district, + companyPostalCode = companyPostalCode, + industrialZone = industrialZone, + building = building, + floor = floor, + door = door, + street = with(street) { + this?.let { + StreetDto( + name, + houseNumber, + houseNumberSupplement, + milestone, + direction, + namePrefix, + additionalNamePrefix, + nameSuffix, + additionalNameSuffix + ) + } } - } else { - throw BpdmValidationException(CleaningError.INVALID_SITE_BPN.message) - } - updateSite + ) } - if (hasChanges || bpn == null) { - siteRepository.save(upsertSite) - changelogService.createChangelogEntries( - listOf( - ChangelogEntryCreateRequest(upsertSite.bpn, changelogType, BusinessPartnerType.SITE) - ) + private fun toPoolDto(alternativeAddress: AlternativePostalAddressDto) = + with(alternativeAddress) { + AlternativePostalAddressPoolDto( + geographicCoordinates = with(geographicCoordinates) { this?.let { GeoCoordinateDto(longitude, latitude, altitude) } }, + country = country ?: throw BpdmValidationException(CleaningError.ALTERNATIVE_ADDRESS_COUNTRY_MISSING.message), + administrativeAreaLevel1 = administrativeAreaLevel1, + postalCode = postalCode, + city = city ?: throw BpdmValidationException(CleaningError.ALTERNATIVE_ADDRESS_CITY_MISSING.message), + deliveryServiceType = deliveryServiceType + ?: throw BpdmValidationException(CleaningError.ALTERNATIVE_ADDRESS_DELIVERY_SERVICE_TYPE_MISSING.message), + deliveryServiceQualifier = deliveryServiceQualifier, + deliveryServiceNumber = deliveryServiceNumber + ?: throw BpdmValidationException(CleaningError.ALTERNATIVE_ADDRESS_DELIVERY_SERVICE_NUMBER_MISSING.message), ) } - return upsertSite - } - - private fun AddressMetadataDto.toMapping() = - BusinessPartnerBuildService.AddressMetadataMapping( - idTypes = idTypes.associateBy { it.technicalKey }, - regions = regions.associateBy { it.regionCode } - ) + private fun toPoolDto(confidenceCriteria: ConfidenceCriteriaDto) = + with(confidenceCriteria) { + ConfidenceCriteriaPoolDto( + sharedByOwner, + checkedByExternalDataSource, + numberOfBusinessPartners, + lastConfidenceCheckAt, + nextConfidenceCheckAt, + confidenceLevel + ) + } - private fun LegalEntityMetadataDto.toMapping() = - BusinessPartnerBuildService.LegalEntityMetadataMapping( - idTypes = idTypes.associateBy { it.technicalKey }, - legalForms = legalForms.associateBy { it.technicalKey } - ) + private fun toBpnReference(bpn: String?) = + bpn?.let { BpnReferenceDto(bpn, BpnReferenceType.Bpn) } + + data class LegalEntityUpsertResponse( + val legalEntityBpn: String?, + val legalAddressBpn: String?, + val errors: Collection> + ) + + data class SiteUpsertResponse( + val siteBpn: String?, + val mainAddressBpn: String?, + val errors: Collection> + ) + + data class AddressUpsertResponse( + val addressBpn: String?, + val errors: Collection> + ) } \ No newline at end of file diff --git a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepFetchAndReserveService.kt b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepFetchAndReserveService.kt index bf7194bb6..543dea4ac 100644 --- a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepFetchAndReserveService.kt +++ b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepFetchAndReserveService.kt @@ -88,6 +88,17 @@ class TaskStepFetchAndReserveService( ) ) ) + } catch (ex: Throwable) { + logger.error(ex) { "An unexpected error occurred during golden record task processing" } + TaskStepResultEntryDto( + taskId = taskStep.taskId, + errors = listOf( + TaskErrorDto( + type = TaskErrorType.Unspecified, + description = "An unexpected error occurred during Pool update" + ) + ) + ) } } diff --git a/bpdm-pool/src/test/kotlin/org/eclipse/tractusx/bpdm/pool/controller/ChangelogControllerIT.kt b/bpdm-pool/src/test/kotlin/org/eclipse/tractusx/bpdm/pool/controller/ChangelogControllerIT.kt index 692efd6bf..12719f73f 100644 --- a/bpdm-pool/src/test/kotlin/org/eclipse/tractusx/bpdm/pool/controller/ChangelogControllerIT.kt +++ b/bpdm-pool/src/test/kotlin/org/eclipse/tractusx/bpdm/pool/controller/ChangelogControllerIT.kt @@ -77,8 +77,8 @@ class ChangelogControllerIT @Autowired constructor( val bpnA2 = createdStructures[1].legalEntity.legalAddress.bpna val toUpdate = listOf( - BusinessPartnerNonVerboseValues.legalEntityUpdate1.copy(bpnl = bpnL1), - BusinessPartnerNonVerboseValues.legalEntityUpdate2.copy(bpnl = bpnL2) + with(BusinessPartnerNonVerboseValues.legalEntityUpdate1) { copy(bpnl = bpnL1, legalEntity = legalEntity.copy(legalName = "New Name 1")) }, + with(BusinessPartnerNonVerboseValues.legalEntityUpdate2) { copy(bpnl = bpnL2, legalEntity = legalEntity.copy(legalName = "New Name 2")) } ) poolClient.legalEntities.updateBusinessPartners( toUpdate @@ -144,7 +144,10 @@ class ChangelogControllerIT @Autowired constructor( bpns = bpnS1, site = BusinessPartnerNonVerboseValues.siteUpdate1.site.copy(name = name1) ), - BusinessPartnerNonVerboseValues.siteUpdate2.copy(bpns = bpnS2) + BusinessPartnerNonVerboseValues.siteUpdate2.copy( + bpns = bpnS2, site = + BusinessPartnerNonVerboseValues.siteUpdate2.site.copy(name = name1) + ) ) ) @@ -212,7 +215,10 @@ class ChangelogControllerIT @Autowired constructor( bpna = bpnA1, address = BusinessPartnerNonVerboseValues.addressPartnerUpdate1.address.copy(name = nameUpdate) ), - BusinessPartnerNonVerboseValues.addressPartnerUpdate2.copy(bpna = bpnA2) + BusinessPartnerNonVerboseValues.addressPartnerUpdate2.copy( + bpna = bpnA2, + address = BusinessPartnerNonVerboseValues.addressPartnerUpdate2.address.copy(name = nameUpdate) + ) ) )