diff --git a/partner_relations/__init__.py b/partner_relations/__init__.py new file mode 100644 index 000000000..4d083ea93 --- /dev/null +++ b/partner_relations/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV (). +# +# 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 . +# +############################################################################## +import model diff --git a/partner_relations/__openerp__.py b/partner_relations/__openerp__.py new file mode 100644 index 000000000..2fccd144c --- /dev/null +++ b/partner_relations/__openerp__.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV (). +# +# 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 . +# +############################################################################## +{ + "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', + ], + "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': [], + }, +} diff --git a/partner_relations/i18n/nl.po b/partner_relations/i18n/nl.po new file mode 100644 index 000000000..30f9f8b47 --- /dev/null +++ b/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." diff --git a/partner_relations/i18n/partner_relations.pot b/partner_relations/i18n/partner_relations.pot new file mode 100644 index 000000000..fb9a8249a --- /dev/null +++ b/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 "" + diff --git a/partner_relations/model/__init__.py b/partner_relations/model/__init__.py new file mode 100644 index 000000000..eb5702410 --- /dev/null +++ b/partner_relations/model/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV (). +# +# 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 . +# +############################################################################## +import res_partner +import res_partner_relation +import res_partner_relation_type +import res_partner_relation_type_selection +from . import res_partner_relation_all diff --git a/partner_relations/model/res_partner.py b/partner_relations/model/res_partner.py new file mode 100644 index 000000000..760bfce7b --- /dev/null +++ b/partner_relations/model/res_partner.py @@ -0,0 +1,238 @@ +# -*- coding: utf-8 -*- +'''Extend res.partner model''' +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV (). +# +# 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 . +# +############################################################################## +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( + self, cr, uid, ids, dummy_name, dummy_arg, context=None): + if context is None: + context = {} + + #TODO: do a permission test on returned ids + 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)) + ) + result = dict([(i, []) for i in ids]) + for row in cr.fetchall(): + 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): + 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 diff --git a/partner_relations/model/res_partner_relation.py b/partner_relations/model/res_partner_relation.py new file mode 100644 index 000000000..515f64a6b --- /dev/null +++ b/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 (). +# +# 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 . +# +############################################################################## +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' + } diff --git a/partner_relations/model/res_partner_relation_all.py b/partner_relations/model/res_partner_relation_all.py new file mode 100644 index 000000000..7baf084dd --- /dev/null +++ b/partner_relations/model/res_partner_relation_all.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Therp BV (). +# +# 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 . +# +############################################################################## +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' + + def _auto_init(self, cr, context=None): + drop_view_if_exists(cr, self._table) + 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 + 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 + from res_partner_relation''' % self._table) + + return super(ResPartnerRelationAll, self)._auto_init( + cr, context=context) + + _columns = { + 'record_type': fields.selection( + ResPartnerRelationTypeSelection._RECORD_TYPES, 'Record type'), + 'relation_id': fields.many2one( + 'res.partner.relation', 'Relation'), + 'type_id': fields.many2one( + 'res.partner.relation.type', 'Relation type'), + 'type_selection_id': fields.many2one( + 'res.partner.relation.type.selection', 'Relation type'), + 'this_partner_id': fields.many2one('res.partner', 'Current partner'), + 'other_partner_id': fields.many2one('res.partner', 'Other partner'), + '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)]) diff --git a/partner_relations/model/res_partner_relation_type.py b/partner_relations/model/res_partner_relation_type.py new file mode 100644 index 000000000..23e320b9d --- /dev/null +++ b/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 (). +# +# 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 . +# +############################################################################## +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'), + } diff --git a/partner_relations/model/res_partner_relation_type_selection.py b/partner_relations/model/res_partner_relation_type_selection.py new file mode 100644 index 000000000..7d071f565 --- /dev/null +++ b/partner_relations/model/res_partner_relation_type_selection.py @@ -0,0 +1,167 @@ +# -*- 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.integer('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) diff --git a/partner_relations/security/ir.model.access.csv b/partner_relations/security/ir.model.access.csv new file mode 100644 index 000000000..840b35875 --- /dev/null +++ b/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 diff --git a/partner_relations/static/src/img/icon.png b/partner_relations/static/src/img/icon.png new file mode 100644 index 000000000..c6863a3e4 Binary files /dev/null and b/partner_relations/static/src/img/icon.png differ diff --git a/partner_relations/view/menu.xml b/partner_relations/view/menu.xml new file mode 100644 index 000000000..f5b63bcd0 --- /dev/null +++ b/partner_relations/view/menu.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/partner_relations/view/res_partner.xml b/partner_relations/view/res_partner.xml new file mode 100644 index 000000000..c1426cc9d --- /dev/null +++ b/partner_relations/view/res_partner.xml @@ -0,0 +1,96 @@ + + + + + res.partner + + + + + + + + + + + + + + res.partner + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/partner_relations/view/res_partner_relation.xml b/partner_relations/view/res_partner_relation.xml new file mode 100644 index 000000000..2055bc959 --- /dev/null +++ b/partner_relations/view/res_partner_relation.xml @@ -0,0 +1,33 @@ + + + + res.partner.relation + +
+ + + + + +
+
+
+ + + code + ir.actions.server + + action = self.get_action_related_partners(cr, uid, context.get('active_ids', []), dict(context or {}, partner_relations_show_side='right')) + True + Show partners + + + Show partners + action + client_action_multi + res.partner.relation.all + + + +
+
diff --git a/partner_relations/view/res_partner_relation_all.xml b/partner_relations/view/res_partner_relation_all.xml new file mode 100644 index 000000000..b05e28d42 --- /dev/null +++ b/partner_relations/view/res_partner_relation_all.xml @@ -0,0 +1,34 @@ + + + + + res.partner.relation.all + + + + + + + + + + + + res.partner.relation.all + +
+ + + + + + + + + + +
+
+
+
+
diff --git a/partner_relations/view/res_partner_relation_type.xml b/partner_relations/view/res_partner_relation_type.xml new file mode 100644 index 000000000..d0a3c0746 --- /dev/null +++ b/partner_relations/view/res_partner_relation_type.xml @@ -0,0 +1,44 @@ + + + + res.partner.relation.type + tree + + + + + + + + + + + res.partner.relation.type + form + +
+ + + + + + + + + + + + + + +
+
+
+
+