# Part of Odoo. See LICENSE file for full copyright and licensing details. from werkzeug.urls import url_join from odoo import _, api, fields, models from odoo.exceptions import ValidationError class Product(models.Model): _inherit = "product.product" _mail_post_access = 'read' website_id = fields.Many2one(related='product_tmpl_id.website_id', readonly=False) product_variant_image_ids = fields.One2many('product.image', 'product_variant_id', string="Extra Variant Images") website_url = fields.Char('Website URL', compute='_compute_product_website_url', help='The full URL to access the document through the website.') ribbon_id = fields.Many2one(string="Variant Ribbon", comodel_name='product.ribbon') base_unit_count = fields.Float('Base Unit Count', required=True, default=1, help="Display base unit price on your eCommerce pages. Set to 0 to hide it for this product.") base_unit_id = fields.Many2one('website.base.unit', string='Custom Unit of Measure', help="Define a custom unit to display in the price per unit of measure field.") base_unit_price = fields.Monetary("Price Per Unit", currency_field="currency_id", compute="_compute_base_unit_price") base_unit_name = fields.Char(compute='_compute_base_unit_name', help='Displays the custom unit for the products if defined or the selected unit of measure otherwise.') def _get_base_unit_price(self, price): self.ensure_one() return self.base_unit_count and price / self.base_unit_count @api.depends('lst_price', 'base_unit_count') def _compute_base_unit_price(self): for product in self: if not product.id: product.base_unit_price = 0 else: product.base_unit_price = product._get_base_unit_price(product.lst_price) @api.depends('uom_name', 'base_unit_id') def _compute_base_unit_name(self): for product in self: product.base_unit_name = product.base_unit_id.name or product.uom_name @api.constrains('base_unit_count') def _check_base_unit_count(self): if any(product.base_unit_count < 0 for product in self): raise ValidationError(_('The value of Base Unit Count must be greater than 0. Use 0 to hide the price per unit on this product.')) @api.depends_context('lang') @api.depends('product_tmpl_id.website_url', 'product_template_attribute_value_ids') def _compute_product_website_url(self): for product in self: attributes = ','.join(str(x) for x in product.product_template_attribute_value_ids.ids) url = product.product_tmpl_id.website_url if attributes: url = url_join(url, f"#attr={attributes}") product.website_url = url def _prepare_variant_values(self, combination): variant_dict = super()._prepare_variant_values(combination) variant_dict['base_unit_count'] = self.base_unit_count return variant_dict def website_publish_button(self): self.ensure_one() return self.product_tmpl_id.website_publish_button() def open_website_url(self): self.ensure_one() res = self.product_tmpl_id.open_website_url() res['url'] = self.website_url return res def _get_images(self): """Return a list of records implementing `image.mixin` to display on the carousel on the website for this variant. This returns a list and not a recordset because the records might be from different models (template, variant and image). It contains in this order: the main image of the variant (which will fall back on the main image of the template, if unset), the Variant Extra Images, and the Template Extra Images. """ self.ensure_one() variant_images = list(self.product_variant_image_ids) template_images = list(self.product_tmpl_id.product_template_image_ids) return [self] + variant_images + template_images def _get_combination_info_variant(self, **kwargs): """Return the variant info based on its combination. See `_get_combination_info` for more information. """ self.ensure_one() return self.product_tmpl_id._get_combination_info( combination=self.product_template_attribute_value_ids, product_id=self.id, **kwargs) def _website_show_quick_add(self): website = self.env['website'].get_current_website() return self.sale_ok and (not website.prevent_zero_price_sale or self._get_contextual_price()) def _is_add_to_cart_allowed(self): self.ensure_one() return self.user_has_groups('base.group_system') or (self.active and self.sale_ok and self.website_published) def _get_contextual_price_tax_selection(self): self.ensure_one() website = self.env['website'].get_current_website() fiscal_position_sudo = website.sudo().fiscal_position_id product_taxes = self.sudo().taxes_id._filter_taxes_by_company(self.env.company) return self.env['product.template']._apply_taxes_to_price( self._get_contextual_price(), website.currency_id, product_taxes, fiscal_position_sudo.map_tax(product_taxes), self, )