class ShopinvaderProduct(models.Model): _inherit = "shopinvader.product" hierarchical_categories = fields.Serialized( compute="_compute_shopinvader_category", string="Hierarchical Categories", ) def _category_to_elastic_index_data(self, shopinvader_category): categ = shopinvader_category parent_names = [] parent_id = categ.parent_id while parent_id: parent_names.append(parent_id.name) parent_id = parent_id.parent_id return { "level": categ.level + 1, "value": categ.name, "ancestors": parent_names, "order": categ.sequence, } def _compute_shopinvader_category(self): super(ShopinvaderProduct, self)._compute_shopinvader_category() for record in self: values = [] for categ in record.shopinvader_categ_ids: values.append(self._category_to_elastic_index_data(categ)) record.hierarchical_categories = values
class ShopinvaderVariant(models.Model): _inherit = 'shopinvader.variant' stock_data = fields.Serialized(compute='_compute_stock_data') def _get_stock_export_key(self): self.ensure_one() line = self.env['ir.exports.line'].search([ ('export_id', '=', self.index_id.exporter_id.id), ('name', '=', 'stock_data'), ]) if line.alias: return line.alias.split(':')[1] else: return line.name def _prepare_stock_data(self): stock_field = self.backend_id.product_stock_field_id.name return {'qty': self[stock_field]} def _compute_stock_data(self): result = defaultdict(dict) for backend in self.mapped('backend_id'): for wh_key, wh_ids in\ backend._get_warehouse_list_for_export().items(): for loc_record in self.filtered( lambda s: s.backend_id == backend)\ .with_context(warehouse=wh_ids): result[loc_record.id][wh_key] =\ loc_record._prepare_stock_data() for record in self: record.stock_data = result[record.id]
class IrAttachmentStorage(models.Model): _name = 'ir.attachment.storage' _description = 'Attachment Storage' # TODO name = fields.Char(required=True) model = fields.Char(required=True) config = fields.Serialized(help="JSON containing all the information")
class KpiKpiHistory(models.Model): _name = "kpi.kpi.history" _description = "KPI history" _order = "create_date DESC" kpi_id = fields.Many2one( "kpi.kpi", required=True, ondelete="cascade", readonly=True ) value = fields.Serialized(readonly=True) raw_value = fields.Char(compute="_compute_raw_value") name = fields.Char(related="kpi_id.name") widget = fields.Selection( selection=lambda self: self.env["kpi.kpi"]._fields["widget"].selection, required=True, ) @api.depends("value") def _compute_raw_value(self): for record in self: record.raw_value = json.dumps(record.value) def show_form(self): self.ensure_one() action = self.env.ref("kpi_dashboard.kpi_kpi_history_act_window") result = action.read()[0] result.update( { "res_id": self.id, "target": "new", "view_mode": "form", "views": [(self.env.context.get("form_id"), "form")], } ) return result
class ShopinvaderVariant(models.Model): _name = "shopinvader.variant" _inherit = ["shopinvader.variant"] image_categories = fields.Serialized(compute="_compute_image_categories") @api.model def _get_category_by_backend(self, record): return record.shopinvader_categ_ids.filtered( lambda c, v=record: c.record_id == v.categ_id and c.index_id.backend_id == v.index_id.backend_id and c.images ) @api.multi @api.depends( "shopinvader_categ_ids", "shopinvader_categ_ids.index_id", "shopinvader_categ_ids.index_id.backend_id", "shopinvader_categ_ids.images", ) def _compute_image_categories(self): """ Compute function for image_categories field. This field should contains the value of images field into the first category found (with same backend) :return: """ for record in self: category = self._get_category_by_backend(record) record.image_categories = first(category).images or []
class ShopinvaderVariant(models.Model): _inherit = "shopinvader.variant" stock_data = fields.Serialized(compute="_compute_stock_data") def _get_stock_export_key(self): self.ensure_one() line = self.env["ir.exports.line"].search([ ("export_id", "=", self.index_id.exporter_id.id), ("name", "=", "stock_data"), ]) if line.alias: return line.alias.split(":")[1] else: return line.name def _prepare_stock_data(self): stock_field = self.backend_id.product_stock_field_id.name return {"qty": self[stock_field]} @api.multi def _compute_stock_data(self): result = defaultdict(dict) for backend in self.mapped("backend_id"): loc_records = self.filtered(lambda s: s.backend_id == backend) for ( wh_key, wh_ids, ) in backend._get_warehouse_list_for_export().items(): for loc_record in loc_records.with_context(warehouse=wh_ids): result[loc_record. id][wh_key] = loc_record._prepare_stock_data() for record in self: record.stock_data = result[record.id]
class ShopinvaderImageMixin(models.AbstractModel): _name = 'shopinvader.image.mixin' images = fields.Serialized(compute='_compute_image', string='Shopinvader Image') def _compute_image(self): for record in self: record.images = record._get_images_for_record() def _get_odoo_image_url(self, base_url): return base_url + '/web/image/%s/%s/image' % (self._name, self.id) def _get_images_for_record(self): self.ensure_one() base_url = self.env['ir.config_parameter'].sudo()\ .get_param('web.base.url') image_url = self._get_odoo_image_url(base_url) res = { 'original': { 'src': image_url, 'alt': self.name, } } resizes = self.backend_id['%s_resize_ids' % self._name.replace('.', '_')] for resize in resizes: res[resize.key] = { 'src': image_url + '/%sx%s' % (resize.size_x, resize.size_y), 'alt': self.name, } return [res]
class KpiKpiHistory(models.Model): _name = 'kpi.kpi.history' _description = 'KPI history' _order = 'create_date DESC' kpi_id = fields.Many2one('kpi.kpi', required=True, ondelete='cascade', readonly=True) value = fields.Serialized(readonly=True) raw_value = fields.Char(compute='_compute_raw_value') name = fields.Char(related='kpi_id.name') widget = fields.Selection( selection=lambda self: self.env['kpi.kpi']._fields['widget'].selection, required=True) @api.depends('value') def _compute_raw_value(self): for record in self: record.raw_value = json.dumps(record.value) def show_form(self): self.ensure_one() action = self.env.ref('kpi_dashboard.kpi_kpi_history_act_window') result = action.read()[0] result.update({ 'res_id': self.id, 'target': 'new', 'view_mode': 'form', 'views': [(self.env.context.get('form_id'), 'form')], }) return result
class SeIndexConfig(models.Model): _name = "se.index.config" _description = "Elasticsearch index configuration" name = fields.Char(required=True) body = fields.Serialized(required=True, default="{}") # This field is used since no widget exists to edit a serialized field # into the web fontend body_str = fields.Text(compute="_compute_body_str", inverse="_inverse_body_str") @api.multi @api.depends("body") def _compute_body_str(self): for rec in self: if rec.body: rec.body_str = json.dumps(rec.body) @api.multi def _inverse_body_str(self): for rec in self: data = None if rec.body_str: data = json.loads(rec.body_str) rec.body = data
class ShopinvaderVariant(models.Model): _inherit = 'shopinvader.variant' rating = fields.Serialized( compute='_compute_rating', string='Rating') def _compute_rating(self): for record in self: reviews = [] distribution = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0} for rating in record.rating_ids: if rating.state == 'approved': reviews.append({ 'nickname': rating.nickname, 'name': rating.name, 'comment': rating.comment, 'rating': rating.rating, 'product_code': rating.product_id.default_code, }) distribution[rating.rating] += 1 if reviews: count = len(reviews) average = sum([c['rating'] for c in reviews]) / count record.rating = { 'reviews': reviews, 'summary': { 'average': average, 'count': count, 'distribution': distribution, }} else: record.rating = {}
class ShopinvaderVariant(models.Model): _inherit = 'shopinvader.variant' attributes = fields.Serialized(compute='_compute_attributes', string='Shopinvader attributes Fields') structured_attributes = fields.Serialized( compute='_compute_structured_attributes', string='Shopinvader attributes Fields') def _get_attr_vals(self, attr): self.ensure_one() if attr.attribute_type == 'select': return self[attr.name]['name'] elif attr.attribute_type == 'multiselect': return self[attr.name].mapped('name') else: return self[attr.name] def _compute_attributes(self): for record in self: attributes = {} for group in record.attribute_set_id.attribute_group_ids: for attr in group.attribute_ids: # all attr start with "x_" we remove it for the export attributes[attr.name[2:]] = record._get_attr_vals(attr) record.attributes = attributes def _compute_structured_attributes(self): for record in self: strc_attr = [] for group in record.attribute_set_id.attribute_group_ids: group_data = { 'group_name': group.name, 'fields': [], } for attr in group.attribute_ids: group_data['fields'].append({ 'name': attr.field_description, 'key': attr.name[2:], 'value': record._get_attr_vals(attr), }) strc_attr.append(group_data) record.structured_attributes = strc_attr
class ShopinvaderImageMixin(models.AbstractModel): _name = "shopinvader.image.mixin" _description = "Shopinvader Image Mixin" _image_field = None images = fields.Serialized( compute="_compute_image", string="Shopinvader Image" ) @api.multi def _compute_image(self): # Note: this computed field depend on the lang used in the context # as the name of the record is used for generating the thumbnail for record in self: record.images = record._get_image_data_for_record() @api.multi def _get_image_url_key(self, image_relation): # You can inherit this method to change the name of the image of # your website. By default we use the name of the product or category # linked to the image processed # Note the url will be slugify by the get_or_create_thumnail self.ensure_one() return self.display_name @api.multi def _get_image_data_for_record(self): self.ensure_one() res = [] resizes = self.backend_id[ "%s_resize_ids" % self._name.replace(".", "_") ] for image_relation in self[self._image_field]: url_key = self._get_image_url_key(image_relation) image_data = {} for resize in resizes: thumbnail = image_relation.image_id.get_or_create_thumbnail( resize.size_x, resize.size_y, url_key=url_key ) image_data[resize.key] = self._prepare_data_resize( thumbnail, image_relation ) res.append(image_data) return res @api.multi def _prepare_data_resize(self, thumbnail, image_relation): """ Prepare data to fill images serialized field :param thumbnail: storage.thumbnail recordset :param image_relation: product.image.relation recordset :return: dict """ self.ensure_one() tag = "" if image_relation.tag_id: tag = image_relation.tag_id.name return {"src": thumbnail.url, "alt": self.name, "tag": tag}
class ShopinvaderVariant(models.Model): _inherit = "shopinvader.variant" attributes = fields.Serialized( compute="_compute_attributes", string="Shopinvader attributes Fields" ) structured_attributes = fields.Serialized( compute="_compute_structured_attributes", string="Shopinvader attributes Fields", ) def _get_attr_vals(self, attr): self.ensure_one() if attr.attribute_type == "select": return self[attr.name]["name"] elif attr.attribute_type == "multiselect": return self[attr.name].mapped("name") else: return self[attr.name] def _compute_attributes(self): for record in self: attributes = {} for group in record.attribute_set_id.attribute_group_ids: for attr in group.attribute_ids: # all attr start with "x_" we remove it for the export attributes[attr.name[2:]] = record._get_attr_vals(attr) record.attributes = attributes def _compute_structured_attributes(self): for record in self: strc_attr = [] for group in record.attribute_set_id.attribute_group_ids: group_data = {"group_name": group.name, "fields": []} for attr in group.attribute_ids: group_data["fields"].append( { "name": attr.field_description, "key": attr.name[2:], "value": record._get_attr_vals(attr), } ) strc_attr.append(group_data) record.structured_attributes = strc_attr
class Sparse(models.Model): _name = 'test_new_api.sparse' data = fields.Serialized() boolean = fields.Boolean(sparse='data') integer = fields.Integer(sparse='data') float = fields.Float(sparse='data') char = fields.Char(sparse='data') selection = fields.Selection([('one', 'One'), ('two', 'Two')], sparse='data') partner = fields.Many2one('res.partner', sparse='data')
class TestSparse(models.TransientModel): _name = 'sparse_fields.test' data = fields.Serialized() boolean = fields.Boolean(sparse='data') integer = fields.Integer(sparse='data') float = fields.Float(sparse='data') char = fields.Char(sparse='data') selection = fields.Selection([('one', 'One'), ('two', 'Two')], sparse='data') partner = fields.Many2one('res.partner', sparse='data')
class ShopinvaderProduct(models.Model): _inherit = "shopinvader.product" hierarchical_categories = fields.Serialized( compute="_compute_shopinvader_category", string="Hierarchical Categories", ) def _compute_shopinvader_category(self): super(ShopinvaderProduct, self)._compute_shopinvader_category() def get_full_name(categ): result = [] while categ: result.insert(0, categ.name) categ = categ.parent_id return " > ".join(result) for record in self: hierarchical_categories = {} for categ in record.shopinvader_categ_ids: hierarchical_categories["lvl%s" % categ.level] = get_full_name( categ.record_id) record.hierarchical_categories = hierarchical_categories @api.model def _get_facetting_values(self, se_bakend, lang): default = [ "categories.id", "Categories.lvl0hierarchical", "Categories.lvl1hierarchical", "Categories.lvl2hierarchical", "main", "redirect_url_key", "url_key", "sku", "price.default.value", ] # Search backend via search engine backend invader_backend = self.env["shopinvader.backend"].search( [("se_backend_id", "=", se_bakend.id)], limit=1) # Product attributes do not have a unique code to reference them. # In the index data, the attributes are exported per each lang # using the translated name, eg: # `variant_attributes.model` for English # `variant_attributes.modele` for French # which means there's no unique key to search for in the search engine. # As a workaround, let's make sure that facet filters use the right one. # TODO: in the long term it would be better to have a unique name # for every filter/attribute # to unify them in indexes and Locomotive settings. filters = invader_backend.with_context(lang=lang.code).filter_ids filter_facetting_values = [f.display_name for f in filters] return default + filter_facetting_values
class HttpSessionUser(models.TransientModel): _name = "http.session.user" _description = "User sessions" user_id = fields.Many2one("res.users", readonly=True) current_session = fields.Boolean(readonly=True) session_id = fields.Char(readonly=True) session_token = fields.Char(readonly=True) ctx = fields.Serialized(readonly=True) geoip = fields.Serialized(readonly=True) update_time = fields.Datetime(readonly=True) @api.multi def kill(self): self.ensure_one() if self.current_session: raise UserError(_("Current session cannot be killed")) store = http.root.session_store session = store.get(self.session_id) session.logout(keep_db=True) store.save(session) self.unlink()
class StockPicking(models.Model): _inherit = "stock.picking" tnt_consignment_data = fields.Serialized() tnt_consignment_mumber = fields.Char(sparse="tnt_consignment_data") tnt_consignment_date = fields.Char(sparse="tnt_consignment_data") tnt_consignment_free_circulation = fields.Char( sparse="tnt_consignment_data") tnt_consignment_sort_split = fields.Char(sparse="tnt_consignment_data") tnt_consignment_destination_depot = fields.Char( sparse="tnt_consignment_data") tnt_consignment_destination_depot_day = fields.Integer( sparse="tnt_consignment_data") tnt_consignment_cluster_code = fields.Char(sparse="tnt_consignment_data") tnt_consignment_origin_depot = fields.Char(sparse="tnt_consignment_data") tnt_consignment_product = fields.Char(sparse="tnt_consignment_data") tnt_consignment_option = fields.Char(sparse="tnt_consignment_data") tnt_consignment_market = fields.Char(sparse="tnt_consignment_data") tnt_consignment_transport = fields.Char(sparse="tnt_consignment_data") tnt_consignment_transit_depot = fields.Char(sparse="tnt_consignment_data") tnt_consignment_xray = fields.Char(sparse="tnt_consignment_data") tnt_piece_data = fields.Serialized() tnt_piece_barcode = fields.Char(sparse="tnt_piece_data")
class ShopinvaderBinding(models.AbstractModel): _name = "shopinvader.binding" _description = "Shopinvader Binding" backend_id = fields.Many2one("shopinvader.backend", string="Backend", required=True) company_id = fields.Many2one(related="backend_id.company_id", store=True) external_id = fields.Char(string="External ID") sync_date = fields.Datetime(string="Last synchronization date") redirect_url_key = fields.Serialized(compute="_compute_redirect_url_key", string="Redirect Url Keys") def _compute_redirect_url_key(self): for record in self: record.redirect_url_key = record.mapped( "redirect_url_url_ids.url_key")
class TaxjarAccountFiscalPosition(models.Model): _name = 'taxjar.account.fiscal.position' _inherit = 'taxjar.binding' _inherits = {'account.fiscal.position': 'odoo_id'} _description = 'TaxJar Nexus Regions' _rec_name = 'name' odoo_id = fields.Many2one( string='Fiscal Position', comodel_name='account.fiscal.position', required=True, ondelete='cascade', ) api_address = fields.Serialized(compute='_compute_api_address', ) @api.multi def _compute_api_address(self): for record in self: record.api_address = { 'id': record.external_id or record.id, 'country': record.country_id.code, 'state': record.state_ids[0].code, } @api.model def get_by_partner(self, partner, backend=None): if backend is None: backend = partner.company_id.taxjar_backend_id if not backend: return if partner.property_account_position_id: return self.search([ ('odoo_id', '=', partner.property_account_position_id.id), ('backend_id', '=', backend.id), ]) return self.search([ ('backend_id', '=', backend.id), ('country_id', '=', partner.state_id.country_id.id), ('state_ids', 'in', partner.state_id.ids), ])
class ShopinvaderProduct(models.Model): _inherit = "shopinvader.product" hierarchical_categories = fields.Serialized( compute="_compute_shopinvader_category", string="Hierarchical Categories", ) def _compute_shopinvader_category(self): super(ShopinvaderProduct, self)._compute_shopinvader_category() def get_full_name(categ): result = [] while categ: result.insert(0, categ.name) categ = categ.parent_id return " > ".join(result) for record in self: hierarchical_categories = {} for categ in record.shopinvader_categ_ids: hierarchical_categories["lvl%s" % categ.level] = get_full_name( categ.record_id ) record.hierarchical_categories = hierarchical_categories @api.model def _get_facetting_values(self, se_bakend): default = [ "categories.id", "Categories.lvl0hierarchical", "Categories.lvl1hierarchical", "Categories.lvl2hierarchical", "main", "redirect_url_key", "url_key", "sku", "price.default.value", ] invader_backend = self.env["shopinvader.backend"].search( [("se_backend_id", "=", se_bakend.id)] ) filters = invader_backend.filter_ids filter_facetting_values = [f.display_name for f in filters] return default + filter_facetting_values
class ProductTemplate(models.Model): _inherit = 'product.template' price_quantity_tiers = fields.Serialized( compute='_compute_price_quantity_tiers', help='This is a list of minimum quantities where pricing for the' ' product changes, along with total costs for each of those' ' quantities. It is computed for the current pricelist.', ) @api.multi def _compute_price_quantity_tiers(self): for record in self: current_website = self.env['website'].get_current_website() pricelist = current_website.get_current_pricelist() pricelist_items = self.env['product.pricelist.item'].search([ ('pricelist_id', '=', pricelist.id), ('product_tmpl_id', '=', record.id), '|', ('date_start', '<=', fields.Date.today()), ('date_start', '=', False), '|', ('date_end', '>=', fields.Date.today()), ('date_end', '=', False), ]) min_quantities = set([]) for price in pricelist_items: min_quantities.add( 1 if price.min_quantity < 1 else price.min_quantity) if current_website.show_implicit_price_tier: min_quantities.add(1) list_results = set([]) for min_qty in min_quantities: subtotal = record.with_context( quantity=min_qty, ).website_price list_results.add((min_qty, subtotal)) list_results = sorted(list(list_results)) # A tier with min qty of 1 should not exist on its own if len(list_results) == 1 and list_results[0][0] == 1: list_results = [] record.price_quantity_tiers = json.dumps(list_results)
class StockPicking(models.Model): _inherit = 'stock.picking' data = fields.Serialized() test_integer = fields.Integer(sparse='data', allow_search=True) test_char = fields.Char(sparse='data', allow_search=True) test_text = fields.Text(sparse='data', allow_search=True) test_float = fields.Float(sparse='data', allow_search=True) test_many2one = fields.Many2one('product.product', sparse='data', allow_search=True) test_many2many = fields.Many2many('product.product', sparse='data', allow_search=True) test_selection = fields.Selection(selection=[('test1', 'TEST 1'), ('test2', 'TEST 2')], sparse='data', allow_search=True) test_boolean = fields.Boolean(sparse='data', allow_search=True)
class RancherStack(models.Model): _name = 'rancher.stack' _inherit = 'rancher.binding' _inherits = {'infrastructure.stack': 'odoo_id'} _description = 'Rancher Stacks' _rec_name = 'name' odoo_id = fields.Many2one( string='Stack', comodel_name='infrastructure.stack', required=True, ondelete='cascade', ) docker_compose = fields.Text() rancher_compose = fields.Text() answers = fields.Serialized(default={}, ) start_on_create = fields.Boolean()
class ShopinvaderImageMixin(models.AbstractModel): _name = 'shopinvader.image.mixin' _image_field = None images = fields.Serialized(compute='_compute_image', string='Shopinvader Image') def _compute_image(self): # Note: this computed field depend on the lang used in the context # as the name of the record is used for generating the thumbnail for record in self: record.images = record._get_image_data_for_record() def _get_image_url_key(self, image_relation): # You can inherit this method to change the name of the image of # your website. By default we use the name of the product or category # linked to the image processed # Note the url will be slugify by the get_or_create_thumnail return self.display_name def _get_image_data_for_record(self): self.ensure_one() res = [] resizes = self.backend_id['%s_resize_ids' % self._name.replace('.', '_')] for image_relation in self[self._image_field]: url_key = self._get_image_url_key(image_relation) image_data = {} tag = '' if image_relation.tag_id: tag = image_relation.tag_id.name for resize in resizes: thumbnail = image_relation.image_id.get_or_create_thumbnail( resize.size_x, resize.size_y, url_key=url_key) image_data[resize.key] = { 'src': thumbnail.url, 'alt': self.name, 'tag': tag, } res.append(image_data) return res
class ShopinvaderVariant(models.Model): _inherit = 'shopinvader.variant' hierarchical_categories = fields.Serialized( compute='_compute_shopinvader_category', string='Hierarchical Categories') def _compute_shopinvader_category(self): super(ShopinvaderVariant, self)._compute_shopinvader_category() def get_full_name(categ): result = [] while categ: result.insert(0, categ.name) categ = categ.parent_id return ' > '.join(result) for record in self: record.hierarchical_categories = {} for categ in record.shopinvader_categ_ids: record.hierarchical_categories['lvl%s' % categ.level] =\ get_full_name(categ.record_id)
class SeIndexConfig(models.Model): _name = "se.index.config" _description = "Elasticsearch index configuration" name = fields.Char(required=True) body = fields.Serialized(required=True) # This field is used since no widget exists to edit a serialized field # into the web fontend body_str = fields.Text(compute="_compute_body_str", inverse="_inverse_body_str") @api.model def create(self, values): # For new record creation, the inverse function for `body_str` is # called after the record is inserted into database. The field # `body` would be empty, and a validation error will pop up as field # `body` is required. The solution is to override create function, # and initialize field `body` based on field `body_str`. if 'body' not in values and 'body_str' in values: values['body'] = json.loads(values['body_str']) return super(SeIndexConfig, self).create(values) @api.multi @api.depends("body") def _compute_body_str(self): for rec in self: if rec.body: rec.body_str = json.dumps(rec.body) @api.multi def _inverse_body_str(self): for rec in self: data = None if rec.body_str: data = json.loads(rec.body_str) rec.body = data
class SeIndexConfig(models.Model): _name = "se.index.config" _description = "Elasticsearch index configuration" name = fields.Char(required=True) doc_type = fields.Char(required=True, default="odoo") body = fields.Serialized(required=True) # This field is used since no widget exists to edit a serialized field # into the web fontend body_str = fields.Text(compute="_compute_body_str", inverse="_inverse_body_str") @api.multi @api.depends("body") def _compute_body_str(self): for rec in self: if rec.body: rec.body_str = json.dumps(rec.body) @api.multi def _inverse_body_str(self): for rec in self: data = None if rec.body_str: data = json.loads(rec.body_str) rec.body = data @api.constrains("doc_type", "body") def _check_body(self): for rec in self: if ("mappings" in rec.body and rec.doc_type not in rec.body["mappings"]): raise ValidationError( _("You must specify a mapping into the same doc type " "(%s)") % rec.doc_type)
class ShopinvaderProduct(models.Model): _inherit = "shopinvader.product" hierarchical_categories = fields.Serialized( compute="_compute_shopinvader_category", string="Hierarchical Categories", ) def _compute_shopinvader_category(self): super(ShopinvaderProduct, self)._compute_shopinvader_category() def get_full_name(categ): result = [] while categ: result.insert(0, categ.name) categ = categ.parent_id return " > ".join(result) for record in self: record.hierarchical_categories = {} for categ in record.shopinvader_categ_ids: record.hierarchical_categories[ "lvl%s" % categ.level ] = get_full_name(categ.record_id)
class ShopinvaderVariant(models.Model): _name = "shopinvader.variant" _description = "Shopinvader Variant" _inherits = { "shopinvader.product": "shopinvader_product_id", "product.product": "record_id", } default_code = fields.Char(related="record_id.default_code", readonly=True) shopinvader_product_id = fields.Many2one("shopinvader.product", required=True, ondelete="cascade", index=True) record_id = fields.Many2one("product.product", required=True, ondelete="cascade", index=True) object_id = fields.Integer(compute="_compute_object_id", store=True, index=True) variant_count = fields.Integer(related="product_variant_count") variant_attributes = fields.Serialized( compute="_compute_variant_attributes", string="Shopinvader Attributes") main = fields.Boolean(compute="_compute_main_product") redirect_url_key = fields.Serialized(compute="_compute_redirect_url_key", string="Redirect Url Keys") active = fields.Boolean(default=True) price = fields.Serialized(compute="_compute_price", string="Shopinvader Price") short_name = fields.Char(compute="_compute_names") full_name = fields.Char(compute="_compute_names") @api.multi def _build_seo_title(self): """ Build the SEO product name. Call the same function on the related shopinvader product. :return: str """ self.ensure_one() return self.shopinvader_product_id._build_seo_title() def _prepare_variant_name_and_short_name(self): self.ensure_one() attributes = self.attribute_line_ids.filtered( lambda l: len(l.value_ids) > 1).mapped("attribute_id") short_name = self.attribute_value_ids._variant_name(attributes) full_name = self.shopinvader_display_name if short_name: full_name += " (%s)" % short_name return full_name, short_name def _compute_names(self): for record in self: record.full_name, record.short_name = ( record._prepare_variant_name_and_short_name()) def _compute_price(self): for record in self: record.price = record._get_all_price() def _get_all_price(self): self.ensure_one() res = {} pricelist = self.backend_id.pricelist_id if pricelist: res["default"] = self._get_price(pricelist, None, self.backend_id.company_id) return res @api.depends("record_id") def _compute_object_id(self): for record in self: record.object_id = record.record_id.id def _compute_redirect_url_key(self): for record in self: res = [] for url in record.redirect_url_url_ids: res.append(url.url_key) record.redirect_url_key = res def _compute_variant_attributes(self): for record in self: variant_attributes = dict() for att_value in record.attribute_value_ids: sanitized_key = sanitize_attr_name(att_value.attribute_id) variant_attributes[sanitized_key] = att_value.name record.variant_attributes = variant_attributes def _get_price(self, pricelist, fposition, company=None): self.ensure_one() return self._get_price_per_qty(1, pricelist, fposition, company) def _get_price_per_qty(self, qty, pricelist, fposition, company=None): product_id = self.record_id taxes = product_id.taxes_id.sudo().filtered( lambda r: not company or r.company_id == company) # get the expeced tax to apply from the fiscal position tax_id = fposition.map_tax(taxes, product_id) if fposition else taxes tax_id = tax_id and tax_id[0] product = product_id.with_context(quantity=qty, pricelist=pricelist.id, fiscal_position=fposition) final_price, rule_id = pricelist.get_product_price_rule( product, qty or 1.0, None) tax_included = tax_id.price_include account_tax_obj = self.env["account.tax"] # fix tax on the price value = account_tax_obj._fix_tax_included_price_company( final_price, product.taxes_id, tax_id, company) res = { "value": value, "tax_included": tax_included, "original_value": value, "discount": 0.0, } if pricelist.discount_policy == "without_discount": sol = self.env["sale.order.line"] new_list_price, currency_id = sol._get_real_price_currency( product, rule_id, qty or 1.0, product.uom_id, pricelist.id) # fix tax on the real price new_list_price = account_tax_obj._fix_tax_included_price_company( new_list_price, product.taxes_id, tax_id, company) product_precision = self.env["decimal.precision"].precision_get( "Product Price") if (float_compare(new_list_price, value, precision_digits=product_precision) == 0): # Both prices are equals. Product is wihout discount, avoid # divide by 0 exception return res discount = (new_list_price - value) / new_list_price * 100 # apply the right precision on discount dicount_precision = self.env["decimal.precision"].precision_get( "Discount") discount = float_round(discount, dicount_precision) res.update({ "original_value": new_list_price, "discount": discount }) return res def _compute_main_product(self): for record in self: record.main = record == record.shopinvader_variant_ids[0]