From 8653023d8e87f8054f10e0fbbb93a6258ec1720f Mon Sep 17 00:00:00 2001 From: Moises Lopez Date: Thu, 15 Aug 2013 18:29:02 -0700 Subject: [PATCH] [REM][partner_do_merge] Revert this merge. Waiting feedback --- partner_do_merge/__init__.py | 2 - partner_do_merge/__openerp__.py | 70 -- partner_do_merge/model/__init__.py | 1 - partner_do_merge/model/partner.py | 129 --- partner_do_merge/view/res_partner_view.xml | 21 - partner_do_merge/wizard/__init__.py | 2 - partner_do_merge/wizard/base_partner_merge.py | 852 ------------------ .../wizard/base_partner_merge_view.xml | 123 --- partner_do_merge/wizard/merge_by_partner.py | 65 -- partner_do_merge/wizard/merge_by_partner.xml | 36 - partner_do_merge/wizard/validate_email.py | 123 --- 11 files changed, 1424 deletions(-) delete mode 100644 partner_do_merge/__init__.py delete mode 100644 partner_do_merge/__openerp__.py delete mode 100644 partner_do_merge/model/__init__.py delete mode 100644 partner_do_merge/model/partner.py delete mode 100644 partner_do_merge/view/res_partner_view.xml delete mode 100644 partner_do_merge/wizard/__init__.py delete mode 100644 partner_do_merge/wizard/base_partner_merge.py delete mode 100644 partner_do_merge/wizard/base_partner_merge_view.xml delete mode 100644 partner_do_merge/wizard/merge_by_partner.py delete mode 100644 partner_do_merge/wizard/merge_by_partner.xml delete mode 100644 partner_do_merge/wizard/validate_email.py diff --git a/partner_do_merge/__init__.py b/partner_do_merge/__init__.py deleted file mode 100644 index b31324c4e..000000000 --- a/partner_do_merge/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -import wizard -import model diff --git a/partner_do_merge/__openerp__.py b/partner_do_merge/__openerp__.py deleted file mode 100644 index 7461e5585..000000000 --- a/partner_do_merge/__openerp__.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# 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' : 'Merge Duplicate Partner', - 'version' : '0.1', - 'author': 'OpenERP SA', - 'website': 'http://www.openerp.com', - 'category' : 'Base', - 'description' : """ -Merge Partners -============== -We can merge duplicates partners and set the new id in all documents of -partner merged - -We can merge partner using like mach parameter these fields: --Email --VAT --Company --Is company --Name --Parent Company - -We can select which partner will be the main partner - -This feature is in the follow path Sales/Tools/Deduplicate Contacts also is -created an action menu in the partner view - """, - 'images' : [], - 'depends' : [ - 'base', - 'crm', - ], - 'data': [ - 'wizard/base_partner_merge_view.xml', - 'wizard/merge_by_partner.xml', - 'view/res_partner_view.xml', - ], - 'js': [ - ], - 'qweb' : [ - ], - 'css':[ - ], - 'demo': [ - ], - 'test': [ - ], - 'installable': True, - 'auto_install': False, -} -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - diff --git a/partner_do_merge/model/__init__.py b/partner_do_merge/model/__init__.py deleted file mode 100644 index 0f63679e7..000000000 --- a/partner_do_merge/model/__init__.py +++ /dev/null @@ -1 +0,0 @@ -import partner diff --git a/partner_do_merge/model/partner.py b/partner_do_merge/model/partner.py deleted file mode 100644 index 36b9cf6f5..000000000 --- a/partner_do_merge/model/partner.py +++ /dev/null @@ -1,129 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# 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 openerp -from openerp import SUPERUSER_ID -from openerp import tools -from openerp.osv import osv, fields -from openerp.tools.translate import _ -from openerp.tools.yaml_import import is_comment - - -class res_partner(osv.Model): - _description = 'Partner' - _inherit = "res.partner" - - def _commercial_partner_compute(self, cr, uid, ids, name, args, - context=None): - """ Returns the partner that is considered the commercial - entity of this partner. The commercial entity holds the master data - for all commercial fields (see :py:meth:`~_commercial_fields`) """ - result = dict.fromkeys(ids, False) - for partner in self.browse(cr, uid, ids, context=context): - current_partner = partner - while not current_partner.is_company and current_partner.parent_id: - current_partner = current_partner.parent_id - result[partner.id] = current_partner.id - return result - - def _display_name_compute(self, cr, uid, ids, name, args, context=None): - return dict(self.name_get(cr, uid, ids, context=context)) - - # indirections to avoid passing a copy of the overridable method when - # declaring the function field - _display_name = lambda self, *args, **kwargs: \ - self._display_name_compute(*args, **kwargs) - - _display_name_store_triggers = { - 'res.partner': (lambda self, cr, uid, ids, context=None: - self.search(cr, uid, [( - 'id', 'child_of', ids)]), - ['parent_id', 'is_company', 'name'], 10) - } - - _order = "display_name" - _columns = { - 'display_name': fields.function(_display_name, type='char', - string='Name', - store=_display_name_store_triggers), - 'id': fields.integer('Id', readonly=True), - 'create_date': fields.datetime('Create Date', readonly=True), - 'partner_merged_ids' : fields.many2many('res.partner',\ - 'partners_mergeds', 'partner_active', 'partner_id', 'Relation '\ - 'with partner merged', domain=['|', ('active','=',True), (\ - 'active','=',False)], readonly=True) - - - } - - def name_get(self, cr, uid, ids, context=None): - if context is None: - context = {} - if isinstance(ids, (int, long)): - ids = [ids] - res = [] - for record in self.browse(cr, uid, ids, context=context): - name = record.name - if record.parent_id and not record.is_company: - name = "%s, %s" % (record.parent_id.name, name) - if context.get('show_address'): - name = name + "\n" + \ - self._display_address(cr, uid, record, - without_company=True, - context=context) - name = name.replace('\n\n', '\n') - name = name.replace('\n\n', '\n') - if context.get('show_email') and record.email: - name = "%s <%s>" % (name, record.email) - res.append((record.id, name)) - return res - - def name_search(self, cr, uid, name, args=None, operator='ilike', - context=None, limit=100): - if not args: - args = [] - if name and operator in ('=', 'ilike', '=ilike', 'like', '=like'): - # search on the name of the contacts and of its company - search_name = name - if operator in ('ilike', 'like'): - search_name = '%%%s%%' % name - if operator in ('=ilike', '=like'): - operator = operator[1:] - query_args = {'name': search_name} - limit_str = '' - if limit: - limit_str = ' limit %(limit)s' - query_args['limit'] = limit - cr.execute('''SELECT partner.id FROM res_partner partner - LEFT JOIN res_partner company - ON partner.parent_id = company.id - WHERE partner.email ''' + operator + ''' %(name)s OR - partner.display_name ''' + operator + - ' %(name)s ' + limit_str, query_args) - ids = map(lambda x: x[0], cr.fetchall()) - ids = self.search(cr, uid, [('id', 'in', ids)] + args, - limit=limit, context=context) - if ids: - return self.name_get(cr, uid, ids, context) - return super(res_partner, self).name_search(cr, uid, name, args, - operator=operator, - context=context, - limit=limit) diff --git a/partner_do_merge/view/res_partner_view.xml b/partner_do_merge/view/res_partner_view.xml deleted file mode 100644 index 615860a99..000000000 --- a/partner_do_merge/view/res_partner_view.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - view.partner.form.inh.partner.merge.form - form - res.partner - - - - - - - - - - - - - diff --git a/partner_do_merge/wizard/__init__.py b/partner_do_merge/wizard/__init__.py deleted file mode 100644 index 30b45aaab..000000000 --- a/partner_do_merge/wizard/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -import base_partner_merge -import merge_by_partner diff --git a/partner_do_merge/wizard/base_partner_merge.py b/partner_do_merge/wizard/base_partner_merge.py deleted file mode 100644 index 27928dd30..000000000 --- a/partner_do_merge/wizard/base_partner_merge.py +++ /dev/null @@ -1,852 +0,0 @@ -#!/usr/bin/env python -from __future__ import absolute_import -from email.utils import parseaddr -import functools -import htmlentitydefs -import itertools -import logging -import operator -import re -from ast import literal_eval -from openerp.tools import mute_logger - -# Validation Library https://pypi.python.org/pypi/validate_email/1.1 -from .validate_email import validate_email - -import openerp -from openerp.osv import osv, orm -from openerp.osv import fields -from openerp.osv.orm import browse_record -from openerp.tools.translate import _ - -pattern = re.compile("&(\w+?);") - -_logger = logging.getLogger('base.partner.merge') - - -# http://www.php2python.com/wiki/function.html-entity-decode/ -def html_entity_decode_char(m, defs=htmlentitydefs.entitydefs): - try: - return defs[m.group(1)] - except KeyError: - return m.group(0) - - -def html_entity_decode(string): - return pattern.sub(html_entity_decode_char, string) - - -def sanitize_email(email): - assert isinstance(email, basestring) and email - - result = re.subn(r';|/|:', ',', - html_entity_decode(email or ''))[0].split(',') - - emails = [parseaddr(email)[1] - for item in result - for email in item.split()] - - return [email.lower() - for email in emails - if validate_email(email)] - - -def is_integer_list(ids): - return all(isinstance(i, (int, long)) for i in ids) - - -class MergePartnerLine(osv.TransientModel): - _name = 'base.partner.merge.line' - - _columns = { - 'wizard_id': fields.many2one('base.partner.merge.automatic.wizard', - 'Wizard'), - 'min_id': fields.integer('MinID'), - 'aggr_ids': fields.char('Ids', required=True), - } - - _order = 'min_id asc' - - -class MergePartnerAutomatic(osv.TransientModel): - """ - The idea behind this wizard is to create a list of potential partners - to merge. We use two objects, the first one is the wizard for - the end-user. - And the second will contain the partner list to merge. - """ - _name = 'base.partner.merge.automatic.wizard' - - _columns = { - # Group by - 'group_by_email': fields.boolean('Email'), - 'group_by_name': fields.boolean('Name'), - 'group_by_is_company': fields.boolean('Is Company'), - 'group_by_vat': fields.boolean('VAT'), - 'group_by_parent_id': fields.boolean('Parent Company'), - - 'state': fields.selection([('option', 'Option'), - ('selection', 'Selection'), - ('finished', 'Finished')], - 'State', - readonly=True, - required=True), - 'number_group': fields.integer("Group of Contacts", readonly=True), - 'current_line_id': fields.many2one('base.partner.merge.line', - 'Current Line'), - 'line_ids': fields.one2many('base.partner.merge.line', - 'wizard_id', 'Lines'), - 'partner_ids': fields.many2many('res.partner', string='Contacts'), - 'dst_partner_id': fields.many2one('res.partner', - string='Destination Contact'), - - 'exclude_contact': fields.boolean('A user associated to the contact'), - 'exclude_journal_item': fields.boolean('Journal Items associated to ' - 'the contact'), - 'maximum_group': fields.integer("Maximum of Group of Contacts"), - } - - def default_get(self, cr, uid, fields, context=None): - if context is None: - context = {} - res = super(MergePartnerAutomatic, self).default_get( - cr, uid, fields, context) - if context.get('active_model') == 'res.partner' and \ - context.get('active_ids'): - partner_ids = context['active_ids'] - res['state'] = 'selection' - res['partner_ids'] = partner_ids - res['dst_partner_id'] = self._get_ordered_partner( - cr, uid, partner_ids, context=context)[-1].id - return res - - _defaults = { - 'state': 'option' - } - - def get_fk_on(self, cr, table): - q = """ SELECT cl1.relname as table, - att1.attname as column - FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, - pg_attribute as att1, pg_attribute as att2 - WHERE con.conrelid = cl1.oid - AND con.confrelid = cl2.oid - AND array_lower(con.conkey, 1) = 1 - AND con.conkey[1] = att1.attnum - AND att1.attrelid = cl1.oid - AND cl2.relname = %s - AND att2.attname = 'id' - AND array_lower(con.confkey, 1) = 1 - AND con.confkey[1] = att2.attnum - AND att2.attrelid = cl2.oid - AND con.contype = 'f' - """ - return cr.execute(q, (table,)) - - def _update_foreign_keys(self, cr, uid, src_partners, dst_partner, - context=None): - _logger.debug( - '_update_foreign_keys for dst_partner: %s for src_partners: %r', - dst_partner.id, list(map(operator.attrgetter('id'), src_partners))) - - # find the many2one relation to a partner - proxy = self.pool.get('res.partner') - self.get_fk_on(cr, 'res_partner') - - # ignore two tables - - for table, column in cr.fetchall(): - if 'base_partner_merge_' in table: - continue - partner_ids = tuple(map(int, src_partners)) - - query = """SELECT column_name FROM information_schema.columns - WHERE table_name LIKE '%s'""" % ( - table) - cr.execute(query, ()) - columns = [] - for data in cr.fetchall(): - if data[0] != column: - columns.append(data[0]) - - query_dic = { - 'table': table, - 'column': column, - 'value': columns[0], - } - if len(columns) <= 1: - # unique key treated - query = """ - UPDATE "%(table)s" as ___tu - SET %(column)s = %%s - WHERE - %(column)s = %%s AND - NOT EXISTS ( - SELECT 1 - FROM "%(table)s" as ___tw - WHERE - %(column)s = %%s AND - ___tu.%(value)s = ___tw.%(value)s - )""" % query_dic - for partner_id in partner_ids: - cr.execute(query, ( - dst_partner.id, partner_id, dst_partner.id)) - else: - cr.execute("SAVEPOINT recursive_partner_savepoint") - try: - query = '''UPDATE "%(table)s" SET %(column)s = %%s - WHERE %(column)s IN %%s''' % query_dic - cr.execute(query, (dst_partner.id, partner_ids,)) - - if column == proxy._parent_name and table == 'res_partner': - query = """ - WITH RECURSIVE cycle(id, parent_id) AS ( - SELECT id, parent_id FROM res_partner - UNION - SELECT cycle.id, res_partner.parent_id - FROM res_partner, cycle - WHERE res_partner.id = cycle.parent_id AND - cycle.id != cycle.parent_id - ) - SELECT id FROM cycle - WHERE id = parent_id AND id = %s - """ - cr.execute(query, (dst_partner.id,)) - if cr.fetchall(): - cr.execute( - "ROLLBACK TO SAVEPOINT " - "recursive_partner_savepoint") - finally: - cr.execute("RELEASE SAVEPOINT recursive_partner_savepoint") - - def _update_reference_fields(self, cr, uid, src_partners, - dst_partner, context=None): - _logger.debug('''_update_reference_fields for dst_partner: %s - for src_partners: %r''', dst_partner.id, list( - map(operator.attrgetter('id'), src_partners))) - - def update_records(model, src, field_model='model', - field_id='res_id', context=None): - proxy = self.pool.get(model) - if proxy is None: - return - domain = [(field_model, '=', 'res.partner'), ( - field_id, '=', src.id)] - ids = proxy.search( - cr, openerp.SUPERUSER_ID, domain, context=context) - return proxy.write(cr, openerp.SUPERUSER_ID, ids, - {field_id: dst_partner.id}, context=context) - - update_records = functools.partial(update_records, context=context) - - for partner in src_partners: - update_records( - 'base.calendar', src=partner, field_model='model_id.model') - update_records( - 'ir.attachment', src=partner, field_model='res_model') - update_records( - 'mail.followers', src=partner, field_model='res_model') - update_records('mail.message', src=partner) - update_records('marketing.campaign.workitem', - src=partner, field_model='object_id.model') - update_records('ir.model.data', src=partner) - - proxy = self.pool['ir.model.fields'] - domain = [('ttype', '=', 'reference')] - record_ids = proxy.search( - cr, openerp.SUPERUSER_ID, domain, context=context) - - for record in proxy.browse(cr, openerp.SUPERUSER_ID, record_ids, - context=context): - proxy_model = self.pool[record.model] - - field_type = proxy_model._columns.get(record.name).__class__._type - - if field_type == 'function': - continue - - for partner in src_partners: - domain = [ - (record.name, '=', 'res.partner,%d' % partner.id) - ] - model_ids = proxy_model.search( - cr, openerp.SUPERUSER_ID, domain, context=context) - values = { - record.name: 'res.partner,%d' % dst_partner.id, - } - proxy_model.write( - cr, openerp.SUPERUSER_ID, model_ids, values, - context=context) - - def _update_values(self, cr, uid, src_partners, dst_partner, context=None): - _logger.debug( - '_update_values for dst_partner: %s for src_partners: %r', - dst_partner.id, list(map(operator.attrgetter('id'), src_partners))) - - columns = dst_partner._columns - - def write_serializer(column, item): - if isinstance(item, browse_record): - return item.id - else: - return item - - values = dict() - for column, field in columns.iteritems(): - if field._type not in ('many2many', 'one2many') and not \ - isinstance(field, fields.function): - for item in itertools.chain(src_partners, [dst_partner]): - if item[column]: - values[column] = write_serializer(column, item[column]) - - values.pop('id', None) - parent_id = values.pop('parent_id', None) - dst_partner.write(values) - if parent_id and parent_id != dst_partner.id: - try: - dst_partner.write({'parent_id': parent_id}) - except (osv.except_osv, orm.except_orm): - _logger.info( - '''Skip recursive partner hierarchies - for parent_id %s of partner: %s''', parent_id, - dst_partner.id) - - @mute_logger('openerp.osv.expression', 'openerp.osv.orm') - def _merge(self, cr, uid, partner_ids, dst_partner=None, context=None): - proxy = self.pool.get('res.partner') - - partner_ids = proxy.exists(cr, uid, list(partner_ids), context=context) - if len(partner_ids) < 2: - return - - if len(partner_ids) > 10: - raise osv.except_osv(_('Error'), _( - """For safety reasons, you cannot merge more than 10 contacts - together. You can re-open the wizard several - times if needed.""")) - - if openerp.SUPERUSER_ID != uid and \ - len(set(partner.email for partner in - proxy.browse(cr, uid, - partner_ids, - context))) > 1: - raise osv.except_osv(_('Error'), _( - """All contacts must have the same email. Only the - Administrator can merge contacts with different - emails.""")) - - if dst_partner and dst_partner.id in partner_ids: - src_partners = proxy.browse(cr, uid, [ - id for id in partner_ids - if id != dst_partner.id], - context=context) - else: - ordered_partners = self._get_ordered_partner( - cr, uid, partner_ids, context) - dst_partner = ordered_partners[-1] - src_partners = ordered_partners[:-1] - _logger.info("dst_partner: %s", dst_partner.id) - - if openerp.SUPERUSER_ID != uid and \ - self._model_is_installed(cr, uid, 'account.move.line', - context=context) and \ - self.pool.get('account.move.line').\ - search(cr, openerp.SUPERUSER_ID, - [('partner_id', 'in', [partner.id for partner in - src_partners])], - context=context): - raise osv.except_osv(_('Error'), _( - """Only the destination contact may be linked to existing - Journal Items. Please ask the Administrator if you need - to merge several contacts linked to existing Journal - Items.""")) - - call_it = lambda function: function(cr, uid, src_partners, dst_partner, - context=context) - - call_it(self._update_foreign_keys) - call_it(self._update_reference_fields) - call_it(self._update_values) - - _logger.info('(uid = %s) merged the partners %r with %s', uid, list( - map(operator.attrgetter('id'), src_partners)), dst_partner.id) - dst_partner.message_post(body='%s %s' % - (_("Merged with the following partners:"), - ", ".join('%s<%s>(ID %s)' % - (p.name, p.email or 'n/a', p.id) for p in - src_partners))) - - for partner in src_partners: - partner.write({'active' : False}) - - def clean_emails(self, cr, uid, context=None): - """ - Clean the email address of the partner, if there is an email field - with a mimum of two addresses, the system will create a new partner, - with the information of the previous one and will copy the new cleaned - email into the email field. - """ - if context is None: - context = {} - - proxy_model = self.pool['ir.model.fields'] - field_ids = proxy_model.search(cr, uid, [('model', '=', 'res.partner'), - ('ttype', 'like', '%2many')], - context=context) - fields = proxy_model.read(cr, uid, field_ids, context=context) - reset_fields = dict((field['name'], []) for field in fields) - - proxy_partner = self.pool['res.partner'] - context['active_test'] = False - ids = proxy_partner.search(cr, uid, [], context=context) - - fields = ['name', 'var' 'partner_id' 'is_company', 'email'] - partners = proxy_partner.read(cr, uid, ids, fields, context=context) - - partners.sort(key=operator.itemgetter('id')) - partners_len = len(partners) - - _logger.info('partner_len: %r', partners_len) - - for idx, partner in enumerate(partners): - if not partner['email']: - continue - - percent = (idx / float(partners_len)) * 100.0 - _logger.info('idx: %r', idx) - _logger.info('percent: %r', percent) - try: - emails = sanitize_email(partner['email']) - head, tail = emails[:1], emails[1:] - email = head[0] if head else False - - proxy_partner.write(cr, uid, [partner['id']], - {'email': email}, context=context) - - for email in tail: - values = dict(reset_fields, email=email) - proxy_partner.copy(cr, uid, partner['id'], values, - context=context) - - except Exception: - _logger.exception( - "There is a problem with this partner: %r", partner) - raise - return True - - def close_cb(self, cr, uid, ids, context=None): - return {'type': 'ir.actions.act_window_close'} - - def _generate_query(self, fields, maximum_group=100): - group_fields = ', '.join(fields) - - filters = [] - for field in fields: - if field in ['email', 'name']: - filters.append((field, 'IS NOT', 'NULL')) - - criteria = ' AND '.join('%s %s %s' % (field, operator, value) - for field, operator, value in filters) - - text = [ - "SELECT min(id), array_agg(id)", - "FROM res_partner", - ] - - if criteria: - text.append('WHERE %s' % criteria) - - text.extend([ - "GROUP BY %s" % group_fields, - "HAVING COUNT(*) >= 2", - "ORDER BY min(id)", - ]) - - if maximum_group: - text.extend([ - "LIMIT %s" % maximum_group, - ]) - - return ' '.join(text) - - def _compute_selected_groupby(self, this): - group_by_str = 'group_by_' - group_by_len = len(group_by_str) - - fields = [ - key[group_by_len:] - for key in self._columns.keys() - if key.startswith(group_by_str) - ] - - groups = [ - field - for field in fields - if getattr(this, '%s%s' % (group_by_str, field), False) - ] - - if not groups: - raise osv.except_osv(_('Error'), - _("""You have to specify a filter for your - selection""")) - - return groups - - def next_cb(self, cr, uid, ids, context=None): - """ - Don't compute any thing - """ - context = dict(context or {}, active_test=False) - this = self.browse(cr, uid, ids[0], context=context) - if this.current_line_id: - this.current_line_id.unlink() - return self._next_screen(cr, uid, this, context) - - def _get_ordered_partner(self, cr, uid, partner_ids, context=None): - partners = self.pool.get('res.partner').browse( - cr, uid, list(partner_ids), context=context) - ordered_partners = sorted(sorted(partners, - key=operator.attrgetter( - 'create_date'), reverse=True), - key=operator.attrgetter('active'), - reverse=True) - return ordered_partners - - def _next_screen(self, cr, uid, this, context=None): - this.refresh() - values = {} - if this.line_ids: - # in this case, we try to find the next record. - current_line = this.line_ids[0] - current_partner_ids = literal_eval(current_line.aggr_ids) - values.update({ - 'current_line_id': current_line.id, - 'partner_ids': [(6, 0, current_partner_ids)], - 'dst_partner_id': self. - _get_ordered_partner(cr, uid, - current_partner_ids, - context)[-1].id, - 'state': 'selection', - }) - else: - values.update({ - 'current_line_id': False, - 'partner_ids': [], - 'state': 'finished', - }) - - this.write(values) - - return { - 'type': 'ir.actions.act_window', - 'res_model': this._name, - 'res_id': this.id, - 'view_mode': 'form', - 'target': 'new', - } - - def _model_is_installed(self, cr, uid, model, context=None): - proxy = self.pool.get('ir.model') - domain = [('model', '=', model)] - return proxy.search_count(cr, uid, domain, context=context) > 0 - - def _partner_use_in(self, cr, uid, aggr_ids, models, context=None): - """ - Check if there is no occurence of this group of partner in the selected - model - """ - for model, field in models.iteritems(): - proxy = self.pool.get(model) - domain = [(field, 'in', aggr_ids)] - if proxy.search_count(cr, uid, domain, context=context): - return True - return False - - def compute_models(self, cr, uid, ids, context=None): - """ - Compute the different models needed by the system if you want to - exclude some partners. - """ - assert is_integer_list(ids) - - this = self.browse(cr, uid, ids[0], context=context) - - models = {} - if this.exclude_contact: - models['res.users'] = 'partner_id' - - if self._model_is_installed(cr, uid, 'account.move.line', - context=context) and \ - this.exclude_journal_item: - models['account.move.line'] = 'partner_id' - - return models - - def _process_query(self, cr, uid, ids, query, context=None): - """ - Execute the select request and write the result in this wizard - """ - proxy = self.pool.get('base.partner.merge.line') - this = self.browse(cr, uid, ids[0], context=context) - models = self.compute_models(cr, uid, ids, context=context) - cr.execute(query) - - counter = 0 - for min_id, aggr_ids in cr.fetchall(): - if models and self._partner_use_in(cr, uid, aggr_ids, - models, context=context): - continue - values = { - 'wizard_id': this.id, - 'min_id': min_id, - 'aggr_ids': aggr_ids, - } - - proxy.create(cr, uid, values, context=context) - counter += 1 - - values = { - 'state': 'selection', - 'number_group': counter, - } - - this.write(values) - - _logger.info("counter: %s", counter) - - def start_process_cb(self, cr, uid, ids, context=None): - """ - Start the process. - * Compute the selected groups (with duplication) - * If the user has selected the 'exclude_XXX' fields, avoid the - partners. - """ - assert is_integer_list(ids) - - context = dict(context or {}, active_test=False) - this = self.browse(cr, uid, ids[0], context=context) - groups = self._compute_selected_groupby(this) - query = self._generate_query(groups, this.maximum_group) - self._process_query(cr, uid, ids, query, context=context) - - return self._next_screen(cr, uid, this, context) - - def automatic_process_cb(self, cr, uid, ids, context=None): - assert is_integer_list(ids) - this = self.browse(cr, uid, ids[0], context=context) - this.start_process_cb() - this.refresh() - - for line in this.line_ids: - partner_ids = literal_eval(line.aggr_ids) - self._merge(cr, uid, partner_ids, context=context) - line.unlink() - cr.commit() - - this.write({'state': 'finished'}) - return { - 'type': 'ir.actions.act_window', - 'res_model': this._name, - 'res_id': this.id, - 'view_mode': 'form', - 'target': 'new', - } - - def parent_migration_process_cb(self, cr, uid, ids, context=None): - assert is_integer_list(ids) - - context = dict(context or {}, active_test=False) - this = self.browse(cr, uid, ids[0], context=context) - - query = """ - SELECT - min(p1.id), - array_agg(DISTINCT p1.id) - FROM - res_partner as p1 - INNER join - res_partner as p2 - ON - p1.email = p2.email AND - p1.name = p2.name AND - (p1.parent_id = p2.id OR p1.id = p2.parent_id) - WHERE - p2.id IS NOT NULL - GROUP BY - p1.email, - p1.name, - CASE WHEN p1.parent_id = p2.id THEN p2.id - ELSE p1.id - END - HAVING COUNT(*) >= 2 - ORDER BY - min(p1.id) - """ - - self._process_query(cr, uid, ids, query, context=context) - - for line in this.line_ids: - partner_ids = literal_eval(line.aggr_ids) - self._merge(cr, uid, partner_ids, context=context) - line.unlink() - cr.commit() - - this.write({'state': 'finished'}) - - cr.execute(""" - UPDATE - res_partner - SET - is_company = NULL, - parent_id = NULL - WHERE - parent_id = id - """) - - return { - 'type': 'ir.actions.act_window', - 'res_model': this._name, - 'res_id': this.id, - 'view_mode': 'form', - 'target': 'new', - } - - def update_all_process_cb(self, cr, uid, ids, context=None): - assert is_integer_list(ids) - - # WITH RECURSIVE cycle(id, parent_id) AS ( - # SELECT id, parent_id FROM res_partner - # UNION - # SELECT cycle.id, res_partner.parent_id - # FROM res_partner, cycle - # WHERE res_partner.id = cycle.parent_id AND - # cycle.id != cycle.parent_id - # ) - # UPDATE res_partner - # SET parent_id = NULL - # WHERE id in (SELECT id FROM cycle WHERE id = parent_id); - - this = self.browse(cr, uid, ids[0], context=context) - - self.parent_migration_process_cb(cr, uid, ids, context=None) - - list_merge = [ - {'group_by_vat': True, 'group_by_email': - True, 'group_by_name': True}, - # {'group_by_name': True, 'group_by_is_company': True, 'group_by_parent_id': True}, - # {'group_by_email': True, 'group_by_is_company': True, 'group_by_parent_id': True}, - # {'group_by_name': True, 'group_by_vat': True, 'group_by_is_company': True, 'exclude_journal_item': True}, - # {'group_by_email': True, 'group_by_vat': True, 'group_by_is_company': True, 'exclude_journal_item': True}, - # {'group_by_email': True, 'group_by_is_company': True, 'exclude_contact': True, 'exclude_journal_item': True}, - # {'group_by_name': True, 'group_by_is_company': True, 'exclude_contact': True, 'exclude_journal_item': True} - ] - - for merge_value in list_merge: - id = self.create(cr, uid, merge_value, context=context) - self.automatic_process_cb(cr, uid, [id], context=context) - - cr.execute(""" - UPDATE - res_partner - SET - is_company = NULL - WHERE - parent_id IS NOT NULL AND - is_company IS NOT NULL - """) - - # cr.execute(""" - # UPDATE - # res_partner as p1 - # SET - # is_company = NULL, - # parent_id = ( - # SELECT p2.id - # FROM res_partner as p2 - # WHERE p2.email = p1.email AND - # p2.parent_id != p2.id - # LIMIT 1 - # ) - # WHERE - # p1.parent_id = p1.id - # """) - - return self._next_screen(cr, uid, this, context) - - def merge_pbp(self, cr, uid, partner_ids, dst_partner_id, context=None): - self._merge(cr, uid, partner_ids, dst_partner_id, context=context) - return True - - def merge_cb(self, cr, uid, ids, context=None): - assert is_integer_list(ids) - - context = dict(context or {}, active_test=False) - this = self.browse(cr, uid, ids[0], context=context) - - partner_ids = set(map(int, this.partner_ids)) - if not partner_ids: - this.write({'state': 'finished'}) - return { - 'type': 'ir.actions.act_window', - 'res_model': this._name, - 'res_id': this.id, - 'view_mode': 'form', - 'target': 'new', - } - - self._merge(cr, uid, partner_ids, this.dst_partner_id, context=context) - - if this.current_line_id: - this.current_line_id.unlink() - - return self._next_screen(cr, uid, this, context) - - def auto_set_parent_id(self, cr, uid, ids, context=None): - assert is_integer_list(ids) - - # select partner who have one least invoice - partner_treated = ['@gmail.com'] - cr.execute(""" SELECT p.id, p.email - FROM res_partner as p - LEFT JOIN account_invoice as a - ON p.id = a.partner_id AND a.state in ('open','paid') - WHERE p.grade_id is NOT NULL - GROUP BY p.id - ORDER BY COUNT(a.id) DESC - """) - re_email = re.compile(r".*@") - for id, email in cr.fetchall(): - # check email domain - email = re_email.sub("@", email or "") - if not email or email in partner_treated: - continue - partner_treated.append(email) - - # don't update the partners if they are more of one who have - # invoice - cr.execute(""" SELECT * - FROM res_partner as p - WHERE p.id != %s AND p.email LIKE '%%%s' AND - EXISTS (SELECT * FROM account_invoice as a - WHERE p.id = a.partner_id AND - a.state in ('open','paid')) - """ % (id, email)) - - if len(cr.fetchall()) > 1: - _logger.info("%s MORE OF ONE COMPANY", email) - continue - - # to display changed values - cr.execute(""" SELECT id,email - FROM res_partner - WHERE parent_id != %s AND id != %s - AND email LIKE '%%%s' - """ % (id, id, email)) - _logger.info("%r", cr.fetchall()) - - # upgrade - cr.execute(""" UPDATE res_partner - SET parent_id = %s - WHERE id != %s AND email LIKE '%%%s' - """ % (id, id, email)) - return False diff --git a/partner_do_merge/wizard/base_partner_merge_view.xml b/partner_do_merge/wizard/base_partner_merge_view.xml deleted file mode 100644 index 8ce0b5fd9..000000000 --- a/partner_do_merge/wizard/base_partner_merge_view.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - Deduplicate Contacts - base.partner.merge.automatic.wizard - form - form - new - {'active_test': False} - - - - - - base.partner.merge.automatic.wizard.form - base.partner.merge.automatic.wizard - -
-
-
- - -

There is no more contacts to merge for this request...

-