# HG changeset patch
# User Víctor Martínez Romanos <victor.martinez@openbravo.com>
# Date 1519822115 -3600
#      Wed Feb 28 13:48:35 2018 +0100
# Node ID 05167010402d89147ee568a4f2e82efe686ed06b
# Parent  5c21517ea155534712ce7ced27fa91049b0790e6
Fixed bug 38015: [ri] Attribute Set sometimes is not copied when multiboxing

When calculating the new attribute set instance per storage detail for boxing, the system first tries to recover any previously cloned attribute set instance for this referenced inventory.
It can find it either in the cache created in this boxing execution, or by searching in the database for an existing attribute from a previous boxing activity for the same attribute.
If a previously cloned attribute set instance is not found, it creates a new one.

The existing index has been extended to perform properly in the query to search for an existing cloned attribute set instance.

diff --git a/src-db/database/model/tables/M_ATTRIBUTESETINSTANCE.xml b/src-db/database/model/tables/M_ATTRIBUTESETINSTANCE.xml
--- a/src-db/database/model/tables/M_ATTRIBUTESETINSTANCE.xml
+++ b/src-db/database/model/tables/M_ATTRIBUTESETINSTANCE.xml
@@ -93,6 +93,7 @@
       </foreign-key>
       <index name="M_ATTRIBUTESETINST_REFINV_IX" unique="false">
         <index-column name="M_REFINVENTORY_ID"/>
+        <index-column name="PARENT_ATTRIBUTESETINSTANCE_ID"/>
         <whereClause><![CDATA[M_REFINVENTORY_ID IS NOT NULL]]></whereClause>
       </index>
       <check name="M_ATTRIBUTESETINST_REFINV_CH"><![CDATA[M_REFINVENTORY_ID IS NULL OR M_REFINVENTORY_ID IS NOT NULL AND PARENT_ATTRIBUTESETINSTANCE_ID IS NOT NULL]]></check>
diff --git a/src/org/openbravo/materialmgmt/refinventory/BoxProcessor.java b/src/org/openbravo/materialmgmt/refinventory/BoxProcessor.java
--- a/src/org/openbravo/materialmgmt/refinventory/BoxProcessor.java
+++ b/src/org/openbravo/materialmgmt/refinventory/BoxProcessor.java
@@ -20,6 +20,8 @@
 package org.openbravo.materialmgmt.refinventory;
 
 import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
 
 import org.apache.commons.lang.StringUtils;
 import org.codehaus.jettison.json.JSONArray;
@@ -39,7 +41,8 @@
  */
 public class BoxProcessor extends ReferencedInventoryProcessor {
   private String newStorageBinId;
-  private String newAttributeSetInstanceId;
+  // StorageDetailId:NewAttributeSetInstanceId created by this object
+  private final Map<String, String> storageDetailNewAttributeIdMap;
 
   public BoxProcessor(final ReferencedInventory referencedInventory,
       final JSONArray selectedStorageDetails, final String newStorageBinId) throws JSONException {
@@ -47,6 +50,7 @@
     super.setSelectedStorageDetailsAndValidateThem(selectedStorageDetails);
     checkStorageDetailsNotAlreadyInReferencedInventory(selectedStorageDetails);
     setAndValidateNewStorageBinId(newStorageBinId);
+    storageDetailNewAttributeIdMap = new HashMap<>(selectedStorageDetails.length());
   }
 
   private void checkStorageDetailsNotAlreadyInReferencedInventory(
@@ -73,14 +77,24 @@
   }
 
   @Override
-  protected AttributeSetInstance getAttributeSetInstanceTo(StorageDetail storageDetail) {
-    if (newAttributeSetInstanceId == null) {
-      final AttributeSetInstance attributeSetInstance = ReferencedInventoryUtil
+  protected AttributeSetInstance getAttributeSetInstanceTo(final StorageDetail storageDetail) {
+    // Attribute previously created in this box execution
+    if (storageDetailNewAttributeIdMap.containsKey(storageDetail.getId())) {
+      return OBDal.getInstance().getProxy(AttributeSetInstance.class,
+          storageDetailNewAttributeIdMap.get(storageDetail.getId()));
+    }
+
+    // Attribute previously created in other box executions for this refInventory
+    final AttributeSetInstance previouslyClonedAttributeSetInstance = ReferencedInventoryUtil
+        .getAlreadyClonedAttributeSetInstance(storageDetail.getAttributeSetValue(),
+            getReferencedInventory());
+    if (previouslyClonedAttributeSetInstance == null) {
+      final AttributeSetInstance newAttributeSetInstance = ReferencedInventoryUtil
           .cloneAttributeSetInstance(storageDetail.getAttributeSetValue(), getReferencedInventory());
-      newAttributeSetInstanceId = attributeSetInstance.getId();
-      return attributeSetInstance;
+      storageDetailNewAttributeIdMap.put(storageDetail.getId(), newAttributeSetInstance.getId());
+      return newAttributeSetInstance;
     } else {
-      return OBDal.getInstance().getProxy(AttributeSetInstance.class, newAttributeSetInstanceId);
+      return previouslyClonedAttributeSetInstance;
     }
   }
 
diff --git a/src/org/openbravo/materialmgmt/refinventory/ReferencedInventoryUtil.java b/src/org/openbravo/materialmgmt/refinventory/ReferencedInventoryUtil.java
--- a/src/org/openbravo/materialmgmt/refinventory/ReferencedInventoryUtil.java
+++ b/src/org/openbravo/materialmgmt/refinventory/ReferencedInventoryUtil.java
@@ -27,12 +27,15 @@
 import org.hibernate.ScrollMode;
 import org.hibernate.ScrollableResults;
 import org.hibernate.Session;
+import org.hibernate.criterion.Restrictions;
 import org.openbravo.advpaymentmngt.utility.FIN_Utility;
 import org.openbravo.base.exception.OBException;
 import org.openbravo.base.provider.OBProvider;
 import org.openbravo.dal.core.DalUtil;
 import org.openbravo.dal.core.OBContext;
+import org.openbravo.dal.service.OBCriteria;
 import org.openbravo.dal.service.OBDal;
+import org.openbravo.dal.service.OBDao;
 import org.openbravo.model.ad.utility.Sequence;
 import org.openbravo.model.common.enterprise.Locator;
 import org.openbravo.model.common.enterprise.Organization;
@@ -76,6 +79,33 @@
   }
 
   /**
+   * Returns an AttributeSetInstance previously created from the given _originalAttributeSetInstance
+   * and referenced inventory. If not found returns null.
+   */
+  public static final AttributeSetInstance getAlreadyClonedAttributeSetInstance(
+      final AttributeSetInstance _originalAttributeSetInstance,
+      final ReferencedInventory referencedInventory) {
+    try {
+      OBContext.setAdminMode(true);
+      final AttributeSetInstance originalAttributeSetInstance = _originalAttributeSetInstance == null ? OBDal
+          .getInstance().getProxy(AttributeSetInstance.class, "0") : _originalAttributeSetInstance;
+
+      final OBCriteria<AttributeSetInstance> criteria = OBDao.getFilteredCriteria(
+          AttributeSetInstance.class, Restrictions.eq(
+              AttributeSetInstance.PROPERTY_PARENTATTRIBUTESETINSTANCE + ".id",
+              originalAttributeSetInstance.getId()), Restrictions.eq(
+              AttributeSetInstance.PROPERTY_REFERENCEDINVENTORY + ".id",
+              referencedInventory.getId()));
+      criteria.setMaxResults(1);
+      return criteria.list().get(0);
+    } catch (final Exception notFound) {
+      return null;
+    } finally {
+      OBContext.restorePreviousMode();
+    }
+  }
+
+  /**
    * Generates a description with the originalDesc + {@value #REFERENCEDINVENTORYPREFIX} +
    * referenced Inventory search key + {@value #REFERENCEDINVENTORYSUFFIX}
    */
