def _map_direct(self, record, from_attr, to_attr): """ Apply the ``direct`` mappings. :param record: record to convert from a source to a target :param from_attr: name of the source attribute or a callable :type from_attr: callable | str :param to_attr: name of the target attribute :type to_attr: str """ if callable(from_attr): return from_attr(self, record, to_attr) value = record[from_attr] if not value: return False # Backward compatibility: when a field is a relation, and a modifier is # not used, we assume that the relation model is a binding. # Use an explicit modifier external_to_m2o in the 'direct' mappings to # change that. field = self.model._fields[to_attr] if field.type == 'many2one': mapping_func = external_to_m2o(from_attr) value = mapping_func(self, record, to_attr) return value
class AddressImportMapper(Component): _name = 'prestashop.address.mappper' _inherit = 'prestashop.import.mapper' _apply_on = 'prestashop.address' direct = [ ('address1', 'street'), ('address2', 'street2'), ('city', 'city'), ('other', 'comment'), ('phone', 'phone'), ('phone_mobile', 'mobile'), ('postcode', 'zip'), ('date_add', 'date_add'), ('date_upd', 'date_upd'), (external_to_m2o('id_customer'), 'prestashop_partner_id'), ] @mapping def backend_id(self, record): return {'backend_id': self.backend_record.id} @mapping def parent_id(self, record): binder = self.binder_for('prestashop.res.partner') parent = binder.to_internal(record['id_customer'], unwrap=True) return {'parent_id': parent.id} @mapping def name(self, record): parts = [record['firstname'], record['lastname']] if record['alias']: parts.append('(%s)' % record['alias']) name = ' '.join(p.strip() for p in parts if p.strip()) return {'name': name} @mapping def customer(self, record): return {'customer': True} @mapping def country(self, record): if record.get('id_country'): binder = self.binder_for('prestashop.res.country') country = binder.to_internal(record['id_country'], unwrap=True) return {'country_id': country.id} return {} @mapping def company_id(self, record): return {'company_id': self.backend_record.company_id.id} @only_create @mapping def type(self, record): # do not set 'contact', otherwise the address fields are shared with # the parent return {'type': 'other'}
class RancherServiceImportMapper(Component): _name = 'rancher.import.mapper.service' _inherit = 'rancher.import.mapper' _apply_on = 'rancher.service' direct = [('name', 'name'), ('description', 'description'), ('state', 'state'), ('scale', 'scale_max'), ('healthState', 'state_health'), ('scale', 'scale_current'), (external_to_m2o('accountId', 'rancher.environment'), 'environment_id'), (external_to_m2o('stackId', 'rancher.stack'), 'stack_id'), ] config_serialized = config_serialized
class RancherInstanceImportMapper(Component): _name = 'rancher.import.mapper.instance' _inherit = 'rancher.import.mapper' _apply_on = 'rancher.instance' direct = [ ('name', 'name'), ('description', 'description'), ('state', 'state'), ('healthState', 'state_health'), (external_to_m2o('accountId', 'rancher.environment'), 'environment_id'), (external_to_m2o('hostId', 'rancher.host'), 'host_id'), ] @mapping def service_and_stack_id(self, record): if not record.get('serviceIds'): return service = self.env['rancher.service'].search([ ('backend_id', '=', self.backend_record.id), ('external_id', '=', record['serviceIds'][0]) ]) if service: return { 'service_id': service.odoo_id.id, 'stack_id': service.stack_id.id, } @mapping def mount_ids(self, record): binder = self.binder_for('rancher.volume') mounts = [(5, 0, 0)] for mount in record['mounts'] or []: volume = binder.to_internal(mount.volumeId, unwrap=True) values = { 'volume_id': volume.id, 'path': mount.path, 'is_read_only': mount.permission == 'ro', } mounts.append((0, 0, values)) return {'mount_ids': mounts}
class RancherStackImportMapper(Component): _name = 'rancher.import.mapper.stack' _inherit = 'rancher.import.mapper' _apply_on = 'rancher.stack' direct = [ ('name', 'name'), ('description', 'description'), ('state', 'state'), ('healthState', 'state_health'), (external_to_m2o('accountId', 'rancher.environment'), 'environment_id'), ]
class RancherVolumeImportMapper(Component): _name = 'rancher.import.mapper.volume' _inherit = 'rancher.import.mapper' _apply_on = 'rancher.volume' direct = [ ('name', 'name'), ('externalId', 'external_name'), ('state', 'state'), ('accessMode', 'access_mode'), ('driver', 'driver'), ('isHostPath', 'is_host_path'), ('kind', 'type'), ('size_mb', 'capacity'), (external_to_m2o('accountId', 'rancher.environment'), 'environment_id'), (external_to_m2o('hostId', 'rancher.host'), 'host_id'), ] @mapping def capacity_uom_id(self, _): mib = self.env.ref('product_uom_technology.product_uom_gib') return {'capacity_uom_id': mib.id}
class ProductCategoryMapper(Component): _name = 'prestashop.product.category.import.mapper' _inherit = 'prestashop.import.mapper' _apply_on = 'prestashop.product.category' _model_name = 'prestashop.product.category' direct = [ ('description', 'description'), ('link_rewrite', 'link_rewrite'), ('meta_description', 'meta_description'), ('meta_keywords', 'meta_keywords'), ('meta_title', 'meta_title'), (external_to_m2o('id_shop_default'), 'default_shop_id'), ('active', 'active'), ('position', 'position') ] @mapping def name(self, record): if record['name'] is None: return {'name': ''} return {'name': record['name']} # @mapping # def backend_id(self, record): # return {'backend_id': self.backend_record.id} @mapping def parent_id(self, record): if record['id_parent'] == '0': return {} category = self.binder_for('prestashop.product.category').to_internal( record['id_parent'], unwrap=True) return { 'parent_id': category.id, } @mapping def data_add(self, record): if record['date_add'] == '0000-00-00 00:00:00': return {'date_add': datetime.datetime.now()} return {'date_add': record['date_add']} @mapping def data_upd(self, record): if record['date_upd'] == '0000-00-00 00:00:00': return {'date_upd': datetime.datetime.now()} return {'date_upd': record['date_upd']}
class ShopImportMapper(Component): _name = 'prestashop.shop.mapper' _inherit = 'prestashop.import.mapper' _apply_on = 'prestashop.shop' direct = [ ('name', 'name'), (external_to_m2o('id_shop_group'), 'shop_group_id'), ] @mapping def backend_id(self, record): return {'backend_id': self.backend_record.id} @mapping def opener_id(self, record): return {'odoo_id': self.backend_record.warehouse_id.id}
class RancherApplicationVersionImportMapper(Component): _name = 'rancher.import.mapper.application.version' _inherit = 'rancher.import.mapper' _apply_on = 'rancher.application.version' direct = [('version', 'name'), (external_to_m2o('templateId', 'rancher.application'), 'application_id'), ] @mapping @only_create def file_ids(self, record): files = [] for name, data in (record['files'] or {}).items(): file_values = { 'datas': base64.b64encode(data.encode('utf-8')), 'datas_fname': name, 'name': name, 'type': 'binary', 'mimetype': 'text/plain', } files.append((0, 0, file_values)) return {'file_ids': files} @mapping def option_ids(self, record): options = [(5, 0, 0)] for question in record['questions'] or []: option_values = { 'name': question['variable'], 'description': question['description'], 'display_name': question['label'], 'is_required': question['required'], 'value_default': question['default'], } options.append((0, 0, option_values)) return {'option_ids': options} @mapping def external_id(self, record): return { 'external_id': 'catalog://%s' % record['id'], }
class TemplateMapper(Component): _name = 'prestashop.product.template.mapper' _inherit = 'prestashop.import.mapper' _apply_on = 'prestashop.product.template' direct = [ ('weight', 'weight'), ('wholesale_price', 'wholesale_price'), ('wholesale_price', 'standard_price'), (external_to_m2o('id_shop_default'), 'default_shop_id'), ('link_rewrite', 'link_rewrite'), ('reference', 'reference'), ('available_for_order', 'available_for_order'), ('on_sale', 'on_sale'), ] def _apply_taxes(self, tax, price): if self.backend_record.taxes_included == tax.price_include: return price factor_tax = tax.price_include and (1 + tax.amount / 100) or 1.0 if self.backend_record.taxes_included: if not tax.price_include: return price / factor_tax else: if tax.price_include: return price * factor_tax @mapping def list_price(self, record): price = 0.0 tax = self._get_tax_ids(record) if record['price'] != '': price = float(record['price']) price = self._apply_taxes(tax, price) return {'list_price': price} @mapping def tags_to_text(self, record): associations = record.get('associations', {}) tags = associations.get('tags', {}).get( self.backend_record.get_version_ps_key('tag'), []) tag_adapter = self.component(usage='backend.adapter', model_name='_prestashop_product_tag') if not isinstance(tags, list): tags = [tags] if tags: ps_tags = tag_adapter.search( filters={ 'filter[id]': '[%s]' % '|'.join(x['id'] for x in tags), 'display': '[name]' }) if ps_tags: return {'tags': ','.join(x['name'] for x in ps_tags)} @mapping def name(self, record): if record['name']: return {'name': record['name']} return {'name': 'noname'} @mapping def date_add(self, record): if record['date_add'] == '0000-00-00 00:00:00': return {'date_add': datetime.datetime.now()} return {'date_add': record['date_add']} @mapping def date_upd(self, record): if record['date_upd'] == '0000-00-00 00:00:00': return {'date_upd': datetime.datetime.now()} return {'date_upd': record['date_upd']} def has_combinations(self, record): associations = record.get('associations', {}) combinations = associations.get('combinations', {}).get( self.backend_record.get_version_ps_key('combinations')) return len(combinations or '') != 0 @only_create @mapping def odoo_id(self, record): """ Will bind the product to an existing one with the same code """ if self.backend_record.matching_product_template: if self.has_combinations(record): # Browse combinations for matching products and find if there # is a potential template to be matched template = self.env['product.template'] associations = record.get('associations', {}) combinations = associations.get('combinations', {}).get( self.backend_record.get_version_ps_key('combinations')) if len(combinations) == 1: # Defensive mode when product have no combinations, force # the list mode combinations = [combinations] for prod in combinations: backend_adapter = self.component( usage='backend.adapter', model_name='prestashop.product.combination') variant = backend_adapter.read(int(prod['id'])) code = variant.get(self.backend_record.matching_product_ch) if not code and self.backend_record.matching_product_ch == 'barcode': code = variant.get('ean13') if code: if self.backend_record.matching_product_ch == 'reference': product = self.env['product.product'].search([ ('default_code', '=', code) ]) if len(product) > 1: raise ValidationError( _('Error! Multiple products found with ' 'combinations reference %s. Maybe consider to ' 'update you datas') % code) template |= product.product_tmpl_id if self.backend_record.matching_product_ch == 'barcode': product = self.env['product.product'].search([ ('barcode', '=', code) ]) if len(product) > 1: raise ValidationError( _('Error! Multiple products found with ' 'combinations reference %s. Maybe consider to ' 'update you datas') % code) template |= product.product_tmpl_id _logger.debug('template %s' % template) if len(template) == 1: return {'odoo_id': template.id} if len(template) > 1: raise ValidationError( _('Error! Multiple templates are found with ' 'combinations reference. Maybe consider to change ' 'matching option')) else: code = record.get(self.backend_record.matching_product_ch) if not code and self.backend_record.matching_product_ch == 'barcode': code = record.get('ean13') if code: if self.backend_record.matching_product_ch == 'reference': if self._template_code_exists(code): product = self.env['product.template'].search( [('default_code', '=', code)], limit=1) if product: return {'odoo_id': product.id} if self.backend_record.matching_product_ch == 'barcode': product = self.env['product.template'].search( [('barcode', '=', code)], limit=1) if product: return {'odoo_id': product.id} return {} def _template_code_exists(self, code): model = self.env['product.template'] template_ids = model.search([ ('default_code', '=', code), ('company_id', '=', self.backend_record.company_id.id), ], limit=1) return len(template_ids) > 0 @mapping def default_code(self, record): if self.has_combinations(record): return {} code = record.get('reference') if not code: code = "backend_%d_product_%s" % (self.backend_record.id, record['id']) if not self._template_code_exists(code): return {'default_code': code} i = 1 current_code = '%s_%d' % (code, i) while self._template_code_exists(current_code): i += 1 current_code = '%s_%d' % (code, i) return {'default_code': current_code} def clear_html_field(self, content): html = html2text.HTML2Text() html.ignore_images = True html.ignore_links = True return html.handle(content) @staticmethod def sanitize_html(content): content = BeautifulSoup(content, 'html.parser') # Prestashop adds both 'lang="fr-ch"' and 'xml:lang="fr-ch"' # but Odoo tries to parse the xml for the translation and fails # due to the unknow namespace for child in content.find_all(lambda tag: tag.has_attr('xml:lang')): del child['xml:lang'] return content.prettify() @mapping def descriptions(self, record): return { 'description': self.clear_html_field(record.get('description_short', '')), 'description_html': self.sanitize_html(record.get('description', '')), 'description_short_html': self.sanitize_html(record.get('description_short', '')), } @mapping def active(self, record): return {'always_available': bool(int(record['active']))} @mapping def sale_ok(self, record): # if this product has combinations, we do not want to sell this # product, but its combinations (so sale_ok = False in that case). return {'sale_ok': True} @mapping def purchase_ok(self, record): return {'purchase_ok': True} @mapping def categ_ids(self, record): categories = record['associations'].get('categories', {}).get( self.backend_record.get_version_ps_key('category'), []) if not isinstance(categories, list): categories = [categories] product_categories = self.env['product.category'].browse() binder = self.binder_for('prestashop.product.category') for ps_category in categories: product_categories |= binder.to_internal( ps_category['id'], unwrap=True, ) return {'categ_ids': [(6, 0, product_categories.ids)]} @mapping def default_category_id(self, record): if not int(record['id_category_default']): return binder = self.binder_for('prestashop.product.category') category = binder.to_internal( record['id_category_default'], unwrap=True, ) if category: return {'prestashop_default_category_id': category.id} @mapping def backend_id(self, record): return {'backend_id': self.backend_record.id} @mapping def company_id(self, record): return {'company_id': self.backend_record.company_id.id} @mapping def barcode(self, record): if self.has_combinations(record): return {} barcode = record.get('barcode') or record.get('ean13') if barcode in ['', '0']: return {} if self.env['barcode.nomenclature'].check_ean(barcode): return {'barcode': barcode} return {} def _get_tax_ids(self, record): # if record['id_tax_rules_group'] == '0': # return {} binder = self.binder_for('prestashop.account.tax.group') tax_group = binder.to_internal( record['id_tax_rules_group'], unwrap=True, ) return tax_group.tax_ids # TOREVIEW: Tax rules group is not the same that odoo tax groups # @mapping # def taxes_id(self, record): # taxes = self._get_tax_ids(record) # return {'taxes_id': [(6, 0, taxes.ids)]} @mapping def type(self, record): # If the product has combinations, this main product is not a real # product. So it is set to a 'service' kind of product. Should better # be a 'virtual' product... but it does not exist... # The same if the product is a virtual one in prestashop. if record['type']['value'] and record['type']['value'] == 'virtual': return {"type": 'service'} return {"type": 'product'}
class MyMapper(Component): _name = "my.mapper" _inherit = "base.import.mapper" _apply_on = "res.partner" direct = [(external_to_m2o("country"), "country_id")]
class MyMapper(Component): _name = 'my.mapper' _inherit = 'base.import.mapper' _apply_on = 'res.partner' direct = [(external_to_m2o('country'), 'country_id')]
class PartnerImportMapper(Component): _name = 'prestashop.res.partner.mapper' _inherit = 'prestashop.import.mapper' _apply_on = 'prestashop.res.partner' direct = [ ('date_add', 'date_add'), ('date_upd', 'date_upd'), ('email', 'email'), ('newsletter', 'newsletter'), ('company', 'company'), ('active', 'active'), ('note', 'comment'), (external_to_m2o('id_shop_group'), 'shop_group_id'), (external_to_m2o('id_shop'), 'shop_id'), (external_to_m2o('id_default_group'), 'default_category_id'), ] @mapping def backend_id(self, record): return {'backend_id': self.backend_record.id} @mapping def pricelist(self, record): binder = self.binder_for('prestashop.groups.pricelist') pricelist = binder.to_internal(record['id_default_group'], unwrap=True) if not pricelist: return {} return {'property_product_pricelist': pricelist.id} @mapping def birthday(self, record): if record['birthday'] in ['0000-00-00', '']: return {} return {'birthday': record['birthday']} @mapping def name(self, record): parts = [record['firstname'], record['lastname']] name = ' '.join(p.strip() for p in parts if p.strip()) return {'name': name} @mapping def groups(self, record): groups = record.get('associations', {}).get('groups', {}).get( self.backend_record.get_version_ps_key('group'), []) if not isinstance(groups, list): groups = [groups] model_name = 'prestashop.res.partner.category' partner_category_bindings = self.env[model_name].browse() binder = self.binder_for(model_name) for group in groups: partner_category_bindings |= binder.to_internal(group['id']) result = { 'group_ids': [(6, 0, partner_category_bindings.ids)], 'category_id': [(4, b.odoo_id.id) for b in partner_category_bindings] } return result @mapping def lang(self, record): binder = self.binder_for('prestashop.res.lang') erp_lang = None if record.get('id_lang'): erp_lang = binder.to_internal(record['id_lang']) if not erp_lang: erp_lang = self.env.ref('base.lang_en') return {'lang': erp_lang.code} @mapping def customer(self, record): return {'customer': True} @mapping def is_company(self, record): # This is sad because we _have_ to have a company partner if we want to # store multiple adresses... but... well... we have customers who want # to be billed at home and be delivered at work... (...)... return {'is_company': True} @mapping def company_id(self, record): return {'company_id': self.backend_record.company_id.id} @mapping def backend_id(self, record): return {'backend_id': self.backend_record.id}
class RancherHostImportMapper(Component): _name = 'rancher.import.mapper.host' _inherit = 'rancher.import.mapper' _apply_on = 'rancher.host' direct = [ ('hostname', 'name'), ('description', 'description'), ('state', 'state'), (external_to_m2o('accountId', 'rancher.environment'), 'environment_id'), ] @mapping def cpu_metric_id(self, record): cpu_info = record['info'].cpuInfo mhz = self.env.ref('product_uom_technology.product_uom_mhz') metric = self.env['infrastructure.metric.cpu'].create({ 'name': cpu_info.modelName, 'load_one': cpu_info.loadAvg[0], 'load_five': cpu_info.loadAvg[1], 'load_fifteen': cpu_info.loadAvg[2], 'core_count': cpu_info.count, 'frequency': cpu_info.mhz, 'frequency_uom_id': mhz.id, 'core_metric_ids': [(0, 0, { 'percent_use': p }) for p in cpu_info.cpuCoresPercentages], }) return {'cpu_metric_id': metric.id} @mapping def memory_metric_id(self, record): memory_info = record['info'].memoryInfo mib = self.env.ref('product_uom_technology.product_uom_mib') metric = self.env['infrastructure.metric.memory'].create({ 'memory_free': memory_info.memFree, 'memory_cache': memory_info.cached, 'memory_buffer': memory_info.buffers, 'memory_total': memory_info.memTotal, 'memory_available': memory_info.memAvailable, 'memory_used': memory_info.memTotal - memory_info.memFree, 'swap_cache': memory_info.swapCached, 'swap_free': memory_info.swapfree, 'swap_total': memory_info.swaptotal, 'uom_id': mib.id, }) return {'memory_metric_id': metric.id} @mapping def operating_system_id(self, record): regex = re.compile(r'(?P<name>.+?) (?P<version>\d+\.\d+.*)') matches = regex.search(record['info'].osInfo.operatingSystem) software = self.env['infrastructure.software.version'].get_or_create( name=matches.group('name').strip(), version=matches.group('version').strip(), type='os', ) return {'operating_system_id': software.id} @mapping def kernel_id(self, record): software = self.env['infrastructure.software.version'].get_or_create( name='Linux', version=record['info'].osInfo.kernelVersion, type='kernel', ) return {'kernel_id': software.id} @mapping def virtualization_id(self, record): regex = re.compile( r'(?P<name>.+?) (v(ersion)? ?)?(?P<version>\d+\.\d+.*)', ) matches = regex.search(record['info'].osInfo.dockerVersion) software = self.env['infrastructure.software.version'].get_or_create( name=matches.group('name').strip(), version=matches.group('version').strip(), type='virtualization', ) return {'virtualization_id': software.id} @mapping def label_ids(self, record): labels = self.env['infrastructure.option'] for name, value in record['labels'].items(): labels += labels.get_or_create(name, value) return {'label_ids': [(6, 0, labels.ids)]}