diff --git a/partner_multi_relation_parent/README.rst b/partner_multi_relation_parent/README.rst new file mode 100644 index 000000000..4ba583e4e --- /dev/null +++ b/partner_multi_relation_parent/README.rst @@ -0,0 +1,65 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +=================================== +Parent contact Mapping in relations +=================================== + +This module maps automatically creates contact relations for contact +addresss of a partner. Inversely, you can create a contact relation between a +person and another partner, and the person will automatically become a +contact address for the other partner. A person may only have one contact +relation, because a partner may only have one parent. + + + +Known issues / Roadmap +====================== + +* hide/forbade the deletion of the "Is contact of" and "Has Contact" installed + relation types + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, +please check there if your issue has already been reported. If you spotted it +first, help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Giovanni Francesco Capalbo +* Ronald Portier + +Do not contact contributors directly about help with questions or problems +concerning this addon, but use the +`community mailing list `_ or the +`appropriate specialized mailinglist `_ +for help, and the bug tracker linked in `Bug Tracker`_ above for +technical issues. + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/partner_multi_relation_parent/__init__.py b/partner_multi_relation_parent/__init__.py new file mode 100644 index 000000000..3e51f5587 --- /dev/null +++ b/partner_multi_relation_parent/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import models diff --git a/partner_multi_relation_parent/__manifest__.py b/partner_multi_relation_parent/__manifest__.py new file mode 100644 index 000000000..4405f8a4e --- /dev/null +++ b/partner_multi_relation_parent/__manifest__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Partner Contact Mapping in relations", + "version": "10.0.1.0.0", + "author": "Therp BV,Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "Customer Relationship Management", + "summary": "Show partner addresses also as relations", + "depends": [ + 'partner_multi_relation', + ], + "data": [ + 'data/data.xml', + 'views/res_partner_relation_type.xml', + ], + "installable": True, +} diff --git a/partner_multi_relation_parent/data/data.xml b/partner_multi_relation_parent/data/data.xml new file mode 100644 index 000000000..b53179884 --- /dev/null +++ b/partner_multi_relation_parent/data/data.xml @@ -0,0 +1,48 @@ + + + + + Is contact of + Has contact + contact + + restrict + + + + Is invoice address for + Has invoice address + invoice + + restrict + + + + Is delivery address for + Has delivery address + delivery + + restrict + + + + Is alternative address for + Has alternative address + other + + restrict + + + diff --git a/partner_multi_relation_parent/i18n/nl.po b/partner_multi_relation_parent/i18n/nl.po new file mode 100644 index 000000000..e4716e4cf --- /dev/null +++ b/partner_multi_relation_parent/i18n/nl.po @@ -0,0 +1,134 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * partner_multi_relation_parent +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-11-01 09:32+0000\n" +"PO-Revision-Date: 2017-11-01 09:32+0000\n" +"Last-Translator: <>\n" +"Language-Team: Dutch (https://www.transifex.com/oca/teams/23907/nl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: nl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: partner_multi_relation_parent +#: model:ir.model,name:partner_multi_relation_parent.model_res_partner_relation_all +msgid "All (non-inverse + inverse) relations between partners" +msgstr "Alle connecties (van beide kanten) tussen relaties" + +#. module: partner_multi_relation_parent +#: selection:res.partner.relation.type,partner_type:0 +msgid "Contact" +msgstr "Contact" + +#. module: partner_multi_relation_parent +#: code:addons/partner_multi_relation_parent/models/res_partner_relation_all.py:145 +#, python-format +msgid "Creating a relation for address with type %s is not allowed at self time." +msgstr "Het leggen van een connectie tussen partners van type %s is op dit moment niet toegestaan." + +#. module: partner_multi_relation_parent +#: code:addons/partner_multi_relation_parent/models/res_partner_relation_all.py:74 +#, python-format +msgid "Creating a relation for address with type %s is not allowed at this time." +msgstr "Het aanmaken van een adres met type %s is op dit moment niet toegestaan." + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_parent.relation_type_parent_other +msgid "Has alternative address" +msgstr "Heeft alternatief adres" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_parent.relation_type_parent_contact +msgid "Has contact" +msgstr "Heeft contact" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_parent.relation_type_parent_delivery +msgid "Has delivery address" +msgstr "Heeft afleveradres" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_parent.relation_type_parent_invoice +msgid "Has invoice address" +msgstr "Heeft factuuradres" + +#. module: partner_multi_relation_parent +#: model:ir.model.fields,help:partner_multi_relation_parent.field_res_partner_relation_type_partner_type +msgid "If filled connections will be automatically created when partners of the specified type are linked to a parent.\n" +"Also the parent of the left contact will be updated when connections of this type are added or updated." +msgstr "Indien ingevuld, dan zullen er automatisch connecties aangemaakt worden wanneer een adres van dit type voor een relatie wordt aangemaakt.\n" +"Tevens zal de linker partner automatisch gedefinieerd worden als een adres van dit type voor de relatie aan de rechterkant van de connectie." + +#. module: partner_multi_relation_parent +#: selection:res.partner.relation.type,partner_type:0 +msgid "Invoice address" +msgstr "Factuuradres" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name:partner_multi_relation_parent.relation_type_parent_other +msgid "Is alternative address for" +msgstr "Is een alternatief adres voor" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name:partner_multi_relation_parent.relation_type_parent_contact +msgid "Is contact of" +msgstr "Is contactadres voor" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name:partner_multi_relation_parent.relation_type_parent_delivery +msgid "Is delivery address for" +msgstr "Is afleveradres voor" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name:partner_multi_relation_parent.relation_type_parent_invoice +msgid "Is invoice address for" +msgstr "Is factuuradres voor" + +#. module: partner_multi_relation_parent +#: selection:res.partner.relation.type,partner_type:0 +msgid "Other address" +msgstr "Overig adres" + +#. module: partner_multi_relation_parent +#: model:ir.model,name:partner_multi_relation_parent.model_res_partner +msgid "Partner" +msgstr "Relatie" + +#. module: partner_multi_relation_parent +#: model:ir.model.fields,field_description:partner_multi_relation_parent.field_res_partner_relation_type_partner_type +msgid "Partner Address Type" +msgstr "Soort adres" + +#. module: partner_multi_relation_parent +#: model:ir.model,name:partner_multi_relation_parent.model_res_partner_relation_type +msgid "Partner Relation Type" +msgstr "Type connectie voor de relatie" + +#. module: partner_multi_relation_parent +#: model:ir.model,name:partner_multi_relation_parent.model_res_partner_relation +msgid "Partner relation" +msgstr "Connectie" + +#. module: partner_multi_relation_parent +#: selection:res.partner.relation.type,partner_type:0 +msgid "Shipping address" +msgstr "Afleveradres" + +#. module: partner_multi_relation_parent +#: model:ir.model.fields,field_description:partner_multi_relation_parent.field_res_partner_relation_type_partner_synchronization_active +msgid "Synchronize relations and addresses" +msgstr "Synchroniseer connecties en adressen" + +#. module: partner_multi_relation_parent +#: model:ir.model.fields,help:partner_multi_relation_parent.field_res_partner_relation_type_partner_synchronization_active +msgid "This field can only be true if Partner Address Type filled.\n" +" When enabled will make sure that for all these connections the left partner will have the right partner as parent." +msgstr "Dit veld kan alleen aangevinkt worden als soort adres voor relatie gevuld is." +" Indien aangevinkt, dan zullen voor alle connecties van dit type de linker partner als adres verschijnen bij de rechter partner." + diff --git a/partner_multi_relation_parent/i18n/partner_multi_relation_parent.pot b/partner_multi_relation_parent/i18n/partner_multi_relation_parent.pot new file mode 100644 index 000000000..be8656c92 --- /dev/null +++ b/partner_multi_relation_parent/i18n/partner_multi_relation_parent.pot @@ -0,0 +1,131 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * partner_multi_relation_parent +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-11-01 09:32+0000\n" +"PO-Revision-Date: 2017-11-01 09:32+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_multi_relation_parent +#: model:ir.model,name:partner_multi_relation_parent.model_res_partner_relation_all +msgid "All (non-inverse + inverse) relations between partners" +msgstr "" + +#. module: partner_multi_relation_parent +#: selection:res.partner.relation.type,partner_type:0 +msgid "Contact" +msgstr "" + +#. module: partner_multi_relation_parent +#: code:addons/partner_multi_relation_parent/models/res_partner_relation_all.py:145 +#, python-format +msgid "Creating a relation for address with type %s is not allowed at self time." +msgstr "" + +#. module: partner_multi_relation_parent +#: code:addons/partner_multi_relation_parent/models/res_partner_relation_all.py:74 +#, python-format +msgid "Creating a relation for address with type %s is not allowed at this time." +msgstr "" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_parent.relation_type_parent_other +msgid "Has alternative address" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_parent.relation_type_parent_contact +msgid "Has contact" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_parent.relation_type_parent_delivery +msgid "Has delivery address" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_parent.relation_type_parent_invoice +msgid "Has invoice address" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:ir.model.fields,help:partner_multi_relation_parent.field_res_partner_relation_type_partner_type +msgid "If filled connections will be automatically created when partners of the specified type are linked to a parent.\n" +"Also the parent of the left contact will be updated when connections of this type are added or updated." +msgstr "" + +#. module: partner_multi_relation_parent +#: selection:res.partner.relation.type,partner_type:0 +msgid "Invoice address" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name:partner_multi_relation_parent.relation_type_parent_other +msgid "Is alternative address for" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name:partner_multi_relation_parent.relation_type_parent_contact +msgid "Is contact of" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name:partner_multi_relation_parent.relation_type_parent_delivery +msgid "Is delivery address for" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:res.partner.relation.type,name:partner_multi_relation_parent.relation_type_parent_invoice +msgid "Is invoice address for" +msgstr "" + +#. module: partner_multi_relation_parent +#: selection:res.partner.relation.type,partner_type:0 +msgid "Other address" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:ir.model,name:partner_multi_relation_parent.model_res_partner +msgid "Partner" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:ir.model.fields,field_description:partner_multi_relation_parent.field_res_partner_relation_type_partner_type +msgid "Partner Address Type" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:ir.model,name:partner_multi_relation_parent.model_res_partner_relation_type +msgid "Partner Relation Type" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:ir.model,name:partner_multi_relation_parent.model_res_partner_relation +msgid "Partner relation" +msgstr "" + +#. module: partner_multi_relation_parent +#: selection:res.partner.relation.type,partner_type:0 +msgid "Shipping address" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:ir.model.fields,field_description:partner_multi_relation_parent.field_res_partner_relation_type_partner_synchronization_active +msgid "Synchronize relations and addresses" +msgstr "" + +#. module: partner_multi_relation_parent +#: model:ir.model.fields,help:partner_multi_relation_parent.field_res_partner_relation_type_partner_synchronization_active +msgid "This field can only be true if Partner Address Type filled.\n" +" When enabled will make sure that for all these connections the left partner will have the right partner as parent." +msgstr "" + diff --git a/partner_multi_relation_parent/models/__init__.py b/partner_multi_relation_parent/models/__init__.py new file mode 100644 index 000000000..d04979419 --- /dev/null +++ b/partner_multi_relation_parent/models/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import res_partner_relation_type +from . import res_partner_relation_all +from . import res_partner_relation diff --git a/partner_multi_relation_parent/models/res_partner_relation.py b/partner_multi_relation_parent/models/res_partner_relation.py new file mode 100644 index 000000000..b36c293e2 --- /dev/null +++ b/partner_multi_relation_parent/models/res_partner_relation.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from openerp import _, api, models +from odoo.exceptions import ValidationError + + +class ResPartnerRelation(models.Model): + _inherit = 'res.partner.relation' + + @api.multi + def write(self, vals): + """Synchronize parent_id in left partner with connection. + + - If changed to non contact type, clear parent_id in partner; + - If changed to contact type, set parent_id and contact type + in partner. + """ + type_model = self.env['res.partner.relation.type'] + partner_model = self.env['res.partner'] + vals_type = 'type_id' in vals and \ + type_model.browse(vals['type_id']) or False + vals_left_partner = 'left_partner_id' in vals and \ + partner_model.browse(vals['left_partner_id']) or False + vals_right_partner = 'right_partner_id' in vals and \ + partner_model.browse(vals['right_partner_id']) or False + for this in self: + # Determine old and new type + old_type = this.type_id + new_type = vals_type or old_type + # First handle simple case: no address type involved + if not old_type.partner_type and not new_type.partner_type: + super(ResPartnerRelation, this).write(vals) + continue + # Store existing values + existing_left_partner = this.left_partner_id + existing_right_partner = this.right_partner_id + left_partner = vals_left_partner or existing_left_partner + right_partner = vals_right_partner or existing_right_partner + # Second relatively simple case is where non address + # connection is replaced by address connection + if not old_type.partner_type: + # Unlink existing connection + super(ResPartnerRelation, this).unlink() + # Create new connection + left_partner.write({ + 'type': new_type.partner_type, + 'parent_id': right_partner.id, + }) + continue + # Third handle case where address connection is changed into + # regular connection: + if not new_type.partner_type: + # Clear existing parent: + existing_left_partner.write({ + 'parent_id': False, + }) + # Now create new connection: + vals['left_partner_id'] = left_partner.id + vals['type_id'] = new_type.id + vals['right_partner_id'] = right_partner.id + super(ResPartnerRelation, this).create(vals) + continue + # If we get here, both old and new connection are for address: + # Check wether new type is already allowed: + if not new_type.partner_synchronization_active: + raise ValidationError( + _("Creating a relation for address with type %s is" + " not allowed at this time.") % + (new_type.partner_type, )) + # If left partner changed, clear parent on left partner: + if left_partner != existing_left_partner: + existing_left_partner.write({ + 'parent_id': False, + }) + left_partner.write({ + 'type': new_type.partner_type, + 'parent_id': right_partner.id, + }) + return True + + @api.multi + def unlink(self): + """Unlinking relations for address, means clearing parent_id.""" + for this in self: + if this.type_id.partner_type: + this.left_partner_id.write({ + 'parent_id': False, + }) + continue + super(ResPartnerRelation, this).unlink() + return True + + @api.model + def create(self, vals): + """Creating a relation for an address means updating parent.""" + type_model = self.env['res.partner.relation.type'] + partner_model = self.env['res.partner'] + relation_type = type_model.browse(vals['type_id']) + if relation_type.partner_type: + if not relation_type.partner_synchronization_active: + raise ValidationError( + _("Creating a relation for address with type %s is" + " not allowed at this time.") % + (relation_type.partner_type, )) + left_partner = partner_model.browse(vals['left_partner_id']) + left_partner.write({ + 'type': relation_type.partner_type, + 'parent_id': vals['right_partner_id'], + }) + # Return the left partner. + # Create in res_partner_relation_all will know what to do. + return left_partner + return super(ResPartnerRelation, self).create(vals) diff --git a/partner_multi_relation_parent/models/res_partner_relation_all.py b/partner_multi_relation_parent/models/res_partner_relation_all.py new file mode 100644 index 000000000..c219ccd5a --- /dev/null +++ b/partner_multi_relation_parent/models/res_partner_relation_all.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from openerp import _, api, models +from openerp.exceptions import ValidationError + +from openerp.addons.partner_multi_relation.models.res_partner_relation_all \ + import register_select_specification + + +# Register relation from address partner to parent partner: +register_select_specification( + base_name='partner_address', + is_inverse=False, + select_sql="""\ +SELECT + (partner.id * %%(padding)s) + %(key_offset)s as ID, + 'res.partner' AS res_model, + partner.id AS res_id, + partner.id AS left_partner_id, + partner.parent_id AS right_partner_id, + rprt.id AS type_id, + NULL AS date_start, + NULL AS date_end, + %(is_inverse)s AS is_inverse + FROM res_partner partner + JOIN res_partner_relation_type rprt ON partner.type = rprt.partner_type + WHERE NOT partner.parent_id IS NULL + AND rprt.partner_synchronization_active""") + +# Register relation from parent partner to address partner: +register_select_specification( + base_name='partner_address', + is_inverse=True, + select_sql="""\ +SELECT + (partner.id * %%(padding)s) + %(key_offset)s as ID, + 'res.partner' AS res_model, + partner.id AS res_id, + partner.parent_id AS left_partner_id, + partner.id AS right_partner_id, + rprt.id AS type_id, + NULL AS date_start, + NULL AS date_end, + %(is_inverse)s AS is_inverse + FROM res_partner partner + JOIN res_partner_relation_type rprt ON partner.type = rprt.partner_type + WHERE NOT partner.parent_id IS NULL + AND rprt.partner_synchronization_active""") + + +class ResPartnerRelationAll(models.AbstractModel): + """Show addresses as relations if so configured.""" + _inherit = 'res.partner.relation.all' + + def _get_active_selects(self): + """Return selects actually to be used. + + Selects are registered from all modules PRESENT. But should only be + used to build view if module actually INSTALLED. + """ + return super(ResPartnerRelationAll, self)._get_active_selects() +\ + ['partner_address', 'partner_address_inverse'] + + @api.model + def _compute_base_name(self, type_selection): + """This will be overridden for each inherit model.""" + if type_selection.type_id.partner_type: + return 'partner_address' + return super(ResPartnerRelationAll, self)._compute_base_name( + type_selection) + + @api.model + def create_resource(self, vals, type_selection): + if self._compute_base_name(type_selection) != 'partner_address': + return super(ResPartnerRelationAll, self).create_resource( + vals, type_selection) + partner_model = self.env['res.partner'] + relation_type = type_selection.type_id + if not relation_type.partner_synchronization_active: + raise ValidationError( + _("Creating a relation for address with type %s is" + " not allowed at this time.") % + (relation_type.partner_type, )) + left_partner = partner_model.browse(vals['left_partner_id']) + left_partner.write({ + 'type': relation_type.partner_type, + 'parent_id': vals['right_partner_id']}) + return left_partner + + @api.multi + def write_resource(self, base_resource, vals): + """Synchronize parent_id in left partner with connection. + + - If changed to non contact type, clear parent_id in partner; + - If changed to contact type, set parent_id and contact type + in partner. + """ + self.ensure_one() + # Determine old and new type + type_model = self.env['res.partner.relation.type'] + vals_type = 'type_id' in vals and \ + type_model.browse(vals['type_id']) or False + type_selection = self.type_selection_id + old_type = type_selection.type_id + new_type = vals_type or old_type + # If neither old, nor new type are for partner address, + # write can/should be handled by super method: + if not old_type.partner_type and not new_type.partner_type: + return super(ResPartnerRelationAll, self).write_resource( + base_resource, vals) + # We have to handle partner address: + partner_model = self.env['res.partner'] + vals_left_partner = 'left_partner_id' in vals and \ + partner_model.browse(vals['left_partner_id']) or False + vals_right_partner = 'right_partner_id' in vals and \ + partner_model.browse(vals['right_partner_id']) or False + # Store existing values + existing_left_partner = base_resource + existing_right_partner = base_resource.parent_id + left_partner = vals_left_partner or existing_left_partner + right_partner = vals_right_partner or existing_right_partner + # Relatively simple case is where non address + # connection is replaced by address connection + if not old_type.partner_type: + # Unlink existing connection + super(ResPartnerRelationAll, self).unlink() + # Create new connection + left_partner.write({ + 'type': new_type.partner_type, + 'parent_id': right_partner.id}) + return True + # Handle case where address connection is changed into + # regular connection: + if not new_type.partner_type: + # Clear existing parent: + existing_left_partner.write({'parent_id': False}) + # Now create new connection: + relation_model = self.env['res.partner.relation'] + vals['left_partner_id'] = left_partner.id + vals['type_id'] = new_type.id + vals['right_partner_id'] = right_partner.id + relation_model.create(vals) + return True + # If we get here, both old and new connection are for address: + # Check wether new type is already allowed: + if not new_type.partner_synchronization_active: + raise ValidationError( + _("Creating a relation for address with type %s is" + " not allowed at self time.") % + (new_type.partner_type, )) + # If left partner changed, clear parent on left partner: + existing_left_partner.write({'parent_id': False}) + left_partner.write({ + 'type': new_type.partner_type, + 'parent_id': right_partner.id}) + return True + + @api.multi + def unlink_resource(self, base_resource): + """Deleting an address connection is clearing parent_id.""" + self.ensure_one() + type_selection = self.type_selection_id + if self._compute_base_name(type_selection) != 'partner_address': + return super(ResPartnerRelationAll, self).unlink_resource( + base_resource) + base_resource.write({'parent_id': False}) diff --git a/partner_multi_relation_parent/models/res_partner_relation_type.py b/partner_multi_relation_parent/models/res_partner_relation_type.py new file mode 100644 index 000000000..0716b1be8 --- /dev/null +++ b/partner_multi_relation_parent/models/res_partner_relation_type.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +"""Define synchronization between relations and addresses.""" +from openerp import api, fields, models + + +class ResPartnerRelationType(models.Model): + """Model that defines relation types that might exist between partners""" + _inherit = 'res.partner.relation.type' + + partner_type = fields.Selection( + # TODO: determine automatically from selection in res.partner + selection=[ + ('contact', 'Contact'), + ('invoice', 'Invoice address'), + ('delivery', 'Shipping address'), + ('other', 'Other address'), + ], + string='Partner Address Type', + readonly=True, + help="If filled connections will be automatically created when" + " partners of the specified type are linked to a parent.\n" + "Also the parent of the left contact will be updated when" + " connections of this type are added or updated." + ) + partner_synchronization_active = fields.Boolean( + string="Synchronize relations and addresses", + default=False, + help="This field can only be true if Partner Address Type filled.\n" + " When enabled will make sure that for all these connections" + " the left partner will have the right partner as parent." + ) + + @api.multi + def write(self, vals): + """For address relation types, you can only change active flag.""" + for this in self: + if not this.partner_type: + super(ResPartnerRelationType, this).write(vals) + continue + if 'partner_synchronization_active' not in vals: + continue # Do nothing + super(ResPartnerRelationType, this).write({ + 'partner_synchronization_active': + vals['partner_synchronization_active'] + }) + return True diff --git a/partner_multi_relation_parent/static/description/icon.png b/partner_multi_relation_parent/static/description/icon.png new file mode 100644 index 000000000..c6863a3e4 Binary files /dev/null and b/partner_multi_relation_parent/static/description/icon.png differ diff --git a/partner_multi_relation_parent/tests/__init__.py b/partner_multi_relation_parent/tests/__init__.py new file mode 100644 index 000000000..3be452732 --- /dev/null +++ b/partner_multi_relation_parent/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import test_partner_multi_relation_parent diff --git a/partner_multi_relation_parent/tests/test_partner_multi_relation_parent.py b/partner_multi_relation_parent/tests/test_partner_multi_relation_parent.py new file mode 100644 index 000000000..85c9aea8e --- /dev/null +++ b/partner_multi_relation_parent/tests/test_partner_multi_relation_parent.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo.addons.partner_multi_relation.tests.test_partner_relation_common \ + import TestPartnerRelationCommon + + +class TestPartnerMultiRelationParent(TestPartnerRelationCommon): + + # By default it will be false, this makes it run after the modules are + # installed + post_install = True + + def count_relations( + self, this_partner_id=False, type_id=False, + other_partner_id=False, any_partner_id=False): + """Count relations satifying criteria passed.""" + domain = [] + if this_partner_id: + domain.append(('this_partner_id', '=', this_partner_id)) + if type_id: + domain.append(('type_id', '=', type_id)) + if other_partner_id: + domain.append(('other_partner_id', '=', other_partner_id)) + if any_partner_id: + domain.append(('any_partner_id', '=', any_partner_id)) + if not domain: + return 0 + relation_all_model = self.env['res.partner.relation.all'] + return relation_all_model.search_count(domain) + + def test_address_changes(self): + """Test wether changes in address are reflected in relations.""" + # make sure that existing contact relation type enabled for sync: + contact_type = self.env.ref( + 'partner_multi_relation_parent.relation_type_parent_contact' + ) + contact_type.write({'partner_synchronization_active': True}) + # create a contact for ngo , partner n03 + ngo_contact = self.partner_model.create({ + 'name': '03 NGO ACCOUNTANT', + 'is_company': False, + 'ref': 'PR03C01', + 'type': 'contact', + 'parent_id': self.partner_03_ngo.id + }) + self.assertEqual( + self.count_relations( + this_partner_id=ngo_contact.id, + type_id=contact_type.id, + other_partner_id=ngo_contact.parent_id.id + ), 1) + # then modify partner and verify it + old_parent_id = ngo_contact.parent_id.id + ngo_contact.write( + {'parent_id': self.partner_02_company.id} + ) + # check no more relations with old_parent + self.assertEqual( + self.count_relations( + this_partner_id=ngo_contact.id, + type_id=contact_type.id, + other_partner_id=old_parent_id + ), 0) + # check relations are there with current parent + self.assertEqual( + self.count_relations( + this_partner_id=ngo_contact.id, + type_id=contact_type.id, + other_partner_id=ngo_contact.parent_id.id + ), 1) + # delete NGO ACCOUNTANT + old_id = ngo_contact.id + old_parent_id = ngo_contact.parent_id.id + ngo_contact.unlink() + self.assertEqual( + self.count_relations( + this_partner_id=old_id, + type_id=contact_type.id, + other_partner_id=old_parent_id + ), 0) + + def test_relation_type_changes(self): + """Test wether changes in address are reflected in relations.""" + # make sure that existing delivery relation type NOT enabled for sync: + delivery_type = self.env.ref( + 'partner_multi_relation_parent.relation_type_parent_delivery' + ) + delivery_type.write({'partner_synchronization_active': False}) + # create a delivery address for ngo , partner n03 + ngo_delivery = self.partner_model.create({ + 'name': '03 NGO Delivery Address', + 'is_company': False, + 'ref': 'PR03D01', + 'type': 'delivery', + 'parent_id': self.partner_03_ngo.id + }) + self.assertEqual( + self.count_relations( + this_partner_id=ngo_delivery.id, + type_id=delivery_type.id, + other_partner_id=ngo_delivery.parent_id.id + ), 0) + # after enabling delivery relation type, relations should be there: + delivery_type.write({'partner_synchronization_active': True}) + self.assertEqual( + self.count_relations( + this_partner_id=ngo_delivery.id, + type_id=delivery_type.id, + other_partner_id=ngo_delivery.parent_id.id + ), 1) + + def test_relation_changes(self): + """Test wether changes in relation are reflected in address. + + NB: left partner is the address partner, right partner the parent. + """ + # make sure that existing contact relation type enabled for sync: + contact_type = self.env.ref( + 'partner_multi_relation_parent.relation_type_parent_contact') + contact_type.write({'partner_synchronization_active': True}) + # create a contact address, to be linked to ngo partner: + ngo_contact = self.partner_model.create({ + 'name': '03 NGO ACCOUNTANT', + 'is_company': False, + 'ref': 'PR03C01', + }) + # Now create a contact address relation: + ngo_relation = self.relation_all_model.create({ + 'this_partner_id': ngo_contact.id, + 'type_id': contact_type.id, + 'other_partner_id': self.partner_03_ngo.id}) + # Now the contact address should be linked to parent as contact: + self.assertEqual(ngo_contact.parent_id.name, self.partner_03_ngo.name) + self.assertEqual(ngo_contact.type, 'contact') + # Now link the contact address to another company: + old_parent_id = ngo_contact.parent_id.id + ngo_relation.write({'other_partner_id': self.partner_02_company.id}) + # check no more relations with old_parent + self.assertEqual( + self.count_relations( + this_partner_id=ngo_contact.id, + other_partner_id=old_parent_id), 0) + # check relations are there with current parent + self.assertEqual( + self.count_relations( + this_partner_id=ngo_contact.id, + other_partner_id=ngo_contact.parent_id.id), 1) + # Remove the relation altoghether: + old_parent_id = ngo_contact.parent_id.id + ngo_relation.unlink() + self.assertEqual( + self.count_relations( + this_partner_id=ngo_contact.id, + other_partner_id=old_parent_id), 0) + self.assertEqual(ngo_contact.parent_id.id, False) diff --git a/partner_multi_relation_parent/views/res_partner_relation_type.xml b/partner_multi_relation_parent/views/res_partner_relation_type.xml new file mode 100644 index 000000000..3770f066d --- /dev/null +++ b/partner_multi_relation_parent/views/res_partner_relation_type.xml @@ -0,0 +1,18 @@ + + + + + res.partner.relation.type + + + + + + + + + +