[FORWARD] Merged 7.0 addons revisions until 9021

bzr revid: tde@openerp.com-20130415084302-hx7zxtpl5u6yphfg
bzr revid: tde@openerp.com-20130415153732-v4ols3t56rku92oz
This commit is contained in:
Thibault Delavallée 2013-04-15 17:37:32 +02:00
commit 812c852fd0
23 changed files with 773 additions and 67 deletions

View File

@ -221,7 +221,7 @@ class hr_employee(osv.osv):
(model, mail_group_id) = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'group_all_employees')
employee = self.browse(cr, uid, employee_id, context=context)
self.pool.get('mail.group').message_post(cr, uid, [mail_group_id],
body='Welcome to %s! Please help them take the first steps with OpenERP!' % (employee.name),
body='Welcome to %s! Please help him/her take the first steps with OpenERP!' % (employee.name),
subtype='mail.mt_comment', context=context)
except:
pass # group deleted: do not push a message

View File

@ -180,7 +180,8 @@ class hr_holidays(osv.osv):
]
_sql_constraints = [
('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL))", "The employee or employee category of this request is missing."),
('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL))",
"The employee or employee category of this request is missing. Please make sure that your user login is linked to an employee."),
('date_check2', "CHECK ( (type='add') OR (date_from <= date_to))", "The start date must be anterior to the end date."),
('date_check', "CHECK ( number_of_days_temp >= 0 )", "The number of days must be greater than 0."),
]

View File

@ -41,7 +41,7 @@
<field name="arch" type="xml">
<tree string="Applicants" fonts="bold:message_unread==True" colors="grey:state in ('cancel','done');blue:state=='pending'">
<field name="message_unread" invisible="1"/>
<field name="create_date" groups="base.group_no_one"/>
<field name="create_date"/>
<field name="name" string="Subject"/>
<field name="partner_name"/>
<field name="email_from"/>
@ -199,7 +199,7 @@
<filter string="Appreciation" domain="[]" context="{'group_by':'priority'}"/>
<filter string="Stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Source" domain="[]" context="{'group_by':'source_id'}"/>
<filter string="Creation Date" domain="[]" context="{'group_by':'create_date'}" groups="base.group_no_one"/>
<filter string="Creation Date" domain="[]" context="{'group_by':'create_date'}"/>
</group>
</search>
</field>

View File

@ -22,12 +22,6 @@
<!-- Fiscal Position Account Templates -->
<record id="fiscal_position_account_template_1" model="account.fiscal.position.account.template">
<field name="position_id" ref="fiscal_position_template_1" />
<field name="account_src_id" ref="l10n_be.a7000" />
<field name="account_dest_id" ref="l10n_be.a_sale" />
</record>
<record id="fiscal_position_account_template_3" model="account.fiscal.position.account.template">
<field name="position_id" ref="fiscal_position_template_3" />
<field name="account_src_id" ref="l10n_be.a7000" />

View File

@ -128,6 +128,8 @@ class account_coda_import(osv.osv_memory):
raise osv.except_osv(_('Error') + ' R1004', _("No matching Bank Account (with Account Journal) found.\n\nPlease set-up a Bank Account with as Account Number '%s' and as Currency '%s' and an Account Journal.") % (statement['acc_number'], statement['currency']))
statement['description'] = rmspaces(line[90:125])
statement['balance_start'] = float(rmspaces(line[43:58])) / 1000
if line[42] == '1': #1 = Debit, the starting balance is negative
statement['balance_start'] = - statement['balance_start']
statement['balance_start_date'] = time.strftime(tools.DEFAULT_SERVER_DATE_FORMAT, time.strptime(rmspaces(line[58:64]), '%d%m%y'))
statement['accountHolder'] = rmspaces(line[64:90])
statement['paperSeqNumber'] = rmspaces(line[2:5])

View File

@ -906,6 +906,7 @@ class pos_order(osv.osv):
account_tax_obj = self.pool.get('account.tax')
user_proxy = self.pool.get('res.users')
property_obj = self.pool.get('ir.property')
cur_obj = self.pool.get('res.currency')
period = account_period_obj.find(cr, uid, context=context)[0]
@ -1001,17 +1002,19 @@ class pos_order(osv.osv):
#TOFIX: a deep refactoring of this method (and class!) is needed in order to get rid of this stupid hack
assert order.lines, _('The POS order must have lines when calling this method')
# Create an move for each order line
cur = order.pricelist_id.currency_id
for line in order.lines:
tax_amount = 0
taxes = [t for t in line.product_id.taxes_id]
computed_taxes = account_tax_obj.compute_all(cr, uid, taxes, line.price_unit * (100.0-line.discount) / 100.0, line.qty)['taxes']
for tax in computed_taxes:
tax_amount += round(tax['amount'], 2)
tax_amount += cur_obj.round(cr, uid, cur, tax['amount'])
group_key = (tax['tax_code_id'], tax['base_code_id'], tax['account_collected_id'], tax['id'])
group_tax.setdefault(group_key, 0)
group_tax[group_key] += round(tax['amount'], 2)
group_tax[group_key] += cur_obj.round(cr, uid, cur, tax['amount'])
amount = line.price_subtotal

View File

@ -1,24 +1,8 @@
function openerp_pos_models(instance, module){ //module is instance.point_of_sale
var QWeb = instance.web.qweb;
// rounds a value with a fixed number of decimals.
// round(3.141492,2) -> 3.14
function round(value,decimals){
var mult = Math.pow(10,decimals || 0);
return Math.round(value*mult)/mult;
}
window.round = round;
// rounds a value with decimal form precision
// round(3.141592,0.025) ->3.125
function round_pr(value,precision){
if(!precision || precision < 0){
throw new Error('round_pr(): needs a precision greater than zero, got '+precision+' instead');
}
return Math.round(value / precision) * precision;
}
window.round_pr = round_pr;
var round_di = instance.web.round_decimals;
var round_pr = instance.web.round_precision
// The PosModel contains the Point Of Sale's representation of the backend.
// Since the PoS must work in standalone ( Without connection to the server )
@ -391,7 +375,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
var quant = Math.max(parseFloat(quantity) || 0, 0);
var unit = this.get_unit();
if(unit){
this.quantity = Math.max(unit.rounding, Math.round(quant / unit.rounding) * unit.rounding);
this.quantity = Math.max(unit.rounding, round_pr(quant, unit.rounding));
this.quantityStr = this.quantity.toFixed(Math.max(0,Math.ceil(Math.log(1.0 / unit.rounding) / Math.log(10))));
}else{
this.quantity = quant;
@ -484,7 +468,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
},
// changes the base price of the product for this orderline
set_unit_price: function(price){
this.price = round(parseFloat(price) || 0, 2);
this.price = round_di(parseFloat(price) || 0, 2);
this.trigger('change');
},
get_unit_price: function(){

View File

@ -983,7 +983,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
}
if(this.pos_widget.action_bar){
this.pos_widget.action_bar.set_button_disabled('validation', remaining > 0);
this.pos_widget.action_bar.set_button_disabled('validation', remaining > 0.000001);
}
},
set_numpad_state: function(numpadState) {

View File

@ -19,3 +19,4 @@
#
##############################################################################
import project

View File

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-TODAY OpenERP S.A (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import osv
class portal_project(osv.Model):
""" Update of mail_mail class, to add the signin URL to notifications. """
_inherit = 'project.project'
def _get_visibility_selection(self, cr, uid, context=None):
""" Override to add portal option. """
selection = super(portal_project, self)._get_visibility_selection(cr, uid, context=context)
idx = [item[0] for item in selection].index('public')
selection.insert((idx + 1), ('portal', 'Portal Users and Employees'))
return selection
# return [('public', 'All Users'),
# ('portal', 'Portal Users and Employees'),
# ('employees', 'Employees Only'),
# ('followers', 'Followers Only')]

View File

@ -5,3 +5,9 @@ access_task_type,task_type,project.model_project_task_type,portal.group_portal,1
access_task_work,task_work,project.model_project_task_work,portal.group_portal,1,0,0,0
access_project_category,project_category,project.model_project_category,portal.group_portal,1,0,0,0
access_account_analytic_account,account_analytic_account,analytic.model_account_analytic_account,portal.group_portal,1,0,0,0
access_project_anonymous,project,project.model_project_project,portal.group_anonymous,1,0,0,0
access_task_anonymous,task,project.model_project_task,portal.group_anonymous,1,0,0,0
access_task_type_anonymous,task_type,project.model_project_task_type,portal.group_anonymous,1,0,0,0
access_task_work_anonymous,task_work,project.model_project_task_work,portal.group_anonymous,1,0,0,0
access_project_category_anonymous,project_category,project.model_project_category,portal.group_anonymous,1,0,0,0
access_account_analytic_account_anonymous,account_analytic_account,analytic.model_account_analytic_account,portal.group_anonymous,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
5 access_task_work task_work project.model_project_task_work portal.group_portal 1 0 0 0
6 access_project_category project_category project.model_project_category portal.group_portal 1 0 0 0
7 access_account_analytic_account account_analytic_account analytic.model_account_analytic_account portal.group_portal 1 0 0 0
8 access_project_anonymous project project.model_project_project portal.group_anonymous 1 0 0 0
9 access_task_anonymous task project.model_project_task portal.group_anonymous 1 0 0 0
10 access_task_type_anonymous task_type project.model_project_task_type portal.group_anonymous 1 0 0 0
11 access_task_work_anonymous task_work project.model_project_task_work portal.group_anonymous 1 0 0 0
12 access_project_category_anonymous project_category project.model_project_category portal.group_anonymous 1 0 0 0
13 access_account_analytic_account_anonymous account_analytic_account analytic.model_account_analytic_account portal.group_anonymous 1 0 0 0

View File

@ -2,19 +2,68 @@
<openerp>
<data noupdate="1">
<record model="ir.rule" id="project.project_public_members_rule">
<field name="name">Project: employees: public, portal, employees or following</field>
<field name="domain_force">['|',
('privacy_visibility', 'in', ['public', 'portal', 'employees']),
'&amp;',
('privacy_visibility', '=', 'followers'),
('message_follower_ids', 'in', [user.partner_id.id]),
]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
</record>
<record id="portal_project_rule" model="ir.rule">
<field name="name">Portal Projects</field>
<field ref="project.model_project_project" name="model_id"/>
<field name="domain_force">['|', ('privacy_visibility', '=', 'public'), ('message_follower_ids', 'in', [user.partner_id.id])]</field>
<field name="name">Project: portal users: public, portal or following</field>
<field name="model_id" ref="project.model_project_project"/>
<field name="domain_force">['|',
('privacy_visibility', 'in', ['public', 'portal']),
'&amp;',
('privacy_visibility', '=', 'followers'),
('message_follower_ids', 'in', [user.partner_id.id]),
]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
<record model="ir.rule" id="project_anonymous_rule">
<field name="name">Project: anonymous users: public only</field>
<field name="model_id" ref="project.model_project_project"/>
<field name="domain_force">[('privacy_visibility', '=', 'public')]</field>
<field name="groups" eval="[(4, ref('portal.group_anonymous'))]"/>
</record>
<record model="ir.rule" id="project.task_visibility_rule">
<field name="name">Project/Task: employees: public, portal, employee or following or assigned</field>
<field name="domain_force">['|',
('user_id', '=', user.id),
'|',
('project_id.privacy_visibility', 'in', ['public', 'portal', 'employees']),
'&amp;',
('project_id.privacy_visibility', '=', 'followers'),
('message_follower_ids', 'in', [user.partner_id.id]),
]</field>
</record>
<record id="portal_task_rule" model="ir.rule">
<field name="name">Portal Tasks</field>
<field ref="project.model_project_task" name="model_id"/>
<field name="domain_force">[('message_follower_ids','in', [user.partner_id.id])]</field>
<field name="name">Project/Task: portal users: public or portal and following</field>
<field name="model_id" ref="project.model_project_task"/>
<field name="domain_force">['|',
('project_id.privacy_visibility', '=', 'public'),
'&amp;',
('project_id.privacy_visibility', 'in', ['portal', 'followers']),
'|',
('message_follower_ids','in', [user.partner_id.id]),
('user_id', '=', user.id),
]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
<record model="ir.rule" id="task_anonymous_rule">
<field name="name">Project/Task: anonymous users: public only</field>
<field name="model_id" ref="project.model_project_task"/>
<field name="domain_force">[('project_id.privacy_visibility', '=', 'public')]</field>
<field name="groups" eval="[(4, ref('portal.group_anonymous'))]"/>
</record>
</data>
</openerp>

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import test_access_rights
checks = [
test_access_rights,
]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,300 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2013-TODAY OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv.orm import except_orm
from openerp.tests import common
from openerp.tools import mute_logger
class TestPortalProject(common.TransactionCase):
def setUp(self):
super(TestPortalProject, self).setUp()
cr, uid = self.cr, self.uid
# Useful models
self.project_project = self.registry('project.project')
self.project_task = self.registry('project.task')
self.res_users = self.registry('res.users')
self.res_partner = self.registry('res.partner')
# Find Employee group
group_employee_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user')
self.group_employee_id = group_employee_ref and group_employee_ref[1] or False
# Find Project User group
group_project_user_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'project', 'group_project_user')
self.group_project_user_id = group_project_user_ref and group_project_user_ref[1] or False
# Find Project Manager group
group_project_manager_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'project', 'group_project_manager')
self.group_project_manager_id = group_project_manager_ref and group_project_manager_ref[1] or False
# Find Portal group
group_portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal')
self.group_portal_id = group_portal_ref and group_portal_ref[1] or False
# Find Anonymous group
group_anonymous_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_anonymous')
self.group_anonymous_id = group_anonymous_ref and group_anonymous_ref[1] or False
# Test users to use through the various tests
self.user_alfred_id = self.res_users.create(cr, uid, {
'name': 'Alfred EmployeeUser',
'login': 'alfred',
'alias_name': 'alfred',
'groups_id': [(6, 0, [self.group_employee_id, self.group_project_user_id])]
})
self.user_bert_id = self.res_users.create(cr, uid, {
'name': 'Bert Nobody',
'login': 'bert',
'alias_name': 'bert',
'groups_id': [(6, 0, [])]
})
self.user_chell_id = self.res_users.create(cr, uid, {
'name': 'Chell Portal',
'login': 'chell',
'alias_name': 'chell',
'groups_id': [(6, 0, [self.group_portal_id])]
})
self.user_donovan_id = self.res_users.create(cr, uid, {
'name': 'Donovan Anonymous',
'login': 'donovan',
'alias_name': 'donovan',
'groups_id': [(6, 0, [self.group_anonymous_id])]
})
self.user_ernest_id = self.res_users.create(cr, uid, {
'name': 'Ernest Manager',
'login': 'ernest',
'alias_name': 'ernest',
'groups_id': [(6, 0, [self.group_project_manager_id])]
})
# Test 'Pigs' project
self.project_pigs_id = self.project_project.create(cr, uid,
{'name': 'Pigs', 'privacy_visibility': 'public'},
{'mail_create_nolog': True})
# Various test tasks
self.task_1_id = self.project_task.create(cr, uid,
{'name': 'Test1', 'user_id': False, 'project_id': self.project_pigs_id},
{'mail_create_nolog': True})
self.task_2_id = self.project_task.create(cr, uid,
{'name': 'Test2', 'user_id': False, 'project_id': self.project_pigs_id},
{'mail_create_nolog': True})
self.task_3_id = self.project_task.create(cr, uid,
{'name': 'Test3', 'user_id': False, 'project_id': self.project_pigs_id},
{'mail_create_nolog': True})
self.task_4_id = self.project_task.create(cr, uid,
{'name': 'Test4', 'user_id': self.user_alfred_id, 'project_id': self.project_pigs_id},
{'mail_create_nolog': True})
self.task_5_id = self.project_task.create(cr, uid,
{'name': 'Test5', 'user_id': self.user_chell_id, 'project_id': self.project_pigs_id},
{'mail_create_nolog': True})
self.task_6_id = self.project_task.create(cr, uid,
{'name': 'Test6', 'user_id': self.user_donovan_id, 'project_id': self.project_pigs_id},
{'mail_create_nolog': True})
@mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
def test_00_project_access_rights(self):
""" Test basic project access rights, for project and portal_project """
cr, uid, pigs_id = self.cr, self.uid, self.project_pigs_id
# ----------------------------------------
# CASE1: public project
# ----------------------------------------
# Do: Alfred reads project -> ok (employee ok public)
self.project_project.read(cr, self.user_alfred_id, pigs_id, ['name'])
# Test: all project tasks visible
task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_1_id, self.task_2_id, self.task_3_id, self.task_4_id, self.task_5_id, self.task_6_id])
self.assertEqual(set(task_ids), test_task_ids,
'access rights: project user cannot see all tasks of a public project')
# Test: all project tasks readable
self.project_task.read(cr, self.user_alfred_id, task_ids, ['name'])
# Test: all project tasks writable
self.project_task.write(cr, self.user_alfred_id, task_ids, {'description': 'TestDescription'})
# Do: Bert reads project -> crash, no group
self.assertRaises(except_orm, self.project_project.read,
cr, self.user_bert_id, pigs_id, ['name'])
# Test: no project task visible
self.assertRaises(except_orm, self.project_task.search,
cr, self.user_bert_id, [('project_id', '=', pigs_id)])
# Test: no project task readable
self.assertRaises(except_orm, self.project_task.read,
cr, self.user_bert_id, task_ids, ['name'])
# Test: no project task writable
self.assertRaises(except_orm, self.project_task.write,
cr, self.user_bert_id, task_ids, {'description': 'TestDescription'})
# Do: Chell reads project -> ok (portal ok public)
self.project_project.read(cr, self.user_chell_id, pigs_id, ['name'])
# Test: all project tasks visible
task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)])
self.assertEqual(set(task_ids), test_task_ids,
'access rights: project user cannot see all tasks of a public project')
# Test: all project tasks readable
self.project_task.read(cr, self.user_chell_id, task_ids, ['name'])
# Test: no project task writable
self.assertRaises(except_orm, self.project_task.write,
cr, self.user_chell_id, task_ids, {'description': 'TestDescription'})
# Do: Donovan reads project -> ok (anonymous ok public)
self.project_project.read(cr, self.user_donovan_id, pigs_id, ['name'])
# Test: all project tasks visible
task_ids = self.project_task.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)])
self.assertEqual(set(task_ids), test_task_ids,
'access rights: anonymous user cannot see all tasks of a public project')
# Test: all project tasks readable
self.project_task.read(cr, self.user_donovan_id, task_ids, ['name'])
# Test: no project task writable
self.assertRaises(except_orm, self.project_task.write,
cr, self.user_donovan_id, task_ids, {'description': 'TestDescription'})
# ----------------------------------------
# CASE2: portal project
# ----------------------------------------
self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'portal'})
# Do: Alfred reads project -> ok (employee ok public)
self.project_project.read(cr, self.user_alfred_id, pigs_id, ['name'])
# Test: all project tasks visible
task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)])
self.assertEqual(set(task_ids), test_task_ids,
'access rights: project user cannot see all tasks of a portal project')
# Do: Bert reads project -> crash, no group
self.assertRaises(except_orm, self.project_project.read,
cr, self.user_bert_id, pigs_id, ['name'])
# Test: no project task searchable
self.assertRaises(except_orm, self.project_task.search,
cr, self.user_bert_id, [('project_id', '=', pigs_id)])
# Data: task follower
self.project_task.message_subscribe_users(cr, self.user_alfred_id, [self.task_1_id, self.task_3_id], [self.user_chell_id])
# Do: Chell reads project -> ok (portal ok public)
self.project_project.read(cr, self.user_chell_id, pigs_id, ['name'])
# Test: only followed project tasks visible + assigned
task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_1_id, self.task_3_id, self.task_5_id])
self.assertEqual(set(task_ids), test_task_ids,
'access rights: portal user should see the followed tasks of a portal project')
# Do: Donovan reads project -> ko (anonymous ko portal)
self.assertRaises(except_orm, self.project_project.read,
cr, self.user_donovan_id, pigs_id, ['name'])
# Test: no project task visible
task_ids = self.project_task.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)])
self.assertFalse(task_ids, 'access rights: anonymous user should not see tasks of a portal project')
# Data: task follower cleaning
self.project_task.message_unsubscribe_users(cr, self.user_alfred_id, [self.task_1_id, self.task_3_id], [self.user_chell_id])
# ----------------------------------------
# CASE3: employee project
# ----------------------------------------
self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'employees'})
# Do: Alfred reads project -> ok (employee ok employee)
self.project_project.read(cr, self.user_alfred_id, pigs_id, ['name'])
# Test: all project tasks visible
task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_1_id, self.task_2_id, self.task_3_id, self.task_4_id, self.task_5_id, self.task_6_id])
self.assertEqual(set(task_ids), test_task_ids,
'access rights: project user cannot see all tasks of an employees project')
# Do: Bert reads project -> crash, no group
self.assertRaises(except_orm, self.project_project.read,
cr, self.user_bert_id, pigs_id, ['name'])
# Do: Chell reads project -> ko (portal ko employee)
self.assertRaises(except_orm, self.project_project.read,
cr, self.user_chell_id, pigs_id, ['name'])
# Test: no project task visible + assigned
task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)])
self.assertFalse(task_ids, 'access rights: portal user should not see tasks of an employees project, even if assigned')
# Do: Donovan reads project -> ko (anonymous ko employee)
self.assertRaises(except_orm, self.project_project.read,
cr, self.user_donovan_id, pigs_id, ['name'])
# Test: no project task visible
task_ids = self.project_task.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)])
self.assertFalse(task_ids, 'access rights: anonymous user should not see tasks of an employees project')
# ----------------------------------------
# CASE4: followers project
# ----------------------------------------
self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'followers'})
# Do: Alfred reads project -> ko (employee ko followers)
self.assertRaises(except_orm, self.project_project.read,
cr, self.user_alfred_id, pigs_id, ['name'])
# Test: no project task visible
task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_4_id])
self.assertEqual(set(task_ids), test_task_ids,
'access rights: employee user should not see tasks of a not-followed followers project, only assigned')
# Do: Bert reads project -> crash, no group
self.assertRaises(except_orm, self.project_project.read,
cr, self.user_bert_id, pigs_id, ['name'])
# Do: Chell reads project -> ko (portal ko employee)
self.assertRaises(except_orm, self.project_project.read,
cr, self.user_chell_id, pigs_id, ['name'])
# Test: no project task visible
task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_5_id])
self.assertEqual(set(task_ids), test_task_ids,
'access rights: portal user should not see tasks of a not-followed followers project, only assigned')
# Do: Donovan reads project -> ko (anonymous ko employee)
self.assertRaises(except_orm, self.project_project.read,
cr, self.user_donovan_id, pigs_id, ['name'])
# Test: no project task visible
task_ids = self.project_task.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)])
self.assertFalse(task_ids, 'access rights: anonymous user should not see tasks of a followers project')
# Data: subscribe Alfred, Chell and Donovan as follower
self.project_project.message_subscribe_users(cr, uid, [pigs_id], [self.user_alfred_id, self.user_chell_id, self.user_donovan_id])
self.project_task.message_subscribe_users(cr, self.user_alfred_id, [self.task_1_id, self.task_3_id], [self.user_chell_id, self.user_alfred_id])
# Do: Alfred reads project -> ok (follower ok followers)
self.project_project.read(cr, self.user_alfred_id, pigs_id, ['name'])
# Test: followed + assigned tasks visible
task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_1_id, self.task_3_id, self.task_4_id])
self.assertEqual(set(task_ids), test_task_ids,
'access rights: employee user should not see followed + assigned tasks of a follower project')
# Do: Chell reads project -> ok (follower ok follower)
self.project_project.read(cr, self.user_chell_id, pigs_id, ['name'])
# Test: followed + assigned tasks visible
task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_1_id, self.task_3_id, self.task_5_id])
self.assertEqual(set(task_ids), test_task_ids,
'access rights: employee user should not see followed + assigned tasks of a follower project')
# Do: Donovan reads project -> ko (anonymous ko follower even if follower)
self.assertRaises(except_orm, self.project_project.read,
cr, self.user_donovan_id, pigs_id, ['name'])

View File

@ -1,3 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_issues,project_issue,project_issue.model_project_issue,portal.group_portal,1,0,0,0
access_case_section,crm_case_section,crm.model_crm_case_section,portal.group_portal,1,0,0,0
access_issues_anonymous,project_issue,project_issue.model_project_issue,portal.group_anonymous,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_issues project_issue project_issue.model_project_issue portal.group_portal 1 0 0 0
3 access_case_section crm_case_section crm.model_crm_case_section portal.group_portal 1 0 0 0
4 access_issues_anonymous project_issue project_issue.model_project_issue portal.group_anonymous 1 0 0 0

View File

@ -3,14 +3,36 @@
<data noupdate="1">
<record id="portal_issue_rule" model="ir.rule">
<field name="name">Portal Personal Issues</field>
<field ref="project_issue.model_project_issue" name="model_id"/>
<field name="domain_force">[('message_follower_ids','in', [user.partner_id.id])]</field>
<field name="name">Project/Issue: portal users: public or portal and following</field>
<field name="model_id" ref="project_issue.model_project_issue"/>
<field name="domain_force">['|',
('project_id.privacy_visibility', '=', 'public'),
'&amp;',
('project_id.privacy_visibility', 'in', ['portal', 'followers']),
'|',
('message_follower_ids','in', [user.partner_id.id]),
('user_id', '=', user.id),
]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
<field eval="1" name="perm_unlink"/>
<field eval="1" name="perm_write"/>
<field eval="1" name="perm_read"/>
<field eval="0" name="perm_create"/>
</record>
<record model="ir.rule" id="project_issue.issue_user_rule">
<field name="name">Project/Issue: employees: public, portal, employee or following or assigned</field>
<field name="domain_force">['|',
('user_id', '=', user.id),
'|',
('project_id.privacy_visibility', 'in', ['public', 'portal', 'employees']),
'&amp;',
('project_id.privacy_visibility', '=', 'followers'),
('message_follower_ids', 'in', [user.partner_id.id]),
]</field>
</record>
<record model="ir.rule" id="issue_anonymous_rule">
<field name="name">Project/Issue: anonymous users: public only</field>
<field name="model_id" ref="project_issue.model_project_issue"/>
<field name="domain_force">[('project_id.privacy_visibility', '=', 'public')]</field>
<field name="groups" eval="[(4, ref('portal.group_anonymous'))]"/>
</record>
</data>

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2013-TODAY OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import test_access_rights
checks = [
test_access_rights,
]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,185 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2013-TODAY OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.addons.portal_project.tests.test_access_rights import TestPortalProject
from openerp.osv.orm import except_orm
from openerp.tools import mute_logger
class TestPortalIssueProject(TestPortalProject):
def setUp(self):
super(TestPortalIssueProject, self).setUp()
cr, uid = self.cr, self.uid
# Useful models
self.project_issue = self.registry('project.issue')
# Various test issues
self.issue_1_id = self.project_issue.create(cr, uid,
{'name': 'Test1', 'user_id': False, 'project_id': self.project_pigs_id},
{'mail_create_nolog': True})
self.issue_2_id = self.project_issue.create(cr, uid,
{'name': 'Test2', 'user_id': False, 'project_id': self.project_pigs_id},
{'mail_create_nolog': True})
self.issue_3_id = self.project_issue.create(cr, uid,
{'name': 'Test3', 'user_id': False, 'project_id': self.project_pigs_id},
{'mail_create_nolog': True})
self.issue_4_id = self.project_issue.create(cr, uid,
{'name': 'Test4', 'user_id': self.user_alfred_id, 'project_id': self.project_pigs_id},
{'mail_create_nolog': True})
self.issue_5_id = self.project_issue.create(cr, uid,
{'name': 'Test5', 'user_id': self.user_chell_id, 'project_id': self.project_pigs_id},
{'mail_create_nolog': True})
self.issue_6_id = self.project_issue.create(cr, uid,
{'name': 'Test6', 'user_id': self.user_donovan_id, 'project_id': self.project_pigs_id},
{'mail_create_nolog': True})
@mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
def test_00_project_access_rights(self):
""" Test basic project access rights, for project and portal_project """
cr, uid, pigs_id = self.cr, self.uid, self.project_pigs_id
# ----------------------------------------
# CASE1: public project
# ----------------------------------------
# Do: Alfred reads project -> ok (employee ok public)
# Test: all project issues visible
issue_ids = self.project_issue.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)])
test_issue_ids = set([self.issue_1_id, self.issue_2_id, self.issue_3_id, self.issue_4_id, self.issue_5_id, self.issue_6_id])
self.assertEqual(set(issue_ids), test_issue_ids,
'access rights: project user cannot see all issues of a public project')
# Test: all project issues readable
self.project_issue.read(cr, self.user_alfred_id, issue_ids, ['name'])
# Test: all project issues writable
self.project_issue.write(cr, self.user_alfred_id, issue_ids, {'description': 'TestDescription'})
# Do: Bert reads project -> crash, no group
# Test: no project issue visible
self.assertRaises(except_orm, self.project_issue.search,
cr, self.user_bert_id, [('project_id', '=', pigs_id)])
# Test: no project issue readable
self.assertRaises(except_orm, self.project_issue.read,
cr, self.user_bert_id, issue_ids, ['name'])
# Test: no project issue writable
self.assertRaises(except_orm, self.project_issue.write,
cr, self.user_bert_id, issue_ids, {'description': 'TestDescription'})
# Do: Chell reads project -> ok (portal ok public)
# Test: all project issues visible
issue_ids = self.project_issue.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)])
self.assertEqual(set(issue_ids), test_issue_ids,
'access rights: project user cannot see all issues of a public project')
# Test: all project issues readable
self.project_issue.read(cr, self.user_chell_id, issue_ids, ['name'])
# Test: no project issue writable
self.assertRaises(except_orm, self.project_issue.write,
cr, self.user_chell_id, issue_ids, {'description': 'TestDescription'})
# Do: Donovan reads project -> ok (anonymous ok public)
# Test: all project issues visible
issue_ids = self.project_issue.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)])
self.assertEqual(set(issue_ids), test_issue_ids,
'access rights: project user cannot see all issues of a public project')
# ----------------------------------------
# CASE2: portal project
# ----------------------------------------
self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'portal'})
# Do: Alfred reads project -> ok (employee ok public)
# Test: all project issues visible
issue_ids = self.project_issue.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)])
self.assertEqual(set(issue_ids), test_issue_ids,
'access rights: project user cannot see all issues of a portal project')
# Do: Bert reads project -> crash, no group
# Test: no project issue searchable
self.assertRaises(except_orm, self.project_issue.search,
cr, self.user_bert_id, [('project_id', '=', pigs_id)])
# Data: issue follower
self.project_issue.message_subscribe_users(cr, self.user_alfred_id, [self.issue_1_id, self.issue_3_id], [self.user_chell_id])
# Do: Chell reads project -> ok (portal ok public)
# Test: only followed project issues visible + assigned
issue_ids = self.project_issue.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)])
test_issue_ids = set([self.issue_1_id, self.issue_3_id, self.issue_5_id])
self.assertEqual(set(issue_ids), test_issue_ids,
'access rights: portal user should see the followed issues of a portal project')
# Data: issue follower cleaning
self.project_issue.message_unsubscribe_users(cr, self.user_alfred_id, [self.issue_1_id, self.issue_3_id], [self.user_chell_id])
# ----------------------------------------
# CASE3: employee project
# ----------------------------------------
self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'employees'})
# Do: Alfred reads project -> ok (employee ok employee)
# Test: all project issues visible
issue_ids = self.project_issue.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)])
test_issue_ids = set([self.issue_1_id, self.issue_2_id, self.issue_3_id, self.issue_4_id, self.issue_5_id, self.issue_6_id])
self.assertEqual(set(issue_ids), test_issue_ids,
'access rights: project user cannot see all issues of an employees project')
# Do: Chell reads project -> ko (portal ko employee)
# Test: no project issue visible + assigned
issue_ids = self.project_issue.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)])
self.assertFalse(issue_ids, 'access rights: portal user should not see issues of an employees project, even if assigned')
# ----------------------------------------
# CASE4: followers project
# ----------------------------------------
self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'followers'})
# Do: Alfred reads project -> ko (employee ko followers)
# Test: no project issue visible
issue_ids = self.project_issue.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)])
test_issue_ids = set([self.issue_4_id])
self.assertEqual(set(issue_ids), test_issue_ids,
'access rights: employee user should not see issues of a not-followed followers project, only assigned')
# Do: Chell reads project -> ko (portal ko employee)
# Test: no project issue visible
issue_ids = self.project_issue.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)])
test_issue_ids = set([self.issue_5_id])
self.assertEqual(set(issue_ids), test_issue_ids,
'access rights: portal user should not see issues of a not-followed followers project, only assigned')
# Data: subscribe Alfred, Chell and Donovan as follower
self.project_project.message_subscribe_users(cr, uid, [pigs_id], [self.user_alfred_id, self.user_chell_id, self.user_donovan_id])
self.project_issue.message_subscribe_users(cr, self.user_alfred_id, [self.issue_1_id, self.issue_3_id], [self.user_chell_id, self.user_alfred_id])
# Do: Alfred reads project -> ok (follower ok followers)
# Test: followed + assigned issues visible
issue_ids = self.project_issue.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)])
test_issue_ids = set([self.issue_1_id, self.issue_3_id, self.issue_4_id])
self.assertEqual(set(issue_ids), test_issue_ids,
'access rights: employee user should not see followed + assigned issues of a follower project')
# Do: Chell reads project -> ok (follower ok follower)
# Test: followed + assigned issues visible
issue_ids = self.project_issue.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)])
test_issue_ids = set([self.issue_1_id, self.issue_3_id, self.issue_5_id])
self.assertEqual(set(issue_ids), test_issue_ids,
'access rights: employee user should not see followed + assigned issues of a follower project')

View File

@ -1,2 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_issues,project_phase,project_long_term.model_project_phase,portal.group_portal,1,0,0,0
access_issues_anonymous,project_phase_anonymous,project_long_term.model_project_phase,portal.group_anonymous,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_issues project_phase project_long_term.model_project_phase portal.group_portal 1 0 0 0
3 access_issues_anonymous project_phase_anonymous project_long_term.model_project_phase portal.group_anonymous 1 0 0 0

View File

@ -3,14 +3,24 @@
<data>
<record id="portal_project_long_term_rule" model="ir.rule">
<field name="name">Portal Personal Long term project</field>
<field ref="project_long_term.model_project_phase" name="model_id"/>
<field name="domain_force">['|',('project_id.message_follower_ids','in', [user.partner_id.id]),('task_ids.message_follower_ids','in', [user.partner_id.id])]</field>
<field name="name">Project/Phase: portal users: public or portal and following</field>
<field name="model_id" ref="project_long_term.model_project_phase"/>
<field name="domain_force">['|',
('project_id.privacy_visibility', '=', 'public'),
'&amp;',
('project_id.privacy_visibility', 'in', ['portal', 'followers']),
'|',
('message_follower_ids','in', [user.partner_id.id]),
('user_id', '=', user.id),
]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
<field eval="1" name="perm_unlink"/>
<field eval="1" name="perm_write"/>
<field eval="1" name="perm_read"/>
<field eval="0" name="perm_create"/>
</record>
<record model="ir.rule" id="project_phase_anonymous_rule">
<field name="name">Project/Phase: anonymous users: public only</field>
<field name="model_id" ref="project_long_term.model_project_phase"/>
<field name="domain_force">[('project_id.privacy_visibility', '=', 'public')]</field>
<field name="groups" eval="[(4, ref('portal.group_anonymous'))]"/>
</record>
</data>

View File

@ -208,6 +208,12 @@ class project(osv.osv):
"""Overriden in project_issue to offer more options"""
return [('project.task', "Tasks")]
def _get_visibility_selection(self, cr, uid, context=None):
""" Overriden in portal_project to offer more options """
return [('public', 'All Users'),
('employees', 'Employees Only'),
('followers', 'Followers Only')]
def attachment_tree_view(self, cr, uid, ids, context):
task_ids = self.pool.get('project.task').search(cr, uid, [('project_id', 'in', ids)])
domain = [
@ -229,6 +235,8 @@ class project(osv.osv):
}
# Lambda indirection method to avoid passing a copy of the overridable method when declaring the field
_alias_models = lambda self, *args, **kwargs: self._get_alias_models(*args, **kwargs)
_visibility_selection = lambda self, *args, **kwargs: self._get_visibility_selection(*args, **kwargs)
_columns = {
'complete_name': fields.function(_complete_name, string="Project Name", type='char', size=250),
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the project without removing it."),
@ -267,7 +275,7 @@ class project(osv.osv):
"with Tasks (or optionally Issues if the Issue Tracker module is installed)."),
'alias_model': fields.selection(_alias_models, "Alias Model", select=True, required=True,
help="The kind of document created when an email is received on this project's email alias"),
'privacy_visibility': fields.selection([('public','All Users'), ('followers','Followers Only')], 'Privacy / Visibility', required=True),
'privacy_visibility': fields.selection(_visibility_selection, 'Privacy / Visibility', required=True),
'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'), ('cancelled', 'Cancelled'),('pending','Pending'),('close','Closed')], 'Status', required=True,),
'doc_count':fields.function(_get_attached_docs, string="Number of documents attached", type='int')
}

View File

@ -38,39 +38,62 @@
<data noupdate="1">
<record model="ir.rule" id="project_comp_rule">
<field name="name">Project multi-company</field>
<field name="name">Project: multi-company</field>
<field name="model_id" ref="model_project_project"/>
<field name="global" eval="True"/>
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
<field name="domain_force">['|',
('company_id', '=', False),
('company_id', 'child_of', [user.company_id.id]),
]</field>
</record>
<record model="ir.rule" id="project_project_manager_rule">
<field name="name">Project: project manager: see all</field>
<field name="model_id" ref="model_project_project"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4,ref('project.group_project_manager'))]"/>
</record>
<record model="ir.rule" id="project_public_members_rule">
<field name="name">Public Members</field>
<field name="name">Project: employees: public, employees or followers</field>
<field name="model_id" ref="model_project_project"/>
<field name="global" eval="True"/>
<field name="domain_force">['|',('privacy_visibility','in',[False,'public']),('message_follower_ids','in',[user.partner_id.id])]</field>
<field name="domain_force">['|',
('privacy_visibility', 'in', ['public', 'employees']),
('message_follower_ids', 'in', [user.partner_id.id])
]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
</record>
<record model="ir.rule" id="task_comp_rule">
<field name="name" >Task multi-company</field>
<field name="name">Project/Task: multi-company</field>
<field name="model_id" ref="model_project_task"/>
<field name="global" eval="True"/>
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
<field name="domain_force">['|',
('company_id', '=', False),
('company_id', 'child_of', [user.company_id.id]),
]</field>
</record>
<record model="ir.rule" id="task_visibility_rule">
<field name="name" >Tasks According to User and Project</field>
<field name="name">Project/Task: employees: public or employee or following or assigned</field>
<field name="model_id" ref="model_project_task"/>
<field name="global" eval="True"/>
<field name="domain_force">['|','|','|',('user_id','=',False),('user_id','=',user.id),('project_id.members','in', [user.id]),('project_id.user_id','=',user.id)]</field>
<field name="groups" eval="[(4,ref('project.group_project_user'))]"/>
<field name="domain_force">['|',
('user_id', '=', user.id),
'|',
('project_id.privacy_visibility', 'in', ['public', 'employees']),
'&amp;',
('project_id.privacy_visibility', '=', 'followers'),
('message_follower_ids', 'in', [user.partner_id.id]),
]</field>
<field name="groups" eval="[(4,ref('base.group_user'))]"/>
</record>
<record model="ir.rule" id="project_manager_all_project_tasks_rule">
<field name="name">Project Managers: all tasks from all projects</field>
<field name="name">Project/Task: project manager: see all</field>
<field name="model_id" ref="model_project_task"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4,ref('project.group_project_manager'))]"/>
</record>
</data>
</openerp>

View File

@ -1,5 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record model="ir.rule" id="issue_project_manager_rule">
<field name="name">Project/Issue: project manager: see all</field>
<field name="model_id" ref="model_project_issue"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4,ref('project.group_project_manager'))]"/>
</record>
<record model="ir.rule" id="issue_user_rule">
<field name="name">Project/Issue: employees: public or employee or following or assigned</field>
<field name="model_id" ref="model_project_issue"/>
<field name="domain_force">['|',
'|',
('project_id.privacy_visibility', 'in', ['public', 'employees']),
'&amp;',
('project_id.privacy_visibility', '=', 'followers'),
('message_follower_ids', 'in', [user.id]),
('user_id', '=', user.id),
]</field>
<field name="groups" eval="[(4,ref('base.group_user'))]"/>
</record>
</data>
</openerp>