Browse Source

[IMP] PEP8

pull/2/head
Yannick Vaucher 11 years ago
parent
commit
eac4bcb9a2
  1. 309
      base_partner_merge/base_partner_merge.py

309
base_partner_merge/base_partner_merge.py

@ -36,11 +36,11 @@ def html_entity_decode(string):
return pattern.sub(html_entity_decode_char, string)
def sanitize_email(email):
assert isinstance(email, basestring) and email
def sanitize_email(partner_email):
assert isinstance(partner_email, basestring) and partner_email
result = re.subn(r';|/|:', ',',
html_entity_decode(email or ''))[0].split(',')
html_entity_decode(partner_email or ''))[0].split(',')
emails = [parseaddr(email)[1]
for item in result
@ -63,6 +63,7 @@ class ResPartner(osv.Model):
'create_date': fields.datetime('Create Date', readonly=True),
}
class MergePartnerLine(osv.TransientModel):
_name = 'base.partner.merge.line'
@ -78,9 +79,10 @@ class MergePartnerLine(osv.TransientModel):
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.
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'
@ -99,25 +101,34 @@ class MergePartnerAutomatic(osv.TransientModel):
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'),
'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'),
'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'),
'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'):
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
res['dst_partner_id'] = self._get_ordered_partner(cr, uid,
partner_ids,
context=context
)[-1].id
return res
_defaults = {
@ -143,8 +154,12 @@ class MergePartnerAutomatic(osv.TransientModel):
"""
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)))
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')
@ -157,7 +172,8 @@ class MergePartnerAutomatic(osv.TransientModel):
continue
partner_ids = tuple(map(int, src_partners))
query = "SELECT column_name FROM information_schema.columns WHERE table_name LIKE '%s'" % (table)
query = ("SELECT column_name FROM information_schema.columns"
" WHERE table_name LIKE '%s'") % (table)
cr.execute(query, ())
columns = []
for data in cr.fetchall():
@ -184,57 +200,77 @@ class MergePartnerAutomatic(osv.TransientModel):
___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))
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
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':
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 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
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")
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):
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)
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('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('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)
record_ids = proxy.search(cr, openerp.SUPERUSER_ID, domain,
context=context)
for record in proxy.browse(cr, openerp.SUPERUSER_ID, record_ids, context=context):
for record in proxy.browse(cr, openerp.SUPERUSER_ID, record_ids,
context=context):
try:
proxy_model = self.pool[record.model]
except KeyError:
@ -253,16 +289,22 @@ class MergePartnerAutomatic(osv.TransientModel):
domain = [
(record.name, '=', 'res.partner,%d' % partner.id)
]
model_ids = proxy_model.search(cr, openerp.SUPERUSER_ID, domain, context=context)
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)
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)))
_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
@ -271,10 +313,12 @@ class MergePartnerAutomatic(osv.TransientModel):
values = dict()
for column, field in columns.iteritems():
if field._type not in ('many2many', 'one2many') and not isinstance(field, fields.function):
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[column] = write_serializer(column,
item[column])
values.pop('id', None)
parent_id = values.pop('parent_id', None)
@ -283,60 +327,98 @@ class MergePartnerAutomatic(osv.TransientModel):
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)
_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)
partner_ids = proxy.exists(cr, uid, list(partner_ids),
context=context)
if len(partner_ids) < 2:
return
if len(partner_ids) > 3:
raise osv.except_osv(_('Error'), _("For safety reasons, you cannot merge more than 3 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=context))) > 1:
raise osv.except_osv(_('Error'), _("All contacts must have the same email. Only the Administrator can merge contacts with different emails."))
raise osv.except_osv(
_('Error'),
_("For safety reasons, you cannot merge more than 3 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=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)
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)
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)
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)))
_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.unlink()
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.
Clean the email address of the partner, if there is an email field
with a minimum 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')],
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)
@ -374,7 +456,8 @@ class MergePartnerAutomatic(osv.TransientModel):
context=context)
except Exception:
_logger.exception("There is a problem with this partner: %r", partner)
_logger.exception("There is a problem with this partner: %r",
partner)
raise
return True
@ -431,7 +514,8 @@ class MergePartnerAutomatic(osv.TransientModel):
if not groups:
raise osv.except_osv(_('Error'),
_("You have to specify a filter for your selection"))
_("You have to specify a filter for your "
"selection"))
return groups
@ -446,10 +530,15 @@ class MergePartnerAutomatic(osv.TransientModel):
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)
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)
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):
@ -462,7 +551,11 @@ class MergePartnerAutomatic(osv.TransientModel):
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,
'dst_partner_id': self._get_ordered_partner(
cr, uid,
current_partner_ids,
context
)[-1].id,
'state': 'selection',
})
else:
@ -501,8 +594,8 @@ class MergePartnerAutomatic(osv.TransientModel):
def compute_models(self, cr, uid, ids, context=None):
"""
Compute the different models needed by the system if you want to exclude
some partners.
Compute the different models needed by the system if you want to
exclude some partners.
"""
assert is_integer_list(ids)
@ -512,7 +605,9 @@ class MergePartnerAutomatic(osv.TransientModel):
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:
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
@ -528,7 +623,8 @@ class MergePartnerAutomatic(osv.TransientModel):
counter = 0
for min_id, aggr_ids in cr.fetchall():
if models and self._partner_use_in(cr, uid, aggr_ids, models, context=context):
if models and self._partner_use_in(cr, uid, aggr_ids, models,
context=context):
continue
values = {
'wizard_id': this.id,
@ -552,7 +648,8 @@ class MergePartnerAutomatic(osv.TransientModel):
"""
Start the process.
* Compute the selected groups (with duplication)
* If the user has selected the 'exclude_XXX' fields, avoid the partners.
* If the user has selected the 'exclude_XXX' fields, avoid the
partners.
"""
assert is_integer_list(ids)
@ -664,13 +761,31 @@ class MergePartnerAutomatic(osv.TransientModel):
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}
{'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:
@ -722,7 +837,8 @@ class MergePartnerAutomatic(osv.TransientModel):
'target': 'new',
}
self._merge(cr, uid, partner_ids, this.dst_partner_id, context=context)
self._merge(cr, uid, partner_ids, this.dst_partner_id,
context=context)
if this.current_line_id:
this.current_line_id.unlink()
@ -735,8 +851,8 @@ class MergePartnerAutomatic(osv.TransientModel):
# 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
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
@ -750,11 +866,15 @@ class MergePartnerAutomatic(osv.TransientModel):
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'))
# 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:
@ -764,7 +884,8 @@ class MergePartnerAutomatic(osv.TransientModel):
# to display changed values
cr.execute(""" SELECT id,email
FROM res_partner
WHERE parent_id != %s AND id != %s AND email LIKE '%%%s'
WHERE parent_id != %s
AND id != %s AND email LIKE '%%%s'
""" % (id, id, email))
_logger.info("%r", cr.fetchall())

Loading…
Cancel
Save