From bce745e45892585334ccb4c9e812b200b967dbfd Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 14 Dec 2025 10:39:15 +0100 Subject: [PATCH] Bugfixes for #72, #73, #75, #68 --- .../lcc/service/bulk/BulkExportService.java | 8 +--- .../lcc/service/bulk/BulkImportService.java | 4 -- .../excelMapper/ContainerRateExcelMapper.java | 45 +++++++++++++++---- .../service/excelMapper/NodeExcelMapper.java | 16 +++++++ .../excelMapper/PackagingExcelMapper.java | 2 +- 5 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/main/java/de/avatic/lcc/service/bulk/BulkExportService.java b/src/main/java/de/avatic/lcc/service/bulk/BulkExportService.java index 6baf9f7..6bd323d 100644 --- a/src/main/java/de/avatic/lcc/service/bulk/BulkExportService.java +++ b/src/main/java/de/avatic/lcc/service/bulk/BulkExportService.java @@ -24,7 +24,6 @@ public class BulkExportService { private final HeaderCellStyleProvider headerCellStyleProvider; private final ContainerRateExcelMapper containerRateExcelMapper; private final MatrixRateExcelMapper matrixRateExcelMapper; - private final MaterialExcelMapper materialExcelMapper; private final PackagingExcelMapper packagingExcelMapper; private final NodeExcelMapper nodeExcelMapper; private final HiddenNodeExcelMapper hiddenNodeExcelMapper; @@ -32,11 +31,10 @@ public class BulkExportService { private final String sheetPassword; private final MaterialFastExcelMapper materialFastExcelMapper; - public BulkExportService(@Value("${lcc.bulk.sheet_password}") String sheetPassword, HeaderCellStyleProvider headerCellStyleProvider, ContainerRateExcelMapper containerRateExcelMapper, MatrixRateExcelMapper matrixRateExcelMapper, MaterialExcelMapper materialExcelMapper, PackagingExcelMapper packagingExcelMapper, NodeExcelMapper nodeExcelMapper, HiddenNodeExcelMapper hiddenNodeExcelMapper, HiddenCountryExcelMapper hiddenCountryExcelMapper, MaterialFastExcelMapper materialFastExcelMapper) { + public BulkExportService(@Value("${lcc.bulk.sheet_password}") String sheetPassword, HeaderCellStyleProvider headerCellStyleProvider, ContainerRateExcelMapper containerRateExcelMapper, MatrixRateExcelMapper matrixRateExcelMapper, PackagingExcelMapper packagingExcelMapper, NodeExcelMapper nodeExcelMapper, HiddenNodeExcelMapper hiddenNodeExcelMapper, HiddenCountryExcelMapper hiddenCountryExcelMapper, MaterialFastExcelMapper materialFastExcelMapper) { this.headerCellStyleProvider = headerCellStyleProvider; this.containerRateExcelMapper = containerRateExcelMapper; this.matrixRateExcelMapper = matrixRateExcelMapper; - this.materialExcelMapper = materialExcelMapper; this.packagingExcelMapper = packagingExcelMapper; this.nodeExcelMapper = nodeExcelMapper; this.hiddenNodeExcelMapper = hiddenNodeExcelMapper; @@ -93,10 +91,6 @@ public class BulkExportService { matrixRateExcelMapper.fillSheet(worksheet, style, periodId); matrixRateExcelMapper.createConstraints(workbook, worksheet); break; -// case MATERIAL: -// materialExcelMapper.fillSheet(worksheet, style); -// materialExcelMapper.createConstraints(worksheet); -// break; case PACKAGING: packagingExcelMapper.fillSheet(worksheet, style); packagingExcelMapper.createConstraints(workbook, worksheet); diff --git a/src/main/java/de/avatic/lcc/service/bulk/BulkImportService.java b/src/main/java/de/avatic/lcc/service/bulk/BulkImportService.java index aa77891..762192f 100644 --- a/src/main/java/de/avatic/lcc/service/bulk/BulkImportService.java +++ b/src/main/java/de/avatic/lcc/service/bulk/BulkImportService.java @@ -96,10 +96,6 @@ public class BulkImportService { var matrixRates = matrixRateExcelMapper.extractSheet(sheet); matrixRateImportService.processMatrixRates(matrixRates); break; -// case MATERIAL: -// var materials = materialExcelMapper.extractSheet(sheet); -// materials.forEach(materialBulkImportService::processMaterialInstructions); -// break; case PACKAGING: var packaging = packagingExcelMapper.extractSheet(sheet); packaging.forEach(packagingBulkImportService::processPackagingInstructions); diff --git a/src/main/java/de/avatic/lcc/service/excelMapper/ContainerRateExcelMapper.java b/src/main/java/de/avatic/lcc/service/excelMapper/ContainerRateExcelMapper.java index 57afb87..b901ee5 100644 --- a/src/main/java/de/avatic/lcc/service/excelMapper/ContainerRateExcelMapper.java +++ b/src/main/java/de/avatic/lcc/service/excelMapper/ContainerRateExcelMapper.java @@ -4,9 +4,12 @@ import de.avatic.lcc.dto.generic.TransportType; import de.avatic.lcc.model.bulk.HiddenTableType; import de.avatic.lcc.model.bulk.header.ContainerRateHeader; import de.avatic.lcc.model.bulk.header.HiddenNodeHeader; +import de.avatic.lcc.model.db.nodes.Node; +import de.avatic.lcc.model.db.properties.SystemPropertyMappingId; import de.avatic.lcc.model.db.rates.ContainerRate; import de.avatic.lcc.repositories.NodeRepository; import de.avatic.lcc.repositories.rates.ContainerRateRepository; +import de.avatic.lcc.service.access.PropertyService; import de.avatic.lcc.service.bulk.helper.ConstraintGenerator; import de.avatic.lcc.service.bulk.helper.HeaderGenerator; import de.avatic.lcc.util.exception.internalerror.ExcelValidationError; @@ -16,6 +19,7 @@ import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import java.util.Optional; @Service public class ContainerRateExcelMapper { @@ -24,12 +28,14 @@ public class ContainerRateExcelMapper { private final ContainerRateRepository containerRateRepository; private final NodeRepository nodeRepository; private final ConstraintGenerator constraintGenerator; + private final PropertyService propertyService; - public ContainerRateExcelMapper(HeaderGenerator headerGenerator, ContainerRateRepository containerRateRepository, NodeRepository nodeRepository, ConstraintGenerator constraintGenerator) { + public ContainerRateExcelMapper(HeaderGenerator headerGenerator, ContainerRateRepository containerRateRepository, NodeRepository nodeRepository, ConstraintGenerator constraintGenerator, PropertyService propertyService) { this.headerGenerator = headerGenerator; this.containerRateRepository = containerRateRepository; this.nodeRepository = nodeRepository; this.constraintGenerator = constraintGenerator; + this.propertyService = propertyService; } public void fillSheet(Sheet sheet, CellStyle headerStyle, Integer periodId) { @@ -73,10 +79,20 @@ public class ContainerRateExcelMapper { return true; } + private String getRefStart() { + Optional ref = propertyService.getProperty(SystemPropertyMappingId.START_REF); + return ref.orElseThrow(() -> new InternalError("Unable to get start reference from properties")); + } + + private String getRefEnd() { + Optional ref = propertyService.getProperty(SystemPropertyMappingId.END_REF); + return ref.orElseThrow(() -> new InternalError("Unable to get end reference from properties")); + } + public List extractSheet(Sheet sheet) { headerGenerator.validateHeader(sheet, ContainerRateHeader.class); - var rates = new ArrayList(); + var rates = new ArrayList(); sheet.forEach(row -> { if (row.getRowNum() == 0) return; @@ -84,7 +100,18 @@ public class ContainerRateExcelMapper { rates.add(mapToEntity(row)); }); - return rates; + validateReferenceRoute(rates); + + return rates.stream().map(ExcelContainerRate::rate).toList(); + } + + private void validateReferenceRoute(List rates) { + var startRef = getRefStart(); + var endRef = getRefEnd(); + + if(rates.stream().noneMatch(rate -> rate.fromNode().getExternalMappingId().equals(startRef) || rate.toNode().getExternalMappingId().equals(endRef))) + throw new ExcelValidationError(String.format("Container rates must reference route (%s - %s) not found in container rates", startRef, endRef)); + } private boolean isEmpty(Row row) { @@ -109,7 +136,7 @@ public class ContainerRateExcelMapper { return result.toString(); } - private ContainerRate mapToEntity(Row row) { + private ExcelContainerRate mapToEntity(Row row) { ContainerRate entity = new ContainerRate(); validateConstraints(row); @@ -134,7 +161,7 @@ public class ContainerRateExcelMapper { entity.setRateTeu(BigDecimal.valueOf(row.getCell(ContainerRateHeader.RATE_TEU.ordinal()).getNumericCellValue())); entity.setRateHc(BigDecimal.valueOf(row.getCell(ContainerRateHeader.RATE_HC.ordinal()).getNumericCellValue())); - return entity; + return new ExcelContainerRate(entity, fromNode.orElseThrow(), toNode.orElseThrow()); } private void validateConstraints(Row row) { @@ -142,10 +169,12 @@ public class ContainerRateExcelMapper { constraintGenerator.validateStringCell(row, ContainerRateHeader.FROM_NODE.ordinal()); constraintGenerator.validateStringCell(row, ContainerRateHeader.TO_NODE.ordinal()); constraintGenerator.validateEnumConstraint(row, ContainerRateHeader.CONTAINER_RATE_TYPE.ordinal(), TransportType.class); - constraintGenerator.validateDecimalConstraint(row, ContainerRateHeader.RATE_FEU.ordinal(), 0.0, 1000000.0); - constraintGenerator.validateDecimalConstraint(row, ContainerRateHeader.RATE_TEU.ordinal(), 0.0, 1000000.0); - constraintGenerator.validateDecimalConstraint(row, ContainerRateHeader.RATE_HC.ordinal(), 0.0, 1000000.0); + constraintGenerator.validateDecimalConstraint(row, ContainerRateHeader.RATE_FEU.ordinal(), 1.0, 1000000.0); + constraintGenerator.validateDecimalConstraint(row, ContainerRateHeader.RATE_TEU.ordinal(), 1.0, 1000000.0); + constraintGenerator.validateDecimalConstraint(row, ContainerRateHeader.RATE_HC.ordinal(), 1.0, 1000000.0); constraintGenerator.validateIntegerConstraint(row, ContainerRateHeader.LEAD_TIME.ordinal(), 0, 365); } + + private record ExcelContainerRate(ContainerRate rate, Node fromNode, Node toNode) {} } diff --git a/src/main/java/de/avatic/lcc/service/excelMapper/NodeExcelMapper.java b/src/main/java/de/avatic/lcc/service/excelMapper/NodeExcelMapper.java index 5e2122c..660e281 100644 --- a/src/main/java/de/avatic/lcc/service/excelMapper/NodeExcelMapper.java +++ b/src/main/java/de/avatic/lcc/service/excelMapper/NodeExcelMapper.java @@ -1,5 +1,6 @@ package de.avatic.lcc.service.excelMapper; +import de.avatic.lcc.model.bulk.header.ContainerRateHeader; import de.avatic.lcc.model.excel.ExcelNode; import de.avatic.lcc.model.bulk.BulkInstruction; import de.avatic.lcc.model.bulk.BulkInstructionType; @@ -150,6 +151,11 @@ public class NodeExcelMapper { entity.setSource(Boolean.valueOf(row.getCell(NodeHeader.IS_SOURCE.ordinal()).getStringCellValue())); entity.setIntermediate(Boolean.valueOf(row.getCell(NodeHeader.IS_INTERMEDIATE.ordinal()).getStringCellValue())); entity.setDestination(Boolean.valueOf(row.getCell(NodeHeader.IS_DESTINATION.ordinal()).getStringCellValue())); + + if(!entity.getSource() && !entity.getDestination() && !entity.getIntermediate()) + throw new ExcelValidationError("Unable to validate row " + (row.getRowNum() + 1) + " column " + toExcelLetter(ContainerRateHeader.FROM_NODE.ordinal()) + ": Node with mapping id " + row.getCell(NodeHeader.MAPPING_ID.ordinal()).getStringCellValue() + " must be either source, destination or intermediate"); + + entity.setPredecessorRequired(Boolean.valueOf(row.getCell(NodeHeader.IS_PREDECESSOR_MANDATORY.ordinal()).getStringCellValue())); entity.setNodePredecessors(mapChainsFromCell(CellUtil.getCell(row, NodeHeader.PREDECESSOR_NODES.ordinal()).getStringCellValue())); entity.setOutboundCountries(mapOutboundCountriesFromCell(CellUtil.getCell(row, NodeHeader.OUTBOUND_COUNTRIES.ordinal()).getStringCellValue())); @@ -199,4 +205,14 @@ public class NodeExcelMapper { return Arrays.stream(chain.split(",")).map(String::trim).toList(); } + private String toExcelLetter(int columnIdx) { + StringBuilder result = new StringBuilder(); + columnIdx++; // Convert from 0-based to 1-based for the algorithm + while (columnIdx > 0) { + columnIdx--; // Adjust for 1-based indexing + result.insert(0, (char) ('A' + columnIdx % 26)); + columnIdx /= 26; + } + return result.toString(); + } } diff --git a/src/main/java/de/avatic/lcc/service/excelMapper/PackagingExcelMapper.java b/src/main/java/de/avatic/lcc/service/excelMapper/PackagingExcelMapper.java index cd1e0ec..f6e3860 100644 --- a/src/main/java/de/avatic/lcc/service/excelMapper/PackagingExcelMapper.java +++ b/src/main/java/de/avatic/lcc/service/excelMapper/PackagingExcelMapper.java @@ -60,7 +60,7 @@ public class PackagingExcelMapper { private void mapToRow(Packaging packaging, ArrayList headers, Row row) { Optional shu = packagingDimensionRepository.getById(packaging.getShuId()); - Optional hu = packagingDimensionRepository.getById(packaging.getShuId()); + Optional hu = packagingDimensionRepository.getById(packaging.getHuId()); row.createCell(PackagingHeader.OPERATION.ordinal()).setCellValue(BulkInstructionType.UPDATE.name());