private String getPriceListVersion(String priceList, String clientId) {
    try {
      String whereClause =
          " as plv left outer join plv.priceList pl where plv.active='Y' and plv.active='Y' and "
              + " pl.id = :priceList and plv.client.id = :clientId order by plv.validFromDate desc";

      OBQuery<PriceListVersion> ppriceListVersion =
          OBDal.getInstance().createQuery(PriceListVersion.class, whereClause);
      ppriceListVersion.setNamedParameter("priceList", priceList);
      ppriceListVersion.setNamedParameter("clientId", clientId);

      if (!ppriceListVersion.list().isEmpty()) {
        return ppriceListVersion.list().get(0).getId();
      } else {
        return "0";
      }
    } catch (Exception e) {
      throw new OBException(e);
    }
  }
  /**
   * Returns a message in the right language with parameter substitution. Each occurence of a %
   * parameter (%0, %1 etc) is replaced with the corresponding parameter value. in the params array.
   *
   * @param key the key of the message
   * @param params the parameters to substitute in the message
   * @return the translated message with the parameters substituted
   */
  public static String getI18NMessage(String key, String[] params) {
    OBContext.setAdminMode();
    try {

      // first read the labels from the base table
      final OBQuery<Message> messages =
          OBDal.getInstance().createQuery(Message.class, Message.PROPERTY_SEARCHKEY + "=:key");
      messages.setNamedParameter("key", key);
      if (messages.list().isEmpty()) {
        return null;
      }

      if (messages.list().size() > 1) {
        log4j.warn("More than one message found using key " + key);
      }

      // pick the first one
      final Message message = messages.list().get(0);
      String label = message.getMessageText();
      final String languageId = OBContext.getOBContext().getLanguage().getId();
      for (MessageTrl messageTrl : message.getADMessageTrlList()) {
        if (DalUtil.getId(messageTrl.getLanguage()).equals(languageId)) {
          label = messageTrl.getMessageText();
          break;
        }
      }
      // parameter substitution
      if (params != null && params.length > 0) {
        int cnt = 0;
        for (String param : params) {
          label = label.replace("%" + cnt++, param);
        }
      }
      return label;
    } catch (Exception e) {
      throw new OBException("Exception when getting message for key: " + key, e);
    } finally {
      OBContext.restorePreviousMode();
    }
  }
  @Override
  protected List<Map<String, Object>> getData(
      Map<String, String> parameters, int startRow, int endRow) {
    OBContext.setAdminMode(true);
    final List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
    try {
      readCriteria(parameters);
      final String strProductId = parameters.get("@Product.id@");
      final Product product = OBDal.getInstance().get(Product.class, strProductId);

      int totalMaxLength = product.getSearchKey().length();
      long variantNumber = 1;
      Map<ProductCharacteristic, ProductCharacteristicAux> prChUseCode =
          new HashMap<ProductCharacteristic, ProductCharacteristicAux>();

      OBCriteria<ProductCharacteristic> prChCrit =
          OBDal.getInstance().createCriteria(ProductCharacteristic.class);
      prChCrit.add(Restrictions.eq(ProductCharacteristic.PROPERTY_PRODUCT, product));
      prChCrit.add(Restrictions.eq(ProductCharacteristic.PROPERTY_VARIANT, true));
      prChCrit.addOrderBy(ProductCharacteristic.PROPERTY_SEQUENCENUMBER, true);
      List<ProductCharacteristic> prChs = prChCrit.list();
      int chNumber = prChs.size();
      if (chNumber == 0) {
        return result;
      }
      ProductCharacteristicConf[] currentValues = new ProductCharacteristicConf[chNumber];
      boolean includeInResult = true;

      int i = 0;
      for (ProductCharacteristic prCh : prChs) {
        OBCriteria<ProductCharacteristicConf> prChConfCrit =
            OBDal.getInstance().createCriteria(ProductCharacteristicConf.class);
        prChConfCrit.add(
            Restrictions.eq(ProductCharacteristicConf.PROPERTY_CHARACTERISTICOFPRODUCT, prCh));
        List<ProductCharacteristicConf> prChConfs = prChConfCrit.list();
        long valuesCount = prChConfs.size();

        boolean useCode = true;
        int maxLength = 0;
        for (ProductCharacteristicConf prChConf : prChConfs) {
          if (StringUtils.isBlank(prChConf.getCode())) {
            useCode = false;
            break;
          }
          if (prChConf.getCode().length() > maxLength) {
            maxLength = prChConf.getCode().length();
          }
        }

        variantNumber = variantNumber * valuesCount;
        if (useCode) {
          totalMaxLength += maxLength;
        }
        List<CharacteristicValue> filteredValues =
            selectedChValues.get(prCh.getCharacteristic().getId());
        ProductCharacteristicAux prChAux =
            new ProductCharacteristicAux(useCode, prChConfs, filteredValues);
        currentValues[i] = prChAux.getNextValue();
        if (filteredValues != null) {
          includeInResult =
              includeInResult && filteredValues.contains(currentValues[i].getCharacteristicValue());
        }

        prChUseCode.put(prCh, prChAux);
        i++;
      }
      totalMaxLength += Long.toString(variantNumber).length();
      boolean useCodes = totalMaxLength <= searchKeyLength;

      boolean hasNext = true;
      int productNo = 0;
      do {
        // reset boolean value.
        includeInResult = true;
        // Create variant product
        Map<String, Object> variantMap = new HashMap<String, Object>();
        variantMap.put("Client", product.getClient());
        variantMap.put("Organization", product.getOrganization());
        variantMap.put("Active", "Y");
        variantMap.put("creationDate", new Date());
        variantMap.put("createdBy", OBContext.getOBContext().getUser());
        variantMap.put("updated", new Date());
        variantMap.put("updatedBy", OBContext.getOBContext().getUser());
        variantMap.put("name", product.getName());
        variantMap.put("variantCreated", false);
        variantMap.put("obSelected", false);

        String searchKey = product.getSearchKey();
        for (i = 0; i < chNumber; i++) {
          ProductCharacteristicConf prChConf = currentValues[i];
          ProductCharacteristicAux prChConfAux = prChUseCode.get(prChs.get(i));
          List<CharacteristicValue> filteredValues = prChConfAux.getFilteredValues();
          if (filteredValues != null) {
            includeInResult =
                includeInResult
                    && filteredValues.contains(currentValues[i].getCharacteristicValue());
          }

          if (useCodes && prChConfAux.isUseCode()) {
            searchKey += prChConf.getCode();
          }
        }
        for (int j = 0;
            j < (Long.toString(variantNumber).length() - Integer.toString(productNo).length());
            j++) {
          searchKey += "0";
        }
        searchKey += productNo;
        variantMap.put("searchKey", searchKey);

        StringBuffer where = new StringBuffer();
        where.append(" as p ");
        where.append(" where p." + Product.PROPERTY_GENERICPRODUCT + " = :product");

        String strChDesc = "";
        String strKeyId = "";
        JSONArray valuesArray = new JSONArray();
        for (i = 0; i < chNumber; i++) {
          ProductCharacteristicConf prChConf = currentValues[i];
          Characteristic characteristic = prChConf.getCharacteristicOfProduct().getCharacteristic();
          where.append(buildExistsClause(i));
          if (StringUtils.isNotBlank(strChDesc)) {
            strChDesc += ", ";
          }
          strChDesc += characteristic.getName() + ":";
          strChDesc += " " + prChConf.getCharacteristicValue().getName();
          strKeyId += prChConf.getCharacteristicValue().getId();
          JSONObject value = new JSONObject();
          value.put("characteristic", characteristic.getId());
          value.put("characteristicValue", prChConf.getCharacteristicValue().getId());
          value.put("characteristicConf", prChConf.getId());
          valuesArray.put(value);
        }
        variantMap.put("characteristicArray", valuesArray);
        variantMap.put("characteristicDescription", strChDesc);
        variantMap.put("id", strKeyId);

        OBQuery<Product> variantQry =
            OBDal.getInstance().createQuery(Product.class, where.toString());
        variantQry.setNamedParameter("product", product);
        for (i = 0; i < chNumber; i++) {
          ProductCharacteristicConf prChConf = currentValues[i];
          Characteristic characteristic = prChConf.getCharacteristicOfProduct().getCharacteristic();
          variantQry.setNamedParameter("ch" + i, characteristic.getId());
          variantQry.setNamedParameter("chvalue" + i, prChConf.getCharacteristicValue().getId());
        }
        Product existingProduct = variantQry.uniqueResult();
        if (existingProduct != null) {
          variantMap.put("name", existingProduct.getName());
          variantMap.put("searchKey", existingProduct.getSearchKey());
          variantMap.put("variantCreated", true);
          variantMap.put("variantId", existingProduct.getId());
          variantMap.put("id", existingProduct.getId());
        }
        if (StringUtils.isNotEmpty(searchKeyFilter)) {
          includeInResult =
              includeInResult
                  && StringUtils.contains((String) variantMap.get("searchKey"), searchKeyFilter);
        }
        if (StringUtils.isNotEmpty(nameFilter)) {
          includeInResult =
              includeInResult && StringUtils.contains((String) variantMap.get("name"), nameFilter);
        }
        if (variantCreated != null) {
          includeInResult = includeInResult && variantCreated == (existingProduct != null);
        }
        if (!selectedIds.isEmpty()) {
          includeInResult = includeInResult || selectedIds.contains(variantMap.get("id"));
        }

        if (includeInResult) {
          result.add(variantMap);
        }

        for (i = 0; i < chNumber; i++) {
          ProductCharacteristicAux prChConfAux = prChUseCode.get(prChs.get(i));
          currentValues[i] = prChConfAux.getNextValue();
          if (!prChConfAux.isIteratorReset()) {
            break;
          } else if (i + 1 == chNumber) {
            hasNext = false;
          }
        }
        productNo++;
      } while (hasNext);
      String strSortBy = parameters.get("_sortBy");
      if (strSortBy == null) {
        strSortBy = "characteristicDescription";
      }
      boolean ascending = true;
      if (strSortBy.startsWith("-")) {
        ascending = false;
        strSortBy = strSortBy.substring(1);
      }

      Collections.sort(result, new ResultComparator(strSortBy, ascending));

    } catch (JSONException e) {
      // Do nothing
    } finally {
      OBContext.restorePreviousMode();
    }
    return result;
  }