Refined distance calculations and enhanced reporting layout:
- **Backend**: Adjusted `getDistance` implementation to retrieve distances via `DistanceApiService`, with fallback to `fast` algorithm. Updated `DistanceService` to utilize API responses for more accurate calculations. Enhanced `ExcelReportingService` handling for main run containers and mixed premises logic. - **Frontend**: Improved `Report.vue` structure for better readability and modularity. Added logic to conditionally display container details based on the main run's existence.
This commit is contained in:
parent
f7efc9eb81
commit
be1ef5091b
14 changed files with 297 additions and 254 deletions
|
|
@ -1,7 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="report-container">
|
<div class="report-container">
|
||||||
<div class="box-gap">
|
<div class="box-gap">
|
||||||
<div class="report-header">{{ shorten(report.supplier.name, 35) }}</div></div>
|
<div class="report-header">{{ shorten(report.supplier.name, 35) }}</div>
|
||||||
|
</div>
|
||||||
<div class="report-chart">
|
<div class="report-chart">
|
||||||
<report-chart
|
<report-chart
|
||||||
title=""
|
title=""
|
||||||
|
|
@ -14,7 +15,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="box-gap">
|
<div class="box-gap">
|
||||||
<collapsible-box :is-collapsable="false" variant="border" title="Overview" size="m" :stretch-content="true" >
|
<collapsible-box :is-collapsable="false" variant="border" title="Overview" size="m" :stretch-content="true">
|
||||||
<div class="report-content-container--2-col">
|
<div class="report-content-container--2-col">
|
||||||
|
|
||||||
<div class="report-content-row">
|
<div class="report-content-row">
|
||||||
|
|
@ -92,7 +93,10 @@
|
||||||
<div class="report-content-row" v-if="((report.costs.air_freight_cost ?? null) !== null)">
|
<div class="report-content-row" v-if="((report.costs.air_freight_cost ?? null) !== null)">
|
||||||
<div>Air freight costs</div>
|
<div>Air freight costs</div>
|
||||||
<div class="report-content-data-cell">{{ report.costs.air_freight_cost.total.toFixed(2) }}</div>
|
<div class="report-content-data-cell">{{ report.costs.air_freight_cost.total.toFixed(2) }}</div>
|
||||||
<div class="report-content-data-cell">{{ (report.costs.air_freight_cost.percentage * 100).toFixed(2) }}</div>
|
<div class="report-content-data-cell">{{
|
||||||
|
(report.costs.air_freight_cost.percentage * 100).toFixed(2)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -143,7 +147,8 @@
|
||||||
<collapsible-box class="report-content-container" variant="border" :title="premise.destination.name"
|
<collapsible-box class="report-content-container" variant="border" :title="premise.destination.name"
|
||||||
:stretch-content="true" :initially-collapsed="true">
|
:stretch-content="true" :initially-collapsed="true">
|
||||||
<div>
|
<div>
|
||||||
<report-route :sections="premise.sections" :destination="premise.destination" :route-section-scale="routeSectionScale[idx]" ></report-route>
|
<report-route :sections="premise.sections" :destination="premise.destination"
|
||||||
|
:route-section-scale="routeSectionScale[idx]"></report-route>
|
||||||
|
|
||||||
<div class="report-sub-header">Premisses</div>
|
<div class="report-sub-header">Premisses</div>
|
||||||
|
|
||||||
|
|
@ -192,42 +197,45 @@
|
||||||
|
|
||||||
<div class="report-content-row">
|
<div class="report-content-row">
|
||||||
<div>HU dimensions [{{ premise.dimension_unit }}]</div>
|
<div>HU dimensions [{{ premise.dimension_unit }}]</div>
|
||||||
<div class="report-content-data-cell">{{ toFixedDimension(premise.length, premise.dimension_unit) }} x {{ toFixedDimension(premise.width, premise.dimension_unit) }} x {{ toFixedDimension(premise.height, premise.dimension_unit) }} </div>
|
<div class="report-content-data-cell">{{ toFixedDimension(premise.length, premise.dimension_unit) }} x
|
||||||
|
{{ toFixedDimension(premise.width, premise.dimension_unit) }} x
|
||||||
|
{{ toFixedDimension(premise.height, premise.dimension_unit) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="report-content-row">
|
<div class="report-content-row">
|
||||||
<div>HU weight [{{ premise.weight_unit }}]</div>
|
<div>HU weight [{{ premise.weight_unit }}]</div>
|
||||||
<div class="report-content-data-cell">{{ toFixedWeight(premise.weight, premise.weight_unit) }} </div>
|
<div class="report-content-data-cell">{{ toFixedWeight(premise.weight, premise.weight_unit) }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="report-content-row">
|
<div class="report-content-row">
|
||||||
<div>HU unit count</div>
|
<div>HU unit count</div>
|
||||||
<div class="report-content-data-cell">{{ premise.hu_unit_count }} </div>
|
<div class="report-content-data-cell">{{ premise.hu_unit_count }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="report-content-row">
|
<div class="report-content-row">
|
||||||
<div>Mixed container</div>
|
<div>Mixed container</div>
|
||||||
<div class="report-content-data-cell">{{ premise.mixed ? 'Yes' : 'No' }} </div>
|
<div class="report-content-data-cell">{{ premise.mixed ? 'Yes' : 'No' }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="report-content-row">
|
<div class="report-content-row">
|
||||||
<div>Stacked layers</div>
|
<div>Stacked layers</div>
|
||||||
<div class="report-content-data-cell">{{ premise.layer }} </div>
|
<div class="report-content-data-cell">{{ hasMainRun(premise.sections) ? premise.layer : '-' }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="report-content-row">
|
<div class="report-content-row">
|
||||||
<div>Container unit count</div>
|
<div>Container unit count</div>
|
||||||
<div class="report-content-data-cell">{{ premise.unit_count * premise.hu_unit_count }} </div>
|
<div class="report-content-data-cell">{{hasMainRun(premise.sections) ? (premise.unit_count * premise.hu_unit_count) : '-' }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="report-content-row">
|
<div class="report-content-row">
|
||||||
<div>Container type</div>
|
<div>Container type</div>
|
||||||
<div class="report-content-data-cell">{{ getContainerTypeName(premise.container_type) }} </div>
|
<div class="report-content-data-cell">{{hasMainRun(premise.sections) ? getContainerTypeName(premise.container_type) : '-' }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="report-content-row">
|
<div class="report-content-row">
|
||||||
<div>Limiting factor</div>
|
<div>Limiting factor</div>
|
||||||
<div class="report-content-data-cell">{{ premise.weight_exceeded ? 'Weight' : 'Volume' }} </div>
|
<div class="report-content-data-cell">{{hasMainRun(premise.sections) ? premise.weight_exceeded ? 'Weight' : 'Volume' : '-'}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -265,9 +273,12 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
hasMainRun(sections) {
|
||||||
|
return sections.some(section => section.transport_type === 'SEA' || section.transport_type === 'RAIL');
|
||||||
|
},
|
||||||
shorten(text, length) {
|
shorten(text, length) {
|
||||||
if(text !== null && text !== undefined && text.length > length) {
|
if (text !== null && text !== undefined && text.length > length) {
|
||||||
return `${text.substring(0, length - 3)} …` ;
|
return `${text.substring(0, length - 3)} …`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
|
|
@ -284,11 +295,11 @@ export default {
|
||||||
return ''
|
return ''
|
||||||
},
|
},
|
||||||
toFixedDimension(value, unit) {
|
toFixedDimension(value, unit) {
|
||||||
if(unit === 'm') {
|
if (unit === 'm') {
|
||||||
return value.toFixed(2);
|
return value.toFixed(2);
|
||||||
} else if(unit === 'cm') {
|
} else if (unit === 'cm') {
|
||||||
return value.toFixed(2);
|
return value.toFixed(2);
|
||||||
} else if(unit === 'mm') {
|
} else if (unit === 'mm') {
|
||||||
return value.toFixed();
|
return value.toFixed();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@ export default {
|
||||||
return this.modalType ? this.componentsData[this.modalType] : null;
|
return this.modalType ? this.componentsData[this.modalType] : null;
|
||||||
},
|
},
|
||||||
showProcessingModal() {
|
showProcessingModal() {
|
||||||
return this.premiseEditStore.showProcessingModal;
|
return this.premiseEditStore.showProcessingModal || this.showCalculationModal;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
|
@ -224,7 +224,8 @@ export default {
|
||||||
},
|
},
|
||||||
editIds: null,
|
editIds: null,
|
||||||
dataSourceId: null,
|
dataSourceId: null,
|
||||||
processingMessage: "Please wait. Calculating routes ...",
|
processingMessage: "Please wait. Calculating ...",
|
||||||
|
showCalculationModal: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -244,6 +245,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async startCalculation() {
|
async startCalculation() {
|
||||||
|
this.showCalculationModal = true;
|
||||||
const error = await this.premiseEditStore.startCalculation();
|
const error = await this.premiseEditStore.startCalculation();
|
||||||
|
|
||||||
if (error !== null) {
|
if (error !== null) {
|
||||||
|
|
@ -258,6 +260,7 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.closeMassEdit()
|
this.closeMassEdit()
|
||||||
}
|
}
|
||||||
|
this.showCalculationModal = false;
|
||||||
},
|
},
|
||||||
closeMassEdit() {
|
closeMassEdit() {
|
||||||
this.$router.push({name: "calculation-list"});
|
this.$router.push({name: "calculation-list"});
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,13 @@
|
||||||
<h3 class="sub-header">Destinations & routes</h3>
|
<h3 class="sub-header">Destinations & routes</h3>
|
||||||
<destination-list-view></destination-list-view>
|
<destination-list-view></destination-list-view>
|
||||||
|
|
||||||
|
<modal :z-index="3000" :state="showProcessingModal">
|
||||||
|
<div class="edit-calculation-spinner-container space-around">
|
||||||
|
<spinner></spinner>
|
||||||
|
<span>{{ processingMessage }}</span>
|
||||||
|
</div>
|
||||||
|
</modal>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -131,6 +138,8 @@ export default {
|
||||||
traceModal: false,
|
traceModal: false,
|
||||||
bulkEditQuery: null,
|
bulkEditQuery: null,
|
||||||
id: null,
|
id: null,
|
||||||
|
processingMessage: "Please wait. Calculating ...",
|
||||||
|
showCalculationModal: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -140,15 +149,18 @@ export default {
|
||||||
},
|
},
|
||||||
fromMassEdit() {
|
fromMassEdit() {
|
||||||
return this.bulkEditQuery !== null;
|
return this.bulkEditQuery !== null;
|
||||||
}
|
},
|
||||||
|
showProcessingModal() {
|
||||||
|
return this.premiseEditStore.showProcessingModal || this.showCalculationModal;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
async startCalculation() {
|
async startCalculation() {
|
||||||
|
this.showCalculationModal = true;
|
||||||
const error = await this.premiseEditStore.startCalculation();
|
const error = await this.premiseEditStore.startCalculation();
|
||||||
|
|
||||||
|
|
||||||
if (error !== null) {
|
if (error !== null) {
|
||||||
|
|
||||||
this.$refs.toast.addToast({
|
this.$refs.toast.addToast({
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
message: error.message,
|
message: error.message,
|
||||||
|
|
@ -159,6 +171,8 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.showCalculationModal = false;
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
if (this.bulkEditQuery) {
|
if (this.bulkEditQuery) {
|
||||||
|
|
@ -181,17 +195,6 @@ export default {
|
||||||
success = await this.premiseEditStore.savePackaging();
|
success = await this.premiseEditStore.savePackaging();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// if(success) {
|
|
||||||
// this.$refs.toast.addToast({
|
|
||||||
// icon: 'floppy-disk',
|
|
||||||
// message: `Changes on ${type} saved.`,
|
|
||||||
//
|
|
||||||
// variant: 'primary',
|
|
||||||
// duration: 3000
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
},
|
},
|
||||||
updateMaterial(id, action) {
|
updateMaterial(id, action) {
|
||||||
console.log(id, action);
|
console.log(id, action);
|
||||||
|
|
@ -287,11 +290,18 @@ export default {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.space-around {
|
||||||
|
margin: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
.edit-calculation-spinner-container {
|
.edit-calculation-spinner-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex: 1 1 30rem;
|
gap: 3.6rem;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
font-size: 1.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-calculation-spinner {
|
.edit-calculation-spinner {
|
||||||
|
|
|
||||||
|
|
@ -599,6 +599,8 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
} else {
|
} else {
|
||||||
const id = node.id;
|
const id = node.id;
|
||||||
|
|
||||||
|
this.processDestinationMassEdit = true;
|
||||||
|
|
||||||
|
|
||||||
const toBeUpdated = this.destinations.fromMassEditView ? this.destinations.premise_ids : this.premisses?.filter(p => p.selected).map(p => p.id);
|
const toBeUpdated = this.destinations.fromMassEditView ? this.destinations.premise_ids : this.premisses?.filter(p => p.selected).map(p => p.id);
|
||||||
|
|
||||||
|
|
@ -611,6 +613,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
const {data: destinations} = await performRequest(this, 'POST', url, body).catch(e => {
|
const {data: destinations} = await performRequest(this, 'POST', url, body).catch(e => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.selectedLoading = false;
|
this.selectedLoading = false;
|
||||||
|
this.processDestinationMassEdit = false;
|
||||||
throw e;
|
throw e;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -624,6 +627,8 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
this.destinations.destinations.push(mappedDestination);
|
this.destinations.destinations.push(mappedDestination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.processDestinationMassEdit = false;
|
||||||
|
|
||||||
return mappedIds;
|
return mappedIds;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,10 @@ export const useReportsStore = defineStore('reports', {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
params.append('material', this.materialId);
|
params.append('material', this.materialId);
|
||||||
params.append('sources', this.supplierIds);
|
params.append('sources', this.supplierIds);
|
||||||
|
params.append('userSources', this.userSupplierIds);
|
||||||
|
|
||||||
const url = `${config.backendUrl}/reports/download/${params.size === 0 ? '' : '?'}${params.toString()}`;
|
const url = `${config.backendUrl}/reports/download/${params.size === 0 ? '' : '?'}${params.toString()}`;
|
||||||
const fileName = `report_${this.materialId}_${this.supplierIds.join('_')}.xlsx`;
|
const fileName = `report_${this.materialId}_${this.supplierIds.join('_')}_u${this.userSupplierIds.join('_')}.xlsx`;
|
||||||
await performDownload(url,fileName);
|
await performDownload(url,fileName);
|
||||||
},
|
},
|
||||||
reset() {
|
reset() {
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ public class SecurityConfig {
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
|
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
|
||||||
.requestMatchers("/actuator/health").permitAll()
|
.requestMatchers("/actuator/health").permitAll()
|
||||||
.requestMatchers("/actuator/**").authenticated()
|
.requestMatchers("/actuator/**").hasRole("SERVICE")
|
||||||
.requestMatchers("/oauth2/token").permitAll()
|
.requestMatchers("/oauth2/token").permitAll()
|
||||||
.requestMatchers("/api/**").authenticated()
|
.requestMatchers("/api/**").authenticated()
|
||||||
.requestMatchers("/api/dev/**").denyAll()
|
.requestMatchers("/api/dev/**").denyAll()
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.net.URI;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
@ -35,6 +36,7 @@ public class DistanceApiService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Distance> getDistance(Node from, Node to) {
|
public Optional<Distance> getDistance(Node from, Node to) {
|
||||||
|
|
||||||
if (from == null || to == null) {
|
if (from == null || to == null) {
|
||||||
logger.warn("Source or destination node is null");
|
logger.warn("Source or destination node is null");
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
|
|
@ -46,7 +48,6 @@ public class DistanceApiService {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if distance exists in database and is valid
|
|
||||||
Optional<Distance> cachedDistance = distanceMatrixRepository.getDistance(from, to);
|
Optional<Distance> cachedDistance = distanceMatrixRepository.getDistance(from, to);
|
||||||
|
|
||||||
if (cachedDistance.isPresent()) {
|
if (cachedDistance.isPresent()) {
|
||||||
|
|
@ -54,12 +55,10 @@ public class DistanceApiService {
|
||||||
return cachedDistance;
|
return cachedDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Distance not found or stale, fetch from Azure Maps
|
logger.debug("Fetching distance from Azure Maps for nodes {} to {}", from.getId(), to.getId());
|
||||||
logger.info("Fetching distance from Azure Maps for nodes {} to {}", from.getId(), to.getId());
|
|
||||||
Optional<Distance> fetchedDistance = fetchDistanceFromAzureMaps(from, to);
|
Optional<Distance> fetchedDistance = fetchDistanceFromAzureMaps(from, to);
|
||||||
|
|
||||||
if (fetchedDistance.isPresent()) {
|
if (fetchedDistance.isPresent()) {
|
||||||
// Store in database
|
|
||||||
distanceMatrixRepository.saveDistance(fetchedDistance.get());
|
distanceMatrixRepository.saveDistance(fetchedDistance.get());
|
||||||
return fetchedDistance;
|
return fetchedDistance;
|
||||||
}
|
}
|
||||||
|
|
@ -69,18 +68,19 @@ public class DistanceApiService {
|
||||||
|
|
||||||
private Optional<Distance> fetchDistanceFromAzureMaps(Node from, Node to) {
|
private Optional<Distance> fetchDistanceFromAzureMaps(Node from, Node to) {
|
||||||
try {
|
try {
|
||||||
String url = UriComponentsBuilder.fromHttpUrl(AZURE_MAPS_ROUTE_API)
|
String url = UriComponentsBuilder.fromUriString(AZURE_MAPS_ROUTE_API)
|
||||||
.queryParam("api-version", "1.0")
|
.queryParam("api-version", "1.0")
|
||||||
.queryParam("subscription-key", subscriptionKey)
|
.queryParam("subscription-key", subscriptionKey)
|
||||||
.queryParam("query", String.format("%s,%s:%s,%s",
|
.queryParam("query", String.format("%s,%s:%s,%s",
|
||||||
from.getGeoLat(), from.getGeoLng(),
|
from.getGeoLat(), from.getGeoLng(),
|
||||||
to.getGeoLat(), to.getGeoLng()))
|
to.getGeoLat(), to.getGeoLng()))
|
||||||
|
.encode()
|
||||||
.toUriString();
|
.toUriString();
|
||||||
|
|
||||||
RouteDirectionsResponse response = restTemplate.getForObject(url, RouteDirectionsResponse.class);
|
RouteDirectionsResponse response = restTemplate.getForObject(url, RouteDirectionsResponse.class);
|
||||||
|
|
||||||
if (response != null && response.getRoutes() != null && !response.getRoutes().isEmpty()) {
|
if (response != null && response.getRoutes() != null && !response.getRoutes().isEmpty()) {
|
||||||
Integer distanceInMeters = response.getRoutes().get(0).getSummary().getLengthInMeters();
|
Integer distanceInMeters = response.getRoutes().getFirst().getSummary().getLengthInMeters();
|
||||||
|
|
||||||
Distance distance = new Distance();
|
Distance distance = new Distance();
|
||||||
distance.setFromNodeId(from.getId());
|
distance.setFromNodeId(from.getId());
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import de.avatic.lcc.model.db.nodes.Location;
|
||||||
import de.avatic.lcc.model.db.nodes.Node;
|
import de.avatic.lcc.model.db.nodes.Node;
|
||||||
import de.avatic.lcc.repositories.DistanceMatrixRepository;
|
import de.avatic.lcc.repositories.DistanceMatrixRepository;
|
||||||
import de.avatic.lcc.repositories.country.CountryRepository;
|
import de.avatic.lcc.repositories.country.CountryRepository;
|
||||||
|
import de.avatic.lcc.service.api.DistanceApiService;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
|
@ -14,20 +15,21 @@ public class DistanceService {
|
||||||
private static final double EARTH_RADIUS = 6371.0;
|
private static final double EARTH_RADIUS = 6371.0;
|
||||||
private final DistanceMatrixRepository distanceMatrixRepository;
|
private final DistanceMatrixRepository distanceMatrixRepository;
|
||||||
private final CountryRepository countryRepository;
|
private final CountryRepository countryRepository;
|
||||||
|
private final DistanceApiService distanceApiService;
|
||||||
|
|
||||||
public DistanceService(DistanceMatrixRepository distanceMatrixRepository, CountryRepository countryRepository) {
|
public DistanceService(DistanceMatrixRepository distanceMatrixRepository, CountryRepository countryRepository, DistanceApiService distanceApiService) {
|
||||||
this.distanceMatrixRepository = distanceMatrixRepository;
|
this.distanceMatrixRepository = distanceMatrixRepository;
|
||||||
this.countryRepository = countryRepository;
|
this.countryRepository = countryRepository;
|
||||||
|
this.distanceApiService = distanceApiService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getDistance(Node src, Node dest, boolean fast) {
|
public double getDistance(Node src, Node dest, boolean fast) {
|
||||||
if (fast) return getDistanceFast(src, dest);
|
if (fast) return getDistanceFast(src, dest);
|
||||||
|
|
||||||
var distance = distanceMatrixRepository.getDistance(src, dest);
|
var distance = distanceApiService.getDistance(src, dest);
|
||||||
|
if (distance.isEmpty()) return getDistanceFast(src, dest);
|
||||||
// TODO do a api call if empty!.
|
|
||||||
return distance.map(value -> value.getDistance().intValue()).orElse(0);
|
|
||||||
|
|
||||||
|
return distance.map(value -> value.getDistance().intValue()/1000).orElse(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getDistanceFast(Integer srcCountryId, Location src, Integer destCountryId, Location dest ) {
|
public double getDistanceFast(Integer srcCountryId, Location src, Integer destCountryId, Location dest ) {
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,7 @@ public class RoutingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
finalSection.setRate(matrixRate);
|
finalSection.setRate(matrixRate);
|
||||||
finalSection.setApproxDistance(distanceService.getDistance(container.getSourceNode(), toNode, true));
|
finalSection.setApproxDistance(distanceService.getDistance(container.getSourceNode(), toNode, false));
|
||||||
rates.add(finalSection);
|
rates.add(finalSection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -699,7 +699,7 @@ public class RoutingService {
|
||||||
|
|
||||||
if (matrixRate.isPresent()) {
|
if (matrixRate.isPresent()) {
|
||||||
matrixRateObj.setRate(matrixRate.get());
|
matrixRateObj.setRate(matrixRate.get());
|
||||||
matrixRateObj.setApproxDistance(distanceService.getDistance(startNode, endNode, true));
|
matrixRateObj.setApproxDistance(distanceService.getDistance(startNode, endNode, false));
|
||||||
container.getRates().add(matrixRateObj);
|
container.getRates().add(matrixRateObj);
|
||||||
return matrixRateObj;
|
return matrixRateObj;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ public class RouteSectionCostCalculationService {
|
||||||
Node fromNode = nodeRepository.getById(premise.getSupplierNodeId()).orElseThrow();
|
Node fromNode = nodeRepository.getById(premise.getSupplierNodeId()).orElseThrow();
|
||||||
Node toNode = nodeRepository.getById(destination.getDestinationNodeId()).orElseThrow();
|
Node toNode = nodeRepository.getById(destination.getDestinationNodeId()).orElseThrow();
|
||||||
|
|
||||||
double distance = distanceService.getDistance(fromNode, toNode, true);
|
double distance = distanceService.getDistance(fromNode, toNode, false);
|
||||||
result.setDistance(BigDecimal.valueOf(distance));
|
result.setDistance(BigDecimal.valueOf(distance));
|
||||||
|
|
||||||
// Get rate and transit time
|
// Get rate and transit time
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ public class PreCalculationCheckService {
|
||||||
|
|
||||||
var validDaysInt = Integer.parseInt(validDays.get().getCurrentValue());
|
var validDaysInt = Integer.parseInt(validDays.get().getCurrentValue());
|
||||||
|
|
||||||
if(!period.getStartDate().plusDays((long) validDaysInt * renewals).isAfter(LocalDateTime.now()))
|
if(!period.getStartDate().plusDays((((long) validDaysInt * renewals)+validDaysInt)).isAfter(LocalDateTime.now()))
|
||||||
throw new PremiseValidationError("There are no valid rates for the given date. Please contact your administrator.");
|
throw new PremiseValidationError("There are no valid rates for the given date. Please contact your administrator.");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package de.avatic.lcc.service.report;
|
package de.avatic.lcc.service.report;
|
||||||
|
|
||||||
import de.avatic.lcc.dto.generic.NodeDTO;
|
import de.avatic.lcc.dto.generic.NodeDTO;
|
||||||
|
import de.avatic.lcc.dto.generic.TransportType;
|
||||||
import de.avatic.lcc.dto.report.ReportDTO;
|
import de.avatic.lcc.dto.report.ReportDTO;
|
||||||
import de.avatic.lcc.dto.report.ReportDestinationDTO;
|
import de.avatic.lcc.dto.report.ReportDestinationDTO;
|
||||||
import de.avatic.lcc.dto.report.ReportEntryDTO;
|
import de.avatic.lcc.dto.report.ReportEntryDTO;
|
||||||
|
|
@ -145,6 +146,9 @@ public class ExcelReportingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flattenDestination(ReportDestinationDTO destination) {
|
private void flattenDestination(ReportDestinationDTO destination) {
|
||||||
|
|
||||||
|
var hasMainRun = destination.getSections().stream().anyMatch(s -> s.getTransportType().equals(TransportType.RAIL) || s.getTransportType().equals(TransportType.SEA));
|
||||||
|
|
||||||
addData(DESTINATION_NAME, destination.getDestination().getName());
|
addData(DESTINATION_NAME, destination.getDestination().getName());
|
||||||
addData(DESTINATION_ADDRESS, destination.getDestination().getAddress());
|
addData(DESTINATION_ADDRESS, destination.getDestination().getAddress());
|
||||||
|
|
||||||
|
|
@ -152,6 +156,7 @@ public class ExcelReportingService {
|
||||||
addData(DESTINATION_HS_CODE, destination.getHsCode());
|
addData(DESTINATION_HS_CODE, destination.getHsCode());
|
||||||
addData(DESTINATION_TARIFF_RATE, destination.getTariffRate().toString());
|
addData(DESTINATION_TARIFF_RATE, destination.getTariffRate().toString());
|
||||||
addData(DESTINATION_OVERSHARE, destination.getOverseaShare().toString());
|
addData(DESTINATION_OVERSHARE, destination.getOverseaShare().toString());
|
||||||
|
|
||||||
if(destination.getAirFreightShare() != null)
|
if(destination.getAirFreightShare() != null)
|
||||||
addData(DESTINATION_AIR_FREIGHT_SHARE, destination.getAirFreightShare().toString());
|
addData(DESTINATION_AIR_FREIGHT_SHARE, destination.getAirFreightShare().toString());
|
||||||
addData(DESTINATION_TRANSPORT_TIME, destination.getTransportTime().toString());
|
addData(DESTINATION_TRANSPORT_TIME, destination.getTransportTime().toString());
|
||||||
|
|
@ -164,14 +169,14 @@ public class ExcelReportingService {
|
||||||
addData(DESTINATION_WEIGHT, destination.getWeight().toString());
|
addData(DESTINATION_WEIGHT, destination.getWeight().toString());
|
||||||
addData(DESTINATION_WEIGHT_UNIT, destination.getWeightUnit().toString());
|
addData(DESTINATION_WEIGHT_UNIT, destination.getWeightUnit().toString());
|
||||||
addData(DESTINATION_HU_UNIT_COUNT, destination.getHuUnitCount().toString());
|
addData(DESTINATION_HU_UNIT_COUNT, destination.getHuUnitCount().toString());
|
||||||
addData(DESTINATION_CONTAINER_LAYER, destination.getLayer().toString());
|
addData(DESTINATION_CONTAINER_LAYER, !hasMainRun ? "-" : destination.getLayer().toString());
|
||||||
|
|
||||||
addData(DESTINATION_CONTAINER_UNIT_COUNT, destination.getUnitCount().toString());
|
addData(DESTINATION_CONTAINER_UNIT_COUNT, !hasMainRun ? "-" : destination.getUnitCount().toString());
|
||||||
addData(DESTINATION_CONTAINER_UTILIZATION, destination.getUtilization().toString());
|
addData(DESTINATION_CONTAINER_UTILIZATION, !hasMainRun ? "-" : destination.getUtilization().toString());
|
||||||
addData(DESTINATION_CONTAINER_TYPE, destination.getType().toString());
|
addData(DESTINATION_CONTAINER_TYPE, !hasMainRun ? "-" : destination.getType().toString());
|
||||||
addData(DESTINATION_CONTAINER_WEIGHT_EXCEEDED, destination.getWeightExceeded().toString());
|
addData(DESTINATION_CONTAINER_WEIGHT_EXCEEDED, !hasMainRun ? "-" : destination.getWeightExceeded().toString());
|
||||||
addData(DESTINATION_CONTAINER_RATE, destination.getRate().toString());
|
addData(DESTINATION_CONTAINER_RATE, !hasMainRun ? "-" : destination.getRate().toString());
|
||||||
addData(DESTINATION_MIXED, destination.getMixed().toString());
|
addData(DESTINATION_MIXED, !hasMainRun ? "-" : destination.getMixed().toString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package de.avatic.lcc.service.report;
|
||||||
|
|
||||||
import de.avatic.lcc.dto.generic.NodeDTO;
|
import de.avatic.lcc.dto.generic.NodeDTO;
|
||||||
import de.avatic.lcc.dto.report.ReportDTO;
|
import de.avatic.lcc.dto.report.ReportDTO;
|
||||||
|
import de.avatic.lcc.model.db.calculations.CalculationJob;
|
||||||
import de.avatic.lcc.repositories.NodeRepository;
|
import de.avatic.lcc.repositories.NodeRepository;
|
||||||
import de.avatic.lcc.repositories.calculation.CalculationJobRepository;
|
import de.avatic.lcc.repositories.calculation.CalculationJobRepository;
|
||||||
import de.avatic.lcc.repositories.rates.ValidityPeriodRepository;
|
import de.avatic.lcc.repositories.rates.ValidityPeriodRepository;
|
||||||
|
|
@ -58,9 +59,13 @@ public class ReportingService {
|
||||||
var periodId = tuple.get().periodId();
|
var periodId = tuple.get().periodId();
|
||||||
var setId = tuple.get().propertySetId();
|
var setId = tuple.get().propertySetId();
|
||||||
|
|
||||||
//TODO check user node id and node id for null
|
|
||||||
|
|
||||||
var jobs = new ArrayList<>(nodeIds.stream().map(nodeId -> calculationJobRepository.getCalculationJobWithJobStateValid(periodId, setId, nodeId, materialId)).filter(Optional::isPresent).map(Optional::get).toList());
|
var jobs = new ArrayList<CalculationJob>();
|
||||||
|
|
||||||
|
if(!nodeIds.isEmpty())
|
||||||
|
jobs.addAll(nodeIds.stream().map(nodeId -> calculationJobRepository.getCalculationJobWithJobStateValid(periodId, setId, nodeId, materialId)).filter(Optional::isPresent).map(Optional::get).toList());
|
||||||
|
|
||||||
|
if(!userNodeIds.isEmpty())
|
||||||
jobs.addAll(userNodeIds.stream().map(nodeId -> calculationJobRepository.getCalculationJobWithJobStateValidUserNodeId(periodId, setId,nodeId ,materialId)).filter(Optional::isPresent).map(Optional::get).toList());
|
jobs.addAll(userNodeIds.stream().map(nodeId -> calculationJobRepository.getCalculationJobWithJobStateValidUserNodeId(periodId, setId,nodeId ,materialId)).filter(Optional::isPresent).map(Optional::get).toList());
|
||||||
|
|
||||||
return jobs.stream().map(reportTransformer::toReportDTO).toList();
|
return jobs.stream().map(reportTransformer::toReportDTO).toList();
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,8 @@ public class ReportTransformer {
|
||||||
destinationDTO.setWeight(weightUnit.convertFromG(premise.getIndividualHuWeight()));
|
destinationDTO.setWeight(weightUnit.convertFromG(premise.getIndividualHuWeight()));
|
||||||
destinationDTO.setHuUnitCount(premise.getHuUnitCount());
|
destinationDTO.setHuUnitCount(premise.getHuUnitCount());
|
||||||
|
|
||||||
|
CalculationJobRouteSection mainRun = sections.stream().filter(CalculationJobRouteSection::getMainRun).findFirst().orElse(null);
|
||||||
|
|
||||||
destinationDTO.setLayer(destination.getLayerCount());
|
destinationDTO.setLayer(destination.getLayerCount());
|
||||||
|
|
||||||
destinationDTO.setOverseaShare(premise.getOverseaShare().doubleValue());
|
destinationDTO.setOverseaShare(premise.getOverseaShare().doubleValue());
|
||||||
|
|
@ -199,7 +201,6 @@ public class ReportTransformer {
|
||||||
if (includeAirfreight)
|
if (includeAirfreight)
|
||||||
destinationDTO.setAirFreightShare(destination.getAirFreightShare().doubleValue());
|
destinationDTO.setAirFreightShare(destination.getAirFreightShare().doubleValue());
|
||||||
|
|
||||||
CalculationJobRouteSection mainRun = sections.stream().filter(CalculationJobRouteSection::getMainRun).findFirst().orElse(null);
|
|
||||||
|
|
||||||
destinationDTO.setMixed(premise.getHuMixable());
|
destinationDTO.setMixed(premise.getHuMixable());
|
||||||
destinationDTO.setRate(mainRun == null ? 0 : mainRun.getRate());
|
destinationDTO.setRate(mainRun == null ? 0 : mainRun.getRate());
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue