diff --git a/addons/sale_timesheet/models/product.py b/addons/sale_timesheet/models/product.py index 011944c0d85..9cdae7cb4f7 100644 --- a/addons/sale_timesheet/models/product.py +++ b/addons/sale_timesheet/models/product.py @@ -3,7 +3,7 @@ import threading -from odoo import api, fields, models, _ +from odoo import api, fields, models, tools, _ from odoo.exceptions import ValidationError @@ -68,6 +68,17 @@ class ProductTemplate(models.Model): "project for the order to track the time spent." ) + @api.onchange('type', 'service_type', 'service_policy') + def _onchange_service_fields(self): + for record in self: + if record.type == 'service' and record.service_type == 'timesheet' and \ + not (record._origin.service_policy and record.service_policy == record._origin.service_policy): + record.uom_id = self.env.ref('uom.product_uom_hour') + elif record._origin.uom_id: + record.uom_id = record._origin.uom_id + else: + record.uom_id = self._get_default_uom_id() + def _get_service_to_general_map(self): return { **super()._get_service_to_general_map(), @@ -114,11 +125,26 @@ class ProductTemplate(models.Model): class ProductProduct(models.Model): _inherit = 'product.product' + @tools.ormcache() + def _get_default_uom_id(self): + return self.env.ref('uom.product_uom_unit') + def _is_delivered_timesheet(self): """ Check if the product is a delivered timesheet """ self.ensure_one() return self.type == 'service' and self.service_policy == 'delivered_timesheet' + @api.onchange('type', 'service_type', 'service_policy') + def _onchange_service_fields(self): + for record in self: + if record.type == 'service' and record.service_type == 'timesheet' and \ + not (record._origin.service_policy and record.service_policy == record._origin.service_policy): + record.uom_id = self.env.ref('uom.product_uom_hour') + elif record._origin.uom_id: + record.uom_id = record._origin.uom_id + else: + record.uom_id = self._get_default_uom_id() + @api.onchange('service_policy') def _onchange_service_policy(self): self.product_tmpl_id._inverse_service_policy() diff --git a/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js b/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js index f402185ad4c..0f05bdc1a6d 100644 --- a/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js +++ b/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js @@ -9,6 +9,71 @@ registry.category("web_tour.tours").add('sale_timesheet_tour', { url: '/web', steps: [...stepUtils.goToAppSteps("sale.sale_menu_root", 'Go to the Sales App'), { + trigger: 'button[data-menu-xmlid="sale.product_menu_catalog"]', + content: 'Open Products menu', +}, { + trigger: 'a[data-menu-xmlid="sale.menu_product_template_action"]', + content: 'Go to product catalog', +}, { + trigger: 'button.o-kanban-button-new', + content: 'Create a new product', +}, { + trigger: 'textarea.o_input[id="name_0"]', + content: 'Enter a product name (e.g. Service Product Prepaid Hours)', + run: 'text Service Product Prepaid Hours', +}, { + trigger: 'input.o_input[id="uom_id_0"]', + content: 'Default unit should be Units', + run() { + const subjectValue = document.querySelector('input.o_input[id="uom_id_0"]').value; + if (subjectValue !== "Units") { + console.error( + `Default unit should be Units (actual: ${subjectValue})` + ); + } + }, +}, { + trigger: 'input.o_input[id="categ_id_0"]', + content: 'Enter a category name (e.g. Services)', + run: 'text Services', +}, { + trigger: 'a.dropdown-item:contains(Services)', + content: 'Select the ALL / Saleable / Services category', +}, { + trigger: 'select.o_input[id="detailed_type_0"]', + content: 'Select Service product type', + run: 'text "service"', +}, { + trigger: 'select.o_input[id="detailed_type_0"]', + content: 'Select Service product type', + run: 'click', +}, { + trigger: 'input.o_input[id="uom_id_0"]', + content: 'Unit should be set to Hours for a service product', + run() { + const subjectValue = document.querySelector('input.o_input[id="uom_id_0"]').value; + if (subjectValue !== "Hours") { + console.error( + `Default unit should be Hours (actual: ${subjectValue})` + ); + } + }, +}, { + trigger: 'input.o_input[id="list_price_0"]', + content: 'Choose a sales price for that product (e.g. 250$)', + run: 'text 250', +}, { + trigger: 'input.o_input[id="standard_price_0"]', + content: 'Set the cost of that product (e.g. 190$)', + run: 'text 190', +}, { + trigger: 'button[data-menu-xmlid="sale.sale_order_menu"]', + content: 'Open Order menu', +}, { + trigger: 'a[data-menu-xmlid="sale.menu_sale_quotations"]', + content: 'Go to quotations list', +}, { + trigger: 'button.o_list_button_add', content: 'Click on CREATE button to create a quotation with service products.', }, { @@ -24,10 +89,10 @@ registry.category("web_tour.tours").add('sale_timesheet_tour', { content: 'Click on "Add a product" to add a new product. We will add a service product.', }, { trigger: '.o_field_html[name="product_id"], .o_field_widget[name="product_template_id"] input', - content: Markup('Select a prepaid service product (e.g. Service Product (Prepaid Hours))'), - run: 'text Service Product (Prepaid Hours)', + content: 'Select a prepaid service product (e.g. Service Product Prepaid Hours)', + run: 'text Service Product Prepaid Hours', }, { - trigger: 'ul.ui-autocomplete a:contains(Service Product (Prepaid Hours))', + trigger: 'ul.ui-autocomplete a:contains(Service Product Prepaid Hours)', content: 'Select the prepaid service product in the autocomplete dropdown', }, { trigger: 'div[name="product_uom_qty"] input', diff --git a/addons/sale_timesheet/tests/test_sale_timesheet_ui.py b/addons/sale_timesheet/tests/test_sale_timesheet_ui.py index c1a0a11fae7..a9011f61cda 100644 --- a/addons/sale_timesheet/tests/test_sale_timesheet_ui.py +++ b/addons/sale_timesheet/tests/test_sale_timesheet_ui.py @@ -11,22 +11,9 @@ class TestUi(HttpCase): def setUpClass(cls): super().setUpClass() - service_category_id = cls.env['product.category'].create({ + cls.env['product.category'].create({ 'name': 'Services', 'parent_id': cls.env.ref('product.product_category_1').id, - }).id - - uom_hour_id = cls.env.ref('uom.product_uom_hour').id - cls.prepaid_service_product = cls.env['product.product'].create({ - 'name': 'Service Product (Prepaid Hours)', - 'categ_id': service_category_id, - 'type': 'service', - 'list_price': 250.00, - 'standard_price': 190.00, - 'uom_id': uom_hour_id, - 'uom_po_id': uom_hour_id, - 'service_policy': 'ordered_prepaid', - 'service_tracking': 'no', }) # Enable the "Milestones" feature to be able to create milestones on this tour.