From 70163b8a0ca0e3694ebb1a8aeddb709c88a322d4 Mon Sep 17 00:00:00 2001 From: BenjaminKotnik <158455098+BenjaminKotnik@users.noreply.github.com> Date: Tue, 11 Jun 2024 12:50:12 +0200 Subject: [PATCH] Days of supply (#15) feat: implemented the backend for Days of Supply --- .../DataInjectionCommandLineRunner.java | 2 - .../common/security/SecurityConfig.java | 1 + .../controller/DeliveryController.java | 4 +- ...sitory.java => OwnDeliveryRepository.java} | 2 +- .../service/DeliveryRequestApiService.java | 4 +- .../logic/service/OwnDeliveryService.java | 65 ++++++- .../service/ReportedDeliveryService.java | 59 +++++- .../demand/controller/DemandController.java | 4 +- .../services/DemandRequestApiService.java | 4 +- .../demand/logic/services/DemandService.java | 21 ++- .../logic/services/OwnDemandService.java | 30 +++ .../controller/ProductionController.java | 2 +- .../logic/service/OwnProductionService.java | 46 ++++- .../service/ProductionRequestApiService.java | 2 +- .../stock/logic/service/ItemStockService.java | 37 +++- .../service/MaterialItemStockService.java | 2 - .../supply/controller/SupplyController.java | 90 +++++++++ .../domain/model/OwnCustomerSupply.java | 35 ++++ .../domain/model/OwnSupplierSupply.java | 34 ++++ .../domain/model/ReportedCustomerSupply.java | 35 ++++ .../domain/model/ReportedSupplierSupply.java | 34 ++++ .../backend/supply/domain/model/Supply.java | 104 +++++++++++ .../ReportedCustomerSupplyRepository.java | 30 +++ .../ReportedSupplierSupplyRepository.java | 30 +++ .../backend/supply/logic/dto/SupplyDto.java | 38 ++++ .../logic/service/CustomerSupplyService.java | 175 ++++++++++++++++++ .../logic/service/SupplierSupplyService.java | 173 +++++++++++++++++ 27 files changed, 1033 insertions(+), 30 deletions(-) rename backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/domain/repository/{DeliveryRepository.java => OwnDeliveryRepository.java} (92%) create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/controller/SupplyController.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/OwnCustomerSupply.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/OwnSupplierSupply.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/ReportedCustomerSupply.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/ReportedSupplierSupply.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/Supply.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/repository/ReportedCustomerSupplyRepository.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/repository/ReportedSupplierSupplyRepository.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/logic/dto/SupplyDto.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/logic/service/CustomerSupplyService.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/logic/service/SupplierSupplyService.java 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 b84e8c3d..55ee9109 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 @@ -78,7 +78,6 @@ public class DataInjectionCommandLineRunner implements CommandLineRunner { @Autowired private VariablesService variablesService; - @Autowired private ErpAdapterRequestService erpAdapterRequestService; @@ -490,5 +489,4 @@ private Material getNewCentralControlUnitMaterial() { material.setName("Central Control Unit"); return material; } - } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java index 468ea19d..da69ba0e 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java @@ -96,6 +96,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { "/planned-production/**", "/material-demand/**", "/delivery-information/**", + "/supply/**", "/edc/**", "/erp-adapter/**", "/parttypeinformation/**" diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/controller/DeliveryController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/controller/DeliveryController.java index df82500e..3b75b5ff 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/controller/DeliveryController.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/controller/DeliveryController.java @@ -108,9 +108,9 @@ public List getAllDeliveries(String ownMaterialNumber, Optional { +public interface OwnDeliveryRepository extends JpaRepository { } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/logic/service/DeliveryRequestApiService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/logic/service/DeliveryRequestApiService.java index 11eb0919..37ed56d5 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/logic/service/DeliveryRequestApiService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/logic/service/DeliveryRequestApiService.java @@ -90,7 +90,7 @@ public DeliveryInformation handleDeliverySubmodelRequest(String bpnl, String mat return null; } - var currentDeliveries = ownDeliveryService.findAllByFilters(Optional.of(material.getOwnMaterialNumber()), Optional.empty(), Optional.of(partner.getBpnl())); + var currentDeliveries = ownDeliveryService.findAllByFilters(Optional.of(material.getOwnMaterialNumber()), Optional.empty(), Optional.of(partner.getBpnl()), Optional.empty(), Optional.empty()); return sammMapper.ownDeliveryToSamm(currentDeliveries, partner, material); } @@ -114,7 +114,7 @@ public void doReportedDeliveryRequest(Partner partner, Material material) { } } // delete older data: - var oldDeliveries = reportedDeliveryService.findAllByFilters(Optional.of(material.getOwnMaterialNumber()), Optional.empty(), Optional.of(partner.getBpnl())); + var oldDeliveries = reportedDeliveryService.findAllByFilters(Optional.of(material.getOwnMaterialNumber()), Optional.empty(), Optional.of(partner.getBpnl()), Optional.empty(), Optional.empty()); for (var oldDelivery : oldDeliveries) { reportedDeliveryService.delete(oldDelivery.getUuid()); } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/logic/service/OwnDeliveryService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/logic/service/OwnDeliveryService.java index 2ccca9d0..ba876ad2 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/logic/service/OwnDeliveryService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/logic/service/OwnDeliveryService.java @@ -20,6 +20,11 @@ package org.eclipse.tractusx.puris.backend.delivery.logic.service; +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; import java.util.Optional; @@ -31,14 +36,15 @@ import org.eclipse.tractusx.puris.backend.delivery.domain.model.EventTypeEnumeration; import org.eclipse.tractusx.puris.backend.delivery.domain.model.OwnDelivery; -import org.eclipse.tractusx.puris.backend.delivery.domain.repository.DeliveryRepository; +import org.eclipse.tractusx.puris.backend.delivery.domain.repository.OwnDeliveryRepository; import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner; import org.eclipse.tractusx.puris.backend.masterdata.logic.service.PartnerService; +import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.DirectionCharacteristic; import org.springframework.stereotype.Service; @Service public class OwnDeliveryService { - public final DeliveryRepository repository; + private final OwnDeliveryRepository repository; private final PartnerService partnerService; @@ -46,7 +52,7 @@ public class OwnDeliveryService { private Partner ownPartnerEntity; - public OwnDeliveryService(DeliveryRepository repository, PartnerService partnerService) { + public OwnDeliveryService(OwnDeliveryRepository repository, PartnerService partnerService) { this.repository = repository; this.partnerService = partnerService; this.validator = this::validate; @@ -66,7 +72,12 @@ public final List findAllByOwnMaterialNumber(String ownMaterialNumb .toList(); } - public final List findAllByFilters(Optional ownMaterialNumber, Optional bpns, Optional bpnl) { + public final List findAllByFilters( + Optional ownMaterialNumber, + Optional bpns, + Optional bpnl, + Optional day, + Optional direction) { Stream stream = repository.findAll().stream(); if (ownMaterialNumber.isPresent()) { stream = stream.filter(delivery -> delivery.getMaterial().getOwnMaterialNumber().equals(ownMaterialNumber.get())); @@ -77,6 +88,27 @@ public final List findAllByFilters(Optional ownMaterialNumb if (bpnl.isPresent()) { stream = stream.filter(delivery -> delivery.getPartner().getBpnl().equals(bpnl.get())); } + if (day.isPresent()) { + LocalDate localDayDate = Instant.ofEpochMilli(day.get().getTime()) + .atOffset(ZoneOffset.UTC) + .toLocalDate(); + stream = stream.filter(delivery -> { + long time = direction.get() == DirectionCharacteristic.INBOUND + ? delivery.getDateOfArrival().getTime() + : delivery.getDateOfDeparture().getTime(); + LocalDate deliveryDayDate = Instant.ofEpochMilli(time) + .atOffset(ZoneOffset.UTC) + .toLocalDate(); + return deliveryDayDate.getDayOfMonth() == localDayDate.getDayOfMonth(); + }); + } + if (direction.isPresent()) { + if (direction.get() == DirectionCharacteristic.INBOUND) { + stream = stream.filter(delivery -> delivery.getDestinationBpns().equals(bpns.get())); + } else { + stream = stream.filter(delivery -> delivery.getOriginBpns().equals(bpns.get())); + } + } return stream.toList(); } @@ -84,6 +116,29 @@ public final OwnDelivery findById(UUID id) { return repository.findById(id).orElse(null); } + public final double getSumOfQuantities(List deliveries) { + double sum = 0; + for (OwnDelivery delivery : deliveries) { + sum += delivery.getQuantity(); + } + return sum; + } + + public final List getQuantityForDays(String material, String partnerBpnl, String siteBpns, DirectionCharacteristic direction, int numberOfDays) { + List deliveryQtys = new ArrayList<>(); + LocalDate localDate = LocalDate.now(); + + for (int i = 0; i < numberOfDays; i++) { + Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + List deliveries = findAllByFilters(Optional.of(material), Optional.of(siteBpns), Optional.of(partnerBpnl), Optional.of(date), Optional.of(direction)); + double deliveryQuantity = getSumOfQuantities(deliveries); + deliveryQtys.add(deliveryQuantity); + + localDate = localDate.plusDays(1); + } + return deliveryQtys; + } + public final OwnDelivery create(OwnDelivery delivery) { if (!validator.apply(delivery)) { throw new IllegalArgumentException("Invalid delivery"); @@ -121,7 +176,7 @@ public boolean validate(OwnDelivery delivery) { ownPartnerEntity = partnerService.getOwnPartnerEntity(); } return - delivery.getQuantity() > 0 && + delivery.getQuantity() >= 0 && delivery.getMeasurementUnit() != null && delivery.getMaterial() != null && delivery.getPartner() != null && diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/logic/service/ReportedDeliveryService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/logic/service/ReportedDeliveryService.java index 3ff3c60b..0b96725d 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/logic/service/ReportedDeliveryService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/delivery/logic/service/ReportedDeliveryService.java @@ -20,6 +20,11 @@ package org.eclipse.tractusx.puris.backend.delivery.logic.service; +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; import java.util.Optional; @@ -32,11 +37,12 @@ import org.eclipse.tractusx.puris.backend.delivery.domain.repository.ReportedDeliveryRepository; import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner; import org.eclipse.tractusx.puris.backend.masterdata.logic.service.PartnerService; +import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.DirectionCharacteristic; import org.springframework.stereotype.Service; @Service public class ReportedDeliveryService { - public final ReportedDeliveryRepository repository; + private final ReportedDeliveryRepository repository; private final PartnerService partnerService; @@ -63,7 +69,12 @@ public final ReportedDelivery findById(UUID id) { return repository.findById(id).orElse(null); } - public final List findAllByFilters(Optional ownMaterialNumber, Optional bpns, Optional bpnl) { + public final List findAllByFilters( + Optional ownMaterialNumber, + Optional bpns, + Optional bpnl, + Optional day, + Optional direction) { Stream stream = repository.findAll().stream(); if (ownMaterialNumber.isPresent()) { stream = stream.filter(delivery -> delivery.getMaterial().getOwnMaterialNumber().equals(ownMaterialNumber.get())); @@ -74,9 +85,53 @@ public final List findAllByFilters(Optional ownMateria if (bpnl.isPresent()) { stream = stream.filter(delivery -> delivery.getPartner().getBpnl().equals(bpnl.get())); } + if (day.isPresent()) { + LocalDate localDayDate = Instant.ofEpochMilli(day.get().getTime()) + .atOffset(ZoneOffset.UTC) + .toLocalDate(); + stream = stream.filter(delivery -> { + long time = direction.get() == DirectionCharacteristic.INBOUND + ? delivery.getDateOfArrival().getTime() + : delivery.getDateOfDeparture().getTime(); + LocalDate deliveryDayDate = Instant.ofEpochMilli(time) + .atOffset(ZoneOffset.UTC) + .toLocalDate(); + return deliveryDayDate.getDayOfMonth() == localDayDate.getDayOfMonth(); + }); + } + if (direction.isPresent()) { + if (direction.get() == DirectionCharacteristic.INBOUND) { + stream = stream.filter(delivery -> delivery.getDestinationBpns().equals(bpns.get())); + } else { + stream = stream.filter(delivery -> delivery.getOriginBpns().equals(bpns.get())); + } + } return stream.toList(); } + public final double getSumOfQuantities(List deliveries) { + double sum = 0; + for (ReportedDelivery delivery : deliveries) { + sum += delivery.getQuantity(); + } + return sum; + } + + public final List getQuantityForDays(String material, String partnerBpnl, String siteBpns, DirectionCharacteristic direction, int numberOfDays) { + List deliveryQtys = new ArrayList<>(); + LocalDate localDate = LocalDate.now(); + + for (int i = 0; i < numberOfDays; i++) { + Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + List deliveries = findAllByFilters(Optional.of(material), Optional.of(siteBpns), Optional.of(partnerBpnl), Optional.of(date), Optional.of(direction)); + double deliveryQuantity = getSumOfQuantities(deliveries); + deliveryQtys.add(deliveryQuantity); + + localDate = localDate.plusDays(1); + } + return deliveryQtys; + } + public final ReportedDelivery create(ReportedDelivery delivery) { if (delivery.getUuid() != null && repository.findById(delivery.getUuid()).isPresent()) { return null; diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/controller/DemandController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/controller/DemandController.java index 79bd61cf..15fb5eca 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/controller/DemandController.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/controller/DemandController.java @@ -91,7 +91,7 @@ public class DemandController { @ResponseBody @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 getAllDemands(String ownMaterialNumber, Optional site) { - return ownDemandService.findAllByFilters(Optional.of(ownMaterialNumber), Optional.empty(), site) + return ownDemandService.findAllByFilters(Optional.of(ownMaterialNumber), Optional.empty(), site, Optional.empty()) .stream().map(this::convertToDto).collect(Collectors.toList()); } @@ -171,7 +171,7 @@ public void deleteDemand(@PathVariable UUID id) { ) public List getAllDemandsForPartner(String ownMaterialNumber, Optional bpnl, Optional site) { - return reportedDemandService.findAllByFilters(Optional.of(ownMaterialNumber), bpnl, site) + return reportedDemandService.findAllByFilters(Optional.of(ownMaterialNumber), bpnl, site, Optional.empty()) .stream().map(this::convertToDto).collect(Collectors.toList()); } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/logic/services/DemandRequestApiService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/logic/services/DemandRequestApiService.java index c3811da1..6d3c363a 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/logic/services/DemandRequestApiService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/logic/services/DemandRequestApiService.java @@ -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()); + var currentDemands = ownDemandService.findAllByFilters(Optional.of(material.getOwnMaterialNumber()), Optional.of(partner.getBpnl()), Optional.empty(), Optional.empty()); return sammMapper.ownDemandToSamm(currentDemands, partner, material); } @@ -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()); + var oldDemands = reportedDemandService.findAllByFilters(Optional.of(material.getOwnMaterialNumber()), Optional.of(partner.getBpnl()), Optional.empty(), Optional.empty()); for (var oldDemand : oldDemands) { reportedDemandService.delete(oldDemand.getUuid()); } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/logic/services/DemandService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/logic/services/DemandService.java index 391bbf90..ffde7cdf 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/logic/services/DemandService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/logic/services/DemandService.java @@ -19,6 +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.ZoneOffset; +import java.util.Date; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -63,7 +67,11 @@ public final List findAllByOwnMaterialNumber(String ownMaterialNumber) .toList(); } - public final List findAllByFilters(Optional ownMaterialNumber, Optional bpnl, Optional demandLocationBpns) { + public final List findAllByFilters( + Optional ownMaterialNumber, + Optional bpnl, + Optional demandLocationBpns, + Optional day) { var stream = repository.findAll().stream(); if (ownMaterialNumber.isPresent()) { stream = stream.filter(demand -> demand.getMaterial().getOwnMaterialNumber().equals(ownMaterialNumber.get())); @@ -74,6 +82,17 @@ public final List findAllByFilters(Optional ownMaterialNumber, 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(); } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/logic/services/OwnDemandService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/logic/services/OwnDemandService.java index 68ed3bf2..027a8de2 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/logic/services/OwnDemandService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/demand/logic/services/OwnDemandService.java @@ -19,6 +19,13 @@ See the NOTICE file(s) distributed with this work for additional */ package org.eclipse.tractusx.puris.backend.demand.logic.services; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; + import org.eclipse.tractusx.puris.backend.demand.domain.model.OwnDemand; import org.eclipse.tractusx.puris.backend.demand.domain.repository.OwnDemandRepository; import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner; @@ -32,6 +39,21 @@ public OwnDemandService(OwnDemandRepository repository, PartnerService partnerSe super(repository, partnerService, mprService); } + public final List getQuantityForDays(String material, String partnerBpnl, String siteBpns, int numberOfDays) { + List quantities = new ArrayList<>(); + LocalDate localDate = LocalDate.now(); + + for (int i = 0; i < numberOfDays; i++) { + Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + List demands = findAllByFilters(Optional.of(material), Optional.of(partnerBpnl), Optional.of(siteBpns), Optional.of(date)); + double demandQuantity = getSumOfQuantities(demands); + quantities.add(demandQuantity); + + localDate = localDate.plusDays(1); + } + return quantities; + } + @Override public boolean validate(OwnDemand demand) { Partner ownPartnerEntity = partnerService.getOwnPartnerEntity(); @@ -48,4 +70,12 @@ public boolean validate(OwnDemand demand) { ownPartnerEntity.getSites().stream().anyMatch(site -> site.getBpns().equals(demand.getDemandLocationBpns())) && (demand.getSupplierLocationBpns() == null || demand.getPartner().getSites().stream().anyMatch(site -> site.getBpns().equals(demand.getSupplierLocationBpns()))); } + + private final double getSumOfQuantities(List demands) { + double sum = 0; + for (OwnDemand demand : demands) { + sum += demand.getQuantity(); + } + return sum; + } } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/production/controller/ProductionController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/production/controller/ProductionController.java index 259e7289..3f191757 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/production/controller/ProductionController.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/production/controller/ProductionController.java @@ -91,7 +91,7 @@ public class ProductionController { @ResponseBody @Operation(summary = "Get all planned productions for the given Material", description = "Get all planned productions for the given material number. Optionally the production site can be filtered by its bpns.") public List getAllProductions(String ownMaterialNumber, Optional site) { - return ownProductionService.findAllByFilters(Optional.of(ownMaterialNumber), Optional.empty(), site) + return ownProductionService.findAllByFilters(Optional.of(ownMaterialNumber), Optional.empty(), site, Optional.empty()) .stream().map(this::convertToDto).collect(Collectors.toList()); } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/production/logic/service/OwnProductionService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/production/logic/service/OwnProductionService.java index d7602179..b78170bf 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/production/logic/service/OwnProductionService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/production/logic/service/OwnProductionService.java @@ -20,6 +20,12 @@ See the NOTICE file(s) distributed with this work for additional package org.eclipse.tractusx.puris.backend.production.logic.service; +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; import java.util.Optional; import java.util.UUID; @@ -62,7 +68,11 @@ public final List findAllByOwnMaterialNumber(String ownMaterialNu .toList(); } - public final List findAllByFilters(Optional ownMaterialNumber, Optional bpnl, Optional bpns) { + public final List findAllByFilters( + Optional ownMaterialNumber, + Optional bpnl, + Optional bpns, + Optional dayOfCompletion) { Stream stream = repository.findAll().stream(); if (ownMaterialNumber.isPresent()) { stream = stream.filter(production -> production.getMaterial().getOwnMaterialNumber().equals(ownMaterialNumber.get())); @@ -73,6 +83,17 @@ public final List findAllByFilters(Optional ownMaterialNu if (bpns.isPresent()) { stream = stream.filter(production -> production.getProductionSiteBpns().equals(bpns.get())); } + if (dayOfCompletion.isPresent()) { + LocalDate localEstimatedTimeOfCompletion = Instant.ofEpochMilli(dayOfCompletion.get().getTime()) + .atOffset(ZoneOffset.UTC) + .toLocalDate(); + stream = stream.filter(production -> { + LocalDate productionEstimatedTimeOfCompletion = Instant.ofEpochMilli(production.getEstimatedTimeOfCompletion().getTime()) + .atOffset(ZoneOffset.UTC) + .toLocalDate(); + return productionEstimatedTimeOfCompletion.getDayOfMonth() == localEstimatedTimeOfCompletion.getDayOfMonth(); + }); + } return stream.toList(); } @@ -80,6 +101,21 @@ public final OwnProduction findById(UUID uuid) { return repository.findById(uuid).orElse(null); } + public final List getQuantityForDays(String material, String partnerBpnl, String siteBpns, int numberOfDays) { + List quantities = new ArrayList<>(); + LocalDate localDate = LocalDate.now(); + + for (int i = 0; i < numberOfDays; i++) { + Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + List productions = findAllByFilters(Optional.of(material), Optional.of(partnerBpnl), Optional.of(siteBpns), Optional.of(date)); + double productionQuantity = getSumOfQuantities(productions); + quantities.add(productionQuantity); + + localDate = localDate.plusDays(1); + } + return quantities; + } + public final OwnProduction create(OwnProduction production) { if (!validator.apply(production)) { @@ -133,4 +169,12 @@ public boolean validate(OwnProduction production) { production.getSupplierOrderNumber() == null )); } + + private final double getSumOfQuantities(List productions) { + double sum = 0; + for (OwnProduction production : productions) { + sum += production.getQuantity(); + } + return sum; + } } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/production/logic/service/ProductionRequestApiService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/production/logic/service/ProductionRequestApiService.java index 3aa173b6..d5e5a1d4 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/production/logic/service/ProductionRequestApiService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/production/logic/service/ProductionRequestApiService.java @@ -73,7 +73,7 @@ public PlannedProductionOutput handleProductionSubmodelRequest(String bpnl, Stri // only send an answer if partner is registered as customer return null; } - var currentProduction = ownProductionService.findAllByFilters(Optional.of(material.getOwnMaterialNumber()), Optional.of(partner.getBpnl()), Optional.empty()); + var currentProduction = ownProductionService.findAllByFilters(Optional.of(material.getOwnMaterialNumber()), Optional.of(partner.getBpnl()), Optional.empty(), Optional.empty()); return sammMapper.ownProductionToSamm(currentProduction, partner, material); } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ItemStockService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ItemStockService.java index b3d8258f..06362371 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ItemStockService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ItemStockService.java @@ -20,6 +20,7 @@ package org.eclipse.tractusx.puris.backend.stock.logic.service; import lombok.extern.slf4j.Slf4j; + 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; @@ -32,7 +33,7 @@ import java.util.Objects; import java.util.UUID; import java.util.function.Function; - +import java.util.stream.Stream; @Slf4j public abstract class ItemStockService { @@ -45,7 +46,8 @@ public abstract class ItemStockService { protected final Function validator; - public ItemStockService(PartnerService partnerService, MaterialPartnerRelationService mprService, ItemStockRepository repository) { + public ItemStockService(PartnerService partnerService, MaterialPartnerRelationService mprService, + ItemStockRepository repository) { this.partnerService = partnerService; this.mprService = mprService; this.repository = repository; @@ -105,6 +107,29 @@ public final List findByPartnerBpnlAndOwnMaterialNumber(String partnerBpnl, S return repository.getForPartnerBpnlAndOwnMatNbr(partnerBpnl, ownMaterialNumber); } + public final List findAllByMaterialAndPartner(String ownMaterialNumber, String partnerBpnl) { + Stream stream = repository.findAll().stream(); + + stream = stream.filter(stock -> stock.getMaterial().getOwnMaterialNumber().equals(ownMaterialNumber)); + stream = stream.filter(stock -> stock.getPartner().getBpnl().equals(partnerBpnl)); + return stream.toList(); + } + + public final double getSumOfQuantities(List stocks) { + double sum = 0; + for (T stock : stocks) { + sum = stock.getQuantity(); + } + return sum; + } + + public final double getInitialStockQuantity(String material, String partnerBpnl) { + List stocks = findAllByMaterialAndPartner(material, partnerBpnl); + double initialStockQuantity = getSumOfQuantities(stocks); + + return initialStockQuantity; + } + public abstract boolean validate(T itemStock); protected boolean basicValidation(ItemStock itemStock) { @@ -171,12 +196,12 @@ protected final boolean validateProductItemStock(ItemStock itemStock) { protected boolean validateLocation(ItemStock itemStock, Partner partner) { try { var stockSite = partner.getSites().stream() - .filter(site -> site.getBpns().equals(itemStock.getLocationBpns())) - .findFirst().orElse(null); + .filter(site -> site.getBpns().equals(itemStock.getLocationBpns())) + .findFirst().orElse(null); Objects.requireNonNull(stockSite, "Site not found"); var stockAddress = stockSite.getAddresses().stream() - .filter(addr -> addr.getBpna().equals(itemStock.getLocationBpna())) - .findFirst().orElse(null); + .filter(addr -> addr.getBpna().equals(itemStock.getLocationBpna())) + .findFirst().orElse(null); Objects.requireNonNull(stockAddress, "Address not found"); } catch (Exception e) { log.error("Location Validation failed: " + itemStock + "\n" + e.getMessage()); diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/MaterialItemStockService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/MaterialItemStockService.java index 8f0b79e0..a4430938 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/MaterialItemStockService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/MaterialItemStockService.java @@ -44,6 +44,4 @@ public boolean validate(MaterialItemStock materialItemStock) { return basicValidation(materialItemStock) && validateLocalStock(materialItemStock) && validateMaterialItemStock(materialItemStock); } - - } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/controller/SupplyController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/controller/SupplyController.java new file mode 100644 index 00000000..927af4ab --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/controller/SupplyController.java @@ -0,0 +1,90 @@ +/* + * 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.supply.controller; + +import java.util.List; +import java.util.Optional; + +import org.eclipse.tractusx.puris.backend.supply.domain.model.Supply; +import org.eclipse.tractusx.puris.backend.supply.logic.dto.SupplyDto; +import org.eclipse.tractusx.puris.backend.supply.logic.service.CustomerSupplyService; +import org.eclipse.tractusx.puris.backend.supply.logic.service.SupplierSupplyService; +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + + + +@RestController +@RequestMapping("supply") +public class SupplyController { + @Autowired + private CustomerSupplyService customerSupplyService; + @Autowired + private SupplierSupplyService supplierSupplyService; + + @Autowired + private ModelMapper modelMapper; + + @GetMapping("customer") + @ResponseBody + @Operation(summary = "Calculate days of supply for customer for given number of days.", + description = "Calculate days of supply for customer for given number of days. Filtered by given material number, partner bpnl and site bpns.") + public List calculateCustomerDaysOfSupply(String materialNumber, String bpnl, String siteBpns, int numberOfDays) { + return customerSupplyService.calculateCustomerDaysOfSupply(materialNumber, bpnl, siteBpns, numberOfDays) + .stream().map(this::convertToDto).toList(); + } + + @GetMapping("customer/reported") + @Operation(summary = "Get days of supply for customer.", + description = "Get days of supply for customer for given material number. Optionally it can be filtered by partner bpnl.") + public List getCustomerDaysOfSupply(String materialNumber, Optional bpnl) { + return customerSupplyService.findAllByFilters(Optional.of(materialNumber), bpnl) + .stream().map(this::convertToDto).toList(); + } + + @GetMapping("supplier") + @Operation(summary = "Calculate days of supply for supplier for given number of days.", + description = "Calculate days of supply for supplier for given number of days. Filtered by given material number, partner bpnl and site bpns.") + public List calculateSupplierDaysOfSupply(String materialNumber, String bpnl, String siteBpns, int numberOfDays) { + return supplierSupplyService.calculateSupplierDaysOfSupply(materialNumber, bpnl, siteBpns, numberOfDays) + .stream().map(this::convertToDto).toList(); + } + + @GetMapping("supplier/reported") + @Operation(summary = "Get days of supply for supplier.", + description = "Get days of supply for supplier for given material number. Optionally it can be filtered by partner bpnl.") + public List getSupplierDaysOfSupply(String materialNumber, Optional bpnl) { + return supplierSupplyService.findAllByFilters(Optional.of(materialNumber), bpnl) + .stream().map(this::convertToDto).toList(); + } + + private SupplyDto convertToDto(Supply entity) { + SupplyDto dto = modelMapper.map(entity, SupplyDto.class); + + return dto; + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/OwnCustomerSupply.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/OwnCustomerSupply.java new file mode 100644 index 00000000..ef0bec6b --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/OwnCustomerSupply.java @@ -0,0 +1,35 @@ +/* + * 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.supply.domain.model; + + import jakarta.persistence.Entity; + import lombok.NoArgsConstructor; + import lombok.ToString; + import lombok.experimental.SuperBuilder; + + @Entity + @SuperBuilder + @NoArgsConstructor + @ToString(callSuper = true) + public class OwnCustomerSupply extends Supply { + + } + \ No newline at end of file diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/OwnSupplierSupply.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/OwnSupplierSupply.java new file mode 100644 index 00000000..0bba8c0c --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/OwnSupplierSupply.java @@ -0,0 +1,34 @@ +/* + * 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.supply.domain.model; + +import jakarta.persistence.Entity; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +@Entity +@SuperBuilder +@NoArgsConstructor +@ToString(callSuper = true) +public class OwnSupplierSupply extends Supply { + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/ReportedCustomerSupply.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/ReportedCustomerSupply.java new file mode 100644 index 00000000..6f0b6dd6 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/ReportedCustomerSupply.java @@ -0,0 +1,35 @@ +/* + * 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.supply.domain.model; + + import jakarta.persistence.Entity; + import lombok.NoArgsConstructor; + import lombok.ToString; + import lombok.experimental.SuperBuilder; + + @Entity + @SuperBuilder + @NoArgsConstructor + @ToString(callSuper = true) + public class ReportedCustomerSupply extends Supply { + + } + \ No newline at end of file diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/ReportedSupplierSupply.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/ReportedSupplierSupply.java new file mode 100644 index 00000000..ff2583fa --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/ReportedSupplierSupply.java @@ -0,0 +1,34 @@ +/* + * 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.supply.domain.model; + +import jakarta.persistence.Entity; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +@Entity +@SuperBuilder +@NoArgsConstructor +@ToString(callSuper = true) +public class ReportedSupplierSupply extends Supply { + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/Supply.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/Supply.java new file mode 100644 index 00000000..8a106ea4 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/model/Supply.java @@ -0,0 +1,104 @@ +/* + * 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.supply.domain.model; + +import org.eclipse.tractusx.puris.backend.common.util.PatternStore; +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Material; +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner; + +import java.util.UUID; +import java.util.Date; +import java.util.Objects; + +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@SuperBuilder +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +@Entity +@ToString +public abstract class Supply { + // customer controller, supplier controller + // in dto flatten material and partner + @Id + @GeneratedValue + protected UUID uuid; + + @ManyToOne() + @JoinColumn(name = "partner_uuid") + @ToString.Exclude + @NotNull + protected Partner partner; + + @ManyToOne() + @JoinColumn(name = "material_ownMaterialNumber") + @ToString.Exclude + @NotNull + protected Material material; + + @Pattern(regexp = PatternStore.BPNA_STRING) + private String stockLocationBPNA; + + @Pattern(regexp = PatternStore.BPNS_STRING) + private String stockLocationBPNS; + + private Date date; + private double daysOfSupply; + + @ToString.Include + private String material_ownMaterialNumber() { + return material.getOwnMaterialNumber(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final Supply that = (Supply) o; + return this.getMaterial().getOwnMaterialNumber().equals(that.getMaterial().getOwnMaterialNumber()) && + this.getPartner().getUuid().equals(that.getPartner().getUuid()) && + this.getStockLocationBPNA().equals(that.getStockLocationBPNA()) && + this.getStockLocationBPNS().equals(that.getStockLocationBPNS()) && + this.getDate().equals(that.getDate()) && + this.getDaysOfSupply() == that.getDaysOfSupply(); + } + + @Override + public int hashCode() { + return Objects.hash(partner, material, stockLocationBPNA, stockLocationBPNS, date, daysOfSupply); + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/repository/ReportedCustomerSupplyRepository.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/repository/ReportedCustomerSupplyRepository.java new file mode 100644 index 00000000..4bade94e --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/repository/ReportedCustomerSupplyRepository.java @@ -0,0 +1,30 @@ +/* + * 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.supply.domain.repository; + +import java.util.UUID; + +import org.eclipse.tractusx.puris.backend.supply.domain.model.ReportedCustomerSupply; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReportedCustomerSupplyRepository extends JpaRepository { + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/repository/ReportedSupplierSupplyRepository.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/repository/ReportedSupplierSupplyRepository.java new file mode 100644 index 00000000..b8adca23 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/domain/repository/ReportedSupplierSupplyRepository.java @@ -0,0 +1,30 @@ +/* + * 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.supply.domain.repository; + +import java.util.UUID; + +import org.eclipse.tractusx.puris.backend.supply.domain.model.ReportedSupplierSupply; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReportedSupplierSupplyRepository extends JpaRepository { + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/logic/dto/SupplyDto.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/logic/dto/SupplyDto.java new file mode 100644 index 00000000..2a188af5 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/logic/dto/SupplyDto.java @@ -0,0 +1,38 @@ +/* + * 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.supply.logic.dto; + +import java.io.Serializable; +import java.util.Date; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@NoArgsConstructor +@ToString +public class SupplyDto implements Serializable { + private Date date; + private double daysOfSupply; +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/logic/service/CustomerSupplyService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/logic/service/CustomerSupplyService.java new file mode 100644 index 00000000..ddada0f8 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/logic/service/CustomerSupplyService.java @@ -0,0 +1,175 @@ +/* + * 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.supply.logic.service; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Stream; + +import org.eclipse.tractusx.puris.backend.delivery.logic.service.OwnDeliveryService; +import org.eclipse.tractusx.puris.backend.delivery.logic.service.ReportedDeliveryService; +import org.eclipse.tractusx.puris.backend.demand.logic.services.OwnDemandService; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialServiceImpl; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.PartnerService; +import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.DirectionCharacteristic; +import org.eclipse.tractusx.puris.backend.stock.logic.service.MaterialItemStockService; +import org.eclipse.tractusx.puris.backend.supply.domain.model.OwnCustomerSupply; +import org.eclipse.tractusx.puris.backend.supply.domain.model.ReportedCustomerSupply; +import org.eclipse.tractusx.puris.backend.supply.domain.repository.ReportedCustomerSupplyRepository; +import org.springframework.stereotype.Service; + +@Service +public class CustomerSupplyService { + private final ReportedCustomerSupplyRepository repository; + private final PartnerService partnerService; + private final OwnDeliveryService ownDeliveryService; + private final ReportedDeliveryService reportedDeliveryService; + private final OwnDemandService demandService; + private final MaterialItemStockService stockService; + private final MaterialServiceImpl materialService; + + protected final Function validator; + + public CustomerSupplyService( + ReportedCustomerSupplyRepository customerRepository, + PartnerService partnerService, + OwnDeliveryService ownDeliveryService, + ReportedDeliveryService reportedDeliveryService, + OwnDemandService demandService, + MaterialItemStockService stockService, + MaterialServiceImpl materialService) { + this.repository = customerRepository; + this.partnerService = partnerService; + this.ownDeliveryService = ownDeliveryService; + this.reportedDeliveryService = reportedDeliveryService; + this.demandService = demandService; + this.stockService = stockService; + this.materialService = materialService; + this.validator = this::validate; + } + + public final List calculateCustomerDaysOfSupply(String material, String partnerBpnl, String siteBpns, int numberOfDays) { + List customerSupply = new ArrayList<>(); + LocalDate localDate = LocalDate.now(); + + List demands = demandService.getQuantityForDays(material, partnerBpnl, siteBpns, numberOfDays); + + List ownDeliveries = ownDeliveryService.getQuantityForDays(material, partnerBpnl, siteBpns, DirectionCharacteristic.INBOUND, numberOfDays); + List reportedDeliveries = reportedDeliveryService.getQuantityForDays(material, partnerBpnl, siteBpns, DirectionCharacteristic.INBOUND, numberOfDays); + List deliveries = mergeDeliveries(ownDeliveries, reportedDeliveries); + + double stockQuantity = stockService.getInitialStockQuantity(material, partnerBpnl); + + for (int i = 0; i < numberOfDays; i++) { + Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + + if (i == numberOfDays - 1) { + stockQuantity += deliveries.get(i); + } + + double daysOfSupply = getDaysOfSupply( + stockQuantity, + demands.subList(i, demands.size())); + + OwnCustomerSupply supply = new OwnCustomerSupply(); + supply.setMaterial(materialService.findByOwnMaterialNumber(material)); + supply.setDate(date); + supply.setDaysOfSupply(daysOfSupply); + customerSupply.add(supply); + + stockQuantity = stockQuantity - demands.get(i) + deliveries.get(i); + + localDate = localDate.plusDays(1); + } + + return customerSupply; + } + + public final List findAll() { + return repository.findAll(); + } + + public final ReportedCustomerSupply findById(UUID id) { + return repository.findById(id).orElse(null); + } + + public final List findAllByFilters(Optional ownMaterialNumber, Optional bpnl) { + Stream stream = repository.findAll().stream(); + if (ownMaterialNumber.isPresent()) { + stream = stream.filter(dayOfSupply -> dayOfSupply.getMaterial().getOwnMaterialNumber().equals(ownMaterialNumber.get())); + } + if (bpnl.isPresent()) { + stream = stream.filter(dayOfSupply -> dayOfSupply.getPartner().getBpnl().equals(bpnl.get())); + } + return stream.toList(); + } + + public boolean validate(ReportedCustomerSupply daysOfSupply) { + return + daysOfSupply.getPartner() != null && + daysOfSupply.getMaterial() != null && + daysOfSupply.getDate() != null && + daysOfSupply.getStockLocationBPNS() != null && + daysOfSupply.getPartner() != partnerService.getOwnPartnerEntity() && + daysOfSupply.getPartner().getSites().stream().anyMatch(site -> site.getBpns().equals(daysOfSupply.getStockLocationBPNS())) && + (daysOfSupply.getStockLocationBPNA() == null || daysOfSupply.getStockLocationBPNA() == daysOfSupply.getStockLocationBPNS()); + } + + private final double getDaysOfSupply(double stockQuantity, List demands) { + double daysOfSupply = 0; + + for (int i = 0; i < demands.size(); i++) { + double demand = demands.get(i); + + if (stockQuantity >= demand) { + daysOfSupply += 1; + stockQuantity = stockQuantity - demand; + } else if (stockQuantity < demand && stockQuantity > 0) { + double fractional = stockQuantity / demand; + daysOfSupply = daysOfSupply + fractional; + break; + } else { + break; + } + } + return daysOfSupply; + } + + private static List mergeDeliveries(List list1, List list2) { + if (list1.size() != list2.size()) { + throw new IllegalArgumentException("Lists must be of the same length"); + } + + List mergedList = new ArrayList<>(list1.size()); + + for (int i = 0; i < list1.size(); i++) { + mergedList.add(list1.get(i) + list2.get(i)); + } + + return mergedList; + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/logic/service/SupplierSupplyService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/logic/service/SupplierSupplyService.java new file mode 100644 index 00000000..ee581c26 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/supply/logic/service/SupplierSupplyService.java @@ -0,0 +1,173 @@ +/* + * 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.supply.logic.service; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Stream; + +import org.eclipse.tractusx.puris.backend.delivery.logic.service.OwnDeliveryService; +import org.eclipse.tractusx.puris.backend.delivery.logic.service.ReportedDeliveryService; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialServiceImpl; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.PartnerService; +import org.eclipse.tractusx.puris.backend.production.logic.service.OwnProductionService; +import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.DirectionCharacteristic; +import org.eclipse.tractusx.puris.backend.stock.logic.service.MaterialItemStockService; +import org.eclipse.tractusx.puris.backend.supply.domain.model.OwnSupplierSupply; +import org.eclipse.tractusx.puris.backend.supply.domain.model.ReportedSupplierSupply; +import org.eclipse.tractusx.puris.backend.supply.domain.repository.ReportedSupplierSupplyRepository; +import org.springframework.stereotype.Service; + +@Service +public class SupplierSupplyService { + private final ReportedSupplierSupplyRepository repository; + private final PartnerService partnerService; + private final OwnDeliveryService ownDeliveryService; + private final ReportedDeliveryService reportedDeliveryService; + private final OwnProductionService productionService; + private final MaterialItemStockService stockService; + private final MaterialServiceImpl materialService; + + protected final Function validator; + + public SupplierSupplyService( + ReportedSupplierSupplyRepository repository, + PartnerService partnerService, + OwnDeliveryService ownDeliveryService, + ReportedDeliveryService reportedDeliveryService, + OwnProductionService productionService, + MaterialItemStockService stockService, + MaterialServiceImpl materialService) { + this.repository = repository; + this.partnerService = partnerService; + this.ownDeliveryService = ownDeliveryService; + this.reportedDeliveryService = reportedDeliveryService; + this.productionService = productionService; + this.stockService = stockService; + this.materialService = materialService; + this.validator = this::validate; + } + + public final List calculateSupplierDaysOfSupply(String material, String partnerBpnl, String siteBpns, int numberOfDays) { + List supplierSupply = new ArrayList<>(); + LocalDate localDate = LocalDate.now(); + + List ownDeliveries = ownDeliveryService.getQuantityForDays(material, partnerBpnl, siteBpns, DirectionCharacteristic.OUTBOUND, numberOfDays); + List reportedDeliveries = reportedDeliveryService.getQuantityForDays(material, partnerBpnl, siteBpns, DirectionCharacteristic.OUTBOUND, numberOfDays); + List deliveries = mergeDeliveries(ownDeliveries, reportedDeliveries); + List productions = productionService.getQuantityForDays(material, partnerBpnl, siteBpns, numberOfDays); + double stockQuantity = stockService.getInitialStockQuantity(material, partnerBpnl); + + for (int i = 0; i < numberOfDays; i++) { + Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + + if (i == numberOfDays - 1) { + stockQuantity += productions.get(i); + } + + double daysOfSupply = getDaysOfSupply( + stockQuantity, + deliveries.subList(i, deliveries.size())); + + OwnSupplierSupply supply = new OwnSupplierSupply(); + supply.setMaterial(materialService.findByOwnMaterialNumber(material)); + supply.setDate(date); + supply.setDaysOfSupply(daysOfSupply); + supplierSupply.add(supply); + + stockQuantity = stockQuantity - deliveries.get(i) + productions.get(i); + + localDate = localDate.plusDays(1); + } + + return supplierSupply; + } + + public final List findAll() { + return repository.findAll(); + } + + public final ReportedSupplierSupply findById(UUID id) { + return repository.findById(id).orElse(null); + } + + public final List findAllByFilters(Optional ownMaterialNumber, Optional bpnl) { + Stream stream = repository.findAll().stream(); + if (ownMaterialNumber.isPresent()) { + stream = stream.filter(dayOfSupply -> dayOfSupply.getMaterial().getOwnMaterialNumber().equals(ownMaterialNumber.get())); + } + if (bpnl.isPresent()) { + stream = stream.filter(dayOfSupply -> dayOfSupply.getPartner().getBpnl().equals(bpnl.get())); + } + return stream.toList(); + } + + public boolean validate(ReportedSupplierSupply daysOfSupply) { + return + daysOfSupply.getPartner() != null && + daysOfSupply.getMaterial() != null && + daysOfSupply.getDate() != null && + daysOfSupply.getStockLocationBPNS() != null && + daysOfSupply.getPartner() != partnerService.getOwnPartnerEntity() && + daysOfSupply.getPartner().getSites().stream().anyMatch(site -> site.getBpns().equals(daysOfSupply.getStockLocationBPNS())) && + (daysOfSupply.getStockLocationBPNA() == null || daysOfSupply.getStockLocationBPNA() == daysOfSupply.getStockLocationBPNS()); + } + + private final double getDaysOfSupply(double stockQuantity, List demands) { + double daysOfSupply = 0; + + for (int i = 0; i < demands.size(); i++) { + double demand = demands.get(i); + + if (stockQuantity >= demand) { + daysOfSupply += 1; + stockQuantity = stockQuantity - demand; + } else if (stockQuantity < demand && stockQuantity > 0) { + double fractional = stockQuantity / demand; + daysOfSupply = daysOfSupply + fractional; + break; + } else { + break; + } + } + return daysOfSupply; + } + + private static List mergeDeliveries(List list1, List list2) { + if (list1.size() != list2.size()) { + throw new IllegalArgumentException("Lists must be of the same length"); + } + + List mergedList = new ArrayList<>(list1.size()); + + for (int i = 0; i < list1.size(); i++) { + mergedList.add(list1.get(i) + list2.get(i)); + } + + return mergedList; + } +}