Intermediate commit
This commit is contained in:
parent
33b051cba3
commit
ded21ca949
17 changed files with 953 additions and 705 deletions
|
|
@ -3,14 +3,19 @@
|
||||||
name="list-edit-transition"
|
name="list-edit-transition"
|
||||||
tag="div"
|
tag="div"
|
||||||
class="list-edit-container"
|
class="list-edit-container"
|
||||||
|
|
||||||
>
|
>
|
||||||
<div v-if="show" class="list-edit">
|
<div v-if="show" class="list-edit">
|
||||||
<div class="icon-container"><ph-pencil-simple size="24" /><span class="number-circle">{{selectCount}}</span></div>
|
<div class="icon-container"><ph-selection size="24"/><span class="number-circle">{{selectCount}}</span></div>
|
||||||
<div class="list-edit-button" @click="handleAction('material')">Material</div>
|
|
||||||
<div class="list-edit-button" @click="handleAction('price')">Price</div>
|
|
||||||
<div class="list-edit-button" @click="handleAction('packaging')">Packaging</div>
|
<basic-button icon="package" @click="handleAction('material')">Material</basic-button>
|
||||||
<div class="list-edit-button" @click="handleAction('destinations')">Destinations & Routes</div>
|
<basic-button icon="tag" @click="handleAction('price')">Price</basic-button>
|
||||||
|
<basic-button icon="vectorThree" @click="handleAction('packaging')">Packaging</basic-button>
|
||||||
|
<basic-button icon="stack" @click="handleAction('amount')">Annual quantity</basic-button>
|
||||||
|
<basic-button icon="MapPin" @click="handleAction('routes')">Routes</basic-button>
|
||||||
|
|
||||||
|
|
||||||
|
<basic-button icon="X" @click="handleAction('deselect')">Cancel</basic-button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
@ -19,11 +24,12 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import IconButton from "@/components/UI/IconButton.vue";
|
import IconButton from "@/components/UI/IconButton.vue";
|
||||||
import {PhPencilSimple} from "@phosphor-icons/vue";
|
import {PhPencilSimple, PhSelection} from "@phosphor-icons/vue";
|
||||||
|
import BasicButton from "@/components/UI/BasicButton.vue";
|
||||||
|
|
||||||
export default{
|
export default{
|
||||||
name: "MassEditDialog",
|
name: "MassEditDialog",
|
||||||
components: {PhPencilSimple, IconButton},
|
components: {BasicButton, PhSelection, PhPencilSimple, IconButton},
|
||||||
emits: ['action'],
|
emits: ['action'],
|
||||||
props: {
|
props: {
|
||||||
show: {
|
show: {
|
||||||
|
|
@ -70,7 +76,7 @@ export default{
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 3.6rem;
|
gap: 1.2rem;
|
||||||
background-color: #5AF0B4;
|
background-color: #5AF0B4;
|
||||||
border-radius: 0.8rem;
|
border-radius: 0.8rem;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="bulk-edit-row">
|
<div class="bulk-edit-row" @wheel="handleWheel">
|
||||||
<div class="bulk-edit-row__checkbox">
|
<div class="bulk-edit-row__checkbox">
|
||||||
<checkbox :checked="isSelected" @checkbox-changed="updateSelected"></checkbox>
|
<checkbox :checked="isSelected" @checkbox-changed="updateSelected"></checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bulk-edit-row__cell-container">
|
<div class="bulk-edit-row__cell-container">
|
||||||
<div class="bulk-edit-row__cell bulk-edit-row__cell--status bulk-edit-row__cell--clickable"
|
<div
|
||||||
@click.exact="action('material')" @click.ctrl="action('material-select')">
|
class="bulk-edit-row__cell bulk-edit-row__cell--status bulk-edit-row__cell--clickable bulk-edit-row__cell--filterable"
|
||||||
|
@click="action($event,'material')"
|
||||||
|
@mousedown="handleMouseDown">
|
||||||
<div class="bulk-edit-row__data">
|
<div class="bulk-edit-row__data">
|
||||||
<div class="bulk-edit-row__line">
|
<div class="bulk-edit-row__line">
|
||||||
<ph-package size="16"/>
|
<ph-package size="16"/>
|
||||||
|
|
@ -23,8 +25,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="bulk-edit-row__status">
|
<div class="bulk-edit-row__status">
|
||||||
<transition name="badge-transition" mode="out-in">
|
<transition name="badge-transition" mode="out-in">
|
||||||
<circle-badge v-if="materialCheck" :key="'check-' + id" variant="skeleton-grey" icon="check"></circle-badge>
|
<circle-badge v-if="materialCheck && showMaterialCheck" :key="'check-' + id" variant="primary" icon="check" class="badge--check"></circle-badge>
|
||||||
<circle-badge v-else :key="'error-' + id" variant="exception" icon="exclamation-mark"></circle-badge>
|
<circle-badge v-else-if="!materialCheck" :key="'error-' + id" variant="exception" icon="exclamation-mark"></circle-badge>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -32,7 +34,7 @@
|
||||||
|
|
||||||
<div class="bulk-edit-row__cell-container">
|
<div class="bulk-edit-row__cell-container">
|
||||||
<div class="bulk-edit-row__cell bulk-edit-row__cell--status bulk-edit-row__cell--clickable"
|
<div class="bulk-edit-row__cell bulk-edit-row__cell--status bulk-edit-row__cell--clickable"
|
||||||
@click="action('price')">
|
@click="action($event,'price')">
|
||||||
<div class="bulk-edit-row__data">
|
<div class="bulk-edit-row__data">
|
||||||
<div class="bulk-edit-row__line bulk-edit-row__line--sub">
|
<div class="bulk-edit-row__line bulk-edit-row__line--sub">
|
||||||
<ph-tag size="16"/>
|
<ph-tag size="16"/>
|
||||||
|
|
@ -48,9 +50,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="bulk-edit-row__status">
|
<div class="bulk-edit-row__status">
|
||||||
<transition name="badge-transition" mode="out-in">
|
<transition name="badge-transition" mode="out-in">
|
||||||
<circle-badge v-if="priceCheck" :key="'check-price-' + id" variant="skeleton-grey"
|
<circle-badge v-if="priceCheck && showPriceCheck" :key="'check-price-' + id" variant="primary"
|
||||||
icon="check"></circle-badge>
|
icon="check" class="badge--check"></circle-badge>
|
||||||
<circle-badge v-else :key="'error-price-' + id" variant="exception" icon="exclamation-mark"></circle-badge>
|
<circle-badge v-else-if="!priceCheck" :key="'error-price-' + id" variant="exception" icon="exclamation-mark"></circle-badge>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -58,7 +60,7 @@
|
||||||
|
|
||||||
<div class="bulk-edit-row__cell-container">
|
<div class="bulk-edit-row__cell-container">
|
||||||
<div class="bulk-edit-row__cell bulk-edit-row__cell--status bulk-edit-row__cell--clickable"
|
<div class="bulk-edit-row__cell bulk-edit-row__cell--status bulk-edit-row__cell--clickable"
|
||||||
@click="action('packaging')">
|
@click="action($event,'packaging')">
|
||||||
<div class="bulk-edit-row__data">
|
<div class="bulk-edit-row__data">
|
||||||
<div class="bulk-edit-row__line bulk-edit-row__line--sub">
|
<div class="bulk-edit-row__line bulk-edit-row__line--sub">
|
||||||
<PhVectorThree size="16"/>
|
<PhVectorThree size="16"/>
|
||||||
|
|
@ -81,9 +83,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="bulk-edit-row__status">
|
<div class="bulk-edit-row__status">
|
||||||
<transition name="badge-transition" mode="out-in">
|
<transition name="badge-transition" mode="out-in">
|
||||||
<circle-badge v-if="packagingCheck" :key="'check-packaging-' + id" variant="skeleton-grey"
|
<circle-badge v-if="packagingCheck && showPackagingCheck" :key="'check-packaging-' + id" variant="primary"
|
||||||
icon="check"></circle-badge>
|
icon="check" class="badge--check"></circle-badge>
|
||||||
<circle-badge v-else :key="'error-packaging-' + id" variant="exception"
|
<circle-badge v-else-if="!packagingCheck" :key="'error-packaging-' + id" variant="exception"
|
||||||
icon="exclamation-mark"></circle-badge>
|
icon="exclamation-mark"></circle-badge>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -91,8 +93,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bulk-edit-row__cell-container">
|
<div class="bulk-edit-row__cell-container">
|
||||||
<div class="bulk-edit-row__cell bulk-edit-row__cell--status bulk-edit-row__cell--clickable"
|
<div class="bulk-edit-row__cell bulk-edit-row__cell--status bulk-edit-row__cell--filterable"
|
||||||
@click.ctrl="action('supplier-select')">
|
@click="action($event,'supplier')"
|
||||||
|
@mousedown="handleMouseDown">
|
||||||
<div class="bulk-edit-row__data">
|
<div class="bulk-edit-row__data">
|
||||||
<div class="bulk-edit-row__line bulk-edit-row__line--sub">
|
<div class="bulk-edit-row__line bulk-edit-row__line--sub">
|
||||||
<ph-factory style="display: inline-block; vertical-align: middle;" size="16"/>
|
<ph-factory style="display: inline-block; vertical-align: middle;" size="16"/>
|
||||||
|
|
@ -105,7 +108,7 @@
|
||||||
<div class="bulk-edit-row__cell-container">
|
<div class="bulk-edit-row__cell-container">
|
||||||
<div
|
<div
|
||||||
class="bulk-edit-row__cell bulk-edit-row__cell--status bulk-edit-row__cell--clickable bulk-edit-row__cell--destinations"
|
class="bulk-edit-row__cell bulk-edit-row__cell--status bulk-edit-row__cell--clickable bulk-edit-row__cell--destinations"
|
||||||
@click="action('destinations')">
|
@click="action($event,'amount')">
|
||||||
<div class="bulk-edit-row__data bulk-edit-row__data--destinations">
|
<div class="bulk-edit-row__data bulk-edit-row__data--destinations">
|
||||||
<div class="bulk-edit-row__dest-line"
|
<div class="bulk-edit-row__dest-line"
|
||||||
v-for="(destination, index) in premise.destinations.slice(0, 3)"
|
v-for="(destination, index) in premise.destinations.slice(0, 3)"
|
||||||
|
|
@ -142,9 +145,9 @@
|
||||||
|
|
||||||
<div class="bulk-edit-row__status">
|
<div class="bulk-edit-row__status">
|
||||||
<transition name="badge-transition" mode="out-in">
|
<transition name="badge-transition" mode="out-in">
|
||||||
<circle-badge v-if="destinationCheck" :key="'check-dest-' + id" variant="skeleton-grey"
|
<circle-badge v-if="destinationCheck && showDestinationCheck" :key="'check-dest-' + id" variant="primary"
|
||||||
icon="check"></circle-badge>
|
icon="check" class="badge--check"></circle-badge>
|
||||||
<circle-badge v-else :key="'error-dest-' + id" variant="exception" icon="exclamation-mark"></circle-badge>
|
<circle-badge v-else-if="!destinationCheck" :key="'error-dest-' + id" variant="exception" icon="exclamation-mark"></circle-badge>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -153,7 +156,7 @@
|
||||||
<div class="bulk-edit-row__cell-container">
|
<div class="bulk-edit-row__cell-container">
|
||||||
<div
|
<div
|
||||||
class="bulk-edit-row__cell bulk-edit-row__cell--status bulk-edit-row__cell--clickable bulk-edit-row__cell--destinations"
|
class="bulk-edit-row__cell bulk-edit-row__cell--status bulk-edit-row__cell--clickable bulk-edit-row__cell--destinations"
|
||||||
@click="action('routes')">
|
@click="action($event,'routes')">
|
||||||
<div class="bulk-edit-row__data">
|
<div class="bulk-edit-row__data">
|
||||||
<div class="bulk-edit-row__route-line"
|
<div class="bulk-edit-row__route-line"
|
||||||
v-for="(destination, index) in premise.destinations.slice(0, 3)"
|
v-for="(destination, index) in premise.destinations.slice(0, 3)"
|
||||||
|
|
@ -163,7 +166,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div>{{ toRoute(destination) }}</div>
|
<div>{{ toRoute(destination) }}</div>
|
||||||
<div>
|
<div>
|
||||||
<basic-badge size="compact" variant="secondary">{{ toDestination(destination, 15) }}</basic-badge>
|
<basic-badge size="compact" variant="secondary">{{ toDestination(destination) }}</basic-badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bulk-edit-row__route-line" v-if="premise.destinations.length > 3">
|
<div class="bulk-edit-row__route-line" v-if="premise.destinations.length > 3">
|
||||||
|
|
@ -190,9 +193,9 @@
|
||||||
|
|
||||||
<div class="bulk-edit-row__status">
|
<div class="bulk-edit-row__status">
|
||||||
<transition name="badge-transition" mode="out-in">
|
<transition name="badge-transition" mode="out-in">
|
||||||
<circle-badge v-if="destinationCheck" :key="'check-route-' + id" variant="skeleton-grey"
|
<circle-badge v-if="destinationCheck && showRouteCheck" :key="'check-route-' + id" variant="primary"
|
||||||
icon="check"></circle-badge>
|
icon="check" class="badge--check"></circle-badge>
|
||||||
<circle-badge v-else :key="'error-route-' + id" variant="exception" icon="exclamation-mark"></circle-badge>
|
<circle-badge v-else-if="!destinationCheck" :key="'error-route-' + id" variant="exception" icon="exclamation-mark"></circle-badge>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -255,6 +258,23 @@ export default {
|
||||||
required: true,
|
required: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// Track previous states to detect error->ok transitions
|
||||||
|
prevMaterialCheck: null,
|
||||||
|
prevPriceCheck: null,
|
||||||
|
prevPackagingCheck: null,
|
||||||
|
prevDestinationCheck: null,
|
||||||
|
// Flags to show check badges only on transition
|
||||||
|
showMaterialCheck: false,
|
||||||
|
showPriceCheck: false,
|
||||||
|
showPackagingCheck: false,
|
||||||
|
showDestinationCheck: false,
|
||||||
|
showRouteCheck: false,
|
||||||
|
// Flag to track if component has been initialized
|
||||||
|
isInitialized: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
materialCheck() {
|
materialCheck() {
|
||||||
return (this.premise?.material.part_number != null && this.premise?.hs_code != null && this.premise?.tariff_rate != null)
|
return (this.premise?.material.part_number != null && this.premise?.hs_code != null && this.premise?.tariff_rate != null)
|
||||||
|
|
@ -281,8 +301,42 @@ export default {
|
||||||
},
|
},
|
||||||
...mapStores(usePremiseEditStore),
|
...mapStores(usePremiseEditStore),
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
materialCheck(newVal, oldVal) {
|
||||||
|
if (this.isInitialized && oldVal === false && newVal === true) {
|
||||||
|
this.showMaterialCheck = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
priceCheck(newVal, oldVal) {
|
||||||
|
if (this.isInitialized && oldVal === false && newVal === true) {
|
||||||
|
this.showPriceCheck = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
packagingCheck(newVal, oldVal) {
|
||||||
|
if (this.isInitialized && oldVal === false && newVal === true) {
|
||||||
|
this.showPackagingCheck = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destinationCheck(newVal, oldVal) {
|
||||||
|
if (this.isInitialized && oldVal === false && newVal === true) {
|
||||||
|
this.showDestinationCheck = true;
|
||||||
|
this.showRouteCheck = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// Initialize previous states after first render
|
||||||
|
// Use nextTick to ensure computed properties are evaluated
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.prevMaterialCheck = this.materialCheck;
|
||||||
|
this.prevPriceCheck = this.priceCheck;
|
||||||
|
this.prevPackagingCheck = this.packagingCheck;
|
||||||
|
this.prevDestinationCheck = this.destinationCheck;
|
||||||
|
this.isInitialized = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toDestination(destination, limit = 15) {
|
toDestination(destination, limit = 10) {
|
||||||
return this.toNode(destination.destination_node, limit);
|
return this.toNode(destination.destination_node, limit);
|
||||||
},
|
},
|
||||||
toNode(node, limit = 5) {
|
toNode(node, limit = 5) {
|
||||||
|
|
@ -369,8 +423,27 @@ export default {
|
||||||
const urlStr = new UrlSafeBase64().encodeIds([this.id]);
|
const urlStr = new UrlSafeBase64().encodeIds([this.id]);
|
||||||
this.$router.push({name: 'bulk-single-edit', params: {id: urlStr, ids: bulkQuery}});
|
this.$router.push({name: 'bulk-single-edit', params: {id: urlStr, ids: bulkQuery}});
|
||||||
},
|
},
|
||||||
action(action) {
|
handleMouseDown(event) {
|
||||||
|
if (event.shiftKey || event.ctrlKey) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleWheel(event) {
|
||||||
|
if (event.ctrlKey) {
|
||||||
|
event.preventDefault();
|
||||||
|
window.scrollBy(0, event.deltaY);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
action(event, action) {
|
||||||
|
|
||||||
|
if (event.ctrlKey && !event.shiftKey && (action === 'material' || action === 'supplier')) {
|
||||||
|
this.$emit('action', {id: this.id, action: action.concat('-filter')});
|
||||||
|
} else if (event.ctrlKey && event.shiftKey && (action === 'material' || action === 'supplier')) {
|
||||||
|
this.$emit('action', {id: this.id, action: action.concat('-append')});
|
||||||
|
} else if (action !== 'supplier') {
|
||||||
this.$emit('action', {id: this.id, action: action});
|
this.$emit('action', {id: this.id, action: action});
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
updateSelected(value) {
|
updateSelected(value) {
|
||||||
this.$emit("select", {id: this.id, checked: value});
|
this.$emit("select", {id: this.id, checked: value});
|
||||||
|
|
@ -444,6 +517,7 @@ export default {
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.bulk-edit-row__cell--destinations {
|
.bulk-edit-row__cell--destinations {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
@ -470,21 +544,24 @@ export default {
|
||||||
|
|
||||||
/* Badge Transition Animation */
|
/* Badge Transition Animation */
|
||||||
.badge-transition-enter-active {
|
.badge-transition-enter-active {
|
||||||
animation: badge-enter 0.3s ease-out 0.1s both;
|
animation: badge-enter 0.3s ease-in both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-transition-leave-active {
|
.badge-transition-leave-active {
|
||||||
animation: badge-leave 0.2s ease-in;
|
animation: badge-leave 0.3s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check badge fade-out Animation - wird NACH der Enter-Animation ausgeführt */
|
||||||
|
.badge--check {
|
||||||
|
animation: badge-enter 0.3s ease-out 0.1s both,
|
||||||
|
badge-zoom-fade-out 0.6s ease-out both;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes badge-enter {
|
@keyframes badge-enter {
|
||||||
0% {
|
0% {
|
||||||
transform: scale(0.5);
|
transform: scale(1);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
60% {
|
|
||||||
transform: scale(1.2);
|
|
||||||
}
|
|
||||||
100% {
|
100% {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
@ -502,6 +579,21 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes badge-zoom-fade-out {
|
||||||
|
0% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
transform: scale(3);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(0.5);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Lines */
|
/* Lines */
|
||||||
.bulk-edit-row__line {
|
.bulk-edit-row__line {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ import {parseNumberFromString} from "@/common.js";
|
||||||
export default {
|
export default {
|
||||||
name: "PackagingEdit",
|
name: "PackagingEdit",
|
||||||
components: {Tooltip, Dropdown, Checkbox},
|
components: {Tooltip, Dropdown, Checkbox},
|
||||||
emits: ['update:stackable', 'update:mixable', 'update:length', 'update:width', 'update:height', 'update:weight', 'update:unitCount', 'update:weightUnit', 'update:dimensionUnit', 'save'],
|
emits: ['update:stackable', 'update:mixable', 'update:length', 'update:width', 'update:height', 'update:weight', 'update:unitCount', 'update:weightUnit', 'update:dimensionUnit', 'save', 'accept'],
|
||||||
props: {
|
props: {
|
||||||
length: {
|
length: {
|
||||||
required: true,
|
required: true,
|
||||||
|
|
@ -215,6 +215,7 @@ export default {
|
||||||
const currentIndex = inputOrder.indexOf(currentRef);
|
const currentIndex = inputOrder.indexOf(currentRef);
|
||||||
|
|
||||||
if(currentIndex >= inputOrder.length - 1) {
|
if(currentIndex >= inputOrder.length - 1) {
|
||||||
|
this.validateCount(event);
|
||||||
this.$emit('accept');
|
this.$emit('accept');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ export default {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (this.$refs[nextRef]) {
|
if (this.$refs[nextRef]) {
|
||||||
this.$refs[nextRef].focus();
|
this.$refs[nextRef].focus();
|
||||||
this.$refs[nextRef].select();
|
// this.$refs[nextRef].select();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "DestinationMassCreate"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "DestinationMassEdit"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import {defineComponent} from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "DestinationMassHandlingCost"
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import {defineComponent} from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "DestinationMassQuantity"
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import {defineComponent} from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "DestinationMassRoute"
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -34,7 +34,7 @@ import {
|
||||||
PhUpload,
|
PhUpload,
|
||||||
PhWarning,
|
PhWarning,
|
||||||
PhX,
|
PhX,
|
||||||
PhExclamationMark, PhMapPin, PhEmpty, PhShippingContainer
|
PhExclamationMark, PhMapPin, PhEmpty, PhShippingContainer, PhPackage, PhVectorThree, PhTag
|
||||||
} from "@phosphor-icons/vue";
|
} from "@phosphor-icons/vue";
|
||||||
import {setupSessionRefresh} from "@/store/activeuser.js";
|
import {setupSessionRefresh} from "@/store/activeuser.js";
|
||||||
|
|
||||||
|
|
@ -78,6 +78,9 @@ app.component("PhHardDrives", PhHardDrives );
|
||||||
app.component("PhClipboard", PhClipboard );
|
app.component("PhClipboard", PhClipboard );
|
||||||
app.component("PhExclamationMark", PhExclamationMark );
|
app.component("PhExclamationMark", PhExclamationMark );
|
||||||
app.component("PhMapPin", PhMapPin);
|
app.component("PhMapPin", PhMapPin);
|
||||||
|
app.component("PhPackage", PhPackage);
|
||||||
|
app.component("PhVectorThree", PhVectorThree);
|
||||||
|
app.component("PhTag", PhTag);
|
||||||
|
|
||||||
|
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="edit-calculation-container" :class="{ 'has-selection': hasSelection }">
|
<div class="edit-calculation-container"
|
||||||
|
:class="{ 'has-selection': hasSelection, 'apply-filter': applyFilter, 'add-all': addAll }">
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<h2 class="page-header">Mass edit calculation</h2>
|
<h2 class="page-header">Mass edit calculation</h2>
|
||||||
<div class="header-controls">
|
<div class="header-controls">
|
||||||
|
|
@ -19,70 +20,89 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<transition name="list-edit-container" tag="div">
|
<div class="edit-calculation-list-container">
|
||||||
<transition-group name="list-edit" mode="out-in" class="edit-calculation-list-container" tag="div">
|
<div class="edit-calculation-list-header">
|
||||||
|
|
||||||
<div class="edit-calculation-list-header" key="header">
|
|
||||||
<div>
|
<div>
|
||||||
<checkbox @checkbox-changed="updateCheckBoxes" :checked="overallCheck"
|
<checkbox @checkbox-changed="updateCheckBoxes" :checked="overallCheck"
|
||||||
:indeterminate="overallIndeterminate"></checkbox>
|
:indeterminate="overallIndeterminate"></checkbox>
|
||||||
</div>
|
</div>
|
||||||
<div>Material</div>
|
<div class="edit-calculation-list-header-cell">Material
|
||||||
<div>Price</div>
|
<sort-button :active="premiseEditStore.activeSort === 'material'"
|
||||||
<div>Packaging</div>
|
:direction="premiseEditStore.directionSort('material')" @click="premiseEditStore.sort('material')"/>
|
||||||
<div>Supplier</div>
|
</div>
|
||||||
<div>Annual Quantity</div>
|
<div class="edit-calculation-list-header-cell">Price</div>
|
||||||
<div>Routes</div>
|
<div class="edit-calculation-list-header-cell">Packaging</div>
|
||||||
<div>Actions</div>
|
<div class="edit-calculation-list-header-cell">Supplier
|
||||||
|
<sort-button :active="premiseEditStore.activeSort === 'supplier'"
|
||||||
|
:direction="premiseEditStore.directionSort('supplier')" @click="premiseEditStore.sort('supplier')"/>
|
||||||
|
</div>
|
||||||
|
<div class="edit-calculation-list-header-cell">Annual Quantity</div>
|
||||||
|
<div class="edit-calculation-list-header-cell">Routes</div>
|
||||||
|
<div class="edit-calculation-list-header-cell">Actions</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Loading Spinner - außerhalb der TransitionGroup -->
|
||||||
<div v-if="showLoading" class="spinner-container" key="spinner">
|
<div v-if="showLoading" class="spinner-container">
|
||||||
<spinner class="space-around"></spinner>
|
<spinner class="space-around"></spinner>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="showEmpty" class="empty-container" key="empty">
|
<!-- Empty State - außerhalb der TransitionGroup -->
|
||||||
|
<div v-else-if="showEmpty" class="empty-container">
|
||||||
<span class="space-around">No Calculations found.</span>
|
<span class="space-around">No Calculations found.</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<bulk-edit-row v-else class="edit-calculation-list-item" v-for="premise of this.premises"
|
<!-- Rows mit Sort-Animation -->
|
||||||
:key="premise.id" :id="premise.id" :premise="premise" @action="onClickAction" @select="updateCheckBox"
|
<transition-group
|
||||||
|
v-else
|
||||||
|
name="sort-list"
|
||||||
|
tag="div"
|
||||||
|
class="edit-calculation-list-body"
|
||||||
|
@before-enter="onBeforeEnter"
|
||||||
|
@enter="onEnter"
|
||||||
|
>
|
||||||
|
<bulk-edit-row
|
||||||
|
v-for="(premise, index) of premises"
|
||||||
|
:key="premise.id"
|
||||||
|
:id="premise.id"
|
||||||
|
:premise="premise"
|
||||||
|
:data-index="index"
|
||||||
|
class="edit-calculation-list-item"
|
||||||
|
@action="onClickAction"
|
||||||
|
@select="updateCheckBox"
|
||||||
@remove="updateUrl">
|
@remove="updateUrl">
|
||||||
</bulk-edit-row>
|
</bulk-edit-row>
|
||||||
|
|
||||||
|
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</transition>
|
</div>
|
||||||
|
|
||||||
<mass-edit-dialog v-if="showData" :show="showMultiselectAction" @action="multiselectAction"
|
<mass-edit-dialog v-if="showData" :show="showMultiselectAction" @action="onToolbarAction"
|
||||||
:select-count="selectCount"></mass-edit-dialog>
|
:select-count="selectCount"></mass-edit-dialog>
|
||||||
|
|
||||||
|
|
||||||
<modal :z-index="2000" :state="showEditModal">
|
<modal :z-index="2000" :state="modalShow">
|
||||||
<div class="modal-content-container">
|
<div class="modal-content-container">
|
||||||
<component
|
<component
|
||||||
:is="componentType"
|
:is="modalComponentType"
|
||||||
v-model:partNumber="componentProps.partNumber"
|
v-model:partNumber="modalProps.partNumber"
|
||||||
v-model:hsCode="componentProps.hsCode"
|
v-model:hsCode="modalProps.hsCode"
|
||||||
v-model:tariffRate="componentProps.tariffRate"
|
v-model:tariffRate="modalProps.tariffRate"
|
||||||
v-model:tariffUnlocked="componentProps.tariffUnlocked"
|
v-model:tariffUnlocked="modalProps.tariffUnlocked"
|
||||||
v-model:description="componentProps.description"
|
v-model:description="modalProps.description"
|
||||||
|
|
||||||
v-model:price="componentProps.price"
|
v-model:price="modalProps.price"
|
||||||
v-model:overSeaShare="componentProps.overSeaShare"
|
v-model:overSeaShare="modalProps.overSeaShare"
|
||||||
v-model:includeFcaFee="componentProps.includeFcaFee"
|
v-model:includeFcaFee="modalProps.includeFcaFee"
|
||||||
|
|
||||||
v-model:length="componentProps.length"
|
v-model:length="modalProps.length"
|
||||||
v-model:width="componentProps.width"
|
v-model:width="modalProps.width"
|
||||||
v-model:height="componentProps.height"
|
v-model:height="modalProps.height"
|
||||||
v-model:weight="componentProps.weight"
|
v-model:weight="modalProps.weight"
|
||||||
v-model:weightUnit="componentProps.weightUnit"
|
v-model:weightUnit="modalProps.weightUnit"
|
||||||
v-model:dimensionUnit="componentProps.dimensionUnit"
|
v-model:dimensionUnit="modalProps.dimensionUnit"
|
||||||
v-model:unitCount="componentProps.unitCount"
|
v-model:unitCount="modalProps.unitCount"
|
||||||
v-model:mixable="componentProps.mixable"
|
v-model:mixable="modalProps.mixable"
|
||||||
v-model:stackable="componentProps.stackable"
|
v-model:stackable="modalProps.stackable"
|
||||||
|
|
||||||
v-model:hideDescription="componentProps.hideDescription"
|
v-model:hideDescription="modalProps.hideDescription"
|
||||||
|
|
||||||
:fromMassEdit="true"
|
:fromMassEdit="true"
|
||||||
:countryId=null
|
:countryId=null
|
||||||
|
|
@ -94,7 +114,7 @@
|
||||||
>
|
>
|
||||||
</component>
|
</component>
|
||||||
|
|
||||||
<div class="modal-content-footer">
|
<div class="modal-content-footer" @keydown="handleKeyDown($event)">
|
||||||
<basic-button v-if="!modalCloseOnly" :show-icon="false" @click="closeEditModalAction('accept')">OK
|
<basic-button v-if="!modalCloseOnly" :show-icon="false" @click="closeEditModalAction('accept')">OK
|
||||||
</basic-button>
|
</basic-button>
|
||||||
<basic-button variant="secondary" :show-icon="false" @click="closeEditModalAction('cancel')">
|
<basic-button variant="secondary" :show-icon="false" @click="closeEditModalAction('cancel')">
|
||||||
|
|
@ -123,21 +143,23 @@ import Modal from "@/components/UI/Modal.vue";
|
||||||
import PriceEdit from "@/components/layout/edit/PriceEdit.vue";
|
import PriceEdit from "@/components/layout/edit/PriceEdit.vue";
|
||||||
import MaterialEdit from "@/components/layout/edit/MaterialEdit.vue";
|
import MaterialEdit from "@/components/layout/edit/MaterialEdit.vue";
|
||||||
import PackagingEdit from "@/components/layout/edit/PackagingEdit.vue";
|
import PackagingEdit from "@/components/layout/edit/PackagingEdit.vue";
|
||||||
import DestinationListView from "@/components/layout/edit/DestinationListView.vue";
|
|
||||||
import logger from "@/logger.js";
|
|
||||||
import {useNotificationStore} from "@/store/notification.js";
|
import {useNotificationStore} from "@/store/notification.js";
|
||||||
|
import {useDestinationEditStore} from "@/store/destinationEdit.js";
|
||||||
|
import SortButton from "@/components/UI/SortButton.vue";
|
||||||
|
|
||||||
|
|
||||||
const COMPONENT_TYPES = {
|
const COMPONENT_TYPES = {
|
||||||
price: PriceEdit,
|
price: PriceEdit,
|
||||||
material: MaterialEdit,
|
material: MaterialEdit,
|
||||||
packaging: PackagingEdit,
|
packaging: PackagingEdit,
|
||||||
destinations: DestinationListView,
|
destinations: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MassEdit",
|
name: "MassEdit",
|
||||||
components: {
|
components: {
|
||||||
|
SortButton,
|
||||||
Modal,
|
Modal,
|
||||||
MassEditDialog,
|
MassEditDialog,
|
||||||
ListEdit,
|
ListEdit,
|
||||||
|
|
@ -148,7 +170,7 @@ export default {
|
||||||
BasicButton
|
BasicButton
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(usePremiseEditStore, useNotificationStore),
|
...mapStores(usePremiseEditStore, useNotificationStore, useDestinationEditStore),
|
||||||
disableButtons() {
|
disableButtons() {
|
||||||
return this.premiseEditStore.selectedLoading;
|
return this.premiseEditStore.selectedLoading;
|
||||||
},
|
},
|
||||||
|
|
@ -159,16 +181,19 @@ export default {
|
||||||
if (this.premiseEditStore.isLoading || this.premiseEditStore.selectedLoading) {
|
if (this.premiseEditStore.isLoading || this.premiseEditStore.selectedLoading) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this.premiseEditStore.someChecked;
|
return !this.addAll && !this.applyFilter && this.premiseEditStore.someChecked;
|
||||||
|
},
|
||||||
|
applyFilter() {
|
||||||
|
return this.isCtrlPressed && this.isShiftPressed;
|
||||||
|
},
|
||||||
|
addAll() {
|
||||||
|
return this.isCtrlPressed && !this.isShiftPressed;
|
||||||
},
|
},
|
||||||
showMultiselectAction() {
|
showMultiselectAction() {
|
||||||
return this.selectCount > 0;
|
return this.selectCount > 0;
|
||||||
},
|
},
|
||||||
selectCount() {
|
selectCount() {
|
||||||
return this.selectedPremisses?.length ?? 0;
|
return this.premiseEditStore.getSelectedPremiseIds?.length ?? 0;
|
||||||
},
|
|
||||||
selectedPremisses() {
|
|
||||||
return this.premiseEditStore.getSelectedPremisses;
|
|
||||||
},
|
},
|
||||||
showEmpty() {
|
showEmpty() {
|
||||||
return this.premiseEditStore.showEmpty;
|
return this.premiseEditStore.showEmpty;
|
||||||
|
|
@ -180,20 +205,15 @@ export default {
|
||||||
return this.premiseEditStore.showData;
|
return this.premiseEditStore.showData;
|
||||||
},
|
},
|
||||||
modalCloseOnly() {
|
modalCloseOnly() {
|
||||||
return this.modalType === 'material' && !this.componentProps.tariffUnlocked; //TODO: check all selected.
|
//TODO: fix material editing.
|
||||||
|
return this.modalType === 'material' && !this.modalProps.tariffUnlocked; //TODO: check all selected.
|
||||||
},
|
},
|
||||||
showEditModal() {
|
modalShow() {
|
||||||
return ((this.modalType ?? null) !== null);
|
return ((this.modalType ?? null) !== null);
|
||||||
},
|
},
|
||||||
componentProps() {
|
modalComponentType() {
|
||||||
return this.componentData?.props ?? null;
|
|
||||||
},
|
|
||||||
componentType() {
|
|
||||||
return this.modalType ? COMPONENT_TYPES[this.modalType] : null;
|
return this.modalType ? COMPONENT_TYPES[this.modalType] : null;
|
||||||
},
|
},
|
||||||
componentData() {
|
|
||||||
return this.modalType ? this.componentsData[this.modalType] : null;
|
|
||||||
},
|
|
||||||
showProcessingModal() {
|
showProcessingModal() {
|
||||||
return this.premiseEditStore.showProcessingModal || this.showCalculationModal;
|
return this.premiseEditStore.showProcessingModal || this.showCalculationModal;
|
||||||
},
|
},
|
||||||
|
|
@ -210,54 +230,62 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
async created() {
|
||||||
this.bulkQuery = this.$route.params.ids;
|
this.bulkQuery = this.$route.params.ids;
|
||||||
this.ids = new UrlSafeBase64().decodeIds(this.$route.params.ids);
|
this.ids = new UrlSafeBase64().decodeIds(this.$route.params.ids);
|
||||||
this.premiseEditStore.loadPremissesForced(this.ids);
|
const premisses = await this.premiseEditStore.load(this.ids);
|
||||||
|
this.destinationEditStore.setupDestinations(premisses);
|
||||||
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
ids: [],
|
ids: [],
|
||||||
|
isCtrlPressed: false,
|
||||||
|
isShiftPressed: false,
|
||||||
overallCheck: false,
|
overallCheck: false,
|
||||||
overallIndeterminate: false,
|
overallIndeterminate: false,
|
||||||
bulkQuery: null,
|
bulkQuery: null,
|
||||||
modalType: null,
|
modalType: null,
|
||||||
componentsData: {
|
modalProps: null,
|
||||||
price: {props: {price: 0, overSeaShare: 0, includeFcaFee: false}},
|
|
||||||
material: {
|
|
||||||
props: {
|
|
||||||
partNumber: "",
|
|
||||||
hsCode: null,
|
|
||||||
tariffRate: null,
|
|
||||||
tariffUnlocked: false,
|
|
||||||
description: "",
|
|
||||||
hideDescription: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
packaging: {
|
|
||||||
props: {
|
|
||||||
length: 0,
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
weight: 0,
|
|
||||||
weightUnit: "KG",
|
|
||||||
dimensionUnit: "MM",
|
|
||||||
unitCount: 1,
|
|
||||||
mixable: true,
|
|
||||||
stackable: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
destinations: {props: {}},
|
|
||||||
},
|
|
||||||
editIds: null,
|
editIds: null,
|
||||||
dataSourceId: null,
|
dataSourceId: null,
|
||||||
processingMessage: "Please wait. Calculating ...",
|
processingMessage: "Please wait. Calculating ...",
|
||||||
showCalculationModal: false,
|
showCalculationModal: false,
|
||||||
|
isInitialLoad: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
console.log("add listener")
|
||||||
|
window.addEventListener('keydown', this.handleKeyDown);
|
||||||
|
window.addEventListener('keyup', this.handleKeyUp);
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
console.log("remove listener")
|
||||||
|
window.removeEventListener('keydown', this.handleKeyDown);
|
||||||
|
window.removeEventListener('keyup', this.handleKeyUp);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateUrl(id) {
|
handleKeyDown(event) {
|
||||||
|
if (event.key === 'Control') {
|
||||||
|
this.isCtrlPressed = true;
|
||||||
|
} else if (event.key === 'Shift') {
|
||||||
|
this.isShiftPressed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
this.fillData(this.modalType);
|
||||||
|
this.modalType = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
handleKeyUp(event) {
|
||||||
|
if (event.key === 'Control') {
|
||||||
|
this.isCtrlPressed = false;
|
||||||
|
} else if (event.key === 'Shift') {
|
||||||
|
this.isShiftPressed = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateUrl(id) {
|
||||||
const idx = this.ids.findIndex(curId => curId === id);
|
const idx = this.ids.findIndex(curId => curId === id);
|
||||||
|
|
||||||
if (idx > -1) {
|
if (idx > -1) {
|
||||||
|
|
@ -283,12 +311,14 @@ export default {
|
||||||
close() {
|
close() {
|
||||||
this.$router.push({name: "calculation-list"});
|
this.$router.push({name: "calculation-list"});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/* checkbox handling */
|
||||||
|
|
||||||
updateCheckBox(data) {
|
updateCheckBox(data) {
|
||||||
this.premiseEditStore.setChecked(data.id, data.checked);
|
this.premiseEditStore.setChecked(data.id, data.checked);
|
||||||
this.updateOverallCheckBox();
|
this.updateOverallCheckBox();
|
||||||
},
|
},
|
||||||
updateCheckBoxes(value) {
|
updateCheckBoxes(value) {
|
||||||
console.log("set all", value)
|
|
||||||
this.premiseEditStore.setAll(value);
|
this.premiseEditStore.setAll(value);
|
||||||
this.updateOverallCheckBox();
|
this.updateOverallCheckBox();
|
||||||
},
|
},
|
||||||
|
|
@ -298,60 +328,59 @@ export default {
|
||||||
if (!this.overallCheck)
|
if (!this.overallCheck)
|
||||||
this.overallIndeterminate = this.premiseEditStore.someChecked;
|
this.overallIndeterminate = this.premiseEditStore.someChecked;
|
||||||
},
|
},
|
||||||
multiselectAction(action) {
|
|
||||||
this.openModal(action, this.selectedPremisses.map(p => p.id));
|
|
||||||
|
/* click listeners */
|
||||||
|
|
||||||
|
onToolbarAction(action) {
|
||||||
|
if (action === 'deselect') {
|
||||||
|
this.updateCheckBoxes(false);
|
||||||
|
} else
|
||||||
|
this.openModal(action, this.premiseEditStore.getSelectedPremiseIds);
|
||||||
},
|
},
|
||||||
onClickAction(data) {
|
onClickAction(data) {
|
||||||
if (data.action === 'supplier-select') {
|
|
||||||
this.premiseEditStore.setBy('supplier', data.id);
|
|
||||||
this.updateOverallCheckBox();
|
|
||||||
} else if (data.action === 'material-select') {
|
|
||||||
this.premiseEditStore.setBy('material', data.id);
|
|
||||||
this.updateOverallCheckBox();
|
|
||||||
} else {
|
|
||||||
const massEdit = 0 !== this.selectCount
|
|
||||||
this.openModal(data.action, massEdit ? this.premiseEditStore.getSelectedPremissesIds : [data.id], data.id, massEdit);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const actions = data.action.split("-");
|
||||||
|
|
||||||
|
if (actions.length === 1) {
|
||||||
|
const massEdit = 0 !== this.selectCount;
|
||||||
|
this.openModal(data.action, massEdit ? this.premiseEditStore.getSelectedPremiseIds : [data.id], data.id, massEdit);
|
||||||
|
} else if (actions.length === 2) {
|
||||||
|
this.premiseEditStore.setBy(actions[0], actions[1], data.id);
|
||||||
|
this.updateOverallCheckBox();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/* modal handling */
|
||||||
|
|
||||||
openModal(type, ids, dataSource = -1, massEdit = true) {
|
openModal(type, ids, dataSource = -1, massEdit = true) {
|
||||||
|
|
||||||
if (type !== 'destinations')
|
if (type !== 'amount' && type !== 'route')
|
||||||
this.fillData(type, dataSource, massEdit)
|
this.fillData(type, dataSource, massEdit)
|
||||||
else {
|
else {
|
||||||
this.premiseEditStore.prepareDestinations(dataSource, ids, massEdit, true);
|
//TODO new destination handling
|
||||||
|
|
||||||
|
// 1. all unset -> goto destination create
|
||||||
|
|
||||||
|
// 2. some unset -> ask if goto destination create
|
||||||
|
|
||||||
|
// 3. all set -> goto amount/route
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dataSourceId = dataSource !== -1 ? dataSource : null;
|
this.dataSourceId = dataSource !== -1 ? dataSource : null;
|
||||||
this.editIds = ids;
|
this.editIds = ids;
|
||||||
this.modalType = type;
|
this.modalType = type;
|
||||||
|
|
||||||
logger.info("open modal", massEdit, this.modalType, this.editIds, this.dataSourceId)
|
|
||||||
|
|
||||||
},
|
},
|
||||||
async closeEditModalAction(action) {
|
async closeEditModalAction(action) {
|
||||||
if (this.modalType === "destinations") {
|
if (this.modalType === "destinations") {
|
||||||
if (action === "accept") {
|
//TODO new destination handling
|
||||||
await this.premiseEditStore.executeDestinationsMassEdit();
|
|
||||||
} else {
|
|
||||||
this.premiseEditStore.cancelMassEdit();
|
|
||||||
}
|
|
||||||
} else if (action === "accept") {
|
} else if (action === "accept") {
|
||||||
const props = this.componentsData[this.modalType].props;
|
await this.premiseEditStore.batchUpdate(this.modalType, this.editIds, this.modalProps);
|
||||||
|
|
||||||
switch (this.modalType) {
|
|
||||||
case "price":
|
|
||||||
await this.premiseEditStore.batchUpdatePrice(this.editIds, props);
|
|
||||||
break;
|
|
||||||
case "material":
|
|
||||||
await this.premiseEditStore.batchUpdateMaterial(this.editIds, props);
|
|
||||||
break;
|
|
||||||
case "packaging":
|
|
||||||
await this.premiseEditStore.batchUpdatePackaging(this.editIds, props);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Clear data
|
// Clear data
|
||||||
this.fillData(this.modalType);
|
this.fillData(this.modalType);
|
||||||
this.modalType = null;
|
this.modalType = null;
|
||||||
|
|
@ -359,21 +388,22 @@ export default {
|
||||||
fillData(type, id = -1, hideDescription = false) {
|
fillData(type, id = -1, hideDescription = false) {
|
||||||
|
|
||||||
if (id === -1) {
|
if (id === -1) {
|
||||||
// clear
|
|
||||||
this.componentsData = {
|
if (type === 'price')
|
||||||
price: {props: {price: null, overSeaShare: null, includeFcaFee: null}},
|
this.modalProps = {price: null, overSeaShare: null, includeFcaFee: null};
|
||||||
material: {
|
|
||||||
props: {
|
if (type === 'material')
|
||||||
|
this.modalProps = {
|
||||||
partNumber: "",
|
partNumber: "",
|
||||||
hsCode: null,
|
hsCode: null,
|
||||||
tariffRate: null,
|
tariffRate: null,
|
||||||
tariffUnlocked: false,
|
tariffUnlocked: false,
|
||||||
description: null,
|
description: null,
|
||||||
hideDescription: hideDescription
|
hideDescription: hideDescription
|
||||||
}
|
};
|
||||||
},
|
|
||||||
packaging: {
|
if (type === 'packaging')
|
||||||
props: {
|
this.modalProps = {
|
||||||
length: null,
|
length: null,
|
||||||
width: null,
|
width: null,
|
||||||
height: null,
|
height: null,
|
||||||
|
|
@ -383,22 +413,20 @@ export default {
|
||||||
unitCount: null,
|
unitCount: null,
|
||||||
mixable: true,
|
mixable: true,
|
||||||
stackable: true
|
stackable: true
|
||||||
}
|
|
||||||
},
|
|
||||||
destinations: {props: {}},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const premise = this.premiseEditStore.getById(id);
|
const premise = this.premiseEditStore.getById(id);
|
||||||
|
|
||||||
if (type === "price") {
|
if (type === "price") {
|
||||||
this.componentsData.price.props = {
|
this.modalProps = {
|
||||||
price: premise.material_cost,
|
price: premise.material_cost,
|
||||||
overSeaShare: premise.oversea_share,
|
overSeaShare: premise.oversea_share,
|
||||||
includeFcaFee: premise.is_fca_enabled
|
includeFcaFee: premise.is_fca_enabled
|
||||||
}
|
}
|
||||||
} else if (type === "material") {
|
} else if (type === "material") {
|
||||||
|
|
||||||
this.componentsData.material.props = {
|
this.modalProps = {
|
||||||
partNumber: premise.material.part_number,
|
partNumber: premise.material.part_number,
|
||||||
hsCode: premise.hs_code,
|
hsCode: premise.hs_code,
|
||||||
tariffRate: premise.tariff_rate ?? null,
|
tariffRate: premise.tariff_rate ?? null,
|
||||||
|
|
@ -408,7 +436,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (type === "packaging") {
|
} else if (type === "packaging") {
|
||||||
this.componentsData.packaging.props = {
|
this.modalProps = {
|
||||||
length: premise.handling_unit.length,
|
length: premise.handling_unit.length,
|
||||||
width: premise.handling_unit.width,
|
width: premise.handling_unit.width,
|
||||||
height: premise.handling_unit.height,
|
height: premise.handling_unit.height,
|
||||||
|
|
@ -421,6 +449,42 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Animation hooks */
|
||||||
|
|
||||||
|
onBeforeEnter(el) {
|
||||||
|
if (this.isInitialLoad) {
|
||||||
|
el.style.opacity = 0;
|
||||||
|
el.style.transform = 'translateY(2rem)';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onEnter(el, done) {
|
||||||
|
if (this.isInitialLoad) {
|
||||||
|
const index = parseInt(el.dataset.index) || 0;
|
||||||
|
const delay = index * 50; // 50ms Verzögerung pro Element
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
el.style.transition = 'opacity 0.4s ease, transform 0.4s ease';
|
||||||
|
el.style.opacity = 1;
|
||||||
|
el.style.transform = 'translateY(0)';
|
||||||
|
|
||||||
|
// Cleanup nach Animation
|
||||||
|
setTimeout(() => {
|
||||||
|
el.style.transition = '';
|
||||||
|
el.style.opacity = '';
|
||||||
|
el.style.transform = '';
|
||||||
|
done();
|
||||||
|
|
||||||
|
// Nach dem letzten Element isInitialLoad deaktivieren
|
||||||
|
if (index === this.premises.length - 1) {
|
||||||
|
this.isInitialLoad = false;
|
||||||
|
}
|
||||||
|
}, 400);
|
||||||
|
}, delay);
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -429,7 +493,21 @@ export default {
|
||||||
|
|
||||||
/* Global style für copy-mode cursor */
|
/* Global style für copy-mode cursor */
|
||||||
.edit-calculation-container.has-selection :deep(.bulk-edit-row__cell--clickable:hover) {
|
.edit-calculation-container.has-selection :deep(.bulk-edit-row__cell--clickable:hover) {
|
||||||
cursor: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOCIgaGVpZ2h0PSIyOCIgdmlld0JveD0iMCAwIDI1NiAyNTYiPgogIDxyZWN0IHg9Ijg0IiB5PSIzMiIgd2lkdGg9IjEzNiIgaGVpZ2h0PSIxMzYiIGZpbGw9IndoaXRlIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iOCIgcng9IjQiLz4KICA8cmVjdCB4PSIzNiIgeT0iODQiIHdpZHRoPSIxMzYiIGhlaWdodD0iMTM2IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjgiIHJ4PSI0Ii8+Cjwvc3ZnPg==") 12 12, pointer;
|
cursor: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDEyOC41MSAxMzQuMDUiPjxkZWZzPjxzdHlsZT4uY3tmaWxsOm5vbmU7fS5jLC5ke3N0cm9rZTojMDEwMTAxO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2Utd2lkdGg6NXB4O30uZHtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjxnIGlkPSJhIj48cGF0aCBjbGFzcz0iYyIgZD0ibTU0Ljg5LDExMi41MWgtMi4yNGMtMS4yNCwwLTIuMjQtMS0yLjI0LTIuMjR2LTIuMjQiLz48bGluZSBjbGFzcz0iYyIgeDE9IjcwLjU3IiB5MT0iNzYuNjciIHgyPSI2My44NSIgeTI9Ijc2LjY3Ii8+PGxpbmUgY2xhc3M9ImMiIHgxPSI3MC41NyIgeTE9IjExMi41MSIgeDI9IjY2LjA5IiB5Mj0iMTEyLjUxIi8+PGxpbmUgY2xhc3M9ImMiIHgxPSI4Ni4yNSIgeTE9Ijk5LjA3IiB4Mj0iODYuMjUiIHkyPSI5Mi4zNSIvPjxsaW5lIGNsYXNzPSJjIiB4MT0iNTAuNDEiIHkxPSI5Ni44MyIgeDI9IjUwLjQxIiB5Mj0iOTIuMzUiLz48cGF0aCBjbGFzcz0iYyIgZD0ibTgxLjc3LDExMi41MWgyLjI0YzEuMjQsMCwyLjI0LTEsMi4yNC0yLjI0di0yLjI0Ii8+PHBhdGggY2xhc3M9ImMiIGQ9Im04MS43Nyw3Ni42N2gyLjI0YzEuMjQsMCwyLjI0LDEsMi4yNCwyLjI0djIuMjQiLz48cGF0aCBjbGFzcz0iYyIgZD0ibTU0Ljg5LDc2LjY3aC0yLjI0Yy0xLjI0LDAtMi4yNCwxLTIuMjQsMi4yNHYyLjI0Ii8+PHBhdGggY2xhc3M9ImMiIGQ9Im04Ni4yNSw5OS4wN2gxMS4yYzEuMjQsMCwyLjI0LTEsMi4yNC0yLjI0di0zMS4zNmMwLTEuMjQtMS0yLjI0LTIuMjQtMi4yNGgtMzEuMzZjLTEuMjQsMC0yLjI0LDEtMi4yNCwyLjI0djExLjIiLz48L2c+PGcgaWQ9ImIiPjxwYXRoIGNsYXNzPSJkIiBkPSJtNDQuMDgsNDQuMDdsMzIuOTQtOS4yYzEuNjktLjUyLDIuNjQtMi4zMSwyLjEyLTQtLjMtLjk4LTEuMDUtMS43NS0yLjAxLTIuMDlMNi43MywyLjY3Yy0xLjY3LS41Ny0zLjQ5LjMzLTQuMDYsMi0uMjMuNjYtLjIzLDEuMzgsMCwyLjA1bDI2LjExLDcwLjRjLjU4LDEuNjcsMi40LDIuNTYsNC4wNywxLjk4Ljk3LS4zMywxLjcxLTEuMTEsMi4wMS0yLjA5bDkuMjItMzIuOTRaIi8+PC9nPjwvc3ZnPg==") 12 12, pointer;
|
||||||
|
background-color: #f8fafc;
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global style für filter-mode cursor */
|
||||||
|
.edit-calculation-container.add-all :deep(.bulk-edit-row__cell--filterable:hover) {
|
||||||
|
cursor: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDEyOC41MSAxMzQuMDUiPjxkZWZzPjxzdHlsZT4uY3tmaWxsOm5vbmU7fS5jLC5ke3N0cm9rZTojMDEwMTAxO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2Utd2lkdGg6NXB4O30uZHtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjxnIGlkPSJhIj48bGluZSBjbGFzcz0iYyIgeDE9IjczLjAzIiB5MT0iNzUuNTUiIHgyPSI2Ni4zOCIgeTI9Ijc1LjU1Ii8+PGxpbmUgY2xhc3M9ImMiIHgxPSI2Ni4zOCIgeTE9IjExMi4xNiIgeDI9IjczLjAzIiB5Mj0iMTEyLjE2Ii8+PHBhdGggY2xhc3M9ImMiIGQ9Im04MS4zNSw3NS41NWg0Ljk5Yy45MiwwLDEuNjYuNzUsMS42NiwxLjY2djQuOTkiLz48bGluZSBjbGFzcz0iYyIgeDE9Ijg4LjAxIiB5MT0iOTcuMTgiIHgyPSI4OC4wMSIgeTI9IjkwLjUzIi8+PHBhdGggY2xhc3M9ImMiIGQ9Im04MS4zNSwxMTIuMTZoNC45OWMuOTIsMCwxLjY2LS43NSwxLjY2LTEuNjZ2LTQuOTkiLz48bGluZSBjbGFzcz0iYyIgeDE9IjUxLjQiIHkxPSI5MC41MyIgeDI9IjUxLjQiIHkyPSI5Ny4xOCIvPjxwYXRoIGNsYXNzPSJjIiBkPSJtNTguMDUsMTEyLjE2aC00Ljk5Yy0uOTIsMC0xLjY2LS43NS0xLjY2LTEuNjZ2LTQuOTkiLz48cGF0aCBjbGFzcz0iYyIgZD0ibTU4LjA1LDc1LjU1aC00Ljk5Yy0uOTIsMC0xLjY2Ljc1LTEuNjYsMS42NnY0Ljk5Ii8+PC9nPjxnIGlkPSJiIj48cGF0aCBjbGFzcz0iZCIgZD0ibTQ0LjA4LDQ0LjA3bDMyLjk0LTkuMmMxLjY5LS41MiwyLjY0LTIuMzEsMi4xMi00LS4zLS45OC0xLjA1LTEuNzUtMi4wMS0yLjA5TDYuNzMsMi42N2MtMS42Ny0uNTctMy40OS4zMy00LjA2LDItLjIzLjY2LS4yMywxLjM4LDAsMi4wNWwyNi4xMSw3MC40Yy41OCwxLjY3LDIuNCwyLjU2LDQuMDcsMS45OC45Ny0uMzMsMS43MS0xLjExLDIuMDEtMi4wOWw5LjIyLTMyLjk0WiIvPjwvZz48L3N2Zz4=") 12 12, pointer;
|
||||||
|
background-color: #f8fafc;
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global style für filter-mode cursor */
|
||||||
|
.edit-calculation-container.apply-filter :deep(.bulk-edit-row__cell--filterable:hover) {
|
||||||
|
cursor: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDEyOC41MSAxMzQuMDUiPjxkZWZzPjxzdHlsZT4uZHtzdHJva2U6IzAwMDt9LmQsLmV7ZmlsbDpub25lO30uZCwuZSwuZntzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLXdpZHRoOjVweDt9LmUsLmZ7c3Ryb2tlOiMwMTAxMDE7fS5me2ZpbGw6I2ZmZjt9PC9zdHlsZT48L2RlZnM+PGcgaWQ9ImEiPjxsaW5lIGNsYXNzPSJlIiB4MT0iNzMuMDMiIHkxPSI3NS41NSIgeDI9IjY2LjM4IiB5Mj0iNzUuNTUiLz48bGluZSBjbGFzcz0iZSIgeDE9IjY2LjM4IiB5MT0iMTEyLjE2IiB4Mj0iNzMuMDMiIHkyPSIxMTIuMTYiLz48cGF0aCBjbGFzcz0iZSIgZD0ibTgxLjM1LDc1LjU1aDQuOTljLjkyLDAsMS42Ni43NSwxLjY2LDEuNjZ2NC45OSIvPjxsaW5lIGNsYXNzPSJlIiB4MT0iODguMDEiIHkxPSI5Ny4xOCIgeDI9Ijg4LjAxIiB5Mj0iOTAuNTMiLz48cGF0aCBjbGFzcz0iZSIgZD0ibTgxLjM1LDExMi4xNmg0Ljk5Yy45MiwwLDEuNjYtLjc1LDEuNjYtMS42NnYtNC45OSIvPjxsaW5lIGNsYXNzPSJlIiB4MT0iNTEuNCIgeTE9IjkwLjUzIiB4Mj0iNTEuNCIgeTI9Ijk3LjE4Ii8+PHBhdGggY2xhc3M9ImUiIGQ9Im01OC4wNSwxMTIuMTZoLTQuOTljLS45MiwwLTEuNjYtLjc1LTEuNjYtMS42NnYtNC45OSIvPjxwYXRoIGNsYXNzPSJlIiBkPSJtNTguMDUsNzUuNTVoLTQuOTljLS45MiwwLTEuNjYuNzUtMS42NiwxLjY2djQuOTkiLz48L2c+PGcgaWQ9ImIiPjxwYXRoIGNsYXNzPSJmIiBkPSJtNDQuMDgsNDQuMDdsMzIuOTQtOS4yYzEuNjktLjUyLDIuNjQtMi4zMSwyLjEyLTQtLjMtLjk4LTEuMDUtMS43NS0yLjAxLTIuMDlMNi43MywyLjY3Yy0xLjY3LS41Ny0zLjQ5LjMzLTQuMDYsMi0uMjMuNjYtLjIzLDEuMzgsMCwyLjA1bDI2LjExLDcwLjRjLjU4LDEuNjcsMi40LDIuNTYsNC4wNywxLjk4Ljk3LS4zMywxLjcxLTEuMTEsMi4wMS0yLjA5bDkuMjItMzIuOTRaIi8+PC9nPjxnIGlkPSJjIj48bGluZSBjbGFzcz0iZCIgeDE9Ijk5LjM4IiB5MT0iOTQuMTkiIHgyPSIxMjYuMDEiIHkyPSI5NC4xOSIvPjxsaW5lIGNsYXNzPSJkIiB4MT0iMTEyLjY5IiB5MT0iODAuODciIHgyPSIxMTIuNjkiIHkyPSIxMDcuNTEiLz48L2c+PC9zdmc+") 12 12, pointer;
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
border-radius: 0.8rem;
|
border-radius: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
@ -454,25 +532,19 @@ export default {
|
||||||
gap: 1.6rem;
|
gap: 1.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Container Animation */
|
/* Sort Animation für Rows */
|
||||||
|
.sort-list-move {
|
||||||
|
transition: transform 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
.list-edit-enter-from {
|
/* Verhindere Animation während des Entfernens */
|
||||||
|
.sort-list-leave-active {
|
||||||
|
position: absolute;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-20px);
|
transition: opacity 0.2s ease;
|
||||||
max-height: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-edit-leave-to {
|
/* Enter-Animation wird via JavaScript gesteuert für staggered effect */
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-20px);
|
|
||||||
max-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-edit-enter-active,
|
|
||||||
.list-edit-leave-active {
|
|
||||||
transition: all 0.4s ease;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.spinner-container {
|
.spinner-container {
|
||||||
|
|
@ -495,9 +567,11 @@ export default {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-top: 3rem;
|
margin-top: 3rem;
|
||||||
margin-bottom: 3rem;
|
margin-bottom: 3rem;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edit-calculation-list-body {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.edit-calculation-list-header {
|
.edit-calculation-list-header {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
@ -513,6 +587,12 @@ export default {
|
||||||
letter-spacing: 0.08rem;
|
letter-spacing: 0.08rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edit-calculation-list-header-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
.edit-calculation-container {
|
.edit-calculation-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
329
src/frontend/src/store/destinationEdit.js
Normal file
329
src/frontend/src/store/destinationEdit.js
Normal file
|
|
@ -0,0 +1,329 @@
|
||||||
|
import {defineStore} from 'pinia'
|
||||||
|
import {toRaw} from "vue";
|
||||||
|
|
||||||
|
export const useDestinationEditStore = defineStore('destinationEdit', {
|
||||||
|
state: () => ({
|
||||||
|
destinations: null,
|
||||||
|
loading: false,
|
||||||
|
}),
|
||||||
|
getters: {},
|
||||||
|
actions: {
|
||||||
|
|
||||||
|
setupDestinations(premisses) {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
const temp = new Map();
|
||||||
|
premisses.forEach(p => temp.set(p.id, p.destinations));
|
||||||
|
this.destinations = temp;
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DESTINATION stuff
|
||||||
|
* =================
|
||||||
|
*/
|
||||||
|
|
||||||
|
prepareDestinations(dataSourcePremiseId, editedPremiseIds, massEdit = false, fromMassEditView = false) {
|
||||||
|
if (this.premisses === null) return;
|
||||||
|
if (!editedPremiseIds || !dataSourcePremiseId || editedPremiseIds.length === 0) return;
|
||||||
|
|
||||||
|
this.destinations = {
|
||||||
|
premise_ids: editedPremiseIds,
|
||||||
|
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 executeDestinationsMassEdit() {
|
||||||
|
|
||||||
|
if (!this.destinations.massEdit) {
|
||||||
|
|
||||||
|
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,
|
||||||
|
lead_time_d2d: toDest.lead_time_d2d,
|
||||||
|
route_selected_id: toDest.routes.find(r => r.is_selected)?.id ?? null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const url = `${config.backendUrl}/calculation/destination/${toDest.id}`;
|
||||||
|
performRequest(this, '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: data, headers: headers} = await performRequest(this, '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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.destinations = null;
|
||||||
|
this.processDestinationMassEdit = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancelMassEdit() {
|
||||||
|
this.destinations = 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.lead_time_d2d = from.is_d2d ? from.lead_time_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 ?? {};
|
||||||
|
|
||||||
|
d.annual_amount = from.annual_amount;
|
||||||
|
d.is_d2d = from.is_d2d;
|
||||||
|
d.rate_d2d = from.is_d2d ? from.rate_d2d : null;
|
||||||
|
d.lead_time_d2d = from.is_d2d ? from.lead_time_d2d : null;
|
||||||
|
|
||||||
|
if (from.userDefinedHandlingCosts) {
|
||||||
|
d.disposal_costs = from.disposal_costs;
|
||||||
|
d.repackaging_costs = from.repackaging_costs;
|
||||||
|
d.handling_costs = from.handling_costs;
|
||||||
|
} else {
|
||||||
|
d.disposal_costs = null;
|
||||||
|
d.repackaging_costs = null;
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects all destinations for the given "ids" for editing.
|
||||||
|
* This creates a copy of the destination with id "id".
|
||||||
|
* They are written back as soon as the user closes the dialog.
|
||||||
|
*/
|
||||||
|
selectDestination(id) {
|
||||||
|
if (this.premisses === null) return;
|
||||||
|
|
||||||
|
logger.info("selectDestination:", id)
|
||||||
|
|
||||||
|
const dest = this.destinations.destinations.find(d => d.id === id);
|
||||||
|
|
||||||
|
|
||||||
|
if ((dest ?? null) == null) {
|
||||||
|
const error = {
|
||||||
|
code: 'Frontend error.',
|
||||||
|
message: `Destination not found: ${id}. Please contact support.`,
|
||||||
|
trace: null
|
||||||
|
}
|
||||||
|
throw new Error("Internal frontend error: Destination not found: " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectedDestination = structuredClone(toRaw(dest));
|
||||||
|
},
|
||||||
|
async deselectDestinations(save = false) {
|
||||||
|
if (this.premisses === null) return;
|
||||||
|
|
||||||
|
|
||||||
|
if (save) {
|
||||||
|
const idx = this.destinations.destinations.findIndex(d => d.id === this.selectedDestination.id);
|
||||||
|
this.destinations.destinations.splice(idx, 1, this.selectedDestination);
|
||||||
|
|
||||||
|
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,
|
||||||
|
lead_time_d2d: toDest.lead_time_d2d,
|
||||||
|
route_selected_id: toDest.routes.find(r => r.is_selected)?.id ?? null,
|
||||||
|
};
|
||||||
|
|
||||||
|
logger.info(body)
|
||||||
|
|
||||||
|
const url = `${config.backendUrl}/calculation/destination/${toDest.id}`;
|
||||||
|
await performRequest(this, 'PUT', url, body, false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectedDestination = null;
|
||||||
|
},
|
||||||
|
async deleteDestination(id) {
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 1. delete from destinations copy
|
||||||
|
*/
|
||||||
|
const idx = this.destinations.destinations.findIndex(d => d.id === id);
|
||||||
|
|
||||||
|
if (idx === -1) {
|
||||||
|
logger.info("Destination not found in mass edit: , id)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 origId = id.substring(1);
|
||||||
|
|
||||||
|
const url = `${config.backendUrl}/calculation/destination/${origId}`;
|
||||||
|
await performRequest(this, 'DELETE', url, null, false).catch(async e => {
|
||||||
|
logger.error("Unable to delete destination: " + origId + "");
|
||||||
|
logger.error(e);
|
||||||
|
await this.loadPremissesIfNeeded(this.premisses.map(p => p.id));
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const p of this.premisses) {
|
||||||
|
const toBeDeleted = p.destinations.findIndex(d => String(d.id) === String(origId))
|
||||||
|
|
||||||
|
logger.info(toBeDeleted)
|
||||||
|
|
||||||
|
if (toBeDeleted !== -1) {
|
||||||
|
p.destinations.splice(toBeDeleted, 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async addDestination(node) {
|
||||||
|
|
||||||
|
if (this.destinations.massEdit) {
|
||||||
|
|
||||||
|
const existing = this.destinations.destinations.find(d => d.destination_node.id === node.id);
|
||||||
|
logger.info(existing)
|
||||||
|
|
||||||
|
if ((existing ?? null) !== null) {
|
||||||
|
logger.info("Destination already exists", node.id);
|
||||||
|
return [existing.id];
|
||||||
|
}
|
||||||
|
|
||||||
|
const destination = {
|
||||||
|
id: `v${node.id}`,
|
||||||
|
destination_node: structuredClone(toRaw(node)),
|
||||||
|
massEdit: true,
|
||||||
|
annual_amount: 0,
|
||||||
|
is_d2d: false,
|
||||||
|
rate_d2d: null,
|
||||||
|
lead_time_d2d: null,
|
||||||
|
disposal_costs: null,
|
||||||
|
repackaging_costs: null,
|
||||||
|
handling_costs: null,
|
||||||
|
userDefinedHandlingCosts: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.destinations.destinations.push(destination);
|
||||||
|
|
||||||
|
return [destination.id];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const id = node.id;
|
||||||
|
|
||||||
|
this.processDestinationMassEdit = true;
|
||||||
|
|
||||||
|
|
||||||
|
const toBeUpdated = this.destinations.fromMassEditView ? this.destinations.premise_ids : this.premisses?.filter(p => this.selectedIds.includes(p.id)).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/`;
|
||||||
|
|
||||||
|
|
||||||
|
const {data: destinations} = await performRequest(this, 'POST', url, body).catch(e => {
|
||||||
|
this.loading = false;
|
||||||
|
this.selectedLoading = false;
|
||||||
|
this.processDestinationMassEdit = false;
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
|
||||||
|
const mappedIds = []
|
||||||
|
|
||||||
|
for (const id of Object.keys(destinations)) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.processDestinationMassEdit = false;
|
||||||
|
|
||||||
|
return mappedIds;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -10,6 +10,8 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
return {
|
return {
|
||||||
premisses: null,
|
premisses: null,
|
||||||
selectedIds: [],
|
selectedIds: [],
|
||||||
|
sortedBy: 'id',
|
||||||
|
order: new Map([['id', 'desc'], ['material', 'desc'], ['supplier', 'desc']]),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set to true while the store is loading the premises.
|
* set to true while the store is loading the premises.
|
||||||
|
|
@ -26,44 +28,6 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
|
|
||||||
getCountryIdByPremiseIds(state) {
|
|
||||||
return function (ids) {
|
|
||||||
if (state.loading) {
|
|
||||||
if (state.throwsException)
|
|
||||||
throw new Error("Premises are accessed while still loading.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const premiss = state.premisses?.filter(p => ids.some(id => id === p.id));
|
|
||||||
|
|
||||||
const premiseCountryMap = new Map();
|
|
||||||
premiss?.forEach(premise => {
|
|
||||||
premiseCountryMap.set(premise.id, premise.supplier?.country?.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return premiseCountryMap;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Returns the ids of all premises.
|
|
||||||
// * @param state
|
|
||||||
// * @returns {*}
|
|
||||||
// */
|
|
||||||
// getPremiseIds(state) {
|
|
||||||
// if (state.loading) {
|
|
||||||
// if (state.throwsException)
|
|
||||||
// throw new Error("Premises are accessed while still loading.");
|
|
||||||
//
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return state.premisses?.map(p => p.id);
|
|
||||||
// },
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the premises.
|
* Returns the premises.
|
||||||
* @param state
|
* @param state
|
||||||
|
|
@ -107,7 +71,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
* @param state
|
* @param state
|
||||||
* @returns {T[]}
|
* @returns {T[]}
|
||||||
*/
|
*/
|
||||||
getSelectedPremisses(state) {
|
getSelectedPremiseIds(state) {
|
||||||
if (state.loading || state.selectedLoading) {
|
if (state.loading || state.selectedLoading) {
|
||||||
if (state.throwsException)
|
if (state.throwsException)
|
||||||
throw new Error("Premises are accessed while still loading.");
|
throw new Error("Premises are accessed while still loading.");
|
||||||
|
|
@ -115,24 +79,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.premisses.filter(p => p.selected);
|
return state.selectedIds;
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all premise ids that are selected.
|
|
||||||
* @param state
|
|
||||||
* @returns {T[]}
|
|
||||||
*/
|
|
||||||
getSelectedPremissesIds(state) {
|
|
||||||
if (state.loading || state.selectedLoading) {
|
|
||||||
if (state.throwsException)
|
|
||||||
throw new Error("Premises are accessed while still loading.");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.premisses.filter(p => p.selected).map(p => p.id);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -150,7 +97,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the premises are loaded and not empty. The frontend can show a the loaded premisses.
|
* Returns true if the premises are loaded and not empty. The frontend can show the loaded premisses.
|
||||||
* @param state
|
* @param state
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
|
|
@ -172,70 +119,10 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getters for single edit view
|
* Getters for controlling getters
|
||||||
* ============================
|
* ======================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if only one premise is selected.
|
|
||||||
* @param state
|
|
||||||
* @returns {boolean|null}
|
|
||||||
*/
|
|
||||||
isSingleSelect(state) {
|
|
||||||
if (state.loading || state.selectedLoading) {
|
|
||||||
if (state.throwsException)
|
|
||||||
throw new Error("Premises are accessed while still loading.");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.premisses.filter(p => p.selected).length === 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the id of the single selected premise.
|
|
||||||
* @param state
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
|
||||||
singleSelectId(state) {
|
|
||||||
if (state.loading || state.selectedLoading) {
|
|
||||||
if (state.throwsException)
|
|
||||||
throw new Error("Premises are accessed while still loading.");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!state.isSingleSelect) {
|
|
||||||
return null;
|
|
||||||
// throw new Error("Single selected premise accessed, but not in single select mode");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return state.premisses.find(p => p.selected)?.id;
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the single selected premise.
|
|
||||||
* @param state
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
|
||||||
singleSelectedPremise(state) {
|
|
||||||
if (state.loading || state.selectedLoading) {
|
|
||||||
if (state.throwsException)
|
|
||||||
throw new Error("Premises are accessed while still loading.");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!state.isSingleSelect) {
|
|
||||||
return null;
|
|
||||||
// throw new Error("Single selected premise accessed, but not in single select mode");
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.premisses?.find(p => p.selected);
|
|
||||||
},
|
|
||||||
|
|
||||||
allChecked(state) {
|
allChecked(state) {
|
||||||
if (state.premisses.length > state.selectedIds.length)
|
if (state.premisses.length > state.selectedIds.length)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -260,11 +147,82 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
return state.selectedIds.includes(id);
|
return state.selectedIds.includes(id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
activeSort(state) {
|
||||||
|
return state.sortedBy;
|
||||||
|
},
|
||||||
|
directionSort(state) {
|
||||||
|
return (sort) => {
|
||||||
|
return state.order.get(sort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
|
||||||
|
sort(type) {
|
||||||
|
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
const direction = (type !== this.sortedBy) ? 'desc' : (this.order.get(type) === 'asc' ? 'desc' : 'asc');
|
||||||
|
|
||||||
|
const temp = this.premisses.slice();
|
||||||
|
temp.sort((a, b) => {
|
||||||
|
if (type === 'material')
|
||||||
|
return direction === 'asc' ?
|
||||||
|
a.material.part_number.localeCompare(b.material.part_number) :
|
||||||
|
b.material.part_number.localeCompare(a.material.part_number);
|
||||||
|
else if (type === 'supplier')
|
||||||
|
return direction === 'asc' ?
|
||||||
|
a.supplier.name.localeCompare(b.supplier.name) :
|
||||||
|
b.supplier.name.localeCompare(a.supplier.name);
|
||||||
|
else return a.id - b.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("sort", this.sortedBy, direction, type);
|
||||||
|
this.premisses = temp;
|
||||||
|
this.sortedBy = type;
|
||||||
|
this.order.set(type, direction);
|
||||||
|
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
async startCalculation() {
|
||||||
|
|
||||||
|
const body = this.premisses.map(p => p.id);
|
||||||
|
const url = `${config.backendUrl}/calculation/start/`;
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
await performRequest(this, 'PUT', url, body, false, ['Premiss validation error', 'Internal Server Error']).catch(e => {
|
||||||
|
logger.log("startCalculation exception", e.errorObj);
|
||||||
|
error = e.errorObj;
|
||||||
|
})
|
||||||
|
|
||||||
|
return error;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save
|
||||||
|
*/
|
||||||
|
|
||||||
|
async batchUpdate(type, ids, data) {
|
||||||
|
switch (type) {
|
||||||
|
case 'price':
|
||||||
|
this.batchUpdatePrice(ids, data);
|
||||||
|
break;
|
||||||
|
case 'material':
|
||||||
|
this.batchUpdateMaterial(ids, data);
|
||||||
|
break;
|
||||||
|
case 'packaging':
|
||||||
|
this.batchUpdatePackaging(ids, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
async batchUpdatePrice(ids, priceData) {
|
async batchUpdatePrice(ids, priceData) {
|
||||||
|
|
||||||
|
console.log("batchUpdatePrice", ids, priceData)
|
||||||
|
|
||||||
const updatedPremises = this.premisses.map(p => {
|
const updatedPremises = this.premisses.map(p => {
|
||||||
if (ids.includes(p.id)) {
|
if (ids.includes(p.id)) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -296,12 +254,10 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
});
|
});
|
||||||
this.premisses = updatedPremises;
|
this.premisses = updatedPremises;
|
||||||
|
|
||||||
|
|
||||||
return await this.saveMaterial(ids, materialData);
|
return await this.saveMaterial(ids, materialData);
|
||||||
},
|
},
|
||||||
async batchUpdatePackaging(ids, packagingData) {
|
async batchUpdatePackaging(ids, packagingData) {
|
||||||
|
|
||||||
|
|
||||||
const updatedPremises = this.premisses.map(p => {
|
const updatedPremises = this.premisses.map(p => {
|
||||||
if (ids.includes(p.id)) {
|
if (ids.includes(p.id)) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -324,338 +280,13 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
});
|
});
|
||||||
this.premisses = updatedPremises;
|
this.premisses = updatedPremises;
|
||||||
|
|
||||||
logger.info("packaging data:", toRaw(packagingData), "update result", toRaw(updatedPremises));
|
|
||||||
|
|
||||||
return await this.savePackaging(ids, packagingData);
|
return await this.savePackaging(ids, packagingData);
|
||||||
},
|
},
|
||||||
async startCalculation() {
|
|
||||||
|
|
||||||
const body = this.premisses.map(p => p.id);
|
|
||||||
const url = `${config.backendUrl}/calculation/start/`;
|
|
||||||
let error = null;
|
|
||||||
|
|
||||||
await performRequest(this, 'PUT', url, body, false, ['Premiss validation error', 'Internal Server Error']).catch(e => {
|
|
||||||
logger.log("startCalculation exception", e.errorObj);
|
|
||||||
error = e.errorObj;
|
|
||||||
})
|
|
||||||
|
|
||||||
return error;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DESTINATION stuff
|
|
||||||
* =================
|
|
||||||
*/
|
|
||||||
|
|
||||||
prepareDestinations(dataSourcePremiseId, editedPremiseIds, massEdit = false, fromMassEditView = false) {
|
|
||||||
if (this.premisses === null) return;
|
|
||||||
if (!editedPremiseIds || !dataSourcePremiseId || editedPremiseIds.length === 0) return;
|
|
||||||
|
|
||||||
this.destinations = {
|
|
||||||
premise_ids: editedPremiseIds,
|
|
||||||
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 executeDestinationsMassEdit() {
|
|
||||||
|
|
||||||
if (!this.destinations.massEdit) {
|
|
||||||
|
|
||||||
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,
|
|
||||||
lead_time_d2d: toDest.lead_time_d2d,
|
|
||||||
route_selected_id: toDest.routes.find(r => r.is_selected)?.id ?? null,
|
|
||||||
};
|
|
||||||
|
|
||||||
const url = `${config.backendUrl}/calculation/destination/${toDest.id}`;
|
|
||||||
performRequest(this, '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: data, headers: headers} = await performRequest(this, '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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.destinations = null;
|
|
||||||
this.processDestinationMassEdit = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cancelMassEdit() {
|
|
||||||
this.destinations = 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.lead_time_d2d = from.is_d2d ? from.lead_time_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 ?? {};
|
|
||||||
|
|
||||||
d.annual_amount = from.annual_amount;
|
|
||||||
d.is_d2d = from.is_d2d;
|
|
||||||
d.rate_d2d = from.is_d2d ? from.rate_d2d : null;
|
|
||||||
d.lead_time_d2d = from.is_d2d ? from.lead_time_d2d : null;
|
|
||||||
|
|
||||||
if (from.userDefinedHandlingCosts) {
|
|
||||||
d.disposal_costs = from.disposal_costs;
|
|
||||||
d.repackaging_costs = from.repackaging_costs;
|
|
||||||
d.handling_costs = from.handling_costs;
|
|
||||||
} else {
|
|
||||||
d.disposal_costs = null;
|
|
||||||
d.repackaging_costs = null;
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Selects all destinations for the given "ids" for editing.
|
|
||||||
* This creates a copy of the destination with id "id".
|
|
||||||
* They are written back as soon as the user closes the dialog.
|
|
||||||
*/
|
|
||||||
selectDestination(id) {
|
|
||||||
if (this.premisses === null) return;
|
|
||||||
|
|
||||||
logger.info("selectDestination:", id)
|
|
||||||
|
|
||||||
const dest = this.destinations.destinations.find(d => d.id === id);
|
|
||||||
|
|
||||||
|
|
||||||
if ((dest ?? null) == null) {
|
|
||||||
const error = {
|
|
||||||
code: 'Frontend error.',
|
|
||||||
message: `Destination not found: ${id}. Please contact support.`,
|
|
||||||
trace: null
|
|
||||||
}
|
|
||||||
throw new Error("Internal frontend error: Destination not found: " + id);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectedDestination = structuredClone(toRaw(dest));
|
|
||||||
},
|
|
||||||
async deselectDestinations(save = false) {
|
|
||||||
if (this.premisses === null) return;
|
|
||||||
|
|
||||||
|
|
||||||
if (save) {
|
|
||||||
const idx = this.destinations.destinations.findIndex(d => d.id === this.selectedDestination.id);
|
|
||||||
this.destinations.destinations.splice(idx, 1, this.selectedDestination);
|
|
||||||
|
|
||||||
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,
|
|
||||||
lead_time_d2d: toDest.lead_time_d2d,
|
|
||||||
route_selected_id: toDest.routes.find(r => r.is_selected)?.id ?? null,
|
|
||||||
};
|
|
||||||
|
|
||||||
logger.info(body)
|
|
||||||
|
|
||||||
const url = `${config.backendUrl}/calculation/destination/${toDest.id}`;
|
|
||||||
await performRequest(this, 'PUT', url, body, false);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectedDestination = null;
|
|
||||||
},
|
|
||||||
async deleteDestination(id) {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 1. delete from destinations copy
|
|
||||||
*/
|
|
||||||
const idx = this.destinations.destinations.findIndex(d => d.id === id);
|
|
||||||
|
|
||||||
if (idx === -1) {
|
|
||||||
logger.info("Destination not found in mass edit: , id)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 origId = id.substring(1);
|
|
||||||
|
|
||||||
const url = `${config.backendUrl}/calculation/destination/${origId}`;
|
|
||||||
await performRequest(this, 'DELETE', url, null, false).catch(async e => {
|
|
||||||
logger.error("Unable to delete destination: " + origId + "");
|
|
||||||
logger.error(e);
|
|
||||||
await this.loadPremissesIfNeeded(this.premisses.map(p => p.id));
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const p of this.premisses) {
|
|
||||||
const toBeDeleted = p.destinations.findIndex(d => String(d.id) === String(origId))
|
|
||||||
|
|
||||||
logger.info(toBeDeleted)
|
|
||||||
|
|
||||||
if (toBeDeleted !== -1) {
|
|
||||||
p.destinations.splice(toBeDeleted, 1)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async addDestination(node) {
|
|
||||||
|
|
||||||
if (this.destinations.massEdit) {
|
|
||||||
|
|
||||||
const existing = this.destinations.destinations.find(d => d.destination_node.id === node.id);
|
|
||||||
logger.info(existing)
|
|
||||||
|
|
||||||
if ((existing ?? null) !== null) {
|
|
||||||
logger.info("Destination already exists", node.id);
|
|
||||||
return [existing.id];
|
|
||||||
}
|
|
||||||
|
|
||||||
const destination = {
|
|
||||||
id: `v${node.id}`,
|
|
||||||
destination_node: structuredClone(toRaw(node)),
|
|
||||||
massEdit: true,
|
|
||||||
annual_amount: 0,
|
|
||||||
is_d2d: false,
|
|
||||||
rate_d2d: null,
|
|
||||||
lead_time_d2d: null,
|
|
||||||
disposal_costs: null,
|
|
||||||
repackaging_costs: null,
|
|
||||||
handling_costs: null,
|
|
||||||
userDefinedHandlingCosts: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.destinations.destinations.push(destination);
|
|
||||||
|
|
||||||
return [destination.id];
|
|
||||||
|
|
||||||
} else {
|
|
||||||
const id = node.id;
|
|
||||||
|
|
||||||
this.processDestinationMassEdit = true;
|
|
||||||
|
|
||||||
|
|
||||||
const toBeUpdated = this.destinations.fromMassEditView ? this.destinations.premise_ids : this.premisses?.filter(p => p.selected).map(p => p.id);
|
|
||||||
|
|
||||||
if (toBeUpdated === null || toBeUpdated.length === 0) return;
|
|
||||||
|
|
||||||
const body = {destination_node_id: id, premise_id: toBeUpdated};
|
|
||||||
const url = `${config.backendUrl}/calculation/destination/`;
|
|
||||||
|
|
||||||
|
|
||||||
const {data: destinations} = await performRequest(this, 'POST', url, body).catch(e => {
|
|
||||||
this.loading = false;
|
|
||||||
this.selectedLoading = false;
|
|
||||||
this.processDestinationMassEdit = false;
|
|
||||||
throw e;
|
|
||||||
});
|
|
||||||
|
|
||||||
const mappedIds = []
|
|
||||||
|
|
||||||
for (const id of Object.keys(destinations)) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.processDestinationMassEdit = false;
|
|
||||||
|
|
||||||
return mappedIds;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save
|
|
||||||
*/
|
|
||||||
|
|
||||||
async savePrice(ids = null, priceData = null) {
|
async savePrice(ids = null, priceData = null) {
|
||||||
let success = true;
|
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;
|
const toBeUpdated = this.premisses ? (ids ? (ids.map(id => this.premisses.find(p => String(p.id) === String(id)))) : (this.selectedIds.map(id => this.premisses.find(p => String(p.id) === String(id))))) : null;
|
||||||
|
|
||||||
|
console.log("toBeUpdated", ids, toBeUpdated, priceData);
|
||||||
|
|
||||||
if (!toBeUpdated?.length) return;
|
if (!toBeUpdated?.length) return;
|
||||||
|
|
||||||
|
|
@ -675,7 +306,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
async savePackaging(ids = null, packagingData = null) {
|
async savePackaging(ids = null, packagingData = null) {
|
||||||
let success = true;
|
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;
|
const toBeUpdated = this.premisses ? (ids ? (ids.map(id => this.premisses.find(p => String(p.id) === String(id)))) : (this.premisses.filter(p => this.selectedIds.includes(p.id)))) : null;
|
||||||
|
|
||||||
if (!toBeUpdated?.length) return;
|
if (!toBeUpdated?.length) return;
|
||||||
|
|
||||||
|
|
@ -708,7 +339,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
},
|
},
|
||||||
async saveMaterial(ids = null, materialData = null) {
|
async saveMaterial(ids = null, materialData = null) {
|
||||||
let success = true;
|
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;
|
const toBeUpdated = this.premisses ? (ids ? (ids.map(id => this.premisses.find(p => String(p.id) === String(id)))) : (this.premisses.filter(p => this.selectedIds.includes(p.id)))) : null;
|
||||||
|
|
||||||
|
|
||||||
if (!toBeUpdated?.length) return;
|
if (!toBeUpdated?.length) return;
|
||||||
|
|
@ -761,12 +392,15 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
|
|
||||||
this.selectedLoading = false;
|
this.selectedLoading = false;
|
||||||
},
|
},
|
||||||
setBy(type, ofId) {
|
setBy(type, action, ofId) {
|
||||||
this.selectedLoading = true;
|
this.selectedLoading = true;
|
||||||
const premise = this.premisses.find(p => p.id === ofId);
|
const premise = this.premisses.find(p => p.id === ofId);
|
||||||
|
|
||||||
const temp = [];
|
const temp = [];
|
||||||
|
|
||||||
|
if (action === 'append')
|
||||||
|
temp.push(...this.selectedIds);
|
||||||
|
|
||||||
this.premisses.forEach(p => {
|
this.premisses.forEach(p => {
|
||||||
if (type === 'supplier' && p.supplier.id === premise.supplier.id) {
|
if (type === 'supplier' && p.supplier.id === premise.supplier.id) {
|
||||||
temp.push(p.id);
|
temp.push(p.id);
|
||||||
|
|
@ -779,14 +413,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
|
|
||||||
this.selectedLoading = false;
|
this.selectedLoading = false;
|
||||||
},
|
},
|
||||||
async loadPremissesIfNeeded(ids, exact = false) {
|
async load(ids) {
|
||||||
const reload = this.premisses ? !ids.every((id) => this.premisses.find(d => d.id === id) && (!exact || ids.length === this.premisses.length)) : true;
|
|
||||||
|
|
||||||
if (reload) {
|
|
||||||
await this.loadPremissesForced(ids);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async loadPremissesForced(ids) {
|
|
||||||
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.premises = [];
|
this.premises = [];
|
||||||
|
|
@ -800,10 +427,11 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
});
|
});
|
||||||
this.premisses = data;
|
this.premisses = data;
|
||||||
|
|
||||||
this.premisses.forEach(p => p.selected = false);
|
this.selectedIds = [];
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|
||||||
|
return this.premisses;
|
||||||
},
|
},
|
||||||
removePremise(id) {
|
removePremise(id) {
|
||||||
const idx = this.premisses.findIndex(p => p.id === id);
|
const idx = this.premisses.findIndex(p => p.id === id);
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,23 @@ public class PackagingDimension {
|
||||||
|
|
||||||
private Boolean isDeprecated;
|
private Boolean isDeprecated;
|
||||||
|
|
||||||
|
public static PackagingDimension getEmpty(PackagingType type) {
|
||||||
|
var dimension = new PackagingDimension();
|
||||||
|
|
||||||
|
dimension.setType(type);
|
||||||
|
dimension.setLength(null);
|
||||||
|
dimension.setWidth(null);
|
||||||
|
dimension.setHeight(null);
|
||||||
|
dimension.setDimensionUnit(DimensionUnit.MM);
|
||||||
|
|
||||||
|
dimension.setWeight(null);
|
||||||
|
dimension.setWeightUnit(WeightUnit.KG);
|
||||||
|
|
||||||
|
dimension.setContentUnitCount(null);
|
||||||
|
|
||||||
|
return dimension;
|
||||||
|
}
|
||||||
|
|
||||||
public Integer getId() {
|
public Integer getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@ public class PremiseRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void updatePackaging(List<Integer> premiseIds, PackagingDimension hu, PackagingDimension shu, Boolean stackable, Boolean mixable) {
|
public void resetPackaging(List<Integer> premiseIds, PackagingDimension hu, PackagingDimension shu, Boolean stackable, Boolean mixable) {
|
||||||
|
|
||||||
if (premiseIds == null || premiseIds.isEmpty() || hu == null) {
|
if (premiseIds == null || premiseIds.isEmpty() || hu == null) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -179,7 +179,7 @@ public class PremiseRepository {
|
||||||
params.addValue("weight", hu.getWeight());
|
params.addValue("weight", hu.getWeight());
|
||||||
params.addValue("dimensionUnit", hu.getDimensionUnit().name());
|
params.addValue("dimensionUnit", hu.getDimensionUnit().name());
|
||||||
params.addValue("weightUnit", hu.getWeightUnit().name());
|
params.addValue("weightUnit", hu.getWeightUnit().name());
|
||||||
params.addValue("unitCount", hu.getContentUnitCount() * shu.getContentUnitCount());
|
params.addValue("unitCount", (hu.getContentUnitCount() == null || shu.getContentUnitCount() == null) ? null : hu.getContentUnitCount() * shu.getContentUnitCount());
|
||||||
params.addValue("stackable", isStackable);
|
params.addValue("stackable", isStackable);
|
||||||
params.addValue("mixable", isMixable);
|
params.addValue("mixable", isMixable);
|
||||||
params.addValue("premiseIds", premiseIds);
|
params.addValue("premiseIds", premiseIds);
|
||||||
|
|
@ -203,7 +203,7 @@ public class PremiseRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void updatePackaging(List<Integer> premiseIds, PackagingDimension hu, Boolean stackable, Boolean mixable) {
|
public void resetPackaging(List<Integer> premiseIds, PackagingDimension hu, Boolean stackable, Boolean mixable) {
|
||||||
|
|
||||||
|
|
||||||
if (premiseIds == null || premiseIds.isEmpty()) {
|
if (premiseIds == null || premiseIds.isEmpty()) {
|
||||||
|
|
@ -346,6 +346,18 @@ public class PremiseRepository {
|
||||||
throw new DatabaseException("Premise update failed for " + premiseIds.size() + " premises. Affected rows: " + affectedRows);
|
throw new DatabaseException("Premise update failed for " + premiseIds.size() + " premises. Affected rows: " + affectedRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void resetPrice(List<Integer> premiseIds) {
|
||||||
|
if (premiseIds == null || premiseIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String placeholders = String.join(",", Collections.nCopies(premiseIds.size(), "?"));
|
||||||
|
String query = "UPDATE premise SET material_cost = null, is_fca_enabled = false, oversea_share = null WHERE id IN (" + placeholders + ")";
|
||||||
|
jdbcTemplate.update(query, premiseIds.toArray());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void updatePrice(List<Integer> premiseIds, BigDecimal price, Boolean includeFcaFee, BigDecimal overseaShare) {
|
public void updatePrice(List<Integer> premiseIds, BigDecimal price, Boolean includeFcaFee, BigDecimal overseaShare) {
|
||||||
// Build dynamic SET clause based on non-null parameters
|
// Build dynamic SET clause based on non-null parameters
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,7 @@ public class PremisesService {
|
||||||
|
|
||||||
var dimensions = packagingDTO.getDimensions() == null ? null : dimensionTransformer.toDimensionEntity(packagingDTO.getDimensions());
|
var dimensions = packagingDTO.getDimensions() == null ? null : dimensionTransformer.toDimensionEntity(packagingDTO.getDimensions());
|
||||||
|
|
||||||
premiseRepository.updatePackaging(packagingDTO.getPremiseIds(), dimensions, packagingDTO.getStackable(), packagingDTO.getMixable());
|
premiseRepository.resetPackaging(packagingDTO.getPremiseIds(), dimensions, packagingDTO.getStackable(), packagingDTO.getMixable());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,7 +304,7 @@ public class PremisesService {
|
||||||
|
|
||||||
premiseRepository.updateMaterial(Collections.singletonList(newId), old.getHsCode(), old.getTariffRate(), old.getTariffUnlocked());
|
premiseRepository.updateMaterial(Collections.singletonList(newId), old.getHsCode(), old.getTariffRate(), old.getTariffUnlocked());
|
||||||
premiseRepository.updatePrice(Collections.singletonList(newId), old.getMaterialCost(), old.getFcaEnabled(), old.getOverseaShare());
|
premiseRepository.updatePrice(Collections.singletonList(newId), old.getMaterialCost(), old.getFcaEnabled(), old.getOverseaShare());
|
||||||
premiseRepository.updatePackaging(Collections.singletonList(newId), dimensionTransformer.toDimensionEntity(old), old.getHuStackable(), old.getHuMixable());
|
premiseRepository.resetPackaging(Collections.singletonList(newId), dimensionTransformer.toDimensionEntity(old), old.getHuStackable(), old.getHuMixable());
|
||||||
premiseRepository.setPackagingId(newId, old.getPackagingId());
|
premiseRepository.setPackagingId(newId, old.getPackagingId());
|
||||||
|
|
||||||
destinationService.duplicate(old.getId(), newId);
|
destinationService.duplicate(old.getId(), newId);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package de.avatic.lcc.service.calculation;
|
||||||
|
|
||||||
import de.avatic.lcc.dto.calculation.edit.PremiseDetailDTO;
|
import de.avatic.lcc.dto.calculation.edit.PremiseDetailDTO;
|
||||||
import de.avatic.lcc.model.db.packaging.PackagingDimension;
|
import de.avatic.lcc.model.db.packaging.PackagingDimension;
|
||||||
|
import de.avatic.lcc.model.db.packaging.PackagingType;
|
||||||
import de.avatic.lcc.model.db.premises.Premise;
|
import de.avatic.lcc.model.db.premises.Premise;
|
||||||
import de.avatic.lcc.model.db.premises.PremiseState;
|
import de.avatic.lcc.model.db.premises.PremiseState;
|
||||||
import de.avatic.lcc.model.db.properties.PackagingProperty;
|
import de.avatic.lcc.model.db.properties.PackagingProperty;
|
||||||
|
|
@ -88,6 +89,8 @@ public class PremiseCreationService {
|
||||||
if (createEmpty) {
|
if (createEmpty) {
|
||||||
// reset to defaults.
|
// reset to defaults.
|
||||||
fillPremise(p, tariffs, userId);
|
fillPremise(p, tariffs, userId);
|
||||||
|
// remove destinations
|
||||||
|
destinationService.deleteAllDestinationsByPremiseId(Collections.singletonList(p.getId()), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (p.getPremise().getState().equals(PremiseState.COMPLETED)) {
|
} else if (p.getPremise().getState().equals(PremiseState.COMPLETED)) {
|
||||||
|
|
@ -108,7 +111,7 @@ public class PremiseCreationService {
|
||||||
|
|
||||||
premiseRepository.updateMaterial(Collections.singletonList(p.getId()), old.getHsCode(), old.getTariffRate(), old.getTariffUnlocked());
|
premiseRepository.updateMaterial(Collections.singletonList(p.getId()), old.getHsCode(), old.getTariffRate(), old.getTariffUnlocked());
|
||||||
premiseRepository.updatePrice(Collections.singletonList(p.getId()), old.getMaterialCost(), old.getFcaEnabled(), old.getOverseaShare());
|
premiseRepository.updatePrice(Collections.singletonList(p.getId()), old.getMaterialCost(), old.getFcaEnabled(), old.getOverseaShare());
|
||||||
premiseRepository.updatePackaging(Collections.singletonList(p.getId()), dimensionTransformer.toDimensionEntity(old), old.getHuStackable(), old.getHuMixable());
|
premiseRepository.resetPackaging(Collections.singletonList(p.getId()), dimensionTransformer.toDimensionEntity(old), old.getHuStackable(), old.getHuMixable());
|
||||||
premiseRepository.setPackagingId(p.getId(), old.getPackagingId());
|
premiseRepository.setPackagingId(p.getId(), old.getPackagingId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,9 +125,14 @@ public class PremiseCreationService {
|
||||||
if (hu.isPresent() && shu.isPresent()) {
|
if (hu.isPresent() && shu.isPresent()) {
|
||||||
boolean stackable = packagingPropertiesRepository.getByPackagingIdAndType(packaging.get().getId(), PackagingPropertyMappingId.STACKABLE.name()).map(PackagingProperty::getValue).map(Boolean::valueOf).orElse(false);
|
boolean stackable = packagingPropertiesRepository.getByPackagingIdAndType(packaging.get().getId(), PackagingPropertyMappingId.STACKABLE.name()).map(PackagingProperty::getValue).map(Boolean::valueOf).orElse(false);
|
||||||
boolean mixable = packagingPropertiesRepository.getByPackagingIdAndType(packaging.get().getId(), PackagingPropertyMappingId.MIXABLE.name()).map(PackagingProperty::getValue).map(Boolean::valueOf).orElse(false);
|
boolean mixable = packagingPropertiesRepository.getByPackagingIdAndType(packaging.get().getId(), PackagingPropertyMappingId.MIXABLE.name()).map(PackagingProperty::getValue).map(Boolean::valueOf).orElse(false);
|
||||||
premiseRepository.updatePackaging(Collections.singletonList(p.getId()), hu.get(), shu.get(), stackable, mixable); //TODO clarify if the hu unit count in packaging data is total unit count or shu count (shu*hu or hu)
|
premiseRepository.resetPackaging(Collections.singletonList(p.getId()), hu.get(), shu.get(), stackable, mixable); //TODO clarify if the hu unit count in packaging data is total unit count or shu count (shu*hu or hu)
|
||||||
premiseRepository.setPackagingId(p.getId(), packaging.get().getId());
|
premiseRepository.setPackagingId(p.getId(), packaging.get().getId());
|
||||||
|
} else {
|
||||||
|
premiseRepository.resetPackaging(Collections.singletonList(p.getId()), PackagingDimension.getEmpty(PackagingType.HU), PackagingDimension.getEmpty(PackagingType.SHU), true, true);
|
||||||
|
premiseRepository.setPackagingId(p.getId(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
premiseRepository.resetPrice(Collections.singletonList(p.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
tariffs.stream()
|
tariffs.stream()
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue