From cdaa761cedf657614c30c3d8b146054812daf3fe Mon Sep 17 00:00:00 2001 From: Gorash Date: Tue, 27 Dec 2022 10:30:14 +0000 Subject: [PATCH] [REF] base: Update modifier syntax (invisible, required, readonly) Goal: * Simplified modifiers to only have one way to define modifiers; * Remove states attributes on python field; * Use python expression in view `required`, `readonly`, `invisible`; * More accurate validation of xml views. This commit change the syntax to python expression. The next commit will update/convert all xml views. Before this commit: * the `required`, `readonly` and `invisible` attributes can only have values of `True`, `False`, 1, 0 or a python expression to use the context; * the `attrs` attribute define a dict. The key of this dict was `required`, `readonly` and `invisible` and the values are the domain or a string representing a domain to be evaluate as python expression. This python expressions was evaluate by the javascript with view fields and other contextual values as: context, uid, parent, active_id, active_ids, active_model, allowed_company_ids, current_company_id. * the `states` attribute in the view was a comma separated list of the state. This list was combined with the `invisible` attribute; * the `invisible` attribute on python field is used as default value; * the `states` attribute on python field was dictionnary with state as key and list of tuple. This structure was combined with `readonly` view attribute. * After combining, the resulting domains of the different attributes `required`, `readonly` and `invisible` are evaluated with the values of the fields. The `invisible` attributes is splitted into two use: `invisible` and `column_invisible`. After this commit: * The attributes `required`, `readonly`, `invisible` and `column_invisible` define python expression. This python expressions are evaluate by the javascript with view fields and other contextual values as: context, uid, parent, active_id, active_ids, active_model, allowed_company_ids, current_company_id. The domains can contains contextual value and will be evaluate by the javascript. ```xml ``` will be replaced by ```xml ``` Some inherited views will be modified differently in order to maintain the previous behavior: ```xml ``` ```xml {'readonly': [('field_c', '=', False)], 'invisible': [('field_d', '!=', '3')]} ``` will be replaced by ```xml ``` ```xml field_d != 3 ``` Validation: A stricter control is made on the level of the attributes (modifiers) and the fields necessary for these. The use of the previous attributes 'attr' and 'states' triggers an error (these no longer exist after the application of the migration script) task-2495504 Part-of: odoo/odoo#104741 --- .../tests/test_account_move_in_refund.py | 4 +- .../tests/test_account_move_out_invoice.py | 2 +- addons/base_import/models/base_import.py | 8 +- addons/crm/tests/test_res_partner.py | 8 +- .../hr_holidays/tests/test_leave_requests.py | 4 +- addons/http_routing/tests/test_res_lang.py | 14 + addons/mrp/tests/test_multicompany.py | 2 +- addons/mrp/tests/test_order.py | 2 +- addons/project/tests/test_multicompany.py | 8 +- .../tests/test_purchase_order_report.py | 6 +- .../tests/test_purchase_order.py | 6 +- .../tests/test_stockvaluation.py | 6 +- .../tests/test_project_billing.py | 4 +- addons/stock/tests/test_move2.py | 2 +- addons/stock/tests/test_packing.py | 8 +- addons/stock/tests/test_report.py | 2 +- addons/survey/tests/test_survey_invite.py | 2 +- addons/web/controllers/export.py | 5 +- odoo/addons/base/models/ir_ui_view.py | 287 ++--- odoo/addons/base/rng/common.rng | 28 +- odoo/addons/base/tests/test_expression.py | 50 +- odoo/addons/base/tests/test_form_create.py | 1 + odoo/addons/base/tests/test_res_users.py | 2 +- odoo/addons/base/tests/test_views.py | 1110 +++++++++++++---- .../ir.model.access.csv | 2 + odoo/addons/test_testing_utilities/menu.xml | 53 +- odoo/addons/test_testing_utilities/models.py | 16 + .../test_testing_utilities/nested_o2m.py | 2 +- .../tests/test_form_impl.py | 23 +- odoo/fields.py | 7 +- odoo/models.py | 2 +- odoo/osv/expression.py | 3 +- odoo/tests/form.py | 195 ++- odoo/tools/view_validation.py | 322 +++-- 34 files changed, 1441 insertions(+), 755 deletions(-) create mode 100644 addons/http_routing/tests/test_res_lang.py diff --git a/addons/account/tests/test_account_move_in_refund.py b/addons/account/tests/test_account_move_in_refund.py index 6c3396ec4a7..cbbed7a03ba 100644 --- a/addons/account/tests/test_account_move_in_refund.py +++ b/addons/account/tests/test_account_move_in_refund.py @@ -785,13 +785,13 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon): # https://github.com/odoo/odoo/blob/385884afd31f25d61e99d139ecd4c574d99a1863/addons/purchase/views/account_move_views.xml#L26 self.env.user.groups_id -= self.env.ref('purchase.group_purchase_manager') self.env.user.groups_id -= self.env.ref('purchase.group_purchase_user') - # 'invisible': ['|', ('state', '!=', 'draft'), ('move_type', '!=', 'in_invoice')] + # invisible="state != 'draft' or move_type != 'in_invoice'" # This is an in_refund invoice, `invoice_vendor_bill_id` is not supposed to be visible # and therefore not supposed to be changed. view = self.env.ref('account.view_move_form') tree = etree.fromstring(view.arch) for node in tree.xpath('//field[@name="invoice_vendor_bill_id"]'): - del node.attrib['attrs'] + del node.attrib['invisible'] view.arch = etree.tostring(tree) move_form = Form(self.invoice) diff --git a/addons/account/tests/test_account_move_out_invoice.py b/addons/account/tests/test_account_move_out_invoice.py index f8788814d6d..70eff449d73 100644 --- a/addons/account/tests/test_account_move_out_invoice.py +++ b/addons/account/tests/test_account_move_out_invoice.py @@ -3334,7 +3334,7 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): # Quick edit total amount not activated yet # As quick edit total is not yet activated, it's invisible by default in the view - move_form._view['modifiers']['quick_edit_total_amount']['invisible'] = False + move_form._view['modifiers']['quick_edit_total_amount']['invisible'] = 'False' move_form.quick_edit_total_amount = 100.0 invoice = move_form.save() self.assertEqual(invoice.amount_total, 0.0) diff --git a/addons/base_import/models/base_import.py b/addons/base_import/models/base_import.py index cd0d3cf6191..660b11da9a3 100644 --- a/addons/base_import/models/base_import.py +++ b/addons/base_import/models/base_import.py @@ -285,13 +285,7 @@ class Import(models.TransientModel): if field.get('deprecated', False) is not False: continue if field.get('readonly'): - states = field.get('states') - if not states: - continue - # states = {state: [(attr, value), (attr2, value2)], state2:...} - if not any(attr == 'readonly' and value is False - for attr, value in itertools.chain.from_iterable(states.values())): - continue + continue field_value = { 'id': name, 'name': name, diff --git a/addons/crm/tests/test_res_partner.py b/addons/crm/tests/test_res_partner.py index b80849e3962..c95387de848 100644 --- a/addons/crm/tests/test_res_partner.py +++ b/addons/crm/tests/test_res_partner.py @@ -55,13 +55,7 @@ class TestPartner(TestCrmCommon): # So, set a temporary `parent_id` before setting the contact as company # to make `parent_id` visible in the interface while being a company # partner_form.parent_id = contact_company_1 partner_form.company_type = 'company' diff --git a/addons/hr_holidays/tests/test_leave_requests.py b/addons/hr_holidays/tests/test_leave_requests.py index ebff50fcc8a..aabba6eb897 100644 --- a/addons/hr_holidays/tests/test_leave_requests.py +++ b/addons/hr_holidays/tests/test_leave_requests.py @@ -282,8 +282,8 @@ class TestLeaveRequests(TestHrHolidaysCommon): # 06/16/2022 08:00:00 as date_from and 06/15/2022 17:00:00 as date_to # Bug reported to the rd-fun-vidange channel to the dev who introduced the bug # https://discord.com/channels/678381219515465750/687337760452902925/986918361768263710 - leave_form._view['modifiers']['date_from']['invisible'] = False - leave_form._view['modifiers']['date_to']['invisible'] = False + leave_form._view['modifiers']['date_from']['invisible'] = 'False' + leave_form._view['modifiers']['date_to']['invisible'] = 'False' leave_form.date_from = datetime(2019, 5, 6, 0, 0, 0) leave_form.date_to = datetime(2019, 5, 6, 23, 59, 59) leave = leave_form.save() diff --git a/addons/http_routing/tests/test_res_lang.py b/addons/http_routing/tests/test_res_lang.py new file mode 100644 index 00000000000..80c41592036 --- /dev/null +++ b/addons/http_routing/tests/test_res_lang.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.tests import TransactionCase, tagged, Form + +@tagged('-at_install', 'post_install') +class TestFormCreate(TransactionCase): + + def test_create_res_lang(self): + lang_form = Form(self.env['res.lang']) + lang_form.url_code = 'LANG' + lang_form.name = 'a lang name' + lang_form.code = 'a lang code' + lang_form.save() diff --git a/addons/mrp/tests/test_multicompany.py b/addons/mrp/tests/test_multicompany.py index aeca55a0560..fda22d07180 100644 --- a/addons/mrp/tests/test_multicompany.py +++ b/addons/mrp/tests/test_multicompany.py @@ -135,7 +135,7 @@ class TestMrpMulticompany(common.TransactionCase): mo_form = Form(self.env['mrp.production'].with_user(self.user_a)) mo_form.product_id = product # The mo must be confirmed, no longer in draft, in order for `lot_producing_id` to be visible in the view - #
+ #
mo = mo_form.save() mo.action_confirm() mo_form = Form(mo) diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index 5c856d27d5b..9621e64e044 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -627,7 +627,7 @@ class TestMrpOrder(TestMrpCommon): mo, _, p_final, p1, p2 = self.generate_mo(tracking_base_1='lot', qty_base_1=10, qty_final=1) # Required for `lot_producing_id` to be visible in the view - # + # p_final.tracking = 'lot' self.assertEqual(len(mo), 1, 'MO should have been created') diff --git a/addons/project/tests/test_multicompany.py b/addons/project/tests/test_multicompany.py index d5465acfe3a..1eeab0ac91e 100644 --- a/addons/project/tests/test_multicompany.py +++ b/addons/project/tests/test_multicompany.py @@ -437,8 +437,8 @@ class TestMultiCompanyProject(TestMultiCompanyCommon): # view = self.env.ref('project.view_task_form2').sudo() tree = etree.fromstring(view.arch) - for node in tree.xpath('//field[@name="parent_id"][@attrs]'): - node.attrib.pop('attrs') + for node in tree.xpath('//field[@name="parent_id"][@invisible]'): + node.attrib.pop('invisible') view.arch = etree.tostring(tree) with self.debug_mode(): with Form(self.task_2) as task_form: @@ -457,8 +457,8 @@ class TestMultiCompanyProject(TestMultiCompanyCommon): # view = self.env.ref('project.view_task_form2').sudo() tree = etree.fromstring(view.arch) - for node in tree.xpath('//field[@name="parent_id"][@attrs]'): - node.attrib.pop('attrs') + for node in tree.xpath('//field[@name="parent_id"][@invisible]'): + node.attrib.pop('invisible') view.arch = etree.tostring(tree) with self.sudo('employee-a'): diff --git a/addons/purchase/tests/test_purchase_order_report.py b/addons/purchase/tests/test_purchase_order_report.py index fca8f5e3d78..2f060a7b571 100644 --- a/addons/purchase/tests/test_purchase_order_report.py +++ b/addons/purchase/tests/test_purchase_order_report.py @@ -44,11 +44,11 @@ class TestPurchaseOrderReport(AccountTestInvoicingCommon): # # #