Browse Source

Merge pull request #15 from hbrunn/7.0-partner_relations

[ADD] partner_relations
pull/62/head
Pedro M. Baeza 10 years ago
parent
commit
63836aba22
  1. 21
      partner_relations/__init__.py
  2. 97
      partner_relations/__openerp__.py
  3. 38
      partner_relations/data/demo.xml
  4. 268
      partner_relations/i18n/nl.po
  5. 277
      partner_relations/i18n/partner_relations.pot
  6. 25
      partner_relations/model/__init__.py
  7. 246
      partner_relations/model/res_partner.py
  8. 330
      partner_relations/model/res_partner_relation.py
  9. 129
      partner_relations/model/res_partner_relation_all.py
  10. 48
      partner_relations/model/res_partner_relation_type.py
  11. 168
      partner_relations/model/res_partner_relation_type_selection.py
  12. 7
      partner_relations/security/ir.model.access.csv
  13. BIN
      partner_relations/static/src/img/icon.png
  14. 15
      partner_relations/view/menu.xml
  15. 96
      partner_relations/view/res_partner.xml
  16. 33
      partner_relations/view/res_partner_relation.xml
  17. 34
      partner_relations/view/res_partner_relation_all.xml
  18. 44
      partner_relations/view/res_partner_relation_type.xml

21
partner_relations/__init__.py

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Therp BV (<http://therp.nl>).
#
# 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 model

97
partner_relations/__openerp__.py

@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Therp BV (<http://therp.nl>).
#
# 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/>.
#
##############################################################################
{
"name": "Partner relations",
"version": "1.1",
"author": "Therp BV",
"complexity": "normal",
"description": """
Introduction
------------
This addon aims to provide generic means to model relations between partners.
Examples would be 'is sibling of' or 'is friend of', but also 'has contract X
with' or 'is assistant of'. This way, you can enode your knowledge about your
partners directly in your partner list.
Usage
-----
Before being able to use relations, you'll have define some first. Do that in
Sales / Configuration / Address Book / Partner relations. Here, you need to
name both sides of the relation: To have an assistant-relation, you would name
one side 'is assistant of' and the other side 'has assistant'. This relation
only makes sense between people, so you would choose 'Person' for both partner
types. For the relation 'is a competitor of', both sides would be companies,
while the relation 'has worked for' should have persons on the left side and
companies on the right side. If you leave this field empty, the relation is
applicable to all types of partners.
If you use categories to further specify the type of partners, you could for
example enforce that the 'is member of' relation can only have companies with
label 'Organization' on the left side.
Now open a partner and choose relations as appropriate in the 'Relations' tab.
Searching partners with relations
---------------------------------
Searching for relations is integrated transparently into the partner search
form. To find all assistants in your database, fill in 'is assistant of' and
autocomplete will propose to search for partners having this relation. Now if
you want to find Anna's assistant, you fill in 'Anna' and one of the proposals
is to search for partners having a relation with Anna. This results in Anna's
assistant(s), as you searched for assistants before.
By default, only active, not expired relations are shown. If you need to find
partners that had some relation at a certain date, fill in that date in the
search box and one of the proposals is to search for relations valid at that
date.""",
"category": "Customer Relationship Management",
"depends": [
'base',
'web_m2x_options',
'web_tree_many2one_clickable',
],
"demo": [
"data/demo.xml",
],
"data": [
"view/res_partner_relation_all.xml",
'view/res_partner.xml',
'view/res_partner_relation.xml',
'view/res_partner_relation_type.xml',
'view/menu.xml',
'security/ir.model.access.csv',
],
"js": [
],
"css": [
],
"qweb": [
],
"auto_install": False,
"installable": True,
"external_dependencies": {
'python': [],
},
}

38
partner_relations/data/demo.xml

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record id="rel_type_assistant" model="res.partner.relation.type">
<field name="name">Is assistant of</field>
<field name="name_inverse">Has assistant</field>
<field name="contact_type_left">p</field>
<field name="contact_type_right">p</field>
</record>
<record id="rel_type_competitor" model="res.partner.relation.type">
<field name="name">Is competitor of</field>
<field name="name_inverse">Is competitor of</field>
<field name="contact_type_left">c</field>
<field name="contact_type_right">c</field>
</record>
<record id="rel_type_has_worked_for" model="res.partner.relation.type">
<field name="name">Has worked for</field>
<field name="name_inverse">Has former employee</field>
<field name="contact_type_left">p</field>
<field name="contact_type_right">c</field>
</record>
<record id="rel_1" model="res.partner.relation">
<field name="left_partner_id" ref="base.res_partner_12" />
<field name="right_partner_id" ref="base.res_partner_23" />
<field name="type_id" ref="rel_type_competitor" />
</record>
<record id="rel_2" model="res.partner.relation">
<field name="left_partner_id" ref="base.res_partner_13" />
<field name="right_partner_id" ref="base.res_partner_23" />
<field name="type_id" ref="rel_type_competitor" />
</record>
<record id="rel_3" model="res.partner.relation">
<field name="left_partner_id" ref="base.res_partner_12" />
<field name="right_partner_id" ref="base.res_partner_13" />
<field name="type_id" ref="rel_type_competitor" />
</record>
</data>
</openerp>

268
partner_relations/i18n/nl.po

@ -0,0 +1,268 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * partner_relations
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-06-19 13:02+0000\n"
"PO-Revision-Date: 2014-06-19 13:02+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: partner_relations
#: field:res.partner.relation,active:0
#: field:res.partner.relation.all,active:0
msgid "Active"
msgstr "Actief"
#. module: partner_relations
#: model:ir.model,name:partner_relations.model_res_partner_relation_all
msgid "All (non-inverse + inverse) relations between partners"
msgstr "Alle (non-inverse + inverse) koppelingen tussen relaties"
#. module: partner_relations
#: model:ir.model,name:partner_relations.model_res_partner_relation_type_selection
msgid "All relation types"
msgstr "Alle relaties"
#. module: partner_relations
#: field:res.partner,relation_all_ids:0
msgid "All relations with current partner"
msgstr "Alle koppelingen met huidige relatie"
#. module: partner_relations
#: field:res.partner.relation.all,this_partner_id:0
msgid "Current partner"
msgstr "Huidige relatie"
#. module: partner_relations
#: field:res.partner.relation.type.selection,partner_category_this:0
#: field:res.partner.relation.type.selection,search_partner_category_this:0
msgid "Current record's category"
msgstr "Categorie van huidige record"
#. module: partner_relations
#: field:res.partner.relation.type.selection,contact_type_this:0
msgid "Current record's partner type"
msgstr "Relatietype van huidige record"
#. module: partner_relations
#: field:res.partner.relation,date_end:0
#: field:res.partner.relation.all,date_end:0
msgid "Ending date"
msgstr "Einddatum"
#. module: partner_relations
#: field:res.partner,search_relation_id:0
msgid "Has relation of type"
msgstr "Heeft koppeling"
#. module: partner_relations
#: field:res.partner,search_relation_partner_id:0
msgid "Has relation with"
msgstr "Heeft koppeling met"
#. module: partner_relations
#: field:res.partner.relation.type,name_inverse:0
msgid "Inverse name"
msgstr "Inverse naam"
#. module: partner_relations
#: selection:res.partner.relation.all,record_type:0
#: selection:res.partner.relation.type.selection,record_type:0
msgid "Inverse type"
msgstr "Inverse type"
#. module: partner_relations
#: field:res.partner.relation,left_partner_id:0
msgid "Left partner"
msgstr "Linker relatie"
#. module: partner_relations
#: field:res.partner.relation.type,partner_category_left:0
msgid "Left partner category"
msgstr "Linker relatielabel"
#. module: partner_relations
#: field:res.partner.relation.type,contact_type_left:0
msgid "Left partner type"
msgstr "Linker relatietype"
#. module: partner_relations
#: view:res.partner.relation.type:0
msgid "Left side of relation"
msgstr "Linkerkant van de koppeling"
#. module: partner_relations
#: field:res.partner.relation.type,name:0
#: field:res.partner.relation.type.selection,name:0
msgid "Name"
msgstr "Naam"
#. module: partner_relations
#: field:res.partner.relation.all,other_partner_id:0
msgid "Other partner"
msgstr "Andere relatie"
#. module: partner_relations
#: field:res.partner.relation.type.selection,partner_category_other:0
msgid "Other record's category"
msgstr "Categorie andere record"
#. module: partner_relations
#: field:res.partner.relation.type.selection,contact_type_other:0
msgid "Other record's partner type"
msgstr "Type andere record"
#. module: partner_relations
#: model:ir.model,name:partner_relations.model_res_partner_relation_type
#: model:ir.model,name:partner_relations.model_res_partner_relation_type_inverse
msgid "Parter relation type"
msgstr "Type relatiekoppeling"
#. module: partner_relations
#: model:ir.model,name:partner_relations.model_res_partner
#: field:res.partner.relation,partner_id_display:0
msgid "Partner"
msgstr "Relatie"
#. module: partner_relations
#: model:ir.model,name:partner_relations.model_res_partner_category
msgid "Partner Categories"
msgstr "Relatie Categorieën"
#. module: partner_relations
#: model:ir.model,name:partner_relations.model_res_partner_relation
#: view:res.partner.relation:0
#: view:res.partner.relation.type:0
msgid "Partner relation"
msgstr "Relatiekoppeling"
#. module: partner_relations
#: model:ir.actions.act_window,name:partner_relations.action_res_partner_relation_type
#: model:ir.ui.menu,name:partner_relations.menu_res_partner_relation_type
msgid "Partner relations"
msgstr "Relatiekoppelingen"
#. module: partner_relations
#: constraint:res.partner.relation:0
msgid "Partners cannot have a relation with themselves."
msgstr "Relaties kunnen niet aan zichzelf gekoppeld worden."
#. module: partner_relations
#: field:res.partner.relation.all,record_type:0
#: field:res.partner.relation.type.selection,record_type:0
msgid "Record type"
msgstr "Record type"
#. module: partner_relations
#: field:res.partner.relation.all,relation_id:0
msgid "Relation"
msgstr "Koppeling"
#. module: partner_relations
#: field:res.partner.relation,is_relation_expired:0
msgid "Relation is expired"
msgstr "Koppeling is afgelopen"
#. module: partner_relations
#: field:res.partner.relation,is_relation_future:0
msgid "Relation is in the future"
msgstr "Koppeling is in de toekomst"
#. module: partner_relations
#: field:res.partner.relation.all,type_id:0
msgid "Relation type"
msgstr "Koppelingstype"
#. module: partner_relations
#: field:res.partner,search_relation_date:0
msgid "Relation valid"
msgstr "Datum koppeling"
#. module: partner_relations
#: view:res.partner:0
#: field:res.partner,relation_ids:0
msgid "Relations"
msgstr "Koppelingen"
#. module: partner_relations
#: field:res.partner.relation,right_partner_id:0
msgid "Right partner"
msgstr "Rechter relatie"
#. module: partner_relations
#: field:res.partner.relation.type,partner_category_right:0
msgid "Right partner category"
msgstr "Rechter relatielabel"
#. module: partner_relations
#: field:res.partner.relation.type,contact_type_right:0
msgid "Right partner type"
msgstr "Rechter relatietype"
#. module: partner_relations
#: view:res.partner.relation.type:0
msgid "Right side of relation"
msgstr "Rechterkant van de koppeling"
#. module: partner_relations
#: field:res.partner.relation,date_start:0
#: field:res.partner.relation.all,date_start:0
msgid "Starting date"
msgstr "Begindatum"
#. module: partner_relations
#: constraint:res.partner.relation:0
msgid "The left partner is not applicable for this relation type."
msgstr "De linker relatie is niet geldig voor dit type koppeling."
#. module: partner_relations
#: constraint:res.partner.relation:0
msgid "The right partner is not applicable for this relation type."
msgstr "De rechter relatie is niet geldig voor dit type koppeling."
#. module: partner_relations
#: constraint:res.partner.relation:0
msgid "The starting date cannot be after the ending date."
msgstr "De begindatum mag niet na de einddatum liggen."
#. module: partner_relations
#: field:res.partner.relation,type_id:0
#: field:res.partner.relation,type_selection_id:0
#: selection:res.partner.relation.all,record_type:0
#: selection:res.partner.relation.type.selection,record_type:0
#: field:res.partner.relation.type.selection,type_id:0
msgid "Type"
msgstr "Type"
#. module: partner_relations
#: field:res.partner.category,only_for_organisation:0
msgid "Valid for organisation only"
msgstr "Alleen geldig voor organisaties"
#. module: partner_relations
#: field:res.partner.category,only_for_person:0
msgid "Valid for person only"
msgstr "Alleen geldig voor personen"
#. module: partner_relations
#: field:res.partner,search_relation_partner_category_id:0
msgid "Has relation with a partner in category"
msgstr "Heeft koppeling met relatie van de categorie"
#. module: partner_relations
#: model:ir.actions.act_window,name:partner_relations.action_show_partner_relations
msgid "Show partner's relations"
msgstr "Toon relatiekoppelingen"
#. module: partner_relations
#: constraint:res.partner.relation:0
msgid "The same relation can't be created twice."
msgstr "Dezelfde koppeling kan niet dubbel aan worden gemaakt."

277
partner_relations/i18n/partner_relations.pot

@ -0,0 +1,277 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * partner_relations
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-07-01 08:52+0000\n"
"PO-Revision-Date: 2014-07-01 08:52+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: partner_relations
#: field:res.partner.relation,active:0
#: field:res.partner.relation.all,active:0
msgid "Active"
msgstr ""
#. module: partner_relations
#: model:ir.model,name:partner_relations.model_res_partner_relation_all
msgid "All (non-inverse + inverse) relations between partners"
msgstr ""
#. module: partner_relations
#: model:ir.model,name:partner_relations.model_res_partner_relation_type_selection
msgid "All relation types"
msgstr ""
#. module: partner_relations
#: field:res.partner,relation_all_ids:0
msgid "All relations with current partner"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.all,this_partner_id:0
msgid "Current partner"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.type.selection,partner_category_this:0
#: field:res.partner.relation.type.selection,search_partner_category_this:0
msgid "Current record's category"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.type.selection,contact_type_this:0
msgid "Current record's partner type"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation,date_end:0
#: field:res.partner.relation.all,date_end:0
msgid "Ending date"
msgstr ""
#. module: partner_relations
#: field:res.partner,search_relation_id:0
msgid "Has relation of type"
msgstr ""
#. module: partner_relations
#: field:res.partner,search_relation_partner_id:0
msgid "Has relation with"
msgstr ""
#. module: partner_relations
#: field:res.partner,search_relation_partner_category_id:0
msgid "Has relation with a partner in category"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.type,name_inverse:0
msgid "Inverse name"
msgstr ""
#. module: partner_relations
#: selection:res.partner.relation.all,record_type:0
#: selection:res.partner.relation.type.selection,record_type:0
msgid "Inverse type"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation,left_partner_id:0
msgid "Left partner"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.type,partner_category_left:0
msgid "Left partner category"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.type,contact_type_left:0
msgid "Left partner type"
msgstr ""
#. module: partner_relations
#: view:res.partner.relation.type:0
msgid "Left side of relation"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.type,name:0
#: field:res.partner.relation.type.selection,name:0
msgid "Name"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.all,other_partner_id:0
msgid "Other partner"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.type.selection,partner_category_other:0
msgid "Other record's category"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.type.selection,contact_type_other:0
msgid "Other record's partner type"
msgstr ""
#. module: partner_relations
#: model:ir.model,name:partner_relations.model_res_partner_relation_type
#: model:ir.model,name:partner_relations.model_res_partner_relation_type_inverse
msgid "Parter relation type"
msgstr ""
#. module: partner_relations
#: model:ir.model,name:partner_relations.model_res_partner
#: field:res.partner.relation,partner_id_display:0
msgid "Partner"
msgstr ""
#. module: partner_relations
#: model:ir.model,name:partner_relations.model_res_partner_category
msgid "Partner Categories"
msgstr ""
#. module: partner_relations
#: model:ir.model,name:partner_relations.model_res_partner_relation
#: view:res.partner.relation:0
#: view:res.partner.relation.all:0
#: view:res.partner.relation.type:0
msgid "Partner relation"
msgstr ""
#. module: partner_relations
#: model:ir.actions.act_window,name:partner_relations.action_res_partner_relation_type
#: model:ir.ui.menu,name:partner_relations.menu_res_partner_relation_type
#: view:res.partner.relation.all:0
msgid "Partner relations"
msgstr ""
#. module: partner_relations
#: constraint:res.partner.relation:0
msgid "Partners cannot have a relation with themselves."
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.all,record_type:0
#: field:res.partner.relation.type.selection,record_type:0
msgid "Record type"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.all,relation_id:0
msgid "Relation"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation,is_relation_expired:0
msgid "Relation is expired"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation,is_relation_future:0
msgid "Relation is in the future"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.all,type_id:0
#: field:res.partner.relation.all,type_selection_id:0
msgid "Relation type"
msgstr ""
#. module: partner_relations
#: field:res.partner,search_relation_date:0
msgid "Relation valid"
msgstr ""
#. module: partner_relations
#: view:res.partner:0
#: field:res.partner,relation_ids:0
msgid "Relations"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation,right_partner_id:0
msgid "Right partner"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.type,partner_category_right:0
msgid "Right partner category"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation.type,contact_type_right:0
msgid "Right partner type"
msgstr ""
#. module: partner_relations
#: view:res.partner.relation.type:0
msgid "Right side of relation"
msgstr ""
#. module: partner_relations
#: model:ir.actions.act_window,name:partner_relations.action_show_partner_relations
msgid "Show partner's relations"
msgstr ""
#. module: partner_relations
#: field:res.partner.relation,date_start:0
#: field:res.partner.relation.all,date_start:0
msgid "Starting date"
msgstr ""
#. module: partner_relations
#: constraint:res.partner.relation:0
msgid "The left partner is not applicable for this relation type."
msgstr ""
#. module: partner_relations
#: constraint:res.partner.relation:0
msgid "The right partner is not applicable for this relation type."
msgstr ""
#. module: partner_relations
#: sql_constraint:res.partner.relation:0
msgid "The same relation can't be created twice."
msgstr ""
#. module: partner_relations
#: constraint:res.partner.relation:0
msgid "The starting date cannot be after the ending date."
msgstr ""
#. module: partner_relations
#: field:res.partner.relation,type_id:0
#: field:res.partner.relation,type_selection_id:0
#: selection:res.partner.relation.all,record_type:0
#: selection:res.partner.relation.type.selection,record_type:0
#: field:res.partner.relation.type.selection,type_id:0
msgid "Type"
msgstr ""
#. module: partner_relations
#: field:res.partner.category,only_for_organisation:0
msgid "Valid for organisation only"
msgstr ""
#. module: partner_relations
#: field:res.partner.category,only_for_person:0
msgid "Valid for person only"
msgstr ""
#. module: partner_relations
#: model:ir.model,name:partner_relations.model_ir_translation
msgid "ir.translation"
msgstr ""

25
partner_relations/model/__init__.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Therp BV (<http://therp.nl>).
#
# 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 res_partner
from . import res_partner_relation
from . import res_partner_relation_type
from . import res_partner_relation_type_selection
from . import res_partner_relation_all

246
partner_relations/model/res_partner.py

@ -0,0 +1,246 @@
# -*- coding: utf-8 -*-
'''Extend res.partner model'''
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Therp BV (<http://therp.nl>).
#
# 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/>.
#
##############################################################################
import time
from openerp.osv import orm, fields
from openerp.osv.expression import is_leaf
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
class ResPartner(orm.Model):
_inherit = 'res.partner'
def _get_relation_ids_select(self, cr, uid, ids, field_name, arg,
context=None):
'''return the partners' relations as tuple
(id, left_partner_id, right_partner_id)'''
cr.execute(
'''select id, left_partner_id, right_partner_id
from res_partner_relation
where (left_partner_id in %s or right_partner_id in %s)''' +
' order by ' + self.pool['res.partner.relation']._order,
(tuple(ids), tuple(ids))
)
return cr.fetchall()
def _get_relation_ids(
self, cr, uid, ids, field_name, arg, context=None):
'''getter for relation_ids'''
if context is None:
context = {}
result = dict([(i, []) for i in ids])
# TODO: do a permission test on returned ids
for row in self._get_relation_ids_select(
cr, uid, ids, field_name, arg, context=context):
if row[1] in result:
result[row[1]].append(row[0])
if row[2] in result:
result[row[2]].append(row[0])
return result
def _set_relation_ids(
self, cr, uid, ids, dummy_name, field_value, dummy_arg,
context=None):
'''setter for relation_ids'''
if context is None:
context = {}
relation_obj = self.pool.get('res.partner.relation')
context2 = self._update_context(context, ids)
for value in field_value:
if value[0] == 0:
relation_obj.create(cr, uid, value[2], context=context2)
if value[0] == 1:
relation_obj.write(
cr, uid, value[1], value[2], context=context2)
if value[0] == 2:
relation_obj.unlink(cr, uid, value[1], context=context2)
def _search_relation_id(
self, cr, uid, dummy_obj, name, args, context=None):
result = []
for arg in args:
if isinstance(arg, tuple) and arg[0] == name:
if arg[1] != '=':
continue
type_id, is_inverse = self\
.pool['res.partner.relation.type.selection']\
.get_type_from_selection_id(cr, uid, arg[2])
result.extend([
'&',
('relation_all_ids.type_id', '=', type_id),
('relation_all_ids.record_type', '=',
'b' if is_inverse else 'a')
])
return result
def _search_relation_date(self, cr, uid, obj, name, args, context=None):
result = []
for arg in args:
if isinstance(arg, tuple) and arg[0] == name:
# TODO: handle {<,>}{,=}
if arg[1] != '=':
continue
result.extend([
'&',
'|',
('relation_all_ids.date_start', '=', False),
('relation_all_ids.date_start', '<=', arg[2]),
'|',
('relation_all_ids.date_end', '=', False),
('relation_all_ids.date_end', '>=', arg[2]),
])
return result
def _search_related_partner_id(
self, cr, uid, dummy_obj, name, args, context=None):
result = []
for arg in args:
if isinstance(arg, tuple) and arg[0] == name:
result.append(
(
'relation_all_ids.other_partner_id',
arg[1],
arg[2],
))
return result
def _search_related_partner_category_id(
self, cr, uid, dummy_obj, name, args, context=None):
result = []
for arg in args:
if isinstance(arg, tuple) and arg[0] == name:
result.append(
(
'relation_all_ids.other_partner_id.category_id',
arg[1],
arg[2],
))
return result
_columns = {
'relation_ids': fields.function(
lambda self, *args, **kwargs: self._get_relation_ids(
*args, **kwargs),
fnct_inv=_set_relation_ids,
type='one2many', obj='res.partner.relation',
string='Relations',
selectable=False,
),
'relation_all_ids': fields.one2many(
'res.partner.relation.all', 'this_partner_id',
string='All relations with current partner',
auto_join=True,
selectable=False,
),
'search_relation_id': fields.function(
lambda self, cr, uid, ids, *args: dict([
(i, False) for i in ids]),
fnct_search=_search_relation_id,
string='Has relation of type',
type='many2one', obj='res.partner.relation.type.selection'
),
'search_relation_partner_id': fields.function(
lambda self, cr, uid, ids, *args: dict([
(i, False) for i in ids]),
fnct_search=_search_related_partner_id,
string='Has relation with',
type='many2one', obj='res.partner'
),
'search_relation_date': fields.function(
lambda self, cr, uid, ids, *args: dict([
(i, False) for i in ids]),
fnct_search=_search_relation_date,
string='Relation valid', type='date'
),
'search_relation_partner_category_id': fields.function(
lambda self, cr, uid, ids, *args: dict([
(i, False) for i in ids]),
fnct_search=_search_related_partner_category_id,
string='Has relation with a partner in category',
type='many2one', obj='res.partner.category'
),
}
def copy_data(self, cr, uid, id, default=None, context=None):
if default is None:
default = {}
default.setdefault('relation_ids', [])
default.setdefault('relation_all_ids', [])
return super(ResPartner, self).copy_data(cr, uid, id, default=default,
context=context)
def search(self, cr, uid, args, offset=0, limit=None, order=None,
context=None, count=False):
if context is None:
context = {}
# inject searching for current relation date if we search for relation
# properties and no explicit date was given
date_args = []
for arg in args:
if is_leaf(arg) and arg[0].startswith('search_relation'):
if arg[0] == 'search_relation_date':
date_args = []
break
if not date_args:
date_args = [
('search_relation_date', '=', time.strftime(
DEFAULT_SERVER_DATE_FORMAT))]
# because of auto_join, we have to do the active test by hand
active_args = []
if context.get('active_test', True):
for arg in args:
if is_leaf(arg) and\
arg[0].startswith('search_relation'):
active_args = [('relation_all_ids.active', '=', True)]
break
return super(ResPartner, self).search(
cr, uid, args + date_args + active_args, offset=offset,
limit=limit, order=order, context=context, count=count)
def read(
self, cr, uid, ids, fields=None, context=None,
load='_classic_read'):
return super(ResPartner, self).read(
cr, uid, ids, fields=fields,
context=self._update_context(context, ids))
def write(self, cr, uid, ids, vals, context=None):
return super(ResPartner, self).write(
cr, uid, ids, vals, context=self._update_context(context, ids))
def _update_context(self, context, ids):
if context is None:
context = {}
ids = ids if isinstance(ids, list) else [ids] if ids else []
result = context.copy()
result.setdefault('active_id', ids[0] if ids else None)
result.setdefault('active_ids', ids)
result.setdefault('active_model', self._name)
return result

330
partner_relations/model/res_partner_relation.py

@ -0,0 +1,330 @@
# -*- coding: utf-8 -*-
'''Define model res.partner.relation'''
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Therp BV (<http://therp.nl>).
#
# 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 Model
from openerp.osv import fields
from openerp.tools.translate import _
class ResPartnerRelation(Model):
'''Model res.partner.relation is used to describe all links or relations
between partners in the database.
In many parts of the code we have to know whether the active partner is
the left partner, or the right partner. If the active partner is the
right partner we have to show the inverse name.
Because the active partner is crucial for the working of partner
relationships, we make sure on the res.partner model that the partner id
is set in the context where needed.
'''
_name = 'res.partner.relation'
_description = 'Partner relation'
_order = 'active desc, date_start desc, date_end desc'
def _on_right_partner(self, cr, uid, right_partner_id, context=None):
'''Determine wether functions are called in a situation where the
active partner is the right partner. Default False!
'''
if (context and 'active_ids' in context
and right_partner_id in context.get('active_ids', [])):
return True
return False
def _correct_vals(self, cr, uid, vals, context=None):
'''Fill type and left and right partner id, according to wether
we have a normal relation type or an inverse relation type'''
vals = vals.copy()
# If type_selection_id ends in 1, it is a reverse relation type
if 'type_selection_id' in vals:
prts_model = self.pool['res.partner.relation.type.selection']
type_selection_id = vals['type_selection_id']
(type_id, is_reverse) = (
prts_model.get_type_from_selection_id(
cr, uid, type_selection_id))
vals['type_id'] = type_id
if context.get('active_id'):
if is_reverse:
vals['right_partner_id'] = context['active_id']
else:
vals['left_partner_id'] = context['active_id']
if vals.get('partner_id_display'):
if is_reverse:
vals['left_partner_id'] = vals['partner_id_display']
else:
vals['right_partner_id'] = vals['partner_id_display']
return vals
def _get_computed_fields(
self, cr, uid, ids, field_names, arg, context=None):
'''Return a dictionary of dictionaries, with for every partner for
ids, the computed values.'''
def get_values(this, dummy_field_names, dummy_arg, context=None):
'''Get computed values for record'''
values = {}
on_right_partner = self._on_right_partner(
cr, uid, this.right_partner_id.id, context=context)
# type_selection_id
values['type_selection_id'] = (
((this.type_id.id) * 10) + (on_right_partner and 1 or 0))
# partner_id_display
values['partner_id_display'] = (
on_right_partner and this.left_partner_id.id
or this.right_partner_id.id
)
# is_relation_expired
today = fields.date.context_today(self, cr, uid, context=context)
values['is_relation_expired'] = (
this.date_end and (this.date_end < today))
# is_relation_future
values['is_relation_future'] = this.date_start > today
return values
return dict([
(this.id, get_values(this, field_names, arg, context=context))
for this in self.browse(cr, uid, ids, context=context)
])
def write(self, cr, uid, ids, vals, context=None):
'''Override write to correct values, before being stored.'''
vals = self._correct_vals(cr, uid, vals, context=context)
return super(ResPartnerRelation, self).write(
cr, uid, ids, vals, context=context)
def create(self, cr, uid, vals, context=None):
'''Override create to correct values, before being stored.'''
vals = self._correct_vals(cr, uid, vals, context=context)
return super(ResPartnerRelation, self).create(
cr, uid, vals, context=context)
def on_change_type_selection_id(
self, cr, uid, dummy_ids, type_selection_id, context=None):
'''Set domain on partner_id_display, when selection a relation type'''
result = {
'domain': {'partner_id_display': []},
'value': {'type_id': False}
}
if not type_selection_id:
return result
prts_model = self.pool['res.partner.relation.type.selection']
type_model = self.pool['res.partner.relation.type']
(type_id, is_reverse) = (
prts_model.get_type_from_selection_id(
cr, uid, type_selection_id)
)
result['value']['type_id'] = type_id
type_obj = type_model.browse(cr, uid, type_id, context=context)
partner_domain = []
check_contact_type = type_obj.contact_type_right
check_partner_category = (
type_obj.partner_category_right and
type_obj.partner_category_right.id
)
if is_reverse:
# partner_id_display is left partner
check_contact_type = type_obj.contact_type_left
check_partner_category = (
type_obj.partner_category_left and
type_obj.partner_category_left.id
)
if check_contact_type == 'c':
partner_domain.append(('is_company', '=', True))
if check_contact_type == 'p':
partner_domain.append(('is_company', '=', False))
if check_partner_category:
partner_domain.append(
('category_id', 'child_of', check_partner_category))
result['domain']['partner_id_display'] = partner_domain
return result
_columns = {
'left_partner_id': fields.many2one(
'res.partner', string='Left partner', required=True,
auto_join=True, ondelete='cascade'),
'right_partner_id': fields.many2one(
'res.partner', string='Right partner', required=True,
auto_join=True, ondelete='cascade'),
'type_id': fields.many2one(
'res.partner.relation.type', string='Type', required=True,
auto_join=True),
'date_start': fields.date('Starting date'),
'date_end': fields.date('Ending date'),
'type_selection_id': fields.function(
_get_computed_fields,
multi="computed_fields",
fnct_inv=lambda *args: None,
type='many2one', obj='res.partner.relation.type.selection',
string='Type',
),
'partner_id_display': fields.function(
_get_computed_fields,
multi="computed_fields",
fnct_inv=lambda *args: None,
type='many2one', obj='res.partner',
string='Partner'
),
'is_relation_expired': fields.function(
_get_computed_fields,
multi="computed_fields",
type='boolean',
method=True,
string='Relation is expired',
),
'is_relation_future': fields.function(
_get_computed_fields,
multi="computed_fields",
type='boolean',
method=True,
string='Relation is in the future',
),
'active': fields.boolean('Active'),
}
_defaults = {
'active': True,
}
def _check_dates(self, cr, uid, ids, context=None):
'''End date should not be before start date, if noth filled'''
for line in self.browse(cr, uid, ids, context=context):
if line.date_start and line.date_end:
if line.date_start > line.date_end:
return False
return True
def _check_partner_type_left(self, cr, uid, ids, context=None):
'''Check left partner for required company or person'''
for this in self.browse(cr, uid, ids, context=context):
ptype = this.type_id.contact_type_left
company = this.left_partner_id.is_company
if (ptype == 'c' and not company) or (ptype == 'p' and company):
return False
return True
def _check_partner_type_right(self, cr, uid, ids, context=None):
'''Check right partner for required company or person'''
for this in self.browse(cr, uid, ids, context=context):
ptype = this.type_id.contact_type_right
company = this.right_partner_id.is_company
if (ptype == 'c' and not company) or (ptype == 'p' and company):
return False
return True
def _check_not_with_self(self, cr, uid, ids, context=None):
'''Not allowed to link partner to same partner'''
for this in self.browse(cr, uid, ids, context=context):
if this.left_partner_id == this.right_partner_id:
return False
return True
def _check_relation_uniqueness(self, cr, uid, ids, context=None):
'''Forbid multiple active relations of the same type between the same
partners'''
for this in self.browse(cr, uid, ids, context=context):
if not this.active:
continue
if self.search(
cr, uid,
[
('type_id', '=', this.type_id.id),
('active', '=', True),
('id', '!=', this.id),
('left_partner_id', '=', this.left_partner_id.id),
('right_partner_id', '=', this.right_partner_id.id),
],
context=context):
return False
return True
_constraints = [
(
_check_dates,
'The starting date cannot be after the ending date.',
['date_start', 'date_end']
),
(
_check_partner_type_left,
'The left partner is not applicable for this relation type.',
['left_partner_id', 'type_id']
),
(
_check_partner_type_right,
'The right partner is not applicable for this relation type.',
['right_partner_id', 'type_id']
),
(
_check_not_with_self,
'Partners cannot have a relation with themselves.',
['left_partner_id', 'right_partner_id']
),
(
_check_relation_uniqueness,
"The same relation can't be created twice.",
['left_partner_id', 'right_partner_id', 'active']
)
]
def get_action_related_partners(self, cr, uid, ids, context=None):
'''return a window action showing a list of partners taking part in the
relations names by ids. Context key 'partner_relations_show_side'
determines if we show 'left' side, 'right' side or 'all' (default)
partners.
If active_model is res.partner.relation.all, left=this and
right=other'''
if context is None:
context = {}
field_names = {}
if context.get('active_model', self._name) == self._name:
field_names = {
'left': ['left'],
'right': ['right'],
'all': ['left', 'right']
}
elif context.get('active_model') == 'res.partner.relation.all':
field_names = {
'left': ['this'],
'right': ['other'],
'all': ['this', 'other']
}
else:
assert False, 'Unknown active_model!'
partner_ids = []
field_names = field_names[
context.get('partner_relations_show_side', 'all')]
field_names = ['%s_partner_id' % n for n in field_names]
for relation in self.pool[context.get('active_model')].read(
cr, uid, ids, context=context, load='_classic_write'):
for name in field_names:
partner_ids.append(relation[name])
return {
'name': _('Related partners'),
'type': 'ir.actions.act_window',
'res_model': 'res.partner',
'domain': [('id', 'in', partner_ids)],
'views': [(False, 'tree'), (False, 'form')],
'view_type': 'form'
}

129
partner_relations/model/res_partner_relation_all.py

@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
#
# 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 Model
from openerp.osv import fields
from openerp.tools import drop_view_if_exists
from .res_partner_relation_type_selection\
import ResPartnerRelationTypeSelection
class ResPartnerRelationAll(Model):
_auto = False
_log_access = False
_name = 'res.partner.relation.all'
_description = 'All (non-inverse + inverse) relations between partners'
_additional_view_fields = []
'''append to this list if you added fields to res_partner_relation that
you need in this model and related fields are not adequate (ie for sorting)
You must use the same name as in res_partner_relation.
Don't overwrite this list in your declatarion but append in _auto_init:
def _auto_init(self, cr, context=None):
self._additional_view_fields.append('my_field')
return super(ResPartnerRelationAll, self)._auto_init(
cr, context=context)
_columns = {
'my_field': ....
}
'''
def _auto_init(self, cr, context=None):
drop_view_if_exists(cr, self._table)
additional_view_fields = ','.join(self._additional_view_fields)
additional_view_fields = (',' + additional_view_fields)\
if additional_view_fields else ''
cr.execute(
'''create or replace view %s as
select
id * 10 as id,
id as relation_id,
type_id,
cast('a' as char(1)) as record_type,
left_partner_id as this_partner_id,
right_partner_id as other_partner_id,
date_start,
date_end,
active,
type_id * 10 as type_selection_id
%s
from res_partner_relation
union select
id * 10 + 1,
id,
type_id,
cast('b' as char(1)),
right_partner_id,
left_partner_id,
date_start,
date_end,
active,
type_id * 10 + 1
%s
from res_partner_relation''' % (
self._table,
additional_view_fields,
additional_view_fields,
)
)
return super(ResPartnerRelationAll, self)._auto_init(
cr, context=context)
_columns = {
'record_type': fields.selection(
ResPartnerRelationTypeSelection._RECORD_TYPES, 'Record type',
readonly=True),
'relation_id': fields.many2one(
'res.partner.relation', 'Relation', readonly=True),
'type_id': fields.many2one(
'res.partner.relation.type', 'Relation type', readonly=True),
'type_selection_id': fields.many2one(
'res.partner.relation.type.selection', 'Relation type',
readonly=True),
'this_partner_id': fields.many2one(
'res.partner', 'Current partner', readonly=True),
'other_partner_id': fields.many2one(
'res.partner', 'Other partner', readonly=True),
'date_start': fields.date('Starting date'),
'date_end': fields.date('Ending date'),
'active': fields.boolean('Active'),
}
def name_get(self, cr, uid, ids, context=None):
return dict([
(this.id, '%s %s %s' % (
this.this_partner_id.name,
this.type_selection_id.name_get()[0][1],
this.other_partner_id.name,
))
for this in self.browse(cr, uid, ids, context=context)])
def write(self, cr, uid, ids, vals, context=None):
'''divert non-problematic writes to underlying table'''
return self.pool['res.partner.relation'].write(
cr, uid,
[i / 10 for i in ids],
dict([(k, vals[k])
for k in vals
if not self._columns[k].readonly]),
context=context)

48
partner_relations/model/res_partner_relation_type.py

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
'''Define model res.partner.relation.type'''
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Therp BV (<http://therp.nl>).
#
# 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 Model
from openerp.osv import fields
class ResPartnerRelationType(Model):
'''Model that defines relation types that might exist between partners'''
_name = 'res.partner.relation.type'
_description = 'Parter relation type'
_order = 'name'
def _get_partner_types(self, cr, uid, context=None):
return (('c', 'Company'), ('p', 'Person'),)
_columns = {
'name': fields.char(
'Name', size=128, required=True, translate=True),
'name_inverse': fields.char(
'Inverse name', size=128, required=True, translate=True),
'contact_type_left': fields.selection(
_get_partner_types, 'Left partner type'),
'contact_type_right': fields.selection(
_get_partner_types, 'Right partner type'),
'partner_category_left': fields.many2one(
'res.partner.category', 'Left partner category'),
'partner_category_right': fields.many2one(
'res.partner.category', 'Right partner category'),
}

168
partner_relations/model/res_partner_relation_type_selection.py

@ -0,0 +1,168 @@
# -*- coding: UTF-8 -*-
'''
Created on 23 may 2014
@author: Ronald Portier, Therp
rportier@therp.nl
http://www.therp.nl
For the model defined here _auto is set to False to prevent creating a
database file. All i/o operations are overridden to use a sql SELECT that
takes data from res_partner_connection_type where each type is included in the
result set twice, so it appears that the connection type and the inverse
type are separate records..
The original function _auto_init is still called because this function
normally (if _auto == True) not only creates the db tables, but it also takes
care of registering all fields in ir_model_fields. This is needed to make
the field labels translatable.
example content for last lines of _statement:
select id, record_type,
customer_id, customer_name, customer_city, customer_zip, customer_street,
caller_id, caller_name, caller_phone, caller_fax, caller_email
from FULL_LIST as ResPartnerRelationTypeSelection where record_type = 'c'
ORDER BY ResPartnerRelationTypeSelection.customer_name asc,
ResPartnerRelationTypeSelection.caller_name asc;
'''
from openerp.osv import fields
from openerp.osv import orm
from openerp.tools import drop_view_if_exists
from openerp.addons.partner_relations.model.res_partner_relation_type\
import ResPartnerRelationType
class ResPartnerRelationTypeSelection(orm.Model):
'''Virtual relation types'''
_RECORD_TYPES = [
('a', 'Type'),
('b', 'Inverse type'),
]
_auto = False # Do not try to create table in _auto_init(..)
_log_access = False
def get_type_from_selection_id(self, cr, uid, selection_id):
'''Selection id ic computed from id of underlying type and the
kind of record. This function does the inverse computation to give
back the original type id, and about the record type.'''
type_id = selection_id / 10
is_reverse = (selection_id % 10) > 0
return (type_id, is_reverse)
def _auto_init(self, cr, context=None):
drop_view_if_exists(cr, self._table)
# TODO: we lose field value's translations here.
# probably we need to patch ir_translation.get_source for that
# to get res_partner_relation_type's translations
cr.execute(
'''create or replace view %s as
select
id * 10 as id,
id as type_id,
cast('a' as char(1)) as record_type,
name as name,
contact_type_left as contact_type_this,
contact_type_right as contact_type_other,
partner_category_left as partner_category_this,
partner_category_right as partner_category_other
from res_partner_relation_type
union select
id * 10 + 1,
id,
cast('b' as char(1)),
name_inverse,
contact_type_right,
contact_type_left,
partner_category_right,
partner_category_left
from res_partner_relation_type''' % self._table)
return super(ResPartnerRelationTypeSelection, self)._auto_init(
cr, context=context)
def _search_partner_category_this(self, cr, uid, obj, field_name, args,
context=None):
category_ids = []
for arg in args:
if isinstance(arg, tuple) and arg[0] == field_name\
and (arg[1] == '=' or arg[1] == 'in'):
# TODO don't we have an api function to eval that?
for delta in arg[2]:
if delta[0] == 6:
category_ids.extend(delta[2])
if category_ids:
return [
'|',
('partner_category_this', '=', False),
('partner_category_this', 'in', category_ids),
]
else:
return [('partner_category_this', '=', False)]
_name = 'res.partner.relation.type.selection'
_description = 'All relation types'
_foreign_keys = []
_columns = {
'record_type': fields.selection(_RECORD_TYPES, 'Record type', size=16),
'type_id': fields.many2one(
'res.partner.relation.type', 'Type'),
'name': fields.char('Name', size=64),
'contact_type_this': fields.selection(
ResPartnerRelationType._get_partner_types.im_func,
'Current record\'s partner type'),
'contact_type_other': fields.selection(
ResPartnerRelationType._get_partner_types.im_func,
'Other record\'s partner type'),
'partner_category_this': fields.many2one(
'res.partner.category', 'Current record\'s category'),
'partner_category_other': fields.many2one(
'res.partner.category', 'Other record\'s category'),
# search field to handle many2many deltas from the client
'search_partner_category_this': fields.function(
lambda self, cr, uid, ids, context=None: dict(
[(i, False) for i in ids]),
fnct_search=_search_partner_category_this,
type='many2many', obj='res.partner.category',
string='Current record\'s category'),
}
_order = 'name asc'
def name_get(self, cr, uid, ids, context=None):
'translate name using translations from res.partner.relation.type'
result = super(ResPartnerRelationTypeSelection, self).name_get(
cr, uid, ids, context=context)
ir_translation = self.pool['ir.translation']
return [
(i, ir_translation._get_source(
cr, uid,
'res.partner.relation.type,name_inverse'
if self.get_type_from_selection_id(cr, uid, i)[1]
else 'res.partner.relation.type,name',
'model', context.get('lang'), name))
for i, name in result]
def name_search(self, cr, uid, name='', args=None, operator='ilike',
context=None, limit=100):
'search for translated names in res.partner.relation.type'
res_partner_relation_type = self.pool['res.partner.relation.type']
relation_ids = res_partner_relation_type.search(
cr, uid, [('name', operator, name)],
context=context)
inverse_relation_ids = res_partner_relation_type.search(
cr, uid, [('name_inverse', operator, name)],
context=context)
all_ids = self.search(
cr, uid,
[
('id', 'in',
map(lambda x: x * 10, relation_ids) +
map(lambda x: x * 10 + 1, inverse_relation_ids)),
] + (args or []),
context=context, limit=limit)
return self.name_get(cr, uid, all_ids, context=context)

7
partner_relations/security/ir.model.access.csv

@ -0,0 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
read_res_partner_relation,access_res_partner_relation,model_res_partner_relation,,1,0,0,0
read_res_partner_relation_all,access_res_partner_relation,model_res_partner_relation_all,,1,0,0,0
read_res_partner_relation_type,access_res_partner_relation_type,model_res_partner_relation_type,,1,0,0,0
read_res_partner_relation_type_selection,access_res_partner_relation_type,model_res_partner_relation_type_selection,,1,0,0,0
crud_res_partner_relation,access_res_partner_relation,model_res_partner_relation,base.group_partner_manager,1,1,1,1
crud_res_partner_relation_type,access_res_partner_relation_type,model_res_partner_relation_type,base.group_sale_manager,1,1,1,1

BIN
partner_relations/static/src/img/icon.png

After

Width: 90  |  Height: 90  |  Size: 13 KiB

15
partner_relations/view/menu.xml

@ -0,0 +1,15 @@
<openerp>
<data>
<act_window
id="action_res_partner_relation_type"
res_model="res.partner.relation.type"
view_mode="tree,form"
name="Partner relations"
/>
<menuitem
id="menu_res_partner_relation_type"
parent="base.menu_config_address_book"
action="action_res_partner_relation_type"
/>
</data>
</openerp>

96
partner_relations/view/res_partner.xml

@ -0,0 +1,96 @@
<openerp>
<data>
<record id="view_res_partner_filter" model="ir.ui.view">
<field name="inherit_id" ref="base.view_res_partner_filter" />
<field name="model">res.partner</field>
<field type="xml" name="arch">
<data>
<field name="parent_id" position="after">
<field name="search_relation_partner_id" />
<field name="search_relation_id" />
<field name="search_relation_date" />
<field name="search_relation_partner_category_id" />
</field>
</data>
</field>
</record>
<record id="view_partner_form" model="ir.ui.view">
<field name="inherit_id" ref="base.view_partner_form" />
<field name="model">res.partner</field>
<field type="xml" name="arch">
<data>
<xpath expr="//sheet/notebook" position="inside">
<page string="Relations">
<field
name="relation_ids"
context="{
'active_model': 'res.partner',
'active_id': id, 'active_ids': [id],
'active_test': False,
}"
>
<tree
editable="top"
colors="gray:is_relation_expired==True or not active;blue:is_relation_future==True"
>
<field
name="type_selection_id"
required="True"
context="{
'parent_model': 'res.partner',
'parent_id': parent.id,
}"
domain="[
'|',
('contact_type_this', '=', parent.is_company and 'c' or 'p'),
('contact_type_this', '=', False),
('search_partner_category_this', '=', parent.category_id),
]
"
options="{'create': false, 'create_edit': false}"
on_change="on_change_type_selection_id(type_selection_id)"
/>
<field name="type_id" invisible="True" />
<field
name="partner_id_display"
required="True"
attrs="{
'readonly': [('type_selection_id','=',False)],
}"
options="{'create': false, 'create_edit': false}"
widget="many2one_clickable"
/>
<field
name="date_start"
/>
<field
name="date_end"
/>
<field
name="active"
/>
<field
name="is_relation_expired"
invisible="True"
/>
<field
name="is_relation_future"
invisible="True"
/>
</tree>
</field>
</page>
</xpath>
</data>
</field>
</record>
<act_window id="action_show_partner_relations"
name="Show partner's relations"
src_model="res.partner"
res_model="res.partner.relation.all"
domain="[('this_partner_id', 'in', active_ids)]"
key2="client_action_multi" />
</data>
</openerp>

33
partner_relations/view/res_partner_relation.xml

@ -0,0 +1,33 @@
<openerp>
<data>
<record id="form_res_partner_relation" model="ir.ui.view">
<field name="model">res.partner.relation</field>
<field type="xml" name="arch">
<form version="7.0" string="Partner relation">
<sheet>
<field name="left_partner_id" />
<field name="type_id" />
<field name="right_partner_id" />
</sheet>
</form>
</field>
</record>
<record id="action_show_right_relation_partners" model="ir.actions.server">
<field name="sequence" eval="5"/>
<field name="state">code</field>
<field name="type">ir.actions.server</field>
<field name="model_id" ref="model_res_partner_relation"/>
<field name="code">action = self.get_action_related_partners(cr, uid, context.get('active_ids', []), dict(context or {}, partner_relations_show_side='right'))</field>
<field name="condition">True</field>
<field name="name">Show partners</field>
</record>
<record id="action_show_right_relation_partners_value" model="ir.values">
<field name="name">Show partners</field>
<field name="key">action</field>
<field name="key2">client_action_multi</field>
<field name="model">res.partner.relation.all</field>
<field name="value" eval="'ir.actions.server,%d' % ref('partner_relations.action_show_right_relation_partners')" />
</record>
</data>
</openerp>

34
partner_relations/view/res_partner_relation_all.xml

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record id="tree_res_partner_relation_all" model="ir.ui.view">
<field name="model">res.partner.relation.all</field>
<field name="arch" type="xml">
<tree string="Partner relations">
<field name="this_partner_id" />
<field name="type_selection_id" />
<field name="other_partner_id" />
<field name="date_start" />
<field name="date_end" />
</tree>
</field>
</record>
<record id="form_res_partner_relation_all" model="ir.ui.view">
<field name="model">res.partner.relation.all</field>
<field name="arch" type="xml">
<form string="Partner relation">
<group>
<field name="this_partner_id" />
<field name="type_selection_id" />
<field name="other_partner_id" />
</group>
<group>
<field name="date_start" />
<field name="date_end" />
<field name="active" />
</group>
</form>
</field>
</record>
</data>
</openerp>

44
partner_relations/view/res_partner_relation_type.xml

@ -0,0 +1,44 @@
<openerp>
<data>
<record id="tree_res_partner_relation_type" model="ir.ui.view">
<field name="model">res.partner.relation.type</field>
<field name="type">tree</field>
<field type="xml" name="arch">
<tree version="7.0" string="Partner relation">
<field name="name" />
<field name="name_inverse" />
<field name="contact_type_left" />
<field name="contact_type_right" />
</tree>
</field>
</record>
<record id="form_res_partner_relation_type" model="ir.ui.view">
<field name="model">res.partner.relation.type</field>
<field name="type">form</field>
<field type="xml" name="arch">
<form version="7.0" string="Partner relation">
<sheet>
<group>
<group
colspan="2" col="2"
string="Left side of relation"
>
<field name="name" />
<field name="contact_type_left" />
<field name="partner_category_left" />
</group>
<group
colspan="2" col="2"
string="Right side of relation"
>
<field name="name_inverse" />
<field name="contact_type_right" />
<field name="partner_category_right" />
</group>
</group>
</sheet>
</form>
</field>
</record>
</data>
</openerp>
Loading…
Cancel
Save