diff --git a/src/main/java/de/avatic/lcc/controller/calculation/CalculationController.java b/src/main/java/de/avatic/lcc/controller/calculation/CalculationController.java index 921fdc2..7190689 100644 --- a/src/main/java/de/avatic/lcc/controller/calculation/CalculationController.java +++ b/src/main/java/de/avatic/lcc/controller/calculation/CalculationController.java @@ -117,7 +117,7 @@ public class CalculationController { } @PutMapping("/material") - public ResponseEntity> setMaterial(SetDataDTO setMaterialDTO) { + public ResponseEntity> setMaterial(@RequestBody SetDataDTO setMaterialDTO) { return ResponseEntity.ok(changeMaterialService.setMaterial(setMaterialDTO)); } diff --git a/src/main/java/de/avatic/lcc/controller/report/ReportingController.java b/src/main/java/de/avatic/lcc/controller/report/ReportingController.java index eba49c9..a608436 100644 --- a/src/main/java/de/avatic/lcc/controller/report/ReportingController.java +++ b/src/main/java/de/avatic/lcc/controller/report/ReportingController.java @@ -3,7 +3,6 @@ package de.avatic.lcc.controller.report; import de.avatic.lcc.dto.generic.NodeDTO; import de.avatic.lcc.dto.report.ReportDTO; import de.avatic.lcc.service.report.ExcelReportingService; -import de.avatic.lcc.service.report.ReportFinderService; import de.avatic.lcc.service.report.ReportingService; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpHeaders; @@ -23,19 +22,16 @@ public class ReportingController { private final ReportingService reportingService; private final ExcelReportingService excelReportingService; - private final ReportFinderService reportFinderService; /** * Constructor for ReportingController. * * @param reportingService Service used for generating reports. * @param excelReportingService Service used for generating Excel files for reports. - * @param reportFinderService Service used for finding suppliers for reporting. */ - public ReportingController(ReportingService reportingService, ExcelReportingService excelReportingService, ReportFinderService reportFinderService) { + public ReportingController(ReportingService reportingService, ExcelReportingService excelReportingService) { this.reportingService = reportingService; this.excelReportingService = excelReportingService; - this.reportFinderService = reportFinderService; } /** @@ -46,7 +42,7 @@ public class ReportingController { */ @GetMapping("/search") public ResponseEntity>> findSupplierForReporting(@RequestParam(value = "material") Integer materialId) { - return ResponseEntity.ok(reportFinderService.findSupplierForReporting(materialId)); + return ResponseEntity.ok(reportingService.findSupplierForReporting(materialId)); } /** @@ -57,7 +53,7 @@ public class ReportingController { * @return The generated report details. */ @GetMapping("/view") - public ResponseEntity getReport(@RequestParam(value = "material") Integer materialId, @RequestParam(value = "sources") List nodeIds) { + public ResponseEntity> getReport(@RequestParam(value = "material") Integer materialId, @RequestParam(value = "sources") List nodeIds) { return ResponseEntity.ok(reportingService.getReport(materialId, nodeIds)); } diff --git a/src/main/java/de/avatic/lcc/dto/report/ReportDTO.java b/src/main/java/de/avatic/lcc/dto/report/ReportDTO.java index d4d6a72..07c4b84 100644 --- a/src/main/java/de/avatic/lcc/dto/report/ReportDTO.java +++ b/src/main/java/de/avatic/lcc/dto/report/ReportDTO.java @@ -2,33 +2,33 @@ package de.avatic.lcc.dto.report; import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.HashMap; +import java.util.Map; public class ReportDTO { @JsonProperty("costs") - public HashMap cost; + public Map cost; @JsonProperty("risk") - public HashMap risk; + public Map risk; @JsonProperty("premises") public ReportPremisesDTO premises; - public HashMap getCost() { + public Map getCost() { return cost; } - public void setCost(HashMap cost) { + public void setCost(Map cost) { this.cost = cost; } - public HashMap getRisk() { + public Map getRisk() { return risk; } - public void setRisk(HashMap risk) { + public void setRisk(Map risk) { this.risk = risk; } diff --git a/src/main/java/de/avatic/lcc/dto/report/ReportQuantityDTO.java b/src/main/java/de/avatic/lcc/dto/report/ReportQuantityDTO.java index ffb8b07..a8bfe7f 100644 --- a/src/main/java/de/avatic/lcc/dto/report/ReportQuantityDTO.java +++ b/src/main/java/de/avatic/lcc/dto/report/ReportQuantityDTO.java @@ -1,21 +1,22 @@ package de.avatic.lcc.dto.report; import com.fasterxml.jackson.annotation.JsonProperty; +import de.avatic.lcc.dto.generic.NodeDTO; import java.util.List; public class ReportQuantityDTO { - private String destination; + private NodeDTO destination; private Number quantity; private List route; - public String getDestination() { + public NodeDTO getDestination() { return destination; } - public void setDestination(String destination) { + public void setDestination(NodeDTO destination) { this.destination = destination; } diff --git a/src/main/java/de/avatic/lcc/model/calculations/CalculationJob.java b/src/main/java/de/avatic/lcc/model/calculations/CalculationJob.java index 6204f1b..fda95d8 100644 --- a/src/main/java/de/avatic/lcc/model/calculations/CalculationJob.java +++ b/src/main/java/de/avatic/lcc/model/calculations/CalculationJob.java @@ -1,44 +1,28 @@ package de.avatic.lcc.model.calculations; -import de.avatic.lcc.model.premises.Premise; -import de.avatic.lcc.model.properties.PropertySet; -import de.avatic.lcc.model.user.SysUser; -import de.avatic.lcc.model.rates.ValidityPeriod; -import jakarta.validation.constraints.NotNull; -import org.springframework.data.annotation.Id; -import org.springframework.data.jdbc.core.mapping.AggregateReference; -import org.springframework.data.relational.core.mapping.MappedCollection; -import org.springframework.data.relational.core.mapping.Table; - +import java.math.BigDecimal; +import java.time.LocalDateTime; import java.time.OffsetDateTime; -import java.util.Set; -@Table(name = "calculation_job") public class CalculationJob { - @Id + private Integer id; - @NotNull - private OffsetDateTime calculationDate; + private LocalDateTime calculationDate; private CalculationJobState jobState; - @NotNull - private AggregateReference premiss; + private Integer premiseId; - @NotNull - private AggregateReference validityPeriod; + private Integer validityPeriodId; - @NotNull - private AggregateReference propertySet; + private Integer propertySetId; - @NotNull - private AggregateReference user; + private Integer userId; - @MappedCollection(idColumn = "calculation_job_id") - private Set sinks; + private BigDecimal weightedTotalCosts; public Integer getId() { return id; @@ -48,11 +32,11 @@ public class CalculationJob { this.id = id; } - public OffsetDateTime getCalculationDate() { + public LocalDateTime getCalculationDate() { return calculationDate; } - public void setCalculationDate(OffsetDateTime calculationDate) { + public void setCalculationDate(LocalDateTime calculationDate) { this.calculationDate = calculationDate; } @@ -64,43 +48,43 @@ public class CalculationJob { this.jobState = jobState; } - public AggregateReference getPremiss() { - return premiss; + public Integer getPremiseId() { + return premiseId; } - public void setPremiss(AggregateReference premiss) { - this.premiss = premiss; + public void setPremiseId(Integer premiseId) { + this.premiseId = premiseId; } - public AggregateReference getValidityPeriod() { - return validityPeriod; + public Integer getValidityPeriodId() { + return validityPeriodId; } - public void setValidityPeriod(AggregateReference validityPeriod) { - this.validityPeriod = validityPeriod; + public void setValidityPeriodId(Integer validityPeriodId) { + this.validityPeriodId = validityPeriodId; } - public AggregateReference getPropertySet() { - return propertySet; + public Integer getPropertySetId() { + return propertySetId; } - public void setPropertySet(AggregateReference propertySet) { - this.propertySet = propertySet; + public void setPropertySetId(Integer propertySetId) { + this.propertySetId = propertySetId; } - public AggregateReference getUser() { - return user; + public Integer getUserId() { + return userId; } - public void setUser(AggregateReference user) { - this.user = user; + public void setUserId(Integer userId) { + this.userId = userId; } - public Set getSinks() { - return sinks; + public BigDecimal getWeightedTotalCosts() { + return weightedTotalCosts; } - public void setSinks(Set sinks) { - this.sinks = sinks; + public void setWeightedTotalCosts(BigDecimal weightedTotalCosts) { + this.weightedTotalCosts = weightedTotalCosts; } } diff --git a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobAirfreight.java b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobAirfreight.java deleted file mode 100644 index 39421f1..0000000 --- a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobAirfreight.java +++ /dev/null @@ -1,85 +0,0 @@ -package de.avatic.lcc.model.calculations; - - -import jakarta.validation.constraints.Digits; -import jakarta.validation.constraints.NotNull; -import org.springframework.data.annotation.Id; -import org.springframework.data.relational.core.mapping.Table; - -import java.math.BigDecimal; - - -@Table(name = "calculation_job_airfreight") -public class CalculationJobAirfreight { - - @Id - private Integer id; - - @NotNull - @Digits(integer = 7, fraction = 4) - private BigDecimal airFreightShareMax; - - @NotNull - @Digits(integer = 7, fraction = 4) - private BigDecimal airFreightShare; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal airFreightVolumetricWeight; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal airFreightWeight; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal annualCost; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public BigDecimal getAirFreightShareMax() { - return airFreightShareMax; - } - - public void setAirFreightShareMax(BigDecimal airFreightShareMax) { - this.airFreightShareMax = airFreightShareMax; - } - - public BigDecimal getAirFreightShare() { - return airFreightShare; - } - - public void setAirFreightShare(BigDecimal airFreightShare) { - this.airFreightShare = airFreightShare; - } - - public BigDecimal getAirFreightVolumetricWeight() { - return airFreightVolumetricWeight; - } - - public void setAirFreightVolumetricWeight(BigDecimal airFreightVolumetricWeight) { - this.airFreightVolumetricWeight = airFreightVolumetricWeight; - } - - public BigDecimal getAirFreightWeight() { - return airFreightWeight; - } - - public void setAirFreightWeight(BigDecimal airFreightWeight) { - this.airFreightWeight = airFreightWeight; - } - - public BigDecimal getAnnualCost() { - return annualCost; - } - - public void setAnnualCost(BigDecimal annualCost) { - this.annualCost = annualCost; - } -} diff --git a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobCustom.java b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobCustom.java deleted file mode 100644 index a402195..0000000 --- a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobCustom.java +++ /dev/null @@ -1,72 +0,0 @@ -package de.avatic.lcc.model.calculations; - -import jakarta.validation.constraints.Digits; -import jakarta.validation.constraints.NotNull; -import org.springframework.data.annotation.Id; -import org.springframework.data.relational.core.mapping.Table; - -import java.math.BigDecimal; - - -@Table(name = "calculation_job_custom") -public class CalculationJobCustom { - - @Id - private Integer id; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal customValue; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal customDuties; - - @NotNull - @Digits(integer = 7, fraction = 4) - private BigDecimal customRate; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal annualCost; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public BigDecimal getCustomValue() { - return customValue; - } - - public void setCustomValue(BigDecimal customValue) { - this.customValue = customValue; - } - - public BigDecimal getCustomDuties() { - return customDuties; - } - - public void setCustomDuties(BigDecimal customDuties) { - this.customDuties = customDuties; - } - - public BigDecimal getCustomRate() { - return customRate; - } - - public void setCustomRate(BigDecimal customRate) { - this.customRate = customRate; - } - - public BigDecimal getAnnualCost() { - return annualCost; - } - - public void setAnnualCost(BigDecimal annualCost) { - this.annualCost = annualCost; - } -} diff --git a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobDestination.java b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobDestination.java new file mode 100644 index 0000000..233f358 --- /dev/null +++ b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobDestination.java @@ -0,0 +1,376 @@ +package de.avatic.lcc.model.calculations; + +import java.math.BigDecimal; +import java.util.List; + +public class CalculationJobDestination { + private Integer id; + private Integer calculationJobId; + private Integer premiseDestinationId; + private Integer shippingFrequency; + private BigDecimal totalCost; + private BigDecimal annualAmount; + + // Risk + private BigDecimal annualRiskCost; + private BigDecimal annualChanceCost; + + // Handling + private Boolean isSmallUnit; + private BigDecimal annualRepackingCost; + private BigDecimal annualHandlingCost; + private BigDecimal annualDisposalCost; + + // Inventory + private BigDecimal operationalStock; + private BigDecimal safetyStock; + private BigDecimal stockedInventory; + private BigDecimal inTransportStock; + private BigDecimal stockBeforePayment; + private BigDecimal annualCapitalCost; + private BigDecimal annualStorageCost; + + // Custom + private BigDecimal customValue; + private BigDecimal customDuties; + private BigDecimal tariffRate; + private BigDecimal annualCustomCost; + + // Air Freight Risk + private BigDecimal airFreightShareMax; + private BigDecimal airFreightShare; + private BigDecimal airFreightVolumetricWeight; + private BigDecimal airFreightWeight; + private BigDecimal annualAirFreightCost; + + // Transportation + private String transportationType; + private Integer huPerLayer; + private String layerStructure; // JSON as String + private Integer layerCount; + private Boolean transportWeightExceeded; + private BigDecimal transportsPerYear; + private BigDecimal annualTransportationCost; + + private BigDecimal containerUtilization; + + private Integer totalTransitTime; + + // Material Cost + private BigDecimal materialCost; + private BigDecimal fcaCost; + + + public BigDecimal getContainerUtilization() { + return containerUtilization; + } + + public void setContainerUtilization(BigDecimal containerUtilization) { + this.containerUtilization = containerUtilization; + } + + public Integer getTotalTransitTime() { + return totalTransitTime; + } + + public void setTotalTransitTime(Integer totalTransitTime) { + this.totalTransitTime = totalTransitTime; + } + + public BigDecimal getAnnualAmount() { + return annualAmount; + } + + public void setAnnualAmount(BigDecimal annualAmount) { + this.annualAmount = annualAmount; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getCalculationJobId() { + return calculationJobId; + } + + public void setCalculationJobId(Integer calculationJobId) { + this.calculationJobId = calculationJobId; + } + + public Integer getPremiseDestinationId() { + return premiseDestinationId; + } + + public void setPremiseDestinationId(Integer premiseDestinationId) { + this.premiseDestinationId = premiseDestinationId; + } + + public Integer getShippingFrequency() { + return shippingFrequency; + } + + public void setShippingFrequency(Integer shippingFrequency) { + this.shippingFrequency = shippingFrequency; + } + + public BigDecimal getTotalCost() { + return totalCost; + } + + public void setTotalCost(BigDecimal totalCost) { + this.totalCost = totalCost; + } + + public BigDecimal getAnnualRiskCost() { + return annualRiskCost; + } + + public void setAnnualRiskCost(BigDecimal annualRiskCost) { + this.annualRiskCost = annualRiskCost; + } + + public BigDecimal getAnnualChanceCost() { + return annualChanceCost; + } + + public void setAnnualChanceCost(BigDecimal annualChanceCost) { + this.annualChanceCost = annualChanceCost; + } + + public Boolean getSmallUnit() { + return isSmallUnit; + } + + public void setSmallUnit(Boolean smallUnit) { + isSmallUnit = smallUnit; + } + + public BigDecimal getAnnualRepackingCost() { + return annualRepackingCost; + } + + public void setAnnualRepackingCost(BigDecimal annualRepackingCost) { + this.annualRepackingCost = annualRepackingCost; + } + + public BigDecimal getAnnualHandlingCost() { + return annualHandlingCost; + } + + public void setAnnualHandlingCost(BigDecimal annualHandlingCost) { + this.annualHandlingCost = annualHandlingCost; + } + + public BigDecimal getAnnualDisposalCost() { + return annualDisposalCost; + } + + public void setAnnualDisposalCost(BigDecimal annualDisposalCost) { + this.annualDisposalCost = annualDisposalCost; + } + + public BigDecimal getOperationalStock() { + return operationalStock; + } + + public void setOperationalStock(BigDecimal operationalStock) { + this.operationalStock = operationalStock; + } + + public BigDecimal getSafetyStock() { + return safetyStock; + } + + public void setSafetyStock(BigDecimal safetyStock) { + this.safetyStock = safetyStock; + } + + public BigDecimal getStockedInventory() { + return stockedInventory; + } + + public void setStockedInventory(BigDecimal stockedInventory) { + this.stockedInventory = stockedInventory; + } + + public BigDecimal getInTransportStock() { + return inTransportStock; + } + + public void setInTransportStock(BigDecimal inTransportStock) { + this.inTransportStock = inTransportStock; + } + + public BigDecimal getStockBeforePayment() { + return stockBeforePayment; + } + + public void setStockBeforePayment(BigDecimal stockBeforePayment) { + this.stockBeforePayment = stockBeforePayment; + } + + public BigDecimal getAnnualCapitalCost() { + return annualCapitalCost; + } + + public void setAnnualCapitalCost(BigDecimal annualCapitalCost) { + this.annualCapitalCost = annualCapitalCost; + } + + public BigDecimal getAnnualStorageCost() { + return annualStorageCost; + } + + public void setAnnualStorageCost(BigDecimal annualStorageCost) { + this.annualStorageCost = annualStorageCost; + } + + public BigDecimal getCustomValue() { + return customValue; + } + + public void setCustomValue(BigDecimal customValue) { + this.customValue = customValue; + } + + public BigDecimal getCustomDuties() { + return customDuties; + } + + public void setCustomDuties(BigDecimal customDuties) { + this.customDuties = customDuties; + } + + public BigDecimal getTariffRate() { + return tariffRate; + } + + public void setTariffRate(BigDecimal tariffRate) { + this.tariffRate = tariffRate; + } + + public BigDecimal getAnnualCustomCost() { + return annualCustomCost; + } + + public void setAnnualCustomCost(BigDecimal annualCustomCost) { + this.annualCustomCost = annualCustomCost; + } + + public BigDecimal getAirFreightShareMax() { + return airFreightShareMax; + } + + public void setAirFreightShareMax(BigDecimal airFreightShareMax) { + this.airFreightShareMax = airFreightShareMax; + } + + public BigDecimal getAirFreightShare() { + return airFreightShare; + } + + public void setAirFreightShare(BigDecimal airFreightShare) { + this.airFreightShare = airFreightShare; + } + + public BigDecimal getAirFreightVolumetricWeight() { + return airFreightVolumetricWeight; + } + + public void setAirFreightVolumetricWeight(BigDecimal airFreightVolumetricWeight) { + this.airFreightVolumetricWeight = airFreightVolumetricWeight; + } + + public BigDecimal getAirFreightWeight() { + return airFreightWeight; + } + + public void setAirFreightWeight(BigDecimal airFreightWeight) { + this.airFreightWeight = airFreightWeight; + } + + public BigDecimal getAnnualAirFreightCost() { + return annualAirFreightCost; + } + + public void setAnnualAirFreightCost(BigDecimal annualAirFreightCost) { + this.annualAirFreightCost = annualAirFreightCost; + } + + public String getTransportationType() { + return transportationType; + } + + public void setTransportationType(String transportationType) { + this.transportationType = transportationType; + } + + public Integer getHuPerLayer() { + return huPerLayer; + } + + public void setHuPerLayer(Integer huPerLayer) { + this.huPerLayer = huPerLayer; + } + + public String getLayerStructure() { + return layerStructure; + } + + public void setLayerStructure(String layerStructure) { + this.layerStructure = layerStructure; + } + + public Integer getLayerCount() { + return layerCount; + } + + public void setLayerCount(Integer layerCount) { + this.layerCount = layerCount; + } + + public Boolean getTransportWeightExceeded() { + return transportWeightExceeded; + } + + public void setTransportWeightExceeded(Boolean transportWeightExceeded) { + this.transportWeightExceeded = transportWeightExceeded; + } + + public BigDecimal getTransportsPerYear() { + return transportsPerYear; + } + + public void setTransportsPerYear(BigDecimal transportsPerYear) { + this.transportsPerYear = transportsPerYear; + } + + public BigDecimal getAnnualTransportationCost() { + return annualTransportationCost; + } + + public void setAnnualTransportationCost(BigDecimal annualTransportationCost) { + this.annualTransportationCost = annualTransportationCost; + } + + public BigDecimal getMaterialCost() { + return materialCost; + } + + public void setMaterialCost(BigDecimal materialCost) { + this.materialCost = materialCost; + } + + public BigDecimal getFcaCost() { + return fcaCost; + } + + public void setFcaCost(BigDecimal fcaCost) { + this.fcaCost = fcaCost; + } + +} diff --git a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobHandling.java b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobHandling.java deleted file mode 100644 index 50d699b..0000000 --- a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobHandling.java +++ /dev/null @@ -1,70 +0,0 @@ -package de.avatic.lcc.model.calculations; - -import jakarta.validation.constraints.Digits; -import jakarta.validation.constraints.NotNull; -import org.springframework.data.annotation.Id; -import org.springframework.data.relational.core.mapping.Table; - -import java.math.BigDecimal; - - -@Table(name = "calculation_job_handling") -public class CalculationJobHandling { - - @Id - private Integer id; - - private Boolean isSmallUnit; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal annualRepackingCost; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal annualHandlingCost; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal annualDisposalCost; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public Boolean getSmallUnit() { - return isSmallUnit; - } - - public void setSmallUnit(Boolean smallUnit) { - isSmallUnit = smallUnit; - } - - public BigDecimal getAnnualRepackingCost() { - return annualRepackingCost; - } - - public void setAnnualRepackingCost(BigDecimal annualRepackingCost) { - this.annualRepackingCost = annualRepackingCost; - } - - public BigDecimal getAnnualHandlingCost() { - return annualHandlingCost; - } - - public void setAnnualHandlingCost(BigDecimal annualHandlingCost) { - this.annualHandlingCost = annualHandlingCost; - } - - public BigDecimal getAnnualDisposalCost() { - return annualDisposalCost; - } - - public void setAnnualDisposalCost(BigDecimal annualDisposalCost) { - this.annualDisposalCost = annualDisposalCost; - } -} diff --git a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobInventory.java b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobInventory.java deleted file mode 100644 index 89a5232..0000000 --- a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobInventory.java +++ /dev/null @@ -1,108 +0,0 @@ -package de.avatic.lcc.model.calculations; - -import jakarta.validation.constraints.Digits; -import jakarta.validation.constraints.NotNull; -import org.springframework.data.annotation.Id; -import org.springframework.data.relational.core.mapping.Table; - -import java.math.BigDecimal; - - -@Table(name = "calculation_job_inventory") -public class CalculationJobInventory { - - @Id - private Integer id; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal operationalStock; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal safetyStock; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal stockedInventory; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal inTransportStock; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal stockBeforePayment; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal annualCapitalCost; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal annualStorageCost; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public BigDecimal getOperationalStock() { - return operationalStock; - } - - public void setOperationalStock(BigDecimal operationalStock) { - this.operationalStock = operationalStock; - } - - public BigDecimal getSafetyStock() { - return safetyStock; - } - - public void setSafetyStock(BigDecimal safetyStock) { - this.safetyStock = safetyStock; - } - - public BigDecimal getStockedInventory() { - return stockedInventory; - } - - public void setStockedInventory(BigDecimal stockedInventory) { - this.stockedInventory = stockedInventory; - } - - public BigDecimal getInTransportStock() { - return inTransportStock; - } - - public void setInTransportStock(BigDecimal inTransportStock) { - this.inTransportStock = inTransportStock; - } - - public BigDecimal getStockBeforePayment() { - return stockBeforePayment; - } - - public void setStockBeforePayment(BigDecimal stockBeforePayment) { - this.stockBeforePayment = stockBeforePayment; - } - - public BigDecimal getAnnualCapitalCost() { - return annualCapitalCost; - } - - public void setAnnualCapitalCost(BigDecimal annualCapitalCost) { - this.annualCapitalCost = annualCapitalCost; - } - - public BigDecimal getAnnualStorageCost() { - return annualStorageCost; - } - - public void setAnnualStorageCost(BigDecimal annualStorageCost) { - this.annualStorageCost = annualStorageCost; - } -} diff --git a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobRisk.java b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobRisk.java deleted file mode 100644 index 55fe8ca..0000000 --- a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobRisk.java +++ /dev/null @@ -1,48 +0,0 @@ -package de.avatic.lcc.model.calculations; - -import jakarta.validation.constraints.Digits; -import jakarta.validation.constraints.NotNull; -import org.springframework.data.annotation.Id; -import org.springframework.data.relational.core.mapping.Table; - -import java.math.BigDecimal; - - -@Table(name = "calculation_job_risk") -public class CalculationJobRisk { - - @Id - private Integer id; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal annualRiskCost; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal annualChanceCost; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public BigDecimal getAnnualRiskCost() { - return annualRiskCost; - } - - public void setAnnualRiskCost(BigDecimal annualRiskCost) { - this.annualRiskCost = annualRiskCost; - } - - public BigDecimal getAnnualChanceCost() { - return annualChanceCost; - } - - public void setAnnualChanceCost(BigDecimal annualChanceCost) { - this.annualChanceCost = annualChanceCost; - } -} 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 8af8a61..633cd1f 100644 --- a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobRouteSection.java +++ b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobRouteSection.java @@ -1,184 +1,159 @@ package de.avatic.lcc.model.calculations; -import de.avatic.lcc.model.premises.route.RouteSection; -import jakarta.validation.constraints.Digits; -import jakarta.validation.constraints.NotNull; -import org.springframework.data.annotation.Id; -import org.springframework.data.jdbc.core.mapping.AggregateReference; -import org.springframework.data.relational.core.mapping.Table; + import java.math.BigDecimal; -import java.math.BigDecimal; + public class CalculationJobRouteSection { + private Integer id; + private Integer premiseRouteSectionId; + private Integer calculationJobDestinationId; + private CalculationJobTransportationRuleType usedRule; + private Boolean isUnmixedPrice; + private Boolean isCbmPrice; + private Boolean isWeightPrice; + private Boolean isStacked; + private Boolean isPreRun; + private Boolean isMainRun; + private Boolean isPostRun; + private BigDecimal rate; + private BigDecimal distance; + private BigDecimal cbmPrice; + private BigDecimal weightPrice; + private BigDecimal annualCost; + private Integer transitTime; + public Integer getId() { + return id; + } -@Table(name = "calculation_job_route_section") -public class CalculationJobRouteSection { + public void setId(Integer id) { + this.id = id; + } - @Id - private Integer id; + public Integer getPremiseRouteSectionId() { + return premiseRouteSectionId; + } - private CalculationJobTransportationRuleType usedRule; + public void setPremiseRouteSectionId(Integer premiseRouteSectionId) { + this.premiseRouteSectionId = premiseRouteSectionId; + } - private Boolean isUnmixedPrice; + public Integer getCalculationJobDestinationId() { + return calculationJobDestinationId; + } - private Boolean isCbmPrice; + public void setCalculationJobDestinationId(Integer calculationJobDestinationId) { + this.calculationJobDestinationId = calculationJobDestinationId; + } - private Boolean isWeightPrice; + public CalculationJobTransportationRuleType getUsedRule() { + return usedRule; + } - private Boolean isStacked; + public void setUsedRule(CalculationJobTransportationRuleType usedRule) { + this.usedRule = usedRule; + } - private Boolean isPreRun; + public Boolean getUnmixedPrice() { + return isUnmixedPrice; + } - private Boolean isMainRun; + public void setUnmixedPrice(Boolean unmixedPrice) { + isUnmixedPrice = unmixedPrice; + } - private Boolean isPostRun; + public Boolean isCbmPrice() { + return isCbmPrice; + } - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal rate; + public BigDecimal getCbmPrice() { + return this.cbmPrice; + } - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal distance; + public BigDecimal getWeightPrice() { + return this.weightPrice; + } - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal cbmPrice; + public void setCbmPrice(BigDecimal cbmPrice) { + this.cbmPrice = cbmPrice; + } - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal weightPrice; + public void setCbmPrice(Boolean cbmPrice) { + isCbmPrice = cbmPrice; + } - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal annualCost; + public Boolean isWeightPrice() { + return isWeightPrice; + } - @NotNull - @Digits(integer = 15, fraction = 2) - private Integer transitTime; + public void setWeightPrice(BigDecimal weightPrice) { + this.weightPrice = weightPrice; + } - @NotNull - private AggregateReference premissRouteSection; + public BigDecimal getAnnualCost() { + return annualCost; + } - public Integer getId() { - return id; + public void setAnnualCost(BigDecimal annualCost) { + this.annualCost = annualCost; + } + + public Integer getTransitTime() { + return transitTime; + } + + public void setTransitTime(Integer transitTime) { + this.transitTime = transitTime; + } + + public void setWeightPrice(Boolean weightPrice) { + isWeightPrice = weightPrice; + } + + public Boolean getStacked() { + return isStacked; + } + + public void setStacked(Boolean stacked) { + isStacked = stacked; + } + + public Boolean getPreRun() { + return isPreRun; + } + + public void setPreRun(Boolean preRun) { + isPreRun = preRun; + } + + public Boolean getMainRun() { + return isMainRun; + } + + public void setMainRun(Boolean mainRun) { + isMainRun = mainRun; + } + + public Boolean getPostRun() { + return isPostRun; + } + + public void setPostRun(Boolean postRun) { + isPostRun = postRun; + } + + public BigDecimal getRate() { + return rate; + } + + public void setRate(BigDecimal rate) { + this.rate = rate; + } + + public BigDecimal getDistance() { + return distance; + } + + public void setDistance(BigDecimal distance) { + this.distance = distance; + } } - - public void setId(Integer id) { - this.id = id; - } - - public CalculationJobTransportationRuleType getUsedRule() { - return usedRule; - } - - public void setUsedRule(CalculationJobTransportationRuleType usedRule) { - this.usedRule = usedRule; - } - - public Boolean getUnmixedPrice() { - return isUnmixedPrice; - } - - public void setUnmixedPrice(Boolean unmixedPrice) { - isUnmixedPrice = unmixedPrice; - } - - public Boolean getCbmPrice() { - return isCbmPrice; - } - - public void setCbmPrice(BigDecimal cbmPrice) { - this.cbmPrice = cbmPrice; - } - - public void setCbmPrice(Boolean cbmPrice) { - isCbmPrice = cbmPrice; - } - - public Boolean getWeightPrice() { - return isWeightPrice; - } - - public void setWeightPrice(BigDecimal weightPrice) { - this.weightPrice = weightPrice; - } - - public BigDecimal getAnnualCost() { - return annualCost; - } - - public void setAnnualCost(BigDecimal annualCost) { - this.annualCost = annualCost; - } - - public Integer getTransitTime() { - return transitTime; - } - - public void setTransitTime(Integer transitTime) { - this.transitTime = transitTime; - } - - public AggregateReference getPremissRouteSection() { - return premissRouteSection; - } - - public void setPremissRouteSection(AggregateReference premissRouteSection) { - this.premissRouteSection = premissRouteSection; - } - - public void setWeightPrice(Boolean weightPrice) { - isWeightPrice = weightPrice; - } - - public Boolean getStacked() { - return isStacked; - } - - public void setStacked(Boolean stacked) { - isStacked = stacked; - } - - public Boolean getPreRun() { - return isPreRun; - } - - public void setPreRun(Boolean preRun) { - isPreRun = preRun; - } - - public Boolean getMainRun() { - return isMainRun; - } - - public void setMainRun(Boolean mainRun) { - isMainRun = mainRun; - } - - public Boolean getPostRun() { - return isPostRun; - } - - public void setPostRun(Boolean postRun) { - isPostRun = postRun; - } - - public BigDecimal getRate() { - return rate; - } - - public void setRate(BigDecimal rate) { - this.rate = rate; - } - - public BigDecimal getDistance() { - return distance; - } - - public void setDistance(BigDecimal distance) { - this.distance = distance; - } - - - -} diff --git a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobSink.java b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobSink.java deleted file mode 100644 index 0b38daf..0000000 --- a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobSink.java +++ /dev/null @@ -1,141 +0,0 @@ -package de.avatic.lcc.model.calculations; - -import de.avatic.lcc.model.premises.route.Destination; -import jakarta.validation.constraints.Digits; -import jakarta.validation.constraints.NotNull; -import jdk.jfr.Unsigned; -import org.springframework.data.annotation.Id; -import org.springframework.data.jdbc.core.mapping.AggregateReference; -import org.springframework.data.relational.core.mapping.Column; -import org.springframework.data.relational.core.mapping.MappedCollection; -import org.springframework.data.relational.core.mapping.Table; - -import java.math.BigDecimal; - - -@Table(name = "calculation_job_sink") -public class CalculationJobSink { - - @Id - private Integer id; - - @Unsigned - private Integer safetyStock; - - @Unsigned - private Integer shippingFrequency; - - @Digits(integer = 15, fraction = 2) - private BigDecimal totalCost; - - @NotNull - private AggregateReference premissSink; - - @MappedCollection - - @Column("calculation_job_transportation_id") - private CalculationJobTransportation transportation; - - @Column("calculation_job_airfreight_id") - private CalculationJobAirfreight airfreight; - - @Column("calculation_job_custom_id") - private CalculationJobCustom custom; - - @Column("calculation_job_inventory_id") - private CalculationJobInventory inventory; - - @Column("calculation_job_handling_id") - private CalculationJobHandling handling; - - @Column("calculation_job_risk_id") - private CalculationJobRisk risk; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public Integer getSafetyStock() { - return safetyStock; - } - - public void setSafetyStock(Integer safetyStock) { - this.safetyStock = safetyStock; - } - - public Integer getShippingFrequency() { - return shippingFrequency; - } - - public void setShippingFrequency(Integer shippingFrequency) { - this.shippingFrequency = shippingFrequency; - } - - public BigDecimal getTotalCost() { - return totalCost; - } - - public void setTotalCost(BigDecimal totalCost) { - this.totalCost = totalCost; - } - - public AggregateReference getPremissSink() { - return premissSink; - } - - public void setPremissSink(AggregateReference premissSink) { - this.premissSink = premissSink; - } - - public CalculationJobTransportation getTransportation() { - return transportation; - } - - public void setTransportation(CalculationJobTransportation transportation) { - this.transportation = transportation; - } - - public CalculationJobAirfreight getAirfreight() { - return airfreight; - } - - public void setAirfreight(CalculationJobAirfreight airfreight) { - this.airfreight = airfreight; - } - - public CalculationJobCustom getCustom() { - return custom; - } - - public void setCustom(CalculationJobCustom custom) { - this.custom = custom; - } - - public CalculationJobInventory getInventory() { - return inventory; - } - - public void setInventory(CalculationJobInventory inventory) { - this.inventory = inventory; - } - - public CalculationJobHandling getHandling() { - return handling; - } - - public void setHandling(CalculationJobHandling handling) { - this.handling = handling; - } - - public CalculationJobRisk getRisk() { - return risk; - } - - public void setRisk(CalculationJobRisk risk) { - this.risk = risk; - } -} diff --git a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobTransportation.java b/src/main/java/de/avatic/lcc/model/calculations/CalculationJobTransportation.java deleted file mode 100644 index 9976029..0000000 --- a/src/main/java/de/avatic/lcc/model/calculations/CalculationJobTransportation.java +++ /dev/null @@ -1,117 +0,0 @@ -package de.avatic.lcc.model.calculations; - -import jakarta.validation.constraints.Digits; -import jakarta.validation.constraints.NotNull; -import jdk.jfr.Unsigned; -import org.springframework.data.annotation.Id; -import org.springframework.data.relational.core.mapping.MappedCollection; -import org.springframework.data.relational.core.mapping.Table; - -import java.math.BigDecimal; -import java.util.Set; - - -@Table(name = "calculation_job_transportation") -public class CalculationJobTransportation { - - @Id - private Integer id; - - private CalculationJobTransportationType transportationType; - - @Unsigned - @NotNull - private Integer huPerLayer; - - @NotNull - private String layerStructure; - - @Unsigned - @NotNull - private Integer layerCount; - - private Boolean transportWeightExceeded; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal transportsPerYear; - - @NotNull - @Digits(integer = 15, fraction = 2) - private BigDecimal annualCost; - - @MappedCollection(idColumn = "calculation_job_transportation_id") - private Set sections; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public CalculationJobTransportationType getTransportationType() { - return transportationType; - } - - public void setTransportationType(CalculationJobTransportationType transportationType) { - this.transportationType = transportationType; - } - - public Integer getHuPerLayer() { - return huPerLayer; - } - - public void setHuPerLayer(Integer huPerLayer) { - this.huPerLayer = huPerLayer; - } - - public String getLayerStructure() { - return layerStructure; - } - - public void setLayerStructure(String layerStructure) { - this.layerStructure = layerStructure; - } - - public Integer getLayerCount() { - return layerCount; - } - - public void setLayerCount(Integer layerCount) { - this.layerCount = layerCount; - } - - public Boolean getTransportWeightExceeded() { - return transportWeightExceeded; - } - - public void setTransportWeightExceeded(Boolean transportWeightExceeded) { - this.transportWeightExceeded = transportWeightExceeded; - } - - public BigDecimal getTransportsPerYear() { - return transportsPerYear; - } - - public void setTransportsPerYear(BigDecimal transportsPerYear) { - this.transportsPerYear = transportsPerYear; - } - - public BigDecimal getAnnualCost() { - return annualCost; - } - - public void setAnnualCost(BigDecimal annualCost) { - this.annualCost = annualCost; - } - - public Set getSections() { - return sections; - } - - public void setSections(Set sections) { - this.sections = sections; - } -} diff --git a/src/main/java/de/avatic/lcc/repositories/NodeRepository.java b/src/main/java/de/avatic/lcc/repositories/NodeRepository.java index c9fe46b..8495c7d 100644 --- a/src/main/java/de/avatic/lcc/repositories/NodeRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/NodeRepository.java @@ -1,6 +1,5 @@ package de.avatic.lcc.repositories; -import de.avatic.lcc.dto.generic.NodeDTO; import de.avatic.lcc.dto.generic.NodeType; import de.avatic.lcc.model.nodes.Node; import de.avatic.lcc.repositories.pagination.SearchQueryPagination; @@ -188,7 +187,7 @@ public class NodeRepository { public Optional getByExternalMappingId(String mappingId) { String query = """ SELECT node.id AS id, node.name AS name, node.address as address, node.is_source as is_source, - v.is_destination as is_destination, node.is_intermediate as is_intermediate, node.country_id as country_id, node.predecessor_required as predecessor_required + node.is_destination as is_destination, node.is_intermediate as is_intermediate, node.country_id as country_id, node.predecessor_required as predecessor_required FROM node WHERE node.external_mapping_id = ?"""; @@ -197,6 +196,51 @@ public class NodeRepository { return Optional.ofNullable(chain); } + @Transactional + public List> findNodeListsForReportingByMaterialId(Integer materialId) { + + String validityPeriodSql = """ + SELECT DISTINCT cj.validity_period_id + FROM premise p + INNER JOIN calculation_job cj ON p.id = cj.premise_id + WHERE p.material_id = ? + """; + + List validityPeriodIds = jdbcTemplate.queryForList(validityPeriodSql, Integer.class, materialId); + + // For each validity period, get the set of supplier_node_ids + List> nodes = new ArrayList<>(); + + for (Integer validityPeriodId : validityPeriodIds) { + + String suppliersSql = """ + SELECT DISTINCT n.* + FROM premise p + INNER JOIN calculation_job cj ON p.id = cj.premise_id + INNER JOIN node n ON p.supplier_node_id = n.id + WHERE p.material_id = ? + AND cj.validity_period_id = ? + AND p.supplier_node_id IS NOT NULL + """; + + String userSuppliersSql = """ + SELECT DISTINCT un.* + FROM premise p + INNER JOIN calculation_job cj ON p.id = cj.premise_id + INNER JOIN sys_user_node un ON p.user_supplier_node_id = un.id + WHERE p.material_id = ? + AND cj.validity_period_id = ? + AND p.user_supplier_node_id IS NOT NULL + """; + + var periodNodes = new ArrayList<>(jdbcTemplate.query(suppliersSql, new NodeMapper(), materialId, validityPeriodId)); + periodNodes.addAll(jdbcTemplate.query(userSuppliersSql, new NodeMapper(), materialId, validityPeriodId)); + nodes.add(periodNodes); + } + + return nodes; + } + /** * Resolves chains of predecessors for a specified destination chain by its ID. @@ -294,6 +338,37 @@ public class NodeRepository { return jdbcTemplate.query(query, new NodeMapper(), countryId); } + public List findNodeListsForReportingByPeriodId(Integer materialId, Integer periodId) { + + + String suppliersSql = """ + SELECT DISTINCT n.* + FROM premise p + INNER JOIN calculation_job cj ON p.id = cj.premise_id + INNER JOIN node n ON p.supplier_node_id = n.id + WHERE p.material_id = ? + AND cj.validity_period_id = ? + AND p.supplier_node_id IS NOT NULL + """; + + + return jdbcTemplate.query(suppliersSql, new NodeMapper(), materialId, periodId); + } + + public Optional getByDestinationId(Integer id) { + + String query = "SELECT node.* FROM node INNER JOIN premise_destination WHERE node.id = premise_destination.destination_node_id AND premise_destination.id = ?"; + + var node = jdbcTemplate.query(query, new NodeMapper(), id); + + if(node.isEmpty()) { + return Optional.empty(); + } + + return Optional.ofNullable(node.getFirst()); + + } + private class NodeMapper implements RowMapper { diff --git a/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobDestinationRepository.java b/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobDestinationRepository.java new file mode 100644 index 0000000..0a5543b --- /dev/null +++ b/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobDestinationRepository.java @@ -0,0 +1,96 @@ +package de.avatic.lcc.repositories.calculation; + +import de.avatic.lcc.model.calculations.CalculationJobDestination; +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.List; + +@Repository +public class CalculationJobDestinationRepository { + + private final JdbcTemplate jdbcTemplate; + + public CalculationJobDestinationRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Transactional + public List getDestinationsByJobId(Integer jobId) { + + String query = "SELECT * FROM calculation_job_destination WHERE calculation_job_id = ?"; + + return jdbcTemplate.query(query, new CalculationJobDestinationMapper(), jobId); + + } + + + private static class CalculationJobDestinationMapper implements RowMapper { + @Override + public CalculationJobDestination mapRow(ResultSet rs, int rowNum) throws SQLException { + + CalculationJobDestination entity = new CalculationJobDestination(); + + // Basic fields already mapped + entity.setId(rs.getInt("id")); + entity.setPremiseDestinationId(rs.getInt("premise_destination_id")); + entity.setCalculationJobId(rs.getInt("calculation_job_id")); + entity.setSafetyStock(rs.getBigDecimal("safety_stock")); + entity.setShippingFrequency(rs.getInt("shipping_frequency")); + entity.setTotalCost(rs.getBigDecimal("total_cost")); + entity.setTotalCost(rs.getBigDecimal("annual_amount")); + + // Risk fields + entity.setAnnualRiskCost(rs.getBigDecimal("annual_risk_cost")); + entity.setAnnualChanceCost(rs.getBigDecimal("annual_chance_cost")); + + // Handling fields + entity.setSmallUnit(rs.getBoolean("is_small_unit")); + entity.setAnnualRepackingCost(rs.getBigDecimal("annual_repacking_cost")); + entity.setAnnualHandlingCost(rs.getBigDecimal("annual_handling_cost")); + entity.setAnnualDisposalCost(rs.getBigDecimal("annual_disposal_cost")); + + // Inventory fields + entity.setOperationalStock(rs.getBigDecimal("operational_stock")); + // Safety stock already set above + entity.setStockedInventory(rs.getBigDecimal("stocked_inventory")); + entity.setInTransportStock(rs.getBigDecimal("in_transport_stock")); + entity.setStockBeforePayment(rs.getBigDecimal("stock_before_payment")); + entity.setAnnualCapitalCost(rs.getBigDecimal("annual_capital_cost")); + entity.setAnnualStorageCost(rs.getBigDecimal("annual_storage_cost")); + + // Custom fields + entity.setCustomValue(rs.getBigDecimal("custom_value")); + entity.setCustomDuties(rs.getBigDecimal("custom_duties")); + entity.setTariffRate(rs.getBigDecimal("tariff_rate")); + entity.setAnnualCustomCost(rs.getBigDecimal("annual_custom_cost")); + + // Air Freight Risk fields + entity.setAirFreightShareMax(rs.getBigDecimal("air_freight_share_max")); + entity.setAirFreightShare(rs.getBigDecimal("air_freight_share")); + entity.setAirFreightVolumetricWeight(rs.getBigDecimal("air_freight_volumetric_weight")); + entity.setAirFreightWeight(rs.getBigDecimal("air_freight_weight")); + entity.setAnnualAirFreightCost(rs.getBigDecimal("annual_air_freight_cost")); + + // Transportation fields + entity.setTransportationType(rs.getString("transportation_type")); + entity.setHuPerLayer(rs.getInt("hu_per_layer")); + 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.setAnnualTransportationCost(rs.getBigDecimal("annual_transportation_cost")); + + // Material Cost fields + entity.setMaterialCost(rs.getBigDecimal("material_cost")); + entity.setFcaCost(rs.getBigDecimal("fca_cost")); + + return entity; + + } + } +} diff --git a/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRepository.java b/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRepository.java new file mode 100644 index 0000000..63ed174 --- /dev/null +++ b/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRepository.java @@ -0,0 +1,58 @@ +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 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.List; +import java.util.Optional; + +@Repository +public class CalculationJobRepository { + private final JdbcTemplate jdbcTemplate; + + public CalculationJobRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Transactional + public Optional getCalculationJob(Integer periodId, Integer nodeId, Integer materialId) { + + /* there should only be one job per period id, node id and material id combination */ + String query = "SELECT * FROM calculation_job AS cj INNER JOIN premise AS p ON cj.premise_id = p.id WHERE validity_period_id = ? AND p.supplier_node_id = ? AND material_id = ? LIMIT 1"; + + var job = jdbcTemplate.query(query, new CalculationJobMapper(), periodId, nodeId, materialId); + + if(job.isEmpty()) + return Optional.empty(); + + return Optional.of(job.getFirst()); + } + + + private static class CalculationJobMapper implements RowMapper { + @Override + public CalculationJob mapRow(ResultSet rs, int rowNum) throws SQLException { + + CalculationJob entity = new CalculationJob(); + + entity.setId(rs.getInt("id")); + entity.setPremiseId(rs.getInt("premise_id")); + entity.setValidityPeriodId(rs.getInt("validity_period_id")); + entity.setJobState(CalculationJobState.valueOf(rs.getString("job_state"))); + entity.setPropertySetId(rs.getInt("property_set_id")); + entity.setUserId(rs.getInt("user_id")); + entity.setCalculationDate(rs.getTimestamp("calculation_date").toLocalDateTime()); + entity.setWeightedTotalCosts(rs.getBigDecimal("weighted_total_costs")); + + return entity; + + } + } +} diff --git a/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRouteSectionRepository.java b/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRouteSectionRepository.java new file mode 100644 index 0000000..f8a752b --- /dev/null +++ b/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRouteSectionRepository.java @@ -0,0 +1,92 @@ +package de.avatic.lcc.repositories.calculation; + +import de.avatic.lcc.model.calculations.CalculationJobRouteSection; +import de.avatic.lcc.model.calculations.CalculationJobTransportationRuleType; +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.*; + +@Repository +public class CalculationJobRouteSectionRepository { + + private final JdbcTemplate jdbcTemplate; + + public CalculationJobRouteSectionRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Transactional + public List getRouteSectionsByDestinationId(Integer destinationId) { + String query = "SELECT * FROM calculation_job_route_section WHERE calculation_job_destination_id = ?"; + return jdbcTemplate.query(query, new CalculationJobRouteSectionMapper(), destinationId); + } + + @Transactional + public Map> getRouteSectionsByDestinationIds(List destinationIds) { + if (destinationIds == null || destinationIds.isEmpty()) { + return Collections.emptyMap(); + } + + String placeholders = String.join(",", Collections.nCopies(destinationIds.size(), "?")); + String query = "SELECT * FROM calculation_job_route_section WHERE calculation_job_destination_id IN (" + placeholders + ")"; + + List allSections = jdbcTemplate.query( + query, + new CalculationJobRouteSectionMapper(), + destinationIds.toArray() + ); + + // Group by destination ID + Map> groupedSections = new HashMap<>(); + for (CalculationJobRouteSection section : allSections) { + groupedSections + .computeIfAbsent(section.getCalculationJobDestinationId(), k -> new ArrayList<>()) + .add(section); + } + + return groupedSections; + } + + private static class CalculationJobRouteSectionMapper implements RowMapper { + @Override + public CalculationJobRouteSection mapRow(ResultSet rs, int rowNum) throws SQLException { + CalculationJobRouteSection entity = new CalculationJobRouteSection(); + + // Basic fields + entity.setId(rs.getInt("id")); + entity.setPremiseRouteSectionId(rs.getInt("premise_route_section_id")); + entity.setCalculationJobDestinationId(rs.getInt("calculation_job_destination_id")); + + // Rule and price type flags + entity.setUsedRule(CalculationJobTransportationRuleType.valueOf(rs.getString("used_rule"))); + entity.setUnmixedPrice(rs.getBoolean("is_unmixed_price")); + entity.setCbmPrice(rs.getBoolean("is_cbm_price")); + entity.setWeightPrice(rs.getBoolean("is_weight_price")); + + // Route section properties + entity.setStacked(rs.getBoolean("is_stacked")); + entity.setPreRun(rs.getBoolean("is_pre_run")); + entity.setMainRun(rs.getBoolean("is_main_run")); + entity.setPostRun(rs.getBoolean("is_post_run")); + + // Pricing information + entity.setRate(rs.getBigDecimal("rate")); + entity.setDistance(rs.getBigDecimal("distance")); + entity.setCbmPrice(rs.getBigDecimal("cbm_price")); + entity.setWeightPrice(rs.getBigDecimal("weight_price")); + entity.setAnnualCost(rs.getBigDecimal("annual_cost")); + + // Transit time + entity.setTransitTime(rs.getInt("transit_time")); + + return entity; + } + } + + +} diff --git a/src/main/java/de/avatic/lcc/repositories/calculation/ReportingRepository.java b/src/main/java/de/avatic/lcc/repositories/calculation/ReportingRepository.java new file mode 100644 index 0000000..9c503f7 --- /dev/null +++ b/src/main/java/de/avatic/lcc/repositories/calculation/ReportingRepository.java @@ -0,0 +1,21 @@ +package de.avatic.lcc.repositories.calculation; + + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.List; + +@Repository +public class ReportingRepository { + + + private final JdbcTemplate jdbcTemplate; + + public ReportingRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + +} diff --git a/src/main/java/de/avatic/lcc/repositories/premise/RouteSectionRepository.java b/src/main/java/de/avatic/lcc/repositories/premise/RouteSectionRepository.java index c489289..4127a00 100644 --- a/src/main/java/de/avatic/lcc/repositories/premise/RouteSectionRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/premise/RouteSectionRepository.java @@ -14,6 +14,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.Collections; import java.util.List; +import java.util.Optional; @Repository public class RouteSectionRepository { @@ -64,6 +65,12 @@ public class RouteSectionRepository { return keyHolder.getKey() != null ? keyHolder.getKey().intValue() : null; } + public Optional getById(Integer id) { + String query = "SELECT * FROM premise_route_section WHERE id = ?"; + var section = jdbcTemplate.query(query, new RouteSectionMapper(), id); + return section.isEmpty() ? Optional.empty() : Optional.of(section.getFirst()); + } + private static class RouteSectionMapper implements RowMapper { @Override public RouteSection mapRow(ResultSet rs, int rowNum) throws SQLException { diff --git a/src/main/java/de/avatic/lcc/repositories/rates/ValidityPeriodRepository.java b/src/main/java/de/avatic/lcc/repositories/rates/ValidityPeriodRepository.java index 7173de4..d839e7a 100644 --- a/src/main/java/de/avatic/lcc/repositories/rates/ValidityPeriodRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/rates/ValidityPeriodRepository.java @@ -11,6 +11,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.time.LocalDateTime; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -159,6 +160,63 @@ public class ValidityPeriodRepository { jdbcTemplate.update("UPDATE validity_period SET state = ?, start_date = ? WHERE id = ? AND state = ? ", ValidityPeriodState.VALID.name(), currentTimestamp, ValidityPeriodState.DRAFT.name()); } + @Transactional + public Optional getValidPeriodForReportingByMaterialId(Integer materialId, List nodeIds) { + + if (nodeIds == null || nodeIds.isEmpty()) { + return Optional.empty(); + } + + String placeholders = String.join(",", Collections.nCopies(nodeIds.size(), "?")); + + String validityPeriodSql = """ + SELECT vp.* + FROM validity_period vp + INNER JOIN ( + SELECT + cj.validity_period_id, + COUNT(DISTINCT p.supplier_node_id) as node_count + FROM + premise p + INNER JOIN + calculation_job cj ON p.id = cj.premise_id + WHERE + p.material_id = ? + AND p.supplier_node_id IN (""" + + placeholders + """ + ) + GROUP BY + cj.validity_period_id + HAVING + COUNT(DISTINCT p.supplier_node_id) = ? + ) matching_periods ON vp.id = matching_periods.validity_period_id + ORDER BY + vp.start_date DESC + LIMIT 1 + """; + + + var periods = jdbcTemplate.query(validityPeriodSql, new ValidityPeriodMapper(), materialId, nodeIds.toArray(), nodeIds.size()); + + if (periods.isEmpty()) { + return Optional.empty(); + } + + return Optional.of(periods.getFirst()); + } + + public List findValidityPeriodsWithReportByMaterialId(Integer materialId) { + + String validityPeriodSql = """ + SELECT DISTINCT cj.validity_period_id + FROM premise p + INNER JOIN calculation_job cj ON p.id = cj.premise_id + WHERE p.material_id = ? + """; + + return jdbcTemplate.queryForList(validityPeriodSql, Integer.class, materialId); + } + /** * Maps rows of a {@link ResultSet} to {@link ValidityPeriod} objects. */ @@ -170,9 +228,16 @@ public class ValidityPeriodRepository { var period = new ValidityPeriod(); period.setId(rs.getInt("id")); - period.setStartDate(rs.getTimestamp("start_date").toLocalDateTime()); - period.setEndDate(rs.getTimestamp("end_date").toLocalDateTime()); - period.setState(ValidityPeriodState.valueOf(rs.getString("state"))); + + Timestamp startTs = rs.getTimestamp("start_date"); + period.setStartDate(startTs != null ? startTs.toLocalDateTime() : null); + + Timestamp endTs = rs.getTimestamp("end_date"); + period.setEndDate(endTs != null ? endTs.toLocalDateTime() : null); + + String stateStr = rs.getString("state"); + period.setState(stateStr != null ? ValidityPeriodState.valueOf(stateStr) : null); + return period; diff --git a/src/main/java/de/avatic/lcc/repositories/users/UserNodeRepository.java b/src/main/java/de/avatic/lcc/repositories/users/UserNodeRepository.java index 4c15feb..b4d4f79 100644 --- a/src/main/java/de/avatic/lcc/repositories/users/UserNodeRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/users/UserNodeRepository.java @@ -8,6 +8,7 @@ import org.springframework.stereotype.Repository; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; +import java.util.List; import java.util.Optional; @Repository @@ -49,6 +50,21 @@ public class UserNodeRepository { return Optional.of(nodes.getFirst()); } + public List findNodeListsForReportingByPeriodId(Integer materialId, Integer periodId) { + String userSuppliersSql = """ + SELECT DISTINCT un.* + FROM premise p + INNER JOIN calculation_job cj ON p.id = cj.premise_id + INNER JOIN sys_user_node un ON p.user_supplier_node_id = un.id + WHERE p.material_id = ? + AND cj.validity_period_id = ? + AND p.user_supplier_node_id IS NOT NULL + """; + + return jdbcTemplate.query(userSuppliersSql, new NodeMapper(), materialId, periodId)); + + } + private static class NodeMapper implements RowMapper { @Override 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 a99f76f..87b3d5d 100644 --- a/src/main/java/de/avatic/lcc/service/access/PremisesService.java +++ b/src/main/java/de/avatic/lcc/service/access/PremisesService.java @@ -92,8 +92,4 @@ public class PremisesService { destinationService.deleteAllDestinationsByPremiseId(premiseIds, false); premiseRepository.deletePremisesById(premiseIds); } - - public void fillPackaging(List premiseIds) { - - } } diff --git a/src/main/java/de/avatic/lcc/service/report/ReportFinderService.java b/src/main/java/de/avatic/lcc/service/report/ReportFinderService.java deleted file mode 100644 index 002a45e..0000000 --- a/src/main/java/de/avatic/lcc/service/report/ReportFinderService.java +++ /dev/null @@ -1,16 +0,0 @@ -package de.avatic.lcc.service.report; - - -import de.avatic.lcc.dto.generic.NodeDTO; -import org.springframework.stereotype.Service; - -import java.util.List; - -@Service -public class ReportFinderService { - - - public List> findSupplierForReporting(Integer materialId) { - return null; - } -} diff --git a/src/main/java/de/avatic/lcc/service/report/ReportingService.java b/src/main/java/de/avatic/lcc/service/report/ReportingService.java index e26e086..6fd9212 100644 --- a/src/main/java/de/avatic/lcc/service/report/ReportingService.java +++ b/src/main/java/de/avatic/lcc/service/report/ReportingService.java @@ -1,13 +1,70 @@ package de.avatic.lcc.service.report; +import de.avatic.lcc.dto.generic.NodeDTO; import de.avatic.lcc.dto.report.ReportDTO; +import de.avatic.lcc.model.calculations.CalculationJobDestination; +import de.avatic.lcc.model.calculations.CalculationJobRouteSection; +import de.avatic.lcc.repositories.NodeRepository; +import de.avatic.lcc.repositories.calculation.CalculationJobDestinationRepository; +import de.avatic.lcc.repositories.calculation.CalculationJobRepository; +import de.avatic.lcc.repositories.calculation.CalculationJobRouteSectionRepository; +import de.avatic.lcc.repositories.rates.ValidityPeriodRepository; +import de.avatic.lcc.repositories.users.UserNodeRepository; +import de.avatic.lcc.service.transformer.generic.NodeTransformer; +import de.avatic.lcc.service.transformer.report.ReportTransformer; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Optional; @Service public class ReportingService { - public ReportDTO getReport(Integer materialId, List nodeIds) { - return null; + private final NodeRepository nodeRepository; + private final NodeTransformer nodeTransformer; + private final ValidityPeriodRepository validityPeriodRepository; + private final CalculationJobRepository calculationJobRepository; + private final UserNodeRepository userNodeRepository; + + private final ReportTransformer reportTransformer; + + public ReportingService(NodeRepository nodeRepository, NodeTransformer nodeTransformer, ValidityPeriodRepository validityPeriodRepository, CalculationJobRepository calculationJobRepository, UserNodeRepository userNodeRepository, ReportTransformer reportTransformer) { + this.nodeRepository = nodeRepository; + this.nodeTransformer = nodeTransformer; + this.validityPeriodRepository = validityPeriodRepository; + this.calculationJobRepository = calculationJobRepository; + this.userNodeRepository = userNodeRepository; + this.reportTransformer = reportTransformer; } + + public List> findSupplierForReporting(Integer materialId) { + var periodIds = validityPeriodRepository.findValidityPeriodsWithReportByMaterialId(materialId); + + var nodes = new ArrayList>(); + for (var periodId : periodIds) { + var periodNodes = new ArrayList<>(nodeRepository.findNodeListsForReportingByPeriodId(materialId, periodId).stream().map(nodeTransformer::toNodeDTO).toList()); + periodNodes.addAll(userNodeRepository.findNodeListsForReportingByPeriodId(materialId, periodId).stream().map(nodeTransformer::toNodeDTO).peek(n -> n.setUserNode(true)).toList()); + + //TODO destinations have to match. + + nodes.add(periodNodes); + } + + return nodes; + } + + public List getReport(Integer materialId, List nodeIds) { + var period = validityPeriodRepository.getValidPeriodForReportingByMaterialId(materialId, nodeIds); + + if (period.isEmpty()) + throw new IllegalArgumentException("No valid period found"); + + var periodId = period.get().getId(); + + var jobs = nodeIds.stream().map(nodeId -> calculationJobRepository.getCalculationJob(periodId, nodeId,materialId)).filter(Optional::isPresent).map(Optional::get).toList(); + + return jobs.stream().map(reportTransformer::toReportDTO).toList(); + } + } diff --git a/src/main/java/de/avatic/lcc/service/transformer/report/ReportTransformer.java b/src/main/java/de/avatic/lcc/service/transformer/report/ReportTransformer.java new file mode 100644 index 0000000..f17310f --- /dev/null +++ b/src/main/java/de/avatic/lcc/service/transformer/report/ReportTransformer.java @@ -0,0 +1,230 @@ +package de.avatic.lcc.service.transformer.report; + +import de.avatic.lcc.dto.generic.ContainerType; +import de.avatic.lcc.dto.report.*; +import de.avatic.lcc.model.calculations.CalculationJob; +import de.avatic.lcc.model.calculations.CalculationJobDestination; +import de.avatic.lcc.model.calculations.CalculationJobRouteSection; +import de.avatic.lcc.model.premises.Premise; +import de.avatic.lcc.repositories.NodeRepository; +import de.avatic.lcc.repositories.calculation.CalculationJobDestinationRepository; +import de.avatic.lcc.repositories.calculation.CalculationJobRouteSectionRepository; +import de.avatic.lcc.repositories.premise.DestinationRepository; +import de.avatic.lcc.repositories.premise.PremiseRepository; +import de.avatic.lcc.repositories.premise.RouteSectionRepository; +import de.avatic.lcc.service.transformer.generic.NodeTransformer; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class ReportTransformer { + + private final CalculationJobDestinationRepository calculationJobDestinationRepository; + private final CalculationJobRouteSectionRepository calculationJobRouteSectionRepository; + private final PremiseRepository premiseRepository; + private final DestinationRepository destinationRepository; + private final NodeRepository nodeRepository; + private final RouteSectionRepository routeSectionRepository; + private final NodeTransformer nodeTransformer; + + public ReportTransformer(CalculationJobDestinationRepository calculationJobDestinationRepository, CalculationJobRouteSectionRepository calculationJobRouteSectionRepository, PremiseRepository premiseRepository, DestinationRepository destinationRepository, NodeRepository nodeRepository, RouteSectionRepository routeSectionRepository, NodeTransformer nodeTransformer) { + this.calculationJobDestinationRepository = calculationJobDestinationRepository; + this.calculationJobRouteSectionRepository = calculationJobRouteSectionRepository; + this.premiseRepository = premiseRepository; + this.destinationRepository = destinationRepository; + this.nodeRepository = nodeRepository; + this.routeSectionRepository = routeSectionRepository; + this.nodeTransformer = nodeTransformer; + } + + public ReportDTO toReportDTO(CalculationJob job) { + + ReportDTO reportDTO = new ReportDTO(); + + List destinations = calculationJobDestinationRepository.getDestinationsByJobId(job.getId()); + Map> sections = calculationJobRouteSectionRepository.getRouteSectionsByDestinationIds(destinations.stream().map(CalculationJobDestination::getId).toList()); + + Premise premise = premiseRepository.getPremiseById(job.getPremiseId()).orElseThrow(); + + reportDTO.setCost(getCostMap(job, destinations)); + reportDTO.setRisk(getRisk(job, destinations)); + + ReportPremisesDTO premisesDTO = new ReportPremisesDTO(); + premisesDTO.setQuantities(destinations.stream().map(q -> getQuantitiesDTO(q, sections.get(q.getId()))).toList()); + + premisesDTO.setContainer(getContainerDTO(destinations, sections, premise)); + premisesDTO.setPackaging(getPackagingDTO(destinations, premise)); + premisesDTO.setQuotaShare(getQuotaShare(destinations, sections, premise)); + premisesDTO.setHsCode(premise.getHsCode()); + + reportDTO.setPremises(premisesDTO); + + return reportDTO; + + } + + private ReportQuotaShareDTO getQuotaShare(List destinations, Map> sections, Premise premise) { + ReportQuotaShareDTO quotaShare = new ReportQuotaShareDTO(); + + var safetyStock = destinations.stream().map(CalculationJobDestination::getSafetyStock).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(destinations.size()), 2, RoundingMode.HALF_UP); + double transportTime = (double) (destinations.stream().map(CalculationJobDestination::getId).map(sections::get).mapToInt(l -> l.stream().mapToInt(CalculationJobRouteSection::getTransitTime).sum()).sum()) / sections.size(); + var airfreight = destinations.stream().map(CalculationJobDestination::getAirFreightShare).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(destinations.size()), 2, RoundingMode.HALF_UP); + + quotaShare.setOverseaShare(premise.getOverseaShare().doubleValue()); + quotaShare.setSafetyStock(safetyStock.doubleValue()); + quotaShare.setTransportTime(transportTime); + quotaShare.setAirFreightShare(airfreight.doubleValue()); + + return quotaShare; + } + + private ReportPackagingDTO getPackagingDTO(List destination, Premise premise) { + ReportPackagingDTO packaging = new ReportPackagingDTO(); + + var dimensionUnit = premise.getDimensionUnit(); + var weightUnit = premise.getWeightUnit(); + + packaging.setDimensionUnit(dimensionUnit); + packaging.setWeightUnit(weightUnit); + packaging.setHeight(dimensionUnit.convertFromMM(premise.getIndividualHuHeight()).doubleValue()); + packaging.setWidth(dimensionUnit.convertFromMM(premise.getIndividualHuWidth()).doubleValue()); + packaging.setLength(dimensionUnit.convertFromMM(premise.getIndividualHuLength()).doubleValue()); + packaging.setWeight(weightUnit.convertFromG(premise.getIndividualHuWeight()).doubleValue()); + + packaging.setLayer(destination.stream().mapToInt(CalculationJobDestination::getLayerCount).sum()/destination.size()); + packaging.setUnitCount(premise.getHuUnitCount()); + + return packaging; + } + + private ReportContainerDTO getContainerDTO(List destination, List sections, Premise premise) { + ReportContainerDTO container = new ReportContainerDTO(); + + CalculationJobRouteSection mainRun = sections.stream().filter(CalculationJobRouteSection::getMainRun).findFirst().orElse(null); + + container.setMixed(premise.getHuMixable()); + container.setRate(mainRun == null ? 0 : mainRun.getRate()); + container.setType(ContainerType.valueOf(destination.getFirst().getTransportationType())); + container.setUtilization(destination.getFirst().getContainerUtilization()); + container.setUnitCount(premise.getHuUnitCount()); + container.setWeightExceeded(destination.getFirst().getTransportWeightExceeded()); + + return container; + } + + private ReportQuantityDTO getQuantitiesDTO(CalculationJobDestination destination, List sections) { + ReportQuantityDTO quantity = new ReportQuantityDTO(); + + var destinationNode = nodeRepository.getByDestinationId(destination.getPremiseDestinationId()).orElseThrow(); + + quantity.setQuantity(destination.getAnnualAmount()); + quantity.setDestination(nodeTransformer.toNodeDTO(destinationNode)); + quantity.setRoute(sections.stream().map(this::getRouteEntryDTO).toList()); + + var total = quantity.getRoute().stream().map(ReportRouteEntryDTO::getCost).map(ReportEntryDTO::getTotal).mapToDouble(Number::doubleValue).sum(); + quantity.getRoute().forEach(r -> r.getCost().setPercentage(BigDecimal.valueOf(r.getCost().getTotal().doubleValue() / total).setScale(2, RoundingMode.HALF_UP))); + + return quantity; + } + + private ReportRouteEntryDTO getRouteEntryDTO(CalculationJobRouteSection routeSection) { + ReportRouteEntryDTO routeEntry = new ReportRouteEntryDTO(); + + var premiseSection = routeSectionRepository.getById(routeSection.getPremiseRouteSectionId()).orElseThrow(); + var sectionNode = nodeRepository.getById(premiseSection.getFromRouteNodeId()).orElseThrow(); + + routeEntry.setName(sectionNode.getExternalMappingId()); + routeEntry.setType(premiseSection.getTransportType()); + routeEntry.setCost(new ReportEntryDTO()); + routeEntry.getCost().setTotal(routeSection.getAnnualCost()); + + return routeEntry; + } + + private Map getRisk(CalculationJob job, List destination) { + Map risk = new HashMap<>(); + + var totalValue = job.getWeightedTotalCosts(); + + ReportEntryDTO airfreight = new ReportEntryDTO(); + var airfreightValue = destination.stream().map(CalculationJobDestination::getAnnualAirFreightCost).reduce(BigDecimal.ZERO, BigDecimal::add); + airfreight.setTotal(airfreightValue); + airfreight.setPercentage(airfreightValue.divide(totalValue, 2, RoundingMode.HALF_UP)); + risk.put("air_freight_cost", airfreight); + + ReportEntryDTO worst = new ReportEntryDTO(); + var worstValue = destination.stream().map(CalculationJobDestination::getAnnualRiskCost).reduce(BigDecimal.ZERO, BigDecimal::add); + worst.setTotal(worstValue); + worst.setPercentage(worstValue.divide(totalValue, 2, RoundingMode.HALF_UP)); + risk.put("worst_case_cost", worst); + + ReportEntryDTO best = new ReportEntryDTO(); + var bestValue = destination.stream().map(CalculationJobDestination::getAnnualChanceCost).reduce(BigDecimal.ZERO, BigDecimal::add); + best.setTotal(bestValue); + best.setPercentage(bestValue.divide(totalValue, 2, RoundingMode.HALF_UP)); + risk.put("best_case_cost", best); + + return risk; + } + + private Map getCostMap(CalculationJob job, List destination) { + Map cost = new HashMap<>(); + + ReportEntryDTO total = new ReportEntryDTO(); + var totalValue = job.getWeightedTotalCosts(); + total.setTotal(totalValue); + total.setPercentage(BigDecimal.valueOf(100)); + cost.put("total", total); + + ReportEntryDTO material = new ReportEntryDTO(); + var materialValue = destination.stream().map(CalculationJobDestination::getMaterialCost).reduce(BigDecimal.ZERO, BigDecimal::add); + material.setTotal(materialValue); + material.setPercentage(materialValue.divide(totalValue, 2, RoundingMode.HALF_UP)); + cost.put("material", material); + + ReportEntryDTO fcaFees = new ReportEntryDTO(); + var fcaFeesValues = destination.stream().map(CalculationJobDestination::getFcaCost).reduce(BigDecimal.ZERO, BigDecimal::add); + fcaFees.setTotal(fcaFeesValues); + fcaFees.setPercentage(fcaFeesValues.divide(totalValue, 2, RoundingMode.HALF_UP)); + cost.put("fcaFees", fcaFees); + + ReportEntryDTO repacking = new ReportEntryDTO(); + var repackingValues = destination.stream().map(CalculationJobDestination::getAnnualRepackingCost).reduce(BigDecimal.ZERO, BigDecimal::add); + repacking.setTotal(repackingValues); + repacking.setPercentage(repackingValues.divide(totalValue, 2, RoundingMode.HALF_UP)); + cost.put("repacking", repacking); + + ReportEntryDTO handling = new ReportEntryDTO(); + var handlingValues = destination.stream().map(CalculationJobDestination::getAnnualHandlingCost).reduce(BigDecimal.ZERO, BigDecimal::add); + handling.setTotal(handlingValues); + handling.setPercentage(handlingValues.divide(totalValue, 2, RoundingMode.HALF_UP)); + cost.put("handling", handling); + + ReportEntryDTO storage = new ReportEntryDTO(); + var storageValues = destination.stream().map(CalculationJobDestination::getAnnualStorageCost).reduce(BigDecimal.ZERO, BigDecimal::add); + storage.setTotal(storageValues); + storage.setPercentage(storageValues.divide(totalValue, 2, RoundingMode.HALF_UP)); + cost.put("storage", storage); + + ReportEntryDTO capital = new ReportEntryDTO(); + var capitalValues = destination.stream().map(CalculationJobDestination::getAnnualCapitalCost).reduce(BigDecimal.ZERO, BigDecimal::add); + capital.setTotal(capitalValues); + capital.setPercentage(capitalValues.divide(totalValue, 2, RoundingMode.HALF_UP)); + cost.put("capital", capital); + + ReportEntryDTO disposal = new ReportEntryDTO(); + var disposalValues = destination.stream().map(CalculationJobDestination::getAnnualDisposalCost).reduce(BigDecimal.ZERO, BigDecimal::add); + disposal.setTotal(disposalValues); + disposal.setPercentage(disposalValues.divide(totalValue, 2, RoundingMode.HALF_UP)); + cost.put("disposal", disposal); + + return cost; + } + + +} diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 8a219e4..20286ed 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -130,7 +130,7 @@ CREATE TABLE IF NOT EXISTS `sys_user_node` -- logistic nodes -CREATE TABLE IF NOT EXISTS chain +CREATE TABLE IF NOT EXISTS node ( id INT PRIMARY KEY, country_id INT NOT NULL, @@ -153,7 +153,7 @@ CREATE TABLE IF NOT EXISTS node_predecessor_chain ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, node_id INT NOT NULL, - FOREIGN KEY (node_id) REFERENCES chain (id) + FOREIGN KEY (node_id) REFERENCES node (id) ); @@ -163,7 +163,7 @@ CREATE TABLE IF NOT EXISTS node_predecessor_entry node_id INT NOT NULL, node_predecessor_chain_id INT NOT NULL, sequence_number INT NOT NULL CHECK (sequence_number > 0), - FOREIGN KEY (node_id) REFERENCES chain (id), + FOREIGN KEY (node_id) REFERENCES node (id), FOREIGN KEY (node_predecessor_chain_id) REFERENCES node_predecessor_chain (id), UNIQUE KEY uk_node_predecessor (node_predecessor_chain_id, sequence_number), INDEX idx_node_predecessor (node_predecessor_chain_id), @@ -175,7 +175,7 @@ CREATE TABLE IF NOT EXISTS outbound_country_mapping id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, node_id INT NOT NULL, country_id INT NOT NULL, - FOREIGN KEY (node_id) REFERENCES chain (id), + FOREIGN KEY (node_id) REFERENCES node (id), FOREIGN KEY (country_id) REFERENCES country (id), UNIQUE KEY uk_node_id_country_id (node_id, country_id), INDEX idx_node_id (node_id), @@ -194,8 +194,8 @@ CREATE TABLE IF NOT EXISTS distance_matrix distance DECIMAL(15, 2) NOT NULL COMMENT 'travel distance between the two nodes in meters', updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, state CHAR(10), - FOREIGN KEY (from_node_id) REFERENCES chain (id), - FOREIGN KEY (to_node_id) REFERENCES chain (id), + FOREIGN KEY (from_node_id) REFERENCES node (id), + FOREIGN KEY (to_node_id) REFERENCES node (id), CONSTRAINT `chk_state` CHECK (`state` IN ('VALID', 'STALE')), INDEX idx_from_to_nodes (from_node_id, to_node_id) @@ -223,8 +223,8 @@ CREATE TABLE IF NOT EXISTS container_rate rate_hc DECIMAL(15, 2) NOT NULL COMMENT 'rate for 40ft HQ container in EUR', lead_time INT UNSIGNED NOT NULL COMMENT 'lead time in days', validity_period_id INT NOT NULL, - FOREIGN KEY (from_node_id) REFERENCES chain (id), - FOREIGN KEY (to_node_id) REFERENCES chain (id), + FOREIGN KEY (from_node_id) REFERENCES node (id), + FOREIGN KEY (to_node_id) REFERENCES node (id), FOREIGN KEY (validity_period_id) REFERENCES validity_period (id), INDEX idx_from_to_nodes (from_node_id, to_node_id), INDEX idx_validity_period_id (validity_period_id) @@ -285,7 +285,7 @@ CREATE TABLE IF NOT EXISTS packaging `hu_dimension_id` INT NOT NULL, `shu_dimension_id` INT NOT NULL, `is_deprecated` BOOLEAN NOT NULL DEFAULT FALSE, - FOREIGN KEY (supplier_node_id) REFERENCES chain (id), + FOREIGN KEY (supplier_node_id) REFERENCES node (id), FOREIGN KEY (material_id) REFERENCES material (id), FOREIGN KEY (hu_dimension_id) REFERENCES packaging_dimension (id), FOREIGN KEY (shu_dimension_id) REFERENCES packaging_dimension (id), @@ -331,8 +331,8 @@ CREATE TABLE IF NOT EXISTS premise user_supplier_node_id INT, packaging_id INT DEFAULT NULL, user_id INT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, material_cost DECIMAL(15, 2) COMMENT 'aka MEK_A in EUR', is_fca_enabled BOOLEAN DEFAULT FALSE, oversea_share DECIMAL(7, 4), @@ -349,7 +349,7 @@ CREATE TABLE IF NOT EXISTS premise hu_stackable BOOLEAN DEFAULT TRUE, hu_mixable BOOLEAN DEFAULT TRUE, FOREIGN KEY (material_id) REFERENCES material (id), - FOREIGN KEY (supplier_node_id) REFERENCES chain (id), + FOREIGN KEY (supplier_node_id) REFERENCES node (id), FOREIGN KEY (user_supplier_node_id) REFERENCES sys_user_node (id), FOREIGN KEY (packaging_id) REFERENCES packaging (id), FOREIGN KEY (user_id) REFERENCES sys_user (id), @@ -373,13 +373,13 @@ CREATE TABLE IF NOT EXISTS premise_destination premise_id INT NOT NULL, annual_amount INT UNSIGNED NOT NULL COMMENT 'annual amount in single pieces', destination_node_id INT NOT NULL, - is_d2d BOOLEAN DEFAULT FALSE, + is_d2d BOOLEAN DEFAULT FALSE, rate_d2d DECIMAL(15, 2) DEFAULT NULL, repacking_cost DECIMAL(15, 2) DEFAULT NULL, handling_cost DECIMAL(15, 2) DEFAULT NULL, disposal_cost DECIMAL(15, 2) DEFAULT NULL, FOREIGN KEY (premise_id) REFERENCES premise (id), - FOREIGN KEY (destination_node_id) REFERENCES chain (id), + FOREIGN KEY (destination_node_id) REFERENCES node (id), INDEX idx_destination_node_id (destination_node_id), INDEX idx_premise_id (premise_id) ); @@ -401,14 +401,14 @@ CREATE TABLE IF NOT EXISTS premise_route_node user_node_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, address VARCHAR(500), - country_id INT NOT NULL, + country_id INT NOT NULL, is_destination BOOLEAN DEFAULT FALSE, is_intermediate BOOLEAN DEFAULT FALSE, is_source BOOLEAN DEFAULT FALSE, geo_lat DECIMAL(7, 4) CHECK (geo_lat BETWEEN -90 AND 90), geo_lng DECIMAL(7, 4) CHECK (geo_lng BETWEEN -180 AND 180), is_outdated BOOLEAN DEFAULT FALSE, - FOREIGN KEY (node_id) REFERENCES chain (id), + FOREIGN KEY (node_id) REFERENCES node (id), FOREIGN KEY (country_id) REFERENCES country (id), FOREIGN KEY (user_node_id) REFERENCES sys_user_node (id), INDEX idx_node_id (node_id), @@ -463,129 +463,88 @@ CREATE TABLE IF NOT EXISTS calculation_job ); -CREATE TABLE IF NOT EXISTS calculation_job_material -( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - material_cost DECIMAL(15, 2) NOT NULL, - fca_cost DECIMAL(15, 2) NOT NULL -); - -CREATE TABLE IF NOT EXISTS calculation_job_transportation +CREATE TABLE IF NOT EXISTS calculation_job_destination ( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - 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_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?!', - annual_cost DECIMAL(15, 2) NOT NULL COMMENT 'total annual transportation costs in EUR' + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + calculation_job_id INT NOT NULL, + premise_destination_id INT NOT NULL, + shipping_frequency INT UNSIGNED COMMENT 'annual shipping frequency', + total_cost DECIMAL(15, 2) COMMENT 'aka MEK_B in EUR', + annual_amount DECIMAL(15, 2) COMMENT 'annual quantity for this destinations', + -- risk + annual_risk_cost DECIMAL(15, 2) NOT NULL COMMENT 'complete calculation with globally stored worst case container rates', + annual_chance_cost DECIMAL(15, 2) NOT NULL COMMENT 'complete calculation with globally stored best case container rates', + -- handling + is_small_unit BOOLEAN DEFAULT FALSE COMMENT 'small unit equals KLT, volume of a handling unit is smaller than 0.08 cbm ', + annual_repacking_cost DECIMAL(15, 2) NOT NULL, + annual_handling_cost DECIMAL(15, 2) NOT NULL, + annual_disposal_cost DECIMAL(15, 2) NOT NULL, + -- inventory + operational_stock DECIMAL(15, 2) NOT NULL COMMENT 'operational stock in single pieces', + safety_stock DECIMAL(15, 2) NOT NULL COMMENT 'safety stock in single pieces', + stocked_inventory DECIMAL(15, 2) NOT NULL COMMENT 'sum of operational and safety stock ?!', + in_transport_stock DECIMAL(15, 2) NOT NULL, + stock_before_payment DECIMAL(15, 2) NOT NULL, + annual_capital_cost DECIMAL(15, 2) NOT NULL, + annual_storage_cost DECIMAL(15, 2) NOT NULL, -- Flächenkosten + -- custom + custom_value DECIMAL(15, 2) NOT NULL,-- Zollwert, + custom_duties DECIMAL(15, 2) NOT NULL,-- Zollabgaben, + tariff_rate DECIMAL(7, 4) NOT NULL,-- Zollsatz, + annual_custom_cost DECIMAL(15, 2) NOT NULL,-- Zollabgaben inkl. Einmalkosten, + -- air freight risk + air_freight_share_max DECIMAL(7, 4) NOT NULL, + air_freight_share DECIMAL(7, 4) NOT NULL, + air_freight_volumetric_weight DECIMAL(15, 2) NOT NULL, + air_freight_weight DECIMAL(15, 2) NOT NULL, + annual_air_freight_cost DECIMAL(15, 2) NOT NULL, + -- transportation + 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_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?!', + annual_transportation_cost DECIMAL(15, 2) NOT NULL COMMENT 'total annual transportation costs in EUR', + container_utilization DECIMAL(7, 4) NOT NULL, + -- material cost + material_cost DECIMAL(15, 2) NOT NULL, + fca_cost DECIMAL(15, 2) NOT NULL, + + FOREIGN KEY (calculation_job_id) REFERENCES calculation_job (id), + FOREIGN KEY (premise_destination_id) REFERENCES premise_destination (id), + INDEX idx_calculation_job_id (calculation_job_id), + INDEX idx_premise_destination_id (premise_destination_id) ); 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_transportation_id INT NOT NULL, - used_rule CHAR(8) CHECK (used_rule IN - ('CONTAINER', 'MATRIX')), - is_unmixed_price BOOLEAN DEFAULT FALSE, - is_cbm_price BOOLEAN DEFAULT FALSE, - is_weight_price BOOLEAN DEFAULT FALSE, - is_stacked BOOLEAN DEFAULT FALSE, - is_pre_run BOOLEAN DEFAULT FALSE, - is_main_run BOOLEAN DEFAULT FALSE, - is_post_run BOOLEAN DEFAULT FALSE, - rate DECIMAL(15, 2) NOT NULL COMMENT 'copy of the container rate resp. price matrix in EUR (depends on used_rule)', - distance DECIMAL(15, 2) DEFAULT NULL COMMENT 'distance of this routeInformationObject section im meters', - cbm_price DECIMAL(15, 2) NOT NULL COMMENT 'calculated price per cubic meter', - weight_price DECIMAL(15, 2) NOT NULL COMMENT 'calculated price per kilogram', - annual_cost DECIMAL(15, 2) NOT NULL COMMENT 'annual costs for this routeInformationObject section, result depends on calculation method (mixed or unmixed, stacked or unstacked, per volume/per weight resp. container rate/price matrix)', - transit_time INT UNSIGNED NOT NULL, + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + premise_route_section_id INT NOT NULL, + calculation_job_destination_id INT NOT NULL, + used_rule CHAR(8) CHECK (used_rule IN + ('CONTAINER', 'MATRIX')), + is_unmixed_price BOOLEAN DEFAULT FALSE, + is_cbm_price BOOLEAN DEFAULT FALSE, + is_weight_price BOOLEAN DEFAULT FALSE, + is_stacked BOOLEAN DEFAULT FALSE, + is_pre_run BOOLEAN DEFAULT FALSE, + is_main_run BOOLEAN DEFAULT FALSE, + is_post_run BOOLEAN DEFAULT FALSE, + rate DECIMAL(15, 2) NOT NULL COMMENT 'copy of the container rate resp. price matrix in EUR (depends on used_rule)', + distance DECIMAL(15, 2) DEFAULT NULL COMMENT 'distance of this routeInformationObject section im meters', + cbm_price DECIMAL(15, 2) NOT NULL COMMENT 'calculated price per cubic meter', + weight_price DECIMAL(15, 2) NOT NULL COMMENT 'calculated price per kilogram', + annual_cost DECIMAL(15, 2) NOT NULL COMMENT 'annual costs for this routeInformationObject section, result depends on calculation method (mixed or unmixed, stacked or unstacked, per volume/per weight resp. container rate/price matrix)', + transit_time INT UNSIGNED NOT NULL, FOREIGN KEY (premise_route_section_id) REFERENCES premise_route_section (id), - FOREIGN KEY (calculation_job_transportation_id) REFERENCES calculation_job_transportation (id), + FOREIGN KEY (calculation_job_destination_id) REFERENCES calculation_job_destination (id), INDEX idx_premise_route_section_id (premise_route_section_id), - INDEX idx_calculation_job_transportation_id (calculation_job_transportation_id), + INDEX idx_calculation_job_destination_id (calculation_job_destination_id), CONSTRAINT chk_stacked CHECK (is_unmixed_price IS TRUE OR is_stacked IS TRUE), -- only unmixed transports can be unstacked CONSTRAINT chk_cbm_weight_price CHECK (is_unmixed_price IS FALSE OR (is_cbm_price IS FALSE AND is_weight_price IS FALSE)) ); -CREATE TABLE IF NOT EXISTS calculation_job_airfreight -( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - air_freight_share_max DECIMAL(7, 4) NOT NULL, - air_freight_share DECIMAL(7, 4) NOT NULL, - air_freight_volumetric_weight DECIMAL(15, 2) NOT NULL, - air_freight_weight DECIMAL(15, 2) NOT NULL, - annual_cost DECIMAL(15, 2) NOT NULL - -); - -CREATE TABLE IF NOT EXISTS calculation_job_custom -( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - custom_value DECIMAL(15, 2) NOT NULL,-- Zollwert, - custom_duties DECIMAL(15, 2) NOT NULL,-- Zollabgaben, - tariff_rate DECIMAL(7, 4) NOT NULL,-- Zollsatz, - annual_cost DECIMAL(15, 2) NOT NULL-- Zollabgaben inkl. Einmalkosten, -); - -CREATE TABLE IF NOT EXISTS calculation_job_inventory -( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - operational_stock DECIMAL(15, 2) NOT NULL COMMENT 'operational stock in single pieces', - safety_stock DECIMAL(15, 2) NOT NULL COMMENT 'safety stock in single pieces', - stocked_inventory DECIMAL(15, 2) NOT NULL COMMENT 'sum of operational and safety stock ?!', - in_transport_stock DECIMAL(15, 2) NOT NULL, - stock_before_payment DECIMAL(15, 2) NOT NULL, - annual_capital_cost DECIMAL(15, 2) NOT NULL, - annual_storage_cost DECIMAL(15, 2) NOT NULL -- Flächenkosten, -); - -CREATE TABLE IF NOT EXISTS calculation_job_handling -( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - is_small_unit BOOLEAN DEFAULT FALSE COMMENT 'small unit equals KLT, volume of a handling unit is smaller than 0.08 cbm ', - annual_repacking_cost DECIMAL(15, 2) NOT NULL, - annual_handling_cost DECIMAL(15, 2) NOT NULL, - annual_disposal_cost DECIMAL(15, 2) NOT NULL -); - -CREATE TABLE IF NOT EXISTS calculation_job_risk -( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - annual_risk_cost DECIMAL(15, 2) NOT NULL COMMENT 'complete calculation with globally stored worst case container rates', - annual_chance_cost DECIMAL(15, 2) NOT NULL COMMENT 'complete calculation with globally stored best case container rates' -); - - -CREATE TABLE IF NOT EXISTS calculation_job_destination -( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - calculation_job_id INT NOT NULL, - premise_destination_id INT NOT NULL, - safety_stock INT UNSIGNED COMMENT 'safety stock in single pieces ?!?!', - shipping_frequency INT UNSIGNED COMMENT 'annual shipping frequency', - total_cost DECIMAL(15, 2) COMMENT 'aka MEK_B in EUR', - calculation_job_custom_id INT NOT NULL, - calculation_job_inventory_id INT NOT NULL, - calculation_job_handling_id INT NOT NULL, - calculation_job_risk_id INT NOT NULL, - calculation_job_material_id INT NOT NULL, - calculation_job_transportation_id INT NOT NULL, - calculation_job_airfreight_id INT NOT NULL, - FOREIGN KEY (calculation_job_id) REFERENCES calculation_job (id), - FOREIGN KEY (premise_destination_id) REFERENCES premise_destination (id), - FOREIGN KEY (calculation_job_custom_id) REFERENCES calculation_job_custom (id), - FOREIGN KEY (calculation_job_inventory_id) REFERENCES calculation_job_inventory (id), - FOREIGN KEY (calculation_job_handling_id) REFERENCES calculation_job_handling (id), - FOREIGN KEY (calculation_job_risk_id) REFERENCES calculation_job_risk (id), - FOREIGN KEY (calculation_job_material_id) REFERENCES calculation_job_material (id), - FOREIGN KEY (calculation_job_transportation_id) REFERENCES calculation_job_transportation (id), - FOREIGN KEY (calculation_job_airfreight_id) REFERENCES calculation_job_airfreight (id), - INDEX idx_calculation_job_id (calculation_job_id), - INDEX idx_premise_destination_id (premise_destination_id) -);