Browse Source

FIX pylint

(cherry picked from commit eadcae2171)

Conflicts:
	partner_identification/__openerp__.py
	Set version to 8.0.1.0.0.
	Remove dependency on sales_team, as the relevant change is not
in 8.0.

Change emails to the new ones
(cherry picked from commit 3455ae614e)

Update new name
(cherry picked from commit 5b592d7562)

[FIX] try me on runbot link

(cherry picked from commit bd587b6058)

Conflicts:
	partner_identification/README.rst
	Changed runbot to 8.0

[IMP] partner_identification: Add context override (#373)

Allow for context override of validations using ``id_no_validate``
(cherry picked from commit 76c2e7b784)

[10.0][IMP] partner_identification: Add field computation and inverses (#419)

* [IMP] partner_identification: Add field computation and inverses
* Add methods to allow for computation and inverse of an ID field of a specific category type

* [IMP] partner_identification: Add search option

(cherry picked from commit 19c5fb6de2)

[FIX] partner_identification: Infinite loop in search (#436)

(cherry picked from commit fa9b390dc6)

[FIX] partner-contact CI interactions

(cherry picked from commit bc93e7bbc3)

[ADD][8.0] Backport of the 9.0 module.

(cherry picked from commit a42540381d)

[8.0][MIG] partner_identification backport
pull/612/head
Juan Jose Scarafia 8 years ago
committed by Kevin Graveman
parent
commit
3e7004b836
  1. 17
      partner_identification/README.rst
  2. 7
      partner_identification/__openerp__.py
  3. 161
      partner_identification/models/res_partner.py
  4. 2
      partner_identification/models/res_partner_id_category.py
  5. 2
      partner_identification/tests/__init__.py
  6. 22
      partner_identification/tests/test_partner_identification.py
  7. 131
      partner_identification/tests/test_res_partner.py

17
partner_identification/README.rst

@ -15,7 +15,7 @@ and vary from country to country.
* Fiscal ID's
* Membership numbers
* Driver license
* ...
* etc
Installation
@ -35,8 +35,15 @@ Name:
Code:
Code, abbreviation or acronym of this ID type. For example, 'driver_license'
Python validation code:
Optional python code called to validate ID numbers of this ID type.
Optional python code called to validate ID numbers of this ID type. This functionality can be
overridden by setting ``id_no_validate`` to ``True`` in the context, such as:
.. code-block:: python
partner.with_context(id_no_validate=True).write({
'name': 'Bad Value',
'category_id': self.env.ref('id_category_only_numerics').id,
})
Usage
=====
@ -63,7 +70,7 @@ Notes:
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/
:target: https://runbot.odoo-community.org/runbot/134/8.0
Known issues / Roadmap
@ -97,7 +104,9 @@ Contributors
* Ferdinand Gassauer <office@chrcar.at>
* Gerhard Könighofer <gerhard.koenighofer@swing-system.com>
* Laurent Mignon <laurent.mignon@acsone.eu>
* Yajo <Yajo@users.noreply.github.com>
* Jairo Llopis <jairo.llopis@tecnativa.com>
* Dave Lasley <dave@laslabs.com>
* Kevin Graveman <k.graveman@onestein.nl>
Maintainer
----------

7
partner_identification/__openerp__.py

@ -11,7 +11,7 @@
{
'name': 'Partner Identification Numbers',
'category': 'Customer Relationship Management',
'version': '8.0.1.0.0',
'version': '8.0.1.1.1',
'data': [
'views/res_partner_id_category_view.xml',
'views/res_partner_id_number_view.xml',
@ -19,10 +19,11 @@
'security/ir.model.access.csv',
],
'author': 'ChriCar Beteiligungs- und Beratungs- GmbH, '
'Antiun Ingeniería S.L.',
'Tecnativa,'
'Camptocamp,'
'ACSONE SA/NV,'
'Odoo Community Association (OCA)'
'LasLabs,'
'Odoo Community Association (OCA)',
'website': 'https://odoo-community.org/',
'license': 'AGPL-3',
'installable': True,

161
partner_identification/models/res_partner.py

@ -8,12 +8,165 @@
# Antonio Espinosa <antonioea@antiun.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields
from openerp import api, models, fields, _
from openerp.exceptions import ValidationError
class ResPartner(models.Model):
_inherit = 'res.partner'
_inherit = 'res.partner' # pylint: disable=R7980
id_numbers = fields.One2many(
comodel_name='res.partner.id_number', inverse_name='partner_id',
string="Identification Numbers")
comodel_name='res.partner.id_number',
inverse_name='partner_id',
string="Identification Numbers",
)
@api.multi
@api.depends('id_numbers')
def _compute_identification(self, field_name, category_code):
""" Compute a field that indicates a certain ID type.
Use this on a field that represents a certain ID type. It will compute
the desired field as that ID(s).
This ID can be worked with as if it were a Char field, but it will
be relating back to a ``res.partner.id_number`` instead.
Example:
.. code-block:: python
social_security = fields.Char(
compute=lambda s: s._compute_identification(
'social_security', 'SSN',
),
inverse=lambda s: s._inverse_identification(
'social_security', 'SSN',
),
search=lambda s, *a: s._search_identification(
'SSN', *a
),
)
Args:
field_name (str): Name of field to set.
category_code (str): Category code of the Identification type.
"""
for record in self:
id_numbers = record.id_numbers.filtered(
lambda r: r.category_id.code == category_code
)
if not id_numbers:
continue
value = id_numbers[0].name
record[field_name] = value
@api.multi
def _inverse_identification(self, field_name, category_code):
""" Inverse for an identification field.
This method will create a new record, or modify the existing one
in order to allow for the associated field to work like a Char.
If a category does not exist of the correct code, it will be created
using `category_code` as both the `name` and `code` values.
If the value of the target field is unset, the associated ID will
be deactivated in order to preserve history.
Example:
.. code-block:: python
social_security = fields.Char(
compute=lambda s: s._compute_identification(
'social_security', 'SSN',
),
inverse=lambda s: s._inverse_identification(
'social_security', 'SSN',
),
search=lambda s, *a: s._search_identification(
'SSN', *a
),
)
Args:
field_name (str): Name of field to set.
category_code (str): Category code of the Identification type.
"""
for record in self:
id_number = record.id_numbers.filtered(
lambda r: r.category_id.code == category_code
)
record_len = len(id_number)
# Record for category is not existent.
if record_len == 0:
name = record[field_name]
if not name:
# No value to set
continue
category = self.env['res.partner.id_category'].search([
('code', '=', category_code),
])
if not category:
category = self.env['res.partner.id_category'].create({
'code': category_code,
'name': category_code,
})
self.env['res.partner.id_number'].create({
'partner_id': record.id,
'category_id': category.id,
'name': name,
})
# There was an identification record singleton found.
elif record_len == 1:
value = record[field_name]
if value:
id_number.name = value
else:
id_number.active = False
# Guard against writing wrong records.
else:
raise ValidationError(_(
'This %s has multiple IDs of this type (%s), so a write '
'via the %s field is not possible. In order to fix this, '
'please use the IDs tab.',
) % (
record._name, category_code, field_name,
))
@api.model
def _search_identification(self, category_code, operator, value):
""" Search method for an identification field.
Example:
.. code-block:: python
social_security = fields.Char(
compute=lambda s: s._compute_identification(
'social_security', 'SSN',
),
inverse=lambda s: s._inverse_identification(
'social_security', 'SSN',
),
search=lambda s, *a: s._search_identification(
'SSN', *a
),
)
Args:
category_code (str): Category code of the Identification type.
operator (str): Operator of domain.
value (str): Value to search for.
Returns:
list: Domain to search with.
"""
id_numbers = self.env['res.partner.id_number'].search([
('name', operator, value),
('category_id.code', '=', category_code),
])
return [
('id_numbers.id', 'in', id_numbers.ids),
]

2
partner_identification/models/res_partner_id_category.py

@ -55,6 +55,8 @@ class ResPartnerIdCategory(models.Model):
python validation code fails
"""
self.ensure_one()
if self.env.context.get('id_no_validate'):
return
eval_context = self._validation_eval_context(id_number)
try:
safe_eval(self.validation_code,

2
partner_identification/tests/__init__.py

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import test_partner_identification
from . import test_res_partner

22
partner_identification/tests/test_partner_identification.py

@ -2,7 +2,7 @@
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from psycopg2._psycopg import IntegrityError
import openerp.tests.common as common
from openerp.tests import common
from openerp.exceptions import ValidationError
@ -75,7 +75,7 @@ if id_number.name != '1235':
'category_id': partner_id_category2.id
})
def test_bad_calidation_code(self):
def test_bad_validation_code(self):
partner_id_category = self.env['res.partner.id_category'].create({
'code': 'id_code',
'name': 'id_name',
@ -90,3 +90,21 @@ if id_number.name != '1234' # missing :
'name': '1234',
'category_id': partner_id_category.id
})]})
def test_bad_validation_code_override(self):
""" It should allow a bad validation code if context overrides. """
partner_id_category = self.env['res.partner.id_category'].create({
'code': 'id_code',
'name': 'id_name',
'validation_code': """
if id_number.name != '1234' # missing :
failed = True
"""
})
partner_1 = self.env.ref('base.res_partner_1').with_context(
id_no_validate=True,
)
partner_1.write({'id_numbers': [(0, 0, {
'name': '1234',
'category_id': partner_id_category.id
})]})

131
partner_identification/tests/test_res_partner.py

@ -0,0 +1,131 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import fields, models
from openerp.tests import common
from openerp.exceptions import ValidationError
class ResPartner(models.Model):
_inherit = 'res.partner'
social_security = fields.Char(
compute=lambda s: s._compute_identification(
'social_security', 'SSN',
),
inverse=lambda s: s._inverse_identification(
'social_security', 'SSN',
),
search=lambda s, *a: s._search_identification(
'SSN', *a
),
)
class TestResPartner(common.SavepointCase):
@classmethod
def _init_test_model(cls, model_cls):
""" Build a model from model_cls in order to test abstract models.
Note that this does not actually create a table in the database, so
there may be some unidentified edge cases.
Args:
model_cls (openerp.models.BaseModel): Class of model to initialize
Returns:
model_cls: Instance
"""
registry = cls.env.registry
cr = cls.env.cr
inst = model_cls._build_model(registry, cr)
model = cls.env[model_cls._inherit].with_context(todo=[])
model._prepare_setup()
model._setup_base(partial=False)
model._setup_fields()
model._setup_complete()
model._auto_init()
# model.init()
model._auto_end()
return inst
@classmethod
def setUpClass(cls):
super(TestResPartner, cls).setUpClass()
cls.env.registry.enter_test_mode()
cls._init_test_model(ResPartner)
@classmethod
def tearDownClass(cls):
cls.env.registry.leave_test_mode()
super(TestResPartner, cls).tearDownClass()
def setUp(self):
super(TestResPartner, self).setUp()
bad_cat = self.env['res.partner.id_category'].create({
'code': 'another_code',
'name': 'another_name',
})
self.env['res.partner.id_number'].create({
'name': 'Bad ID',
'category_id': bad_cat.id,
'partner_id': self.env.user.partner_id.id,
})
self.partner_id_category = self.env['res.partner.id_category'].create({
'code': 'id_code',
'name': 'id_name',
})
self.partner = self.env.user.partner_id
self.partner_id = self.env['res.partner.id_number'].create({
'name': 'Good ID',
'category_id': self.partner_id_category.id,
'partner_id': self.partner.id,
})
def test_compute_identification(self):
""" It should set the proper field to the proper ID name. """
self.partner._compute_identification('name', 'id_code')
self.assertEqual(self.partner.name, self.partner_id.name)
def test_inverse_identification_saves(self):
""" It should set the ID name to the proper field value. """
self.partner._inverse_identification('name', 'id_code')
self.assertEqual(self.partner_id.name, self.partner.name)
def test_inverse_identification_creates_new_category(self):
""" It should create a new category of the type if non-existent. """
self.partner._inverse_identification('name', 'new_code_type')
category = self.env['res.partner.id_category'].search([
('code', '=', 'new_code_type'),
])
self.assertTrue(category)
def test_inverse_identification_creates_new_id(self):
""" It should create a new ID of the type if non-existent. """
category = self.env['res.partner.id_category'].create({
'code': 'new_code_type',
'name': 'new_code_type',
})
self.partner._inverse_identification('name', 'new_code_type')
identification = self.env['res.partner.id_number'].search([
('category_id', '=', category.id),
('partner_id', '=', self.partner.id),
])
self.assertEqual(identification.name, self.partner.name)
def test_inverse_identification_multi_exception(self):
""" It should not allow a write when multiple IDs of same type. """
self.env['res.partner.id_number'].create({
'name': 'Another ID',
'category_id': self.partner_id_category.id,
'partner_id': self.partner.id,
})
with self.assertRaises(ValidationError):
self.partner._inverse_identification('name', 'id_code')
def test_search_identification(self):
""" It should return the right record when searched by ID. """
self.partner.social_security = 'Test'
partner = self.env['res.partner'].search([
('social_security', '=', 'Test'),
])
self.assertEqual(partner, self.partner)
Loading…
Cancel
Save