[MERGE] base: make ir.property private

Only system users can access ir.property records.

This PR is part of the long-term task id 8203 that plans to remove all access to
 ir.* models

"Official" methods to interact with `ir.property`:
- `_set_default(name, model, value, company=False)`
- `_set_multi(name, model, values, default_value=None)`
- `_get(name, model, res_id=False)`
- `_get_multi(name, model, ids)`

Done in this PR:
- remove ACL on ir.property (only system users can access directly)
- make all getter/setters private with sudo
- use these methods everywhere instead of CRUD calls
- add sudo where needed

closes odoo/odoo#51658

Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
This commit is contained in:
Odoo's Mergebot
2020-05-28 07:13:56 +02:00
committed by GitHub
29 changed files with 130 additions and 194 deletions
+5 -2
View File
@@ -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):
@@ -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()
+17 -29
View File
@@ -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']
@@ -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',
+5 -5
View File
@@ -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({
-1
View File
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
8 access_hr_department_user hr.department.user model_hr_department group_hr_user 1 1 1 1
9 access_hr_department_employee hr.department.employee model_hr_department base.group_user 1 0 0 0
10 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
11 access_hr_plan_activity_type_employee access_hr_plan_activity_type model_hr_plan_activity_type base.group_user 1 0 0 0
12 access_hr_plan_employee access_hr_plan_employee model_hr_plan base.group_user 1 0 0 0
13 access_hr_plan_activity_type_hr_user access_hr_plan_activity_type model_hr_plan_activity_type group_hr_user 1 1 1 1
+2 -2
View File
@@ -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
@@ -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
+6 -12
View File
@@ -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')
+1 -1
View File
@@ -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,
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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,
+2 -2
View File
@@ -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']._set_multi(
'property_product_pricelist',
partner._name,
{partner.id: partner.property_product_pricelist or default_for_country.id},
-1
View File
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
19 access_sale_order_accountant sale.order.accountant model_sale_order account.group_account_user 1 1 0 0
20 access_sale_report_salesman sale.report model_sale_report sales_team.group_sale_salesman 1 1 1 0
21 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
22 access_sale_account_journal account.journal sale order.user account.model_account_journal sales_team.group_sale_salesman 1 0 0 0
23 access_res_partner_sale_user res.partner.sale.user base.model_res_partner sales_team.group_sale_salesman 1 0 0 0
24 access_res_partner_sale_manager res.partner.sale.manager base.model_res_partner sales_team.group_sale_manager 1 1 1 0
+17 -18
View File
@@ -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,16 +128,16 @@ 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')])
companies_having_property = self.env['ir.property'].search([('fields_id', '=', inventory_loss_product_template_field.id)]).mapped('company_id')
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()
@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')])
companies_having_property = self.env['ir.property'].search([('fields_id', '=', production_product_template_field.id)]).mapped('company_id')
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()
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
27 access_product_packaging_stock_manager product.packaging stock_manager product.model_product_packaging stock.group_stock_manager 1 1 1 1
28 access_product_supplierinfo_stock_manager product.supplierinfo stock_manager product.model_product_supplierinfo stock.group_stock_manager 1 1 1 1
29 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
30 access_product_group_res_partner_stock_manager res_partner group_stock_manager base.model_res_partner stock.group_stock_manager 1 1 1 0
31 access_product_pricelist_item_stock_manager product.pricelist.item stock_manager product.model_product_pricelist_item stock.group_stock_manager 1 1 1 1
32 access_stock_warehouse_orderpoint stock.warehouse.orderpoint model_stock_warehouse_orderpoint stock.group_stock_user 1 0 0 0
+5 -4
View File
@@ -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
@@ -34,7 +35,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 +52,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,
@@ -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
+2 -2
View File
@@ -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,
@@ -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(
@@ -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',
@@ -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
+21 -14
View File
@@ -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
@@ -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,
@@ -204,7 +204,14 @@ 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
:param model: the field's model name
:param res_id: optional resource, format: "<id>" (int) or
"<model>,<id>" (str)
"""
if not res_id:
t, v = self._get_default_property(name, model)
if not v or t != 'many2one':
@@ -216,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')
@@ -232,10 +239,12 @@ 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')
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))
@@ -246,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.
@@ -310,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
@@ -331,14 +340,14 @@ 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))
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)),
@@ -351,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})
@@ -370,7 +377,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):
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
20 access_ir_module_module_group_user ir_module_module group_user model_ir_module_module group_system 1 1 1 1
21 access_ir_module_module_dependency_group_system ir_module_module_dependency group_system model_ir_module_module_dependency group_system 1 1 1 1
22 access_ir_module_module_exclusion_group_system ir_module_module_exclusion group_system model_ir_module_module_exclusion group_system 1 1 1 1
23 access_ir_property_group_user ir_property group_user model_ir_property 1 0 0 0 0
24 access_ir_property_group_user_manager access_ir_property_group_system ir_property group_manager ir_property group_system model_ir_property base.group_partner_manager base.group_system 1 1 1 0
25 access_ir_rule_group_erp_manager ir_rule group_erp_manager model_ir_rule group_erp_manager 1 1 1 1
26 access_ir_sequence_group_user ir_sequence group_user model_ir_sequence group_user 1 0 0 0
27 access_ir_sequence_group_system ir_sequence group_system model_ir_sequence group_system 1 1 1 1
+6 -9
View File
@@ -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 """
@@ -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',
@@ -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')])
@@ -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({})
@@ -485,14 +485,9 @@ 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'))
self.assertEqual(eggs, self.env['ir.property']._get('property_eggs', 'test_performance.bacon'))
# warmup
self.Bacon.create({})
+3 -3
View File
@@ -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']
+1 -1
View File
@@ -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: