From 1be35b5a8dd157b3cc0e28c8f41573b8b59e529e Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 16 Dec 2025 22:16:03 +0100 Subject: [PATCH 01/86] Bugfix: shipping frequency custom calculation. Stacking in container calcualtion --- .../calculation/execution/CalculationExecutionService.java | 4 ++-- .../execution/steps/ContainerCalculationService.java | 4 ++-- .../execution/steps/CustomCostCalculationService.java | 4 ++-- .../execution/steps/RouteSectionCostCalculationService.java | 2 ++ 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/CalculationExecutionService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/CalculationExecutionService.java index b24a44b..cd85c23 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/CalculationExecutionService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/CalculationExecutionService.java @@ -142,7 +142,7 @@ public class CalculationExecutionService { BigDecimal leadTime = null; if (destination.getD2d()) { - var containerCalculation = containerCalculationService.doCalculation(setId, premiseToHuService.createHuFromPremise(premise), ContainerType.FEU, premise.getHuMixable()); + var containerCalculation = containerCalculationService.doCalculation(setId, premiseToHuService.createHuFromPremise(premise), ContainerType.FEU, premise.getHuMixable(), premise.getHuStackable()); sections = List.of(new SectionInfo(null, routeSectionCostCalculationService.doD2dCalculation(setId, periodId, premise, destination, containerCalculation), containerCalculation)); leadTime = BigDecimal.valueOf(destination.getLeadTimeD2d()); usedContainerType = ContainerType.FEU; @@ -246,7 +246,7 @@ public class CalculationExecutionService { // Get container calculation for (var containerType : ContainerType.values()) { - containerCalculation.put(containerType, containerCalculationService.doCalculation(setId, hu, containerType, premise.getHuMixable())); + containerCalculation.put(containerType, containerCalculationService.doCalculation(setId, hu, containerType, premise.getHuMixable(), premise.getHuStackable())); } for (var containerType : ContainerType.values()) { diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ContainerCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ContainerCalculationService.java index 9411e31..32da301 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ContainerCalculationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ContainerCalculationService.java @@ -49,7 +49,7 @@ public class ContainerCalculationService { * @param containerType The type of container to be loaded * @return ContainerCalculationResult containing loading pattern and capacity information */ - public ContainerCalculationResult doCalculation(Integer setId, PackagingDimension hu, ContainerType containerType, boolean mixable) { + public ContainerCalculationResult doCalculation(Integer setId, PackagingDimension hu, ContainerType containerType, boolean mixable, boolean stackable) { var weightInKg = BigDecimal.valueOf(WeightUnit.KG.convertFromG(hu.getWeight())); var maxContainerLoad = BigDecimal.valueOf(getMaxContainerLoad(containerType, setId)); @@ -60,7 +60,7 @@ public class ContainerCalculationService { var solutionHorizontal = solveLayer(SolutionType.HORIZONTAL, dimensions, containerType.getLength(), containerType.getWidth()); var solutionVertical = solveLayer(SolutionType.VERTICAL, dimensions, containerType.getWidth(), containerType.getLength()); var bestSolution = solutionHorizontal.getTotal() < solutionVertical.getTotal() ? solutionVertical : solutionHorizontal; - int layers = mixable ? getLayerCount(dimensions, containerType) : 1; + int layers = stackable ? getLayerCount(dimensions, containerType) : 1; if(PalletType.EURO_PALLET.fitsOn(hu) && bestSolution.getTotal() < containerType.getPalletCount(PalletType.EURO_PALLET)) { return new ContainerCalculationResult(Math.min(containerType.getPalletCount(PalletType.EURO_PALLET)*layers,maxUnitByWeight), layers, null, (containerType.getPalletCount(PalletType.EURO_PALLET)*layers) > maxUnitByWeight, containerType, dimensions, maxContainerLoad.intValueExact()); diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/CustomCostCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/CustomCostCalculationService.java index 55bfd61..2dd079c 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/CustomCostCalculationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/CustomCostCalculationService.java @@ -62,7 +62,7 @@ public class CustomCostCalculationService { var transportationRiskCost = relevantSections.stream().map(s -> s.result().getAnnualRiskCost()).reduce(BigDecimal.ZERO, BigDecimal::add); - double huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(relevantSections.getFirst().containerResult().getHuUnitCount()),2, RoundingMode.HALF_UP).doubleValue(); + double huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(premise.getHuUnitCount()),0, RoundingMode.CEILING).doubleValue(); return getCustomCalculationResult(setId, premise, destination, getContainerShare(premise, relevantSections.getFirst().containerResult()), huAnnualAmount, transportationCost, transportationChanceCost, transportationRiskCost); } @@ -87,7 +87,7 @@ public class CustomCostCalculationService { var customValue = materialCost.add(fcaFee).add(transportationCost); var customDuties = customValue.multiply(tariffRate); - var annualCustomFee = BigDecimal.valueOf(shippingFrequency).multiply(BigDecimal.valueOf(customFee)).multiply(containerShare); + var annualCustomFee = BigDecimal.valueOf(shippingFrequency).multiply(BigDecimal.valueOf(customFee)); var annualCost = customDuties.add(annualCustomFee); var customRiskValue = materialCost.add(fcaFee).add(transportationRiskCost); diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java index daf571a..cfe5b39 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java @@ -229,6 +229,8 @@ public class RouteSectionCostCalculationService { volumePrice = cbmRate.divide(totalVolumeUtilization, 10, RoundingMode.HALF_UP); weightPrice = weightRate.divide(totalWeightUtilization, 10, RoundingMode.HALF_UP); utilization = weightExceeded ? totalWeightUtilization : totalVolumeUtilization; + //TODO: wenn shippingfreq > annual hu -> shippingfreq * containerprice. + // gleiches für containercalculation * shippingfreq < annual hu ammount. } return new PriceCalculationResult(volumePrice, weightPrice, utilization); -- 2.45.3 From 9ac3cb7815761df6d02848b5e47798171795ceea Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 17 Dec 2025 09:42:19 +0100 Subject: [PATCH 02/86] Bugfix: if hu amount is less than min shipping frequency, fix total utilization accordingly --- .../steps/CustomCostCalculationService.java | 15 ++------ .../RouteSectionCostCalculationService.java | 34 +++++++++++++++---- .../ShippingFrequencyCalculationService.java | 8 ++--- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/CustomCostCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/CustomCostCalculationService.java index 2dd079c..1e2686d 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/CustomCostCalculationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/CustomCostCalculationService.java @@ -37,17 +37,6 @@ public class CustomCostCalculationService { this.shippingFrequencyCalculationService = shippingFrequencyCalculationService; } - private BigDecimal getContainerShare(Premise premise, ContainerCalculationResult containerCalculationResult) { - var weightExceeded = containerCalculationResult.isWeightExceeded(); - var mixable = premise.getHuMixable(); - - if (mixable) { - return BigDecimal.valueOf(weightExceeded ? containerCalculationResult.getHuUtilizationByWeight() : containerCalculationResult.getHuUtilizationByVolume()); - } else { - return BigDecimal.ONE.divide(BigDecimal.valueOf(containerCalculationResult.getHuUnitCount()), 10, RoundingMode.HALF_UP); - } - } - public CustomResult doCalculation(Integer setId, Premise premise, Destination destination, List sections) { var destUnion = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, setId, destination.getCountryId()).orElseThrow(); @@ -64,13 +53,13 @@ public class CustomCostCalculationService { double huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(premise.getHuUnitCount()),0, RoundingMode.CEILING).doubleValue(); - return getCustomCalculationResult(setId, premise, destination, getContainerShare(premise, relevantSections.getFirst().containerResult()), huAnnualAmount, transportationCost, transportationChanceCost, transportationRiskCost); + return getCustomCalculationResult(setId, premise, destination, huAnnualAmount, transportationCost, transportationChanceCost, transportationRiskCost); } return CustomResult.EMPTY; } - private CustomResult getCustomCalculationResult(Integer setId, Premise premise, Destination destination, BigDecimal containerShare, double huAnnualAmount, BigDecimal transportationCost, BigDecimal transportationChanceCost, BigDecimal transportationRiskCost) { + private CustomResult getCustomCalculationResult(Integer setId, Premise premise, Destination destination, double huAnnualAmount, BigDecimal transportationCost, BigDecimal transportationChanceCost, BigDecimal transportationRiskCost) { var shippingFrequency = shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount); var customFee = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.CUSTOM_FEE, setId).orElseThrow().getCurrentValue()); diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java index cfe5b39..db414e9 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java @@ -40,8 +40,9 @@ public class RouteSectionCostCalculationService { private final ChangeRiskFactorCalculationService changeRiskFactorCalculationService; private final NodeRepository nodeRepository; private final UserNodeRepository userNodeRepository; + private final ShippingFrequencyCalculationService shippingFrequencyCalculationService; - public RouteSectionCostCalculationService(ContainerRateRepository containerRateRepository, MatrixRateRepository matrixRateRepository, RouteNodeRepository routeNodeRepository, DistanceService distanceService, PropertyRepository propertyRepository, ChangeRiskFactorCalculationService changeRiskFactorCalculationService, NodeRepository nodeRepository, UserNodeRepository userNodeRepository) { + public RouteSectionCostCalculationService(ContainerRateRepository containerRateRepository, MatrixRateRepository matrixRateRepository, RouteNodeRepository routeNodeRepository, DistanceService distanceService, PropertyRepository propertyRepository, ChangeRiskFactorCalculationService changeRiskFactorCalculationService, NodeRepository nodeRepository, UserNodeRepository userNodeRepository, ShippingFrequencyCalculationService shippingFrequencyCalculationService) { this.containerRateRepository = containerRateRepository; this.matrixRateRepository = matrixRateRepository; this.routeNodeRepository = routeNodeRepository; @@ -50,6 +51,7 @@ public class RouteSectionCostCalculationService { this.changeRiskFactorCalculationService = changeRiskFactorCalculationService; this.nodeRepository = nodeRepository; this.userNodeRepository = userNodeRepository; + this.shippingFrequencyCalculationService = shippingFrequencyCalculationService; } public CalculationJobRouteSection doD2dCalculation(Integer setId, Integer periodId, Premise premise, Destination destination, ContainerCalculationResult containerCalculation) { @@ -94,7 +96,10 @@ public class RouteSectionCostCalculationService { containerCalculation.getMaxContainerWeight(), BigDecimal.valueOf(containerCalculation.getTotalUtilizationByVolume()), BigDecimal.valueOf(containerCalculation.getHuUtilizationByWeight()), - utilization); + utilization, + shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount.doubleValue()), + huAnnualAmount.doubleValue(), + containerCalculation); result.setCbmPrice(!containerCalculation.isWeightExceeded()); result.setWeightPrice(containerCalculation.isWeightExceeded()); @@ -177,7 +182,10 @@ public class RouteSectionCostCalculationService { containerCalculation.getMaxContainerWeight(), BigDecimal.valueOf(containerCalculation.getTotalUtilizationByVolume()), BigDecimal.valueOf(containerCalculation.getTotalUtilizationByWeight()), - utilization); + utilization, + shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount.doubleValue()), + huAnnualAmount.doubleValue(), + containerCalculation); result.setCbmPrice(!containerCalculation.isWeightExceeded()); result.setWeightPrice(containerCalculation.isWeightExceeded()); @@ -211,7 +219,11 @@ public class RouteSectionCostCalculationService { int maxContainerWeight, BigDecimal totalVolumeUtilization, BigDecimal totalWeightUtilization, - BigDecimal propertyUtilization) { + BigDecimal propertyUtilization, + double shippingFrequency, + double annualHuAmount, + ContainerCalculationResult containerCalculationResult + ) { BigDecimal utilization; @@ -221,16 +233,26 @@ public class RouteSectionCostCalculationService { BigDecimal cbmRate = rate.divide(BigDecimal.valueOf(containerType.getVolume()), 10, RoundingMode.HALF_UP); BigDecimal weightRate = rate.divide(BigDecimal.valueOf(maxContainerWeight), 10, RoundingMode.HALF_UP); + if (huMixable) { volumePrice = cbmRate.divide(propertyUtilization, 10, RoundingMode.HALF_UP); weightPrice = weightRate.divide(BigDecimal.valueOf(1), 10, RoundingMode.HALF_UP); utilization = weightExceeded ? BigDecimal.ONE : propertyUtilization; } else { + + double huPerContainer = annualHuAmount / shippingFrequency; + + // if the shipping frequency is bigger than the annual amount the "totalXXUtilization" cannot be used. + if(huPerContainer < (containerCalculationResult.getHuUnitCount() * containerCalculationResult.getLayer())) { + + totalVolumeUtilization = BigDecimal.valueOf(huPerContainer * containerCalculationResult.getHu().getVolume(DimensionUnit.M)).divide(BigDecimal.valueOf(containerCalculationResult.getContainerType().getVolume()), 20, RoundingMode.HALF_UP); + totalWeightUtilization = BigDecimal.valueOf(huPerContainer * containerCalculationResult.getHu().getWeight(WeightUnit.KG)).divide(BigDecimal.valueOf(containerCalculationResult.getMaxContainerWeight()), 20, RoundingMode.HALF_UP); + } + volumePrice = cbmRate.divide(totalVolumeUtilization, 10, RoundingMode.HALF_UP); weightPrice = weightRate.divide(totalWeightUtilization, 10, RoundingMode.HALF_UP); utilization = weightExceeded ? totalWeightUtilization : totalVolumeUtilization; - //TODO: wenn shippingfreq > annual hu -> shippingfreq * containerprice. - // gleiches für containercalculation * shippingfreq < annual hu ammount. + } return new PriceCalculationResult(volumePrice, weightPrice, utilization); diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ShippingFrequencyCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ShippingFrequencyCalculationService.java index 04687f4..aa7213a 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ShippingFrequencyCalculationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ShippingFrequencyCalculationService.java @@ -25,13 +25,13 @@ public class ShippingFrequencyCalculationService { } public double doCalculation(Integer setId, double huAnnualAmount) { - Integer minAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MIN, setId).orElseThrow().getCurrentValue()); - Integer maxAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MAX, setId).orElseThrow().getCurrentValue()); + int minAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MIN, setId).orElseThrow().getCurrentValue()); + int maxAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MAX, setId).orElseThrow().getCurrentValue()); - if (huAnnualAmount > maxAnnualFrequency.doubleValue()) + if (huAnnualAmount > (double) maxAnnualFrequency) return maxAnnualFrequency; - return Math.max(huAnnualAmount, minAnnualFrequency.doubleValue()); + return Math.max(huAnnualAmount, (double) minAnnualFrequency); } -- 2.45.3 From 1788a7ef1c8a011d43a1bf59dc95703ef3788714 Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 17 Dec 2025 14:29:08 +0100 Subject: [PATCH 03/86] Refactor: Pass `ContainerCalculationResult` into cost calculation services and update `ShippingFrequencyCalculationService` logic to consider `HU per container` handling. --- .../configuration/apps/AppExchangeDTO.java | 4 ++++ .../ContainerCalculationResult.java | 5 ++++- .../CalculationExecutionService.java | 20 +++++++++++-------- .../steps/CustomCostCalculationService.java | 9 +++++---- .../steps/HandlingCostCalculationService.java | 11 +++++----- .../InventoryCostCalculationService.java | 5 +++-- .../RouteSectionCostCalculationService.java | 6 +++--- .../ShippingFrequencyCalculationService.java | 15 ++++++++++++-- 8 files changed, 50 insertions(+), 25 deletions(-) create mode 100644 src/main/java/de/avatic/lcc/dto/configuration/apps/AppExchangeDTO.java diff --git a/src/main/java/de/avatic/lcc/dto/configuration/apps/AppExchangeDTO.java b/src/main/java/de/avatic/lcc/dto/configuration/apps/AppExchangeDTO.java new file mode 100644 index 0000000..1ce2641 --- /dev/null +++ b/src/main/java/de/avatic/lcc/dto/configuration/apps/AppExchangeDTO.java @@ -0,0 +1,4 @@ +package de.avatic.lcc.dto.configuration.apps; + +public class AppExchangeDTO { +} diff --git a/src/main/java/de/avatic/lcc/model/calculation/ContainerCalculationResult.java b/src/main/java/de/avatic/lcc/model/calculation/ContainerCalculationResult.java index c2e69ca..cd879c9 100644 --- a/src/main/java/de/avatic/lcc/model/calculation/ContainerCalculationResult.java +++ b/src/main/java/de/avatic/lcc/model/calculation/ContainerCalculationResult.java @@ -215,7 +215,7 @@ public class ContainerCalculationResult { * @return The total utilization value for the container. */ public double getTotalUtilizationByVolume() { - return getHuUtilizationByVolume() * huUnitCount * layer; + return getHuUtilizationByVolume() * huUnitCount; } /** @@ -254,4 +254,7 @@ public class ContainerCalculationResult { return WeightUnit.KG.convertFromG(hu.getWeight()) / maxContainerWeight; } + public int getHuPerContainer() { + return this.huUnitCount; + } } diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/CalculationExecutionService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/CalculationExecutionService.java index cd85c23..dfad5a0 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/CalculationExecutionService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/CalculationExecutionService.java @@ -137,13 +137,15 @@ public class CalculationExecutionService { AirfreightResult airfreightCost = airfreightCalculationService.doCalculation(setId, periodId, premise, destination); ContainerType usedContainerType = null; + ContainerCalculationResult selectedContainerCalculation = null; + CalculationJobDestination destinationCalculationJob = new CalculationJobDestination(); boolean hasMainRun = true; BigDecimal leadTime = null; if (destination.getD2d()) { - var containerCalculation = containerCalculationService.doCalculation(setId, premiseToHuService.createHuFromPremise(premise), ContainerType.FEU, premise.getHuMixable(), premise.getHuStackable()); - sections = List.of(new SectionInfo(null, routeSectionCostCalculationService.doD2dCalculation(setId, periodId, premise, destination, containerCalculation), containerCalculation)); + selectedContainerCalculation = containerCalculationService.doCalculation(setId, premiseToHuService.createHuFromPremise(premise), ContainerType.FEU, premise.getHuMixable(), premise.getHuStackable()); + sections = List.of(new SectionInfo(null, routeSectionCostCalculationService.doD2dCalculation(setId, periodId, premise, destination, selectedContainerCalculation), selectedContainerCalculation)); leadTime = BigDecimal.valueOf(destination.getLeadTimeD2d()); usedContainerType = ContainerType.FEU; } else { @@ -161,6 +163,8 @@ public class CalculationExecutionService { s.result().setPostRun(false); }); } + + selectedContainerCalculation = bestContainerTypeResult.selectedContainerCalculation; } destinationCalculationJob.setD2D(destination.getD2d()); @@ -168,9 +172,9 @@ public class CalculationExecutionService { if(destination.getD2d()) destinationCalculationJob.setRateD2D(destination.getRateD2d()); - customCost = customCostCalculationService.doCalculation(setId, premise, destination, sections); - handlingCost = handlingCostCalculationService.doCalculation(setId, premise, destination, hasMainRun); - inventoryCost = inventoryCostCalculationService.doCalculation(setId, premise, destination, leadTime); + customCost = customCostCalculationService.doCalculation(setId, premise, destination, sections, selectedContainerCalculation); + handlingCost = handlingCostCalculationService.doCalculation(setId, premise, destination, hasMainRun, selectedContainerCalculation); + inventoryCost = inventoryCostCalculationService.doCalculation(setId, premise, destination, leadTime, selectedContainerCalculation); destinationCalculationJob.setContainerType(usedContainerType); @@ -209,7 +213,7 @@ public class CalculationExecutionService { destinationCalculationJob.setHuCount(sections.getFirst().containerResult().getHuUnitCount()); destinationCalculationJob.setAnnualAmount(BigDecimal.valueOf(destination.getAnnualAmount())); - destinationCalculationJob.setShippingFrequency(shippingFrequencyCalculationService.doCalculation(setId, destination.getAnnualAmount())); + destinationCalculationJob.setShippingFrequency(shippingFrequencyCalculationService.doCalculation(setId, destination.getAnnualAmount(), selectedContainerCalculation.getHuPerContainer(),!premise.getHuMixable())); var commonCost = destinationCalculationJob.getAnnualHandlingCost() .add(destinationCalculationJob.getAnnualDisposalCost()) @@ -264,10 +268,10 @@ public class CalculationExecutionService { } var bestContainerType = getBestContainerType(sectionInfos); - return new BestContainerTypeResult(bestContainerType, sectionInfos.get(bestContainerType)); + return new BestContainerTypeResult(bestContainerType, sectionInfos.get(bestContainerType), containerCalculation.get(bestContainerType)); } - private record BestContainerTypeResult(ContainerType containerType, List sections) { + private record BestContainerTypeResult(ContainerType containerType, List sections, ContainerCalculationResult selectedContainerCalculation) { } } diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/CustomCostCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/CustomCostCalculationService.java index 1e2686d..2b34abf 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/CustomCostCalculationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/CustomCostCalculationService.java @@ -37,7 +37,7 @@ public class CustomCostCalculationService { this.shippingFrequencyCalculationService = shippingFrequencyCalculationService; } - public CustomResult doCalculation(Integer setId, Premise premise, Destination destination, List sections) { + public CustomResult doCalculation(Integer setId, Premise premise, Destination destination, List sections, ContainerCalculationResult containerCalculationResult) { var destUnion = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, setId, destination.getCountryId()).orElseThrow(); var sourceUnion = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, setId, premise.getCountryId()).orElseThrow(); @@ -53,14 +53,15 @@ public class CustomCostCalculationService { double huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(premise.getHuUnitCount()),0, RoundingMode.CEILING).doubleValue(); - return getCustomCalculationResult(setId, premise, destination, huAnnualAmount, transportationCost, transportationChanceCost, transportationRiskCost); + return getCustomCalculationResult(setId, premise, destination, huAnnualAmount, transportationCost, transportationChanceCost, transportationRiskCost, containerCalculationResult); } return CustomResult.EMPTY; } - private CustomResult getCustomCalculationResult(Integer setId, Premise premise, Destination destination, double huAnnualAmount, BigDecimal transportationCost, BigDecimal transportationChanceCost, BigDecimal transportationRiskCost) { - var shippingFrequency = shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount); + private CustomResult getCustomCalculationResult(Integer setId, Premise premise, Destination destination, double huAnnualAmount, BigDecimal transportationCost, BigDecimal transportationChanceCost, BigDecimal transportationRiskCost, ContainerCalculationResult containerCalculationResult) { + + var shippingFrequency = shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount, containerCalculationResult.getHuPerContainer(), !premise.getHuMixable()); var customFee = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.CUSTOM_FEE, setId).orElseThrow().getCurrentValue()); var tariffRate = premise.getTariffRate(); diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/HandlingCostCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/HandlingCostCalculationService.java index d172f77..7c22837 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/HandlingCostCalculationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/HandlingCostCalculationService.java @@ -1,5 +1,6 @@ package de.avatic.lcc.service.calculation.execution.steps; +import de.avatic.lcc.model.calculation.ContainerCalculationResult; import de.avatic.lcc.model.calculation.HandlingResult; import de.avatic.lcc.model.db.packaging.LoadCarrierType; import de.avatic.lcc.model.db.packaging.PackagingDimension; @@ -30,13 +31,13 @@ public class HandlingCostCalculationService { this.shippingFrequencyCalculationService = shippingFrequencyCalculationService; } - public HandlingResult doCalculation(Integer setId, Premise premise, Destination destination, Boolean addRepackingAndDisposalCost) { + public HandlingResult doCalculation(Integer setId, Premise premise, Destination destination, Boolean addRepackingAndDisposalCost, ContainerCalculationResult containerCalculationResult) { var hu = premiseToHuService.createHuFromPremise(premise); - return (LoadCarrierType.SLC == hu.getLoadCarrierType() ? getSLCCost(setId, destination, hu, hu.getLoadCarrierType(), addRepackingAndDisposalCost) : getLLCCost(setId, destination, hu, hu.getLoadCarrierType(), addRepackingAndDisposalCost)); + return (LoadCarrierType.SLC == hu.getLoadCarrierType() ? getSLCCost(setId, premise, destination, hu, hu.getLoadCarrierType(), addRepackingAndDisposalCost, containerCalculationResult) : getLLCCost(setId, premise, destination, hu, hu.getLoadCarrierType(), addRepackingAndDisposalCost, containerCalculationResult)); } - private HandlingResult getSLCCost(Integer setId, Destination destination, PackagingDimension hu, LoadCarrierType loadCarrierType, boolean addRepackingAndDisposalCost) { + private HandlingResult getSLCCost(Integer setId, Premise premise, Destination destination, PackagingDimension hu, LoadCarrierType loadCarrierType, boolean addRepackingAndDisposalCost, ContainerCalculationResult containerCalculationResult) { var destinationHandling = destination.getHandlingCost(); var destinationDisposal = destination.getDisposalCost(); @@ -77,7 +78,7 @@ public class HandlingCostCalculationService { } - private HandlingResult getLLCCost(Integer setId, Destination destination, PackagingDimension hu, LoadCarrierType type, boolean addRepackingAndDisposalCost) { + private HandlingResult getLLCCost(Integer setId, Premise premise, Destination destination, PackagingDimension hu, LoadCarrierType type, boolean addRepackingAndDisposalCost, ContainerCalculationResult containerCalculationResult) { var destinationHandling = destination.getHandlingCost(); var destinationDisposal = destination.getDisposalCost(); @@ -93,7 +94,7 @@ public class HandlingCostCalculationService { BigDecimal booking = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING, setId).orElseThrow().getCurrentValue())); var annualRepacking = getRepackingCost(setId, hu, type, addRepackingAndDisposalCost, destinationRepacking).multiply(wageFactor).multiply( huAnnualAmount); - var annualHandling = ((handling.add(dispatch).add(release)).multiply(wageFactor).multiply(huAnnualAmount)).add(booking.multiply(BigDecimal.valueOf(shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount.doubleValue())))); + var annualHandling = ((handling.add(dispatch).add(release)).multiply(wageFactor).multiply(huAnnualAmount)).add(booking.multiply(BigDecimal.valueOf(shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount.doubleValue(), containerCalculationResult.getHuPerContainer(), !premise.getHuMixable())))); var annualDisposal = (disposal.multiply(huAnnualAmount)); return new HandlingResult(LoadCarrierType.LLC, annualRepacking, annualHandling, annualDisposal, annualRepacking.add(annualHandling).add(annualDisposal)); diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/InventoryCostCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/InventoryCostCalculationService.java index d9b0658..dafb733 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/InventoryCostCalculationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/InventoryCostCalculationService.java @@ -1,5 +1,6 @@ package de.avatic.lcc.service.calculation.execution.steps; +import de.avatic.lcc.model.calculation.ContainerCalculationResult; import de.avatic.lcc.model.calculation.InventoryCostResult; import de.avatic.lcc.model.db.packaging.PackagingDimension; import de.avatic.lcc.model.db.premises.Premise; @@ -30,7 +31,7 @@ public class InventoryCostCalculationService { this.premiseToHuService = premiseToHuService; } - public InventoryCostResult doCalculation(Integer setId, Premise premise, Destination destination, BigDecimal leadTime) { + public InventoryCostResult doCalculation(Integer setId, Premise premise, Destination destination, BigDecimal leadTime, ContainerCalculationResult containerCalculationResult) { var fcaFee = BigDecimal.ZERO; @@ -53,7 +54,7 @@ public class InventoryCostCalculationService { var dailyAmount = annualAmount.divide(BigDecimal.valueOf(365), 10, RoundingMode.HALF_UP); var workdayAmount = annualAmount.divide(workdays, 10, RoundingMode.HALF_UP); - var opStock = (annualAmount.divide(BigDecimal.valueOf(Math.max(shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount),1)), 10, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(.5))); + var opStock = (annualAmount.divide(BigDecimal.valueOf(Math.max(shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount, containerCalculationResult.getHuPerContainer(), !premise.getHuMixable()),1)), 10, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(.5))); var safetyStock = safetyDays.multiply(workdayAmount); var stockedInventory = opStock.add(safetyStock); var inTransportStock = dailyAmount.multiply(leadTime); diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java index db414e9..6139420 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java @@ -97,7 +97,7 @@ public class RouteSectionCostCalculationService { BigDecimal.valueOf(containerCalculation.getTotalUtilizationByVolume()), BigDecimal.valueOf(containerCalculation.getHuUtilizationByWeight()), utilization, - shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount.doubleValue()), + shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount.doubleValue(), containerCalculation.getHuPerContainer(), !premise.getHuMixable()), huAnnualAmount.doubleValue(), containerCalculation); @@ -183,7 +183,7 @@ public class RouteSectionCostCalculationService { BigDecimal.valueOf(containerCalculation.getTotalUtilizationByVolume()), BigDecimal.valueOf(containerCalculation.getTotalUtilizationByWeight()), utilization, - shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount.doubleValue()), + shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount.doubleValue(), containerCalculation.getHuPerContainer(), !premise.getHuMixable()), huAnnualAmount.doubleValue(), containerCalculation); @@ -243,7 +243,7 @@ public class RouteSectionCostCalculationService { double huPerContainer = annualHuAmount / shippingFrequency; // if the shipping frequency is bigger than the annual amount the "totalXXUtilization" cannot be used. - if(huPerContainer < (containerCalculationResult.getHuUnitCount() * containerCalculationResult.getLayer())) { + if(huPerContainer < (containerCalculationResult.getHuPerContainer())) { totalVolumeUtilization = BigDecimal.valueOf(huPerContainer * containerCalculationResult.getHu().getVolume(DimensionUnit.M)).divide(BigDecimal.valueOf(containerCalculationResult.getContainerType().getVolume()), 20, RoundingMode.HALF_UP); totalWeightUtilization = BigDecimal.valueOf(huPerContainer * containerCalculationResult.getHu().getWeight(WeightUnit.KG)).divide(BigDecimal.valueOf(containerCalculationResult.getMaxContainerWeight()), 20, RoundingMode.HALF_UP); diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ShippingFrequencyCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ShippingFrequencyCalculationService.java index aa7213a..969e838 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ShippingFrequencyCalculationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ShippingFrequencyCalculationService.java @@ -13,10 +13,16 @@ public class ShippingFrequencyCalculationService { this.propertyRepository = propertyRepository; } - public int doCalculation(Integer setId, int huAnnualAmount) { + public int doCalculation(Integer setId, int huAnnualAmount, int maxHuPerContainer, boolean fillContainer) { var minAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MIN, setId).orElseThrow().getCurrentValue()); var maxAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MAX, setId).orElseThrow().getCurrentValue()); + var fullContainers = huAnnualAmount / maxHuPerContainer; + + + if(fillContainer && huAnnualAmount > maxAnnualFrequency) + return fullContainers; + if (huAnnualAmount > maxAnnualFrequency) return maxAnnualFrequency; @@ -24,10 +30,15 @@ public class ShippingFrequencyCalculationService { } - public double doCalculation(Integer setId, double huAnnualAmount) { + public double doCalculation(Integer setId, double huAnnualAmount, int maxHuPerContainer, boolean fillContainer) { int minAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MIN, setId).orElseThrow().getCurrentValue()); int maxAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MAX, setId).orElseThrow().getCurrentValue()); + var fullContainers = huAnnualAmount / maxHuPerContainer; + + if(fillContainer && huAnnualAmount > (double) maxAnnualFrequency) + return fullContainers; + if (huAnnualAmount > (double) maxAnnualFrequency) return maxAnnualFrequency; -- 2.45.3 From 6add528c02f89b8c020235cf98e9a82acc7de891 Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 17 Dec 2025 16:06:59 +0100 Subject: [PATCH 04/86] Add import/export functionality for apps, including client-side file handling and backend encryption/decryption logic --- .../src/components/UI/AppListItem.vue | 26 +++- .../src/components/layout/config/Apps.vue | 83 ++++++++++- src/frontend/src/store/apps.js | 12 ++ .../configuration/AppsController.java | 13 ++ .../configuration/apps/AppExchangeDTO.java | 10 ++ .../avatic/lcc/service/apps/AppsService.java | 134 +++++++++++++++++- .../transformer/apps/AppTransformer.java | 23 ++- 7 files changed, 287 insertions(+), 14 deletions(-) diff --git a/src/frontend/src/components/UI/AppListItem.vue b/src/frontend/src/components/UI/AppListItem.vue index fc022a2..eb41c46 100644 --- a/src/frontend/src/components/UI/AppListItem.vue +++ b/src/frontend/src/components/UI/AppListItem.vue @@ -1,10 +1,20 @@