From abed6b82e5215e5f43add5458c9ba687523945ba Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 9 Sep 2025 19:10:05 +0200 Subject: [PATCH] FRONTEND/BACKEND: Enriching informations in report. Added route widget to destinations --- src/frontend/src/backend.js | 97 ++++++ src/frontend/src/components/UI/Box.vue | 36 ++- .../src/components/UI/CollapsibleBox.vue | 141 +++++++++ .../src/components/UI/ReportRoute.vue | 188 ++++++++++- .../src/components/layout/report/Report.vue | 294 +++++++++++++++++- .../layout/report/SelectForReport.vue | 1 + src/frontend/src/pages/Reporting.vue | 43 ++- src/frontend/src/store/reports.js | 144 ++------- .../lcc/dto/report/ReportSectionDTO.java | 24 +- .../CalculationJobRouteSectionRepository.java | 17 +- .../transformer/report/ReportTransformer.java | 72 +++-- 11 files changed, 888 insertions(+), 169 deletions(-) create mode 100644 src/frontend/src/backend.js create mode 100644 src/frontend/src/components/UI/CollapsibleBox.vue diff --git a/src/frontend/src/backend.js b/src/frontend/src/backend.js new file mode 100644 index 0000000..c4d8221 --- /dev/null +++ b/src/frontend/src/backend.js @@ -0,0 +1,97 @@ +import logger from "@/logger.js"; +import {useErrorStore} from "@/store/error.js"; + +const performRequest = async (requestingStore, method, url, body, expectResponse = true) => { + + const params = { + method: method, + headers: { + 'Content-Type': 'application/json' + } + }; + + if (body) { + params.body = JSON.stringify(body); + } + + const request = {url: url, params: params}; + logger.info("Request:", request); + + const response = await fetch(url, params + ).catch(e => { + const error = { + code: 'Network error.', + message: "Please check your internet connection.", + trace: null + } + + logger.error(error); + const errorStore = useErrorStore(); + void errorStore.addError(error, {store: requestingStore, request: request}); + + throw e; + }); + + let data = null; + if (expectResponse) { + data = await response.json().catch(e => { + const error = { + code: 'Malformed response', + message: "Malformed server response. Please contact support.", + trace: null + } + + logger.error(error); + const errorStore = useErrorStore(); + void errorStore.addError(error, {store: requestingStore, request: request}); + throw e; + }); + + if (!response.ok) { + const error = { + code: data.error.code, + title: data.error.title, + message: data.error.message, + trace: data.error.trace + } + + logger.error(error); + const errorStore = useErrorStore(); + void errorStore.addError(error, {store: requestingStore, request: request}); + throw new Error('Internal backend error'); + } + } else { + if (!response.ok) { + + const data = await response.json().catch(e => { + const error = { + code: "Return code error " + response.status, + message: "Server returned wrong response code", + trace: null + } + logger.error(error); + const errorStore = useErrorStore(); + void errorStore.addError(error, {store: requestingStore, request: request}); + throw new Error('Internal backend error'); + + }); + + const error = { + code: data.error.code, + title: data.error.title, + message: data.error.message, + trace: data.error.trace + } + + logger.error(error); + const errorStore = useErrorStore(); + void errorStore.addError(error, {store: requestingStore, request: request}); + throw new Error('Internal backend error'); + } + } + + logger.info("Response:", data); + return data; +} + +export default performRequest; diff --git a/src/frontend/src/components/UI/Box.vue b/src/frontend/src/components/UI/Box.vue index 9485219..1f07423 100644 --- a/src/frontend/src/components/UI/Box.vue +++ b/src/frontend/src/components/UI/Box.vue @@ -1,5 +1,5 @@ @@ -7,20 +7,50 @@ \ No newline at end of file diff --git a/src/frontend/src/components/UI/ReportRoute.vue b/src/frontend/src/components/UI/ReportRoute.vue index 459e43e..3e0403a 100644 --- a/src/frontend/src/components/UI/ReportRoute.vue +++ b/src/frontend/src/components/UI/ReportRoute.vue @@ -1,15 +1,187 @@ - - + + \ No newline at end of file diff --git a/src/frontend/src/components/layout/report/Report.vue b/src/frontend/src/components/layout/report/Report.vue index b75a6ba..bbbdb56 100644 --- a/src/frontend/src/components/layout/report/Report.vue +++ b/src/frontend/src/components/layout/report/Report.vue @@ -1,14 +1,298 @@ + + - - \ No newline at end of file diff --git a/src/frontend/src/components/layout/report/SelectForReport.vue b/src/frontend/src/components/layout/report/SelectForReport.vue index 91b736e..38ad991 100644 --- a/src/frontend/src/components/layout/report/SelectForReport.vue +++ b/src/frontend/src/components/layout/report/SelectForReport.vue @@ -55,6 +55,7 @@ export default { } }, created() { + //todo reset the store instead. this.selectedMaterialId = this.reportSearchStore.getMaterial?.id; }, computed: { diff --git a/src/frontend/src/pages/Reporting.vue b/src/frontend/src/pages/Reporting.vue index d531ea1..cbf2927 100644 --- a/src/frontend/src/pages/Reporting.vue +++ b/src/frontend/src/pages/Reporting.vue @@ -7,9 +7,16 @@ - -
+
+
+ +
+
+
+ + +
@@ -33,10 +40,13 @@ import SelectForReport from "@/components/layout/report/SelectForReport.vue"; import {mapStores} from "pinia"; import {useReportsStore} from "@/store/reports.js"; import Box from "@/components/UI/Box.vue"; +import Spinner from "@/components/UI/Spinner.vue"; +import ReportChart from "@/components/UI/ReportChart.vue"; +import Report from "@/components/layout/report/Report.vue"; export default { name: "Reporting", - components: {Box, SelectForReport, BasicButton, Modal}, + components: {Report, ReportChart, Spinner, Box, SelectForReport, BasicButton, Modal}, data() { return { showModal: false, @@ -45,7 +55,13 @@ export default { computed: { ...mapStores(useReportsStore), hasReport() { - return false; + return this.reportsStore.reports?.length > 0; + }, + reports() { + return this.reportsStore.reports + }, + loading() { + return this.reportsStore.loading; } }, methods: { @@ -59,7 +75,7 @@ export default { } }, created() { - if(!this.hasReport) + if (!this.hasReport) this.showModal = true; } } @@ -84,6 +100,7 @@ export default { gap: 1.6rem; } + .empty-container { display: flex; justify-content: center; @@ -92,4 +109,20 @@ export default { font-weight: 500; } +.report-spinner-container { + display: flex; + align-items: center; + justify-content: center; + flex: 1 1 30rem +} + +.report-spinner { + font-size: 1.6rem; + width: 24rem; + height: 12rem; + display: flex; + justify-content: center; + align-items: center; +} + \ No newline at end of file diff --git a/src/frontend/src/store/reports.js b/src/frontend/src/store/reports.js index 1b99460..328ebea 100644 --- a/src/frontend/src/store/reports.js +++ b/src/frontend/src/store/reports.js @@ -1,135 +1,35 @@ import {defineStore} from 'pinia' import {config} from '@/config' -import {useErrorStore} from "@/store/error.js"; -import {useStageStore} from "@/store/stage.js"; -import {usePropertySetsStore} from "@/store/propertySets.js"; -import logger from "@/logger.js"; +import performRequest from '@/backend.js' + export const useReportsStore = defineStore('reports', { - state() { - return { - reports: [], - loading: false, - } - }, - getters: { - - }, - actions: { - async fetchReports(materialId, supplierIds) { - if (supplierIds == null || materialId == null) return; - - this.loading = true; - this.reports = []; - - console.log("fetchreports") - - const params = new URLSearchParams(); - params.append('material', materialId); - params.append('sources', supplierIds); - - const url = `${config.backendUrl}/reports/view/${params.size === 0 ? '' : '?'}${params.toString()}`; - - this.reports = await this.performRequest('GET', url, null).catch(e => { - this.loading = false; - }); - - this.loading = false; - }, - async performRequest(method, url, body, expectResponse = true) { - - const params = { - method: method, - headers: { - 'Content-Type': 'application/json' - } - }; - - if (body) { - params.body = JSON.stringify(body); + state() { + return { + reports: [], + loading: false, } + }, + getters: {}, + actions: { + async fetchReports(materialId, supplierIds) { + if (supplierIds == null || materialId == null) return; - const request = {url: url, params: params}; - logger.info("Request:", request); + this.loading = true; + this.reports = []; - const response = await fetch(url, params - ).catch(e => { - const error = { - code: 'Network error.', - message: "Please check your internet connection.", - trace: null - } + const params = new URLSearchParams(); + params.append('material', materialId); + params.append('sources', supplierIds); - logger.error(error); - const errorStore = useErrorStore(); - void errorStore.addError(error, {store: this, request: request}); + const url = `${config.backendUrl}/reports/view/${params.size === 0 ? '' : '?'}${params.toString()}`; - throw e; - }); - - let data = null; - if (expectResponse) { - data = await response.json().catch(e => { - const error = { - code: 'Malformed response', - message: "Malformed server response. Please contact support.", - trace: null - } - - logger.error(error); - const errorStore = useErrorStore(); - void errorStore.addError(error, {store: this, request: request}); - throw e; + this.reports = await performRequest(this,'GET', url, null).catch(e => { + this.loading = false; }); - if (!response.ok) { - const error = { - code: data.error.code, - title: data.error.title, - message: data.error.message, - trace: data.error.trace - } - - logger.error(error); - const errorStore = useErrorStore(); - void errorStore.addError(error, {store: this, request: request}); - throw new Error('Internal backend error'); - } - } else { - if (!response.ok) { - - const data = await response.json().catch(e => { - const error = { - code: "Return code error " + response.status, - message: "Server returned wrong response code", - trace: null - } - logger.error(error); - const errorStore = useErrorStore(); - void errorStore.addError(error, {store: this, request: request}); - throw new Error('Internal backend error'); - - }); - - const error = { - code: data.error.code, - title: data.error.title, - message: data.error.message, - trace: data.error.trace - } - - logger.error(error); - const errorStore = useErrorStore(); - void errorStore.addError(error, {store: this, request: request}); - throw new Error('Internal backend error'); - - - } + this.loading = false; } - - logger.info("Response:", data); - return data; } - }, - -}); \ No newline at end of file + } +); \ No newline at end of file diff --git a/src/main/java/de/avatic/lcc/dto/report/ReportSectionDTO.java b/src/main/java/de/avatic/lcc/dto/report/ReportSectionDTO.java index 50f6e5d..a9f4576 100644 --- a/src/main/java/de/avatic/lcc/dto/report/ReportSectionDTO.java +++ b/src/main/java/de/avatic/lcc/dto/report/ReportSectionDTO.java @@ -9,7 +9,7 @@ public class ReportSectionDTO { private Integer id; - @JsonProperty("route_type") + @JsonProperty("transport_type") private TransportType transportType; @JsonProperty("rate_type") @@ -18,12 +18,18 @@ public class ReportSectionDTO { @JsonProperty("from_node") private NodeDTO fromNode; + @JsonProperty("to_node") + private NodeDTO toNode; + @JsonProperty("cost") private ReportEntryDTO cost; @JsonProperty("duration") private ReportEntryDTO duration; + @JsonProperty("distance") + private ReportEntryDTO distance; + public Integer getId() { return id; } @@ -71,4 +77,20 @@ public class ReportSectionDTO { public void setDuration(ReportEntryDTO duration) { this.duration = duration; } + + public ReportEntryDTO getDistance() { + return distance; + } + + public void setDistance(ReportEntryDTO distance) { + this.distance = distance; + } + + public NodeDTO getToNode() { + return toNode; + } + + public void setToNode(NodeDTO toNode) { + this.toNode = toNode; + } } diff --git a/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRouteSectionRepository.java b/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRouteSectionRepository.java index 98c9bef..b482e91 100644 --- a/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRouteSectionRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/calculation/CalculationJobRouteSectionRepository.java @@ -1,8 +1,8 @@ package de.avatic.lcc.repositories.calculation; +import de.avatic.lcc.dto.generic.RateType; import de.avatic.lcc.dto.generic.TransportType; import de.avatic.lcc.model.calculations.CalculationJobRouteSection; -import de.avatic.lcc.model.premises.route.Destination; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.support.GeneratedKeyHolder; @@ -117,7 +117,20 @@ public class CalculationJobRouteSectionRepository { entity.setCalculationJobDestinationId(rs.getInt("calculation_job_destination_id")); // Rule and price type flags - entity.setTransportType(TransportType.valueOf(rs.getString("transport_type"))); + String transportType = rs.getString("transport_type"); + + if ("MATRIX".equals(transportType)) { + entity.setTransportType(TransportType.ROAD); + entity.setRateType(RateType.MATRIX); + } else if ("D2D".equals(transportType)) { + entity.setTransportType(TransportType.ROAD); + entity.setRateType(RateType.D2D); + } else { + entity.setRateType(RateType.CONTAINER); + entity.setTransportType(TransportType.valueOf(rs.getString("transport_type"))); + } + + entity.setUnmixedPrice(rs.getBoolean("is_unmixed_price")); entity.setCbmPrice(rs.getBoolean("is_cbm_price")); entity.setWeightPrice(rs.getBoolean("is_weight_price")); 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 0ae7e34..1dd8f11 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 @@ -9,11 +9,13 @@ import de.avatic.lcc.model.calculations.CalculationJob; import de.avatic.lcc.model.calculations.CalculationJobDestination; import de.avatic.lcc.model.calculations.CalculationJobRouteSection; import de.avatic.lcc.model.premises.Premise; +import de.avatic.lcc.model.properties.SystemPropertyMappingId; import de.avatic.lcc.repositories.NodeRepository; import de.avatic.lcc.repositories.calculation.CalculationJobDestinationRepository; import de.avatic.lcc.repositories.calculation.CalculationJobRouteSectionRepository; import de.avatic.lcc.repositories.premise.PremiseRepository; import de.avatic.lcc.repositories.premise.RouteNodeRepository; +import de.avatic.lcc.repositories.properties.PropertyRepository; import de.avatic.lcc.service.transformer.generic.NodeTransformer; import org.springframework.stereotype.Service; @@ -32,20 +34,25 @@ public class ReportTransformer { private final NodeRepository nodeRepository; private final NodeTransformer nodeTransformer; private final RouteNodeRepository routeNodeRepository; + private final PropertyRepository propertyRepository; - public ReportTransformer(CalculationJobDestinationRepository calculationJobDestinationRepository, CalculationJobRouteSectionRepository calculationJobRouteSectionRepository, PremiseRepository premiseRepository, NodeRepository nodeRepository, NodeTransformer nodeTransformer, RouteNodeRepository routeNodeRepository) { + public ReportTransformer(CalculationJobDestinationRepository calculationJobDestinationRepository, CalculationJobRouteSectionRepository calculationJobRouteSectionRepository, PremiseRepository premiseRepository, NodeRepository nodeRepository, NodeTransformer nodeTransformer, RouteNodeRepository routeNodeRepository, PropertyRepository propertyRepository) { this.calculationJobDestinationRepository = calculationJobDestinationRepository; this.calculationJobRouteSectionRepository = calculationJobRouteSectionRepository; this.premiseRepository = premiseRepository; this.nodeRepository = nodeRepository; this.nodeTransformer = nodeTransformer; this.routeNodeRepository = routeNodeRepository; + this.propertyRepository = propertyRepository; } public ReportDTO toReportDTO(CalculationJob job) { ReportDTO reportDTO = new ReportDTO(); + var reportingProperty = propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.REPORTING).orElseThrow(); + boolean includeAirfreight = reportingProperty.getCurrentValue().equals("MEK_C"); + List destinations = calculationJobDestinationRepository.getDestinationsByJobId(job.getId()); Map> sections = calculationJobRouteSectionRepository.getRouteSectionsByDestinationIds(destinations.stream().map(CalculationJobDestination::getId).toList()); @@ -53,8 +60,8 @@ public class ReportTransformer { Premise premise = premiseRepository.getPremiseById(job.getPremiseId()).orElseThrow(); - reportDTO.setCost(getCostMap(job, destinations, weightedTotalCost)); - reportDTO.setRisk(getRisk(job, destinations, weightedTotalCost)); + reportDTO.setCost(getCostMap(job, destinations, weightedTotalCost, includeAirfreight)); + reportDTO.setRisk(getRisk(job, destinations, weightedTotalCost, includeAirfreight)); reportDTO.setDestination(destinations.stream().map(d -> getDestinationDTO(d, sections.get(d.getId()), premise)).toList()); if (!reportDTO.getDestinations().isEmpty()) { @@ -79,9 +86,9 @@ public class ReportTransformer { if (section.getPreRun()) totalPreRunCost = totalPreRunCost.add(section.getAnnualCost()); - if(section.getMainRun()) + if (section.getMainRun()) totalMainRunCost = totalMainRunCost.add(section.getAnnualCost()); - if(section.getPostRun()) + if (section.getPostRun()) totalPostRunCost = totalPostRunCost.add(section.getAnnualCost()); totalCost = totalCost.add(section.getAnnualCost()); @@ -150,8 +157,14 @@ public class ReportTransformer { sectionDTO.setId(section.getId()); sectionDTO.setTransportType(section.getTransportType()); sectionDTO.setFromNode(nodeTransformer.toNodeDTO(routeNodeRepository.getFromNodeBySectionId(section.getPremiseRouteSectionId()).orElseThrow())); + sectionDTO.setToNode(nodeTransformer.toNodeDTO(routeNodeRepository.getToNodeBySectionId(section.getPremiseRouteSectionId()).orElseThrow())); sectionDTO.setRateType(section.getRateType()); + + var distance = new ReportEntryDTO(); + distance.setTotal(section.getDistance()); + sectionDTO.setDistance(distance); + var duration = new ReportEntryDTO(); duration.setTotal(section.getTransitTime()); sectionDTO.setDuration(duration); @@ -164,43 +177,45 @@ public class ReportTransformer { } - private Map getRisk(CalculationJob job, List destination, WeightedTotalCosts weightedTotalCost) { + private Map getRisk(CalculationJob job, List destination, WeightedTotalCosts weightedTotalCost, boolean includeAirfreight) { Map risk = new HashMap<>(); var annualAmount = destination.stream().map(CalculationJobDestination::getAnnualAmount).reduce(BigDecimal.ZERO, BigDecimal::add); - - var airfreightValue = destination.stream().map(CalculationJobDestination::getAnnualAirFreightCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP); - var worstValue = destination.stream().map(CalculationJobDestination::getTotalRiskCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP); - var bestValue = destination.stream().map(CalculationJobDestination::getTotalChanceCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP); - + var airfreightValue = destination.stream().map(CalculationJobDestination::getAnnualAirFreightCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP); + var worstValue = destination.stream().map(CalculationJobDestination::getTotalRiskCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP); + var bestValue = destination.stream().map(CalculationJobDestination::getTotalChanceCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP); var totalValue = annualAmount.equals(BigDecimal.ZERO) ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getTotalCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP); //var totalValue = weightedTotalCost.totalCost.divide(annualAmount, 2, RoundingMode.HALF_UP); - ReportEntryDTO airfreight = new ReportEntryDTO(); - airfreight.setTotal(airfreightValue); - airfreight.setPercentage(airfreightValue.divide(totalValue, 4, RoundingMode.HALF_UP)); - risk.put("air_freight_cost", airfreight); + totalValue = totalValue.add(airfreightValue.multiply(BigDecimal.valueOf(includeAirfreight ? 1 : 0))); + + ReportEntryDTO total = new ReportEntryDTO(); + total.setTotal(totalValue); + total.setPercentage(totalValue.divide(totalValue, 4, RoundingMode.HALF_UP)); + risk.put("mek_b", total); ReportEntryDTO worst = new ReportEntryDTO(); worst.setTotal(worstValue); worst.setPercentage(worstValue.divide(totalValue, 4, RoundingMode.HALF_UP)); - risk.put("worst_case_cost", worst); + risk.put("risk_scenario", worst); ReportEntryDTO best = new ReportEntryDTO(); best.setTotal(bestValue); best.setPercentage(bestValue.divide(totalValue, 4, RoundingMode.HALF_UP)); - risk.put("best_case_cost", best); + risk.put("opportunity_scenario", best); return risk; } - private Map getCostMap(CalculationJob job, List destination, WeightedTotalCosts weightedTotalCost) { + private Map getCostMap(CalculationJob job, List destination, WeightedTotalCosts weightedTotalCost, boolean includeAirfreight) { Map cost = new HashMap<>(); var annualAmount = destination.stream().map(CalculationJobDestination::getAnnualAmount).reduce(BigDecimal.ZERO, BigDecimal::add); + var airfreightValue = destination.stream().map(CalculationJobDestination::getAnnualAirFreightCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP); + var materialValue = destination.stream().map(CalculationJobDestination::getMaterialCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(destination.size()), 4, RoundingMode.HALF_UP); var fcaFeesValues = destination.stream().map(CalculationJobDestination::getFcaCost).reduce(BigDecimal.ZERO, BigDecimal::add); var repackingValues = annualAmount.equals(BigDecimal.ZERO) ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getAnnualRepackingCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP); @@ -217,30 +232,41 @@ public class ReportTransformer { var totalValue = annualAmount.equals(BigDecimal.ZERO) ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getTotalCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP); + if(includeAirfreight) { + totalValue =totalValue.add(airfreightValue); + } + ReportEntryDTO total = new ReportEntryDTO(); total.setTotal(totalValue); total.setPercentage(BigDecimal.valueOf(1)); cost.put("total", total); + if (includeAirfreight) { + ReportEntryDTO airfreight = new ReportEntryDTO(); + airfreight.setTotal(airfreightValue); + airfreight.setPercentage(airfreightValue.divide(totalValue, 4, RoundingMode.HALF_UP)); + cost.put("air_freight_cost", airfreight); + } + ReportEntryDTO preRun = new ReportEntryDTO(); preRun.setTotal(preRunValues); preRun.setPercentage(preRunValues.divide(totalValue, 4, RoundingMode.HALF_UP)); - cost.put("preRun", preRun); + cost.put("pre_run", preRun); ReportEntryDTO mainRun = new ReportEntryDTO(); mainRun.setTotal(mainRunValues); mainRun.setPercentage(mainRunValues.divide(totalValue, 4, RoundingMode.HALF_UP)); - cost.put("mainRun", mainRun); + cost.put("main_run", mainRun); ReportEntryDTO postRun = new ReportEntryDTO(); postRun.setTotal(postRunValues); postRun.setPercentage(postRunValues.divide(totalValue, 4, RoundingMode.HALF_UP)); - cost.put("postRun", postRun); + cost.put("post_run", postRun); ReportEntryDTO material = new ReportEntryDTO(); material.setTotal(materialValue); material.setPercentage(materialValue.divide(totalValue, 4, RoundingMode.HALF_UP)); - cost.put("material", material); + cost.put("mek_a", material); ReportEntryDTO custom = new ReportEntryDTO(); custom.setTotal(customValues); @@ -250,7 +276,7 @@ public class ReportTransformer { ReportEntryDTO fcaFees = new ReportEntryDTO(); fcaFees.setTotal(fcaFeesValues); fcaFees.setPercentage(fcaFeesValues.divide(totalValue, 4, RoundingMode.HALF_UP)); - cost.put("fcaFees", fcaFees); + cost.put("fca_fees", fcaFees); ReportEntryDTO repacking = new ReportEntryDTO(); repacking.setTotal(repackingValues);