added supplier and part number info to error message
This commit is contained in:
parent
27166e4b00
commit
8763efd8fc
2 changed files with 73 additions and 68 deletions
|
|
@ -13,19 +13,16 @@ import de.avatic.lcc.model.db.properties.SystemPropertyMappingId;
|
|||
import de.avatic.lcc.model.db.rates.ValidityPeriod;
|
||||
import de.avatic.lcc.model.db.rates.ValidityPeriodState;
|
||||
import de.avatic.lcc.model.db.utils.WeightUnit;
|
||||
import de.avatic.lcc.repositories.MaterialRepository;
|
||||
import de.avatic.lcc.repositories.NodeRepository;
|
||||
import de.avatic.lcc.repositories.premise.*;
|
||||
import de.avatic.lcc.repositories.properties.PropertyRepository;
|
||||
import de.avatic.lcc.repositories.properties.PropertySetRepository;
|
||||
import de.avatic.lcc.repositories.rates.ContainerRateRepository;
|
||||
import de.avatic.lcc.repositories.rates.MatrixRateRepository;
|
||||
import de.avatic.lcc.repositories.rates.ValidityPeriodRepository;
|
||||
import de.avatic.lcc.repositories.users.UserNodeRepository;
|
||||
import de.avatic.lcc.service.access.PropertyService;
|
||||
import de.avatic.lcc.service.api.CustomApiService;
|
||||
import de.avatic.lcc.service.api.TaxationResolverService;
|
||||
import de.avatic.lcc.service.transformer.generic.DimensionTransformer;
|
||||
import de.avatic.lcc.util.exception.internalerror.PremiseValidationError;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
|
@ -34,7 +31,6 @@ import java.time.LocalDateTime;
|
|||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Service
|
||||
public class PreCalculationCheckService {
|
||||
|
|
@ -42,7 +38,6 @@ public class PreCalculationCheckService {
|
|||
private static final double DIMENSION_TOLERANCE = 0.02;
|
||||
|
||||
private final PremiseRepository premiseRepository;
|
||||
private final CustomApiService customApiService;
|
||||
private final DestinationRepository destinationRepository;
|
||||
private final RouteRepository routeRepository;
|
||||
private final NodeRepository nodeRepository;
|
||||
|
|
@ -53,14 +48,12 @@ public class PreCalculationCheckService {
|
|||
private final RouteNodeRepository routeNodeRepository;
|
||||
private final MatrixRateRepository matrixRateRepository;
|
||||
private final ContainerRateRepository containerRateRepository;
|
||||
private final ValidityPeriodRepository validityPeriodRepository;
|
||||
private final PropertySetRepository propertySetRepository;
|
||||
private final PropertyRepository propertyRepository;
|
||||
private final TaxationResolverService eUTaxationResolverService;
|
||||
private final MaterialRepository materialRepository;
|
||||
private final UserNodeRepository userNodeRepository;
|
||||
|
||||
public PreCalculationCheckService(PremiseRepository premiseRepository, CustomApiService customApiService, DestinationRepository destinationRepository, RouteRepository routeRepository, NodeRepository nodeRepository, DimensionTransformer dimensionTransformer, PropertyService propertyService, RouteSectionRepository routeSectionRepository, RouteNodeRepository routeNodeRepository, MatrixRateRepository matrixRateRepository, ContainerRateRepository containerRateRepository, ValidityPeriodRepository validityPeriodRepository, PropertySetRepository propertySetRepository, PropertyRepository propertyRepository, TaxationResolverService eUTaxationResolverService) {
|
||||
public PreCalculationCheckService(PremiseRepository premiseRepository, DestinationRepository destinationRepository, RouteRepository routeRepository, NodeRepository nodeRepository, DimensionTransformer dimensionTransformer, PropertyService propertyService, RouteSectionRepository routeSectionRepository, RouteNodeRepository routeNodeRepository, MatrixRateRepository matrixRateRepository, ContainerRateRepository containerRateRepository, PropertyRepository propertyRepository, MaterialRepository materialRepository, UserNodeRepository userNodeRepository) {
|
||||
this.premiseRepository = premiseRepository;
|
||||
this.customApiService = customApiService;
|
||||
this.destinationRepository = destinationRepository;
|
||||
this.routeRepository = routeRepository;
|
||||
this.nodeRepository = nodeRepository;
|
||||
|
|
@ -71,10 +64,9 @@ public class PreCalculationCheckService {
|
|||
this.routeNodeRepository = routeNodeRepository;
|
||||
this.matrixRateRepository = matrixRateRepository;
|
||||
this.containerRateRepository = containerRateRepository;
|
||||
this.validityPeriodRepository = validityPeriodRepository;
|
||||
this.propertySetRepository = propertySetRepository;
|
||||
this.propertyRepository = propertyRepository;
|
||||
this.eUTaxationResolverService = eUTaxationResolverService;
|
||||
this.materialRepository = materialRepository;
|
||||
this.userNodeRepository = userNodeRepository;
|
||||
}
|
||||
|
||||
public void doPrecheck(Integer premiseId, Optional<PropertySet> set, Optional<ValidityPeriod> period) {
|
||||
|
|
@ -87,54 +79,63 @@ public class PreCalculationCheckService {
|
|||
|
||||
supplierCheck(premise);
|
||||
|
||||
materialCheck(premise);
|
||||
var material = materialRepository.getById(premise.getMaterialId());
|
||||
var supplier = premise.getSupplierNodeId() == null ? userNodeRepository.getById(premise.getUserSupplierNodeId()) : nodeRepository.getById(premise.getSupplierNodeId());
|
||||
|
||||
packagingCheck(premise);
|
||||
if(material.isEmpty() || supplier.isEmpty())
|
||||
throw new PremiseValidationError("No material or supplier - please contact your administrator");
|
||||
|
||||
priceCheck(premise);
|
||||
var partNumber = material.get().getPartNumber();
|
||||
var supplierName = supplier.get().getName();
|
||||
|
||||
materialCheck(premise, supplierName, partNumber);
|
||||
|
||||
packagingCheck(premise, supplierName, partNumber);
|
||||
|
||||
priceCheck(premise, supplierName, partNumber);
|
||||
|
||||
var destinations = destinationRepository.getByPremiseId(premiseId);
|
||||
|
||||
if (destinations == null || destinations.isEmpty()) {
|
||||
throw new PremiseValidationError("Please add at least one destination to continue");
|
||||
throw new PremiseValidationError("Please add at least one destination to continue", supplierName, partNumber);
|
||||
}
|
||||
|
||||
for (Destination destination : destinations) {
|
||||
|
||||
var node = nodeRepository.getByDestinationId(destination.getId()).orElseThrow();
|
||||
|
||||
destinationCheck(destination, node);
|
||||
destinationCheck(destination, node, supplierName, partNumber);
|
||||
|
||||
var routes = routeRepository.getByDestinationId(destination.getId());
|
||||
|
||||
|
||||
if (routes.isEmpty() && destination.getD2d() == false)
|
||||
throw new PremiseValidationError(String.format("No standard route found for %s - try using an individual rate instead", node.getName()));
|
||||
throw new PremiseValidationError(String.format("No standard route found for %s - try using an individual rate instead", node.getName()), supplierName, partNumber);
|
||||
|
||||
if (routes.stream().noneMatch(Route::getSelected) && destination.getD2d() == false)
|
||||
throw new PremiseValidationError(String.format("Please select a route for %s", node.getName()));
|
||||
throw new PremiseValidationError(String.format("Please select a route for %s", node.getName()), supplierName, partNumber);
|
||||
|
||||
if (destination.getD2d() && (destination.getRateD2d() == null || destination.getRateD2d().compareTo(BigDecimal.ZERO) == 0)) {
|
||||
throw new PremiseValidationError("Please enter a door-to-door rate greater than zero");
|
||||
throw new PremiseValidationError("Please enter a door-to-door rate greater than zero", supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (destination.getD2d() && (destination.getLeadTimeD2d() == null || destination.getLeadTimeD2d() == 0)) {
|
||||
throw new PremiseValidationError("Please enter a door-to-door lead time");
|
||||
throw new PremiseValidationError("Please enter a door-to-door lead time", supplierName, partNumber);
|
||||
}
|
||||
|
||||
periodCheck(period.orElse(null), set.orElse(null), date);
|
||||
periodCheck(period.orElse(null), set.orElse(null), date, supplierName, partNumber);
|
||||
|
||||
routes.stream().filter(Route::getSelected).findAny().ifPresent(r -> {
|
||||
var sections = routeSectionRepository.getByRouteId(r.getId());
|
||||
routeCheck(sections, period.orElseThrow(), date);
|
||||
routeCheck(sections, period.orElseThrow(), date, supplierName, partNumber);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void periodCheck(ValidityPeriod period, PropertySet set, LocalDate calculationDate) {
|
||||
private void periodCheck(ValidityPeriod period, PropertySet set, LocalDate calculationDate, String supplierName, String partNumber) {
|
||||
|
||||
if (set == null)
|
||||
throw new PremiseValidationError("No system data available for this date - please contact your administrator");
|
||||
throw new PremiseValidationError("No system configuration available for this date - please contact your administrator");
|
||||
|
||||
if (period == null)
|
||||
throw new PremiseValidationError("No rates available for this date - please contact your administrator");
|
||||
|
|
@ -152,7 +153,7 @@ public class PreCalculationCheckService {
|
|||
var renewals = period.getRenewals();
|
||||
|
||||
if (validDays.isEmpty())
|
||||
throw new PremiseValidationError("Missing configuration - please contact your administrator");
|
||||
throw new PremiseValidationError("Missing system configuration (VALID_DAYS) - please contact your administrator");
|
||||
|
||||
var validDaysInt = Integer.parseInt(validDays.get().getCurrentValue());
|
||||
|
||||
|
|
@ -162,74 +163,74 @@ public class PreCalculationCheckService {
|
|||
|
||||
}
|
||||
|
||||
private void routeCheck(List<RouteSection> sections, ValidityPeriod period, LocalDate calculationDate) {
|
||||
private void routeCheck(List<RouteSection> sections, ValidityPeriod period, LocalDate calculationDate, String supplierName, String partNumber) {
|
||||
|
||||
sections.forEach(section -> {
|
||||
var fromRouteNode = routeNodeRepository.getFromNodeBySectionId(section.getId());
|
||||
var toRouteNode = routeNodeRepository.getToNodeBySectionId(section.getId());
|
||||
|
||||
if (fromRouteNode.isEmpty() || toRouteNode.isEmpty())
|
||||
throw new PremiseValidationError("Route configuration issue - please contact your administrator");
|
||||
throw new PremiseValidationError("Route configuration issue - please contact your administrator", supplierName, partNumber);
|
||||
|
||||
if (RateType.MATRIX == section.getRateType()) {
|
||||
var rate = matrixRateRepository.getByCountryIds(fromRouteNode.get().getCountryId(), toRouteNode.get().getCountryId(), period.getId());
|
||||
constructRouteSectionError(calculationDate, fromRouteNode.get(), toRouteNode.get(), rate.isEmpty());
|
||||
constructRouteSectionError(calculationDate, fromRouteNode.get(), toRouteNode.get(), rate.isEmpty(), supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (RateType.CONTAINER == section.getRateType()) {
|
||||
var rate = containerRateRepository.findRoute(fromRouteNode.get().getNodeId(), toRouteNode.get().getNodeId(), period.getId(), section.getTransportType());
|
||||
constructRouteSectionError(calculationDate, fromRouteNode.get(), toRouteNode.get(), rate.isEmpty());
|
||||
constructRouteSectionError(calculationDate, fromRouteNode.get(), toRouteNode.get(), rate.isEmpty(), supplierName, partNumber);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void constructRouteSectionError(LocalDate calculationDate, RouteNode fromRouteNode, RouteNode toRouteNode, boolean empty) {
|
||||
private void constructRouteSectionError(LocalDate calculationDate, RouteNode fromRouteNode, RouteNode toRouteNode, boolean empty, String supplierName, String partNumber) {
|
||||
if (empty) {
|
||||
var dateStr = calculationDate == null ? "" : String.format("on %s", calculationDate.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")));
|
||||
var errStr = String.format("Transport rate missing for %s to %s %s - please contact your administrator",
|
||||
fromRouteNode.getExternalMappingId(),
|
||||
toRouteNode.getExternalMappingId(),
|
||||
dateStr);
|
||||
throw new PremiseValidationError(errStr);
|
||||
throw new PremiseValidationError(errStr, supplierName, partNumber);
|
||||
}
|
||||
}
|
||||
|
||||
private void destinationCheck(Destination destination, Node node) {
|
||||
private void destinationCheck(Destination destination, Node node, String supplierName, String partNumber) {
|
||||
|
||||
if (destination.getAnnualAmount() == null || destination.getAnnualAmount() == 0)
|
||||
throw new PremiseValidationError(String.format("Annual quantity for %s must be greater than zero", node.getName()));
|
||||
throw new PremiseValidationError(String.format("Annual quantity for %s must be greater than zero", node.getName()), supplierName, partNumber);
|
||||
|
||||
if (destination.getD2d() == null)
|
||||
throw new PremiseValidationError(String.format("Something's missing for %s - please contact your administrator", node.getName()));
|
||||
throw new PremiseValidationError(String.format("Something's missing for %s - please contact your administrator", node.getName()), supplierName, partNumber);
|
||||
|
||||
if (destination.getD2d() == true) {
|
||||
if (destination.getRateD2d() == null || destination.getRateD2d().compareTo(BigDecimal.ZERO) == 0) {
|
||||
throw new PremiseValidationError(String.format("Door-to-door rate for %s needs to be greater than zero", node.getName()));
|
||||
throw new PremiseValidationError(String.format("Door-to-door rate for %s needs to be greater than zero", node.getName()), supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (destination.getLeadTimeD2d() == null || destination.getLeadTimeD2d() == 0) {
|
||||
throw new PremiseValidationError(String.format("Please set a lead time for door-to-door delivery to %s", node.getName()));
|
||||
throw new PremiseValidationError(String.format("Please set a lead time for door-to-door delivery to %s", node.getName()), supplierName, partNumber);
|
||||
}
|
||||
}
|
||||
|
||||
if (destination.getCountryId() == null || destination.getCountryId() == 0) {
|
||||
throw new PremiseValidationError(String.format("Configuration issue with %s - please contact your administrator", node.getName()));
|
||||
throw new PremiseValidationError(String.format("Configuration issue with %s - please contact your administrator", node.getName()), supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (destination.getGeoLat() == null || destination.getGeoLng() == null) {
|
||||
throw new PremiseValidationError(String.format("Location data missing for %s - please contact your administrator", node.getName()));
|
||||
throw new PremiseValidationError(String.format("Location data missing for %s - please contact your administrator", node.getName()), supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (destination.getDisposalCost() != null && destination.getDisposalCost().compareTo(BigDecimal.ZERO) < 0) {
|
||||
throw new PremiseValidationError(String.format("Disposal costs for %s can't be negative", node.getName()));
|
||||
throw new PremiseValidationError(String.format("Disposal costs for %s can't be negative", node.getName()), supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (destination.getHandlingCost() != null && destination.getHandlingCost().compareTo(BigDecimal.ZERO) < 0) {
|
||||
throw new PremiseValidationError(String.format("Handling costs for %s can't be negative", node.getName()));
|
||||
throw new PremiseValidationError(String.format("Handling costs for %s can't be negative", node.getName()), supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (destination.getRepackingCost() != null && destination.getRepackingCost().compareTo(BigDecimal.ZERO) < 0) {
|
||||
throw new PremiseValidationError(String.format("Repackaging costs for %s can't be negative", node.getName()));
|
||||
throw new PremiseValidationError(String.format("Repackaging costs for %s can't be negative", node.getName()), supplierName, partNumber);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -241,62 +242,62 @@ public class PreCalculationCheckService {
|
|||
|
||||
}
|
||||
|
||||
private void priceCheck(Premise premise) {
|
||||
private void priceCheck(Premise premise, String supplierName, String partNumber) {
|
||||
|
||||
if (premise.getMaterialCost() == null || premise.getMaterialCost().compareTo(BigDecimal.ZERO) == 0) {
|
||||
throw new PremiseValidationError("Please enter a material cost (MEK_A) greater than zero");
|
||||
throw new PremiseValidationError("Please enter a material cost (MEK_A) greater than zero", supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (premise.getOverseaShare() == null) {
|
||||
throw new PremiseValidationError("Please enter the overseas share");
|
||||
throw new PremiseValidationError("Please enter the overseas share", supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (premise.getFcaEnabled() == null) {
|
||||
throw new PremiseValidationError("FCA configuration missing - please contact your administrator");
|
||||
throw new PremiseValidationError("FCA configuration missing - please contact your administrator", supplierName, partNumber);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void packagingCheck(Premise premise) {
|
||||
private void packagingCheck(Premise premise, String supplierName, String partNumber) {
|
||||
|
||||
if (premise.getHuMixable() == null) {
|
||||
throw new PremiseValidationError("Packaging configuration incomplete - please contact your administrator");
|
||||
throw new PremiseValidationError("Packaging configuration incomplete - please contact your administrator", supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (premise.getHuStackable() == null) {
|
||||
throw new PremiseValidationError("Packaging configuration incomplete - please contact your administrator");
|
||||
throw new PremiseValidationError("Packaging configuration incomplete - please contact your administrator", supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (premise.getHuStackable() == false && premise.getHuMixable() == true) {
|
||||
throw new PremiseValidationError("If packaging is mixable, it must also be stackable");
|
||||
throw new PremiseValidationError("If packaging is mixable, it must also be stackable", supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (premise.getIndividualHuLength() == null || premise.getIndividualHuLength() == 0) {
|
||||
throw new PremiseValidationError("Please enter packaging length");
|
||||
throw new PremiseValidationError("Please enter packaging length", supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (premise.getIndividualHuWidth() == null || premise.getIndividualHuWidth() == 0) {
|
||||
throw new PremiseValidationError("Please enter packaging width");
|
||||
throw new PremiseValidationError("Please enter packaging width", supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (premise.getIndividualHuHeight() == null || premise.getIndividualHuHeight() == 0) {
|
||||
throw new PremiseValidationError("Please enter packaging height");
|
||||
throw new PremiseValidationError("Please enter packaging height", supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (premise.getIndividualHuWeight() == null || premise.getIndividualHuWeight() == 0) {
|
||||
throw new PremiseValidationError("Please enter packaging weight");
|
||||
throw new PremiseValidationError("Please enter packaging weight", supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (premise.getHuUnitCount() == null || premise.getHuUnitCount() == 0) {
|
||||
throw new PremiseValidationError("Please enter the number of units per package");
|
||||
throw new PremiseValidationError("Please enter the number of units per package", supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (premise.getHuDisplayedWeightUnit() == null) {
|
||||
throw new PremiseValidationError("Weight unit configuration missing - please contact your administrator");
|
||||
throw new PremiseValidationError("Weight unit configuration missing - please contact your administrator", supplierName, partNumber);
|
||||
}
|
||||
|
||||
if (premise.getHuDisplayedDimensionUnit() == null) {
|
||||
throw new PremiseValidationError("Dimension unit configuration missing - please contact your administrator");
|
||||
throw new PremiseValidationError("Dimension unit configuration missing - please contact your administrator", supplierName, partNumber);
|
||||
}
|
||||
|
||||
var hu = dimensionTransformer.toDimensionEntity(premise).withTolerance(DIMENSION_TOLERANCE);
|
||||
|
|
@ -305,38 +306,38 @@ public class PreCalculationCheckService {
|
|||
Optional<Integer> feuLoad = propertyService.getProperty(SystemPropertyMappingId.TEU_LOAD);
|
||||
|
||||
if (teuLoad.isEmpty() || feuLoad.isEmpty())
|
||||
throw new PremiseValidationError("System configuration incomplete - please contact your administrator");
|
||||
throw new PremiseValidationError("System configuration incomplete - please contact your administrator", supplierName, partNumber);
|
||||
|
||||
if (WeightUnit.KG.convertFromG(hu.getWeight()) > teuLoad.get() && hu.getWeight() > feuLoad.get())
|
||||
throw new PremiseValidationError(String.format("Package weight exceeds %d kg - please check your weight and unit", Math.max(teuLoad.get(), feuLoad.get())));
|
||||
throw new PremiseValidationError(String.format("Package weight exceeds %d kg - please check your weight and unit", Math.max(teuLoad.get(), feuLoad.get())), supplierName, partNumber);
|
||||
|
||||
var teuFitsXY = (hu.getLength() < ContainerType.TEU.getLength() && hu.getWidth() < ContainerType.TEU.getWidth());
|
||||
var teuFitsYX = (hu.getWidth() < ContainerType.TEU.getLength() && hu.getLength() < ContainerType.TEU.getWidth());
|
||||
|
||||
if (!teuFitsYX && !teuFitsXY) {
|
||||
throw new PremiseValidationError("Package dimensions are too large - please check your measurements and unit");
|
||||
throw new PremiseValidationError("Package dimensions are too large - please check your measurements and unit", supplierName, partNumber);
|
||||
}
|
||||
|
||||
|
||||
if ((hu.getLength() * hu.getWidth()) < 20000) {
|
||||
throw new PremiseValidationError("Package dimensions are too small - please check your measurements and unit");
|
||||
throw new PremiseValidationError("Package dimensions are too small - please check your measurements and unit", supplierName, partNumber);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void materialCheck(Premise premise) {
|
||||
private void materialCheck(Premise premise, String supplierName, String partNumber) {
|
||||
|
||||
// if (premise.getTariffUnlocked()) {
|
||||
// if (premise.getHsCode() == null || premise.getHsCode().length() < 10)
|
||||
// throw new PremiseValidationError("Invalid HS code (10 digits expected).");
|
||||
// throw new PremiseValidationError("Invalid HS code (10 digits expected).", supplierName, partNumber);
|
||||
//
|
||||
// var isDeclarable = eUTaxationResolverService.validate(premise.getHsCode());
|
||||
//
|
||||
// if (!isDeclarable) throw new PremiseValidationError("Invalid HS code (not declarable).");
|
||||
// if (!isDeclarable) throw new PremiseValidationError("Invalid HS code (not declarable).", supplierName, partNumber);
|
||||
// }
|
||||
|
||||
if (premise.getTariffRate() == null) {
|
||||
throw new PremiseValidationError("Please enter a tariff rate");
|
||||
throw new PremiseValidationError("Please enter a tariff rate", supplierName, partNumber);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import de.avatic.lcc.util.exception.base.InternalErrorException;
|
|||
|
||||
public class PremiseValidationError extends InternalErrorException {
|
||||
|
||||
public PremiseValidationError(String message, String supplierName, String partNumber) {
|
||||
super("Calculation data validation failed.", String.format("%s (Part number:%s - %s)",message, partNumber, supplierName));
|
||||
}
|
||||
|
||||
public PremiseValidationError(String message) {
|
||||
super("Calculation data validation failed.", message);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue