diff --git a/src/main/java/de/avatic/lcc/calculationmodel/ContainerCalculationResult.java b/src/main/java/de/avatic/lcc/calculationmodel/ContainerCalculationResult.java index 3d963eb..207a610 100644 --- a/src/main/java/de/avatic/lcc/calculationmodel/ContainerCalculationResult.java +++ b/src/main/java/de/avatic/lcc/calculationmodel/ContainerCalculationResult.java @@ -1,9 +1,251 @@ package de.avatic.lcc.calculationmodel; +import de.avatic.lcc.dto.generic.ContainerType; +import de.avatic.lcc.model.packaging.PackagingDimension; +import de.avatic.lcc.model.utils.DimensionUnit; +import de.avatic.lcc.model.utils.WeightUnit; + +/** + * Represents the result of a container calculation process, including details + * about the utilized packaging dimensions, container type, and various metrics + * such as layer count, structural arrangement, and utilization. + *

+ * This class provides methods to retrieve metrics related to container + * utilization, such as the total number of handling units and whether the + * allowed weight limit has been exceeded. + */ public class ContainerCalculationResult { + + private Integer maxContainerWeight; + + /** + * The dimensions of the handling unit (HU), including weight, + * height, width, and length specifications. Includes a tolerance according + * to system property + */ + private PackagingDimension hu; + + /** + * The number of layers in the container calculation result, + * representing the vertical stacking level. + */ private int layer; + + /** + * The structural arrangement of the handling units within + * the container, represented as a descriptive string. + */ private String structure; + + /** + * Total count of handling units (HU) that are recognized + * based on the calculation. + */ private int huUnitCount; + /** + * Indicates whether the calculated weight of the container exceeds + * the permitted limit for the container type. + */ + private boolean weightExceeded; + + /** + * Type of container used in the calculation, e.g., TEU/FEU. + */ + private ContainerType containerType; + + /** + * Constructs a new ContainerCalculationResult with the provided parameters. + * + * @param huUnitCount The total number of handling units. + * @param layers The total number of layers to stack. + * @param structure A textual description of the container's arrangement. + * @param weightExceeded Boolean indicating if the allowed weight is exceeded. + * @param containerType The type of container being used in the process. + * @param hu The packaging dimensions of the handling unit (incl. tolerance). + */ + public ContainerCalculationResult(int huUnitCount, int layers, String structure, boolean weightExceeded, ContainerType containerType, PackagingDimension hu, Integer maxContainerWeight) { + this.huUnitCount = huUnitCount; + this.layer = layers; + this.structure = structure; + this.weightExceeded = weightExceeded; + this.containerType = containerType; + this.maxContainerWeight = maxContainerWeight; + this.hu = hu; + } + + public Integer getMaxContainerWeight() { + return maxContainerWeight; + } + + public void setMaxContainerWeight(Integer maxContainerWeight) { + this.maxContainerWeight = maxContainerWeight; + } + + /** + * Sets the PackagingDimension object representing the handling unit attributes. + * + * @param hu A PackagingDimension object containing weight and size information of the handling unit. + */ + public void setHu(PackagingDimension hu) { + this.hu = hu; + } + + /** + * Retrieves the PackagingDimension object representing the handling unit attributes. + * + * @return A PackagingDimension object containing weight and size information of the handling unit. + */ + public PackagingDimension getHu() { + return hu; + } + + /** + * Default constructor for ContainerCalculationResult. + * Initializes an empty object with no predefined values. + */ + public ContainerCalculationResult() { + } + + /** + * Gets the type of container used in this calculation. + * + * @return The container type (e.g., TEU, FEU). + */ + public ContainerType getContainerType() { + return containerType; + } + + /** + * Sets the type of container used in this calculation. + * + * @param containerType The container type (e.g., TEU, FEU). + */ + public void setContainerType(ContainerType containerType) { + this.containerType = containerType; + } + + /** + * Checks if the weight limit is exceeded for the container. + * + * @return True if the weight limit is exceeded, false otherwise. + */ + public boolean isWeightExceeded() { + return weightExceeded; + } + + /** + * Sets the status of whether the weight limit is exceeded. + * + * @param weightExceeded A boolean indicating if the weight limit is exceeded. + */ + public void setWeightExceeded(boolean weightExceeded) { + this.weightExceeded = weightExceeded; + } + + /** + * Gets the number of layers in the container. + * + * @return The number of layers. + */ + public int getLayer() { + return layer; + } + + /** + * Sets the number of layers in the container. + * + * @param layer The number of layers to set. + */ + public void setLayer(int layer) { + this.layer = layer; + } + + /** + * Gets the structural arrangement of handling units in the container. + * + * @return A string specifying the arrangement structure. + */ + public String getStructure() { + return structure; + } + + /** + * Sets the structural arrangement of handling units in the container. + * + * @param structure A string specifying the arrangement structure. + */ + public void setStructure(String structure) { + this.structure = structure; + } + + /** + * Gets the total number of handling units in the container calculation result. + * + * @return The total count of handling units. + */ + public int getHuUnitCount() { + return huUnitCount; + } + + /** + * Sets the total number of handling units in the container calculation result. + * + * @param huUnitCount The total count of handling units to set. + */ + public void setHuUnitCount(int huUnitCount) { + this.huUnitCount = huUnitCount; + } + + /** + * Determines the total utilization of the container based on either + * weight or volume, depending on whether the weight limit is exceeded. + * + * @return Total container utilization as a percentage. + */ + public double getTotalUtilization() { + return weightExceeded ? getTotalUtilizationByWeight() : getTotalUtilizationByVolume(); + } + + /** + * Calculates the total utilization of the container, + * factoring in the HU utilization and count. + * + * @return The total utilization value for the container. + */ + public double getTotalUtilizationByVolume() { + return getHuUtilizationByVolume() * huUnitCount; + } + + /** + * Calculates the utilization of a single handling unit (HU) + * as a proportion of the container volume. + * + * @return The volume utilization ratio of one HU to container volume. + */ + public double getHuUtilizationByVolume() { + return hu.getVolume(DimensionUnit.M) / containerType.getVolume(); + } + + /** + * Calculates the total utilization of the container based on weight, + * factoring in the handling unit utilization and count. + * + * @return Total weight utilization of the container as a percentage. + */ + public double getTotalUtilizationByWeight() { + return getHuUtilizationByWeight() * huUnitCount; + } + + /** + * Calculates the utilization of a single handling unit (HU) as a proportion + * of the container weight capacity. + * + * @return Weight utilization ratio of one HU to the container's maximum weight. + */ + public double getHuUtilizationByWeight() { + return WeightUnit.KG.convertFromG(hu.getWeight()).doubleValue() / maxContainerWeight; + } + } diff --git a/src/main/java/de/avatic/lcc/controller/calculation/PremiseController.java b/src/main/java/de/avatic/lcc/controller/calculation/PremiseController.java index 633f356..b8e7ee2 100644 --- a/src/main/java/de/avatic/lcc/controller/calculation/PremiseController.java +++ b/src/main/java/de/avatic/lcc/controller/calculation/PremiseController.java @@ -79,7 +79,7 @@ public class PremiseController { return ResponseEntity.ok(premisesServices.updatePackaging(packagingDTO)); } - @PutMapping("/material") + @PostMapping("/material") public ResponseEntity> updateMaterial(@RequestBody MaterialUpdateDTO materialUpdateDTO) { return ResponseEntity.ok(premisesServices.updateMaterial(materialUpdateDTO)); } 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 d225544..7eaf8da 100644 --- a/src/main/java/de/avatic/lcc/dto/generic/ContainerType.java +++ b/src/main/java/de/avatic/lcc/dto/generic/ContainerType.java @@ -1,5 +1,40 @@ package de.avatic.lcc.dto.generic; public enum ContainerType { - FEU, TEU, HQ + 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; + private final int height; + private final double volume; + private final int euroPalletCount; + private final int industrialPalletCount; + + ContainerType(int length, int width, int height, double volume, int euroPalletCount, int industrialPalletCount) { + this.length = length; + this.width = width; + this.height = height; + this.volume = volume; + this.euroPalletCount = euroPalletCount; + this.industrialPalletCount = industrialPalletCount; + + } + public int getLength() { + return length; + } + public int getWidth() { + return width; + } + public int getHeight() { + return height; + } + + public double getVolume() { + return volume; + } + + public int getPalletCount(PalletType palletType) { + return palletType == PalletType.EURO_PALLET ? euroPalletCount : industrialPalletCount; + + } } diff --git a/src/main/java/de/avatic/lcc/dto/generic/PalletType.java b/src/main/java/de/avatic/lcc/dto/generic/PalletType.java new file mode 100644 index 0000000..743c4ff --- /dev/null +++ b/src/main/java/de/avatic/lcc/dto/generic/PalletType.java @@ -0,0 +1,29 @@ +package de.avatic.lcc.dto.generic; + +import de.avatic.lcc.model.packaging.PackagingDimension; + +public enum PalletType { + INDUSTRIAL_PALLET(1200,1000), EURO_PALLET(1200,800); + + private final int width; + private final int length; + + PalletType(int length, int width) { + this.length = length; + this.width = width; + } + + public int getLength() { + return length; + } + public int getWidth() { + return width; + } + + public boolean fitsOn(PackagingDimension dimension) { + return (dimension.getLength() <= this.length && dimension.getWidth() <= this.width) || (dimension.getLength() <= this.width && dimension.getWidth() <= this.length); + } + + + +} 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 2e6e5d3..01d8dbe 100644 --- a/src/main/java/de/avatic/lcc/model/packaging/PackagingDimension.java +++ b/src/main/java/de/avatic/lcc/model/packaging/PackagingDimension.java @@ -1,9 +1,10 @@ package de.avatic.lcc.model.packaging; -import com.fasterxml.jackson.annotation.JsonProperty; import de.avatic.lcc.model.utils.DimensionUnit; import de.avatic.lcc.model.utils.WeightUnit; +import java.math.BigDecimal; + public class PackagingDimension { @@ -122,4 +123,12 @@ public class PackagingDimension { return dimensions; } + + public double getVolume(DimensionUnit unit) { + var lengthInM = unit.convertFromMM(length); + var widthInM = unit.convertFromMM(width); + var heightInM = unit.convertFromMM(height); + + return lengthInM.doubleValue() * widthInM.doubleValue() * heightInM.doubleValue(); + } } 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 6916814..5bc37f5 100644 --- a/src/main/java/de/avatic/lcc/model/premises/Premise.java +++ b/src/main/java/de/avatic/lcc/model/premises/Premise.java @@ -1,5 +1,6 @@ package de.avatic.lcc.model.premises; +import de.avatic.lcc.model.nodes.Location; import de.avatic.lcc.model.utils.DimensionUnit; import de.avatic.lcc.model.utils.WeightUnit; import jakarta.validation.constraints.Digits; @@ -68,10 +69,37 @@ public class Premise { private Integer userSupplierNodeId; + private Location location; + + private Integer countryId; + private Integer packagingId; private Integer userId; + public DimensionUnit getHuDisplayedDimensionUnit() { + return huDisplayedDimensionUnit; + } + + public WeightUnit getHuDisplayedWeightUnit() { + return huDisplayedWeightUnit; + } + + public Location getLocation() { + return location; + } + + public void setLocation(Location location) { + this.location = location; + } + + public Integer getCountryId() { + return countryId; + } + + public void setCountryId(Integer countryId) { + this.countryId = countryId; + } public Integer getId() { return id; @@ -177,18 +205,10 @@ public class Premise { this.individualHuWeight = individualHuWeight; } - public DimensionUnit getDimensionUnit() { - return huDisplayedDimensionUnit; - } - public void setHuDisplayedDimensionUnit(DimensionUnit huDisplayedDimensionUnit) { this.huDisplayedDimensionUnit = huDisplayedDimensionUnit; } - public WeightUnit getWeightUnit() { - return huDisplayedWeightUnit; - } - public void setHuDisplayedWeightUnit(WeightUnit huDisplayedWeightUnit) { this.huDisplayedWeightUnit = huDisplayedWeightUnit; } diff --git a/src/main/java/de/avatic/lcc/model/premises/route/Destination.java b/src/main/java/de/avatic/lcc/model/premises/route/Destination.java index d267f5e..75f7004 100644 --- a/src/main/java/de/avatic/lcc/model/premises/route/Destination.java +++ b/src/main/java/de/avatic/lcc/model/premises/route/Destination.java @@ -9,7 +9,7 @@ public class Destination { private Integer premiseId; - private BigDecimal annualAmount; + private Integer annualAmount; private Integer destinationNodeId; @@ -27,6 +27,16 @@ public class Destination { private BigDecimal geoLng; + private Integer countryId; + + public Integer getCountryId() { + return countryId; + } + + public void setCountryId(Integer countryId) { + this.countryId = countryId; + } + public BigDecimal getGeoLat() { return geoLat; } @@ -59,11 +69,11 @@ public class Destination { this.premiseId = premiseId; } - public BigDecimal getAnnualAmount() { + public Integer getAnnualAmount() { return annualAmount; } - public void setAnnualAmount(BigDecimal annualAmount) { + public void setAnnualAmount(Integer annualAmount) { this.annualAmount = annualAmount; } diff --git a/src/main/java/de/avatic/lcc/model/properties/CountryPropertyMappingId.java b/src/main/java/de/avatic/lcc/model/properties/CountryPropertyMappingId.java new file mode 100644 index 0000000..3361888 --- /dev/null +++ b/src/main/java/de/avatic/lcc/model/properties/CountryPropertyMappingId.java @@ -0,0 +1,24 @@ +package de.avatic.lcc.model.properties; + +public enum CountryPropertyMappingId { + UNION("Customs Union", "NONE"), + SAFETY_STOCK("Safety Stock [working days]", "30"), + AIR_SHARE("Air freight share [%]", "2"), + WAGE("Wage factor [%]", "100%"); + + private final String description; + private final String defaultValue; + + CountryPropertyMappingId(String description, String defaultValue) { + this.description = description; + this.defaultValue = defaultValue; + } + + public String getDescription() { + return description; + } + + public String getDefaultValue() { + return defaultValue; + } +} diff --git a/src/main/java/de/avatic/lcc/model/properties/CustomUnionType.java b/src/main/java/de/avatic/lcc/model/properties/CustomUnionType.java new file mode 100644 index 0000000..156ca5d --- /dev/null +++ b/src/main/java/de/avatic/lcc/model/properties/CustomUnionType.java @@ -0,0 +1,5 @@ +package de.avatic.lcc.model.properties; + +public enum CustomUnionType { + EU, NONE +} diff --git a/src/main/java/de/avatic/lcc/model/properties/PackagingPropertyMappingId.java b/src/main/java/de/avatic/lcc/model/properties/PackagingPropertyMappingId.java index 65ac3eb..ce72e41 100644 --- a/src/main/java/de/avatic/lcc/model/properties/PackagingPropertyMappingId.java +++ b/src/main/java/de/avatic/lcc/model/properties/PackagingPropertyMappingId.java @@ -1,5 +1,23 @@ package de.avatic.lcc.model.properties; +import org.springframework.jmx.export.naming.IdentityNamingStrategy; + public enum PackagingPropertyMappingId { - STACKABLE, MIXABLE, RUST_PREVENTION + STACKABLE("Stackable", "true"), MIXABLE("Mixable", "true"), RUST_PREVENTION("Needs rust prevention", "false"); + + private final String defaultValue; + private final String description; + + PackagingPropertyMappingId(String description, String defaultValue) { + this.description = description; + this.defaultValue = defaultValue; + } + + public String getDefaultValue() { + return defaultValue; + } + + public String getDescription() { + return description; + } } 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 f2a35ec..9d1e68a 100644 --- a/src/main/java/de/avatic/lcc/model/properties/SystemPropertyMappingId.java +++ b/src/main/java/de/avatic/lcc/model/properties/SystemPropertyMappingId.java @@ -20,19 +20,21 @@ public enum SystemPropertyMappingId { TRUCK_UTIL("Truck utilization road transport EMEA [%]", "70%"), VALID_DAYS("Max validity of container freight rates [days]", "60"), RADIUS_REGION("Metropolition region size (diameter) [km]", "20"), - FREQ_MIN("Min delivery frequency / year for containtrer transports", "3"), - FREQ_MAX("Max delivery frequency / year for containtrer transports", "50"), + FREQ_MIN("Min delivery frequency / year for container transports", "3"), + FREQ_MAX("Max delivery frequency / year for container transports", "50"), TEU_LOAD("Max load of 20' container [kg]", "20000"), FEU_LOAD("Max load of 40' container [kg]", "21000"), + TRUCK_LOAD("Maximum truck load [kg]", "25000"), - AIR_PRECARRIAGE("Pre-carriage [EUR/kg]", "0,10 €"), - AIR_HANDLING("Pre-carriage handling [EUR]", "80,00 €"), - AIR_MAINCARRIAGE("Main carriage [EUR/kg]", "3,50 €"), - AIR_HANDOVER_FEE("Hand over fee [EUR]", "35,00 €"), - AIR_CUSTOM_FEE("Customs clearance fee [EUR]", "45,00 €"), + + AIR_PRECARRIAGE("Pre-carriage [EUR/kg]", "0,10"), + AIR_HANDLING("Pre-carriage handling [EUR]", "80,00"), + AIR_MAINCARRIAGE("Main carriage [EUR/kg]", "3,50"), + AIR_HANDOVER_FEE("Hand over fee [EUR]", "35,00"), + AIR_CUSTOM_FEE("Customs clearance fee [EUR]", "45,00"), AIR_ONCARRIAGE("On-carriage [EUR/kg]", "0,20 €"), - AIR_TERMINAL_FEE("Terminal handling fee [EUR/kg]", "0,20 €"), + AIR_TERMINAL_FEE("Terminal handling fee [EUR/kg]", "0,20"), KLT_HANDLING("GR handling KLT [EUR/HU]", "0,71 €"), @@ -41,7 +43,7 @@ public enum SystemPropertyMappingId { GLT_RELEASE("GLT release from storage [EUR/GLT release]", "2,23 €"), KLT_RELEASE("KLT release from storage [EUR/KLT release]", "1,12 €"), GLT_DISPATCH("GLT dispatch [EUR/GLT dispatch]", "1,61 €"), - KLT_DISPATCH("KLT dispacth [EUR/KLT dispatch]", "0,33 €"), + KLT_DISPATCH("KLT dispatch [EUR/KLT dispatch]", "0,33 €"), KLT_REPACK_S("Repacking KLT, HU <15kg [EUR/HU]", "2,08 €"), KLT_REPACK_M("Repacking KLT, HU >=15kg [EUR/HU]", "3,02 €"), GLT_REPACK_S("Repacking GLT, HU <15kg [EUR/HU]", "3,02 €"), @@ -50,14 +52,9 @@ public enum SystemPropertyMappingId { DISPOSAL("GLT disposal [EUR/GLT]", "6,00 €"), - SPACE_COST("Space costs / m3 per night [EUR/m3]", "0,26 €"), + SPACE_COST("Space costs / m3 per night [EUR/m3]", "0,26 €"); - UNION("Customs Union", ""), - SAFTY_STOCK("Safety Stock [working days]", "30"), - AIR_SHARE("Air freight share [%]", "2"), - WAGE("Wage factor [%]", "100%"); - private final String description; private final String defaultValue; diff --git a/src/main/java/de/avatic/lcc/model/rates/ContainerRate.java b/src/main/java/de/avatic/lcc/model/rates/ContainerRate.java index a407b53..4f2b767 100644 --- a/src/main/java/de/avatic/lcc/model/rates/ContainerRate.java +++ b/src/main/java/de/avatic/lcc/model/rates/ContainerRate.java @@ -1,5 +1,6 @@ package de.avatic.lcc.model.rates; +import de.avatic.lcc.dto.generic.TransportType; import jakarta.validation.constraints.Digits; import jdk.jfr.Unsigned; @@ -10,7 +11,7 @@ public class ContainerRate { private Integer id; - private ContainerRateType type; + private TransportType type; @Digits(integer = 15, fraction = 2) private BigDecimal rateTeu; @@ -65,11 +66,11 @@ public class ContainerRate { this.id = id; } - public ContainerRateType getType() { + public TransportType getType() { return type; } - public void setType(ContainerRateType type) { + public void setType(TransportType type) { this.type = type; } diff --git a/src/main/java/de/avatic/lcc/model/rates/ContainerRateType.java b/src/main/java/de/avatic/lcc/model/rates/ContainerRateType.java deleted file mode 100644 index ab216b9..0000000 --- a/src/main/java/de/avatic/lcc/model/rates/ContainerRateType.java +++ /dev/null @@ -1,5 +0,0 @@ -package de.avatic.lcc.model.rates; - -public enum ContainerRateType { - RAIL, SEA, POST_RUN, ROAD -} diff --git a/src/main/java/de/avatic/lcc/repositories/country/CountryPropertyRepository.java b/src/main/java/de/avatic/lcc/repositories/country/CountryPropertyRepository.java index 3b2244f..ccfa754 100644 --- a/src/main/java/de/avatic/lcc/repositories/country/CountryPropertyRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/country/CountryPropertyRepository.java @@ -1,12 +1,18 @@ package de.avatic.lcc.repositories.country; import de.avatic.lcc.dto.generic.PropertyDTO; +import de.avatic.lcc.model.properties.CountryPropertyMappingId; +import org.springframework.data.mapping.model.Property; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.Collection; import java.util.List; +import java.util.Optional; @Repository public class CountryPropertyRepository { @@ -29,6 +35,26 @@ public class CountryPropertyRepository { jdbcTemplate.update(query, value, countryId, typeId, setId, value); } + public Optional getByMappingIdAndCountryId(CountryPropertyMappingId mappingId, Integer countryId) { + String query = """ + SELECT type.name as name, type.data_type as dataType, type.external_mapping_id as externalMappingId, type.validation_rule as validationRule, + draft.property_value as draftValue, valid.property_value as validValue + FROM country_property_type AS type + LEFT JOIN country_property AS draft ON draft.country_property_type_id = type.id + LEFT JOIN country_property AS valid ON valid.country_property_type_id = type.id + LEFT JOIN property_set AS draftSet ON draftSet.id = draft.property_set_id + LEFT JOIN property_set AS validSet ON validSet.id = valid.property_set_id + WHERE draftSet.state = 'DRAFT' AND validSet.state = 'VALID' AND draft.country_id = ? AND valid.country_id = ? + AND type.external_mapping_id = ? + """; + + var property = jdbcTemplate.query(query, new PropertyMapper(), countryId, countryId, mappingId.name()); + + if(property.isEmpty()) + return Optional.empty(); + + return Optional.of(property.getFirst()); + } private Integer getTypeIdByMappingId(String mappingId) { String query = "SELECT id FROM country_property_type WHERE external_mapping_id = ?"; @@ -49,19 +75,7 @@ public class CountryPropertyRepository { WHERE draftSet.state = 'DRAFT' AND validSet.state = 'VALID' AND draft.country_id = ? AND valid.country_id = ? """; - return jdbcTemplate.query(query, (rs, rowNum) -> { - var dto = new PropertyDTO(); - - dto.setName(rs.getString("name")); - dto.setDraftValue(rs.getString("draftValue")); - dto.setCurrentValue(rs.getString("validValue")); - dto.setValidationRule(rs.getString("validationRule")); - dto.setExternalMappingId(rs.getString("externalMappingId")); - dto.setRequired(true); - dto.setDataType(rs.getString("dataType")); - - return dto; - }, id, id); + return jdbcTemplate.query(query, new PropertyMapper(), id, id); } public Collection listPropertiesByCountryIdAndPropertySetId(Integer id, Integer propertySetId) { @@ -75,19 +89,7 @@ public class CountryPropertyRepository { WHERE propertySet.state = 'EXPIRED' AND property.country_id = ? AND propertySet.id = ? """; - return jdbcTemplate.query(query, (rs, rowNum) -> { - var dto = new PropertyDTO(); - - dto.setName(rs.getString("name")); - dto.setDraftValue(rs.getString("draftValue")); - dto.setCurrentValue(rs.getString("validValue")); - dto.setValidationRule(rs.getString("validationRule")); - dto.setExternalMappingId(rs.getString("externalMappingId")); - dto.setRequired(true); - dto.setDataType(rs.getString("dataType")); - - return dto; - }, id, propertySetId); + return jdbcTemplate.query(query, new PropertyMapper(), id, propertySetId); } public void fillDraft(Integer setId) { @@ -105,4 +107,23 @@ public class CountryPropertyRepository { } + + private static class PropertyMapper implements RowMapper + { + + @Override + public PropertyDTO mapRow(ResultSet rs, int rowNum) throws SQLException { + var dto = new PropertyDTO(); + + dto.setName(rs.getString("name")); + dto.setDraftValue(rs.getString("draftValue")); + dto.setCurrentValue(rs.getString("validValue")); + dto.setValidationRule(rs.getString("validationRule")); + dto.setExternalMappingId(rs.getString("externalMappingId")); + dto.setRequired(true); + dto.setDataType(rs.getString("dataType")); + + return dto; + } + } } 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 3521cc7..d2f61cc 100644 --- a/src/main/java/de/avatic/lcc/repositories/premise/DestinationRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/premise/DestinationRepository.java @@ -87,7 +87,7 @@ public class DestinationRepository { jdbcTemplate.update(connection -> { var ps = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); - ps.setBigDecimal(1, destination.getAnnualAmount()); + ps.setInt(1, destination.getAnnualAmount()); ps.setInt(2, destination.getPremiseId()); ps.setInt(3, destination.getDestinationNodeId()); ps.setBigDecimal(4, destination.getRateD2d()); @@ -112,7 +112,7 @@ public class DestinationRepository { Destination entity = new Destination(); entity.setId(rs.getInt("id")); - entity.setAnnualAmount(rs.getBigDecimal("annual_amount")); + entity.setAnnualAmount(rs.getInt("annual_amount")); entity.setPremiseId(rs.getInt("premise_id")); entity.setDestinationNodeId(rs.getInt("destination_node_id")); entity.setRateD2d(rs.getBigDecimal("rate_d2d")); @@ -122,6 +122,7 @@ public class DestinationRepository { entity.setDisposalCost(rs.getBigDecimal("disposal_cost")); entity.setGeoLat(rs.getBigDecimal("geo_lat")); entity.setGeoLng(rs.getBigDecimal("geo_lng")); + entity.setCountryId(rs.getInt("country_id")); return entity; } 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 9c4e8a0..a8bc2d9 100644 --- a/src/main/java/de/avatic/lcc/repositories/premise/PremiseRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/premise/PremiseRepository.java @@ -1,6 +1,8 @@ package de.avatic.lcc.repositories.premise; import de.avatic.lcc.model.materials.Material; +import de.avatic.lcc.model.nodes.Location; +import de.avatic.lcc.model.nodes.Node; import de.avatic.lcc.model.packaging.PackagingDimension; import de.avatic.lcc.model.premises.Premise; import de.avatic.lcc.model.premises.PremiseListEntry; @@ -301,14 +303,14 @@ public class PremiseRepository { } @Transactional - public void setSupplierId(List premiseId, Integer supplierId, boolean userSupplierNode) { + public void setSupplierId(List premiseId, Node supplier, boolean userSupplierNode) { String placeholders = String.join(",", Collections.nCopies(premiseId.size(), "?")); - String sql = "UPDATE premise SET supplier_node_id = ?, user_supplier_node_id = ? WHERE id IN ("+placeholders+")"; + String sql = "UPDATE premise SET supplier_node_id = ?, user_supplier_node_id = ?, geo_lat = ?, geo_lng = ?, country_id = ? WHERE id IN ("+placeholders+")"; if(userSupplierNode) { - jdbcTemplate.update(sql, 0, supplierId, premiseId.toArray()); + jdbcTemplate.update(sql, 0, supplier.getId(), supplier.getGeoLat(), supplier.getGeoLng(), supplier.getCountryId(), premiseId.toArray()); } else { - jdbcTemplate.update(sql, supplierId, 0, premiseId.toArray()); + jdbcTemplate.update(sql, supplier.getId(), 0, supplier.getGeoLat(), supplier.getGeoLng(), supplier.getCountryId(), premiseId.toArray()); } } @@ -375,7 +377,7 @@ public class PremiseRepository { jdbcTemplate.update(connection -> { PreparedStatement ps = connection.prepareStatement( - "INSERT INTO premise (material_id, supplier_node_id, user_supplier_node_id, user_id, state, createdAt, updatedAt)" + + "INSERT INTO premise (material_id, supplier_node_id, user_supplier_node_id, user_id, state, created_at, updated_at)" + " VALUES (?, ?, ?, ?, 'DRAFT', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)", Statement.RETURN_GENERATED_KEYS); @@ -576,6 +578,9 @@ public class PremiseRepository { entity.setSupplierNodeId(rs.getInt("supplier_node_id")); entity.setUserSupplierNodeId(rs.getInt("user_supplier_node_id")); + entity.setLocation(new Location(rs.getBigDecimal("geo_lng").doubleValue(), rs.getBigDecimal("geo_lat").doubleValue())); + entity.setCountryId(rs.getInt("country_id")); + entity.setUserId(rs.getInt("user_id")); entity.setMaterialCost(rs.getBigDecimal("material_cost")); diff --git a/src/main/java/de/avatic/lcc/repositories/premise/RouteRepository.java b/src/main/java/de/avatic/lcc/repositories/premise/RouteRepository.java index 595c6d6..d604a7e 100644 --- a/src/main/java/de/avatic/lcc/repositories/premise/RouteRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/premise/RouteRepository.java @@ -7,6 +7,7 @@ import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Repository; +import javax.swing.text.html.Option; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -14,6 +15,7 @@ import java.sql.Statement; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Optional; @Repository public class RouteRepository { @@ -29,6 +31,21 @@ public class RouteRepository { return jdbcTemplate.query(query, new RouteMapper(), id); } + public Optional getSelectedByDestinationId(Integer id) { + String query = "SELECT * FROM premise_route WHERE premise_destination_id = ?"; + var route = jdbcTemplate.query(query, new RouteMapper(), id); + + if(route.isEmpty()) { + return Optional.empty(); + } + +/* if(1 < route.size()) + TODO throw something */ + + + return Optional.of(route.getFirst()); + } + public void deleteAllById(List ids) { if (ids == null || ids.isEmpty()) { return; diff --git a/src/main/java/de/avatic/lcc/repositories/rates/ContainerRateRepository.java b/src/main/java/de/avatic/lcc/repositories/rates/ContainerRateRepository.java index 10d251c..4279922 100644 --- a/src/main/java/de/avatic/lcc/repositories/rates/ContainerRateRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/rates/ContainerRateRepository.java @@ -1,7 +1,7 @@ package de.avatic.lcc.repositories.rates; +import de.avatic.lcc.dto.generic.TransportType; import de.avatic.lcc.model.rates.ContainerRate; -import de.avatic.lcc.model.rates.ContainerRateType; import de.avatic.lcc.model.rates.ValidityPeriodState; import de.avatic.lcc.repositories.pagination.SearchQueryPagination; import de.avatic.lcc.repositories.pagination.SearchQueryResult; @@ -78,8 +78,8 @@ public class ContainerRateRepository { List params = new ArrayList<>(); params.add(ValidityPeriodState.VALID.name()); - params.add(ContainerRateType.SEA.name()); - params.add(ContainerRateType.RAIL.name()); + params.add(TransportType.SEA.name()); + params.add(TransportType.RAIL.name()); params.add(startNodeId); params.addAll(destinationCountryIds); @@ -108,10 +108,10 @@ public class ContainerRateRepository { WHERE validity_period.state = ? AND container_rate.from_node_id = ? AND container_rate.container_rate_type = ?"""; - return jdbcTemplate.query(query, new ContainerRateMapper(true), ValidityPeriodState.VALID.name(), mainRun.getToNodeId(), ContainerRateType.POST_RUN.name()); + return jdbcTemplate.query(query, new ContainerRateMapper(true), ValidityPeriodState.VALID.name(), mainRun.getToNodeId(), TransportType.POST_RUN.name()); } - public Optional findRoute(Integer fromNodeId, Integer toNodeId, ContainerRateType type) { + public Optional findRoute(Integer fromNodeId, Integer toNodeId, TransportType type) { String query = """ SELECT * FROM container_rate WHERE from_node_id = ? AND to_node_id = ? AND container_rate_type = ? """; @@ -144,7 +144,7 @@ public class ContainerRateRepository { entity.setValidityPeriodId(rs.getInt("validity_period_id")); entity.setFromNodeId(rs.getInt("from_node_id")); entity.setToNodeId(rs.getInt("to_node_id")); - entity.setType(ContainerRateType.valueOf(rs.getString("container_rate_type"))); + entity.setType(TransportType.valueOf(rs.getString("container_rate_type"))); entity.setLeadTime(rs.getInt("lead_time")); entity.setRateFeu(rs.getBigDecimal("rate_feu")); entity.setRateTeu(rs.getBigDecimal("rate_teu")); 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 b662d08..814eb0f 100644 --- a/src/main/java/de/avatic/lcc/service/access/DestinationService.java +++ b/src/main/java/de/avatic/lcc/service/access/DestinationService.java @@ -69,7 +69,7 @@ public class DestinationService { var destination = new Destination(); destination.setDestinationNodeId(dto.getDestinationNodeId()); destination.setPremiseId(premise.getId()); - destination.setAnnualAmount(BigDecimal.ZERO); + destination.setAnnualAmount(0); destination.setD2d(false); destination.setDisposalCost(null); destination.setHandlingCost(null); diff --git a/src/main/java/de/avatic/lcc/service/calculation/CalculationExecutionService.java b/src/main/java/de/avatic/lcc/service/calculation/CalculationExecutionService.java index b5b6ab7..de6caaa 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/CalculationExecutionService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/CalculationExecutionService.java @@ -1,24 +1,63 @@ package de.avatic.lcc.service.calculation; +import de.avatic.lcc.model.country.Country; import de.avatic.lcc.model.premises.Premise; +import de.avatic.lcc.model.premises.route.Destination; +import de.avatic.lcc.model.premises.route.RouteSection; +import de.avatic.lcc.repositories.country.CountryRepository; +import de.avatic.lcc.repositories.premise.DestinationRepository; import de.avatic.lcc.repositories.premise.PremiseRepository; +import de.avatic.lcc.repositories.premise.RouteRepository; +import de.avatic.lcc.repositories.premise.RouteSectionRepository; +import de.avatic.lcc.service.calculation.steps.CustomCostCalculationService; +import de.avatic.lcc.service.calculation.steps.RouteSectionCostCalculationService; import org.springframework.stereotype.Service; +import java.util.List; + @Service public class CalculationExecutionService { private final PremiseRepository premiseRepository; + private final DestinationRepository destinationRepository; + private final RouteRepository routeRepository; + private final RouteSectionRepository routeSectionRepository; + private final CustomCostCalculationService customCostCalculationService; + private final CountryRepository countryRepository; + private final RouteSectionCostCalculationService routeSectionCostCalculationService; - public CalculationExecutionService(PremiseRepository premiseRepository) { + public CalculationExecutionService(PremiseRepository premiseRepository, DestinationRepository destinationRepository, RouteRepository routeRepository, RouteSectionRepository routeSectionRepository, CustomCostCalculationService customCostCalculationService, CountryRepository countryRepository, RouteSectionCostCalculationService routeSectionCostCalculationService) { this.premiseRepository = premiseRepository; + this.destinationRepository = destinationRepository; + this.routeRepository = routeRepository; + this.routeSectionRepository = routeSectionRepository; + this.customCostCalculationService = customCostCalculationService; + this.countryRepository = countryRepository; + this.routeSectionCostCalculationService = routeSectionCostCalculationService; } public void doCalculation(Integer premiseId) { - var premise = premiseRepository.getPremiseById(premiseId); + Premise premise = premiseRepository.getPremiseById(premiseId).orElseThrow(); + List destinations = destinationRepository.getByPremiseId(premiseId); + destinations.forEach(destination -> doDestinationCalculation(destination, premise)); + + } + + private void doDestinationCalculation(Destination destination, Premise premise) { + var route = routeRepository.getSelectedByDestinationId(destination.getId()).orElseThrow(); + List section = routeSectionRepository.getByRouteId(route.getId()); + + routeSectionCostCalculationService.doCalculation(); + + Country supplierCountry = countryRepository.getById(premise.getCountryId()).orElseThrow(); + Country destinationCountry = countryRepository.getById(destination.getCountryId()).orElseThrow(); + customCostCalculationService.doCalculation(premise.getHsCode(), supplierCountry, destinationCountry, route, section); } + + } diff --git a/src/main/java/de/avatic/lcc/service/calculation/ChangeSupplierService.java b/src/main/java/de/avatic/lcc/service/calculation/ChangeSupplierService.java index 4468bdf..62ca4ec 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/ChangeSupplierService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/ChangeSupplierService.java @@ -121,7 +121,7 @@ public class ChangeSupplierService { } // actually update supplier supplierNodeId. - premiseRepository.setSupplierId(premisesToProcess.stream().map(Premise::getId).toList(), supplierNodeId, dto.isUserSupplierNode()); + premiseRepository.setSupplierId(premisesToProcess.stream().map(Premise::getId).toList(), supplier, dto.isUserSupplierNode()); //delete all conflicting premises: diff --git a/src/main/java/de/avatic/lcc/service/calculation/ContainerCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/ContainerCalculationService.java deleted file mode 100644 index c0b6901..0000000 --- a/src/main/java/de/avatic/lcc/service/calculation/ContainerCalculationService.java +++ /dev/null @@ -1,41 +0,0 @@ -package de.avatic.lcc.service.calculation; - -import de.avatic.lcc.calculationmodel.ContainerCalculationResult; -import de.avatic.lcc.model.packaging.Packaging; -import de.avatic.lcc.model.packaging.PackagingDimension; -import de.avatic.lcc.model.utils.DimensionUnit; -import de.avatic.lcc.repositories.packaging.PackagingDimensionRepository; -import org.springframework.stereotype.Service; - -@Service -public class ContainerCalculationService { - - private static final double DIMENSION_TOLERANCE = 0.02; - - private static final int TEU_LENGTH = 5890; - private static final int TEU_WIDTH = 2350; - private static final int TEU_HEIGHT = 2390; - - private static final int FEU_LENGTH = 12030; - private static final int FEU_WIDTH = 2350; - private static final int FEU_HEIGHT = 2390; - - private static final int HQ_LENGTH = 12030; - private static final int HQ_WIDTH = 2350; - private static final int HQ_HEIGHT = 2690; - - - - public ContainerCalculationResult doCalculation(PackagingDimension hu) { - var dimensions = hu.withTolerance(DIMENSION_TOLERANCE); - } - - private int calculateLayer(PackagingDimension dimension, ) { - - } - - private static class Container { - - } - -} diff --git a/src/main/java/de/avatic/lcc/service/calculation/DistanceService.java b/src/main/java/de/avatic/lcc/service/calculation/DistanceService.java index f39cfb5..b2e418e 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/DistanceService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/DistanceService.java @@ -1,6 +1,8 @@ package de.avatic.lcc.service.calculation; import de.avatic.lcc.model.country.DetourIndex; +import de.avatic.lcc.model.country.IsoCode; +import de.avatic.lcc.model.nodes.Location; import de.avatic.lcc.model.nodes.Node; import de.avatic.lcc.repositories.DistanceMatrixRepository; import de.avatic.lcc.repositories.country.CountryRepository; @@ -28,6 +30,23 @@ public class DistanceService { } + public double getDistanceFast(Integer srcCountryId, Location src, Integer destCountryId, Location dest ) { + double srcLatitudeRadians = Math.toRadians(src.getLatitude()); + double srcLongitudeRad = Math.toRadians(src.getLongitude()); + double destLatitudeRadians = Math.toRadians(dest.getLatitude()); + double destLongitudeRadians = Math.toRadians(dest.getLongitude()); + + double latitudeDifference = destLatitudeRadians - srcLatitudeRadians; + double longitudeDifference = destLongitudeRadians - srcLongitudeRad; + + double a = Math.sin(latitudeDifference / 2) * Math.sin(latitudeDifference / 2) + Math.cos(srcLatitudeRadians) * Math.cos(destLatitudeRadians) * Math.sin(longitudeDifference / 2) * Math.sin(longitudeDifference / 2); + + IsoCode isoSrc = countryRepository.getById(srcCountryId).orElseThrow().getIsoCode(); + IsoCode isoDest = countryRepository.getById(destCountryId).orElseThrow().getIsoCode(); + + return (getDetourIndex(isoSrc, isoDest) * (EARTH_RADIUS * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)))); + } + private double getDistanceFast(Node src, Node dest) { double srcLatitudeRadians = Math.toRadians(src.getGeoLat().doubleValue()); @@ -53,4 +72,14 @@ public class DistanceService { return (srcDetour.getAverageDetourIndex() + destDetour.getAverageDetourIndex()) / 2; } + private double getDetourIndex(IsoCode src, IsoCode dest) { + DetourIndex srcDetour = DetourIndex.getDetourIndex(src); + + if (src.equals(dest)) return srcDetour.getAverageDetourIndex(); + + DetourIndex destDetour = DetourIndex.getDetourIndex(dest); + + return (srcDetour.getAverageDetourIndex() + destDetour.getAverageDetourIndex()) / 2; + } + } diff --git a/src/main/java/de/avatic/lcc/service/calculation/RoutingService.java b/src/main/java/de/avatic/lcc/service/calculation/RoutingService.java index a6d99ef..00688f7 100644 --- a/src/main/java/de/avatic/lcc/service/calculation/RoutingService.java +++ b/src/main/java/de/avatic/lcc/service/calculation/RoutingService.java @@ -6,7 +6,6 @@ import de.avatic.lcc.model.nodes.Node; import de.avatic.lcc.model.premises.route.*; import de.avatic.lcc.model.properties.SystemPropertyMappingId; import de.avatic.lcc.model.rates.ContainerRate; -import de.avatic.lcc.model.rates.ContainerRateType; import de.avatic.lcc.model.rates.MatrixRate; import de.avatic.lcc.repositories.NodeRepository; import de.avatic.lcc.repositories.properties.PropertyRepository; @@ -485,7 +484,7 @@ public class RoutingService { if (container.getRates().contains(matrixRateObj)) return container.getRates().stream().filter(r -> r.equals(matrixRateObj)).findFirst().orElseThrow(); - Optional containerRate = containerRateRepository.findRoute(startNode.getId(), endNode.getId(), ContainerRateType.ROAD); + Optional containerRate = containerRateRepository.findRoute(startNode.getId(), endNode.getId(), TransportType.ROAD); if (containerRate.isPresent()) { containerRateObj.setRate(containerRate.get()); @@ -787,7 +786,7 @@ public class RoutingService { return type; } - public ContainerRateType getContainerRateTye() { + public TransportType getContainerRateTye() { return containerRate.getType(); } diff --git a/src/main/java/de/avatic/lcc/service/calculation/steps/AirfreightCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/steps/AirfreightCalculationService.java new file mode 100644 index 0000000..bdcd7a7 --- /dev/null +++ b/src/main/java/de/avatic/lcc/service/calculation/steps/AirfreightCalculationService.java @@ -0,0 +1,49 @@ +package de.avatic.lcc.service.calculation.steps; + +import de.avatic.lcc.model.packaging.PackagingDimension; +import de.avatic.lcc.model.properties.SystemPropertyMappingId; +import de.avatic.lcc.model.utils.DimensionUnit; +import de.avatic.lcc.model.utils.WeightUnit; +import de.avatic.lcc.repositories.properties.PropertyRepository; +import org.springframework.stereotype.Service; + +@Service +public class AirfreightCalculationService { + + private final PropertyRepository propertyRepository; + + public AirfreightCalculationService(PropertyRepository propertyRepository) { + this.propertyRepository = propertyRepository; + } + + public double doCalculation(PackagingDimension hu, double annualHuAmount) { + + var preCarriage = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_PRECARRIAGE).orElseThrow().getCurrentValue()); + var mainCarriage = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_MAINCARRIAGE).orElseThrow().getCurrentValue()); + var postCarriage = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_ONCARRIAGE).orElseThrow().getCurrentValue()); + var terminalFee = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_TERMINAL_FEE).orElseThrow().getCurrentValue()); + var preCarriageFee = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_HANDLING).orElseThrow().getCurrentValue()); + var customsClearanceFee = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_CUSTOM_FEE).orElseThrow().getCurrentValue()); + var handOverFee = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_HANDOVER_FEE).orElseThrow().getCurrentValue()); + + return 1.08*((getShippingWeight(hu)* preCarriage+mainCarriage+postCarriage+terminalFee)+preCarriageFee+customsClearanceFee+handOverFee); + + } + + private double getShippingWeight(PackagingDimension hu) { + return Math.max(getWeightInKg(hu), getVolumeWeight(hu)); + } + + private double getVolumeWeight(PackagingDimension hu) { + return 0.0002 * getDimensionInCm(hu.getLength(), hu.getDimensionUnit()) * getDimensionInCm(hu.getWidth(), hu.getDimensionUnit()) * getDimensionInCm(hu.getHeight(), hu.getDimensionUnit()) ; + } + + private double getDimensionInCm(Number dimension, DimensionUnit unit) { + return DimensionUnit.CM.convertFromMM(dimension).doubleValue(); + } + + private double getWeightInKg(PackagingDimension hu) { + return WeightUnit.KG.convertFromG(hu.getWeight()).doubleValue(); + } + +} diff --git a/src/main/java/de/avatic/lcc/service/calculation/steps/AirfreightShareCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/steps/AirfreightShareCalculationService.java new file mode 100644 index 0000000..e568f96 --- /dev/null +++ b/src/main/java/de/avatic/lcc/service/calculation/steps/AirfreightShareCalculationService.java @@ -0,0 +1,41 @@ +package de.avatic.lcc.service.calculation.steps; + +import de.avatic.lcc.model.properties.CountryPropertyMappingId; +import de.avatic.lcc.model.properties.SystemPropertyMappingId; +import de.avatic.lcc.repositories.country.CountryPropertyRepository; +import de.avatic.lcc.repositories.properties.PropertyRepository; +import org.springframework.stereotype.Service; + +@Service +public class AirfreightShareCalculationService { + + private final CountryPropertyRepository propertyRepository; + + public AirfreightShareCalculationService(CountryPropertyRepository propertyRepository) { + this.propertyRepository = propertyRepository; + } + + public double doCalculation(double overseaShare, Integer countryId) { + var maxAirfreightShare = Double.parseDouble(propertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.AIR_SHARE, countryId).orElseThrow().getCurrentValue()); + + // Ensure oversea share is within valid range + if (overseaShare < 0 || overseaShare > 100) { + throw new IllegalArgumentException("Oversea share must be between 0 and 100"); + } + + // First segment: from (0,0) to (50, 20% of max) + if (overseaShare <= 50) { + // Linear interpolation: y = mx + b + // m = (0.2 * maxAirfreightShare - 0) / (50 - 0) = 0.004 * maxAirfreightShare + // b = 0 + return 0.004 * maxAirfreightShare * overseaShare; + } + // Second segment: from (50, 20% of max) to (100, max) + else { + // Linear interpolation: y = mx + b + // m = (maxAirfreightShare - 0.2 * maxAirfreightShare) / (100 - 50) = 0.016 * maxAirfreightShare + // b = 0.2 * maxAirfreightShare - 50 * 0.016 * maxAirfreightShare = -0.6 * maxAirfreightShare + return 0.016 * maxAirfreightShare * overseaShare - 0.6 * maxAirfreightShare; + } + } +} diff --git a/src/main/java/de/avatic/lcc/service/calculation/steps/ContainerCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/steps/ContainerCalculationService.java new file mode 100644 index 0000000..cdae882 --- /dev/null +++ b/src/main/java/de/avatic/lcc/service/calculation/steps/ContainerCalculationService.java @@ -0,0 +1,208 @@ +package de.avatic.lcc.service.calculation.steps; + +import de.avatic.lcc.calculationmodel.ContainerCalculationResult; +import de.avatic.lcc.dto.generic.ContainerType; +import de.avatic.lcc.dto.generic.PalletType; +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.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +@Service +public class ContainerCalculationService { + + private static final double DIMENSION_TOLERANCE = 0.02; + + private final PropertyRepository propertyRepository; + + public ContainerCalculationService(PropertyRepository propertyRepository) { + this.propertyRepository = propertyRepository; + } + + public ContainerCalculationResult doCalculation(PackagingDimension hu, ContainerType containerType) { + + var weightInKg = WeightUnit.KG.convertFromG(hu.getWeight()).intValue(); + var maxContainerLoad = getMaxContainerLoad(containerType); + var maxUnitByWeight = maxContainerLoad / weightInKg; + + var dimensions = hu.withTolerance(DIMENSION_TOLERANCE); + + var solutionHorizontal = solveLayer(dimensions, containerType.getLength(), containerType.getWidth()); + var solutionVertical = solveLayer(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)) { + 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)) { + return new ContainerCalculationResult(Math.min(containerType.getPalletCount(PalletType.INDUSTRIAL_PALLET)*layers,maxUnitByWeight), layers, null, containerType.getPalletCount(PalletType.INDUSTRIAL_PALLET) > maxUnitByWeight, containerType, dimensions, maxContainerLoad); + } + + return toContainerCalculationResult(bestSolution, layers, maxUnitByWeight, containerType, dimensions, maxContainerLoad); + } + + private static Solution solveLayer(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); + + int maxHorizontalColumns = containerLength / horizontalColumn.getColumnWidth(); + int maxVerticalColumns = containerLength / verticalColumn.getColumnWidth(); + int maxCircularColumns = containerLength / circularColumn.getColumnWidth(); + + for (int horizontalColumnCount = 0; horizontalColumnCount <= maxHorizontalColumns; horizontalColumnCount++) { + for (int verticalColumnCount = 0; verticalColumnCount <= maxVerticalColumns; verticalColumnCount++) { + for (int circularColumnCount = 0; circularColumnCount <= maxCircularColumns; circularColumnCount++) { + // Check constraints + if (horizontalColumn.getColumnWidth() * horizontalColumnCount + + horizontalColumn.getColumnWidth() * verticalColumnCount + + horizontalColumn.getColumnWidth() * circularColumnCount <= containerLength) { + var current = new Solution(horizontalColumnCount, verticalColumnCount, circularColumnCount, horizontalColumn, verticalColumn, circularColumn); + if (current.getTotal() > bestSolution.getTotal()) { + bestSolution = current; + } + } + } + } + } + + return bestSolution; + } + + private int getMaxContainerLoad(ContainerType containerType) { + + int load = 0; + + if(containerType == ContainerType.FEU) + { + load = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FEU_LOAD).orElseThrow().getCurrentValue()); + } else if(containerType == ContainerType.TRUCK) { + load = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.TRUCK_LOAD).orElseThrow().getCurrentValue()); + } else if(containerType == ContainerType.TEU) { + load = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.TEU_LOAD).orElseThrow().getCurrentValue()); + } + + return load; + } + + private int getLayerCount(PackagingDimension dimensions, ContainerType containerType) { + return containerType.getHeight()/dimensions.getHeight(); + } + + private ContainerCalculationResult toContainerCalculationResult(Solution solution, int layers, int maxUnitByWeight, ContainerType containerType, PackagingDimension hu, int maxContainerLoad) { + ContainerCalculationResult result = new ContainerCalculationResult(); + + result.setLayer(layers); + result.setHuUnitCount(Math.min(solution.getTotal()*layers, maxUnitByWeight)); + result.setWeightExceeded(solution.getTotal() > maxUnitByWeight); + result.setStructure(null); + result.setContainerType(containerType); + result.setHu(hu); + result.setMaxContainerWeight(maxContainerLoad); + + return result; + } + + private enum BlockType { + HORIZONTAL(1), VERTICAL(1), CIRCULAR(4); + + private final int resultValue; + + BlockType(int resultValue) { + this.resultValue = resultValue; + } + + public int getBlockColumnLength(PackagingDimension dimension) { + return switch (this) { + case HORIZONTAL -> dimension.getLength(); + case VERTICAL -> dimension.getWidth(); + case CIRCULAR -> dimension.getWidth() + dimension.getLength(); + }; + } + + public int getColumnWidth(PackagingDimension dimension) { + return switch (this) { + case VERTICAL -> dimension.getLength(); + case HORIZONTAL -> dimension.getWidth(); + case CIRCULAR -> dimension.getWidth() + dimension.getLength(); + }; + } + + public int getResultValue() { + return resultValue; + } + + } + + private static class Solution { + + Map rowCount = new HashMap<>(); + Map columns = new HashMap<>(); + + + public Solution(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); + } + + public int getTotal() { + return rowCount.get(BlockType.HORIZONTAL) * columns.get(BlockType.HORIZONTAL).getResultValue() + + rowCount.get(BlockType.VERTICAL) * columns.get(BlockType.VERTICAL).getResultValue() + + rowCount.get(BlockType.CIRCULAR) * columns.get(BlockType.CIRCULAR).getResultValue(); + } + } + + private static class Column { + + private final int blocksPerColumn; + private final Block block; + + public Column(Block block, int columnLength) { + this.block = block; + this.blocksPerColumn = block.calculateBlockCount(columnLength); + } + + public int getResultValue() { + return block.getResultValue() * blocksPerColumn; + } + + public int getColumnWidth() { + return block.getBlockWidth(); + } + } + + private static class Block { + + private final PackagingDimension dimension; + private final BlockType type; + + public Block(PackagingDimension dimension, BlockType type) { + this.dimension = dimension; + this.type = type; + } + + public int calculateBlockCount(int containerColumnLength) { + return containerColumnLength / type.getBlockColumnLength(dimension); + } + + public int getResultValue() { + return type.getResultValue(); + } + + public int getBlockWidth() { + return type.getColumnWidth(dimension); + } + } +} diff --git a/src/main/java/de/avatic/lcc/service/calculation/steps/CustomCostCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/steps/CustomCostCalculationService.java new file mode 100644 index 0000000..c6965f0 --- /dev/null +++ b/src/main/java/de/avatic/lcc/service/calculation/steps/CustomCostCalculationService.java @@ -0,0 +1,69 @@ +package de.avatic.lcc.service.calculation.steps; + +import de.avatic.lcc.model.country.Country; +import de.avatic.lcc.model.premises.route.Route; +import de.avatic.lcc.model.premises.route.RouteSection; +import de.avatic.lcc.model.properties.CountryPropertyMappingId; +import de.avatic.lcc.model.properties.CustomUnionType; +import de.avatic.lcc.repositories.country.CountryPropertyRepository; +import de.avatic.lcc.repositories.premise.RouteNodeRepository; +import de.avatic.lcc.service.CustomApiService; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class CustomCostCalculationService { + + private final CustomApiService customApiService; + private final CountryPropertyRepository countryPropertyRepository; + private final RouteNodeRepository routeNodeRepository; + + public CustomCostCalculationService(CustomApiService customApiService, CountryPropertyRepository countryPropertyRepository, RouteNodeRepository routeNodeRepository) { + this.customApiService = customApiService; + this.countryPropertyRepository = countryPropertyRepository; + this.routeNodeRepository = routeNodeRepository; + } + + public double doCalculation(String hsCode, Country supplierCountry, Country destinationCountry, Route route, List section) { + + var destUnion = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, destinationCountry.getId()).orElseThrow(); + var sourceUnion = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, supplierCountry.getId()).orElseThrow(); + + if (CustomUnionType.EU.name().equals(destUnion.getCurrentValue()) && CustomUnionType.NONE.name().equals(sourceUnion.getCurrentValue())) { + var tariffRate = customApiService.getTariffRate(hsCode, supplierCountry.getId()); + + getCustomRelevantRouteSections(section); + + + } + + return 0; + + } + + private List getCustomRelevantRouteSections(List sections) { + + List customSections = new ArrayList<>(); + + for(RouteSection section : sections) { + if(CustomUnionType.EU == getCustomUnionByRouteNodeId(section.getFromRouteNodeId()) && + CustomUnionType.EU ==getCustomUnionByRouteNodeId(section.getToRouteNodeId())) { + customSections.add(section); + } + } + + return customSections; + } + + private CustomUnionType getCustomUnionByRouteNodeId(Integer routeNodeId) { + var node = routeNodeRepository.getById(routeNodeId).orElseThrow(); + return getCustomUnionByCountryId(node.getCountryId()); + } + + private CustomUnionType getCustomUnionByCountryId(Integer countryId) { + var property = countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.UNION, countryId).orElseThrow(); + return CustomUnionType.valueOf(property.getCurrentValue().toUpperCase()); + } +} diff --git a/src/main/java/de/avatic/lcc/service/calculation/steps/Door2DoorCostCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/steps/Door2DoorCostCalculationService.java new file mode 100644 index 0000000..d3895d1 --- /dev/null +++ b/src/main/java/de/avatic/lcc/service/calculation/steps/Door2DoorCostCalculationService.java @@ -0,0 +1,7 @@ +package de.avatic.lcc.service.calculation.steps; + +import org.springframework.stereotype.Service; + +@Service +public class Door2DoorCostCalculationService { +} diff --git a/src/main/java/de/avatic/lcc/service/calculation/steps/HandlingCostCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/steps/HandlingCostCalculationService.java new file mode 100644 index 0000000..df3f97a --- /dev/null +++ b/src/main/java/de/avatic/lcc/service/calculation/steps/HandlingCostCalculationService.java @@ -0,0 +1,22 @@ +package de.avatic.lcc.service.calculation.steps; + +import org.springframework.stereotype.Service; + +@Service +public class HandlingCostCalculationService { + + + public void doCalculation() { + + } + + private void calculateHandlingCost() { + } + + private void calculateInventoryCost() { + + } + private void calculateCapitalCost() { + + } +} diff --git a/src/main/java/de/avatic/lcc/service/calculation/steps/RouteSectionCostCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/steps/RouteSectionCostCalculationService.java new file mode 100644 index 0000000..f6ddfd6 --- /dev/null +++ b/src/main/java/de/avatic/lcc/service/calculation/steps/RouteSectionCostCalculationService.java @@ -0,0 +1,170 @@ +package de.avatic.lcc.service.calculation.steps; + +import de.avatic.lcc.dto.generic.ContainerType; +import de.avatic.lcc.dto.generic.RateType; +import de.avatic.lcc.dto.generic.TransportType; +import de.avatic.lcc.model.calculations.CalculationJobRouteSection; +import de.avatic.lcc.model.nodes.Location; +import de.avatic.lcc.model.packaging.PackagingDimension; +import de.avatic.lcc.model.packaging.PackagingType; +import de.avatic.lcc.model.premises.Premise; +import de.avatic.lcc.model.premises.route.Destination; +import de.avatic.lcc.model.premises.route.RouteNode; +import de.avatic.lcc.model.premises.route.RouteSection; +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.repositories.premise.RouteNodeRepository; +import de.avatic.lcc.repositories.properties.PropertyRepository; +import de.avatic.lcc.repositories.rates.ContainerRateRepository; +import de.avatic.lcc.repositories.rates.MatrixRateRepository; +import de.avatic.lcc.service.calculation.DistanceService; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +@Service +public class RouteSectionCostCalculationService { + + + private final ContainerCalculationService containerCalculationService; + private final ContainerRateRepository containerRateRepository; + private final MatrixRateRepository matrixRateRepository; + private final RouteNodeRepository routeNodeRepository; + private final DistanceService distanceService; + private final PropertyRepository propertyRepository; + + public RouteSectionCostCalculationService(ContainerCalculationService containerCalculationService, ContainerRateRepository containerRateRepository, MatrixRateRepository matrixRateRepository, RouteNodeRepository routeNodeRepository, DistanceService distanceService, PropertyRepository propertyRepository) { + this.containerCalculationService = containerCalculationService; + this.containerRateRepository = containerRateRepository; + this.matrixRateRepository = matrixRateRepository; + this.routeNodeRepository = routeNodeRepository; + this.distanceService = distanceService; + this.propertyRepository = propertyRepository; + } + + public CalculationJobRouteSection doCalculation(Premise premise, Destination destination, RouteSection section, ContainerType containerType) { + CalculationJobRouteSection calculationJobRouteSection = new CalculationJobRouteSection(); + + double utilization = getUtilization(section); + RouteNode fromNode = routeNodeRepository.getById(section.getFromRouteNodeId()).orElseThrow(); + RouteNode toNode = routeNodeRepository.getById(section.getFromRouteNodeId()).orElseThrow(); + double distance = getDistance(fromNode, toNode); + + int annualAmount = destination.getAnnualAmount(); + var containerCalculationResult = containerCalculationService.doCalculation(createHu(premise), containerType); + var annualVolume = annualAmount * containerCalculationResult.getHu().getVolume(DimensionUnit.M); + var transitTime = 3; + + ContainerRate containerRate = containerRateRepository.findRoute(section.getFromRouteNodeId(), section.getToRouteNodeId(), TransportType.valueOf(section.getTransportType().name())).orElseThrow(); + MatrixRate matrixRate = matrixRateRepository.getByCountryIds(fromNode.getCountryId(), toNode.getCountryId()).orElseThrow(); + + var rate = 0.0; + if (RateType.CONTAINER == section.getRateType()) { + rate = getContainerRate(containerRate, containerType); + transitTime = containerRate.getLeadTime(); + } else if (RateType.MATRIX == section.getRateType()) { + rate = getTruckRate(matrixRate, distance); + } + + double volumePrice; + double weightPrice; + + if (premise.getHuMixable()) { + volumePrice = (rate / containerType.getVolume()) / utilization; + weightPrice = (rate / containerCalculationResult.getMaxContainerWeight()) / utilization; + + } else { + volumePrice = (rate / containerType.getVolume()) / containerCalculationResult.getTotalUtilizationByVolume(); + weightPrice = (rate / containerCalculationResult.getMaxContainerWeight()) / containerCalculationResult.getTotalUtilizationByVolume(); + } + + double annualCost = annualVolume * (containerCalculationResult.isWeightExceeded() ? weightPrice : volumePrice); + + calculationJobRouteSection.setCbmPrice(!containerCalculationResult.isWeightExceeded()); + calculationJobRouteSection.setWeightPrice(containerCalculationResult.isWeightExceeded()); + calculationJobRouteSection.setCbmPrice(BigDecimal.valueOf(volumePrice)); + calculationJobRouteSection.setWeightPrice(BigDecimal.valueOf(weightPrice)); + + calculationJobRouteSection.setDistance(BigDecimal.valueOf(distance)); + calculationJobRouteSection.setRate(BigDecimal.valueOf(rate)); + + calculationJobRouteSection.setMainRun(section.getMainRun()); + calculationJobRouteSection.setPostRun(section.getPostRun()); + calculationJobRouteSection.setPreRun(section.getPreRun()); + + calculationJobRouteSection.setStacked(premise.getHuStackable()); + calculationJobRouteSection.setUnmixedPrice(premise.getHuMixable()); + + calculationJobRouteSection.setTransportType(TransportType.valueOf(section.getTransportType().name())); + calculationJobRouteSection.setRateType(RateType.valueOf(section.getRateType().name())); + + calculationJobRouteSection.setRate(BigDecimal.valueOf(rate)); + calculationJobRouteSection.setAnnualCost(BigDecimal.valueOf(annualCost)); + + calculationJobRouteSection.setTransitTime(transitTime); + + return calculationJobRouteSection; + } + + private double getUtilization(RouteSection section) { + double utilization = 0; + if (section.getRateType() == RateType.CONTAINER) { + utilization = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.CONTAINER_UTIL).orElseThrow().getCurrentValue()); + } + else if (section.getRateType() == RateType.MATRIX) { + utilization = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.TRUCK_UTIL).orElseThrow().getCurrentValue()); + } else + throw new IllegalArgumentException("Unknown rate type"); + + return utilization; + } + + private PackagingDimension createHu(Premise premise) { + var hu = new PackagingDimension(); + + hu.setWeight(premise.getIndividualHuWeight()); + hu.setWeightUnit(premise.getHuDisplayedWeightUnit()); + + hu.setHeight(premise.getIndividualHuHeight()); + hu.setWidth(premise.getIndividualHuWidth()); + hu.setLength(premise.getIndividualHuLength()); + hu.setDimensionUnit(premise.getHuDisplayedDimensionUnit()); + + hu.setContentUnitCount(premise.getHuUnitCount()); + hu.setType(PackagingType.HU); + + return hu; + } + + + private double getContainerRate(ContainerRate rate, ContainerType containerType) { + + switch (containerType) { + case HQ -> { + return rate.getRateHc().doubleValue(); + } + case FEU -> { + return rate.getRateFeu().doubleValue(); + } + case TEU -> { + return rate.getRateTeu().doubleValue(); + } + } + + throw new IllegalArgumentException("Unknown container type"); + } + + private double getDistance(RouteNode fromNode, RouteNode toNode) { + return distanceService.getDistanceFast( + fromNode.getCountryId(), new Location(fromNode.getGeoLng().doubleValue(), fromNode.getGeoLat().doubleValue()), + toNode.getCountryId(), new Location(fromNode.getGeoLng().doubleValue(), fromNode.getGeoLat().doubleValue())); + } + + private double getTruckRate(MatrixRate rate, double distance) { + + return rate.getRate().doubleValue() * distance; + } + +} diff --git a/src/main/java/de/avatic/lcc/service/calculation/steps/ShippingFrequencyCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/steps/ShippingFrequencyCalculationService.java new file mode 100644 index 0000000..efa3da3 --- /dev/null +++ b/src/main/java/de/avatic/lcc/service/calculation/steps/ShippingFrequencyCalculationService.java @@ -0,0 +1,27 @@ +package de.avatic.lcc.service.calculation.steps; + +import de.avatic.lcc.model.properties.SystemPropertyMappingId; +import de.avatic.lcc.repositories.properties.PropertyRepository; +import org.springframework.stereotype.Service; + +@Service +public class ShippingFrequencyCalculationService { + + private final PropertyRepository propertyRepository; + + public ShippingFrequencyCalculationService(PropertyRepository propertyRepository) { + this.propertyRepository = propertyRepository; + } + + public int doCalculation(int huAnnualAmount) { + var minAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MIN).orElseThrow().getCurrentValue()); + var maxAnnualFrequency = Integer.parseInt(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.FREQ_MAX).orElseThrow().getCurrentValue()); + + if (huAnnualAmount > maxAnnualFrequency) + return maxAnnualFrequency; + + return Math.max(huAnnualAmount, minAnnualFrequency); + + } + +} diff --git a/src/main/java/de/avatic/lcc/service/excelMapper/ContainerRateExcelMapper.java b/src/main/java/de/avatic/lcc/service/excelMapper/ContainerRateExcelMapper.java index f5fc599..8c153af 100644 --- a/src/main/java/de/avatic/lcc/service/excelMapper/ContainerRateExcelMapper.java +++ b/src/main/java/de/avatic/lcc/service/excelMapper/ContainerRateExcelMapper.java @@ -1,10 +1,10 @@ package de.avatic.lcc.service.excelMapper; +import de.avatic.lcc.dto.generic.TransportType; import de.avatic.lcc.model.bulk.ContainerRateHeader; import de.avatic.lcc.model.bulk.HiddenNodeHeader; import de.avatic.lcc.model.bulk.HiddenTableType; import de.avatic.lcc.model.rates.ContainerRate; -import de.avatic.lcc.model.rates.ContainerRateType; import de.avatic.lcc.repositories.NodeRepository; import de.avatic.lcc.repositories.rates.ContainerRateRepository; import de.avatic.lcc.service.bulk.helper.ConstraintGenerator; @@ -56,7 +56,7 @@ public class ContainerRateExcelMapper { constraintGenerator.createFormulaListConstraint(sheet, ContainerRateHeader.FROM_NODE.ordinal(), namedRange.getNameName()); constraintGenerator.createFormulaListConstraint(sheet, ContainerRateHeader.TO_NODE.ordinal(), namedRange.getNameName()); - constraintGenerator.createEnumConstraint(sheet, ContainerRateHeader.CONTAINER_RATE_TYPE.ordinal(), ContainerRateType.class); + constraintGenerator.createEnumConstraint(sheet, ContainerRateHeader.CONTAINER_RATE_TYPE.ordinal(), TransportType.class); constraintGenerator.createDecimalConstraint(sheet, ContainerRateHeader.RATE_FEU.ordinal(), 0.0, 1000000.0); constraintGenerator.createDecimalConstraint(sheet, ContainerRateHeader.RATE_TEU.ordinal(),0.0, 1000000.0); constraintGenerator.createDecimalConstraint(sheet, ContainerRateHeader.RATE_HC.ordinal(),0.0, 1000000.0); @@ -78,9 +78,9 @@ public class ContainerRateExcelMapper { entity.setFromNodeId(nodeRepository.getByExternalMappingId(row.getCell(ContainerRateHeader.FROM_NODE.ordinal()).getStringCellValue()).orElseThrow().getId()); entity.setFromNodeId(nodeRepository.getByExternalMappingId(row.getCell(ContainerRateHeader.TO_NODE.ordinal()).getStringCellValue()).orElseThrow().getId()); - entity.setType(ContainerRateType.valueOf(row.getCell(ContainerRateHeader.CONTAINER_RATE_TYPE.ordinal()).getStringCellValue())); + entity.setType(TransportType.valueOf(row.getCell(ContainerRateHeader.CONTAINER_RATE_TYPE.ordinal()).getStringCellValue())); entity.setLeadTime(Double.valueOf(row.getCell(ContainerRateHeader.LEAD_TIME.ordinal()).getNumericCellValue()).intValue()); - entity.setType(ContainerRateType.valueOf(row.getCell(ContainerRateHeader.CONTAINER_RATE_TYPE.ordinal()).getStringCellValue())); + entity.setType(TransportType.valueOf(row.getCell(ContainerRateHeader.CONTAINER_RATE_TYPE.ordinal()).getStringCellValue())); entity.setRateFeu(BigDecimal.valueOf(row.getCell(ContainerRateHeader.RATE_FEU.ordinal()).getNumericCellValue())); entity.setRateTeu(BigDecimal.valueOf(row.getCell(ContainerRateHeader.RATE_TEU.ordinal()).getNumericCellValue())); entity.setRateHc(BigDecimal.valueOf(row.getCell(ContainerRateHeader.RATE_HC.ordinal()).getNumericCellValue())); diff --git a/src/main/java/de/avatic/lcc/service/report/ExcelReportingService.java b/src/main/java/de/avatic/lcc/service/report/ExcelReportingService.java index 75aa63c..8bd8f1f 100644 --- a/src/main/java/de/avatic/lcc/service/report/ExcelReportingService.java +++ b/src/main/java/de/avatic/lcc/service/report/ExcelReportingService.java @@ -44,7 +44,7 @@ public class ExcelReportingService { CellStyle headerStyle = headerCellStyleProvider.createHeaderCellStyle(workbook); headerGenerator.generateHeader(sheet, reports.stream().map(ReportDTO::getSupplier).map(NodeDTO::getName).toArray(String[]::new), headerStyle); - List flatteners = reports.stream().map(ReportFlattener::new).toList(); + List flatterers = reports.stream().map(ReportFlattener::new).toList(); while(true) { @@ -53,7 +53,7 @@ public class ExcelReportingService { var row = sheet.createRow(sheet.getLastRowNum() + 1); int cellIdx = 1 /* 0 is the header column */; - for(ReportFlattener flattener : flatteners) { + for(ReportFlattener flattener : flatterers) { Cell cell = row.createCell(cellIdx); diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index c0fbf42..23c367a 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -329,6 +329,9 @@ CREATE TABLE IF NOT EXISTS premise material_id INT NOT NULL, supplier_node_id INT, user_supplier_node_id INT, + geo_lat DECIMAL(7, 4) CHECK (geo_lat BETWEEN -90 AND 90), + geo_lng DECIMAL(7, 4) CHECK (geo_lng BETWEEN -180 AND 180), + country_id INT NOT NULL, packaging_id INT DEFAULT NULL, user_id INT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, @@ -378,9 +381,11 @@ CREATE TABLE IF NOT EXISTS premise_destination repacking_cost DECIMAL(15, 2) DEFAULT NULL, handling_cost DECIMAL(15, 2) DEFAULT NULL, disposal_cost DECIMAL(15, 2) DEFAULT NULL, - geo_lat DECIMAL(7, 4) CHECK (geo_lat BETWEEN -90 AND 90), - geo_lng DECIMAL(7, 4) CHECK (geo_lng BETWEEN -180 AND 180), + geo_lat DECIMAL(7, 4) CHECK (geo_lat BETWEEN -90 AND 90), + geo_lng DECIMAL(7, 4) CHECK (geo_lng BETWEEN -180 AND 180), + country_id INT NOT NULL, FOREIGN KEY (premise_id) REFERENCES premise (id), + FOREIGN KEY (country_id) REFERENCES country (id), FOREIGN KEY (destination_node_id) REFERENCES node (id), INDEX idx_destination_node_id (destination_node_id), INDEX idx_premise_id (premise_id) @@ -502,7 +507,7 @@ CREATE TABLE IF NOT EXISTS calculation_job_destination transportation_type CHAR(8) CHECK (transportation_type IN ('TEU', 'FEU', 'HC', 'TRUCK')), hu_per_layer INT UNSIGNED NOT NULL COMMENT 'number of handling units per layer', - layer_structure JSON NOT NULL COMMENT 'json representation of a single layer', + layer_structure JSON COMMENT 'json representation of a single layer', layer_count INT UNSIGNED NOT NULL COMMENT 'number of layers per full container or truck', transport_weight_exceeded BOOLEAN DEFAULT FALSE COMMENT 'limiting factor: TRUE if weight limited or FALSE if volume limited', transports_per_year DECIMAL(15, 2) NOT NULL COMMENT 'TODO: what is this?!', @@ -523,8 +528,8 @@ CREATE TABLE IF NOT EXISTS calculation_job_route_section id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, premise_route_section_id INT NOT NULL, calculation_job_destination_id INT NOT NULL, - transport_type CHAR(16) CHECK (transport_type IN - ('RAIL', 'SEA', 'ROAD', 'POST-RUN', 'MATRIX', 'D2D')), + transport_type CHAR(16) CHECK (transport_type IN + ('RAIL', 'SEA', 'ROAD', 'POST-RUN', 'MATRIX', 'D2D')), is_unmixed_price BOOLEAN DEFAULT FALSE, is_cbm_price BOOLEAN DEFAULT FALSE, is_weight_price BOOLEAN DEFAULT FALSE,