diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py index 90b1267f006..3d2aaa5076f 100644 --- a/addons/product/models/product_template.py +++ b/addons/product/models/product_template.py @@ -51,14 +51,9 @@ class ProductTemplate(models.Model): 'A consumable product is a product for which stock is not managed.\n' 'A service is a non-material product you provide.') type = fields.Selection( - selection=[ - ('consu', 'Consumable'), - ('service', 'Service') - ], - compute='_compute_type', - store=True, - readonly=False, - ) + [('consu', 'Consumable'), + ('service', 'Service')], + compute='_compute_type', store=True, readonly=False, precompute=True) categ_id = fields.Many2one( 'product.category', 'Product Category', change_default=True, default=_get_default_category_id, group_expand='_read_group_categ_id', @@ -389,11 +384,6 @@ class ProductTemplate(models.Model): if self.uom_id and self.uom_po_id and self.uom_id.category_id != self.uom_po_id.category_id: self.uom_po_id = self.uom_id - @api.onchange('type') - def _onchange_type(self): - # Do nothing but needed for inheritance - return {} - def _sanitize_vals(self, vals): """Sanitize vales for writing/creating product templates and variants. diff --git a/addons/sale/models/product_template.py b/addons/sale/models/product_template.py index a4a8992262b..b1cb1e555d7 100644 --- a/addons/sale/models/product_template.py +++ b/addons/sale/models/product_template.py @@ -15,11 +15,12 @@ _logger = logging.getLogger(__name__) class ProductTemplate(models.Model): _inherit = 'product.template' - service_type = fields.Selection([('manual', 'Manually set quantities on order')], string='Track Service', + service_type = fields.Selection( + [('manual', 'Manually set quantities on order')], string='Track Service', + compute='_compute_service_type', store=True, readonly=False, precompute=True, help="Manually set quantities on order: Invoice based on the manually entered quantity, without creating an analytic account.\n" "Timesheets on contract: Invoice based on the tracked hours on the related timesheet.\n" - "Create a task and track hours: Create a task on the sales order validation and track the work hours.", - default='manual') + "Create a task and track hours: Create a task on the sales order validation and track the work hours.") sale_line_warn = fields.Selection(WARNING_MESSAGE, 'Sales Order Line', help=WARNING_HELP, required=True, default="no-message") sale_line_warn_msg = fields.Text('Message for Sales Order Line') expense_policy = fields.Selection( @@ -33,12 +34,12 @@ class ProductTemplate(models.Model): visible_expense_policy = fields.Boolean("Re-Invoice Policy visible", compute='_compute_visible_expense_policy') sales_count = fields.Float(compute='_compute_sales_count', string='Sold') visible_qty_configurator = fields.Boolean("Quantity visible in configurator", compute='_compute_visible_qty_configurator') - invoice_policy = fields.Selection([ - ('order', 'Ordered quantities'), - ('delivery', 'Delivered quantities')], string='Invoicing Policy', + invoice_policy = fields.Selection( + [('order', 'Ordered quantities'), + ('delivery', 'Delivered quantities')], string='Invoicing Policy', + compute='_compute_invoice_policy', store=True, readonly=False, precompute=True, help='Ordered Quantity: Invoice quantities ordered by the customer.\n' - 'Delivered Quantity: Invoice quantities delivered to the customer.', - default='order') + 'Delivered Quantity: Invoice quantities delivered to the customer.') def _compute_visible_qty_configurator(self): for product_template in self: @@ -50,7 +51,6 @@ class ProductTemplate(models.Model): for product_template in self: product_template.visible_expense_policy = visibility - @api.depends('sale_ok') def _compute_expense_policy(self): self.filtered(lambda t: not t.sale_ok).expense_policy = 'no' @@ -126,15 +126,13 @@ class ProductTemplate(models.Model): return self._create_product_variant(combination, log_warning=True).id or 0 - @api.onchange('type') - def _onchange_type(self): - """ Force values to stay consistent with integrity constraints """ - res = super(ProductTemplate, self)._onchange_type() - if self.type == 'consu': - if not self.invoice_policy: - self.invoice_policy = 'order' - self.service_type = 'manual' - return res + @api.depends('type') + def _compute_service_type(self): + self.filtered(lambda t: t.type == 'consu' or not t.service_type).service_type = 'manual' + + @api.depends('type') + def _compute_invoice_policy(self): + self.filtered(lambda t: t.type == 'consu' or not t.invoice_policy).invoice_policy = 'order' @api.model def get_import_templates(self): diff --git a/addons/sale_purchase/models/product_template.py b/addons/sale_purchase/models/product_template.py index 9b92440333a..70e926e084e 100644 --- a/addons/sale_purchase/models/product_template.py +++ b/addons/sale_purchase/models/product_template.py @@ -8,7 +8,10 @@ from odoo.exceptions import ValidationError class ProductTemplate(models.Model): _inherit = 'product.template' - service_to_purchase = fields.Boolean("Subcontract Service", help="If ticked, each time you sell this product through a SO, a RfQ is automatically created to buy the product. Tip: don't forget to set a vendor on the product.") + service_to_purchase = fields.Boolean( + "Subcontract Service", + compute='_compute_service_to_purchase', store=True, readonly=False, + help="If ticked, each time you sell this product through a SO, a RfQ is automatically created to buy the product. Tip: don't forget to set a vendor on the product.") _sql_constraints = [ ('service_to_purchase', "CHECK((type != 'service' AND service_to_purchase != true) or (type = 'service'))", 'Product that is not a service can not create RFQ.'), @@ -19,14 +22,8 @@ class ProductTemplate(models.Model): if self.service_to_purchase and not self.seller_ids: raise ValidationError("Please define the vendor from whom you would like to purchase this service automatically.") - @api.onchange('type') - def _onchange_type(self): - res = super(ProductTemplate, self)._onchange_type() - if self.type != 'service': - self.service_to_purchase = False - return res - - @api.onchange('expense_policy') - def _onchange_expense_policy(self): - if self.expense_policy != 'no': - self.service_to_purchase = False + @api.depends('type', 'expense_policy') + def _compute_service_to_purchase(self): + for template in self: + if template.type != 'service' or template.expense_policy != 'no': + template.service_to_purchase = False diff --git a/addons/sale_stock/models/product_template.py b/addons/sale_stock/models/product_template.py index 11673475314..bcc8b5a6f78 100644 --- a/addons/sale_stock/models/product_template.py +++ b/addons/sale_stock/models/product_template.py @@ -7,13 +7,12 @@ from odoo import api, models class ProductTemplate(models.Model): _inherit = 'product.template' - @api.onchange('type') - def _onchange_type(self): - """ We want to prevent storable product to be expensed, since it make no sense as when confirm - expenses, the product is already out of our stock. - """ - res = super(ProductTemplate, self)._onchange_type() - if self.type == 'product': - self.expense_policy = 'no' - self.service_type = 'manual' - return res + @api.depends('type') + def _compute_expense_policy(self): + super()._compute_expense_policy() + self.filtered(lambda t: t.type == 'product').expense_policy = 'no' + + @api.depends('type') + def _compute_service_type(self): + super()._compute_service_type() + self.filtered(lambda t: t.type == 'product').service_type = 'manual' diff --git a/addons/sale_timesheet/models/product.py b/addons/sale_timesheet/models/product.py index 8ea40e9c7ca..5f5767dd83b 100644 --- a/addons/sale_timesheet/models/product.py +++ b/addons/sale_timesheet/models/product.py @@ -24,7 +24,7 @@ class ProductTemplate(models.Model): ], string="Service Invoicing Policy", compute='_compute_service_policy', inverse='_inverse_service_policy') service_type = fields.Selection(selection_add=[ ('timesheet', 'Timesheets on project (one fare per SO/Project)'), - ], ondelete={'timesheet': 'set default'}) + ], ondelete={'timesheet': 'set manual'}) # override domain project_id = fields.Many2one(domain="[('company_id', '=', current_company_id), ('allow_billable', '=', True), ('pricing_type', '=', 'task_rate'), ('allow_timesheets', 'in', [service_policy == 'delivered_timesheet', True])]") project_template_id = fields.Many2one(domain="[('company_id', '=', current_company_id), ('allow_billable', '=', True), ('allow_timesheets', 'in', [service_policy == 'delivered_timesheet', True])]") diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py index 4e66ec6c356..64c4305ab6b 100644 --- a/addons/stock/models/product.py +++ b/addons/stock/models/product.py @@ -624,7 +624,10 @@ class ProductTemplate(models.Model): tracking = fields.Selection([ ('serial', 'By Unique Serial Number'), ('lot', 'By Lots'), - ('none', 'No Tracking')], string="Tracking", help="Ensure the traceability of a storable product in your warehouse.", default='none', required=True) + ('none', 'No Tracking')], + string="Tracking", required=True, + compute='_compute_tracking', store=True, readonly=False, precompute=True, + help="Ensure the traceability of a storable product in your warehouse.") description_picking = fields.Text('Description on Picking', translate=True) description_pickingout = fields.Text('Description on Delivery Orders', translate=True) description_pickingin = fields.Text('Description on Receptions', translate=True) @@ -799,25 +802,28 @@ class ProductTemplate(models.Model): def _onchange_tracking(self): return self.mapped('product_variant_ids')._onchange_tracking() + @api.depends('type') + def _compute_tracking(self): + self.filtered( + lambda t: not t.tracking or t.type == 'consu' and t.tracking != 'none' + ).tracking = 'none' + @api.onchange('type') def _onchange_type(self): - res = super(ProductTemplate, self)._onchange_type() or {} - if self.type == 'consu' and self.tracking != 'none': - self.tracking = 'none' - # Return a warning when trying to change the product type if self.ids and self.product_variant_ids.ids and self.env['stock.move.line'].sudo().search_count([ ('product_id', 'in', self.product_variant_ids.ids), ('state', '!=', 'cancel') ]): - res['warning'] = { - 'title': _('Warning!'), - 'message': _( - 'This product has been used in at least one inventory movement. ' - 'It is not advised to change the Product Type since it can lead to inconsistencies. ' - 'A better solution could be to archive the product and create a new one instead.' - ) + return { + 'warning': { + 'title': _('Warning!'), + 'message': _( + 'This product has been used in at least one inventory movement. ' + 'It is not advised to change the Product Type since it can lead to inconsistencies. ' + 'A better solution could be to archive the product and create a new one instead.' + ) + } } - return res def write(self, vals): self._sanitize_vals(vals) diff --git a/addons/website_sale_stock_wishlist/tests/test_wishlist_emails.py b/addons/website_sale_stock_wishlist/tests/test_wishlist_emails.py index 28b0875595c..ad01fa9ed9b 100644 --- a/addons/website_sale_stock_wishlist/tests/test_wishlist_emails.py +++ b/addons/website_sale_stock_wishlist/tests/test_wishlist_emails.py @@ -3,8 +3,6 @@ from odoo.addons.website_sale_stock.tests.test_website_sale_stock_product_warehouse import TestWebsiteSaleStockProductWarehouse -from odoo.tests import tagged - class TestWishlistEmail(TestWebsiteSaleStockProductWarehouse): @classmethod