diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java index 11b98b29..d7a05fdd 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java @@ -28,16 +28,15 @@ import org.eclipse.tractusx.puris.backend.common.api.domain.model.datatype.DT_RequestStateEnum; import org.eclipse.tractusx.puris.backend.common.api.domain.model.datatype.DT_UseCaseEnum; import org.eclipse.tractusx.puris.backend.common.api.logic.service.VariablesService; -import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Material; -import org.eclipse.tractusx.puris.backend.masterdata.domain.model.MaterialPartnerRelation; -import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner; +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.*; import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialPartnerRelationService; import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialService; import org.eclipse.tractusx.puris.backend.masterdata.logic.service.PartnerService; import org.eclipse.tractusx.puris.backend.stock.domain.model.*; import org.eclipse.tractusx.puris.backend.stock.domain.model.measurement.MeasurementUnit; +import org.eclipse.tractusx.puris.backend.stock.logic.adapter.ItemStockSammMapper; import org.eclipse.tractusx.puris.backend.stock.logic.adapter.ProductStockSammMapper; -import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.ItemUnitEnumeration; +import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.*; import org.eclipse.tractusx.puris.backend.stock.logic.dto.samm.LocationIdTypeEnum; import org.eclipse.tractusx.puris.backend.stock.logic.dto.samm.ProductStockSammDto; import org.eclipse.tractusx.puris.backend.stock.logic.service.*; @@ -45,6 +44,7 @@ import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; @@ -71,7 +71,7 @@ public class DataInjectionCommandLineRunner implements CommandLineRunner { @Autowired private MaterialItemStockService materialItemStockService; @Autowired - private ReportedMaterialItemStockService reportedMaterialItemStockService; + private ItemStockSammMapper itemStockSammMapper; @Autowired private PartnerProductStockService partnerProductStockService; @@ -234,22 +234,83 @@ private void setupCustomerRole() throws JsonProcessingException { .build(); var createdMaterialItemStock = materialItemStockService.create(materialItemStock); log.info("Created MaterialItemStock: \n" + createdMaterialItemStock.toString()); - - var builder2 = ReportedMaterialItemStock.builder(); - var reportedMaterialItemStock = - builder2 - .material(semiconductorMaterial) + Site newSite = new Site(); + newSite.setBpns("BPNS4444444444DD"); + newSite.setName("Dummy Site"); + Address address = new Address("BPNA4444444444DD", "Feldweg 1", "54545 Neustadt", "Germany"); + newSite.getAddresses().add(address); + mySelf.getSites().add(newSite); + partnerService.update(mySelf); + + builder = MaterialItemStock.builder(); + var otherMaterialItemStock = + builder .partner(supplierPartner) + .material(semiconductorMaterial) .lastUpdatedOnDateTime(new Date()) - .locationBpns(supplierPartner.getSites().first().getBpns()) - .locationBpna(supplierPartner.getSites().first().getAddresses().first().getBpna()) + .locationBpns(newSite.getBpns()) + .locationBpna(address.getBpna()) .measurementUnit(ItemUnitEnumeration.UNIT_PIECE) - .quantity(50) + .quantity(12) .build(); + otherMaterialItemStock = materialItemStockService.create(otherMaterialItemStock); - var createdReportedMaterialItemStock = reportedMaterialItemStockService.create(reportedMaterialItemStock); - log.info("Created ReportedMaterialItemStock: \n" + createdReportedMaterialItemStock); - + var thirdMaterialItemStock = MaterialItemStock.builder() + .partner(supplierPartner) + .material(semiconductorMaterial) + .lastUpdatedOnDateTime(new Date()) + .locationBpna(address.getBpna()) + .locationBpns(newSite.getBpns()) + .measurementUnit(ItemUnitEnumeration.UNIT_PIECE) + .quantity(23) + .isBlocked(true) + .build(); + thirdMaterialItemStock = materialItemStockService.create(thirdMaterialItemStock); + + var samm = itemStockSammMapper.materialItemStocksToItemStockSamm(List.of(createdMaterialItemStock, otherMaterialItemStock, thirdMaterialItemStock)); + var node = objectMapper.readTree(objectMapper.writeValueAsString(samm)); + log.info("Created SAMM\n" + node.toPrettyString()); + + mySelf.getSites().remove(newSite); + partnerService.update(mySelf); + ItemStockSamm itemStockSAMM = new ItemStockSamm(); + itemStockSAMM.setMaterialNumberSupplier(semiconductorMatNbrSupplier); + itemStockSAMM.setMaterialNumberCustomer(semiconductorMatNbrCustomer); + itemStockSAMM.setDirection(DirectionCharacteristic.OUTBOUND); + var posList = new ArrayList(); + itemStockSAMM.setPositions(posList); + var pos1 = new Position(); + posList.add(pos1); + pos1.setLastUpdatedOnDateTime(new Date()); + ItemQuantityEntity itemQuantityEntity = new ItemQuantityEntity(31.0, ItemUnitEnumeration.UNIT_PIECE); + var allocatedStocks = new ArrayList(); + pos1.setAllocatedStocks(allocatedStocks); + var allocatedStock = new AllocatedStock(itemQuantityEntity, "BPNS123456789012", true, "BPNA123456789012"); + allocatedStocks.add(allocatedStock); + itemQuantityEntity = new ItemQuantityEntity(67.0, ItemUnitEnumeration.UNIT_KILOGRAM); + allocatedStock = new AllocatedStock(itemQuantityEntity, "BPNS123456789012", false, "BPNA123456789012"); + allocatedStocks.add(allocatedStock); + var pos2 = new Position(); + pos2.setLastUpdatedOnDateTime(new Date()); + var opr = new OrderPositionReference("123", "234", "345"); + pos2.setOrderPositionReference(opr); + allocatedStocks = new ArrayList<>(); + pos2.setAllocatedStocks(allocatedStocks); + itemQuantityEntity = new ItemQuantityEntity(43.1, ItemUnitEnumeration.UNIT_KILOGRAM); + allocatedStock = new AllocatedStock(itemQuantityEntity, "BPNS1234567890AB", true, "BPNS1234567890CD"); + allocatedStocks.add(allocatedStock); + itemQuantityEntity = new ItemQuantityEntity(83.7, ItemUnitEnumeration.UNIT_CUBIC_CENTIMETRE); + allocatedStock = new AllocatedStock(itemQuantityEntity, "BPNS1234567890EF", false, "BPNS1234567890GH"); + allocatedStocks.add(allocatedStock); + posList.add(pos2); + + log.info("Sample SAMM: " + objectMapper.readTree(objectMapper.writeValueAsString(itemStockSAMM))); + + log.info("To MaterialItemStockList: "); + var list = itemStockSammMapper.itemStockSammToReportedMaterialItemStock(itemStockSAMM, supplierPartner); + for (var s : list) { + log.info(s.toString()); + } } /** diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/domain/model/ItemStock.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/domain/model/ItemStock.java index e699fb6f..78549b9d 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/domain/model/ItemStock.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/domain/model/ItemStock.java @@ -60,7 +60,7 @@ public abstract class ItemStock { @ToString.Exclude @NotNull protected Material material; - + protected double quantity; @NotNull protected ItemUnitEnumeration measurementUnit; @@ -82,15 +82,27 @@ public abstract class ItemStock { protected String customerOrderPositionId; @ToString.Include - private String material_ownMaterialNumber(){ + private String material_ownMaterialNumber() { return material.getOwnMaterialNumber(); } @ToString.Include - private String partner_partnerBpnl(){ + private String partner_partnerBpnl() { return partner.getBpnl(); } + public String getNonNullSupplierOrderId() { + return supplierOrderId == null ? "" : supplierOrderId; + } + + public String getNonNullCustomerOrderId() { + return customerOrderId == null ? "" : customerOrderId; + } + + public String getNonNullCustomerOrderPositionId() { + return customerOrderPositionId == null ? "" : customerOrderPositionId; + } + } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/adapter/ItemStockSammMapper.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/adapter/ItemStockSammMapper.java index ad4828e6..e1ded015 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/adapter/ItemStockSammMapper.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/adapter/ItemStockSammMapper.java @@ -25,10 +25,7 @@ import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner; import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialPartnerRelationService; import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialService; -import org.eclipse.tractusx.puris.backend.stock.domain.model.MaterialItemStock; -import org.eclipse.tractusx.puris.backend.stock.domain.model.ProductItemStock; -import org.eclipse.tractusx.puris.backend.stock.domain.model.ReportedMaterialItemStock; -import org.eclipse.tractusx.puris.backend.stock.domain.model.ReportedProductItemStock; +import org.eclipse.tractusx.puris.backend.stock.domain.model.*; import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -36,6 +33,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.stream.Collectors; @Service @Slf4j @@ -46,66 +44,122 @@ public class ItemStockSammMapper { @Autowired private MaterialPartnerRelationService mprService; - public ItemStockSAMM toItemStockSAMM(MaterialItemStock materialItemStock) { - ItemStockSAMM samm = new ItemStockSAMM(); + public ItemStockSamm materialItemStocksToItemStockSamm(List materialItemStocks) { + return listToItemStockSamm(materialItemStocks, DirectionCharacteristic.INBOUND); + } + + public ItemStockSamm productItemStocksToItemStockSamm(List productItemStocks) { + return listToItemStockSamm(productItemStocks, DirectionCharacteristic.OUTBOUND); + } + + private ItemStockSamm listToItemStockSamm(List itemStocks, DirectionCharacteristic directionCharacteristic) { + if (itemStocks == null || itemStocks.isEmpty()) { + log.warn("Can't map empty list"); + return null; + } + Partner partner = itemStocks.get(0).getPartner(); + Material material = itemStocks.get(0).getMaterial(); + if (itemStocks.stream().anyMatch(stock -> !stock.getPartner().equals(partner))) { + log.warn("Can't map item stock list with different partners"); + return null; + } + + if (itemStocks.stream().anyMatch(stock -> !stock.getMaterial().equals(material))) { + log.warn("Can't map item stock list with different materials"); + return null; + } + var groupedByPositionAttributes = itemStocks + .stream() + .collect(Collectors.groupingBy( + itemStock -> new PositionsMappingHelper(itemStock.getLastUpdatedOnDateTime(), + itemStock.getNonNullCustomerOrderId(), itemStock.getNonNullSupplierOrderId(), + itemStock.getNonNullCustomerOrderPositionId()))); + ItemStockSamm samm = new ItemStockSamm(); + samm.setMaterialGlobalAssetId(material.getMaterialNumberCx()); + + String partnerMaterialNumber = mprService.find(material, partner).getPartnerMaterialNumber(); + String customerMatNbr = directionCharacteristic == + DirectionCharacteristic.INBOUND ? material.getOwnMaterialNumber() : partnerMaterialNumber; + String supplierMatNbr = directionCharacteristic == + DirectionCharacteristic.INBOUND ? partnerMaterialNumber : material.getOwnMaterialNumber(); + samm.setMaterialNumberCustomer(customerMatNbr); + samm.setMaterialNumberSupplier(supplierMatNbr); + samm.setDirection(directionCharacteristic); + var posList = new ArrayList(); + samm.setPositions(posList); + for (var mappingHelperListEntry : groupedByPositionAttributes.entrySet()) { + var key = mappingHelperListEntry.getKey(); + var stock = mappingHelperListEntry.getValue().get(0); + Position position = new Position(); + posList.add(position); + position.setLastUpdatedOnDateTime(key.date()); + if (!key.customerOrderId.isEmpty() || !key.supplierOrderId.isEmpty() || !key.customerOrderPositionId.isEmpty()) { + // get opr from stock as this is nullable and prevents mapping empty strings to the samm. + OrderPositionReference opr = new OrderPositionReference(stock.getSupplierOrderId(), stock.getCustomerOrderId(), + stock.getCustomerOrderPositionId()); + position.setOrderPositionReference(opr); + } + var allocatedStocksList = new ArrayList(); + position.setAllocatedStocks(allocatedStocksList); + for (var v : mappingHelperListEntry.getValue()) { + ItemQuantityEntity itemQuantityEntity = new ItemQuantityEntity(v.getQuantity(), v.getMeasurementUnit()); + AllocatedStock allocatedStock = new AllocatedStock(itemQuantityEntity, v.getLocationBpns(), v.isBlocked(), v.getLocationBpna()); + allocatedStocksList.add(allocatedStock); + } + } + return samm; + } + + private record PositionsMappingHelper(Date date, String customerOrderId, String supplierOrderId, + String customerOrderPositionId) { + } + + public ItemStockSamm toItemStockSamm(MaterialItemStock materialItemStock) { + ItemStockSamm samm = new ItemStockSamm(); samm.setDirection(DirectionCharacteristic.INBOUND); samm.setPositions(new ArrayList<>()); samm.setMaterialGlobalAssetId(materialItemStock.getMaterial().getMaterialNumberCx()); samm.setMaterialNumberCustomer(materialItemStock.getMaterial().getOwnMaterialNumber()); samm.setMaterialNumberSupplier(mprService.find(materialItemStock.getMaterial(), materialItemStock.getPartner()).getPartnerMaterialNumber()); - var posList = new ArrayList(); - samm.setPositions(posList); + return createPosition(materialItemStock, samm); + } + + private static ItemStockSamm createPosition(ItemStock itemStock, ItemStockSamm samm) { Position position = new Position(); - if (materialItemStock.getCustomerOrderId() != null || materialItemStock.getCustomerOrderPositionId() != null - || materialItemStock.getSupplierOrderId() != null) { - OrderPositionReference opr = new OrderPositionReference(materialItemStock.getSupplierOrderId(), - materialItemStock.getCustomerOrderId(), materialItemStock.getCustomerOrderPositionId()); + samm.setPositions(List.of(position)); + position.setLastUpdatedOnDateTime(itemStock.getLastUpdatedOnDateTime()); + if (itemStock.getCustomerOrderId() != null || itemStock.getCustomerOrderPositionId() != null + || itemStock.getSupplierOrderId() != null) { + OrderPositionReference opr = new OrderPositionReference(itemStock.getSupplierOrderId(), + itemStock.getCustomerOrderId(), itemStock.getCustomerOrderPositionId()); position.setOrderPositionReference(opr); } - ItemQuantityEntity itemQuantityEntity = new ItemQuantityEntity(materialItemStock.getQuantity(), - materialItemStock.getMeasurementUnit()); - AllocatedStock allocatedStock = new AllocatedStock(itemQuantityEntity, materialItemStock.getLocationBpns(), - materialItemStock.isBlocked(), materialItemStock.getLocationBpna()); - var allocatedStocksList = new ArrayList(); - allocatedStocksList.add(allocatedStock); - position.setAllocatedStocks(allocatedStocksList); + ItemQuantityEntity itemQuantityEntity = new ItemQuantityEntity(itemStock.getQuantity(), + itemStock.getMeasurementUnit()); + AllocatedStock allocatedStock = new AllocatedStock(itemQuantityEntity, itemStock.getLocationBpns(), + itemStock.isBlocked(), itemStock.getLocationBpna()); + position.setAllocatedStocks(List.of(allocatedStock)); return samm; } - public ItemStockSAMM toItemStockSamm(ProductItemStock productItemStock) { - ItemStockSAMM samm = new ItemStockSAMM(); + public ItemStockSamm toItemStockSamm(ProductItemStock productItemStock) { + ItemStockSamm samm = new ItemStockSamm(); samm.setDirection(DirectionCharacteristic.OUTBOUND); samm.setPositions(new ArrayList<>()); samm.setMaterialGlobalAssetId(productItemStock.getMaterial().getMaterialNumberCx()); samm.setMaterialNumberSupplier(productItemStock.getMaterial().getOwnMaterialNumber()); samm.setMaterialNumberCustomer(mprService.find(productItemStock.getMaterial(), productItemStock.getPartner()).getPartnerMaterialNumber()); - var posList = new ArrayList(); - samm.setPositions(posList); - Position position = new Position(); - if (productItemStock.getCustomerOrderId() != null || productItemStock.getCustomerOrderPositionId() != null - || productItemStock.getSupplierOrderId() != null) { - OrderPositionReference opr = new OrderPositionReference(productItemStock.getSupplierOrderId(), - productItemStock.getCustomerOrderId(), productItemStock.getCustomerOrderPositionId()); - position.setOrderPositionReference(opr); - } - ItemQuantityEntity itemQuantityEntity = new ItemQuantityEntity(productItemStock.getQuantity(), - productItemStock.getMeasurementUnit()); - AllocatedStock allocatedStock = new AllocatedStock(itemQuantityEntity, productItemStock.getLocationBpns(), - productItemStock.isBlocked(), productItemStock.getLocationBpna()); - var allocatedStocksList = new ArrayList(); - allocatedStocksList.add(allocatedStock); - position.setAllocatedStocks(allocatedStocksList); - return samm; + return createPosition(productItemStock, samm); } - public List sammToReportedProductItemStock(ItemStockSAMM samm, Partner partner) { + public List itemStockSammToReportedProductItemStock(ItemStockSamm samm, Partner partner) { String matNbrCustomer = samm.getMaterialNumberCustomer(); String matNbrSupplier = samm.getMaterialNumberSupplier(); // should be ownMaterialNumber String matNbrCatenaX = samm.getMaterialGlobalAssetId(); ArrayList outputList = new ArrayList<>(); - if(samm.getDirection() != DirectionCharacteristic.INBOUND) { + if (samm.getDirection() != DirectionCharacteristic.INBOUND) { log.warn("Direction should be INBOUND, aborting"); return outputList; } @@ -143,13 +197,16 @@ public List sammToReportedProductItemStock(ItemStockSA // Use MatNbrSupplier if (material == null && matNbrSupplier != null) { material = materialService.findByOwnMaterialNumber(matNbrSupplier); - if (matNbrCatenaX != null) { - log.warn("Unknown CatenaXNumber for Material " + material.getOwnMaterialNumber()); - } - var mpr = mprService.find(material, partner); - if (mpr != null) { - if (!mpr.getPartnerMaterialNumber().equals(matNbrCustomer)) { - log.warn("Unknown MaterialNumberCustomer " + matNbrCustomer + " for Material " + material.getOwnMaterialNumber()); + + if (material != null) { + if (matNbrCatenaX != null) { + log.warn("Unknown CatenaXNumber for Material " + material.getOwnMaterialNumber()); + } + var mpr = mprService.find(material, partner); + if (mpr != null) { + if (!mpr.getPartnerMaterialNumber().equals(matNbrCustomer)) { + log.warn("Unknown MaterialNumberCustomer " + matNbrCustomer + " for Material " + material.getOwnMaterialNumber()); + } } } } @@ -186,12 +243,12 @@ public List sammToReportedProductItemStock(ItemStockSA return outputList; } - public List sammToReportedMaterialItemStock(ItemStockSAMM samm, Partner partner) { + public List itemStockSammToReportedMaterialItemStock(ItemStockSamm samm, Partner partner) { String matNbrCustomer = samm.getMaterialNumberCustomer(); // should be ownMaterialNumber String matNbrSupplier = samm.getMaterialNumberSupplier(); String matNbrCatenaX = samm.getMaterialGlobalAssetId(); ArrayList outputList = new ArrayList<>(); - if(samm.getDirection() != DirectionCharacteristic.OUTBOUND) { + if (samm.getDirection() != DirectionCharacteristic.OUTBOUND) { log.warn("Direction should be OUTBOUND, aborting"); return outputList; } @@ -216,13 +273,15 @@ public List sammToReportedMaterialItemStock(ItemStock // Use MatNbrCustomer if (material == null && matNbrCustomer != null) { material = materialService.findByOwnMaterialNumber(matNbrCustomer); - if (matNbrCatenaX != null) { - log.warn("Unknown CatenaXNumber for Material " + material.getOwnMaterialNumber()); - } - var mpr = mprService.find(material, partner); - if (mpr != null) { - if (!mpr.getPartnerMaterialNumber().equals(matNbrSupplier)) { - log.warn("Unknown MaterialNumberSupplier " + matNbrSupplier + " for Material " + material.getOwnMaterialNumber()); + if (material != null) { + if (matNbrCatenaX != null) { + log.warn("Unknown CatenaXNumber for Material " + material.getOwnMaterialNumber()); + } + var mpr = mprService.find(material, partner); + if (mpr != null) { + if (!mpr.getPartnerMaterialNumber().equals(matNbrSupplier)) { + log.warn("Unknown MaterialNumberSupplier " + matNbrSupplier + " for Material " + material.getOwnMaterialNumber()); + } } } } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/dto/itemstocksamm/ItemStockSAMM.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/dto/itemstocksamm/ItemStockSamm.java similarity index 95% rename from backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/dto/itemstocksamm/ItemStockSAMM.java rename to backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/dto/itemstocksamm/ItemStockSamm.java index 92a6f1c8..5a49ce82 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/dto/itemstocksamm/ItemStockSAMM.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/dto/itemstocksamm/ItemStockSamm.java @@ -38,7 +38,7 @@ @Getter @Setter @NoArgsConstructor -public class ItemStockSAMM { +public class ItemStockSamm { @NotNull private List positions; @@ -55,7 +55,7 @@ public class ItemStockSAMM { private DirectionCharacteristic direction; @JsonCreator - public ItemStockSAMM(@JsonProperty(value = "positions") List positions, + public ItemStockSamm(@JsonProperty(value = "positions") List positions, @JsonProperty(value = "materialNumberCustomer") String materialNumberCustomer, @JsonProperty(value = "materialGlobalAssetId") String materialGlobalAssetId, @JsonProperty(value = "materialNumberSupplier") String materialNumberSupplier, @@ -76,7 +76,7 @@ public boolean equals(final Object o) { return false; } - final ItemStockSAMM that = (ItemStockSAMM) o; + final ItemStockSamm that = (ItemStockSamm) o; return Objects.equals(positions, that.positions) && Objects.equals(materialNumberCustomer, that.materialNumberCustomer) && Objects.equals(materialGlobalAssetId, that.materialGlobalAssetId) diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ProductItemStockService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ProductItemStockService.java index 1008b05c..f600ccd1 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ProductItemStockService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ProductItemStockService.java @@ -32,17 +32,14 @@ @Slf4j public class ProductItemStockService extends ItemStockService { - public ProductItemStockService(PartnerService partnerService, MaterialPartnerRelationService mprService, ProductItemStockRepository repository) { super(partnerService, mprService, repository); } - + @Override public boolean validate(ProductItemStock productItemStock) { return basicValidation(productItemStock) && validateLocalStock(productItemStock) && validateProductItemStock(productItemStock); } - - } diff --git a/backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/logic/adapter/ItemStockSammMapperTest.java b/backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/logic/adapter/ItemStockSammMapperTest.java new file mode 100644 index 00000000..3e54add3 --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/logic/adapter/ItemStockSammMapperTest.java @@ -0,0 +1,481 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.puris.backend.stock.logic.adapter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Material; +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.MaterialPartnerRelation; +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner; +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Site; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialPartnerRelationService; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialService; +import org.eclipse.tractusx.puris.backend.stock.domain.model.ItemStock; +import org.eclipse.tractusx.puris.backend.stock.domain.model.MaterialItemStock; +import org.eclipse.tractusx.puris.backend.stock.domain.model.ReportedProductItemStock; +import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.*; +import org.junit.jupiter.api.*; +import org.junit.platform.commons.logging.Logger; +import org.junit.platform.commons.logging.LoggerFactory; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.*; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class ItemStockSammMapperTest { + + private static final Logger LOG = LoggerFactory.getLogger(ItemStockSammMapperTest.class); + private static ItemStockSamm SAMM_FROM_CUSTOMER_PARTNER; + final static String CUSTOMER_MAT_NUMBER = "MNR-7307-AU340474.002"; + final static String SUPPLIER_MAT_NUMBER = "MNR-8101-ID146955.001"; + final static String CX_MAT_NUMBER = UUID.randomUUID().toString(); + final static String OWN_BPNL = "BPNL4444444444LL"; + final static String OWN_BPNS = "BPNS4444444444SS"; + final static String OWN_BPNA = "BPNA4444444444AA"; + final static String SUPPLIER_BPNL = "BPNL1111111111LE"; + final static String SUPPLIER_BPNS = "BPNS1111111111SI"; + final static String SUPPLIER_BPNA = "BPNA1111111111AD"; + + final static String OPR_REF_CUSTOMER_ORDER_ID = "C-Nbr-4711"; + final static String OPR_REF_SUPPLIER_ORDER_ID = "S-Nbr-4712"; + final static String OPR_REF_CUSTOMER_POS_ID = "C-Nbr-4711-Pos-1"; + final static String SUPPLIER_BPNS2 = "BPNS2222222222SI"; + final static String SUPPLIER_BPNA2 = "BPNA2222222222AD"; + + final Partner supplierPartner = new Partner( + "Scenario Supplier", + "http://supplier-control-plane:9184/api/v1/dsp", + SUPPLIER_BPNL, + SUPPLIER_BPNS, + "Konzernzentrale Dudelsdorf", + SUPPLIER_BPNA, + "Heinrich-Supplier-Straße 1", + "77785 Dudelsdorf", + "Germany" + ); + + final Partner customerPartner = new Partner( + "Scenario Customer", + "http://customer-control-plane:8184/api/v1/dsp", + "BPNL4444444444XX", + "BPNS4444444444XX", + "Hauptwerk Musterhausen", + "BPNA4444444444ZZ", + "Musterstraße 35b", + "77777 Musterhausen", + "Germany" + ); + + @Mock + private MaterialService materialService; + + @Mock + private MaterialPartnerRelationService mprService; + + @InjectMocks + private ItemStockSammMapper itemStockSammMapper; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + @Order(1) + void map_WhenSingleMaterialItemStock_ReturnsItemStockSamm() { + // Given + Material semiconductorMaterial = Material.builder() + .ownMaterialNumber(CUSTOMER_MAT_NUMBER) + .materialFlag(true) + .productFlag(false) + .name("Semiconductor") + .build(); + + MaterialPartnerRelation mpr = new MaterialPartnerRelation(); + mpr.setPartner(supplierPartner); + mpr.setMaterial(semiconductorMaterial); + mpr.setPartnerBuysMaterial(false); + mpr.setPartnerSuppliesMaterial(true); + mpr.setPartnerMaterialNumber(SUPPLIER_MAT_NUMBER); + + MaterialItemStock materialItemStock = MaterialItemStock.builder() + .partner(supplierPartner) + .material(semiconductorMaterial) + .lastUpdatedOnDateTime(new Date()) + .locationBpna(OWN_BPNA) + .locationBpns(OWN_BPNS) + .measurementUnit(ItemUnitEnumeration.UNIT_PIECE) + .quantity(20) + .isBlocked(true) + .build(); + + System.out.println(materialItemStock); + + // When + when(mprService.find(semiconductorMaterial, supplierPartner)).thenReturn(mpr); + + // Then we could have a message as follows: + // - MaterialItem Stock as above + // - MaterialItem Stock as above BUT with isBlocked = false and other Quanitty + // - MaterialItem Stock as above BUT with OrderPositionReference and other Quantity + // These should result in two positions + // - one WITHOUT orderPositionReference AND two allocatedStocks + // - one WITH orderPosition AND one allocatedStocks + ItemStockSamm materialItemStockSamm = itemStockSammMapper.toItemStockSamm(materialItemStock); + + // Then + assertNotNull(materialItemStockSamm); + + assertEquals(DirectionCharacteristic.INBOUND, materialItemStockSamm.getDirection()); + assertEquals(CUSTOMER_MAT_NUMBER, materialItemStockSamm.getMaterialNumberCustomer()); + assertEquals(SUPPLIER_MAT_NUMBER, materialItemStockSamm.getMaterialNumberSupplier()); + assertNull(materialItemStockSamm.getMaterialGlobalAssetId()); + + assertEquals(1, materialItemStockSamm.getPositions().size()); + + Position position = materialItemStockSamm.getPositions().stream().findFirst().get(); + assertNull(position.getOrderPositionReference()); + + assertEquals(1, position.getAllocatedStocks().size()); + + AllocatedStock allocatedStock = position.getAllocatedStocks().get(0); + assertEquals(20, allocatedStock.getQuantityOnAllocatedStock().getValue()); + assertEquals(ItemUnitEnumeration.UNIT_PIECE, allocatedStock.getQuantityOnAllocatedStock().getUnit()); + assertEquals(OWN_BPNS, allocatedStock.getStockLocationBPNS()); + assertEquals(OWN_BPNA, allocatedStock.getStockLocationBPNA()); + + assertEquals(materialItemStock.getLastUpdatedOnDateTime(), position.getLastUpdatedOnDateTime()); + } + + /** + * Tests following Scenario: Supplier asks the Customer for Stocks of a given material that the customer + * has in place and has been supplied by the Supplier earlier. The supplier receives the INBOUND ItemStockSAMM and + * maps it to the ReportedProductItemStock. + * + * In technical terms, this means the following: + *
  • Given: Create INBOUND ItemStock to represent the Stock level on Customer side
  • + *
  • When: Use mapper to create the ReportedProductItemSoc
  • + *
  • Then: Validate the Mappings
  • + * + * Note: The test brings fills the {@code SAMM_FROM_CUSTOMER_PARTNER}. + */ + @Test + @Order(2) + void map_WhenReportedSammToProductItemStock_ReturnsMultipleReportedProductItemStock() { + // If we want to map a Samm to a ProductItemStock entity, then this implies + // that we are the supplier, who has received a Samm from his customer partner. + // That customer partner therefore would have generated the Samm from his + // MaterialEntityStock entities. + // Since from the customer's point of view, the items he received from his supplier + // are INBOUND, then we have to set the DirectionCharacteristic accordingly. + + // Given + ItemStockSamm inboundProductStockSamm = new ItemStockSamm(); + + inboundProductStockSamm.setDirection(DirectionCharacteristic.INBOUND); + inboundProductStockSamm.setMaterialNumberCustomer(CUSTOMER_MAT_NUMBER); + inboundProductStockSamm.setMaterialNumberSupplier(SUPPLIER_MAT_NUMBER); + inboundProductStockSamm.setMaterialGlobalAssetId(CX_MAT_NUMBER); + + // first position + Position anonymousPosition = new Position(); + anonymousPosition.setLastUpdatedOnDateTime(new Date()); + + // with three allocatedStocks + ItemQuantityEntity tenPieces = new ItemQuantityEntity(); + tenPieces.setUnit(ItemUnitEnumeration.UNIT_PIECE); + tenPieces.setValue(10.0); + + AllocatedStock stockBlocked = new AllocatedStock( + tenPieces, + SUPPLIER_BPNS, + true, + SUPPLIER_BPNA + ); + + ItemQuantityEntity twentyKilo = new ItemQuantityEntity(); + twentyKilo.setUnit(ItemUnitEnumeration.UNIT_KILOGRAM); + twentyKilo.setValue(20.0); + + AllocatedStock stockNotBlocked = new AllocatedStock( + twentyKilo, + SUPPLIER_BPNS, + false, + SUPPLIER_BPNA + ); + + AllocatedStock stockOtherSite = new AllocatedStock( + twentyKilo, + SUPPLIER_BPNS2, + false, + SUPPLIER_BPNA2 + ); + + anonymousPosition.setAllocatedStocks(Arrays.asList(stockBlocked, stockNotBlocked, stockOtherSite)); + + // second position WITH OrderPositionReference + Position positionWithOrderPositionReference = new Position(); + anonymousPosition.setLastUpdatedOnDateTime(new Date()); + + // with two allocatedStocks + AllocatedStock oprStockBlocked = new AllocatedStock( + tenPieces, + SUPPLIER_BPNS, + true, + SUPPLIER_BPNA + ); + + AllocatedStock oprStockNotBlocked = new AllocatedStock( + twentyKilo, + SUPPLIER_BPNS, + false, + SUPPLIER_BPNA + ); + + positionWithOrderPositionReference.setAllocatedStocks(Arrays.asList(oprStockBlocked, oprStockNotBlocked)); + positionWithOrderPositionReference.setOrderPositionReference(new OrderPositionReference( + OPR_REF_SUPPLIER_ORDER_ID, + OPR_REF_CUSTOMER_ORDER_ID, + OPR_REF_CUSTOMER_POS_ID + )); + + inboundProductStockSamm.setPositions(Arrays.asList(anonymousPosition, positionWithOrderPositionReference)); + + Site site2 = new Site(SUPPLIER_BPNS2, "Site 2", SUPPLIER_BPNA2, "Test Street 2", "44174 Dortmund", "Germany"); + + SortedSet adjustedSet = customerPartner.getSites(); + adjustedSet.add(site2); + + customerPartner.setSites(adjustedSet); + + Material semiconductorProduct = Material.builder() + .ownMaterialNumber(CUSTOMER_MAT_NUMBER) + .materialNumberCx(CX_MAT_NUMBER) + .materialFlag(true) + .productFlag(true) + .name("Semiconductor") + .build(); + + MaterialPartnerRelation mpr = new MaterialPartnerRelation(); + mpr.setPartner(customerPartner); + mpr.setMaterial(semiconductorProduct); + mpr.setPartnerBuysMaterial(false); + mpr.setPartnerSuppliesMaterial(true); + mpr.setPartnerMaterialNumber(SUPPLIER_MAT_NUMBER); + + // When + // Find material based on CX number and mpr + when(materialService.findByMaterialNumberCx(CX_MAT_NUMBER)).thenReturn(semiconductorProduct); + when(mprService.find(semiconductorProduct, customerPartner)).thenReturn(mpr); + + // Then we should build 5 reported product stocks: + // - no OrderPositionReference (OPR), blocked, 10 pieces, BPNS & BPNA + // - no OPR, not blocked, 20 kilo, BPNS2 & BPNA2 + // - no OPR, not blocked, 20 kilo, BPNS & BPNA + // - OPR, blocked, 10 pieces, BPNS & BPNA + // - OPR, not blocked, 20 kilo, BPNS & BPNA + List reportedProductItemStocks = itemStockSammMapper.itemStockSammToReportedProductItemStock(inboundProductStockSamm, customerPartner); + + // Then + assertEquals(5, reportedProductItemStocks.size()); + + // 1. anonymous stocks + // Check for stockBlocked + List potentialBlockedAnonymousStock = filterReportedItemStock( + reportedProductItemStocks, + stockBlocked, + anonymousPosition, + CUSTOMER_MAT_NUMBER + ); + + assertEquals(1, potentialBlockedAnonymousStock.size()); + + // Test that the anonymous stock has null values (not empty strings) + ReportedProductItemStock reportedProductItemStock = (ReportedProductItemStock) potentialBlockedAnonymousStock.get(0); + assertNull(reportedProductItemStock.getCustomerOrderId()); + assertNull(reportedProductItemStock.getSupplierOrderId()); + assertNull(reportedProductItemStock.getCustomerOrderPositionId()); + + // Check for stockNOTBlocked + List potentialNotBlockedAnonymousStock = filterReportedItemStock( + reportedProductItemStocks, + stockNotBlocked, + anonymousPosition, + CUSTOMER_MAT_NUMBER + ); + + assertEquals(1, potentialNotBlockedAnonymousStock.size()); + + // Check for stockBlocked + List potentialAnonymousStockOtherSite = filterReportedItemStock( + reportedProductItemStocks, + stockOtherSite, + anonymousPosition, + CUSTOMER_MAT_NUMBER + ); + assertEquals(1, potentialAnonymousStockOtherSite.size()); + + // 2. opr related ones + + // Check for oprStockBlocked + List potentialOprStockBlocked = filterReportedItemStock( + reportedProductItemStocks, + oprStockBlocked, + positionWithOrderPositionReference, + CUSTOMER_MAT_NUMBER + ); + assertEquals(1, potentialOprStockBlocked.size()); + + // Test that the OPR is mapped correctly + ReportedProductItemStock reportedOprProductItemStock = (ReportedProductItemStock) potentialOprStockBlocked.get(0); + assertEquals(OPR_REF_CUSTOMER_ORDER_ID, reportedOprProductItemStock.getCustomerOrderId()); + assertEquals(OPR_REF_SUPPLIER_ORDER_ID, reportedOprProductItemStock.getSupplierOrderId()); + assertEquals(OPR_REF_CUSTOMER_POS_ID, reportedOprProductItemStock.getCustomerOrderPositionId()); + + // Check for oprStockNotBlocked + List potentialOprStockNotBlocked = filterReportedItemStock( + reportedProductItemStocks, + oprStockBlocked, + positionWithOrderPositionReference, + CUSTOMER_MAT_NUMBER + ); + assertEquals(1, potentialOprStockNotBlocked.size()); + + // ATTENTION: This variable is used in marshalling tests + // This has ben done to do the perspective Switch (supplier + SAMM_FROM_CUSTOMER_PARTNER = inboundProductStockSamm; + } + + /** + * Tests following Scenario: Test checks if the SAMM can be transferred into a ReportedProductItemStock + * + * In technical terms, this means the following: + *
  • Given: Use the {@code SAMM_FROM_CUSTOMER_PARTNER} created in previous test
  • + *
  • When: Use objectMapper switch representation from SAMM json to ItemStock class
  • + *
  • Then: Validate that mapping took place
  • + */ + @Test + @Order(3) + void test_unmarshalling() { + // Setup from the suppliers point of view + Material material = new Material(); + material.setProductFlag(true); + material.setOwnMaterialNumber(SUPPLIER_MAT_NUMBER); + material.setMaterialNumberCx(CX_MAT_NUMBER); + + MaterialPartnerRelation mpr = new MaterialPartnerRelation(); + mpr.setPartner(supplierPartner); + mpr.setMaterial(material); + mpr.setPartnerBuysMaterial(true); + mpr.setPartnerMaterialNumber(CUSTOMER_MAT_NUMBER); + + when(materialService.findByMaterialNumberCx(CX_MAT_NUMBER)).thenReturn(material); + when(mprService.findAllByPartnerMaterialNumber(CUSTOMER_MAT_NUMBER)).thenReturn(List.of(material)); + when(mprService.find(material, supplierPartner)).thenReturn(mpr); + + var list = itemStockSammMapper.itemStockSammToReportedProductItemStock(SAMM_FROM_CUSTOMER_PARTNER, supplierPartner); + assertNotNull(list); + assertEquals(5, list.size()); + } + + /** + * Tests following Scenario: Test checks if the SAMM can be transferred into a ReportedProductItemStock via the + * JSON serializationS + * + * In technical terms, this means the following: + *
  • Given: Use the {@code SAMM_FROM_CUSTOMER_PARTNER} created in previous test
  • + *
  • When: Use objectMapper switch representation from SAMM json to ItemStock class
  • + *
  • Then: Validate that mapping took place
  • + */ + @Test + @Order(4) + void test_deserializationFromJson() throws Exception { + // Setup from the suppliers point of view + + Material material = new Material(); + material.setProductFlag(true); + material.setOwnMaterialNumber(SUPPLIER_MAT_NUMBER); + material.setMaterialNumberCx(CX_MAT_NUMBER); + + MaterialPartnerRelation mpr = new MaterialPartnerRelation(); + mpr.setPartner(supplierPartner); + mpr.setMaterial(material); + mpr.setPartnerBuysMaterial(true); + mpr.setPartnerMaterialNumber(CUSTOMER_MAT_NUMBER); + + when(materialService.findByMaterialNumberCx(CX_MAT_NUMBER)).thenReturn(material); + when(mprService.findAllByPartnerMaterialNumber(CUSTOMER_MAT_NUMBER)).thenReturn(List.of(material)); + when(mprService.find(material, supplierPartner)).thenReturn(mpr); + + ObjectMapper objectMapper = new ObjectMapper(); + + String sammJsonString = objectMapper.writeValueAsString(SAMM_FROM_CUSTOMER_PARTNER); + LOG.info(() -> { + try { + return objectMapper.readTree(sammJsonString).toPrettyString(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + var itemStockSamm = objectMapper.readValue(sammJsonString, ItemStockSamm.class); + assertEquals(SAMM_FROM_CUSTOMER_PARTNER, itemStockSamm); + var list = itemStockSammMapper.itemStockSammToReportedProductItemStock(itemStockSamm, supplierPartner); + assertEquals(5, list.size()); + } + + private List filterReportedItemStock(List reportedProductItemStocks, AllocatedStock allocatedStock, Position position, String ownMaterialNumber) { + + if (position.getOrderPositionReference() == null) { + + List potentialStocks = reportedProductItemStocks.stream() + .filter(item -> item.isBlocked() == allocatedStock.getIsBlocked()) + .filter(item -> item.getMeasurementUnit().equals(allocatedStock.getQuantityOnAllocatedStock().getUnit())) + .filter(item -> item.getQuantity() == allocatedStock.getQuantityOnAllocatedStock().getValue()) + .filter(item -> item.getMaterial().getOwnMaterialNumber().equals(ownMaterialNumber)) + .filter(item -> item.getLocationBpna().equals(allocatedStock.getStockLocationBPNA())) + .filter(item -> item.getLocationBpns().equals(allocatedStock.getStockLocationBPNS())) + .filter(item -> item.getLastUpdatedOnDateTime() == position.getLastUpdatedOnDateTime()) + .filter(item -> item.getCustomerOrderId() == null) + .filter(item -> item.getSupplierOrderId() == null) + .filter(item -> item.getCustomerOrderPositionId() == null) + .collect(Collectors.toList()); + return potentialStocks; + } else { + List potentialStocks = reportedProductItemStocks.stream() + .filter(item -> item.isBlocked() == allocatedStock.getIsBlocked()) + .filter(item -> item.getMeasurementUnit().equals(allocatedStock.getQuantityOnAllocatedStock().getUnit())) + .filter(item -> item.getQuantity() == allocatedStock.getQuantityOnAllocatedStock().getValue()) + .filter(item -> item.getMaterial().getOwnMaterialNumber().equals(ownMaterialNumber)) + .filter(item -> item.getLocationBpna().equals(allocatedStock.getStockLocationBPNA())) + .filter(item -> item.getLocationBpns().equals(allocatedStock.getStockLocationBPNS())) + .filter(item -> item.getLastUpdatedOnDateTime() == position.getLastUpdatedOnDateTime()) + .filter(item -> item.getCustomerOrderId().equals(position.getOrderPositionReference().getCustomerOrderId())) + .filter(item -> item.getSupplierOrderId().equals(position.getOrderPositionReference().getSupplierOrderId())) + .filter(item -> item.getCustomerOrderPositionId().equals(position.getOrderPositionReference().getCustomerOrderPositionId())) + .collect(Collectors.toList()); + return potentialStocks; + } + } +}