Browse Source

Merge branch '7.0-travis-update' into 7.0-partner_relations

pull/15/head
Holger Brunn 10 years ago
parent
commit
0c0af385d0
  1. 10
      .travis.yml
  2. 13
      __unported__/partner_address_ldap/__init__.py
  3. 6
      __unported__/partner_address_ldap/__openerp__.py
  4. 239
      __unported__/partner_address_ldap/address.py
  5. 16
      __unported__/partner_address_ldap/company.py
  6. 11
      __unported__/partner_address_ldap/partner.py
  7. 2
      __unported__/partner_address_ldap/wizard/__init__.py
  8. 43
      __unported__/partner_address_ldap/wizard/wiz_import_adresses.py
  9. 1
      account_partner_merge/__init__.py
  10. 12
      account_partner_merge/partner_merge.py
  11. 131
      base_contact/base_contact.py
  12. 127
      base_contact/tests/test_base_contact.py
  13. 1
      base_location/__init__.py
  14. 14
      base_location/better_zip.py
  15. 20
      base_location/company.py
  16. 11
      base_location/partner.py
  17. 9
      base_location/state.py
  18. 2
      base_partner_merge/__init__.py
  19. 4
      base_partner_merge/__openerp__.py
  20. 10
      base_partner_merge/base_partner_merge.py
  21. 51
      base_partner_merge/validate_email.py
  22. 2
      base_partner_sequence/__init__.py
  23. 2
      firstname_display_name_trigger/__init__.py
  24. 6
      firstname_display_name_trigger/__openerp__.py
  25. 29
      firstname_display_name_trigger/res_partner.py
  26. 12
      firstname_display_name_trigger/tests/test_display_name.py
  27. 1
      partner_firstname/__init__.py
  28. 6
      partner_firstname/__openerp__.py
  29. 86
      partner_firstname/partner.py
  30. 16
      partner_firstname/res_user.py
  31. 142
      partner_firstname/tests/test_partner_firstname.py
  32. 41
      passport/tests/test_passport.py
  33. 1
      portal_partner_merge/__init__.py
  34. 4
      portal_partner_merge/wizard/__init__.py
  35. 2
      res_partner_affiliate/__init__.py

10
.travis.yml

@ -4,20 +4,20 @@ python:
- "2.7" - "2.7"
env: env:
- VERSION="7.0"
- VERSION="7.0" ODOO_REPO="odoo/odoo"
- VERSION="7.0" ODOO_REPO="OCA/OCB"
virtualenv: virtualenv:
system_site_packages: true system_site_packages: true
install: install:
- git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools - git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
- git clone --branch ${VERSION} --depth 1 https://github.com/OCA/web.git ${HOME}/web
- export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}
- travis_install_nightly ${VERSION}
- $HOME/maintainer-quality-tools/travis/travis_install_nightly ${VERSION}
- travis_install_nightly
script: script:
- travis_run_flake8
- travis_run_tests ${VERSION}
- travis_run_tests
after_success: after_success:
coveralls coveralls

13
__unported__/partner_address_ldap/__init__.py

@ -4,7 +4,8 @@
# Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com) # Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved # All Right Reserved
# #
# Author : Nicolas Bessi (Camptocamp), Thanks to Laurent Lauden for his code adaptation
# Author : Nicolas Bessi (Camptocamp),
# Thanks to Laurent Lauden for his code adaptation
# Contribution : Joel Grand-Guillaume # Contribution : Joel Grand-Guillaume
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
@ -30,7 +31,9 @@
# #
############################################################################## ##############################################################################
import address
import partner
import company
import wizard
from . import (
address,
partner,
company,
wizard,
)

6
__unported__/partner_address_ldap/__openerp__.py

@ -4,7 +4,8 @@
# Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com) # Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved # All Right Reserved
# #
# Author : Nicolas Bessi (Camptocamp), Thanks to Laurent Lauden for his code adaptation
# Author : Nicolas Bessi (Camptocamp),
# Thanks to Laurent Lauden for his code adaptation
# Active directory Donor: M. Benadiba (Informatique Assistances.fr) # Active directory Donor: M. Benadiba (Informatique Assistances.fr)
# Contribution : Joel Grand-Guillaume # Contribution : Joel Grand-Guillaume
# #
@ -59,7 +60,8 @@ The module requires the python-ldap library
Unicode support --> As python ldap does not support unicode we try to decode Unicode support --> As python ldap does not support unicode we try to decode
string if it fails we transliterate values. string if it fails we transliterate values.
Active Directory Support for Windows server 2003, try 2008 at your own risk Active Directory Support for Windows server 2003, try 2008 at your own risk
(AD support not tested for Version 6 of OpenERP looking for active dir test infra)
(AD support not tested for Version 6 of OpenERP looking for active dir test
infra)
""", """,
"data": [ "data": [

239
__unported__/partner_address_ldap/address.py

@ -4,7 +4,8 @@
# Copyright (c) 2010-2011 Camptocamp SA (http://www.camptocamp.com) # Copyright (c) 2010-2011 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved # All Right Reserved
# #
# Author : Nicolas Bessi (Camptocamp), Thanks to Laurent Lauden for his code adaptation
# Author : Nicolas Bessi (Camptocamp),
# Thanks to Laurent Lauden for his code adaptation
# Active directory Donor: M. Benadiba (Informatique Assistances.fr) # Active directory Donor: M. Benadiba (Informatique Assistances.fr)
# Contribution : Joel Grand-Guillaume # Contribution : Joel Grand-Guillaume
# #
@ -39,7 +40,8 @@ try:
import ldap import ldap
import ldap.modlist import ldap.modlist
except: except:
print('python ldap not installed please install it in order to use this module')
print('python ldap not installed please install it in order to use this '
'module')
from openerp.osv import orm, fields from openerp.osv import orm, fields
@ -49,10 +51,15 @@ logger = netsvc.Logger()
class LdapConnMApper(object): class LdapConnMApper(object):
"""LdapConnMApper: push specific fields from the Terp Partner_contacts to the
LDAP schema inetOrgPerson. Ldap bind options are stored in company.r"""
"""LdapConnMApper: push specific fields from the Terp
Partner_contacts to the LDAP schema inetOrgPerson. Ldap bind options
are stored in company.
"""
def __init__(self, cursor, uid, osv_obj, context=None): def __init__(self, cursor, uid, osv_obj, context=None):
"""Initialize connexion to ldap by using parameter set in the current user compagny"""
"""Initialize connexion to ldap by using parameter set in the
current user compagny
"""
logger.notifyChannel("MY TOPIC", netsvc.LOG_DEBUG, logger.notifyChannel("MY TOPIC", netsvc.LOG_DEBUG,
_('Initalize LDAP CONN')) _('Initalize LDAP CONN'))
self.USER_DN = '' self.USER_DN = ''
@ -64,11 +71,10 @@ class LdapConnMApper(object):
self.ACTIVDIR = False self.ACTIVDIR = False
# Reading ldap pref # Reading ldap pref
user = osv_obj.pool.get('res.users').browse(cursor, uid, uid, context=context)
company = osv_obj.pool.get('res.company').browse(cursor,
uid,
user.company_id.id,
context=context)
user = osv_obj.pool.get('res.users').browse(
cursor, uid, uid, context=context
)
company = user.company_id
self.USER_DN = company.base_dn self.USER_DN = company.base_dn
self.CONTACT_DN = company.contact_dn self.CONTACT_DN = company.contact_dn
self.LDAP_SERVER = company.ldap_server self.LDAP_SERVER = company.ldap_server
@ -77,17 +83,28 @@ class LdapConnMApper(object):
self.OU = company.ounit self.OU = company.ounit
self.ACTIVDIR = company.is_activedir self.ACTIVDIR = company.is_activedir
mand = (self.USER_DN, self.CONTACT_DN, self.LDAP_SERVER, self.PASS, self.OU)
mand = (
self.USER_DN,
self.CONTACT_DN,
self.LDAP_SERVER,
self.PASS, self.OU
)
if company.ldap_active: if company.ldap_active:
for param in mand: for param in mand:
if not param: if not param:
raise orm.except_orm(_('Warning !'),
_('An LDAP parameter is missing for company %s') % (company.name, ))
raise orm.except_orm(
_('Warning !'),
_('An LDAP parameter is missing for company %s') %
(company.name, )
)
def get_connexion(self): def get_connexion(self):
"""create a new ldap connexion""" """create a new ldap connexion"""
logger.notifyChannel("LDAP Address", netsvc.LOG_DEBUG,
_('connecting to server ldap %s') % (self.LDAP_SERVER, ))
logger.notifyChannel(
"LDAP Address",
netsvc.LOG_DEBUG,
_('connecting to server ldap %s') % (self.LDAP_SERVER, )
)
if self.PORT: if self.PORT:
self.connexion = ldap.open(self.LDAP_SERVER, self.PORT) self.connexion = ldap.open(self.LDAP_SERVER, self.PORT)
else: else:
@ -104,19 +121,24 @@ class LDAPAddress(orm.Model):
def init(self, cr): def init(self, cr):
logger = netsvc.Logger() logger = netsvc.Logger()
try: try:
logger.notifyChannel(_('LDAP address init'),
logger.notifyChannel(
_('LDAP address init'),
netsvc.LOG_INFO, netsvc.LOG_INFO,
_('try to ALTER TABLE res_partner_address RENAME ' _('try to ALTER TABLE res_partner_address RENAME '
'column name to lastname ;')) 'column name to lastname ;'))
cr.execute('ALTER TABLE res_partner_address RENAME column name to lastname ;')
cr.execute(
'ALTER TABLE res_partner_address'
'RENAME column name to lastname ;'
)
except Exception: except Exception:
cr.rollback() cr.rollback()
logger.notifyChannel(_('LDAP address init'),
logger.notifyChannel(
_('LDAP address init'),
netsvc.LOG_INFO, netsvc.LOG_INFO,
_('Warning ! impossible to rename column name'
' into lastname, this is probabely aleready'
' done or does not exist'))
_('Warning ! impossible to rename column name into lastname, '
'this is probably already done or does not exist')
)
def _compute_name(self, firstname, lastname): def _compute_name(self, firstname, lastname):
firstname = firstname or u'' firstname = firstname or u''
@ -137,13 +159,16 @@ class LDAPAddress(orm.Model):
return dict(res) return dict(res)
# TODO get the new version of name search not vulnerable to sql injections # TODO get the new version of name search not vulnerable to sql injections
# def name_search(self, cursor, user, name, args=None, operator='ilike', context=None, limit=100):
# def name_search(
# self, cursor, user, name, args=None, operator='ilike',
# context=None, limit=100):
# if not context: context = {} # if not context: context = {}
# prep_name = '.*%s.*' %(name) # prep_name = '.*%s.*' %(name)
# cursor.execute(("select id from res_partner_address where"
# " (to_ascii(convert( lastname, 'UTF8', 'LATIN1'),'LATIN-1') ~* '%s'"
# " or to_ascii(convert( firstname, 'UTF8', 'LATIN1'),'LATIN-1') ~* '%s')"
# " limit %s") % (prep_name, prep_name, limit))
# cursor.execute(("""
# select id from res_partner_address where
# (to_ascii(convert( lastname, 'UTF8', 'LATIN1'),'LATIN-1') ~* '%s'
# or to_ascii(convert( firstname, 'UTF8', 'LATIN1'),'LATIN-1') ~* '%s')
# limit %s""") % (prep_name, prep_name, limit))
# res = cursor.fetchall() # res = cursor.fetchall()
# if res: # if res:
# res = [x[0] for x in res] # res = [x[0] for x in res]
@ -153,8 +178,10 @@ class LDAPAddress(orm.Model):
# partner_obj=self.pool.get('res.partner') # partner_obj=self.pool.get('res.partner')
# part_len = len(res)-limit # part_len = len(res)-limit
# if part_len > 0: # if part_len > 0:
# partner_res = partner_obj.search(cursor, user, [('name', 'ilike', name)],
# limit=part_len, context=context)
# partner_res = partner_obj.search(
# cursor, user, [('name', 'ilike', name)],
# limit=part_len, context=context
# )
# for p in partner_res: # for p in partner_res:
# addresses = partner_obj.browse(cursor, user, p).address # addresses = partner_obj.browse(cursor, user, p).address
# # Take each contact and add it to # # Take each contact and add it to
@ -165,15 +192,22 @@ class LDAPAddress(orm.Model):
_columns = { _columns = {
'firstname': fields.char('First name', size=256), 'firstname': fields.char('First name', size=256),
'lastname': fields.char('Last name', size=256), 'lastname': fields.char('Last name', size=256),
'name': fields.function(_name_get_fnc, method=True,
type="char", size=512,
store=True, string='Contact Name',
'name': fields.function(
_name_get_fnc,
method=True,
type="char",
size=512,
store=True,
string='Contact Name',
help='Name generated from the first name and last name', help='Name generated from the first name and last name',
nodrop=True),
nodrop=True
),
'private_phone': fields.char('Private phone', size=128), 'private_phone': fields.char('Private phone', size=128),
} }
def create(self, cursor, uid, vals, context={}):
def create(self, cursor, uid, vals, context=None):
if context is None:
context = {}
self.getconn(cursor, uid, {}) self.getconn(cursor, uid, {})
ids = None ids = None
self.validate_entries(vals, cursor, uid, ids) self.validate_entries(vals, cursor, uid, ids)
@ -234,7 +268,8 @@ class LDAPAddress(orm.Model):
mobile = vals.get('mobile', False) mobile = vals.get('mobile', False)
private_phone = vals.get('private_phone', False) private_phone = vals.get('private_phone', False)
if email: if email:
if re.match("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$",
if re.match("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\."
"([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$",
email) is None: email) is None:
raise orm.except_orm(_('Warning !'), raise orm.except_orm(_('Warning !'),
_('Please enter a valid e-mail')) _('Please enter a valid e-mail'))
@ -244,11 +279,13 @@ class LDAPAddress(orm.Model):
phone_number = phone_tuple[1] phone_number = phone_tuple[1]
if phone_number: if phone_number:
if not phone_number.startswith('+'): if not phone_number.startswith('+'):
raise orm.except_orm(_('Warning !'),
raise orm.except_orm(
_('Warning !'),
_('Please enter a valid phone number in %s' _('Please enter a valid phone number in %s'
' international format (i.e. leading +)') % phone_tuple[0])
' international format (i.e. leading +)')
% phone_tuple[0])
def getVals(self, att_name, key, vals, dico, uid, ids, cursor, context=None):
def getVals(self, att_name, key, vals, dico, uid, ids, cr, context=None):
"""map to values to dict""" """map to values to dict"""
if not context: if not context:
context = {} context = {}
@ -258,7 +295,7 @@ class LDAPAddress(orm.Model):
else: else:
if context.get('init_mode'): if context.get('init_mode'):
return False return False
tmp = self.read(cursor, uid, ids, [key], context={})
tmp = self.read(cr, uid, ids, [key], context={})
if tmp.get(key, False): if tmp.get(key, False):
dico[att_name] = tmp[key] dico[att_name] = tmp[key]
@ -267,7 +304,9 @@ class LDAPAddress(orm.Model):
try: try:
return in_buf.encode() return in_buf.encode()
except Exception: except Exception:
return unicodedata.normalize("NFKD", in_buf).encode('ascii', 'ignore')
return unicodedata.normalize("NFKD", in_buf).encode(
'ascii', 'ignore'
)
return in_buf return in_buf
def unUnicodize(self, indict): def unUnicodize(self, indict):
@ -293,12 +332,12 @@ class LDAPAddress(orm.Model):
if not vals.get(val): if not vals.get(val):
vals[val] = previousvalue[val] vals[val] = previousvalue[val]
def mappLdapObject(self, id, vals, cursor, uid, context):
def mappLdapObject(self, id, vals, cr, uid, context):
"""Mapp ResPArtner adress to moddlist""" """Mapp ResPArtner adress to moddlist"""
self.addNeededFields(id, vals, cursor, uid)
conn = self.getconn(cursor, uid, {})
self.addNeededFields(id, vals, cr, uid)
conn = self.getconn(cr, uid, {})
partner_obj = self.pool.get('res.partner') partner_obj = self.pool.get('res.partner')
part_name = partner_obj.browse(cursor, uid, vals['partner_id']).name
part_name = partner_obj.browse(cr, uid, vals['partner_id']).name
vals['partner'] = part_name vals['partner'] = part_name
name = self._compute_name(vals.get('firstname'), vals.get('lastname')) name = self._compute_name(vals.get('firstname'), vals.get('lastname'))
if name: if name:
@ -317,16 +356,22 @@ class LDAPAddress(orm.Model):
if not vals.get('street2'): if not vals.get('street2'):
vals['street2'] = u'' vals['street2'] = u''
street_key = 'street' street_key = 'street'
if self.getconn(cursor, uid, {}).ACTIVDIR:
if self.getconn(cr, uid, {}).ACTIVDIR:
# ENTERING THE M$ Realm and it is weird # ENTERING THE M$ Realm and it is weird
# We manage the address # We manage the address
street_key = 'streetAddress' street_key = 'streetAddress'
contact_obj[street_key] = vals['street'] + "\r\n" + vals['street2'] contact_obj[street_key] = vals['street'] + "\r\n" + vals['street2']
# we modifiy the class # we modifiy the class
contact_obj['objectclass'] = ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'user']
contact_obj['objectclass'] = [
'top',
'person',
'organizationalPerson',
'inetOrgPerson',
'user',
]
# we handle the country # we handle the country
if vals.get('country_id'): if vals.get('country_id'):
country = self.browse(cursor, uid, id, context=context).country_id
country = self.browse(cr, uid, id, context=context).country_id
if country: if country:
vals['country_id'] = country.name vals['country_id'] = country.name
vals['c'] = country.code vals['c'] = country.code
@ -334,39 +379,71 @@ class LDAPAddress(orm.Model):
vals['country_id'] = False vals['country_id'] = False
vals['c'] = False vals['c'] = False
if vals.get('country_id', False): if vals.get('country_id', False):
self.getVals('co', 'country_id', vals, contact_obj, uid, id, cursor, context)
self.getVals('c', 'c', vals, contact_obj, uid, id, cursor, context)
self.getVals(
'co', 'country_id', vals, contact_obj, uid, id, cr,
context
)
self.getVals('c', 'c', vals, contact_obj, uid, id, cr, context)
# we compute the display name # we compute the display name
vals['display'] = '%s %s' % (vals['partner'], contact_obj['cn'][0]) vals['display'] = '%s %s' % (vals['partner'], contact_obj['cn'][0])
# we get the title # we get the title
if self.browse(cursor, uid, id).function:
contact_obj['description'] = self.browse(cursor, uid, id).function.name
if self.browse(cr, uid, id).function:
contact_obj['description'] = self.browse(
cr, uid, id).function.name
# we replace carriage return # we replace carriage return
if vals.get('comment', False): if vals.get('comment', False):
vals['comment'] = vals['comment'].replace("\n", "\r\n") vals['comment'] = vals['comment'].replace("\n", "\r\n")
# Active directory specific fields # Active directory specific fields
self.getVals('company', 'partner', vals, contact_obj, uid, id, cursor, context)
self.getVals('info', 'comment', vals, contact_obj, uid, id, cursor, context)
self.getVals('displayName', 'partner', vals, contact_obj, uid, id, cursor, context)
self.getVals(
'company', 'partner', vals, contact_obj, uid, id, cr, context
)
self.getVals(
'info', 'comment', vals, contact_obj, uid, id, cr, context
)
self.getVals(
'displayName', 'partner', vals, contact_obj, uid, id, cr,
context
)
# Web site management # Web site management
if self.browse(cursor, uid, id).partner_id.website:
vals['website'] = self.browse(cursor, uid, id).partner_id.website
self.getVals('wWWHomePage', 'website', vals, contact_obj, uid, id, cursor, context)
if self.browse(cr, uid, id).partner_id.website:
vals['website'] = self.browse(cr, uid, id).partner_id.website
self.getVals(
'wWWHomePage', 'website', vals, contact_obj, uid, id, cr,
context
)
del(vals['website']) del(vals['website'])
self.getVals('title', 'title', vals, contact_obj, uid, id, cursor, context)
self.getVals(
'title', 'title', vals, contact_obj, uid, id, cr, context
)
else: else:
contact_obj[street_key] = vals['street'] + u"\n" + vals['street2'] contact_obj[street_key] = vals['street'] + u"\n" + vals['street2']
self.getVals('o', 'partner', vals, contact_obj, uid, id, cursor, context)
self.getVals(
'o', 'partner', vals, contact_obj, uid, id, cr, context
)
# Common attributes # Common attributes
self.getVals('givenName', 'firstname', vals, contact_obj, uid, id, cursor, context)
self.getVals('mail', 'email', vals, contact_obj, uid, id, cursor, context)
self.getVals('telephoneNumber', 'phone', vals, contact_obj, uid, id, cursor, context)
self.getVals('l', 'city', vals, contact_obj, uid, id, cursor, context)
self.getVals('facsimileTelephoneNumber', 'fax', vals, contact_obj, uid, id, cursor, context)
self.getVals('mobile', 'mobile', vals, contact_obj, uid, id, cursor, context)
self.getVals('homePhone', 'private_phone', vals, contact_obj, uid, id, cursor, context)
self.getVals('postalCode', 'zip', vals, contact_obj, uid, id, cursor, context)
self.getVals(
'givenName', 'firstname', vals, contact_obj, uid, id, cr, context
)
self.getVals('mail', 'email', vals, contact_obj, uid, id, cr, context)
self.getVals(
'telephoneNumber', 'phone', vals, contact_obj, uid, id, cr, context
)
self.getVals('l', 'city', vals, contact_obj, uid, id, cr, context)
self.getVals(
'facsimileTelephoneNumber', 'fax', vals, contact_obj, uid, id, cr,
context
)
self.getVals(
'mobile', 'mobile', vals, contact_obj, uid, id, cr, context
)
self.getVals(
'homePhone', 'private_phone', vals, contact_obj, uid, id, cr,
context
)
self.getVals(
'postalCode', 'zip', vals, contact_obj, uid, id, cr, context
)
self.unUnicodize(contact_obj) self.unUnicodize(contact_obj)
return contact_obj return contact_obj
@ -376,10 +453,17 @@ class LDAPAddress(orm.Model):
conn = self.connectToLdap(cursor, uid, context=context) conn = self.connectToLdap(cursor, uid, context=context)
try: try:
if self.getconn(cursor, uid, context).ACTIVDIR: if self.getconn(cursor, uid, context).ACTIVDIR:
conn.connexion.add_s("CN=%s,OU=%s,%s" % (contact_obj['cn'][0], conn.OU, conn.CONTACT_DN),
ldap.modlist.addModlist(contact_obj))
conn.connexion.add_s(
"CN=%s,OU=%s,%s" % (contact_obj['cn'][0],
conn.OU,
conn.CONTACT_DN),
ldap.modlist.addModlist(contact_obj)
)
else: else:
conn.connexion.add_s("uid=terp_%s,OU=%s,%s" % (str(id), conn.OU, conn.CONTACT_DN),
conn.connexion.add_s(
"uid=terp_%s,OU=%s,%s" % (str(id),
conn.OU,
conn.CONTACT_DN),
ldap.modlist.addModlist(contact_obj)) ldap.modlist.addModlist(contact_obj))
except Exception: except Exception:
raise raise
@ -403,7 +487,10 @@ class LDAPAddress(orm.Model):
val = val[0] val = val[0]
modlist.append((ldap.MOD_REPLACE, key, val)) modlist.append((ldap.MOD_REPLACE, key, val))
else: else:
modlist = ldap.modlist.modifyModlist(old_contatc_obj[1], contact_obj)
modlist = ldap.modlist.modifyModlist(
old_contatc_obj[1],
contact_obj
)
try: try:
conn.connexion.modify_s(old_contatc_obj[0], modlist) conn.connexion.modify_s(old_contatc_obj[0], modlist)
conn.connexion.unbind_s() conn.connexion.unbind_s()
@ -429,18 +516,20 @@ class LDAPAddress(orm.Model):
raise raise
def getLdapContact(self, conn, id): def getLdapContact(self, conn, id):
result = conn.connexion.search_ext_s("ou=%s, %s" % (conn.OU, conn.CONTACT_DN),
result = conn.connexion.search_ext_s(
"ou=%s, %s" % (conn.OU, conn.CONTACT_DN),
ldap.SCOPE_SUBTREE, ldap.SCOPE_SUBTREE,
"(&(objectclass=*)(uid=terp_" + str(id) + "))")
"(&(objectclass=*)(uid=terp_" + str(id) + "))"
)
if not result: if not result:
raise ldap.NO_SUCH_OBJECT raise ldap.NO_SUCH_OBJECT
return result[0] return result[0]
def ldaplinkactive(self, cursor, uid, context=None): def ldaplinkactive(self, cursor, uid, context=None):
"""Check if ldap is activated for this company""" """Check if ldap is activated for this company"""
user = self.pool.get('res.users').browse(cursor, uid, uid, context=context)
company = self.pool.get('res.company').browse(cursor, uid, user.company_id.id, context=context)
return company.ldap_active
users_pool = self.pool['res.users']
user = users_pool.browse(cursor, uid, uid, context=context)
return user.company_id.ldap_active
def getconn(self, cursor, uid, context=None): def getconn(self, cursor, uid, context=None):
"""LdapConnMApper""" """LdapConnMApper"""

16
__unported__/partner_address_ldap/company.py

@ -4,7 +4,8 @@
# Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com) # Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved # All Right Reserved
# #
# Author : Nicolas Bessi (Camptocamp), Thanks to Laurent Lauden for his code adaptation
# Author : Nicolas Bessi (Camptocamp),
# Thanks to Laurent Lauden for his code adaptation
# Active directory Donor: M. Benadiba (Informatique Assistances.fr) # Active directory Donor: M. Benadiba (Informatique Assistances.fr)
# Contribution : Joel Grand-Guillaume # Contribution : Joel Grand-Guillaume
# #
@ -60,7 +61,11 @@ class Res_company(orm.Model):
size=128, size=128,
help="Example: ldap.camptocamp.com" help="Example: ldap.camptocamp.com"
), ),
'passwd': fields.char('ldap password', size=128, help="Example: Mypassword1234"),
'passwd': fields.char(
'ldap password',
size=128,
help="Example: Mypassword1234"
),
'ldap_active': fields.boolean( 'ldap_active': fields.boolean(
'Activate ldap link for this company', 'Activate ldap link for this company',
help='If not check nothing will be reported into the ldap' help='If not check nothing will be reported into the ldap'
@ -69,7 +74,8 @@ class Res_company(orm.Model):
'Active Directory ?', 'Active Directory ?',
help='The ldap is part of an Active Directory' help='The ldap is part of an Active Directory'
), ),
'ldap_port': fields.integer('LDAP Port',
help="If not specified, the default port"
"(389), will be used")
'ldap_port': fields.integer(
'LDAP Port',
help="If not specified, the default port(389), will be used"
),
} }

11
__unported__/partner_address_ldap/partner.py

@ -4,7 +4,8 @@
# Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com) # Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved # All Right Reserved
# #
# Author : Nicolas Bessi (Camptocamp), Thanks to Laurent Lauden for his code adaptation
# Author : Nicolas Bessi (Camptocamp),
# Thanks to Laurent Lauden for his code adaptation
# Active directory Donor: M. Benadiba (Informatique Assistances.fr) # Active directory Donor: M. Benadiba (Informatique Assistances.fr)
# Contribution : Joel Grand-Guillaume # Contribution : Joel Grand-Guillaume
# #
@ -39,11 +40,11 @@ class LdapPartner(orm.Model):
related addresses""" related addresses"""
_inherit = 'res.partner' _inherit = 'res.partner'
def unlink(self, cursor, uid, ids, context=None):
def unlink(self, cr, uid, ids, context=None):
context = context or {} context = context or {}
addr_obj = self.pool.get('res.partner.address') addr_obj = self.pool.get('res.partner.address')
if not isinstance(ids, list): if not isinstance(ids, list):
ids = [ids] ids = [ids]
addr_ids = addr_obj.search(cursor, uid, [('partner_id', 'in', ids)])
addr_obj.unlink(cursor, uid, addr_ids, context=context)
return super(LdapPartner, self).unlink(cursor, uid, ids, context=context)
addr_ids = addr_obj.search(cr, uid, [('partner_id', 'in', ids)])
addr_obj.unlink(cr, uid, addr_ids, context=context)
return super(LdapPartner, self).unlink(cr, uid, ids, context=context)

2
__unported__/partner_address_ldap/wizard/__init__.py

@ -29,4 +29,4 @@
# #
############################################################################## ##############################################################################
import wiz_import_adresses
from . import wiz_import_adresses

43
__unported__/partner_address_ldap/wizard/wiz_import_adresses.py

@ -41,6 +41,8 @@ import re
import wizard import wizard
from openerp import pooler from openerp import pooler
from openerp import netsvc from openerp import netsvc
from openerp.tools.translate import _
from openerp.tools import ustr
_FORM = '''<?xml version="1.0"?> _FORM = '''<?xml version="1.0"?>
<form string="Export addresses to ldap"> <form string="Export addresses to ldap">
@ -63,9 +65,11 @@ _FIELDS = {
} }
# As this is a bulk batch wizard the performance process was not really taken in account ###
# The ideal way of doing would be to modify the connexion settings in order to have a connexion singleton
# in the file partner.py it will avoid connexion renegotiation for each partner.
# As this is a bulk batch wizard the performance process was not really
# taken in account ###
# The ideal way of doing would be to modify the connexion settings in
# order to have a connexion singleton in the file partner.py it will
# avoid connexion renegotiation for each partner.
def _action_import_addresses(self, cr, uid, data, context): def _action_import_addresses(self, cr, uid, data, context):
""" This function create or update each addresses present in the database. """ This function create or update each addresses present in the database.
It will also generate an error report""" It will also generate an error report"""
@ -92,23 +96,29 @@ def _action_import_addresses(self, cr, uid, data, context):
# Validating the mail # Validating the mail
if add.email: if add.email:
if re.match( if re.match(
"^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$", add.email) is None or\
"^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\."
"([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$", add.email) is None or\
re.search(u"[éèàêöüäï&]", add.email) is not None: re.search(u"[éèàêöüäï&]", add.email) is not None:
msg = u'Address %s for partner %s has email that is invalid %s' % (
unicode(vals['firstname']) + ' ' + unicode(vals['lastname']),
msg = _(
'Address %s for partner %s has email that is invalid %s'
) % (
ustr(vals['firstname']) + ' ' + ustr(vals['lastname']),
add.partner_id.name, add.partner_id.name,
unicode(add.email)
ustr(add.email)
) )
logger.notifyChannel('ldap export', netsvc.LOG_INFO, msg) logger.notifyChannel('ldap export', netsvc.LOG_INFO, msg)
error_report.append(msg) error_report.append(msg)
vals['email'] = False vals['email'] = False
# Validating the Phone # Validating the Phone
for key in phone_fields: for key in phone_fields:
if (not unicode(vals[key]).startswith('+') or unicode(vals[key]).find("\n") != -1
or re.search(u"[éèàêöüä#&]", unicode(vals[key])) is not None):
if (not ustr(vals[key]).startswith('+')
or ustr(vals[key]).find("\n") != -1
or re.search(u"[éèàêöüä#&]", ustr(vals[key])) is not None):
vals[key] = False vals[key] = False
msg = u'Addresse %s for partner %s has %s that is invalid ' % (
unicode(vals['firstname']) + ' ' + unicode(vals['lastname']),
msg = _(
'Address %s for partner %s has %s that is invalid '
) % (
ustr(vals['firstname']) + ' ' + ustr(vals['lastname']),
add.partner_id.name, add.partner_id.name,
key key
) )
@ -116,20 +126,23 @@ def _action_import_addresses(self, cr, uid, data, context):
error_report.append(msg) error_report.append(msg)
# Validating the CN # Validating the CN
if not add.lastname and add.firstname: if not add.lastname and add.firstname:
msg = (u'!!! Address %s for partner %s has no last name and first name that is valid partner name was used'
% (unicode(add.id), add.partner_id.name))
msg = (_('!!! Address %s for partner %s has no last name and '
'first name that is valid partner name was used')
% (ustr(add.id), add.partner_id.name))
logger.notifyChannel('ldap export', netsvc.LOG_INFO, msg) logger.notifyChannel('ldap export', netsvc.LOG_INFO, msg)
error_report.append(msg) error_report.append(msg)
# We save to LDAP # We save to LDAP
add.write(vals, {'init_mode': True}) add.write(vals, {'init_mode': True})
# we by pass the encoding errors # we by pass the encoding errors
map(lambda x: unicodedata.normalize("NFKD", x).encode('ascii', 'ignore'), error_report)
map(lambda x: unicodedata.normalize("NFKD", x).encode('ascii', 'ignore'),
error_report)
error_report = "\n".join(error_report) error_report = "\n".join(error_report)
logger.notifyChannel("MY TOPIC", netsvc.LOG_ERROR, error_report) logger.notifyChannel("MY TOPIC", netsvc.LOG_ERROR, error_report)
try: try:
data = base64.encodestring(error_report.encode()) data = base64.encodestring(error_report.encode())
except: except:
data = base64.encodestring("Could not generate report file. Please look in the log for details")
data = base64.encodestring("Could not generate report file. "
"Please look in the log for details")
return {'errors': data} return {'errors': data}

1
account_partner_merge/__init__.py

@ -18,4 +18,5 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
from . import partner_merge from . import partner_merge

12
account_partner_merge/partner_merge.py

@ -25,12 +25,16 @@ class MergePartnerAutomatic(orm.TransientModel):
_inherit = 'base.partner.merge.automatic.wizard' _inherit = 'base.partner.merge.automatic.wizard'
def _update_values(self, cr, uid, src_partners, dst_partner, context=None): def _update_values(self, cr, uid, src_partners, dst_partner, context=None):
"""Make sure we don't forget to update the stored value of
invoice field commercial_partner_id
""" """
Make sure we don't forget to update the stored value of invoice field commercial_partner_id
"""
super(MergePartnerAutomatic, self)._update_values(cr, uid, src_partners, dst_partner, context=context)
super(MergePartnerAutomatic, self)._update_values(
cr, uid, src_partners, dst_partner, context=context
)
invoice_obj = self.pool.get('account.invoice') invoice_obj = self.pool.get('account.invoice')
invoice_ids = invoice_obj.search(cr, uid, [('partner_id', '=', dst_partner.id)], context=context)
invoice_ids = invoice_obj.search(
cr, uid, [('partner_id', '=', dst_partner.id)], context=context
)
# call write to refresh stored value # call write to refresh stored value
invoice_obj.write(cr, uid, invoice_ids, {}, context=context) invoice_obj.write(cr, uid, invoice_ids, {}, context=context)

131
base_contact/base_contact.py

@ -20,14 +20,16 @@
############################################################################## ##############################################################################
from openerp.osv import fields, orm, expression from openerp.osv import fields, orm, expression
from openerp.tools.translate import _
class res_partner(orm.Model): class res_partner(orm.Model):
_inherit = 'res.partner' _inherit = 'res.partner'
_contact_type = [
('standalone', 'Standalone Contact'),
('attached', 'Attached to existing Contact'),
def _type_selection(self, cr, uid, context=None):
return [
('standalone', _('Standalone Contact')),
('attached', _('Attached to existing Contact')),
] ]
def _get_contact_type(self, cr, uid, ids, field_name, args, context=None): def _get_contact_type(self, cr, uid, ids, field_name, args, context=None):
@ -38,14 +40,32 @@ class res_partner(orm.Model):
return result return result
_columns = { _columns = {
'contact_type': fields.function(_get_contact_type, type='selection', selection=_contact_type,
string='Contact Type', required=True, select=1, store=True),
'contact_id': fields.many2one('res.partner', 'Main Contact',
domain=[('is_company', '=', False), ('contact_type', '=', 'standalone')]),
'other_contact_ids': fields.one2many('res.partner', 'contact_id', 'Others Positions'),
'contact_type': fields.function(
_get_contact_type,
type='selection',
selection=lambda self, *a, **kw: self._type_selection(*a, **kw),
string='Contact Type',
required=True,
select=1,
store=True,
),
'contact_id': fields.many2one(
'res.partner',
'Main Contact',
domain=[
('is_company', '=', False),
('contact_type', '=', 'standalone'),
],
),
'other_contact_ids': fields.one2many(
'res.partner',
'contact_id',
'Others Positions',
),
# Person specific fields # Person specific fields
# add a 'birthdate' as date field, i.e different from char 'birthdate' introduced v6.1!
# add a 'birthdate' as date field, i.e different from char
# 'birthdate' introduced v6.1!
'birthdate_date': fields.date('Birthdate'), 'birthdate_date': fields.date('Birthdate'),
'nationality_id': fields.many2one('res.country', 'Nationality'), 'nationality_id': fields.many2one('res.country', 'Nationality'),
} }
@ -57,54 +77,69 @@ class res_partner(orm.Model):
def _basecontact_check_context(self, cr, user, mode, context=None): def _basecontact_check_context(self, cr, user, mode, context=None):
""" Remove 'search_show_all_positions' for non-search mode. """ Remove 'search_show_all_positions' for non-search mode.
Keeping it in context can result in unexpected behaviour (ex: reading Keeping it in context can result in unexpected behaviour (ex: reading
one2many might return wrong result - i.e with "attached contact" removed
even if it's directly linked to a company). """
one2many might return wrong result - i.e with "attached contact"
removed even if it's directly linked to a company).
"""
if context is None: if context is None:
context = {} context = {}
if mode != 'search': if mode != 'search':
context.pop('search_show_all_positions', None) context.pop('search_show_all_positions', None)
return context return context
def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
def search(
self, cr, user, args, offset=0, limit=None, order=None,
context=None, count=False):
""" Display only standalone contact matching ``args`` or having """ Display only standalone contact matching ``args`` or having
attached contact matching ``args`` """ attached contact matching ``args`` """
if context is None: if context is None:
context = {} context = {}
if context.get('search_show_all_positions') is False: if context.get('search_show_all_positions') is False:
args = expression.normalize_domain(args) args = expression.normalize_domain(args)
attached_contact_args = expression.AND((args, [('contact_type', '=', 'attached')]))
attached_contact_ids = super(res_partner, self).search(cr, user, attached_contact_args,
context=context)
attached_contact_args = expression.AND(
(args, [('contact_type', '=', 'attached')])
)
attached_contact_ids = super(res_partner, self).search(
cr, user, attached_contact_args, context=context
)
args = expression.OR(( args = expression.OR((
expression.AND(([('contact_type', '=', 'standalone')], args)), expression.AND(([('contact_type', '=', 'standalone')], args)),
[('other_contact_ids', 'in', attached_contact_ids)], [('other_contact_ids', 'in', attached_contact_ids)],
)) ))
return super(res_partner, self).search(cr, user, args, offset=offset, limit=limit,
order=order, context=context, count=count)
return super(res_partner, self).search(
cr, user, args, offset=offset, limit=limit, order=order,
context=context, count=count
)
def create(self, cr, user, vals, context=None): def create(self, cr, user, vals, context=None):
context = self._basecontact_check_context(cr, user, 'create', context) context = self._basecontact_check_context(cr, user, 'create', context)
if not vals.get('name') and vals.get('contact_id'): if not vals.get('name') and vals.get('contact_id'):
vals['name'] = self.browse(cr, user, vals['contact_id'], context=context).name
vals['name'] = self.browse(
cr, user, vals['contact_id'], context=context).name
return super(res_partner, self).create(cr, user, vals, context=context) return super(res_partner, self).create(cr, user, vals, context=context)
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
def read(
self, cr, user, ids, fields=None, context=None,
load='_classic_read'):
context = self._basecontact_check_context(cr, user, 'read', context) context = self._basecontact_check_context(cr, user, 'read', context)
return super(res_partner, self).read(cr, user, ids, fields=fields, context=context, load=load)
return super(res_partner, self).read(
cr, user, ids, fields=fields, context=context, load=load)
def write(self, cr, user, ids, vals, context=None): def write(self, cr, user, ids, vals, context=None):
context = self._basecontact_check_context(cr, user, 'write', context) context = self._basecontact_check_context(cr, user, 'write', context)
return super(res_partner, self).write(cr, user, ids, vals, context=context)
return super(
res_partner, self).write(cr, user, ids, vals, context=context)
def unlink(self, cr, user, ids, context=None): def unlink(self, cr, user, ids, context=None):
context = self._basecontact_check_context(cr, user, 'unlink', context) context = self._basecontact_check_context(cr, user, 'unlink', context)
return super(res_partner, self).unlink(cr, user, ids, context=context) return super(res_partner, self).unlink(cr, user, ids, context=context)
def _commercial_partner_compute(self, cr, uid, ids, name, args, context=None):
def _commercial_partner_compute(
self, cr, uid, ids, name, args, context=None):
""" Returns the partner that is considered the commercial """ Returns the partner that is considered the commercial
entity of this partner. The commercial entity holds the master data entity of this partner. The commercial entity holds the master data
for all commercial fields (see :py:meth:`~_commercial_fields`) """ for all commercial fields (see :py:meth:`~_commercial_fields`) """
result = super(res_partner, self)._commercial_partner_compute(cr, uid, ids, name, args, context=context)
result = super(res_partner, self)._commercial_partner_compute(
cr, uid, ids, name, args, context=context)
for partner in self.browse(cr, uid, ids, context=context): for partner in self.browse(cr, uid, ids, context=context):
if partner.contact_type == 'attached' and not partner.parent_id: if partner.contact_type == 'attached' and not partner.parent_id:
result[partner.id] = partner.contact_id.id result[partner.id] = partner.contact_id.id
@ -116,12 +151,14 @@ class res_partner(orm.Model):
return ['name', 'title'] return ['name', 'title']
def _contact_sync_from_parent(self, cr, uid, partner, context=None): def _contact_sync_from_parent(self, cr, uid, partner, context=None):
""" Handle sync of contact fields when a new parent contact entity is set,
as if they were related fields """
""" Handle sync of contact fields when a new parent contact entity
is set, as if they were related fields
"""
if partner.contact_id: if partner.contact_id:
contact_fields = self._contact_fields(cr, uid, context=context) contact_fields = self._contact_fields(cr, uid, context=context)
sync_vals = self._update_fields_values(cr, uid, partner.contact_id,
contact_fields, context=context)
sync_vals = self._update_fields_values(
cr, uid, partner.contact_id, contact_fields, context=context
)
partner.write(sync_vals) partner.write(sync_vals)
def update_contact(self, cr, uid, ids, vals, context=None): def update_contact(self, cr, uid, ids, vals, context=None):
@ -130,31 +167,42 @@ class res_partner(orm.Model):
if context.get('__update_contact_lock'): if context.get('__update_contact_lock'):
return return
contact_fields = self._contact_fields(cr, uid, context=context) contact_fields = self._contact_fields(cr, uid, context=context)
contact_vals = dict((field, vals[field]) for field in contact_fields if field in vals)
contact_vals = dict(
(field, vals[field]) for field in contact_fields if field in vals
)
if contact_vals: if contact_vals:
ctx = dict(context, __update_contact_lock=True) ctx = dict(context, __update_contact_lock=True)
self.write(cr, uid, ids, contact_vals, context=ctx) self.write(cr, uid, ids, contact_vals, context=ctx)
def _fields_sync(self, cr, uid, partner, update_values, context=None): def _fields_sync(self, cr, uid, partner, update_values, context=None):
""" Sync commercial fields and address fields from company and to children,
contact fields from contact and to attached contact after create/update,
just as if those were all modeled as fields.related to the parent """
super(res_partner, self)._fields_sync(cr, uid, partner, update_values, context=context)
"""Sync commercial fields and address fields from company and to
children, contact fields from contact and to attached contact
after create/update, just as if those were all modeled as
fields.related to the parent
"""
super(res_partner, self)._fields_sync(
cr, uid, partner, update_values, context=context
)
contact_fields = self._contact_fields(cr, uid, context=context) contact_fields = self._contact_fields(cr, uid, context=context)
# 1. From UPSTREAM: sync from parent contact # 1. From UPSTREAM: sync from parent contact
if update_values.get('contact_id'): if update_values.get('contact_id'):
self._contact_sync_from_parent(cr, uid, partner, context=context) self._contact_sync_from_parent(cr, uid, partner, context=context)
# 2. To DOWNSTREAM: sync contact fields to parent or related # 2. To DOWNSTREAM: sync contact fields to parent or related
elif any(field in contact_fields for field in update_values): elif any(field in contact_fields for field in update_values):
update_ids = [c.id for c in partner.other_contact_ids if not c.is_company]
update_ids = [
c.id for c in partner.other_contact_ids if not c.is_company
]
if partner.contact_id: if partner.contact_id:
update_ids.append(partner.contact_id.id) update_ids.append(partner.contact_id.id)
self.update_contact(cr, uid, update_ids, update_values, context=context)
self.update_contact(
cr, uid, update_ids, update_values, context=context
)
def onchange_contact_id(self, cr, uid, ids, contact_id, context=None): def onchange_contact_id(self, cr, uid, ids, contact_id, context=None):
values = {} values = {}
if contact_id: if contact_id:
values['name'] = self.browse(cr, uid, contact_id, context=context).name
values['name'] = self.browse(
cr, uid, contact_id, context=context).name
return {'value': values} return {'value': values}
def onchange_contact_type(self, cr, uid, ids, contact_type, context=None): def onchange_contact_type(self, cr, uid, ids, contact_type, context=None):
@ -167,18 +215,23 @@ class res_partner(orm.Model):
class ir_actions_window(orm.Model): class ir_actions_window(orm.Model):
_inherit = 'ir.actions.act_window' _inherit = 'ir.actions.act_window'
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
def read(
self, cr, user, ids, fields=None, context=None,
load='_classic_read'):
action_ids = ids action_ids = ids
if isinstance(ids, (int, long)): if isinstance(ids, (int, long)):
action_ids = [ids] action_ids = [ids]
actions = super(ir_actions_window, self).read(cr, user, action_ids, fields=fields, context=context, load=load)
actions = super(ir_actions_window, self).read(
cr, user, action_ids, fields=fields, context=context, load=load
)
for action in actions: for action in actions:
if action.get('res_model', '') == 'res.partner': if action.get('res_model', '') == 'res.partner':
# By default, only show standalone contact # By default, only show standalone contact
action_context = action.get('context', '{}') or '{}' action_context = action.get('context', '{}') or '{}'
if 'search_show_all_positions' not in action_context: if 'search_show_all_positions' not in action_context:
action['context'] = action_context.replace('{',
"{'search_show_all_positions': False,", 1)
action['context'] = action_context.replace(
'{', "{'search_show_all_positions': False,", 1
)
if isinstance(ids, (int, long)): if isinstance(ids, (int, long)):
if actions: if actions:
return actions[0] return actions[0]

127
base_contact/tests/test_base_contact.py

@ -35,14 +35,18 @@ class Test_Base_Contact(common.TransactionCase):
for attr, module, name in [ for attr, module, name in [
('main_partner_id', 'base', 'main_partner'), ('main_partner_id', 'base', 'main_partner'),
('bob_contact_id', 'base_contact', 'res_partner_contact1'), ('bob_contact_id', 'base_contact', 'res_partner_contact1'),
('bob_job1_id', 'base_contact', 'res_partner_contact1_work_position1'),
('bob_job1_id', 'base_contact',
'res_partner_contact1_work_position1'),
('roger_contact_id', 'base', 'res_partner_main2'), ('roger_contact_id', 'base', 'res_partner_main2'),
('roger_job2_id', 'base_contact', 'res_partner_main2_position_consultant')]:
('roger_job2_id', 'base_contact',
'res_partner_main2_position_consultant')]:
r = ModelData.get_object_reference(cr, uid, module, name) r = ModelData.get_object_reference(cr, uid, module, name)
setattr(self, attr, r[1] if r else False) setattr(self, attr, r[1] if r else False)
def test_00_show_only_standalone_contact(self): def test_00_show_only_standalone_contact(self):
"""Check that only standalone contact are shown if context explicitly state to not display all positions"""
"""Check that only standalone contact are shown if context
explicitly state to not display all positions
"""
cr, uid = self.cr, self.uid cr, uid = self.cr, self.uid
ctx = {'search_show_all_positions': False} ctx = {'search_show_all_positions': False}
partner_ids = self.partner.search(cr, uid, [], context=ctx) partner_ids = self.partner.search(cr, uid, [], context=ctx)
@ -51,7 +55,9 @@ class Test_Base_Contact(common.TransactionCase):
self.assertTrue(self.roger_job2_id not in partner_ids) self.assertTrue(self.roger_job2_id not in partner_ids)
def test_01_show_all_positions(self): def test_01_show_all_positions(self):
"""Check that all contact are show if context is empty or explicitly state to display all positions"""
"""Check that all contact are show if context is empty or
explicitly state to display all positions
"""
cr, uid = self.cr, self.uid cr, uid = self.cr, self.uid
partner_ids = self.partner.search(cr, uid, [], context=None) partner_ids = self.partner.search(cr, uid, [], context=None)
@ -64,41 +70,77 @@ class Test_Base_Contact(common.TransactionCase):
self.assertTrue(self.roger_job2_id in partner_ids) self.assertTrue(self.roger_job2_id in partner_ids)
def test_02_reading_other_contact_one2many_show_all_positions(self): def test_02_reading_other_contact_one2many_show_all_positions(self):
"""Check that readonly partner's ``other_contact_ids`` return all values whatever the context"""
"""Check that readonly partner's ``other_contact_ids`` return
all values whatever the context
"""
cr, uid = self.cr, self.uid cr, uid = self.cr, self.uid
def read_other_contacts(pid, context=None): def read_other_contacts(pid, context=None):
return self.partner.read(cr, uid, [pid], ['other_contact_ids'], context=context)[0]['other_contact_ids']
return self.partner.read(
cr, uid, [pid], ['other_contact_ids'],
context=context)[0]['other_contact_ids']
def read_contacts(pid, context=None): def read_contacts(pid, context=None):
return self.partner.read(cr, uid, [pid], ['child_ids'], context=context)[0]['child_ids']
return self.partner.read(
cr, uid, [pid], ['child_ids'], context=context)[0]['child_ids']
ctx = None ctx = None
self.assertEqual(read_other_contacts(self.bob_contact_id, context=ctx), [self.bob_job1_id])
self.assertEqual(
read_other_contacts(self.bob_contact_id, context=ctx),
[self.bob_job1_id],
)
ctx = {'search_show_all_positions': False} ctx = {'search_show_all_positions': False}
self.assertEqual(read_other_contacts(self.bob_contact_id, context=ctx), [self.bob_job1_id])
self.assertEqual(read_other_contacts(
self.bob_contact_id, context=ctx),
[self.bob_job1_id],
)
ctx = {'search_show_all_positions': True} ctx = {'search_show_all_positions': True}
self.assertEqual(read_other_contacts(self.bob_contact_id, context=ctx), [self.bob_job1_id])
self.assertEqual(
read_other_contacts(self.bob_contact_id, context=ctx),
[self.bob_job1_id],
)
ctx = None ctx = None
self.assertTrue(self.bob_job1_id in read_contacts(self.main_partner_id, context=ctx))
self.assertIn(
self.bob_job1_id,
read_contacts(self.main_partner_id, context=ctx),
)
ctx = {'search_show_all_positions': False} ctx = {'search_show_all_positions': False}
self.assertTrue(self.bob_job1_id in read_contacts(self.main_partner_id, context=ctx))
self.assertIn(
self.bob_job1_id,
read_contacts(self.main_partner_id, context=ctx),
)
ctx = {'search_show_all_positions': True} ctx = {'search_show_all_positions': True}
self.assertTrue(self.bob_job1_id in read_contacts(self.main_partner_id, context=ctx))
self.assertIn(
self.bob_job1_id,
read_contacts(self.main_partner_id, context=ctx),
)
def test_03_search_match_attached_contacts(self): def test_03_search_match_attached_contacts(self):
"""Check that searching partner also return partners having attached contacts matching search criteria"""
"""Check that searching partner also return partners having
attached contacts matching search criteria
"""
cr, uid = self.cr, self.uid cr, uid = self.cr, self.uid
# Bob's contact has one other position which is related to 'Your Company'
# so search for all contacts working for 'Your Company' should contain bob position.
partner_ids = self.partner.search(cr, uid, [('parent_id', 'ilike', 'Your Company')], context=None)
self.assertTrue(self.bob_job1_id in partner_ids)
# but when searching without 'all positions', we should get the position standalone contact instead.
# Bob's contact has one other position which is related to
# 'Your Company'
# so search for all contacts working for 'Your Company' should contain
# bob position.
partner_ids = self.partner.search(
cr, uid,
[('parent_id', 'ilike', 'Your Company')],
context=None
)
self.assertIn(self.bob_job1_id, partner_ids, )
# but when searching without 'all positions',
# we should get the position standalone contact instead.
ctx = {'search_show_all_positions': False} ctx = {'search_show_all_positions': False}
partner_ids = self.partner.search(cr, uid, [('parent_id', 'ilike', 'Your Company')], context=ctx)
self.assertTrue(self.bob_contact_id in partner_ids)
partner_ids = self.partner.search(
cr, uid,
[('parent_id', 'ilike', 'Your Company')],
context=ctx
)
self.assertIn(self.bob_contact_id, partner_ids, )
def test_04_contact_creation(self): def test_04_contact_creation(self):
"""Check that we're begin to create a contact""" """Check that we're begin to create a contact"""
@ -106,31 +148,54 @@ class Test_Base_Contact(common.TransactionCase):
# Create a contact using only name # Create a contact using only name
new_contact_id = self.partner.create(cr, uid, {'name': 'Bob Egnops'}) new_contact_id = self.partner.create(cr, uid, {'name': 'Bob Egnops'})
self.assertEqual(self.partner.browse(cr, uid, new_contact_id).contact_type, 'standalone')
self.assertEqual(
self.partner.browse(cr, uid, new_contact_id).contact_type,
'standalone',
)
# Create a contact with only contact_id # Create a contact with only contact_id
new_contact_id = self.partner.create(cr, uid, {'contact_id': self.bob_contact_id})
new_contact_id = self.partner.create(
cr, uid, {'contact_id': self.bob_contact_id}
)
new_contact = self.partner.browse(cr, uid, new_contact_id) new_contact = self.partner.browse(cr, uid, new_contact_id)
self.assertEqual(new_contact.name, 'Bob Egnops') self.assertEqual(new_contact.name, 'Bob Egnops')
self.assertEqual(new_contact.contact_type, 'attached') self.assertEqual(new_contact.contact_type, 'attached')
# Create a contact with both contact_id and name; # Create a contact with both contact_id and name;
# contact's name should override provided value in that case # contact's name should override provided value in that case
new_contact_id = self.partner.create(cr, uid, {'contact_id': self.bob_contact_id, 'name': 'Rob Egnops'})
self.assertEqual(self.partner.browse(cr, uid, new_contact_id).name, 'Bob Egnops')
new_contact_id = self.partner.create(
cr, uid, {'contact_id': self.bob_contact_id, 'name': 'Rob Egnops'}
)
self.assertEqual(
self.partner.browse(cr, uid, new_contact_id).name,
'Bob Egnops'
)
# Reset contact to standalone # Reset contact to standalone
self.partner.write(cr, uid, [new_contact_id], {'contact_id': False}) self.partner.write(cr, uid, [new_contact_id], {'contact_id': False})
self.assertEqual(self.partner.browse(cr, uid, new_contact_id).contact_type, 'standalone')
self.assertEqual(
self.partner.browse(cr, uid, new_contact_id).contact_type,
'standalone',
)
def test_05_contact_fields_sync(self): def test_05_contact_fields_sync(self):
"""Check that contact's fields are correctly synced between parent contact or related contacts"""
"""Check that contact's fields are correctly synced between
parent contact or related contacts
"""
cr, uid = self.cr, self.uid cr, uid = self.cr, self.uid
# Test DOWNSTREAM sync # Test DOWNSTREAM sync
self.partner.write(cr, uid, [self.bob_contact_id], {'name': 'Rob Egnops'})
self.assertEqual(self.partner.browse(cr, uid, self.bob_job1_id).name, 'Rob Egnops')
self.partner.write(
cr, uid, [self.bob_contact_id], {'name': 'Rob Egnops'}
)
self.assertEqual(
self.partner.browse(cr, uid, self.bob_job1_id).name,
'Rob Egnops',
)
# Test UPSTREAM sync # Test UPSTREAM sync
self.partner.write(cr, uid, [self.bob_job1_id], {'name': 'Bob Egnops'}) self.partner.write(cr, uid, [self.bob_job1_id], {'name': 'Bob Egnops'})
self.assertEqual(self.partner.browse(cr, uid, self.bob_contact_id).name, 'Bob Egnops')
self.assertEqual(
self.partner.browse(cr, uid, self.bob_contact_id).name,
'Bob Egnops',
)

1
base_location/__init__.py

@ -19,6 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
from . import better_zip from . import better_zip
from . import partner from . import partner
from . import state from . import state

14
base_location/better_zip.py

@ -57,19 +57,25 @@ class BetterZip(orm.Model):
def onchange_state_id(self, cr, uid, ids, state_id=False, context=None): def onchange_state_id(self, cr, uid, ids, state_id=False, context=None):
result = {} result = {}
if state_id: if state_id:
state = self.pool['res.country.state'].browse(cr, uid, state_id, context=context)
state = self.pool['res.country.state'].browse(
cr, uid, state_id, context=context
)
if state: if state:
result['value'] = {'country_id': state.country_id.id} result['value'] = {'country_id': state.country_id.id}
return result return result
def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
def name_search(
self, cr, uid, name, args=None, operator='ilike', context=None,
limit=100):
if args is None: if args is None:
args = [] args = []
if context is None: if context is None:
context = {} context = {}
ids = [] ids = []
if name: if name:
ids = self.search(cr, uid, [('name', 'ilike', name)] + args, limit=limit)
ids = self.search(
cr, uid, [('name', 'ilike', name)] + args, limit=limit)
if not ids: if not ids:
ids = self.search(cr, uid, [('city', operator, name)] + args, limit=limit)
ids = self.search(
cr, uid, [('city', operator, name)] + args, limit=limit)
return self.name_get(cr, uid, ids, context=context) return self.name_get(cr, uid, ids, context=context)

20
base_location/company.py

@ -31,9 +31,14 @@ class ResCompany(orm.Model):
if context is None: if context is None:
context = {} context = {}
if zip_id: if zip_id:
bzip = self.pool['res.better.zip'].browse(cr, uid, zip_id, context=context)
result = {'value': {'zip': bzip.name,
'country_id': bzip.country_id.id if bzip.country_id else False,
bzip = self.pool['res.better.zip'].browse(
cr, uid, zip_id, context=context
)
country_id = bzip.country_id.id if bzip.country_id else False
result = {
'value': {
'zip': bzip.name,
'country_id': country_id,
'city': bzip.city, 'city': bzip.city,
'state_id': bzip.state_id.id if bzip.state_id else False 'state_id': bzip.state_id.id if bzip.state_id else False
} }
@ -41,7 +46,10 @@ class ResCompany(orm.Model):
return result return result
_columns = { _columns = {
'better_zip_id': fields.many2one('res.better.zip', 'Location', select=1,
help=('Use the city name or the zip code'
' to search the location')),
'better_zip_id': fields.many2one(
'res.better.zip',
'Location',
select=1,
help=('Use the city name or the zip code to search the location'),
),
} }

11
base_location/partner.py

@ -24,15 +24,20 @@ from openerp.osv import orm, fields
class ResPartner(orm.Model): class ResPartner(orm.Model):
_inherit = "res.partner" _inherit = "res.partner"
_columns = {'zip_id': fields.many2one('res.better.zip', 'City/Location')}
_columns = {
'zip_id': fields.many2one('res.better.zip', 'City/Location'),
}
def onchange_zip_id(self, cursor, uid, ids, zip_id, context=None): def onchange_zip_id(self, cursor, uid, ids, zip_id, context=None):
if not zip_id: if not zip_id:
return {} return {}
if isinstance(zip_id, list): if isinstance(zip_id, list):
zip_id = zip_id[0] zip_id = zip_id[0]
bzip = self.pool['res.better.zip'].browse(cursor, uid, zip_id, context=context)
return {'value': {'zip': bzip.name,
bzip_pool = self.pool['res.better.zip']
bzip = bzip_pool.browse(cursor, uid, zip_id, context=context)
return {
'value': {
'zip': bzip.name,
'city': bzip.city, 'city': bzip.city,
'country_id': bzip.country_id.id if bzip.country_id else False, 'country_id': bzip.country_id.id if bzip.country_id else False,
'state_id': bzip.state_id.id if bzip.state_id else False, 'state_id': bzip.state_id.id if bzip.state_id else False,

9
base_location/state.py

@ -25,5 +25,10 @@ from openerp.osv import orm, fields
class ResCountryState(orm.Model): class ResCountryState(orm.Model):
_inherit = 'res.country.state' _inherit = 'res.country.state'
_columns = {'better_zip_ids': fields.one2many('res.better.zip', 'state_id', 'Cities')}
_columns = {
'better_zip_ids': fields.one2many(
'res.better.zip',
'state_id',
'Cities',
),
}

2
base_partner_merge/__init__.py

@ -1 +1 @@
import base_partner_merge
from . import base_partner_merge

4
base_partner_merge/__openerp__.py

@ -3,7 +3,9 @@
'author': 'OpenERP S.A.', 'author': 'OpenERP S.A.',
'category': 'Generic Modules/Base', 'category': 'Generic Modules/Base',
'version': '0.1', 'version': '0.1',
'description': """backport module, to be removed when we switch to saas2 on the private servers""",
'description': """
backport module, to be removed when we switch to saas2 on the private servers
""",
'depends': [ 'depends': [
'base', 'base',
], ],

10
base_partner_merge/base_partner_merge.py

@ -539,11 +539,15 @@ class MergePartnerAutomatic(orm.TransientModel):
).browse(cr, uid, ).browse(cr, uid,
list(partner_ids), list(partner_ids),
context=context) context=context)
ordered_partners = sorted(sorted(partners,
ordered_partners = sorted(
sorted(
partners,
key=operator.attrgetter('create_date'), key=operator.attrgetter('create_date'),
reverse=True),
reverse=True
),
key=operator.attrgetter('active'), key=operator.attrgetter('active'),
reverse=True)
reverse=True
)
return ordered_partners return ordered_partners
def _next_screen(self, cr, uid, this, context=None): def _next_screen(self, cr, uid, this, context=None):

51
base_partner_merge/validate_email.py

@ -42,31 +42,28 @@ except:
# even when it's not strictly necessary. This way we don't forget # even when it's not strictly necessary. This way we don't forget
# when it is necessary.) # when it is necessary.)
# #
WSP = r'[ \t]' # see 2.2.2. Structured Header Field Bodies
CRLF = r'(?:\r\n)' # see 2.2.3. Long Header Fields
NO_WS_CTL = r'\x01-\x08\x0b\x0c\x0f-\x1f\x7f' # see 3.2.1. Primitive Tokens
QUOTED_PAIR = r'(?:\\.)' # see 3.2.2. Quoted characters
FWS = r'(?:(?:{0}*{1})?{0}+)'.format(WSP, CRLF) # see 3.2.3. Folding white space and comments
CTEXT = r'[{0}\x21-\x27\x2a-\x5b\x5d-\x7e]'.format(NO_WS_CTL) # see 3.2.3
# see 3.2.3 (NB: The RFC includes COMMENT here as well, but that would be circular.)
WSP = r'[ \t]'
CRLF = r'(?:\r\n)'
NO_WS_CTL = r'\x01-\x08\x0b\x0c\x0f-\x1f\x7f'
QUOTED_PAIR = r'(?:\\.)'
FWS = r'(?:(?:{0}*{1})?{0}+)'.format(WSP, CRLF)
CTEXT = r'[{0}\x21-\x27\x2a-\x5b\x5d-\x7e]'.format(NO_WS_CTL)
CCONTENT = r'(?:{0}|{1})'.format(CTEXT, QUOTED_PAIR) CCONTENT = r'(?:{0}|{1})'.format(CTEXT, QUOTED_PAIR)
COMMENT = r'\((?:{0}?{1})*{0}?\)'.format(FWS, CCONTENT) # see 3.2.3
CFWS = r'(?:{0}?{1})*(?:{0}?{1}|{0})'.format(FWS, COMMENT) # see 3.2.3
ATEXT = r'[\w!#$%&\'\*\+\-/=\?\^`\{\|\}~]' # see 3.2.4. Atom
ATOM = r'{0}?{1}+{0}?'.format(CFWS, ATEXT) # see 3.2.4
DOT_ATOM_TEXT = r'{0}+(?:\.{0}+)*'.format(ATEXT) # see 3.2.4
DOT_ATOM = r'{0}?{1}{0}?'.format(CFWS, DOT_ATOM_TEXT) # see 3.2.4
QTEXT = r'[{0}\x21\x23-\x5b\x5d-\x7e]'.format(NO_WS_CTL) # see 3.2.5. Quoted strings
QCONTENT = r'(?:{0}|{1})'.format(QTEXT, QUOTED_PAIR) # see 3.2.5
COMMENT = r'\((?:{0}?{1})*{0}?\)'.format(FWS, CCONTENT)
CFWS = r'(?:{0}?{1})*(?:{0}?{1}|{0})'.format(FWS, COMMENT)
ATEXT = r'[\w!#$%&\'\*\+\-/=\?\^`\{\|\}~]'
ATOM = r'{0}?{1}+{0}?'.format(CFWS, ATEXT)
DOT_ATOM_TEXT = r'{0}+(?:\.{0}+)*'.format(ATEXT)
DOT_ATOM = r'{0}?{1}{0}?'.format(CFWS, DOT_ATOM_TEXT)
QTEXT = r'[{0}\x21\x23-\x5b\x5d-\x7e]'.format(NO_WS_CTL)
QCONTENT = r'(?:{0}|{1})'.format(QTEXT, QUOTED_PAIR)
QUOTED_STRING = r'{0}?"(?:{1}?{2})*{1}?"{0}?'.format(CFWS, FWS, QCONTENT) QUOTED_STRING = r'{0}?"(?:{1}?{2})*{1}?"{0}?'.format(CFWS, FWS, QCONTENT)
LOCAL_PART = r'(?:{0}|{1})'.format(DOT_ATOM, QUOTED_STRING) # see 3.4.1. Addr-spec specification
DTEXT = r'[{0}\x21-\x5a\x5e-\x7e]'.format(NO_WS_CTL) # see 3.4.1
DCONTENT = r'(?:{0}|{1})'.format(DTEXT, QUOTED_PAIR) # see 3.4.1
DOMAIN_LITERAL = r'{0}?\[(?:{1}?{2})*{1}?\]{0}?'.format(CFWS, FWS, DCONTENT) # see 3.4.1
DOMAIN = r'(?:{0}|{1})'.format(DOT_ATOM, DOMAIN_LITERAL) # see 3.4.1
ADDR_SPEC = r'{0}@{1}'.format(LOCAL_PART, DOMAIN) # see 3.4.1
# A valid address will match exactly the 3.4.1 addr-spec.
LOCAL_PART = r'(?:{0}|{1})'.format(DOT_ATOM, QUOTED_STRING)
DTEXT = r'[{0}\x21-\x5a\x5e-\x7e]'.format(NO_WS_CTL)
DCONTENT = r'(?:{0}|{1})'.format(DTEXT, QUOTED_PAIR)
DOMAIN_LITERAL = r'{0}?\[(?:{1}?{2})*{1}?\]{0}?'.format(CFWS, FWS, DCONTENT)
DOMAIN = r'(?:{0}|{1})'.format(DOT_ATOM, DOMAIN_LITERAL)
ADDR_SPEC = r'{0}@{1}'.format(LOCAL_PART, DOMAIN)
VALID_ADDRESS_REGEXP = '^' + ADDR_SPEC + '$' VALID_ADDRESS_REGEXP = '^' + ADDR_SPEC + '$'
@ -104,15 +101,11 @@ def validate_email(email, check_mx=False, verify=False):
if status != 250: if status != 250:
return False return False
break break
except smtplib.SMTPServerDisconnected: # Server not permits verify user
# Server not permits verify user
except smtplib.SMTPServerDisconnected:
break break
except smtplib.SMTPConnectError: except smtplib.SMTPConnectError:
continue continue
except (AssertionError, ServerError): except (AssertionError, ServerError):
return False return False
return True return True
# import sys
# sys.modules[__name__], sys.modules['validate_email_module'] = validate_email, sys.modules[__name__]
# from validate_email_module import *

2
base_partner_sequence/__init__.py

@ -18,5 +18,5 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
from . import partner from . import partner
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

2
firstname_display_name_trigger/__init__.py

@ -20,5 +20,3 @@
############################################################################## ##############################################################################
import res_partner import res_partner
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

6
firstname_display_name_trigger/__openerp__.py

@ -20,7 +20,8 @@
############################################################################## ##############################################################################
{ {
'name': 'Link module if partner_lastname and account_report_company are installed',
'name': ('Link module if partner_lastname and account_report_company are '
'installed'),
'version': '1.0', 'version': '1.0',
'author': 'Camptocamp', 'author': 'Camptocamp',
'maintainer': 'Camptocamp', 'maintainer': 'Camptocamp',
@ -31,7 +32,8 @@
'partner_firstname', 'partner_firstname',
], ],
'description': """ 'description': """
Adapt the computation of display name so that it gets visible in tree and kanban views.
Adapt the computation of display name so that it gets visible in tree and
kanban views.
""", """,
'website': 'http://www.camptocamp.com', 'website': 'http://www.camptocamp.com',
'data': [], 'data': [],

29
firstname_display_name_trigger/res_partner.py

@ -37,12 +37,13 @@ class ResPartner(orm.Model):
ids = [ids] ids = [ids]
res = [] res = []
for record in self.browse(cr, uid, ids, context=context): for record in self.browse(cr, uid, ids, context=context):
names = (record.lastname, record.firstname)
name = u" ".join([s for s in names if s])
name = self._prepare_name_custom(cr, uid, record, context=context)
if record.parent_id and not record.is_company: if record.parent_id and not record.is_company:
name = "%s, %s" % (record.parent_id.name, name) name = "%s, %s" % (record.parent_id.name, name)
if context.get('show_address'): if context.get('show_address'):
name = name + "\n" + self._display_address(cr, uid, record, without_company=True, context=context)
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')
name = name.replace('\n\n', '\n') name = name.replace('\n\n', '\n')
if context.get('show_email') and record.email: if context.get('show_email') and record.email:
@ -51,14 +52,26 @@ class ResPartner(orm.Model):
return res return res
_display_name_store_triggers = { _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', 'firstname', 'lastname'], 10)
'res.partner': (
lambda self, cr, uid, ids, context=None:
self.search(cr, uid, [
('id', 'child_of', ids)
]),
['parent_id', 'is_company', 'name', 'firstname', 'lastname'],
10
)
} }
# indirection 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)
# indirection to avoid passing a copy of the overridable method when
# declaring the function field
_display_name = lambda self, *a, **kw: self._display_name_compute(*a, **kw)
_columns = { _columns = {
# extra field to allow ORDER BY to match visible names # extra field to allow ORDER BY to match visible names
'display_name': fields.function(_display_name, type='char', string='Name', store=_display_name_store_triggers),
'display_name': fields.function(
_display_name,
type='char',
string='Name',
store=_display_name_store_triggers
),
} }

12
firstname_display_name_trigger/tests/test_display_name.py

@ -20,7 +20,11 @@ class test_display_name(common.TransactionCase):
}) })
partner_records = self.res_partner.browse(cr, uid, [partner_id]) partner_records = self.res_partner.browse(cr, uid, [partner_id])
p1 = partner_records[0] p1 = partner_records[0]
self.assertEqual(p1.display_name, 'Lastname Firstname', 'Partner display_name incorrect')
self.assertEqual(
p1.display_name,
'Lastname Firstname',
'Partner display_name incorrect'
)
def test_01_res_partner_write_lastname(self): def test_01_res_partner_write_lastname(self):
""" Test if the display name has been correctly set """ """ Test if the display name has been correctly set """
@ -33,7 +37,11 @@ class test_display_name(common.TransactionCase):
partner_records = self.res_partner.browse(cr, uid, [partner_id]) partner_records = self.res_partner.browse(cr, uid, [partner_id])
p1 = partner_records[0] p1 = partner_records[0]
self.res_partner.write(cr, uid, partner_id, {'lastname': 'Last'}) self.res_partner.write(cr, uid, partner_id, {'lastname': 'Last'})
self.assertEqual(p1.display_name, 'Last Firstname', 'Partner display_name incorrect')
self.assertEqual(
p1.display_name,
'Last Firstname',
'Partner display_name incorrect'
)
if __name__ == '__main__': if __name__ == '__main__':
unittest2.main() unittest2.main()

1
partner_firstname/__init__.py

@ -17,5 +17,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
from . import partner from . import partner
from . import res_user from . import res_user

6
partner_firstname/__openerp__.py

@ -24,8 +24,10 @@
This module splits first name and last name for non company partners This module splits first name and last name for non company partners
==================================================================== ====================================================================
The field 'name' becomes a stored function field concatenating lastname and firstname
Note: in version 7.0, installing this module invalidates a yaml test in the 'edi' module
The field 'name' becomes a stored function field concatenating lastname and
firstname
Note: in version 7.0, installing this module invalidates a yaml test in the
'edi' module
Contributors Contributors
============ ============

86
partner_firstname/partner.py

@ -27,13 +27,20 @@ class ResPartner(orm.Model):
_inherit = 'res.partner' _inherit = 'res.partner'
def init(self, cursor): def init(self, cursor):
cursor.execute('SELECT id FROM res_partner WHERE lastname IS NOT NULL Limit 1')
cursor.execute(
'SELECT id FROM res_partner WHERE lastname IS NOT NULL Limit 1'
)
if not cursor.fetchone(): if not cursor.fetchone():
cursor.execute('UPDATE res_partner set lastname = name WHERE name IS NOT NULL')
cursor.execute(
'UPDATE res_partner set lastname = name WHERE name IS NOT NULL'
)
# Create Sql constraint if table is not empty # Create Sql constraint if table is not empty
cursor.execute('SELECT id FROM res_partner Limit 1') cursor.execute('SELECT id FROM res_partner Limit 1')
if cursor.fetchone(): if cursor.fetchone():
cursor.execute('ALTER TABLE res_partner ALTER COLUMN lastname SET NOT NULL')
cursor.execute(
'ALTER TABLE res_partner '
'ALTER COLUMN lastname SET NOT NULL'
)
def _prepare_name_custom(self, cursor, uid, partner, context=None): def _prepare_name_custom(self, cursor, uid, partner, context=None):
""" """
@ -50,18 +57,30 @@ class ResPartner(orm.Model):
cursor, uid, partner, context=context) cursor, uid, partner, context=context)
return res return res
def _write_name(self, cursor, uid, partner_id, field_name, field_value, arg, context=None):
"""
Try to reverse the effect of _compute_name_custom:
* if the partner is not a company and the firstname does not change in the new name
then firstname remains untouched and lastname is updated accordingly
def _write_name(
self, cursor, uid, partner_id, field_name, field_value, arg,
context=None):
"""Try to reverse the effect of _compute_name_custom:
* if the partner is not a company and the firstname does not
change in the new name then firstname remains untouched and
lastname is updated accordingly
* otherwise lastname=new name and firstname=False * otherwise lastname=new name and firstname=False
In addition an heuristic avoids to keep a firstname without a non-blank lastname
In addition an heuristic avoids to keep a firstname without a
non-blank lastname
""" """
field_value = field_value and not field_value.isspace() and field_value or False
field_value = (field_value
and not field_value.isspace()
and field_value
or False)
vals = {'lastname': field_value, 'firstname': False} vals = {'lastname': field_value, 'firstname': False}
if field_value: if field_value:
flds = self.read(cursor, uid, [partner_id], ['firstname', 'is_company'], context=context)[0]
flds = self.read(
cursor,
uid,
[partner_id],
['firstname', 'is_company'],
context=context
)[0]
if not flds['is_company']: if not flds['is_company']:
to_check = ' %s' % flds['firstname'] to_check = ' %s' % flds['firstname']
if field_value.endswith(to_check): if field_value.endswith(to_check):
@ -77,24 +96,32 @@ class ResPartner(orm.Model):
return self.write(cursor, uid, partner_id, vals, context=context) return self.write(cursor, uid, partner_id, vals, context=context)
def copy_data(self, cr, uid, _id, default=None, context=None): def copy_data(self, cr, uid, _id, default=None, context=None):
"""
Avoid to replicate the firstname into the name when duplicating a partner
"""Avoid to replicate the firstname into the name when
duplicating a partner
""" """
default = default or {} default = default or {}
if not default.get('lastname'): if not default.get('lastname'):
default = default.copy() default = default.copy()
default['lastname'] = ( default['lastname'] = (
_('%s (copy)') % self.read(cr, uid, [_id], ['lastname'], context=context)[0]['lastname']
_('%s (copy)') % self.read(
cr,
uid,
[_id],
['lastname'],
context=context
)[0]['lastname']
) )
if default.get('name'): if default.get('name'):
del(default['name']) del(default['name'])
return super(ResPartner, self).copy_data(cr, uid, _id, default, context=context)
return super(ResPartner, self).copy_data(
cr, uid, _id, default, context=context
)
def create(self, cursor, uid, vals, context=None): def create(self, cursor, uid, vals, context=None):
"""
To support data backward compatibility we have to keep this overwrite even if we
use fnct_inv: otherwise we can't create entry because lastname is mandatory and module
will not install if there is demo data
"""To support data backward compatibility we have to keep this
overwrite even if we use fnct_inv: otherwise we can't create
entry because lastname is mandatory and module will not install
if there is demo data
""" """
to_use = vals to_use = vals
if 'name' in vals: if 'name' in vals:
@ -102,12 +129,21 @@ class ResPartner(orm.Model):
corr_vals['lastname'] = corr_vals['name'] corr_vals['lastname'] = corr_vals['name']
del(corr_vals['name']) del(corr_vals['name'])
to_use = corr_vals to_use = corr_vals
return super(ResPartner, self).create(cursor, uid, to_use, context=context)
return super(ResPartner, self).create(
cursor, uid, to_use, context=context
)
_columns = {'name': fields.function(_compute_name_custom, string="Name",
type="char", store=True,
select=True, readonly=True,
fnct_inv=_write_name),
_columns = {
'name': fields.function(
_compute_name_custom,
string="Name",
type="char",
store=True,
select=True,
readonly=True,
fnct_inv=_write_name
),
'firstname': fields.char("Firstname"), 'firstname': fields.char("Firstname"),
'lastname': fields.char("Lastname", required=True)}
'lastname': fields.char("Lastname", required=True),
}

16
partner_firstname/res_user.py

@ -26,15 +26,23 @@ class ResUsers(orm.Model):
_inherit = 'res.users' _inherit = 'res.users'
def copy_data(self, cr, uid, _id, default=None, context=None): def copy_data(self, cr, uid, _id, default=None, context=None):
"""
Avoid to replicate the firstname into the name when duplicating a user
"""Avoid to replicate the firstname into the name when
duplicating a user
""" """
default = default or {} default = default or {}
if not default.get('lastname'): if not default.get('lastname'):
default = default.copy() default = default.copy()
default['lastname'] = ( default['lastname'] = (
_('%s (copy)') % self.read(cr, uid, [_id], ['lastname'], context=context)[0]['lastname']
_('%s (copy)') % self.read(
cr,
uid,
[_id],
['lastname'],
context=context
)[0]['lastname']
) )
if default.get('name'): if default.get('name'):
del(default['name']) del(default['name'])
return super(ResUsers, self).copy_data(cr, uid, _id, default, context=context)
return super(ResUsers, self).copy_data(
cr, uid, _id, default, context=context
)

142
partner_firstname/tests/test_partner_firstname.py

@ -42,16 +42,29 @@ class test_partner_firstname(common.TransactionCase):
self.user_model = self.registry("res.users") self.user_model = self.registry("res.users")
self.partner_model = self.registry("res.partner") self.partner_model = self.registry("res.partner")
self.fields_partner = {'lastname': 'lastname', 'firstname': 'firstname'}
self.fields_user = {'name': 'lastname', 'login': 'v5Ue4Tql0Pm67KX05g25A'}
self.fields_partner = {
'lastname': 'lastname',
'firstname': 'firstname',
}
self.fields_user = {
'name': 'lastname',
'login': 'v5Ue4Tql0Pm67KX05g25A',
}
self.context = self.user_model.context_get(self.cr, self.uid) self.context = self.user_model.context_get(self.cr, self.uid)
def test_copy_partner(self): def test_copy_partner(self):
cr, uid, context = self.cr, self.uid, self.context cr, uid, context = self.cr, self.uid, self.context
res_id = self.partner_model.create(cr, uid, self.fields_partner, context=context)
res_id = self.partner_model.copy(cr, uid, res_id, default={}, context=context)
vals = self.partner_model.read(cr, uid, [res_id], ['name', 'lastname', 'firstname'], context=context)[0]
res_id = self.partner_model.create(
cr, uid, self.fields_partner, context=context
)
res_id = self.partner_model.copy(
cr, uid, res_id, default={}, context=context
)
vals = self.partner_model.read(
cr, uid, [res_id],
['name', 'lastname', 'firstname'], context=context
)[0]
self.assertEqual( self.assertEqual(
vals['name'], vals['name'],
@ -63,51 +76,122 @@ class test_partner_firstname(common.TransactionCase):
_('%s (copy)') % 'lastname', _('%s (copy)') % 'lastname',
'Copy of the partner failed with wrong lastname' 'Copy of the partner failed with wrong lastname'
) )
self.assertEqual(vals['firstname'], 'firstname', 'Copy of the partner failed with wrong firstname')
self.assertEqual(
vals['firstname'],
'firstname',
'Copy of the partner failed with wrong firstname'
)
def test_copy_user(self): def test_copy_user(self):
cr, uid, context = self.cr, self.uid, self.context cr, uid, context = self.cr, self.uid, self.context
# create a user # create a user
res_id = self.user_model.create(cr, uid, self.fields_user, context=context)
res_id = self.user_model.create(
cr, uid, self.fields_user, context=context
)
# get the related partner id and add it a firstname # get the related partner id and add it a firstname
flds = self.user_model.read(cr, uid, [res_id], ['partner_id'], context=context)[0]
self.partner_model.write(cr, uid, flds['partner_id'][0], {'firstname': 'firstname'}, context=context)
flds = self.user_model.read(
cr, uid, [res_id], ['partner_id'], context=context
)[0]
self.partner_model.write(
cr, uid, flds['partner_id'][0], {'firstname': 'firstname'},
context=context
)
# copy the user and compare result # copy the user and compare result
res_id = self.user_model.copy(cr, uid, res_id, default={}, context=context)
vals = self.user_model.read(cr, uid, [res_id], ['name', 'lastname', 'firstname'], context=context)[0]
res_id = self.user_model.copy(
cr, uid, res_id, default={}, context=context
)
vals = self.user_model.read(
cr, uid, [res_id], ['name', 'lastname', 'firstname'],
context=context
)[0]
self.assertEqual( self.assertEqual(
vals['name'], vals['name'],
_('%s (copy)') % 'lastname' + ' firstname', _('%s (copy)') % 'lastname' + ' firstname',
'Copy of the user failed with wrong name' 'Copy of the user failed with wrong name'
) )
self.assertEqual(vals['lastname'], _('%s (copy)') % 'lastname', 'Copy of the user failed with wrong lastname')
self.assertEqual(vals['firstname'], 'firstname', 'Copy of the user failed with wrong firstname')
self.assertEqual(
vals['lastname'],
_('%s (copy)') % 'lastname',
'Copy of the user failed with wrong lastname'
)
self.assertEqual(
vals['firstname'],
'firstname',
'Copy of the user failed with wrong firstname'
)
def test_update_user_lastname(self): def test_update_user_lastname(self):
cr, uid, context = self.cr, self.uid, self.context cr, uid, context = self.cr, self.uid, self.context
# create a user # create a user
res_id = self.user_model.create(cr, uid, self.fields_user, context=context)
res_id = self.user_model.create(
cr, uid, self.fields_user, context=context
)
# get the related partner id and add it a firstname # get the related partner id and add it a firstname
flds = self.user_model.read(cr, uid, [res_id], ['partner_id'], context=context)[0]
self.partner_model.write(cr, uid, flds['partner_id'][0], {'firstname': 'firstname'}, context=context)
self.user_model.write(cr, uid, res_id, {'name': 'change firstname'}, context=context)
vals = self.user_model.read(cr, uid, [res_id], ['name', 'lastname', 'firstname'], context=context)[0]
flds = self.user_model.read(
cr, uid, [res_id], ['partner_id'], context=context
)[0]
self.partner_model.write(
cr, uid, flds['partner_id'][0], {'firstname': 'firstname'},
context=context
)
self.user_model.write(
cr, uid, res_id, {'name': 'change firstname'}, context=context
)
vals = self.user_model.read(
cr, uid, [res_id], ['name', 'lastname', 'firstname'],
context=context
)[0]
self.assertEqual(vals['name'], 'change firstname', 'Update of the user lastname failed with wrong name')
self.assertEqual(vals['lastname'], 'change', 'Update of the user lastname failed with wrong lastname')
self.assertEqual(vals['firstname'], 'firstname', 'Update of the user lastname failed with wrong firstname')
self.assertEqual(
vals['name'],
'change firstname',
'Update of the user lastname failed with wrong name'
)
self.assertEqual(
vals['lastname'],
'change',
'Update of the user lastname failed with wrong lastname'
)
self.assertEqual(
vals['firstname'],
'firstname',
'Update of the user lastname failed with wrong firstname'
)
def test_update_user_firstname(self): def test_update_user_firstname(self):
cr, uid, context = self.cr, self.uid, self.context cr, uid, context = self.cr, self.uid, self.context
# create a user # create a user
res_id = self.user_model.create(cr, uid, self.fields_user, context=context)
res_id = self.user_model.create(
cr, uid, self.fields_user, context=context
)
# get the related partner id and add it a firstname # get the related partner id and add it a firstname
flds = self.user_model.read(cr, uid, [res_id], ['partner_id'], context=context)[0]
self.partner_model.write(cr, uid, flds['partner_id'][0], {'firstname': 'firstname'}, context=context)
self.user_model.write(cr, uid, res_id, {'name': 'lastname other'}, context=context)
vals = self.user_model.read(cr, uid, [res_id], ['name', 'lastname', 'firstname'], context=context)[0]
flds = self.user_model.read(
cr, uid, [res_id], ['partner_id'], context=context
)[0]
self.partner_model.write(
cr, uid, flds['partner_id'][0], {'firstname': 'firstname'},
context=context
)
self.user_model.write(
cr, uid, res_id, {'name': 'lastname other'}, context=context
)
vals = self.user_model.read(
cr, uid, [res_id], ['name', 'lastname', 'firstname'],
context=context
)[0]
self.assertEqual(vals['name'], 'lastname other', 'Update of the user firstname failed with wrong name')
self.assertEqual(vals['lastname'], 'lastname other', 'Update of the user firstname failed with wrong lastname')
self.assertFalse(vals['firstname'], 'Update of the user firstname failed with wrong firstname')
self.assertEqual(
vals['name'],
'lastname other',
'Update of the user firstname failed with wrong name'
)
self.assertEqual(
vals['lastname'],
'lastname other',
'Update of the user firstname failed with wrong lastname'
)
self.assertFalse(
vals['firstname'],
'Update of the user firstname failed with wrong firstname'
)

41
passport/tests/test_passport.py

@ -26,8 +26,7 @@ from datetime import date
class Base_Test_passport(TransactionCase): class Base_Test_passport(TransactionCase):
"""
Simple test creating a passport
"""Simple test creating a passport
This is a base class for passport test cases. This is a base class for passport test cases.
Inherit from this and setup values. Inherit from this and setup values.
""" """
@ -49,14 +48,18 @@ class Base_Test_passport(TransactionCase):
self.vals = dict(self.vals.items() + vals.items()) self.vals = dict(self.vals.items() + vals.items())
# Create the passport object; we will be testing this, so store in self # Create the passport object; we will be testing this, so store in self
res_passport = self.registry('res.passport') res_passport = self.registry('res.passport')
self.passport_id = res_passport.create(self.cr, self.uid, self.vals, context=None)
self.passport_id = res_passport.create(
self.cr, self.uid, self.vals, context=None
)
def test_passport(self): def test_passport(self):
""" """
Checking the passport creation. Checking the passport creation.
""" """
res_passport = self.registry('res.passport') res_passport = self.registry('res.passport')
passport_obj = res_passport.browse(self.cr, self.uid, self.passport_id, context=None)
passport_obj = res_passport.browse(
self.cr, self.uid, self.passport_id, context=None
)
for field in self.vals: for field in self.vals:
val = passport_obj[field] val = passport_obj[field]
if type(val) == browse_record: if type(val) == browse_record:
@ -70,9 +73,8 @@ class Base_Test_passport(TransactionCase):
class Test_passport_bad(Base_Test_passport): class Test_passport_bad(Base_Test_passport):
"""
Simple test creating a passport, test against bad values
"""
"""Simple test creating a passport, test against bad values"""
def setUp(self): def setUp(self):
""" """
Setting up passport, then changing the values to test against. Setting up passport, then changing the values to test against.
@ -93,7 +95,9 @@ class Test_passport_bad(Base_Test_passport):
Checking the passport creation, assertions should all be false. Checking the passport creation, assertions should all be false.
""" """
res_passport = self.registry('res.passport') res_passport = self.registry('res.passport')
passport_obj = res_passport.browse(self.cr, self.uid, self.passport_id, context=None)
passport_obj = res_passport.browse(
self.cr, self.uid, self.passport_id, context=None
)
for field in self.vals: for field in self.vals:
val = passport_obj[field] val = passport_obj[field]
if type(val) == browse_record: if type(val) == browse_record:
@ -107,9 +111,8 @@ class Test_passport_bad(Base_Test_passport):
class Test_passport_name_get(TransactionCase): class Test_passport_name_get(TransactionCase):
"""
Test name_get
"""
"""Test name_get"""
def setUp(self): def setUp(self):
""" """
Setting up passport with name, country, either and none. Setting up passport with name, country, either and none.
@ -139,10 +142,18 @@ class Test_passport_name_get(TransactionCase):
Checking the passport creation, assertions should all be false. Checking the passport creation, assertions should all be false.
""" """
res_passport = self.registry('res.passport') res_passport = self.registry('res.passport')
both_obj = res_passport.browse(self.cr, self.uid, self.both, context=None)
name_only = res_passport.browse(self.cr, self.uid, self.name_only, context=None)
country_only = res_passport.browse(self.cr, self.uid, self.country_only, context=None)
neither = res_passport.browse(self.cr, self.uid, self.neither, context=None)
both_obj = res_passport.browse(
self.cr, self.uid, self.both, context=None
)
name_only = res_passport.browse(
self.cr, self.uid, self.name_only, context=None
)
country_only = res_passport.browse(
self.cr, self.uid, self.country_only, context=None
)
neither = res_passport.browse(
self.cr, self.uid, self.neither, context=None
)
self.assertEquals( self.assertEquals(
both_obj.name_get()[0][1], both_obj.name_get()[0][1],
' | '.join((self.country_name, self.name_on_passport)), ' | '.join((self.country_name, self.name_on_passport)),

1
portal_partner_merge/__init__.py

@ -18,4 +18,5 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
from . import wizard from . import wizard

4
portal_partner_merge/wizard/__init__.py

@ -19,8 +19,4 @@
# #
############################################################################## ##############################################################################
from . import portal_wizard from . import portal_wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

2
res_partner_affiliate/__init__.py

@ -20,5 +20,3 @@
############################################################################## ##############################################################################
import res_partner import res_partner
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
Loading…
Cancel
Save