From ce6b8dd9b045a7b88ee6fbe0afc5f638d6bdfd74 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 6 Nov 2025 17:35:41 +0100 Subject: [PATCH] Integrated tariff rate calculations and customs API: - **Backend**: Introduced `EUTaxationApiService` for TARIC data retrieval and integrated tariff rate lookup functionality. Added supporting DTOs and updated services to handle custom measures and updated logic for material and supplier changes. - **Frontend**: Enhanced mass and single edit calculation components to include tariff rate lookup functionality and warnings. Introduced `useCustomsStore` for state management of customs data. - **Database**: Added `nomenclature` table for storing HS code-related information. - **Other**: Configured SOAP client for TARIC API and added logging for debugging. --- pom.xml | 34 + .../layout/bulkedit/BulkEditRow.vue | 2 +- .../components/layout/edit/MaterialEdit.vue | 225 +- .../src/pages/CalculationMassEdit.vue | 47 +- .../src/pages/CalculationSingleEdit.vue | 15 +- src/frontend/src/store/customs.js | 46 +- src/frontend/src/store/premiseEdit.js | 34 +- .../java/de/avatic/lcc/config/SoapConfig.java | 38 + .../controller/custom/CustomController.java | 26 +- .../edit/masterData/MaterialUpdateDTO.java | 12 + .../de/avatic/lcc/dto/custom/CustomDTO.java | 64 + .../lcc/dto/custom/CustomMeasureDTO.java | 54 + .../avatic/lcc/model/custom/MeasureType.java | 209 + .../lcc/model/taric/AppliedMeasure.java | 100 + .../model/taric/AppliedMeasureCondition.java | 75 + .../avatic/lcc/model/taric/Certificate.java | 56 + .../lcc/model/taric/CertificateDesc.java | 36 + .../lcc/model/taric/CertificateType.java | 24 + .../lcc/model/taric/CertificateTypeDesc.java | 37 + .../avatic/lcc/model/taric/ConditionType.java | 24 + .../lcc/model/taric/ConditionTypeDesc.java | 36 + .../de/avatic/lcc/model/taric/Footnote.java | 57 + .../avatic/lcc/model/taric/FootnotesDesc.java | 40 + .../java/de/avatic/lcc/model/taric/Geo.java | 31 + .../de/avatic/lcc/model/taric/GeoDesc.java | 41 + .../de/avatic/lcc/model/taric/LegalBase.java | 50 + .../de/avatic/lcc/model/taric/Measure.java | 66 + .../avatic/lcc/model/taric/MeasureAction.java | 24 + .../lcc/model/taric/MeasureActionDesc.java | 38 + .../lcc/model/taric/MeasureComponent.java | 90 + .../lcc/model/taric/MeasureCondition.java | 135 + .../avatic/lcc/model/taric/MeasureDesc.java | 37 + .../lcc/model/taric/MeasureExclusion.java | 43 + .../lcc/model/taric/MeasureFootnote.java | 38 + .../avatic/lcc/model/taric/MeasureSeries.java | 24 + .../lcc/model/taric/MeasureSeriesDesc.java | 40 + .../avatic/lcc/model/taric/MonetaryUnit.java | 30 + .../lcc/model/taric/MonetaryUnitDesc.java | 36 + .../avatic/lcc/model/taric/Nomenclature.java | 127 + .../lcc/model/taric/NomenclatureDesc.java | 37 + .../lcc/model/taric/NomenclatureFootnote.java | 22 + .../lcc/model/taric/TariffResponse.java | 69 + .../java/de/avatic/lcc/model/taric/Unit.java | 51 + .../de/avatic/lcc/model/taric/UnitDesc.java | 37 + .../repositories/NomenclatureRepository.java | 37 + .../lcc/service/access/PremisesService.java | 8 + .../lcc/service/api/CustomApiService.java | 159 +- .../lcc/service/api/EUTaxationApiService.java | 63 + .../api/EUTaxationApiWrapperService.java | 120 + .../calculation/ChangeMaterialService.java | 3 +- .../calculation/ChangeSupplierService.java | 2 +- .../calculation/NomenclatureService.java | 21 + .../calculation/PremiseCreationService.java | 2 +- .../steps/CustomCostCalculationService.java | 2 +- src/main/resources/application.properties | 5 +- .../db/migration/V11__Nomenclature.sql | 16929 ++++++++++++++++ src/main/resources/schema.sql | 7 + src/main/resources/wsdl/goods.wsdl | 85 + 58 files changed, 19749 insertions(+), 51 deletions(-) create mode 100644 src/main/java/de/avatic/lcc/config/SoapConfig.java create mode 100644 src/main/java/de/avatic/lcc/dto/custom/CustomDTO.java create mode 100644 src/main/java/de/avatic/lcc/dto/custom/CustomMeasureDTO.java create mode 100644 src/main/java/de/avatic/lcc/model/custom/MeasureType.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/AppliedMeasure.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/AppliedMeasureCondition.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/Certificate.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/CertificateDesc.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/CertificateType.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/CertificateTypeDesc.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/ConditionType.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/ConditionTypeDesc.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/Footnote.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/FootnotesDesc.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/Geo.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/GeoDesc.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/LegalBase.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/Measure.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/MeasureAction.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/MeasureActionDesc.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/MeasureComponent.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/MeasureCondition.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/MeasureDesc.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/MeasureExclusion.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/MeasureFootnote.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/MeasureSeries.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/MeasureSeriesDesc.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/MonetaryUnit.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/MonetaryUnitDesc.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/Nomenclature.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/NomenclatureDesc.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/NomenclatureFootnote.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/TariffResponse.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/Unit.java create mode 100644 src/main/java/de/avatic/lcc/model/taric/UnitDesc.java create mode 100644 src/main/java/de/avatic/lcc/repositories/NomenclatureRepository.java create mode 100644 src/main/java/de/avatic/lcc/service/api/EUTaxationApiService.java create mode 100644 src/main/java/de/avatic/lcc/service/api/EUTaxationApiWrapperService.java create mode 100644 src/main/java/de/avatic/lcc/service/calculation/NomenclatureService.java create mode 100644 src/main/resources/db/migration/V11__Nomenclature.sql create mode 100644 src/main/resources/wsdl/goods.wsdl diff --git a/pom.xml b/pom.xml index 751715b..9bd06f4 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,11 @@ org.springframework.boot spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-web-services + com.azure.spring spring-cloud-azure-starter @@ -162,6 +167,15 @@ flyway-mysql + + org.glassfish.jaxb + jaxb-runtime + + + jakarta.xml.bind + jakarta.xml.bind-api + 4.0.2 + @@ -179,6 +193,26 @@ + org.jvnet.jaxb + jaxb-maven-plugin + 4.0.0 + + + + generate + + + + + ${project.basedir}/src/main/resources/wsdl + + *.wsdl + + eu.europa.ec.taxation.taric.client + + + + org.apache.maven.plugins maven-surefire-plugin 3.2.5 diff --git a/src/frontend/src/components/layout/bulkedit/BulkEditRow.vue b/src/frontend/src/components/layout/bulkedit/BulkEditRow.vue index 1b08ed4..5c787cc 100644 --- a/src/frontend/src/components/layout/bulkedit/BulkEditRow.vue +++ b/src/frontend/src/components/layout/bulkedit/BulkEditRow.vue @@ -10,7 +10,7 @@
{{ premise.material.part_number }}
HS Code: - {{ premise.material.hs_code }} + {{ premise.hs_code }}
diff --git a/src/frontend/src/components/layout/edit/MaterialEdit.vue b/src/frontend/src/components/layout/edit/MaterialEdit.vue index f470a5f..0b16a28 100644 --- a/src/frontend/src/components/layout/edit/MaterialEdit.vue +++ b/src/frontend/src/components/layout/edit/MaterialEdit.vue @@ -1,6 +1,7 @@ @@ -48,21 +86,24 @@ import Flag from "@/components/UI/Flag.vue"; import InputField from "@/components/UI/InputField.vue"; import AutosuggestSearchbar from "@/components/UI/AutoSuggestSearchBar.vue"; import ModalDialog from "@/components/UI/ModalDialog.vue"; -import {PhArrowCounterClockwise} from "@phosphor-icons/vue"; +import {PhArrowCounterClockwise, PhWarning} from "@phosphor-icons/vue"; import {useMaterialStore} from "@/store/material.js"; import {mapStores} from "pinia"; import {parseNumberFromString} from "@/common.js"; import Modal from "@/components/UI/Modal.vue"; import SelectMaterial from "@/components/layout/material/SelectMaterial.vue"; import {useCustomsStore} from "@/store/customs.js"; +import Checkbox from "@/components/UI/Checkbox.vue"; export default { name: "MaterialEdit", components: { + Checkbox, + PhWarning, SelectMaterial, Modal, PhArrowCounterClockwise, ModalDialog, AutosuggestSearchbar, InputField, Flag, IconButton }, - emits: ["update:tariffRate", "updateMaterial", "update:partNumber", "update:hsCode", "save", "close"], + emits: ["update:tariffRate", "updateMaterial", "update:partNumber", "update:hsCode", "save", "close", "startLookup"], props: { description: { type: [String, null], @@ -72,6 +113,10 @@ export default { required: true, validator: (value) => value === null || typeof value === 'string' }, + countryId: { + required: true, + validator: (value) => value === null || typeof value === 'number' + }, tariffRate: { required: true, validator: (value) => value === null || typeof value === 'number' @@ -93,11 +138,29 @@ export default { ...mapStores(useMaterialStore, useCustomsStore), tariffRatePercent() { return ((this.tariffRate ?? null) !== null) ? (this.tariffRate * 100).toFixed(2) : ''; + }, + showTariffWarning() { + return this.customsStore.showWarning(this.countryId); + }, + tariffIsDefault() { + return this.tariffInfo?.default ?? false; + }, + tariffInfo() { + return this.customsStore.findByCountryId(this.countryId); + }, + measures() { + return this.tariffInfo.measures; + }, + showCheckbox() { + return this.countryId === null; + }, + disableTariff() { + return !this.doLookup; } }, data() { return { - + doLookup: false } }, created() { @@ -115,22 +178,54 @@ export default { } }); }, - validateTariffRate(event) { - const percentValue = parseNumberFromString(event.target.value, 2, true); + validateTariffRateEvent(event) { + event.target.value = this.validateTariffRate(event.target.value); + }, + validateTariffRate(value) { + const percentValue = parseNumberFromString(value, 2, true); const validatedPercent = (percentValue === null) ? null : Math.max(0, Math.min(999.99, percentValue)); const validatedDecimal = (validatedPercent === null) ? null : validatedPercent / 100; if (validatedDecimal !== this.tariffRate) { - this.$emit('update:tariffRate', validatedDecimal); + this.$emit('update:tariffRate', this.roundNumber(validatedDecimal, 4)); } - event.target.value = validatedPercent === null ? null : validatedPercent.toFixed(2); + return validatedPercent === null ? null : validatedPercent.toFixed(2); + + }, + roundNumber(number, digits) { + const multiple = Math.pow(10, digits); + return Math.round(number * multiple) / multiple; }, async fetchHsCode(query) { - const hsCodeQuery = {searchTerm: query}; - await this.customsStore.setQuery(hsCodeQuery); - return this.customsStore.hsCodes; + return await this.customsStore.findHsCode(query); + }, + hsCodeChanged() { + const currentValue = this.$refs.hsCodeSearchbar.searchQuery; + this.$emit("update:hsCode", currentValue); + }, + async hsCodeSelected(hsCode) { + let save = false; + + if (hsCode !== this.hsCode) { + this.$emit("update:hsCode", hsCode) + save = true; + } + + if ((this.countryId ?? null) !== null) { + const query = {hsCode: hsCode, countryIds: [this.countryId]}; + await this.customsStore.findTariffRate(query); + this.validateTariffRate((this.customsStore.findByCountryId(this.countryId)?.value ?? 0.03) * 100); + save = true; + } + + if (save) + this.$emit('save', 'material'); + }, + checkBoxChanged(checked) { + this.doLookup = checked + this.$emit('startLookup', checked); } } } @@ -139,11 +234,42 @@ export default {