From 1835d5a6a3f9c0f4a2860b71fb9d0e619f1213ca Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 19 May 2020 13:32:01 +0200 Subject: [PATCH 1/6] [IMP] base: allow integer res_id Before this commit, when retrieving a property, the res_id had to be in the format ,. This was redudant with the model already present in the arguments of the method. This way a property can be retrieved with the more concise form `Property._get("ref", "res.partner", 42)` --- odoo/addons/base/models/ir_property.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/odoo/addons/base/models/ir_property.py b/odoo/addons/base/models/ir_property.py index f554f013ac5..aed37bece8c 100644 --- a/odoo/addons/base/models/ir_property.py +++ b/odoo/addons/base/models/ir_property.py @@ -205,6 +205,13 @@ class Property(models.Model): @api.model def get(self, name, model, res_id=False): + """ Get the given field's generic value for the record. + + :param name: the field's name + :param model: the field's model name + :param res_id: optional resource, format: "" (int) or + "," (str) + """ if not res_id: t, v = self._get_default_property(name, model) if not v or t != 'many2one': @@ -232,6 +239,8 @@ class Property(models.Model): def _get_property(self, name, model, res_id): domain = self._get_domain(name, model) if domain is not None: + if res_id and isinstance(res_id, int): + res_id = "%s,%s" % (model, res_id) domain = [('res_id', '=', res_id)] + domain #make the search with company_id asc to make sure that properties specific to a company are given first return self.search(domain, limit=1, order='company_id') From 8f578637076d4b65de222aa092ce738391ce5260 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 19 May 2020 10:08:11 +0200 Subject: [PATCH 2/6] [IMP] *: remove access to ir.property Only administrators can access properties and configure them. ir.property may contain sensitive information and should only be accessed via code call to specific methods --- addons/hr/security/ir.model.access.csv | 1 - addons/sale/security/ir.model.access.csv | 1 - addons/stock/security/ir.model.access.csv | 1 - odoo/addons/base/security/ir.model.access.csv | 4 ++-- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/addons/hr/security/ir.model.access.csv b/addons/hr/security/ir.model.access.csv index 906cef3ee87..5f5fbd458c9 100644 --- a/addons/hr/security/ir.model.access.csv +++ b/addons/hr/security/ir.model.access.csv @@ -8,7 +8,6 @@ access_hr_employee_resource_user,resource.resource.user,resource.model_resource_ access_hr_department_user,hr.department.user,model_hr_department,group_hr_user,1,1,1,1 access_hr_department_employee,hr.department.employee,model_hr_department,base.group_user,1,0,0,0 access_hr_job_user,hr.job user,model_hr_job,group_hr_user,1,1,1,1 -access_ir_property_hr_user,ir_property hr_user,base.model_ir_property,group_hr_user,1,1,1,0 access_hr_plan_activity_type_employee,access_hr_plan_activity_type,model_hr_plan_activity_type,base.group_user,1,0,0,0 access_hr_plan_employee,access_hr_plan_employee,model_hr_plan,base.group_user,1,0,0,0 access_hr_plan_activity_type_hr_user,access_hr_plan_activity_type,model_hr_plan_activity_type,group_hr_user,1,1,1,1 diff --git a/addons/sale/security/ir.model.access.csv b/addons/sale/security/ir.model.access.csv index c09cd3a62ff..e380e0d77a1 100644 --- a/addons/sale/security/ir.model.access.csv +++ b/addons/sale/security/ir.model.access.csv @@ -19,7 +19,6 @@ access_sale_order_readonly,sale.order.accountant,model_sale_order,account.group_ access_sale_order_accountant,sale.order.accountant,model_sale_order,account.group_account_user,1,1,0,0 access_sale_report_salesman,sale.report,model_sale_report,sales_team.group_sale_salesman,1,1,1,0 access_sale_report_manager,sale.report,model_sale_report,sales_team.group_sale_manager,1,1,1,1 -access_ir_property_sales,ir.property.sales,base.model_ir_property,sales_team.group_sale_salesman,1,1,1,1 access_sale_account_journal,account.journal sale order.user,account.model_account_journal,sales_team.group_sale_salesman,1,0,0,0 access_res_partner_sale_user,res.partner.sale.user,base.model_res_partner,sales_team.group_sale_salesman,1,0,0,0 access_res_partner_sale_manager,res.partner.sale.manager,base.model_res_partner,sales_team.group_sale_manager,1,1,1,0 diff --git a/addons/stock/security/ir.model.access.csv b/addons/stock/security/ir.model.access.csv index ea20a1f51c5..3ad0386e7d6 100644 --- a/addons/stock/security/ir.model.access.csv +++ b/addons/stock/security/ir.model.access.csv @@ -27,7 +27,6 @@ access_product_product_stock_manager,product.product stock_manager,product.model access_product_packaging_stock_manager,product.packaging stock_manager,product.model_product_packaging,stock.group_stock_manager,1,1,1,1 access_product_supplierinfo_stock_manager,product.supplierinfo stock_manager,product.model_product_supplierinfo,stock.group_stock_manager,1,1,1,1 access_product_pricelist_stock_manager,product.pricelist stock_manager,product.model_product_pricelist,stock.group_stock_manager,1,1,1,1 -access_ir_property_group_stock_manager,ir_property group_stock_manager,base.model_ir_property,stock.group_stock_manager,1,1,1,1 access_product_group_res_partner_stock_manager,res_partner group_stock_manager,base.model_res_partner,stock.group_stock_manager,1,1,1,0 access_product_pricelist_item_stock_manager,product.pricelist.item stock_manager,product.model_product_pricelist_item,stock.group_stock_manager,1,1,1,1 access_stock_warehouse_orderpoint,stock.warehouse.orderpoint,model_stock_warehouse_orderpoint,stock.group_stock_user,1,0,0,0 diff --git a/odoo/addons/base/security/ir.model.access.csv b/odoo/addons/base/security/ir.model.access.csv index 328d36cc421..0a0abc9d090 100644 --- a/odoo/addons/base/security/ir.model.access.csv +++ b/odoo/addons/base/security/ir.model.access.csv @@ -20,8 +20,8 @@ "access_ir_module_module_group_user","ir_module_module group_user","model_ir_module_module","group_system",1,1,1,1 "access_ir_module_module_dependency_group_system","ir_module_module_dependency group_system","model_ir_module_module_dependency","group_system",1,1,1,1 "access_ir_module_module_exclusion_group_system","ir_module_module_exclusion group_system","model_ir_module_module_exclusion","group_system",1,1,1,1 -"access_ir_property_group_user","ir_property group_user","model_ir_property",,1,0,0,0 -"access_ir_property_group_user_manager","ir_property group_manager","model_ir_property","base.group_partner_manager",1,1,1,0 +"access_ir_property_group_user","ir_property group_user","model_ir_property",,0,0,0,0 +"access_ir_property_group_system","ir_property group_system","model_ir_property","base.group_system",1,1,1,0 "access_ir_rule_group_erp_manager","ir_rule group_erp_manager","model_ir_rule","group_erp_manager",1,1,1,1 "access_ir_sequence_group_user","ir_sequence group_user","model_ir_sequence","group_user",1,0,0,0 "access_ir_sequence_group_system","ir_sequence group_system","model_ir_sequence","group_system",1,1,1,1 From 4afa747fa1bc99e5626636541a2dabb22c7f22cd Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 19 May 2020 10:19:45 +0200 Subject: [PATCH 3/6] [IMP] *: make ir.property methods private Should only interact with them via python code in a controlled environment, no direct call with RPC --- addons/account/tests/common.py | 10 +++++----- addons/hr_expense/models/hr_expense.py | 4 ++-- addons/point_of_sale/tests/test_frontend.py | 2 +- addons/pos_restaurant/tests/test_frontend.py | 2 +- addons/product/models/product_pricelist.py | 4 ++-- addons/product/models/res_company.py | 4 ++-- addons/product/models/res_partner.py | 4 ++-- addons/stock_account/__init__.py | 4 ++-- addons/stock_account/tests/common.py | 4 ++-- .../tests/test_ui_wslides.py | 4 ++-- odoo/addons/base/models/ir_property.py | 12 ++++++------ odoo/addons/test_main_flows/tests/test_flow.py | 10 +++++----- odoo/addons/test_new_api/tests/test_new_fields.py | 6 +++--- .../test_performance/tests/test_performance.py | 4 ++-- odoo/fields.py | 6 +++--- odoo/models.py | 2 +- 16 files changed, 41 insertions(+), 41 deletions(-) diff --git a/addons/account/tests/common.py b/addons/account/tests/common.py index b46ca6f93c4..5c665ef1092 100644 --- a/addons/account/tests/common.py +++ b/addons/account/tests/common.py @@ -213,11 +213,11 @@ class AccountTestCommon(SavepointCase): # Properties: Product income and expense accounts, default parameters Property = cls.env['ir.property'] - Property.set_default('property_account_receivable_id', 'res.partner', cls.a_recv, cls.company) - Property.set_default('property_account_payable_id', 'res.partner', cls.a_pay, cls.company) - Property.set_default('property_account_position_id', 'res.partner', False, cls.company) - Property.set_default('property_account_expense_categ_id', 'product.category', cls.a_expense, cls.company) - Property.set_default('property_account_income_categ_id', 'product.category', cls.a_sale, cls.company) + Property._set_default('property_account_receivable_id', 'res.partner', cls.a_recv, cls.company) + Property._set_default('property_account_payable_id', 'res.partner', cls.a_pay, cls.company) + Property._set_default('property_account_position_id', 'res.partner', False, cls.company) + Property._set_default('property_account_expense_categ_id', 'product.category', cls.a_expense, cls.company) + Property._set_default('property_account_income_categ_id', 'product.category', cls.a_sale, cls.company) # Bank Accounts cls.bank_account = cls.env['res.partner.bank'].create({ diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index fe9e0bddc60..7c1c6734234 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -26,7 +26,7 @@ class HrExpense(models.Model): @api.model def _default_account_id(self): - return self.env['ir.property'].get('property_account_expense_categ_id', 'product.category') + return self.env['ir.property']._get('property_account_expense_categ_id', 'product.category') @api.model def _get_employee_id_domain(self): @@ -365,7 +365,7 @@ class HrExpense(models.Model): raise UserError( _("No Expense account found for the product %s (or for its category), please configure one.") % (self.product_id.name)) else: - account = self.env['ir.property'].with_company(self.company_id).get('property_account_expense_categ_id', 'product.category') + account = self.env['ir.property'].with_company(self.company_id)._get('property_account_expense_categ_id', 'product.category') if not account: raise UserError(_('Please configure Default Expense account for Product expense: `property_account_expense_categ_id`.')) return account diff --git a/addons/point_of_sale/tests/test_frontend.py b/addons/point_of_sale/tests/test_frontend.py index 63807196588..69fff92983e 100644 --- a/addons/point_of_sale/tests/test_frontend.py +++ b/addons/point_of_sale/tests/test_frontend.py @@ -30,7 +30,7 @@ class TestPointOfSaleHttpCommon(odoo.tests.HttpCase): 'reconcile': True}) self.env.company.account_default_pos_receivable_account_id = account_receivable - self.env['ir.property'].set_default( + self.env['ir.property']._set_default( 'property_account_receivable_id', 'res.partner', account_receivable, diff --git a/addons/pos_restaurant/tests/test_frontend.py b/addons/pos_restaurant/tests/test_frontend.py index 44ae56444c2..28d023f294e 100644 --- a/addons/pos_restaurant/tests/test_frontend.py +++ b/addons/pos_restaurant/tests/test_frontend.py @@ -86,7 +86,7 @@ class TestFrontend(odoo.tests.HttpCase): 'user_type_id': self.env.ref('account.data_account_type_receivable').id, 'reconcile': True}) - self.env['ir.property'].set_default( + self.env['ir.property']._set_default( 'property_account_receivable_id', 'res.partner', account_receivable, diff --git a/addons/product/models/product_pricelist.py b/addons/product/models/product_pricelist.py index 4938c9c313b..680ed729cf9 100644 --- a/addons/product/models/product_pricelist.py +++ b/addons/product/models/product_pricelist.py @@ -340,7 +340,7 @@ class Pricelist(models.Model): pl_domain = self._get_partner_pricelist_multi_search_domain_hook(company_id) # if no specific property, try to find a fitting pricelist - result = Property.get_multi('property_product_pricelist', Partner._name, partner_ids) + result = Property._get_multi('property_product_pricelist', Partner._name, partner_ids) remaining_partner_ids = [pid for pid, val in result.items() if not val or not val._get_partner_pricelist_multi_filter_hook()] @@ -348,7 +348,7 @@ class Pricelist(models.Model): # get fallback pricelist when no pricelist for a given country pl_fallback = ( Pricelist.search(pl_domain + [('country_group_ids', '=', False)], limit=1) or - Property.get('property_product_pricelist', 'res.partner') or + Property._get('property_product_pricelist', 'res.partner') or Pricelist.search(pl_domain, limit=1) ) # group partners by country, and find a pricelist for each country diff --git a/addons/product/models/res_company.py b/addons/product/models/res_company.py index 94e053eda23..323d722fb87 100644 --- a/addons/product/models/res_company.py +++ b/addons/product/models/res_company.py @@ -18,7 +18,7 @@ class ResCompany(models.Model): 'name': _("Default %(currency)s pricelist") % params, 'currency_id': new_company.currency_id.id, }) - self.env['ir.property'].sudo().set_default( + self.env['ir.property'].sudo()._set_default( 'property_product_pricelist', 'res.partner', pricelist, @@ -54,7 +54,7 @@ class ResCompany(models.Model): 'name': _("Default %(currency)s pricelist") % params, 'currency_id': currency_id, }) - self.env['ir.property'].sudo().set_default( + self.env['ir.property'].sudo()._set_default( 'property_product_pricelist', 'res.partner', pricelist, diff --git a/addons/product/models/res_partner.py b/addons/product/models/res_partner.py index 786923ef5a4..f76c0e4da63 100644 --- a/addons/product/models/res_partner.py +++ b/addons/product/models/res_partner.py @@ -29,11 +29,11 @@ class Partner(models.Model): limit=1 ) default_for_country = pls and pls[0] - actual = self.env['ir.property'].get('property_product_pricelist', 'res.partner', 'res.partner,%s' % partner.id) + actual = self.env['ir.property']._get('property_product_pricelist', 'res.partner', 'res.partner,%s' % partner.id) # update at each change country, and so erase old pricelist if partner.property_product_pricelist or (actual and default_for_country and default_for_country.id != actual.id): # keep the company of the current user before sudo - self.env['ir.property'].sudo().set_multi( + self.env['ir.property'].sudo()._set_multi( 'property_product_pricelist', partner._name, {partner.id: partner.property_product_pricelist or default_for_country.id}, diff --git a/addons/stock_account/__init__.py b/addons/stock_account/__init__.py index e0fb76fd2d7..558fd40f096 100644 --- a/addons/stock_account/__init__.py +++ b/addons/stock_account/__init__.py @@ -34,7 +34,7 @@ def _configure_journals(cr, registry): 'company_id': company_id.id, 'show_on_dashboard': False }).id - env['ir.property'].set_default( + env['ir.property']._set_default( 'property_stock_journal', 'product.category', journal_id, @@ -51,7 +51,7 @@ def _configure_journals(cr, registry): for name in todo_list: account = getattr(company_id, name) if account: - env['ir.property'].set_default( + env['ir.property']._set_default( name, 'product.category', account, diff --git a/addons/stock_account/tests/common.py b/addons/stock_account/tests/common.py index 68d79a1b8d9..6eec8bce93c 100644 --- a/addons/stock_account/tests/common.py +++ b/addons/stock_account/tests/common.py @@ -11,13 +11,13 @@ class StockAccountTestCommon(AccountTestCommon): super(StockAccountTestCommon, cls).setUpClass() # Properties: Stock valuation account and journal - cls.env['ir.property'].set_default( + cls.env['ir.property']._set_default( 'property_stock_valuation_account_id', 'product.category', cls.stk, cls.env.company, ) - cls.env['ir.property'].set_default( + cls.env['ir.property']._set_default( 'property_stock_journal', 'product.category', cls.miscellaneous_journal, diff --git a/addons/test_website_slides_full/tests/test_ui_wslides.py b/addons/test_website_slides_full/tests/test_ui_wslides.py index e8b45d55d7e..c3e999db7a8 100644 --- a/addons/test_website_slides_full/tests/test_ui_wslides.py +++ b/addons/test_website_slides_full/tests/test_ui_wslides.py @@ -41,8 +41,8 @@ class TestUi(TestUICommon): }) Property = self.env['ir.property'] - Property.set_default('property_account_receivable_id', 'res.partner', a_recv, self.env.company) - Property.set_default('property_account_payable_id', 'res.partner', a_pay, self.env.company) + Property._set_default('property_account_receivable_id', 'res.partner', a_recv, self.env.company) + Property._set_default('property_account_payable_id', 'res.partner', a_pay, self.env.company) product_course_channel_6 = self.env['product.product'].create({ 'name': 'DIY Furniture Course', diff --git a/odoo/addons/base/models/ir_property.py b/odoo/addons/base/models/ir_property.py index aed37bece8c..02703383df4 100644 --- a/odoo/addons/base/models/ir_property.py +++ b/odoo/addons/base/models/ir_property.py @@ -176,7 +176,7 @@ class Property(models.Model): return False @api.model - def set_default(self, name, model, value, company=False): + def _set_default(self, name, model, value, company=False): """ Set the given field's generic value for the given company. :param name: the field's name @@ -204,7 +204,7 @@ class Property(models.Model): }) @api.model - def get(self, name, model, res_id=False): + def _get(self, name, model, res_id=False): """ Get the given field's generic value for the record. :param name: the field's name @@ -223,7 +223,7 @@ class Property(models.Model): return p.get_by_record() return False - # only cache Property.get(res_id=False) as that's + # only cache Property._get(res_id=False) as that's # sub-optimally. COMPANY_KEY = "self.env.company.id" @ormcache(COMPANY_KEY, 'name', 'model') @@ -255,7 +255,7 @@ class Property(models.Model): return [('fields_id', '=', res[0]), ('company_id', 'in', [company_id, False])] @api.model - def get_multi(self, name, model, ids): + def _get_multi(self, name, model, ids): """ Read the property field `name` for the records of model `model` with the given `ids`, and return a dictionary mapping `ids` to their corresponding value. @@ -319,7 +319,7 @@ class Property(models.Model): } @api.model - def set_multi(self, name, model, values, default_value=None): + def _set_multi(self, name, model, values, default_value=None): """ Assign the property field `name` for the records of model `model` with `values` (dictionary mapping record ids to their value). If the value for a given record is the same as the default @@ -340,7 +340,7 @@ class Property(models.Model): if domain is None: raise Exception() # retrieve the default value for the field - default_value = clean(self.get(name, model)) + default_value = clean(self._get(name, model)) # retrieve the properties corresponding to the given record ids self._cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", (name, model)) diff --git a/odoo/addons/test_main_flows/tests/test_flow.py b/odoo/addons/test_main_flows/tests/test_flow.py index 4aaca90ed4d..6d2cfab311b 100644 --- a/odoo/addons/test_main_flows/tests/test_flow.py +++ b/odoo/addons/test_main_flows/tests/test_flow.py @@ -37,11 +37,11 @@ class BaseTestUi(odoo.tests.HttpCase): }) Property = self.env['ir.property'] - Property.set_default('property_account_receivable_id', 'res.partner', a_recv, self.env.company) - Property.set_default('property_account_payable_id', 'res.partner', a_pay, self.env.company) - Property.set_default('property_account_position_id', 'res.partner', False, self.env.company) - Property.set_default('property_account_expense_categ_id', 'product.category', a_expense, self.env.company) - Property.set_default('property_account_income_categ_id', 'product.category', a_sale, self.env.company) + Property._set_default('property_account_receivable_id', 'res.partner', a_recv, self.env.company) + Property._set_default('property_account_payable_id', 'res.partner', a_pay, self.env.company) + Property._set_default('property_account_position_id', 'res.partner', False, self.env.company) + Property._set_default('property_account_expense_categ_id', 'product.category', a_expense, self.env.company) + Property._set_default('property_account_income_categ_id', 'product.category', a_sale, self.env.company) self.expenses_journal = self.env['account.journal'].create({ 'name': 'Vendor Bills - Test', diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py index 6df342aaf94..44a1fe695bf 100644 --- a/odoo/addons/test_new_api/tests/test_new_fields.py +++ b/odoo/addons/test_new_api/tests/test_new_fields.py @@ -1071,9 +1071,9 @@ class TestFields(TransactionCaseWithUserDemo): tag2 = self.env['test_new_api.multi.tag'].create({'name': 'Quuz'}) # create default values for the company-dependent fields - self.env['ir.property'].set_default('foo', 'test_new_api.company', 'default') - self.env['ir.property'].set_default('foo', 'test_new_api.company', 'default1', company1) - self.env['ir.property'].set_default('tag_id', 'test_new_api.company', tag0) + self.env['ir.property']._set_default('foo', 'test_new_api.company', 'default') + self.env['ir.property']._set_default('foo', 'test_new_api.company', 'default1', company1) + self.env['ir.property']._set_default('tag_id', 'test_new_api.company', tag0) # assumption: users don't have access to 'ir.property' accesses = self.env['ir.model.access'].search([('model_id.model', '=', 'ir.property')]) diff --git a/odoo/addons/test_performance/tests/test_performance.py b/odoo/addons/test_performance/tests/test_performance.py index d77ef7c1a21..104fce93195 100644 --- a/odoo/addons/test_performance/tests/test_performance.py +++ b/odoo/addons/test_performance/tests/test_performance.py @@ -459,7 +459,7 @@ class TestIrPropertyOptimizations(TransactionCase): self.Eggs = self.env['test_performance.eggs'] def test_with_falsy_default(self): - self.assertFalse(self.env['ir.property'].get('property_eggs', 'test_performance.bacon')) + self.assertFalse(self.env['ir.property']._get('property_eggs', 'test_performance.bacon')) # warmup eggs = self.Eggs.create({}) @@ -492,7 +492,7 @@ class TestIrPropertyOptimizations(TransactionCase): 'value': eggs, }) - self.assertEqual(eggs, self.env['ir.property'].get('property_eggs', 'test_performance.bacon')) + self.assertEqual(eggs, self.env['ir.property']._get('property_eggs', 'test_performance.bacon')) # warmup self.Bacon.create({}) diff --git a/odoo/fields.py b/odoo/fields.py index d64555e0025..56f58364809 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -571,12 +571,12 @@ class Field(MetaField('DummyField', (object,), {})): # def _default_company_dependent(self, model): - return model.env['ir.property'].get(self.name, self.model_name) + return model.env['ir.property']._get(self.name, self.model_name) def _compute_company_dependent(self, records): # read property as superuser, as the current user may not have access Property = records.env['ir.property'].sudo() - values = Property.get_multi(self.name, self.model_name, records.ids) + values = Property._get_multi(self.name, self.model_name, records.ids) for record in records: record[self.name] = values.get(record.id) @@ -587,7 +587,7 @@ class Field(MetaField('DummyField', (object,), {})): record.id: self.convert_to_write(record[self.name], record) for record in records } - Property.set_multi(self.name, self.model_name, values) + Property._set_multi(self.name, self.model_name, values) def _search_company_dependent(self, records, operator, value): Property = records.env['ir.property'] diff --git a/odoo/models.py b/odoo/models.py index 8af30b89bb5..f88bcce1dca 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -3705,7 +3705,7 @@ Record ids: %(records)s if not field: raise ValueError("Invalid field %r on model %r" % (key, self._name)) if field.company_dependent: - irprop_def = self.env['ir.property'].get(key, self._name) + irprop_def = self.env['ir.property']._get(key, self._name) cached_def = field.convert_to_cache(irprop_def, self) cached_val = field.convert_to_cache(val, self) if cached_val == cached_def: From a4472b104bb98507ef000896a98eac4a29e752f6 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 19 May 2020 10:30:12 +0200 Subject: [PATCH 4/6] [FIX] *: use sudo when accessing ir.property Add sudo in base to allow a unpriviledged user to interact with ir.property in a safe way usingsetters and getters. By making them callable without sudo, we encourage to use these methods instead of making CRUD requests on ir.property directly For other specific search where no native method exists, add sudo --- addons/account/models/account_account.py | 7 +++++-- addons/account/models/account_payment_term.py | 5 +++-- addons/account/models/chart_template.py | 6 +++--- addons/product/models/res_company.py | 4 ++-- addons/product/models/res_partner.py | 2 +- addons/stock/models/res_company.py | 4 ++-- addons/stock_account/__init__.py | 5 +++-- odoo/addons/base/models/ir_property.py | 12 ++++++------ 8 files changed, 25 insertions(+), 20 deletions(-) diff --git a/addons/account/models/account_account.py b/addons/account/models/account_account.py index c9d2de963bd..d4def8834aa 100644 --- a/addons/account/models/account_account.py +++ b/addons/account/models/account_account.py @@ -432,9 +432,12 @@ class AccountAccount(models.Model): raise UserError(_('You cannot perform this action on an account that contains journal items.')) #Checking whether the account is set as a property to any Partner or not values = ['account.account,%s' % (account_id,) for account_id in self.ids] - partner_prop_acc = self.env['ir.property'].search([('value_reference', 'in', values)], limit=1) + partner_prop_acc = self.env['ir.property'].sudo().search([('value_reference', 'in', values)], limit=1) if partner_prop_acc: - raise UserError(_('You cannot remove/deactivate an account which is set on a customer or vendor.')) + account_name = partner_prop_acc.get_by_record().display_name + raise UserError( + _('You cannot remove/deactivate the account %s which is set on a customer or vendor.') % account_name + ) return super(AccountAccount, self).unlink() def action_read_account(self): diff --git a/addons/account/models/account_payment_term.py b/addons/account/models/account_payment_term.py index bb918807168..c7d8dd8d734 100644 --- a/addons/account/models/account_payment_term.py +++ b/addons/account/models/account_payment_term.py @@ -71,8 +71,9 @@ class AccountPaymentTerm(models.Model): for terms in self: if self.env['account.move'].search([('invoice_payment_term_id', 'in', terms.ids)]): raise UserError(_('You can not delete payment terms as other records still reference it. However, you can archive it.')) - property_recs = self.env['ir.property'].search([('value_reference', 'in', ['account.payment.term,%s'%payment_term.id for payment_term in terms])]) - property_recs.unlink() + self.env['ir.property'].sudo().search( + [('value_reference', 'in', ['account.payment.term,%s'%payment_term.id for payment_term in terms])] + ).unlink() return super(AccountPaymentTerm, self).unlink() diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index 202aa38b291..ebe926d3fc7 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -207,9 +207,9 @@ class AccountChartTemplate(models.Model): existing_journals = self.env['account.journal'].search([('company_id', '=', company.id)]) if existing_journals: prop_values.extend(['account.journal,%s' % (journal_id,) for journal_id in existing_journals.ids]) - accounting_props = self.env['ir.property'].search([('value_reference', 'in', prop_values)]) - if accounting_props: - accounting_props.sudo().unlink() + self.env['ir.property'].sudo().search( + [('value_reference', 'in', prop_values)] + ).unlink() # delete account, journal, tax, fiscal position and reconciliation model models_to_delete = ['account.reconcile.model', 'account.fiscal.position', 'account.tax', 'account.move', 'account.journal', 'account.group'] diff --git a/addons/product/models/res_company.py b/addons/product/models/res_company.py index 323d722fb87..5a44de1afc1 100644 --- a/addons/product/models/res_company.py +++ b/addons/product/models/res_company.py @@ -18,7 +18,7 @@ class ResCompany(models.Model): 'name': _("Default %(currency)s pricelist") % params, 'currency_id': new_company.currency_id.id, }) - self.env['ir.property'].sudo()._set_default( + self.env['ir.property']._set_default( 'property_product_pricelist', 'res.partner', pricelist, @@ -54,7 +54,7 @@ class ResCompany(models.Model): 'name': _("Default %(currency)s pricelist") % params, 'currency_id': currency_id, }) - self.env['ir.property'].sudo()._set_default( + self.env['ir.property']._set_default( 'property_product_pricelist', 'res.partner', pricelist, diff --git a/addons/product/models/res_partner.py b/addons/product/models/res_partner.py index f76c0e4da63..5b641d5f811 100644 --- a/addons/product/models/res_partner.py +++ b/addons/product/models/res_partner.py @@ -33,7 +33,7 @@ class Partner(models.Model): # update at each change country, and so erase old pricelist if partner.property_product_pricelist or (actual and default_for_country and default_for_country.id != actual.id): # keep the company of the current user before sudo - self.env['ir.property'].sudo()._set_multi( + self.env['ir.property']._set_multi( 'property_product_pricelist', partner._name, {partner.id: partner.property_product_pricelist or default_for_country.id}, diff --git a/addons/stock/models/res_company.py b/addons/stock/models/res_company.py index 2e7c5bd474c..fea6cbb88c8 100644 --- a/addons/stock/models/res_company.py +++ b/addons/stock/models/res_company.py @@ -130,7 +130,7 @@ class Company(models.Model): def create_missing_inventory_loss_location(self): company_ids = self.env['res.company'].search([]) inventory_loss_product_template_field = self.env['ir.model.fields'].search([('model','=','product.template'),('name','=','property_stock_inventory')]) - companies_having_property = self.env['ir.property'].search([('fields_id', '=', inventory_loss_product_template_field.id)]).mapped('company_id') + companies_having_property = self.env['ir.property'].sudo().search([('fields_id', '=', inventory_loss_product_template_field.id)]).mapped('company_id') company_without_property = company_ids - companies_having_property company_without_property._create_inventory_loss_location() @@ -138,7 +138,7 @@ class Company(models.Model): def create_missing_production_location(self): company_ids = self.env['res.company'].search([]) production_product_template_field = self.env['ir.model.fields'].search([('model','=','product.template'),('name','=','property_stock_production')]) - companies_having_property = self.env['ir.property'].search([('fields_id', '=', production_product_template_field.id)]).mapped('company_id') + companies_having_property = self.env['ir.property'].sudo().search([('fields_id', '=', production_product_template_field.id)]).mapped('company_id') company_without_property = company_ids - companies_having_property company_without_property._create_production_location() diff --git a/addons/stock_account/__init__.py b/addons/stock_account/__init__.py index 558fd40f096..d4786823b49 100644 --- a/addons/stock_account/__init__.py +++ b/addons/stock_account/__init__.py @@ -16,8 +16,9 @@ def _configure_journals(cr, registry): for company_id in company_ids: # Check if property exists for stock account journal exists - properties = env['ir.property'].search([ - ('name', '=', 'property_stock_journal'), + field = env['ir.model.fields']._get("product.category", "property_stock_journal") + properties = env['ir.property'].sudo().search([ + ('fields_id', '=', field.id), ('company_id', '=', company_id.id)]) # If not, check if you can find a journal that is already there with the same name, otherwise create one diff --git a/odoo/addons/base/models/ir_property.py b/odoo/addons/base/models/ir_property.py index 02703383df4..d5b47d9a9ab 100644 --- a/odoo/addons/base/models/ir_property.py +++ b/odoo/addons/base/models/ir_property.py @@ -186,7 +186,7 @@ class Property(models.Model): """ field_id = self.env['ir.model.fields']._get(model, name).id company_id = int(company) if company else False - prop = self.search([ + prop = self.sudo().search([ ('fields_id', '=', field_id), ('company_id', '=', company_id), ('res_id', '=', False), @@ -194,7 +194,7 @@ class Property(models.Model): if prop: prop.write({'value': value}) else: - self.create({ + prop.create({ 'fields_id': field_id, 'company_id': company_id, 'res_id': False, @@ -243,8 +243,8 @@ class Property(models.Model): res_id = "%s,%s" % (model, res_id) domain = [('res_id', '=', res_id)] + domain #make the search with company_id asc to make sure that properties specific to a company are given first - return self.search(domain, limit=1, order='company_id') - return self.browse(()) + return self.sudo().search(domain, limit=1, order='company_id') + return self.sudo().browse(()) def _get_domain(self, prop_name, model): self._cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", (prop_name, model)) @@ -347,7 +347,7 @@ class Property(models.Model): field_id = self._cr.fetchone()[0] company_id = self.env.company.id refs = {('%s,%s' % (model, id)): id for id in values} - props = self.search([ + props = self.sudo().search([ ('fields_id', '=', field_id), ('company_id', '=', company_id), ('res_id', 'in', list(refs)), @@ -379,7 +379,7 @@ class Property(models.Model): 'value': value, 'type': self.env[model]._fields[name].type, }) - self.create(vals_list) + self.sudo().create(vals_list) @api.model def search_multi(self, name, model, operator, value): From 2f26eee981e6857fc0d9aeebe53ec85939bebfb2 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 19 May 2020 11:37:17 +0200 Subject: [PATCH 5/6] [IMP] base: do not verify ACL on ir.property Users have no longer access to unlink if not system. Property are accessed via sudo so ACL check is no longer relevant. --- odoo/addons/base/models/ir_property.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/odoo/addons/base/models/ir_property.py b/odoo/addons/base/models/ir_property.py index d5b47d9a9ab..aa9a6c04535 100644 --- a/odoo/addons/base/models/ir_property.py +++ b/odoo/addons/base/models/ir_property.py @@ -360,8 +360,6 @@ class Property(models.Model): if value == default_value: # avoid prop.unlink(), as it clears the record cache that can # contain the value of other properties to set on record! - prop.check_access_rights('unlink') - prop.check_access_rule('unlink') self._cr.execute("DELETE FROM ir_property WHERE id=%s", [prop.id]) elif value != clean(prop.get_by_record()): prop.write({'value': value}) From 6db2a81ea2578615ccc17199dfc488745b2fc596 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 19 May 2020 10:29:16 +0200 Subject: [PATCH 6/6] [IMP] *: use specific ir.property methods Do not make direct CRUD to ir.property records, use appropriate methods for that --- addons/account/models/chart_template.py | 40 +++++++------------ .../mrp_subcontracting/models/res_company.py | 16 +++----- addons/point_of_sale/tests/test_frontend.py | 18 +++------ addons/stock/models/res_company.py | 31 +++++++------- .../models/account_chart_template.py | 37 +++-------------- .../models/stock_landed_cost.py | 7 +--- .../tests/test_website_sale_pricelist.py | 16 ++------ odoo/addons/base/tests/test_orm.py | 15 +++---- .../tests/test_performance.py | 7 +--- 9 files changed, 57 insertions(+), 130 deletions(-) diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index ebe926d3fc7..6f12b955724 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -463,34 +463,22 @@ class AccountChartTemplate(models.Model): self.ensure_one() PropertyObj = self.env['ir.property'] todo_list = [ - ('property_account_receivable_id', 'res.partner', 'account.account'), - ('property_account_payable_id', 'res.partner', 'account.account'), - ('property_account_expense_categ_id', 'product.category', 'account.account'), - ('property_account_income_categ_id', 'product.category', 'account.account'), - ('property_account_expense_id', 'product.template', 'account.account'), - ('property_account_income_id', 'product.template', 'account.account'), - ('property_tax_payable_account_id', 'account.tax.group', 'account.account'), - ('property_tax_receivable_account_id', 'account.tax.group', 'account.account'), - ('property_advance_tax_payment_account_id', 'account.tax.group', 'account.account'), + ('property_account_receivable_id', 'res.partner'), + ('property_account_payable_id', 'res.partner'), + ('property_account_expense_categ_id', 'product.category'), + ('property_account_income_categ_id', 'product.category'), + ('property_account_expense_id', 'product.template'), + ('property_account_income_id', 'product.template'), + ('property_tax_payable_account_id', 'account.tax.group'), + ('property_tax_receivable_account_id', 'account.tax.group'), + ('property_advance_tax_payment_account_id', 'account.tax.group'), ] - for record in todo_list: - account = getattr(self, record[0]) - value = account and 'account.account,' + str(acc_template_ref[account.id]) or False + for field, model in todo_list: + account = self[field] + value = acc_template_ref[account.id] if account else False if value: - field = self.env['ir.model.fields'].search([('name', '=', record[0]), ('model', '=', record[1]), ('relation', '=', record[2])], limit=1) - vals = { - 'name': record[0], - 'company_id': company.id, - 'fields_id': field.id, - 'value': value, - } - properties = PropertyObj.search([('name', '=', record[0]), ('company_id', '=', company.id)]) - if properties: - #the property exist: modify it - properties.write(vals) - else: - #create the property - PropertyObj.create(vals) + PropertyObj._set_default(field, model, value, company=company) + stock_properties = [ 'property_stock_account_input_categ_id', 'property_stock_account_output_categ_id', diff --git a/addons/mrp_subcontracting/models/res_company.py b/addons/mrp_subcontracting/models/res_company.py index 14a7955e98f..583d2413916 100644 --- a/addons/mrp_subcontracting/models/res_company.py +++ b/addons/mrp_subcontracting/models/res_company.py @@ -21,10 +21,6 @@ class ResCompany(models.Model): def _create_subcontracting_location(self): parent_location = self.env.ref('stock.stock_location_locations', raise_if_not_found=False) - property_stock_subcontractor_res_partner_field = self.env['ir.model.fields'].search([ - ('model', '=', 'res.partner'), - ('name', '=', 'property_stock_subcontractor') - ]) for company in self: subcontracting_location = self.env['stock.location'].create({ 'name': _('Subcontracting Location'), @@ -32,10 +28,10 @@ class ResCompany(models.Model): 'location_id': parent_location.id, 'company_id': company.id, }) - self.env['ir.property'].create({ - 'name': 'property_stock_subcontractor_%s' % company.name, - 'fields_id': property_stock_subcontractor_res_partner_field.id, - 'company_id': company.id, - 'value': 'stock.location,%d' % subcontracting_location.id, - }) + self.env['ir.property']._set_default( + "property_stock_subcontractor", + "res.partner", + subcontracting_location, + company, + ) company.subcontracting_location_id = subcontracting_location diff --git a/addons/point_of_sale/tests/test_frontend.py b/addons/point_of_sale/tests/test_frontend.py index 69fff92983e..9fe59733ba1 100644 --- a/addons/point_of_sale/tests/test_frontend.py +++ b/addons/point_of_sale/tests/test_frontend.py @@ -30,12 +30,7 @@ class TestPointOfSaleHttpCommon(odoo.tests.HttpCase): 'reconcile': True}) self.env.company.account_default_pos_receivable_account_id = account_receivable - self.env['ir.property']._set_default( - 'property_account_receivable_id', - 'res.partner', - account_receivable, - main_company, - ) + self.env['ir.property']._set_default('property_account_receivable_id', 'res.partner', account_receivable, main_company) cash_journal = journal_obj.create({ 'name': 'Cash Test', @@ -400,12 +395,11 @@ class TestPointOfSaleHttpCommon(odoo.tests.HttpCase): # Change the default sale pricelist of customers, # so the js tests can expect deterministically this pricelist when selecting a customer. - field = env['ir.model.fields']._get('res.partner', 'property_product_pricelist') - env['ir.property'].search([ - ('name', '=', 'property_product_pricelist'), - ('fields_id', '=', field.id), - ('res_id', '=', False) - ]).write({'value_reference': 'product.pricelist,%s' % public_pricelist.id}) + env['ir.property']._set_default( + "property_product_pricelist", + "res.partner", + public_pricelist, + ) @odoo.tests.tagged('post_install', '-at_install') diff --git a/addons/stock/models/res_company.py b/addons/stock/models/res_company.py index fea6cbb88c8..4ae579ebaeb 100644 --- a/addons/stock/models/res_company.py +++ b/addons/stock/models/res_company.py @@ -47,7 +47,6 @@ class Company(models.Model): def _create_inventory_loss_location(self): parent_location = self.env.ref('stock.stock_location_locations_virtual', raise_if_not_found=False) - inventory_loss_product_template_field = self.env['ir.model.fields'].search([('model','=','product.template'),('name','=','property_stock_inventory')]) for company in self: inventory_loss_location = self.env['stock.location'].create({ 'name': 'Inventory adjustment', @@ -55,16 +54,15 @@ class Company(models.Model): 'location_id': parent_location.id, 'company_id': company.id, }) - self.env['ir.property'].create({ - 'name': 'property_stock_inventory_%s' % company.name, - 'fields_id': inventory_loss_product_template_field.id, - 'company_id': company.id, - 'value': 'stock.location,%d' % inventory_loss_location.id, - }) + self.env['ir.property']._set_default( + "property_stock_inventory", + "product.template", + inventory_loss_location, + company.id, + ) def _create_production_location(self): parent_location = self.env.ref('stock.stock_location_locations_virtual', raise_if_not_found=False) - production_product_template_field = self.env['ir.model.fields'].search([('model','=','product.template'),('name','=','property_stock_production')]) for company in self: production_location = self.env['stock.location'].create({ 'name': 'Production', @@ -72,12 +70,13 @@ class Company(models.Model): 'location_id': parent_location.id, 'company_id': company.id, }) - self.env['ir.property'].create({ - 'name': 'property_stock_inventory_%s' % company.name, - 'fields_id': production_product_template_field.id, - 'company_id': company.id, - 'value': 'stock.location,%d' % production_location.id, - }) + self.env['ir.property']._set_default( + "property_stock_production", + "product.template", + production_location, + company.id, + ) + def _create_scrap_location(self): parent_location = self.env.ref('stock.stock_location_locations_virtual', raise_if_not_found=False) @@ -129,7 +128,7 @@ class Company(models.Model): @api.model def create_missing_inventory_loss_location(self): company_ids = self.env['res.company'].search([]) - inventory_loss_product_template_field = self.env['ir.model.fields'].search([('model','=','product.template'),('name','=','property_stock_inventory')]) + inventory_loss_product_template_field = self.env['ir.model.fields']._get('product.template', 'property_stock_inventory') companies_having_property = self.env['ir.property'].sudo().search([('fields_id', '=', inventory_loss_product_template_field.id)]).mapped('company_id') company_without_property = company_ids - companies_having_property company_without_property._create_inventory_loss_location() @@ -137,7 +136,7 @@ class Company(models.Model): @api.model def create_missing_production_location(self): company_ids = self.env['res.company'].search([]) - production_product_template_field = self.env['ir.model.fields'].search([('model','=','product.template'),('name','=','property_stock_production')]) + production_product_template_field = self.env['ir.model.fields']._get('product.template', 'property_stock_production') companies_having_property = self.env['ir.property'].sudo().search([('fields_id', '=', production_product_template_field.id)]).mapped('company_id') company_without_property = company_ids - companies_having_property company_without_property._create_production_location() diff --git a/addons/stock_account/models/account_chart_template.py b/addons/stock_account/models/account_chart_template.py index 16796b947c5..8f6105d09bb 100644 --- a/addons/stock_account/models/account_chart_template.py +++ b/addons/stock_account/models/account_chart_template.py @@ -20,43 +20,16 @@ class AccountChartTemplate(models.Model): PropertyObj = self.env['ir.property'] # Property Stock Journal value = self.env['account.journal'].search([('company_id', '=', company.id), ('code', '=', 'STJ'), ('type', '=', 'general')], limit=1) if value: - field = self.env['ir.model.fields'].search([('name', '=', 'property_stock_journal'), ('model', '=', 'product.category'), ('relation', '=', 'account.journal')], limit=1) - vals = { - 'name': 'property_stock_journal', - 'company_id': company.id, - 'fields_id': field.id, - 'value': 'account.journal,%s' % value.id, - } - properties = PropertyObj.search([('name', '=', 'property_stock_journal'), ('company_id', '=', company.id)]) - if properties: - # the property exist: modify it - properties.write(vals) - else: - # create the property - PropertyObj.create(vals) + PropertyObj._set_default("property_stock_journal", "product.category", value, company) todo_list = [ # Property Stock Accounts 'property_stock_account_input_categ_id', 'property_stock_account_output_categ_id', 'property_stock_valuation_account_id', ] - for record in todo_list: - account = getattr(self, record) - value = account and 'account.account,' + str(acc_template_ref[account.id]) or False - if value: - field = self.env['ir.model.fields'].search([('name', '=', record), ('model', '=', 'product.category'), ('relation', '=', 'account.account')], limit=1) - vals = { - 'name': record, - 'company_id': company.id, - 'fields_id': field.id, - 'value': value, - } - properties = PropertyObj.search([('name', '=', record), ('company_id', '=', company.id)], limit=1) - if not properties: - # create the property - PropertyObj.create(vals) - elif not properties.value_reference: - # update the property if False - properties.write(vals) + for field in todo_list: + account = self[field] + value = acc_template_ref[account.id] if account else False + PropertyObj._set_default(field, "product.category", value, company) return res diff --git a/addons/stock_landed_costs/models/stock_landed_cost.py b/addons/stock_landed_costs/models/stock_landed_cost.py index 66f5a6e9a45..63222bc1385 100644 --- a/addons/stock_landed_costs/models/stock_landed_cost.py +++ b/addons/stock_landed_costs/models/stock_landed_cost.py @@ -28,12 +28,7 @@ class StockLandedCost(models.Model): if self.env.company.lc_journal_id: lc_journal = self.env.company.lc_journal_id else: - ir_property = self.env['ir.property'].search([ - ('name', '=', 'property_stock_journal'), - ('company_id', '=', self.env.company.id) - ], limit=1) - if ir_property: - lc_journal = ir_property.get_by_record() + lc_journal = self.env['ir.property']._get("property_stock_journal", "product.category") return lc_journal name = fields.Char( diff --git a/addons/website_sale/tests/test_website_sale_pricelist.py b/addons/website_sale/tests/test_website_sale_pricelist.py index 04306b9b263..01d5e58049d 100644 --- a/addons/website_sale/tests/test_website_sale_pricelist.py +++ b/addons/website_sale/tests/test_website_sale_pricelist.py @@ -432,19 +432,9 @@ class TestWebsitePriceListMultiCompany(TransactionCaseWithUserDemo): # Ensure everything was done correctly self.assertEqual(self.demo_user.partner_id.with_company(self.company1.id).property_product_pricelist, self.c1_pl) self.assertEqual(self.demo_user.partner_id.with_company(self.company2.id).property_product_pricelist, self.c2_pl) - irp1 = self.env['ir.property'].search([ - ('name', '=', 'property_product_pricelist'), - ('company_id', '=', self.company1.id), - ('res_id', '=', 'res.partner,%s' % self.demo_user.partner_id.id), - ('value_reference', '=', 'product.pricelist,%s' % self.c1_pl.id), - ]) - irp2 = self.env['ir.property'].search([ - ('name', '=', 'property_product_pricelist'), - ('company_id', '=', self.company2.id), - ('res_id', '=', 'res.partner,%s' % self.demo_user.partner_id.id), - ('value_reference', '=', 'product.pricelist,%s' % self.c2_pl.id), - ]) - self.assertEqual(len(irp1 + irp2), 2, "Ensure there is an `ir.property` for demo partner for every company, and that the pricelist is the company specific one.") + irp1 = self.env['ir.property'].with_company(self.company1)._get("property_product_pricelist", "res.partner", self.demo_user.partner_id.id) + irp2 = self.env['ir.property'].with_company(self.company2)._get("property_product_pricelist", "res.partner", self.demo_user.partner_id.id) + self.assertEqual((irp1, irp2), (self.c1_pl, self.c2_pl), "Ensure there is an `ir.property` for demo partner for every company, and that the pricelist is the company specific one.") simulate_frontend_context(self) # ---------------------------------- IR.PROPERTY ------------------------------------- # id | name | res_id | company_id | value_reference diff --git a/odoo/addons/base/tests/test_orm.py b/odoo/addons/base/tests/test_orm.py index 2e0406e6b5b..285e4b67290 100644 --- a/odoo/addons/base/tests/test_orm.py +++ b/odoo/addons/base/tests/test_orm.py @@ -235,21 +235,18 @@ class TestORM(TransactionCase): 'groups_id': [(6, 0, [self.ref('base.group_partner_manager')])], }) p1 = self.env['res.partner'].with_user(user).create({'name': 'Zorro'}) - p1_prop = self.env['ir.property'].with_user(user).create({ - 'name': 'Slip en laine', - 'res_id': 'res.partner,{}'.format(p1.id), - 'fields_id': self.env['ir.model.fields'].search([ - ('model', '=', 'res.partner'), ('name', '=', 'ref')], limit=1).id, - 'value_text': 'Nain poilu', - 'type': 'char', - }) + self.env['ir.property'].with_user(user)._set_multi("ref", "res.partner", {p1.id: "Nain poilu"}) + p1_prop = self.env['ir.property'].with_user(user)._get("ref", "res.partner", res_id=p1.id) + self.assertEqual( + p1_prop, "Nain poilu", 'p1_prop should have been created') # Unlink with unprivileged user p1.unlink() # ir.property is deleted + p1_prop = self.env['ir.property'].with_user(user)._get("ref", "res.partner", res_id=p1.id) self.assertEqual( - p1_prop.exists(), self.env['ir.property'], 'p1_prop should have been deleted') + p1_prop, False, 'p1_prop should have been deleted') def test_create_multi(self): """ create for multiple records """ diff --git a/odoo/addons/test_performance/tests/test_performance.py b/odoo/addons/test_performance/tests/test_performance.py index 104fce93195..01bab7187e2 100644 --- a/odoo/addons/test_performance/tests/test_performance.py +++ b/odoo/addons/test_performance/tests/test_performance.py @@ -485,12 +485,7 @@ class TestIrPropertyOptimizations(TransactionCase): def test_with_truthy_default(self): eggs = self.Eggs.create({}) - field = self.env['ir.model.fields']._get('test_performance.bacon', 'property_eggs') - self.env['ir.property'].create({ - 'name': 'property_eggs_with_bacon', - 'fields_id': field.id, - 'value': eggs, - }) + self.env['ir.property']._set_default("property_eggs", "test_performance.bacon", eggs) self.assertEqual(eggs, self.env['ir.property']._get('property_eggs', 'test_performance.bacon'))