diff --git a/base_partner_merge/README.rst b/base_partner_merge/README.rst index fd216e9db..62bd37b74 100644 --- a/base_partner_merge/README.rst +++ b/base_partner_merge/README.rst @@ -2,20 +2,38 @@ :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 -============================ -Deduplicate contact (No CRM) -============================ +======================== +Deduplicate contacts OCA +======================== -This module installs the deduplicate wizard from CRM without the dependency on CRM. +This module installs the deduplicate wizard from Odoo CRM, but without the +dependency on the CRM module and with some extra features. -If you have CRM installed you don't need this module. +The extra features are: +- Can be installed with or without the CRM module +- Deduplicate also on `ref` (partner reference) +- To deduplicate only a subset of partners (eg. one category), the context + variable `extra_domain` may contain a domain string to search on before + deduplicating. (TODO: offer this in the wizard) +- A function `deduplicate_on_field(self, field, domain=[]):` is added to the + `res.partner` object. It takes the field to deduplicate on as a parameter, + as well as the domain mentioned above. It can be called from `ir.cron` + Automated Actions. Installation ============ -To install this module, you need to have crm module present (but not installed). -This is because we reuse the existing wizard without installing CRM. +To install this module, you need to have `crm` module present on the system. +This is because we reuse the existing code from Odoo CRM. + + +Known issues +============ + +If this module is installed, `crm` module installation gives an error. +Workaround for this is to remove this module, install `crm`, then install +this module again. Bug Tracker @@ -34,6 +52,7 @@ Contributors ------------ * Charbel Jacquin +* Holger Brunn * Tom Blauwendraat * Terrence Nzaywa @@ -41,8 +60,9 @@ Author ------ Yannick Vaucher -Based on Holger Brunn's idea. -Backport to 8.0 by Tom Blauwendraat and Terrence Nzaywa +Based on Holger Brunn's idea +Backported to 8.0 by Tom Blauwendraat and Terrence Nzaywa +Features added by Tom Blauwendraat Maintainer ---------- diff --git a/base_partner_merge/__init__.py b/base_partner_merge/__init__.py index c0f8ea2bd..1287bea19 100644 --- a/base_partner_merge/__init__.py +++ b/base_partner_merge/__init__.py @@ -1,3 +1,5 @@ # -*- coding: utf-8 -*- + from . import base_partner_merge # NOQA from .hooks import post_load_hook +from . import models diff --git a/base_partner_merge/__openerp__.py b/base_partner_merge/__openerp__.py index dd6e615fe..115b4aae8 100644 --- a/base_partner_merge/__openerp__.py +++ b/base_partner_merge/__openerp__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- { - 'name': "Deduplicate Contacts (No CRM)", - 'author': "Camptocamp,Odoo Community Association (OCA)", + 'name': "Deduplicate Contacts (OCA)", + 'author': "Camptocamp,Sunflower IT,Odoo Community Association (OCA)", 'category': 'Generic Modules/Base', 'version': '8.0.1.0.0', 'license': 'AGPL-3', diff --git a/base_partner_merge/hooks.py b/base_partner_merge/hooks.py index 7e3dbd9c0..487715229 100644 --- a/base_partner_merge/hooks.py +++ b/base_partner_merge/hooks.py @@ -6,7 +6,8 @@ from openerp.models import MetaModel def post_load_hook(): - """We try to be smart here: If the crm module is to be loaded too + """ + We try to be smart here: If the crm module is to be loaded too (or is already loaded), we remove our own models again in order not to clash with the CRM ones: https://github.com/OCA/partner-contact/issues/283 """ @@ -15,6 +16,7 @@ def post_load_hook(): if 'graph' in frame.f_locals: graph = frame.f_locals['graph'] package = frame.f_locals['package'] + package.data['data'].remove('views/base_partner_merge.xml') if any(p.name == 'crm' for p in graph): # so crm is installed, then we need to remove your model # from the list of models to be registered @@ -22,5 +24,7 @@ def post_load_hook(): # to be ditched (if crm is in their mro) MetaModel.module_to_models['base_partner_merge'] = [] # and in this case, we also don't want to load our xml files + else: + # if crm is not installed, we package.data['data'].remove('views/base_partner_merge.xml') break diff --git a/base_partner_merge/tests/__init__.py b/base_partner_merge/tests/__init__.py index b4368a398..035d2bbec 100644 --- a/base_partner_merge/tests/__init__.py +++ b/base_partner_merge/tests/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -from . import test_merge \ No newline at end of file +from . import test_merge diff --git a/base_partner_merge/tests/test_merge.py b/base_partner_merge/tests/test_merge.py index 842b4aa72..ebb86223b 100644 --- a/base_partner_merge/tests/test_merge.py +++ b/base_partner_merge/tests/test_merge.py @@ -2,9 +2,9 @@ # © 2017 Sunflower IT # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -import os from openerp.tests.common import TransactionCase + class PartnerMergeTestCase(TransactionCase): """Tests for Partner Merge""" @@ -13,40 +13,52 @@ class PartnerMergeTestCase(TransactionCase): self.partner = self.env['res.partner'] self.merge_wizard = \ self.env['base.partner.merge.automatic.wizard'] + self.donald_domain = [('name', '=', 'Donald Duck')] + self.mickey_domain = [('name', '=', 'Mickey Mouse')] - def test_10_all_functionality(self): - # Delete all Donald Ducks - donald_domain = [('name', '=', 'Donald Duck')] - self.partner.search(donald_domain).unlink() - - # Create two partners called Donald Duck - partner_donald = self.partner.create({ - 'name': 'Donald Duck', - 'email': 'donald@sunflowerweb.nl', - }) - partner_donald2 = self.partner.create({ - 'name': 'Donald Duck', - 'email': 'donald@therp.nl', - }) + def _unlink_all(self): + self.partner.search(self.donald_domain).unlink() + self.partner.search(self.mickey_domain).unlink() - # Test if there are two Donald Ducks - donalds = self.partner.search(donald_domain) - self.assertEquals(len(donalds), 2) + def _count_donalds_mickeys(self, donalds, mickeys): + self.assertEquals( + len(self.partner.search(self.donald_domain)), donalds) + self.assertEquals( + len(self.partner.search(self.mickey_domain)), mickeys) - # Merge them, - wizard_id = self.merge_wizard.create({ - 'group_by_name': True, - 'state': "option" - }) - wizard_id.automatic_process_cb() + def _create_duplicates(self, field1, value1, field2, values2): + for value2 in values2: + self.partner.create({ + field1: value1, + field2: value2, + }) - # Test if there is now one Donald Duck - donalds = self.partner.search(donald_domain) - self.assertEquals(len(donalds), 1) - - # Delete all Donald Ducks - self.partner.search(donald_domain).unlink() + def test_10_all_functionality(self): + """ All functionality """ + # Create users with duplicate names + self._unlink_all() + self._create_duplicates('name', 'Donald Duck', 'email', + ['donald@therp.nl', 'donald@sunflowerweb.nl']) + self._create_duplicates('name', 'Mickey Mouse', 'email', + ['mickey@therp.nl', 'mickey@sunflowerweb.nl']) + # Test if there are two Donald Ducks and Mickey Mouses + self._count_donalds_mickeys(2, 2) + # Merge all names that start with 'D', + self.partner.deduplicate_on_field('name', + domain=[('name', 'like', 'D%')]) + # Test if there is one Donald but still two Mickeys + self._count_donalds_mickeys(1, 2) + # Create users with duplicate references + self._unlink_all() + self._create_duplicates('ref', 'DD123', + 'name', ['Donald Duck', 'Mickey Mouse']) + # Merge on reference, leaving out guys that have no ref + self.partner.deduplicate_on_field('ref', + domain=[('ref', '!=', False)]) + # Test if only one remains after + self.assertEquals(len(self.partner.search([ + ('ref', '=', 'DD123')])), 1) diff --git a/base_partner_merge/views/base_partner_merge.xml b/base_partner_merge/views/base_partner_merge.xml index 5ae786aaa..75e9ae719 100644 --- a/base_partner_merge/views/base_partner_merge.xml +++ b/base_partner_merge/views/base_partner_merge.xml @@ -79,6 +79,7 @@ +