Update Java version to 23 and enhance tariff services with improved repository integration, optional return types, and unique constraints in the schema.
This commit is contained in:
parent
10cc6bc5f0
commit
ce79b20808
16 changed files with 525 additions and 613 deletions
2
pom.xml
2
pom.xml
|
|
@ -27,7 +27,7 @@
|
|||
<url/>
|
||||
</scm>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<java.version>23</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package de.avatic.taric.controller;
|
||||
|
||||
import de.avatic.taric.service.TariffService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
|
@ -18,10 +19,8 @@ public class TariffController {
|
|||
}
|
||||
|
||||
@GetMapping("")
|
||||
public void getTariffRate(@RequestParam String hsCode, @RequestParam String countryCode) {
|
||||
|
||||
tariffService.importTariffs( hsCode, countryCode);
|
||||
|
||||
public ResponseEntity<Double> getTariffRate(@RequestParam String hsCode, @RequestParam String countryCode) {
|
||||
return tariffService.importTariffs( hsCode, countryCode).map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package de.avatic.taric.controller;
|
||||
|
||||
import de.avatic.taric.service.TariffService2;
|
||||
import de.avatic.taric.service.TariffService2.TariffResult;
|
||||
//import de.avatic.taric.service.TariffService2.TariffResult;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
|
|
@ -23,109 +23,109 @@ import java.util.Map;
|
|||
@Tag(name = "Tariff API", description = "API zur Abfrage von Zolltarifen")
|
||||
public class TariffController2 {
|
||||
|
||||
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TariffController2.class);
|
||||
|
||||
private final TariffService2 tariffService;
|
||||
|
||||
public TariffController2(TariffService2 tariffService) {
|
||||
this.tariffService = tariffService;
|
||||
}
|
||||
|
||||
@GetMapping("/rate")
|
||||
@Operation(summary = "Zolltarif abfragen",
|
||||
description = "Ermittelt den Zolltarif für einen HS-Code und ein Herkunftsland")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Tarif erfolgreich gefunden"),
|
||||
@ApiResponse(responseCode = "404", description = "Kein Tarif gefunden"),
|
||||
@ApiResponse(responseCode = "400", description = "Ungültige Eingabeparameter")
|
||||
})
|
||||
public ResponseEntity<TariffResponse> getTariffRate(
|
||||
@Parameter(description = "HS-Code (6, 8 oder 10 Stellen)", example = "850410")
|
||||
@RequestParam
|
||||
@NotBlank(message = "HS Code ist erforderlich")
|
||||
@Pattern(regexp = "^[0-9]{4,10}$", message = "HS Code muss 4-10 Ziffern enthalten")
|
||||
String hsCode,
|
||||
|
||||
@Parameter(description = "ISO-2 Ländercode", example = "CN")
|
||||
@RequestParam
|
||||
@NotBlank(message = "Ländercode ist erforderlich")
|
||||
@Pattern(regexp = "^[A-Z]{2}$", message = "Ländercode muss 2 Großbuchstaben sein")
|
||||
String countryCode) {
|
||||
|
||||
log.info("Tariff rate request - HS Code: {}, Country: {}", hsCode, countryCode);
|
||||
|
||||
try {
|
||||
TariffResult result = tariffService.getTariffRate(hsCode, countryCode);
|
||||
|
||||
if (result.isFound()) {
|
||||
TariffResponse response = TariffResponse.success(
|
||||
result.getRate(),
|
||||
result.getHsCode(),
|
||||
result.getCountryCode()
|
||||
);
|
||||
return ResponseEntity.ok(response);
|
||||
} else {
|
||||
TariffResponse response = TariffResponse.notFound(result.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting tariff rate", e);
|
||||
TariffResponse response = TariffResponse.error("Internal server error: " + e.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/health")
|
||||
@Operation(summary = "Health Check", description = "Prüft ob der Service verfügbar ist")
|
||||
public ResponseEntity<Map<String, String>> health() {
|
||||
Map<String, String> response = new HashMap<>();
|
||||
response.put("status", "UP");
|
||||
response.put("service", "TARIC Tariff Service");
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Response-Klasse für Tarif-Abfragen
|
||||
*/
|
||||
public static class TariffResponse {
|
||||
private final boolean success;
|
||||
private final BigDecimal tariffRate;
|
||||
private final String hsCode;
|
||||
private final String countryCode;
|
||||
private final String message;
|
||||
private final String formattedRate;
|
||||
|
||||
private TariffResponse(boolean success, BigDecimal tariffRate,
|
||||
String hsCode, String countryCode, String message) {
|
||||
this.success = success;
|
||||
this.tariffRate = tariffRate;
|
||||
this.hsCode = hsCode;
|
||||
this.countryCode = countryCode;
|
||||
this.message = message;
|
||||
this.formattedRate = tariffRate != null ?
|
||||
tariffRate.toString() + " %" : null;
|
||||
}
|
||||
|
||||
public static TariffResponse success(BigDecimal rate, String hsCode, String countryCode) {
|
||||
return new TariffResponse(true, rate, hsCode, countryCode,
|
||||
"Tariff rate found successfully");
|
||||
}
|
||||
|
||||
public static TariffResponse notFound(String message) {
|
||||
return new TariffResponse(false, null, null, null, message);
|
||||
}
|
||||
|
||||
public static TariffResponse error(String message) {
|
||||
return new TariffResponse(false, null, null, null, message);
|
||||
}
|
||||
|
||||
// Getters
|
||||
public boolean isSuccess() { return success; }
|
||||
public BigDecimal getTariffRate() { return tariffRate; }
|
||||
public String getHsCode() { return hsCode; }
|
||||
public String getCountryCode() { return countryCode; }
|
||||
public String getMessage() { return message; }
|
||||
public String getFormattedRate() { return formattedRate; }
|
||||
}
|
||||
// private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TariffController2.class);
|
||||
//
|
||||
// private final TariffService2 tariffService;
|
||||
//
|
||||
// public TariffController2(TariffService2 tariffService) {
|
||||
// this.tariffService = tariffService;
|
||||
// }
|
||||
//
|
||||
// @GetMapping("/rate")
|
||||
// @Operation(summary = "Zolltarif abfragen",
|
||||
// description = "Ermittelt den Zolltarif für einen HS-Code und ein Herkunftsland")
|
||||
// @ApiResponses(value = {
|
||||
// @ApiResponse(responseCode = "200", description = "Tarif erfolgreich gefunden"),
|
||||
// @ApiResponse(responseCode = "404", description = "Kein Tarif gefunden"),
|
||||
// @ApiResponse(responseCode = "400", description = "Ungültige Eingabeparameter")
|
||||
// })
|
||||
// public ResponseEntity<TariffResponse> getTariffRate(
|
||||
// @Parameter(description = "HS-Code (6, 8 oder 10 Stellen)", example = "850410")
|
||||
// @RequestParam
|
||||
// @NotBlank(message = "HS Code ist erforderlich")
|
||||
// @Pattern(regexp = "^[0-9]{4,10}$", message = "HS Code muss 4-10 Ziffern enthalten")
|
||||
// String hsCode,
|
||||
//
|
||||
// @Parameter(description = "ISO-2 Ländercode", example = "CN")
|
||||
// @RequestParam
|
||||
// @NotBlank(message = "Ländercode ist erforderlich")
|
||||
// @Pattern(regexp = "^[A-Z]{2}$", message = "Ländercode muss 2 Großbuchstaben sein")
|
||||
// String countryCode) {
|
||||
//
|
||||
// log.info("Tariff rate request - HS Code: {}, Country: {}", hsCode, countryCode);
|
||||
//
|
||||
// try {
|
||||
// TariffResult result = tariffService.getTariffRate(hsCode, countryCode);
|
||||
//
|
||||
// if (result.isFound()) {
|
||||
// TariffResponse response = TariffResponse.success(
|
||||
// result.getRate(),
|
||||
// result.getHsCode(),
|
||||
// result.getCountryCode()
|
||||
// );
|
||||
// return ResponseEntity.ok(response);
|
||||
// } else {
|
||||
// TariffResponse response = TariffResponse.notFound(result.getMessage());
|
||||
// return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
|
||||
// }
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// log.error("Error getting tariff rate", e);
|
||||
// TariffResponse response = TariffResponse.error("Internal server error: " + e.getMessage());
|
||||
// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @GetMapping("/health")
|
||||
// @Operation(summary = "Health Check", description = "Prüft ob der Service verfügbar ist")
|
||||
// public ResponseEntity<Map<String, String>> health() {
|
||||
// Map<String, String> response = new HashMap<>();
|
||||
// response.put("status", "UP");
|
||||
// response.put("service", "TARIC Tariff Service");
|
||||
// return ResponseEntity.ok(response);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Response-Klasse für Tarif-Abfragen
|
||||
// */
|
||||
// public static class TariffResponse {
|
||||
// private final boolean success;
|
||||
// private final BigDecimal tariffRate;
|
||||
// private final String hsCode;
|
||||
// private final String countryCode;
|
||||
// private final String message;
|
||||
// private final String formattedRate;
|
||||
//
|
||||
// private TariffResponse(boolean success, BigDecimal tariffRate,
|
||||
// String hsCode, String countryCode, String message) {
|
||||
// this.success = success;
|
||||
// this.tariffRate = tariffRate;
|
||||
// this.hsCode = hsCode;
|
||||
// this.countryCode = countryCode;
|
||||
// this.message = message;
|
||||
// this.formattedRate = tariffRate != null ?
|
||||
// tariffRate.toString() + " %" : null;
|
||||
// }
|
||||
//
|
||||
// public static TariffResponse success(BigDecimal rate, String hsCode, String countryCode) {
|
||||
// return new TariffResponse(true, rate, hsCode, countryCode,
|
||||
// "Tariff rate found successfully");
|
||||
// }
|
||||
//
|
||||
// public static TariffResponse notFound(String message) {
|
||||
// return new TariffResponse(false, null, null, null, message);
|
||||
// }
|
||||
//
|
||||
// public static TariffResponse error(String message) {
|
||||
// return new TariffResponse(false, null, null, null, message);
|
||||
// }
|
||||
//
|
||||
// // Getters
|
||||
// public boolean isSuccess() { return success; }
|
||||
// public BigDecimal getTariffRate() { return tariffRate; }
|
||||
// public String getHsCode() { return hsCode; }
|
||||
// public String getCountryCode() { return countryCode; }
|
||||
// public String getMessage() { return message; }
|
||||
// public String getFormattedRate() { return formattedRate; }
|
||||
// }
|
||||
}
|
||||
|
|
@ -3,20 +3,28 @@ package de.avatic.taric.model;
|
|||
import java.time.LocalDate;
|
||||
import java.util.Set;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.jdbc.core.mapping.AggregateReference;
|
||||
import org.springframework.data.relational.core.mapping.Column;
|
||||
import org.springframework.data.relational.core.mapping.MappedCollection;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.springframework.data.relational.core.mapping.Table;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Table("applied_measure")
|
||||
public class AppliedMeasure {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
@Column("start_date")
|
||||
private LocalDate startDate;
|
||||
|
||||
@Column("end_date")
|
||||
private LocalDate endDate;
|
||||
|
||||
private String amount;
|
||||
|
|
@ -44,84 +52,7 @@ public class AppliedMeasure {
|
|||
@MappedCollection(idColumn = "applied_measure_id")
|
||||
private Set<AppliedMeasureCondition> conditions;
|
||||
|
||||
public AppliedMeasure(AggregateReference<Measure, Integer> measure, Set<MeasureFootnote> measureFootnotes,
|
||||
AggregateReference<LegalBase, Integer> legalBase, String additionalLegalBase, Set<MeasureExclusion> exclusions,
|
||||
Set<AppliedMeasureCondition> conditions, LocalDate startDate, LocalDate endDate, String amount, Integer orderNumber) {
|
||||
this.measure = measure;
|
||||
this.measureFootnotes = measureFootnotes;
|
||||
this.legalBase = legalBase;
|
||||
this.exclusions = exclusions;
|
||||
this.conditions = conditions;
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
this.amount = amount;
|
||||
this.orderNumber = orderNumber;
|
||||
this.additionalLegalBase = additionalLegalBase;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public LocalDate getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public void setStartDate(final LocalDate startDate) {
|
||||
this.startDate = startDate;
|
||||
}
|
||||
|
||||
public LocalDate getEndDate() {
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public void setEndDate(final LocalDate endDate) {
|
||||
this.endDate = endDate;
|
||||
}
|
||||
|
||||
public String getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(final String amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public AggregateReference<Measure, Integer> getMeasure() {
|
||||
return measure;
|
||||
}
|
||||
|
||||
public void setMeasure(final AggregateReference<Measure, Integer> measure) {
|
||||
this.measure = measure;
|
||||
}
|
||||
|
||||
public AggregateReference<LegalBase, Integer> getLegalBase() {
|
||||
return legalBase;
|
||||
}
|
||||
|
||||
public void setLegalBase(final AggregateReference<LegalBase, Integer> legalBase) {
|
||||
this.legalBase = legalBase;
|
||||
}
|
||||
|
||||
public Integer getOrderNumber() {
|
||||
return orderNumber;
|
||||
}
|
||||
|
||||
public Set<MeasureFootnote> getMeasureFootnotes() {
|
||||
return measureFootnotes;
|
||||
}
|
||||
|
||||
public Set<MeasureExclusion> getExclusions() {
|
||||
return exclusions;
|
||||
}
|
||||
|
||||
public Set<AppliedMeasureCondition> getConditions() {
|
||||
return conditions;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
package de.avatic.taric.model;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.jdbc.core.mapping.AggregateReference;
|
||||
import org.springframework.data.relational.core.mapping.Column;
|
||||
import org.springframework.data.relational.core.mapping.Table;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Table("applied_measure_condition")
|
||||
public class AppliedMeasureCondition {
|
||||
|
||||
@Id
|
||||
|
|
@ -37,82 +42,6 @@ public class AppliedMeasureCondition {
|
|||
@Column("condition_type_id")
|
||||
private AggregateReference<ConditionType, Integer> conditionType;
|
||||
|
||||
public AppliedMeasureCondition(AggregateReference<MeasureAction, Integer> measureAction,
|
||||
AggregateReference<MonetaryUnit, Integer> monetaryUnit,
|
||||
AggregateReference<Unit, Integer> unit,
|
||||
AggregateReference<Certificate, Integer> certificate,
|
||||
AggregateReference<ConditionType, Integer> conditionType, String amount, Integer squenceNo) {
|
||||
this.measureAction = measureAction;
|
||||
this.monetaryUnit = monetaryUnit;
|
||||
this.unit = unit;
|
||||
this.certificate = certificate;
|
||||
this.conditionType = conditionType;
|
||||
this.amount = amount;
|
||||
this.sequenceNo = squenceNo;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Integer getSequenceNo() {
|
||||
return sequenceNo;
|
||||
}
|
||||
|
||||
public void setSequenceNo(final Integer sequenceNo) {
|
||||
this.sequenceNo = sequenceNo;
|
||||
}
|
||||
|
||||
public String getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(final String amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public AggregateReference<MeasureAction, Integer> getMeasureAction() {
|
||||
return measureAction;
|
||||
}
|
||||
|
||||
public void setMeasureAction(final AggregateReference<MeasureAction, Integer> measureAction) {
|
||||
this.measureAction = measureAction;
|
||||
}
|
||||
|
||||
public AggregateReference<MonetaryUnit, Integer> getMonetaryUnit() {
|
||||
return monetaryUnit;
|
||||
}
|
||||
|
||||
public void setMonetaryUnit(final AggregateReference<MonetaryUnit, Integer> monetaryUnit) {
|
||||
this.monetaryUnit = monetaryUnit;
|
||||
}
|
||||
|
||||
public AggregateReference<Unit, Integer> getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public void setUnit(final AggregateReference<Unit, Integer> unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public AggregateReference<Certificate, Integer> getCertificate() {
|
||||
return certificate;
|
||||
}
|
||||
|
||||
public void setCertificate(final AggregateReference<Certificate, Integer> certificate) {
|
||||
this.certificate = certificate;
|
||||
}
|
||||
|
||||
public AggregateReference<ConditionType, Integer> getConditionType() {
|
||||
return conditionType;
|
||||
}
|
||||
|
||||
public void setConditionType(final AggregateReference<ConditionType, Integer> conditionType) {
|
||||
this.conditionType = conditionType;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package de.avatic.taric.model;
|
|||
|
||||
import java.util.Set;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.jdbc.core.mapping.AggregateReference;
|
||||
import org.springframework.data.relational.core.mapping.Column;
|
||||
|
|
@ -10,6 +12,8 @@ import org.springframework.data.relational.core.mapping.Table;
|
|||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@Table("import")
|
||||
public class Import {
|
||||
|
||||
|
|
@ -18,10 +22,10 @@ public class Import {
|
|||
|
||||
@NotNull
|
||||
@Column("nomenclature_id")
|
||||
private AggregateReference<Nomenclature, Integer> nomenclature;
|
||||
private Integer nomenclature;
|
||||
|
||||
@Column("additional_code_id")
|
||||
private AggregateReference<AdditionalCode, Integer> additionalCode;
|
||||
private Integer additionalCode;
|
||||
|
||||
@Column("geo_id")
|
||||
private AggregateReference<Geo, Integer> geo;
|
||||
|
|
@ -32,56 +36,5 @@ public class Import {
|
|||
@MappedCollection(idColumn = "import_id")
|
||||
Set<AppliedMeasure> appliedMeasures;
|
||||
|
||||
public Import(AggregateReference<Nomenclature, Integer> nomenclature,
|
||||
AggregateReference<AdditionalCode, Integer> additionalCode, Set<AppliedMeasure> appliedMeasures, AggregateReference<Geo,Integer> geo,
|
||||
AggregateReference<GeoGroup, Integer> geoGroup) {
|
||||
this.nomenclature = nomenclature;
|
||||
this.additionalCode = additionalCode;
|
||||
this.appliedMeasures = appliedMeasures;
|
||||
this.geo = geo;
|
||||
this.geoGroup = geoGroup;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AggregateReference<Nomenclature, Integer> getNomenclature() {
|
||||
return nomenclature;
|
||||
}
|
||||
|
||||
public void setNomenclature(final AggregateReference<Nomenclature, Integer> nomenclature) {
|
||||
this.nomenclature = nomenclature;
|
||||
}
|
||||
|
||||
public AggregateReference<AdditionalCode, Integer> getAdditionalCode() {
|
||||
return additionalCode;
|
||||
}
|
||||
|
||||
public void setAdditionalCode(final AggregateReference<AdditionalCode, Integer> additionalCode) {
|
||||
this.additionalCode = additionalCode;
|
||||
}
|
||||
|
||||
public AggregateReference<Geo, Integer> getGeo() {
|
||||
return geo;
|
||||
}
|
||||
|
||||
public void setGeo(final AggregateReference<Geo, Integer> geo) {
|
||||
this.geo = geo;
|
||||
}
|
||||
|
||||
public AggregateReference<GeoGroup, Integer> getGeoGroup() {
|
||||
return geoGroup;
|
||||
}
|
||||
|
||||
public void setGeoGroup(final AggregateReference<GeoGroup, Integer> geoGroup) {
|
||||
this.geoGroup = geoGroup;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ public class Measure {
|
|||
|
||||
private Integer tmCode;
|
||||
|
||||
@Column("start_date")
|
||||
private LocalDate startDate;
|
||||
|
||||
@MappedCollection(idColumn = "ref_id")
|
||||
|
|
|
|||
|
|
@ -7,20 +7,24 @@ import org.springframework.data.jdbc.core.mapping.AggregateReference;
|
|||
import org.springframework.data.relational.core.mapping.Column;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.springframework.data.relational.core.mapping.Table;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Table("measure_exclusion")
|
||||
public class MeasureExclusion {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
LocalDate startDate;
|
||||
@Column("start_date")
|
||||
private LocalDate startDate;
|
||||
|
||||
LocalDate endDate;
|
||||
@Column("end_date")
|
||||
private LocalDate endDate;
|
||||
|
||||
@NotNull
|
||||
@Column("geo_id")
|
||||
|
|
|
|||
|
|
@ -6,21 +6,26 @@ import lombok.Setter;
|
|||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.jdbc.core.mapping.AggregateReference;
|
||||
import org.springframework.data.relational.core.mapping.Column;
|
||||
import org.springframework.data.relational.core.mapping.Table;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Table("measure_footnote")
|
||||
public class MeasureFootnote {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
LocalDate startDate;
|
||||
LocalDate endDate;
|
||||
@Column("start_date")
|
||||
private LocalDate startDate;
|
||||
|
||||
@Column("end_date")
|
||||
private LocalDate endDate;
|
||||
|
||||
@Column("footnote_id")
|
||||
private AggregateReference<Footnote, Integer> footnote;
|
||||
private Integer footnote;
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import lombok.Getter;
|
|||
import lombok.Setter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.relational.core.mapping.MappedCollection;
|
||||
import org.springframework.data.relational.core.mapping.Table;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Set;
|
||||
|
|
@ -16,6 +17,7 @@ import java.util.Set;
|
|||
|
||||
@Getter
|
||||
@Setter
|
||||
@Table("nomenclature")
|
||||
public class Nomenclature {
|
||||
|
||||
@Id
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package de.avatic.taric.repository;
|
||||
|
||||
import de.avatic.taric.model.Import;
|
||||
import org.springframework.data.jdbc.repository.query.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import java.util.List;
|
||||
|
|
@ -9,9 +8,8 @@ import java.util.List;
|
|||
@Repository
|
||||
public interface ImportRepository extends CrudRepository<Import, Integer> {
|
||||
|
||||
@Query("SELECT * FROM import WHERE nomenclature_id = :nomenclatureId AND geo_id = :geoId")
|
||||
List<Import> findByNomenclatureIdAndGeoId(Integer nomenclatureId, Integer geoId);
|
||||
List<Import> findByNomenclatureAndGeo(Integer nomenclatureId, Integer geoId);
|
||||
|
||||
List<Import> findByNomenclatureAndGeoGroupIn(Integer nomenclatureId, List<Integer> geoGroupIds);
|
||||
|
||||
@Query("SELECT * FROM import WHERE nomenclature_id = :nomenclatureId AND geo_group_id = :geoGroupId")
|
||||
List<Import> findByNomenclatureIdAndGeoGroupId(Integer nomenclatureId, Integer geoGroupId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,7 @@ import de.avatic.taric.model.Nomenclature;
|
|||
import de.avatic.taric.repository.NomenclatureRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class NomenclatureService {
|
||||
|
|
@ -19,7 +16,6 @@ public class NomenclatureService {
|
|||
}
|
||||
|
||||
|
||||
|
||||
public Optional<Nomenclature> getNomenclature(String hscode) {
|
||||
return nomenclatureRepository.findByHscode(normalize(hscode.replaceAll("[^0-9]", "")));
|
||||
}
|
||||
|
|
@ -40,17 +36,22 @@ public class NomenclatureService {
|
|||
if (hscode == null) return Collections.emptyList();
|
||||
List<String> cascade = getCascade(normalize(hscode.replaceAll("[^0-9]", "")));
|
||||
|
||||
var cascadeResp = cascade.stream().map(nomenclatureRepository::findByHscode).toList();
|
||||
|
||||
return cascade.stream().map(nomenclatureRepository::findByHscode).flatMap(Optional::stream).toList();
|
||||
}
|
||||
|
||||
private List<String> getCascade(String hscode) {
|
||||
var set = new HashSet<String>();
|
||||
var parents = new ArrayList<String>();
|
||||
var hierarchyLevel = getHierarchyLevel(hscode);
|
||||
|
||||
while (hierarchyLevel > 0) {
|
||||
parents.add(getParent(hscode, hierarchyLevel));
|
||||
var parent = getParent(hscode, hierarchyLevel);
|
||||
|
||||
if (!set.contains(parent)) {
|
||||
set.add(parent);
|
||||
parents.add(parent);
|
||||
}
|
||||
|
||||
hierarchyLevel -= 2;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,21 +2,32 @@ package de.avatic.taric.service;
|
|||
|
||||
|
||||
import de.avatic.taric.error.ArgumentException;
|
||||
import de.avatic.taric.model.AppliedMeasure;
|
||||
import de.avatic.taric.model.Geo;
|
||||
import de.avatic.taric.model.GeoGroup;
|
||||
import de.avatic.taric.model.Nomenclature;
|
||||
import de.avatic.taric.repository.ImportRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class TariffService {
|
||||
|
||||
private final GeoService geoService;
|
||||
private final NomenclatureService nomenclatureService;
|
||||
private final ImportRepository importRepository;
|
||||
|
||||
public TariffService(GeoService geoService, NomenclatureService nomenclatureService) {
|
||||
public TariffService(GeoService geoService, NomenclatureService nomenclatureService, ImportRepository importRepository) {
|
||||
this.geoService = geoService;
|
||||
this.nomenclatureService = nomenclatureService;
|
||||
this.importRepository = importRepository;
|
||||
}
|
||||
|
||||
public void importTariffs(String hsCode, String countryCode) {
|
||||
public Optional<Double> importTariffs(String hsCode, String countryCode) {
|
||||
|
||||
var geoGroups = geoService.getGeoGroupByCountryCode(countryCode);
|
||||
var geo = geoService.getGeo(countryCode);
|
||||
|
|
@ -28,8 +39,80 @@ public class TariffService {
|
|||
if (geo.isEmpty()) throw new ArgumentException("countryCode");
|
||||
|
||||
|
||||
for (Nomenclature n : cascade) {
|
||||
var applMeasures = findAppliedMeasureByGeo(n, geo.get());
|
||||
|
||||
if (!applMeasures.isEmpty()) {
|
||||
var tariff = findTariff(applMeasures);
|
||||
|
||||
if (tariff.isPresent())
|
||||
return tariff;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (Nomenclature n : cascade) {
|
||||
var applMeasures = findAppliedMeasureByGeoGroup(n, geo.get(), geoGroups);
|
||||
|
||||
if (!applMeasures.isEmpty()) {
|
||||
var tariff = findTariff(applMeasures);
|
||||
|
||||
if (tariff.isPresent())
|
||||
return tariff;
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
|
||||
}
|
||||
|
||||
private Optional<Double> findTariff(Collection<AppliedMeasure> measures) {
|
||||
|
||||
List<Double> percentages = measures.stream().map(AppliedMeasure::getAmount)
|
||||
.filter(str -> str != null && str.trim().matches("\\d+\\.\\d+\\s*%"))
|
||||
.map(str -> Double.parseDouble(str.trim().replace("%", "").trim())/100)
|
||||
.toList();
|
||||
|
||||
if (!percentages.isEmpty()) return Optional.of(percentages.getFirst());
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public Collection<AppliedMeasure> findAppliedMeasureByGeo(Nomenclature nomenclature, Geo geo) {
|
||||
|
||||
var foundImport = importRepository.findByNomenclatureAndGeo(nomenclature.getId(), geo.getId());
|
||||
var applMeasures = new ArrayList<AppliedMeasure>();
|
||||
|
||||
if (foundImport.isEmpty()) return Collections.emptyList();
|
||||
|
||||
for (var imp : foundImport) {
|
||||
for (var applMeasure : imp.getAppliedMeasures()) {
|
||||
if (applMeasure.getExclusions().stream().noneMatch(ex -> ex.getGeo().getId().equals(geo.getId()))) {
|
||||
applMeasures.add(applMeasure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return applMeasures;
|
||||
|
||||
}
|
||||
|
||||
public Collection<AppliedMeasure> findAppliedMeasureByGeoGroup(Nomenclature nomenclature, Geo geo, List<GeoGroup> geoGroups) {
|
||||
var foundImport = importRepository.findByNomenclatureAndGeoGroupIn(nomenclature.getId(), geoGroups.stream().map(GeoGroup::getId).toList());
|
||||
var applMeasures = new ArrayList<AppliedMeasure>();
|
||||
|
||||
if (foundImport.isEmpty()) return Collections.emptyList();
|
||||
|
||||
for (var imp : foundImport) {
|
||||
for (var applMeasure : imp.getAppliedMeasures()) {
|
||||
if (applMeasure.getExclusions().stream().noneMatch(ex -> ex.getGeo().getId().equals(geo.getId()))) {
|
||||
applMeasures.add(applMeasure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return applMeasures;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,267 +37,267 @@ public class TariffService2 {
|
|||
this.measureExclusionRepository = measureExclusionRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ermittelt den Zolltarif für einen HS-Code und ein Herkunftsland
|
||||
*
|
||||
* @param hsCode HS-Code (kann 6, 8 oder 10 Stellen haben)
|
||||
* @param countryCode ISO-2 Ländercode (z.B. "CN" für China)
|
||||
* @return Zolltarif in Prozent
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public TariffResult getTariffRate(String hsCode, String countryCode) {
|
||||
log.info("Getting tariff rate for HS Code: {} from country: {}", hsCode, countryCode);
|
||||
|
||||
// Normalisiere HS-Code auf 10 Stellen
|
||||
String normalizedHsCode = normalizeHsCode(hsCode);
|
||||
|
||||
// Finde alle relevanten Nomenclature-Codes (inkl. Parent-Codes durch Cascade-Prinzip)
|
||||
List<String> relevantCodes = findRelevantNomenclatureCodes(normalizedHsCode);
|
||||
log.debug("Found relevant codes: {}", relevantCodes);
|
||||
|
||||
// Hole das Land
|
||||
Optional<Geo> countryOpt = geoRepository.findByIso3166Code(countryCode);
|
||||
if (countryOpt.isEmpty()) {
|
||||
log.warn("Country not found: {}", countryCode);
|
||||
return TariffResult.notFound("Country code not found: " + countryCode);
|
||||
}
|
||||
|
||||
Geo country = countryOpt.get();
|
||||
|
||||
// Finde alle Imports für die relevanten Codes
|
||||
List<Import> imports = findRelevantImports(relevantCodes, country);
|
||||
|
||||
if (imports.isEmpty()) {
|
||||
log.info("No imports found for codes: {} and country: {}", relevantCodes, countryCode);
|
||||
// Versuche Erga Omnes (alle Länder)
|
||||
imports = findErgaOmnesImports(relevantCodes);
|
||||
}
|
||||
|
||||
if (imports.isEmpty()) {
|
||||
return TariffResult.notFound("No tariff data found for HS code: " + hsCode);
|
||||
}
|
||||
|
||||
// Finde die anwendbaren Maßnahmen
|
||||
BigDecimal tariffRate = calculateTariffRate(imports, country);
|
||||
|
||||
return TariffResult.success(tariffRate, normalizedHsCode, countryCode);
|
||||
}
|
||||
|
||||
private String normalizeHsCode(String hsCode) {
|
||||
// Entferne alle nicht-numerischen Zeichen
|
||||
String cleaned = hsCode.replaceAll("[^0-9]", "");
|
||||
|
||||
// Fülle auf 10 Stellen mit Nullen auf
|
||||
while (cleaned.length() < 10) {
|
||||
cleaned += "0";
|
||||
}
|
||||
|
||||
// Begrenze auf 10 Stellen
|
||||
if (cleaned.length() > 10) {
|
||||
cleaned = cleaned.substring(0, 10);
|
||||
}
|
||||
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
private List<String> findRelevantNomenclatureCodes(String hsCode) {
|
||||
List<String> codes = new ArrayList<>();
|
||||
codes.add(hsCode);
|
||||
|
||||
// Füge Parent-Codes hinzu (Cascade-Prinzip)
|
||||
// Beispiel: 8504101010 -> auch 85041010, 850410, 8504, 85
|
||||
String code = hsCode;
|
||||
while (code.length() > 2) {
|
||||
// Entferne die letzten 2 Nullen
|
||||
if (code.endsWith("00")) {
|
||||
code = code.substring(0, code.length() - 2);
|
||||
codes.add(code + "0".repeat(10 - code.length()));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return codes;
|
||||
}
|
||||
|
||||
private List<Import> findRelevantImports(List<String> nomenclatureCodes, Geo country) {
|
||||
List<Import> imports = new ArrayList<>();
|
||||
|
||||
for (String code : nomenclatureCodes) {
|
||||
// Suche direkte Länder-Zuordnungen
|
||||
Optional<Nomenclature> nomenclatureOpt = nomenclatureRepository.findByHscode(code);
|
||||
if (nomenclatureOpt.isPresent()) {
|
||||
Nomenclature nomenclature = nomenclatureOpt.get();
|
||||
|
||||
// Suche Imports mit direkter Geo-Zuordnung
|
||||
imports.addAll(importRepository.findByNomenclatureIdAndGeoId(
|
||||
nomenclature.getId(), country.getId()));
|
||||
|
||||
// Suche auch nach Ländergruppen-Mitgliedschaften
|
||||
List<GeoGroupMembership> memberships =
|
||||
geoGroupMembershipRepository.findByGeoId(country.getId());
|
||||
|
||||
for (GeoGroupMembership membership : memberships) {
|
||||
// Hole die geo_group_id aus der membership
|
||||
Integer geoGroupId = geoGroupMembershipRepository
|
||||
.findGeoGroupIdByMembershipId(membership.getId());
|
||||
if (geoGroupId != null) {
|
||||
imports.addAll(importRepository.findByNomenclatureIdAndGeoGroupId(
|
||||
nomenclature.getId(), geoGroupId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
private List<Import> findErgaOmnesImports(List<String> nomenclatureCodes) {
|
||||
List<Import> imports = new ArrayList<>();
|
||||
|
||||
// Erga Omnes hat normalerweise den Code "1011"
|
||||
Optional<GeoGroup> ergaOmnes = geoGroupRepository.findByGeoGroupCode("1011");
|
||||
if (ergaOmnes.isEmpty()) {
|
||||
return imports;
|
||||
}
|
||||
|
||||
for (String code : nomenclatureCodes) {
|
||||
Optional<Nomenclature> nomenclatureOpt = nomenclatureRepository.findByHscode(code);
|
||||
if (nomenclatureOpt.isPresent()) {
|
||||
imports.addAll(importRepository.findByNomenclatureIdAndGeoGroupId(
|
||||
nomenclatureOpt.get().getId(), ergaOmnes.get().getId()));
|
||||
}
|
||||
}
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
private BigDecimal calculateTariffRate(List<Import> imports, Geo country) {
|
||||
BigDecimal lowestRate = null;
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
for (Import imp : imports) {
|
||||
// Nutze die korrekte Repository-Methode mit Import-Reference
|
||||
List<AppliedMeasure> measures = appliedMeasureRepository
|
||||
.findByImport(AggregateReference.to(imp.getId()));
|
||||
|
||||
for (AppliedMeasure appliedMeasure : measures) {
|
||||
// Prüfe ob Maßnahme gültig ist
|
||||
if (!isMeasureValid(appliedMeasure, today)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prüfe ob das Land ausgeschlossen ist
|
||||
if (isCountryExcluded(appliedMeasure, country)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Hole die Maßnahme über die AggregateReference
|
||||
if (appliedMeasure.getMeasure() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Optional<Measure> measureOpt = measureRepository.findById(
|
||||
appliedMeasure.getMeasure().getId());
|
||||
if (measureOpt.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Measure measure = measureOpt.get();
|
||||
|
||||
// Wir interessieren uns hauptsächlich für Third Country Duty (103)
|
||||
// und Preferential Tariff (142, 143)
|
||||
if (!"103".equals(measure.getMeasureCode()) &&
|
||||
!"142".equals(measure.getMeasureCode()) &&
|
||||
!"143".equals(measure.getMeasureCode())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse den Zollsatz aus dem amount String
|
||||
BigDecimal rate = parseTariffRate(appliedMeasure.getAmount());
|
||||
if (rate != null && (lowestRate == null || rate.compareTo(lowestRate) < 0)) {
|
||||
lowestRate = rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lowestRate != null ? lowestRate : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
private boolean isMeasureValid(AppliedMeasure measure, LocalDate date) {
|
||||
if (measure.getStartDate() != null && measure.getStartDate().isAfter(date)) {
|
||||
return false;
|
||||
}
|
||||
if (measure.getEndDate() != null && measure.getEndDate().isBefore(date)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isCountryExcluded(AppliedMeasure measure, Geo country) {
|
||||
// Finde Exclusions für diese AppliedMeasure
|
||||
List<MeasureExclusion> exclusions =
|
||||
measureExclusionRepository.findByAppliedMeasure(
|
||||
AggregateReference.to(measure.getId()));
|
||||
|
||||
return exclusions.stream()
|
||||
.anyMatch(exc -> exc.getGeo() != null &&
|
||||
exc.getGeo().getId().equals(country.getId()));
|
||||
}
|
||||
|
||||
private BigDecimal parseTariffRate(String amount) {
|
||||
if (amount == null || amount.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Einfacher Parser für Prozentsätze
|
||||
// Format: "12.5 %" oder "12.5% + ..." oder "12.5 % MAX ..."
|
||||
Pattern pattern = Pattern.compile("^([0-9]+\\.?[0-9]*)\\s*%");
|
||||
Matcher matcher = pattern.matcher(amount);
|
||||
|
||||
if (matcher.find()) {
|
||||
try {
|
||||
return new BigDecimal(matcher.group(1));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("Could not parse tariff rate from: {}", amount);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result-Klasse für Tarif-Abfragen
|
||||
*/
|
||||
public static class TariffResult {
|
||||
private final boolean found;
|
||||
private final BigDecimal rate;
|
||||
private final String hsCode;
|
||||
private final String countryCode;
|
||||
private final String message;
|
||||
|
||||
private TariffResult(boolean found, BigDecimal rate, String hsCode,
|
||||
String countryCode, String message) {
|
||||
this.found = found;
|
||||
this.rate = rate;
|
||||
this.hsCode = hsCode;
|
||||
this.countryCode = countryCode;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public static TariffResult success(BigDecimal rate, String hsCode, String countryCode) {
|
||||
return new TariffResult(true, rate, hsCode, countryCode, null);
|
||||
}
|
||||
|
||||
public static TariffResult notFound(String message) {
|
||||
return new TariffResult(false, null, null, null, message);
|
||||
}
|
||||
|
||||
// Getters
|
||||
public boolean isFound() { return found; }
|
||||
public BigDecimal getRate() { return rate; }
|
||||
public String getHsCode() { return hsCode; }
|
||||
public String getCountryCode() { return countryCode; }
|
||||
public String getMessage() { return message; }
|
||||
}
|
||||
// /**
|
||||
// * Ermittelt den Zolltarif für einen HS-Code und ein Herkunftsland
|
||||
// *
|
||||
// * @param hsCode HS-Code (kann 6, 8 oder 10 Stellen haben)
|
||||
// * @param countryCode ISO-2 Ländercode (z.B. "CN" für China)
|
||||
// * @return Zolltarif in Prozent
|
||||
// */
|
||||
// @Transactional(readOnly = true)
|
||||
// public TariffResult getTariffRate(String hsCode, String countryCode) {
|
||||
// log.info("Getting tariff rate for HS Code: {} from country: {}", hsCode, countryCode);
|
||||
//
|
||||
// // Normalisiere HS-Code auf 10 Stellen
|
||||
// String normalizedHsCode = normalizeHsCode(hsCode);
|
||||
//
|
||||
// // Finde alle relevanten Nomenclature-Codes (inkl. Parent-Codes durch Cascade-Prinzip)
|
||||
// List<String> relevantCodes = findRelevantNomenclatureCodes(normalizedHsCode);
|
||||
// log.debug("Found relevant codes: {}", relevantCodes);
|
||||
//
|
||||
// // Hole das Land
|
||||
// Optional<Geo> countryOpt = geoRepository.findByIso3166Code(countryCode);
|
||||
// if (countryOpt.isEmpty()) {
|
||||
// log.warn("Country not found: {}", countryCode);
|
||||
// return TariffResult.notFound("Country code not found: " + countryCode);
|
||||
// }
|
||||
//
|
||||
// Geo country = countryOpt.get();
|
||||
//
|
||||
// // Finde alle Imports für die relevanten Codes
|
||||
// List<Import> imports = findRelevantImports(relevantCodes, country);
|
||||
//
|
||||
// if (imports.isEmpty()) {
|
||||
// log.info("No imports found for codes: {} and country: {}", relevantCodes, countryCode);
|
||||
// // Versuche Erga Omnes (alle Länder)
|
||||
// imports = findErgaOmnesImports(relevantCodes);
|
||||
// }
|
||||
//
|
||||
// if (imports.isEmpty()) {
|
||||
// return TariffResult.notFound("No tariff data found for HS code: " + hsCode);
|
||||
// }
|
||||
//
|
||||
// // Finde die anwendbaren Maßnahmen
|
||||
// BigDecimal tariffRate = calculateTariffRate(imports, country);
|
||||
//
|
||||
// return TariffResult.success(tariffRate, normalizedHsCode, countryCode);
|
||||
// }
|
||||
//
|
||||
// private String normalizeHsCode(String hsCode) {
|
||||
// // Entferne alle nicht-numerischen Zeichen
|
||||
// String cleaned = hsCode.replaceAll("[^0-9]", "");
|
||||
//
|
||||
// // Fülle auf 10 Stellen mit Nullen auf
|
||||
// while (cleaned.length() < 10) {
|
||||
// cleaned += "0";
|
||||
// }
|
||||
//
|
||||
// // Begrenze auf 10 Stellen
|
||||
// if (cleaned.length() > 10) {
|
||||
// cleaned = cleaned.substring(0, 10);
|
||||
// }
|
||||
//
|
||||
// return cleaned;
|
||||
// }
|
||||
//
|
||||
// private List<String> findRelevantNomenclatureCodes(String hsCode) {
|
||||
// List<String> codes = new ArrayList<>();
|
||||
// codes.add(hsCode);
|
||||
//
|
||||
// // Füge Parent-Codes hinzu (Cascade-Prinzip)
|
||||
// // Beispiel: 8504101010 -> auch 85041010, 850410, 8504, 85
|
||||
// String code = hsCode;
|
||||
// while (code.length() > 2) {
|
||||
// // Entferne die letzten 2 Nullen
|
||||
// if (code.endsWith("00")) {
|
||||
// code = code.substring(0, code.length() - 2);
|
||||
// codes.add(code + "0".repeat(10 - code.length()));
|
||||
// } else {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return codes;
|
||||
// }
|
||||
//
|
||||
// private List<Import> findRelevantImports(List<String> nomenclatureCodes, Geo country) {
|
||||
// List<Import> imports = new ArrayList<>();
|
||||
//
|
||||
// for (String code : nomenclatureCodes) {
|
||||
// // Suche direkte Länder-Zuordnungen
|
||||
// Optional<Nomenclature> nomenclatureOpt = nomenclatureRepository.findByHscode(code);
|
||||
// if (nomenclatureOpt.isPresent()) {
|
||||
// Nomenclature nomenclature = nomenclatureOpt.get();
|
||||
//
|
||||
// // Suche Imports mit direkter Geo-Zuordnung
|
||||
// imports.addAll(importRepository.findByNomenclatureIdAndGeoId(
|
||||
// nomenclature.getId(), country.getId()));
|
||||
//
|
||||
// // Suche auch nach Ländergruppen-Mitgliedschaften
|
||||
// List<GeoGroupMembership> memberships =
|
||||
// geoGroupMembershipRepository.findByGeoId(country.getId());
|
||||
//
|
||||
// for (GeoGroupMembership membership : memberships) {
|
||||
// // Hole die geo_group_id aus der membership
|
||||
// Integer geoGroupId = geoGroupMembershipRepository
|
||||
// .findGeoGroupIdByMembershipId(membership.getId());
|
||||
// if (geoGroupId != null) {
|
||||
// imports.addAll(importRepository.findByNomenclatureIdAndGeoGroupId(
|
||||
// nomenclature.getId(), geoGroupId));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return imports;
|
||||
// }
|
||||
//
|
||||
// private List<Import> findErgaOmnesImports(List<String> nomenclatureCodes) {
|
||||
// List<Import> imports = new ArrayList<>();
|
||||
//
|
||||
// // Erga Omnes hat normalerweise den Code "1011"
|
||||
// Optional<GeoGroup> ergaOmnes = geoGroupRepository.findByGeoGroupCode("1011");
|
||||
// if (ergaOmnes.isEmpty()) {
|
||||
// return imports;
|
||||
// }
|
||||
//
|
||||
// for (String code : nomenclatureCodes) {
|
||||
// Optional<Nomenclature> nomenclatureOpt = nomenclatureRepository.findByHscode(code);
|
||||
// if (nomenclatureOpt.isPresent()) {
|
||||
// imports.addAll(importRepository.findByNomenclatureIdAndGeoGroupId(
|
||||
// nomenclatureOpt.get().getId(), ergaOmnes.get().getId()));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return imports;
|
||||
// }
|
||||
//
|
||||
// private BigDecimal calculateTariffRate(List<Import> imports, Geo country) {
|
||||
// BigDecimal lowestRate = null;
|
||||
// LocalDate today = LocalDate.now();
|
||||
//
|
||||
// for (Import imp : imports) {
|
||||
// // Nutze die korrekte Repository-Methode mit Import-Reference
|
||||
// List<AppliedMeasure> measures = appliedMeasureRepository
|
||||
// .findByImport(AggregateReference.to(imp.getId()));
|
||||
//
|
||||
// for (AppliedMeasure appliedMeasure : measures) {
|
||||
// // Prüfe ob Maßnahme gültig ist
|
||||
// if (!isMeasureValid(appliedMeasure, today)) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// // Prüfe ob das Land ausgeschlossen ist
|
||||
// if (isCountryExcluded(appliedMeasure, country)) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// // Hole die Maßnahme über die AggregateReference
|
||||
// if (appliedMeasure.getMeasure() == null) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// Optional<Measure> measureOpt = measureRepository.findById(
|
||||
// appliedMeasure.getMeasure().getId());
|
||||
// if (measureOpt.isEmpty()) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// Measure measure = measureOpt.get();
|
||||
//
|
||||
// // Wir interessieren uns hauptsächlich für Third Country Duty (103)
|
||||
// // und Preferential Tariff (142, 143)
|
||||
// if (!"103".equals(measure.getMeasureCode()) &&
|
||||
// !"142".equals(measure.getMeasureCode()) &&
|
||||
// !"143".equals(measure.getMeasureCode())) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// // Parse den Zollsatz aus dem amount String
|
||||
// BigDecimal rate = parseTariffRate(appliedMeasure.getAmount());
|
||||
// if (rate != null && (lowestRate == null || rate.compareTo(lowestRate) < 0)) {
|
||||
// lowestRate = rate;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return lowestRate != null ? lowestRate : BigDecimal.ZERO;
|
||||
// }
|
||||
//
|
||||
// private boolean isMeasureValid(AppliedMeasure measure, LocalDate date) {
|
||||
// if (measure.getStartDate() != null && measure.getStartDate().isAfter(date)) {
|
||||
// return false;
|
||||
// }
|
||||
// if (measure.getEndDate() != null && measure.getEndDate().isBefore(date)) {
|
||||
// return false;
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// private boolean isCountryExcluded(AppliedMeasure measure, Geo country) {
|
||||
// // Finde Exclusions für diese AppliedMeasure
|
||||
// List<MeasureExclusion> exclusions =
|
||||
// measureExclusionRepository.findByAppliedMeasure(
|
||||
// AggregateReference.to(measure.getId()));
|
||||
//
|
||||
// return exclusions.stream()
|
||||
// .anyMatch(exc -> exc.getGeo() != null &&
|
||||
// exc.getGeo().getId().equals(country.getId()));
|
||||
// }
|
||||
//
|
||||
// private BigDecimal parseTariffRate(String amount) {
|
||||
// if (amount == null || amount.isEmpty()) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// // Einfacher Parser für Prozentsätze
|
||||
// // Format: "12.5 %" oder "12.5% + ..." oder "12.5 % MAX ..."
|
||||
// Pattern pattern = Pattern.compile("^([0-9]+\\.?[0-9]*)\\s*%");
|
||||
// Matcher matcher = pattern.matcher(amount);
|
||||
//
|
||||
// if (matcher.find()) {
|
||||
// try {
|
||||
// return new BigDecimal(matcher.group(1));
|
||||
// } catch (NumberFormatException e) {
|
||||
// log.warn("Could not parse tariff rate from: {}", amount);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Result-Klasse für Tarif-Abfragen
|
||||
// */
|
||||
// public static class TariffResult {
|
||||
// private final boolean found;
|
||||
// private final BigDecimal rate;
|
||||
// private final String hsCode;
|
||||
// private final String countryCode;
|
||||
// private final String message;
|
||||
//
|
||||
// private TariffResult(boolean found, BigDecimal rate, String hsCode,
|
||||
// String countryCode, String message) {
|
||||
// this.found = found;
|
||||
// this.rate = rate;
|
||||
// this.hsCode = hsCode;
|
||||
// this.countryCode = countryCode;
|
||||
// this.message = message;
|
||||
// }
|
||||
//
|
||||
// public static TariffResult success(BigDecimal rate, String hsCode, String countryCode) {
|
||||
// return new TariffResult(true, rate, hsCode, countryCode, null);
|
||||
// }
|
||||
//
|
||||
// public static TariffResult notFound(String message) {
|
||||
// return new TariffResult(false, null, null, null, message);
|
||||
// }
|
||||
//
|
||||
// // Getters
|
||||
// public boolean isFound() { return found; }
|
||||
// public BigDecimal getRate() { return rate; }
|
||||
// public String getHsCode() { return hsCode; }
|
||||
// public String getCountryCode() { return countryCode; }
|
||||
// public String getMessage() { return message; }
|
||||
// }
|
||||
}
|
||||
|
|
@ -1 +1,2 @@
|
|||
spring.application.name=taric
|
||||
logging.level.org.springframework.data.jdbc.core.convert.RowDocumentResultSetExtractor=ERROR
|
||||
|
|
@ -73,7 +73,8 @@ create table if not exists `measure`
|
|||
create table if not exists `measure_action`
|
||||
(
|
||||
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
measure_action_code CHAR(2)
|
||||
measure_action_code CHAR(2),
|
||||
CONSTRAINT UC_MeasureAction UNIQUE (measure_action_code)
|
||||
);
|
||||
|
||||
-- geo
|
||||
|
|
@ -82,7 +83,8 @@ create table if not exists `geo`
|
|||
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
iso_3166_code CHAR(2),
|
||||
start_date DATE,
|
||||
end_date DATE
|
||||
end_date DATE,
|
||||
CONSTRAINT UC_IsoCode UNIQUE (iso_3166_code)
|
||||
);
|
||||
|
||||
create table if not exists `geo_group`
|
||||
|
|
@ -91,7 +93,8 @@ create table if not exists `geo_group`
|
|||
geo_group_code CHAR(4),
|
||||
abbr TEXT,
|
||||
start_date DATE,
|
||||
end_date DATE
|
||||
end_date DATE,
|
||||
CONSTRAINT UC_GroupCode UNIQUE (geo_group_code)
|
||||
);
|
||||
|
||||
create table if not exists `geo_group_membership`
|
||||
|
|
@ -102,7 +105,8 @@ create table if not exists `geo_group_membership`
|
|||
start_date DATE,
|
||||
end_date DATE,
|
||||
FOREIGN KEY (geo_id) REFERENCES geo(id),
|
||||
FOREIGN KEY (geo_group_id) REFERENCES geo_group(id)
|
||||
FOREIGN KEY (geo_group_id) REFERENCES geo_group(id),
|
||||
CONSTRAINT UC_GeoGroupTuple UNIQUE (geo_id, geo_group_id)
|
||||
);
|
||||
|
||||
-- nomenclature
|
||||
|
|
@ -134,7 +138,8 @@ create table if not exists `additional_code`
|
|||
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
additional_code CHAR(4),
|
||||
start_date DATE,
|
||||
end_date DATE
|
||||
end_date DATE,
|
||||
CONSTRAINT UC_AdditionalCode UNIQUE (additional_code)
|
||||
);
|
||||
|
||||
-- import, applied_measures
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue