diff --git a/src/main/java/de/avatic/lcc/calculationmodel/ContainerCalculationResult.java b/src/main/java/de/avatic/lcc/calculationmodel/ContainerCalculationResult.java index 207a610..2deaa38 100644 --- a/src/main/java/de/avatic/lcc/calculationmodel/ContainerCalculationResult.java +++ b/src/main/java/de/avatic/lcc/calculationmodel/ContainerCalculationResult.java @@ -215,7 +215,7 @@ public class ContainerCalculationResult { * @return The total utilization value for the container. */ public double getTotalUtilizationByVolume() { - return getHuUtilizationByVolume() * huUnitCount; + return getHuUtilizationByVolume() * huUnitCount * layer; } /** diff --git a/src/main/java/de/avatic/lcc/dto/calculation/edit/destination/DestinationUpdateDTO.java b/src/main/java/de/avatic/lcc/dto/calculation/edit/destination/DestinationUpdateDTO.java index 8080ba8..ee9f43b 100644 --- a/src/main/java/de/avatic/lcc/dto/calculation/edit/destination/DestinationUpdateDTO.java +++ b/src/main/java/de/avatic/lcc/dto/calculation/edit/destination/DestinationUpdateDTO.java @@ -7,6 +7,10 @@ import jakarta.validation.constraints.Min; public class DestinationUpdateDTO { + @JsonProperty("annual_amount") + @Min(1) + private Integer annualAmount; + @JsonProperty("repacking_cost") @DecimalMin(value = "0.00", message = "Amount must be greater than or equal 0") @Digits(integer = 13, fraction = 2, message = "Amount must have at most 2 decimal places") @@ -57,4 +61,12 @@ public class DestinationUpdateDTO { public void setRouteSelectedId(Integer routeSelectedId) { this.routeSelectedId = routeSelectedId; } + + public Integer getAnnualAmount() { + return annualAmount; + } + + public void setAnnualAmount(Integer annualAmount) { + this.annualAmount = annualAmount; + } } diff --git a/src/main/java/de/avatic/lcc/dto/generic/ContainerType.java b/src/main/java/de/avatic/lcc/dto/generic/ContainerType.java index 7eaf8da..37b5ba1 100644 --- a/src/main/java/de/avatic/lcc/dto/generic/ContainerType.java +++ b/src/main/java/de/avatic/lcc/dto/generic/ContainerType.java @@ -1,7 +1,10 @@ package de.avatic.lcc.dto.generic; public enum ContainerType { - FEU(12030, 2350, 2390, 67.7, 24,21), TEU(5890 ,2350,2390, 33.0, 11,10), HQ(12030, 2350, 2690, 76.4, 24,21), TRUCK(13600,2450, 2650, 88.3, 34, 33); + FEU(12030, 2350, 2390, 67.7, 24,21), + TEU(5890 ,2350,2390, 33.0, 11,10), + HQ(12030, 2350, 2690, 76.4, 24,21), + TRUCK(13600,2450, 2650, 88.3, 34, 33); private final int length; private final int width; diff --git a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobDestination.java b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobDestination.java index 447cdde..d7bd743 100644 --- a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobDestination.java +++ b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobDestination.java @@ -51,7 +51,6 @@ public class CalculationJobDestination { private String layerStructure; // JSON as String private Integer layerCount; private Boolean transportWeightExceeded; - private BigDecimal transportsPerYear; private BigDecimal annualTransportationCost; private BigDecimal containerUtilization; @@ -343,14 +342,6 @@ public class CalculationJobDestination { this.transportWeightExceeded = transportWeightExceeded; } - public BigDecimal getTransportsPerYear() { - return transportsPerYear; - } - - public void setTransportsPerYear(BigDecimal transportsPerYear) { - this.transportsPerYear = transportsPerYear; - } - public BigDecimal getAnnualTransportationCost() { return annualTransportationCost; } diff --git a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobRouteSection.java b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobRouteSection.java index 5d2e814..8fe7be5 100644 --- a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobRouteSection.java +++ b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobRouteSection.java @@ -27,6 +27,7 @@ private BigDecimal annualChanceCost; private BigDecimal annualCost; private Integer transitTime; + private BigDecimal utilization; public BigDecimal getAnnualRiskCost() { @@ -188,4 +189,12 @@ public void setDistance(BigDecimal distance) { this.distance = distance; } + + public void setUtilization(BigDecimal utilization) { + this.utilization = utilization; + } + + public BigDecimal getUtilization() { + return utilization; + } } diff --git a/src/main/java/de/avatic/lcc/model/packaging/PackagingDimension.java b/src/main/java/de/avatic/lcc/model/packaging/PackagingDimension.java index 8e7c36f..458e352 100644 --- a/src/main/java/de/avatic/lcc/model/packaging/PackagingDimension.java +++ b/src/main/java/de/avatic/lcc/model/packaging/PackagingDimension.java @@ -145,4 +145,8 @@ public class PackagingDimension { var widthInX = unit.convertFromMM(width); return lengthInX.doubleValue() * widthInX.doubleValue(); } + + public double getWeight(WeightUnit weightUnit) { + return weightUnit.convertFromG(this.weight); + } } diff --git a/src/main/java/de/avatic/lcc/model/premises/Premise.java b/src/main/java/de/avatic/lcc/model/premises/Premise.java index 5bc37f5..4011447 100644 --- a/src/main/java/de/avatic/lcc/model/premises/Premise.java +++ b/src/main/java/de/avatic/lcc/model/premises/Premise.java @@ -34,7 +34,7 @@ public class Premise { private String hsCode; @Digits(integer = 7, fraction = 2) - private BigDecimal customRate; + private BigDecimal tariffRate; @Size(max = 16) private PremiseState state; @@ -157,12 +157,12 @@ public class Premise { this.hsCode = hsCode; } - public BigDecimal getCustomRate() { - return customRate; + public BigDecimal getTariffRate() { + return tariffRate; } - public void setCustomRate(BigDecimal customRate) { - this.customRate = customRate; + public void setTariffRate(BigDecimal tariffRate) { + this.tariffRate = tariffRate; } public PremiseState getState() { diff --git a/src/main/java/de/avatic/lcc/model/properties/SystemPropertyMappingId.java b/src/main/java/de/avatic/lcc/model/properties/SystemPropertyMappingId.java index e8f5a80..f3f19dd 100644 --- a/src/main/java/de/avatic/lcc/model/properties/SystemPropertyMappingId.java +++ b/src/main/java/de/avatic/lcc/model/properties/SystemPropertyMappingId.java @@ -27,7 +27,7 @@ public enum SystemPropertyMappingId { TRUCK_LOAD("Maximum truck load [kg]", "25000"), START_REF("Reference route segment start node mapping id", "CNXMN"), - END_REF("Reference route segment end node mapping id","HAM"), + END_REF("Reference route segment end node mapping id","DEHAM"), RISK_REF("The historically highest rate for the reference route segment for 40ft containers [EUR]", "1000,00 €"), CHANCE_REF("The historically lowest rate for the reference route segment for 40ft containers [EUR]", "20000,00 €"), diff --git a/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobDestinationRepository.java b/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobDestinationRepository.java index 499f713..a6b03f8 100644 --- a/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobDestinationRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobDestinationRepository.java @@ -86,7 +86,6 @@ public class CalculationJobDestinationRepository { entity.setLayerStructure(rs.getString("layer_structure")); entity.setLayerCount(rs.getInt("layer_count")); entity.setTransportWeightExceeded(rs.getBoolean("transport_weight_exceeded")); - entity.setTransportsPerYear(rs.getBigDecimal("transports_per_year")); entity.setShippingFrequency(rs.getInt("shipping_frequency")); entity.setAnnualTransportationCost(rs.getBigDecimal("annual_transportation_cost")); diff --git a/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRepository.java b/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRepository.java index a2b453f..35752d0 100644 --- a/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRepository.java @@ -3,13 +3,15 @@ package de.avatic.lcc.repositories.calculation; import de.avatic.lcc.model.calculations.CalculationJob; import de.avatic.lcc.model.calculations.CalculationJobDestination; import de.avatic.lcc.model.calculations.CalculationJobState; +import de.avatic.lcc.util.exception.internalerror.DatabaseException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import java.sql.ResultSet; -import java.sql.SQLException; +import javax.xml.crypto.Data; +import java.sql.*; import java.util.List; import java.util.Optional; @@ -23,7 +25,28 @@ public class CalculationJobRepository { @Transactional public Integer insert(CalculationJob job) { - return null; //TODO implement me + String sql = """ + INSERT INTO calculation_job (premise_id, calculation_date, validity_period_id, property_set_id, job_state, user_id) + VALUES (?, ?, ?, ?, ?, ?) + """; + + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + ps.setInt(1, job.getPremiseId()); + + ps.setTimestamp(2, job.getCalculationDate() == null ? null : Timestamp.valueOf(job.getCalculationDate())); + + ps.setInt(3, job.getValidityPeriodId()); + ps.setInt(4, job.getPropertySetId()); + ps.setString(5, job.getJobState().name()); // Convert enum to string + ps.setInt(6, job.getUserId()); + return ps; + }, keyHolder); + + // Return the generated ID + return keyHolder.getKey() != null ? keyHolder.getKey().intValue() : null; } @Transactional @@ -52,6 +75,16 @@ public class CalculationJobRepository { return Optional.of(job.getFirst()); } + public void setStateTo(Integer id, CalculationJobState calculationJobState) { + String sql = "UPDATE calculation_job SET job_state = ? WHERE id = ?"; + + var affectedRows = jdbcTemplate.update(sql, calculationJobState.name(), id); + + if(1 != affectedRows) { + throw new DatabaseException("Unable to update calculation job with id " + id); + } + } + private static class CalculationJobMapper implements RowMapper { @Override diff --git a/src/main/java/de/avatic/lcc/repositories/premise/DestinationRepository.java b/src/main/java/de/avatic/lcc/repositories/premise/DestinationRepository.java index c7bdf78..c4c265a 100644 --- a/src/main/java/de/avatic/lcc/repositories/premise/DestinationRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/premise/DestinationRepository.java @@ -51,7 +51,7 @@ public class DestinationRepository { } @Transactional - public void update(Integer id, BigDecimal repackingCost, BigDecimal disposalCost, BigDecimal handlingCost) { + public void update(Integer id, Integer annualAmount, BigDecimal repackingCost, BigDecimal disposalCost, BigDecimal handlingCost) { if (id == null) { throw new InvalidArgumentException("ID cannot be null"); } @@ -76,6 +76,11 @@ public class DestinationRepository { parameters.put("handlingCost", handlingCost); } + if (annualAmount != null) { + setClauses.add("annual_amount = :annualAmount"); + parameters.put("annualAmount", annualAmount); + } + // If no parameters to update, return early if (setClauses.isEmpty()) { return; @@ -125,7 +130,7 @@ public class DestinationRepository { @Transactional public List getByPremiseIdsAndNodeId(List premiseId, Integer nodeId, Integer userId) { String placeholder = String.join(",", Collections.nCopies(premiseId.size(), "?")); - String query = "SELECT * FROM premise_destination JOIN premise ON premise_destination.premise_id = premise.id WHERE premise_id IN ("+placeholder+") AND premise_destination.destination_node_id AND premise.user_id = ?"; + String query = "SELECT * FROM premise_destination JOIN premise ON premise_destination.premise_id = premise.id WHERE premise_id IN ("+placeholder+") AND premise_destination.destination_node_id = ? AND premise.user_id = ?"; return jdbcTemplate.query(query, new DestinationMapper(), premiseId, nodeId, userId); } @@ -134,7 +139,7 @@ public class DestinationRepository { public Integer insert(Destination destination) { KeyHolder keyHolder = new GeneratedKeyHolder(); - String query = "INSERT INTO premise_destination (annual_amount, premise_id, destination_node_id, country_id, rate_d2d, lead_time_d2d, is_d2d, repacking_cost, handling_cost, disposal_cost) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + String query = "INSERT INTO premise_destination (annual_amount, premise_id, destination_node_id, country_id, rate_d2d, lead_time_d2d, is_d2d, repacking_cost, handling_cost, disposal_cost, geo_lat, geo_lng) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; jdbcTemplate.update(connection -> { var ps = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); @@ -159,6 +164,10 @@ public class DestinationRepository { ps.setBigDecimal(8, destination.getRepackingCost()); ps.setBigDecimal(9, destination.getHandlingCost()); ps.setBigDecimal(10, destination.getDisposalCost()); + + ps.setBigDecimal(11, destination.getGeoLat()); + ps.setBigDecimal(12, destination.getGeoLng()); + return ps; }, keyHolder); diff --git a/src/main/java/de/avatic/lcc/repositories/premise/PremiseRepository.java b/src/main/java/de/avatic/lcc/repositories/premise/PremiseRepository.java index 5543740..bad367a 100644 --- a/src/main/java/de/avatic/lcc/repositories/premise/PremiseRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/premise/PremiseRepository.java @@ -832,7 +832,7 @@ public class PremiseRepository { entity.setHsCode(rs.getString("hs_code")); - entity.setCustomRate(rs.getBigDecimal("tariff_rate")); + entity.setTariffRate(rs.getBigDecimal("tariff_rate")); entity.setFcaEnabled(rs.getBoolean("is_fca_enabled")); if(rs.wasNull()) diff --git a/src/main/java/de/avatic/lcc/service/access/DestinationService.java b/src/main/java/de/avatic/lcc/service/access/DestinationService.java index ad5e10f..aff3525 100644 --- a/src/main/java/de/avatic/lcc/service/access/DestinationService.java +++ b/src/main/java/de/avatic/lcc/service/access/DestinationService.java @@ -121,6 +121,7 @@ public class DestinationService { destinationRepository.update(id, + destinationUpdateDTO.getAnnualAmount(), destinationUpdateDTO.getRepackingCost() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getRepackingCost().doubleValue()), destinationUpdateDTO.getDisposalCost() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getDisposalCost().doubleValue()), destinationUpdateDTO.getHandlingCost() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getHandlingCost().doubleValue())); 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 671ad51..e32b9dd 100644 --- a/src/main/java/de/avatic/lcc/service/access/PremisesService.java +++ b/src/main/java/de/avatic/lcc/service/access/PremisesService.java @@ -79,6 +79,7 @@ public class PremisesService { + @Transactional public Integer startCalculation(List premises) { var userId = 1; // TODO get current user id @@ -101,11 +102,19 @@ public class PremisesService { calculationIds.add(calculationJobRepository.insert(job)); }); + //TODO set premise to completed. - calculationIds.forEach(calculationExecutionService::calculateJob); + + calculationIds.forEach(this::scheduleCalculation); return calculationStatusService.schedule(calculationIds); } + //TODO: scheduled should be set by worker thread that processes the job. + public void scheduleCalculation(Integer id) { + calculationJobRepository.setStateTo(id, CalculationJobState.SCHEDULED); + calculationExecutionService.calculateJob(id); + } + public CalculationStatus getCalculationStatus(Integer processId) { return calculationStatusService.getCalculationStatus(processId); } diff --git a/src/main/java/de/avatic/lcc/service/calculation/PremiseCreationService.java b/src/main/java/de/avatic/lcc/service/calculation/PremiseCreationService.java index 352142a..ef70c6d 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/PremiseCreationService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/PremiseCreationService.java @@ -97,7 +97,7 @@ public class PremiseCreationService { private void copyPremise(TemporaryPremise p, Integer userId) { var old = p.getPremise(); - premiseRepository.updateMaterial(Collections.singletonList(p.getId()), old.getHsCode(), old.getCustomRate()); + premiseRepository.updateMaterial(Collections.singletonList(p.getId()), old.getHsCode(), old.getTariffRate()); premiseRepository.updatePrice(Collections.singletonList(p.getId()), old.getMaterialCost(), old.getFcaEnabled(), old.getOverseaShare()); premiseRepository.updatePackaging(Collections.singletonList(p.getId()), dimensionTransformer.toDimensionEntity(old), old.getHuStackable(), old.getHuMixable()); premiseRepository.setPackagingId(p.getId(), old.getId()); 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 38b9ce6..9dfaaca 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 @@ -21,15 +21,19 @@ import de.avatic.lcc.repositories.premise.RouteRepository; import de.avatic.lcc.repositories.premise.RouteSectionRepository; import de.avatic.lcc.repositories.properties.PropertyRepository; import de.avatic.lcc.service.calculation.execution.steps.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.*; @Service public class CalculationExecutionService { + private static final Logger log = LoggerFactory.getLogger(CalculationExecutionService.class); private final PremiseRepository premiseRepository; private final DestinationRepository destinationRepository; private final RouteRepository routeRepository; @@ -46,6 +50,7 @@ public class CalculationExecutionService { private final CalculationJobRepository calculationJobRepository; private final CalculationJobDestinationRepository calculationJobDestinationRepository; + public CalculationExecutionService(PremiseRepository premiseRepository, DestinationRepository destinationRepository, RouteRepository routeRepository, RouteSectionRepository routeSectionRepository, CustomCostCalculationService customCostCalculationService, RouteSectionCostCalculationService routeSectionCostCalculationService, HandlingCostCalculationService handlingCostCalculationService, InventoryCostCalculationService inventoryCostCalculationService, PropertyRepository propertyRepository, AirfreightCalculationService airfreightCalculationService, PremiseToHuService premiseToHuService, ContainerCalculationService containerCalculationService, ShippingFrequencyCalculationService shippingFrequencyCalculationService, CalculationJobRepository calculationJobRepository, CalculationJobDestinationRepository calculationJobDestinationRepository) { this.premiseRepository = premiseRepository; this.destinationRepository = destinationRepository; @@ -79,7 +84,7 @@ public class CalculationExecutionService { CalculationJob calculation = calculationJobRepository.getCalculationJob(calculationId).orElseThrow(); - if(CalculationJobState.SCHEDULED.equals(calculation.getJobState())) { + if (CalculationJobState.SCHEDULED.equals(calculation.getJobState())) { Premise premise = premiseRepository.getPremiseById(calculation.getId()).orElseThrow(); // material cost + fca cost @@ -100,10 +105,10 @@ public class CalculationExecutionService { //TODO: save the stuff //calculationJobDestinationRepository.createDestination(destinationInfos); + log.info("Calculation job {} finished", calculationId); } - } private DestinationInfo doDestinationCalculation(Destination destination, Premise premise, BigDecimal materialCost, BigDecimal fcaFee) { @@ -113,46 +118,31 @@ public class CalculationExecutionService { CustomResult customCost; List sections; AirfreightResult airfreightCost = airfreightCalculationService.doCalculation(premise, destination); + ContainerType usedContainerType = null; CalculationJobDestination destinationCalculationJob = new CalculationJobDestination(); + boolean hasMainRun = true; + BigDecimal leadTime = null; if (destination.getD2d()) { - - // Get container calculation - PackagingDimension hu = premiseToHuService.createHuFromPremise(premise); - var containerCalculation = containerCalculationService.doCalculation(hu, ContainerType.FEU); - - var routeCost = routeSectionCostCalculationService.doD2dCalculation(premise, destination, containerCalculation); - handlingCost = handlingCostCalculationService.doCalculation(premise, destination, true); - inventoryCost = inventoryCostCalculationService.doCalculation(premise, destination, BigDecimal.valueOf(destination.getLeadTimeD2d())); - customCost = customCostCalculationService.doD2dCalculation(premise, destination, routeCost); - sections = List.of(new SectionInfo(null, routeCost, containerCalculation)); - - destinationCalculationJob.setContainerType(ContainerType.FEU); - + var containerCalculation = containerCalculationService.doCalculation(premiseToHuService.createHuFromPremise(premise), ContainerType.FEU); + sections = List.of(new SectionInfo(null, routeSectionCostCalculationService.doD2dCalculation(premise, destination, containerCalculation), containerCalculation)); + leadTime = BigDecimal.valueOf(destination.getLeadTimeD2d()); + usedContainerType = ContainerType.FEU; } else { - var route = routeRepository.getSelectedByDestinationId(destination.getId()).orElseThrow(); - List routeSections = routeSectionRepository.getByRouteId(route.getId()); - - Map> sectionInfos = new HashMap<>(); - Map containerCalculation = new HashMap<>(); - - // Get container calculation - PackagingDimension hu = premiseToHuService.createHuFromPremise(premise); - Arrays.stream(ContainerType.values()).forEach(type -> containerCalculation.put(type, containerCalculationService.doCalculation(hu, type))); - - Arrays.stream(ContainerType.values()).forEach(type -> sectionInfos.put(type, routeSections.stream().map(s -> new SectionInfo(s, doSectionCalculation(premise, destination, s, type, containerCalculation), containerCalculation.get(type))).toList())); - ContainerType bestContainerType = getBestContainerType(sectionInfos); - - customCost = customCostCalculationService.doCalculation(premise, destination, sectionInfos.get(bestContainerType)); - handlingCost = handlingCostCalculationService.doCalculation(premise, destination, sectionInfos.get(bestContainerType).stream().anyMatch(s -> s.section().getMainRun())); - inventoryCost = inventoryCostCalculationService.doCalculation(premise, destination, BigDecimal.valueOf(sectionInfos.get(bestContainerType).stream().mapToInt(s -> s.result().getTransitTime()).sum())); - sections = sectionInfos.get(bestContainerType); - - destinationCalculationJob.setContainerType(bestContainerType); - + var bestContainerTypeResult = getSectionsFromBestContainerType(destination, premise); + sections = bestContainerTypeResult.sections; + usedContainerType = bestContainerTypeResult.containerType; + hasMainRun = sections.stream().anyMatch(s -> s.section().getMainRun()); + leadTime = BigDecimal.valueOf(sections.stream().mapToInt(s -> s.result().getTransitTime()).sum()); } + customCost = customCostCalculationService.doCalculation(premise, destination, sections); + handlingCost = handlingCostCalculationService.doCalculation(premise, destination, hasMainRun); + inventoryCost = inventoryCostCalculationService.doCalculation(premise, destination, leadTime); + + destinationCalculationJob.setContainerType(usedContainerType); + destinationCalculationJob.setMaterialCost(materialCost); destinationCalculationJob.setFcaCost(fcaFee); @@ -205,15 +195,43 @@ public class CalculationExecutionService { destinationCalculationJob.setTotalRiskCost(totalRiskCost); destinationCalculationJob.setTotalChanceCost(totalChanceCost); + destinationCalculationJob.setTotalTransitTime(sections.stream().mapToInt(s -> s.result().getTransitTime()).sum()); + destinationCalculationJob.setContainerUtilization(sections.stream().map(s -> s.result().getUtilization()).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(sections.size()), 10, RoundingMode.HALF_UP)); + return new DestinationInfo(destination, destinationCalculationJob, sections); } - private CalculationJobRouteSection doSectionCalculation(Premise premise, Destination destination, RouteSection section, ContainerType containerType, Map calculationResult) { - if (RateType.MATRIX == section.getRateType()) { - return routeSectionCostCalculationService.doCalculation(premise, destination, section, calculationResult.get(ContainerType.TRUCK)); - } else { - return routeSectionCostCalculationService.doCalculation(premise, destination, section, calculationResult.get(containerType)); + private BestContainerTypeResult getSectionsFromBestContainerType(Destination destination, Premise premise) { + PackagingDimension hu = premiseToHuService.createHuFromPremise(premise); + + var route = routeRepository.getSelectedByDestinationId(destination.getId()).orElseThrow(); + List routeSections = routeSectionRepository.getByRouteId(route.getId()); + + Map> sectionInfos = new HashMap<>(); + Map containerCalculation = new HashMap<>(); + + // Get container calculation + for (var containerType : ContainerType.values()) { + containerCalculation.put(containerType, containerCalculationService.doCalculation(hu, containerType)); + + if (!containerType.equals(ContainerType.TRUCK)) { + + var sectionInfo = new ArrayList(); + + 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))); + } + + sectionInfos.put(containerType, sectionInfo); + } + } + var bestContainerType = getBestContainerType(sectionInfos); + return new BestContainerTypeResult(bestContainerType, sectionInfos.get(bestContainerType)); + } + + private record BestContainerTypeResult(ContainerType containerType, List sections) { } private record DestinationInfo(Destination destination, CalculationJobDestination destinationCalculationJob, 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 3ce6a20..3711196 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 @@ -27,12 +27,12 @@ public class ChangeRiskFactorCalculationService { public ChangeRiskFactorCalculationResult getChanceRiskFactors() { var rate = findRate(); - var riskValue = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.RISK_REF).orElseThrow().getCurrentValue()); - var chanceValue = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.CHANCE_REF).orElseThrow().getCurrentValue()); + 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())); var result = new ChangeRiskFactorCalculationResult(); - result.setChanceFactor(rate.getRateFeu().divide(BigDecimal.valueOf(chanceValue), 2, RoundingMode.HALF_UP)); - result.setRiskFactor(rate.getRateFeu().divide(BigDecimal.valueOf(riskValue), 2, RoundingMode.HALF_UP)); + result.setChanceFactor(chanceValue.divide(rate.getRateFeu(), 2, RoundingMode.HALF_UP)); + result.setRiskFactor(riskValue.divide(rate.getRateFeu(), 2, RoundingMode.HALF_UP)); return result; } 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 3211cd3..263be73 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 @@ -7,6 +7,7 @@ import de.avatic.lcc.model.packaging.PackagingDimension; import de.avatic.lcc.model.properties.SystemPropertyMappingId; import de.avatic.lcc.model.utils.WeightUnit; import de.avatic.lcc.repositories.properties.PropertyRepository; +import org.jetbrains.annotations.Debug.Renderer; import org.springframework.stereotype.Service; import java.util.HashMap; @@ -31,14 +32,14 @@ public class ContainerCalculationService { var dimensions = hu.withTolerance(DIMENSION_TOLERANCE); - var solutionHorizontal = solveLayer(dimensions, containerType.getLength(), containerType.getWidth()); - var solutionVertical = solveLayer(dimensions, containerType.getWidth(), containerType.getLength()); + var solutionHorizontal = solveLayer(SolutionType.HORIZONTAL, dimensions, containerType.getLength(), containerType.getWidth()); + var solutionVertical = solveLayer(SolutionType.VERTICAL, dimensions, containerType.getWidth(), containerType.getLength()); var bestSolution = solutionHorizontal.getTotal() < solutionVertical.getTotal() ? solutionVertical : solutionHorizontal; int layers = getLayerCount(dimensions, containerType); - if(PalletType.EURO_PALLET.fitsOn(dimensions) && bestSolution.getTotal() < containerType.getPalletCount(PalletType.EURO_PALLET)) { + if(PalletType.EURO_PALLET.fitsOn(hu) && bestSolution.getTotal() < containerType.getPalletCount(PalletType.EURO_PALLET)) { return new ContainerCalculationResult(Math.min(containerType.getPalletCount(PalletType.EURO_PALLET)*layers,maxUnitByWeight), layers, null, containerType.getPalletCount(PalletType.EURO_PALLET) > maxUnitByWeight, containerType, dimensions, maxContainerLoad); - } else if(PalletType.INDUSTRIAL_PALLET.fitsOn(dimensions) && bestSolution.getTotal() < containerType.getPalletCount(PalletType.INDUSTRIAL_PALLET)) { + } else if(PalletType.INDUSTRIAL_PALLET.fitsOn(hu) && bestSolution.getTotal() < containerType.getPalletCount(PalletType.INDUSTRIAL_PALLET)) { return new ContainerCalculationResult(Math.min(containerType.getPalletCount(PalletType.INDUSTRIAL_PALLET)*layers,maxUnitByWeight), layers, null, containerType.getPalletCount(PalletType.INDUSTRIAL_PALLET) > maxUnitByWeight, containerType, dimensions, maxContainerLoad); } @@ -46,14 +47,15 @@ public class ContainerCalculationService { } - private static Solution solveLayer(PackagingDimension dimension, + + private static Solution solveLayer(SolutionType type, PackagingDimension dimension, int containerWidth, int containerLength) { var horizontalColumn = new Column(new Block(dimension, BlockType.HORIZONTAL), containerWidth); var verticalColumn = new Column(new Block(dimension, BlockType.VERTICAL), containerWidth); var circularColumn = new Column(new Block(dimension, BlockType.CIRCULAR), containerWidth); - Solution bestSolution = new Solution(0, 0, 0, horizontalColumn, verticalColumn, circularColumn); + Solution bestSolution = new Solution(type,0, 0, 0, horizontalColumn, verticalColumn, circularColumn); int maxHorizontalColumns = containerLength / horizontalColumn.getColumnWidth(); int maxVerticalColumns = containerLength / verticalColumn.getColumnWidth(); @@ -66,7 +68,7 @@ public class ContainerCalculationService { if (fitsInContainer(horizontalColumn, verticalColumn, circularColumn, horizontalColumnCount, verticalColumnCount, circularColumnCount, containerLength)) { - Solution current = new Solution( + Solution current = new Solution(type, horizontalColumnCount, verticalColumnCount, circularColumnCount, horizontalColumn, verticalColumn, circularColumn); @@ -93,7 +95,8 @@ public class ContainerCalculationService { Map mappings = Map.of( ContainerType.FEU, SystemPropertyMappingId.FEU_LOAD, ContainerType.TRUCK, SystemPropertyMappingId.TRUCK_LOAD, - ContainerType.TEU, SystemPropertyMappingId.TEU_LOAD + ContainerType.TEU, SystemPropertyMappingId.TEU_LOAD, + ContainerType.HQ, SystemPropertyMappingId.FEU_LOAD ); SystemPropertyMappingId mappingId = mappings.get(containerType); @@ -101,9 +104,11 @@ public class ContainerCalculationService { throw new IllegalArgumentException("Unsupported container type: " + containerType); } - return Integer.parseInt(propertyRepository.getPropertyByMappingId(mappingId) + var value = propertyRepository.getPropertyByMappingId(mappingId) .orElseThrow(() -> new IllegalStateException("Missing property: " + mappingId)) - .getCurrentValue()); + .getCurrentValue(); + + return Integer.parseInt(value); } private int getLayerCount(PackagingDimension dimensions, ContainerType containerType) { @@ -115,7 +120,7 @@ public class ContainerCalculationService { ContainerType containerType, PackagingDimension hu, int maxContainerLoad) { int totalUnits = Math.min(solution.getTotal() * layers, maxUnitByWeight); - boolean weightExceeded = solution.getTotal() > maxUnitByWeight; + boolean weightExceeded = (solution.getTotal()* layers) > maxUnitByWeight; return new ContainerCalculationResult( totalUnits, layers, null, weightExceeded, @@ -153,19 +158,27 @@ public class ContainerCalculationService { } + private static enum SolutionType { + HORIZONTAL, VERTICAL + } + + @Renderer(text = "getFullDebugInfo()") private static class Solution { + SolutionType type; + Map rowCount = new HashMap<>(); Map columns = new HashMap<>(); - public Solution(int horizontalCount, int verticalCount, int circularCount, Column horizontalColumn, Column verticalColumn, Column circularColumn) { + public Solution(SolutionType type, int horizontalCount, int verticalCount, int circularCount, Column horizontalColumn, Column verticalColumn, Column circularColumn) { rowCount.put(BlockType.HORIZONTAL, horizontalCount); rowCount.put(BlockType.VERTICAL, verticalCount); rowCount.put(BlockType.CIRCULAR, circularCount); columns.put(BlockType.HORIZONTAL, horizontalColumn); columns.put(BlockType.VERTICAL, verticalColumn); columns.put(BlockType.CIRCULAR, circularColumn); + this.type = type; } public int getTotal() { @@ -173,8 +186,14 @@ public class ContainerCalculationService { + rowCount.get(BlockType.VERTICAL) * columns.get(BlockType.VERTICAL).getResultValue() + rowCount.get(BlockType.CIRCULAR) * columns.get(BlockType.CIRCULAR).getResultValue(); } + + public String getFullDebugInfo() { + return String.format("Solution [%s HUs: %d]", + type, getTotal()); + } } + @Renderer(text = "getFullDebugInfo()") private static class Column { private final int blocksPerColumn; @@ -192,8 +211,13 @@ public class ContainerCalculationService { public int getColumnWidth() { return block.getBlockWidth(); } + + public String getFullDebugInfo() { + return "Column " + blocksPerColumn + "x" + block.getFullDebugInfo(); + } } + @Renderer(text = "getFullDebugInfo()") private static class Block { private final PackagingDimension dimension; @@ -215,5 +239,9 @@ public class ContainerCalculationService { public int getBlockWidth() { return type.getColumnWidth(dimension); } + + public String getFullDebugInfo() { + return "BLOCK %s W%s L%s H%s".formatted(type.name(), type.getColumnWidth(dimension), type.getBlockColumnLength(dimension), dimension.getHeight()); + } } } 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 e26ec94..ee82a0e 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 @@ -1,7 +1,6 @@ package de.avatic.lcc.service.calculation.execution.steps; import de.avatic.lcc.calculationmodel.CustomResult; -import de.avatic.lcc.model.calculations.CalculationJobRouteSection; import de.avatic.lcc.model.premises.Premise; import de.avatic.lcc.model.premises.route.Destination; import de.avatic.lcc.model.properties.CountryPropertyMappingId; @@ -35,7 +34,7 @@ public class CustomCostCalculationService { this.shippingFrequencyCalculationService = shippingFrequencyCalculationService; } - public CustomResult doD2dCalculation(Premise premise, Destination destination, CalculationJobRouteSection routeCost) { + public CustomResult doD2dCalculation(Premise premise, Destination destination, List sections) { var destUnion = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, destination.getCountryId()).orElseThrow(); var sourceUnion = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, premise.getCountryId()).orElseThrow(); @@ -43,7 +42,7 @@ public class CustomCostCalculationService { if (!CustomUnionType.EU.name().equals(destUnion.getCurrentValue()) || !CustomUnionType.NONE.name().equals(sourceUnion.getCurrentValue())) return CustomResult.EMPTY; - return getCustomCalculationResult(premise, destination, routeCost.getAnnualCost(), routeCost.getAnnualChanceCost(), routeCost.getAnnualRiskCost()); + return getCustomCalculationResult(premise, destination, sections.getFirst().result().getAnnualCost(), sections.getFirst().result().getAnnualChanceCost(), sections.getFirst().result().getAnnualRiskCost()); } public CustomResult doCalculation(Premise premise, Destination destination, List sections) { @@ -51,10 +50,14 @@ public class CustomCostCalculationService { var destUnion = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, destination.getCountryId()).orElseThrow(); var sourceUnion = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, premise.getCountryId()).orElseThrow(); - if (CustomUnionType.EU.name().equals(destUnion.getCurrentValue()) && CustomUnionType.NONE.name().equals(sourceUnion.getCurrentValue())) { - var transportationCost = getCustomRelevantRouteSections(sections).stream().map(s -> s.result().getAnnualCost()).reduce(BigDecimal.ZERO, BigDecimal::add); - var transportationChanceCost = getCustomRelevantRouteSections(sections).stream().map(s -> s.result().getAnnualChanceCost()).reduce(BigDecimal.ZERO, BigDecimal::add); - var transportationRiskCost = getCustomRelevantRouteSections(sections).stream().map(s -> s.result().getAnnualChanceCost()).reduce(BigDecimal.ZERO, BigDecimal::add); + if (CustomUnionType.EU.name().equals(destUnion.getCurrentValue()) && (sourceUnion.getCurrentValue() == null || CustomUnionType.NONE.name().equals(sourceUnion.getCurrentValue()))) { + + var relevantSections = getCustomRelevantRouteSections(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); + var transportationRiskCost = relevantSections.stream().map(s -> s.result().getAnnualRiskCost()).reduce(BigDecimal.ZERO, BigDecimal::add); + return getCustomCalculationResult(premise, destination, transportationCost, transportationChanceCost, transportationRiskCost); } @@ -64,7 +67,8 @@ public class CustomCostCalculationService { private CustomResult getCustomCalculationResult(Premise premise, Destination destination, BigDecimal transportationCost, BigDecimal transportationChanceCost, BigDecimal transportationRiskCost) { var shippingFrequency = shippingFrequencyCalculationService.doCalculation(destination.getAnnualAmount()); var customFee = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.CUSTOM_FEE).orElseThrow().getCurrentValue()); - var tariffRate = customApiService.getTariffRate(premise.getHsCode(), premise.getCountryId()); + + var tariffRate = premise.getTariffRate() == null ? customApiService.getTariffRate(premise.getHsCode(), premise.getCountryId()) : premise.getTariffRate(); var materialCost = premise.getMaterialCost(); var fcaFee = BigDecimal.ZERO; @@ -96,8 +100,8 @@ public class CustomCostCalculationService { List customSections = new ArrayList<>(); for (SectionInfo section : sections) { - if (CustomUnionType.EU == getCustomUnionByRouteNodeId(section.section().getFromRouteNodeId()) && - CustomUnionType.EU == getCustomUnionByRouteNodeId(section.section().getToRouteNodeId())) { + if (!(CustomUnionType.EU == getCustomUnionByRouteNodeId(section.section().getFromRouteNodeId()) && + CustomUnionType.EU == getCustomUnionByRouteNodeId(section.section().getToRouteNodeId()))) { customSections.add(section); } } @@ -112,6 +116,9 @@ public class CustomCostCalculationService { private CustomUnionType getCustomUnionByCountryId(Integer countryId) { var property = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, countryId).orElseThrow(); + if(property.getCurrentValue() == null) + return CustomUnionType.NONE; + return CustomUnionType.valueOf(property.getCurrentValue().toUpperCase()); } 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 b24ffd5..521b668 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 @@ -20,11 +20,13 @@ public class HandlingCostCalculationService { private final CountryPropertyRepository countryPropertyRepository; private final PropertyRepository propertyRepository; private final PremiseToHuService premiseToHuService; + private final ShippingFrequencyCalculationService shippingFrequencyCalculationService; - public HandlingCostCalculationService(CountryPropertyRepository countryPropertyRepository, PropertyRepository propertyRepository, PremiseToHuService premiseToHuService) { + public HandlingCostCalculationService(CountryPropertyRepository countryPropertyRepository, PropertyRepository propertyRepository, PremiseToHuService premiseToHuService, ShippingFrequencyCalculationService shippingFrequencyCalculationService) { this.countryPropertyRepository = countryPropertyRepository; this.propertyRepository = propertyRepository; this.premiseToHuService = premiseToHuService; + this.shippingFrequencyCalculationService = shippingFrequencyCalculationService; } public HandlingResult doCalculation(Premise premise, Destination destination, Boolean addRepackingCosts) { @@ -56,9 +58,9 @@ public class HandlingCostCalculationService { return switch (type) { case SLC -> - Double.parseDouble((hu.getWeight() < 15.000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_S).orElseThrow().getCurrentValue() : propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_M).orElseThrow().getCurrentValue()); + Double.parseDouble((hu.getWeight() < 15_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_S).orElseThrow().getCurrentValue() : propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_M).orElseThrow().getCurrentValue()); case LLC -> - Double.parseDouble(((hu.getWeight() < 15.000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_S) : (hu.getWeight() < 2000.000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_M) : propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_L)).orElseThrow().getCurrentValue()); + 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()); }; } @@ -73,11 +75,11 @@ public class HandlingCostCalculationService { double wageFactor = Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.WAGE, destination.getCountryId()).orElseThrow().getCurrentValue()); double booking = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING).orElseThrow().getCurrentValue()); - return new HandlingResult(LoadCarrierType.SLC, - BigDecimal.valueOf(getRepackingCost(hu, type, addRepackingCosts) * huAnnualAmount), - BigDecimal.valueOf(handling*huAnnualAmount), - BigDecimal.valueOf(disposal * huAnnualAmount), - BigDecimal.valueOf(huAnnualAmount * (((handling + booking + release + dispatch + getRepackingCost(hu, type, addRepackingCosts)) * wageFactor) + disposal))); + var annualRepacking = BigDecimal.valueOf(getRepackingCost(hu, type, addRepackingCosts) * wageFactor * huAnnualAmount); + var annualHandling = BigDecimal.valueOf((handling+dispatch+release)*wageFactor*huAnnualAmount + (booking * shippingFrequencyCalculationService.doCalculation(huAnnualAmount))); + var annualDisposal = BigDecimal.valueOf(disposal * 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 328f2e2..910b831 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 @@ -48,20 +48,20 @@ public class InventoryCostCalculationService { var interestRate = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.INTEREST_RATE).orElseThrow().getCurrentValue())); var annualAmount = BigDecimal.valueOf(destination.getAnnualAmount().doubleValue()); - var dailyAmount = annualAmount.divide(BigDecimal.valueOf(365)); - var workdayAmount = annualAmount.divide(workdays); + 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(destination.getAnnualAmount()),1))).multiply(BigDecimal.valueOf(.5))); + var opStock = (annualAmount.divide(BigDecimal.valueOf(Math.max(shippingFrequencyCalculationService.doCalculation(destination.getAnnualAmount()),1)), 10, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(.5))); var safetyStock = safetydays.multiply(workdayAmount); var stockedInventory = opStock.add(safetyStock); var inTransportStock = dailyAmount.multiply(leadTime); var stockBeforePayment = dailyAmount.multiply(paymentTerms); var capitalCostStock = stockedInventory.add(inTransportStock).subtract(stockBeforePayment); - var storageCostStock = roundToHu(hu, safetyStock).add(opStock); + var storageCostStock = roundToHu(hu, safetyStock.add(opStock)); var capitalCost = capitalCostStock.multiply(interestRate).multiply(premise.getMaterialCost().add(fcaFee)); - var storageCost = storageCostStock.divide(BigDecimal.valueOf(hu.getContentUnitCount())).multiply(getSpaceCostPerHu(hu)); + var storageCost = storageCostStock.multiply(getSpaceCostPerHu(hu)).multiply(BigDecimal.valueOf(365)); return new InventoryCostResult(opStock, safetyStock, stockedInventory, inTransportStock, stockBeforePayment, capitalCost, storageCost, safetydays ); @@ -70,7 +70,8 @@ public class InventoryCostCalculationService { private BigDecimal getSpaceCostPerHu(PackagingDimension hu) { var spaceCost = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.SPACE_COST).orElseThrow().getCurrentValue())); - return BigDecimal.valueOf(hu.getFloorArea(DimensionUnit.M)*hu.getRoundedHeight(DimensionUnit.M)).multiply(spaceCost); + var spaceCostPerHu = BigDecimal.valueOf(hu.getFloorArea(DimensionUnit.M)*hu.getRoundedHeight(DimensionUnit.M)).multiply(spaceCost); + return spaceCostPerHu; } private BigDecimal roundToHu(PackagingDimension hu, BigDecimal safetyStock) { 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 36f7623..99ced59 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 @@ -14,6 +14,7 @@ import de.avatic.lcc.model.properties.SystemPropertyMappingId; import de.avatic.lcc.model.rates.ContainerRate; import de.avatic.lcc.model.rates.MatrixRate; import de.avatic.lcc.model.utils.DimensionUnit; +import de.avatic.lcc.model.utils.WeightUnit; import de.avatic.lcc.repositories.premise.RouteNodeRepository; import de.avatic.lcc.repositories.properties.PropertyRepository; import de.avatic.lcc.repositories.rates.ContainerRateRepository; @@ -22,6 +23,7 @@ import de.avatic.lcc.service.calculation.DistanceService; import org.springframework.stereotype.Service; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.NoSuchElementException; @Service @@ -86,6 +88,7 @@ public class RouteSectionCostCalculationService { result.setWeightPrice(containerCalculation.isWeightExceeded()); result.setCbmPrice(prices.volumePrice); result.setWeightPrice(prices.weightPrice); + result.setUtilization(prices.utilization); var chanceRiskFactors = changeRiskFactorCalculationService.getChanceRiskFactors(); @@ -112,7 +115,7 @@ public class RouteSectionCostCalculationService { // Set premise metadata result.setStacked(premise.getHuStackable()); - result.setUnmixedPrice(premise.getHuMixable()); + result.setUnmixedPrice(!premise.getHuMixable()); // Get nodes and distance RouteNode fromNode = routeNodeRepository.getById(section.getFromRouteNodeId()).orElseThrow(); @@ -125,7 +128,7 @@ public class RouteSectionCostCalculationService { int transitTime; if (RateType.CONTAINER == section.getRateType()) { - ContainerRate containerRate = findContainerRate(section); + ContainerRate containerRate = findContainerRate(section, fromNode, toNode); rate = getContainerRate(containerRate, containerCalculation.getContainerType()); transitTime = containerRate.getLeadTime(); } else if (RateType.MATRIX == section.getRateType()) { @@ -140,8 +143,10 @@ public class RouteSectionCostCalculationService { result.setTransitTime(transitTime); // Calculate price and annual cost + int huAnnualAmount = destination.getAnnualAmount() / containerCalculation.getHu().getContentUnitCount(); BigDecimal utilization = getUtilization(section.getRateType()); - double annualVolume = destination.getAnnualAmount() * containerCalculation.getHu().getVolume(DimensionUnit.M); + BigDecimal annualVolume = BigDecimal.valueOf(huAnnualAmount * containerCalculation.getHu().getVolume(DimensionUnit.M)); + BigDecimal annualWeight = BigDecimal.valueOf(huAnnualAmount * containerCalculation.getHu().getWeight(WeightUnit.KG)); PriceCalculationResult prices = calculatePrices( premise.getHuMixable(), @@ -155,10 +160,11 @@ public class RouteSectionCostCalculationService { result.setWeightPrice(containerCalculation.isWeightExceeded()); result.setCbmPrice(prices.volumePrice); result.setWeightPrice(prices.weightPrice); + result.setUtilization(prices.utilization); var chanceRiskFactors = changeRiskFactorCalculationService.getChanceRiskFactors(); - BigDecimal annualCost = (containerCalculation.isWeightExceeded() ? prices.weightPrice : prices.volumePrice).multiply(BigDecimal.valueOf(annualVolume)); + BigDecimal annualCost = (containerCalculation.isWeightExceeded() ? prices.weightPrice.multiply(annualWeight) : prices.volumePrice.multiply(annualVolume)); BigDecimal annualRiskCost = annualCost.multiply(chanceRiskFactors.getRiskFactor()); BigDecimal annualChanceCost = annualCost.multiply(chanceRiskFactors.getChanceFactor()); @@ -175,31 +181,36 @@ public class RouteSectionCostCalculationService { ContainerType containerType, int maxContainerWeight, BigDecimal totalVolumeUtilization, - BigDecimal utilization) { + BigDecimal propertyUtilization) { + BigDecimal utilization; BigDecimal volumePrice; BigDecimal weightPrice; - BigDecimal cbmRate = rate.divide(BigDecimal.valueOf(containerType.getVolume())); + BigDecimal cbmRate = rate.divide(BigDecimal.valueOf(containerType.getVolume()), 10, RoundingMode.HALF_UP); + BigDecimal weightRate = rate.divide(BigDecimal.valueOf(maxContainerWeight), 10, RoundingMode.HALF_UP); + if (huMixable) { - volumePrice = cbmRate.divide(utilization); - weightPrice = (rate.divide(BigDecimal.valueOf(maxContainerWeight))).divide(utilization); + volumePrice = cbmRate.divide(propertyUtilization, 10, RoundingMode.HALF_UP); + weightPrice = weightRate.divide(propertyUtilization, 10, RoundingMode.HALF_UP); + utilization = propertyUtilization; } else { - volumePrice = cbmRate.divide(totalVolumeUtilization); - weightPrice = (rate.divide(BigDecimal.valueOf(maxContainerWeight))).divide(totalVolumeUtilization); + volumePrice = cbmRate.divide(totalVolumeUtilization, 10, RoundingMode.HALF_UP); + weightPrice = weightRate.divide(totalVolumeUtilization, 10, RoundingMode.HALF_UP); + utilization = totalVolumeUtilization; } - return new PriceCalculationResult(volumePrice, weightPrice); + return new PriceCalculationResult(volumePrice, weightPrice, utilization); } - private ContainerRate findContainerRate(RouteSection section) { + private ContainerRate findContainerRate(RouteSection section, RouteNode fromNode, RouteNode toNode) { return containerRateRepository.findRoute( - section.getFromRouteNodeId(), - section.getToRouteNodeId(), + fromNode.getNodeId(), + toNode.getNodeId(), TransportType.valueOf(section.getTransportType().name())) .orElseThrow(() -> new NoSuchElementException( - "ContainerRate not found for route: " + section.getFromRouteNodeId() + - " to " + section.getToRouteNodeId())); + "ContainerRate not found for route: " + fromNode.getName() + "(" + fromNode.getNodeId() + ")" + + " to " + toNode.getName() + "(" + toNode.getNodeId() + ")")); } private MatrixRate findMatrixRate(RouteNode fromNode, RouteNode toNode) { @@ -250,6 +261,6 @@ public class RouteSectionCostCalculationService { /** * Simple data class to hold volume and weight price results */ - private record PriceCalculationResult(BigDecimal volumePrice, BigDecimal weightPrice) {} + private record PriceCalculationResult(BigDecimal volumePrice, BigDecimal weightPrice, BigDecimal utilization) {} } diff --git a/src/main/resources/master_data/01-properties.sql b/src/main/resources/master_data/01-properties.sql index 96683a3..a2fa703 100644 --- a/src/main/resources/master_data/01-properties.sql +++ b/src/main/resources/master_data/01-properties.sql @@ -5,51 +5,134 @@ -- Description -> name -- =================================================== +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Reference route segment start node mapping id', 'START_REF', 'TEXT', '{}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Reference route segment end node mapping id', 'END_REF', 'TEXT', '{}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'The historically highest rate for the reference route segment for 40ft containers [EUR]', 'RISK_REF', 'CURRENCY', '{"GT":0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'The historically lowest rate for the reference route segment for 40ft containers [EUR]', 'CHANCE_REF', 'CURRENCY', '{"GT":0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Annual working days', 'WORKDAYS', 'INT', '{GT: 0, LT: 366}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Interest rate inventory [%]', 'INTEREST_RATE', 'PERCENTAGE', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'FCA fee [%]', 'FCA_FEE', 'PERCENTAGE', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Default customs duty [%]', 'TARIFF_RATE', 'PERCENTAGE', '{GTE:0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Customs clearance fee per import & HS code', 'CUSTOM_FEE', 'CURRENCY', '{GTE:0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Standard-Report Format', 'REPORTING', 'ENUMERATION', '{ENUM:[\'MEK_B\',\'MEK_C\']}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Payment terms [days]', 'PAYMENT_TERMS', 'INT', '{}'); + + + +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Annual working days', 'WORKDAYS', 'INT', '{"GT": 0, "LT": 366}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Interest rate inventory [%]', 'INTEREST_RATE', 'PERCENTAGE', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'FCA fee [%]', 'FCA_FEE', 'PERCENTAGE', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Default customs duty [%]', 'TARIFF_RATE', 'PERCENTAGE', '{"GTE":0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Customs clearance fee per import & HS code', 'CUSTOM_FEE', 'CURRENCY', '{"GTE":0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Standard-Report Format', 'REPORTING', 'ENUMERATION', '{"ENUM":["MEK_B","MEK_C"]}'); INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( '40''', 'FEU', 'BOOLEAN', '{}'); INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( '20''', 'TEU', 'BOOLEAN', '{}'); INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( '40''HC', 'FEU_HQ', 'BOOLEAN', '{}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Container utilization in mixed containers [%]', 'CONTAINER_UTIL', 'PERCENTAGE', '{GTE:0,LTE:1}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Truck utilization road transport EMEA [%]', 'TRUCK_UTIL', 'PERCENTAGE', '{GTE:0,LTE:1}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Max validity of container freight rates [days]', 'VALID_DAYS', 'INT', '{GT: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Metropolition region size (diameter) [km]', 'RADIUS_REGION', 'INT', '{GT: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Min delivery frequency / year for containtrer transports', 'FREQ_MIN', 'INT', '{GT: 0, LT: 366}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Max delivery frequency / year for containtrer transports', 'FREQ_MAX', 'INT', '{GT: 0, LT: 366}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Max load of 20'' container [kg]', 'TEU_LOAD', 'INT', '{GT: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Max load of 40'' container [kg]', 'FEU_LOAD', 'INT', '{GT: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Pre-carriage [EUR/kg]', 'AIR_PRECARRIAGE', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Pre-carriage handling [EUR]', 'AIR_HANDLING', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Main carriage [EUR/kg]', 'AIR_MAINCARRIAGE', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Hand over fee [EUR]', 'AIR_HANDOVER_FEE', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Customs clearance fee [EUR]', 'AIR_CUSTOM_FEE', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'On-carriage [EUR/kg]', 'AIR_ONCARRIAGE', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Terminal handling fee [EUR/kg]', 'AIR_TERMINAL_FEE', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'GR handling KLT [EUR/HU]', 'KLT_HANDLING', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'GR handling GLT [EUR/HU]', 'GLT_HANDLING', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'GLT/KLT booking & document handling [EUR/GR]', 'BOOKING', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'GLT release from storage [EUR/GLT release]', 'GLT_RELEASE', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'KLT release from storage [EUR/KLT release]', 'KLT_RELEASE', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'GLT dispatch [EUR/GLT dispatch]', 'GLT_DISPATCH', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'KLT dispacth [EUR/KLT dispatch]', 'KLT_DISPATCH', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Repacking KLT, HU <15kg [EUR/HU]', 'KLT_REPACK_S', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Repacking KLT, HU >=15kg [EUR/HU]', 'KLT_REPACK_M', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Repacking GLT, HU <15kg [EUR/HU]', 'GLT_REPACK_S', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Repacking GLT, HU 15 - 2000kg [EUR/HU]', 'GLT_REPACK_M', 'CURRENCY', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Repacking GLT, HU > 2000kg [EUR/HU]', 'GLT_REPACK_L', 'INT', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'GLT disposal [EUR/GLT]', 'DISPOSAL', 'INT', '{GTE: 0}'); -INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Space costs / m3 per night [EUR/m3]', 'SPACE_COST', 'CURRENCY', '{GTE: 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Container utilization in mixed containers [%]', 'CONTAINER_UTIL', 'PERCENTAGE', '{"GTE":0,"LTE":1}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Truck utilization road transport EMEA [%]', 'TRUCK_UTIL', 'PERCENTAGE', '{"GTE":0,"LTE":1}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Max validity of container freight rates [days]', 'VALID_DAYS', 'INT', '{"GT": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Metropolition region size (diameter) [km]', 'RADIUS_REGION', 'INT', '{"GT": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Min delivery frequency / year for containtrer transports', 'FREQ_MIN', 'INT', '{"GT": 0, "LT": 366}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Max delivery frequency / year for containtrer transports', 'FREQ_MAX', 'INT', '{"GT": 0, "LT": 366}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Max load of 20'' container [kg]', 'TEU_LOAD', 'INT', '{"GT": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Max load of 40'' container [kg]', 'FEU_LOAD', 'INT', '{"GT": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Max load of a truck [kg]', 'TRUCK_LOAD', 'INT', '{"GT": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Pre-carriage [EUR/kg]', 'AIR_PRECARRIAGE', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Pre-carriage handling [EUR]', 'AIR_HANDLING', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Main carriage [EUR/kg]', 'AIR_MAINCARRIAGE', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Hand over fee [EUR]', 'AIR_HANDOVER_FEE', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Customs clearance fee [EUR]', 'AIR_CUSTOM_FEE', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'On-carriage [EUR/kg]', 'AIR_ONCARRIAGE', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Terminal handling fee [EUR/kg]', 'AIR_TERMINAL_FEE', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'GR handling KLT [EUR/HU]', 'KLT_HANDLING', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'GR handling GLT [EUR/HU]', 'GLT_HANDLING', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'GLT/KLT booking & document handling [EUR/GR]', 'BOOKING', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'GLT release from storage [EUR/GLT release]', 'GLT_RELEASE', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'KLT release from storage [EUR/KLT release]', 'KLT_RELEASE', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'GLT dispatch [EUR/GLT dispatch]', 'GLT_DISPATCH', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'KLT dispacth [EUR/KLT dispatch]', 'KLT_DISPATCH', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Repacking KLT, HU <15kg [EUR/HU]', 'KLT_REPACK_S', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Repacking KLT, HU >=15kg [EUR/HU]', 'KLT_REPACK_M', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Repacking GLT, HU <15kg [EUR/HU]', 'GLT_REPACK_S', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Repacking GLT, HU 15 - 2000kg [EUR/HU]', 'GLT_REPACK_M', 'CURRENCY', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Repacking GLT, HU > 2000kg [EUR/HU]', 'GLT_REPACK_L', 'INT', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'GLT disposal [EUR/GLT]', 'DISPOSAL', 'INT', '{"GTE": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Space costs / m3 per night [EUR/m3]', 'SPACE_COST', 'CURRENCY', '{"GTE": 0}'); -- =================================================== -- INSERT Statements für system_property -- Verwendung von Subqueries für dynamische ID-Ermittlung -- =================================================== +INSERT INTO system_property (property_set_id, system_property_type_id, property_value) +VALUES ( + (SELECT ps.id FROM `property_set` ps + WHERE ps.state = 'VALID' + AND ps.start_date <= NOW() + AND (ps.end_date IS NULL OR ps.end_date > NOW()) + ORDER BY ps.start_date DESC + LIMIT 1), + (SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'PAYMENT_TERMS'), + '30' + ); + +INSERT INTO system_property (property_set_id, system_property_type_id, property_value) +VALUES ( + (SELECT ps.id FROM `property_set` ps + WHERE ps.state = 'VALID' + AND ps.start_date <= NOW() + AND (ps.end_date IS NULL OR ps.end_date > NOW()) + ORDER BY ps.start_date DESC + LIMIT 1), + (SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'START_REF'), + 'CNXMN' + ); + +INSERT INTO system_property (property_set_id, system_property_type_id, property_value) +VALUES ( + (SELECT ps.id FROM `property_set` ps + WHERE ps.state = 'VALID' + AND ps.start_date <= NOW() + AND (ps.end_date IS NULL OR ps.end_date > NOW()) + ORDER BY ps.start_date DESC + LIMIT 1), + (SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'END_REF'), + 'DEHAM' + ); + +INSERT INTO system_property (property_set_id, system_property_type_id, property_value) +VALUES ( + (SELECT ps.id FROM `property_set` ps + WHERE ps.state = 'VALID' + AND ps.start_date <= NOW() + AND (ps.end_date IS NULL OR ps.end_date > NOW()) + ORDER BY ps.start_date DESC + LIMIT 1), + (SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'RISK_REF'), + '20000.00' + ); + +INSERT INTO system_property (property_set_id, system_property_type_id, property_value) +VALUES ( + (SELECT ps.id FROM `property_set` ps + WHERE ps.state = 'VALID' + AND ps.start_date <= NOW() + AND (ps.end_date IS NULL OR ps.end_date > NOW()) + ORDER BY ps.start_date DESC + LIMIT 1), + (SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'CHANCE_REF'), + '1000.00' + ); + + +INSERT INTO system_property (property_set_id, system_property_type_id, property_value) +VALUES ( + (SELECT ps.id FROM `property_set` ps + WHERE ps.state = 'VALID' + AND ps.start_date <= NOW() + AND (ps.end_date IS NULL OR ps.end_date > NOW()) + ORDER BY ps.start_date DESC + LIMIT 1), + (SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'TRUCK_UTIL'), + '0.7' + ); + + INSERT INTO system_property (property_set_id, system_property_type_id, property_value) VALUES ( (SELECT ps.id FROM `property_set` ps diff --git a/src/main/resources/master_data/02-country.sql b/src/main/resources/master_data/02-country.sql index 0edc8b2..69637fd 100644 --- a/src/main/resources/master_data/02-country.sql +++ b/src/main/resources/master_data/02-country.sql @@ -595,6 +595,7 @@ SELECT FROM `country` c, `country_property_type` cpt WHERE cpt.external_mapping_id = 'AIR_SHARE'; +-- Wage Factor Properties (only for countries with defined values) -- Wage Factor Properties (only for countries with defined values) INSERT INTO `country_property` (`country_id`, `country_property_type_id`, `property_set_id`, `property_value`) @@ -608,32 +609,32 @@ SELECT ORDER BY ps.start_date DESC LIMIT 1), CASE c.iso_code - WHEN 'AT' THEN '99' - WHEN 'BE' THEN '114' - WHEN 'BG' THEN '23' - WHEN 'CZ' THEN '44' - WHEN 'DE' THEN '100' - WHEN 'DK' THEN '116' - WHEN 'EE' THEN '60' - WHEN 'ES' THEN '90' - WHEN 'FI' THEN '102' - WHEN 'FR' THEN '105' - WHEN 'GR' THEN '35' - WHEN 'HR' THEN '31' - WHEN 'HU' THEN '35' - WHEN 'IE' THEN '97' - WHEN 'IT' THEN '72' - WHEN 'LT' THEN '36' - WHEN 'LU' THEN '131' - WHEN 'LV' THEN '33' - WHEN 'MT' THEN '41' - WHEN 'NL' THEN '105' - WHEN 'PL' THEN '27' - WHEN 'PT' THEN '41' - WHEN 'RO' THEN '27' - WHEN 'SE' THEN '94' - WHEN 'SI' THEN '62' - WHEN 'SK' THEN '42' + WHEN 'AT' THEN '0.99' + WHEN 'BE' THEN '1.14' + WHEN 'BG' THEN '0.23' + WHEN 'CZ' THEN '0.44' + WHEN 'DE' THEN '1.00' + WHEN 'DK' THEN '1.16' + WHEN 'EE' THEN '0.60' + WHEN 'ES' THEN '0.90' + WHEN 'FI' THEN '1.02' + WHEN 'FR' THEN '1.05' + WHEN 'GR' THEN '0.35' + WHEN 'HR' THEN '0.31' + WHEN 'HU' THEN '0.35' + WHEN 'IE' THEN '0.97' + WHEN 'IT' THEN '0.72' + WHEN 'LT' THEN '0.36' + WHEN 'LU' THEN '1.31' + WHEN 'LV' THEN '0.33' + WHEN 'MT' THEN '0.41' + WHEN 'NL' THEN '1.05' + WHEN 'PL' THEN '0.27' + WHEN 'PT' THEN '0.41' + WHEN 'RO' THEN '0.27' + WHEN 'SE' THEN '0.94' + WHEN 'SI' THEN '0.62' + WHEN 'SK' THEN '0.42' END FROM `country` c, `country_property_type` cpt WHERE c.iso_code IN ('AT', 'BE', 'BG', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK') diff --git a/src/test/java/de/avatic/lcc/controller/calculation/CalculationIntegrationTests.java b/src/test/java/de/avatic/lcc/controller/calculation/CalculationIntegrationTests.java new file mode 100644 index 0000000..72f4271 --- /dev/null +++ b/src/test/java/de/avatic/lcc/controller/calculation/CalculationIntegrationTests.java @@ -0,0 +1,96 @@ +package de.avatic.lcc.controller.calculation; + + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import de.avatic.lcc.dto.calculation.DestinationDTO; +import de.avatic.lcc.dto.calculation.edit.destination.DestinationCreateDTO; +import de.avatic.lcc.dto.calculation.edit.destination.DestinationUpdateDTO; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +@Import({PremiseControllerTestData.class, PremiseTestsHelper.class}) +public class CalculationIntegrationTests { + + @Autowired + protected JdbcTemplate jdbcTemplate; + @Autowired + private MockMvc mockMvc; + @Autowired + private ObjectMapper objectMapper; + @Autowired + private PremiseTestsHelper testHelper; + + @Nested + @Sql(scripts = {"classpath:master_data/alldata.sql", "classpath:master_data/users.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) + @Sql(scripts = {"classpath:master_data/users-cleanup.sql", "classpath:master_data/alldata-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_CLASS) + @Sql(scripts = {"classpath:master_data/premises.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = {"classpath:master_data/job-cleanup.sql", "classpath:master_data/premises-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) + @DisplayName("GET /api/calculation/start - Start Calculations Tests") + class StartCalculationTests { + + @Test + @DisplayName("PUT /api/calculation/start - happy path") + public void startCalculationCase1() throws Exception { + + var nodeId = testHelper.getNodeIdByExternalMappingId("AB"); + + var premisesBeforeUpdate = testHelper.getPremisesFromDb(); + var premise1 = premisesBeforeUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow(); + var premise3 = premisesBeforeUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow(); + + var createDto = new DestinationCreateDTO(); + createDto.setPremiseId(Collections.singletonList(premise1.getId())); + createDto.setDestinationNodeId(nodeId); + + var response = mockMvc.perform(post("/api/calculation/destination") + .content(objectMapper.writeValueAsString(createDto)) + .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andReturn(); + + var destinations = objectMapper.readValue(response.getResponse().getContentAsString(), new TypeReference>() {}); + var destinationId = destinations.get(premise1.getId()).getId(); + var routeId = destinations.get(premise1.getId()).getRoutes().getFirst().getId(); + + var updateDto = new DestinationUpdateDTO(); + updateDto.setAnnualAmount(100); + updateDto.setRouteSelectedId(routeId); + + mockMvc.perform(put("/api/calculation/destination/" + destinationId) + .content(objectMapper.writeValueAsString(updateDto)).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + + + mockMvc.perform(put("/api/calculation/start") + .content(objectMapper.writeValueAsString(Arrays.asList(premise1.getId(), premise3.getId()))) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andDo(print()); + + } + } + + //Test unselected route. + //modifiy premise in database with invalid values. + +} diff --git a/src/test/java/de/avatic/lcc/controller/calculation/DestinationIntegrationTest.java b/src/test/java/de/avatic/lcc/controller/calculation/DestinationIntegrationTest.java index 7be1345..6c0cdb3 100644 --- a/src/test/java/de/avatic/lcc/controller/calculation/DestinationIntegrationTest.java +++ b/src/test/java/de/avatic/lcc/controller/calculation/DestinationIntegrationTest.java @@ -330,37 +330,5 @@ public class DestinationIntegrationTest { } - @Nested - @Sql(scripts = {"classpath:master_data/alldata.sql", "classpath:master_data/users.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) - @Sql(scripts = {"classpath:master_data/users-cleanup.sql", "classpath:master_data/alldata-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_CLASS) - @Sql(scripts = {"classpath:master_data/premises.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) - @Sql(scripts = {"classpath:master_data/premises-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) - @DisplayName("GET /api/calculation/start - Start Calculations Tests") - class StartCalculationTests { - @Test - @DisplayName("PUT /api/calculation/start - happy path") - public void startCalculationCase1() throws Exception { - - var nodeId = testHelper.getNodeIdByExternalMappingId("AB"); - - var premisesBeforeUpdate = testHelper.getPremisesFromDb(); - var premise1 = premisesBeforeUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow(); - var premise3 = premisesBeforeUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow(); - - var dto = new DestinationCreateDTO(); - dto.setPremiseId(Arrays.asList(premise1.getId(), premise3.getId())); - dto.setDestinationNodeId(nodeId); - - mockMvc.perform(post("/api/calculation/destination") - .content(objectMapper.writeValueAsString(dto)) - .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print()); - - mockMvc.perform(put("/api/calculation/start") - .content(objectMapper.writeValueAsString(Arrays.asList(premise1, premise3))) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()).andDo(print()); - - } - } } diff --git a/src/test/java/de/avatic/lcc/controller/calculation/PremiseControllerIntegrationTest.java b/src/test/java/de/avatic/lcc/controller/calculation/PremiseControllerIntegrationTest.java index 0bdfeb6..7874632 100644 --- a/src/test/java/de/avatic/lcc/controller/calculation/PremiseControllerIntegrationTest.java +++ b/src/test/java/de/avatic/lcc/controller/calculation/PremiseControllerIntegrationTest.java @@ -719,9 +719,9 @@ public class PremiseControllerIntegrationTest { var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow(); var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow(); - assertThat(premiseAfter1.getCustomRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5)); - assertThat(premiseAfter3.getCustomRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5)); - assertThat(premiseAfter8.getCustomRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5)); + assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5)); + assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5)); + assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5)); assertThat(premiseAfter1.getHsCode()).isEqualTo(premise1.getHsCode()); assertThat(premiseAfter3.getHsCode()).isEqualTo(premise3.getHsCode()); @@ -751,9 +751,9 @@ public class PremiseControllerIntegrationTest { var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow(); var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow(); - assertThat(premiseAfter1.getCustomRate()).isEqualByComparingTo(premise1.getCustomRate()); - assertThat(premiseAfter3.getCustomRate()).isEqualByComparingTo(premise3.getCustomRate()); - assertThat(premiseAfter8.getCustomRate()).isEqualByComparingTo(premise8.getCustomRate()); + assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(premise1.getTariffRate()); + assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(premise3.getTariffRate()); + assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(premise8.getTariffRate()); assertThat(premiseAfter1.getHsCode()).isEqualTo("12345678"); assertThat(premiseAfter3.getHsCode()).isEqualTo("12345678"); @@ -784,9 +784,9 @@ public class PremiseControllerIntegrationTest { var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow(); var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow(); - assertThat(premiseAfter1.getCustomRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5)); - assertThat(premiseAfter3.getCustomRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5)); - assertThat(premiseAfter8.getCustomRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5)); + assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5)); + assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5)); + assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5)); assertThat(premiseAfter1.getHsCode()).isEqualTo("12345678"); assertThat(premiseAfter3.getHsCode()).isEqualTo("12345678"); @@ -817,9 +817,9 @@ public class PremiseControllerIntegrationTest { var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow(); var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow(); - assertThat(premiseAfter1.getCustomRate()).isEqualByComparingTo(premise1.getCustomRate()); - assertThat(premiseAfter3.getCustomRate()).isEqualByComparingTo(premise3.getCustomRate()); - assertThat(premiseAfter8.getCustomRate()).isEqualByComparingTo(premise8.getCustomRate()); + assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(premise1.getTariffRate()); + assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(premise3.getTariffRate()); + assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(premise8.getTariffRate()); assertThat(premiseAfter1.getHsCode()).isEqualTo(premise1.getHsCode()); assertThat(premiseAfter3.getHsCode()).isEqualTo(premise3.getHsCode()); @@ -849,9 +849,9 @@ public class PremiseControllerIntegrationTest { var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow(); var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow(); - assertThat(premiseAfter1.getCustomRate()).isEqualByComparingTo(premise1.getCustomRate()); - assertThat(premiseAfter3.getCustomRate()).isEqualByComparingTo(premise3.getCustomRate()); - assertThat(premiseAfter8.getCustomRate()).isEqualByComparingTo(premise8.getCustomRate()); + assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(premise1.getTariffRate()); + assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(premise3.getTariffRate()); + assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(premise8.getTariffRate()); assertThat(premiseAfter1.getHsCode()).isEqualTo(premise1.getHsCode()); assertThat(premiseAfter3.getHsCode()).isEqualTo(premise3.getHsCode()); @@ -881,9 +881,9 @@ public class PremiseControllerIntegrationTest { var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow(); var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow(); - assertThat(premiseAfter1.getCustomRate()).isEqualByComparingTo(premise1.getCustomRate()); - assertThat(premiseAfter3.getCustomRate()).isEqualByComparingTo(premise3.getCustomRate()); - assertThat(premiseAfter8.getCustomRate()).isEqualByComparingTo(premise8.getCustomRate()); + assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(premise1.getTariffRate()); + assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(premise3.getTariffRate()); + assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(premise8.getTariffRate()); assertThat(premiseAfter1.getHsCode()).isEqualTo(premise1.getHsCode()); assertThat(premiseAfter3.getHsCode()).isEqualTo(premise3.getHsCode()); @@ -914,9 +914,9 @@ public class PremiseControllerIntegrationTest { var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow(); var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow(); - assertThat(premiseAfter1.getCustomRate()).isEqualByComparingTo(premise1.getCustomRate()); - assertThat(premiseAfter3.getCustomRate()).isEqualByComparingTo(premise3.getCustomRate()); - assertThat(premiseAfter8.getCustomRate()).isEqualByComparingTo(premise8.getCustomRate()); + assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(premise1.getTariffRate()); + assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(premise3.getTariffRate()); + assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(premise8.getTariffRate()); assertThat(premiseAfter1.getHsCode()).isEqualTo(premise1.getHsCode()); assertThat(premiseAfter3.getHsCode()).isEqualTo(premise3.getHsCode()); @@ -964,9 +964,9 @@ public class PremiseControllerIntegrationTest { var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow(); var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow(); - assertThat(premiseAfter1.getCustomRate()).isEqualByComparingTo(premise1.getCustomRate()); - assertThat(premiseAfter3.getCustomRate()).isEqualByComparingTo(premise3.getCustomRate()); - assertThat(premiseAfter8.getCustomRate()).isEqualByComparingTo(premise8.getCustomRate()); + assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(premise1.getTariffRate()); + assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(premise3.getTariffRate()); + assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(premise8.getTariffRate()); assertThat(premiseAfter1.getHsCode()).isEqualTo(premise1.getHsCode()); assertThat(premiseAfter3.getHsCode()).isEqualTo(premise3.getHsCode()); diff --git a/src/test/java/de/avatic/lcc/controller/calculation/PremiseTestsHelper.java b/src/test/java/de/avatic/lcc/controller/calculation/PremiseTestsHelper.java index 8ef3dcb..bc73554 100644 --- a/src/test/java/de/avatic/lcc/controller/calculation/PremiseTestsHelper.java +++ b/src/test/java/de/avatic/lcc/controller/calculation/PremiseTestsHelper.java @@ -59,7 +59,7 @@ public class PremiseTestsHelper { entity.setHsCode(rs.getString("hs_code")); - entity.setCustomRate(rs.getBigDecimal("tariff_rate")); + entity.setTariffRate(rs.getBigDecimal("tariff_rate")); entity.setFcaEnabled(rs.getBoolean("is_fca_enabled")); if (rs.wasNull()) diff --git a/src/test/resources/master_data/alldata.sql b/src/test/resources/master_data/alldata.sql index 7f6a4a1..32dc18c 100644 --- a/src/test/resources/master_data/alldata.sql +++ b/src/test/resources/master_data/alldata.sql @@ -26,6 +26,13 @@ WHERE NOT EXISTS ( -- Mapping: external mapping id -> external_mapping_id -- Description -> name -- =================================================== +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Reference route segment start node mapping id', 'START_REF', 'TEXT', '{}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Reference route segment end node mapping id', 'END_REF', 'TEXT', '{}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'The historically highest rate for the reference route segment for 40ft containers [EUR]', 'RISK_REF', 'CURRENCY', '{"GT":0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'The historically lowest rate for the reference route segment for 40ft containers [EUR]', 'CHANCE_REF', 'CURRENCY', '{"GT":0}'); + +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Payment terms [days]', 'PAYMENT_TERMS', 'INT', '{}'); + INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Annual working days', 'WORKDAYS', 'INT', '{"GT": 0, "LT": 366}'); INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Interest rate inventory [%]', 'INTEREST_RATE', 'PERCENTAGE', '{"GTE": 0}'); @@ -44,6 +51,7 @@ INSERT INTO system_property_type ( name, external_mapping_id, data_type, validat INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Max delivery frequency / year for containtrer transports', 'FREQ_MAX', 'INT', '{"GT": 0, "LT": 366}'); INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Max load of 20'' container [kg]', 'TEU_LOAD', 'INT', '{"GT": 0}'); INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Max load of 40'' container [kg]', 'FEU_LOAD', 'INT', '{"GT": 0}'); +INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Max load of a truck [kg]', 'TRUCK_LOAD', 'INT', '{"GT": 0}'); INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Pre-carriage [EUR/kg]', 'AIR_PRECARRIAGE', 'CURRENCY', '{"GTE": 0}'); INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Pre-carriage handling [EUR]', 'AIR_HANDLING', 'CURRENCY', '{"GTE": 0}'); INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Main carriage [EUR/kg]', 'AIR_MAINCARRIAGE', 'CURRENCY', '{"GTE": 0}'); @@ -71,6 +79,66 @@ INSERT INTO system_property_type ( name, external_mapping_id, data_type, validat -- Verwendung von Subqueries für dynamische ID-Ermittlung -- =================================================== +INSERT INTO system_property (property_set_id, system_property_type_id, property_value) +VALUES ( + (SELECT ps.id FROM `property_set` ps + WHERE ps.state = 'VALID' + AND ps.start_date <= NOW() + AND (ps.end_date IS NULL OR ps.end_date > NOW()) + ORDER BY ps.start_date DESC + LIMIT 1), + (SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'START_REF'), + 'CNXMN' + ); + +INSERT INTO system_property (property_set_id, system_property_type_id, property_value) +VALUES ( + (SELECT ps.id FROM `property_set` ps + WHERE ps.state = 'VALID' + AND ps.start_date <= NOW() + AND (ps.end_date IS NULL OR ps.end_date > NOW()) + ORDER BY ps.start_date DESC + LIMIT 1), + (SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'PAYMENT_TERMS'), + '30' + ); + +INSERT INTO system_property (property_set_id, system_property_type_id, property_value) +VALUES ( + (SELECT ps.id FROM `property_set` ps + WHERE ps.state = 'VALID' + AND ps.start_date <= NOW() + AND (ps.end_date IS NULL OR ps.end_date > NOW()) + ORDER BY ps.start_date DESC + LIMIT 1), + (SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'END_REF'), + 'DEHAM' + ); + +INSERT INTO system_property (property_set_id, system_property_type_id, property_value) +VALUES ( + (SELECT ps.id FROM `property_set` ps + WHERE ps.state = 'VALID' + AND ps.start_date <= NOW() + AND (ps.end_date IS NULL OR ps.end_date > NOW()) + ORDER BY ps.start_date DESC + LIMIT 1), + (SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'RISK_REF'), + '20000.00' + ); + +INSERT INTO system_property (property_set_id, system_property_type_id, property_value) +VALUES ( + (SELECT ps.id FROM `property_set` ps + WHERE ps.state = 'VALID' + AND ps.start_date <= NOW() + AND (ps.end_date IS NULL OR ps.end_date > NOW()) + ORDER BY ps.start_date DESC + LIMIT 1), + (SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'CHANCE_REF'), + '1000.00' + ); + INSERT INTO system_property (property_set_id, system_property_type_id, property_value) VALUES ( (SELECT ps.id FROM `property_set` ps @@ -276,6 +344,18 @@ VALUES ( '21000' ); +INSERT INTO system_property (property_set_id, system_property_type_id, property_value) +VALUES ( + (SELECT ps.id FROM `property_set` ps + WHERE ps.state = 'VALID' + AND ps.start_date <= NOW() + AND (ps.end_date IS NULL OR ps.end_date > NOW()) + ORDER BY ps.start_date DESC + LIMIT 1), + (SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'TRUCK_LOAD'), + '25000' + ); + INSERT INTO system_property (property_set_id, system_property_type_id, property_value) VALUES ( (SELECT ps.id FROM `property_set` ps @@ -1126,6 +1206,7 @@ SELECT FROM `country` c, `country_property_type` cpt WHERE cpt.external_mapping_id = 'AIR_SHARE'; +-- Wage Factor Properties (only for countries with defined values) -- Wage Factor Properties (only for countries with defined values) INSERT INTO `country_property` (`country_id`, `country_property_type_id`, `property_set_id`, `property_value`) @@ -1139,37 +1220,36 @@ SELECT ORDER BY ps.start_date DESC LIMIT 1), CASE c.iso_code - WHEN 'AT' THEN '99' - WHEN 'BE' THEN '114' - WHEN 'BG' THEN '23' - WHEN 'CZ' THEN '44' - WHEN 'DE' THEN '100' - WHEN 'DK' THEN '116' - WHEN 'EE' THEN '60' - WHEN 'ES' THEN '90' - WHEN 'FI' THEN '102' - WHEN 'FR' THEN '105' - WHEN 'GR' THEN '35' - WHEN 'HR' THEN '31' - WHEN 'HU' THEN '35' - WHEN 'IE' THEN '97' - WHEN 'IT' THEN '72' - WHEN 'LT' THEN '36' - WHEN 'LU' THEN '131' - WHEN 'LV' THEN '33' - WHEN 'MT' THEN '41' - WHEN 'NL' THEN '105' - WHEN 'PL' THEN '27' - WHEN 'PT' THEN '41' - WHEN 'RO' THEN '27' - WHEN 'SE' THEN '94' - WHEN 'SI' THEN '62' - WHEN 'SK' THEN '42' + WHEN 'AT' THEN '0.99' + WHEN 'BE' THEN '1.14' + WHEN 'BG' THEN '0.23' + WHEN 'CZ' THEN '0.44' + WHEN 'DE' THEN '1.00' + WHEN 'DK' THEN '1.16' + WHEN 'EE' THEN '0.60' + WHEN 'ES' THEN '0.90' + WHEN 'FI' THEN '1.02' + WHEN 'FR' THEN '1.05' + WHEN 'GR' THEN '0.35' + WHEN 'HR' THEN '0.31' + WHEN 'HU' THEN '0.35' + WHEN 'IE' THEN '0.97' + WHEN 'IT' THEN '0.72' + WHEN 'LT' THEN '0.36' + WHEN 'LU' THEN '1.31' + WHEN 'LV' THEN '0.33' + WHEN 'MT' THEN '0.41' + WHEN 'NL' THEN '1.05' + WHEN 'PL' THEN '0.27' + WHEN 'PT' THEN '0.41' + WHEN 'RO' THEN '0.27' + WHEN 'SE' THEN '0.94' + WHEN 'SI' THEN '0.62' + WHEN 'SK' THEN '0.42' END FROM `country` c, `country_property_type` cpt WHERE c.iso_code IN ('AT', 'BE', 'BG', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK') AND cpt.external_mapping_id = 'WAGE'; - -- ============================================================================= -- VERIFICATION QUERIES (Optional - for testing) -- ============================================================================= diff --git a/src/test/resources/master_data/countries_properties.sql b/src/test/resources/master_data/countries_properties.sql index 8b308ef..04bd2d4 100644 --- a/src/test/resources/master_data/countries_properties.sql +++ b/src/test/resources/master_data/countries_properties.sql @@ -1482,32 +1482,32 @@ SELECT ORDER BY ps.start_date DESC LIMIT 1), CASE c.iso_code - WHEN 'AT' THEN '99' - WHEN 'BE' THEN '114' - WHEN 'BG' THEN '23' - WHEN 'CZ' THEN '44' - WHEN 'DE' THEN '100' - WHEN 'DK' THEN '116' - WHEN 'EE' THEN '60' - WHEN 'ES' THEN '90' - WHEN 'FI' THEN '102' - WHEN 'FR' THEN '105' - WHEN 'GR' THEN '35' - WHEN 'HR' THEN '31' - WHEN 'HU' THEN '35' - WHEN 'IE' THEN '97' - WHEN 'IT' THEN '72' - WHEN 'LT' THEN '36' - WHEN 'LU' THEN '131' - WHEN 'LV' THEN '33' - WHEN 'MT' THEN '41' - WHEN 'NL' THEN '105' - WHEN 'PL' THEN '27' - WHEN 'PT' THEN '41' - WHEN 'RO' THEN '27' - WHEN 'SE' THEN '94' - WHEN 'SI' THEN '62' - WHEN 'SK' THEN '42' + WHEN 'AT' THEN '0.99' + WHEN 'BE' THEN '1.14' + WHEN 'BG' THEN '0.23' + WHEN 'CZ' THEN '0.44' + WHEN 'DE' THEN '1.00' + WHEN 'DK' THEN '1.16' + WHEN 'EE' THEN '0.60' + WHEN 'ES' THEN '0.90' + WHEN 'FI' THEN '1.02' + WHEN 'FR' THEN '1.05' + WHEN 'GR' THEN '0.35' + WHEN 'HR' THEN '0.31' + WHEN 'HU' THEN '0.35' + WHEN 'IE' THEN '0.97' + WHEN 'IT' THEN '0.72' + WHEN 'LT' THEN '0.36' + WHEN 'LU' THEN '1.31' + WHEN 'LV' THEN '0.33' + WHEN 'MT' THEN '0.41' + WHEN 'NL' THEN '1.05' + WHEN 'PL' THEN '0.27' + WHEN 'PT' THEN '0.41' + WHEN 'RO' THEN '0.27' + WHEN 'SE' THEN '0.94' + WHEN 'SI' THEN '0.62' + WHEN 'SK' THEN '0.42' END FROM `country` c, `country_property_type` cpt WHERE c.iso_code IN ('AT', 'BE', 'BG', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK') diff --git a/src/test/resources/master_data/job-cleanup.sql b/src/test/resources/master_data/job-cleanup.sql new file mode 100644 index 0000000..e276081 --- /dev/null +++ b/src/test/resources/master_data/job-cleanup.sql @@ -0,0 +1,13 @@ +delete +from calculation_job_route_section +where 1; +delete +from calculation_job_destination +where 1; +delete +from calculation_job +where 1; + +ALTER TABLE calculation_job_route_section AUTO_INCREMENT = 1; +ALTER TABLE calculation_job_destination AUTO_INCREMENT = 1; +ALTER TABLE calculation_job AUTO_INCREMENT = 1; \ No newline at end of file