Skip to content

Commit

Permalink
Merge pull request #694 from achtzig20/days-of-supply-edc-integration
Browse files Browse the repository at this point in the history
feat: implemented days of supply edc integration
  • Loading branch information
tom-rm-meyer-ISST authored Dec 20, 2024
2 parents c55e29c + 238e1c5 commit 1bf86a2
Show file tree
Hide file tree
Showing 29 changed files with 906 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public JsonNode createMaterialRegistrationRequestBody(MaterialPartnerRelation ma
submodelDescriptorsArray.add(createSubmodelObject(AssetType.ITEM_STOCK_SUBMODEL.URN_SEMANTIC_ID, href + DirectionCharacteristic.INBOUND + "/", variablesService.getItemStockSubmodelApiAssetId()));
submodelDescriptorsArray.add(createSubmodelObject(AssetType.DEMAND_SUBMODEL.URN_SEMANTIC_ID, href, variablesService.getDemandSubmodelApiAssetId()));
submodelDescriptorsArray.add(createSubmodelObject(AssetType.DELIVERY_SUBMODEL.URN_SEMANTIC_ID, href, variablesService.getDeliverySubmodelApiAssetId()));

submodelDescriptorsArray.add(createSubmodelObject(AssetType.DAYS_OF_SUPPLY.URN_SEMANTIC_ID, href + DirectionCharacteristic.INBOUND + "/", variablesService.getDaysOfSupplySubmodelApiAssetId()));
log.debug("Created body for material " + material.getOwnMaterialNumber() + "\n" + body.toPrettyString());
return body;
}
Expand Down Expand Up @@ -145,6 +145,7 @@ public JsonNode createProductRegistrationRequestBody(Material material, String p
submodelDescriptorsArray.add(createSubmodelObject(AssetType.ITEM_STOCK_SUBMODEL.URN_SEMANTIC_ID, href + DirectionCharacteristic.OUTBOUND + "/", variablesService.getItemStockSubmodelApiAssetId()));
submodelDescriptorsArray.add(createSubmodelObject(AssetType.PRODUCTION_SUBMODEL.URN_SEMANTIC_ID, href, variablesService.getProductionSubmodelApiAssetId()));
submodelDescriptorsArray.add(createSubmodelObject(AssetType.DELIVERY_SUBMODEL.URN_SEMANTIC_ID, href, variablesService.getDeliverySubmodelApiAssetId()));
submodelDescriptorsArray.add(createSubmodelObject(AssetType.DAYS_OF_SUPPLY.URN_SEMANTIC_ID, href + DirectionCharacteristic.OUTBOUND + "/", variablesService.getDaysOfSupplySubmodelApiAssetId()));
submodelDescriptorsArray.add(createPartTypeSubmodelObject(material.getOwnMaterialNumber()));

log.debug("Created body for product " + material.getOwnMaterialNumber() + "\n" + body.toPrettyString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public enum AssetType {
DEMAND_SUBMODEL("urn:samm:io.catenax.short_term_material_demand:1.0.0#ShortTermMaterialDemand", "$value", "ShortTermMaterialDemand", "1.0"),
DELIVERY_SUBMODEL("urn:samm:io.catenax.delivery_information:2.0.0#DeliveryInformation", "$value", "DeliveryInformation", "2.0"),
NOTIFICATION("urn:samm:io.catenax.demand_and_capacity_notification:2.0.0#DemandAndCapacityNotification", "none", "none", "2.0"),
DAYS_OF_SUPPLY("urn:samm:io.catenax.days_of_supply:2.0.0#DaysOfSupply", "$value", "DaysOfSupply", "2.0"),
PART_TYPE_INFORMATION_SUBMODEL("urn:samm:io.catenax.part_type_information:1.0.0#PartTypeInformation", "$value", "none", "1.0");

public final String URN_SEMANTIC_ID;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2024 Volkswagen AG
* Copyright (c) 2024 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.common.edc.domain.model;

import jakarta.persistence.Entity;
import lombok.ToString;

@Entity
@ToString(callSuper = true)
public class DaysOfSupplyContractMapping extends ContractMapping {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2024 Volkswagen AG
* Copyright (c) 2024 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.common.edc.domain.repository;

import org.eclipse.tractusx.puris.backend.common.edc.domain.model.ContractMapping;
import org.eclipse.tractusx.puris.backend.common.edc.domain.model.DaysOfSupplyContractMapping;
import org.springframework.stereotype.Repository;

@Repository
public interface DaysOfSupplyContractMappingRepository extends GeneralContractMappingRepository<DaysOfSupplyContractMapping> {
@Override
default Class<? extends ContractMapping> getType() {
return DaysOfSupplyContractMapping.class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ public boolean registerAssetsInitially() {
variablesService.getNotificationApiAssetId(),
variablesService.getNotificationEndpoint()
)));
log.info("Registration of Days of Supply 2.0.0 submodel successful {}", (assetRegistration = registerSubmodelAsset(
variablesService.getDaysOfSupplySubmodelApiAssetId(),
variablesService.getDaysOfSupplySubmodelEndpoint(),
AssetType.DAYS_OF_SUPPLY.URN_SEMANTIC_ID
)));
log.info("Registration of PartTypeInformation 1.0.0 submodel successful {}", (assetRegistration = registerPartTypeInfoSubmodelAsset()));
result &= assetRegistration;
return result;
Expand All @@ -194,6 +199,7 @@ public boolean createPolicyAndContractDefForPartner(Partner partner) {
result &= createSubmodelContractDefinitionForPartner(AssetType.DEMAND_SUBMODEL.URN_SEMANTIC_ID, variablesService.getDemandSubmodelApiAssetId(), partner);
result &= createSubmodelContractDefinitionForPartner(AssetType.DELIVERY_SUBMODEL.URN_SEMANTIC_ID, variablesService.getDeliverySubmodelApiAssetId(), partner);
result &= createSubmodelContractDefinitionForPartner(AssetType.NOTIFICATION.URN_SEMANTIC_ID, variablesService.getNotificationApiAssetId(), partner);
result &= createSubmodelContractDefinitionForPartner(AssetType.DAYS_OF_SUPPLY.URN_SEMANTIC_ID, variablesService.getDaysOfSupplySubmodelApiAssetId(), partner);
result &= createDtrContractDefinitionForPartner(partner);
return createSubmodelContractDefinitionForPartner(AssetType.PART_TYPE_INFORMATION_SUBMODEL.URN_SEMANTIC_ID, variablesService.getPartTypeSubmodelApiAssetId(), partner) && result;
}
Expand Down Expand Up @@ -597,6 +603,7 @@ private JsonNode getSubmodelFromPartner(MaterialPartnerRelation mpr, AssetType t
case DEMAND_SUBMODEL -> fetchSubmodelDataByDirection(mpr, AssetType.DEMAND_SUBMODEL.URN_SEMANTIC_ID, direction);
case DELIVERY_SUBMODEL -> fetchSubmodelDataByDirection(mpr, AssetType.DELIVERY_SUBMODEL.URN_SEMANTIC_ID, direction);
case NOTIFICATION -> throw new IllegalArgumentException("DemandAndCapacityNotification not supported");
case DAYS_OF_SUPPLY -> fetchSubmodelDataByDirection(mpr, AssetType.DAYS_OF_SUPPLY.URN_SEMANTIC_ID, direction);
case PART_TYPE_INFORMATION_SUBMODEL -> fetchPartTypeSubmodelData(mpr);
};
boolean failed = true;
Expand Down Expand Up @@ -1032,6 +1039,7 @@ private boolean negotiateContractForSubmodel(MaterialPartnerRelation mpr, AssetT
case DEMAND_SUBMODEL -> fetchSubmodelDataByDirection(mpr, AssetType.DEMAND_SUBMODEL.URN_SEMANTIC_ID, direction);
case DELIVERY_SUBMODEL -> fetchSubmodelDataByDirection(mpr, AssetType.DELIVERY_SUBMODEL.URN_SEMANTIC_ID, direction);
case NOTIFICATION -> throw new IllegalArgumentException("DemandAndCapacityNotification not supported");
case DAYS_OF_SUPPLY -> fetchSubmodelDataByDirection(mpr, AssetType.DAYS_OF_SUPPLY.URN_SEMANTIC_ID, direction);
case PART_TYPE_INFORMATION_SUBMODEL -> fetchPartTypeSubmodelData(mpr);
};
Map<String, String> equalFilters = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.eclipse.tractusx.puris.backend.common.edc.domain.model.ContractMapping;
import org.eclipse.tractusx.puris.backend.common.edc.domain.model.DtrContractMapping;
import org.eclipse.tractusx.puris.backend.common.edc.domain.model.AssetType;
import org.eclipse.tractusx.puris.backend.common.edc.domain.repository.DaysOfSupplyContractMappingRepository;
import org.eclipse.tractusx.puris.backend.common.edc.domain.repository.DeliveryContractMappingRepository;
import org.eclipse.tractusx.puris.backend.common.edc.domain.repository.DemandAndCapacityNotificationContractMappingRepository;
import org.eclipse.tractusx.puris.backend.common.edc.domain.repository.DemandContractMappingRepository;
Expand Down Expand Up @@ -60,6 +61,9 @@ public class EdcContractMappingService {
@Autowired
private DemandAndCapacityNotificationContractMappingRepository demandAndCapacityNotificationContractMappingRepository;

@Autowired
private DaysOfSupplyContractMappingRepository daysOfSupplyContractMappingRepository;

@Autowired
private PartTypeContractMappingRepository partTypeContractMappingRepository;

Expand Down Expand Up @@ -124,6 +128,7 @@ private GeneralContractMappingRepository<? extends ContractMapping> getContractM
case DEMAND_SUBMODEL -> demandContractMappingRepository;
case DELIVERY_SUBMODEL -> deliveryContractMappingRepository;
case NOTIFICATION -> demandAndCapacityNotificationContractMappingRepository;
case DAYS_OF_SUPPLY -> daysOfSupplyContractMappingRepository;
case PART_TYPE_INFORMATION_SUBMODEL -> partTypeContractMappingRepository;
};
return repository;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,21 @@ public String getNotificationEndpoint() {
*/
private String notificationAssetId;

/**
* The url under which this application's request endpoint can
* be reached by external machines.
*/
public String getDaysOfSupplySubmodelEndpoint() {
return getPurisBaseUrl() + getContextPath() + "days-of-supply/request";
}

@Value("${puris.daysofsupplysubmodel.apiassetid}")
/**
* The assetId that shall be assigned to the request API
* during asset creation.
*/
private String daysOfSupplySubmodelAssetId;

@Value("${puris.frameworkagreement.credential}")
/**
* The name of the framework agreement to be used.
Expand Down Expand Up @@ -285,6 +300,10 @@ public String getDeliverySubmodelApiAssetId() {
return deliverySubmodelAssetId + "@" + ownBpnl;
}

public String getDaysOfSupplySubmodelApiAssetId() {
return daysOfSupplySubmodelAssetId + "@" + ownBpnl;
}

public String getNotificationApiAssetId() {
return notificationAssetId + "@" + ownBpnl;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public class DemandController {
@Operation(summary = "Get all own demands for the given Material", description = "Get all own demands for the given material number. Optionally the demanding site can be filtered by its bpns.")
public List<DemandDto> getAllDemands(@Parameter(description = "encoded in base64") String ownMaterialNumber, Optional<String> site) {
ownMaterialNumber = new String(Base64.getDecoder().decode(ownMaterialNumber));
return ownDemandService.findAllByFilters(Optional.of(ownMaterialNumber), Optional.empty(), site, Optional.empty())
return ownDemandService.findAllByFilters(Optional.of(ownMaterialNumber), Optional.empty(), site)
.stream().map(this::convertToDto).collect(Collectors.toList());
}

Expand Down Expand Up @@ -175,7 +175,7 @@ public List<DemandDto> getAllDemandsForPartner(@Parameter(description = "encoded
if (ownMaterialNumber != null) {
ownMaterialNumber = new String(Base64.getDecoder().decode(ownMaterialNumber));
}
return reportedDemandService.findAllByFilters(Optional.of(ownMaterialNumber), bpnl, site, Optional.empty())
return reportedDemandService.findAllByFilters(Optional.of(ownMaterialNumber), bpnl, site)
.stream().map(this::convertToDto).collect(Collectors.toList());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public ShortTermMaterialDemand handleDemandSubmodelRequest(String bpnl, String m
return null;
}

var currentDemands = ownDemandService.findAllByFilters(Optional.of(material.getOwnMaterialNumber()), Optional.of(partner.getBpnl()), Optional.empty(), Optional.empty());
var currentDemands = ownDemandService.findAllByFilters(Optional.of(material.getOwnMaterialNumber()), Optional.of(partner.getBpnl()), Optional.empty());
return sammMapper.ownDemandToSamm(currentDemands, partner, material);
}

Expand All @@ -109,7 +109,7 @@ public void doReportedDemandRequest(Partner partner, Material material) {
}
}
// delete older data:
var oldDemands = reportedDemandService.findAllByFilters(Optional.of(material.getOwnMaterialNumber()), Optional.of(partner.getBpnl()), Optional.empty(), Optional.empty());
var oldDemands = reportedDemandService.findAllByFilters(Optional.of(material.getOwnMaterialNumber()), Optional.of(partner.getBpnl()), Optional.empty());
for (var oldDemand : oldDemands) {
reportedDemandService.delete(oldDemand.getUuid());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ public final List<TEntity> findAllByOwnMaterialNumber(String ownMaterialNumber)
public final List<TEntity> findAllByFilters(
Optional<String> ownMaterialNumber,
Optional<String> bpnl,
Optional<String> demandLocationBpns,
Optional<Date> day) {
Optional<String> demandLocationBpns) {
var stream = repository.findAll().stream();
if (ownMaterialNumber.isPresent()) {
stream = stream.filter(demand -> demand.getMaterial().getOwnMaterialNumber().equals(ownMaterialNumber.get()));
Expand All @@ -82,17 +81,6 @@ public final List<TEntity> findAllByFilters(
if (demandLocationBpns.isPresent()) {
stream = stream.filter(demand -> demand.getDemandLocationBpns().equals(demandLocationBpns.get()));
}
if (day.isPresent()) {
LocalDate localDayDate = Instant.ofEpochMilli(day.get().getTime())
.atOffset(ZoneOffset.UTC)
.toLocalDate();
stream = stream.filter(demand -> {
LocalDate demandDayDate = Instant.ofEpochMilli(demand.getDay().getTime())
.atOffset(ZoneOffset.UTC)
.toLocalDate();
return demandDayDate.getDayOfMonth() == localDayDate.getDayOfMonth();
});
}
return stream.toList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ See the NOTICE file(s) distributed with this work for additional
*/
package org.eclipse.tractusx.puris.backend.demand.logic.services;

import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
Expand All @@ -33,6 +35,9 @@ See the NOTICE file(s) distributed with this work for additional
import org.eclipse.tractusx.puris.backend.masterdata.logic.service.PartnerService;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class OwnDemandService extends DemandService<OwnDemand, OwnDemandRepository> {
public OwnDemandService(OwnDemandRepository repository, PartnerService partnerService, MaterialPartnerRelationService mprService) {
Expand All @@ -42,11 +47,15 @@ public OwnDemandService(OwnDemandRepository repository, PartnerService partnerSe
public final List<Double> getQuantityForDays(String material, String partnerBpnl, String siteBpns, int numberOfDays) {
List<Double> quantities = new ArrayList<>();
LocalDate localDate = LocalDate.now();

List<OwnDemand> demands = findAllByFilters(Optional.of(material), Optional.of(partnerBpnl), Optional.of(siteBpns));
for (int i = 0; i < numberOfDays; i++) {
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
List<OwnDemand> demands = findAllByFilters(Optional.of(material), Optional.of(partnerBpnl), Optional.of(siteBpns), Optional.of(date));
double demandQuantity = getSumOfQuantities(demands);
LocalDate localDayDate = Instant.ofEpochMilli(date.getTime()).atOffset(ZoneOffset.UTC).toLocalDate();
List<OwnDemand> demandsForDate = demands.stream().filter(demand -> {
LocalDate demandDayDate = Instant.ofEpochMilli(demand.getDay().getTime()).atOffset(ZoneOffset.UTC).toLocalDate();
return demandDayDate.getDayOfMonth() == localDayDate.getDayOfMonth();
}).toList();
double demandQuantity = getSumOfQuantities(demandsForDate);
quantities.add(demandQuantity);

localDate = localDate.plusDays(1);
Expand Down
Loading

0 comments on commit 1bf86a2

Please sign in to comment.