From dcaeadd1ae7a5f76a78b50da4947fffa547556e4 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 28 Jul 2014 10:03:22 +0200 Subject: [PATCH 01/40] [ADD] partner_relations --- partner_relations/__init__.py | 21 ++ partner_relations/__openerp__.py | 94 +++++ partner_relations/i18n/nl.po | 268 ++++++++++++++ partner_relations/i18n/partner_relations.pot | 277 +++++++++++++++ partner_relations/model/__init__.py | 25 ++ partner_relations/model/res_partner.py | 238 +++++++++++++ .../model/res_partner_relation.py | 330 ++++++++++++++++++ .../model/res_partner_relation_all.py | 88 +++++ .../model/res_partner_relation_type.py | 48 +++ .../res_partner_relation_type_selection.py | 167 +++++++++ .../security/ir.model.access.csv | 7 + partner_relations/static/src/img/icon.png | Bin 0 -> 12960 bytes partner_relations/view/menu.xml | 15 + partner_relations/view/res_partner.xml | 96 +++++ .../view/res_partner_relation.xml | 33 ++ .../view/res_partner_relation_all.xml | 34 ++ .../view/res_partner_relation_type.xml | 44 +++ 17 files changed, 1785 insertions(+) create mode 100644 partner_relations/__init__.py create mode 100644 partner_relations/__openerp__.py create mode 100644 partner_relations/i18n/nl.po create mode 100644 partner_relations/i18n/partner_relations.pot create mode 100644 partner_relations/model/__init__.py create mode 100644 partner_relations/model/res_partner.py create mode 100644 partner_relations/model/res_partner_relation.py create mode 100644 partner_relations/model/res_partner_relation_all.py create mode 100644 partner_relations/model/res_partner_relation_type.py create mode 100644 partner_relations/model/res_partner_relation_type_selection.py create mode 100644 partner_relations/security/ir.model.access.csv create mode 100644 partner_relations/static/src/img/icon.png create mode 100644 partner_relations/view/menu.xml create mode 100644 partner_relations/view/res_partner.xml create mode 100644 partner_relations/view/res_partner_relation.xml create mode 100644 partner_relations/view/res_partner_relation_all.xml create mode 100644 partner_relations/view/res_partner_relation_type.xml 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 0000000000000000000000000000000000000000..c6863a3e4852ea743449d6a17547b5ddc16c821c GIT binary patch literal 12960 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 ?Im#$t6v{tsveBQHiC6tSq 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 WM@fu$C{uy}0000 + + + + + 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 + +
+ + + + + + + + + + + + + + +
+
+
+
+
From 3500d55cd7d90f1493d41f0350bd6f7285706095 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 28 Jul 2014 10:33:34 +0200 Subject: [PATCH 02/40] [FIX] flake8, relative import, unused context initialization --- partner_relations/__init__.py | 2 +- partner_relations/model/__init__.py | 8 ++++---- partner_relations/model/res_partner.py | 13 +++++-------- .../model/res_partner_relation_type_selection.py | 10 +++++----- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/partner_relations/__init__.py b/partner_relations/__init__.py index 4d083ea93..9da2ac3b6 100644 --- a/partner_relations/__init__.py +++ b/partner_relations/__init__.py @@ -18,4 +18,4 @@ # along with this program. If not, see . # ############################################################################## -import model +from . import model diff --git a/partner_relations/model/__init__.py b/partner_relations/model/__init__.py index eb5702410..b5afe7aa1 100644 --- a/partner_relations/model/__init__.py +++ b/partner_relations/model/__init__.py @@ -18,8 +18,8 @@ # 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 +from . import res_partner_relation +from . import res_partner_relation_type +from . 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 index 760bfce7b..1f6b8f2f4 100644 --- a/partner_relations/model/res_partner.py +++ b/partner_relations/model/res_partner.py @@ -30,10 +30,7 @@ class ResPartner(orm.Model): 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 + # TODO: do a permission test on returned ids cr.execute( '''select id, left_partner_id, right_partner_id from res_partner_relation @@ -90,7 +87,7 @@ class ResPartner(orm.Model): result = [] for arg in args: if isinstance(arg, tuple) and arg[0] == name: - #TODO: handle {<,>}{,=} + # TODO: handle {<,>}{,=} if arg[1] != '=': continue @@ -190,8 +187,8 @@ class ResPartner(orm.Model): 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 + # 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'): @@ -203,7 +200,7 @@ class ResPartner(orm.Model): ('search_relation_date', '=', time.strftime( DEFAULT_SERVER_DATE_FORMAT))] - #because of auto_join, we have to do the active test by hand + # 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: diff --git a/partner_relations/model/res_partner_relation_type_selection.py b/partner_relations/model/res_partner_relation_type_selection.py index 7d071f565..ee9b969d1 100644 --- a/partner_relations/model/res_partner_relation_type_selection.py +++ b/partner_relations/model/res_partner_relation_type_selection.py @@ -55,9 +55,9 @@ class ResPartnerRelationTypeSelection(orm.Model): 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 + # 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 @@ -91,7 +91,7 @@ class ResPartnerRelationTypeSelection(orm.Model): 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? + # 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]) @@ -122,7 +122,7 @@ class ResPartnerRelationTypeSelection(orm.Model): '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 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]), From 30600b3bb89f16ac2b9345f1ba2548c845acf47e Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Wed, 13 Aug 2014 12:50:47 +0200 Subject: [PATCH 03/40] [IMP] context check, make type_id browsable --- partner_relations/model/res_partner.py | 3 +++ partner_relations/model/res_partner_relation_type_selection.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/partner_relations/model/res_partner.py b/partner_relations/model/res_partner.py index 1f6b8f2f4..cae4f0427 100644 --- a/partner_relations/model/res_partner.py +++ b/partner_relations/model/res_partner.py @@ -30,6 +30,9 @@ class ResPartner(orm.Model): 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 diff --git a/partner_relations/model/res_partner_relation_type_selection.py b/partner_relations/model/res_partner_relation_type_selection.py index ee9b969d1..e691936cf 100644 --- a/partner_relations/model/res_partner_relation_type_selection.py +++ b/partner_relations/model/res_partner_relation_type_selection.py @@ -110,7 +110,8 @@ class ResPartnerRelationTypeSelection(orm.Model): _foreign_keys = [] _columns = { 'record_type': fields.selection(_RECORD_TYPES, 'Record type', size=16), - 'type_id': fields.integer('Type'), + '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, From 75fd2387d0f4b658d722054e215dc68e52b9b0c3 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Wed, 13 Aug 2014 14:34:18 +0200 Subject: [PATCH 04/40] [RFR] make collecting relation ids simple to override --- partner_relations/model/res_partner.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/partner_relations/model/res_partner.py b/partner_relations/model/res_partner.py index cae4f0427..f265bb452 100644 --- a/partner_relations/model/res_partner.py +++ b/partner_relations/model/res_partner.py @@ -28,12 +28,10 @@ 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 + 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 @@ -41,8 +39,17 @@ class ResPartner(orm.Model): ' 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]) - for row in cr.fetchall(): + # 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: @@ -52,6 +59,7 @@ class ResPartner(orm.Model): 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') From 680134ce79d56fe1f431613f0f6570bfb895ad7b Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 25 Aug 2014 11:39:09 +0200 Subject: [PATCH 05/40] [IMP] allow to write certain fields on relation view --- .../model/res_partner_relation_all.py | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/partner_relations/model/res_partner_relation_all.py b/partner_relations/model/res_partner_relation_all.py index 7baf084dd..e5b47d638 100644 --- a/partner_relations/model/res_partner_relation_all.py +++ b/partner_relations/model/res_partner_relation_all.py @@ -64,15 +64,19 @@ class ResPartnerRelationAll(Model): _columns = { 'record_type': fields.selection( - ResPartnerRelationTypeSelection._RECORD_TYPES, 'Record type'), + ResPartnerRelationTypeSelection._RECORD_TYPES, 'Record type', + readonly=True), 'relation_id': fields.many2one( - 'res.partner.relation', 'Relation'), + 'res.partner.relation', 'Relation', readonly=True), 'type_id': fields.many2one( - 'res.partner.relation.type', 'Relation type'), + 'res.partner.relation.type', 'Relation type', readonly=True), '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'), + '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'), @@ -86,3 +90,13 @@ class ResPartnerRelationAll(Model): 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) From 0063676107e9baa3176afa6cf9336de454fb5aed Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Wed, 27 Aug 2014 08:57:42 +0200 Subject: [PATCH 06/40] [RFR] provide a modular way to inject fields into res_partner_relation_all --- .../model/res_partner_relation_all.py | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/partner_relations/model/res_partner_relation_all.py b/partner_relations/model/res_partner_relation_all.py index e5b47d638..6f53d5e91 100644 --- a/partner_relations/model/res_partner_relation_all.py +++ b/partner_relations/model/res_partner_relation_all.py @@ -30,8 +30,27 @@ class ResPartnerRelationAll(Model): _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 @@ -45,6 +64,7 @@ class ResPartnerRelationAll(Model): date_end, active, type_id * 10 as type_selection_id + %s from res_partner_relation union select id * 10 + 1, @@ -57,7 +77,13 @@ class ResPartnerRelationAll(Model): date_end, active, type_id * 10 + 1 - from res_partner_relation''' % self._table) + %s + from res_partner_relation''' % ( + self._table, + additional_view_fields, + additional_view_fields, + ) + ) return super(ResPartnerRelationAll, self)._auto_init( cr, context=context) From 73877cbbf204c56638b67ee5a6021f0e6456c58d Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Wed, 27 Aug 2014 09:06:05 +0200 Subject: [PATCH 07/40] [FIX] pep8 --- partner_relations/model/res_partner_relation_all.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/partner_relations/model/res_partner_relation_all.py b/partner_relations/model/res_partner_relation_all.py index 6f53d5e91..b3902a994 100644 --- a/partner_relations/model/res_partner_relation_all.py +++ b/partner_relations/model/res_partner_relation_all.py @@ -78,11 +78,11 @@ class ResPartnerRelationAll(Model): active, type_id * 10 + 1 %s - from res_partner_relation''' % ( - self._table, - additional_view_fields, - additional_view_fields, - ) + from res_partner_relation''' % ( + self._table, + additional_view_fields, + additional_view_fields, + ) ) return super(ResPartnerRelationAll, self)._auto_init( From c65f3a94cc1f2df58229460d95e65e54b3bb1e1c Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 20 Oct 2014 10:19:38 +0200 Subject: [PATCH 08/40] [FIX] coding style issues --- partner_relations/model/res_partner_relation_all.py | 3 ++- partner_relations/model/res_partner_relation_type.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/partner_relations/model/res_partner_relation_all.py b/partner_relations/model/res_partner_relation_all.py index b3902a994..2f3e71b9d 100644 --- a/partner_relations/model/res_partner_relation_all.py +++ b/partner_relations/model/res_partner_relation_all.py @@ -21,7 +21,8 @@ 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 +from .res_partner_relation_type_selection\ + import ResPartnerRelationTypeSelection class ResPartnerRelationAll(Model): diff --git a/partner_relations/model/res_partner_relation_type.py b/partner_relations/model/res_partner_relation_type.py index 23e320b9d..6e19eb47e 100644 --- a/partner_relations/model/res_partner_relation_type.py +++ b/partner_relations/model/res_partner_relation_type.py @@ -45,4 +45,4 @@ class ResPartnerRelationType(Model): 'res.partner.category', 'Left partner category'), 'partner_category_right': fields.many2one( 'res.partner.category', 'Right partner category'), - } + } From 43908340b79477fdc2f8154e01b3d63ed8a8adfd Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 20 Oct 2014 10:37:55 +0200 Subject: [PATCH 09/40] [ADD] some demo data --- partner_relations/__openerp__.py | 3 +++ partner_relations/data/demo.xml | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 partner_relations/data/demo.xml diff --git a/partner_relations/__openerp__.py b/partner_relations/__openerp__.py index 2fccd144c..d978c66d4 100644 --- a/partner_relations/__openerp__.py +++ b/partner_relations/__openerp__.py @@ -72,6 +72,9 @@ date.""", 'web_m2x_options', 'web_tree_many2one_clickable', ], + "demo": [ + "data/demo.xml", + ], "data": [ "view/res_partner_relation_all.xml", 'view/res_partner.xml', diff --git a/partner_relations/data/demo.xml b/partner_relations/data/demo.xml new file mode 100644 index 000000000..d177cecd2 --- /dev/null +++ b/partner_relations/data/demo.xml @@ -0,0 +1,38 @@ + + + + + Is assistant of + Has assistant + p + p + + + Is competitor of + Is competitor of + c + c + + + Has worked for + Has former employee + p + c + + + + + + + + + + + + + + + + + + From 4967c79bd6b2e3ccf5044e7b8a93d6434d1c8bc1 Mon Sep 17 00:00:00 2001 From: Stefan Rijnhart Date: Wed, 3 Sep 2014 13:05:30 +0200 Subject: [PATCH 10/40] [FIX] #688, partner_relations: creating equal but non-overlapping relations was not allowed Conflicts: partner_relations/model/res_partner_relation_all.py --- partner_relations/i18n/nl.po | 37 ++++++++++++++----- .../model/res_partner_relation.py | 31 ++++++++++------ 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/partner_relations/i18n/nl.po b/partner_relations/i18n/nl.po index 30f9f8b47..0997a6345 100644 --- a/partner_relations/i18n/nl.po +++ b/partner_relations/i18n/nl.po @@ -68,6 +68,11 @@ msgstr "Heeft koppeling" msgid "Has relation with" msgstr "Heeft koppeling met" +#. 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 #: field:res.partner.relation.type,name_inverse:0 msgid "Inverse name" @@ -82,17 +87,17 @@ msgstr "Inverse type" #. module: partner_relations #: field:res.partner.relation,left_partner_id:0 msgid "Left partner" -msgstr "Linker relatie" +msgstr "Linkerrelatie" #. module: partner_relations #: field:res.partner.relation.type,partner_category_left:0 msgid "Left partner category" -msgstr "Linker relatielabel" +msgstr "Linker-relatielabel" #. module: partner_relations #: field:res.partner.relation.type,contact_type_left:0 msgid "Left partner type" -msgstr "Linker relatietype" +msgstr "Linker-relatietype" #. module: partner_relations #: view:res.partner.relation.type:0 @@ -120,6 +125,12 @@ msgstr "Categorie andere record" msgid "Other record's partner type" msgstr "Type andere record" +#. module: partner_relations +#: code:addons/partner_relations/model/res_partner_relation.py:259 +#, python-format +msgid "Overlapping relation" +msgstr "Overlappende koppeling" + #. 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 @@ -135,7 +146,7 @@ msgstr "Relatie" #. module: partner_relations #: model:ir.model,name:partner_relations.model_res_partner_category msgid "Partner Categories" -msgstr "Relatie Categorieën" +msgstr "Relatielabels" #. module: partner_relations #: model:ir.model,name:partner_relations.model_res_partner_relation @@ -169,7 +180,7 @@ msgstr "Koppeling" #. module: partner_relations #: field:res.partner.relation,is_relation_expired:0 msgid "Relation is expired" -msgstr "Koppeling is afgelopen" +msgstr "Koppeling is beëindigd" #. module: partner_relations #: field:res.partner.relation,is_relation_future:0 @@ -195,17 +206,17 @@ msgstr "Koppelingen" #. module: partner_relations #: field:res.partner.relation,right_partner_id:0 msgid "Right partner" -msgstr "Rechter relatie" +msgstr "Rechterrelatie" #. module: partner_relations #: field:res.partner.relation.type,partner_category_right:0 msgid "Right partner category" -msgstr "Rechter relatielabel" +msgstr "Rechter-relatielabel" #. module: partner_relations #: field:res.partner.relation.type,contact_type_right:0 msgid "Right partner type" -msgstr "Rechter relatietype" +msgstr "Rechter-relatietype" #. module: partner_relations #: view:res.partner.relation.type:0 @@ -221,18 +232,24 @@ 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." +msgstr "De linkerrelatie 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." +msgstr "De rechterrelatie 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 +#: code:addons/partner_relations/model/res_partner_relation.py:260 +#, python-format +msgid "There is already a similar relation with overlapping dates" +msgstr "Er is al een soortgelijke, overlappende koppeling." + #. module: partner_relations #: field:res.partner.relation,type_id:0 #: field:res.partner.relation,type_selection_id:0 diff --git a/partner_relations/model/res_partner_relation.py b/partner_relations/model/res_partner_relation.py index 515f64a6b..0c2733eb3 100644 --- a/partner_relations/model/res_partner_relation.py +++ b/partner_relations/model/res_partner_relation.py @@ -19,7 +19,7 @@ # along with this program. If not, see . # ############################################################################## -from openerp.osv.orm import Model +from openerp.osv.orm import Model, except_orm from openerp.osv import fields from openerp.tools.translate import _ @@ -241,17 +241,24 @@ class ResPartnerRelation(Model): 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 + domain = [ + ('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), + ] + if this.date_start: + domain += ['|', ('date_end', '=', False), + ('date_end', '>=', this.date_start)] + if this.date_end: + domain += ['|', ('date_start', '=', False), + ('date_start', '<=', this.date_end)] + if self.search(cr, uid, domain, context=context): + raise except_orm( + _('Overlapping relation'), + _('There is already a similar relation ' + 'with overlapping dates')) return True From 879e421368772bd30f6d683056e48995cbd182bf Mon Sep 17 00:00:00 2001 From: Ronald Portier Date: Thu, 9 Oct 2014 12:35:59 +0200 Subject: [PATCH 11/40] [ENH] Module partner_relations: - Edit relations in pop-up form instead of through inline tree. --- partner_relations/view/res_partner.xml | 50 +++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/partner_relations/view/res_partner.xml b/partner_relations/view/res_partner.xml index c1426cc9d..74084d054 100644 --- a/partner_relations/view/res_partner.xml +++ b/partner_relations/view/res_partner.xml @@ -1,6 +1,7 @@ + partner_relations.view_partner_filter res.partner @@ -15,6 +16,7 @@ + partner_relations.view_partner_form res.partner @@ -30,7 +32,6 @@ }" > +
+ + + + + + + + + From 09c511baf61fe5c491d1a28cc369ba8a97fa2c2c Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Tue, 25 Nov 2014 15:32:49 +0100 Subject: [PATCH 12/40] [IMP] put one attribute xml elements on one line --- partner_relations/view/res_partner.xml | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/partner_relations/view/res_partner.xml b/partner_relations/view/res_partner.xml index 74084d054..1773859be 100644 --- a/partner_relations/view/res_partner.xml +++ b/partner_relations/view/res_partner.xml @@ -62,15 +62,9 @@ options="{'create': false, 'create_edit': false}" widget="many2one_clickable" /> - - - + + + - - - + + + Date: Wed, 7 Jan 2015 12:52:14 +0100 Subject: [PATCH 13/40] [ADD] support more search operands when search for relations --- partner_relations/i18n/nl.po | 6 ++++ partner_relations/model/res_partner.py | 46 +++++++++++++++++++------- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/partner_relations/i18n/nl.po b/partner_relations/i18n/nl.po index 0997a6345..07fcc753f 100644 --- a/partner_relations/i18n/nl.po +++ b/partner_relations/i18n/nl.po @@ -244,6 +244,12 @@ msgstr "De rechterrelatie is niet geldig voor dit type koppeling." msgid "The starting date cannot be after the ending date." msgstr "De begindatum mag niet na de einddatum liggen." +#. module: partner_relations +#: code:addons/partner_relations/model/res_partner_relation.py:260 +#, python-format +msgid "Unsupported search operand \"%s\"" +msgstr "Operator \"%s\" is niet ondersteund" + #. module: partner_relations #: code:addons/partner_relations/model/res_partner_relation.py:260 #, python-format diff --git a/partner_relations/model/res_partner.py b/partner_relations/model/res_partner.py index f265bb452..68b744442 100644 --- a/partner_relations/model/res_partner.py +++ b/partner_relations/model/res_partner.py @@ -21,8 +21,9 @@ ############################################################################## import time from openerp.osv import orm, fields -from openerp.osv.expression import is_leaf +from openerp.osv.expression import is_leaf, OR, FALSE_LEAF from openerp.tools import DEFAULT_SERVER_DATE_FORMAT +from openerp.tools.translate import _ class ResPartner(orm.Model): @@ -78,19 +79,40 @@ class ResPartner(orm.Model): result = [] for arg in args: if isinstance(arg, tuple) and arg[0] == name: - if arg[1] != '=': - continue + if arg[1] not in ['=', 'like', 'not like', 'ilike', + 'not ilike', 'in', 'not in']: + raise orm.except_orm( + _('Error'), + _('Unsupported search operand "%s"') % arg[1]) - type_id, is_inverse = self\ - .pool['res.partner.relation.type.selection']\ - .get_type_from_selection_id(cr, uid, arg[2]) + relation_type_selection_ids = [] + relation_type_selection = self\ + .pool['res.partner.relation.type.selection'] - result.extend([ - '&', - ('relation_all_ids.type_id', '=', type_id), - ('relation_all_ids.record_type', '=', - 'b' if is_inverse else 'a') - ]) + if arg[1] == '=' and isinstance(arg[2], (long, int)): + relation_type_selection_ids.append(arg[2]) + else: + relation_type_selection_ids = relation_type_selection\ + .search(cr, uid, [('name', arg[1], arg[2])], + context=context) + + if not relation_type_selection_ids: + result = OR([result, FALSE_LEAF]) + + for relation_type_selection_id in relation_type_selection_ids: + type_id, is_inverse = relation_type_selection\ + .get_type_from_selection_id( + cr, uid, relation_type_selection_id) + + result = OR([ + result, + [ + '&', + ('relation_all_ids.type_id', '=', type_id), + ('relation_all_ids.record_type', '=', + 'b' if is_inverse else 'a') + ], + ]) return result From b4b6af58156493c2556d20bcf9f44e36a4c5a1bc Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 19 Jan 2015 09:12:31 +0100 Subject: [PATCH 14/40] [ADD] update pot file --- partner_relations/i18n/partner_relations.pot | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/partner_relations/i18n/partner_relations.pot b/partner_relations/i18n/partner_relations.pot index fb9a8249a..df3c5b428 100644 --- a/partner_relations/i18n/partner_relations.pot +++ b/partner_relations/i18n/partner_relations.pot @@ -251,6 +251,12 @@ msgstr "" msgid "The starting date cannot be after the ending date." msgstr "" +#. module: partner_relations +#: code:addons/partner_relations/model/res_partner_relation.py:260 +#, python-format +msgid "Unsupported search operand \"%s\"" +msgstr "" + #. module: partner_relations #: field:res.partner.relation,type_id:0 #: field:res.partner.relation,type_selection_id:0 From b10f3756b55240d38057b94562380bcd7aa93145 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 26 Jan 2015 14:10:46 +0100 Subject: [PATCH 15/40] [FIX] allow to change partner_id_display --- partner_relations/model/res_partner.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/partner_relations/model/res_partner.py b/partner_relations/model/res_partner.py index 68b744442..1dddfd913 100644 --- a/partner_relations/model/res_partner.py +++ b/partner_relations/model/res_partner.py @@ -69,6 +69,16 @@ class ResPartner(orm.Model): if value[0] == 0: relation_obj.create(cr, uid, value[2], context=context2) if value[0] == 1: + # if we write partner_id_display, we also need to pass + # type_selection_id in order to have this write end up on + # the correct field + if 'partner_id_display' in value[2] and 'type_selection_id'\ + not in value[2]: + relation_data = relation_obj.read( + cr, uid, [value[1]], ['type_selection_id'], + context=context)[0] + value[2]['type_selection_id'] =\ + relation_data['type_selection_id'] relation_obj.write( cr, uid, value[1], value[2], context=context2) if value[0] == 2: From e4a9386211c4715f864499238190676307cddc29 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Wed, 28 Jan 2015 11:30:31 +0100 Subject: [PATCH 16/40] [FIX] logic error --- partner_relations/model/res_partner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/partner_relations/model/res_partner.py b/partner_relations/model/res_partner.py index 1dddfd913..3372407aa 100644 --- a/partner_relations/model/res_partner.py +++ b/partner_relations/model/res_partner.py @@ -21,7 +21,7 @@ ############################################################################## import time from openerp.osv import orm, fields -from openerp.osv.expression import is_leaf, OR, FALSE_LEAF +from openerp.osv.expression import is_leaf, AND, OR, FALSE_LEAF from openerp.tools import DEFAULT_SERVER_DATE_FORMAT from openerp.tools.translate import _ @@ -107,7 +107,7 @@ class ResPartner(orm.Model): context=context) if not relation_type_selection_ids: - result = OR([result, FALSE_LEAF]) + result = AND([result, [FALSE_LEAF]]) for relation_type_selection_id in relation_type_selection_ids: type_id, is_inverse = relation_type_selection\ From 4d388cfff115286338327ef76350601a77070b6e Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Thu, 29 Jan 2015 17:13:07 +0100 Subject: [PATCH 17/40] [FIX] search in res.partner.relation.type to support translated searches --- partner_relations/model/res_partner.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/partner_relations/model/res_partner.py b/partner_relations/model/res_partner.py index 3372407aa..288802f50 100644 --- a/partner_relations/model/res_partner.py +++ b/partner_relations/model/res_partner.py @@ -103,8 +103,14 @@ class ResPartner(orm.Model): relation_type_selection_ids.append(arg[2]) else: relation_type_selection_ids = relation_type_selection\ - .search(cr, uid, [('name', arg[1], arg[2])], - context=context) + .search( + cr, uid, + [ + '|', + ('type_id.name', arg[1], arg[2]), + ('type_id.name_inverse', arg[1], arg[2]), + ], + context=context) if not relation_type_selection_ids: result = AND([result, [FALSE_LEAF]]) From ad3455c17f480ae34e1307aa35438eef416eba67 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Thu, 29 Jan 2015 17:22:46 +0100 Subject: [PATCH 18/40] [FIX] search both sides of the relation isolated to avoid false positives --- partner_relations/model/res_partner.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/partner_relations/model/res_partner.py b/partner_relations/model/res_partner.py index 288802f50..ff307b36e 100644 --- a/partner_relations/model/res_partner.py +++ b/partner_relations/model/res_partner.py @@ -106,11 +106,18 @@ class ResPartner(orm.Model): .search( cr, uid, [ - '|', ('type_id.name', arg[1], arg[2]), - ('type_id.name_inverse', arg[1], arg[2]), + ('record_type', '=', 'a'), ], context=context) + relation_type_selection_ids.extend( + relation_type_selection.search( + cr, uid, + [ + ('type_id.name_inverse', arg[1], arg[2]), + ('record_type', '=', 'b'), + ], + context=context)) if not relation_type_selection_ids: result = AND([result, [FALSE_LEAF]]) From fd13e6cb14e87e5ada83556b8919b3e2dc166434 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Fri, 6 Feb 2015 13:40:02 +0100 Subject: [PATCH 19/40] [IMP] support != operator --- partner_relations/model/res_partner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/partner_relations/model/res_partner.py b/partner_relations/model/res_partner.py index ff307b36e..4a29cc63d 100644 --- a/partner_relations/model/res_partner.py +++ b/partner_relations/model/res_partner.py @@ -89,7 +89,7 @@ class ResPartner(orm.Model): result = [] for arg in args: if isinstance(arg, tuple) and arg[0] == name: - if arg[1] not in ['=', 'like', 'not like', 'ilike', + if arg[1] not in ['=', '!=', 'like', 'not like', 'ilike', 'not ilike', 'in', 'not in']: raise orm.except_orm( _('Error'), From 8459f66ca348678256e86614f5cc1a2d1371846e Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Fri, 6 Feb 2015 13:58:54 +0100 Subject: [PATCH 20/40] [FIX] actually do something useful with != --- partner_relations/model/res_partner.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/partner_relations/model/res_partner.py b/partner_relations/model/res_partner.py index 4a29cc63d..369fd52ad 100644 --- a/partner_relations/model/res_partner.py +++ b/partner_relations/model/res_partner.py @@ -101,6 +101,17 @@ class ResPartner(orm.Model): if arg[1] == '=' and isinstance(arg[2], (long, int)): relation_type_selection_ids.append(arg[2]) + elif arg[1] == '!=' and isinstance(arg[2], (long, int)): + type_id, is_inverse = relation_type_selection\ + .get_type_from_selection_id( + cr, uid, arg[2]) + result = OR([ + result, + [ + ('relation_all_ids.type_id', '!=', type_id), + ] + ]) + continue else: relation_type_selection_ids = relation_type_selection\ .search( From cae880754013ff14df7e0b374eb0160f84e4a5bf Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Mon, 2 Mar 2015 17:26:29 +0100 Subject: [PATCH 21/40] Add OCA as author of OCA addons In order to get visibility on https://www.odoo.com/apps the OCA board has decided to add the OCA as author of all the addons maintained as part of the association. --- partner_relations/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/partner_relations/__openerp__.py b/partner_relations/__openerp__.py index d978c66d4..f70ab862f 100644 --- a/partner_relations/__openerp__.py +++ b/partner_relations/__openerp__.py @@ -21,7 +21,7 @@ { "name": "Partner relations", "version": "1.1", - "author": "Therp BV", + "author": "Therp BV,Odoo Community Association (OCA)", "complexity": "normal", "description": """ Introduction From a7cd4e1116f24bc64bb5259c6622e9f51230f1aa Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Thu, 19 Feb 2015 16:16:45 -0500 Subject: [PATCH 22/40] Fix added pep8 conventions Put boolean operators at end of lines instead of beggining of newlines Put lambda definition in fields.function def Remove callit lambda, use function directly --- .../model/res_partner_relation.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/partner_relations/model/res_partner_relation.py b/partner_relations/model/res_partner_relation.py index 0c2733eb3..f969ad26e 100644 --- a/partner_relations/model/res_partner_relation.py +++ b/partner_relations/model/res_partner_relation.py @@ -44,8 +44,8 @@ class ResPartnerRelation(Model): '''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', [])): + if (context and 'active_ids' in context and + right_partner_id in context.get('active_ids', [])): return True return False @@ -77,30 +77,31 @@ class ResPartnerRelation(Model): 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): + def get_values(self, 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) + cr, uid, self.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)) + ((self.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 + self.left_partner_id.id + if on_right_partner + else self.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)) + self.date_end and (self.date_end < today)) # is_relation_future - values['is_relation_future'] = this.date_start > today + values['is_relation_future'] = self.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) + (i.id, get_values(i, field_names, arg, context=context)) + for i in self.browse(cr, uid, ids, context=context) ]) def write(self, cr, uid, ids, vals, context=None): From 8ecfd7ea8ba445727984acb59bbb50ebb36d7c25 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Thu, 26 Mar 2015 04:31:39 -0400 Subject: [PATCH 23/40] Remove explicit dependencies on web --- partner_relations/__openerp__.py | 2 -- partner_relations/view/res_partner.xml | 6 ++++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/partner_relations/__openerp__.py b/partner_relations/__openerp__.py index f70ab862f..5f7a9d0d3 100644 --- a/partner_relations/__openerp__.py +++ b/partner_relations/__openerp__.py @@ -69,8 +69,6 @@ date.""", "category": "Customer Relationship Management", "depends": [ 'base', - 'web_m2x_options', - 'web_tree_many2one_clickable', ], "demo": [ "data/demo.xml", diff --git a/partner_relations/view/res_partner.xml b/partner_relations/view/res_partner.xml index 1773859be..bdd4a6776 100644 --- a/partner_relations/view/res_partner.xml +++ b/partner_relations/view/res_partner.xml @@ -1,5 +1,7 @@ + + partner_relations.view_partner_filter @@ -15,6 +17,7 @@
+ partner_relations.view_partner_form @@ -60,7 +63,6 @@ 'readonly': [('type_selection_id','=',False)], }" options="{'create': false, 'create_edit': false}" - widget="many2one_clickable" /> @@ -101,7 +103,6 @@ 'readonly': [('type_selection_id','=',False)], }" options="{'create': false, 'create_edit': false}" - widget="many2one_clickable" /> @@ -128,5 +129,6 @@ res_model="res.partner.relation.all" domain="[('this_partner_id', 'in', active_ids)]" key2="client_action_multi" /> +
From 2e7d596cf4bfe1ad4be06162e6b9e73b2c0411ce Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Thu, 26 Mar 2015 04:38:21 -0400 Subject: [PATCH 24/40] Fix v8 api calls --- partner_relations/model/res_partner_relation.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/partner_relations/model/res_partner_relation.py b/partner_relations/model/res_partner_relation.py index f969ad26e..667e07e2c 100644 --- a/partner_relations/model/res_partner_relation.py +++ b/partner_relations/model/res_partner_relation.py @@ -80,8 +80,7 @@ class ResPartnerRelation(Model): def get_values(self, dummy_field_names, dummy_arg, context=None): '''Get computed values for record''' values = {} - on_right_partner = self._on_right_partner( - cr, uid, self.right_partner_id.id, context=context) + on_right_partner = self._on_right_partner(self.right_partner_id.id) # type_selection_id values['type_selection_id'] = ( ((self.type_id.id) * 10) + (on_right_partner and 1 or 0)) From d1c7606b8e68a5d6d4a0e480c2c348556d80acc0 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Thu, 26 Mar 2015 05:15:25 -0400 Subject: [PATCH 25/40] Replace Table with smart button --- partner_relations/__openerp__.py | 2 +- partner_relations/model/res_partner.py | 30 +++-- partner_relations/view/res_partner.xml | 113 +++--------------- .../view/res_partner_relation.xml | 90 +++++++++++++- 4 files changed, 126 insertions(+), 109 deletions(-) diff --git a/partner_relations/__openerp__.py b/partner_relations/__openerp__.py index 5f7a9d0d3..3ab1a70de 100644 --- a/partner_relations/__openerp__.py +++ b/partner_relations/__openerp__.py @@ -75,8 +75,8 @@ date.""", ], "data": [ "view/res_partner_relation_all.xml", - 'view/res_partner.xml', 'view/res_partner_relation.xml', + 'view/res_partner.xml', 'view/res_partner_relation_type.xml', 'view/menu.xml', 'security/ir.model.access.csv', diff --git a/partner_relations/model/res_partner.py b/partner_relations/model/res_partner.py index 369fd52ad..80edb05b2 100644 --- a/partner_relations/model/res_partner.py +++ b/partner_relations/model/res_partner.py @@ -20,15 +20,26 @@ # ############################################################################## import time -from openerp.osv import orm, fields +from openerp import osv, models, fields, exceptions, api from openerp.osv.expression import is_leaf, AND, OR, FALSE_LEAF from openerp.tools import DEFAULT_SERVER_DATE_FORMAT from openerp.tools.translate import _ -class ResPartner(orm.Model): +class ResPartner(models.Model): _inherit = 'res.partner' + relation_count = fields.Integer( + 'Relation Count', + compute="_count_relations" + ) + + @api.one + @api.depends("relation_ids") + def _count_relations(self): + """Count the number of relations this partner has for Smart Button""" + self.relation_count = len(self.relation_ids) + def _get_relation_ids_select(self, cr, uid, ids, field_name, arg, context=None): '''return the partners' relations as tuple @@ -91,8 +102,7 @@ class ResPartner(orm.Model): if isinstance(arg, tuple) and arg[0] == name: if arg[1] not in ['=', '!=', 'like', 'not like', 'ilike', 'not ilike', 'in', 'not in']: - raise orm.except_orm( - _('Error'), + raise exceptions.ValidationError( _('Unsupported search operand "%s"') % arg[1]) relation_type_selection_ids = [] @@ -199,7 +209,7 @@ class ResPartner(orm.Model): return result _columns = { - 'relation_ids': fields.function( + 'relation_ids': osv.fields.function( lambda self, *args, **kwargs: self._get_relation_ids( *args, **kwargs), fnct_inv=_set_relation_ids, @@ -207,33 +217,33 @@ class ResPartner(orm.Model): string='Relations', selectable=False, ), - 'relation_all_ids': fields.one2many( + 'relation_all_ids': osv.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( + 'search_relation_id': osv.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( + 'search_relation_partner_id': osv.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( + 'search_relation_date': osv.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( + 'search_relation_partner_category_id': osv.fields.function( lambda self, cr, uid, ids, *args: dict([ (i, False) for i in ids]), fnct_search=_search_related_partner_category_id, diff --git a/partner_relations/view/res_partner.xml b/partner_relations/view/res_partner.xml index bdd4a6776..4f777d6bd 100644 --- a/partner_relations/view/res_partner.xml +++ b/partner_relations/view/res_partner.xml @@ -23,103 +23,22 @@ res.partner - - - - - - - - - - - - - - -
- - - - - - - - - - -
-
-
+ + +
diff --git a/partner_relations/view/res_partner_relation.xml b/partner_relations/view/res_partner_relation.xml index 2055bc959..0366662be 100644 --- a/partner_relations/view/res_partner_relation.xml +++ b/partner_relations/view/res_partner_relation.xml @@ -1,9 +1,11 @@ + + res.partner.relation -
+ @@ -12,6 +14,60 @@
+ + + res.partner.relation + + + + + + + + + + + + + + code @@ -21,6 +77,7 @@ True Show partners + Show partners action @@ -29,5 +86,36 @@ + + res.partner.relation + + + + + + + + + + + + + + + + Relations + res.partner.relation + form + tree + + + {"search_default_left_partner_id": active_id} + +

+ Record and track your partners' relations. Relations may be linked to other partners with a type either directly or inversely. +

+
+
+
From 06e071f6de2342ddae6390f25be0a36fdd0f66f1 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Thu, 26 Mar 2015 13:45:43 -0400 Subject: [PATCH 26/40] Fix Read Write, Create and Unlink for realtion_all --- partner_relations/model/__init__.py | 16 ++- partner_relations/model/res_partner.py | 13 +- .../model/res_partner_relation.py | 119 ++++++++-------- .../model/res_partner_relation_all.py | 129 ++++++++++++------ .../model/res_partner_relation_type.py | 17 ++- .../res_partner_relation_type_selection.py | 37 +++-- partner_relations/view/res_partner.xml | 2 +- .../view/res_partner_relation.xml | 41 +----- .../view/res_partner_relation_all.xml | 63 ++++++++- 9 files changed, 271 insertions(+), 166 deletions(-) diff --git a/partner_relations/model/__init__.py b/partner_relations/model/__init__.py index b5afe7aa1..9e65bc55b 100644 --- a/partner_relations/model/__init__.py +++ b/partner_relations/model/__init__.py @@ -18,8 +18,22 @@ # along with this program. If not, see . # ############################################################################## + +PADDING = 10 + + +def get_partner_type(partner): + """Get partner type for relation. + + :param partner: a res.partner either a company or not + :return: 'c' for company or 'p' for person + :rtype: str + """ + return 'c' if partner.is_company else 'p' + + 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 +from . import res_partner_relation_type_selection diff --git a/partner_relations/model/res_partner.py b/partner_relations/model/res_partner.py index 80edb05b2..2f1af81e1 100644 --- a/partner_relations/model/res_partner.py +++ b/partner_relations/model/res_partner.py @@ -112,9 +112,10 @@ class ResPartner(models.Model): if arg[1] == '=' and isinstance(arg[2], (long, int)): relation_type_selection_ids.append(arg[2]) elif arg[1] == '!=' and isinstance(arg[2], (long, int)): - type_id, is_inverse = relation_type_selection\ - .get_type_from_selection_id( - cr, uid, arg[2]) + type_id, is_inverse = ( + relation_type_selection.browse(arg[2]) + .get_type_from_selection_id() + ) result = OR([ result, [ @@ -144,9 +145,9 @@ class ResPartner(models.Model): result = AND([result, [FALSE_LEAF]]) for relation_type_selection_id in relation_type_selection_ids: - type_id, is_inverse = relation_type_selection\ - .get_type_from_selection_id( - cr, uid, relation_type_selection_id) + type_id, is_inverse = ( + relation_type_selection_id.get_type_from_selection_id() + ) result = OR([ result, diff --git a/partner_relations/model/res_partner_relation.py b/partner_relations/model/res_partner_relation.py index 667e07e2c..6459b20ae 100644 --- a/partner_relations/model/res_partner_relation.py +++ b/partner_relations/model/res_partner_relation.py @@ -19,12 +19,13 @@ # along with this program. If not, see . # ############################################################################## -from openerp.osv.orm import Model, except_orm -from openerp.osv import fields -from openerp.tools.translate import _ +from openerp import osv, models, fields, api, exceptions, _ -class ResPartnerRelation(Model): +from . import get_partner_type + + +class ResPartnerRelation(models.Model): '''Model res.partner.relation is used to describe all links or relations between partners in the database. @@ -40,6 +41,27 @@ class ResPartnerRelation(Model): _description = 'Partner relation' _order = 'active desc, date_start desc, date_end desc' + left_contact_type = fields.Selection( + lambda s: s.env['res.partner.relation.type']._get_partner_types(), + 'Left Partner Type', + compute='_get_partner_type', + store=True, + ) + + right_contact_type = fields.Selection( + lambda s: s.env['res.partner.relation.type']._get_partner_types(), + 'Right Partner Type', + compute='_get_partner_type', + store=True, + ) + + @api.one + @api.depends('left_partner_id', 'right_partner_id') + def _get_partner_type(self): + + self.left_contact_type = get_partner_type(self.left_partner_id) + self.right_contact_type = get_partner_type(self.right_partner_id) + 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! @@ -49,28 +71,38 @@ class ResPartnerRelation(Model): 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''' + def _correct_vals(self, vals): + """Fill type and left and right partner id, according to whether + 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'] + prts_model = self.env['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)) + prts_model.browse(type_selection_id). + get_type_from_selection_id() + ) vals['type_id'] = type_id - if context.get('active_id'): + if self._context.get('active_id'): if is_reverse: - vals['right_partner_id'] = context['active_id'] + vals['right_partner_id'] = self._context['active_id'] else: - vals['left_partner_id'] = context['active_id'] + vals['left_partner_id'] = self._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'] + if vals.get('other_partner_id'): + if is_reverse: + vals['left_partner_id'] = vals['other_partner_id'] + else: + vals['right_partner_id'] = vals['other_partner_id'] + del vals['other_partner_id'] + if vals.get('contact_type'): + del vals['contact_type'] return vals def _get_computed_fields( @@ -90,12 +122,6 @@ class ResPartnerRelation(Model): if on_right_partner else self.right_partner_id.id ) - # is_relation_expired - today = fields.date.context_today(self, cr, uid, context=context) - values['is_relation_expired'] = ( - self.date_end and (self.date_end < today)) - # is_relation_future - values['is_relation_future'] = self.date_start > today return values return dict([ @@ -103,17 +129,17 @@ class ResPartnerRelation(Model): for i 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) + @api.multi + def write(self, vals): + """Override write to correct values, before being stored.""" + vals = self._correct_vals(vals) + return super(ResPartnerRelation, self).write(vals) - 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) + @api.model + def create(self, vals): + """Override create to correct values, before being stored.""" + vals = self._correct_vals(vals) + return super(ResPartnerRelation, self).create(vals) def on_change_type_selection_id( self, cr, uid, dummy_ids, type_selection_id, context=None): @@ -156,46 +182,32 @@ class ResPartnerRelation(Model): return result _columns = { - 'left_partner_id': fields.many2one( + 'left_partner_id': osv.fields.many2one( 'res.partner', string='Left partner', required=True, auto_join=True, ondelete='cascade'), - 'right_partner_id': fields.many2one( + 'right_partner_id': osv.fields.many2one( 'res.partner', string='Right partner', required=True, auto_join=True, ondelete='cascade'), - 'type_id': fields.many2one( + 'type_id': osv.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( + 'date_start': osv.fields.date('Starting date'), + 'date_end': osv.fields.date('Ending date'), + 'type_selection_id': osv.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( + 'partner_id_display': osv.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'), + 'active': osv.fields.boolean('Active'), } _defaults = { @@ -247,7 +259,7 @@ class ResPartnerRelation(Model): ('id', '!=', this.id), ('left_partner_id', '=', this.left_partner_id.id), ('right_partner_id', '=', this.right_partner_id.id), - ] + ] if this.date_start: domain += ['|', ('date_end', '=', False), ('date_end', '>=', this.date_start)] @@ -255,8 +267,7 @@ class ResPartnerRelation(Model): domain += ['|', ('date_start', '=', False), ('date_start', '<=', this.date_end)] if self.search(cr, uid, domain, context=context): - raise except_orm( - _('Overlapping relation'), + raise exceptions.Warning( _('There is already a similar relation ' 'with overlapping dates')) diff --git a/partner_relations/model/res_partner_relation_all.py b/partner_relations/model/res_partner_relation_all.py index 2f3e71b9d..280749f3e 100644 --- a/partner_relations/model/res_partner_relation_all.py +++ b/partner_relations/model/res_partner_relation_all.py @@ -18,24 +18,26 @@ # along with this program. If not, see . # ############################################################################## -from openerp.osv.orm import Model -from openerp.osv import fields + +from openerp import osv, models, fields, api from openerp.tools import drop_view_if_exists -from .res_partner_relation_type_selection\ - import ResPartnerRelationTypeSelection +from .res_partner_relation_type_selection import \ + ResPartnerRelationTypeSelection +from . import get_partner_type, PADDING -class ResPartnerRelationAll(Model): +class ResPartnerRelationAll(models.AbstractModel): _auto = False _log_access = False _name = 'res.partner.relation.all' + _overlays = 'res.partner.relation' _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: + Don't overwrite this list in your declaration but append in _auto_init: def _auto_init(self, cr, context=None): self._additional_view_fields.append('my_field') @@ -53,61 +55,82 @@ class ResPartnerRelationAll(Model): additional_view_fields = (',' + additional_view_fields)\ if additional_view_fields else '' cr.execute( - '''create or replace view %s as + '''create or replace view %(table)s as select - id * 10 as id, + id * %(padding)d as id, id as relation_id, type_id, cast('a' as char(1)) as record_type, + left_contact_type as contact_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 + type_id * %(padding)d as type_selection_id + %(additional_view_fields)s + from %(underlying_table)s union select - id * 10 + 1, + id * %(padding)d + 1, id, type_id, cast('b' as char(1)), + right_contact_type, 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, - ) + type_id * %(padding)d + 1 + %(additional_view_fields)s + from %(underlying_table)s''' % { + 'table': self._table, + 'padding': PADDING, + 'additional_view_fields': additional_view_fields, + 'underlying_table': 'res_partner_relation', + } ) return super(ResPartnerRelationAll, self)._auto_init( cr, context=context) + @api.one + def get_underlying_object(self): + """Get the record on which this record is overlaid""" + return self.env[self._overlays].browse(self.id / PADDING) + + contact_type = fields.Selection( + lambda s: s.env['res.partner.relation.type']._get_partner_types(), + 'Partner Type', + default=lambda self: self._get_default_contact_type() + ) + _columns = { - 'record_type': fields.selection( + 'record_type': osv.fields.selection( ResPartnerRelationTypeSelection._RECORD_TYPES, 'Record type', readonly=True), - 'relation_id': fields.many2one( + 'relation_id': osv.fields.many2one( 'res.partner.relation', 'Relation', readonly=True), - 'type_id': fields.many2one( + 'type_id': osv.fields.many2one( 'res.partner.relation.type', 'Relation type', readonly=True), - 'type_selection_id': fields.many2one( + 'type_selection_id': osv.fields.many2one( 'res.partner.relation.type.selection', 'Relation type', - readonly=True), - 'this_partner_id': fields.many2one( + ), + 'this_partner_id': osv.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'), + 'other_partner_id': osv.fields.many2one( + 'res.partner', 'Other partner'), + 'date_start': osv.fields.date('Starting date'), + 'date_end': osv.fields.date('Ending date'), } + active = fields.Boolean('Active', default=True) + + def _get_default_contact_type(self): + partner_id = self._context.get('default_this_partner_id') + if partner_id: + partner = self.env['res.partner'].browse(partner_id) + return get_partner_type(partner) + return False def name_get(self, cr, uid, ids, context=None): return dict([ @@ -118,12 +141,40 @@ class ResPartnerRelationAll(Model): )) 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) + @api.one + def write(self, vals): + """divert non-problematic writes to underlying table""" + underlying_objs = self.get_underlying_object() + vals = { + key: val + for key, val in vals.iteritems() + if not self._columns[key].readonly + } + vals['type_selection_id'] = vals.get( + 'type_selection_id', + underlying_objs.type_selection_id.id + ) + return underlying_objs.write(vals) + + @api.model + def create(self, vals): + """divert non-problematic creates to underlying table + + Create a res.partner.relation but return the converted id + """ + vals = { + key: val + for key, val in vals.iteritems() + if not self._columns[key].readonly + } + vals['type_selection_id'] = vals.get( + 'type_selection_id', + False, + ) + res = self.env[self._overlays].create(vals) + return self.browse(res.id * PADDING) + + @api.one + def unlink(self): + """divert non-problematic creates to underlying table""" + return self.get_underlying_object().unlink() diff --git a/partner_relations/model/res_partner_relation_type.py b/partner_relations/model/res_partner_relation_type.py index 6e19eb47e..c23e394af 100644 --- a/partner_relations/model/res_partner_relation_type.py +++ b/partner_relations/model/res_partner_relation_type.py @@ -19,18 +19,23 @@ # along with this program. If not, see . # ############################################################################## -from openerp.osv.orm import Model + from openerp.osv import fields +from openerp import models, api, _ -class ResPartnerRelationType(Model): - '''Model that defines relation types that might exist between partners''' +class ResPartnerRelationType(models.Model): + """Model that defines relation types that might exist between partners""" _name = 'res.partner.relation.type' - _description = 'Parter relation type' + _description = 'Partner Relation Type' _order = 'name' - def _get_partner_types(self, cr, uid, context=None): - return (('c', 'Company'), ('p', 'Person'),) + @api.model + def _get_partner_types(self): + return [ + ('c', _('Company')), + ('p', _('Person')), + ] _columns = { 'name': fields.char( diff --git a/partner_relations/model/res_partner_relation_type_selection.py b/partner_relations/model/res_partner_relation_type_selection.py index e691936cf..e8f8f0a48 100644 --- a/partner_relations/model/res_partner_relation_type_selection.py +++ b/partner_relations/model/res_partner_relation_type_selection.py @@ -27,11 +27,13 @@ ORDER BY ResPartnerRelationTypeSelection.customer_name asc, ResPartnerRelationTypeSelection.caller_name asc; ''' + +from openerp import api 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 +from .res_partner_relation_type import ResPartnerRelationType +from . import PADDING class ResPartnerRelationTypeSelection(orm.Model): @@ -45,13 +47,14 @@ class ResPartnerRelationTypeSelection(orm.Model): _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 + @api.multi + def get_type_from_selection_id(self): + """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) + back the original type id, and about the record type.""" + type_id = self.id / PADDING + is_reverse = (self.id % PADDING) > 0 + return type_id, is_reverse def _auto_init(self, cr, context=None): drop_view_if_exists(cr, self._table) @@ -59,9 +62,9 @@ class ResPartnerRelationTypeSelection(orm.Model): # 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 + '''create or replace view %(table)s as select - id * 10 as id, + id * %(padding)d as id, id as type_id, cast('a' as char(1)) as record_type, name as name, @@ -69,9 +72,9 @@ class ResPartnerRelationTypeSelection(orm.Model): 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 + from %(underlying_table)s union select - id * 10 + 1, + id * %(padding)d + 1, id, cast('b' as char(1)), name_inverse, @@ -79,7 +82,11 @@ class ResPartnerRelationTypeSelection(orm.Model): contact_type_left, partner_category_right, partner_category_left - from res_partner_relation_type''' % self._table) + from %(underlying_table)s''' % { + 'table': self._table, + 'padding': PADDING, + 'underlying_table': 'res_partner_relation_type', + }) return super(ResPartnerRelationTypeSelection, self)._auto_init( cr, context=context) @@ -161,8 +168,8 @@ class ResPartnerRelationTypeSelection(orm.Model): cr, uid, [ ('id', 'in', - map(lambda x: x * 10, relation_ids) + - map(lambda x: x * 10 + 1, inverse_relation_ids)), + map(lambda x: x * PADDING, relation_ids) + + map(lambda x: x * PADDING + 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/view/res_partner.xml b/partner_relations/view/res_partner.xml index 4f777d6bd..d6446b0b4 100644 --- a/partner_relations/view/res_partner.xml +++ b/partner_relations/view/res_partner.xml @@ -34,7 +34,7 @@ 'active_ids': [id], 'active_test': False, }" - name="%(action_res_partner_relation)d" + name="%(action_res_partner_relation_all)d" icon="fa-users"> diff --git a/partner_relations/view/res_partner_relation.xml b/partner_relations/view/res_partner_relation.xml index 0366662be..bd5942b71 100644 --- a/partner_relations/view/res_partner_relation.xml +++ b/partner_relations/view/res_partner_relation.xml @@ -20,7 +20,7 @@ - - @@ -86,36 +78,5 @@ - - res.partner.relation - - - - - - - - - - - - - - - - Relations - res.partner.relation - form - tree - - - {"search_default_left_partner_id": active_id} - -

- Record and track your partners' relations. Relations may be linked to other partners with a type either directly or inversely. -

-
-
- diff --git a/partner_relations/view/res_partner_relation_all.xml b/partner_relations/view/res_partner_relation_all.xml index b05e28d42..ab336ab35 100644 --- a/partner_relations/view/res_partner_relation_all.xml +++ b/partner_relations/view/res_partner_relation_all.xml @@ -1,18 +1,42 @@ + res.partner.relation.all - - - - + + + + + + + res.partner.relation.all @@ -30,5 +54,36 @@ + + + res.partner.relation.all + + + + + + + + + + + + + + + Relations + res.partner.relation.all + form + tree + + + {"search_default_this_partner_id": active_id} + +

+ Record and track your partners' relations. Relations may be linked to other partners with a type either directly or inversely. +

+
+
+
From 1cb76ba1cb3e1623d32bece2222735c1851382a3 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Thu, 26 Mar 2015 14:28:02 -0400 Subject: [PATCH 27/40] Hide inactive relations --- partner_relations/model/res_partner.py | 7 ++- .../model/res_partner_relation.py | 15 +++-- partner_relations/view/menu.xml | 12 +++- partner_relations/view/res_partner.xml | 1 + .../view/res_partner_relation.xml | 55 ++++++++++++------- .../view/res_partner_relation_all.xml | 3 +- 6 files changed, 65 insertions(+), 28 deletions(-) diff --git a/partner_relations/model/res_partner.py b/partner_relations/model/res_partner.py index 2f1af81e1..1a867d62e 100644 --- a/partner_relations/model/res_partner.py +++ b/partner_relations/model/res_partner.py @@ -37,8 +37,11 @@ class ResPartner(models.Model): @api.one @api.depends("relation_ids") def _count_relations(self): - """Count the number of relations this partner has for Smart Button""" - self.relation_count = len(self.relation_ids) + """Count the number of relations this partner has for Smart Button + + Don't count inactive relations. + """ + self.relation_count = len([r for r in self.relation_ids if r.active]) def _get_relation_ids_select(self, cr, uid, ids, field_name, arg, context=None): diff --git a/partner_relations/model/res_partner_relation.py b/partner_relations/model/res_partner_relation.py index 6459b20ae..c3159d438 100644 --- a/partner_relations/model/res_partner_relation.py +++ b/partner_relations/model/res_partner_relation.py @@ -44,24 +44,31 @@ class ResPartnerRelation(models.Model): left_contact_type = fields.Selection( lambda s: s.env['res.partner.relation.type']._get_partner_types(), 'Left Partner Type', - compute='_get_partner_type', + compute='_get_partner_type_any', store=True, ) right_contact_type = fields.Selection( lambda s: s.env['res.partner.relation.type']._get_partner_types(), 'Right Partner Type', - compute='_get_partner_type', + compute='_get_partner_type_any', store=True, ) + any_partner_id = fields.Many2one( + 'res.partner', + string='Partner', + compute='_get_partner_type_any', + ) + @api.one @api.depends('left_partner_id', 'right_partner_id') - def _get_partner_type(self): - + def _get_partner_type_any(self): self.left_contact_type = get_partner_type(self.left_partner_id) self.right_contact_type = get_partner_type(self.right_partner_id) + self.any_partner_id = self.left_partner_id + self.right_partner_id + 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! diff --git a/partner_relations/view/menu.xml b/partner_relations/view/menu.xml index f5b63bcd0..40230348a 100644 --- a/partner_relations/view/menu.xml +++ b/partner_relations/view/menu.xml @@ -1,15 +1,25 @@ + + + + + diff --git a/partner_relations/view/res_partner.xml b/partner_relations/view/res_partner.xml index d6446b0b4..3297815cf 100644 --- a/partner_relations/view/res_partner.xml +++ b/partner_relations/view/res_partner.xml @@ -27,6 +27,7 @@