diff --git a/addons/hr_expense/i18n/hr_expense.pot b/addons/hr_expense/i18n/hr_expense.pot index e4a7a5add31..47ef63fda3b 100644 --- a/addons/hr_expense/i18n/hr_expense.pot +++ b/addons/hr_expense/i18n/hr_expense.pot @@ -2015,6 +2015,15 @@ msgstr "" msgid "The sum of after split shut remain the same" msgstr "" +#. module: hr_expense +#. odoo-python +#: code:addons/hr_expense/models/product_product.py:0 +#, python-format +msgid "" +"There are unposted expenses linked to this category. Updating the category " +"cost will change expense amounts. Make sure it is what you want to do." +msgstr "" + #. module: hr_expense #. odoo-python #: code:addons/hr_expense/models/hr_expense_sheet.py:0 diff --git a/addons/hr_expense/models/__init__.py b/addons/hr_expense/models/__init__.py index 85fd0506c63..00625929f9f 100644 --- a/addons/hr_expense/models/__init__.py +++ b/addons/hr_expense/models/__init__.py @@ -8,6 +8,7 @@ from . import account_tax from . import hr_department from . import hr_expense from . import hr_expense_sheet +from . import product_product from . import product_template from . import res_config_settings from . import account_journal_dashboard diff --git a/addons/hr_expense/models/product_product.py b/addons/hr_expense/models/product_product.py new file mode 100644 index 00000000000..fe628d03520 --- /dev/null +++ b/addons/hr_expense/models/product_product.py @@ -0,0 +1,26 @@ +from odoo import api, fields, models, _ + + +class ProductProduct(models.Model): + _inherit = "product.product" + + standard_price_update_warning = fields.Char(compute="_compute_standard_price_update_warning") + + @api.onchange('standard_price') + def _compute_standard_price_update_warning(self): + undone_expenses = self.env['hr.expense']._read_group( + domain=[('state', 'in', ['draft', 'reported', 'approved']), ('product_id', 'in', self.ids)], + groupby=['price_unit'], + ) + # The following list is composed of all the unit_amounts of expenses that use this product and should NOT trigger a warning. + # Those are the amounts of any undone expense using this product and 0.0 which is the default unit_amount. + unit_amounts_no_warning = [self.env.company.currency_id.round(row[0]) for row in undone_expenses] + for product in self: + product.standard_price_update_warning = False + if undone_expenses: + rounded_price = self.env.company.currency_id.round(product.standard_price) + if rounded_price and (len(unit_amounts_no_warning) > 1 or (len(unit_amounts_no_warning) == 1 and rounded_price not in unit_amounts_no_warning)): + product.standard_price_update_warning = _( + "There are unposted expenses linked to this category. Updating the category cost will change expense amounts. " + "Make sure it is what you want to do." + ) diff --git a/addons/hr_expense/tests/__init__.py b/addons/hr_expense/tests/__init__.py index 699673b16c3..01f8b5dcd31 100644 --- a/addons/hr_expense/tests/__init__.py +++ b/addons/hr_expense/tests/__init__.py @@ -7,3 +7,4 @@ from . import test_expenses_access_rights from . import test_expenses_mail_import from . import test_expenses_multi_company from . import test_expenses_tax +from . import test_expenses_standard_price_update_warning diff --git a/addons/hr_expense/tests/test_expenses_standard_price_update_warning.py b/addons/hr_expense/tests/test_expenses_standard_price_update_warning.py new file mode 100644 index 00000000000..0d08293dc35 --- /dev/null +++ b/addons/hr_expense/tests/test_expenses_standard_price_update_warning.py @@ -0,0 +1,56 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo.addons.hr_expense.tests.common import TestExpenseCommon +from odoo.tests import tagged, Form + + +@tagged('post_install', '-at_install') +class TestExpenseStandardPriceUpdateWarning(TestExpenseCommon): + def test_expense_standard_price_update_warning(self): + self.expense_cat_A = self.env['product.product'].create({ + 'name': 'Category A', + 'default_code': 'CA', + 'standard_price': 0.0, + }) + self.expense_cat_B = self.env['product.product'].create({ + 'name': 'Category B', + 'default_code': 'CB', + 'standard_price': 0.0, + }) + self.expense_cat_C = self.env['product.product'].create({ + 'name': 'Category C', + 'default_code': 'CC', + 'standard_price': 0.0, + }) + self.expense_1 = self.env['hr.expense'].create({ + 'employee_id': self.expense_employee.id, + 'name': 'Expense 1', + 'product_id': self.expense_cat_A.id, + 'total_amount': 1, + }) + self.expense_2 = self.env['hr.expense'].create({ + 'employee_id': self.expense_employee.id, + 'name': 'Expense 2', + 'product_id': self.expense_cat_B.id, + 'total_amount': 5, + }) + + # At first, there is no warning message on the categories because their prices are 0 + self.assertFalse(self.expense_cat_A.standard_price_update_warning) + self.assertFalse(self.expense_cat_B.standard_price_update_warning) + self.assertFalse(self.expense_cat_C.standard_price_update_warning) + + # When modifying the price of the first category, a message should appear as a an expense will be modified. + with Form(self.expense_cat_A, view="hr_expense.product_product_expense_form_view") as form: + form.standard_price = 5 + self.assertTrue(form.standard_price_update_warning) + + # When modifying the price of the second category, no message should appear as the price of the linked + # expense is the price of the category that is going to be saved. + with Form(self.expense_cat_B, view="hr_expense.product_product_expense_form_view") as form: + form.standard_price = 5 + self.assertFalse(form.standard_price_update_warning) + + # When modifying the price of the thirs category, no message should appear as no expense is linked to it. + with Form(self.expense_cat_C, view="hr_expense.product_product_expense_form_view") as form: + form.standard_price = 5 + self.assertFalse(form.standard_price_update_warning) diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml index 2430fe85dbf..08834baf26e 100644 --- a/addons/hr_expense/views/hr_expense_views.xml +++ b/addons/hr_expense/views/hr_expense_views.xml @@ -550,6 +550,9 @@ product.product
+