927 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			927 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Python
		
	
	
# -*- coding: utf-8 -*-
 | 
						|
# Copyright 2013-2017 Camptocamp SA
 | 
						|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
 | 
						|
 | 
						|
import mock
 | 
						|
from odoo.addons.component.core import Component, WorkContext
 | 
						|
from odoo.addons.component.tests.common import (
 | 
						|
    ComponentRegistryCase,
 | 
						|
    TransactionComponentRegistryCase,
 | 
						|
)
 | 
						|
 | 
						|
from odoo.addons.connector.components.mapper import (
 | 
						|
    MappingDefinition,
 | 
						|
    changed_by,
 | 
						|
    only_create,
 | 
						|
    convert,
 | 
						|
    follow_m2o_relations,
 | 
						|
    m2o_to_external,
 | 
						|
    external_to_m2o,
 | 
						|
    none,
 | 
						|
    MapOptions,
 | 
						|
    mapping,
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
class TestMapper(ComponentRegistryCase):
 | 
						|
 | 
						|
    def setUp(self):
 | 
						|
        super(TestMapper, self).setUp()
 | 
						|
        self.comp_registry.load_components('connector')
 | 
						|
 | 
						|
    def test_mapping_decorator(self):
 | 
						|
        class KifKrokerMapper(Component):
 | 
						|
            _name = 'kif.kroker.mapper'
 | 
						|
            _inherit = 'base.mapper'
 | 
						|
 | 
						|
            @changed_by('name', 'city')
 | 
						|
            @mapping
 | 
						|
            @only_create
 | 
						|
            def name(self):
 | 
						|
                pass
 | 
						|
 | 
						|
            @changed_by('email')
 | 
						|
            @mapping
 | 
						|
            def email(self):
 | 
						|
                pass
 | 
						|
 | 
						|
            @changed_by('street')
 | 
						|
            @mapping
 | 
						|
            def street(self):
 | 
						|
                pass
 | 
						|
 | 
						|
            def no_decorator(self):
 | 
						|
                pass
 | 
						|
 | 
						|
        # build our mapper component
 | 
						|
        KifKrokerMapper._build_component(self.comp_registry)
 | 
						|
 | 
						|
        # what mappings we expect
 | 
						|
        name_def = MappingDefinition(changed_by=set(('name', 'city')),
 | 
						|
                                     only_create=True)
 | 
						|
        email_def = MappingDefinition(changed_by=set(('email',)),
 | 
						|
                                      only_create=False)
 | 
						|
        street_def = MappingDefinition(changed_by=set(('street',)),
 | 
						|
                                       only_create=False)
 | 
						|
 | 
						|
        # get our component by name in the components registry
 | 
						|
        comp = self.comp_registry['kif.kroker.mapper']
 | 
						|
        # _map_methods contains the aggregated mapping methods for a Mapper
 | 
						|
        self.assertEqual(comp._map_methods,
 | 
						|
                         {'name': name_def,
 | 
						|
                          'email': email_def,
 | 
						|
                          'street': street_def,
 | 
						|
                          })
 | 
						|
 | 
						|
    def test_mapping_decorator_cross_classes(self):
 | 
						|
        """ Mappings should not propagate to other classes"""
 | 
						|
        class MomMapper(Component):
 | 
						|
            _name = 'mom.mapper'
 | 
						|
            _inherit = 'base.mapper'
 | 
						|
            _apply_on = 'res.users'
 | 
						|
 | 
						|
            @changed_by('name', 'city')
 | 
						|
            @mapping
 | 
						|
            def name(self):
 | 
						|
                pass
 | 
						|
 | 
						|
        class ZappMapper(Component):
 | 
						|
            _name = 'zapp.mapper'
 | 
						|
            _inherit = 'base.mapper'
 | 
						|
            _apply_on = 'res.users'
 | 
						|
 | 
						|
            @changed_by('email')
 | 
						|
            @only_create
 | 
						|
            @mapping
 | 
						|
            def email(self):
 | 
						|
                pass
 | 
						|
 | 
						|
        self._build_components(MomMapper, ZappMapper)
 | 
						|
 | 
						|
        mom_def = MappingDefinition(changed_by=set(('name', 'city')),
 | 
						|
                                    only_create=False)
 | 
						|
        zapp_def = MappingDefinition(changed_by=set(('email',)),
 | 
						|
                                     only_create=True)
 | 
						|
 | 
						|
        comp = self.comp_registry['mom.mapper']
 | 
						|
        self.assertEqual(comp._map_methods, {'name': mom_def})
 | 
						|
        comp = self.comp_registry['zapp.mapper']
 | 
						|
        self.assertEqual(comp._map_methods, {'email': zapp_def})
 | 
						|
 | 
						|
    def test_mapping_decorator_cumul(self):
 | 
						|
        """ Mappings should cumulate the ``super`` mappings
 | 
						|
        and the local mappings."""
 | 
						|
        class FryMapper(Component):
 | 
						|
            _name = 'fry.mapper'
 | 
						|
            _inherit = 'base.mapper'
 | 
						|
            _apply_on = 'res.users'
 | 
						|
 | 
						|
            @changed_by('name', 'city')
 | 
						|
            @mapping
 | 
						|
            def name(self):
 | 
						|
                pass
 | 
						|
 | 
						|
        class FryMapperInherit(Component):
 | 
						|
            _inherit = 'fry.mapper'
 | 
						|
 | 
						|
            @changed_by('email')
 | 
						|
            @mapping
 | 
						|
            def email(self):
 | 
						|
                pass
 | 
						|
 | 
						|
        self._build_components(FryMapper, FryMapperInherit)
 | 
						|
 | 
						|
        name_def = MappingDefinition(changed_by=set(('name', 'city')),
 | 
						|
                                     only_create=False)
 | 
						|
        email_def = MappingDefinition(changed_by=set(('email',)),
 | 
						|
                                      only_create=False)
 | 
						|
 | 
						|
        comp = self.comp_registry['fry.mapper']
 | 
						|
        self.assertEqual(comp._map_methods,
 | 
						|
                         {'name': name_def,
 | 
						|
                          'email': email_def})
 | 
						|
 | 
						|
    def test_mapping_decorator_cumul_changed_by(self):
 | 
						|
        """ Mappings should cumulate the changed_by fields of the
 | 
						|
        ``super`` mappings and the local mappings """
 | 
						|
        class FryMapper(Component):
 | 
						|
            _name = 'fry.mapper'
 | 
						|
            _inherit = 'base.mapper'
 | 
						|
            _apply_on = 'res.users'
 | 
						|
 | 
						|
            @changed_by('name', 'city')
 | 
						|
            @mapping
 | 
						|
            def name(self):
 | 
						|
                pass
 | 
						|
 | 
						|
        class FryMapperInherit(Component):
 | 
						|
            _inherit = 'fry.mapper'
 | 
						|
            _apply_on = 'res.users'
 | 
						|
 | 
						|
            @changed_by('email')
 | 
						|
            @mapping
 | 
						|
            def name(self):
 | 
						|
                pass
 | 
						|
 | 
						|
        class ThirdMapper(Component):
 | 
						|
            _name = 'third.mapper'
 | 
						|
            _inherit = 'fry.mapper'
 | 
						|
            _apply_on = 'res.users'
 | 
						|
 | 
						|
            @changed_by('email', 'street')
 | 
						|
            @mapping
 | 
						|
            def name(self):
 | 
						|
                pass
 | 
						|
 | 
						|
        self._build_components(FryMapper, FryMapperInherit, ThirdMapper)
 | 
						|
 | 
						|
        name_def = MappingDefinition(changed_by=set(('name', 'city', 'email')),
 | 
						|
                                     only_create=False)
 | 
						|
 | 
						|
        comp = self.comp_registry['fry.mapper']
 | 
						|
        self.assertEqual(comp._map_methods, {'name': name_def})
 | 
						|
 | 
						|
        name_def = MappingDefinition(changed_by=set(('name', 'city',
 | 
						|
                                                     'email', 'street')),
 | 
						|
                                     only_create=False)
 | 
						|
        comp = self.comp_registry['third.mapper']
 | 
						|
        self.assertEqual(comp._map_methods, {'name': name_def})
 | 
						|
 | 
						|
    def test_several_bases_cumul(self):
 | 
						|
        class FryMapper(Component):
 | 
						|
            _name = 'fry.mapper'
 | 
						|
            _inherit = 'base.mapper'
 | 
						|
            _apply_on = 'res.users'
 | 
						|
 | 
						|
            @changed_by('name', 'city')
 | 
						|
            @mapping
 | 
						|
            def name(self):
 | 
						|
                pass
 | 
						|
 | 
						|
            @only_create
 | 
						|
            @mapping
 | 
						|
            def street(self):
 | 
						|
                pass
 | 
						|
 | 
						|
            @only_create
 | 
						|
            @mapping
 | 
						|
            def zip(self):
 | 
						|
                pass
 | 
						|
 | 
						|
        class FarnsworthMapper(Component):
 | 
						|
            _name = 'farnsworth.mapper'
 | 
						|
            _inherit = 'base.mapper'
 | 
						|
            _apply_on = 'res.users'
 | 
						|
 | 
						|
            @changed_by('email')
 | 
						|
            @mapping
 | 
						|
            def name(self):
 | 
						|
                pass
 | 
						|
 | 
						|
            @changed_by('street')
 | 
						|
            @mapping
 | 
						|
            def city(self):
 | 
						|
                pass
 | 
						|
 | 
						|
            @mapping
 | 
						|
            def zip(self):
 | 
						|
                pass
 | 
						|
 | 
						|
        class ThirdMapper(Component):
 | 
						|
            _name = 'third.mapper'
 | 
						|
            _inherit = ['fry.mapper', 'farnsworth.mapper']
 | 
						|
            _apply_on = 'res.users'
 | 
						|
 | 
						|
            @changed_by('email', 'street')
 | 
						|
            @mapping
 | 
						|
            def name(self):
 | 
						|
                pass
 | 
						|
 | 
						|
            @mapping
 | 
						|
            def email(self):
 | 
						|
                pass
 | 
						|
 | 
						|
        self._build_components(FryMapper, FarnsworthMapper, ThirdMapper)
 | 
						|
 | 
						|
        name_def = MappingDefinition(changed_by=set(('name', 'city',
 | 
						|
                                                     'email', 'street')),
 | 
						|
                                     only_create=False)
 | 
						|
        street_def = MappingDefinition(changed_by=set([]),
 | 
						|
                                       only_create=True)
 | 
						|
        city_def = MappingDefinition(changed_by=set(('street',)),
 | 
						|
                                     only_create=False)
 | 
						|
        email_def = MappingDefinition(changed_by=set([]),
 | 
						|
                                      only_create=False)
 | 
						|
        zip_def = MappingDefinition(changed_by=set([]),
 | 
						|
                                    only_create=True)
 | 
						|
 | 
						|
        comp = self.comp_registry['third.mapper']
 | 
						|
        self.assertEqual(comp._map_methods['name'], name_def)
 | 
						|
        self.assertEqual(comp._map_methods['street'], street_def)
 | 
						|
        self.assertEqual(comp._map_methods['city'], city_def)
 | 
						|
        self.assertEqual(comp._map_methods['email'], email_def)
 | 
						|
        self.assertEqual(comp._map_methods['zip'], zip_def)
 | 
						|
 | 
						|
    def test_mapping_record(self):
 | 
						|
        """ Map a record and check the result """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
            _apply_on = 'res.users'
 | 
						|
 | 
						|
            direct = [('name', 'out_name')]
 | 
						|
 | 
						|
            @mapping
 | 
						|
            def street(self, record):
 | 
						|
                return {'out_street': record['street'].upper()}
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {'name': 'Guewen', 'street': 'street'}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'out_name': 'Guewen', 'out_street': 'STREET'}
 | 
						|
        self.assertEqual(map_record.values(), expected)
 | 
						|
        self.assertEqual(map_record.values(for_create=True), expected)
 | 
						|
 | 
						|
    def test_mapping_record_on_create(self):
 | 
						|
        """ Map a record and check the result for creation of record """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
            _apply_on = 'res.users'
 | 
						|
 | 
						|
            direct = [('name', 'out_name')]
 | 
						|
 | 
						|
            @mapping
 | 
						|
            def street(self, record):
 | 
						|
                return {'out_street': record['street'].upper()}
 | 
						|
 | 
						|
            @only_create
 | 
						|
            @mapping
 | 
						|
            def city(self, record):
 | 
						|
                return {'out_city': 'city'}
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {'name': 'Guewen', 'street': 'street'}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'out_name': 'Guewen', 'out_street': 'STREET'}
 | 
						|
        self.assertEqual(map_record.values(), expected)
 | 
						|
        expected = {'out_name': 'Guewen',
 | 
						|
                    'out_street': 'STREET',
 | 
						|
                    'out_city': 'city'}
 | 
						|
        self.assertEqual(map_record.values(for_create=True), expected)
 | 
						|
 | 
						|
    def test_mapping_update(self):
 | 
						|
        """ Force values on a map record """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
 | 
						|
            direct = [('name', 'out_name')]
 | 
						|
 | 
						|
            @mapping
 | 
						|
            def street(self, record):
 | 
						|
                return {'out_street': record['street'].upper()}
 | 
						|
 | 
						|
            @only_create
 | 
						|
            @mapping
 | 
						|
            def city(self, record):
 | 
						|
                return {'out_city': 'city'}
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {'name': 'Guewen', 'street': 'street'}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        map_record.update({'test': 1}, out_city='forced')
 | 
						|
        expected = {'out_name': 'Guewen',
 | 
						|
                    'out_street': 'STREET',
 | 
						|
                    'out_city': 'forced',
 | 
						|
                    'test': 1}
 | 
						|
        self.assertEqual(map_record.values(), expected)
 | 
						|
        expected = {'out_name': 'Guewen',
 | 
						|
                    'out_street': 'STREET',
 | 
						|
                    'out_city': 'forced',
 | 
						|
                    'test': 1}
 | 
						|
        self.assertEqual(map_record.values(for_create=True), expected)
 | 
						|
 | 
						|
    def test_finalize(self):
 | 
						|
        """ Inherit finalize to modify values """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
 | 
						|
            direct = [('name', 'out_name')]
 | 
						|
 | 
						|
            def finalize(self, record, values):
 | 
						|
                result = super(MyMapper, self).finalize(record, values)
 | 
						|
                result['test'] = 'abc'
 | 
						|
                return result
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {'name': 'Guewen',
 | 
						|
                  'street': 'street'}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'out_name': 'Guewen',
 | 
						|
                    'test': 'abc'}
 | 
						|
        self.assertEqual(map_record.values(), expected)
 | 
						|
        expected = {'out_name': 'Guewen',
 | 
						|
                    'test': 'abc'}
 | 
						|
        self.assertEqual(map_record.values(for_create=True), expected)
 | 
						|
 | 
						|
    def test_some_fields(self):
 | 
						|
        """ Map only a selection of fields """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
 | 
						|
            direct = [('name', 'out_name'),
 | 
						|
                      ('street', 'out_street'),
 | 
						|
                      ]
 | 
						|
 | 
						|
            @changed_by('country')
 | 
						|
            @mapping
 | 
						|
            def country(self, record):
 | 
						|
                return {'country': 'country'}
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {'name': 'Guewen',
 | 
						|
                  'street': 'street',
 | 
						|
                  'country': 'country'}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'out_name': 'Guewen',
 | 
						|
                    'country': 'country'}
 | 
						|
        self.assertEqual(map_record.values(fields=['name', 'country']),
 | 
						|
                         expected)
 | 
						|
        expected = {'out_name': 'Guewen',
 | 
						|
                    'country': 'country'}
 | 
						|
        self.assertEqual(map_record.values(for_create=True,
 | 
						|
                                           fields=['name', 'country']),
 | 
						|
                         expected)
 | 
						|
 | 
						|
    def test_mapping_modifier(self):
 | 
						|
        """ Map a direct record with a modifier function """
 | 
						|
 | 
						|
        def do_nothing(field):
 | 
						|
            def transform(self, record, to_attr):
 | 
						|
                return record[field]
 | 
						|
            return transform
 | 
						|
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
 | 
						|
            direct = [(do_nothing('name'), 'out_name')]
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {'name': 'Guewen'}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'out_name': 'Guewen'}
 | 
						|
        self.assertEqual(map_record.values(), expected)
 | 
						|
        self.assertEqual(map_record.values(for_create=True), expected)
 | 
						|
 | 
						|
    def test_mapping_direct_property(self):
 | 
						|
        """ Map a direct record with 'direct' being a property """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
 | 
						|
            @property
 | 
						|
            def direct(self):
 | 
						|
                return [('name', 'out_name')]
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {'name': 'Foo'}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'out_name': 'Foo'}
 | 
						|
        self.assertEqual(map_record.values(), expected)
 | 
						|
        self.assertEqual(map_record.values(for_create=True), expected)
 | 
						|
 | 
						|
    def test_mapping_convert(self):
 | 
						|
        """ Map a direct record with the convert modifier function """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
 | 
						|
            direct = [(convert('name', int), 'out_name')]
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {'name': '300'}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'out_name': 300}
 | 
						|
        self.assertEqual(map_record.values(), expected)
 | 
						|
        self.assertEqual(map_record.values(for_create=True), expected)
 | 
						|
 | 
						|
    def test_mapping_modifier_none(self):
 | 
						|
        """ Pipeline of modifiers """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
 | 
						|
            direct = [(none('in_f'), 'out_f'),
 | 
						|
                      (none('in_t'), 'out_t')]
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {'in_f': False, 'in_t': True}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'out_f': None, 'out_t': True}
 | 
						|
        self.assertEqual(map_record.values(), expected)
 | 
						|
        self.assertEqual(map_record.values(for_create=True), expected)
 | 
						|
 | 
						|
    def test_mapping_modifier_pipeline(self):
 | 
						|
        """ Pipeline of modifiers """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
 | 
						|
            direct = [(none(convert('in_f', bool)), 'out_f'),
 | 
						|
                      (none(convert('in_t', bool)), 'out_t')]
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {'in_f': 0, 'in_t': 1}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'out_f': None, 'out_t': True}
 | 
						|
        self.assertEqual(map_record.values(), expected)
 | 
						|
        self.assertEqual(map_record.values(for_create=True), expected)
 | 
						|
 | 
						|
    def test_modifier_import_filter_field(self):
 | 
						|
        """ A direct mapping with a modifier must still be considered
 | 
						|
        from the list of fields
 | 
						|
        """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
 | 
						|
            direct = [('field', 'field2'),
 | 
						|
                      ('no_field', 'no_field2'),
 | 
						|
                      (convert('name', int), 'out_name')]
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {'name': '300', 'field': 'value', 'no_field': 'no_value'}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'out_name': 300, 'field2': 'value'}
 | 
						|
        self.assertEqual(map_record.values(fields=['field', 'name']), expected)
 | 
						|
        self.assertEqual(map_record.values(for_create=True,
 | 
						|
                                           fields=['field', 'name']), expected)
 | 
						|
 | 
						|
    def test_modifier_export_filter_field(self):
 | 
						|
        """ A direct mapping with a modifier on an export mapping """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.export.mapper'
 | 
						|
 | 
						|
            direct = [('field', 'field2'),
 | 
						|
                      ('no_field', 'no_field2'),
 | 
						|
                      (convert('name', int), 'out_name')]
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {'name': '300', 'field': 'value', 'no_field': 'no_value'}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'out_name': 300, 'field2': 'value'}
 | 
						|
        self.assertEqual(map_record.values(fields=['field', 'name']), expected)
 | 
						|
        self.assertEqual(map_record.values(for_create=True,
 | 
						|
                                           fields=['field', 'name']), expected)
 | 
						|
 | 
						|
    def test_mapping_custom_option(self):
 | 
						|
        """ Usage of custom options in mappings """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
 | 
						|
            @mapping
 | 
						|
            def any(self, record):
 | 
						|
                if self.options.custom:
 | 
						|
                    res = True
 | 
						|
                else:
 | 
						|
                    res = False
 | 
						|
                return {'res': res}
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'res': True}
 | 
						|
        self.assertEqual(map_record.values(custom=True), expected)
 | 
						|
 | 
						|
    def test_mapping_custom_option_not_defined(self):
 | 
						|
        """ Usage of custom options not defined raise AttributeError """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
 | 
						|
            @mapping
 | 
						|
            def any(self, record):
 | 
						|
                if self.options.custom is None:
 | 
						|
                    res = True
 | 
						|
                else:
 | 
						|
                    res = False
 | 
						|
                return {'res': res}
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {}
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'res': True}
 | 
						|
        self.assertEqual(map_record.values(), expected)
 | 
						|
 | 
						|
    def test_map_options(self):
 | 
						|
        """ Test MapOptions """
 | 
						|
        options = MapOptions({'xyz': 'abc'}, k=1)
 | 
						|
        options.l = 2
 | 
						|
        self.assertEqual(options['xyz'], 'abc')
 | 
						|
        self.assertEqual(options['k'], 1)
 | 
						|
        self.assertEqual(options['l'], 2)
 | 
						|
        self.assertEqual(options.xyz, 'abc')
 | 
						|
        self.assertEqual(options.k, 1)
 | 
						|
        self.assertEqual(options.l, 2)
 | 
						|
        self.assertEqual(options['undefined'], None)
 | 
						|
        self.assertEqual(options.undefined, None)
 | 
						|
 | 
						|
    def test_changed_by_fields(self):
 | 
						|
        """ Test attribute ``_changed_by_fields`` on Mapper."""
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.export.mapper'
 | 
						|
 | 
						|
            direct = [('street', 'out_street'),
 | 
						|
                      (none('in_t'), 'out_t'),
 | 
						|
                      (none(convert('in_f', bool)), 'out_f')]
 | 
						|
 | 
						|
            @changed_by('name', 'city')
 | 
						|
            @mapping
 | 
						|
            def name(self):
 | 
						|
                pass
 | 
						|
 | 
						|
            @changed_by('email')
 | 
						|
            @mapping
 | 
						|
            def email(self):
 | 
						|
                pass
 | 
						|
 | 
						|
            def no_decorator(self):
 | 
						|
                pass
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        work = mock.MagicMock(name='WorkContext()')
 | 
						|
        mapper = self.comp_registry['my.mapper'](work)
 | 
						|
 | 
						|
        self.assertEqual(
 | 
						|
            mapper.changed_by_fields(),
 | 
						|
            set(['street', 'in_t', 'in_f', 'name', 'city', 'email']))
 | 
						|
 | 
						|
 | 
						|
class TestMapperRecordsets(TransactionComponentRegistryCase):
 | 
						|
    """ Test mapper with "real" records instead of mocks """
 | 
						|
 | 
						|
    def setUp(self):
 | 
						|
        super(TestMapperRecordsets, self).setUp()
 | 
						|
        self.comp_registry.load_components('connector')
 | 
						|
 | 
						|
        backend_record = mock.Mock()
 | 
						|
        backend_record.env = self.env
 | 
						|
        self.work = WorkContext(model_name='res.partner',
 | 
						|
                                collection=backend_record,
 | 
						|
                                components_registry=self.comp_registry)
 | 
						|
 | 
						|
    def test_mapping_modifier_follow_m2o_relations(self):
 | 
						|
        """ Map with the follow_m2o_relations modifier """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
 | 
						|
            direct = [
 | 
						|
                (follow_m2o_relations('parent_id.name'), 'parent_name'),
 | 
						|
            ]
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        partner = self.env.ref('base.res_partner_address_4')
 | 
						|
        mapper = self.comp_registry['my.mapper'](self.work)
 | 
						|
        map_record = mapper.map_record(partner)
 | 
						|
        expected = {'parent_name': 'Agrolait'}
 | 
						|
        self.assertEqual(map_record.values(), expected)
 | 
						|
        self.assertEqual(map_record.values(for_create=True), expected)
 | 
						|
 | 
						|
 | 
						|
class TestMapperBinding(TransactionComponentRegistryCase):
 | 
						|
    """ Test Mapper with Bindings"""
 | 
						|
 | 
						|
    def setUp(self):
 | 
						|
        super(TestMapperBinding, self).setUp()
 | 
						|
        self.comp_registry.load_components('connector')
 | 
						|
 | 
						|
        backend_record = mock.Mock()
 | 
						|
        backend_record.env = self.env
 | 
						|
        backend_record._name = 'my.collection'
 | 
						|
        self.work = WorkContext(model_name='res.partner',
 | 
						|
                                collection=backend_record,
 | 
						|
                                components_registry=self.comp_registry)
 | 
						|
 | 
						|
        self.country_binder = mock.MagicMock(name='country_binder')
 | 
						|
        self.country_binder.return_value = self.country_binder
 | 
						|
        self.country_binder._name = 'test.binder'
 | 
						|
        self.country_binder._inherit = 'base.binder'
 | 
						|
        self.country_binder.apply_on_models = ['res.country']
 | 
						|
        self.country_binder._usage = 'binder'
 | 
						|
        self.country_binder._collection = 'my.collection'
 | 
						|
        self.country_binder._abstract = False
 | 
						|
        self.comp_registry['test.binder'] = self.country_binder
 | 
						|
 | 
						|
    def test_mapping_m2o_to_external(self):
 | 
						|
        """ Map a direct record with the m2o_to_external modifier function """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
            _apply_on = 'res.partner'
 | 
						|
 | 
						|
            direct = [(m2o_to_external('country_id'), 'country')]
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        partner = self.env.ref('base.main_partner')
 | 
						|
        partner.write({'country_id': self.env.ref('base.ch').id})
 | 
						|
        self.country_binder.to_external.return_value = 10
 | 
						|
 | 
						|
        mapper = self.comp_registry['my.mapper'](self.work)
 | 
						|
        map_record = mapper.map_record(partner)
 | 
						|
        self.assertEqual(map_record.values(), {'country': 10})
 | 
						|
        self.country_binder.to_external.assert_called_once_with(
 | 
						|
            partner.country_id.id, wrap=False)
 | 
						|
 | 
						|
    def test_mapping_backend_to_m2o(self):
 | 
						|
        """ Map a direct record with the backend_to_m2o modifier function """
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
            _apply_on = 'res.partner'
 | 
						|
 | 
						|
            direct = [(external_to_m2o('country'), 'country_id')]
 | 
						|
 | 
						|
        self._build_components(MyMapper)
 | 
						|
 | 
						|
        record = {'country': 10}
 | 
						|
        ch = self.env.ref('base.ch')
 | 
						|
        self.country_binder.to_internal.return_value = ch
 | 
						|
        mapper = self.comp_registry['my.mapper'](self.work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        self.assertEqual(map_record.values(), {'country_id': ch.id})
 | 
						|
        self.country_binder.to_internal.assert_called_once_with(
 | 
						|
            10, unwrap=False)
 | 
						|
 | 
						|
    def test_mapping_record_children_no_map_child(self):
 | 
						|
        """ Map a record with children, using default MapChild """
 | 
						|
        # we need these components which make the 'link' between
 | 
						|
        # the main mapper and the line mapper
 | 
						|
 | 
						|
        class LineMapper(Component):
 | 
						|
            _name = 'line.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
            _apply_on = 'res.currency.rate'
 | 
						|
 | 
						|
            direct = [('name', 'name')]
 | 
						|
 | 
						|
            @mapping
 | 
						|
            def price(self, record):
 | 
						|
                return {'rate': record['rate'] * 2}
 | 
						|
 | 
						|
            @only_create
 | 
						|
            @mapping
 | 
						|
            def discount(self, record):
 | 
						|
                return {'test': .5}
 | 
						|
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
            _apply_on = 'res.currency'
 | 
						|
 | 
						|
            direct = [('name', 'name')]
 | 
						|
 | 
						|
            children = [('lines', 'line_ids', 'res.currency.rate')]
 | 
						|
 | 
						|
        self._build_components(LineMapper, MyMapper)
 | 
						|
 | 
						|
        record = {'name': 'SO1',
 | 
						|
                  'lines': [{'name': '2013-11-07',
 | 
						|
                             'rate': 10},
 | 
						|
                            {'name': '2013-11-08',
 | 
						|
                             'rate': 20}]}
 | 
						|
        mapper = self.comp_registry['my.mapper'](self.work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'name': 'SO1',
 | 
						|
                    'line_ids': [(0, 0, {'name': '2013-11-07',
 | 
						|
                                         'rate': 20}),
 | 
						|
                                 (0, 0, {'name': '2013-11-08',
 | 
						|
                                         'rate': 40})]
 | 
						|
                    }
 | 
						|
        self.assertEqual(map_record.values(), expected)
 | 
						|
        expected = {'name': 'SO1',
 | 
						|
                    'line_ids': [(0, 0, {'name': '2013-11-07',
 | 
						|
                                         'rate': 20,
 | 
						|
                                         'test': .5}),
 | 
						|
                                 (0, 0, {'name': '2013-11-08',
 | 
						|
                                         'rate': 40,
 | 
						|
                                         'test': .5})]
 | 
						|
                    }
 | 
						|
        self.assertEqual(map_record.values(for_create=True), expected)
 | 
						|
 | 
						|
    def test_mapping_record_children(self):
 | 
						|
        """ Map a record with children, using defined MapChild """
 | 
						|
        # we need these components which make the 'link' between
 | 
						|
        # the main mapper and the line mapper
 | 
						|
 | 
						|
        class LineMapper(Component):
 | 
						|
            _name = 'line.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
            _apply_on = 'res.currency.rate'
 | 
						|
 | 
						|
            direct = [('name', 'name')]
 | 
						|
 | 
						|
            @mapping
 | 
						|
            def price(self, record):
 | 
						|
                return {'rate': record['rate'] * 2}
 | 
						|
 | 
						|
            @only_create
 | 
						|
            @mapping
 | 
						|
            def discount(self, record):
 | 
						|
                return {'test': .5}
 | 
						|
 | 
						|
        class LineImportMapChild(Component):
 | 
						|
            _name = 'line.map.child.import'
 | 
						|
            _inherit = 'base.map.child.import'
 | 
						|
            _apply_on = 'res.currency.rate'
 | 
						|
 | 
						|
            def format_items(self, items_values):
 | 
						|
                return [('ABC', values) for values in items_values]
 | 
						|
 | 
						|
        class MyMapper(Component):
 | 
						|
            _name = 'my.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
            _apply_on = 'res.currency'
 | 
						|
 | 
						|
            direct = [('name', 'name')]
 | 
						|
 | 
						|
            children = [('lines', 'line_ids', 'res.currency.rate')]
 | 
						|
 | 
						|
        self._build_components(LineMapper, LineImportMapChild, MyMapper)
 | 
						|
 | 
						|
        record = {'name': 'SO1',
 | 
						|
                  'lines': [{'name': '2013-11-07',
 | 
						|
                             'rate': 10},
 | 
						|
                            {'name': '2013-11-08',
 | 
						|
                             'rate': 20}]}
 | 
						|
        mapper = self.comp_registry['my.mapper'](self.work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
        expected = {'name': 'SO1',
 | 
						|
                    'line_ids': [('ABC', {'name': '2013-11-07',
 | 
						|
                                          'rate': 20}),
 | 
						|
                                 ('ABC', {'name': '2013-11-08',
 | 
						|
                                          'rate': 40})]
 | 
						|
                    }
 | 
						|
        self.assertEqual(map_record.values(), expected)
 | 
						|
        expected = {'name': 'SO1',
 | 
						|
                    'line_ids': [('ABC', {'name': '2013-11-07',
 | 
						|
                                          'rate': 20,
 | 
						|
                                          'test': .5}),
 | 
						|
                                 ('ABC', {'name': '2013-11-08',
 | 
						|
                                          'rate': 40,
 | 
						|
                                          'test': .5})]
 | 
						|
                    }
 | 
						|
        self.assertEqual(map_record.values(for_create=True), expected)
 | 
						|
 | 
						|
    def test_mapping_record_children_void(self):
 | 
						|
        """ Map a record with children, using defined MapChild """
 | 
						|
 | 
						|
        class LineMapper(Component):
 | 
						|
            _name = 'line.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
            _apply_on = 'res.currency.rate'
 | 
						|
 | 
						|
            @mapping
 | 
						|
            def price(self, record):
 | 
						|
                rate = record.get('rate')
 | 
						|
                if rate and rate < 40:
 | 
						|
                    return {'rate': record['rate'] * 2}
 | 
						|
 | 
						|
        class SaleLineImportMapChild(Component):
 | 
						|
            _name = 'sale.line.mapper'
 | 
						|
            _inherit = 'base.map.child.import'
 | 
						|
            _apply_on = 'res.currency.rate'
 | 
						|
 | 
						|
            def format_items(self, items_values):
 | 
						|
                return [('ABC', values) for values in items_values]
 | 
						|
 | 
						|
        class ObjectMapper(Component):
 | 
						|
            _name = 'currency.mapper'
 | 
						|
            _inherit = 'base.import.mapper'
 | 
						|
            _apply_on = 'res.currency'
 | 
						|
 | 
						|
            direct = [('name', 'name')]
 | 
						|
 | 
						|
            children = [('lines', 'line_ids', 'res.currency.rate')]
 | 
						|
 | 
						|
        self._build_components(
 | 
						|
            ObjectMapper,
 | 
						|
            SaleLineImportMapChild,
 | 
						|
            LineMapper
 | 
						|
        )
 | 
						|
 | 
						|
        # Test with an excluded child record
 | 
						|
        record = {
 | 
						|
            'name': 'SO1',
 | 
						|
            'lines': [
 | 
						|
                {'rate': 10},
 | 
						|
                {'rate': 20},
 | 
						|
                {'rate': 30},
 | 
						|
                {'rate': 40},
 | 
						|
            ]
 | 
						|
        }
 | 
						|
        mapper = self.comp_registry['currency.mapper'](self.work)
 | 
						|
        map_record = mapper.map_record(record)
 | 
						|
 | 
						|
        expected = {
 | 
						|
            'name': 'SO1',
 | 
						|
            'line_ids': [
 | 
						|
                ('ABC', {'rate': 20}),
 | 
						|
                ('ABC', {'rate': 40}),
 | 
						|
                ('ABC', {'rate': 60}),
 | 
						|
            ]
 | 
						|
        }
 | 
						|
        self.assertEqual(map_record.values(), expected)
 |