Skip to content
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The **need for configuration updates** is **marked bold**.

### Added

- /
- Added improved error messages for refresh materials ([#995](https://github.com/eclipse-tractusx/puris/pull/995))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update to latest changelog version (changed meanwhile) and consider this a major version.

Please also update:

  • frontend/package.json
  • frontend/package-lock.json
  • backend/pom.xml
  • charts/Chart.yaml
  • charts/README.md
  • docs/admin/Migration_Guide

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please merge main / bump to major version


### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,22 @@
import org.eclipse.tractusx.puris.backend.common.edc.logic.service.EdcAdapterService;
import org.eclipse.tractusx.puris.backend.delivery.domain.model.DeliveryResponsibilityEnumeration;
import org.eclipse.tractusx.puris.backend.delivery.domain.model.OwnDelivery;
import org.eclipse.tractusx.puris.backend.delivery.domain.model.ReportedDelivery;
import org.eclipse.tractusx.puris.backend.delivery.logic.adapter.DeliveryInformationSammMapper;
import org.eclipse.tractusx.puris.backend.delivery.logic.dto.deliverysamm.DeliveryInformation;
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.RefreshError;
import org.eclipse.tractusx.puris.backend.masterdata.domain.model.RefreshResult;
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.logic.dto.itemstocksamm.DirectionCharacteristic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
Expand Down Expand Up @@ -144,7 +148,8 @@ public DeliveryInformation handleDeliverySubmodelRequest(String bpnl, String mat
return sammMapper.ownDeliveryToSamm(currentDeliveries, partner, material);
}

public void doReportedDeliveryRequest(Partner partner, Material material) {
public RefreshResult doReportedDeliveryRequest(Partner partner, Material material) {
List<RefreshError> errors = new ArrayList<>();
try {
var mpr = mprService.find(material, partner);
if (mpr.getPartnerCXNumber() == null) {
Expand All @@ -159,10 +164,27 @@ public void doReportedDeliveryRequest(Partner partner, Material material) {
var deliveryPartner = delivery.getPartner();
var deliveryMaterial = delivery.getMaterial();
if (!partner.equals(deliveryPartner) || !material.equals(deliveryMaterial)) {
log.warn("Received inconsistent data from " + partner.getBpnl() + "\n" + deliveries);
return;
errors.add(new RefreshError(List.of("Received inconsistent data: partner or material mismatch (expected bpnl=%s, ownMaterialNumber=%s; received bpnl=%s, ownMaterialNumber=%s)".formatted(
partner.getBpnl(),
material.getOwnMaterialNumber(),
deliveryPartner.getBpnl(),
deliveryMaterial.getOwnMaterialNumber()
))));
continue;
}

List<String> validationErrors = reportedDeliveryService.validateWithDetails(delivery);
if (!validationErrors.isEmpty()) {
errors.add(new RefreshError(validationErrors));
}
}

if (!errors.isEmpty()) {
log.warn("Validation errors found for ReportedDelivery request from partner {} for material {}: {}",
partner.getBpnl(), material.getOwnMaterialNumber(), errors);
return new RefreshResult("Validation failed for reported deliveries", errors);
}

// delete older data:
var oldDeliveries = reportedDeliveryService.findAllByFilters(Optional.of(material.getOwnMaterialNumber()), Optional.empty(), Optional.of(partner.getBpnl()), Optional.empty(), Optional.empty());
for (var oldDelivery : oldDeliveries) {
Expand All @@ -171,11 +193,14 @@ public void doReportedDeliveryRequest(Partner partner, Material material) {
for (var newDelivery : deliveries) {
reportedDeliveryService.create(newDelivery);
}
log.info("Updated Reported Deliveries for " + material.getOwnMaterialNumber() + " and partner " + partner.getBpnl());

log.info("Successfully updated ReportedDelivery for {} and partner {}",
material.getOwnMaterialNumber(), partner.getBpnl());
materialService.updateTimestamp(material.getOwnMaterialNumber());
return new RefreshResult("Successfully processed all reported deliveries", errors);
} catch (Exception e) {
log.error("Error in Reported Deliveries Request for " + material.getOwnMaterialNumber() + " and partner " + partner.getBpnl(), e);
errors.add(new RefreshError(List.of("System error: " + e.getMessage())));
return new RefreshResult("System error occurred during processing", errors);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.stream.Stream;

import org.eclipse.tractusx.puris.backend.delivery.domain.model.Delivery;
import org.eclipse.tractusx.puris.backend.delivery.domain.model.EventTypeEnumeration;
import org.eclipse.tractusx.puris.backend.delivery.domain.repository.DeliveryRepository;
import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner;
import org.eclipse.tractusx.puris.backend.masterdata.logic.service.PartnerService;
Expand Down Expand Up @@ -130,4 +131,225 @@ public final T update(T delivery) {
public final void delete(UUID id) {
repository.deleteById(id);
}

protected List<String> basicValidation(Delivery delivery) {
List<String> errors = new ArrayList<>();

if (delivery.getQuantity() < 0) {
errors.add("Quantity must be greater than or equal to 0.");
}
if (delivery.getMeasurementUnit() == null) {
errors.add("Missing measurement unit.");
}
if (delivery.getLastUpdatedOnDateTime() == null) {
errors.add("Missing lastUpdatedOnTime.");
} else if (delivery.getLastUpdatedOnDateTime().after(new Date())) {
errors.add("lastUpdatedOnDateTime cannot be in the future.");
}
if (delivery.getMaterial() == null) {
errors.add("Missing material.");
}
if (delivery.getPartner() == null) {
errors.add("Missing partner.");
}
// errors.addAll(validateResponsibility(delivery));
errors.addAll(validateTransitEvent(delivery));
if (!((delivery.getCustomerOrderNumber() != null && delivery.getCustomerOrderPositionNumber() != null) ||
(delivery.getCustomerOrderNumber() == null && delivery.getCustomerOrderPositionNumber() == null && delivery.getSupplierOrderNumber() == null))) {
errors.add("If an order position reference is given, customer order number and customer order position number must be set.");
}

return errors;
}

protected List<String> validateOwnPartner(Delivery delivery) {
List<String> errors = new ArrayList<>();
if (ownPartnerEntity == null) {
ownPartnerEntity = partnerService.getOwnPartnerEntity();
}
if (delivery.getPartner().equals(ownPartnerEntity)) {
errors.add("Partner cannot be the same as own partner entity.");
}
return errors;
}

protected List<String> validateTransitEvent(Delivery delivery) {
List<String> errors = new ArrayList<>();
var now = new Date().getTime();

if (delivery.getDepartureType() == null) {
errors.add("Missing departure type.");
} else if (!(delivery.getDepartureType() == EventTypeEnumeration.ESTIMATED_DEPARTURE || delivery.getDepartureType() == EventTypeEnumeration.ACTUAL_DEPARTURE)) {
errors.add("Invalid departure type.");
}
if (delivery.getArrivalType() == null) {
errors.add("Missing arrival type.");
} else if (!(delivery.getArrivalType() == EventTypeEnumeration.ESTIMATED_ARRIVAL || delivery.getArrivalType() == EventTypeEnumeration.ACTUAL_ARRIVAL)) {
errors.add("Invalid arrival type.");
}
if (delivery.getDepartureType() == EventTypeEnumeration.ESTIMATED_DEPARTURE && delivery.getArrivalType() == EventTypeEnumeration.ACTUAL_ARRIVAL) {
errors.add("Estimated departure cannot have actual arrival.");
}
if (delivery.getDateOfDeparture() == null) {
errors.add("Missing date of departure.");
}
if (delivery.getDateOfArrival() == null) {
errors.add("Missing date of arrival.");
}
if (delivery.getDateOfArrival() != null && delivery.getDateOfDeparture() != null &&
delivery.getDateOfDeparture().getTime() >= delivery.getDateOfArrival().getTime()) {
errors.add("Date of departure must be before date of arrival.");
}
if (delivery.getDateOfArrival() != null &&
delivery.getArrivalType() == EventTypeEnumeration.ACTUAL_ARRIVAL && delivery.getDateOfArrival().getTime() >= now) {
errors.add("Actual arrival date must be in the past.");
}
if (delivery.getDateOfDeparture() != null &&
delivery.getDepartureType() == EventTypeEnumeration.ACTUAL_DEPARTURE && delivery.getDateOfDeparture().getTime() >= now) {
errors.add("Actual departure date must be in the past.");
}

return errors;
}

protected List<String> validateOwnResponsibility(Delivery delivery) {
List<String> errors = new ArrayList<>();
if (ownPartnerEntity == null) {
ownPartnerEntity = partnerService.getOwnPartnerEntity();
}
var ownSites = ownPartnerEntity.getSites();
var partnerSites = delivery.getPartner().getSites();

if (delivery.getIncoterm() == null) {
errors.add("Missing Incoterm.");
} else {
switch (delivery.getIncoterm().getResponsibility()) {
case SUPPLIER:
var ownSite = ownSites.stream().filter(site -> site.getBpns().equals(delivery.getOriginBpns())).findFirst();
var partnerSite = partnerSites.stream().filter(site -> site.getBpns().equals(delivery.getDestinationBpns())).findFirst();
if (!delivery.getMaterial().isProductFlag()) {
errors.add("Material must have product flag for supplier responsibility.");
}
if (!ownSite.isPresent()) {
errors.add("Origin BPNS must match one of the own partner entity's site BPNS for supplier responsibility.");
} else if (delivery.getOriginBpna() != null && ownSite.get().getAddresses().stream().noneMatch(address -> address.getBpna().equals(delivery.getOriginBpna()))) {
errors.add("Origin BPNA must match one of the own partner entity's site' address BPNAs for supplier responsibility.");
}
if (!partnerSite.isPresent()) {
errors.add("Destination BPNS must match one of the partner's site BPNS for supplier responsibility.");
} else if (delivery.getDestinationBpna() != null && partnerSite.get().getAddresses().stream().noneMatch(address -> address.getBpna().equals(delivery.getDestinationBpna()))) {
errors.add("Destination BPNA must match one of the own partner entity's site' address BPNAs for supplier responsibility.");
}
break;
case CUSTOMER:
ownSite = ownSites.stream().filter(site -> site.getBpns().equals(delivery.getDestinationBpns())).findFirst();
partnerSite = partnerSites.stream().filter(site -> site.getBpns().equals(delivery.getOriginBpns())).findFirst();
if (!delivery.getMaterial().isMaterialFlag()) {
errors.add("Material must have material flag for customer responsibility.");
}
if (!ownSite.isPresent()) {
errors.add("Destination BPNS must match one of the own partner entity's site BPNS for customer responsibility.");
} else if (delivery.getDestinationBpna() != null && ownSite.get().getAddresses().stream().noneMatch(address -> address.getBpna().equals(delivery.getDestinationBpna()))) {
errors.add("Destination BPNA must match one of the own partner entity's site' address BPNAs for customer responsibility.");
}
if (!partnerSite.isPresent()) {
errors.add("Origin BPNS must match one of the partner's site BPNS for customer responsibility.");
} else if (delivery.getOriginBpna() != null && partnerSite.get().getAddresses().stream().noneMatch(address -> address.getBpna().equals(delivery.getOriginBpna()))) {
errors.add("Origin BPNA must match one of the own partner entity's site' address BPNAs for customer responsibility.");
}
break;
case PARTIAL:
if (delivery.getMaterial().isProductFlag()) {
ownSite = ownSites.stream().filter(site -> site.getBpns().equals(delivery.getOriginBpns())).findFirst();
partnerSite = partnerSites.stream().filter(site -> site.getBpns().equals(delivery.getDestinationBpns())).findFirst();
if (ownSite.isPresent() && partnerSite.isPresent() && (
delivery.getOriginBpna() == null ||
ownSite.get().getAddresses().stream().anyMatch(address -> address.getBpna().equals(delivery.getOriginBpna()))
) && (
delivery.getDestinationBpna() == null ||
partnerSite.get().getAddresses().stream().anyMatch(address -> address.getBpna().equals(delivery.getDestinationBpna()))
)) {
return new ArrayList<>();
}
}
if (delivery.getMaterial().isMaterialFlag()) {
ownSite = ownSites.stream().filter(site -> site.getBpns().equals(delivery.getDestinationBpns())).findFirst();
partnerSite = partnerSites.stream().filter(site -> site.getBpns().equals(delivery.getOriginBpns())).findFirst();
if (ownSite.isPresent() && partnerSite.isPresent() && (
delivery.getDestinationBpna() == null ||
ownSite.get().getAddresses().stream().anyMatch(address -> address.getBpna().equals(delivery.getDestinationBpna()))
) && (
delivery.getOriginBpna() == null ||
partnerSite.get().getAddresses().stream().anyMatch(address -> address.getBpna().equals(delivery.getOriginBpna()))
)) {
return new ArrayList<>();
}
}
errors.add("Responsibility conditions for partial responsibility are not met.");
break;
default:
errors.add("Invalid incoterm responsibility.");
break;
}
}
return errors;
}

protected List<String> validateReportedResponsibility(Delivery delivery) {
List<String> errors = new ArrayList<>();
if (ownPartnerEntity == null) {
ownPartnerEntity = partnerService.getOwnPartnerEntity();
}

if (delivery.getIncoterm() == null) {
errors.add("Missing Incoterm.");
} else {
switch (delivery.getIncoterm().getResponsibility()) {
case SUPPLIER:
if (!delivery.getMaterial().isMaterialFlag()) {
errors.add("Material must have material flag for supplier responsibility.");
}
if (delivery.getPartner().getSites().stream().noneMatch(site -> site.getBpns().equals(delivery.getOriginBpns()))) {
errors.add("Origin BPNA must match one of the partner entity's site' address BPNAs for supplier responsibility.");
}
if (ownPartnerEntity.getSites().stream().noneMatch(site -> site.getBpns().equals(delivery.getDestinationBpns()))) {
errors.add("Destination BPNA must match one of the partner entity's site' address BPNAs for supplier responsibility.");
}

break;
case CUSTOMER:
if (!delivery.getMaterial().isProductFlag()) {
errors.add("Material must have product flag for customer responsibility.");
}
if (ownPartnerEntity.getSites().stream().noneMatch(site -> site.getBpns().equals(delivery.getOriginBpns()))) {
errors.add("Site BPNS must match one of the own partner entity's site BPNS for customer responsibility.");
}
if (delivery.getPartner().getSites().stream().noneMatch(site -> site.getBpns().equals(delivery.getDestinationBpns()))) {
errors.add("Site BPNA must match one of the partner entity's site' address BPNAs for customer responsibility.");
}

break;
case PARTIAL:
if (delivery.getMaterial().isProductFlag()) {
if (delivery.getPartner().getSites().stream().anyMatch(site -> site.getBpns().equals(delivery.getDestinationBpns())) &&
ownPartnerEntity.getSites().stream().anyMatch(site -> site.getBpns().equals(delivery.getOriginBpns()))
) {
return new ArrayList<>();
}
}
if (delivery.getMaterial().isMaterialFlag()) {
if (ownPartnerEntity.getSites().stream().anyMatch(site -> site.getBpns().equals(delivery.getDestinationBpns())) &&
delivery.getPartner().getSites().stream().anyMatch(site -> site.getBpns().equals(delivery.getOriginBpns()))) {
return new ArrayList<>();
}
}
errors.add("Responsibility conditions for partial responsibility are not met.");
break;
default:
errors.add("Invalid incoterm responsibility.");
break;
}
}
return errors;
}
}
Loading
Loading