[IMP] edi: review/cleanup (wip)
[odoo/odoo.git] / openerp / addons / base / ir / ir_edi.py
index ad9fd2a..6efdd64 100644 (file)
@@ -1,8 +1,8 @@
 # -*- coding: utf-8 -*-
 ##############################################################################
-#    
-#    OpenERP, Open Source Management Solution
-#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
+#
+#    OpenERP, Open Source Business Applications
+#    Copyright (c) 2011 OpenERP S.A. <http://openerp.com>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -15,7 +15,7 @@
 #    GNU Affero General Public License for more details.
 #
 #    You should have received a copy of the GNU Affero General Public License
-#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
 
@@ -26,173 +26,213 @@ import time
 import base64
 import urllib2
 import openerp.release as release
-def safe_unique_id(model, database_id):
-    """Generate a unique string to represent a (model,database id) pair
-    without being too long, without revealing the database id, and
-    with a very low probability of collisions.
+from tools.translate import _
+import netsvc
 
-    Each EDI record and each relationship value are represented using a unique
-    database identifier. These database identifiers include the database unique
-    ID, as a way to uniquely refer to any record within any OpenERP instance,
-    without conflict.
+def split_xml_id(xml_id):
+    assert len(xml_id.split('.'))==2, \
+            _("'%s' contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id") % (xml_id)
+    return xml_id.split('.')
 
-    For OpenERP records that have an existing "XML ID" (i.e. an entry in
-    ir.model.data), the EDI unique identifier for this record will be made of
-    "%s:%s" % (the database's UUID, the XML ID). The database's UUID MUST
-    NOT contain a colon characters (this is guaranteed by the UUID algorithm).
-
-    For OpenERP records that have no existing "XML ID", a new one should be
-    created during the EDI export. It is recommended that the generated XML ID
-    contains a readable reference to the record model, plus a unique value that
-    hides the database ID.
+def safe_unique_id(database_id, model, record_id):
+    """Generate a unique string to represent a (database id,model,record_id) pair
+    without being too long, without revealing the database id, and
+    with a very low probability of collisions.
     """
-    msg = "%s-%s-%s" % (model, time.time(), database_id)
+    msg = "%s-%s-%s-%s" % (time.time(), database_id, model, record_id)
     digest = hashlib.sha1(msg).digest()
     digest = ''.join(chr(ord(x) ^ ord(y)) for (x,y) in zip(digest[:9], digest[9:-2]))
     # finally, use the b64-encoded folded digest as ID part of the unique ID:
     digest = base64.urlsafe_b64encode(digest)
-        
-    return '%s-%s' % (model,digest)
+    return '%s-%s' % (model.replace('.','_'), digest)
 
 class ir_edi_document(osv.osv):
     _name = 'ir.edi.document'
-    _description = 'To represent the EDI Document of any OpenERP record.'
+    _description = 'EDI Document'
     _columns = {
-                'name': fields.char("EDI token", size = 128, help="EDI Token is a unique identifier for the EDI document."),
-                'document': fields.text("Document", help="hold the serialization of the EDI document.")
-                
+                'name': fields.char("EDI token", size = 128, help="Unique identifier for retrieving an EDI document."),
+                'document': fields.text("Document", help="EDI document content")
     }
     _sql_constraints = [
-        ('name_uniq', 'unique (name)', 'The EDI Token must be unique!')
+        ('name_uniq', 'unique (name)', 'EDI Tokens must be unique!')
     ]
-    
-    
-    def new_edi_token(self, record):
-        """
-        Return a new, random unique token to identify an edi.document
-        :param record: It's a object of browse_record of any model
+
+    def new_edi_token(self, cr, uid, record):
+        """Return a new, random unique token to identify this model record,
+        and to be used as token when exporting it as an EDI document.
+
+        :param browse_record record: model record for which a token is needed
         """
-        db_uuid = safe_unique_id(record._name, record.id)
-        edi_token = hashlib.sha256('%s-%s-%s' % (time.time(), db_uuid, time.time())).hexdigest()
+        db_uuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid')
+        edi_token = hashlib.sha256('%s-%s-%s-%s' % (time.time(), db_uuid, record._name, record.id)).hexdigest()
         return edi_token
-    
+
     def serialize(self, edi_documents):
-        """Serialize the list of dictionaries using json dumps method
-        perform a JSON serialization of a list of dicts prepared by generate_edi() and return a UTF-8 encoded string that could be passed to deserialize()
-        :param edi_dicts: it's list of edi_dict
+        """Serialize the given EDI document structures (Python dicts holding EDI data),
+        using JSON serialization.
+
+        :param [dict] edi_documents: list of EDI document structures to serialize
+        :return: UTF-8 encoded string containing the serialized document
         """
         serialized_list = json.dumps(edi_documents)
         return serialized_list
-    
+
     def generate_edi(self, cr, uid, records, context=None):
+        """Generates a final EDI document containing the EDI serialization
+        of the given records, which should all be instances of a Model
+        that has the :meth:`~.edi` mixin. The document is not saved in the
+        database, this is done by :meth:`~.export_edi`.
+
+        :param list(browse_record) records: records to export as EDI
+        :return: UTF-8 encoded string containing the serialized records
         """
-        Generate the list of dictionaries using edi_export method of edi class 
-        :param records: it's a object of browse_record_list of any model
-        """
-        
         edi_list = []
         for record in records:
             record_model_obj = self.pool.get(record._name)
             edi_list += record_model_obj.edi_export(cr, uid, [record], context=context)
         return self.serialize(edi_list)
-    
+
     def get_document(self, cr, uid, edi_token, context=None):
+        """Retrieve the EDI document corresponding to the given edi_token.
+
+        :return: EDI document string
+        :raise: ValueError if requested EDI token does not match any know document
         """
-        Get the edi document from database using given edi token 
-        returns the string serialization that is in the database (column: document) for the given edi_token or raise.
-        """
-        
-        records = self.name_search(cr, uid, edi_token, context=context)
-        if records:
-            record = records[0][0]
-            edi = self.browse(cr, uid, record, context=context)
-            return edi.document
-        else:  
-            pass
-    
+        edi_ids = self.search(cr, uid, [('name','=', edi_token)], context=context)
+        if not edi_ids:
+            raise ValueError('Invalid EDI token: %s' % edi_token)
+        edi = self.browse(cr, uid, edi_ids[0], context=context)
+        return edi.document
+
     def load_edi(self, cr, uid, edi_documents, context=None):
-        """
-        loads the values from list of dictionaries to the corresponding OpenERP records
-        using the edi_import method of edi class
-        For each edi record (dict) in the list, call the corresponding osv.edi_import() method, based on the __model attribute (see section 2.4 of  for spec of 
-        osv.edi_import)
+        """Import the given EDI document structures into the system, using
+        :meth:`~.import_edi`.
 
-        :param edi_dicts: list of edi_dict
+        :param edi_documents: list of Python dicts containing the deserialized
+                              version of EDI documents
+        :return: list of (model, id) pairs containing the model and database ID
+                 of all records that were imported in the system
         """
-       
+        ir_module = self.pool.get('ir.module.module')
+        res = []
         for edi_document in edi_documents:
+            module = edi_document.get('__module')
+            if not ir_module.search(cr, uid, [('name','=',module),('state','=','installed')]):
+                raise osv.except_osv(_('Missing Application'),
+                            _("The document you are trying to import requires the OpenERP `%s` application. "
+                              "The OpenERP configuration assistant will help with this if you are connected as an administrator.")%(module,))
             model = edi_document.get('__model')
-            assert model, _('model should be provided in EDI Dict')
+            assert model, '__model attribute is required in each EDI document'
             model_obj = self.pool.get(model)
-            model_obj.edi_import(cr, uid, edi_document, context=context)
-        return True
-    
-    def deserialize(self, edi_document_string):
-        """ Deserialized the edi document string
-        perform JSON deserialization from an edi document string, and returns a list of dicts
+            record_id = model_obj.edi_import(cr, uid, edi_document, context=context)
+            res.append((model, record_id))
+        return res
+
+    def deserialize(self, edi_documents_string):
+        """Return deserialized version of the given EDI Document string.
+
+        :param str|unicode edi_documents_string: UTF-8 string (or unicode) containing
+                                                 JSON-serialized EDI document(s)
+        :return: Python object representing the EDI document(s) (usually a list of dicts) 
         """
-        edi_document = json.loads(edi_document_string)
-        
-        return edi_document
-    
+        return json.loads(edi_documents_string)
+
     def export_edi(self, cr, uid, records, context=None):
-        """
-        The method handles the flow of the edi document generation and store it in 
-            the database and return the edi_token of the particular document
-        Steps: 
-        * call generate_edi() to get a serialization and new_edi_token() to get a unique ID
-        * serialize the list returned by generate_edi() using serialize(), and save it in database with unique ID.
-        * return the unique ID
-
-        : param records: list of browse_record of any model
+        """Export the given database records as EDI documents, stores them
+        permanently with a new unique EDI token, for later retrieval via :meth:`~.get_document`,
+        and returns the list of the new corresponding ``ir.edi.document`` records.
+
+        :param records: list of browse_record of any model
+        :return: list of IDs of the new ``ir.edi.document`` entries, in the same 
+                 order as the provided ``records``.
         """
         exported_ids = []
         for record in records:
             document = self.generate_edi(cr, uid, [record], context)
-            token = self.new_edi_token(record)
+            token = self.new_edi_token(cr, uid, record)
             self.create(cr, uid, {
                          'name': token,
                          'document': document
                         }, context=context)
-        
             exported_ids.append(token)
         return exported_ids
-    
+
     def import_edi(self, cr, uid, edi_document=None, edi_url=None, context=None):
+        """Import a JSON serialized EDI Document string into the system, first retrieving it
+        from the given ``edi_url`` if provided.
+
+        :param str|unicode edi_document: UTF-8 string or unicode containing JSON-serialized
+                                         EDI Document to import. Must not be provided if 
+                                         ``edi_url`` is given.
+        :param str|unicode edi_url: URL where the EDI document (same format as ``edi_document``)
+                                    may be retrieved, without authentication.
         """
-        The method handles the flow of importing particular edi document and 
-        updates the database values on the basis of the edi document using 
-        edi_loads method
-        
-        * N: a serialized edi.document or the URL to download a serialized document
-        * If a URL is provided, download it first to get the document
-        * Calls deserialize() to get the resulting list of dicts from the document
-        * Call load_edi() with the list of dicts, to create or update the corresponding OpenERP records based on the edi.document.
-        """
-        
-        if edi_url and not edi_document:
+        if edi_url:
+            assert not edi_document, 'edi_document must not be provided if edi_url is given'
             edi_document = urllib2.urlopen(edi_url).read()
-
-        assert edi_document, _('EDI Document should be provided')
+        assert edi_document, 'EDI Document is empty!'
         edi_documents = self.deserialize(edi_document)
         return self.load_edi(cr, uid, edi_documents, context=context)
-    
-ir_edi_document()
+
 
 class edi(object):
-    _name = 'edi'
-    _description = 'edi document handler'
-    
-    """Mixin class for OSV objects that want be exposed as EDI documents.
-       Classes that inherit from this mixin class should override the 
+    """Mixin class for Model objects that want be exposed as EDI documents.
+       Classes that inherit from this mixin class should override the
        ``edi_import()`` and ``edi_export()`` methods to implement their
-       specific behavior, based on the primitives provided by this superclass."""
-    
+       specific behavior, based on the primitives provided by this mixin."""
+
+    def record_xml_id(self, cr, uid, record, context=None):
+        #TODO: remove this -> use ir.model.data directly
+        model_data_pool = self.pool.get('ir.model.data')
+        data_ids = model_data_pool.search(cr, uid, [('res_id','=',record.id),('model','=',record._name)])
+        if not data_ids:
+            return False
+        xml_record_id = data_ids[0]
+        xml_record = model_data_pool.browse(cr, uid, xml_record_id, context=context)
+        return xml_record.module, xml_record.name
+
+    def edi_xml_id(self, cr, uid, record, xml_id=None, context=None):
+        """
+        Generate a unique string to represent a pair of (the database's UUID, the XML ID).
+        Each EDI record and each relationship value are represented using a unique
+        database identifier. These database identifiers include the database unique
+        ID, as a way to uniquely refer to any record within any OpenERP instance,
+        without conflict.
+
+        For OpenERP records that have an existing "XML ID" (i.e. an entry in
+        ir.model.data), the EDI unique identifier for this record will be made of
+        "%s:%s" % (the database's UUID, the XML ID). The database's UUID MUST
+        NOT contain a colon characters (this is guaranteed by the UUID algorithm).
+
+        For OpenERP records that have no existing "XML ID", a new one should be
+        created during the EDI export. It is recommended that the generated XML ID
+        contains a readable reference to the record model, plus a unique value that
+        hides the database ID.
+        """
+        model_data_pool = self.pool.get('ir.model.data')
+        db_uuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid')
+
+        _record_xml = self.record_xml_id(cr, uid, record, context=context)
+        if _record_xml:
+            module, xml_id = _record_xml
+            xml_id = '%s:%s.%s' % (db_uuid, module, xml_id)
+        else:
+            if not xml_id:
+                uuid = safe_unique_id(db_uuid, record._name, record.id)
+                xml_id = '%s:%s.%s' % (db_uuid, record._module, uuid)
+            module, xml_id2 = split_xml_id(xml_id)
+            xml_record_id = model_data_pool.create(cr, uid, {
+                'name': xml_id2,
+                'model': record._name,
+                'module': module,
+                'res_id': record.id}, context=context)
+        return xml_id
+
     def edi_metadata(self, cr, uid, records, context=None):
         """Return a list representing the boilerplate EDI structure for
            exporting the record in the given browse_rec_list, including
            the metadata fields
-        
+
         The metadata fields MUST always include:
         - __model': the OpenERP model name
         - __module': the OpenERP module containing the model
@@ -201,8 +241,8 @@ class edi(object):
         - __version': a list of components for the version
         - __attachments': a list (possibly empty) of dicts describing the files attached to this record.
         """
-        # generic implementation!
-        model_data = self.pool.get('ir.model.data')
+        if context is None:
+            context = {}
         data_ids = []
         attachment_object = self.pool.get('ir.attachment')
         edi_dict_list = []
@@ -214,6 +254,7 @@ class edi(object):
             except:
                 pass
             version.append(ver)
+
         for record in records:
             attachment_ids = attachment_object.search(cr, uid, [('res_model','=', record._name), ('res_id', '=', record.id)])
             attachment_dict_list = []
@@ -223,25 +264,22 @@ class edi(object):
                         'content': base64.encodestring(attachment.datas),
                         'file_name': attachment.datas_fname,
                 })
-            
-            
-            data_ids = model_data.search(cr, uid, [('res_id','=',record.id),('model','=',record._name)])
-            
-            if len(data_ids):
-                xml_record = model_data.browse(cr, uid, data_ids[0])
-                uuid = safe_unique_id(record._name, record.id)
-                db_uuid = '%s:%s.%s' % (uuid, xml_record.module, xml_record.name)
-                
+
+
+            uuid = self.edi_xml_id(cr, uid, record, context=context)
             edi_dict = {
-                '__model' : record._name,
-                '__module' : record._module,
-                '__id': db_uuid,
+                '__id': uuid,
                 '__last_update': False, #record.write_date, #TODO: convert into UTC
-                '__version': version,
-                '__attachments': attachment_dict_list
             }
+            if not context.get('o2m_export'):
+                edi_dict.update({
+                    '__model' : record._name,
+                    '__module' : record._module,
+                    '__version': version,
+                    '__attachments': attachment_dict_list
+                })
             edi_dict_list.append(edi_dict)
-            
+
         return edi_dict_list
 
     def edi_m2o(self, cr, uid, record, context=None):
@@ -251,20 +289,13 @@ class edi(object):
         Exmaple: ['db-uuid:xml-id',  'Partner Name']
         """
         # generic implementation!
-        data_object = self.pool.get('ir.model.data')
-        db_uuid = safe_unique_id(record._name, record.id)
-        xml_ids = data_object.search(cr, uid, [('model','=',record._name),('res_id','=',record.id)])
-        
-        if xml_ids:
-            xml_id = data_object.browse(cr, uid, xml_ids[0], context=context)
-            xml_id = '%s.%s' %(xml_id.module, xml_id.name)
-        else:
-            xml_id = record._name    
-        db_uuid = '%s:%s' %(db_uuid, xml_id)
-        name = record.name
+        db_uuid = self.edi_xml_id(cr, uid, record, context=context)
+        relation_model_pool = self.pool.get(record._name)
+        name = relation_model_pool.name_get(cr, uid, [record.id], context=context)
+        name = name and name[0][1] or False
         return [db_uuid, name]
-        
-    def edi_o2m(self, cr, uid, records, context=None):
+
+    def edi_o2m(self, cr, uid, records, edi_struct=None, context=None):
         """Return a list representing a O2M EDI value for
            the browse_records from the given browse_record_list.
 
@@ -278,19 +309,22 @@ class edi(object):
         """
         # generic implementation!
         dict_list = []
-        
+        if context is None:
+            context = {}
+        ctx = context.copy()
+        ctx.update({'o2m_export':True})
         for record in records:
-            
+
             model_obj = self.pool.get(record._name)
-            dict_list += model_obj.edi_export(cr, uid, [record], context=context)
-        
+            dict_list += model_obj.edi_export(cr, uid, [record], edi_struct=edi_struct, context=ctx)
+
         return dict_list
-        
+
     def edi_m2m(self, cr, uid, records, context=None):
         """Return a list representing a M2M EDI value for
            the browse_records from the given browse_record_list.
 
-        Example: 
+        Example:
         'related_tasks': [                 # M2M fields would exported as a list of pairs,
                   ['db-uuid:xml-id1',      # similar to a list of M2O values.
                    'Task 01: bla bla'],
@@ -300,10 +334,10 @@ class edi(object):
         """
         # generic implementation!
         dict_list = []
-        
+
         for record in records:
-            dict_list.append(self.edi_o2m(cr, uid, [record], context=None))
-      
+            dict_list.append(self.edi_m2o(cr, uid, record, context=None))
+
         return dict_list
 
     def edi_export(self, cr, uid, records, edi_struct=None, context=None):
@@ -326,38 +360,89 @@ class edi(object):
                               be included in the exported data.
         """
         # generic implementation!
-        
+
         if context is None:
             context = {}
-        _fields = self.fields_get(cr, uid, context=context)
-        fields_to_export = edi_struct and edi_struct.keys() or _fields.keys()
+        if edi_struct is None:
+            edi_struct = {}
+        _columns = self._all_columns
+        fields_to_export = edi_struct and edi_struct.keys() or _columns.keys()
         edi_dict_list = []
         value = None
         for row in records:
             edi_dict = {}
             edi_dict.update(self.edi_metadata(cr, uid, [row], context=context)[0])
             for field in fields_to_export:
-                cols = _fields[field]
+                _column = _columns[field].column
+                _column_dict = fields.field_to_dict(self, cr, uid, _column, context=context)
                 record = getattr(row, field)
                 if not record:
                     continue
-                if _fields[field].has_key('function') or _fields[field].has_key('related_columns'):
-                    # Do not Export Function Fields and related fields
-                    continue
-                elif cols['type'] == 'many2one':
+                #if _fields[field].has_key('function') or _fields[field].has_key('related_columns'):
+                #    # Do not Export Function Fields and related fields
+                #    continue
+                elif _column_dict['type'] == 'many2one':
                     value = self.edi_m2o(cr, uid, record, context=context)
-                elif cols['type'] == 'many2many':
+                elif _column_dict['type'] == 'many2many':
                     value = self.edi_m2m(cr, uid, record, context=context)
-                elif cols['type'] == 'one2many':
-                    value = self.edi_o2m(cr, uid, record, context=context )
+                elif _column_dict['type'] == 'one2many':
+                    value = self.edi_o2m(cr, uid, record, edi_struct=edi_struct.get(field, {}), context=context )
                 else:
                     value = record
                 edi_dict[field] = value
             edi_dict_list.append(edi_dict)
         return edi_dict_list
-        
+
+
+    def edi_get_object_by_name(self, cr, uid, value, model_name, context=None):
+        openobject = False
+        model = self.pool.get(model_name)
+        object_ids = model.name_search(cr, uid, value, operator='=', context=context)
+        if object_ids and len(object_ids) == 1:
+            object_id = object_ids[0][0]
+            openobject = model.browse(cr, uid, object_id, context=context)
+        return openobject
+
+    def edi_get_object(self, cr, uid, xml_id, model, context=None):
+        model_data = self.pool.get('ir.model.data')
+        module, xml_id2 = split_xml_id(xml_id)
+        openobject = False
+        data_ids = model_data.search(cr, uid, [('model','=', model), ('name','=', xml_id2)])
+        if data_ids:
+            model = self.pool.get(model)
+            data = model_data.browse(cr, uid, data_ids[0], context=context)
+            openobject = model.browse(cr, uid, data.res_id, context=context)
+        return openobject
+
+    def edi_create_relation(self, cr, uid, relation_model, relation_value, context=None):
+        relation_model = self.pool.get(relation_model)
+        values = {} #relation_model.default_get(cr, uid, fields, context=context)
+        values[relation_model._rec_name] = relation_value
+        return relation_model.create(cr, uid, values, context=context)
+
+    def edi_import_relation(self, cr, uid, relation_model, relation_value, xml_id=None, context=None):
+        relation = False
+        if xml_id:
+            relation = self.edi_get_object(cr, uid, xml_id, relation_model, context=context)
+        if not relation:
+            relation = self.edi_get_object_by_name(cr, uid, relation_value, relation_model, context=context)
+        if not relation:
+            relation_id = self.edi_create_relation(cr, uid, relation_model, relation_value, context=context)
+            relation = relation_model.browse(cr, uid, relation_id, context=context)
+
+        record_xml = self.record_xml_id(cr, uid, relation, context=context)
+        if record_xml:
+            module, xml_id = record_xml
+            xml_id = '%s.%s' % (module, xml_id)
+        if not xml_id:
+            xml_id = self.edi_xml_id(cr, uid, relation, context=context)
+
+        #TODO: update record from values ?
+        #relation_id = model_data._update(cr, uid, relation_model, module, values, xml_id=xml_id, context=context)
+        return relation and relation.id or False
+
     def edi_import(self, cr, uid, edi_document, context=None):
-    
+
         """Imports a list of dicts representing an edi.document, using the
            generic algorithm.
 
@@ -401,104 +486,73 @@ class edi(object):
              For each pair in the M2M:
                  a. Perform the same steps as for a Many2One (see 1.2.1.1)
                  b. After finding the database ID of the final record in the database,
-                    connect it to the parent record via a write value like (4, db_id).        
+                    connect it to the parent record via a write value like (4, db_id).
         """
         # generic implementation!
-        
-        fields = edi_document.keys()
-        fields_to_import = []
-        data_line = []
         model_data = self.pool.get('ir.model.data')
-        _fields = self.fields_get(cr, uid, context=context)
-        current_module = 'edi_import'
-        values = {}
-        for field in edi_document.keys():
-            if not field.startswith('__'):
-                fields_to_import.append(field)
-                edi_field_value = edi_document[field]
-                if not edi_field_value:
+        assert self._name == edi_document['__model'], 'EDI Document could not import. Model of EDI Document and current model are does not match'
+        def process(datas, model_name):
+            values = {}
+            model_pool = self.pool.get(model_name)
+            xml_id = datas['__id']
+            module, xml_id2 = split_xml_id(xml_id)
+            _columns = model_pool._all_columns
+            for field in datas.keys():
+                if field not in _columns:
                     continue
-                if _fields[field].has_key('function') or _fields[field].has_key('related_columns'):
-                    # DO NOT IMPORT FUNCTION FIELD AND RELATED FIELD
-                    continue
-                elif _fields[field]['type'] in ('many2one', 'many2many'):
-                    if _fields[field]['type'] == 'many2one':
-                        edi_parent_documents = [edi_field_value]
-                    else:
-                        edi_parent_documents = edi_field_value
-
-                    parent_lines = []
-
-                    for edi_parent_document in edi_parent_documents:
-                        #Look in ir.model.data for a record that matches the db_id.
-                        #If found, replace the m2o value with the correct database ID and stop.
-                        #If not found, continue to next step
-                        if edi_parent_document[0].find(':') != -1 and edi_parent_document[1] != None:
-                            db_uuid, xml_id =  tuple(edi_parent_document[0].split(':'))
-                            data_ids = model_data.name_search(cr, uid, xml_id)
-                            if data_ids:
-                                for data in model_data.browse(cr, uid, [data_ids[0][0]], context=context):
-                                    parent_lines.append(data.res_id)
-                            else:
-                                #Perform name_search(name) to look for a record that matches the
-                                #given m2o name. If only one record is found, create the missing
-                                #ir.model.data record to link it to the db_id, and the replace the m2o
-                                #value with the correct database ID, then stop. If zero result or
-                                #multiple results are found, go to next step.
-                                #Create the new record using the only field value that is known: the
-                                #name, and create the ir.model.data entry to map to it.
-
-                                relation_object = self.pool.get(_fields[field]['relation'])
-                                relation_ids = relation_object.search(cr, uid, [('name','=',edi_parent_document[1])])
-                                if relation_ids and len(relation_ids) == 1:
-                                    relation_id = relation_ids[0]
-                                else:
-                                    relation_id = relation_object.create(cr, uid, {'name': edi_parent_document[1]}, context=context)
-                                model_data.create(cr, uid, {
-                                                    'name': xml_id,
-                                                    'model': relation_object._name,
-                                                    'module':relation_object._module,
-                                                    'res_id':relation_id 
-                                                    }, context=context)
-                                
-                                parent_lines.append(relation_id)
-                                
-                        
-                    if len(parent_lines):   
-                        if _fields[field]['type'] == 'many2one':
-                            values[field] = parent_lines[0]
-                            
+                if not field.startswith('__'):
+                    _column = _columns[field].column
+                    _column_dict = fields.field_to_dict(self, cr, uid, _column, context=context)
+
+                    edi_field_value = datas[field]
+                    field_type = _column_dict['type']
+                    relation_model = _column_dict.get('relation')
+                    if not edi_field_value:
+                        continue
+                    if _column_dict.has_key('function') or _column_dict.has_key('related_columns'):
+                        # DO NOT IMPORT FUNCTION FIELD AND RELATED FIELD
+                        continue
+                    elif field_type == 'one2many':
+                        # recursive call for getting children and returning [(0,0,{})] or [(1,ID,{})]
+                        relations = []
+                        relation_object = self.pool.get(relation_model)
+                        for edi_relation_document in edi_field_value:
+                            relation = self.edi_get_object(cr, uid, edi_relation_document['__id'], relation_model, context=context)
+                            if not relation and edi_relation_document.get('name'):
+                                relation = self.edi_get_object_by_name(cr, uid, edi_relation_document['name'], relation_model, context=context)
+                            if relation:
+                                self.edi_xml_id(cr, uid, relation, \
+                                                    xml_id=edi_relation_document['__id'], context=context)
+                                relations.append((4, relation.id))
+                            res_module, res_xml_id, newrow = process(edi_relation_document, relation_model)
+                            relations.append( (relation and 1 or 0, relation and relation.id or 0, newrow))
+                        values[field] = relations
+                    elif field_type in ('many2one', 'many2many'):
+                        if field_type == 'many2one':
+                            edi_parent_documents = [edi_field_value]
                         else:
-                            many2many_ids = []
-                            for m2m_id in parent_lines:
-                                many2many_ids.append((4,m2m_id))
-                            values[field] = many2many_ids
-                elif _fields[field]['type'] == 'one2many':
-                    #Look for a record that matches the db_id provided in the __id field. If
-                    #found, keep the corresponding database id, and connect it to the parent
-                    #using a write value like (4,db_id).
-                    #If not found via db_id, create a new entry using the same method that
-                    #imports a full EDI record (recursive call!), grab the resulting db id,
-                    #and use it to connect to the parent via a write value like (4, db_id).
-            
-                    relations = []
-                    relation_object = self.pool.get(_fields[field]['relation'])
-                    for edi_relation_document in edi_field_value:
-                        if edi_relation_document['__id'].find(':') != -1:
-                            db_uuid, xml_id = tuple(edi_relation_document['__id'].split(':'))
-                            data_ids = model_data.name_search(cr, uid, xml_id)
-                            if data_ids:
-                                for data in model_data.browse(cr,uid,[data_ids[0][0]]):
-                                    relations.append(data.res_id)
+                            edi_parent_documents = edi_field_value
+
+                        parent_lines = []
+
+                        for edi_parent_document in edi_parent_documents:
+                            relation_id = self.edi_import_relation(cr, uid, relation_model, edi_parent_document[1], edi_parent_document[0], context=context)
+                            parent_lines.append(relation_id)
+
+                        if len(parent_lines):
+                            if field_type == 'many2one':
+                                values[field] = parent_lines[0]
+
                             else:
-                                r = relation_object.edi_import(cr, uid, edi_relation_document, context=context)
-                                relations.append(r)
-                    one2many_ids = []
-                    for o2m_id in relations:
-                        one2many_ids.append((4,o2m_id))
-                    values[field] = one2many_ids
-                else:
-                    values[field] = edi_field_value
-        
-        return model_data._update(cr, uid, self._name, current_module, values, context=context)
+                                many2many_ids = []
+                                for m2m_id in parent_lines:
+                                    many2many_ids.append((4, m2m_id))
+                                values[field] = many2many_ids
+
+                    else:
+                        values[field] = edi_field_value
+            return module, xml_id2, values
+
+        module, xml_id, data_values = process(edi_document, self._name)
+        return model_data._update(cr, uid, self._name, module, data_values, xml_id=xml_id, context=context)
 # vim: ts=4 sts=4 sw=4 si et