Skip to content

Commit

Permalink
Merge 7556a70 into e3251d6
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrobaeza committed Mar 5, 2016
2 parents e3251d6 + 7556a70 commit 34573e6
Show file tree
Hide file tree
Showing 12 changed files with 306 additions and 0 deletions.
84 changes: 84 additions & 0 deletions sales_team_security/README.rst
@@ -0,0 +1,84 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3

==============================
Security rules for sales teams
==============================

This module sets different permissions levels for accessing sales records
based on the sales team: customers, sales orders and sales teams.

It also handles the propagation of the sales team from commercial partners to
the contacts, which standard doesn't make.

Installation
============

At installation time, this module sets int all the contacts that have the sales
team empty the sales team of the parent. If you have a lot of contacts, this
operation can take a while.

Usage
=====

On the user configuration (Configuration > Users > Users), select in the
*Sales Team* section the option "See only own team". Then, the documents
mentioned before will be filtered out to have only those belonging to the
teams the user belongs to.

This is complementary to the Sales level access, but sometimes can be
incoherent depending on the combination chosen. If you chose "See Own Leads"
on *Sales* section, marking on unmarking the sales team check will be
irrelevant, because the most restricting level, which the sales one, will
prevail.

.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/167/8.0

Bug Tracker
===========

Bugs are tracked on `GitHub Issues
<https://github.com/OCA/sale-workflow/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed `feedback
<https://github.com/OCA/
multi-company/issues/new?body=module:%20
sale-workflow%0Aversion:%20
8.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Known issues/Roadmap
====================

* This module is designed for support only sales part, so someone that has
access to other Odoo parts (for example, an accountant), shouldn't have
this new permission, or some access errors will be found when seeing invoices
and other documents.
* An expansion module is needed for handling CRM objects (leads/opportunities),
and would be convenient to make it isolated from this one to preserve
modularity

Credits
=======

Contributors
------------

* Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>

Maintainer
----------

.. image:: http://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

This module is maintained by the OCA.

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

To contribute to this module, please visit https://odoo-community.org.
6 changes: 6 additions & 0 deletions sales_team_security/__init__.py
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# © 2016 Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from . import models
from .hooks import assign_contacts_team
22 changes: 22 additions & 0 deletions sales_team_security/__openerp__.py
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# © 2016 Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

{
"name": "Sales teams security",
"version": "8.0.1.0.0",
"license": "AGPL-3",
"depends": [
"sales_team",
"sale",
],
"author": "Serv. Tecnol. Avanzados - Pedro M. Baeza, "
"Odoo Community Association (OCA)",
"category": "Sales Management",
"installable": True,
"data": [
'security/sales_team_security.xml',
'views/res_partner_view.xml',
],
"post_init_hook": "assign_contacts_team",
}
19 changes: 19 additions & 0 deletions sales_team_security/hooks.py
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# © 2016 Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html


def assign_contacts_team(cr, registry):
"""At installation time, propagate the parent sales team to the children
contacts that have this field empty, as it's supposed that the intention
is to have the same.
"""
cr.execute(
"""
UPDATE res_partner
SET section_id=parent.section_id
FROM res_partner AS parent
WHERE parent.section_id IS NOT NULL
AND res_partner.parent_id = parent.id
AND res_partner.section_id IS NULL
""")
6 changes: 6 additions & 0 deletions sales_team_security/models/__init__.py
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# © 2016 Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from . import res_partner
from . import res_users
40 changes: 40 additions & 0 deletions sales_team_security/models/res_partner.py
@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# © 2016 Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from openerp import api, fields, models
from lxml import etree


class ResPartner(models.Model):
_inherit = 'res.partner'

@api.model
def fields_view_get(self, view_id=None, view_type='form', toolbar=False,
submenu=False):
"""Patch view to inject the default value for the section_id."""
res = super(ResPartner, self).fields_view_get(
view_id=view_id, view_type=view_type, toolbar=toolbar,
submenu=submenu)
if view_type == 'form':
eview = etree.fromstring(res['arch'])
xml_fields = eview.xpath("//field[@name='child_ids']")
if xml_fields:
context_str = xml_fields[0].get('context', '{}')
context_str = (
context_str[:1] + "'default_section_id': section_id, " +
context_str[1:])
xml_fields[0].set('context', context_str)
res['arch'] = etree.tostring(eview)
return res

@api.multi
def onchange_address(self, use_parent_address, parent_id):
res = super(ResPartner, self).onchange_address(
use_parent_address, parent_id)
if parent_id:
parent = self.browse(parent_id)
if parent.section_id:
value = res.setdefault('value', {})
value['section_id'] = parent.section_id.id
return res
13 changes: 13 additions & 0 deletions sales_team_security/models/res_users.py
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# © 2016 Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from openerp import fields, models


class ResUsers(models.Model):
_inherit = 'res.users'

section_ids = fields.Many2many(
comodel_name="crm.case.section", string="Sales teams",
relation='sale_member_rel', column1='member_id', column2='section_id')
54 changes: 54 additions & 0 deletions sales_team_security/security/sales_team_security.xml
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">

<record model="ir.module.category" id="module_category_sales_team">
<field name="name">Sales Teams</field>
<field name="sequence">2</field>
</record>

<record id="group_see_only_own_team" model="res.groups">
<field name="name">See only own team</field>
<field name="category_id" ref="sales_team_security.module_category_sales_team"/>
</record>

</data>
<data noupdate="1">

<record id="sale_order_team_rule" model="ir.rule">
<field name="name">Sales Team Orders</field>
<field ref="sale.model_sale_order" name="model_id"/>
<field name="domain_force">['|', ('section_id', 'in', user.section_ids.ids), ('section_id', '=', False)]</field>
<field name="groups" eval="[(4, ref('sales_team_security.group_see_only_own_team'))]"/>
</record>

<record id="sale_order_report_team_rule" model="ir.rule">
<field name="name">Sales Team Orders Analysis</field>
<field ref="sale.model_sale_report" name="model_id"/>
<field name="domain_force">['|', ('section_id', 'in', user.section_ids.ids), ('section_id', '=', False)]</field>
<field name="groups" eval="[(4, ref('sales_team_security.group_see_only_own_team'))]"/>
</record>

<record id="sale_order_line_team_rule" model="ir.rule">
<field name="name">Sales Team Order Lines</field>
<field ref="sale.model_sale_order_line" name="model_id"/>
<field name="domain_force">['|', ('section_id', 'in', user.section_ids.ids), ('section_id', '=', False)]</field>
<field name="groups" eval="[(4, ref('sales_team_security.group_see_only_own_team'))]"/>
</record>

<record id="sales_team_team_rule" model="ir.rule">
<field name="name">Own Sales Teams</field>
<field ref="sales_team.model_crm_case_section" name="model_id"/>
<field name="domain_force">[('id', 'in', user.section_ids.ids)]</field>
<field name="groups" eval="[(4, ref('sales_team_security.group_see_only_own_team'))]"/>
</record>

<record id="res_partner_team_rule" model="ir.rule">
<field name="name">Sales Team Partners</field>
<field ref="base.model_res_partner" name="model_id"/>
<field name="domain_force">['|', ('section_id', 'in', user.section_ids.ids), ('section_id', '=', False)]</field>
<field name="groups" eval="[(4, ref('sales_team_security.group_see_only_own_team'))]"/>
</record>

</data>
</openerp>
Binary file added sales_team_security/static/description/icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions sales_team_security/tests/__init__.py
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2016 Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from . import test_sales_team_security
42 changes: 42 additions & 0 deletions sales_team_security/tests/test_sales_team_security.py
@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# © 2016 Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from openerp.tests import common
from ..hooks import assign_contacts_team
from lxml import etree


class TestSalesTeamSecurity(common.TransactionCase):
def setUp(self):
super(TestSalesTeamSecurity, self).setUp()
self.section = self.env['crm.case.section'].create({
'name': 'Test section',
})
self.partner = self.env['res.partner'].create({
'name': 'Test partner',
'section_id': self.section.id,
})

def test_onchange_parent_id(self):
res = self.env['res.partner'].onchange_address(True, self.partner.id)
self.assertEqual(res['value']['section_id'], self.section.id)

def test_assign_contacts_team(self):
contact = self.env['res.partner'].create({
'name': 'Test contact',
'parent_id': self.partner.id,
'section_id': False,
})
assign_contacts_team(self.env.cr, self.env.registry)
contact.refresh()
self.assertEqual(contact.section_id, self.partner.section_id)

def test_partner_fields_view_get(self):
res = self.env['res.partner'].fields_view_get(
view_id=self.ref('base.view_partner_form'))
eview = etree.fromstring(res['arch'])
xml_fields = eview.xpath("//field[@name='child_ids']")
self.assertTrue(xml_fields)
self.assertTrue(
'default_section_id' in xml_fields[0].get('context', ''))
15 changes: 15 additions & 0 deletions sales_team_security/views/res_partner_view.xml
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_partner_form" model="ir.ui.view">
<field name="name">Partner form (with sales team in contacts)</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='child_ids']/form//field[@name='function']" position="before">
<field name="section_id" invisible="1"/>
</xpath>
</field>
</record>
</data>
</openerp>

0 comments on commit 34573e6

Please sign in to comment.