+
Import
dataset
- nodes
- kilometer rates
+ nodes
- container rates
+ kilometer rates
+
+ container rates
+
+
+ materials
+
+
+ packaging
- materials
- packaging
file
@@ -79,8 +93,11 @@
-
No recent bulk operations
-
+
No recent bulk
+ operations
+
+
@@ -101,6 +118,7 @@ import {useValidityPeriodStore} from "@/store/validityPeriod.js";
import {useBulkOperationStore} from "@/store/bulkOperation.js";
import BulkOperation from "@/components/layout/bulkoperation/BulkOperation.vue";
import logger from "@/logger.js";
+import {useActiveUserStore} from "@/store/activeuser.js";
export default {
name: "BulkOperations",
@@ -125,13 +143,13 @@ export default {
},
watch: {
async isSelected(newVal) {
- if(newVal === true)
+ if (newVal === true && this.activeUserStore.allowRates)
await this.validityPeriodStore.loadPeriods();
await this.bulkOperationStore.manageStatus();
}
},
computed: {
- ...mapStores(useValidityPeriodStore, useBulkOperationStore),
+ ...mapStores(useValidityPeriodStore, useBulkOperationStore, useActiveUserStore),
showValidityPeriod() {
return this.exportType === "download" && (this.exportDataset === "COUNTRY_MATRIX" || this.exportDataset === "CONTAINER_RATE");
},
@@ -167,8 +185,20 @@ export default {
}
},
methods: {
+ canModify(role) {
+ switch (role) {
+ case "nodes":
+ return this.activeUserStore.isSuper;
+ case "rates":
+ return this.activeUserStore.isSuper || this.activeUserStore.isFreight;
+ case "materials":
+ return this.activeUserStore.isSuper || this.activeUserStore.isMaterial;
+ case "packaging":
+ return this.activeUserStore.isSuper || this.activeUserStore.isPackaging;
+ }
+ },
buildDate(date) {
- if(date === null) return "not set";
+ if (date === null) return "not set";
return `${date[0]}-${date[1].toString().padStart(2, '0')}-${date[2].toString().padStart(2, '0')} ${date[3]?.toString().padStart(2, '0') ?? '00'}:${date[4]?.toString().padStart(2, '0') ?? '00'}:${date[5]?.toString().padStart(2, '0') ?? '00'}`
},
@@ -182,7 +212,7 @@ export default {
const url = `${config.backendUrl}/bulk/templates/${this.exportDataset}/`
await performDownload(url, fileName);
} else {
- const isCurrent = this.selectedPeriod === this.currentPeriod;
+ const isCurrent = this.selectedPeriod === this.currentPeriod;
await this.bulkOperationStore.scheduleDownload(this.exportDataset, isCurrent ? null : this.selectedPeriod);
}
},
@@ -195,7 +225,7 @@ export default {
this.fileBlob = await this.readFileAsBlob(file);
// File-Objekt mit dem Blob erstellen, das den originalen Namen und Typ behält
- this.selectedFile = new File([this.fileBlob], file.name, { type: file.type });
+ this.selectedFile = new File([this.fileBlob], file.name, {type: file.type});
this.selectedFileName = file.name;
logger.info(`File loaded into memory: ${file.name} (${(file.size / 1024).toFixed(2)} KB)`);
@@ -220,7 +250,7 @@ export default {
reader.onload = (e) => {
// ArrayBuffer in Blob konvertieren
- const blob = new Blob([e.target.result], { type: file.type });
+ const blob = new Blob([e.target.result], {type: file.type});
resolve(blob);
};
diff --git a/src/frontend/src/pages/Config.vue b/src/frontend/src/pages/Config.vue
index 0062814..8989796 100644
--- a/src/frontend/src/pages/Config.vue
+++ b/src/frontend/src/pages/Config.vue
@@ -77,19 +77,22 @@ export default {
tabsConfig() {
const tabs = [];
- if(this.activeUserStore.isSuper) {
+ if (this.activeUserStore.isSuper) {
tabs.push(this.propertiesTab);
tabs.push(this.systemLogTab);
}
- if(this.activeUserStore.isService) {
+ if (this.activeUserStore.isService) {
tabs.push(this.appsTab);
}
- tabs.push(this.materialsTab);
+ if (this.activeUserStore.isSuper || this.activeUserStore.isMaterial) {
+ tabs.push(this.materialsTab);
+ }
+
tabs.push(this.nodesTab);
- if(this.activeUserStore.allowRates)
+ if (this.activeUserStore.allowRates)
tabs.push(this.ratesTab);
tabs.push(this.bulkOperationsTab);
diff --git a/src/frontend/src/store/activeuser.js b/src/frontend/src/store/activeuser.js
index 4545498..ab74960 100644
--- a/src/frontend/src/store/activeuser.js
+++ b/src/frontend/src/store/activeuser.js
@@ -18,12 +18,12 @@ export const useActiveUserStore = defineStore('activeUser', {
allowConfiguration(state) {
if (state.user === null)
return false;
- return state.user.groups?.includes("super") || state.user.groups?.includes("freight") || state.user.groups?.includes("packaging") || state.user.groups?.includes("service");
+ return state.user.groups?.includes("super") || state.user.groups?.includes("freight") || state.user.groups?.includes("packaging") || state.user.groups?.includes("service") || state.user.groups?.includes("material");
},
allowReporting(state) {
if (state.user === null)
return false;
- return state.user.groups?.includes("super") || state.user.groups?.includes("freight") || state.user.groups?.includes("packaging") || state.user.groups?.includes("basic") || state.user.groups?.includes("calculation");
+ return state.user.groups?.includes("super") || state.user.groups?.includes("freight") || state.user.groups?.includes("packaging") || state.user.groups?.includes("material") || state.user.groups?.includes("basic") || state.user.groups?.includes("calculation");
},
isSuper(state) {
if (state.user === null)
@@ -45,6 +45,11 @@ export const useActiveUserStore = defineStore('activeUser', {
return false;
return state.user.groups?.includes("freight");
},
+ isMaterial(state) {
+ if (state.user === null)
+ return false;
+ return state.user.groups?.includes("material");
+ },
allowRates(state) {
if (state.user === null)
return false;
diff --git a/src/frontend/src/store/material.js b/src/frontend/src/store/material.js
index 8ba9355..cbb7852 100644
--- a/src/frontend/src/store/material.js
+++ b/src/frontend/src/store/material.js
@@ -2,6 +2,7 @@ import {defineStore} from 'pinia'
import {config} from '@/config'
import {useErrorStore} from "@/store/error.js";
import performRequest from "@/backend.js";
+import logger from "@/logger.js";
export const useMaterialStore = defineStore('material', {
state() {
diff --git a/src/main/java/de/avatic/lcc/config/CorsConfig.java b/src/main/java/de/avatic/lcc/config/CorsConfig.java
index f4d7ea7..5e828ac 100644
--- a/src/main/java/de/avatic/lcc/config/CorsConfig.java
+++ b/src/main/java/de/avatic/lcc/config/CorsConfig.java
@@ -14,63 +14,63 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
-@Configuration
-@EnableWebMvc
-@Order(Ordered.HIGHEST_PRECEDENCE)
+//@Configuration
+//@EnableWebMvc
+//@Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsConfig implements WebMvcConfigurer {
- private final Environment environment;
-
- @Value("${lcc.allowed_cors}")
- private String allowedCors;
-
- public CorsConfig(Environment environment) {
- this.environment = environment;
- }
-
- @Override
- public void addCorsMappings(@NotNull CorsRegistry registry) {
- String[] activeProfiles = environment.getActiveProfiles();
-
- System.out.println("Active profiles: " + Arrays.toString(activeProfiles));
- System.out.println("Allowed CORS: " + allowedCors);
-
- if (Arrays.asList(activeProfiles).contains("dev")) {
-
- System.out.println("Applying DEV CORS configuration");
-
- // Development CORS configuration
- registry.addMapping("/api/**")
- .allowedOriginPatterns("http://localhost:*")
- .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
- .allowedHeaders("*")
- .exposedHeaders("X-Total-Count", "X-Page-Count", "X-Current-Page")
- .allowCredentials(true);
-
- // OAuth endpoints
- registry.addMapping("/oauth/**")
- .allowedOriginPatterns("http://localhost:*")
- .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
- .allowedHeaders("*")
- .allowCredentials(true);
- } else {
-
- System.out.println("Applying PROD CORS configuration");
-
- // Production CORS configuration
- registry.addMapping("/api/**")
- .allowedOrigins(allowedCors)
- .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
- .allowedHeaders("*")
- .exposedHeaders("X-Total-Count", "X-Page-Count", "X-Current-Page")
- .allowCredentials(true);
-
- // OAuth endpoints
- registry.addMapping("/oauth/**")
- .allowedOriginPatterns("*")
- .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
- .allowedHeaders("*")
- .allowCredentials(true);
- }
- }
+// private final Environment environment;
+//
+// @Value("${lcc.allowed_cors}")
+// private String allowedCors;
+//
+// public CorsConfig(Environment environment) {
+// this.environment = environment;
+// }
+//
+// @Override
+// public void addCorsMappings(@NotNull CorsRegistry registry) {
+// String[] activeProfiles = environment.getActiveProfiles();
+//
+// System.out.println("Active profiles: " + Arrays.toString(activeProfiles));
+// System.out.println("Allowed CORS: " + allowedCors);
+//
+// if (Arrays.asList(activeProfiles).contains("dev")) {
+//
+// System.out.println("Applying DEV CORS configuration");
+//
+// // Development CORS configuration
+// registry.addMapping("/api/**")
+// .allowedOriginPatterns("http://localhost:*")
+// .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
+// .allowedHeaders("*")
+// .exposedHeaders("X-Total-Count", "X-Page-Count", "X-Current-Page")
+// .allowCredentials(true);
+//
+// // OAuth endpoints
+// registry.addMapping("/oauth/**")
+// .allowedOriginPatterns("http://localhost:*")
+// .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
+// .allowedHeaders("*")
+// .allowCredentials(true);
+// } else {
+//
+// System.out.println("Applying PROD CORS configuration");
+//
+// // Production CORS configuration
+// registry.addMapping("/api/**")
+// .allowedOrigins(allowedCors)
+// .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
+// .allowedHeaders("*")
+// .exposedHeaders("X-Total-Count", "X-Page-Count", "X-Current-Page")
+// .allowCredentials(true);
+//
+// // OAuth endpoints
+// registry.addMapping("/oauth/**")
+// .allowedOriginPatterns("*")
+// .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
+// .allowedHeaders("*")
+// .allowCredentials(true);
+// }
+// }
}
\ No newline at end of file
diff --git a/src/main/java/de/avatic/lcc/config/SecurityConfig.java b/src/main/java/de/avatic/lcc/config/SecurityConfig.java
index 4667294..7a65bc0 100644
--- a/src/main/java/de/avatic/lcc/config/SecurityConfig.java
+++ b/src/main/java/de/avatic/lcc/config/SecurityConfig.java
@@ -104,10 +104,13 @@ public class SecurityConfig {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(List.of("http://localhost:*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
- configuration.setAllowedHeaders(Arrays.asList("*"));
+ configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
+ configuration.setExposedHeaders(Arrays.asList("X-Total-Count", "X-Page-Count", "X-Current-Page"));
+
+
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
@@ -131,6 +134,9 @@ public class SecurityConfig {
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
+ configuration.setExposedHeaders(Arrays.asList("X-Total-Count", "X-Page-Count", "X-Current-Page"));
+
+
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
diff --git a/src/main/java/de/avatic/lcc/controller/bulk/BulkOperationController.java b/src/main/java/de/avatic/lcc/controller/bulk/BulkOperationController.java
index 2a6d774..4de5e26 100644
--- a/src/main/java/de/avatic/lcc/controller/bulk/BulkOperationController.java
+++ b/src/main/java/de/avatic/lcc/controller/bulk/BulkOperationController.java
@@ -37,20 +37,20 @@ public class BulkOperationController {
}
@GetMapping({"/status/", "/status"})
- @PreAuthorize("hasAnyRole('SUPER', 'FREIGHT', 'PACKAGING')")
+ @PreAuthorize("hasAnyRole('SUPER', 'FREIGHT', 'PACKAGING', 'MATERIAL')")
public ResponseEntity
> getBulkStatus() {
return ResponseEntity.ok(bulkOperationService.getStatus());
}
@PostMapping({"/upload/{type}", "/upload/{type}/"})
- @PreAuthorize("hasAnyRole('SUPER', 'FREIGHT', 'PACKAGING')")
+ @PreAuthorize("hasAnyRole('SUPER', 'FREIGHT', 'PACKAGING', 'MATERIAL')")
public ResponseEntity uploadFile(@PathVariable BulkFileType type, @BodyParam("file") MultipartFile file) {
bulkOperationService.processFileImport(type, file);
return ResponseEntity.ok().build();
}
@GetMapping({"/templates/{type}", "/templates/{type}/"})
- @PreAuthorize("hasAnyRole('SUPER', 'FREIGHT', 'PACKAGING')")
+ @PreAuthorize("hasAnyRole('SUPER', 'FREIGHT', 'PACKAGING', 'MATERIAL')")
public ResponseEntity generateTemplate(@PathVariable BulkFileType type) {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment; filename=lcc_template_" + type.name().toLowerCase() + ".xlsx");
@@ -64,7 +64,7 @@ public class BulkOperationController {
@GetMapping({"/download/{type}", "/download/{type}/"})
- @PreAuthorize("hasAnyRole('SUPER', 'FREIGHT', 'PACKAGING')")
+ @PreAuthorize("hasAnyRole('SUPER', 'FREIGHT', 'PACKAGING', 'MATERIAL')")
public ResponseEntity scheduleDownload(@PathVariable BulkFileType type) {
bulkOperationService.processFileExport(type);
return ResponseEntity.ok().build();
@@ -72,7 +72,7 @@ public class BulkOperationController {
@GetMapping({"/download/{type}/{validity_period_id}", "/download/{type}/{validity_period_id}/"})
- @PreAuthorize("hasAnyRole('SUPER', 'FREIGHT', 'PACKAGING')")
+ @PreAuthorize("hasAnyRole('SUPER', 'FREIGHT', 'PACKAGING', 'MATERIAL')")
public ResponseEntity scheduleDownload(@PathVariable BulkFileType type, @PathVariable("validity_period_id") Integer validityPeriodId) {
bulkOperationService.processFileExport(type, validityPeriodId);
return ResponseEntity.ok().build();
@@ -80,7 +80,7 @@ public class BulkOperationController {
}
@GetMapping({"/file/{processId}", "/file/{processId}/"})
- @PreAuthorize("hasAnyRole('SUPER', 'FREIGHT', 'PACKAGING')")
+ @PreAuthorize("hasAnyRole('SUPER', 'FREIGHT', 'PACKAGING', 'MATERIAL')")
public ResponseEntity download(@PathVariable("processId") Integer id) {
var op = bulkOperationService.getBulkOperation(id);
diff --git a/src/main/java/de/avatic/lcc/controller/configuration/MaterialController.java b/src/main/java/de/avatic/lcc/controller/configuration/MaterialController.java
index 542f131..c5e3b98 100644
--- a/src/main/java/de/avatic/lcc/controller/configuration/MaterialController.java
+++ b/src/main/java/de/avatic/lcc/controller/configuration/MaterialController.java
@@ -37,7 +37,7 @@ public class MaterialController {
* X-Total-Count (total elements), X-Page-Count (total pages), and X-Current-Page (current page).
*/
@GetMapping("/")
- @PreAuthorize("hasAnyRole('SUPER', 'CALCULATION', 'BASIC', 'FREIGHT', 'PACKAGING')")
+ @PreAuthorize("hasAnyRole('SUPER', 'CALCULATION', 'BASIC', 'FREIGHT', 'PACKAGING', 'MATERIAL')")
public ResponseEntity> listMaterials(
@RequestParam(defaultValue = "true") String excludeDeprecated,
@RequestParam(defaultValue = "20") @Min(1) int limit,
@@ -61,7 +61,7 @@ public class MaterialController {
* @throws RuntimeException if the material with the given ID is not found.
*/
@GetMapping("/{id}")
- @PreAuthorize("hasAnyRole('SUPER', 'CALCULATION', 'BASIC', 'FREIGHT', 'PACKAGING')")
+ @PreAuthorize("hasAnyRole('SUPER', 'CALCULATION', 'BASIC', 'FREIGHT', 'PACKAGING', 'MATERIAL')")
public ResponseEntity getMaterialDetails(@PathVariable Integer id) {
return ResponseEntity.ok(materialService.getMaterial(id));
}
diff --git a/src/main/java/de/avatic/lcc/dto/generic/LocationDTO.java b/src/main/java/de/avatic/lcc/dto/generic/LocationDTO.java
index 3ecc6ab..ae02f94 100644
--- a/src/main/java/de/avatic/lcc/dto/generic/LocationDTO.java
+++ b/src/main/java/de/avatic/lcc/dto/generic/LocationDTO.java
@@ -1,26 +1,15 @@
package de.avatic.lcc.dto.generic;
-import com.fasterxml.jackson.annotation.JsonFormat;
-
-import java.util.Objects;
-
/**
* Represents a geographical location with latitude and longitude.
* This immutable DTO (Data Transfer Object) is used to transfer location data across system layers.
+ *
+ * @param latitude The latitude of the location.
+ * Positive values indicate north and negative values indicate south.
+ * @param longitude The longitude of the location.
+ * Positive values indicate east and negative values indicate west.
*/
-public class LocationDTO {
-
- /**
- * The latitude of the location.
- * Positive values indicate north and negative values indicate south.
- */
- private final double latitude;
-
- /**
- * The longitude of the location.
- * Positive values indicate east and negative values indicate west.
- */
- private final double longitude;
+public record LocationDTO(double latitude, double longitude) {
/**
* Constructs a new {@code LocationDTO} with the specified latitude and longitude.
@@ -28,9 +17,7 @@ public class LocationDTO {
* @param latitude the latitude of the location, where north is positive and south is negative
* @param longitude the longitude of the location, where east is positive and west is negative
*/
- public LocationDTO(double latitude, double longitude) {
- this.latitude = latitude;
- this.longitude = longitude;
+ public LocationDTO {
}
/**
@@ -46,7 +33,8 @@ public class LocationDTO {
*
* @return the latitude, where positive values indicate north and negative values indicate south
*/
- public double getLatitude() {
+ @Override
+ public double latitude() {
return latitude;
}
@@ -55,7 +43,8 @@ public class LocationDTO {
*
* @return the longitude, where positive values indicate east and negative values indicate west
*/
- public double getLongitude() {
+ @Override
+ public double longitude() {
return longitude;
}
@@ -68,11 +57,6 @@ public class LocationDTO {
Double.compare(that.longitude, longitude) == 0;
}
- @Override
- public int hashCode() {
- return Objects.hash(latitude, longitude);
- }
-
@Override
public String toString() {
return "LocationDTO{" +
diff --git a/src/main/java/de/avatic/lcc/repositories/bulk/BulkOperationRepository.java b/src/main/java/de/avatic/lcc/repositories/bulk/BulkOperationRepository.java
index 1fc0201..bd91cb5 100644
--- a/src/main/java/de/avatic/lcc/repositories/bulk/BulkOperationRepository.java
+++ b/src/main/java/de/avatic/lcc/repositories/bulk/BulkOperationRepository.java
@@ -160,38 +160,32 @@ public class BulkOperationRepository {
);
}
- private static class BulkOperationRowMapper implements RowMapper {
+ private record BulkOperationRowMapper(boolean skipFile) implements RowMapper {
- private final boolean skipFile;
-
- BulkOperationRowMapper() {
- this(false);
- }
-
- BulkOperationRowMapper(boolean skipFile) {
- this.skipFile = skipFile;
- }
+ BulkOperationRowMapper() {
+ this(false);
+ }
@Override
- public BulkOperation mapRow(ResultSet rs, int rowNum) throws SQLException {
- BulkOperation operation = new BulkOperation();
- operation.setId(rs.getInt("id"));
- operation.setUserId(rs.getInt("user_id"));
- operation.setProcessingType(BulkProcessingType.valueOf(rs.getString("bulk_processing_type")));
- operation.setFileType(BulkFileType.valueOf(rs.getString("bulk_file_type")));
- operation.setProcessState(BulkOperationState.valueOf(rs.getString("state")));
+ public BulkOperation mapRow(ResultSet rs, int rowNum) throws SQLException {
+ BulkOperation operation = new BulkOperation();
+ operation.setId(rs.getInt("id"));
+ operation.setUserId(rs.getInt("user_id"));
+ operation.setProcessingType(BulkProcessingType.valueOf(rs.getString("bulk_processing_type")));
+ operation.setFileType(BulkFileType.valueOf(rs.getString("bulk_file_type")));
+ operation.setProcessState(BulkOperationState.valueOf(rs.getString("state")));
- operation.setValidityPeriodId(rs.getInt("validity_period_id"));
- if (rs.wasNull())
- operation.setValidityPeriodId(null);
+ operation.setValidityPeriodId(rs.getInt("validity_period_id"));
+ if (rs.wasNull())
+ operation.setValidityPeriodId(null);
- if (!skipFile)
- operation.setFile(rs.getBytes("file"));
- operation.setCreatedAt(rs.getTimestamp("created_at").toLocalDateTime());
- return operation;
+ if (!skipFile)
+ operation.setFile(rs.getBytes("file"));
+ operation.setCreatedAt(rs.getTimestamp("created_at").toLocalDateTime());
+ return operation;
+ }
}
- }
}
diff --git a/src/main/java/de/avatic/lcc/repositories/error/DumpRepository.java b/src/main/java/de/avatic/lcc/repositories/error/DumpRepository.java
index 9082fb5..a0d7eed 100644
--- a/src/main/java/de/avatic/lcc/repositories/error/DumpRepository.java
+++ b/src/main/java/de/avatic/lcc/repositories/error/DumpRepository.java
@@ -30,15 +30,18 @@ import java.util.Map;
@Repository
public class DumpRepository {
- @Autowired
- private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
+ private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
- @Autowired
- private JdbcTemplate jdbcTemplate;
- @Autowired
- private PremiseRepository premiseRepository;
- @Autowired
- private PremiseTransformer premiseTransformer;
+ private final JdbcTemplate jdbcTemplate;
+ private final PremiseRepository premiseRepository;
+ private final PremiseTransformer premiseTransformer;
+
+ public DumpRepository(NamedParameterJdbcTemplate namedParameterJdbcTemplate, JdbcTemplate jdbcTemplate, PremiseRepository premiseRepository, PremiseTransformer premiseTransformer) {
+ this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
+ this.jdbcTemplate = jdbcTemplate;
+ this.premiseRepository = premiseRepository;
+ this.premiseTransformer = premiseTransformer;
+ }
@Transactional(readOnly = true)
public CalculationJobDumpDTO getDump(Integer id) {
diff --git a/src/main/java/de/avatic/lcc/repositories/error/SysErrorRepository.java b/src/main/java/de/avatic/lcc/repositories/error/SysErrorRepository.java
index 94d2726..0f3ae12 100644
--- a/src/main/java/de/avatic/lcc/repositories/error/SysErrorRepository.java
+++ b/src/main/java/de/avatic/lcc/repositories/error/SysErrorRepository.java
@@ -122,7 +122,7 @@ public class SysErrorRepository {
}
// Count total elements
- String countSql = "SELECT COUNT(*) FROM sys_error e" + whereClause.toString();
+ String countSql = "SELECT COUNT(*) FROM sys_error e" + whereClause;
Integer totalElements = namedParameterJdbcTemplate.queryForObject(countSql, parameters, Integer.class);
// Build main query with pagination
@@ -130,7 +130,7 @@ public class SysErrorRepository {
SELECT e.id, e.user_id, e.title, e.code, e.message, e.pinia,
e.calculation_job_id, e.bulk_operation_id, e.type, e.created_at
FROM sys_error e
- """ + whereClause.toString() + """
+ """ + whereClause + """
ORDER BY e.created_at DESC
LIMIT :limit OFFSET :offset
""";
diff --git a/src/main/java/de/avatic/lcc/repositories/pagination/SearchQueryResult.java b/src/main/java/de/avatic/lcc/repositories/pagination/SearchQueryResult.java
index 1070b6e..ad98b49 100644
--- a/src/main/java/de/avatic/lcc/repositories/pagination/SearchQueryResult.java
+++ b/src/main/java/de/avatic/lcc/repositories/pagination/SearchQueryResult.java
@@ -14,7 +14,7 @@ import java.util.function.Function;
*/
public class SearchQueryResult {
- private List result;
+ private final List result;
private final Integer page, totalPages, totalElements, elementsPerPage;
diff --git a/src/main/java/de/avatic/lcc/repositories/premise/DestinationRepository.java b/src/main/java/de/avatic/lcc/repositories/premise/DestinationRepository.java
index 3ac7311..7b0c996 100644
--- a/src/main/java/de/avatic/lcc/repositories/premise/DestinationRepository.java
+++ b/src/main/java/de/avatic/lcc/repositories/premise/DestinationRepository.java
@@ -135,6 +135,28 @@ public class DestinationRepository {
return Optional.of(userId.getFirst());
}
+ @Transactional
+ public Map getOwnerIdsByIds(List ids) {
+ if (ids == null || ids.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ String placeholders = String.join(",", Collections.nCopies(ids.size(), "?"));
+ String query = String.format("""
+ SELECT pd.id AS pd_id, p.user_id AS user_id
+ FROM premise_destination pd
+ JOIN premise p ON pd.premise_id = p.id
+ WHERE pd.id IN (%s)""", placeholders);
+
+ return jdbcTemplate.query(query, rs -> {
+ Map result = new HashMap<>();
+ while (rs.next()) {
+ result.put(rs.getInt("pd_id"), rs.getInt("user_id"));
+ }
+ return result;
+ }, ids.toArray());
+ }
+
@Transactional
public List getByPremiseIdsAndNodeId(List premiseId, Integer nodeId, Integer userId) {
String placeholder = String.join(",", Collections.nCopies(premiseId.size(), "?"));
@@ -226,6 +248,29 @@ public class DestinationRepository {
}
}
+ @Transactional
+ public void checkOwner(List ids, Integer userId) {
+ if (ids == null || ids.isEmpty()) {
+ return;
+ }
+
+ Map ownerMap = getOwnerIdsByIds(ids);
+ List violations = new ArrayList<>();
+
+ for (Integer id : ids) {
+ Integer ownerId = ownerMap.get(id);
+
+ if (ownerId == null || !ownerId.equals(userId)) {
+ violations.add("id " + id + " (owner: " + ownerId + " user: " + userId + ")");
+ }
+ }
+
+ if (!violations.isEmpty()) {
+ throw new ForbiddenException("Access violation. Accessing destinations: " +
+ String.join(", ", violations));
+ }
+ }
+
private static class DestinationMapper implements RowMapper {
@Override
diff --git a/src/main/java/de/avatic/lcc/repositories/rates/ContainerRateRepository.java b/src/main/java/de/avatic/lcc/repositories/rates/ContainerRateRepository.java
index 55f1fb9..257e1e3 100644
--- a/src/main/java/de/avatic/lcc/repositories/rates/ContainerRateRepository.java
+++ b/src/main/java/de/avatic/lcc/repositories/rates/ContainerRateRepository.java
@@ -288,38 +288,32 @@ public class ContainerRateRepository {
}
- private static class ContainerRateMapper implements RowMapper {
-
- private final boolean fetchCountryIds;
-
- public ContainerRateMapper(boolean fetchCountryIds) {
- this.fetchCountryIds = fetchCountryIds;
- }
+ private record ContainerRateMapper(boolean fetchCountryIds) implements RowMapper {
public ContainerRateMapper() {
- this(false);
- }
-
- @Override
- public ContainerRate mapRow(ResultSet rs, int rowNum) throws SQLException {
- var entity = new ContainerRate();
-
- entity.setId(rs.getInt("id"));
- entity.setValidityPeriodId(rs.getInt("validity_period_id"));
- entity.setFromNodeId(rs.getInt("from_node_id"));
- entity.setToNodeId(rs.getInt("to_node_id"));
- entity.setType(TransportType.valueOf(rs.getString("container_rate_type")));
- entity.setLeadTime(rs.getInt("lead_time"));
- entity.setRateFeu(rs.getBigDecimal("rate_feu"));
- entity.setRateTeu(rs.getBigDecimal("rate_teu"));
- entity.setRateHc(rs.getBigDecimal("rate_hc"));
-
- if (fetchCountryIds) {
- entity.setToCountryId(rs.getInt("to_country_id"));
- entity.setFromCountryId(rs.getInt("from_country_id"));
+ this(false);
}
- return entity;
+ @Override
+ public ContainerRate mapRow(ResultSet rs, int rowNum) throws SQLException {
+ var entity = new ContainerRate();
+
+ entity.setId(rs.getInt("id"));
+ entity.setValidityPeriodId(rs.getInt("validity_period_id"));
+ entity.setFromNodeId(rs.getInt("from_node_id"));
+ entity.setToNodeId(rs.getInt("to_node_id"));
+ entity.setType(TransportType.valueOf(rs.getString("container_rate_type")));
+ entity.setLeadTime(rs.getInt("lead_time"));
+ entity.setRateFeu(rs.getBigDecimal("rate_feu"));
+ entity.setRateTeu(rs.getBigDecimal("rate_teu"));
+ entity.setRateHc(rs.getBigDecimal("rate_hc"));
+
+ if (fetchCountryIds) {
+ entity.setToCountryId(rs.getInt("to_country_id"));
+ entity.setFromCountryId(rs.getInt("from_country_id"));
+ }
+
+ return entity;
+ }
}
- }
}
diff --git a/src/main/java/de/avatic/lcc/service/access/DestinationService.java b/src/main/java/de/avatic/lcc/service/access/DestinationService.java
index 60f10f3..384978f 100644
--- a/src/main/java/de/avatic/lcc/service/access/DestinationService.java
+++ b/src/main/java/de/avatic/lcc/service/access/DestinationService.java
@@ -15,7 +15,6 @@ import de.avatic.lcc.repositories.users.UserNodeRepository;
import de.avatic.lcc.service.calculation.RoutingService;
import de.avatic.lcc.service.transformer.premise.DestinationTransformer;
import de.avatic.lcc.service.users.AuthorizationService;
-import de.avatic.lcc.util.exception.base.ForbiddenException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -34,15 +33,12 @@ public class DestinationService {
private final RouteSectionRepository routeSectionRepository;
private final RouteNodeRepository routeNodeRepository;
private final RoutingService routingService;
- ;
private final NodeRepository nodeRepository;
private final PremiseRepository premiseRepository;
private final UserNodeRepository userNodeRepository;
- private final PropertyRepository propertyRepository;
- private final PropertyService propertyService;
private final AuthorizationService authorizationService;
- public DestinationService(DestinationRepository destinationRepository, DestinationTransformer destinationTransformer, RouteRepository routeRepository, RouteSectionRepository routeSectionRepository, RouteNodeRepository routeNodeRepository, RoutingService routingService, NodeRepository nodeRepository, PremiseRepository premiseRepository, UserNodeRepository userNodeRepository, PropertyRepository propertyRepository, PropertyService propertyService, AuthorizationService authorizationService) {
+ public DestinationService(DestinationRepository destinationRepository, DestinationTransformer destinationTransformer, RouteRepository routeRepository, RouteSectionRepository routeSectionRepository, RouteNodeRepository routeNodeRepository, RoutingService routingService, NodeRepository nodeRepository, PremiseRepository premiseRepository, UserNodeRepository userNodeRepository, AuthorizationService authorizationService) {
this.destinationRepository = destinationRepository;
this.destinationTransformer = destinationTransformer;
this.routeRepository = routeRepository;
@@ -52,16 +48,16 @@ public class DestinationService {
this.nodeRepository = nodeRepository;
this.premiseRepository = premiseRepository;
this.userNodeRepository = userNodeRepository;
- this.propertyRepository = propertyRepository;
- this.propertyService = propertyService;
this.authorizationService = authorizationService;
}
@Transactional
public Map> setDestination(DestinationSetDTO dto) {
- //TODO fix user authorization
- var userId = 1;
- //dto.getPremiseId().forEach(id -> destinationRepository.checkOwner(id, userId));
+ var admin = authorizationService.isSuper();
+ Integer userId = authorizationService.getUserId();
+
+ if (!admin)
+ destinationRepository.checkOwner(dto.getPremiseId(), userId);
deleteAllDestinationsByPremiseId(dto.getPremiseId(), false);
@@ -80,7 +76,6 @@ public class DestinationService {
}
-
private List createDestination(List premisesToProcess, Integer destinationNodeId, Integer annualAmount, Number repackingCost, Number disposalCost, Number handlingCost, Map> routes) {
Node destinationNode = nodeRepository.getById(destinationNodeId).orElseThrow();
@@ -141,17 +136,22 @@ public class DestinationService {
}
public DestinationDTO getDestination(Integer id) {
- //todo check authorization
+ var admin = authorizationService.isSuper();
+ var userId = authorizationService.getUserId();
+
+ if (!admin)
+ destinationRepository.checkOwner(id, userId);
return destinationTransformer.toDestinationDTO(destinationRepository.getById(id).orElseThrow());
}
@Transactional
public void updateDestination(Integer id, DestinationUpdateDTO destinationUpdateDTO) {
- //todo check authorization
+ var admin = authorizationService.isSuper();
Integer userId = authorizationService.getUserId();
- destinationRepository.checkOwner(id, userId);
+ if (!admin)
+ destinationRepository.checkOwner(id, userId);
var selectedRouteId = destinationUpdateDTO.getRouteSelectedId();
@@ -159,7 +159,6 @@ public class DestinationService {
routeRepository.updateSelectedByDestinationId(id, selectedRouteId);
}
-
destinationRepository.update(id,
destinationUpdateDTO.getAnnualAmount(),
destinationUpdateDTO.getRepackingCost() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getRepackingCost().doubleValue()),
@@ -177,21 +176,21 @@ public class DestinationService {
Map nodes = new HashMap<>();
Map userNodes = new HashMap<>();
- for(var premise : premisses) {
- for(var destinationId : destinationIds) {
+ for (var premise : premisses) {
+ for (var destinationId : destinationIds) {
boolean isUserSupplierNode = (premise.getSupplierNodeId() == null);
var ids = new RouteIds(isUserSupplierNode ? premise.getUserSupplierNodeId() : premise.getSupplierNodeId(), destinationId, isUserSupplierNode);
- if(routes.containsKey(ids)) continue;
+ if (routes.containsKey(ids)) continue;
- if(!nodes.containsKey(destinationId)) {
+ if (!nodes.containsKey(destinationId)) {
nodes.put(destinationId, nodeRepository.getById(destinationId).orElseThrow());
}
- if(!isUserSupplierNode && !nodes.containsKey(premise.getSupplierNodeId())) {
+ if (!isUserSupplierNode && !nodes.containsKey(premise.getSupplierNodeId())) {
nodes.put(premise.getSupplierNodeId(), nodeRepository.getById(premise.getSupplierNodeId()).orElseThrow());
}
- if(isUserSupplierNode && !userNodes.containsKey(premise.getUserSupplierNodeId())) {
+ if (isUserSupplierNode && !userNodes.containsKey(premise.getUserSupplierNodeId())) {
userNodes.put(premise.getUserSupplierNodeId(), userNodeRepository.getById(premise.getUserSupplierNodeId()).orElseThrow());
}
@@ -205,7 +204,6 @@ public class DestinationService {
@Transactional
public void saveRoute(List routeObjs, Integer destinationId) {
-
for (var routeObj : routeObjs) {
boolean first = true;
Integer fromNodeId = null;
@@ -238,44 +236,12 @@ public class DestinationService {
}
}
- private record RouteIds(Integer fromNodeId, Integer toNodeId, boolean isUserSupplierNode) {}
-
-
@Transactional
public void findRouteAndSave(Integer destinationId, Node destination, Node supplier, boolean isUserSupplierNode) {
var routeObjs = routingService.findRoutes(destination, supplier, isUserSupplierNode);
- for (var routeObj : routeObjs) {
- boolean first = true;
- Integer fromNodeId = null;
-
- var premiseRoute = routeObj.getRoute();
- premiseRoute.setDestinationId(destinationId);
- int routeId = routeRepository.insert(premiseRoute);
-
-
- for (RouteSectionInformation section : routeObj.getSections()) {
-
- if (first) {
- fromNodeId = routeNodeRepository.insert(section.getFromNode());
- first = false;
- }
-
- var toNode = section.getToNode();
- Integer toNodeId = routeNodeRepository.insert(toNode);
-
- var premiseRouteSection = section.getSection();
-
- premiseRouteSection.setRouteId(routeId);
- premiseRouteSection.setFromRouteNodeId(fromNodeId);
- premiseRouteSection.setToRouteNodeId(toNodeId);
-
- routeSectionRepository.insert(premiseRouteSection);
-
- fromNodeId = toNodeId;
- }
- }
+ saveRoute(routeObjs, destinationId);
}
@Transactional
@@ -289,31 +255,26 @@ public class DestinationService {
destinations.forEach(destination -> deleteDestinationById(destination.getId(), deleteRoutesOnly));
}
-
@Transactional
public void deleteDestinationById(Integer id, boolean deleteRoutesOnly) {
- //todo check authorization
+ var admin = authorizationService.isSuper();
Integer userId = authorizationService.getUserId();
- Optional ownerId = destinationRepository.getOwnerIdById(id);
- if (ownerId.isPresent() && ownerId.get().equals(userId)) {
- List routes = routeRepository.getByDestinationId(id);
+ if (!admin)
+ destinationRepository.checkOwner(id, userId);
- for (var route : routes) {
- List sections = routeSectionRepository.getByRouteId(route.getId());
- routeSectionRepository.deleteAllById(sections.stream().map(RouteSection::getId).toList());
- routeNodeRepository.deleteAllById(sections.stream().flatMap(section -> Stream.of(section.getFromRouteNodeId(), section.getToRouteNodeId())).toList());
- }
+ List routes = routeRepository.getByDestinationId(id);
- routeRepository.deleteAllById(routes.stream().map(Route::getId).toList());
-
- if (!deleteRoutesOnly)
- destinationRepository.deleteById(id);
-
- return;
+ for (var route : routes) {
+ List sections = routeSectionRepository.getByRouteId(route.getId());
+ routeSectionRepository.deleteAllById(sections.stream().map(RouteSection::getId).toList());
+ routeNodeRepository.deleteAllById(sections.stream().flatMap(section -> Stream.of(section.getFromRouteNodeId(), section.getToRouteNodeId())).toList());
}
- throw new ForbiddenException("Not authorized to delete destination with id " + id);
+ routeRepository.deleteAllById(routes.stream().map(Route::getId).toList());
+
+ if (!deleteRoutesOnly)
+ destinationRepository.deleteById(id);
}
@@ -349,5 +310,8 @@ public class DestinationService {
}
}
+ private record RouteIds(Integer fromNodeId, Integer toNodeId, boolean isUserSupplierNode) {
+ }
+
}
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 3b6b037..ca9f5a0 100644
--- a/src/main/java/de/avatic/lcc/service/access/PremisesService.java
+++ b/src/main/java/de/avatic/lcc/service/access/PremisesService.java
@@ -90,20 +90,25 @@ public class PremisesService {
@Transactional(readOnly = true)
public List getPremises(List premiseIds) {
+ var admin = authorizationService.isSuper();
var userId = authorizationService.getUserId();
- premiseRepository.checkOwner(premiseIds, userId);
+
+ if (!admin)
+ premiseRepository.checkOwner(premiseIds, userId);
return premiseRepository.getPremisesById(premiseIds).stream().filter(p -> p.getState().equals(PremiseState.DRAFT)).map(premiseTransformer::toPremiseDetailDTO).toList();
}
public void startCalculation(List premises) {
-
+ var admin = authorizationService.isSuper();
var userId = authorizationService.getUserId();
- premiseRepository.checkOwner(premises, userId);
+
+ if (!admin)
+ premiseRepository.checkOwner(premises, userId);
var validSetId = propertySetRepository.getValidSetId();
- var validPeriodId = validityPeriodRepository.getValidPeriodId().orElseThrow(() -> new InternalErrorException("no valid period found that is VALID"));
+ var validPeriodId = validityPeriodRepository.getValidPeriodId().orElseThrow(() -> new InternalErrorException("no set of transport rates found that is VALID"));
premises.forEach(premiseId -> preCalculationCheckService.doPrecheck(premiseId, validSetId, validPeriodId));
@@ -183,12 +188,13 @@ public class PremisesService {
return calculationStatusService.getCalculationStatus(processId);
}
- @Transactional
- public void updatePackaging(PackagingUpdateDTO packagingDTO) {
- //TODO check values. and return errors if needed
+ public void updatePackaging(PackagingUpdateDTO packagingDTO) {
+ var admin = authorizationService.isSuper();
var userId = authorizationService.getUserId();
- premiseRepository.checkOwner(packagingDTO.getPremiseIds(), userId);
+
+ if (!admin)
+ premiseRepository.checkOwner(packagingDTO.getPremiseIds(), userId);
var dimensions = packagingDTO.getDimensions() == null ? null : dimensionTransformer.toDimensionEntity(packagingDTO.getDimensions());
@@ -197,9 +203,11 @@ public class PremisesService {
}
public void updateMaterial(MaterialUpdateDTO materialUpdateDTO) {
- //TODO check values. and return errors if needed
+ var admin = authorizationService.isSuper();
var userId = authorizationService.getUserId();
- premiseRepository.checkOwner(materialUpdateDTO.getPremiseIds(), userId);
+
+ if (!admin)
+ premiseRepository.checkOwner(materialUpdateDTO.getPremiseIds(), userId);
var tariffRate = materialUpdateDTO.getTariffRate() == null ? null : BigDecimal.valueOf(materialUpdateDTO.getTariffRate().doubleValue());
premiseRepository.updateMaterial(materialUpdateDTO.getPremiseIds(), materialUpdateDTO.getHsCode(), tariffRate);
@@ -207,9 +215,11 @@ public class PremisesService {
}
public void updatePrice(PriceUpdateDTO priceUpdateDTO) {
- //TODO check values. and return errors if needed
+ var admin = authorizationService.isSuper();
var userId = authorizationService.getUserId();
- premiseRepository.checkOwner(priceUpdateDTO.getPremiseIds(), userId);
+
+ if (!admin)
+ premiseRepository.checkOwner(priceUpdateDTO.getPremiseIds(), userId);
var price = priceUpdateDTO.getPrice() == null ? null : BigDecimal.valueOf(priceUpdateDTO.getPrice().doubleValue());
var overseaShare = priceUpdateDTO.getOverseaShare() == null ? null : BigDecimal.valueOf(priceUpdateDTO.getOverseaShare().doubleValue());
@@ -219,8 +229,12 @@ public class PremisesService {
@Transactional
public void delete(List premiseIds) {
+ var admin = authorizationService.isSuper();
var userId = authorizationService.getUserId();
- premiseRepository.checkOwner(premiseIds, userId);
+
+ if (!admin)
+ premiseRepository.checkOwner(premiseIds, userId);
+
// only delete drafts.
var toBeDeleted = premiseRepository.getPremisesById(premiseIds).stream().filter(p -> p.getState().equals(PremiseState.DRAFT)).map(Premise::getId).toList();
@@ -232,9 +246,14 @@ public class PremisesService {
}
+ @Transactional
public void archive(List premiseIds) {
+ var admin = authorizationService.isSuper();
var userId = authorizationService.getUserId();
- premiseRepository.checkOwner(premiseIds, userId);
+
+ if (!admin)
+ premiseRepository.checkOwner(premiseIds, userId);
+
// only archive completed.
var premisses = premiseRepository.getPremisesById(premiseIds);
@@ -262,9 +281,14 @@ public class PremisesService {
return resolveDTO;
}
+ @Transactional
public List duplicate(List premissIds) {
+ var admin = authorizationService.isSuper();
var userId = authorizationService.getUserId();
- premiseRepository.checkOwner(premissIds, userId);
+
+ if (!admin)
+ premiseRepository.checkOwner(premissIds, userId);
+
var newIds = new ArrayList();
premissIds.forEach(id -> {
diff --git a/src/main/java/de/avatic/lcc/service/access/PropertyValidationService.java b/src/main/java/de/avatic/lcc/service/access/PropertyValidationService.java
index 5273a60..49f626f 100644
--- a/src/main/java/de/avatic/lcc/service/access/PropertyValidationService.java
+++ b/src/main/java/de/avatic/lcc/service/access/PropertyValidationService.java
@@ -172,7 +172,7 @@ public class PropertyValidationService {
public void evaluate(String value) {
if (operator == CompareOperator.ENUM) {
if (!((List) this.value).contains(value)) {
- throw new PropertyValidationException(propertyId, operator.getIdentifier(), ((List) this.value).toString(), value);
+ throw new PropertyValidationException(propertyId, operator.getIdentifier(), this.value.toString(), value);
}
} else {
try {
diff --git a/src/main/java/de/avatic/lcc/service/access/UserNodeService.java b/src/main/java/de/avatic/lcc/service/access/UserNodeService.java
index 85b7596..7ef8b3a 100644
--- a/src/main/java/de/avatic/lcc/service/access/UserNodeService.java
+++ b/src/main/java/de/avatic/lcc/service/access/UserNodeService.java
@@ -53,8 +53,8 @@ public class UserNodeService {
node.setName(dto.getName());
node.setAddress(dto.getAddress());
- node.setGeoLng(BigDecimal.valueOf(dto.getLocation().getLongitude()));
- node.setGeoLat(BigDecimal.valueOf(dto.getLocation().getLatitude()));
+ node.setGeoLng(BigDecimal.valueOf(dto.getLocation().longitude()));
+ node.setGeoLat(BigDecimal.valueOf(dto.getLocation().latitude()));
node.setDestination(false);
node.setSource(true);
node.setIntermediate(false);
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 acecd03..9acb10b 100644
--- a/src/main/java/de/avatic/lcc/service/bulk/BulkImportService.java
+++ b/src/main/java/de/avatic/lcc/service/bulk/BulkImportService.java
@@ -2,11 +2,9 @@ package de.avatic.lcc.service.bulk;
import de.avatic.lcc.model.bulk.BulkFileTypes;
import de.avatic.lcc.model.bulk.BulkOperation;
-import de.avatic.lcc.repositories.NodeRepository;
import de.avatic.lcc.service.api.BatchGeoApiService;
import de.avatic.lcc.service.bulk.bulkImport.*;
import de.avatic.lcc.service.excelMapper.*;
-import de.avatic.lcc.service.transformer.generic.NodeTransformer;
import de.avatic.lcc.util.exception.internalerror.ExcelValidationError;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
@@ -26,8 +24,7 @@ public class BulkImportService {
private final MaterialExcelMapper materialExcelMapper;
private final PackagingExcelMapper packagingExcelMapper;
private final NodeExcelMapper nodeExcelMapper;
- private final NodeRepository nodeRepository;
- private final NodeTransformer nodeTransformer;
+
private final NodeBulkImportService nodeBulkImportService;
private final PackagingBulkImportService packagingBulkImportService;
private final MaterialBulkImportService materialBulkImportService;
@@ -35,14 +32,12 @@ public class BulkImportService {
private final ContainerRateImportService containerRateImportService;
private final BatchGeoApiService batchGeoApiService;
- public BulkImportService(MatrixRateExcelMapper matrixRateExcelMapper, ContainerRateExcelMapper containerRateExcelMapper, MaterialExcelMapper materialExcelMapper, PackagingExcelMapper packagingExcelMapper, NodeExcelMapper nodeExcelMapper, NodeRepository nodeRepository, NodeTransformer nodeTransformer, NodeBulkImportService nodeBulkImportService, PackagingBulkImportService packagingBulkImportService, MaterialBulkImportService materialBulkImportService, MatrixRateImportService matrixRateImportService, ContainerRateImportService containerRateImportService, BatchGeoApiService batchGeoApiService) {
+ public BulkImportService(MatrixRateExcelMapper matrixRateExcelMapper, ContainerRateExcelMapper containerRateExcelMapper, MaterialExcelMapper materialExcelMapper, PackagingExcelMapper packagingExcelMapper, NodeExcelMapper nodeExcelMapper, NodeBulkImportService nodeBulkImportService, PackagingBulkImportService packagingBulkImportService, MaterialBulkImportService materialBulkImportService, MatrixRateImportService matrixRateImportService, ContainerRateImportService containerRateImportService, BatchGeoApiService batchGeoApiService) {
this.matrixRateExcelMapper = matrixRateExcelMapper;
this.containerRateExcelMapper = containerRateExcelMapper;
this.materialExcelMapper = materialExcelMapper;
this.packagingExcelMapper = packagingExcelMapper;
this.nodeExcelMapper = nodeExcelMapper;
- this.nodeRepository = nodeRepository;
- this.nodeTransformer = nodeTransformer;
this.nodeBulkImportService = nodeBulkImportService;
this.packagingBulkImportService = packagingBulkImportService;
this.materialBulkImportService = materialBulkImportService;
diff --git a/src/main/java/de/avatic/lcc/service/bulk/BulkOperationService.java b/src/main/java/de/avatic/lcc/service/bulk/BulkOperationService.java
index 756e4b8..7aaff8a 100644
--- a/src/main/java/de/avatic/lcc/service/bulk/BulkOperationService.java
+++ b/src/main/java/de/avatic/lcc/service/bulk/BulkOperationService.java
@@ -10,7 +10,9 @@ import de.avatic.lcc.repositories.rates.ValidityPeriodRepository;
import de.avatic.lcc.service.transformer.bulk.BulkOperationTransformer;
import de.avatic.lcc.service.users.AuthorizationService;
import de.avatic.lcc.util.exception.badrequest.FileFormatNotSupportedException;
+import de.avatic.lcc.util.exception.base.ForbiddenException;
import de.avatic.lcc.util.exception.base.InternalErrorException;
+import de.avatic.lcc.util.exception.internalerror.ExcelValidationError;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@@ -39,6 +41,8 @@ public class BulkOperationService {
public void processFileImport(BulkFileType fileType, MultipartFile file) {
var userId = authorizationService.getUserId();
+ checkAuthorized(fileType);
+
String contentType = file.getContentType();
if (!"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet".equals(contentType) &&
@@ -62,10 +66,23 @@ public class BulkOperationService {
} catch (IOException e) {
- throw new RuntimeException(e); //TODO throw a nice exception
+ throw new ExcelValidationError("Unable to read uploaded file.");
}
}
+ private void checkAuthorized(BulkFileType fileType) {
+
+ if( switch (fileType) {
+ case CONTAINER_RATE, COUNTRY_MATRIX -> authorizationService.hasAnyRole("SUPER", "FREIGHT");
+ case MATERIAL -> authorizationService.hasAnyRole("SUPER", "MATERIAL");
+ case PACKAGING -> authorizationService.hasAnyRole("SUPER", "PACKAGING");
+ case NODE -> authorizationService.hasAnyRole("SUPER");
+ }) {
+ throw new ForbiddenException("You are not authorized to perform this operation");
+ }
+
+ }
+
public void processFileExport(BulkFileType type, Integer validityPeriodId) {
var userId = authorizationService.getUserId();
diff --git a/src/main/java/de/avatic/lcc/service/bulk/TemplateExportService.java b/src/main/java/de/avatic/lcc/service/bulk/TemplateExportService.java
index 89883ed..e5159f4 100644
--- a/src/main/java/de/avatic/lcc/service/bulk/TemplateExportService.java
+++ b/src/main/java/de/avatic/lcc/service/bulk/TemplateExportService.java
@@ -49,7 +49,7 @@ public class TemplateExportService {
public InputStreamSource generateTemplate(BulkFileType bulkFileType) {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- Workbook workbook = new XSSFWorkbook();) {
+ Workbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet(BulkFileTypes.valueOf(bulkFileType.name()).getSheetName());
diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ContainerCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ContainerCalculationService.java
index b3ce8fa..3ef74ca 100644
--- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ContainerCalculationService.java
+++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/ContainerCalculationService.java
@@ -229,7 +229,7 @@ public class ContainerCalculationService {
}
- private static enum SolutionType {
+ private enum SolutionType {
HORIZONTAL, VERTICAL
}
@@ -289,30 +289,22 @@ public class ContainerCalculationService {
}
@Renderer(text = "getFullDebugInfo()")
- private static class Block {
-
- private final PackagingDimension dimension;
- private final BlockType type;
-
- public Block(PackagingDimension dimension, BlockType type) {
- this.dimension = dimension;
- this.type = type;
- }
+ private record Block(PackagingDimension dimension, BlockType type) {
public int calculateBlockCount(int containerColumnLength) {
- return containerColumnLength / type.getBlockColumnLength(dimension);
- }
+ return containerColumnLength / type.getBlockColumnLength(dimension);
+ }
- public int getResultValue() {
- return type.getResultValue();
- }
+ public int getResultValue() {
+ return type.getResultValue();
+ }
- public int getBlockWidth() {
- return type.getColumnWidth(dimension);
- }
+ public int getBlockWidth() {
+ return type.getColumnWidth(dimension);
+ }
- public String getFullDebugInfo() {
- return "BLOCK %s W%s L%s H%s".formatted(type.name(), type.getColumnWidth(dimension), type.getBlockColumnLength(dimension), dimension.getHeight());
+ public String getFullDebugInfo() {
+ return "BLOCK %s W%s L%s H%s".formatted(type.name(), type.getColumnWidth(dimension), type.getBlockColumnLength(dimension), dimension.getHeight());
+ }
}
- }
}
diff --git a/src/main/java/de/avatic/lcc/service/precalculation/PreCalculationCheckService.java b/src/main/java/de/avatic/lcc/service/precalculation/PreCalculationCheckService.java
index 72b67aa..21fe638 100644
--- a/src/main/java/de/avatic/lcc/service/precalculation/PreCalculationCheckService.java
+++ b/src/main/java/de/avatic/lcc/service/precalculation/PreCalculationCheckService.java
@@ -14,6 +14,7 @@ import de.avatic.lcc.model.db.rates.ValidityPeriodState;
import de.avatic.lcc.model.db.utils.WeightUnit;
import de.avatic.lcc.repositories.NodeRepository;
import de.avatic.lcc.repositories.premise.*;
+import de.avatic.lcc.repositories.properties.PropertyRepository;
import de.avatic.lcc.repositories.properties.PropertySetRepository;
import de.avatic.lcc.repositories.rates.ContainerRateRepository;
import de.avatic.lcc.repositories.rates.MatrixRateRepository;
@@ -25,9 +26,12 @@ import de.avatic.lcc.util.exception.internalerror.PremiseValidationError;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
+import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalUnit;
import java.util.List;
import java.util.Optional;
+import java.util.concurrent.TimeUnit;
@Service
public class PreCalculationCheckService {
@@ -48,8 +52,9 @@ public class PreCalculationCheckService {
private final ContainerRateRepository containerRateRepository;
private final ValidityPeriodRepository validityPeriodRepository;
private final PropertySetRepository propertySetRepository;
+ private final PropertyRepository propertyRepository;
- public PreCalculationCheckService(PremiseRepository premiseRepository, CustomApiService customApiService, DestinationRepository destinationRepository, RouteRepository routeRepository, NodeRepository nodeRepository, DimensionTransformer dimensionTransformer, PropertyService propertyService, RouteSectionRepository routeSectionRepository, RouteNodeRepository routeNodeRepository, MatrixRateRepository matrixRateRepository, ContainerRateRepository containerRateRepository, ValidityPeriodRepository validityPeriodRepository, PropertySetRepository propertySetRepository) {
+ public PreCalculationCheckService(PremiseRepository premiseRepository, CustomApiService customApiService, DestinationRepository destinationRepository, RouteRepository routeRepository, NodeRepository nodeRepository, DimensionTransformer dimensionTransformer, PropertyService propertyService, RouteSectionRepository routeSectionRepository, RouteNodeRepository routeNodeRepository, MatrixRateRepository matrixRateRepository, ContainerRateRepository containerRateRepository, ValidityPeriodRepository validityPeriodRepository, PropertySetRepository propertySetRepository, PropertyRepository propertyRepository) {
this.premiseRepository = premiseRepository;
this.customApiService = customApiService;
this.destinationRepository = destinationRepository;
@@ -64,6 +69,7 @@ public class PreCalculationCheckService {
this.containerRateRepository = containerRateRepository;
this.validityPeriodRepository = validityPeriodRepository;
this.propertySetRepository = propertySetRepository;
+ this.propertyRepository = propertyRepository;
}
public void doPrecheck(Integer premiseId, Integer setId, Integer periodId) {
@@ -125,17 +131,30 @@ public class PreCalculationCheckService {
private void periodCheck(ValidityPeriod period, PropertySet set) {
+
if(set == null)
- throw new PremiseValidationError("There are no system properties for the given date.");
+ throw new PremiseValidationError("There are no system properties for the given date. Please contact your administrator.");
if(period == null)
- throw new PremiseValidationError("There are no rates for the given date.");
+ throw new PremiseValidationError("There are no rates for the given date. Please contact your administrator.");
if(ValidityPeriodState.VALID != period.getState() && ValidityPeriodState.EXPIRED != period.getState())
- throw new PremiseValidationError("There are no valid rates for the given date.");
+ throw new PremiseValidationError("There are no valid rates for the given date. Please contact your administrator.");
if(ValidityPeriodState.VALID != set.getState() && ValidityPeriodState.EXPIRED != period.getState())
- throw new PremiseValidationError("There are no valid system properties for the given date.");
+ throw new PremiseValidationError("There are no valid system properties for the given date. Please contact your administrator.");
+
+ //TODO: sicherstellen, dass die valid days für den zeitpunkt galten zu dem die valid period galt (wenn rückwirkend gerechnet wird)
+ var validDays = propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.VALID_DAYS, set.getId());
+ var renewals = period.getRenewals();
+
+ if(validDays.isEmpty())
+ throw new PremiseValidationError("There are no valid days property. Please contact your administrator");
+
+ var validDaysInt = Integer.parseInt(validDays.get().getCurrentValue());
+
+ if(!period.getStartDate().plusDays((long) validDaysInt * renewals).isAfter(LocalDateTime.now()))
+ throw new PremiseValidationError("There are no valid rates for the given date. Please contact your administrator.");
}
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
index 3450b83..af370df 100644
--- a/src/main/java/de/avatic/lcc/service/transformer/report/ReportTransformer.java
+++ b/src/main/java/de/avatic/lcc/service/transformer/report/ReportTransformer.java
@@ -130,8 +130,6 @@ public class ReportTransformer {
return new TimePeriod(startDate, endDate);
}
- ;
-
private WeightedTotalCosts getWeightedTotalCosts(Map> sectionsMap) {
BigDecimal totalPreRunCost = BigDecimal.ZERO;
diff --git a/src/main/java/de/avatic/lcc/service/users/AuthorizationService.java b/src/main/java/de/avatic/lcc/service/users/AuthorizationService.java
index 5a0e429..58f1cfe 100644
--- a/src/main/java/de/avatic/lcc/service/users/AuthorizationService.java
+++ b/src/main/java/de/avatic/lcc/service/users/AuthorizationService.java
@@ -9,6 +9,8 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
+import java.util.Arrays;
+
@Service
public class AuthorizationService {
@@ -35,9 +37,22 @@ public class AuthorizationService {
}
public boolean isSuper() {
+ return hasRole("SUPER");
+ }
+
+ public boolean hasAnyRole(String... roles) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth.getPrincipal() instanceof LccOidcUser user) {
- return user.getAuthorities().stream().anyMatch(authority -> authority.getAuthority().equals("ROLE_SUPER"));
+ return user.getAuthorities().stream().anyMatch(authority -> Arrays.asList(roles).contains(authority.getAuthority().replace("ROLE_", "")));
+ }
+
+ throw new ForbiddenException("No user found");
+ }
+
+ public boolean hasRole(String role) {
+ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+ if (auth.getPrincipal() instanceof LccOidcUser user) {
+ return user.getAuthorities().stream().anyMatch(authority -> authority.getAuthority().equals("ROLE_" + role.toUpperCase()));
}
throw new ForbiddenException("No user found");
diff --git a/src/main/java/de/avatic/lcc/service/users/UserService.java b/src/main/java/de/avatic/lcc/service/users/UserService.java
index 2adedd2..002a04f 100644
--- a/src/main/java/de/avatic/lcc/service/users/UserService.java
+++ b/src/main/java/de/avatic/lcc/service/users/UserService.java
@@ -49,8 +49,7 @@ public class UserService {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//todo make a service. and simulate user rights in dev profile.
- if (authentication != null && authentication.getPrincipal() instanceof LccOidcUser) {
- LccOidcUser oidcUser = (LccOidcUser) authentication.getPrincipal();
+ if (authentication != null && authentication.getPrincipal() instanceof LccOidcUser oidcUser) {
return oidcUser.getAuthorities().stream().anyMatch(authority -> authority.getAuthority().equals("ROLE_SUPER"));
}
diff --git a/src/main/resources/db/migration/V9__Material.sql b/src/main/resources/db/migration/V10__Material.sql
similarity index 100%
rename from src/main/resources/db/migration/V9__Material.sql
rename to src/main/resources/db/migration/V10__Material.sql
diff --git a/src/main/resources/db/migration/V9__Groups.sql b/src/main/resources/db/migration/V9__Groups.sql
new file mode 100644
index 0000000..3332df8
--- /dev/null
+++ b/src/main/resources/db/migration/V9__Groups.sql
@@ -0,0 +1,20 @@
+INSERT INTO sys_group(group_name, group_description)
+VALUES ('none', 'no rights');
+INSERT INTO sys_group(group_name, group_description)
+VALUES ('basic', 'Login, generate reports');
+INSERT INTO sys_group(group_name, group_description)
+VALUES ('calculation', 'Login, generate reports, do calculations');
+INSERT INTO sys_group(group_name, group_description)
+VALUES ('freight', 'Login, generate reports, edit freight rates');
+INSERT INTO sys_group(group_name, group_description)
+VALUES ('packaging', 'Login, generate reports, edit packaging data');
+INSERT INTO sys_group(group_name, group_description)
+VALUES ('material', 'Login, generate reports, edit material data');
+INSERT INTO sys_group(group_name, group_description)
+VALUES ('super',
+ 'Login, generate reports, do calculations, edit freight rates, edit packaging data');
+INSERT INTO sys_group(group_name, group_description)
+VALUES ('service', 'Register API Tokens');
+INSERT INTO sys_group(group_name, group_description)
+VALUES ('right-management',
+ 'Add/Remove users, groups, etc.');
\ No newline at end of file
diff --git a/src/test/resources/master_data/users.sql b/src/test/resources/master_data/users.sql
index 7589c08..57f3f28 100644
--- a/src/test/resources/master_data/users.sql
+++ b/src/test/resources/master_data/users.sql
@@ -18,6 +18,8 @@ VALUES ('freight', 'Login, generate reports, edit freight rates');
INSERT INTO sys_group(group_name, group_description)
VALUES ('packaging', 'Login, generate reports, edit packaging data');
INSERT INTO sys_group(group_name, group_description)
+VALUES ('material', 'Login, generate reports, edit material data');
+INSERT INTO sys_group(group_name, group_description)
VALUES ('super',
'Login, generate reports, do calculations, edit freight rates, edit packaging data');
INSERT INTO sys_group(group_name, group_description)