Renamed error store to notification store, centralized spinner modal

Increased thread pool capacities in `AsyncConfig.java` and adjusted database schema for larger text fields. Enhanced HS code validation logic and added logging to `EUTaxationApiService` for improved traceability. Cleaned up unused error modal code and aligned styles for calculation processing spinners.
This commit is contained in:
Jan 2025-11-16 23:02:11 +01:00
parent 5adadb671e
commit cd66b5bba5
30 changed files with 173 additions and 132 deletions

View file

@ -1,5 +1,5 @@
import logger from "@/logger.js"; import logger from "@/logger.js";
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import {config} from "@/config"; import {config} from "@/config";
const getCsrfToken = () => { const getCsrfToken = () => {
@ -141,7 +141,7 @@ function handleErrorResponse(data, requestingStore, request) {
const error = new Error('Internal backend error'); const error = new Error('Internal backend error');
error.errorObj = errorObj; error.errorObj = errorObj;
const errorStore = useErrorStore(); const errorStore = useNotificationStore();
if (request.expectedException === null || (Array.isArray(request.expectResponse) && !request.expectedException.includes(data.error.title)) || (typeof request.expectedException === 'string' && data.error.title !== request.expectedException)) { if (request.expectedException === null || (Array.isArray(request.expectResponse) && !request.expectedException.includes(data.error.title)) || (typeof request.expectedException === 'string' && data.error.title !== request.expectedException)) {
logger.error(errorObj, request.expectedException); logger.error(errorObj, request.expectedException);
@ -170,7 +170,7 @@ const executeRequest = async (requestingStore, request) => {
} }
logger.error(error, e); logger.error(error, e);
const errorStore = useErrorStore(); const errorStore = useNotificationStore();
void errorStore.addError(error, {store: requestingStore, request: request}); void errorStore.addError(error, {store: requestingStore, request: request});
throw e; throw e;
@ -199,7 +199,7 @@ const executeRequest = async (requestingStore, request) => {
} }
logger.error(error); logger.error(error);
const errorStore = useErrorStore(); const errorStore = useNotificationStore();
void errorStore.addError(error, {store: requestingStore, request: request}); void errorStore.addError(error, {store: requestingStore, request: request});
throw e; throw e;
} }
@ -217,7 +217,7 @@ const executeRequest = async (requestingStore, request) => {
trace: null trace: null
} }
logger.error(error); logger.error(error);
const errorStore = useErrorStore(); const errorStore = useNotificationStore();
void errorStore.addError(error, {store: requestingStore, request: request}); void errorStore.addError(error, {store: requestingStore, request: request});
throw new Error('Internal backend error'); throw new Error('Internal backend error');

View file

@ -1,54 +1,47 @@
<template> <template>
<teleport to="body"> <teleport to="body">
<toast ref="toast"></toast> <toast ref="toast"></toast>
<!-- <transition-->
<!-- name="error-container"-->
<!-- tag="div"--> <modal :z-index="9000" :state="hasSpinner">
<!-- class="error-notification-container">--> <div class="spinner-box space-around">
<!-- <div class="error-notification" v-if="error">--> <spinner></spinner>
<!-- <div>--> <span>{{ spinnerMsg }}</span>
<!-- <ph-warning size="24"></ph-warning>--> </div>
<!-- </div>--> </modal>
<!-- <div class="error-message">-->
<!-- <div class="error-message-title">-->
<!-- {{ title }}-->
<!-- </div>-->
<!-- <div class="error-message-content">-->
<!-- {{ message }}-->
<!-- </div>-->
<!-- <div class="error-view-trace" v-if="trace" @click="activateTrace">-->
<!-- View trace-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="icon-error-notification">-->
<!-- <ph-x size="24" @click="close"></ph-x>-->
<!-- </div>-->
<!-- <modal :z-index="9001" :state="showTrace"><trace-view :error="error" @close="deactivateTrace"></trace-view></modal>-->
<!-- </div>-->
<!-- </transition>-->
</teleport> </teleport>
</template> </template>
<script> <script>
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import {mapStores} from "pinia"; import {mapStores} from "pinia";
import Toast from "@/components/UI/Toast.vue"; import Toast from "@/components/UI/Toast.vue";
import logger from "@/logger.js"; import logger from "@/logger.js";
import Box from "@/components/UI/Box.vue";
import Spinner from "@/components/UI/Spinner.vue";
import Modal from "@/components/UI/Modal.vue";
export default { export default {
name: "TheNotificationSystem", name: "TheNotificationSystem",
components: {Toast}, components: {Modal, Spinner, Box, Toast},
data() { data() {
return {} return {}
}, },
computed: { computed: {
...mapStores(useErrorStore), ...mapStores(useNotificationStore),
hasSpinner() {
return this.notificationStore.hasSpinner;
},
spinnerMsg() {
return this.notificationStore.spinnerMsg;
},
notifications() { notifications() {
return this.errorStore.notifications; return this.notificationStore.notifications;
}, },
hasNotifications() { hasNotifications() {
return this.errorStore.notifications?.length !== 0; return this.notificationStore.notifications?.length !== 0;
} }
}, },
watch: { watch: {
@ -60,7 +53,7 @@ export default {
methods: { methods: {
fireToasts() { fireToasts() {
while (this.hasNotifications) { while (this.hasNotifications) {
const msg = this.errorStore.popNotification(); const msg = this.notificationStore.popNotification();
logger.log("fire msg", msg); logger.log("fire msg", msg);
@ -170,4 +163,18 @@ export default {
transition: transform 0.3s ease; transition: transform 0.3s ease;
} }
.spinner-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 3.6rem;
flex: 1 1 auto;
font-size: 1.6rem;
}
.space-around {
margin: 3rem;
}
</style> </style>

View file

@ -182,9 +182,11 @@ export default {
hsCodeChanged(event) { hsCodeChanged(event) {
let sanitized = event.target.value let sanitized = event.target.value
.replace(/\D/g, '') .replace(/\D/g, '')
.substring(0, 10); .substring(0, 10)
if(sanitized.length !== 10) if(sanitized.length !== 0)
sanitized = sanitized.padEnd(10, '0');
else
sanitized = null; sanitized = null;
this.$emit("update:hsCode", sanitized); this.$emit("update:hsCode", sanitized);

View file

@ -88,7 +88,7 @@ import {buildDate} from "@/common.js";
import BasicButton from "@/components/UI/BasicButton.vue"; import BasicButton from "@/components/UI/BasicButton.vue";
import logger from "@/logger.js"; import logger from "@/logger.js";
import {mapStores} from "pinia"; import {mapStores} from "pinia";
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
export default { export default {
name: "ErrorModalOverview", name: "ErrorModalOverview",
@ -108,7 +108,7 @@ export default {
try { try {
await navigator.clipboard.writeText(JSON.stringify(this.error)); await navigator.clipboard.writeText(JSON.stringify(this.error));
this.errorStore.addNotification({ this.notificationStore.addNotification({
icon: 'Clipboard', icon: 'Clipboard',
message: "The error has been copied to clipboard", message: "The error has been copied to clipboard",
title: "Successful copied to clipboard", title: "Successful copied to clipboard",
@ -120,7 +120,7 @@ export default {
} catch (err) { } catch (err) {
logger.error('Fehler beim Kopieren:', err); logger.error('Fehler beim Kopieren:', err);
this.errorStore.addNotification({ this.notificationStore.addNotification({
icon: 'Clipboard', icon: 'Clipboard',
message: "Cannot copy to clipboard", message: "Cannot copy to clipboard",
title: "Not copied to clipboard", title: "Not copied to clipboard",
@ -132,7 +132,7 @@ export default {
} }
}, },
computed: { computed: {
...mapStores(useErrorStore), ...mapStores(useNotificationStore),
badgeIcon() { badgeIcon() {
if (this.error.type === "FRONTEND") { if (this.error.type === "FRONTEND") {
return "desktop"; return "desktop";

View file

@ -134,7 +134,7 @@ export default {
}, },
formattedBody() { formattedBody() {
if (!this.body) return '[]'; if (!this.body) return '[]';
return JSON.stringify(this.body, null, 2); return JSON.stringify(JSON.parse(this.body), null, 2);
}, },
formattedHeader() { formattedHeader() {
if (!this.header) return ''; if (!this.header) return '';

View file

@ -1,6 +1,6 @@
import router from './router.js'; import router from './router.js';
//import store from './store/index.js'; //import store from './store/index.js';
import { setupErrorBuffer } from './store/error.js' import { setupErrorBuffer } from './store/notification.js'
import {createApp} from 'vue' import {createApp} from 'vue'
import {createPinia} from 'pinia'; import {createPinia} from 'pinia';
import {startSessionRefresh} from "@/backend.js"; import {startSessionRefresh} from "@/backend.js";

View file

@ -87,13 +87,14 @@ import {useAssistantStore} from "@/store/assistant.js";
import CreateNewNode from "@/components/layout/node/CreateNewNode.vue"; import CreateNewNode from "@/components/layout/node/CreateNewNode.vue";
import Checkbox from "@/components/UI/Checkbox.vue"; import Checkbox from "@/components/UI/Checkbox.vue";
import {UrlSafeBase64} from "@/common.js"; import {UrlSafeBase64} from "@/common.js";
import {useNotificationStore} from "@/store/notification.js";
export default { export default {
name: "CalculationAssistant", name: "CalculationAssistant",
components: {Checkbox, CreateNewNode, Modal, SupplierItem, MaterialItem, BasicButton, AutosuggestSearchbar}, components: {Checkbox, CreateNewNode, Modal, SupplierItem, MaterialItem, BasicButton, AutosuggestSearchbar},
computed: { computed: {
...mapStores(useNodeStore, useAssistantStore), ...mapStores(useNodeStore, useAssistantStore, useNotificationStore),
showPartNumberModal() { showPartNumberModal() {
return this.partNumberModalState; return this.partNumberModalState;
} }
@ -102,7 +103,8 @@ export default {
return { return {
newSupplierModalState: false, newSupplierModalState: false,
partNumberModalState: true, partNumberModalState: true,
partNumberField: '' partNumberField: '',
createMsg: 'Creating calculations ...'
} }
}, },
methods: { methods: {
@ -111,6 +113,8 @@ export default {
}, },
async createPremises() { async createPremises() {
this.notificationStore.setSpinner(this.createMsg);
const ids = await this.assistantStore.createPremises(); const ids = await this.assistantStore.createPremises();
if (ids.length === 1) { if (ids.length === 1) {
@ -118,6 +122,9 @@ export default {
} else { } else {
this.$router.push({name: "bulk", params: {ids: new UrlSafeBase64().encodeIds(ids)}}); this.$router.push({name: "bulk", params: {ids: new UrlSafeBase64().encodeIds(ids)}});
} }
this.notificationStore.clearSpinner();
this.assistantStore.reset(); this.assistantStore.reset();
}, },
selectedSupplier(supplier) { selectedSupplier(supplier) {

View file

@ -53,14 +53,6 @@
</transition-group> </transition-group>
</transition> </transition>
<modal :z-index="3000" :state="showProcessingModal">
<div class="edit-calculation-spinner-container space-around">
<spinner></spinner>
<span>{{ shownProcessingMessage }}</span>
</div>
</modal>
<mass-edit-dialog v-if="showData" :show="showMultiselectAction" @action="multiselectAction" <mass-edit-dialog v-if="showData" :show="showMultiselectAction" @action="multiselectAction"
:select-count="selectCount"></mass-edit-dialog> :select-count="selectCount"></mass-edit-dialog>
@ -124,6 +116,7 @@ import DestinationListView from "@/components/layout/edit/DestinationListView.vu
import Toast from "@/components/UI/Toast.vue"; import Toast from "@/components/UI/Toast.vue";
import logger from "@/logger.js"; import logger from "@/logger.js";
import {useCustomsStore} from "@/store/customs.js"; import {useCustomsStore} from "@/store/customs.js";
import {useNotificationStore} from "@/store/notification.js";
const COMPONENT_TYPES = { const COMPONENT_TYPES = {
@ -147,7 +140,7 @@ export default {
BasicButton BasicButton
}, },
computed: { computed: {
...mapStores(usePremiseEditStore, useCustomsStore), ...mapStores(usePremiseEditStore, useCustomsStore, useNotificationStore),
hasSelection() { hasSelection() {
if (this.premiseEditStore.isLoading || this.premiseEditStore.selectedLoading) { if (this.premiseEditStore.isLoading || this.premiseEditStore.selectedLoading) {
return false; return false;
@ -191,14 +184,20 @@ export default {
return this.modalType ? this.componentsData[this.modalType] : null; return this.modalType ? this.componentsData[this.modalType] : null;
}, },
showProcessingModal() { showProcessingModal() {
return this.premiseEditStore.showProcessingModal || this.showCalculationModal || this.customsStore.loadingTariff; return this.premiseEditStore.showProcessingModal || this.showCalculationModal;
}, },
shownProcessingMessage() { shownProcessingMessage() {
if(this.customsStore.loadingTariff)
return "Looking up tariff rate ..."
return this.processingMessage; return this.processingMessage;
}
},
watch: {
showProcessingModal(newState, _) {
if(newState) {
this.notificationStore.setSpinner(this.shownProcessingMessage);
}
else {
this.notificationStore.clearSpinner();
}
} }
}, },
created() { created() {
@ -521,7 +520,7 @@ export default {
flex: 1 1 30rem flex: 1 1 30rem
} }
.edit-calculation-spinner { .spinner-box {
font-size: 1.6rem; font-size: 1.6rem;
width: 24rem; width: 24rem;
height: 12rem; height: 12rem;

View file

@ -19,12 +19,12 @@
</div> </div>
<div v-if="premiseSingleEditStore.showLoadingSpinner" class="edit-calculation-spinner-container"> <div v-if="premiseSingleEditStore.showLoadingSpinner" class="edit-calculation-spinner-container">
<box class="edit-calculation-spinner"> <box class="spinner-box">
<spinner></spinner> <spinner></spinner>
</box> </box>
</div> </div>
<div v-else-if="premiseSingleEditStore.isEmpty" class="edit-calculation-spinner-container"> <div v-else-if="premiseSingleEditStore.isEmpty" class="edit-calculation-spinner-container">
<box class="edit-calculation-spinner">No calculation found.</box> <box class="spinner-box">No calculation found.</box>
</div> </div>
<div v-else> <div v-else>
@ -85,13 +85,6 @@
<h3 class="sub-header">Destinations & routes</h3> <h3 class="sub-header">Destinations & routes</h3>
<destination-list-view></destination-list-view> <destination-list-view></destination-list-view>
<modal :z-index="3000" :state="showProcessingModal">
<div class="edit-calculation-spinner-container space-around">
<spinner></spinner>
<span>{{ shownProcessingMessage }}</span>
</div>
</modal>
</div> </div>
</div> </div>
</template> </template>
@ -106,24 +99,24 @@ import PackagingEdit from "@/components/layout/edit/PackagingEdit.vue";
import PriceEdit from "@/components/layout/edit/PriceEdit.vue"; import PriceEdit from "@/components/layout/edit/PriceEdit.vue";
import DestinationListView from "@/components/layout/edit/DestinationListView.vue"; import DestinationListView from "@/components/layout/edit/DestinationListView.vue";
import {mapStores} from "pinia"; import {mapStores} from "pinia";
import Spinner from "@/components/UI/Spinner.vue";
import NotificationBar from "@/components/UI/NotificationBar.vue"; import NotificationBar from "@/components/UI/NotificationBar.vue";
import Modal from "@/components/UI/Modal.vue"; import Modal from "@/components/UI/Modal.vue";
import TraceView from "@/components/layout/TraceView.vue"; import TraceView from "@/components/layout/TraceView.vue";
import IconButton from "@/components/UI/IconButton.vue"; import IconButton from "@/components/UI/IconButton.vue";
import {UrlSafeBase64} from "@/common.js"; import {UrlSafeBase64} from "@/common.js";
import {usePremiseSingleEditStore} from "@/store/premiseSingleEdit.js"; import {usePremiseSingleEditStore} from "@/store/premiseSingleEdit.js";
import logger from "@/logger.js"; import {useNotificationStore} from "@/store/notification.js";
import Spinner from "@/components/UI/Spinner.vue";
export default { export default {
name: "SingleEdit", name: "SingleEdit",
components: { components: {
Spinner,
IconButton, IconButton,
TraceView, TraceView,
Modal, Modal,
NotificationBar, NotificationBar,
Spinner,
DestinationListView, DestinationListView,
PriceEdit, PriceEdit,
PackagingEdit, PackagingEdit,
@ -137,11 +130,10 @@ export default {
traceModal: false, traceModal: false,
bulkEditQuery: null, bulkEditQuery: null,
id: null, id: null,
showCalculationModal: false,
} }
}, },
computed: { computed: {
...mapStores(usePremiseSingleEditStore), ...mapStores(usePremiseSingleEditStore, useNotificationStore),
premise() { premise() {
return this.premiseSingleEditStore.premise; return this.premiseSingleEditStore.premise;
}, },
@ -149,7 +141,7 @@ export default {
return this.bulkEditQuery !== null; return this.bulkEditQuery !== null;
}, },
showProcessingModal() { showProcessingModal() {
return this.premiseSingleEditStore.showProcessingModal || this.showCalculationModal; return this.premiseSingleEditStore.showProcessingModal;
}, },
shownProcessingMessage() { shownProcessingMessage() {
if (this.premiseSingleEditStore.routing) if (this.premiseSingleEditStore.routing)
@ -158,6 +150,16 @@ export default {
return "Please wait. Calculating ..."; return "Please wait. Calculating ...";
} }
}, },
watch: {
showProcessingModal(newState, _) {
if(newState) {
this.notificationStore.setSpinner(this.shownProcessingMessage);
}
else {
this.notificationStore.clearSpinner();
}
}
},
methods: { methods: {
async startCalculation() { async startCalculation() {
@ -275,17 +277,7 @@ export default {
margin: 3rem; margin: 3rem;
} }
.edit-calculation-spinner-container { .spinner-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 3.6rem;
flex: 1 1 auto;
font-size: 1.6rem;
}
.edit-calculation-spinner {
font-size: 1.6rem; font-size: 1.6rem;
width: 24rem; width: 24rem;
height: 12rem; height: 12rem;

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import performRequest from "@/backend.js"; import performRequest from "@/backend.js";
import logger from "@/logger.js"; import logger from "@/logger.js";

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import performRequest from "@/backend.js"; import performRequest from "@/backend.js";

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import performRequest, {performDownload, performUpload} from "@/backend.js"; import performRequest, {performDownload, performUpload} from "@/backend.js";
export const useBulkOperationStore = defineStore('bulkOperation', { export const useBulkOperationStore = defineStore('bulkOperation', {
@ -39,6 +39,14 @@ export const useBulkOperationStore = defineStore('bulkOperation', {
this.stopTimer(); this.stopTimer();
if(!restart)
useNotificationStore().addNotification({
title: 'Bulk operation',
message: 'All your bulk operations have been completed.',
type: 'success',
icon: 'stack',
})
if(restart) { if(restart) {
this.startTimer(); this.startTimer();
} }

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import performRequest from "@/backend.js"; import performRequest from "@/backend.js";
export const useContainerRateStore = defineStore('containerRate', { export const useContainerRateStore = defineStore('containerRate', {

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import {useStageStore} from "@/store/stage.js"; import {useStageStore} from "@/store/stage.js";
import {usePropertySetsStore} from "@/store/propertySets.js"; import {usePropertySetsStore} from "@/store/propertySets.js";
import performRequest from "@/backend.js"; import performRequest from "@/backend.js";

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import performRequest from "@/backend.js"; import performRequest from "@/backend.js";
import logger from "@/logger.js"; import logger from "@/logger.js";

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import performRequest from "@/backend.js"; import performRequest from "@/backend.js";
export const useMatrixRateStore = defineStore('matrixRate', { export const useMatrixRateStore = defineStore('matrixRate', {

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import performRequest from "@/backend.js"; import performRequest from "@/backend.js";
import logger from "@/logger.js"; import logger from "@/logger.js";

View file

@ -4,22 +4,30 @@ import {toRaw} from "vue";
import {getCsrfToken} from "@/backend.js"; import {getCsrfToken} from "@/backend.js";
import logger from "@/logger.js"; import logger from "@/logger.js";
export const useErrorStore = defineStore('error', { export const useNotificationStore = defineStore('notification', {
state() { state() {
return { return {
notifications: [], notifications: [],
sendCache: [], sendCache: [],
autoSubmitInterval: 30000, autoSubmitInterval: 30000,
autoSubmitTimer: null autoSubmitTimer: null,
spinnerMessage: null
} }
}, },
getters: { getters: {
lastError: (state) => state.notifications.length > 0 ? state.notifications[state.notifications.length - 1].error : null, hasSpinner(state) {
return state.spinnerMessage !== null;
},
spinnerMsg(state) {
return state.spinnerMessage;
}
}, },
actions: { actions: {
clearErrors() { setSpinner(msg) {
this.notifications = []; this.spinnerMessage = msg;
console.log("Cleared errors"); },
clearSpinner() {
this.spinnerMessage = null;
}, },
popNotification() { popNotification() {
return this.notifications.pop(); return this.notifications.pop();
@ -118,7 +126,7 @@ export const useErrorStore = defineStore('error', {
const pinia = this.$pinia || getActivePinia() const pinia = this.$pinia || getActivePinia()
if (pinia && pinia._s) { if (pinia && pinia._s) {
pinia._s.forEach((store, storeId) => { pinia._s.forEach((store, storeId) => {
if (storeId !== 'error' && store.$state) { if (storeId !== 'error' && storeId !== 'errorLog' && store.$state) {
storeState[storeId] = { storeState[storeId] = {
...toRaw(store.$state) ...toRaw(store.$state)
} }
@ -147,20 +155,20 @@ export const useErrorStore = defineStore('error', {
// Global Error Handler Setup // Global Error Handler Setup
export function setupErrorBuffer() { export function setupErrorBuffer() {
const errorStore = useErrorStore() const errorStore = useNotificationStore()
//Unhandled Promise Rejections //Unhandled Promise Rejections
window.addEventListener('unhandledrejection', (event) => { // window.addEventListener('unhandledrejection', (event) => {
//
const error = { // const error = {
code: "Unhandled rejection", // code: "Unhandled rejection",
title: "Frontend error", // title: "Frontend error",
message: event.reason?.message || 'Unhandled Promise Rejection', // message: event.reason?.message || 'Unhandled Promise Rejection',
traceCombined: event.reason?.stack, // traceCombined: event.reason?.stack,
}; // };
//
errorStore.addError(error, {global: true}).then(r => {} ); // errorStore.addError(error, {global: true}).then(r => {} );
}) // })
// // JavaScript Errors // // JavaScript Errors
window.addEventListener('error', (event) => { window.addEventListener('error', (event) => {

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import performRequest from "@/backend.js"; import performRequest from "@/backend.js";
import logger from "@/logger.js"; import logger from "@/logger.js";

View file

@ -1,7 +1,7 @@
import {defineStore} from 'pinia' 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 {useNotificationStore} from "@/store/notification.js";
import logger from "@/logger.js" import logger from "@/logger.js"
import performRequest from '@/backend.js' import performRequest from '@/backend.js'

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import { useStageStore } from './stage.js' import { useStageStore } from './stage.js'
import {usePropertySetsStore} from "@/store/propertySets.js"; import {usePropertySetsStore} from "@/store/propertySets.js";
import performRequest from "@/backend.js"; import performRequest from "@/backend.js";

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import { useStageStore } from './stage.js' import { useStageStore } from './stage.js'
import performRequest from "@/backend.js"; import performRequest from "@/backend.js";

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import performRequest from "@/backend.js"; import performRequest from "@/backend.js";
export const useStageStore = defineStore('stage', { export const useStageStore = defineStore('stage', {

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import performRequest from "@/backend.js"; import performRequest from "@/backend.js";
export const useStagedRatesStore = defineStore('stagedRates', { export const useStagedRatesStore = defineStore('stagedRates', {

View file

@ -1,6 +1,6 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {config} from '@/config' import {config} from '@/config'
import {useErrorStore} from "@/store/error.js"; import {useNotificationStore} from "@/store/notification.js";
import { useStageStore } from './stage.js' import { useStageStore } from './stage.js'
import performRequest from "@/backend.js"; import performRequest from "@/backend.js";

View file

@ -16,9 +16,9 @@ public class AsyncConfig {
@Bean(name = "calculationExecutor") @Bean(name = "calculationExecutor")
public Executor taskExecutor() { public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4); executor.setCorePoolSize(16);
executor.setMaxPoolSize(8); executor.setMaxPoolSize(32);
executor.setQueueCapacity(100); executor.setQueueCapacity(500);
executor.setThreadNamePrefix("calc-"); executor.setThreadNamePrefix("calc-");
executor.initialize(); executor.initialize();
return executor; return executor;
@ -27,9 +27,9 @@ public class AsyncConfig {
@Bean(name = "customLookupExecutor") @Bean(name = "customLookupExecutor")
public Executor customLookupExecutor() { public Executor customLookupExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4); executor.setCorePoolSize(16);
executor.setMaxPoolSize(8); executor.setMaxPoolSize(32);
executor.setQueueCapacity(100); executor.setQueueCapacity(500);
executor.setThreadNamePrefix("lookup-"); executor.setThreadNamePrefix("lookup-");
executor.initialize(); executor.initialize();
return executor; return executor;
@ -40,7 +40,7 @@ public class AsyncConfig {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1); executor.setCorePoolSize(1);
executor.setMaxPoolSize(1); executor.setMaxPoolSize(1);
executor.setQueueCapacity(100); executor.setQueueCapacity(500);
executor.setThreadNamePrefix("bulk-"); executor.setThreadNamePrefix("bulk-");
executor.setWaitForTasksToCompleteOnShutdown(true); executor.setWaitForTasksToCompleteOnShutdown(true);

View file

@ -2,6 +2,8 @@ package de.avatic.lcc.service.api;
import eu.europa.ec.taxation.taric.client.*; import eu.europa.ec.taxation.taric.client.*;
import jakarta.xml.bind.JAXBElement; import jakarta.xml.bind.JAXBElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.ws.client.core.WebServiceTemplate; import org.springframework.ws.client.core.WebServiceTemplate;
@ -17,6 +19,7 @@ public class EUTaxationApiService {
private final WebServiceTemplate webServiceTemplate; private final WebServiceTemplate webServiceTemplate;
private final ObjectFactory objectFactory; private final ObjectFactory objectFactory;
private Logger logger = LoggerFactory.getLogger(EUTaxationApiService.class);
public EUTaxationApiService(WebServiceTemplate webServiceTemplate) { public EUTaxationApiService(WebServiceTemplate webServiceTemplate) {
this.webServiceTemplate = webServiceTemplate; this.webServiceTemplate = webServiceTemplate;
@ -25,6 +28,9 @@ public class EUTaxationApiService {
@Async("customLookupExecutor") @Async("customLookupExecutor")
public CompletableFuture<GoodsDescrForWsResponse> getGoodsDescription(String goodsCode, String languageCode) { public CompletableFuture<GoodsDescrForWsResponse> getGoodsDescription(String goodsCode, String languageCode) {
logger.info("Start lookup for {} and {}", goodsCode, languageCode);
GoodsDescrForWs request = new GoodsDescrForWs(); GoodsDescrForWs request = new GoodsDescrForWs();
request.setGoodsCode(goodsCode); request.setGoodsCode(goodsCode);
request.setLanguageCode(languageCode); request.setLanguageCode(languageCode);
@ -64,4 +70,12 @@ public class EUTaxationApiService {
throw new RuntimeException("Fehler beim Erstellen des Datums", e); throw new RuntimeException("Fehler beim Erstellen des Datums", e);
} }
} }
public Logger getLogger() {
return logger;
}
public void setLogger(Logger logger) {
this.logger = logger;
}
} }

View file

@ -325,6 +325,10 @@ public class PreCalculationCheckService {
private void materialCheck(Premise premise) { private void materialCheck(Premise premise) {
if(premise.getHsCode() == null || premise.getHsCode().length() < 10)
throw new PremiseValidationError("Invalid HS code.");
var isDeclarable = eUTaxationResolverService.validate(premise.getHsCode()); var isDeclarable = eUTaxationResolverService.validate(premise.getHsCode());
if (!isDeclarable) if (!isDeclarable)

View file

@ -637,8 +637,8 @@ CREATE TABLE IF NOT EXISTS sys_error
title VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL,
code VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL,
message VARCHAR(1024) NOT NULL, message VARCHAR(1024) NOT NULL,
request TEXT, request MEDIUMTEXT,
pinia TEXT, pinia MEDIUMTEXT,
calculation_job_id INT DEFAULT NULL, calculation_job_id INT DEFAULT NULL,
bulk_operation_id INT DEFAULT NULL, bulk_operation_id INT DEFAULT NULL,
type CHAR(16) NOT NULL DEFAULT 'BACKEND', type CHAR(16) NOT NULL DEFAULT 'BACKEND',

View file

@ -637,8 +637,8 @@ CREATE TABLE IF NOT EXISTS sys_error
title VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL,
code VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL,
message VARCHAR(1024) NOT NULL, message VARCHAR(1024) NOT NULL,
request TEXT, request MEDIUMTEXT,
pinia TEXT, pinia MEDIUMTEXT,
calculation_job_id INT DEFAULT NULL, calculation_job_id INT DEFAULT NULL,
bulk_operation_id INT DEFAULT NULL, bulk_operation_id INT DEFAULT NULL,
type CHAR(16) NOT NULL DEFAULT 'BACKEND', type CHAR(16) NOT NULL DEFAULT 'BACKEND',