diff --git a/src/main/java/de/avatic/lcc/repositories/country/CountryPropertyRepository.java b/src/main/java/de/avatic/lcc/repositories/country/CountryPropertyRepository.java index 4e9fe97..ec3cfcd 100644 --- a/src/main/java/de/avatic/lcc/repositories/country/CountryPropertyRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/country/CountryPropertyRepository.java @@ -54,10 +54,16 @@ public class CountryPropertyRepository { throw new DatabaseException("Could not update property value for country " + countryId + " and property type " + mappingId); } + @Transactional public Optional getByMappingIdAndCountryId(CountryPropertyMappingId mappingId, Integer countryId) { return getByMappingIdAndCountryId(mappingId.name(), countryId); } + @Transactional + public Optional getByMappingIdAndCountryId(CountryPropertyMappingId mappingId, Integer setId, Integer countryId) { + return getByMappingIdAndCountryId(mappingId.name(), setId, countryId); + } + public Optional getByMappingIdAndCountryId(String mappingId, Integer countryId) { String query = """ SELECT type.name as name, @@ -74,14 +80,14 @@ public class CountryPropertyRepository { FROM country_property_type AS type LEFT JOIN ( SELECT cp.property_value, cp.country_property_type_id - FROM country_property cp - JOIN property_set ps ON ps.id = cp.property_set_id + FROM country_property cp + JOIN property_set ps ON ps.id = cp.property_set_id WHERE cp.country_id = ? AND ps.state = 'DRAFT' ) AS draft ON draft.country_property_type_id = type.id LEFT JOIN ( SELECT cp.property_value, cp.country_property_type_id - FROM country_property cp - JOIN property_set ps ON ps.id = cp.property_set_id + FROM country_property cp + JOIN property_set ps ON ps.id = cp.property_set_id WHERE cp.country_id = ? AND ps.state = 'VALID' ) AS valid ON valid.country_property_type_id = type.id WHERE type.external_mapping_id = ? @@ -95,6 +101,36 @@ public class CountryPropertyRepository { return Optional.of(property.getFirst()); } + public Optional getByMappingIdAndCountryId(String mappingId, Integer setId, Integer countryId) { + String query = """ + SELECT type.name as name, + type.data_type as dataType, + type.external_mapping_id as externalMappingId, + type.validation_rule as validationRule, + type.is_required as is_required, + type.description as description, + type.property_group as propertyGroup, + type.sequence_number as sequenceNumber, + valid.property_value as validValue + + FROM country_property_type AS type + LEFT JOIN ( + SELECT cp.property_value, cp.country_property_type_id + FROM country_property cp + JOIN property_set ps ON ps.id = cp.property_set_id + WHERE cp.country_id = ? AND ps.id = ? + ) AS valid ON valid.country_property_type_id = type.id + WHERE type.external_mapping_id = ? + """; + + var property = jdbcTemplate.query(query, new PropertyMapper(), countryId, countryId, setId, mappingId); + + if (property.isEmpty()) + return Optional.empty(); + + return Optional.of(property.getFirst()); + } + private Integer getTypeIdByMappingId(String mappingId) { String query = "SELECT id FROM country_property_type WHERE external_mapping_id = ?"; return jdbcTemplate.queryForObject(query, Integer.class, mappingId); diff --git a/src/main/java/de/avatic/lcc/repositories/properties/PropertyRepository.java b/src/main/java/de/avatic/lcc/repositories/properties/PropertyRepository.java index 91d4717..7211c8b 100644 --- a/src/main/java/de/avatic/lcc/repositories/properties/PropertyRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/properties/PropertyRepository.java @@ -146,6 +146,25 @@ public class PropertyRepository { } + @Transactional + public Optional getPropertyByMappingId(SystemPropertyMappingId mappingId, Integer setId) { + String query = """ + SELECT type.name as name, type.data_type as dataType, type.external_mapping_id as externalMappingId, type.validation_rule as validationRule, + type.description as description, type.property_group as propertyGroup, type.sequence_number as sequenceNumber, + property.property_value as draftValue, property.property_value as validValue + FROM system_property_type AS type + LEFT JOIN system_property AS property ON property.system_property_type_id = type.id + LEFT JOIN property_set AS propertySet ON propertySet.id = property.property_set_id + WHERE propertySet.id = ? AND type.external_mapping_id = ? + """; + + var property = jdbcTemplate.query(query, new PropertyMapper(), setId, mappingId.name()); + + if (property.isEmpty()) return Optional.empty(); + else return Optional.of(property.getFirst()); + + } + /** * Fills the draft property set with values from a valid property set. * This method ensures that the draft set contains all properties with their latest valid values. diff --git a/src/main/java/de/avatic/lcc/repositories/rates/ContainerRateRepository.java b/src/main/java/de/avatic/lcc/repositories/rates/ContainerRateRepository.java index 9c38f2c..22ed6f0 100644 --- a/src/main/java/de/avatic/lcc/repositories/rates/ContainerRateRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/rates/ContainerRateRepository.java @@ -171,12 +171,39 @@ public class ContainerRateRepository { return jdbcTemplate.query(query, new ContainerRateMapper(true), ValidityPeriodState.VALID.name(), mainRun.getToNodeId(), TransportType.POST_RUN.name()); } - public Optional findRoute(Integer fromNodeId, Integer toNodeId, TransportType type) { + + @Transactional + public Optional findRoute(Integer fromNodeId, Integer toNodeId, Integer periodId, TransportType type) { String query = """ - SELECT * FROM container_rate WHERE from_node_id = ? AND to_node_id = ? AND container_rate_type = ? + SELECT * FROM container_rate WHERE from_node_id = ? AND to_node_id = ? AND container_rate_type = ? AND validity_period_id = ? """; - var route = jdbcTemplate.query(query, new ContainerRateMapper(), fromNodeId, toNodeId, type.name()); + var route = jdbcTemplate.query(query, new ContainerRateMapper(), fromNodeId, toNodeId, type.name(), periodId); + + if(route.isEmpty()) + return Optional.empty(); + + return Optional.of(route.getFirst()); + } + + @Transactional + public Optional findRoute(Integer fromNodeId, Integer toNodeId, TransportType type) { + + String query = """ + SELECT cr.id AS id, + cr.validity_period_id AS validity_period_id, + cr.container_rate_type AS container_rate_type, + cr.from_node_id AS from_node_id, + cr.to_node_id AS to_node_id, + cr.rate_feu AS rate_feu, + cr.rate_teu AS rate_teu, + cr.rate_hc AS rate_hc, + cr.lead_time AS lead_time + FROM container_rate AS cr LEFT JOIN validity_period AS vp ON cr.validity_period_id = vp.id + WHERE cr.from_node_id = ? AND cr.to_node_id = ? AND cr.container_rate_type = ? AND vp.state = ? + """; + + var route = jdbcTemplate.query(query, new ContainerRateMapper(), fromNodeId, toNodeId, type.name(), ValidityPeriodState.VALID.name()); if(route.isEmpty()) return Optional.empty(); diff --git a/src/main/java/de/avatic/lcc/repositories/rates/MatrixRateRepository.java b/src/main/java/de/avatic/lcc/repositories/rates/MatrixRateRepository.java index e8380c4..3afa5b3 100644 --- a/src/main/java/de/avatic/lcc/repositories/rates/MatrixRateRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/rates/MatrixRateRepository.java @@ -1,6 +1,7 @@ package de.avatic.lcc.repositories.rates; import de.avatic.lcc.model.rates.MatrixRate; +import de.avatic.lcc.model.rates.ValidityPeriodState; import de.avatic.lcc.repositories.pagination.SearchQueryPagination; import de.avatic.lcc.repositories.pagination.SearchQueryResult; import org.springframework.jdbc.core.JdbcTemplate; @@ -125,8 +126,35 @@ public class MatrixRateRepository { @Transactional public Optional getByCountryIds(Integer fromCountryId, Integer toCountryId) { - String query = "SELECT * FROM country_matrix_rate WHERE from_country_id = ? AND to_country_id = ?"; - var rates = jdbcTemplate.query(query, new MatrixRateMapper(), fromCountryId, toCountryId); + + String query = """ + SELECT cmr.id AS id, + cmr.rate AS rate, + cmr.from_country_id AS from_country_id, + cmr.to_country_id AS to_country_id, + cmr.validity_period_id AS validity_period_id + FROM country_matrix_rate AS cmr LEFT JOIN validity_period AS vp ON vp.id = cmr.validity_period_id + WHERE cmr.from_country_id = ? AND cmr.to_country_id = ? AND vp.state = ? + """; + + var rates = jdbcTemplate.query(query, new MatrixRateMapper(), fromCountryId, toCountryId, ValidityPeriodState.VALID.name()); + + if(rates.isEmpty()) + return Optional.empty(); + else + return Optional.of(rates.getFirst()); + } + + @Transactional + public Optional getByCountryIds(Integer fromCountryId, Integer toCountryId, Integer periodId) { + + String query = """ + SELECT * + FROM country_matrix_rate + WHERE from_country_id = ? AND to_country_id = ? AND validity_period_id = ? + """; + + var rates = jdbcTemplate.query(query, new MatrixRateMapper(), fromCountryId, toCountryId, periodId); if(rates.isEmpty()) return Optional.empty(); diff --git a/src/main/java/de/avatic/lcc/service/access/PremisesService.java b/src/main/java/de/avatic/lcc/service/access/PremisesService.java index 09e5507..cbdcf08 100644 --- a/src/main/java/de/avatic/lcc/service/access/PremisesService.java +++ b/src/main/java/de/avatic/lcc/service/access/PremisesService.java @@ -100,14 +100,13 @@ public class PremisesService { public void startCalculation(List premises) { var userId = authorizationService.getUserId(); - - // todo check if user is allowed to schedule this - - premises.forEach(preCalculationCheckService::doPrecheck); + premiseRepository.checkOwner(premises, userId); var validSetId = propertySetRepository.getValidSetId(); var validPeriodId = validityPeriodRepository.getValidPeriodId().orElseThrow(() -> new InternalErrorException("no valid period found that is VALID")); + premises.forEach(premiseId -> preCalculationCheckService.doPrecheck(premiseId, validSetId, validPeriodId)); + var calculationIds = new ArrayList(); premises.forEach(p -> { 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 f54baa1..8a0d465 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 @@ -102,7 +102,7 @@ public class CalculationExecutionService { BigDecimal fcaFee; if (premise.getFcaEnabled()) { - var fcaProperty = propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FCA_FEE).orElseThrow(); + var fcaProperty = propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FCA_FEE, calculation.getPropertySetId()).orElseThrow(); var fcaShare = Double.parseDouble(fcaProperty.getCurrentValue()); fcaFee = BigDecimal.valueOf(fcaShare).multiply(materialCost); } else { @@ -110,19 +110,19 @@ public class CalculationExecutionService { } List destinations = destinationRepository.getByPremiseId(premise.getId()); - return destinations.stream().map(destination -> doDestinationCalculation(destination, premise, materialCost, fcaFee)).toList(); + return destinations.stream().map(destination -> doDestinationCalculation(calculation.getPropertySetId(), calculation.getValidityPeriodId(), destination, premise, materialCost, fcaFee)).toList(); } throw new IllegalStateException("Calculation job is not scheduled"); } - private DestinationInfo doDestinationCalculation(Destination destination, Premise premise, BigDecimal materialCost, BigDecimal fcaFee) { + private DestinationInfo doDestinationCalculation(Integer setId, Integer periodId, Destination destination, Premise premise, BigDecimal materialCost, BigDecimal fcaFee) { InventoryCostResult inventoryCost; HandlingResult handlingCost; CustomResult customCost; List sections; - AirfreightResult airfreightCost = airfreightCalculationService.doCalculation(premise, destination); + AirfreightResult airfreightCost = airfreightCalculationService.doCalculation(setId, periodId, premise, destination); ContainerType usedContainerType = null; CalculationJobDestination destinationCalculationJob = new CalculationJobDestination(); @@ -130,12 +130,12 @@ public class CalculationExecutionService { BigDecimal leadTime = null; if (destination.getD2d()) { - var containerCalculation = containerCalculationService.doCalculation(premiseToHuService.createHuFromPremise(premise), ContainerType.FEU); - sections = List.of(new SectionInfo(null, routeSectionCostCalculationService.doD2dCalculation(premise, destination, containerCalculation), containerCalculation)); + var containerCalculation = containerCalculationService.doCalculation(setId, premiseToHuService.createHuFromPremise(premise), ContainerType.FEU); + sections = List.of(new SectionInfo(null, routeSectionCostCalculationService.doD2dCalculation(setId, periodId, premise, destination, containerCalculation), containerCalculation)); leadTime = BigDecimal.valueOf(destination.getLeadTimeD2d()); usedContainerType = ContainerType.FEU; } else { - var bestContainerTypeResult = getSectionsFromBestContainerType(destination, premise); + var bestContainerTypeResult = getSectionsFromBestContainerType(setId, periodId, destination, premise); sections = bestContainerTypeResult.sections; usedContainerType = bestContainerTypeResult.containerType; hasMainRun = sections.stream().anyMatch(s -> s.section().getMainRun()); @@ -156,9 +156,9 @@ public class CalculationExecutionService { if(destination.getD2d()) destinationCalculationJob.setRateD2D(destination.getRateD2d()); - customCost = customCostCalculationService.doCalculation(premise, destination, sections); - handlingCost = handlingCostCalculationService.doCalculation(premise, destination, hasMainRun); - inventoryCost = inventoryCostCalculationService.doCalculation(premise, destination, leadTime); + customCost = customCostCalculationService.doCalculation(setId, premise, destination, sections); + handlingCost = handlingCostCalculationService.doCalculation(setId, premise, destination, hasMainRun); + inventoryCost = inventoryCostCalculationService.doCalculation(setId, premise, destination, leadTime); destinationCalculationJob.setContainerType(usedContainerType); @@ -197,7 +197,7 @@ public class CalculationExecutionService { destinationCalculationJob.setHuCount(sections.getFirst().containerResult().getHuUnitCount()); destinationCalculationJob.setAnnualAmount(BigDecimal.valueOf(destination.getAnnualAmount())); - destinationCalculationJob.setShippingFrequency(shippingFrequencyCalculationService.doCalculation(destination.getAnnualAmount())); + destinationCalculationJob.setShippingFrequency(shippingFrequencyCalculationService.doCalculation(setId, destination.getAnnualAmount())); var commonCost = destinationCalculationJob.getAnnualHandlingCost() .add(destinationCalculationJob.getAnnualDisposalCost()) @@ -223,7 +223,7 @@ public class CalculationExecutionService { return new DestinationInfo(destination, destinationCalculationJob, sections); } - private BestContainerTypeResult getSectionsFromBestContainerType(Destination destination, Premise premise) { + private BestContainerTypeResult getSectionsFromBestContainerType(Integer setId, Integer periodId, Destination destination, Premise premise) { PackagingDimension hu = premiseToHuService.createHuFromPremise(premise); var route = routeRepository.getSelectedByDestinationId(destination.getId()).orElseThrow(); @@ -234,7 +234,7 @@ public class CalculationExecutionService { // Get container calculation for (var containerType : ContainerType.values()) { - containerCalculation.put(containerType, containerCalculationService.doCalculation(hu, containerType)); + containerCalculation.put(containerType, containerCalculationService.doCalculation(setId, hu, containerType)); } for (var containerType : ContainerType.values()) { @@ -244,7 +244,7 @@ public class CalculationExecutionService { for (var section : routeSections) { var container = RateType.MATRIX == section.getRateType() ? containerCalculation.get(ContainerType.TRUCK) : containerCalculation.get(containerType); - sectionInfo.add(new SectionInfo(section, routeSectionCostCalculationService.doCalculation(premise, destination, section, container), containerCalculation.get(containerType))); + sectionInfo.add(new SectionInfo(section, routeSectionCostCalculationService.doCalculation(setId, periodId, premise, destination, section, container), containerCalculation.get(containerType))); } sectionInfos.put(containerType, sectionInfo); diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/AirfreightCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/AirfreightCalculationService.java index be72a08..d20cfde 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/AirfreightCalculationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/AirfreightCalculationService.java @@ -28,21 +28,21 @@ public class AirfreightCalculationService { this.countryPropertyRepository = countryPropertyRepository; } - public AirfreightResult doCalculation(Premise premise, Destination destination) { + public AirfreightResult doCalculation(Integer setId, Integer periodId, Premise premise, Destination destination) { - var maxAirfreightShare = Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.AIR_SHARE, premise.getCountryId()).orElseThrow().getCurrentValue()); + var maxAirfreightShare = Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.AIR_SHARE, setId, premise.getCountryId()).orElseThrow().getCurrentValue()); var overseaShare = premise.getOverseaShare().doubleValue(); var airfreightShare = getAirfreightShare(maxAirfreightShare, overseaShare); var hu = premiseToHuService.createHuFromPremise(premise); - var preCarriage = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_PRECARRIAGE).orElseThrow().getCurrentValue())); - var mainCarriage = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_MAINCARRIAGE).orElseThrow().getCurrentValue())); - var postCarriage = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_ONCARRIAGE).orElseThrow().getCurrentValue())); - var terminalFee = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_TERMINAL_FEE).orElseThrow().getCurrentValue())); - var preCarriageFee = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_HANDLING).orElseThrow().getCurrentValue())); - var customsClearanceFee = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_CUSTOM_FEE).orElseThrow().getCurrentValue())); - var handOverFee = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_HANDOVER_FEE).orElseThrow().getCurrentValue())); + var preCarriage = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_PRECARRIAGE, setId).orElseThrow().getCurrentValue())); + var mainCarriage = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_MAINCARRIAGE, setId).orElseThrow().getCurrentValue())); + var postCarriage = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_ONCARRIAGE, setId).orElseThrow().getCurrentValue())); + var terminalFee = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_TERMINAL_FEE, setId).orElseThrow().getCurrentValue())); + var preCarriageFee = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_HANDLING, setId).orElseThrow().getCurrentValue())); + var customsClearanceFee = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_CUSTOM_FEE, setId).orElseThrow().getCurrentValue())); + var handOverFee = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_HANDOVER_FEE, setId).orElseThrow().getCurrentValue())); var result = new AirfreightResult(); @@ -69,11 +69,11 @@ public class AirfreightCalculationService { } private double getDimensionInCm(Number dimension, DimensionUnit unit) { - return DimensionUnit.CM.convertFromMM(dimension).doubleValue(); + return DimensionUnit.CM.convertFromMM(dimension); } private double getWeightInKg(PackagingDimension hu) { - return WeightUnit.KG.convertFromG(hu.getWeight()).doubleValue(); + return WeightUnit.KG.convertFromG(hu.getWeight()); } private double getAirfreightShare(double maxAirfreightShare, double overseaShare) { diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ChangeRiskFactorCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ChangeRiskFactorCalculationService.java index 3711196..e36541b 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ChangeRiskFactorCalculationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ChangeRiskFactorCalculationService.java @@ -25,10 +25,10 @@ public class ChangeRiskFactorCalculationService { this.nodeRepository = nodeRepository; } - public ChangeRiskFactorCalculationResult getChanceRiskFactors() { - var rate = findRate(); - var riskValue = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.RISK_REF).orElseThrow().getCurrentValue())); - var chanceValue = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.CHANCE_REF).orElseThrow().getCurrentValue())); + public ChangeRiskFactorCalculationResult getChanceRiskFactors(Integer setId, Integer periodId) { + var rate = findRate(setId, periodId); + var riskValue = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.RISK_REF, setId).orElseThrow().getCurrentValue())); + var chanceValue = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.CHANCE_REF, setId).orElseThrow().getCurrentValue())); var result = new ChangeRiskFactorCalculationResult(); result.setChanceFactor(chanceValue.divide(rate.getRateFeu(), 2, RoundingMode.HALF_UP)); @@ -36,14 +36,14 @@ public class ChangeRiskFactorCalculationService { return result; } - private ContainerRate findRate() { - var startReference = propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.START_REF).orElseThrow().getCurrentValue(); - var endReference = propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.END_REF).orElseThrow().getCurrentValue(); + private ContainerRate findRate(Integer setId, Integer periodId) { + var startReference = propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.START_REF, setId).orElseThrow().getCurrentValue(); + var endReference = propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.END_REF, setId).orElseThrow().getCurrentValue(); var startNode = nodeRepository.getByExternalMappingId(startReference).orElseThrow(); var endNode = nodeRepository.getByExternalMappingId(endReference).orElseThrow(); - return containerRateRepository.findRoute(startNode.getId(), endNode.getId(), TransportType.SEA).orElseThrow(); + return containerRateRepository.findRoute(startNode.getId(), endNode.getId(), periodId, TransportType.SEA).orElseThrow(); } } 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 d4e3c3f..f841ca6 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,10 +49,10 @@ public class ContainerCalculationService { * @param containerType The type of container to be loaded * @return ContainerCalculationResult containing loading pattern and capacity information */ - public ContainerCalculationResult doCalculation(PackagingDimension hu, ContainerType containerType) { + public ContainerCalculationResult doCalculation(Integer setId, PackagingDimension hu, ContainerType containerType) { var weightInKg = BigDecimal.valueOf(WeightUnit.KG.convertFromG(hu.getWeight())); - var maxContainerLoad = BigDecimal.valueOf(getMaxContainerLoad(containerType)); + var maxContainerLoad = BigDecimal.valueOf(getMaxContainerLoad(containerType, setId)); var maxUnitByWeight = maxContainerLoad.divide(weightInKg, 0, RoundingMode.HALF_UP).intValueExact(); var dimensions = hu.withTolerance(DIMENSION_TOLERANCE); @@ -144,7 +144,7 @@ public class ContainerCalculationService { * @throws IllegalArgumentException if container type is not supported * @throws IllegalStateException if required property is missing */ - private int getMaxContainerLoad(ContainerType containerType) { + private int getMaxContainerLoad(ContainerType containerType, Integer setId) { Map mappings = Map.of( ContainerType.FEU, SystemPropertyMappingId.FEU_LOAD, ContainerType.TRUCK, SystemPropertyMappingId.TRUCK_LOAD, @@ -157,7 +157,7 @@ public class ContainerCalculationService { throw new IllegalArgumentException("Unsupported container type: " + containerType); } - var value = propertyRepository.getPropertyByMappingId(mappingId) + var value = propertyRepository.getPropertyByMappingId(mappingId, setId) .orElseThrow(() -> new IllegalStateException("Missing property: " + mappingId)) .getCurrentValue(); 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 631aab2..53ca223 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 @@ -48,14 +48,14 @@ public class CustomCostCalculationService { } } - public CustomResult doCalculation(Premise premise, Destination destination, List sections) { + public CustomResult doCalculation(Integer setId, Premise premise, Destination destination, List sections) { - var destUnion = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, destination.getCountryId()).orElseThrow(); - var sourceUnion = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, premise.getCountryId()).orElseThrow(); + var destUnion = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, setId, destination.getCountryId()).orElseThrow(); + var sourceUnion = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, setId, premise.getCountryId()).orElseThrow(); if (CustomUnionType.EU.name().equals(destUnion.getCurrentValue()) && (sourceUnion.getCurrentValue() == null || CustomUnionType.NONE.name().equals(sourceUnion.getCurrentValue()))) { - var relevantSections = getCustomRelevantRouteSections(sections); + var relevantSections = getCustomRelevantRouteSections(setId, sections); var transportationCost = relevantSections.stream().map(s -> s.result().getAnnualCost()).reduce(BigDecimal.ZERO, BigDecimal::add); var transportationChanceCost = relevantSections.stream().map(s -> s.result().getAnnualChanceCost()).reduce(BigDecimal.ZERO, BigDecimal::add); @@ -64,15 +64,15 @@ public class CustomCostCalculationService { double huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(relevantSections.getFirst().containerResult().getHuUnitCount()),2, RoundingMode.HALF_UP).doubleValue(); - return getCustomCalculationResult(premise, destination, getContainerShare(premise, relevantSections.getFirst().containerResult()), huAnnualAmount, transportationCost, transportationChanceCost, transportationRiskCost); + return getCustomCalculationResult(setId, premise, destination, getContainerShare(premise, relevantSections.getFirst().containerResult()), huAnnualAmount, transportationCost, transportationChanceCost, transportationRiskCost); } return CustomResult.EMPTY; } - private CustomResult getCustomCalculationResult(Premise premise, Destination destination, BigDecimal containerShare, double huAnnualAmount, BigDecimal transportationCost, BigDecimal transportationChanceCost, BigDecimal transportationRiskCost) { - var shippingFrequency = shippingFrequencyCalculationService.doCalculation(huAnnualAmount); - var customFee = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.CUSTOM_FEE).orElseThrow().getCurrentValue()); + private CustomResult getCustomCalculationResult(Integer setId, Premise premise, Destination destination, BigDecimal containerShare, 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()); var tariffRate = premise.getTariffRate() == null ? customApiService.getTariffRate(premise.getHsCode(), premise.getCountryId()) : premise.getTariffRate(); @@ -80,7 +80,7 @@ public class CustomCostCalculationService { var fcaFee = BigDecimal.ZERO; if (premise.getFcaEnabled()) { - var fcaProperty = propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FCA_FEE).orElseThrow(); + var fcaProperty = propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FCA_FEE, setId).orElseThrow(); var fcaShare = Double.parseDouble(fcaProperty.getCurrentValue()); fcaFee = BigDecimal.valueOf(fcaShare).multiply(materialCost); } @@ -101,7 +101,7 @@ public class CustomCostCalculationService { return new CustomResult(customValue, customRiskValue, customChanceValue, customDuties, tariffRate, annualCost, annualRiskCost, annualChanceCost); } - private List getCustomRelevantRouteSections(List sections) { + private List getCustomRelevantRouteSections(Integer setId, List sections) { List customSections = new ArrayList<>(); @@ -111,8 +111,8 @@ public class CustomCostCalculationService { } for (SectionInfo section : sections) { - if (!(CustomUnionType.EU == getCustomUnionByRouteNodeId(section.section().getFromRouteNodeId()) && - CustomUnionType.EU == getCustomUnionByRouteNodeId(section.section().getToRouteNodeId()))) { + if (!(CustomUnionType.EU == getCustomUnionByRouteNodeId(setId, section.section().getFromRouteNodeId()) && + CustomUnionType.EU == getCustomUnionByRouteNodeId(setId, section.section().getToRouteNodeId()))) { customSections.add(section); } } @@ -120,13 +120,13 @@ public class CustomCostCalculationService { return customSections; } - private CustomUnionType getCustomUnionByRouteNodeId(Integer routeNodeId) { + private CustomUnionType getCustomUnionByRouteNodeId(Integer setId, Integer routeNodeId) { var node = routeNodeRepository.getById(routeNodeId).orElseThrow(); - return getCustomUnionByCountryId(node.getCountryId()); + return getCustomUnionByCountryId(setId, node.getCountryId()); } - private CustomUnionType getCustomUnionByCountryId(Integer countryId) { - var property = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, countryId).orElseThrow(); + private CustomUnionType getCustomUnionByCountryId(Integer setId, Integer countryId) { + var property = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, setId, countryId).orElseThrow(); if (property.getCurrentValue() == null) return CustomUnionType.NONE; 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 9a248a3..c44e856 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 @@ -30,36 +30,36 @@ public class HandlingCostCalculationService { this.shippingFrequencyCalculationService = shippingFrequencyCalculationService; } - public HandlingResult doCalculation(Premise premise, Destination destination, Boolean addRepackingCosts) { + public HandlingResult doCalculation(Integer setId, Premise premise, Destination destination, Boolean addRepackingCosts) { var hu = premiseToHuService.createHuFromPremise(premise); - return (LoadCarrierType.SLC == hu.getLoadCarrierType() ? getSLCCost(destination, hu, hu.getLoadCarrierType(), addRepackingCosts) : getLLCCost(destination, hu, hu.getLoadCarrierType(), addRepackingCosts)); + return (LoadCarrierType.SLC == hu.getLoadCarrierType() ? getSLCCost(setId, destination, hu, hu.getLoadCarrierType(), addRepackingCosts) : getLLCCost(setId, destination, hu, hu.getLoadCarrierType(), addRepackingCosts)); } - private HandlingResult getSLCCost(Destination destination, PackagingDimension hu, LoadCarrierType loadCarrierType, boolean addRepackingCosts) { + private HandlingResult getSLCCost(Integer setId, Destination destination, PackagingDimension hu, LoadCarrierType loadCarrierType, boolean addRepackingCosts) { var destinationHandling = destination.getHandlingCost(); var destinationDisposal = destination.getDisposalCost(); var destinationRepacking = destination.getRepackingCost(); BigDecimal huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(hu.getContentUnitCount()),4, RoundingMode.UP ); - BigDecimal handling = destinationHandling != null ? destinationHandling : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_HANDLING).orElseThrow().getCurrentValue())); - BigDecimal release = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_RELEASE).orElseThrow().getCurrentValue())); - BigDecimal dispatch = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_DISPATCH).orElseThrow().getCurrentValue())); - BigDecimal disposal = destinationDisposal != null ? destinationDisposal : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.DISPOSAL).orElseThrow().getCurrentValue())); + BigDecimal handling = destinationHandling != null ? destinationHandling : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_HANDLING, setId).orElseThrow().getCurrentValue())); + BigDecimal release = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_RELEASE, setId).orElseThrow().getCurrentValue())); + BigDecimal dispatch = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_DISPATCH, setId).orElseThrow().getCurrentValue())); + BigDecimal disposal = destinationDisposal != null ? destinationDisposal : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.DISPOSAL, setId).orElseThrow().getCurrentValue())); - BigDecimal wageFactor = BigDecimal.valueOf(Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.WAGE, destination.getCountryId()).orElseThrow().getCurrentValue())); - BigDecimal booking = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING_KLT).orElseThrow().getCurrentValue())); + BigDecimal wageFactor = BigDecimal.valueOf(Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.WAGE, setId, destination.getCountryId()).orElseThrow().getCurrentValue())); + BigDecimal booking = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING_KLT, setId).orElseThrow().getCurrentValue())); return new HandlingResult(LoadCarrierType.SLC, - getRepackingCost(hu, loadCarrierType, addRepackingCosts, destinationRepacking).multiply(huAnnualAmount), + getRepackingCost(setId, hu, loadCarrierType, addRepackingCosts, destinationRepacking).multiply(huAnnualAmount), handling.multiply(huAnnualAmount), destinationDisposal == null ? BigDecimal.ZERO : (disposal.multiply(huAnnualAmount)), //TODO: disposal SLC, ignore? - huAnnualAmount.multiply((handling.add(booking).add(release).add(dispatch).add(getRepackingCost(hu, loadCarrierType, addRepackingCosts, destinationRepacking)))).multiply(wageFactor)); + huAnnualAmount.multiply((handling.add(booking).add(release).add(dispatch).add(getRepackingCost(setId, hu, loadCarrierType, addRepackingCosts, destinationRepacking)))).multiply(wageFactor)); } - private BigDecimal getRepackingCost(PackagingDimension hu, LoadCarrierType type, boolean addRepackingCosts, BigDecimal userInput) { + private BigDecimal getRepackingCost(Integer setId, PackagingDimension hu, LoadCarrierType type, boolean addRepackingCosts, BigDecimal userInput) { if(userInput != null) return userInput; @@ -70,30 +70,30 @@ public class HandlingCostCalculationService { return switch (type) { case SLC -> - BigDecimal.valueOf(Double.parseDouble((hu.getWeight() < 15_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_S).orElseThrow().getCurrentValue() : propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_M).orElseThrow().getCurrentValue())); + BigDecimal.valueOf(Double.parseDouble((hu.getWeight() < 15_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_S, setId).orElseThrow().getCurrentValue() : propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_M, setId).orElseThrow().getCurrentValue())); case LLC -> - BigDecimal.valueOf(Double.parseDouble(((hu.getWeight() < 15_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_S) : (hu.getWeight() < 2_000_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_M) : propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_L)).orElseThrow().getCurrentValue())); + BigDecimal.valueOf(Double.parseDouble(((hu.getWeight() < 15_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_S, setId) : (hu.getWeight() < 2_000_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_M, setId) : propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_L, setId)).orElseThrow().getCurrentValue())); }; } - private HandlingResult getLLCCost(Destination destination, PackagingDimension hu, LoadCarrierType type, boolean addRepackingCosts) { + private HandlingResult getLLCCost(Integer setId, Destination destination, PackagingDimension hu, LoadCarrierType type, boolean addRepackingCosts) { var destinationHandling = destination.getHandlingCost(); var destinationDisposal = destination.getDisposalCost(); var destinationRepacking = destination.getRepackingCost(); BigDecimal huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(hu.getContentUnitCount()),4, RoundingMode.UP ); - BigDecimal handling = destinationHandling != null ? destinationHandling : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_HANDLING).orElseThrow().getCurrentValue())); - BigDecimal release = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_RELEASE).orElseThrow().getCurrentValue())); - BigDecimal dispatch = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_DISPATCH).orElseThrow().getCurrentValue())); - BigDecimal disposal = destinationDisposal != null ? destinationDisposal : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.DISPOSAL).orElseThrow().getCurrentValue())); + BigDecimal handling = destinationHandling != null ? destinationHandling : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_HANDLING, setId).orElseThrow().getCurrentValue())); + BigDecimal release = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_RELEASE, setId).orElseThrow().getCurrentValue())); + BigDecimal dispatch = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_DISPATCH, setId).orElseThrow().getCurrentValue())); + BigDecimal disposal = destinationDisposal != null ? destinationDisposal : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.DISPOSAL, setId).orElseThrow().getCurrentValue())); - BigDecimal wageFactor = BigDecimal.valueOf(Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.WAGE, destination.getCountryId()).orElseThrow().getCurrentValue())); - BigDecimal booking = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING).orElseThrow().getCurrentValue())); + BigDecimal wageFactor = BigDecimal.valueOf(Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.WAGE, setId, destination.getCountryId()).orElseThrow().getCurrentValue())); + BigDecimal booking = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING, setId).orElseThrow().getCurrentValue())); - var annualRepacking = getRepackingCost(hu, type, addRepackingCosts, destinationRepacking).multiply(wageFactor).multiply( huAnnualAmount); - var annualHandling = ((handling.add(dispatch).add(release)).multiply(wageFactor).multiply(huAnnualAmount)).add(booking.multiply(BigDecimal.valueOf(shippingFrequencyCalculationService.doCalculation(huAnnualAmount.doubleValue())))); + var annualRepacking = getRepackingCost(setId, hu, type, addRepackingCosts, 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 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 e441ef1..89180e3 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 @@ -30,7 +30,7 @@ public class InventoryCostCalculationService { this.premiseToHuService = premiseToHuService; } - public InventoryCostResult doCalculation(Premise premise, Destination destination, BigDecimal leadTime) { + public InventoryCostResult doCalculation(Integer setId, Premise premise, Destination destination, BigDecimal leadTime) { var fcaFee = BigDecimal.ZERO; @@ -44,16 +44,16 @@ public class InventoryCostCalculationService { var hu = premiseToHuService.createHuFromPremise(premise); double huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(hu.getContentUnitCount()),0, RoundingMode.UP ).doubleValue(); - var safetyDays = BigDecimal.valueOf(Integer.parseInt(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.SAFETY_STOCK, premise.getCountryId()).orElseThrow().getCurrentValue())); - var workdays = BigDecimal.valueOf(Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.WORKDAYS).orElseThrow().getCurrentValue())); - var paymentTerms = BigDecimal.valueOf(Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.PAYMENT_TERMS).orElseThrow().getCurrentValue())); - var interestRate = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.INTEREST_RATE).orElseThrow().getCurrentValue())); + var safetyDays = BigDecimal.valueOf(Integer.parseInt(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.SAFETY_STOCK, setId, premise.getCountryId()).orElseThrow().getCurrentValue())); + var workdays = BigDecimal.valueOf(Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.WORKDAYS, setId).orElseThrow().getCurrentValue())); + var paymentTerms = BigDecimal.valueOf(Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.PAYMENT_TERMS, setId).orElseThrow().getCurrentValue())); + var interestRate = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.INTEREST_RATE, setId).orElseThrow().getCurrentValue())); var annualAmount = BigDecimal.valueOf(destination.getAnnualAmount().doubleValue()); 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(huAnnualAmount),1)), 10, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(.5))); + var opStock = (annualAmount.divide(BigDecimal.valueOf(Math.max(shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount),1)), 10, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(.5))); var safetyStock = safetyDays.multiply(workdayAmount); var stockedInventory = opStock.add(safetyStock); var inTransportStock = dailyAmount.multiply(leadTime); @@ -63,15 +63,15 @@ public class InventoryCostCalculationService { var storageCostStock = roundToHu(hu, safetyStock.add(opStock)); var capitalCost = capitalCostStock.multiply(interestRate).multiply(premise.getMaterialCost().add(fcaFee)); - var storageCost = storageCostStock.multiply(getSpaceCostPerHu(hu)).multiply(BigDecimal.valueOf(365)); + var storageCost = storageCostStock.multiply(getSpaceCostPerHu(setId, hu)).multiply(BigDecimal.valueOf(365)); return new InventoryCostResult(opStock, safetyStock, stockedInventory, inTransportStock, stockBeforePayment, capitalCost, storageCost, safetyDays ); } - private BigDecimal getSpaceCostPerHu(PackagingDimension hu) { - var spaceCost = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.SPACE_COST).orElseThrow().getCurrentValue())); + private BigDecimal getSpaceCostPerHu(Integer setId, PackagingDimension hu) { + var spaceCost = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.SPACE_COST, setId).orElseThrow().getCurrentValue())); var spaceCostPerHu = BigDecimal.valueOf(hu.getFloorArea(DimensionUnit.M)*hu.getRoundedHeight(DimensionUnit.M)).multiply(spaceCost); return spaceCostPerHu; } 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 d0bd341..a25c775 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 @@ -49,7 +49,7 @@ public class RouteSectionCostCalculationService { this.nodeRepository = nodeRepository; } - public CalculationJobRouteSection doD2dCalculation(Premise premise, Destination destination, ContainerCalculationResult containerCalculation) { + public CalculationJobRouteSection doD2dCalculation(Integer setId, Integer periodId, Premise premise, Destination destination, ContainerCalculationResult containerCalculation) { CalculationJobRouteSection result = new CalculationJobRouteSection(); // Set route metadata @@ -78,7 +78,7 @@ public class RouteSectionCostCalculationService { result.setTransitTime(transitTime); // Calculate price and annual cost - BigDecimal utilization = getUtilization(RateType.CONTAINER); /* D2D is always 40ft container */ + BigDecimal utilization = getUtilization(setId, RateType.CONTAINER); /* D2D is always 40ft container */ double annualVolume = destination.getAnnualAmount() * containerCalculation.getHu().getVolume(DimensionUnit.M); PriceCalculationResult prices = calculatePrices( @@ -97,7 +97,7 @@ public class RouteSectionCostCalculationService { result.setWeightPrice(prices.weightPrice); result.setUtilization(prices.utilization); - var chanceRiskFactors = changeRiskFactorCalculationService.getChanceRiskFactors(); + var chanceRiskFactors = changeRiskFactorCalculationService.getChanceRiskFactors(setId, periodId); BigDecimal annualCost = (containerCalculation.isWeightExceeded() ? prices.weightPrice : prices.volumePrice).multiply(BigDecimal.valueOf(annualVolume)); BigDecimal annualRiskCost = annualCost.multiply(chanceRiskFactors.getRiskFactor()); @@ -110,7 +110,7 @@ public class RouteSectionCostCalculationService { return result; } - public CalculationJobRouteSection doCalculation(Premise premise, Destination destination, RouteSection section, ContainerCalculationResult containerCalculation) { + public CalculationJobRouteSection doCalculation(Integer setId, Integer periodId, Premise premise, Destination destination, RouteSection section, ContainerCalculationResult containerCalculation) { CalculationJobRouteSection result = new CalculationJobRouteSection(); // Set route metadata @@ -135,11 +135,11 @@ public class RouteSectionCostCalculationService { int transitTime; if (RateType.CONTAINER == section.getRateType()) { - ContainerRate containerRate = findContainerRate(section, fromNode, toNode); + ContainerRate containerRate = findContainerRate(section, fromNode, toNode, periodId); rate = getContainerRate(containerRate, containerCalculation.getContainerType()); transitTime = containerRate.getLeadTime(); } else if (RateType.MATRIX == section.getRateType()) { - MatrixRate matrixRate = findMatrixRate(fromNode, toNode); + MatrixRate matrixRate = findMatrixRate(fromNode, toNode, periodId); rate = matrixRate.getRate().multiply(BigDecimal.valueOf(distance)); transitTime = 3; // Default transit time for matrix rate } else if (RateType.NEAR_BY == section.getRateType()) { @@ -154,7 +154,7 @@ public class RouteSectionCostCalculationService { // Calculate price and annual cost BigDecimal huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(containerCalculation.getHu().getContentUnitCount()), 2, RoundingMode.HALF_UP); - BigDecimal utilization = getUtilization(section.getRateType()); + BigDecimal utilization = getUtilization(setId, section.getRateType()); BigDecimal annualVolume = huAnnualAmount.multiply(BigDecimal.valueOf(containerCalculation.getHu().getVolume(DimensionUnit.M))); BigDecimal annualWeight = huAnnualAmount.multiply(BigDecimal.valueOf(containerCalculation.getHu().getWeight(WeightUnit.KG))); @@ -174,7 +174,7 @@ public class RouteSectionCostCalculationService { result.setWeightPrice(prices.weightPrice); result.setUtilization(!containerCalculation.isWeightExceeded() || !premise.getHuMixable() ? prices.utilization : BigDecimal.valueOf(1.0)); - var chanceRiskFactors = changeRiskFactorCalculationService.getChanceRiskFactors(); + var chanceRiskFactors = changeRiskFactorCalculationService.getChanceRiskFactors(setId, periodId); BigDecimal annualCost = (containerCalculation.isWeightExceeded() ? prices.weightPrice.multiply(annualWeight) : prices.volumePrice.multiply(annualVolume)); BigDecimal annualRiskCost = annualCost.multiply(chanceRiskFactors.getRiskFactor()); @@ -220,31 +220,32 @@ public class RouteSectionCostCalculationService { return new PriceCalculationResult(volumePrice, weightPrice, utilization); } - private ContainerRate findContainerRate(RouteSection section, RouteNode fromNode, RouteNode toNode) { + private ContainerRate findContainerRate(RouteSection section, RouteNode fromNode, RouteNode toNode, Integer periodId) { return containerRateRepository.findRoute( fromNode.getNodeId(), toNode.getNodeId(), + periodId, TransportType.valueOf(section.getTransportType().name())) .orElseThrow(() -> new NoSuchElementException( "ContainerRate not found for route: " + fromNode.getName() + "(" + fromNode.getNodeId() + ")" + " to " + toNode.getName() + "(" + toNode.getNodeId() + ")")); } - private MatrixRate findMatrixRate(RouteNode fromNode, RouteNode toNode) { - return matrixRateRepository.getByCountryIds(fromNode.getCountryId(), toNode.getCountryId()) + private MatrixRate findMatrixRate(RouteNode fromNode, RouteNode toNode, Integer periodId) { + return matrixRateRepository.getByCountryIds(fromNode.getCountryId(), toNode.getCountryId(), periodId) .orElseThrow(() -> new NoSuchElementException( "MatrixRate not found for countries: " + fromNode.getCountryId() + " to " + toNode.getCountryId())); } - private BigDecimal getUtilization(RateType rateType) { + private BigDecimal getUtilization(Integer setId, RateType rateType) { BigDecimal utilization; if (rateType == RateType.NEAR_BY) { - utilization = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.CONTAINER_UTIL).orElseThrow().getCurrentValue())); + utilization = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.CONTAINER_UTIL, setId).orElseThrow().getCurrentValue())); } else if (rateType == RateType.CONTAINER) { - utilization = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.CONTAINER_UTIL).orElseThrow().getCurrentValue())); + utilization = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.CONTAINER_UTIL, setId).orElseThrow().getCurrentValue())); } else if (rateType == RateType.MATRIX) { - utilization = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.TRUCK_UTIL).orElseThrow().getCurrentValue())); + utilization = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.TRUCK_UTIL, setId).orElseThrow().getCurrentValue())); } else throw new IllegalArgumentException("Unknown rate type"); 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 c06e410..8c5bee5 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,9 +13,9 @@ public class ShippingFrequencyCalculationService { this.propertyRepository = propertyRepository; } - public int doCalculation(int huAnnualAmount) { - var minAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MIN).orElseThrow().getCurrentValue()); - var maxAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MAX).orElseThrow().getCurrentValue()); + public int doCalculation(Integer setId, int huAnnualAmount) { + var minAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MIN, setId).orElseThrow().getCurrentValue()); + var maxAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MAX, setId).orElseThrow().getCurrentValue()); if (huAnnualAmount > maxAnnualFrequency) return maxAnnualFrequency; @@ -24,9 +24,9 @@ public class ShippingFrequencyCalculationService { } - public double doCalculation(double huAnnualAmount) { - Integer minAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MIN).orElseThrow().getCurrentValue()); - Integer maxAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MAX).orElseThrow().getCurrentValue()); + 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()); if (huAnnualAmount > maxAnnualFrequency.doubleValue()) return maxAnnualFrequency; diff --git a/src/main/java/de/avatic/lcc/service/precalculation/PreCalculationCheckService.java b/src/main/java/de/avatic/lcc/service/precalculation/PreCalculationCheckService.java index 2b635b8..a0e1a99 100644 --- a/src/main/java/de/avatic/lcc/service/precalculation/PreCalculationCheckService.java +++ b/src/main/java/de/avatic/lcc/service/precalculation/PreCalculationCheckService.java @@ -1,24 +1,32 @@ package de.avatic.lcc.service.precalculation; import de.avatic.lcc.dto.generic.ContainerType; +import de.avatic.lcc.dto.generic.RateType; import de.avatic.lcc.model.nodes.Node; import de.avatic.lcc.model.premises.Premise; import de.avatic.lcc.model.premises.route.Destination; import de.avatic.lcc.model.premises.route.Route; +import de.avatic.lcc.model.premises.route.RouteSection; +import de.avatic.lcc.model.properties.PropertySet; import de.avatic.lcc.model.properties.SystemPropertyMappingId; +import de.avatic.lcc.model.rates.ValidityPeriod; +import de.avatic.lcc.model.rates.ValidityPeriodState; import de.avatic.lcc.model.utils.WeightUnit; import de.avatic.lcc.repositories.NodeRepository; -import de.avatic.lcc.repositories.premise.DestinationRepository; -import de.avatic.lcc.repositories.premise.PremiseRepository; -import de.avatic.lcc.repositories.premise.RouteRepository; -import de.avatic.lcc.repositories.properties.PropertyRepository; -import de.avatic.lcc.service.api.CustomApiService; +import de.avatic.lcc.repositories.premise.*; +import de.avatic.lcc.repositories.properties.PropertySetRepository; +import de.avatic.lcc.repositories.rates.ContainerRateRepository; +import de.avatic.lcc.repositories.rates.MatrixRateRepository; +import de.avatic.lcc.repositories.rates.ValidityPeriodRepository; import de.avatic.lcc.service.access.PropertyService; +import de.avatic.lcc.service.api.CustomApiService; import de.avatic.lcc.service.transformer.generic.DimensionTransformer; import de.avatic.lcc.util.exception.internalerror.PremiseValidationError; import org.springframework.stereotype.Service; import java.math.BigDecimal; +import java.time.format.DateTimeFormatter; +import java.util.List; import java.util.Optional; @Service @@ -32,28 +40,39 @@ public class PreCalculationCheckService { private final RouteRepository routeRepository; private final NodeRepository nodeRepository; private final DimensionTransformer dimensionTransformer; - private final PropertyRepository propertyRepository; - private final PropertyService propertyService; - public PreCalculationCheckService(PremiseRepository premiseRepository, CustomApiService customApiService, DestinationRepository destinationRepository, RouteRepository routeRepository, NodeRepository nodeRepository, DimensionTransformer dimensionTransformer, PropertyRepository propertyRepository, PropertyService propertyService) { + private final PropertyService propertyService; + private final RouteSectionRepository routeSectionRepository; + private final RouteNodeRepository routeNodeRepository; + private final MatrixRateRepository matrixRateRepository; + private final ContainerRateRepository containerRateRepository; + private final ValidityPeriodRepository validityPeriodRepository; + private final PropertySetRepository propertySetRepository; + + public PreCalculationCheckService(PremiseRepository premiseRepository, CustomApiService customApiService, DestinationRepository destinationRepository, RouteRepository routeRepository, NodeRepository nodeRepository, DimensionTransformer dimensionTransformer, PropertyService propertyService, RouteSectionRepository routeSectionRepository, RouteNodeRepository routeNodeRepository, MatrixRateRepository matrixRateRepository, ContainerRateRepository containerRateRepository, ValidityPeriodRepository validityPeriodRepository, PropertySetRepository propertySetRepository) { this.premiseRepository = premiseRepository; this.customApiService = customApiService; this.destinationRepository = destinationRepository; this.routeRepository = routeRepository; this.nodeRepository = nodeRepository; this.dimensionTransformer = dimensionTransformer; - this.propertyRepository = propertyRepository; + this.propertyService = propertyService; + this.routeSectionRepository = routeSectionRepository; + this.routeNodeRepository = routeNodeRepository; + this.matrixRateRepository = matrixRateRepository; + this.containerRateRepository = containerRateRepository; + this.validityPeriodRepository = validityPeriodRepository; + this.propertySetRepository = propertySetRepository; } - public void doPrecheck(Integer premiseId) { + public void doPrecheck(Integer premiseId, Integer setId, Integer periodId) { var premise = premiseRepository.getPremiseById(premiseId).orElseThrow(); supplierCheck(premise); materialCheck(premise); - packagingCheck(premise); priceCheck(premise); @@ -88,10 +107,68 @@ public class PreCalculationCheckService { throw new PremiseValidationError("Door-2-door lead time not entered or zero."); } + var period = validityPeriodRepository.getById(periodId); + var set = propertySetRepository.getById(setId); + + periodCheck(period, set); + + routes.stream().filter(Route::getSelected).findAny().ifPresent(r -> { + var sections = routeSectionRepository.getByRouteId(r.getId()); + routeCheck(sections, period); + }); + + } } + private void periodCheck(ValidityPeriod period, PropertySet set) { + + + if(set == null) + throw new PremiseValidationError("There are no system properties for the given date."); + + if(period == null) + throw new PremiseValidationError("There are no rates for the given date."); + + if(ValidityPeriodState.VALID != period.getState() && ValidityPeriodState.EXPIRED != period.getState()) + throw new PremiseValidationError("There are no valid rates for the given date."); + + if(ValidityPeriodState.VALID != set.getState() && ValidityPeriodState.EXPIRED != period.getState()) + throw new PremiseValidationError("There are no valid system properties for the given date."); + + } + + private void routeCheck(List sections, ValidityPeriod period) { + + sections.forEach(section -> { + var fromRouteNode = routeNodeRepository.getFromNodeBySectionId(section.getId()); + var toRouteNode = routeNodeRepository.getToNodeBySectionId(section.getId()); + + if (fromRouteNode.isEmpty() || toRouteNode.isEmpty()) + throw new PremiseValidationError("Error in route. Please contact your administrator."); + + if (RateType.MATRIX == section.getRateType()) { + var rate = matrixRateRepository.getByCountryIds(fromRouteNode.get().getCountryId(), toRouteNode.get().getCountryId(), period.getId()); + + + if (rate.isEmpty()) + throw new PremiseValidationError("The transport rates for the period " + period.getStartDate().format(DateTimeFormatter.ISO_DATE) + " to " + period.getEndDate().format(DateTimeFormatter.ISO_DATE) + " are insufficient to perform the calculation for route segment" + fromRouteNode.get().getExternalMappingId() + " to " + toRouteNode.get().getExternalMappingId()); + + } + + if (RateType.CONTAINER == section.getRateType()) { + var rate = containerRateRepository.findRoute(fromRouteNode.get().getNodeId(), toRouteNode.get().getNodeId(), period.getId(), section.getTransportType()); + + if (rate.isEmpty()) + throw new PremiseValidationError("The transport rates for the period " + period.getStartDate().format(DateTimeFormatter.ISO_DATE) + " to " + period.getEndDate().format(DateTimeFormatter.ISO_DATE) + " are insufficient to perform the calculation for route segment" + fromRouteNode.get().getExternalMappingId() + " to " + toRouteNode.get().getExternalMappingId()); + + } + + }); + + } + private void destinationCheck(Destination destination, Node node) { if (destination.getAnnualAmount() == null || destination.getAnnualAmount() == 0) @@ -216,7 +293,7 @@ public class PreCalculationCheckService { } - if((hu.getLength() * hu.getWidth()) < 20000) { + if ((hu.getLength() * hu.getWidth()) < 20000) { throw new PremiseValidationError("HU dimensions too small. Review entered length, width, height and selected dimension unit [mm, cm, m]."); }