Working on MassEdit and SingleEdit
This commit is contained in:
parent
0061d01b75
commit
f5c4e1159f
28 changed files with 604 additions and 289 deletions
|
|
@ -17,7 +17,6 @@
|
|||
weight="regular"
|
||||
size="24"
|
||||
class="icon-btn"
|
||||
:class="toast__icon_sizing"
|
||||
/>
|
||||
</div>
|
||||
<div class="toast__content">
|
||||
|
|
|
|||
|
|
@ -10,23 +10,24 @@
|
|||
{{ premise.material.name }}
|
||||
</div>
|
||||
<div class="edit-calculation-cell-line edit-calculation-cell-subline" v-if="premise.hs_code">
|
||||
<PhBarcode/>
|
||||
HS Code:
|
||||
{{ premise.material.hs_code }}
|
||||
</div>
|
||||
<div class="edit-calculation-cell-line edit-calculation-cell-subline" v-if="premise.tariff_rate">
|
||||
<PhPercent/>
|
||||
{{ premise.tariff_rate }}
|
||||
<div class="edit-calculation-cell-line edit-calculation-cell-subline" v-if="premise.tariff_rate && premise.tariff_rate > 0">
|
||||
Tariff rate:
|
||||
{{ toPercent(premise.tariff_rate) }} %
|
||||
</div>
|
||||
</div>
|
||||
<div class="edit-calculation-cell--price" :class="copyModeClass" v-if="showPrice"
|
||||
@click="action('price')">
|
||||
<div class="edit-calculation-cell-line">{{ premise.material_cost }} EUR</div>
|
||||
<div class="edit-calculation-cell-line edit-calculation-cell-subline" >
|
||||
<div class="edit-calculation-cell-line edit-calculation-cell-subline">Oversea share: {{ toPercent(premise.oversea_share) }} %</div>
|
||||
<div class="edit-calculation-cell-line edit-calculation-cell-subline" v-if="premise.is_fca_enabled">
|
||||
<basic-badge icon="plus" variant="primary">FCA FEE</basic-badge>
|
||||
</div>
|
||||
</div>
|
||||
<div class="edit-calculation-empty" :class="copyModeClass" v-else @click="action('price')">
|
||||
<basic-badge variant="exception" icon="warning">MISSING</basic-badge>
|
||||
<basic-badge variant="exception" icon="warning">INCOMPLETE</basic-badge>
|
||||
</div>
|
||||
<div v-if="showHu" class="edit-calculation-cell edit-calculation-cell--packaging" :class="copyModeClass"
|
||||
@click="action('packaging')">
|
||||
|
|
@ -36,11 +37,11 @@
|
|||
{{ premise.handling_unit.width }} x
|
||||
{{ premise.handling_unit.height }} {{ premise.handling_unit.dimension_unit }}
|
||||
</div>
|
||||
<div class="edit-calculation-cell-line">
|
||||
<div class="edit-calculation-cell-line edit-calculation-cell-subline">
|
||||
<PhBarbell/>
|
||||
<span>{{ premise.handling_unit.weight }} {{ premise.handling_unit.weight_unit }}</span>
|
||||
</div>
|
||||
<div class="edit-calculation-cell-line">
|
||||
<div class="edit-calculation-cell-line edit-calculation-cell-subline">
|
||||
<PhHash/>
|
||||
{{ premise.handling_unit.content_unit_count }} pcs.
|
||||
</div>
|
||||
|
|
@ -51,7 +52,7 @@
|
|||
</div>
|
||||
<div class="edit-calculation-empty" :class="copyModeClass" v-else
|
||||
@click="action('packaging')">
|
||||
<basic-badge variant="exception" icon="warning">MISSING</basic-badge>
|
||||
<basic-badge variant="exception" icon="warning">INCOMPLETE</basic-badge>
|
||||
</div>
|
||||
<div class="edit-calculation-cell--supplier" :class="copyModeClass"
|
||||
@click="action('supplier')">
|
||||
|
|
@ -77,7 +78,7 @@
|
|||
</div>
|
||||
<div class="edit-calculation-empty" :class="copyModeClass" v-else
|
||||
@click="action('destinations')">
|
||||
<basic-badge variant="exception" icon="warning">MISSING</basic-badge>
|
||||
<basic-badge variant="exception" icon="warning">INCOMPLETE</basic-badge>
|
||||
</div>
|
||||
|
||||
<div class="edit-calculation-actions-cell">
|
||||
|
|
@ -178,7 +179,9 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
toPercent(value) {
|
||||
return value !== null ? (value * 100).toFixed(2) : '0.00';
|
||||
},
|
||||
updateSelected(value) {
|
||||
this.premiseEditStore.setSelectTo([this.id], value);
|
||||
},
|
||||
|
|
@ -214,10 +217,6 @@ export default {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
/*.bulk-edit-row:hover {
|
||||
// background-color: rgba(107, 134, 156, 0.05);
|
||||
//} */
|
||||
|
||||
.bulk-edit-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
|
@ -249,11 +248,6 @@ export default {
|
|||
|
||||
.edit-calculation-cell--copy-mode:hover {
|
||||
cursor: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOCIgaGVpZ2h0PSIyOCIgdmlld0JveD0iMCAwIDI1NiAyNTYiPgogIDxyZWN0IHg9Ijg0IiB5PSIzMiIgd2lkdGg9IjEzNiIgaGVpZ2h0PSIxMzYiIGZpbGw9IndoaXRlIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iOCIgcng9IjQiLz4KICA8cmVjdCB4PSIzNiIgeT0iODQiIHdpZHRoPSIxMzYiIGhlaWdodD0iMTM2IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjgiIHJ4PSI0Ii8+Cjwvc3ZnPg==") 12 12, pointer;
|
||||
|
||||
/*cursor: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjQiIHZpZXdCb3g9IjAgMCAyNTYgMjU2Ij4KICA8cGF0aCBkPSJNMjAwLDMySDE2My43NGE0Ny45Miw0Ny45MiwwLDAsMC03MS40OCwwSDU2QTE2LDE2LDAsMCwwLDQwLDQ4VjIxNmExNiwxNiwwLDAsMCwxNiwxNkgyMDBhMTYsMTYsMCwwLDAsMTYtMTZWNDhBMTYsMTYsMCwwLDAsMjAwLDMyWm0tNzIsMGEzMiwzMiwwLDAsMSwzMiwzMkg5NkEzMiwzMiwwLDAsMSwxMjgsMzJaIj48L3BhdGg+Cjwvc3ZnPg==") 12 12, pointer;
|
||||
*/
|
||||
|
||||
|
||||
background-color: rgba(107, 134, 156, 0.05);
|
||||
border-radius: 0.8rem;
|
||||
box-shadow: 0 0.4rem 0.6rem -0.1rem rgba(0, 0, 0, 0.1);
|
||||
|
|
|
|||
|
|
@ -93,12 +93,10 @@ export default {
|
|||
},
|
||||
async addDestination(node) {
|
||||
console.log(node)
|
||||
// todo add to massEdit copy only
|
||||
const [id] = await this.premiseEditStore.addDestination(node);
|
||||
this.editDestination(id);
|
||||
},
|
||||
deleteDestination(id) {
|
||||
// todo delete from to massEdit copy only
|
||||
this.premiseEditStore.deleteDestination(id);
|
||||
},
|
||||
editDestination(id) {
|
||||
|
|
|
|||
|
|
@ -28,14 +28,14 @@
|
|||
<div class="input-column">
|
||||
<div class="hs-code-container">
|
||||
<autosuggest-searchbar :fetch-suggestions="fetchHsCode" :initial-value="hsCode"
|
||||
placeholder="Find hs code"></autosuggest-searchbar>
|
||||
placeholder="Find hs code" no-results-text="Not found."></autosuggest-searchbar>
|
||||
<icon-button icon="ArrowCounterClockwise"></icon-button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="caption-column">Tariff rate [%]</div>
|
||||
<div class="input-field-container input-field-tariffrate">
|
||||
<input ref="tariffRateInput" :value="tariffRatePercent" @blur="validateInput('tariffRate',$event)"
|
||||
<input ref="tariffRateInput" :value="tariffRatePercent" @blur="validateTariffRate"
|
||||
class="input-field"
|
||||
autocomplete="off"/>
|
||||
</div>
|
||||
|
|
@ -59,6 +59,7 @@ import {mapStores} from "pinia";
|
|||
import {parseNumberFromString} from "@/common.js";
|
||||
import Modal from "@/components/UI/Modal.vue";
|
||||
import SelectMaterial from "@/components/layout/material/SelectMaterial.vue";
|
||||
import {useCustomsStore} from "@/store/customs.js";
|
||||
|
||||
export default {
|
||||
name: "MaterialEdit",
|
||||
|
|
@ -66,7 +67,7 @@ export default {
|
|||
SelectMaterial,
|
||||
Modal, PhArrowCounterClockwise, ModalDialog, AutosuggestSearchbar, InputField, Flag, IconButton
|
||||
},
|
||||
emits: ["update:tariffRate", "updateMaterial", "update:partNumber", "update:hsCode"],
|
||||
emits: ["update:tariffRate", "updateMaterial", "update:partNumber", "update:hsCode", "save"],
|
||||
props: {
|
||||
description: {
|
||||
type: String,
|
||||
|
|
@ -86,7 +87,7 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useMaterialStore),
|
||||
...mapStores(useMaterialStore, useCustomsStore),
|
||||
tariffRatePercent() {
|
||||
return this.tariffRate ? (this.tariffRate * 100).toFixed(2) : '';
|
||||
}
|
||||
|
|
@ -119,25 +120,25 @@ export default {
|
|||
}
|
||||
});
|
||||
},
|
||||
validateInput(type, event) {
|
||||
const decimals = 2
|
||||
const parsed = parseNumberFromString(event.target.value, decimals);
|
||||
console.log('validateInput', type, event.target.value, parsed);
|
||||
validateTariffRate(event) {
|
||||
const percentValue = parseNumberFromString(event.target.value, 4);
|
||||
|
||||
this.$emit(`update:${type}`, parsed);
|
||||
const validatedPercent = Math.max(0, Math.min(999.99, percentValue));
|
||||
const validatedDecimal = validatedPercent / 100;
|
||||
|
||||
// Force update the input field with the correctly formatted value
|
||||
const formattedValue = parsed.toFixed(decimals);
|
||||
const inputRef = `${type}Input`;
|
||||
this.updateInputValue(inputRef, formattedValue);
|
||||
if (validatedDecimal !== this.tariffRate) {
|
||||
this.$emit('update:tariffRate', validatedDecimal);
|
||||
}
|
||||
|
||||
event.target.value = validatedPercent.toFixed(2);
|
||||
},
|
||||
activateEditMode() {
|
||||
this.modalSelectMaterial = true;
|
||||
},
|
||||
async fetchHsCode(query) {
|
||||
const hsCodeQuery = {searchTerm: query};
|
||||
await this.customs.setQuery(hsCodeQuery);
|
||||
return this.customs.hsCodes;
|
||||
await this.customsStore.setQuery(hsCodeQuery);
|
||||
return this.customsStore.hsCodes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import {parseNumberFromString} from "@/common.js";
|
|||
export default {
|
||||
name: "PriceEdit",
|
||||
components: {Tooltip, Checkbox},
|
||||
emits: ['update:price', 'update:overSeaShare', 'update:includeFcaFee'],
|
||||
emits: ['update:price', 'update:overSeaShare', 'update:includeFcaFee', 'save'],
|
||||
props: {
|
||||
price: {
|
||||
required: true,
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export default {
|
|||
flex-direction: column;
|
||||
gap: 1.6rem;
|
||||
flex: 1 0 min(60vw, 120rem);
|
||||
height: min(70vh, 50rem);
|
||||
height: min(60vh, 120rem);
|
||||
min-height: 0; /* Critical: allows flex child to shrink below content size */
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -102,13 +102,13 @@ export default {
|
|||
return `Annual quantity that "${this.destination.destination_node.name}" will source from the supplier`
|
||||
},
|
||||
showMassEditWarning() {
|
||||
return (this.destination.massEdit ?? false);
|
||||
return (this.destination.routes ?? null) === null;
|
||||
},
|
||||
showRoutes() {
|
||||
return !this.destination.is_d2d && this.destination.routes?.length > 0 | false;
|
||||
return !this.destination.is_d2d && (this.destination.routes?.length > 0);
|
||||
},
|
||||
showRouteWarning() {
|
||||
return !this.destination.is_d2d && this.destination.routes.length === 0;
|
||||
return !this.destination.is_d2d && this.destination.routes?.length === 0;
|
||||
},
|
||||
calculationModel: {
|
||||
get() {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
<div class="select-material-input-column" v-if="materialSelected">{{ hsCode }}</div>
|
||||
<div class="select-material-checkbox" v-if="materialSelected">
|
||||
<tooltip
|
||||
text="Your current master data (like packaging dimensions) might be outdated after material change. Tick to reload master data">
|
||||
text="Tick to reload master data from database (if present) and overwrite current values.">
|
||||
<checkbox :checked="updateMasterData" @checkbox-changed="checkboxChanged">update master data</checkbox>
|
||||
</tooltip>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
<!--TODO: isMassEdit-->
|
||||
|
||||
<template>
|
||||
<div class="edit-calculation-container">
|
||||
<div class="header-container">
|
||||
|
|
@ -47,7 +49,7 @@
|
|||
</transition>
|
||||
|
||||
|
||||
<modal :z-index="3000" :state="showProcessingModal" @close="closeEditModal">
|
||||
<modal :z-index="3000" :state="showProcessingModal">
|
||||
<div class="edit-calculation-spinner-container space-around">
|
||||
<spinner></spinner>
|
||||
<span>{{ processingMessage }}</span>
|
||||
|
|
@ -58,11 +60,32 @@
|
|||
:select-count="selectCount"></mass-edit-dialog>
|
||||
|
||||
|
||||
<modal :z-index="2000" :state="showEditModal" @close="closeEditModal">
|
||||
<modal :z-index="2000" :state="showEditModal">
|
||||
<div class="modal-content-container">
|
||||
<component
|
||||
:is="componentType"
|
||||
v-bind="componentProps"
|
||||
v-model:partNumber="componentProps.partNumber"
|
||||
v-model:hsCode="componentProps.hsCode"
|
||||
v-model:tariffRate="componentProps.tariffRate"
|
||||
v-model:description="componentProps.description"
|
||||
v-model:price="componentProps.price"
|
||||
v-model:overSeaShare="componentProps.overSeaShare"
|
||||
v-model:includeFcaFee="componentProps.includeFcaFee"
|
||||
v-model:length="componentProps.length"
|
||||
v-model:width="componentProps.width"
|
||||
v-model:height="componentProps.height"
|
||||
v-model:weight="componentProps.weight"
|
||||
v-model:weightUnit="componentProps.weightUnit"
|
||||
v-model:dimensionUnit="componentProps.dimensionUnit"
|
||||
v-model:unitCount="componentProps.unitCount"
|
||||
v-model:mixable="componentProps.mixable"
|
||||
v-model:stackable="componentProps.stackable"
|
||||
v-model:supplierName="componentProps.supplierName"
|
||||
v-model:supplierAddress="componentProps.supplierAddress"
|
||||
v-model:supplierCoordinates="componentProps.supplierCoordinates"
|
||||
v-model:isoCode="componentProps.isoCode"
|
||||
@update-material="updateMaterial"
|
||||
|
||||
>
|
||||
</component>
|
||||
<div class="modal-content-footer">
|
||||
|
|
@ -128,7 +151,7 @@ export default {
|
|||
return this.premiseEditStore.isLoading ? false : this.premiseEditStore.getPremisses?.every(p => p.selected === true) ?? false;
|
||||
},
|
||||
showMultiselectAction() {
|
||||
return this.premiseEditStore.getPremisses?.some(p => p.selected === true) ?? false;
|
||||
return this.selectCount > 0;
|
||||
},
|
||||
showEditModal() {
|
||||
return ((this.modalType ?? null) !== null);
|
||||
|
|
@ -183,10 +206,20 @@ export default {
|
|||
destinations: {props: {}},
|
||||
},
|
||||
editIds: null,
|
||||
dataSourceId: null,
|
||||
processingMessage: "Please wait. Calculating routes ...",
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async updateMaterial(id, action) {
|
||||
console.log(id, action);
|
||||
await this.premiseEditStore.setMaterial(id, action === 'updateMasterData', this.editIds);
|
||||
|
||||
if (this.dataSourceId !== null) {
|
||||
this.fillData("material", this.dataSourceId);
|
||||
}
|
||||
|
||||
},
|
||||
updateCheckBoxes(value) {
|
||||
this.premiseEditStore.setSelectTo(this.ids, value);
|
||||
},
|
||||
|
|
@ -194,94 +227,91 @@ export default {
|
|||
this.openModal(action, this.selectedPremisses.map(p => p.id));
|
||||
},
|
||||
onClickAction(data) {
|
||||
if (0 !== this.selectCount) {
|
||||
this.openModal(data.action, this.premiseEditStore.getSelectedPremissesIds, data.id);
|
||||
} else {
|
||||
this.openModal(data.action, [data.id], data.id, true);
|
||||
}
|
||||
const massEdit = 0 !== this.selectCount
|
||||
this.openModal(data.action, massEdit ? this.premiseEditStore.getSelectedPremissesIds : [data.id], data.id, massEdit);
|
||||
},
|
||||
openModal(type, ids, dataSource = -1, noMassEdit = false) {
|
||||
openModal(type, ids, dataSource = -1, massEdit = true) {
|
||||
|
||||
if (type !== 'destinations')
|
||||
this.fillData(type, dataSource)
|
||||
else {
|
||||
if (noMassEdit)
|
||||
this.premiseEditStore.selectPremise(dataSource);
|
||||
else
|
||||
this.premiseEditStore.prepareMassEdit(dataSource, ids);
|
||||
console.log(ids, dataSource, massEdit)
|
||||
this.premiseEditStore.prepareDestinations(dataSource, ids, massEdit, true);
|
||||
}
|
||||
|
||||
this.dataSourceId = dataSource !== -1 ? dataSource : null;
|
||||
this.editIds = ids;
|
||||
this.modalType = type;
|
||||
},
|
||||
closeEditModalAction(action) {
|
||||
|
||||
if (this.modalType === "destinations") {
|
||||
if(this.premiseEditStore.isMassEdit) {
|
||||
if (action === "accept") {
|
||||
this.premiseEditStore.finishMassEdit();
|
||||
} else {
|
||||
console.log("cancel mass edit")
|
||||
this.premiseEditStore.cancelMassEdit();
|
||||
}
|
||||
if (action === "accept") {
|
||||
this.premiseEditStore.executeDestinationsMassEdit();
|
||||
} else {
|
||||
|
||||
//TODO save
|
||||
|
||||
this.premiseEditStore.deselectPremise();
|
||||
console.log("cancel mass edit")
|
||||
this.premiseEditStore.cancelMassEdit();
|
||||
}
|
||||
} else {
|
||||
if (action === "accept") {
|
||||
|
||||
if (this.modalType === "price") {
|
||||
this.selectedPremisses.forEach(p => {
|
||||
p.material_cost = componentsData[this.modalType].props.price;
|
||||
p.oversea_share = componentsData[this.modalType].props.overSeaShare;
|
||||
p.is_fca_enabled = componentsData[this.modalType].props.includeFcaFee;
|
||||
})
|
||||
this.editIds.forEach(id => {
|
||||
const p = this.premiseEditStore.getById(id);
|
||||
p.material_cost = this.componentsData[this.modalType].props.price;
|
||||
p.oversea_share = this.componentsData[this.modalType].props.overSeaShare;
|
||||
p.is_fca_enabled = this.componentsData[this.modalType].props.includeFcaFee;
|
||||
});
|
||||
|
||||
this.premiseEditStore.savePrice(this.editIds);
|
||||
|
||||
} else if (this.modalType === "material") {
|
||||
this.selectedPremisses.forEach(p => {
|
||||
p.material.part_number = componentsData[this.modalType].props.partNumber;
|
||||
p.material.hs_code = componentsData[this.modalType].props.hsCode;
|
||||
p.tariff_rate = componentsData[this.modalType].props.tariffRate;
|
||||
|
||||
this.editIds.forEach(id => {
|
||||
const p = this.premiseEditStore.getById(id);
|
||||
p.material.part_number = this.componentsData[this.modalType].props.partNumber;
|
||||
p.material.hs_code = this.componentsData[this.modalType].props.hsCode;
|
||||
p.tariff_rate = this.componentsData[this.modalType].props.tariffRate;
|
||||
});
|
||||
|
||||
this.premiseEditStore.saveMaterial(this.editIds);
|
||||
|
||||
|
||||
} else if (this.modalType === "packaging") {
|
||||
this.selectedPremisses.forEach(p => {
|
||||
p.handling_unit.weight = componentsData[this.modalType].props.weight;
|
||||
p.handling_unit.height = componentsData[this.modalType].props.height;
|
||||
p.handling_unit.length = componentsData[this.modalType].props.length;
|
||||
p.handling_unit.height = componentsData[this.modalType].props.height;
|
||||
|
||||
p.handling_unit.weight_unit = componentsData[this.modalType].props.weightUnit;
|
||||
p.handling_unit.dimension_unit = componentsData[this.modalType].props.dimensionUnit;
|
||||
p.handling_unit.content_unit_count = componentsData[this.modalType].props.unitCount;
|
||||
this.editIds.forEach(id => {
|
||||
const p = this.premiseEditStore.getById(id);
|
||||
p.handling_unit.weight = this.componentsData[this.modalType].props.weight;
|
||||
p.handling_unit.height = this.componentsData[this.modalType].props.height;
|
||||
p.handling_unit.length = this.componentsData[this.modalType].props.length;
|
||||
p.handling_unit.height = this.componentsData[this.modalType].props.height;
|
||||
|
||||
p.is_stackable = componentsData[this.modalType].props.is_stackable;
|
||||
p.is_mixable = componentsData[this.modalType].props.is_mixable;
|
||||
p.handling_unit.weight_unit = this.componentsData[this.modalType].props.weightUnit;
|
||||
p.handling_unit.dimension_unit = this.componentsData[this.modalType].props.dimensionUnit;
|
||||
p.handling_unit.content_unit_count = this.componentsData[this.modalType].props.unitCount;
|
||||
|
||||
p.is_stackable = this.componentsData[this.modalType].props.stackable;
|
||||
p.is_mixable = this.componentsData[this.modalType].props.mixable;
|
||||
});
|
||||
|
||||
this.premiseEditStore.savePackaging(this.editIds);
|
||||
|
||||
} else if (this.modalType === "supplier") {
|
||||
//set supplier.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// clear data.
|
||||
this.fillData(this.modalType);
|
||||
this.closeEditModal();
|
||||
},
|
||||
closeEditModal() {
|
||||
if (this.modalType === "destinations") {
|
||||
|
||||
if(!this.premiseEditStore.isMassEdit) {
|
||||
this.premiseEditStore.deselectPremise();
|
||||
}
|
||||
|
||||
this.premiseEditStore.cancelMassEdit();
|
||||
|
||||
}
|
||||
this.modalType = null;
|
||||
},
|
||||
fillData(type, id = -1) {
|
||||
|
||||
console.log("fillData", type, id);
|
||||
|
||||
if (id === -1) {
|
||||
// clear
|
||||
this.componentsData = {
|
||||
|
|
@ -332,8 +362,8 @@ export default {
|
|||
width: premise.handling_unit.width ?? 0,
|
||||
height: premise.handling_unit.height ?? 0,
|
||||
weight: premise.handling_unit.weight ?? 0,
|
||||
weightUnit: premise.handling_unit.weightUnit ?? "KG",
|
||||
dimensionUnit: premise.handling_unit.dimensionUnit ?? "MM",
|
||||
weightUnit: premise.handling_unit.weight_unit ?? "KG",
|
||||
dimensionUnit: premise.handling_unit.dimension_unit ?? "MM",
|
||||
unitCount: premise.handling_unit.content_unit_count ?? 1,
|
||||
mixable: premise.is_mixable ?? true,
|
||||
stackable: premise.is_stackable ?? true
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@
|
|||
:supplier-name="premise.supplier.name"
|
||||
:supplier-coordinates="premise.supplier.location"
|
||||
:iso-code="premise.supplier.country.iso_code"
|
||||
@update-supplier="updateSupplier"></supplier-view>
|
||||
@update-supplier="updateSupplier"
|
||||
></supplier-view>
|
||||
</box>
|
||||
<box class="master-data-item master-data-stretched-item master-data-packaging">
|
||||
<packaging-edit v-model:length="premise.handling_unit.length"
|
||||
|
|
@ -50,11 +51,14 @@
|
|||
:id="premise.material.id"
|
||||
v-model:hs-code="premise.hs_code"
|
||||
v-model:tariff-rate="premise.tariff_rate"
|
||||
@update-material="updateMaterial"></material-edit>
|
||||
@update-material="updateMaterial"
|
||||
@save="save"></material-edit>
|
||||
</box>
|
||||
<box class="master-data-item">
|
||||
<price-edit v-model:include-fca-fee="premise.is_fca_enabled" v-model:over-sea-share="premise.oversea_share"
|
||||
v-model:price="premise.material_cost"></price-edit>
|
||||
<price-edit v-model:include-fca-fee="premise.is_fca_enabled"
|
||||
v-model:over-sea-share="premise.oversea_share"
|
||||
v-model:price="premise.material_cost"
|
||||
@save="save"></price-edit>
|
||||
</box>
|
||||
|
||||
</div>
|
||||
|
|
@ -129,17 +133,29 @@ export default {
|
|||
this.$router.push({name: 'home'});
|
||||
}
|
||||
},
|
||||
save(type) {
|
||||
console.log(type);
|
||||
},
|
||||
showCustomToast() {
|
||||
this.$refs.toast.addToast({
|
||||
icon: 'floppy-disk',
|
||||
message: 'Changes saved.',
|
||||
title: 'Success',
|
||||
variant: 'primary',
|
||||
duration: 3000
|
||||
})
|
||||
async save(type) {
|
||||
let success = false;
|
||||
|
||||
if(type === 'price') {
|
||||
success = await this.premiseEditStore.savePrice();
|
||||
} else if(type === 'material') {
|
||||
success = await this.premiseEditStore.saveMaterial();
|
||||
} else if(type === 'packaging') {
|
||||
success = await this.premiseEditStore.savePackaging();
|
||||
}
|
||||
|
||||
console.log("save success: ", success);
|
||||
|
||||
if(success) {
|
||||
this.$refs.toast.addToast({
|
||||
icon: 'floppy-disk',
|
||||
message: `Changes on ${type} saved.`,
|
||||
|
||||
variant: 'primary',
|
||||
duration: 3000
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
updateMaterial(id, action) {
|
||||
console.log(id, action);
|
||||
|
|
@ -153,11 +169,11 @@ export default {
|
|||
}
|
||||
},
|
||||
created() {
|
||||
this.id = new UrlSafeBase64().decodeIds(this.$route.params.id);
|
||||
[this.id] = new UrlSafeBase64().decodeIds(this.$route.params.id);
|
||||
|
||||
if(this.$route.params.ids) {
|
||||
this.bulkEditQuery = this.$route.params.ids;
|
||||
this.premiseEditStore.selectPremises(this.id, new UrlSafeBase64().decodeIds(this.$route.params.ids));
|
||||
this.premiseEditStore.selectSinglePremise(this.id, new UrlSafeBase64().decodeIds(this.$route.params.ids));
|
||||
} else {
|
||||
this.premiseEditStore.loadAndSelectSinglePremise(this.id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
selectedLoading: false,
|
||||
|
||||
|
||||
massEditDestinations: null,
|
||||
processMassEdit: false,
|
||||
destinations: null,
|
||||
processDestinationMassEdit: false,
|
||||
|
||||
selectedDestination: null,
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
},
|
||||
|
||||
showProcessingModal(state) {
|
||||
return state.processMassEdit;
|
||||
return state.processDestinationMassEdit;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -223,11 +223,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
*/
|
||||
|
||||
getDestinationsView(state) {
|
||||
if (state.massEditDestinations !== null) {
|
||||
return state.massEditDestinations.destinations;
|
||||
}
|
||||
|
||||
return state.singleSelectedPremise?.destinations ?? [];
|
||||
return state.destinations?.destinations ?? [];
|
||||
},
|
||||
|
||||
getDestinationById(state) {
|
||||
|
|
@ -248,14 +244,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
|
||||
getSelectedDestinationsData: (state) => state.selectedDestination,
|
||||
|
||||
/**
|
||||
* Mass editing stuff
|
||||
* ===============================
|
||||
*/
|
||||
|
||||
isMassEdit(state) {
|
||||
return state.massEditDestinations !== null;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
||||
|
|
@ -264,73 +253,118 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
* =================
|
||||
*/
|
||||
|
||||
prepareMassEdit(dataSourcePremiseId, editedPremiseIds) {
|
||||
prepareDestinations(dataSourcePremiseId, editedPremiseIds, massEdit = false, fromMassEditView = false) {
|
||||
if (this.premisses === null) return;
|
||||
|
||||
if (!editedPremiseIds || !dataSourcePremiseId || editedPremiseIds.length === 0) return;
|
||||
|
||||
this.massEditDestinations = {
|
||||
this.destinations = {
|
||||
premise_ids: editedPremiseIds,
|
||||
destinations: this.getById(dataSourcePremiseId)?.destinations.map(d => this.copyAllButRouting(d)) ?? [],
|
||||
toBeAdded: null,
|
||||
toBeRemoved: null,
|
||||
massEdit: massEdit,
|
||||
fromMassEditView: fromMassEditView,
|
||||
destinations: this.premisses.find(p => String(p.id) === String(dataSourcePremiseId))?.destinations.map(d => this.copyAllFromPremises(d, !massEdit)) ?? [],
|
||||
};
|
||||
|
||||
this.selectedDestination = null;
|
||||
|
||||
},
|
||||
async finishMassEdit() {
|
||||
this.processMassEdit = true;
|
||||
async executeDestinationsMassEdit() {
|
||||
|
||||
const destinations = [];
|
||||
if (!this.destinations.massEdit) {
|
||||
|
||||
this.massEditDestinations.destinations.forEach(d => {
|
||||
const dest = {
|
||||
destination_node_id: d.destination_node.id,
|
||||
annual_amount: d.annual_amount,
|
||||
disposal_costs: d.userDefinedHandlingCosts ? d.disposal_costs : null,
|
||||
repackaging_costs: d.userDefinedHandlingCosts ? d.repackaging_costs : null,
|
||||
handling_costs: d.userDefinedHandlingCosts ? d.handling_costs : null,
|
||||
this.destinations.premise_ids.forEach(premiseId => {
|
||||
const toPremise = this.getById(premiseId);
|
||||
|
||||
this.destinations.destinations.forEach(fromDest => {
|
||||
const toDest = toPremise.destinations.find(to => fromDest.id.substring(1) === String(to.id));
|
||||
|
||||
if ((toDest ?? null) === null) {
|
||||
throw new Error("Destination not found in premise: " + premiseId + " -> " + d.id);
|
||||
}
|
||||
|
||||
this.copyAllToPremise(fromDest, toDest);
|
||||
|
||||
const body = {
|
||||
annual_amount: toDest.annual_amount,
|
||||
repackaging_costs: toDest.repackaging_costs,
|
||||
handling_costs: toDest.handling_costs,
|
||||
disposal_costs: toDest.disposal_costs,
|
||||
is_d2d: toDest.is_d2d,
|
||||
rate_d2d: toDest.rate_d2d,
|
||||
route_selected_id: toDest.routes.find(r => r.is_selected)?.id ?? null,
|
||||
};
|
||||
|
||||
const url = `${config.backendUrl}/calculation/destination/${toDest.id}`;
|
||||
this.performRequest('PUT', url, body, false);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
} else {
|
||||
this.processDestinationMassEdit = true;
|
||||
|
||||
const destinations = [];
|
||||
|
||||
this.destinations.destinations.forEach(d => {
|
||||
const dest = {
|
||||
destination_node_id: d.destination_node.id,
|
||||
annual_amount: d.annual_amount,
|
||||
disposal_costs: d.userDefinedHandlingCosts ? d.disposal_costs : null,
|
||||
repackaging_costs: d.userDefinedHandlingCosts ? d.repackaging_costs : null,
|
||||
handling_costs: d.userDefinedHandlingCosts ? d.handling_costs : null,
|
||||
}
|
||||
destinations.push(dest);
|
||||
})
|
||||
|
||||
const body = {destinations: destinations, premise_id: this.destinations.premise_ids};
|
||||
const url = `${config.backendUrl}/calculation/destination/`;
|
||||
|
||||
const data = await this.performRequest('PUT', url, body).catch(e => {
|
||||
this.destinations = null;
|
||||
this.processDestinationMassEdit = false;
|
||||
})
|
||||
|
||||
if (data) {
|
||||
for (const id of Object.keys(data)) {
|
||||
this.premisses.find(p => String(p.id) === id).destinations = data[id];
|
||||
}
|
||||
}
|
||||
destinations.push(dest);
|
||||
})
|
||||
|
||||
const body = {destinations: destinations, premise_id: this.massEditDestinations.premise_ids};
|
||||
const url = `${config.backendUrl}/calculation/destination/`;
|
||||
|
||||
const data = await this.performRequest('PUT', url, body).catch(e => {
|
||||
this.massEditDestinations = null;
|
||||
this.processMassEdit = false;
|
||||
})
|
||||
|
||||
if (data) {
|
||||
for (const id of Object.keys(data)) {
|
||||
this.premisses.find(p => String(p.id) === id).destinations = data[id];
|
||||
}
|
||||
this.destinations = null;
|
||||
this.processDestinationMassEdit = false;
|
||||
}
|
||||
|
||||
this.massEditDestinations = null;
|
||||
this.processMassEdit = false;
|
||||
},
|
||||
cancelMassEdit() {
|
||||
this.massEditDestinations = null;
|
||||
this.destinations = null;
|
||||
},
|
||||
copyAllButRouting(from, to = null) {
|
||||
|
||||
const fromIsOrig = (to === null);
|
||||
copyAllFromPremises(from, fullCopy = true) {
|
||||
|
||||
const d = {};
|
||||
|
||||
d.id = `e${from.id}`;
|
||||
d.destination_node = structuredClone(toRaw(from.destination_node));
|
||||
d.routes = fullCopy ? structuredClone(toRaw(from.routes)) : null;
|
||||
|
||||
d.annual_amount = from.annual_amount;
|
||||
d.is_d2d = from.is_d2d;
|
||||
d.rate_d2d = from.is_d2d ? from.rate_d2d : null;
|
||||
d.handling_costs = from.handling_costs;
|
||||
d.disposal_costs = from.disposal_costs;
|
||||
d.repackaging_costs = from.repackaging_costs;
|
||||
d.userDefinedHandlingCosts = from.handling_costs !== null || from.disposal_costs !== null || from.repackaging_costs !== null;
|
||||
|
||||
return d;
|
||||
},
|
||||
copyAllToPremise(from, to, fullCopy = true) {
|
||||
|
||||
const d = to ?? {};
|
||||
|
||||
if (fromIsOrig) {
|
||||
d.id = `e${from.id}`;
|
||||
d.destination_node = structuredClone(toRaw(from.destination_node));
|
||||
d.massEdit = true;
|
||||
}
|
||||
|
||||
d.annual_amount = from.annual_amount;
|
||||
d.is_d2d = from.is_d2d;
|
||||
d.rate_d2d = from.is_d2d ? from.rate_d2d : null;
|
||||
|
||||
if (fromIsOrig || from.userDefinedHandlingCosts) {
|
||||
if (from.userDefinedHandlingCosts) {
|
||||
d.disposal_costs = from.disposal_costs;
|
||||
d.repackaging_costs = from.repackaging_costs;
|
||||
d.handling_costs = from.handling_costs;
|
||||
|
|
@ -340,6 +374,10 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
d.handling_costs = null;
|
||||
}
|
||||
|
||||
if (fullCopy && (from.routes ?? null) !== null) {
|
||||
to.routes.forEach(route => route.is_selected = from.routes.find(r => r.id === route.id)?.is_selected ?? false);
|
||||
}
|
||||
|
||||
return d;
|
||||
},
|
||||
|
||||
|
|
@ -351,10 +389,12 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
selectDestination(id) {
|
||||
if (this.premisses === null) return;
|
||||
|
||||
const destination = !this.isMassEdit ? this.getDestinationById(id) : this.massEditDestinations.destinations.find(d => d.id === id);
|
||||
console.log("selectDestination:", id)
|
||||
|
||||
const dest = this.destinations.destinations.find(d => d.id === id);
|
||||
|
||||
|
||||
if ((destination ?? null) == null) {
|
||||
if ((dest ?? null) == null) {
|
||||
const error = {
|
||||
code: 'Frontend error.',
|
||||
message: `Destination not found: ${id}. Please contact support.`,
|
||||
|
|
@ -363,54 +403,79 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
throw new Error("Internal frontend error: Destination not found: " + id);
|
||||
}
|
||||
|
||||
const data = structuredClone(toRaw(destination));
|
||||
data.userDefinedHandlingCosts = data.handling_costs !== null || data.disposal_costs !== null || data.repackaging_costs !== null;
|
||||
this.selectedDestination = data;
|
||||
this.selectedDestination = structuredClone(toRaw(dest));
|
||||
},
|
||||
deselectDestinations(save = false) {
|
||||
async deselectDestinations(save = false) {
|
||||
if (this.premisses === null) return;
|
||||
|
||||
|
||||
if (save) {
|
||||
const orig = this.isMassEdit ?
|
||||
this.massEditDestinations.destinations.find(d => d.id === this.selectedDestination.id) :
|
||||
this.getDestinationById(this.selectedDestination.id)
|
||||
const idx = this.destinations.destinations.findIndex(d => d.id === this.selectedDestination.id);
|
||||
this.destinations.destinations.splice(idx, 1, this.selectedDestination);
|
||||
|
||||
this.copyAllButRouting(this.selectedDestination, orig);
|
||||
if (!this.destinations.fromMassEditView) {
|
||||
//TODO write trough backend if no massEdit
|
||||
|
||||
const toDest = this.singleSelectedPremise.destinations.find(to => this.selectedDestination.id.substring(1) === String(to.id));
|
||||
this.copyAllToPremise(this.selectedDestination, toDest);
|
||||
|
||||
const body = {
|
||||
annual_amount: toDest.annual_amount,
|
||||
repackaging_costs: toDest.repackaging_costs,
|
||||
handling_costs: toDest.handling_costs,
|
||||
disposal_costs: toDest.disposal_costs,
|
||||
is_d2d: toDest.is_d2d,
|
||||
rate_d2d: toDest.rate_d2d,
|
||||
route_selected_id: toDest.routes.find(r => r.is_selected)?.id ?? null,
|
||||
};
|
||||
|
||||
console.log(body)
|
||||
|
||||
const url = `${config.backendUrl}/calculation/destination/${toDest.id}`;
|
||||
await this.performRequest('PUT', url, body, false);
|
||||
|
||||
if (!this.isMassEdit) {
|
||||
this.selectedDestination.routes.forEach(route => {
|
||||
const origRoute = orig.routes.find(r => r.id === route.id);
|
||||
origRoute.is_selected = route.is_selected;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.selectedDestination = null;
|
||||
},
|
||||
async deleteDestination(id) {
|
||||
|
||||
if (this.isMassEdit) {
|
||||
|
||||
const idx = this.massEditDestinations.destinations.findIndex(d => d.id === id);
|
||||
/*
|
||||
* 1. delete from destinations copy
|
||||
*/
|
||||
const idx = this.destinations.destinations.findIndex(d => d.id === id);
|
||||
|
||||
if (idx === -1) {
|
||||
console.info("Destination not found in mass edit: , id)");
|
||||
return;
|
||||
}
|
||||
if (idx === -1) {
|
||||
console.info("Destination not found in mass edit: , id)");
|
||||
return;
|
||||
}
|
||||
|
||||
this.massEditDestinations.destinations.splice(idx, 1);
|
||||
} else {
|
||||
this.destinations.destinations.splice(idx, 1);
|
||||
|
||||
/*
|
||||
* 2. delete from backend if not mass edit
|
||||
*/
|
||||
|
||||
if (!this.destinations.massEdit && id.startsWith('e')) { /* 'v'-ids cannot be deleted because they only exist in the frontend */
|
||||
if (this.premisses === null) return;
|
||||
|
||||
const url = `${config.backendUrl}/calculation/destination/${id}`;
|
||||
const origId = id.substring(1);
|
||||
|
||||
const url = `${config.backendUrl}/calculation/destination/${origId}`;
|
||||
await this.performRequest('DELETE', url, null, false).catch(async e => {
|
||||
console.error("Unable to delete destination: " + id + "");
|
||||
console.error("Unable to delete destination: " + origId + "");
|
||||
console.error(e);
|
||||
await this.loadPremissesIfNeeded(this.premisses.map(p => p.id));
|
||||
});
|
||||
|
||||
for (const p of this.premisses) {
|
||||
const toBeDeleted = p.destinations.findIndex(d => d.id === id)
|
||||
const toBeDeleted = p.destinations.findIndex(d => String(d.id) === String(origId))
|
||||
|
||||
console.log(toBeDeleted)
|
||||
|
||||
if (toBeDeleted !== -1) {
|
||||
p.destinations.splice(toBeDeleted, 1)
|
||||
break;
|
||||
|
|
@ -420,9 +485,9 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
},
|
||||
async addDestination(node) {
|
||||
|
||||
if (this.isMassEdit) {
|
||||
if (this.destinations.massEdit) {
|
||||
|
||||
const existing = this.massEditDestinations.destinations.find(d => d.destination_node.id === node.id);
|
||||
const existing = this.destinations.destinations.find(d => d.destination_node.id === node.id);
|
||||
console.log(existing)
|
||||
|
||||
if ((existing ?? null) !== null) {
|
||||
|
|
@ -443,16 +508,17 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
userDefinedHandlingCosts: false,
|
||||
};
|
||||
|
||||
this.massEditDestinations.destinations.push(destination);
|
||||
this.destinations.destinations.push(destination);
|
||||
|
||||
return [destination.id];
|
||||
|
||||
} else {
|
||||
const id = node.id;
|
||||
|
||||
const toBeUpdated = this.premisses?.filter(p => p.selected).map(p => p.id);
|
||||
|
||||
if (toBeUpdated === null) return;
|
||||
const toBeUpdated = this.destinations.fromMassEditView ? this.destinations.premise_ids : this.premisses?.filter(p => p.selected).map(p => p.id);
|
||||
|
||||
if (toBeUpdated === null || toBeUpdated.length === 0) return;
|
||||
|
||||
const body = {destination_node_id: id, premise_id: toBeUpdated};
|
||||
const url = `${config.backendUrl}/calculation/destination/`;
|
||||
|
|
@ -464,11 +530,17 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
throw e;
|
||||
});
|
||||
|
||||
const mappedIds = []
|
||||
|
||||
for (const id of Object.keys(destinations)) {
|
||||
this.premisses.find(p => String(p.id) === id).destinations.push(destinations[id]);
|
||||
const premise = this.premisses.find(p => String(p.id) === id)
|
||||
premise.destinations.push(destinations[id]);
|
||||
const mappedDestination = this.copyAllFromPremises(destinations[id], true);
|
||||
mappedIds.push(mappedDestination.id);
|
||||
this.destinations.destinations.push(mappedDestination);
|
||||
}
|
||||
|
||||
return Object.values(destinations).map(d => d.id);
|
||||
return mappedIds;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -478,21 +550,29 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
* (these are more extensive changes. The edited premises are replaced by the one returned by the backend)
|
||||
*/
|
||||
|
||||
async setSupplier(id, updateMasterData) {
|
||||
async setSupplier(id, updateMasterData, ids = null) {
|
||||
console.log("setSupplier");
|
||||
|
||||
const selectedId = this.singleSelectId;
|
||||
|
||||
const body = {supplier_node_id: id, update_master_data: updateMasterData};
|
||||
const url = `${config.backendUrl}/calculation/supplier/`;
|
||||
await this.setData(url, body);
|
||||
|
||||
if (selectedId != null && this.destinations && !this.destinations.fromMassEditView) {
|
||||
this.prepareDestinations(selectedId, [selectedId]);
|
||||
}
|
||||
|
||||
},
|
||||
async setMaterial(id, updateMasterData) {
|
||||
async setMaterial(id, updateMasterData, ids = null) {
|
||||
console.log("setMaterial");
|
||||
const body = {material_id: id, update_master_data: updateMasterData};
|
||||
const url = `${config.backendUrl}/calculation/material/`;
|
||||
await this.setData(url, body);
|
||||
await this.setData(url, body, ids);
|
||||
},
|
||||
async setData(url, body) {
|
||||
async setData(url, body, ids = null) {
|
||||
|
||||
const toBeUpdated = this.premisses ? this.premisses.filter(p => p.selected).map(p => p.id) : null;
|
||||
const toBeUpdated = this.premisses ? (ids ? (ids) : (this.premisses.filter(p => p.selected).map(p => p.id))) : null;
|
||||
|
||||
if (null !== toBeUpdated) {
|
||||
this.selectedLoading = true;
|
||||
|
|
@ -529,6 +609,88 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
return replaced;
|
||||
},
|
||||
|
||||
/**
|
||||
* Save
|
||||
*/
|
||||
|
||||
async savePrice(ids = null) {
|
||||
let success = true;
|
||||
const toBeUpdated = this.premisses ? (ids ? (ids.map(id => this.premisses.find(p => String(p.id) === String(id)))) : (this.premisses.filter(p => p.selected))) : null;
|
||||
|
||||
if (!toBeUpdated?.length) return;
|
||||
|
||||
const body = {
|
||||
premise_ids: toBeUpdated.map(p => p.id),
|
||||
material_cost: toBeUpdated[0].material_cost,
|
||||
oversea_share: toBeUpdated[0].oversea_share,
|
||||
is_fca_enabled: toBeUpdated[0].is_fca_enabled
|
||||
};
|
||||
|
||||
await this.performRequest('POST', `${config.backendUrl}/calculation/price/`, body, false).catch(e => {
|
||||
success = false;
|
||||
})
|
||||
|
||||
return success;
|
||||
},
|
||||
async savePackaging(ids = null) {
|
||||
let success = true;
|
||||
|
||||
const toBeUpdated = this.premisses ? (ids ? (ids.map(id => this.premisses.find(p => String(p.id) === String(id)))) : (this.premisses.filter(p => p.selected))) : null;
|
||||
|
||||
if (!toBeUpdated?.length) return;
|
||||
|
||||
console.log(toBeUpdated[0]);
|
||||
|
||||
const body = {
|
||||
premise_ids: toBeUpdated.map(p => p.id),
|
||||
|
||||
handling_unit: {
|
||||
weight: toBeUpdated[0].handling_unit.weight,
|
||||
weight_unit: toBeUpdated[0].handling_unit.weight_unit,
|
||||
|
||||
length: toBeUpdated[0].handling_unit.length,
|
||||
width: toBeUpdated[0].handling_unit.width,
|
||||
height: toBeUpdated[0].handling_unit.height,
|
||||
dimension_unit: toBeUpdated[0].handling_unit.dimension_unit,
|
||||
|
||||
content_unit_count: toBeUpdated[0].handling_unit.content_unit_count,
|
||||
},
|
||||
|
||||
is_mixable: toBeUpdated[0].is_mixable,
|
||||
is_stackable: toBeUpdated[0].is_stackable,
|
||||
|
||||
};
|
||||
|
||||
await this.performRequest('POST', `${config.backendUrl}/calculation/packaging/`, body, false).catch(() => {
|
||||
success = false;
|
||||
})
|
||||
|
||||
return success;
|
||||
|
||||
},
|
||||
async saveMaterial(ids = null) {
|
||||
let success = true;
|
||||
const toBeUpdated = this.premisses ? (ids ? (ids.map(id => this.premisses.find(p => String(p.id) === String(id)))) : (this.premisses.filter(p => p.selected))) : null;
|
||||
|
||||
|
||||
|
||||
if (!toBeUpdated?.length) return;
|
||||
|
||||
const body = {
|
||||
premise_ids: toBeUpdated.map(p => p.id),
|
||||
|
||||
hs_code: toBeUpdated[0].hs_code,
|
||||
tariff_rate: toBeUpdated[0].tariff_rate,
|
||||
};
|
||||
|
||||
await this.performRequest('POST', `${config.backendUrl}/calculation/material/`, body, false).catch(() => {
|
||||
success = false;
|
||||
})
|
||||
|
||||
|
||||
return success;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* PREMISE stuff
|
||||
|
|
@ -536,24 +698,29 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
*/
|
||||
deselectPremise() {
|
||||
this.selectedLoading = true;
|
||||
|
||||
this.premisses.forEach(p => p.selected = false);
|
||||
this.selectedLoading = false;
|
||||
},
|
||||
selectPremise(id) {
|
||||
this.selectedLoading = true;
|
||||
this.premisses.forEach(p => p.selected = p.id === id);
|
||||
|
||||
this.destinations = null;
|
||||
this.selectedDestination = null;
|
||||
this.selectedLoading = false;
|
||||
},
|
||||
setSelectTo(ids, value) {
|
||||
this.selectedLoading = true;
|
||||
|
||||
this.premisses.forEach(p => p.selected = ids.includes(p.id) ? value : p.selected);
|
||||
|
||||
this.selectedLoading = false;
|
||||
},
|
||||
async selectPremises(id, ids) {
|
||||
async selectSinglePremise(id, ids) {
|
||||
this.selectedLoading = true;
|
||||
|
||||
await this.loadPremissesIfNeeded(ids);
|
||||
const toSelect = String(id);
|
||||
this.premisses.forEach(p => p.selected = toSelect.includes(String(p.id)));
|
||||
|
||||
this.premisses.forEach(p => p.selected = String(id) === String(p.id));
|
||||
|
||||
this.prepareDestinations(id, [id]);
|
||||
|
||||
this.selectedLoading = false;
|
||||
},
|
||||
async loadAndSelectSinglePremise(id) {
|
||||
|
|
@ -563,7 +730,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
this.premises = [];
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('premissIds', `${id}`);
|
||||
params.append('premissIds', `${[id]}`);
|
||||
const url = `${config.backendUrl}/calculation/edit/${params.size === 0 ? '' : '?'}${params.toString()}`;
|
||||
|
||||
this.premisses = await this.performRequest('GET', url, null).catch(e => {
|
||||
|
|
@ -572,6 +739,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
});
|
||||
|
||||
this.premisses.forEach(p => p.selected = true);
|
||||
this.prepareDestinations(id, [id]);
|
||||
this.selectedLoading = false;
|
||||
this.loading = false;
|
||||
},
|
||||
|
|
@ -605,7 +773,6 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
const idx = this.premisses.findIndex(p => p.id === id);
|
||||
this.premisses.splice(idx, 1);
|
||||
},
|
||||
|
||||
async performRequest(method, url, body, expectResponse = true) {
|
||||
|
||||
const params = {
|
||||
|
|
@ -667,15 +834,33 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
|||
}
|
||||
} 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
|
||||
}
|
||||
console.error(error);
|
||||
const errorStore = useErrorStore();
|
||||
void errorStore.addError(error, {store: this, request: request});
|
||||
throw new Error('Internal backend error');
|
||||
|
||||
});
|
||||
|
||||
const error = {
|
||||
code: "Return code error " + response.status,
|
||||
message: "Server returned wrong response code",
|
||||
trace: null
|
||||
code: data.error.code,
|
||||
title: data.error.title,
|
||||
message: data.error.message,
|
||||
trace: data.error.trace
|
||||
}
|
||||
|
||||
console.error(error);
|
||||
const errorStore = useErrorStore();
|
||||
void errorStore.addError(error, {store: this, request: request});
|
||||
throw new Error('Internal backend error');
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import de.avatic.lcc.dto.error.ErrorDTO;
|
|||
import de.avatic.lcc.dto.error.ErrorResponseDTO;
|
||||
import de.avatic.lcc.util.exception.base.BadRequestException;
|
||||
import de.avatic.lcc.util.exception.base.ForbiddenException;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
|
@ -18,6 +19,7 @@ import org.springframework.web.method.annotation.MethodArgumentTypeMismatchExcep
|
|||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
|
@ -42,33 +44,28 @@ public class GlobalExceptionHandler {
|
|||
public ResponseEntity<ErrorResponseDTO> handleValidationExceptions(
|
||||
MethodArgumentNotValidException exception) {
|
||||
|
||||
// Extract field-specific validation errors
|
||||
Map<String, String> fieldErrors = new HashMap<>();
|
||||
exception.getBindingResult().getFieldErrors().forEach(error -> {
|
||||
fieldErrors.put(error.getField(), error.getDefaultMessage());
|
||||
});
|
||||
|
||||
// Create a readable error message
|
||||
String errorMessage = fieldErrors.isEmpty() ?
|
||||
"Validation failed" :
|
||||
"Validation failed for fields: " + String.join(", ", fieldErrors.keySet().stream().map(key -> key + " - " + fieldErrors.get(key)).toList());
|
||||
|
||||
// You might want to create a custom ErrorDTO that can handle field errors
|
||||
ErrorDTO error = new ErrorDTO(
|
||||
exception.getClass().getName(),
|
||||
"Constraint Violation",
|
||||
exception.getMessage(),
|
||||
"Validation Failed",
|
||||
errorMessage,
|
||||
Arrays.asList(exception.getStackTrace())
|
||||
);
|
||||
|
||||
return new ResponseEntity<>(new ErrorResponseDTO(error), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// @ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
// @ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
// public ResponseEntity<ErrorResponseDTO> handleMethodArgumentNotValid(MethodArgumentNotValidException exception) {
|
||||
//
|
||||
// ErrorDTO error = new ErrorDTO(
|
||||
// exception.getClass().getSimpleName(),
|
||||
// exception.getTitle(),
|
||||
// exception.getMessage(),
|
||||
// "Invalid Arguments",
|
||||
// new HashMap<>() {{
|
||||
// put("errorMessage", exception.getMessage());
|
||||
// }}
|
||||
// );
|
||||
//
|
||||
// return new ResponseEntity<>(new ErrorResponseDTO(error), HttpStatus.BAD_REQUEST);
|
||||
// }
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@ExceptionHandler(BadRequestException.class)
|
||||
public ResponseEntity<ErrorResponseDTO> handleMethodArgumentNotValid(BadRequestException exception) {
|
||||
|
|
@ -115,12 +112,18 @@ public class GlobalExceptionHandler {
|
|||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@ExceptionHandler(ConstraintViolationException.class)
|
||||
public ResponseEntity<ErrorResponseDTO> handleConstraintViolation(ConstraintViolationException exception) { //
|
||||
public ResponseEntity<ErrorResponseDTO> handleConstraintViolation(ConstraintViolationException exception) {
|
||||
|
||||
// Extract constraint violation details
|
||||
String errorMessage = exception.getConstraintViolations()
|
||||
.stream()
|
||||
.map(ConstraintViolation::getMessage)
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
ErrorDTO error = new ErrorDTO(
|
||||
exception.getClass().getName(),
|
||||
"Constraint Violation",
|
||||
exception.getMessage(),
|
||||
errorMessage.isEmpty() ? "Validation constraints violated" : errorMessage,
|
||||
Arrays.asList(exception.getStackTrace())
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import de.avatic.lcc.util.exception.badrequest.InvalidArgumentException;
|
|||
import de.avatic.lcc.util.exception.base.BadRequestException;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
|
@ -40,6 +42,7 @@ import java.util.Map;
|
|||
@Validated
|
||||
public class PremiseController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PremiseController.class);
|
||||
private final PremiseSearchStringAnalyzerService premiseSearchStringAnalyzerService;
|
||||
private final PremisesService premisesServices;
|
||||
private final PremiseCreationService premiseCreationService;
|
||||
|
|
@ -146,7 +149,7 @@ public class PremiseController {
|
|||
}
|
||||
|
||||
@PostMapping({"/destination", "/destination/"})
|
||||
public ResponseEntity<Map<Integer, DestinationDTO>> createDestination(@RequestBody DestinationCreateDTO destinationCreateDTO) {
|
||||
public ResponseEntity<Map<Integer, DestinationDTO>> createDestination(@RequestBody @Valid DestinationCreateDTO destinationCreateDTO) {
|
||||
return ResponseEntity.ok(destinationService.createDestination(destinationCreateDTO));
|
||||
}
|
||||
|
||||
|
|
@ -162,6 +165,7 @@ public class PremiseController {
|
|||
|
||||
@PutMapping({"/destination/{id}", "/destination/{id}/"})
|
||||
public ResponseEntity<Void> updateDestination(@PathVariable @Min(1) Integer id, @RequestBody @Valid DestinationUpdateDTO destinationUpdateDTO) {
|
||||
log.info("Updating destination {}", destinationUpdateDTO);
|
||||
destinationService.updateDestination(id, destinationUpdateDTO);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
package de.avatic.lcc.dto.calculation.edit.destination;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DestinationCreateDTO {
|
||||
|
||||
@NotEmpty (message = "At least one premise must be selected")
|
||||
@NotNull (message = "At least one premise must be selected")
|
||||
@JsonProperty("premise_id")
|
||||
List<Integer> premiseId;
|
||||
List<@Min(value = 1, message = "Invalid premise id") Integer> premiseId;
|
||||
|
||||
@Min(value = 1, message = "Invalid destination node id")
|
||||
@NotNull (message = "Destination node id must be provided")
|
||||
@JsonProperty("destination_node_id")
|
||||
Integer destinationNodeId;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,17 +16,17 @@ public class DestinationSetListItemDTO {
|
|||
@Min(1)
|
||||
private Integer annualAmount;
|
||||
|
||||
@JsonProperty("repacking_cost")
|
||||
@JsonProperty("repackaging_costs")
|
||||
@DecimalMin(value = "0.00", message = "Amount must be greater than or equal 0")
|
||||
@Digits(integer = 13, fraction = 2, message = "Amount must have at most 2 decimal places")
|
||||
private Number repackingCost;
|
||||
|
||||
@JsonProperty("handling_cost")
|
||||
@JsonProperty("handling_costs")
|
||||
@DecimalMin(value = "0.00", message = "Amount must be greater than or equal 0")
|
||||
@Digits(integer = 13, fraction = 2, message = "Amount must have at most 2 decimal places")
|
||||
private Number handlingCost;
|
||||
|
||||
@JsonProperty("disposal_cost")
|
||||
@JsonProperty("disposal_costs")
|
||||
@DecimalMin(value = "0.00", message = "Amount must be greater than or equal 0")
|
||||
@Digits(integer = 13, fraction = 2, message = "Amount must have at most 2 decimal places")
|
||||
private Number disposalCost;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package de.avatic.lcc.dto.calculation.edit.destination;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.Digits;
|
||||
|
|
@ -11,17 +12,17 @@ public class DestinationUpdateDTO {
|
|||
@Min(1)
|
||||
private Integer annualAmount;
|
||||
|
||||
@JsonProperty("repacking_cost")
|
||||
@JsonProperty("repackaging_costs")
|
||||
@DecimalMin(value = "0.00", message = "Amount must be greater than or equal 0")
|
||||
@Digits(integer = 13, fraction = 2, message = "Amount must have at most 2 decimal places")
|
||||
private Number repackingCost;
|
||||
|
||||
@JsonProperty("handling_cost")
|
||||
@JsonProperty("handling_costs")
|
||||
@DecimalMin(value = "0.00", message = "Amount must be greater than or equal 0")
|
||||
@Digits(integer = 13, fraction = 2, message = "Amount must have at most 2 decimal places")
|
||||
private Number handlingCost;
|
||||
|
||||
@JsonProperty("disposal_cost")
|
||||
@JsonProperty("disposal_costs")
|
||||
@DecimalMin(value = "0.00", message = "Amount must be greater than or equal 0")
|
||||
@Digits(integer = 13, fraction = 2, message = "Amount must have at most 2 decimal places")
|
||||
private Number disposalCost;
|
||||
|
|
@ -30,6 +31,14 @@ public class DestinationUpdateDTO {
|
|||
@Min(1)
|
||||
private Integer routeSelectedId;
|
||||
|
||||
@JsonProperty("is_d2d")
|
||||
private Boolean d2d;
|
||||
|
||||
@DecimalMin(value = "0.00", message = "Amount must be greater than or equal 0")
|
||||
@Digits(integer = 13, fraction = 2, message = "Amount must have at most 2 decimal places")
|
||||
@JsonProperty("rate_d2d")
|
||||
private Number rateD2d;
|
||||
|
||||
public Number getRepackingCost() {
|
||||
return repackingCost;
|
||||
}
|
||||
|
|
@ -69,4 +78,34 @@ public class DestinationUpdateDTO {
|
|||
public void setAnnualAmount(Integer annualAmount) {
|
||||
this.annualAmount = annualAmount;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public Boolean getD2d() {
|
||||
return d2d;
|
||||
}
|
||||
|
||||
public void setD2d(Boolean d2d) {
|
||||
this.d2d = d2d;
|
||||
}
|
||||
|
||||
public Number getRateD2d() {
|
||||
return rateD2d;
|
||||
}
|
||||
|
||||
public void setRateD2d(Number rateD2d) {
|
||||
this.rateD2d = rateD2d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DestinationUpdateDTO{" +
|
||||
"annualAmount=" + annualAmount +
|
||||
", repackingCost=" + repackingCost +
|
||||
", handlingCost=" + handlingCost +
|
||||
", disposalCost=" + disposalCost +
|
||||
", routeSelectedId=" + routeSelectedId +
|
||||
", d2d=" + d2d +
|
||||
", rateD2d=" + rateD2d +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import java.util.List;
|
|||
|
||||
public class MaterialUpdateDTO {
|
||||
|
||||
@JsonProperty("premise_id")
|
||||
@JsonProperty("premise_ids")
|
||||
private List<Integer> premiseIds;
|
||||
|
||||
@JsonProperty("hs_code")
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ public class PackagingUpdateDTO {
|
|||
@JsonProperty("is_stackable")
|
||||
private Boolean isStackable;
|
||||
|
||||
private List<@Min(1) Integer> premiseIds;
|
||||
|
||||
@JsonProperty("premise_ids")
|
||||
private List<@Min(value = 1, message = "Invalid value for premise id. Must be equal to 1 or greater") Integer> premiseIds;
|
||||
|
||||
public DimensionDTO getDimensions() {
|
||||
return dimensions;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ public class PriceUpdateDTO {
|
|||
@JsonProperty("premise_ids")
|
||||
private List<@Min(1) Integer> premiseIds;
|
||||
|
||||
@JsonProperty("material_cost")
|
||||
@DecimalMin(value = "0.01", message = "Amount must be greater than 0")
|
||||
@Digits(integer = 13, fraction = 2, message = "Amount must have at most 2 decimal places")
|
||||
private Number price;
|
||||
|
|
@ -23,7 +24,7 @@ public class PriceUpdateDTO {
|
|||
@Digits(integer = 4, fraction = 4, message = "Amount must have at most 4 decimal places")
|
||||
private Number overseaShare;
|
||||
|
||||
@JsonProperty("fca_fee_included")
|
||||
@JsonProperty("is_fca_enabled")
|
||||
private Boolean includeFcaFee;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -179,4 +179,8 @@ public class Node {
|
|||
public String getDebugText() {
|
||||
return externalMappingId == null ? "\uD83D\uDC64" + name : externalMappingId;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getDebugText();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ public class MaterialRepository {
|
|||
|
||||
var materials = filter.isPresent() ?
|
||||
jdbcTemplate.query(query, new MaterialMapper(),
|
||||
"%" + filter.get() + "%", "%" + filter.get() + "%", pagination.getLimit(), pagination.getOffset()) :
|
||||
filter.get() + "%", filter.get() + "%", pagination.getLimit(), pagination.getOffset()) :
|
||||
jdbcTemplate.query(query, new MaterialMapper(),
|
||||
pagination.getLimit(), pagination.getOffset());
|
||||
|
||||
|
|
@ -100,7 +100,7 @@ public class MaterialRepository {
|
|||
|
||||
Integer totalCount = filter.isPresent() ?
|
||||
jdbcTemplate.queryForObject(countQuery, Integer.class,
|
||||
"%" + filter.get() + "%", "%" + filter.get() + "%") :
|
||||
filter.get() + "%", filter.get() + "%") :
|
||||
jdbcTemplate.queryForObject(countQuery, Integer.class);
|
||||
|
||||
return new SearchQueryResult<>(materials, pagination.getPage(), totalCount, pagination.getLimit());
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public class DestinationRepository {
|
|||
}
|
||||
|
||||
@Transactional
|
||||
public void update(Integer id, Integer annualAmount, BigDecimal repackingCost, BigDecimal disposalCost, BigDecimal handlingCost) {
|
||||
public void update(Integer id, Integer annualAmount, BigDecimal repackingCost, BigDecimal disposalCost, BigDecimal handlingCost, Boolean isD2d, BigDecimal d2dRate) {
|
||||
if (id == null) {
|
||||
throw new InvalidArgumentException("ID cannot be null");
|
||||
}
|
||||
|
|
@ -61,20 +61,25 @@ public class DestinationRepository {
|
|||
List<String> setClauses = new ArrayList<>();
|
||||
|
||||
// Build dynamic SET clauses based on non-null parameters
|
||||
if (repackingCost != null) {
|
||||
setClauses.add("repacking_cost = :repackingCost");
|
||||
parameters.put("repackingCost", repackingCost);
|
||||
}
|
||||
|
||||
if (disposalCost != null) {
|
||||
setClauses.add("disposal_cost = :disposalCost");
|
||||
parameters.put("disposalCost", disposalCost);
|
||||
}
|
||||
setClauses.add("repacking_cost = :repackingCost");
|
||||
parameters.put("repackingCost", repackingCost);
|
||||
|
||||
|
||||
setClauses.add("disposal_cost = :disposalCost");
|
||||
parameters.put("disposalCost", disposalCost);
|
||||
|
||||
|
||||
setClauses.add("handling_cost = :handlingCost");
|
||||
parameters.put("handlingCost", handlingCost);
|
||||
|
||||
var setD2d = isD2d != null ? isD2d : false;
|
||||
setClauses.add("is_d2d = :isD2d");
|
||||
parameters.put("isD2d", setD2d);
|
||||
|
||||
setClauses.add("rate_d2d = :d2dRate");
|
||||
parameters.put("d2dRate", setD2d ? d2dRate : null);
|
||||
|
||||
if (handlingCost != null) {
|
||||
setClauses.add("handling_cost = :handlingCost");
|
||||
parameters.put("handlingCost", handlingCost);
|
||||
}
|
||||
|
||||
if (annualAmount != null) {
|
||||
setClauses.add("annual_amount = :annualAmount");
|
||||
|
|
@ -147,7 +152,7 @@ public class DestinationRepository {
|
|||
public Map<Integer, List<Destination>> getByPremiseIdsAndNodeIds(List<Integer> premiseIds, List<Integer> nodeIds, Integer userId) {
|
||||
|
||||
Map<Integer, List<Destination>> destinations = new HashMap<>();
|
||||
|
||||
|
||||
for (Integer premiseId : premiseIds) {
|
||||
String placeholder = String.join(",", Collections.nCopies(nodeIds.size(), "?"));
|
||||
String query = "SELECT * FROM premise_destination JOIN premise ON premise_destination.premise_id = premise.id WHERE premise_destination.premise_id = ? AND premise_destination.destination_node_id IN (" + placeholder + ") AND premise.user_id = ?";
|
||||
|
|
@ -159,9 +164,9 @@ public class DestinationRepository {
|
|||
}
|
||||
params[nodeIds.size()] = premiseId;
|
||||
params[nodeIds.size() + 1] = userId;
|
||||
destinations.put(premiseId,jdbcTemplate.query(query, new DestinationMapper(), params));
|
||||
destinations.put(premiseId, jdbcTemplate.query(query, new DestinationMapper(), params));
|
||||
}
|
||||
|
||||
|
||||
return destinations;
|
||||
}
|
||||
|
||||
|
|
@ -214,7 +219,7 @@ public class DestinationRepository {
|
|||
var ownerId = getOwnerIdById(id);
|
||||
|
||||
if (ownerId.isEmpty() || !ownerId.get().equals(userId)) {
|
||||
throw new ForbiddenException("Access violation. Accessing destination with id " + id + " (owner: "+ownerId.orElse(null)+" user: " + userId + ")");
|
||||
throw new ForbiddenException("Access violation. Accessing destination with id " + id + " (owner: " + ownerId.orElse(null) + " user: " + userId + ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ public class DestinationService {
|
|||
var destination = new Destination();
|
||||
destination.setDestinationNodeId(destinationNodeId);
|
||||
destination.setPremiseId(premise.getId());
|
||||
destination.setAnnualAmount(annualAmount);
|
||||
destination.setAnnualAmount(annualAmount == null ? 0 : annualAmount);
|
||||
destination.setD2d(false);
|
||||
destination.setLeadTimeD2d(null);
|
||||
destination.setRateD2d(null);
|
||||
|
|
@ -151,7 +151,8 @@ public class DestinationService {
|
|||
destinationUpdateDTO.getAnnualAmount(),
|
||||
destinationUpdateDTO.getRepackingCost() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getRepackingCost().doubleValue()),
|
||||
destinationUpdateDTO.getDisposalCost() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getDisposalCost().doubleValue()),
|
||||
destinationUpdateDTO.getHandlingCost() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getHandlingCost().doubleValue()));
|
||||
destinationUpdateDTO.getHandlingCost() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getHandlingCost().doubleValue()),
|
||||
destinationUpdateDTO.getD2d(), destinationUpdateDTO.getRateD2d() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getRateD2d().doubleValue()));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ public class ChainResolver {
|
|||
}
|
||||
|
||||
log.info("Found {} chains for node {}", foundChains.size(), nodeId);
|
||||
log.info("Found chains: {}", foundChains);
|
||||
return foundChains;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,13 +63,13 @@ public class ChangeMaterialService {
|
|||
Map<Integer, Premise> uniqueMap = new HashMap<>();
|
||||
List<Premise> premisesToBeDeleted = new ArrayList<>();
|
||||
allPremises.forEach(p -> {
|
||||
if (null != uniqueMap.putIfAbsent(p.getMaterialId(), p)) premisesToBeDeleted.add(p);
|
||||
if (null != uniqueMap.putIfAbsent(p.getSupplierNodeId(), p)) premisesToBeDeleted.add(p);
|
||||
});
|
||||
Collection<Premise> premisesToProcess = uniqueMap.values();
|
||||
|
||||
// check if user owns all premises:
|
||||
if (allPremises.stream().anyMatch(p -> !p.getUserId().equals(userId)))
|
||||
throw new IllegalArgumentException("Not authorized to change suppliers of premises owned by other users");
|
||||
throw new IllegalArgumentException("Not authorized to change material of premises owned by other users");
|
||||
|
||||
// check for any other collisions, and mark as "to be deleted":
|
||||
premisesToBeDeleted.addAll(premisesToProcess.stream().map(p -> premiseRepository.getCollidingPremisesOnChange(userId, p.getId(), materialId, p.getSupplierNodeId())).flatMap(List::stream).toList());
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import de.avatic.lcc.dto.generic.DimensionDTO;
|
|||
import de.avatic.lcc.model.packaging.PackagingDimension;
|
||||
import de.avatic.lcc.model.packaging.PackagingType;
|
||||
import de.avatic.lcc.model.premises.Premise;
|
||||
import de.avatic.lcc.model.utils.DimensionUnit;
|
||||
import de.avatic.lcc.model.utils.WeightUnit;
|
||||
import de.avatic.lcc.util.exception.badrequest.InvalidArgumentException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
|
@ -41,11 +43,11 @@ public class DimensionTransformer {
|
|||
entity.setId(dto.getId());
|
||||
entity.setType(dto.getType());
|
||||
|
||||
entity.setLength(dto.getDimensionUnit().convertToMM(dto.getLength()));
|
||||
entity.setWidth(dto.getDimensionUnit().convertToMM(dto.getWidth()));
|
||||
entity.setHeight( dto.getDimensionUnit().convertToMM(dto.getHeight()));
|
||||
entity.setLength(doDimensionConversion(dto.getLength(), dto.getDimensionUnit()));
|
||||
entity.setWidth(doDimensionConversion(dto.getWidth(), dto.getDimensionUnit()));
|
||||
entity.setHeight( doDimensionConversion(dto.getHeight(), dto.getDimensionUnit()));
|
||||
entity.setDimensionUnit(dto.getDimensionUnit());
|
||||
entity.setWeight(dto.getWeightUnit().convertToG(dto.getWeight()));
|
||||
entity.setWeight(doWeightConversion(dto.getWeight(), dto.getWeightUnit()));
|
||||
entity.setWeightUnit(dto.getWeightUnit());
|
||||
entity.setContentUnitCount(dto.getContentUnitCount());
|
||||
entity.setDeprecated(dto.getDeprecated());
|
||||
|
|
@ -53,6 +55,26 @@ public class DimensionTransformer {
|
|||
return entity;
|
||||
}
|
||||
|
||||
private Integer doWeightConversion(Double value, WeightUnit unit) {
|
||||
if(value == null) return null;
|
||||
|
||||
if(value <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return unit.convertToG(value);
|
||||
}
|
||||
|
||||
private Integer doDimensionConversion(Double value, DimensionUnit unit) {
|
||||
if(value == null) return null;
|
||||
|
||||
if(value <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return unit.convertToMM(value);
|
||||
}
|
||||
|
||||
public PackagingDimension toDimensionEntity(Premise entity) {
|
||||
var packaging = new PackagingDimension();
|
||||
|
||||
|
|
|
|||
|
|
@ -112,10 +112,13 @@ public class PremiseTransformer {
|
|||
dto.setDestinations(destinationRepository.getByPremiseId(entity.getId()).stream().map(destinationTransformer::toDestinationDTO).toList());
|
||||
|
||||
dto.setHsCode(entity.getHsCode());
|
||||
dto.setTariffRate(entity.getTariffRate() == null ? null : entity.getTariffRate().doubleValue());
|
||||
|
||||
dto.setFcaEnabled(entity.getFcaEnabled());
|
||||
dto.setOverseaShare(entity.getOverseaShare() == null ? null : entity.getOverseaShare().doubleValue());
|
||||
dto.setMaterialCost(entity.getMaterialCost() == null ? null : entity.getMaterialCost().doubleValue());
|
||||
|
||||
|
||||
return dto;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@ public class RouteTransformer {
|
|||
List<RouteSection> sections = routeSectionRepository.getByRouteId(entity.getId());
|
||||
|
||||
dto.setTransitNodes(getRouteNodes(sections).stream().map(nodeTransformer::toNodeDTO).toList());
|
||||
dto.setType(TransportType.valueOf(sections.stream().filter(RouteSection::getMainRun).findFirst().orElseThrow().getTransportType().name()));
|
||||
|
||||
var mainRun = sections.stream().filter(RouteSection::getMainRun).findFirst().orElse(null);
|
||||
dto.setType(mainRun == null ? TransportType.ROAD : mainRun.getTransportType());
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue