diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ed9eb71..3214411e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## v4.0.1 +## v4.1.0 The following Changelog lists the changes. Please refer to the [documentation](docs/README.md) for configuration needs and understanding the concept changes. @@ -14,6 +14,7 @@ The **need for configuration updates** is **marked bold**. ### Added - Added Material Number in material details view ([#1005](https://github.com/eclipse-tractusx/puris/pull/1005)) +- added formula support for excel imports ([#1020](https://github.com/eclipse-tractusx/puris/pull/1020)) ### Changed diff --git a/backend/pom.xml b/backend/pom.xml index a01ab6a8..0a872a9e 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -33,7 +33,7 @@ org.eclipse.tractusx.puris puris-backend - 4.0.1 + 4.1.0 puris-backend PURIS Backend diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/file/logic/service/ExcelService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/file/logic/service/ExcelService.java index a0a5b39f..6567ffe9 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/file/logic/service/ExcelService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/file/logic/service/ExcelService.java @@ -134,6 +134,8 @@ public class ExcelService { public DataImportResult readExcelFile(InputStream is) throws IOException { Workbook workbook = WorkbookFactory.create(is); Sheet sheet = workbook.getSheetAt(0); + FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); + evaluator.evaluateAll(); var result = extractAndSaveData(sheet); workbook.close(); return result; @@ -688,9 +690,23 @@ private List extractHeader(Sheet sheet) { } private String getStringCellValue(Cell cell) { - return cell == null || cell.getCellType() == CellType.BLANK ? null : cell.getCellType() == CellType.STRING - ? cell.getStringCellValue().trim() - : String.valueOf(cell.getNumericCellValue()).trim(); + if (cell == null || cell.getCellType() == CellType.BLANK) { + return null; + } + CellType type = cell.getCellType(); + if (type == CellType.FORMULA) { + type = cell.getCachedFormulaResultType(); + } + switch (type) { + case STRING: + return cell.getStringCellValue() == null ? null : cell.getStringCellValue().trim(); + case NUMERIC: + return String.valueOf(cell.getNumericCellValue()).trim(); + case BOOLEAN: + return String.valueOf(cell.getBooleanCellValue()); + default: + return null; + } } private Date getDateCellValue(Cell cell) { diff --git a/backend/src/test/java/org/eclipse/tractusx/puris/backend/file/logic/services/ExcelServiceTest.java b/backend/src/test/java/org/eclipse/tractusx/puris/backend/file/logic/services/ExcelServiceTest.java index fd84a528..44ab1117 100644 --- a/backend/src/test/java/org/eclipse/tractusx/puris/backend/file/logic/services/ExcelServiceTest.java +++ b/backend/src/test/java/org/eclipse/tractusx/puris/backend/file/logic/services/ExcelServiceTest.java @@ -81,7 +81,9 @@ public class ExcelServiceTest { private static final Material testMaterial; private static final Partner testPartner; - private static final Date testDate = Date.from(Instant.parse("2025-01-15T10:00:00Z")); + private static final String todaysDateFromFormula = "=TODAY()"; + private static final String tomorrowsDateFromFormula = "=TODAY()+1"; + private static final Date todaysDateFromParsing = Date.from(Instant.now()); private static final String OWN_BPNS = "BPNS1234567890AB"; private static final String OWN_BPNA = "BPNA1234567890AB"; @@ -135,27 +137,27 @@ public class ExcelServiceTest { SAMPLE_DEMAND_ROW = List.of( testMaterial.getOwnMaterialNumber(), testPartner.getBpnl(), 100.0, "unit:piece", OWN_BPNS, PARTNER_BPNS, "0001", - testDate, testDate + todaysDateFromFormula, todaysDateFromParsing ); SAMPLE_PRODUCTION_ROW = List.of( testMaterial.getOwnMaterialNumber(), testPartner.getBpnl(), 100.0, "unit:piece", - OWN_BPNS, testDate, "ORDER-001", - "POS-001", "SUPPLY-001", testDate + OWN_BPNS, todaysDateFromFormula, "ORDER-001", + "POS-001", "SUPPLY-001", todaysDateFromParsing ); SAMPLE_DELIVERY_ROW = List.of( testMaterial.getOwnMaterialNumber(), testPartner.getBpnl(), 100.0, "unit:piece", OWN_BPNS, OWN_BPNA, PARTNER_BPNS, PARTNER_BPNA, - "estimated-departure", testDate, "estimated-arrival", testDate, + "estimated-departure", todaysDateFromFormula, "estimated-arrival", tomorrowsDateFromFormula, "TRACK-001", "EXW", "ORDER-001", "POS-001", - "SUPPLY-001", testDate + "SUPPLY-001", todaysDateFromParsing ); SAMPLE_STOCK_ROW = List.of( testMaterial.getOwnMaterialNumber(), testPartner.getBpnl(), 100.0, "unit:piece", OWN_BPNS, OWN_BPNA, "ORDER-001", "POS-001", - "SUPPLY-001", false, testDate, "INBOUND" + "SUPPLY-001", false, todaysDateFromParsing, "INBOUND" ); } @@ -508,7 +510,7 @@ private ByteArrayInputStream createDemandExcelFileWithInvalidUnit() throws IOExc List dataValues = List.of( testMaterial.getOwnMaterialNumber(), testPartner.getBpnl(), 100.0, "invalid-unit", OWN_BPNS, PARTNER_BPNS, "0001", - testDate, testDate + todaysDateFromFormula, todaysDateFromFormula ); return createExcelFile("Demands", DEMAND_HEADERS, List.of(dataValues)); @@ -518,7 +520,7 @@ private ByteArrayInputStream createDemandExcelFileWithInvalidCategory() throws I List dataValues = List.of( testMaterial.getOwnMaterialNumber(), testPartner.getBpnl(), 100.0, "unit:piece", OWN_BPNS, PARTNER_BPNS, "invalid-category", - testDate, testDate + todaysDateFromFormula, todaysDateFromFormula ); return createExcelFile("Demands", DEMAND_HEADERS, List.of(dataValues)); @@ -528,9 +530,9 @@ private ByteArrayInputStream createDeliveryExcelFileWithInvalidIncoterm() throws List dataValues = List.of( testMaterial.getOwnMaterialNumber(), testPartner.getBpnl(), 100.0, "unit:piece", OWN_BPNS, OWN_BPNA, PARTNER_BPNS, PARTNER_BPNA, - "estimated-departure", testDate, "estimated-arrival", testDate, + "estimated-departure", todaysDateFromFormula, "estimated-arrival", todaysDateFromFormula, "TRACK-001", "INVALID-INCOTERM", "ORDER-001", "POS-001", - "SUPPLY-001", testDate + "SUPPLY-001", todaysDateFromFormula ); return createExcelFile("Deliveries", DELIVERY_HEADERS, List.of(dataValues)); @@ -540,9 +542,9 @@ private ByteArrayInputStream createDeliveryExcelFileWithInvalidDepartureType() t List dataValues = List.of( testMaterial.getOwnMaterialNumber(), testPartner.getBpnl(), 100.0, "unit:piece", OWN_BPNS, OWN_BPNA, PARTNER_BPNS, PARTNER_BPNA, - "invalid-departure-type", testDate, "estimated-arrival", testDate, + "invalid-departure-type", todaysDateFromFormula, "estimated-arrival", todaysDateFromFormula, "TRACK-001", "EXW", "ORDER-001", "POS-001", - "SUPPLY-001", testDate + "SUPPLY-001", todaysDateFromFormula ); return createExcelFile("Deliveries", DELIVERY_HEADERS, List.of(dataValues)); @@ -552,9 +554,9 @@ private ByteArrayInputStream createDeliveryExcelFileWithInvalidArrivalType() thr List dataValues = List.of( testMaterial.getOwnMaterialNumber(), testPartner.getBpnl(), 100.0, "unit:piece", OWN_BPNS, OWN_BPNA, PARTNER_BPNS, PARTNER_BPNA, - "estimated-departure", testDate, "invalid-arrival-type", testDate, + "estimated-departure", todaysDateFromFormula, "invalid-arrival-type", todaysDateFromFormula, "TRACK-001", "EXW", "ORDER-001", "POS-001", - "SUPPLY-001", testDate + "SUPPLY-001", todaysDateFromFormula ); return createExcelFile("Deliveries", DELIVERY_HEADERS, List.of(dataValues)); @@ -564,7 +566,7 @@ private ByteArrayInputStream createStockExcelFileWithInvalidDirection() throws I List dataValues = List.of( testMaterial.getOwnMaterialNumber(), testPartner.getBpnl(), 100.0, "unit:piece", OWN_BPNS, OWN_BPNA, "ORDER-001", "POS-001", - "SUPPLY-001", false, testDate, "invalid-direction" + "SUPPLY-001", false, todaysDateFromFormula, "invalid-direction" ); return createExcelFile("Stocks", STOCK_HEADERS, List.of(dataValues)); @@ -583,6 +585,7 @@ private ByteArrayInputStream createInvalidHeaderExcelFile() throws IOException { private ByteArrayInputStream createExcelFile(String sheetName, List headers, List> rows) throws IOException { Workbook workbook = new XSSFWorkbook(); + FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); Sheet sheet = workbook.createSheet(sheetName); Row headerRow = sheet.createRow(0); @@ -598,7 +601,12 @@ private ByteArrayInputStream createExcelFile(String sheetName, List head Object value = rowData.get(colIdx); if (value instanceof String) { - cell.setCellValue((String) value); + if (((String) value).startsWith("=")) { + cell.setCellFormula(((String) value).substring(1)); + evaluator.evaluateFormulaCell(cell); + } else { + cell.setCellValue((String) value); + } } else if (value instanceof Double) { cell.setCellValue((Double) value); } else if (value instanceof Boolean) { diff --git a/charts/puris/Chart.yaml b/charts/puris/Chart.yaml index ce1d8ee4..21c16cc2 100644 --- a/charts/puris/Chart.yaml +++ b/charts/puris/Chart.yaml @@ -35,10 +35,10 @@ dependencies: # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 5.0.1 +version: 5.1.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "4.0.1" +appVersion: "4.1.0" diff --git a/docs/user/User_Guide.md b/docs/user/User_Guide.md index 69e87885..b50eadc1 100644 --- a/docs/user/User_Guide.md +++ b/docs/user/User_Guide.md @@ -264,6 +264,10 @@ An Admin can upload data in this page. Once the file is uploaded, all existing d Right now, only files of type xlsx are supported. +### Formula support + +The import feature evaluates formulas to dynamically calculate values. Due to technical limitations of the underlying Java library Apache POI not all functions are supported. You can find a list of supported functions int the [Apache POI documentation](https://poi.apache.org/components/spreadsheet/eval-devguide.html#appendixA). + ### Import view ![Overview of the Import view](img/import_view.png) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f3112a4b..e63e1daa 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "puris-frontend", - "version": "4.0.1", + "version": "4.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "puris-frontend", - "version": "4.0.1", + "version": "4.1.0", "license": "Apache-2.0", "dependencies": { "@catena-x/portal-shared-components": "^2.1.31", diff --git a/frontend/package.json b/frontend/package.json index 9a52bc17..64fc814d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -2,7 +2,7 @@ "name": "puris-frontend", "description": "Puris frontend", "license": "Apache-2.0", - "version": "4.0.1", + "version": "4.1.0", "type": "module", "scripts": { "dev": "vite --host --mode customer --port 5173",