FRONTEND/BACKEND: fixing bugs in schema contstraints. Fixing performance issues in mass calculation (frontend)
This commit is contained in:
parent
32feeb06a0
commit
c47531a335
19 changed files with 758 additions and 574 deletions
911
src/frontend/package-lock.json
generated
911
src/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -14,6 +14,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@phosphor-icons/vue": "^2.2.1",
|
"@phosphor-icons/vue": "^2.2.1",
|
||||||
"@vueuse/core": "^13.6.0",
|
"@vueuse/core": "^13.6.0",
|
||||||
|
"loglevel": "^1.9.2",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"vue": "^3.5.18",
|
"vue": "^3.5.18",
|
||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.5.1"
|
||||||
|
|
|
||||||
|
|
@ -154,3 +154,5 @@ export class UrlSafeBase64 {
|
||||||
return urlSafeBase64.replace(/-/g, '+').replace(/_/g, '/');
|
return urlSafeBase64.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="checkbox-container">
|
<div class="checkbox-container">
|
||||||
<label class="checkbox-item" @change="setFilter">
|
<label class="checkbox-item" :class="{ disabled: disabled }" @change="setFilter">
|
||||||
<input type="checkbox" checked v-model="isChecked" >
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:checked="isChecked"
|
||||||
|
:disabled="disabled"
|
||||||
|
v-model="isChecked"
|
||||||
|
>
|
||||||
<span class="checkmark"></span>
|
<span class="checkmark"></span>
|
||||||
<span class="checkbox-label"><slot></slot></span>
|
<span class="checkbox-label"><slot></slot></span>
|
||||||
</label>
|
</label>
|
||||||
|
|
@ -16,6 +21,11 @@ export default{
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
required: false
|
required: false
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
name: "Checkbox",
|
name: "Checkbox",
|
||||||
|
|
@ -30,6 +40,7 @@ export default{
|
||||||
return this.internalChecked;
|
return this.internalChecked;
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
|
if (this.disabled) return; // Prevent changes when disabled
|
||||||
this.internalChecked = value;
|
this.internalChecked = value;
|
||||||
this.$emit('checkbox-changed', value);
|
this.$emit('checkbox-changed', value);
|
||||||
}
|
}
|
||||||
|
|
@ -42,6 +53,7 @@ export default{
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setFilter(event) {
|
setFilter(event) {
|
||||||
|
if (this.disabled) return; // Prevent action when disabled
|
||||||
// The computed setter will handle the emit
|
// The computed setter will handle the emit
|
||||||
this.isChecked = event.target.checked;
|
this.isChecked = event.target.checked;
|
||||||
}
|
}
|
||||||
|
|
@ -66,6 +78,11 @@ export default{
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox-item.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
.checkbox-item input[type="checkbox"] {
|
.checkbox-item input[type="checkbox"] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
@ -74,6 +91,10 @@ export default{
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox-item.disabled input[type="checkbox"] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.checkmark {
|
.checkmark {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
|
|
@ -88,18 +109,28 @@ export default{
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-item:hover .checkmark {
|
.checkbox-item:not(.disabled):hover .checkmark {
|
||||||
background: #EEF4FF;
|
background: #EEF4FF;
|
||||||
border: 0.2rem solid #8DB3FE;
|
border: 0.2rem solid #8DB3FE;
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox-item.disabled .checkmark {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-color: #d0d0d0;
|
||||||
|
}
|
||||||
|
|
||||||
.checkbox-item input:checked ~ .checkmark {
|
.checkbox-item input:checked ~ .checkmark {
|
||||||
background-color: #002F54;
|
background-color: #002F54;
|
||||||
border-color: #002F54;
|
border-color: #002F54;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox-item.disabled input:checked ~ .checkmark {
|
||||||
|
background-color: #6b7280;
|
||||||
|
border-color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
.checkmark::after {
|
.checkmark::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
@ -123,4 +154,8 @@ export default{
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox-item.disabled .checkbox-label {
|
||||||
|
color: #8a8a8a;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="edit-calculation-checkbox-cell">
|
<div class="edit-calculation-checkbox-cell">
|
||||||
<checkbox :checked="isSelected" @checkbox-changed="updateSelected"></checkbox>
|
<checkbox :checked="isSelected" @checkbox-changed="updateSelected"></checkbox>
|
||||||
</div>
|
</div>
|
||||||
<div class="edit-calculation-cell--material" :class="copyModeClass"
|
<div class="edit-calculation-cell--material copyable-cell"
|
||||||
@click="action('material')">
|
@click="action('material')">
|
||||||
<div class="edit-calculation-cell-line">{{ premise.material.part_number }}</div>
|
<div class="edit-calculation-cell-line">{{ premise.material.part_number }}</div>
|
||||||
<div class="edit-calculation-cell-line edit-calculation-cell-subline" v-if="premise.material.name">
|
<div class="edit-calculation-cell-line edit-calculation-cell-subline" v-if="premise.material.name">
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
{{ toPercent(premise.tariff_rate) }} %
|
{{ toPercent(premise.tariff_rate) }} %
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="edit-calculation-cell--price" :class="copyModeClass" v-if="showPrice"
|
<div class="edit-calculation-cell--price copyable-cell" v-if="showPrice"
|
||||||
@click="action('price')">
|
@click="action('price')">
|
||||||
<div class="edit-calculation-cell-line">{{ premise.material_cost }} EUR</div>
|
<div class="edit-calculation-cell-line">{{ premise.material_cost }} EUR</div>
|
||||||
<div class="edit-calculation-cell-line edit-calculation-cell-subline">Oversea share:
|
<div class="edit-calculation-cell-line edit-calculation-cell-subline">Oversea share:
|
||||||
|
|
@ -28,11 +28,14 @@
|
||||||
<div class="edit-calculation-cell-line edit-calculation-cell-subline" v-if="premise.is_fca_enabled">
|
<div class="edit-calculation-cell-line edit-calculation-cell-subline" v-if="premise.is_fca_enabled">
|
||||||
<basic-badge icon="plus" variant="primary">FCA FEE</basic-badge>
|
<basic-badge icon="plus" variant="primary">FCA FEE</basic-badge>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="edit-calculation-cell-line edit-calculation-cell-subline" v-if="showPriceIncomplete">
|
||||||
|
<basic-badge variant="exception" icon="warning">INCOMPLETE</basic-badge>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="edit-calculation-empty" :class="copyModeClass" v-else @click="action('price')">
|
<div class="edit-calculation-empty copyable-cell" v-else @click="action('price')">
|
||||||
<basic-badge variant="exception" icon="warning">INCOMPLETE</basic-badge>
|
<basic-badge variant="exception" icon="warning">INCOMPLETE</basic-badge>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showHu" class="edit-calculation-cell edit-calculation-cell--packaging" :class="copyModeClass"
|
<div v-if="showHu" class="edit-calculation-cell edit-calculation-cell--packaging copyable-cell"
|
||||||
@click="action('packaging')">
|
@click="action('packaging')">
|
||||||
<div class="edit-calculation-cell-line">
|
<div class="edit-calculation-cell-line">
|
||||||
<PhVectorThree/>
|
<PhVectorThree/>
|
||||||
|
|
@ -53,11 +56,11 @@
|
||||||
<basic-badge v-if="premise.is_mixable" variant="skeleton" icon="shuffle">MIXABLE</basic-badge>
|
<basic-badge v-if="premise.is_mixable" variant="skeleton" icon="shuffle">MIXABLE</basic-badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="edit-calculation-empty" :class="copyModeClass" v-else
|
<div class="edit-calculation-empty copyable-cell" v-else
|
||||||
@click="action('packaging')">
|
@click="action('packaging')">
|
||||||
<basic-badge variant="exception" icon="warning">INCOMPLETE</basic-badge>
|
<basic-badge variant="exception" icon="warning">INCOMPLETE</basic-badge>
|
||||||
</div>
|
</div>
|
||||||
<div class="edit-calculation-cell--supplier" :class="copyModeClass"
|
<div class="edit-calculation-cell--supplier copyable-cell"
|
||||||
@click="action('supplier')">
|
@click="action('supplier')">
|
||||||
<div class="edit-calculation-cell--supplier-container" v-if="premise.supplier">
|
<div class="edit-calculation-cell--supplier-container" v-if="premise.supplier">
|
||||||
<!-- <div class="edit-calculation-cell--supplier-flag">-->
|
<!-- <div class="edit-calculation-cell--supplier-flag">-->
|
||||||
|
|
@ -69,7 +72,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="edit-calculation-cell--destination" :class="copyModeClass" v-if="showDestinations"
|
<div class="edit-calculation-cell--destination copyable-cell" v-if="showDestinations"
|
||||||
@click="action('destinations')">
|
@click="action('destinations')">
|
||||||
<div class="edit-calculation-cell-line">
|
<div class="edit-calculation-cell-line">
|
||||||
<span class="number-circle"> {{ destinationsCount }} </span> Destinations
|
<span class="number-circle"> {{ destinationsCount }} </span> Destinations
|
||||||
|
|
@ -82,7 +85,7 @@
|
||||||
<div class="edit-calculation-empty" v-else-if="showMassEdit">
|
<div class="edit-calculation-empty" v-else-if="showMassEdit">
|
||||||
<spinner></spinner>
|
<spinner></spinner>
|
||||||
</div>
|
</div>
|
||||||
<div class="edit-calculation-empty" :class="copyModeClass" v-else
|
<div class="edit-calculation-empty copyable-cell" v-else
|
||||||
@click="action('destinations')">
|
@click="action('destinations')">
|
||||||
<basic-badge variant="exception" icon="warning">INCOMPLETE</basic-badge>
|
<basic-badge variant="exception" icon="warning">INCOMPLETE</basic-badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -109,7 +112,8 @@ import {
|
||||||
PhBarcode,
|
PhBarcode,
|
||||||
PhEmpty,
|
PhEmpty,
|
||||||
PhFactory,
|
PhFactory,
|
||||||
PhHash, PhMapPin,
|
PhHash,
|
||||||
|
PhMapPin,
|
||||||
PhPercent,
|
PhPercent,
|
||||||
PhVectorThree,
|
PhVectorThree,
|
||||||
PhVectorTwo
|
PhVectorTwo
|
||||||
|
|
@ -133,9 +137,9 @@ export default {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
copyMode: {
|
premise: {
|
||||||
type: Boolean,
|
type: Object,
|
||||||
default: false
|
required: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -169,6 +173,9 @@ export default {
|
||||||
showPrice() {
|
showPrice() {
|
||||||
return (this.premise.material_cost)
|
return (this.premise.material_cost)
|
||||||
},
|
},
|
||||||
|
showPriceIncomplete() {
|
||||||
|
return !(this.premise.oversea_share)
|
||||||
|
},
|
||||||
isSelected() {
|
isSelected() {
|
||||||
return this.premise.selected;
|
return this.premise.selected;
|
||||||
},
|
},
|
||||||
|
|
@ -176,16 +183,6 @@ export default {
|
||||||
return this.premise.handling_unit;
|
return this.premise.handling_unit;
|
||||||
},
|
},
|
||||||
...mapStores(usePremiseEditStore),
|
...mapStores(usePremiseEditStore),
|
||||||
premise() {
|
|
||||||
const data = this.premiseEditStore.getById(this.id);
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
copyModeClass() {
|
|
||||||
if (this.copyMode) {
|
|
||||||
return 'edit-calculation-cell--copy-mode';
|
|
||||||
}
|
|
||||||
return 'edit-calculation-cell';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toPercent(value) {
|
toPercent(value) {
|
||||||
|
|
@ -211,6 +208,19 @@ export default {
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
|
.copyable-cell {
|
||||||
|
padding: 0.8rem;
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
height: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Standard hover ohne copy mode */
|
||||||
|
.copyable-cell:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgba(107, 134, 156, 0.05);
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
box-shadow: 0 0.4rem 0.6rem -0.1rem rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.bulk-edit-row {
|
.bulk-edit-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async saveProperty(property) {
|
async saveProperty(property) {
|
||||||
|
this.countryStore.setProperty(property);
|
||||||
|
|
||||||
},
|
},
|
||||||
async query(query) {
|
async query(query) {
|
||||||
|
|
|
||||||
|
|
@ -147,31 +147,12 @@ export default {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SOLUTION 1: Add relative positioning and min-height to prevent collapse */
|
|
||||||
.properties-list {
|
.properties-list {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 100px; /* Adjust based on your typical content height */
|
min-height: 100px;
|
||||||
|
width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SOLUTION 2: Keep elements in normal flow during transition
|
|
||||||
.properties-fade-enter-active,
|
|
||||||
.properties-fade-leave-active {
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.properties-fade-enter-from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.properties-fade-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/* SOLUTION 3: For transition-group, ensure proper positioning
|
|
||||||
.property-item-enter-active,
|
|
||||||
.property-item-leave-active {
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
.property-item-enter-from {
|
.property-item-enter-from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
@ -193,16 +174,4 @@ export default {
|
||||||
.property-item-move {
|
.property-item-move {
|
||||||
transition: transform 0.3s ease;
|
transition: transform 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ALTERNATIVE SOLUTION: If you still have issues, try this instead */
|
|
||||||
/*
|
|
||||||
.properties-container {
|
|
||||||
min-height: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.properties-list {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
<div class="caption-column">
|
<div class="caption-column">
|
||||||
<div class="caption-column-id">{{ property.name }}:</div>
|
<div class="caption-column-id">{{ property.name }}:</div>
|
||||||
|
<div class="caption-column-name">Ext. Mapping: {{ property.external_mapping_id }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-column">
|
<div class="input-column">
|
||||||
|
|
@ -197,7 +198,6 @@ export default {
|
||||||
grid-template-columns: 3fr 1fr 0.5fr;
|
grid-template-columns: 3fr 1fr 0.5fr;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2.4rem;
|
gap: 2.4rem;
|
||||||
|
|
||||||
height: 8rem;
|
height: 8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,13 +56,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-column-chk">
|
<div class="input-column-chk">
|
||||||
<tooltip position="left" text="Deselect if the handling unit cannot be stacked">
|
|
||||||
<checkbox :checked="stackable" @checkbox-changed="updateStackable">stackable</checkbox>
|
|
||||||
</tooltip>
|
|
||||||
<tooltip position="left"
|
<tooltip position="left"
|
||||||
text="Deselect if the handling unit cannot be transported together with other handling units">
|
text="Deselect if the handling unit cannot be transported together with other handling units">
|
||||||
<checkbox :checked="mixable" @checkbox-changed="updateMixable">mixable</checkbox>
|
<checkbox :checked="mixable" @checkbox-changed="updateMixable">mixable</checkbox>
|
||||||
</tooltip>
|
</tooltip>
|
||||||
|
<tooltip position="left" text="Deselect if the handling unit cannot be stacked">
|
||||||
|
<checkbox :checked="stackable" @checkbox-changed="updateStackable" :disabled="mixable">stackable</checkbox>
|
||||||
|
</tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -238,6 +238,10 @@ export default {
|
||||||
this.$emit('update:stackable', value);
|
this.$emit('update:stackable', value);
|
||||||
},
|
},
|
||||||
updateMixable(value) {
|
updateMixable(value) {
|
||||||
|
|
||||||
|
if(value)
|
||||||
|
this.updateStackable(true);
|
||||||
|
|
||||||
this.$emit('update:mixable', value);
|
this.$emit('update:mixable', value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
9
src/frontend/src/logger.js
Normal file
9
src/frontend/src/logger.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import log from 'loglevel'
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
log.setLevel('silent')
|
||||||
|
} else {
|
||||||
|
log.setLevel('debug')
|
||||||
|
}
|
||||||
|
|
||||||
|
export default log
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="edit-calculation-container">
|
<div class="edit-calculation-container" :class="{ 'has-selection': hasSelection }">
|
||||||
<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">
|
||||||
|
|
@ -43,8 +43,8 @@
|
||||||
<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="id in this.premiseEditStore.getPremiseIds"
|
<bulk-edit-row v-else class="edit-calculation-list-item" v-for="premise of this.premiseEditStore.getPremisses"
|
||||||
:key="id" :id="id" @action="onClickAction" :copy-mode="selectCount !== 0">
|
:key="premise.id" :id="premise.id" :premise="premise" @action="onClickAction">
|
||||||
</bulk-edit-row>
|
</bulk-edit-row>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -118,7 +118,6 @@ 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 SupplierView from "@/components/layout/edit/SupplierView.vue";
|
|
||||||
import DestinationListView from "@/components/layout/edit/DestinationListView.vue";
|
import DestinationListView from "@/components/layout/edit/DestinationListView.vue";
|
||||||
import SelectNode from "@/components/layout/node/SelectNode.vue";
|
import SelectNode from "@/components/layout/node/SelectNode.vue";
|
||||||
|
|
||||||
|
|
@ -136,6 +135,12 @@ export default {
|
||||||
components: {Modal, MassEditDialog, ListEdit, Spinner, CalculationListItem, Checkbox, BulkEditRow, BasicButton},
|
components: {Modal, MassEditDialog, ListEdit, Spinner, CalculationListItem, Checkbox, BulkEditRow, BasicButton},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(usePremiseEditStore),
|
...mapStores(usePremiseEditStore),
|
||||||
|
hasSelection() {
|
||||||
|
if (this.premiseEditStore.isLoading || this.premiseEditStore.selectedLoading) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.premiseEditStore.getSelectedPremissesIds?.length > 0;
|
||||||
|
},
|
||||||
selectCount() {
|
selectCount() {
|
||||||
return this.selectedPremisses?.length ?? 0;
|
return this.selectedPremisses?.length ?? 0;
|
||||||
},
|
},
|
||||||
|
|
@ -179,7 +184,7 @@ export default {
|
||||||
created() {
|
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);
|
this.premiseEditStore.loadPremissesIfNeeded(this.ids);
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -199,7 +204,7 @@ export default {
|
||||||
dimensionUnit: "MM",
|
dimensionUnit: "MM",
|
||||||
unitCount: 1,
|
unitCount: 1,
|
||||||
mixable: true,
|
mixable: true,
|
||||||
stackable: false
|
stackable: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
supplier: {
|
supplier: {
|
||||||
|
|
@ -223,6 +228,7 @@ export default {
|
||||||
this.$router.push({name: "calculation-list"});
|
this.$router.push({name: "calculation-list"});
|
||||||
},
|
},
|
||||||
async updateSupplier(data) {
|
async updateSupplier(data) {
|
||||||
|
console.log("update supplier", data.nodeId, data.action, data.updateMasterData, this.editIds);
|
||||||
console.log("update supplier", data.nodeId, data.action, data.updateMasterData, this.editIds);
|
console.log("update supplier", data.nodeId, data.action, data.updateMasterData, this.editIds);
|
||||||
this.modalType = null;
|
this.modalType = null;
|
||||||
if (data.action === 'accept') {
|
if (data.action === 'accept') {
|
||||||
|
|
@ -240,7 +246,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateCheckBoxes(value) {
|
updateCheckBoxes(value) {
|
||||||
this.premiseEditStore.setSelectTo(this.ids, value);
|
this.premiseEditStore.setAll(value);
|
||||||
},
|
},
|
||||||
multiselectAction(action) {
|
multiselectAction(action) {
|
||||||
this.openModal(action, this.selectedPremisses.map(p => p.id));
|
this.openModal(action, this.selectedPremisses.map(p => p.id));
|
||||||
|
|
@ -264,63 +270,30 @@ export default {
|
||||||
console.log("open modal", massEdit, this.modalType, this.editIds, this.dataSourceId)
|
console.log("open modal", massEdit, this.modalType, this.editIds, this.dataSourceId)
|
||||||
|
|
||||||
},
|
},
|
||||||
closeEditModalAction(action) {
|
async closeEditModalAction(action) {
|
||||||
|
|
||||||
if (this.modalType === "destinations") {
|
if (this.modalType === "destinations") {
|
||||||
if (action === "accept") {
|
if (action === "accept") {
|
||||||
this.premiseEditStore.executeDestinationsMassEdit();
|
await this.premiseEditStore.executeDestinationsMassEdit();
|
||||||
} else {
|
} else {
|
||||||
this.premiseEditStore.cancelMassEdit();
|
this.premiseEditStore.cancelMassEdit();
|
||||||
}
|
}
|
||||||
} else {
|
} else if (action === "accept") {
|
||||||
if (action === "accept") {
|
const props = this.componentsData[this.modalType].props;
|
||||||
|
|
||||||
if (this.modalType === "price") {
|
switch(this.modalType) {
|
||||||
this.editIds.forEach(id => {
|
case "price":
|
||||||
const p = this.premiseEditStore.getById(id);
|
await this.premiseEditStore.batchUpdatePrice(this.editIds, props);
|
||||||
p.material_cost = this.componentsData[this.modalType].props.price;
|
break;
|
||||||
p.oversea_share = this.componentsData[this.modalType].props.overSeaShare;
|
case "material":
|
||||||
p.is_fca_enabled = this.componentsData[this.modalType].props.includeFcaFee;
|
await this.premiseEditStore.batchUpdateMaterial(this.editIds, props);
|
||||||
});
|
break;
|
||||||
|
case "packaging":
|
||||||
this.premiseEditStore.savePrice(this.editIds);
|
await this.premiseEditStore.batchUpdatePackaging(this.editIds, props);
|
||||||
|
break;
|
||||||
} else if (this.modalType === "material") {
|
|
||||||
|
|
||||||
this.editIds.forEach(id => {
|
|
||||||
const p = this.premiseEditStore.getById(id);
|
|
||||||
p.material.part_number = this.componentsData[this.modalType].props.partNumber;
|
|
||||||
p.material.hs_code = this.componentsData[this.modalType].props.hsCode;
|
|
||||||
p.tariff_rate = this.componentsData[this.modalType].props.tariffRate;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.premiseEditStore.saveMaterial(this.editIds);
|
|
||||||
|
|
||||||
|
|
||||||
} else if (this.modalType === "packaging") {
|
|
||||||
|
|
||||||
this.editIds.forEach(id => {
|
|
||||||
const p = this.premiseEditStore.getById(id);
|
|
||||||
p.handling_unit.weight = this.componentsData[this.modalType].props.weight;
|
|
||||||
p.handling_unit.width = this.componentsData[this.modalType].props.width;
|
|
||||||
p.handling_unit.length = this.componentsData[this.modalType].props.length;
|
|
||||||
p.handling_unit.height = this.componentsData[this.modalType].props.height;
|
|
||||||
|
|
||||||
p.handling_unit.weight_unit = this.componentsData[this.modalType].props.weightUnit;
|
|
||||||
p.handling_unit.dimension_unit = this.componentsData[this.modalType].props.dimensionUnit;
|
|
||||||
p.handling_unit.content_unit_count = this.componentsData[this.modalType].props.unitCount;
|
|
||||||
|
|
||||||
p.is_stackable = this.componentsData[this.modalType].props.stackable;
|
|
||||||
p.is_mixable = this.componentsData[this.modalType].props.mixable;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.premiseEditStore.savePackaging(this.editIds);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear data.
|
// Clear data
|
||||||
this.fillData(this.modalType);
|
this.fillData(this.modalType);
|
||||||
this.modalType = null;
|
this.modalType = null;
|
||||||
},
|
},
|
||||||
|
|
@ -341,7 +314,7 @@ export default {
|
||||||
dimensionUnit: "MM",
|
dimensionUnit: "MM",
|
||||||
unitCount: 1,
|
unitCount: 1,
|
||||||
mixable: true,
|
mixable: true,
|
||||||
stackable: false
|
stackable: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
supplier: {
|
supplier: {
|
||||||
|
|
@ -394,9 +367,16 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
|
/* Global style für copy-mode cursor */
|
||||||
|
.edit-calculation-container.has-selection :deep(.copyable-cell:hover) {
|
||||||
|
cursor: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOCIgaGVpZ2h0PSIyOCIgdmlld0JveD0iMCAwIDI1NiAyNTYiPgogIDxyZWN0IHg9Ijg0IiB5PSIzMiIgd2lkdGg9IjEzNiIgaGVpZ2h0PSIxMzYiIGZpbGw9IndoaXRlIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iOCIgcng9IjQiLz4KICA8cmVjdCB4PSIzNiIgeT0iODQiIHdpZHRoPSIxMzYiIGhlaWdodD0iMTM2IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjgiIHJ4PSI0Ii8+Cjwvc3ZnPg==") 12 12, pointer;
|
||||||
|
background-color: rgba(107, 134, 156, 0.05);
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
box-shadow: 0 0.4rem 0.6rem -0.1rem rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.space-around {
|
.space-around {
|
||||||
margin: 3rem;
|
margin: 3rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,7 @@ export default {
|
||||||
close() {
|
close() {
|
||||||
if(this.bulkEditQuery) {
|
if(this.bulkEditQuery) {
|
||||||
//TODO: deselect and save
|
//TODO: deselect and save
|
||||||
|
this.premiseEditStore.deselectPremise();
|
||||||
this.$router.push({name: 'bulk', params: {ids: this.bulkEditQuery}});
|
this.$router.push({name: 'bulk', params: {ids: this.bulkEditQuery}});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ export const useCountryStore = defineStore('country', {
|
||||||
state() {
|
state() {
|
||||||
return {
|
return {
|
||||||
countries: null,
|
countries: null,
|
||||||
properties: null,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
query: null,
|
query: null,
|
||||||
selectedCountryId: null,
|
selectedCountryId: null,
|
||||||
|
|
@ -27,18 +26,21 @@ export const useCountryStore = defineStore('country', {
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async setProperty(property) {
|
async setProperty(property) {
|
||||||
if(this.properties === null) return;
|
if(this.getSelectedCountry === null) return;
|
||||||
|
|
||||||
const prop = this.properties.find(p => p.external_mapping_id === property.id);
|
const prop = this.getSelectedCountry.properties.find(p => p.external_mapping_id === property.id);
|
||||||
|
|
||||||
if((prop ?? null) === null) return;
|
if((prop ?? null) === null) return;
|
||||||
|
|
||||||
const url = `${config.backendUrl}/properties/country/${property.country.iso_code}/${property.id}`;
|
const url = `${config.backendUrl}/properties/country/${this.getSelectedCountry.iso_code}/${property.id}`;
|
||||||
const body = { value: String(property.value)};
|
const body = { value: String(property.value)};
|
||||||
|
|
||||||
await this.performRequest('PUT', url, body, false);
|
await this.performRequest('PUT', url, body, false);
|
||||||
|
|
||||||
prop.draft_value = property.reset ? null : property.value;
|
prop.draft_value = property.reset ? null : property.value;
|
||||||
|
|
||||||
|
const stage = useStageStore();
|
||||||
|
await stage.checkStagedChanges();
|
||||||
},
|
},
|
||||||
async selectPeriod(periodId) {
|
async selectPeriod(periodId) {
|
||||||
this.selectedPeriodId = periodId;
|
this.selectedPeriodId = periodId;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import {defineStore} from 'pinia'
|
||||||
import {config} from '@/config'
|
import {config} from '@/config'
|
||||||
import {toRaw} from "vue";
|
import {toRaw} from "vue";
|
||||||
import {useErrorStore} from "@/store/error.js";
|
import {useErrorStore} from "@/store/error.js";
|
||||||
|
import logger from "@/logger.js"
|
||||||
|
|
||||||
export const usePremiseEditStore = defineStore('premiseEdit', {
|
export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
state() {
|
state() {
|
||||||
|
|
@ -29,6 +30,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ids of all premises.
|
* Returns the ids of all premises.
|
||||||
* @param state
|
* @param state
|
||||||
|
|
@ -247,7 +249,66 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
|
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
async batchUpdatePrice(ids, priceData) {
|
||||||
|
const updatedPremises = this.premisses.map(p => {
|
||||||
|
if (ids.includes(p.id)) {
|
||||||
|
return {
|
||||||
|
...p,
|
||||||
|
material_cost: priceData.price,
|
||||||
|
oversea_share: priceData.overSeaShare,
|
||||||
|
is_fca_enabled: priceData.includeFcaFee
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
this.premisses = updatedPremises;
|
||||||
|
|
||||||
|
return await this.savePrice(ids);
|
||||||
|
},
|
||||||
|
async batchUpdateMaterial(ids, materialData) {
|
||||||
|
const updatedPremises = this.premisses.map(p => {
|
||||||
|
if (ids.includes(p.id)) {
|
||||||
|
return {
|
||||||
|
...p,
|
||||||
|
material: {
|
||||||
|
...p.material,
|
||||||
|
part_number: materialData.partNumber,
|
||||||
|
hs_code: materialData.hsCode
|
||||||
|
},
|
||||||
|
tariff_rate: materialData.tariffRate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
this.premisses = updatedPremises;
|
||||||
|
|
||||||
|
return await this.saveMaterial(ids);
|
||||||
|
},
|
||||||
|
async batchUpdatePackaging(ids, packagingData) {
|
||||||
|
const updatedPremises = this.premisses.map(p => {
|
||||||
|
if (ids.includes(p.id)) {
|
||||||
|
return {
|
||||||
|
...p,
|
||||||
|
handling_unit: {
|
||||||
|
...p.handling_unit,
|
||||||
|
weight: packagingData.weight,
|
||||||
|
width: packagingData.width,
|
||||||
|
length: packagingData.length,
|
||||||
|
height: packagingData.height,
|
||||||
|
weight_unit: packagingData.weightUnit,
|
||||||
|
dimension_unit: packagingData.dimensionUnit,
|
||||||
|
content_unit_count: packagingData.unitCount
|
||||||
|
},
|
||||||
|
is_stackable: packagingData.stackable,
|
||||||
|
is_mixable: packagingData.mixable
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
this.premisses = updatedPremises;
|
||||||
|
|
||||||
|
return await this.savePackaging(ids);
|
||||||
|
},
|
||||||
async startCalculation() {
|
async startCalculation() {
|
||||||
|
|
||||||
const body = this.premisses.map(p => p.id);
|
const body = this.premisses.map(p => p.id);
|
||||||
|
|
@ -406,7 +467,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
selectDestination(id) {
|
selectDestination(id) {
|
||||||
if (this.premisses === null) return;
|
if (this.premisses === null) return;
|
||||||
|
|
||||||
console.log("selectDestination:", id)
|
logger.info("selectDestination:", id)
|
||||||
|
|
||||||
const dest = this.destinations.destinations.find(d => d.id === id);
|
const dest = this.destinations.destinations.find(d => d.id === id);
|
||||||
|
|
||||||
|
|
@ -446,7 +507,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
route_selected_id: toDest.routes.find(r => r.is_selected)?.id ?? null,
|
route_selected_id: toDest.routes.find(r => r.is_selected)?.id ?? null,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(body)
|
logger.info(body)
|
||||||
|
|
||||||
const url = `${config.backendUrl}/calculation/destination/${toDest.id}`;
|
const url = `${config.backendUrl}/calculation/destination/${toDest.id}`;
|
||||||
await this.performRequest('PUT', url, body, false);
|
await this.performRequest('PUT', url, body, false);
|
||||||
|
|
@ -483,15 +544,15 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
|
|
||||||
const url = `${config.backendUrl}/calculation/destination/${origId}`;
|
const url = `${config.backendUrl}/calculation/destination/${origId}`;
|
||||||
await this.performRequest('DELETE', url, null, false).catch(async e => {
|
await this.performRequest('DELETE', url, null, false).catch(async e => {
|
||||||
console.error("Unable to delete destination: " + origId + "");
|
logger.error("Unable to delete destination: " + origId + "");
|
||||||
console.error(e);
|
logger.error(e);
|
||||||
await this.loadPremissesIfNeeded(this.premisses.map(p => p.id));
|
await this.loadPremissesIfNeeded(this.premisses.map(p => p.id));
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const p of this.premisses) {
|
for (const p of this.premisses) {
|
||||||
const toBeDeleted = p.destinations.findIndex(d => String(d.id) === String(origId))
|
const toBeDeleted = p.destinations.findIndex(d => String(d.id) === String(origId))
|
||||||
|
|
||||||
console.log(toBeDeleted)
|
logger.info(toBeDeleted)
|
||||||
|
|
||||||
if (toBeDeleted !== -1) {
|
if (toBeDeleted !== -1) {
|
||||||
p.destinations.splice(toBeDeleted, 1)
|
p.destinations.splice(toBeDeleted, 1)
|
||||||
|
|
@ -505,7 +566,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
if (this.destinations.massEdit) {
|
if (this.destinations.massEdit) {
|
||||||
|
|
||||||
const existing = this.destinations.destinations.find(d => d.destination_node.id === node.id);
|
const existing = this.destinations.destinations.find(d => d.destination_node.id === node.id);
|
||||||
console.log(existing)
|
logger.info(existing)
|
||||||
|
|
||||||
if ((existing ?? null) !== null) {
|
if ((existing ?? null) !== null) {
|
||||||
console.info("Destination already exists", node.id);
|
console.info("Destination already exists", node.id);
|
||||||
|
|
@ -568,7 +629,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async setSupplier(id, updateMasterData, ids = null) {
|
async setSupplier(id, updateMasterData, ids = null) {
|
||||||
console.log("setSupplier");
|
logger.info("setSupplier");
|
||||||
|
|
||||||
const selectedId = this.singleSelectId;
|
const selectedId = this.singleSelectId;
|
||||||
|
|
||||||
|
|
@ -586,7 +647,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
|
|
||||||
},
|
},
|
||||||
async setMaterial(id, updateMasterData, ids = null) {
|
async setMaterial(id, updateMasterData, ids = null) {
|
||||||
console.log("setMaterial");
|
logger.info("setMaterial");
|
||||||
const body = {material_id: id, update_master_data: updateMasterData};
|
const body = {material_id: id, update_master_data: updateMasterData};
|
||||||
const url = `${config.backendUrl}/calculation/material/`;
|
const url = `${config.backendUrl}/calculation/material/`;
|
||||||
await this.setData(url, body, ids);
|
await this.setData(url, body, ids);
|
||||||
|
|
@ -600,7 +661,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
body.premise_id = toBeUpdated;
|
body.premise_id = toBeUpdated;
|
||||||
console.log(url, body)
|
logger.info(url, body)
|
||||||
|
|
||||||
const data = await this.performRequest('PUT', url, body).catch(e => {
|
const data = await this.performRequest('PUT', url, body).catch(e => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|
@ -626,7 +687,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
replacePremissesById(premisses, loadedData) {
|
replacePremissesById(premisses, loadedData) {
|
||||||
const replacementMap = new Map(loadedData.map(obj => [obj.id, obj]));
|
const replacementMap = new Map(loadedData.map(obj => [obj.id, obj]));
|
||||||
const replaced = premisses.map(obj => replacementMap.get(obj.id) || obj);
|
const replaced = premisses.map(obj => replacementMap.get(obj.id) || obj);
|
||||||
console.log("Replaced", replaced);
|
logger.info("Replaced", replaced);
|
||||||
return replaced;
|
return replaced;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -660,7 +721,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
|
|
||||||
if (!toBeUpdated?.length) return;
|
if (!toBeUpdated?.length) return;
|
||||||
|
|
||||||
console.log(toBeUpdated[0]);
|
logger.info(toBeUpdated[0]);
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
premise_ids: toBeUpdated.map(p => p.id),
|
premise_ids: toBeUpdated.map(p => p.id),
|
||||||
|
|
@ -726,10 +787,28 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
this.selectedDestination = null;
|
this.selectedDestination = null;
|
||||||
this.selectedLoading = false;
|
this.selectedLoading = false;
|
||||||
},
|
},
|
||||||
|
setAll(value) {
|
||||||
|
|
||||||
|
this.selectedLoading = true;
|
||||||
|
|
||||||
|
const updatedPremises = this.premisses.map(p => ({
|
||||||
|
...p,
|
||||||
|
selected: value
|
||||||
|
}));
|
||||||
|
this.premisses = updatedPremises;
|
||||||
|
|
||||||
|
this.selectedLoading = false;
|
||||||
|
|
||||||
|
},
|
||||||
setSelectTo(ids, value) {
|
setSelectTo(ids, value) {
|
||||||
this.selectedLoading = true;
|
this.selectedLoading = true;
|
||||||
|
|
||||||
this.premisses.forEach(p => p.selected = ids.includes(p.id) ? value : p.selected);
|
const idsSet = new Set(ids);
|
||||||
|
const updatedPremises = this.premisses.map(p => ({
|
||||||
|
...p,
|
||||||
|
selected: idsSet.has(p.id) ? value : p.selected
|
||||||
|
}));
|
||||||
|
this.premisses = updatedPremises;
|
||||||
|
|
||||||
this.selectedLoading = false;
|
this.selectedLoading = false;
|
||||||
},
|
},
|
||||||
|
|
@ -773,7 +852,6 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
},
|
},
|
||||||
async loadPremissesForced(ids) {
|
async loadPremissesForced(ids) {
|
||||||
|
|
||||||
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.premises = [];
|
this.premises = [];
|
||||||
|
|
||||||
|
|
@ -808,7 +886,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
}
|
}
|
||||||
|
|
||||||
const request = {url: url, params: params};
|
const request = {url: url, params: params};
|
||||||
console.log("Request:", request);
|
logger.info("Request:", request);
|
||||||
|
|
||||||
const response = await fetch(url, params
|
const response = await fetch(url, params
|
||||||
).catch(e => {
|
).catch(e => {
|
||||||
|
|
@ -818,7 +896,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
trace: null
|
trace: null
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(error);
|
logger.error(error);
|
||||||
const errorStore = useErrorStore();
|
const errorStore = useErrorStore();
|
||||||
void errorStore.addError(error, {store: this, request: request});
|
void errorStore.addError(error, {store: this, request: request});
|
||||||
|
|
||||||
|
|
@ -834,7 +912,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
trace: null
|
trace: null
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(error);
|
logger.error(error);
|
||||||
const errorStore = useErrorStore();
|
const errorStore = useErrorStore();
|
||||||
void errorStore.addError(error, {store: this, request: request});
|
void errorStore.addError(error, {store: this, request: request});
|
||||||
throw e;
|
throw e;
|
||||||
|
|
@ -848,7 +926,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
trace: data.error.trace
|
trace: data.error.trace
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(error);
|
logger.error(error);
|
||||||
const errorStore = useErrorStore();
|
const errorStore = useErrorStore();
|
||||||
void errorStore.addError(error, {store: this, request: request});
|
void errorStore.addError(error, {store: this, request: request});
|
||||||
throw new Error('Internal backend error');
|
throw new Error('Internal backend error');
|
||||||
|
|
@ -862,7 +940,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
message: "Server returned wrong response code",
|
message: "Server returned wrong response code",
|
||||||
trace: null
|
trace: null
|
||||||
}
|
}
|
||||||
console.error(error);
|
logger.error(error);
|
||||||
const errorStore = useErrorStore();
|
const errorStore = useErrorStore();
|
||||||
void errorStore.addError(error, {store: this, request: request});
|
void errorStore.addError(error, {store: this, request: request});
|
||||||
throw new Error('Internal backend error');
|
throw new Error('Internal backend error');
|
||||||
|
|
@ -876,7 +954,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
trace: data.error.trace
|
trace: data.error.trace
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(error);
|
logger.error(error);
|
||||||
const errorStore = useErrorStore();
|
const errorStore = useErrorStore();
|
||||||
void errorStore.addError(error, {store: this, request: request});
|
void errorStore.addError(error, {store: this, request: request});
|
||||||
throw new Error('Internal backend error');
|
throw new Error('Internal backend error');
|
||||||
|
|
@ -885,7 +963,7 @@ export const usePremiseEditStore = defineStore('premiseEdit', {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Response:", data);
|
logger.info("Response:", data);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,12 +86,12 @@ public class PropertyController {
|
||||||
*
|
*
|
||||||
* @param isoCode The ISO code of the country.
|
* @param isoCode The ISO code of the country.
|
||||||
* @param mappingId The external mapping ID for the property.
|
* @param mappingId The external mapping ID for the property.
|
||||||
* @param value The value to set for the property.
|
* @param dto The value to set for the property.
|
||||||
* @return ResponseEntity indicating the operation status.
|
* @return ResponseEntity indicating the operation status.
|
||||||
*/
|
*/
|
||||||
@PutMapping({"/country/{iso}/{external_mapping_id}", "/country/{iso}/{external_mapping_id}/"})
|
@PutMapping({"/country/{iso}/{external_mapping_id}", "/country/{iso}/{external_mapping_id}/"})
|
||||||
public ResponseEntity<Void> setCountryProperty(@PathVariable("iso") IsoCode isoCode, @PathVariable(name = "external_mapping_id") String mappingId, @RequestBody String value) {
|
public ResponseEntity<Void> setCountryProperty(@PathVariable("iso") IsoCode isoCode, @PathVariable(name = "external_mapping_id") String mappingId, @RequestBody SetPropertyDTO dto) {
|
||||||
countryService.setProperties(isoCode, mappingId, value);
|
countryService.setProperties(isoCode, mappingId, dto.getValue());
|
||||||
return ResponseEntity.ok().build();
|
return ResponseEntity.ok().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package de.avatic.lcc.repositories.country;
|
||||||
|
|
||||||
import de.avatic.lcc.dto.generic.PropertyDTO;
|
import de.avatic.lcc.dto.generic.PropertyDTO;
|
||||||
import de.avatic.lcc.model.properties.CountryPropertyMappingId;
|
import de.avatic.lcc.model.properties.CountryPropertyMappingId;
|
||||||
|
import de.avatic.lcc.model.rates.ValidityPeriodState;
|
||||||
import de.avatic.lcc.util.exception.internalerror.DatabaseException;
|
import de.avatic.lcc.util.exception.internalerror.DatabaseException;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
|
@ -28,6 +29,21 @@ public class CountryPropertyRepository {
|
||||||
public void setProperty(Integer setId, Integer countryId, String mappingId, String value) {
|
public void setProperty(Integer setId, Integer countryId, String mappingId, String value) {
|
||||||
Integer typeId = getTypeIdByMappingId(mappingId);
|
Integer typeId = getTypeIdByMappingId(mappingId);
|
||||||
|
|
||||||
|
String validValueQuery = """
|
||||||
|
SELECT cp.property_value
|
||||||
|
FROM country_property cp
|
||||||
|
JOIN property_set ps ON ps.id = cp.property_set_id
|
||||||
|
WHERE ps.state = ? AND cp.country_property_type_id = ? AND cp.country_id = ?""";
|
||||||
|
|
||||||
|
String validValue = jdbcTemplate.queryForObject(validValueQuery, String.class,
|
||||||
|
ValidityPeriodState.VALID.name(), typeId, countryId);
|
||||||
|
|
||||||
|
if (value.equals(validValue)) {
|
||||||
|
String deleteQuery = "DELETE FROM country_property WHERE property_set_id = ? AND country_property_type_id = ? AND country_id = ?";
|
||||||
|
jdbcTemplate.update(deleteQuery, setId, typeId, countryId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String query = """
|
String query = """
|
||||||
INSERT INTO country_property (property_value, country_id, country_property_type_id, property_set_id) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE property_value = ?
|
INSERT INTO country_property (property_value, country_id, country_property_type_id, property_set_id) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE property_value = ?
|
||||||
""";
|
""";
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,9 @@ public class CalculationExecutionService {
|
||||||
// Get container calculation
|
// Get container calculation
|
||||||
for (var containerType : ContainerType.values()) {
|
for (var containerType : ContainerType.values()) {
|
||||||
containerCalculation.put(containerType, containerCalculationService.doCalculation(hu, containerType));
|
containerCalculation.put(containerType, containerCalculationService.doCalculation(hu, containerType));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var containerType : ContainerType.values()) {
|
||||||
if (!containerType.equals(ContainerType.TRUCK)) {
|
if (!containerType.equals(ContainerType.TRUCK)) {
|
||||||
|
|
||||||
var sectionInfo = new ArrayList<SectionInfo>();
|
var sectionInfo = new ArrayList<SectionInfo>();
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,6 @@ INSERT INTO system_property_type ( name, external_mapping_id, data_type, validat
|
||||||
|
|
||||||
INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Payment terms [days]', 'PAYMENT_TERMS', 'INT', '{}');
|
INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Payment terms [days]', 'PAYMENT_TERMS', 'INT', '{}');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Annual working days', 'WORKDAYS', 'INT', '{"GT": 0, "LT": 366}');
|
INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Annual working days', 'WORKDAYS', 'INT', '{"GT": 0, "LT": 366}');
|
||||||
INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Interest rate inventory [%]', 'INTEREST_RATE', 'PERCENTAGE', '{"GTE": 0}');
|
INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'Interest rate inventory [%]', 'INTEREST_RATE', 'PERCENTAGE', '{"GTE": 0}');
|
||||||
INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'FCA fee [%]', 'FCA_FEE', 'PERCENTAGE', '{"GTE": 0}');
|
INSERT INTO system_property_type ( name, external_mapping_id, data_type, validation_rule) VALUES ( 'FCA fee [%]', 'FCA_FEE', 'PERCENTAGE', '{"GTE": 0}');
|
||||||
|
|
@ -325,6 +323,18 @@ VALUES (
|
||||||
(SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'FEU_LOAD'),
|
(SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'FEU_LOAD'),
|
||||||
'21000'
|
'21000'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
INSERT INTO system_property (property_set_id, system_property_type_id, property_value)
|
||||||
|
VALUES (
|
||||||
|
(SELECT ps.id FROM `property_set` ps
|
||||||
|
WHERE ps.state = 'VALID'
|
||||||
|
AND ps.start_date <= NOW()
|
||||||
|
AND (ps.end_date IS NULL OR ps.end_date > NOW())
|
||||||
|
ORDER BY ps.start_date DESC
|
||||||
|
LIMIT 1),
|
||||||
|
(SELECT spt.id FROM system_property_type spt WHERE spt.external_mapping_id = 'TRUCK_LOAD'),
|
||||||
|
'25000'
|
||||||
|
);
|
||||||
|
|
||||||
INSERT INTO system_property (property_set_id, system_property_type_id, property_value)
|
INSERT INTO system_property (property_set_id, system_property_type_id, property_value)
|
||||||
VALUES (
|
VALUES (
|
||||||
|
|
|
||||||
|
|
@ -560,8 +560,7 @@ CREATE TABLE IF NOT EXISTS calculation_job_route_section
|
||||||
FOREIGN KEY (calculation_job_destination_id) REFERENCES calculation_job_destination (id),
|
FOREIGN KEY (calculation_job_destination_id) REFERENCES calculation_job_destination (id),
|
||||||
INDEX idx_premise_route_section_id (premise_route_section_id),
|
INDEX idx_premise_route_section_id (premise_route_section_id),
|
||||||
INDEX idx_calculation_job_destination_id (calculation_job_destination_id),
|
INDEX idx_calculation_job_destination_id (calculation_job_destination_id),
|
||||||
CONSTRAINT chk_stacked CHECK (is_unmixed_price IS TRUE OR is_stacked IS TRUE), -- only unmixed transports can be unstacked
|
CONSTRAINT chk_stacked CHECK (is_unmixed_price IS FALSE OR is_stacked IS TRUE) -- only unmixed transports can be unstacked
|
||||||
CONSTRAINT chk_cbm_weight_price CHECK (is_unmixed_price IS FALSE OR
|
|
||||||
(is_cbm_price IS FALSE AND is_weight_price IS FALSE))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue