- Calculations can be started now with any validity period and property set.

- added preflight check for validity period, property set and all route sections
This commit is contained in:
Jan 2025-10-18 19:52:10 +02:00
parent dac371b481
commit 78bd0ad6d6
15 changed files with 318 additions and 131 deletions

View file

@ -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<PropertyDTO> getByMappingIdAndCountryId(CountryPropertyMappingId mappingId, Integer countryId) {
return getByMappingIdAndCountryId(mappingId.name(), countryId);
}
@Transactional
public Optional<PropertyDTO> getByMappingIdAndCountryId(CountryPropertyMappingId mappingId, Integer setId, Integer countryId) {
return getByMappingIdAndCountryId(mappingId.name(), setId, countryId);
}
public Optional<PropertyDTO> 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<PropertyDTO> 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);

View file

@ -146,6 +146,25 @@ public class PropertyRepository {
}
@Transactional
public Optional<PropertyDTO> 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.

View file

@ -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<ContainerRate> findRoute(Integer fromNodeId, Integer toNodeId, TransportType type) {
@Transactional
public Optional<ContainerRate> 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<ContainerRate> 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();

View file

@ -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<MatrixRate> 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<MatrixRate> 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();

View file

@ -100,14 +100,13 @@ public class PremisesService {
public void startCalculation(List<Integer> 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<Integer>();
premises.forEach(p -> {

View file

@ -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<Destination> 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<SectionInfo> 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);

View file

@ -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) {

View file

@ -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();
}
}

View file

@ -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<ContainerType, SystemPropertyMappingId> 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();

View file

@ -48,14 +48,14 @@ public class CustomCostCalculationService {
}
}
public CustomResult doCalculation(Premise premise, Destination destination, List<SectionInfo> sections) {
public CustomResult doCalculation(Integer setId, Premise premise, Destination destination, List<SectionInfo> 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<SectionInfo> getCustomRelevantRouteSections(List<SectionInfo> sections) {
private List<SectionInfo> getCustomRelevantRouteSections(Integer setId, List<SectionInfo> sections) {
List<SectionInfo> 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;

View file

@ -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));

View file

@ -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;
}

View file

@ -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");

View file

@ -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;

View file

@ -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<RouteSection> 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].");
}