FRONTEND: further bugfixing:
- start from scratch fixed. - resetting report store if reporting page is called. - destination incomplete warning fixed for d2d - price edit formatting fixed - report: differentiating between d2d, transport with/without mainrun. BACKEND: - user/groups: fixed the "/api/groups" <-> /api/groups/" error - added missing d2d result in a calculation result. - reporting: allow only valid calculations within reporting. - routing: fixing remove duplicate method. - replacing doubles with big decimals - ... etc
This commit is contained in:
parent
f885704dc9
commit
02249d2da4
30 changed files with 429 additions and 215 deletions
|
|
@ -159,7 +159,12 @@ export default {
|
|||
return names;
|
||||
},
|
||||
showDestinationIncomplete() {
|
||||
return this.premise.destinations.some(p => ((p.annual_amount ?? null) === null) || p.annual_amount === 0 || p.routes?.every(r => !r.is_selected))
|
||||
return this.premise.destinations.some(p => (
|
||||
(((p.annual_amount ?? null) === null) || p.annual_amount === 0 ||
|
||||
((p.routes?.every(r => !r.is_selected) && !p.is_d2d) ||
|
||||
(p.is_d2d && ((p.rate_d2d ?? null) === null || p.rate_d2d === 0 || (p.lead_time_d2d ?? null) === null || p.lead_time_d2d === 0))
|
||||
)
|
||||
)));
|
||||
},
|
||||
showDestinations() {
|
||||
return (this.destinationsCount > 0);
|
||||
|
|
|
|||
|
|
@ -56,11 +56,11 @@
|
|||
</div>
|
||||
|
||||
<div class="input-column-chk">
|
||||
<tooltip position="left"
|
||||
text="Deselect if the handling unit cannot be transported together with other handling units">
|
||||
<tooltip position="top"
|
||||
text="Uncheck for unmixed containers.">
|
||||
<checkbox :checked="mixable" @checkbox-changed="updateMixable">mixable</checkbox>
|
||||
</tooltip>
|
||||
<tooltip position="left" text="Deselect if the handling unit cannot be stacked">
|
||||
<tooltip position="left" text="Uncheck if the handling unit cannot be stacked">
|
||||
<checkbox :checked="stackable" @checkbox-changed="updateStackable" :disabled="mixable">stackable</checkbox>
|
||||
</tooltip>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<div class="container" @focusout="focusLost">
|
||||
<div @focusout="focusLost">
|
||||
<div class="container">
|
||||
<div class="caption-column">MEK_A [EUR]</div>
|
||||
<div class="input-column">
|
||||
<div class="text-container">
|
||||
|
|
@ -20,6 +21,7 @@
|
|||
<checkbox :checked="includeFcaFee" @checkbox-changed="updateIncludeFcaFee"></checkbox>
|
||||
</tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -59,32 +59,45 @@
|
|||
<div class="report-content-data-cell">{{ (report.costs.fca_fees.percentage * 100).toFixed(2) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="report-content-row">
|
||||
<div class="report-content-row" v-if="((report.costs.pre_run ?? null) !== null)">
|
||||
<div>Pre carriage</div>
|
||||
<div class="report-content-data-cell">{{ report.costs.pre_run.total.toFixed(2) }}</div>
|
||||
<div class="report-content-data-cell">{{ (report.costs.pre_run.percentage * 100).toFixed(2) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="report-content-row">
|
||||
<div class="report-content-row" v-if="((report.costs.main_run ?? null) !== null)">
|
||||
<div>Main run</div>
|
||||
<div class="report-content-data-cell">{{ report.costs.main_run.total.toFixed(2) }}</div>
|
||||
<div class="report-content-data-cell">{{ (report.costs.main_run.percentage * 100).toFixed(2) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="report-content-row" v-if="((report.costs.air_freight_cost ?? null) !== null)">
|
||||
<div>Airfreight cost</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>
|
||||
|
||||
<div class="report-content-row">
|
||||
<div class="report-content-row" v-if="((report.costs.post_run ?? null) !== null)">
|
||||
<div>Post carriage</div>
|
||||
<div class="report-content-data-cell">{{ report.costs.post_run.total.toFixed(2) }}</div>
|
||||
<div class="report-content-data-cell">{{ (report.costs.post_run.percentage * 100).toFixed(2) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="report-content-row" v-if="((report.costs.d2d ?? null) !== null)">
|
||||
<div>Door 2 door costs</div>
|
||||
<div class="report-content-data-cell">{{ report.costs.d2d.total.toFixed(2) }}</div>
|
||||
<div class="report-content-data-cell">{{ (report.costs.d2d.percentage * 100).toFixed(2) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="report-content-row" v-if="((report.costs.transport ?? null) !== null)">
|
||||
<div>Transportation costs</div>
|
||||
<div class="report-content-data-cell">{{ report.costs.transport.total.toFixed(2) }}</div>
|
||||
<div class="report-content-data-cell">{{ (report.costs.transport.percentage * 100).toFixed(2) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="report-content-row" v-if="((report.costs.air_freight_cost ?? null) !== null)">
|
||||
<div>Airfreight 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.percentage * 100).toFixed(2) }}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="report-content-row">
|
||||
<div>Custom duty</div>
|
||||
<div>Custom costs</div>
|
||||
<div class="report-content-data-cell">{{ report.costs.custom.total.toFixed(2) }}</div>
|
||||
<div class="report-content-data-cell">{{ (report.costs.custom.percentage * 100).toFixed(2) }}</div>
|
||||
</div>
|
||||
|
|
@ -102,22 +115,23 @@
|
|||
</div>
|
||||
|
||||
<div class="report-content-row">
|
||||
<div>Space cost</div>
|
||||
<div>Disposal costs</div>
|
||||
<div class="report-content-data-cell">{{ report.costs.disposal.total.toFixed(2) }}</div>
|
||||
<div class="report-content-data-cell">{{ (report.costs.disposal.percentage * 100).toFixed(2) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="report-content-row">
|
||||
<div>Space costs</div>
|
||||
<div class="report-content-data-cell">{{ report.costs.storage.total.toFixed(2) }}</div>
|
||||
<div class="report-content-data-cell">{{ (report.costs.storage.percentage * 100).toFixed(2) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="report-content-row">
|
||||
<div>Capital cost</div>
|
||||
<div>Capital costs</div>
|
||||
<div class="report-content-data-cell">{{ report.costs.capital.total.toFixed(2) }}</div>
|
||||
<div class="report-content-data-cell">{{ (report.costs.capital.percentage * 100).toFixed(2) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="report-content-row">
|
||||
<div>Disposal cost</div>
|
||||
<div class="report-content-data-cell">{{ report.costs.disposal.total.toFixed(2) }}</div>
|
||||
<div class="report-content-data-cell">{{ (report.costs.disposal.percentage * 100).toFixed(2) }}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</collapsible-box>
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@
|
|||
<h2 class="page-header">Create Calculation</h2>
|
||||
<div class="part-numbers-headers">
|
||||
<h3 class="sub-header">Part Numbers</h3>
|
||||
<basic-button icon="CloudArrowUp" :showIcon="true" @click="activateModal('partNumber')">Drop part numbers</basic-button>
|
||||
<basic-button icon="CloudArrowUp" :showIcon="true" @click="activateModal('partNumber')">Drop part numbers
|
||||
</basic-button>
|
||||
</div>
|
||||
|
||||
<ul class="item-list">
|
||||
<li class="item-list-element" v-for="material in assistantStore.materials" :key="material.id">
|
||||
<material-item :part-number="material['part_number']" :name="material.name" :id="material.id" @delete="deleteMaterial"></material-item>
|
||||
<material-item :part-number="material['part_number']" :name="material.name" :id="material.id"
|
||||
@delete="deleteMaterial"></material-item>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
|
@ -27,7 +29,8 @@
|
|||
<div class="supplier-headers">
|
||||
<h3 class="sub-header">Suppliers</h3>
|
||||
<div class="supplier-headers-searchbar-container">
|
||||
<autosuggest-searchbar @selected="selectedSupplier" placeholder="Search and add supplier ..." no-results-text='No supplier found for "{query}".' :fetch-suggestions="fetch"
|
||||
<autosuggest-searchbar @selected="selectedSupplier" placeholder="Search and add supplier ..."
|
||||
no-results-text='No supplier found for "{query}".' :fetch-suggestions="fetch"
|
||||
variant="flags" :reset-on-select="true"
|
||||
:flag-resolver="resolveFlag" title-resolver="name"
|
||||
subtitle-resolver="address"></autosuggest-searchbar>
|
||||
|
|
@ -39,7 +42,8 @@
|
|||
<ul class="item-list">
|
||||
<li class="item-list-element" v-for="supplier in assistantStore.suppliers" :key="supplier.id">
|
||||
<supplier-item :id="supplier.id" :iso-code="supplier.iso" :address="supplier.address"
|
||||
:name="supplier.name" @delete="deleteSupplier" :is-user-supplier="supplier.isUserSupplier"></supplier-item>
|
||||
:name="supplier.name" @delete="deleteSupplier"
|
||||
:is-user-supplier="supplier.isUserSupplier"></supplier-item>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
|
@ -53,8 +57,12 @@
|
|||
This will create {{ assistantStore.count }} calculations.
|
||||
</div>
|
||||
<div class="start-calculation-footer-right">
|
||||
<checkbox :checked="!this.assistantStore.createEmpty" @checkbox-changed="setUseExisting">Import data from existing calculations</checkbox>
|
||||
<basic-button :disabled="assistantStore.count==0" @click="createPremises" variant="secondary" :show-icon="false">Create</basic-button>
|
||||
<checkbox :checked="!this.assistantStore.createEmpty" @checkbox-changed="setUseExisting">Import data from
|
||||
existing calculations
|
||||
</checkbox>
|
||||
<basic-button :disabled="assistantStore.count==0" @click="createPremises" variant="secondary"
|
||||
:show-icon="false">Create
|
||||
</basic-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -75,6 +83,7 @@ import Modal from "@/components/UI/Modal.vue";
|
|||
import {useAssistantStore} from "@/store/assistant.js";
|
||||
import CreateNewNode from "@/components/layout/node/CreateNewNode.vue";
|
||||
import Checkbox from "@/components/UI/Checkbox.vue";
|
||||
import {UrlSafeBase64} from "@/common.js";
|
||||
|
||||
|
||||
export default {
|
||||
|
|
@ -98,7 +107,15 @@ export default {
|
|||
this.assistantStore.setCreateEmpty(!useExisting);
|
||||
},
|
||||
async createPremises() {
|
||||
await this.assistantStore.createPremises();
|
||||
|
||||
const ids = await this.assistantStore.createPremises();
|
||||
|
||||
if (ids.length === 1) {
|
||||
this.$router.push({name: "edit", params: {id: new UrlSafeBase64().encodeIds([ids[0]])}});
|
||||
} else {
|
||||
this.$router.push({name: "bulk", params: {ids: new UrlSafeBase64().encodeIds(ids)}});
|
||||
}
|
||||
|
||||
},
|
||||
selectedSupplier(supplier) {
|
||||
this.assistantStore.addSupplier(supplier);
|
||||
|
|
@ -163,7 +180,6 @@ export default {
|
|||
}
|
||||
|
||||
|
||||
|
||||
.part-number-drop-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
|||
|
|
@ -92,7 +92,8 @@ export default {
|
|||
|
||||
|
||||
} else if (action === "edit") {
|
||||
const ids = selectedPremisses.map(p => p.id);
|
||||
|
||||
const ids = selectedPremisses.filter(p => p.state === "DRAFT").map(p => p.id)
|
||||
|
||||
if (ids.length === 1) {
|
||||
this.$router.push({name: "edit", params: {id: new UrlSafeBase64().encodeIds([ids[0]])}});
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
</box>
|
||||
</div>
|
||||
<div v-else class="empty-container">
|
||||
<box><span class="space-around">No report selected</span></box>
|
||||
<box><span class="space-around">No report selected.</span></box>
|
||||
</div>
|
||||
|
||||
|
||||
|
|
@ -90,17 +90,16 @@ export default {
|
|||
return `${date[0]}-${date[1].toString().padStart(2, '0')}-${date[2].toString().padStart(2, '0')}`
|
||||
},
|
||||
closeModal(data) {
|
||||
console.log("closeModal: ", data.action)
|
||||
if (data.action === 'accept') {
|
||||
console.log("create report")
|
||||
this.reportsStore.fetchReports(data.materialId, data.supplierIds);
|
||||
}
|
||||
this.showModal = false;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (!this.hasReport)
|
||||
this.showModal = true;
|
||||
|
||||
this.reportsStore.reset();
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -142,7 +141,7 @@ export default {
|
|||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 1.6rem;
|
||||
font-weight: 500;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.report-spinner-container {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ export const useAssistantStore = defineStore('assistant', {
|
|||
error: null,
|
||||
pagination: {},
|
||||
query: {},
|
||||
premises: null
|
||||
}),
|
||||
getters: {
|
||||
count: state => state.materials.length * state.suppliers.length,
|
||||
|
|
@ -28,7 +27,7 @@ export const useAssistantStore = defineStore('assistant', {
|
|||
const materialIds = this.materials.map((material) => material.id);
|
||||
const supplierIds = this.suppliers.filter(s => s.id.startsWith('s')).map((supplier) => supplier.origId);
|
||||
const userSupplierIds = this.suppliers.filter(s => s.id.startsWith('u')).map((supplier) => supplier.origId);
|
||||
const jsonBody = JSON.stringify({ material: materialIds, supplier: supplierIds, user_supplier: userSupplierIds, createEmpty: this.createEmpty });
|
||||
const jsonBody = JSON.stringify({ material: materialIds, supplier: supplierIds, user_supplier: userSupplierIds, from_scratch: this.createEmpty });
|
||||
|
||||
console.log(`Creation body: ${jsonBody}`);
|
||||
|
||||
|
|
@ -80,8 +79,8 @@ export const useAssistantStore = defineStore('assistant', {
|
|||
return;
|
||||
}
|
||||
|
||||
this.premises = data;
|
||||
console.log(this.premises);
|
||||
|
||||
return data.map(p => p.id);
|
||||
|
||||
|
||||
},
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ export const useReportsStore = defineStore('reports', {
|
|||
}
|
||||
},
|
||||
actions: {
|
||||
reset() {
|
||||
this.reports = [];
|
||||
},
|
||||
async fetchReports(materialId, supplierIds) {
|
||||
if (supplierIds == null || materialId == null) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
package de.avatic.lcc.calculationmodel;
|
||||
|
||||
import de.avatic.lcc.model.calculations.CalculationJobDestination;
|
||||
import de.avatic.lcc.model.premises.route.Destination;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record DestinationInfo(Destination destination, CalculationJobDestination destinationCalculationJob,
|
||||
List<SectionInfo> sectionInfo) {
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ public class GroupController {
|
|||
* @param page The index of the page to retrieve. Defaults to 0 (first page).
|
||||
* @return A ResponseEntity containing the list of groups and pagination headers.
|
||||
*/
|
||||
@GetMapping("/")
|
||||
@GetMapping({"/", ""})
|
||||
public ResponseEntity<List<GroupDTO>> listGroups(@RequestParam(defaultValue = "20") @Min(1) int limit,
|
||||
@RequestParam(defaultValue = "1") @Min(1) int page) {
|
||||
|
||||
|
|
@ -44,16 +44,4 @@ public class GroupController {
|
|||
.body(groups.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the details of an existing group.
|
||||
*
|
||||
* @param group The DTO containing the updated group information.
|
||||
* @return A ResponseEntity indicating the operation status.
|
||||
*/
|
||||
@PutMapping("/")
|
||||
public ResponseEntity<Void> updateGroup(GroupDTO group) {
|
||||
groupService.updateGroup(group);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ public class UserController {
|
|||
* @param page The page number of the users to retrieve, with a default value of 0.
|
||||
* @return A ResponseEntity containing the list of users, along with pagination headers.
|
||||
*/
|
||||
@GetMapping("/")
|
||||
@GetMapping({"/", ""})
|
||||
public ResponseEntity<List<UserDTO>> listUsers(
|
||||
@RequestParam(defaultValue = "20") @Min(1) int limit,
|
||||
@RequestParam(defaultValue = "0") @Min(1) int page) {
|
||||
@RequestParam(defaultValue = "1") @Min(1) int page) {
|
||||
|
||||
SearchQueryResult<UserDTO> users = userService.listUsers(page, limit);
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ public class UserController {
|
|||
* @param user A UserDTO object containing the updated user details.
|
||||
* @return A ResponseEntity indicating the operation was successful.
|
||||
*/
|
||||
@PutMapping("/")
|
||||
@PutMapping({"/", ""})
|
||||
public ResponseEntity<Void> updateUser(UserDTO user) {
|
||||
userService.updateUser(user);
|
||||
return ResponseEntity.ok().build();
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ public class CalculationJobDestination {
|
|||
private Integer layerCount;
|
||||
private Boolean transportWeightExceeded;
|
||||
private BigDecimal annualTransportationCost;
|
||||
private Boolean isD2D;
|
||||
private BigDecimal rateD2D;
|
||||
|
||||
private BigDecimal containerUtilization;
|
||||
|
||||
|
|
@ -373,4 +375,20 @@ public class CalculationJobDestination {
|
|||
public BigDecimal getSafetyStockInDays() {
|
||||
return safetyStockInDays;
|
||||
}
|
||||
|
||||
public Boolean getD2D() {
|
||||
return isD2D;
|
||||
}
|
||||
|
||||
public void setD2D(Boolean d2D) {
|
||||
isD2D = d2D;
|
||||
}
|
||||
|
||||
public BigDecimal getRateD2D() {
|
||||
return rateD2D;
|
||||
}
|
||||
|
||||
public void setRateD2D(BigDecimal rateD2D) {
|
||||
this.rateD2D = rateD2D;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
package de.avatic.lcc.model.calculations;
|
||||
|
||||
import de.avatic.lcc.calculationmodel.DestinationInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CalculationResult {
|
||||
|
||||
private List<DestinationInfo> destinationInfos;
|
||||
Integer jobId;
|
||||
|
||||
CalculationJobState state;
|
||||
|
|
@ -9,10 +14,11 @@ public class CalculationResult {
|
|||
Throwable exception;
|
||||
|
||||
|
||||
public CalculationResult(Integer jobId) {
|
||||
public CalculationResult(Integer jobId, List<DestinationInfo> destinationInfos) {
|
||||
this.jobId = jobId;
|
||||
this.exception = null;
|
||||
this.state = CalculationJobState.VALID;
|
||||
this.destinationInfos = destinationInfos;
|
||||
}
|
||||
|
||||
public CalculationResult(Integer jobId, Throwable e) {
|
||||
|
|
@ -44,4 +50,8 @@ public class CalculationResult {
|
|||
public void setJobId(Integer jobId) {
|
||||
this.jobId = jobId;
|
||||
}
|
||||
|
||||
public List<DestinationInfo> getDestinationInfos() {
|
||||
return destinationInfos;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -393,6 +393,7 @@ public class NodeRepository {
|
|||
AND cj.validity_period_id = ?
|
||||
AND cj.property_set_id = ?
|
||||
AND p.supplier_node_id IS NOT NULL
|
||||
AND cj.job_state = 'VALID'
|
||||
""";
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ public class CalculationJobDestinationRepository {
|
|||
tariff_rate, annual_custom_cost, air_freight_share_max, air_freight_share, air_freight_volumetric_weight,
|
||||
air_freight_weight, annual_air_freight_cost, container_type, hu_count, layer_structure,
|
||||
layer_count, transport_weight_exceeded, annual_transportation_cost, container_utilization,
|
||||
transit_time_in_days, safety_stock_in_days, material_cost, fca_cost
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
transit_time_in_days, safety_stock_in_days, material_cost, fca_cost, is_d2d, rate_d2d
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""";
|
||||
|
||||
KeyHolder keyHolder = new GeneratedKeyHolder();
|
||||
|
|
@ -109,6 +109,9 @@ public class CalculationJobDestinationRepository {
|
|||
ps.setObject(paramIndex++, destination.getMaterialCost());
|
||||
ps.setObject(paramIndex++, destination.getFcaCost());
|
||||
|
||||
ps.setObject(paramIndex++, destination.getD2D());
|
||||
ps.setObject(paramIndex++, destination.getRateD2D());
|
||||
|
||||
return ps;
|
||||
}, keyHolder);
|
||||
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ public class UserNodeRepository {
|
|||
AND cj.validity_period_id = ?
|
||||
AND cj.property_set_id = ?
|
||||
AND p.user_supplier_node_id IS NOT NULL
|
||||
AND cj.job_state = 'VALID'
|
||||
""";
|
||||
|
||||
return jdbcTemplate.query(userSuppliersSql, new NodeMapper(), materialId, tuple.periodId(), tuple.propertySetId());
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ public class UserRepository {
|
|||
|
||||
updateUserGroupMappings(userId, groupIds);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private List<Integer> findGroupIds(List<String> groups) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import java.math.BigDecimal;
|
|||
@Service
|
||||
public class CustomApiService {
|
||||
public BigDecimal getTariffRate(String hsCode, Integer countryId) {
|
||||
return BigDecimal.valueOf(0.3);
|
||||
return BigDecimal.valueOf(0.03);
|
||||
|
||||
//TODO implement me
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,19 +10,23 @@ import de.avatic.lcc.model.calculations.CalculationJob;
|
|||
import de.avatic.lcc.model.calculations.CalculationJobState;
|
||||
import de.avatic.lcc.model.premises.Premise;
|
||||
import de.avatic.lcc.model.premises.PremiseState;
|
||||
import de.avatic.lcc.repositories.calculation.CalculationJobDestinationRepository;
|
||||
import de.avatic.lcc.repositories.calculation.CalculationJobRepository;
|
||||
import de.avatic.lcc.repositories.calculation.CalculationJobRouteSectionRepository;
|
||||
import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
|
||||
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
|
||||
import de.avatic.lcc.repositories.premise.PremiseRepository;
|
||||
import de.avatic.lcc.repositories.properties.PropertyRepository;
|
||||
import de.avatic.lcc.repositories.properties.PropertySetRepository;
|
||||
import de.avatic.lcc.repositories.rates.ValidityPeriodRepository;
|
||||
import de.avatic.lcc.service.calculation.execution.CalculationExecutionService;
|
||||
import de.avatic.lcc.service.calculation.execution.CalculationStatusService;
|
||||
import de.avatic.lcc.service.precalculation.PostCalculationCheckService;
|
||||
import de.avatic.lcc.service.precalculation.PreCalculationCheckService;
|
||||
import de.avatic.lcc.service.transformer.generic.DimensionTransformer;
|
||||
import de.avatic.lcc.service.transformer.premise.PremiseTransformer;
|
||||
import de.avatic.lcc.util.exception.base.InternalErrorException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
|
@ -36,30 +40,39 @@ import java.util.concurrent.ExecutionException;
|
|||
@Service
|
||||
public class PremisesService {
|
||||
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PremisesService.class);
|
||||
|
||||
private final PremiseRepository premiseRepository;
|
||||
private final PremiseTransformer premiseTransformer;
|
||||
private final DimensionTransformer dimensionTransformer;
|
||||
private final DestinationService destinationService;
|
||||
private final CalculationJobRepository calculationJobRepository;
|
||||
private final PropertyRepository propertyRepository;
|
||||
|
||||
private final PropertySetRepository propertySetRepository;
|
||||
private final ValidityPeriodRepository validityPeriodRepository;
|
||||
private final CalculationStatusService calculationStatusService;
|
||||
private final CalculationExecutionService calculationExecutionService;
|
||||
private final PreCalculationCheckService preCalculationCheckService;
|
||||
private final CalculationJobDestinationRepository calculationJobDestinationRepository;
|
||||
private final CalculationJobRouteSectionRepository calculationJobRouteSectionRepository;
|
||||
private final PostCalculationCheckService postCalculationCheckService;
|
||||
|
||||
public PremisesService(PremiseRepository premiseRepository, PremiseTransformer premiseTransformer, DimensionTransformer dimensionTransformer, DestinationService destinationService, CalculationJobRepository calculationJobRepository, PropertyRepository propertyRepository, PropertySetRepository propertySetRepository, ValidityPeriodRepository validityPeriodRepository, CalculationStatusService calculationStatusService, CalculationExecutionService calculationExecutionService, PreCalculationCheckService preCalculationCheckService) {
|
||||
public PremisesService(PremiseRepository premiseRepository, PremiseTransformer premiseTransformer, DimensionTransformer dimensionTransformer, DestinationService destinationService, CalculationJobRepository calculationJobRepository, PropertySetRepository propertySetRepository, ValidityPeriodRepository validityPeriodRepository, CalculationStatusService calculationStatusService, CalculationExecutionService calculationExecutionService, PreCalculationCheckService preCalculationCheckService, CalculationJobDestinationRepository calculationJobDestinationRepository, CalculationJobRouteSectionRepository calculationJobRouteSectionRepository, PostCalculationCheckService postCalculationCheckService) {
|
||||
this.premiseRepository = premiseRepository;
|
||||
this.premiseTransformer = premiseTransformer;
|
||||
this.dimensionTransformer = dimensionTransformer;
|
||||
this.destinationService = destinationService;
|
||||
this.calculationJobRepository = calculationJobRepository;
|
||||
this.propertyRepository = propertyRepository;
|
||||
|
||||
this.propertySetRepository = propertySetRepository;
|
||||
this.validityPeriodRepository = validityPeriodRepository;
|
||||
this.calculationStatusService = calculationStatusService;
|
||||
this.calculationExecutionService = calculationExecutionService;
|
||||
this.preCalculationCheckService = preCalculationCheckService;
|
||||
this.calculationJobDestinationRepository = calculationJobDestinationRepository;
|
||||
this.calculationJobRouteSectionRepository = calculationJobRouteSectionRepository;
|
||||
this.postCalculationCheckService = postCalculationCheckService;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
|
|
@ -122,12 +135,32 @@ public class PremisesService {
|
|||
for (var future : futures) {
|
||||
var jobResult = future.get();
|
||||
if (jobResult.getState().equals(CalculationJobState.EXCEPTION)) {
|
||||
throw new InternalErrorException("Calculation failed: " + jobResult.getException().getMessage(), new Exception(jobResult.getException()));
|
||||
calculationJobRepository.setStateTo(jobResult.getJobId(), CalculationJobState.EXCEPTION);
|
||||
throw new InternalErrorException("Execution of calculation was not successful. Please contact Administrator.", new Exception(jobResult.getException()));
|
||||
} else {
|
||||
|
||||
postCalculationCheckService.doPostcheck(jobResult);
|
||||
|
||||
for (var destinationInfo : jobResult.getDestinationInfos()) {
|
||||
destinationInfo.destinationCalculationJob().setCalculationJobId(jobResult.getJobId());
|
||||
var destinationId = calculationJobDestinationRepository.insert(destinationInfo.destinationCalculationJob());
|
||||
|
||||
for (var sectionInfo : destinationInfo.sectionInfo()) {
|
||||
var section = sectionInfo.result();
|
||||
section.setCalculationJobDestinationId(destinationId);
|
||||
calculationJobRouteSectionRepository.insert(section);
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
calculationJobRepository.setStateTo(jobResult.getJobId(), CalculationJobState.VALID);
|
||||
log.info("Calculation job {} finished", jobResult.getJobId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch (CompletionException | InterruptedException | ExecutionException e) {
|
||||
throw new InternalErrorException("Calculation failed", e);
|
||||
throw new InternalErrorException("Calculation execution failed.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -181,10 +214,15 @@ public class PremisesService {
|
|||
|
||||
// only delete drafts.
|
||||
var toBeDeleted = premiseRepository.getPremisesById(premiseIds).stream().filter(p -> p.getState().equals(PremiseState.DRAFT)).map(Premise::getId).toList();
|
||||
|
||||
if(!toBeDeleted.isEmpty())
|
||||
{
|
||||
destinationService.deleteAllDestinationsByPremiseId(toBeDeleted, false);
|
||||
premiseRepository.deletePremisesById(toBeDeleted);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void archive(List<Integer> premiseIds) {
|
||||
//TODO check authorization
|
||||
var userId = 1;
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ public class ChangeSupplierService {
|
|||
|
||||
|
||||
//delete all conflicting premises:
|
||||
if(!premisesToBeDeleted.isEmpty())
|
||||
premisesService.delete(premisesToBeDeleted.stream().map(Premise::getId).toList());
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -99,11 +99,14 @@ public class RoutingService {
|
|||
*/
|
||||
connectDestinationChainDirectly(container, destinationChains);
|
||||
|
||||
/* remove duplicates. */
|
||||
removeDuplicateRoutes(container.getRoutes());
|
||||
/*
|
||||
* finally find and mark the fastest and the cheapest route.
|
||||
*/
|
||||
findAndMarkCheapestAndFastest(container);
|
||||
|
||||
|
||||
container.getRoutes().stream().map(TemporaryRouteObject::getFullDebugText).forEach(s -> log.info("Found route: {}", s));
|
||||
|
||||
|
||||
|
|
@ -475,23 +478,33 @@ public class RoutingService {
|
|||
|
||||
for (var route : routes) {
|
||||
var sections = route.getSections();
|
||||
boolean hasNearByRouting = sections.getLast().getType().equals(TemporaryRateObject.TemporaryRateObjectType.NEAR_BY);
|
||||
|
||||
|
||||
if(sections.getLast().getType().equals(TemporaryRateObject.TemporaryRateObjectType.NEAR_BY)) {
|
||||
for (var other : routes) {
|
||||
|
||||
//skip same instance.
|
||||
if (other.equals(route)) continue;
|
||||
if (toRemove.contains(other)) continue;
|
||||
|
||||
var otherIter = other.getSections().iterator();
|
||||
var iter = sections.iterator();
|
||||
boolean same = true;
|
||||
|
||||
TemporaryRateObject lastSection = null;
|
||||
while (otherIter.hasNext() && iter.hasNext()) {
|
||||
var otherSection = otherIter.next();
|
||||
var section = iter.next();
|
||||
|
||||
if (!otherSection.hasSameNodes(section)) {
|
||||
same = false;
|
||||
same = otherSection.hasSameNodes(section);
|
||||
|
||||
if(!same && hasNearByRouting && iter.hasNext()) {
|
||||
lastSection = section;
|
||||
section = iter.next();
|
||||
same = section.getType().equals(TemporaryRateObject.TemporaryRateObjectType.NEAR_BY) && otherSection.hasSameNodes(section, lastSection);
|
||||
}
|
||||
|
||||
if(!same) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -501,7 +514,7 @@ public class RoutingService {
|
|||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
routes.removeAll(toRemove);
|
||||
|
|
@ -691,6 +704,7 @@ public class RoutingService {
|
|||
*/
|
||||
private final Node source;
|
||||
private final Node destination;
|
||||
private final Map<Integer, List<List<Node>>> chains = new HashMap<>();
|
||||
/*
|
||||
* mainRuns (maps start node to rate) retrieved from the database.
|
||||
*/
|
||||
|
|
@ -699,11 +713,8 @@ public class RoutingService {
|
|||
* postRuns (maps main_run container rate id to post_run container rate id) retrieved from the database.
|
||||
*/
|
||||
private Map<Integer, List<ContainerRate>> postRuns;
|
||||
|
||||
private MatrixRate sourceMatrixRate;
|
||||
|
||||
private final Map<Integer, List<List<Node>>> chains = new HashMap<>();
|
||||
|
||||
public TemporaryContainer(Node source, Node destination) {
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
|
|
@ -1011,6 +1022,10 @@ public class RoutingService {
|
|||
return fromNode.getId().equals(section.getFromNode().getId()) && toNode.getId().equals(section.getToNode().getId());
|
||||
}
|
||||
|
||||
public boolean hasSameNodes(TemporaryRateObject firstSection, TemporaryRateObject secondSection) {
|
||||
return fromNode.getId().equals(firstSection.getFromNode().getId()) && toNode.getId().equals(secondSection.getToNode().getId());
|
||||
}
|
||||
|
||||
private enum TemporaryRateObjectType {
|
||||
NEAR_BY("\uD83D\uDCCD"), MATRIX("\uD83D\uDCCA"), CONTAINER("\uD83D\uDCE6"), POST_RUN("\uD83D\uDE9B"), MAIN_RUN("\uD83D\uDEA2");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package de.avatic.lcc.service.calculation.execution;
|
||||
|
||||
import de.avatic.lcc.calculationmodel.*;
|
||||
import de.avatic.lcc.dto.calculation.CalculationStatus;
|
||||
import de.avatic.lcc.dto.generic.ContainerType;
|
||||
import de.avatic.lcc.dto.generic.RateType;
|
||||
import de.avatic.lcc.model.calculations.*;
|
||||
|
|
@ -88,16 +87,13 @@ public class CalculationExecutionService {
|
|||
@Async("taskExecutor")
|
||||
public CompletableFuture<CalculationResult> launchJobCalculation(Integer calculationId) {
|
||||
try {
|
||||
calculateJob(calculationId);
|
||||
return CompletableFuture.completedFuture(new CalculationResult(calculationId, calculateJob(calculationId)));
|
||||
} catch (Throwable e) {
|
||||
//TODO put error in database.
|
||||
calculationJobRepository.setStateTo(calculationId, CalculationJobState.EXCEPTION);
|
||||
return CompletableFuture.completedFuture(new CalculationResult(calculationId, e));
|
||||
}
|
||||
return CompletableFuture.completedFuture(new CalculationResult(calculationId));
|
||||
}
|
||||
|
||||
public void calculateJob(Integer calculationId) {
|
||||
public List<DestinationInfo> calculateJob(Integer calculationId) {
|
||||
|
||||
CalculationJob calculation = calculationJobRepository.getCalculationJob(calculationId).orElseThrow();
|
||||
|
||||
|
|
@ -117,24 +113,10 @@ public class CalculationExecutionService {
|
|||
}
|
||||
|
||||
List<Destination> destinations = destinationRepository.getByPremiseId(premise.getId());
|
||||
var destinationInfos = destinations.stream().map(destination -> doDestinationCalculation(destination, premise, materialCost, fcaFee)).toList();
|
||||
|
||||
for (var destinationInfo : destinationInfos) {
|
||||
destinationInfo.destinationCalculationJob.setCalculationJobId(calculation.getId());
|
||||
var destinationId = calculationJobDestinationRepository.insert(destinationInfo.destinationCalculationJob);
|
||||
|
||||
for (var sectionInfo : destinationInfo.sectionInfo) {
|
||||
var section = sectionInfo.result();
|
||||
section.setCalculationJobDestinationId(destinationId);
|
||||
calculationJobRouteSectionRepository.insert(section);
|
||||
}
|
||||
return destinations.stream().map(destination -> doDestinationCalculation(destination, premise, materialCost, fcaFee)).toList();
|
||||
}
|
||||
|
||||
calculationJobRepository.setStateTo(calculationId, CalculationJobState.VALID);
|
||||
log.info("Calculation job {} finished", calculationId);
|
||||
}
|
||||
|
||||
|
||||
throw new IllegalStateException("Calculation job is not scheduled");
|
||||
}
|
||||
|
||||
private DestinationInfo doDestinationCalculation(Destination destination, Premise premise, BigDecimal materialCost, BigDecimal fcaFee) {
|
||||
|
|
@ -161,7 +143,21 @@ public class CalculationExecutionService {
|
|||
usedContainerType = bestContainerTypeResult.containerType;
|
||||
hasMainRun = sections.stream().anyMatch(s -> s.section().getMainRun());
|
||||
leadTime = BigDecimal.valueOf(sections.stream().mapToInt(s -> s.result().getTransitTime()).sum());
|
||||
|
||||
/* inner eu routing */
|
||||
if(!hasMainRun) {
|
||||
sections.forEach(s -> {
|
||||
s.result().setMainRun(false);
|
||||
s.result().setPreRun(false);
|
||||
s.result().setPostRun(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
destinationCalculationJob.setD2D(destination.getD2d());
|
||||
|
||||
if(destination.getD2d())
|
||||
destinationCalculationJob.setRateD2D(destination.getRateD2d());
|
||||
|
||||
customCost = customCostCalculationService.doCalculation(premise, destination, sections);
|
||||
handlingCost = handlingCostCalculationService.doCalculation(premise, destination, hasMainRun);
|
||||
|
|
@ -226,6 +222,7 @@ public class CalculationExecutionService {
|
|||
|
||||
destinationCalculationJob.setPremiseDestinationId(destination.getId());
|
||||
|
||||
|
||||
return new DestinationInfo(destination, destinationCalculationJob, sections);
|
||||
}
|
||||
|
||||
|
|
@ -264,7 +261,4 @@ public class CalculationExecutionService {
|
|||
private record BestContainerTypeResult(ContainerType containerType, List<SectionInfo> sections) {
|
||||
}
|
||||
|
||||
private record DestinationInfo(Destination destination, CalculationJobDestination destinationCalculationJob,
|
||||
List<SectionInfo> sectionInfo) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import de.avatic.lcc.repositories.properties.PropertyRepository;
|
|||
import org.jetbrains.annotations.Debug.Renderer;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -49,9 +51,9 @@ public class ContainerCalculationService {
|
|||
*/
|
||||
public ContainerCalculationResult doCalculation(PackagingDimension hu, ContainerType containerType) {
|
||||
|
||||
var weightInKg = WeightUnit.KG.convertFromG(hu.getWeight()).intValue();
|
||||
var maxContainerLoad = getMaxContainerLoad(containerType);
|
||||
var maxUnitByWeight = maxContainerLoad / weightInKg;
|
||||
var weightInKg = BigDecimal.valueOf(WeightUnit.KG.convertFromG(hu.getWeight()));
|
||||
var maxContainerLoad = BigDecimal.valueOf(getMaxContainerLoad(containerType));
|
||||
var maxUnitByWeight = maxContainerLoad.divide(weightInKg, 0, RoundingMode.HALF_UP).intValueExact();
|
||||
|
||||
var dimensions = hu.withTolerance(DIMENSION_TOLERANCE);
|
||||
|
||||
|
|
@ -61,12 +63,12 @@ public class ContainerCalculationService {
|
|||
int layers = getLayerCount(dimensions, containerType);
|
||||
|
||||
if(PalletType.EURO_PALLET.fitsOn(hu) && bestSolution.getTotal() < containerType.getPalletCount(PalletType.EURO_PALLET)) {
|
||||
return new ContainerCalculationResult(Math.min(containerType.getPalletCount(PalletType.EURO_PALLET)*layers,maxUnitByWeight), layers, null, containerType.getPalletCount(PalletType.EURO_PALLET) > maxUnitByWeight, containerType, dimensions, maxContainerLoad);
|
||||
return new ContainerCalculationResult(Math.min(containerType.getPalletCount(PalletType.EURO_PALLET)*layers,maxUnitByWeight), layers, null, containerType.getPalletCount(PalletType.EURO_PALLET) > maxUnitByWeight, containerType, dimensions, maxContainerLoad.intValueExact());
|
||||
} else if(PalletType.INDUSTRIAL_PALLET.fitsOn(hu) && bestSolution.getTotal() < containerType.getPalletCount(PalletType.INDUSTRIAL_PALLET)) {
|
||||
return new ContainerCalculationResult(Math.min(containerType.getPalletCount(PalletType.INDUSTRIAL_PALLET)*layers,maxUnitByWeight), layers, null, containerType.getPalletCount(PalletType.INDUSTRIAL_PALLET) > maxUnitByWeight, containerType, dimensions, maxContainerLoad);
|
||||
return new ContainerCalculationResult(Math.min(containerType.getPalletCount(PalletType.INDUSTRIAL_PALLET)*layers,maxUnitByWeight), layers, null, containerType.getPalletCount(PalletType.INDUSTRIAL_PALLET) > maxUnitByWeight, containerType, dimensions, maxContainerLoad.intValueExact());
|
||||
}
|
||||
|
||||
return createContainerCalculationResult(bestSolution, layers, maxUnitByWeight, containerType, dimensions, maxContainerLoad);
|
||||
return createContainerCalculationResult(bestSolution, layers, maxUnitByWeight, containerType, dimensions, maxContainerLoad.intValueExact());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -37,48 +37,71 @@ public class HandlingCostCalculationService {
|
|||
}
|
||||
|
||||
private HandlingResult getSLCCost(Destination destination, PackagingDimension hu, LoadCarrierType loadCarrierType, boolean addRepackingCosts) {
|
||||
int huAnnualAmount = destination.getAnnualAmount() / hu.getContentUnitCount();
|
||||
double handling = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_HANDLING).orElseThrow().getCurrentValue());
|
||||
double release = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_RELEASE).orElseThrow().getCurrentValue());
|
||||
double dispatch = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_DISPATCH).orElseThrow().getCurrentValue());
|
||||
double wageFactor = Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.WAGE, destination.getCountryId()).orElseThrow().getCurrentValue());
|
||||
double booking = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING).orElseThrow().getCurrentValue());
|
||||
|
||||
var destinationHandling = destination.getHandlingCost();
|
||||
var destinationDisposal = destination.getDisposalCost();
|
||||
var destinationRepacking = destination.getRepackingCost();
|
||||
|
||||
|
||||
// BigDecimal huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(hu.getContentUnitCount()),4,RoundingMode.UP);
|
||||
// double handling = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_HANDLING).orElseThrow().getCurrentValue());
|
||||
// double release = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_RELEASE).orElseThrow().getCurrentValue());
|
||||
// double dispatch = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_DISPATCH).orElseThrow().getCurrentValue());
|
||||
// double wageFactor = Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.WAGE, destination.getCountryId()).orElseThrow().getCurrentValue());
|
||||
// double booking = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING).orElseThrow().getCurrentValue());
|
||||
|
||||
BigDecimal huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(hu.getContentUnitCount()),4, RoundingMode.UP );
|
||||
BigDecimal handling = destinationHandling != null ? destinationHandling : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_HANDLING).orElseThrow().getCurrentValue()));
|
||||
BigDecimal release = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_RELEASE).orElseThrow().getCurrentValue()));
|
||||
BigDecimal dispatch = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_DISPATCH).orElseThrow().getCurrentValue()));
|
||||
BigDecimal disposal = destinationDisposal != null ? destinationDisposal : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.DISPOSAL).orElseThrow().getCurrentValue()));
|
||||
|
||||
BigDecimal wageFactor = BigDecimal.valueOf(Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.WAGE, destination.getCountryId()).orElseThrow().getCurrentValue()));
|
||||
BigDecimal booking = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING).orElseThrow().getCurrentValue()));
|
||||
|
||||
return new HandlingResult(LoadCarrierType.SLC,
|
||||
BigDecimal.valueOf(getRepackingCost(hu, loadCarrierType, addRepackingCosts) * huAnnualAmount),
|
||||
BigDecimal.valueOf(handling*huAnnualAmount),
|
||||
BigDecimal.ZERO,
|
||||
BigDecimal.valueOf(huAnnualAmount * (handling + booking + release + dispatch + getRepackingCost(hu, loadCarrierType, addRepackingCosts)) * wageFactor));
|
||||
getRepackingCost(hu, loadCarrierType, addRepackingCosts, destinationRepacking).multiply(huAnnualAmount),
|
||||
handling.multiply(huAnnualAmount),
|
||||
destinationDisposal == null ? BigDecimal.ZERO : (disposal.multiply(huAnnualAmount)), //TODO: disposal SLC, ignore?
|
||||
huAnnualAmount.multiply((handling.add(booking).add(release).add(dispatch).add(getRepackingCost(hu, loadCarrierType, addRepackingCosts, destinationRepacking)))).multiply(wageFactor));
|
||||
|
||||
}
|
||||
|
||||
private double getRepackingCost(PackagingDimension hu, LoadCarrierType type, boolean addRepackingCosts) {
|
||||
private BigDecimal getRepackingCost(PackagingDimension hu, LoadCarrierType type, boolean addRepackingCosts, BigDecimal userInput) {
|
||||
|
||||
if (!addRepackingCosts)
|
||||
return 0;
|
||||
return BigDecimal.ZERO;
|
||||
|
||||
if(userInput != null)
|
||||
return userInput;
|
||||
|
||||
return switch (type) {
|
||||
case SLC ->
|
||||
Double.parseDouble((hu.getWeight() < 15_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_S).orElseThrow().getCurrentValue() : propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_M).orElseThrow().getCurrentValue());
|
||||
BigDecimal.valueOf(Double.parseDouble((hu.getWeight() < 15_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_S).orElseThrow().getCurrentValue() : propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_M).orElseThrow().getCurrentValue()));
|
||||
case LLC ->
|
||||
Double.parseDouble(((hu.getWeight() < 15_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_S) : (hu.getWeight() < 2_000_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_M) : propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_L)).orElseThrow().getCurrentValue());
|
||||
BigDecimal.valueOf(Double.parseDouble(((hu.getWeight() < 15_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_S) : (hu.getWeight() < 2_000_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_M) : propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_L)).orElseThrow().getCurrentValue()));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private HandlingResult getLLCCost(Destination destination, PackagingDimension hu, LoadCarrierType type, boolean addRepackingCosts) {
|
||||
double huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(hu.getContentUnitCount()),2, RoundingMode.UP ).doubleValue();
|
||||
double handling = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_HANDLING).orElseThrow().getCurrentValue());
|
||||
double release = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_RELEASE).orElseThrow().getCurrentValue());
|
||||
double dispatch = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_DISPATCH).orElseThrow().getCurrentValue());
|
||||
double disposal = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.DISPOSAL).orElseThrow().getCurrentValue());
|
||||
|
||||
double wageFactor = Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.WAGE, destination.getCountryId()).orElseThrow().getCurrentValue());
|
||||
double booking = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING).orElseThrow().getCurrentValue());
|
||||
var destinationHandling = destination.getHandlingCost();
|
||||
var destinationDisposal = destination.getDisposalCost();
|
||||
var destinationRepacking = destination.getRepackingCost();
|
||||
|
||||
var annualRepacking = BigDecimal.valueOf(getRepackingCost(hu, type, addRepackingCosts) * wageFactor * huAnnualAmount);
|
||||
var annualHandling = BigDecimal.valueOf((handling+dispatch+release)*wageFactor*huAnnualAmount + (booking * shippingFrequencyCalculationService.doCalculation(huAnnualAmount)));
|
||||
var annualDisposal = BigDecimal.valueOf(disposal * huAnnualAmount);
|
||||
BigDecimal huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(hu.getContentUnitCount()),4, RoundingMode.UP );
|
||||
BigDecimal handling = destinationHandling != null ? destinationHandling : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_HANDLING).orElseThrow().getCurrentValue()));
|
||||
BigDecimal release = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_RELEASE).orElseThrow().getCurrentValue()));
|
||||
BigDecimal dispatch = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_DISPATCH).orElseThrow().getCurrentValue()));
|
||||
BigDecimal disposal = destinationDisposal != null ? destinationDisposal : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.DISPOSAL).orElseThrow().getCurrentValue()));
|
||||
|
||||
BigDecimal wageFactor = BigDecimal.valueOf(Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.WAGE, destination.getCountryId()).orElseThrow().getCurrentValue()));
|
||||
BigDecimal booking = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING).orElseThrow().getCurrentValue()));
|
||||
|
||||
var annualRepacking = getRepackingCost(hu, type, addRepackingCosts, destinationRepacking).multiply(wageFactor).multiply( huAnnualAmount);
|
||||
var annualHandling = ((handling.add(dispatch).add(release)).multiply(wageFactor).multiply(huAnnualAmount)).add(booking.multiply(BigDecimal.valueOf(shippingFrequencyCalculationService.doCalculation(huAnnualAmount.doubleValue()))));
|
||||
var annualDisposal = (disposal.multiply(huAnnualAmount));
|
||||
|
||||
return new HandlingResult(LoadCarrierType.LLC, annualRepacking, annualHandling, annualDisposal, annualRepacking.add(annualHandling).add(annualDisposal));
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ 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.nodes.Location;
|
||||
import de.avatic.lcc.model.nodes.Node;
|
||||
import de.avatic.lcc.model.premises.Premise;
|
||||
import de.avatic.lcc.model.premises.route.Destination;
|
||||
import de.avatic.lcc.model.premises.route.RouteNode;
|
||||
|
|
@ -15,6 +16,7 @@ import de.avatic.lcc.model.rates.ContainerRate;
|
|||
import de.avatic.lcc.model.rates.MatrixRate;
|
||||
import de.avatic.lcc.model.utils.DimensionUnit;
|
||||
import de.avatic.lcc.model.utils.WeightUnit;
|
||||
import de.avatic.lcc.repositories.NodeRepository;
|
||||
import de.avatic.lcc.repositories.premise.RouteNodeRepository;
|
||||
import de.avatic.lcc.repositories.properties.PropertyRepository;
|
||||
import de.avatic.lcc.repositories.rates.ContainerRateRepository;
|
||||
|
|
@ -35,14 +37,16 @@ public class RouteSectionCostCalculationService {
|
|||
private final DistanceService distanceService;
|
||||
private final PropertyRepository propertyRepository;
|
||||
private final ChangeRiskFactorCalculationService changeRiskFactorCalculationService;
|
||||
private final NodeRepository nodeRepository;
|
||||
|
||||
public RouteSectionCostCalculationService(ContainerRateRepository containerRateRepository, MatrixRateRepository matrixRateRepository, RouteNodeRepository routeNodeRepository, DistanceService distanceService, PropertyRepository propertyRepository, ChangeRiskFactorCalculationService changeRiskFactorCalculationService) {
|
||||
public RouteSectionCostCalculationService(ContainerRateRepository containerRateRepository, MatrixRateRepository matrixRateRepository, RouteNodeRepository routeNodeRepository, DistanceService distanceService, PropertyRepository propertyRepository, ChangeRiskFactorCalculationService changeRiskFactorCalculationService, NodeRepository nodeRepository) {
|
||||
this.containerRateRepository = containerRateRepository;
|
||||
this.matrixRateRepository = matrixRateRepository;
|
||||
this.routeNodeRepository = routeNodeRepository;
|
||||
this.distanceService = distanceService;
|
||||
this.propertyRepository = propertyRepository;
|
||||
this.changeRiskFactorCalculationService = changeRiskFactorCalculationService;
|
||||
this.nodeRepository = nodeRepository;
|
||||
}
|
||||
|
||||
public CalculationJobRouteSection doD2dCalculation(Premise premise, Destination destination, ContainerCalculationResult containerCalculation) {
|
||||
|
|
@ -60,9 +64,10 @@ public class RouteSectionCostCalculationService {
|
|||
result.setUnmixedPrice(premise.getHuMixable());
|
||||
|
||||
// Get nodes and distance
|
||||
RouteNode fromNode = routeNodeRepository.getById(premise.getSupplierNodeId()).orElseThrow();
|
||||
RouteNode toNode = routeNodeRepository.getById(destination.getDestinationNodeId()).orElseThrow();
|
||||
double distance = getDistance(fromNode, toNode);
|
||||
Node fromNode = nodeRepository.getById(premise.getSupplierNodeId()).orElseThrow();
|
||||
Node toNode = nodeRepository.getById(destination.getDestinationNodeId()).orElseThrow();
|
||||
|
||||
double distance = distanceService.getDistance(fromNode, toNode, true);
|
||||
result.setDistance(BigDecimal.valueOf(distance));
|
||||
|
||||
// Get rate and transit time
|
||||
|
|
@ -267,6 +272,8 @@ public class RouteSectionCostCalculationService {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Simple data class to hold volume and weight price results
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
package de.avatic.lcc.service.precalculation;
|
||||
|
||||
import de.avatic.lcc.model.calculations.CalculationResult;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class PostCalculationCheckService {
|
||||
|
||||
public void doPostcheck(CalculationResult calculationResult) {
|
||||
//TODO check if all destinations are valid
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -38,12 +38,19 @@ public class PreCalculationCheckService {
|
|||
|
||||
materialCheck(premise);
|
||||
|
||||
|
||||
packagingCheck(premise);
|
||||
|
||||
priceCheck(premise);
|
||||
|
||||
var destinations = destinationRepository.getByPremiseId(premiseId);
|
||||
|
||||
for (Destination destination : destinationRepository.getByPremiseId(premiseId)) {
|
||||
if(destinations == null || destinations.isEmpty()) {
|
||||
throw new PremiseValidationError("No destination for calculation. At least one destination is required.");
|
||||
}
|
||||
|
||||
|
||||
for (Destination destination : destinations ) {
|
||||
|
||||
var node = nodeRepository.getByDestinationId(destination.getId()).orElseThrow();
|
||||
|
||||
|
|
@ -53,12 +60,20 @@ public class PreCalculationCheckService {
|
|||
|
||||
|
||||
|
||||
if(routes.isEmpty())
|
||||
if(routes.isEmpty() && destination.getD2d() == false)
|
||||
throw new PremiseValidationError("No route found for destination " + node.getName() + ". Cannot use standard routing.");
|
||||
|
||||
if(routes.stream().noneMatch(Route::getSelected))
|
||||
if(routes.stream().noneMatch(Route::getSelected) && destination.getD2d() == false)
|
||||
throw new PremiseValidationError("No route selected for destination " + node.getName());
|
||||
|
||||
if(destination.getD2d() && (destination.getRateD2d() == null || destination.getRateD2d().compareTo(BigDecimal.ZERO) == 0)) {
|
||||
throw new PremiseValidationError("Door-2-door rate not set or set to zero.");
|
||||
}
|
||||
|
||||
if(destination.getD2d() && (destination.getLeadTimeD2d() == null || destination.getLeadTimeD2d() == 0)) {
|
||||
throw new PremiseValidationError("Door-2-door rate not set or set to zero.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -72,7 +87,7 @@ public class PreCalculationCheckService {
|
|||
throw new PremiseValidationError("In destination " + node.getName() +": D2D not set.");
|
||||
|
||||
if (destination.getD2d() == true) {
|
||||
if (destination.getRateD2d() == null || destination.getRateD2d().equals(BigDecimal.ZERO)) {
|
||||
if (destination.getRateD2d() == null || destination.getRateD2d().compareTo(BigDecimal.ZERO) == 0) {
|
||||
throw new PremiseValidationError("In destination " + node.getName() + ": D2D Rate not set or set to zero.");
|
||||
}
|
||||
|
||||
|
|
@ -89,15 +104,15 @@ public class PreCalculationCheckService {
|
|||
throw new PremiseValidationError("In destination " + node.getName() + ": destination geo location not set. Please contact administrator");
|
||||
}
|
||||
|
||||
if (destination.getDisposalCost() != null && destination.getDisposalCost().equals(BigDecimal.ZERO)) {
|
||||
if (destination.getDisposalCost() != null && destination.getDisposalCost().compareTo(BigDecimal.ZERO) == 0) {
|
||||
throw new PremiseValidationError("In destination " + node.getName() + ": disposal cost set to zero.");
|
||||
}
|
||||
|
||||
if (destination.getHandlingCost() != null && destination.getHandlingCost().equals(BigDecimal.ZERO)) {
|
||||
if (destination.getHandlingCost() != null && destination.getHandlingCost().compareTo(BigDecimal.ZERO) == 0) {
|
||||
throw new PremiseValidationError("In destination " + node.getName() + ": handling cost set to zero.");
|
||||
}
|
||||
|
||||
if (destination.getRepackingCost() != null && destination.getRepackingCost().equals(BigDecimal.ZERO)) {
|
||||
if (destination.getRepackingCost() != null && destination.getRepackingCost().compareTo(BigDecimal.ZERO) == 0) {
|
||||
throw new PremiseValidationError("In destination " + node.getName() + ": repackaging cost set to zero.");
|
||||
}
|
||||
|
||||
|
|
@ -112,11 +127,11 @@ public class PreCalculationCheckService {
|
|||
|
||||
private void priceCheck(Premise premise) {
|
||||
|
||||
if (premise.getMaterialCost() == null || premise.getMaterialCost().equals(BigDecimal.ZERO)) {
|
||||
if (premise.getMaterialCost() == null || premise.getMaterialCost().compareTo(BigDecimal.ZERO) == 0) {
|
||||
throw new PremiseValidationError("MEK_A is not set or set to zero");
|
||||
}
|
||||
|
||||
if (premise.getOverseaShare() == null || premise.getOverseaShare().equals(BigDecimal.ZERO)) {
|
||||
if (premise.getOverseaShare() == null || premise.getOverseaShare().compareTo(BigDecimal.ZERO) == 0) {
|
||||
throw new PremiseValidationError("Oversea share is not set or set to zero");
|
||||
}
|
||||
|
||||
|
|
@ -156,6 +171,10 @@ public class PreCalculationCheckService {
|
|||
throw new PremiseValidationError("Packaging weight is not set or set to zero");
|
||||
}
|
||||
|
||||
if (premise.getHuUnitCount() == null || premise.getHuUnitCount() == 0) {
|
||||
throw new PremiseValidationError("Packaging unit count is not set or set to zero");
|
||||
}
|
||||
|
||||
if (premise.getHuDisplayedWeightUnit() == null) {
|
||||
throw new PremiseValidationError("Packaging weight unit is not set");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import de.avatic.lcc.repositories.rates.ValidityPeriodRepository;
|
|||
import de.avatic.lcc.repositories.users.UserNodeRepository;
|
||||
import de.avatic.lcc.service.transformer.generic.MaterialTransformer;
|
||||
import de.avatic.lcc.service.transformer.generic.NodeTransformer;
|
||||
import de.avatic.lcc.util.exception.base.InternalErrorException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
|
@ -92,12 +93,18 @@ public class ReportTransformer {
|
|||
if (premise.getUserSupplierNodeId() != null)
|
||||
sourceNode = userNodeRepository.getById(premise.getUserSupplierNodeId()).orElseThrow();
|
||||
|
||||
|
||||
if (!destinations.isEmpty()) {
|
||||
reportDTO.setCost(getCostMap(job, destinations, weightedTotalCost, includeAirfreight));
|
||||
reportDTO.setRisk(getRisk(job, destinations, weightedTotalCost, includeAirfreight));
|
||||
Node finalSourceNode = sourceNode;
|
||||
reportDTO.setDestination(destinations.stream().map(d -> getDestinationDTO(d, sections.get(d.getId()), premise, finalSourceNode, includeAirfreight)).toList());
|
||||
} else {
|
||||
throw new InternalErrorException("Malformed calculation. No destinations found.");
|
||||
}
|
||||
|
||||
if (reportDTO.getDestinations() != null && !reportDTO.getDestinations().isEmpty()) {
|
||||
|
||||
if (!reportDTO.getDestinations().isEmpty()) {
|
||||
var source = reportDTO.getDestinations().getFirst().getSections().stream().map(ReportSectionDTO::getFromNode).filter(n -> n.getTypes().contains(NodeType.SOURCE)).findFirst().orElseThrow();
|
||||
reportDTO.setSupplier(source);
|
||||
}
|
||||
|
|
@ -130,6 +137,8 @@ public class ReportTransformer {
|
|||
BigDecimal totalPreRunCost = BigDecimal.ZERO;
|
||||
BigDecimal totalMainRunCost = BigDecimal.ZERO;
|
||||
BigDecimal totalPostRunCost = BigDecimal.ZERO;
|
||||
BigDecimal totalD2D = BigDecimal.ZERO;
|
||||
|
||||
|
||||
BigDecimal totalCost = BigDecimal.ZERO;
|
||||
|
||||
|
|
@ -138,16 +147,19 @@ public class ReportTransformer {
|
|||
|
||||
if (section.getPreRun())
|
||||
totalPreRunCost = totalPreRunCost.add(section.getAnnualCost());
|
||||
if (section.getMainRun())
|
||||
else if (section.getMainRun())
|
||||
totalMainRunCost = totalMainRunCost.add(section.getAnnualCost());
|
||||
if (section.getPostRun())
|
||||
else if (section.getPostRun())
|
||||
totalPostRunCost = totalPostRunCost.add(section.getAnnualCost());
|
||||
else if (section.getRateType().equals(RateType.D2D))
|
||||
totalD2D = totalD2D.add(section.getAnnualCost());
|
||||
|
||||
|
||||
totalCost = totalCost.add(section.getAnnualCost());
|
||||
}
|
||||
}
|
||||
|
||||
return new WeightedTotalCosts(totalPreRunCost, totalMainRunCost, totalPostRunCost, totalCost);
|
||||
return new WeightedTotalCosts(totalPreRunCost, totalMainRunCost, totalPostRunCost, totalD2D, totalCost);
|
||||
}
|
||||
|
||||
private ReportDestinationDTO getDestinationDTO(CalculationJobDestination destination, List<CalculationJobRouteSection> sections, Premise premise, Node sourceNode, boolean includeAirfreight) {
|
||||
|
|
@ -245,7 +257,7 @@ public class ReportTransformer {
|
|||
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 = annualAmount.compareTo(BigDecimal.ZERO) == 0 ? 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);
|
||||
|
||||
totalValue = totalValue.add(airfreightValue.multiply(BigDecimal.valueOf(includeAirfreight ? 1 : 0)));
|
||||
|
|
@ -277,19 +289,25 @@ public class ReportTransformer {
|
|||
|
||||
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);
|
||||
var handlingValues = annualAmount.equals(BigDecimal.ZERO) ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getAnnualHandlingCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
var storageValues = annualAmount.equals(BigDecimal.ZERO) ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getAnnualStorageCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
var capitalValues = annualAmount.equals(BigDecimal.ZERO) ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getAnnualCapitalCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
var disposalValues = annualAmount.equals(BigDecimal.ZERO) ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getAnnualDisposalCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
var customValues = annualAmount.equals(BigDecimal.ZERO) ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getAnnualCustomCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
var repackingValues = annualAmount.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getAnnualRepackingCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
var handlingValues = annualAmount.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getAnnualHandlingCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
var storageValues = annualAmount.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getAnnualStorageCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
var capitalValues = annualAmount.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getAnnualCapitalCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
var disposalValues = annualAmount.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getAnnualDisposalCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
var customValues = annualAmount.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getAnnualCustomCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
|
||||
var noTransportSplit = weightedTotalCost.totalPreRunCost.compareTo(BigDecimal.ZERO) == 0 && weightedTotalCost.totalMainRunCost.compareTo(BigDecimal.ZERO) == 0 && weightedTotalCost.totalPostRunCost.compareTo(BigDecimal.ZERO) == 0;
|
||||
|
||||
|
||||
var preRunValues = weightedTotalCost.totalPreRunCost.divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
var mainRunValues = weightedTotalCost.totalMainRunCost.divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
var postRunValues = weightedTotalCost.totalPostRunCost.divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
|
||||
var d2dValues = weightedTotalCost.totalD2D.divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
var transportValues = weightedTotalCost.totalCost.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 = annualAmount.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : destination.stream().map(CalculationJobDestination::getTotalCost).reduce(BigDecimal.ZERO, BigDecimal::add).divide(annualAmount, 4, RoundingMode.HALF_UP);
|
||||
|
||||
if (includeAirfreight) {
|
||||
totalValue = totalValue.add(airfreightValue);
|
||||
|
|
@ -307,20 +325,35 @@ public class ReportTransformer {
|
|||
cost.put("air_freight_cost", airfreight);
|
||||
}
|
||||
|
||||
if (!noTransportSplit) {
|
||||
ReportEntryDTO preRun = new ReportEntryDTO();
|
||||
preRun.setTotal(preRunValues);
|
||||
preRun.setPercentage(preRunValues.divide(totalValue, 4, RoundingMode.HALF_UP));
|
||||
cost.put("pre_run", preRun);
|
||||
|
||||
}
|
||||
if (!noTransportSplit) {
|
||||
ReportEntryDTO mainRun = new ReportEntryDTO();
|
||||
mainRun.setTotal(mainRunValues);
|
||||
mainRun.setPercentage(mainRunValues.divide(totalValue, 4, RoundingMode.HALF_UP));
|
||||
cost.put("main_run", mainRun);
|
||||
|
||||
}
|
||||
if (!noTransportSplit) {
|
||||
ReportEntryDTO postRun = new ReportEntryDTO();
|
||||
postRun.setTotal(postRunValues);
|
||||
postRun.setPercentage(postRunValues.divide(totalValue, 4, RoundingMode.HALF_UP));
|
||||
cost.put("post_run", postRun);
|
||||
}
|
||||
if (d2dValues.compareTo(BigDecimal.ZERO) != 0) {
|
||||
ReportEntryDTO d2d = new ReportEntryDTO();
|
||||
d2d.setTotal(d2dValues);
|
||||
d2d.setPercentage(d2dValues.divide(totalValue, 4, RoundingMode.HALF_UP));
|
||||
cost.put("d2d", d2d);
|
||||
} else if (noTransportSplit) {
|
||||
ReportEntryDTO transport = new ReportEntryDTO();
|
||||
transport.setTotal(transportValues);
|
||||
transport.setPercentage(transportValues.divide(totalValue, 4, RoundingMode.HALF_UP));
|
||||
cost.put("transport", transport);
|
||||
}
|
||||
|
||||
ReportEntryDTO material = new ReportEntryDTO();
|
||||
material.setTotal(materialValue);
|
||||
|
|
@ -369,6 +402,6 @@ public class ReportTransformer {
|
|||
}
|
||||
|
||||
private record WeightedTotalCosts(BigDecimal totalPreRunCost, BigDecimal totalMainRunCost,
|
||||
BigDecimal totalPostRunCost, BigDecimal totalCost) {
|
||||
BigDecimal totalPostRunCost, BigDecimal totalD2D, BigDecimal totalCost) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -351,7 +351,7 @@ CREATE TABLE IF NOT EXISTS premise
|
|||
individual_hu_width INT UNSIGNED COMMENT 'user entered dimensions in mm (if system-wide packaging is used, packaging dimensions are copied here after creation)',
|
||||
individual_hu_weight INT UNSIGNED COMMENT 'user entered weight in g (if system-wide packaging is used, packaging weight are copied here after creation)',
|
||||
hu_displayed_dimension_unit CHAR(2) DEFAULT 'MM',
|
||||
hu_displayed_weight_unit CHAR(2) DEFAULT 'G',
|
||||
hu_displayed_weight_unit CHAR(2) DEFAULT 'KG',
|
||||
hu_unit_count INT UNSIGNED DEFAULT NULL,
|
||||
hu_stackable BOOLEAN DEFAULT TRUE,
|
||||
hu_mixable BOOLEAN DEFAULT TRUE,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue