# HG changeset patch
# User Naroa Iriarte <naroa.iriarte@openbravo.com>
# Date 1441367018 -7200
#      Fri Sep 04 13:43:38 2015 +0200
# Node ID 196b1e774d1395d9b7f1228908edf1233345e13f
# Parent  496c8deb9c514c78efe0c8c17785c2bf16b7babc
Fixed issue 30761: Product characteristics popup does not work properly.

If any FK filter is used, the values shown in the other filters are restricted by them. This was not
happening with the product characteristics popu. In this case the whole tree was shown.

To fix this some steps have been done.
First of all it was necessary to change the where clause to take into account the filters applied in the grid.
On the other hand it has been necessary to take into account when a filter was applied in the "Product Characteristics"
column, because if at first you choose a value of a product characteristic, the second time you open the popup of
product characteristics, it is not possible to see any other product characteristic but the one previously chosen. That
was not working fine, it only has to take into account the filters applied in the grid.
To fix this, the product characteristics criteria has been removed if it existed one, and these all changes make possible to
have the values of the product characteristics filtered applying the filters of the grid but not taking into account the value
chosen in the first moment in the product characteristics popup.

diff -r 496c8deb9c51 -r 196b1e774d13 modules/org.openbravo.client.application/web/org.openbravo.client.application/js/form/formitem/ob-formitem-characteristics.js
--- a/modules/org.openbravo.client.application/web/org.openbravo.client.application/js/form/formitem/ob-formitem-characteristics.js	Wed Sep 02 15:20:40 2015 +0000
+++ b/modules/org.openbravo.client.application/web/org.openbravo.client.application/js/form/formitem/ob-formitem-characteristics.js	Fri Sep 04 13:43:38 2015 +0200
@@ -11,7 +11,7 @@
  * under the License.
  * The Original Code is Openbravo ERP.
  * The Initial Developer of the Original Code is Openbravo SLU
- * All portions are Copyright (C) 2013-2014 Openbravo SLU
+ * All portions are Copyright (C) 2013-2015 Openbravo SLU
  * All Rights Reserved.
  * Contributor(s):  ______________________________________.
  ************************************************************************
@@ -344,7 +344,11 @@
           treeField.escapeHTML = true;
           fields = [treeField];
         }
-        return this.Super("setDataSource", [ds, fields]);
+
+        ds.requestProperties.params._parentDSIdentifier = me.parentDSIdentifier;
+        ds.requestProperties.params._propertyPath = me.propertyName;
+
+        return this.Super('setDataSource', [ds, fields]);
       }
     });
 
@@ -408,6 +412,7 @@
   hqlExists: 'exists (from ProductCharacteristicValue v where {productInEntity} = v.product and v.characteristicValue.id in ($value))',
   showPickerIcon: false,
   filterDialogConstructor: isc.OBCharacteristicsFilterDialog,
+  propertyName: null,
   pickerIconDefaults: {
     name: 'showDateRange',
     src: OB.Styles.skinsPath + 'Default/org.openbravo.client.application/images/form/productCharacteristicsFilter_ico.png',
@@ -449,6 +454,7 @@
       _constructor: 'AdvancedCriteria',
       operator: 'and',
       internalValue: this.internalValue,
+      isProductCharacteristicsCriteria: true,
       criteria: []
     };
 
@@ -510,22 +516,34 @@
   },
 
   init: function () {
-    var propertyPath, propertyName, i;
+    var propertyPath, i, parentDSIdentifier = null;
 
     // Getting the product property in the entity we are filtering it.
     // It is obtained based on fieldName, in case its path is compound (i.e.
     // product$characteristicDescription), path is included up to the element
     // previous to the last one
-    propertyName = 'e'; // "e" is the base entity
+    this.propertyName = 'e'; // "e" is the base entity
     propertyPath = this.getFieldName().split(OB.Constants.FIELDSEPARATOR);
     for (i = 0; i < propertyPath.length - 1; i++) {
-      propertyName += '.' + propertyPath[i];
+      this.propertyName += '.' + propertyPath[i];
     }
-    this.hqlExists = this.hqlExists.replace('{productInEntity}', propertyName);
+    this.hqlExists = this.hqlExists.replace('{productInEntity}', this.propertyName);
+
+    if (this.grid.parentElement.view && this.grid.parentElement.view.viewGrid) {
+      this.parentGrid = this.grid.parentElement.view.viewGrid;
+    } else {
+      this.parentGrid = this.grid.parentElement;
+    }
+
+    if (this.parentGrid.getDataSource().dataURL.indexOf('org.openbravo.service.datasource') !== -1) {
+      parentDSIdentifier = this.parentGrid.getDataSource().dataURL.substr(this.parentGrid.getDataSource().dataURL.lastIndexOf('/') + 1);
+    }
 
     this.addAutoChild('filterDialog', {
       title: this.title,
-      callback: this.getID() + '.filterDialogCallback(value)'
+      callback: this.getID() + '.filterDialogCallback(value)',
+      parentDSIdentifier: parentDSIdentifier,
+      propertyName: this.propertyName
     });
 
     this.icons = [isc.addProperties({
@@ -535,7 +553,17 @@
     this.Super('init', arguments);
   },
 
+  removeProductCharacteristicsCriteria: function (fullCriteria) {
+    var newCriteria = isc.shallowClone(fullCriteria);
+    if (fullCriteria.criteria && fullCriteria.criteria.find('isProductCharacteristicsCriteria', true)) {
+      newCriteria.criteria.remove(newCriteria.criteria.find('isProductCharacteristicsCriteria', true));
+    }
+    return newCriteria;
+  },
+
   showDialog: function () {
+    var criteria = this.removeProductCharacteristicsCriteria(this.parentGrid.getCriteria());
+    this.filterDialog.tree.fetchData(criteria);
     this.filterDialog.show();
   },
 
diff -r 496c8deb9c51 -r 196b1e774d13 modules/org.openbravo.service.json/src/org/openbravo/service/json/DataEntityQueryService.java
--- a/modules/org.openbravo.service.json/src/org/openbravo/service/json/DataEntityQueryService.java	Wed Sep 02 15:20:40 2015 +0000
+++ b/modules/org.openbravo.service.json/src/org/openbravo/service/json/DataEntityQueryService.java	Fri Sep 04 13:43:38 2015 +0200
@@ -11,7 +11,7 @@
  * under the License. 
  * The Original Code is Openbravo ERP. 
  * The Initial Developer of the Original Code is Openbravo SLU 
- * All portions are Copyright (C) 2009-2014 Openbravo SLU 
+ * All portions are Copyright (C) 2009-2015 Openbravo SLU 
  * All Rights Reserved. 
  * Contributor(s):  ______________________________________.
  ************************************************************************
@@ -118,11 +118,15 @@
     return qry.scroll(ScrollMode.FORWARD_ONLY);
   }
 
+  public String getWhereClause() {
+    return queryBuilder.getJoinClause() + queryBuilder.getWhereClause();
+  }
+
   /**
    * Build an OBQuery object using the generated where, order by and select clauses.
    */
   public OBQuery<BaseOBObject> buildOBQuery() {
-    final String whereOrderBy = queryBuilder.getJoinClause() + queryBuilder.getWhereClause()
+    final String whereOrderBy = getWhereClause()
         + (getSummarySettings() == null ? queryBuilder.getOrderByClause() : "");
 
     log.debug("Querying for " + entityName + " " + whereOrderBy);
@@ -162,7 +166,7 @@
     return obq;
   }
 
-  AdvancedQueryBuilder getQueryBuilder() {
+  public AdvancedQueryBuilder getQueryBuilder() {
     return queryBuilder;
   }
 
diff -r 496c8deb9c51 -r 196b1e774d13 src/org/openbravo/materialmgmt/ProductCharacteristicsDS.java
--- a/src/org/openbravo/materialmgmt/ProductCharacteristicsDS.java	Wed Sep 02 15:20:40 2015 +0000
+++ b/src/org/openbravo/materialmgmt/ProductCharacteristicsDS.java	Fri Sep 04 13:43:38 2015 +0200
@@ -11,7 +11,7 @@
  * under the License.
  * The Original Code is Openbravo ERP.
  * The Initial Developer of the Original Code is Openbravo SLU
- * All portions are Copyright (C) 2013 Openbravo SLU
+ * All portions are Copyright (C) 2013-2015 Openbravo SLU
  * All Rights Reserved.
  * Contributor(s):  ______________________________________.
  *************************************************************************
@@ -19,13 +19,21 @@
 
 package org.openbravo.materialmgmt;
 
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
+import org.apache.commons.lang.StringUtils;
 import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
 import org.codehaus.jettison.json.JSONObject;
 import org.hibernate.Query;
+import org.openbravo.base.model.Entity;
+import org.openbravo.base.model.ModelProvider;
+import org.openbravo.base.provider.OBProvider;
 import org.openbravo.dal.core.DalUtil;
 import org.openbravo.dal.core.OBContext;
 import org.openbravo.dal.security.OrganizationStructureProvider;
@@ -33,6 +41,8 @@
 import org.openbravo.model.ad.access.Role;
 import org.openbravo.model.ad.access.RoleOrganization;
 import org.openbravo.service.datasource.DefaultDataSourceService;
+import org.openbravo.service.json.AdvancedQueryBuilder;
+import org.openbravo.service.json.DataEntityQueryService;
 import org.openbravo.service.json.JsonConstants;
 import org.openbravo.service.json.JsonUtils;
 import org.slf4j.Logger;
@@ -62,57 +72,15 @@
   public String fetch(Map<String, String> parameters) {
     OBContext.setAdminMode(true);
     try {
+      String entityName = parameters.get("_parentDSIdentifier");
 
-      StringBuilder hqlBuilder = new StringBuilder();
-      hqlBuilder.append(" select c.id, c.name, v.id, v.name, tn.reportSet ");
-      hqlBuilder.append(" from ADTreeNode tn, ");
-      hqlBuilder.append("      CharacteristicValue v, ");
-      hqlBuilder.append("      Characteristic c ");
-      hqlBuilder.append(" where tn.tree.typeArea ='CH'");
-      hqlBuilder.append(" and tn.node = v.id");
-      hqlBuilder.append(" and v.characteristic = c");
-      hqlBuilder.append(this.getClientOrgFilter());
-      hqlBuilder.append(" order by c.name, ");
-      hqlBuilder.append("          coalesce(tn.reportSet, '-1'), ");
-      hqlBuilder.append("          tn.sequenceNumber ");
+      Entity parentGridEntity = ModelProvider.getInstance().getEntity(entityName, false);
+      String productPath = parameters.get("_propertyPath");
+      List<String> allNodes = new ArrayList<String>();
+      Set<String> missingNodes = new HashSet<String>();
 
-      String hql = hqlBuilder.toString();
-
-      Query qTree = OBDal.getInstance().getSession().createQuery(hql);
-
-      String currentCharId = null;
-      JSONArray responseData = new JSONArray();
-      for (Object rawNode : qTree.list()) {
-        Object[] node = (Object[]) rawNode;
-        String charId = (String) node[CHAR_ID];
-
-        if (!charId.equals(currentCharId)) {
-          currentCharId = charId;
-          // new characteristic
-          JSONObject characteristic = new JSONObject();
-          characteristic.put("id", charId);
-          characteristic.put("_identifier", node[CHAR_NAME]);
-          characteristic.put("showOpenIcon", true);
-          characteristic.put("isCharacteristic", true);
-          characteristic
-              .put(
-                  "icon",
-                  "../web/org.openbravo.userinterface.smartclient/openbravo/skins/Default/org.openbravo.client.application/images/form/sectionItem-ico.png");
-          // TODO: skinnable icon
-          responseData.put(characteristic);
-        }
-
-        JSONObject value = new JSONObject();
-        String parentId = (String) node[VAL_PARENT];
-        parentId = "0".equals(parentId) ? charId : parentId;
-        value.put("id", node[VAL_ID]);
-        value.put("_identifier", node[VAL_NAME]);
-        value.put("parentId", parentId);
-        value.put("characteristic", charId);
-        value.put("characteristic$_identifier", node[CHAR_NAME]);
-
-        responseData.put(value);
-      }
+      JSONArray responseData = getAllNodes(parameters, parentGridEntity, productPath, allNodes,
+          missingNodes, false);
 
       final JSONObject jsonResult = new JSONObject();
       final JSONObject jsonResponse = new JSONObject();
@@ -122,6 +90,7 @@
       jsonResponse.put(JsonConstants.RESPONSE_TOTALROWS, responseData.length());
       jsonResponse.put(JsonConstants.RESPONSE_STARTROW, 0);
       jsonResponse.put(JsonConstants.RESPONSE_ENDROW, responseData.length() - 1);
+      jsonResponse.put(JsonConstants.ENTITYNAME, entityName);
       jsonResult.put(JsonConstants.RESPONSE_RESPONSE, jsonResponse);
 
       return jsonResult.toString();
@@ -133,6 +102,135 @@
     }
   }
 
+  private JSONArray getAllNodes(Map<String, String> parameters, Entity parentGridEntity,
+      String productPath, List<String> allNodes, Set<String> missingNodes, boolean addMissingNodes)
+      throws JSONException {
+
+    String gridWhereClause = null;
+    AdvancedQueryBuilder qb = null;
+    int initialNumOfMissingNodes = 0;
+    if (!addMissingNodes && parentGridEntity != null) {
+      final DataEntityQueryService queryService = OBProvider.getInstance().get(
+          DataEntityQueryService.class);
+
+      queryService.setEntityName(parentGridEntity.getName());
+      queryService.setFilterOnReadableOrganizations(true);
+      if (parameters.containsKey(JsonConstants.USE_ALIAS)) {
+        queryService.setUseAlias();
+      }
+
+      final JSONObject criteria = JsonUtils.buildCriteria(parameters);
+      queryService.setCriteria(criteria);
+
+      qb = queryService.getQueryBuilder();
+      qb.setMainAlias("e");
+      if (StringUtils.isNotBlank(qb.getWhereClause())) {
+        gridWhereClause = queryService.getWhereClause();
+      }
+    } else {
+      initialNumOfMissingNodes = missingNodes.size();
+    }
+
+    StringBuilder hqlBuilder = new StringBuilder();
+    hqlBuilder.append(" select c.id, c.name, v.id, v.name, tn.reportSet ");
+    hqlBuilder.append(" from ADTreeNode tn, ");
+    hqlBuilder.append("      CharacteristicValue v, ");
+    hqlBuilder.append("      Characteristic c ");
+    hqlBuilder.append(" where tn.tree.typeArea ='CH'");
+    hqlBuilder.append(" and tn.node = v.id");
+    if (addMissingNodes) {
+      hqlBuilder.append(" and v.id in (:missingNodes)");
+    }
+    hqlBuilder.append(" and v.characteristic = c");
+    hqlBuilder.append(this.getClientOrgFilter());
+
+    if (StringUtils.isNotBlank(gridWhereClause)) {
+      hqlBuilder.append("  and exists (from ProductCharacteristicValue pcv, " + parentGridEntity
+          + gridWhereClause + "  and pcv.characteristicValue = v and pcv.product = " + productPath
+          + ")");
+
+    }
+
+    hqlBuilder.append(" order by c.name, ");
+    hqlBuilder.append("          coalesce(tn.reportSet, '-1'), ");
+    hqlBuilder.append("          tn.sequenceNumber ");
+
+    String hql = hqlBuilder.toString();
+    log.info("HQL:\n " + hql);
+
+    Query qTree = OBDal.getInstance().getSession().createQuery(hql);
+    if (StringUtils.isNotBlank(gridWhereClause)) {
+      for (Entry<String, Object> param : qb.getNamedParameters().entrySet()) {
+        qTree.setParameter(param.getKey(), param.getValue());
+      }
+    } else if (addMissingNodes) {
+      qTree.setParameterList("missingNodes", missingNodes);
+    }
+
+    String currentCharId = null;
+    JSONArray responseData = new JSONArray();
+    for (Object rawNode : qTree.list()) {
+      Object[] node = (Object[]) rawNode;
+      String charId = (String) node[CHAR_ID];
+      String nodeId = (String) node[VAL_ID];
+
+      if (!charId.equals(currentCharId) && !allNodes.contains(charId)) {
+        currentCharId = charId;
+        // new characteristic
+        JSONObject characteristic = new JSONObject();
+        characteristic.put("id", charId);
+        characteristic.put("_identifier", node[CHAR_NAME]);
+        characteristic.put("showOpenIcon", true);
+        characteristic.put("isCharacteristic", true);
+        characteristic
+            .put(
+                "icon",
+                "../web/org.openbravo.userinterface.smartclient/openbravo/skins/Default/org.openbravo.client.application/images/form/sectionItem-ico.png");
+        // TODO: skinnable icon
+        responseData.put(characteristic);
+        allNodes.add(charId);
+      }
+
+      if (allNodes.contains(nodeId)) {
+        continue;
+      }
+
+      JSONObject value = new JSONObject();
+      String parentId = (String) node[VAL_PARENT];
+      parentId = "0".equals(parentId) ? charId : parentId;
+
+      value.put("id", nodeId);
+      value.put("_identifier", node[VAL_NAME]);
+      value.put("parentId", parentId);
+      value.put("characteristic", charId);
+      value.put("characteristic$_identifier", node[CHAR_NAME]);
+
+      allNodes.add(nodeId);
+      missingNodes.remove(nodeId);
+      if (!allNodes.contains(parentId)) {
+        missingNodes.add(parentId);
+      }
+
+      responseData.put(value);
+    }
+
+    if (missingNodes.size() > 0) {
+      // we can have missing nodes in case grid criteria has been applied, in this case query for
+      // them recursively
+      if (addMissingNodes && initialNumOfMissingNodes == missingNodes.size()) {
+        log.warn("Could not find all missing nodes in product characteristics {}", missingNodes);
+      } else {
+        JSONArray foundNodes = getAllNodes(parameters, parentGridEntity, productPath, allNodes,
+            missingNodes, true);
+        for (int i = 0; i < foundNodes.length(); i++) {
+          responseData.put(foundNodes.get(i));
+        }
+      }
+    }
+
+    return responseData;
+  }
+
   private String getClientOrgFilter() {
     String clientId = OBContext.getOBContext().getCurrentClient().getId();
     final Set<String> orgs = new HashSet<String>();
