fixes issue 9000: References in the database without using foreign keys can go wrong in import:
authorMartin Taal <martin.taal@openbravo.com>
Mon, 18 May 2009 16:35:00 +0200
changeset 3892 3dac42f31231
parent 3891 18d5d5bf9dde
child 3893 7b5d4a62f221
fixes issue 9000: References in the database without using foreign keys can go wrong in import:
src-test/org/openbravo/test/xml/ClientExportImportTest.java
src/org/openbravo/dal/xml/EntityXMLConverter.java
src/org/openbravo/dal/xml/PrimitiveReferenceHandler.java
src/org/openbravo/service/db/DataImportService.java
--- a/src-test/org/openbravo/test/xml/ClientExportImportTest.java	Mon May 18 15:43:42 2009 +0200
+++ b/src-test/org/openbravo/test/xml/ClientExportImportTest.java	Mon May 18 16:35:00 2009 +0200
@@ -30,14 +30,21 @@
 
 import org.hibernate.criterion.Expression;
 import org.openbravo.base.exception.OBException;
+import org.openbravo.base.model.Entity;
+import org.openbravo.base.model.ModelProvider;
 import org.openbravo.base.session.OBPropertiesProvider;
 import org.openbravo.base.structure.BaseOBObject;
 import org.openbravo.base.structure.ClientEnabled;
 import org.openbravo.dal.security.OrganizationStructureProvider;
 import org.openbravo.dal.service.OBCriteria;
 import org.openbravo.dal.service.OBDal;
+import org.openbravo.dal.service.OBQuery;
 import org.openbravo.model.ad.system.Client;
+import org.openbravo.model.ad.utility.TreeNode;
 import org.openbravo.model.common.enterprise.Organization;
+import org.openbravo.model.financialmgmt.accounting.AccountingFact;
+import org.openbravo.model.financialmgmt.payment.DebtPayment;
+import org.openbravo.model.project.Project;
 import org.openbravo.service.db.ClientImportProcessor;
 import org.openbravo.service.db.DataExportService;
 import org.openbravo.service.db.DataImportService;
@@ -84,13 +91,106 @@
    * completely new client is added in the database.
    * 
    * Also tests mantis 8509: https://issues.openbravo.com/view.php?id=8509
+   * 
+   * Also tests mantis 9000: https://issues.openbravo.com/view.php?id=9000
    */
   public void testExportImportClient1000000() {
+
     final String newClientId = exportImport("1000000");
     testMantis8509(newClientId);
+    testAccountingFactMantis9000(newClientId);
+    testTreeNodesMantis9000(newClientId);
     // SystemService.getInstance().removeAllClientData(newClientId);
   }
 
+  private void testTreeNodesMantis9000(String newClientID) {
+    final OBQuery<TreeNode> nodes = OBDal.getInstance().createQuery(TreeNode.class,
+        "client.id='" + newClientID + "'");
+    nodes.setFilterOnReadableClients(false);
+    nodes.setFilterOnReadableOrganization(false);
+    assertTrue(nodes.list().size() > 0);
+    final Client newClient = OBDal.getInstance().get(Client.class, newClientID);
+
+    boolean testDoneAtLeastOnce = false;
+    for (TreeNode node : nodes.list()) {
+      assertEquals(newClient, node.getClient());
+      // also ignore 0 as there is a business partner/sales region tree node with 0
+      if (node.getNode() != null && !node.getNode().equals("0")) {
+        final Entity entity = ModelProvider.getInstance().getEntityFromTreeType(
+            node.getTree().getTypeArea());
+        if (entity.getName().equals(Project.ENTITY_NAME)) {
+          // can be removed when this issue:
+          // https://issues.openbravo.com/view.php?id=8745
+          // is solved
+          continue;
+        }
+
+        final BaseOBObject bob = OBDal.getInstance().get(entity.getName(), node.getNode());
+        assertTrue("Entity instance not found " + entity.getName() + " " + node.getNode(),
+            bob != null);
+        if (bob instanceof ClientEnabled) {
+          assertEquals(newClient, ((ClientEnabled) bob).getClient());
+          testDoneAtLeastOnce = true;
+        }
+      }
+      // also ignore 0 as there is a business partner/sales region tree node with 0
+      if (node.getReportSet() != null && !node.getReportSet().equals("0")) {
+        final Entity entity = ModelProvider.getInstance().getEntityFromTreeType(
+            node.getTree().getTypeArea());
+        if (entity.getName().equals(Project.ENTITY_NAME)) {
+          // can be removed when this issue:
+          // https://issues.openbravo.com/view.php?id=8745
+          // is solved
+          continue;
+        }
+
+        final BaseOBObject bob = OBDal.getInstance().get(entity.getName(), node.getReportSet());
+        assertTrue("Entity instance not found " + entity.getName() + " " + node.getReportSet(),
+            bob != null);
+        if (bob instanceof ClientEnabled) {
+          assertEquals(newClient, ((ClientEnabled) bob).getClient());
+          testDoneAtLeastOnce = true;
+        }
+      }
+    }
+    assertTrue(testDoneAtLeastOnce);
+
+  }
+
+  private void testAccountingFactMantis9000(String newClientID) {
+    final OBQuery<AccountingFact> facts = OBDal.getInstance().createQuery(AccountingFact.class,
+        "client.id='" + newClientID + "'");
+    facts.setFilterOnReadableClients(false);
+    facts.setFilterOnReadableOrganization(false);
+    assertTrue(facts.list().size() > 0);
+    final Client newClient = OBDal.getInstance().get(Client.class, newClientID);
+    boolean testDoneAtLeastOnce = false;
+    for (AccountingFact fact : facts.list()) {
+      assertEquals(newClient, fact.getClient());
+      if (fact.getRecordID() != null) {
+        final BaseOBObject bob = OBDal.getInstance().get(fact.getTable().getName(),
+            fact.getRecordID());
+        assertTrue("Entity instance not found " + fact.getTable().getName() + " "
+            + fact.getRecordID(), bob != null);
+        if (bob instanceof ClientEnabled) {
+          assertEquals(newClient, ((ClientEnabled) bob).getClient());
+          testDoneAtLeastOnce = true;
+        }
+      }
+      if (fact.getRecordID2() != null) {
+        final BaseOBObject bob = OBDal.getInstance().get(DebtPayment.ENTITY_NAME,
+            fact.getRecordID2());
+        assertTrue("Entity instance not found " + DebtPayment.ENTITY_NAME + " "
+            + fact.getRecordID2(), bob != null);
+        if (bob instanceof ClientEnabled) {
+          assertEquals(newClient, ((ClientEnabled) bob).getClient());
+          testDoneAtLeastOnce = true;
+        }
+      }
+    }
+    assertTrue(testDoneAtLeastOnce);
+  }
+
   /**
    * Exports the 1000001 client and then imports as a new client. Has as side effect that a
    * completely new client is added in the database.
--- a/src/org/openbravo/dal/xml/EntityXMLConverter.java	Mon May 18 15:43:42 2009 +0200
+++ b/src/org/openbravo/dal/xml/EntityXMLConverter.java	Mon May 18 16:35:00 2009 +0200
@@ -347,18 +347,16 @@
         // handle a special case the tree node
         // both the parent and the node should be added to the export list
         if (value != null && obObject instanceof TreeNode) {
-          final boolean isReferingProperty = p.getName().equals(TreeNode.PROPERTY_REPORTSET)
-              || p.getName().equals(TreeNode.PROPERTY_NODE);
-          if (isReferingProperty && value != null && !value.equals("0")) {
+          if (PrimitiveReferenceHandler.getInstance().isPrimitiveReference(p) && value != null
+              && !value.equals("0")) {
             final String strValue = (String) value;
-            final TreeNode treeNode = (TreeNode) obObject;
-            final Entity referedEntity = ModelProvider.getInstance().getEntityFromTreeType(
-                treeNode.getTree().getTypeArea());
+            final Entity referedEntity = PrimitiveReferenceHandler.getInstance()
+                .getPrimitiveReferencedEntity(obObject, p);
             final BaseOBObject obValue = OBDal.getInstance().get(referedEntity.getName(), strValue);
             if (obValue == null) {
-              log.error("TreeNode (with id:" + obObject.getId() + "): The value " + strValue
-                  + " used in treeNode " + treeNode.getId() + " is not valid, there is no "
-                  + referedEntity.getName() + " with that id");
+              log.error("Object (with id:" + obObject.getId() + "): The value " + strValue
+                  + " used in this object is not valid, there is no " + referedEntity.getName()
+                  + " with that id");
               // Check.isNotNull(obValue, "The value " + strValue + " used in treeNode "
               // + treeNode.getId() + " is not valid, there is no " + referedEntity.getName()
               // + " with that id");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openbravo/dal/xml/PrimitiveReferenceHandler.java	Mon May 18 16:35:00 2009 +0200
@@ -0,0 +1,248 @@
+/*
+ *************************************************************************
+ * The contents of this file are subject to the Openbravo  Public  License
+ * Version  1.0  (the  "License"),  being   the  Mozilla   Public  License
+ * Version 1.1  with a permitted attribution clause; you may not  use this
+ * file except in compliance with the License. You  may  obtain  a copy of
+ * the License at http://www.openbravo.com/legal/license.html 
+ * Software distributed under the License  is  distributed  on  an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific  language  governing  rights  and  limitations
+ * under the License. 
+ * The Original Code is Openbravo ERP. 
+ * The Initial Developer of the Original Code is Openbravo SL 
+ * All portions are Copyright (C) 2009 Openbravo SL 
+ * All Rights Reserved. 
+ * Contributor(s):  ______________________________________.
+ ************************************************************************
+ */
+
+package org.openbravo.dal.xml;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openbravo.base.exception.OBException;
+import org.openbravo.base.model.Entity;
+import org.openbravo.base.model.ModelProvider;
+import org.openbravo.base.model.Property;
+import org.openbravo.base.provider.OBProvider;
+import org.openbravo.base.provider.OBSingleton;
+import org.openbravo.base.structure.BaseOBObject;
+import org.openbravo.model.ad.alert.Alert;
+import org.openbravo.model.ad.datamodel.Table;
+import org.openbravo.model.ad.utility.AD_Attachment;
+import org.openbravo.model.ad.utility.Attachment;
+import org.openbravo.model.ad.utility.Tree;
+import org.openbravo.model.ad.utility.TreeNode;
+import org.openbravo.model.financialmgmt.accounting.AccountingFact;
+import org.openbravo.model.financialmgmt.payment.DebtPayment;
+
+/**
+ * Handles primitive reference values. These are references which are not modeled as foreign keys to
+ * a specific table but as string/table combinations stored in different fields. Sometimes the table
+ * is stored in a separate field (see {@link AD_Attachment#PROPERTY_TABLE}, sometimes it needs to be
+ * computed (see {@link Tree#PROPERTY_TYPEAREA}).
+ * 
+ * 
+ * This class provides utility methods to find these references and resolve them.
+ * 
+ * @see TreeNode#PROPERTY_NODE
+ * @see TreeNode#PROPERTY_REPORTSET
+ * @see AD_Attachment#PROPERTY_RECORDID
+ * @see AccountingFact#PROPERTY_RECORDID
+ * @see AccountingFact#PROPERTY_RECORDID2
+ * @see Alert#PROPERTY_RECORDID
+ * 
+ * @see EntityXMLConverter
+ * 
+ * @author mtaal
+ */
+
+public class PrimitiveReferenceHandler implements OBSingleton {
+
+  private static PrimitiveReferenceHandler instance;
+
+  public static PrimitiveReferenceHandler getInstance() {
+    if (instance == null) {
+      instance = OBProvider.getInstance().get(PrimitiveReferenceHandler.class);
+    }
+    return instance;
+  }
+
+  public static void setInstance(PrimitiveReferenceHandler instance) {
+    PrimitiveReferenceHandler.instance = instance;
+  }
+
+  private List<Property> treeNodePrimitiveReferences;
+  private List<Property> alertPrimitiveReferences;
+  private List<Property> attachmentPrimitiveReferences;
+  private List<Property> accountingFactPrimitiveReferences;
+
+  /**
+   * @param entity
+   *          the Entity for which to get the primitive reference properties
+   * @return list of properties of the entity which are primitive references
+   *         {@link #isPrimitiveReference(Property)}).
+   */
+  public List<Property> getPrimitiveReferences(Entity entity) {
+    if (entity.getName().equals(TreeNode.ENTITY_NAME)) {
+      if (treeNodePrimitiveReferences != null) {
+        return treeNodePrimitiveReferences;
+      }
+      final List<Property> result = new ArrayList<Property>();
+      result.add(entity.getProperty(TreeNode.PROPERTY_REPORTSET));
+      result.add(entity.getProperty(TreeNode.PROPERTY_NODE));
+      treeNodePrimitiveReferences = result;
+      return treeNodePrimitiveReferences;
+    }
+    if (entity.getName().equals(Alert.ENTITY_NAME)) {
+      if (alertPrimitiveReferences != null) {
+        return alertPrimitiveReferences;
+      }
+      final List<Property> result = new ArrayList<Property>();
+      result.add(entity.getProperty(Alert.PROPERTY_REFERENCESEARCHKEY));
+      alertPrimitiveReferences = result;
+      return alertPrimitiveReferences;
+    }
+    if (entity.getName().equals(Attachment.ENTITY_NAME)) {
+      if (attachmentPrimitiveReferences != null) {
+        return attachmentPrimitiveReferences;
+      }
+      final List<Property> result = new ArrayList<Property>();
+      result.add(entity.getProperty(Attachment.PROPERTY_RECORD));
+      attachmentPrimitiveReferences = result;
+      return attachmentPrimitiveReferences;
+    }
+    if (entity.getName().equals(AccountingFact.ENTITY_NAME)) {
+      if (accountingFactPrimitiveReferences != null) {
+        return accountingFactPrimitiveReferences;
+      }
+      final List<Property> result = new ArrayList<Property>();
+      result.add(entity.getProperty(AccountingFact.PROPERTY_RECORDID));
+      result.add(entity.getProperty(AccountingFact.PROPERTY_RECORDID2));
+      accountingFactPrimitiveReferences = result;
+      return accountingFactPrimitiveReferences;
+    }
+    throw new OBException("Entity " + entity + " does not have primitive references");
+  }
+
+  /**
+   * Code which handles so-called primitive references, These are references which are modeled as
+   * primitive types and which do not have a foreign key constraint defined in the database. The
+   * reference is defined using 2 columns: one holding the id, the other holding the table
+   * 
+   * @param property
+   *          the property to check if it is a primitive reference
+   * @return true if this property is a primitive reference, false otherwise
+   */
+  public boolean isPrimitiveReference(Property property) {
+    if (property.getEntity().getName().equals(TreeNode.ENTITY_NAME)
+        && (property.getName().equals(TreeNode.PROPERTY_REPORTSET) || property.getName().equals(
+            TreeNode.PROPERTY_NODE))) {
+      return true;
+    }
+    if (property.getEntity().getName().equals(Alert.ENTITY_NAME)
+        && property.getName().equals(Alert.PROPERTY_REFERENCESEARCHKEY)) {
+      return true;
+    }
+    if (property.getEntity().getName().equals(Attachment.ENTITY_NAME)
+        && property.getName().equals(Attachment.PROPERTY_RECORD)) {
+      return true;
+    }
+    if (property.getEntity().getName().equals(AccountingFact.ENTITY_NAME)
+        && (property.getName().equals(AccountingFact.PROPERTY_RECORDID) || property.getName()
+            .equals(AccountingFact.PROPERTY_RECORDID2))) {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Returns true if the passed object has so-called primitive references (
+   * {@link #isPrimitiveReference(Property)}).
+   * 
+   * @param obObject
+   *          the object having a primitive reference property (or not)
+   * @return true if this entity holds a primitive reference
+   */
+  // TODO; store this concept in the entity itself, is faster
+  // Discussion: the methods here however make use of a lot of string comparison
+  // when this would be done in the Entity class then it is not allowed to
+  // refer to generated classes and then property name/entity name comparisons
+  // are not compile-time-checked
+  public boolean hasObjectPrimitiveReference(BaseOBObject obObject) {
+    final Entity entity = obObject.getEntity();
+    if (entity.getName().equals(TreeNode.ENTITY_NAME)) {
+      return true;
+    }
+    if (entity.getName().equals(Alert.ENTITY_NAME)) {
+      return true;
+    }
+    if (entity.getName().equals(Attachment.ENTITY_NAME)) {
+      return true;
+    }
+    if (entity.getName().equals(AccountingFact.ENTITY_NAME)) {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * In case of a primitive reference ({@link #isPrimitiveReference(Property)}) then this method
+   * returns the Entity to which the property value refers.
+   * 
+   * This method may only be called if the primitive reference is unequal to null.
+   * 
+   * @param obObject
+   *          the object refering to an instance of an Entity
+   * @param property
+   *          the primitive reference property
+   * @return the Entity to which the property value refers, can not be null
+   * @throws OBException
+   *           if no Entity can be found
+   */
+  public Entity getPrimitiveReferencedEntity(BaseOBObject obObject, Property property) {
+    if (property.getEntity().getName().equals(TreeNode.ENTITY_NAME)) {
+      final TreeNode treeNode = (TreeNode) obObject;
+      final Entity entity = ModelProvider.getInstance().getEntityFromTreeType(
+          treeNode.getTree().getTypeArea());
+      if (entity == null) {
+        throw new OBException("No entity found for treetype " + treeNode.getTree().getTypeArea());
+      }
+      return entity;
+    }
+    if (property.getEntity().getName().equals(Alert.ENTITY_NAME)) {
+      final Alert alert = (Alert) obObject;
+      final Table table = alert.getAlertRule().getTab().getTable();
+      final Entity entity = ModelProvider.getInstance().getEntity(table.getName());
+      if (entity == null) {
+        throw new OBException("No entity for table with name " + table.getName());
+      }
+      return entity;
+    }
+    if (property.getEntity().getName().equals(Attachment.ENTITY_NAME)) {
+      final Attachment attachment = (Attachment) obObject;
+      final Entity entity = ModelProvider.getInstance().getEntity(attachment.getTable().getName());
+      if (entity == null) {
+        throw new OBException("No entity for table with name " + attachment.getTable().getName());
+      }
+      return entity;
+    }
+    if (property.getEntity().getName().equals(AccountingFact.ENTITY_NAME)) {
+      if (property.getName().equals(AccountingFact.PROPERTY_RECORDID2)) {
+        return ModelProvider.getInstance().getEntity(DebtPayment.ENTITY_NAME);
+      }
+      final AccountingFact accountingFact = (AccountingFact) obObject;
+      final Entity entity = ModelProvider.getInstance().getEntity(
+          accountingFact.getTable().getName());
+      if (entity == null) {
+        throw new OBException("No entity for table with name "
+            + accountingFact.getTable().getName());
+      }
+      return entity;
+    }
+    throw new OBException("Property " + property + " is not a primitive reference");
+  }
+
+}
\ No newline at end of file
--- a/src/org/openbravo/service/db/DataImportService.java	Mon May 18 15:43:42 2009 +0200
+++ b/src/org/openbravo/service/db/DataImportService.java	Mon May 18 16:35:00 2009 +0200
@@ -43,9 +43,12 @@
 import org.openbravo.dal.core.SessionHandler;
 import org.openbravo.dal.core.TriggerHandler;
 import org.openbravo.dal.service.OBDal;
+import org.openbravo.dal.xml.EntityResolver;
 import org.openbravo.dal.xml.EntityXMLProcessor;
+import org.openbravo.dal.xml.PrimitiveReferenceHandler;
 import org.openbravo.dal.xml.StaxXMLEntityConverter;
 import org.openbravo.dal.xml.XMLEntityConverter;
+import org.openbravo.dal.xml.EntityResolver.ResolvingMode;
 import org.openbravo.model.ad.datamodel.Table;
 import org.openbravo.model.ad.module.Module;
 import org.openbravo.model.ad.system.Client;
@@ -200,7 +203,6 @@
         // now save and update
         // do inserts and updates in opposite order, this is important
         // so that the objects on which other depend are inserted first
-        final List<TreeNode> treeNodes = new ArrayList<TreeNode>();
         final List<BaseOBObject> toInsert = xec.getToInsert();
         int done = 0;
         final Set<BaseOBObject> inserted = new HashSet<BaseOBObject>();
@@ -210,11 +212,6 @@
           insertObjectGraph(ins, inserted);
           ir.getInsertedObjects().add(ins);
           done++;
-
-          if (ins instanceof TreeNode) {
-            final TreeNode tn = (TreeNode) ins;
-            treeNodes.add(tn);
-          }
         }
         Check.isTrue(done == toInsert.size(),
             "Not all objects have been inserted, check for loop: " + done + "/" + toInsert.size());
@@ -230,11 +227,6 @@
           OBDal.getInstance().save(upd);
           ir.getUpdatedObjects().add(upd);
           done++;
-
-          if (upd instanceof TreeNode) {
-            final TreeNode tn = (TreeNode) upd;
-            treeNodes.add(tn);
-          }
         }
         Check.isTrue(done == toUpdate.size(),
             "Not all objects have been inserted, check for loop: " + done + "/" + toUpdate.size());
@@ -242,50 +234,16 @@
         // flush to set the ids in the objects
         OBDal.getInstance().flush();
 
-        // now walk through the treenodes to repair id's
-        for (TreeNode tn : treeNodes) {
-          final Entity entity = ModelProvider.getInstance().getEntityFromTreeType(
-              tn.getTree().getTypeArea());
-          if (entity == null) {
-            if (ir.getWarningMessages() == null) {
-              ir.setWarningMessages("Imported tree nodes belong to a tree with a tree type "
-                  + tn.getTree().getTypeArea() + " which is not related to any entity.");
-            } else {
-              ir.setWarningMessages(ir.getWarningMessages()
-                  + "\nImported tree nodes belong to a tree with a tree type "
-                  + tn.getTree().getTypeArea() + " which is not related to any entity.");
-            }
-            continue;
-          }
-          final BaseOBObject bob = (BaseOBObject) xec.getEntityResolver().resolve(entity.getName(),
-              tn.getNode(), true);
-          if (bob == null) {
-            ir.setErrorMessages("The tree node " + tn + " points to an object with id "
-                + tn.getNode() + " which does not exist in the database or in the import set.");
-            OBDal.getInstance().rollbackAndClose();
-            rolledBack = true;
-            return ir;
-          }
-          if (!bob.getId().equals(tn.getNode())) {
-            tn.setNode((String) bob.getId());
-          }
-          // and also correct the parent
-          if (tn.getReportSet() != null) {
-            final BaseOBObject parent = (BaseOBObject) xec.getEntityResolver().resolve(
-                entity.getName(), tn.getReportSet(), true);
-            if (parent == null) {
-              ir.setErrorMessages("The tree node " + tn + " points to an object with id "
-                  + tn.getReportSet()
-                  + " which does not exist in the database or in the import set.");
-              OBDal.getInstance().rollbackAndClose();
-              rolledBack = true;
-              return ir;
-            }
-            if (!parent.getId().equals(tn.getReportSet())) {
-              tn.setReportSet((String) parent.getId());
-            }
-          }
+        // now walk through the objects to repair primitive reference id's
+        // note updates both the ir and changes the entityresolver
+        repairPrimitiveReferences(ir, xec.getEntityResolver());
+
+        if (ir.hasErrorOccured()) {
+          OBDal.getInstance().rollbackAndClose();
+          rolledBack = true;
+          return ir;
         }
+
         OBDal.getInstance().flush();
       } catch (final Throwable t) {
         OBDal.getInstance().rollbackAndClose();
@@ -313,6 +271,73 @@
     }
   }
 
+  // the id's which are repaired are so-called primitive references
+  // (for example AD_Tree_Node.node_id
+  // these are references without foreign key which are modeled as a
+  // string/varchar. During the import the id of an object may change, thereby
+  // invalidating the primitive reference (which still uses the old id). This
+  // method repairs those ids.
+  // see the methods in XMLUtil related to primitive references
+  private void repairPrimitiveReferences(ImportResult ir, EntityResolver entityResolver) {
+
+    // at this point all references must exist
+    entityResolver.setResolvingMode(ResolvingMode.MUST_EXIST);
+    final List<BaseOBObject> repairReferences = new ArrayList<BaseOBObject>();
+    for (BaseOBObject bob : ir.getUpdatedObjects()) {
+      if (PrimitiveReferenceHandler.getInstance().hasObjectPrimitiveReference(bob)) {
+        repairReferences.add(bob);
+      }
+    }
+    for (BaseOBObject bob : ir.getInsertedObjects()) {
+      if (PrimitiveReferenceHandler.getInstance().hasObjectPrimitiveReference(bob)) {
+        repairReferences.add(bob);
+      }
+    }
+    for (BaseOBObject objectToRepair : repairReferences) {
+      if (objectToRepair instanceof TreeNode) {
+        final TreeNode tn = (TreeNode) objectToRepair;
+        final Entity entity = ModelProvider.getInstance().getEntityFromTreeType(
+            tn.getTree().getTypeArea());
+        if (entity == null) {
+          if (ir.getWarningMessages() == null) {
+            ir.setWarningMessages("Imported tree nodes belong to a tree with a tree type "
+                + tn.getTree().getTypeArea() + " which is not related to any entity.");
+          } else {
+            ir.setWarningMessages(ir.getWarningMessages()
+                + "\nImported tree nodes belong to a tree with a tree type "
+                + tn.getTree().getTypeArea() + " which is not related to any entity.");
+          }
+          continue;
+        }
+      }
+      for (Property p : PrimitiveReferenceHandler.getInstance().getPrimitiveReferences(
+          objectToRepair.getEntity())) {
+        final String value = (String) objectToRepair.get(p.getName());
+        // also ignore 0 as there is a business partner tree node with 0
+        if (value != null && !value.equals("0")) {
+          final Entity entity = PrimitiveReferenceHandler.getInstance()
+              .getPrimitiveReferencedEntity(objectToRepair, p);
+          final BaseOBObject referencedBob = (BaseOBObject) entityResolver.resolve(
+              entity.getName(), value, true);
+          if (referencedBob == null) {
+            if (ir.getErrorMessages() == null) {
+              ir.setErrorMessages("The object " + objectToRepair
+                  + " references an object (entity: " + entity + ") with id " + value
+                  + " which does not exist in the database or in the import set.");
+            } else {
+              ir.setErrorMessages(ir.getErrorMessages() + "\nThe object " + objectToRepair
+                  + " references an object (entity: " + entity + ") with id " + value
+                  + " which does not exist in the database or in the import set.");
+            }
+          } else if (!referencedBob.getId().equals(value)) {
+            objectToRepair.set(p.getName(), referencedBob.getId());
+          }
+        }
+      }
+    }
+
+  }
+
   private void validateObject(BaseOBObject bob, ImportResult ir) {
     int i = 0;
     final StringBuilder msgs = new StringBuilder();
@@ -448,7 +473,6 @@
     // now save and update
     // do inserts and updates in opposite order, this is important
     // so that the objects on which other depend are inserted first
-    final List<TreeNode> treeNodes = new ArrayList<TreeNode>();
     final List<BaseOBObject> toInsert = xec.getToInsert();
     int done = 0;
     final Set<BaseOBObject> inserted = new HashSet<BaseOBObject>();
@@ -458,11 +482,6 @@
       insertObjectGraph(ins, inserted);
       ir.getInsertedObjects().add(ins);
       done++;
-
-      if (ins instanceof TreeNode) {
-        final TreeNode tn = (TreeNode) ins;
-        treeNodes.add(tn);
-      }
     }
     Check.isTrue(done == toInsert.size(), "Not all objects have been inserted, check for loop: "
         + done + "/" + toInsert.size());
@@ -478,11 +497,6 @@
       OBDal.getInstance().save(upd);
       ir.getUpdatedObjects().add(upd);
       done++;
-
-      if (upd instanceof TreeNode) {
-        final TreeNode tn = (TreeNode) upd;
-        treeNodes.add(tn);
-      }
     }
     Check.isTrue(done == toUpdate.size(), "Not all objects have been inserted, check for loop: "
         + done + "/" + toUpdate.size());
@@ -490,47 +504,10 @@
     // flush to set the ids in the objects
     OBDal.getInstance().flush();
 
-    // now walk through the treenodes to repair id's
+    // now walk through the objects to repair primitive reference id's
+    // note updates both the ir and changes the entityresolver
+    repairPrimitiveReferences(ir, xec.getEntityResolver());
 
-    // now walk through the treenodes to repair id's
-    for (TreeNode tn : treeNodes) {
-      final Entity entity = ModelProvider.getInstance().getEntityFromTreeType(
-          tn.getTree().getTypeArea());
-      if (entity == null) {
-        if (ir.getWarningMessages() == null) {
-          ir.setWarningMessages("Imported tree nodes belong to a tree with a tree type "
-              + tn.getTree().getTypeArea() + " which is not related to any entity.");
-        } else {
-          ir.setWarningMessages(ir.getWarningMessages()
-              + "\nImported tree nodes belong to a tree with a tree type "
-              + tn.getTree().getTypeArea() + " which is not related to any entity.");
-        }
-        continue;
-      }
-      final BaseOBObject bob = (BaseOBObject) xec.getEntityResolver().resolve(entity.getName(),
-          tn.getNode(), true);
-      if (bob == null) {
-        ir.setErrorMessages("The tree node " + tn + " points to an object with id " + tn.getNode()
-            + " which does not exist in the database or in the import set.");
-        return;
-      }
-      if (!bob.getId().equals(tn.getNode())) {
-        tn.setNode((String) bob.getId());
-      }
-      // and also correct the parent
-      if (tn.getReportSet() != null) {
-        final BaseOBObject parent = (BaseOBObject) xec.getEntityResolver().resolve(
-            entity.getName(), tn.getReportSet(), true);
-        if (parent == null) {
-          ir.setErrorMessages("The tree node " + tn + " points to an object with id "
-              + tn.getReportSet() + " which does not exist in the database or in the import set.");
-          return;
-        }
-        if (!parent.getId().equals(tn.getReportSet())) {
-          tn.setReportSet((String) parent.getId());
-        }
-      }
-    }
     OBDal.getInstance().flush();
 
     // store the ad_ref_data_loaded