Files
tradon/modules/party/tests/test_module.py
2026-03-14 09:42:12 +00:00

804 lines
26 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import unittest
from unittest.mock import patch
from stdnum import get_cc_module
try:
import phonenumbers
except ImportError:
phonenumbers = None
from trytond.exceptions import UserError
from trytond.model.exceptions import AccessError
from trytond.modules.party.party import (
IDENTIFIER_TYPES, IDENTIFIER_VAT, replace_vat)
from trytond.pool import Pool
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.tools import is_instance_method
from trytond.transaction import Transaction
class PartyCheckEraseMixin:
@with_transaction()
def test_check_erase_party(self):
"Test check erase of party"
pool = Pool()
Erase = pool.get('party.erase', type='wizard')
Session = pool.get('ir.session.wizard')
party = self.setup_check_erase_party()
session = Session()
session.save()
Erase(session.id).check_erase(party)
def setup_check_erase_party(self):
pool = Pool()
Party = pool.get('party.party')
party = Party(active=False)
party.save()
return party
class PartyCheckReplaceMixin:
@with_transaction()
def test_check_replace_party(self):
"Test fields to replace"
pool = Pool()
Replace = pool.get('party.replace', type='wizard')
for model_name, field_name in Replace.fields_to_replace():
with self.subTest(model_name=model_name, field_name=field_name):
Model = pool.get(model_name)
field = getattr(Model, field_name)
if field._type == 'reference':
if isinstance(field.selection, (tuple, list)):
self.assertIn('party.party', dict(field.selection))
else:
sel_func = getattr(Model, field.selection)
instance_sel_func = is_instance_method(
Model, field.selection)
if not instance_sel_func:
self.assertIn('party.party', dict(sel_func()))
else:
self.assertEqual(field._type, 'many2one')
self.assertEqual(field.model_name, 'party.party')
class PartyTestCase(
PartyCheckEraseMixin, PartyCheckReplaceMixin, ModuleTestCase):
'Test Party module'
module = 'party'
@with_transaction()
def test_category(self):
'Create category'
pool = Pool()
Category = pool.get('party.category')
category1, = Category.create([{
'name': 'Category 1',
}])
self.assertTrue(category1.id)
@with_transaction()
def test_category_recursion(self):
'Test category recursion'
pool = Pool()
Category = pool.get('party.category')
category1, = Category.create([{
'name': 'Category 1',
}])
category2, = Category.create([{
'name': 'Category 2',
'parent': category1.id,
}])
self.assertTrue(category2.id)
self.assertRaises(Exception, Category.write, [category1], {
'parent': category2.id,
})
@with_transaction()
def test_party(self):
'Create party'
pool = Pool()
Party = pool.get('party.party')
party1, = Party.create([{
'name': 'Party 1',
}])
self.assertTrue(party1.id)
@with_transaction()
def test_party_code(self):
'Test party code constraint'
pool = Pool()
Party = pool.get('party.party')
party1, = Party.create([{
'name': 'Party 1',
}])
code = party1.code
self.assertTrue(party1.code_alnum.isalnum())
self.assertTrue(party1.code_digit)
party2, = Party.create([{
'name': 'Party 2',
}])
self.assertRaises(Exception, Party.write, [party2], {
'code': code,
})
@with_transaction()
def test_party_autocomplete_eu_vat(self):
"Test party autocomplete eu_vat"
pool = Pool()
Party = pool.get('party.party')
self.assertEqual(
Party.autocomplete('BE500923836'), [{
'id': None,
'name': 'BE0500923836',
'defaults': {
'identifiers': [{
'type': 'eu_vat',
'code': 'BE0500923836',
}],
},
}])
@with_transaction()
def test_party_autocomplete_eu_vat_without_country(self):
"Test party autocomplete eu_vat without country"
pool = Pool()
Party = pool.get('party.party')
self.assertIn({
'id': None,
'name': 'BE0500923836',
'defaults': {
'identifiers': [{
'type': 'eu_vat',
'code': 'BE0500923836',
}],
},
},
Party.autocomplete('500923836'))
@with_transaction()
def test_party_autocomplete_be_vat(self):
"Test party autocomplete be_vat"
pool = Pool()
Party = pool.get('party.party')
Configuration = pool.get('party.configuration')
configuration = Configuration(1)
configuration.identifier_types = ['be_vat']
configuration.save()
self.assertEqual(
Party.autocomplete('BE500923836'), [{
'id': None,
'name': '0500923836',
'defaults': {
'identifiers': [{
'type': 'be_vat',
'code': '0500923836',
}],
},
}])
@with_transaction()
def test_party_default_get_eu_vat(self):
"Test party default_get eu_vat"
pool = Pool()
Party = pool.get('party.party')
Country = pool.get('country.country')
belgium = Country(code='BE', name="Belgium")
belgium.save()
eu_vat = get_cc_module('eu', 'vat')
with patch.object(eu_vat, 'check_vies') as check_vies:
check_vies.return_value = {
'valid': True,
'name': "Tryton Foundation",
'address': "Street",
}
with Transaction().set_context(default_identifiers=[{
'type': 'eu_vat',
'code': 'BE0500923836',
}]):
self.assertEqual(
Party.default_get(['name', 'addresses', 'identifiers']), {
'name': "Tryton Foundation",
'addresses': [{
'street': "Street",
'country': belgium.id,
'country.': {
'rec_name': "🇧🇪 Belgium",
},
}],
'identifiers': [{
'type': 'eu_vat',
'code': 'BE0500923836',
}],
})
@with_transaction()
def test_address_strip(self):
"Test address strip"
pool = Pool()
Address = pool.get('party.address')
for value, result in [
('', ''),
(' ', ''),
('\n', ''),
(' \n \n', ''),
('foo\n\n', 'foo'),
(',foo', 'foo'),
(',,foo', 'foo'),
(', , foo', 'foo'),
('foo, , bar', 'foo, bar'),
('foo,,', 'foo'),
('foo, , ', 'foo'),
(',/foo,/', 'foo'),
('foo, /bar', 'foo/bar'),
('foo, /, bar', 'foo, bar'),
('foo,/bar\nfoo, \n/bar, foo', 'foo/bar\nfoo\nbar, foo'),
]:
with self.subTest(value=value):
self.assertEqual(Address._strip(value), result)
@with_transaction()
def test_address(self):
'Create address'
pool = Pool()
Party = pool.get('party.party')
Address = pool.get('party.address')
party1, = Party.create([{
'name': 'Party 1',
}])
address, = Address.create([{
'party': party1.id,
'street': 'St sample, 15',
'city': 'City',
}])
self.assertTrue(address.id)
self.assertMultiLineEqual(address.full_address,
"St sample, 15\n"
"City")
with Transaction().set_context(address_with_party=True):
address = Address(address.id)
self.assertMultiLineEqual(address.full_address,
"Party 1\n"
"St sample, 15\n"
"City")
@with_transaction()
def test_address_structured(self):
"Test address structured"
pool = Pool()
Address = pool.get('party.address')
Country = pool.get('country.country')
us = Country(code='US', name="US")
us.save()
be = Country(code='BE', name="BE")
be.save()
address = Address(
street_name="St sample",
building_number="15",
unit_number="B",
floor_number=1,
room_number=3,
)
for country, result in [
(None, "St sample 15/B/1/3"),
(us, "15 St sample B"),
(be, "St sample 15 box B"),
]:
with self.subTest(country=country.code if country else None):
address.country = country
self.assertEqual(address.street, result)
@with_transaction()
def test_address_numbers(self):
"Test address numbers"
pool = Pool()
Party = pool.get('party.party')
Address = pool.get('party.address')
party = Party(name="Dunder Mifflin")
party.save()
address = Address(
party=party,
street_name="St sample",
building_number="15",
unit_number="B",
floor_number=1,
room_number=3,
)
address.save()
self.assertEqual(address.numbers, "15/B/1/3")
@with_transaction()
def test_address_autocomplete_postal_code(self):
"Test autocomplete of postal code"
pool = Pool()
Country = pool.get('country.country')
Subdivision = pool.get('country.subdivision')
PostalCode = pool.get('country.postal_code')
Address = pool.get('party.address')
country = Country(name="Country")
country.save()
subdivision = Subdivision(name="Subdivision", country=country)
subdivision.save()
postal_code = PostalCode(
country=country,
subdivision=subdivision,
postal_code="12345",
city="City",
)
postal_code.save()
address = Address(
country=country, subdivision=subdivision, city="C")
completions = address.autocomplete_postal_code()
self.assertEqual(completions, ["12345"])
@with_transaction()
def test_address_autocomplete_postal_code_empty(self):
"Test autocomplete with empty postal code"
pool = Pool()
Country = pool.get('country.country')
PostalCode = pool.get('country.postal_code')
Address = pool.get('party.address')
country = Country(name="Country")
country.save()
for postal_code in [None, '12345']:
PostalCode(
country=country,
postal_code=postal_code,
city="City",
).save()
completions = Address(city="City").autocomplete_postal_code()
self.assertEqual(completions, ["12345"])
@with_transaction()
def test_address_autocomplete_city(self):
"Test autocomplete of city"
pool = Pool()
Country = pool.get('country.country')
Subdivision = pool.get('country.subdivision')
PostalCode = pool.get('country.postal_code')
Address = pool.get('party.address')
country = Country(name="Country")
country.save()
subdivision = Subdivision(name="Subdivision", country=country)
subdivision.save()
postal_code = PostalCode(
country=country,
subdivision=subdivision,
postal_code="12345",
city="City",
)
postal_code.save()
address = Address(
country=country, subdivision=subdivision, postal_code="123")
completions = address.autocomplete_city()
self.assertEqual(completions, ["City"])
@with_transaction()
def test_address_autocomplete_city_empty(self):
"Test autocomplete with empty city"
pool = Pool()
Country = pool.get('country.country')
PostalCode = pool.get('country.postal_code')
Address = pool.get('party.address')
country = Country(name="Country")
country.save()
for city in [None, "City"]:
PostalCode(
country=country,
postal_code="12345",
city=city,
).save()
completions = Address(postal_code="12345").autocomplete_city()
self.assertEqual(completions, ["City"])
@with_transaction()
def test_full_address_country_subdivision(self):
'Test full address with country and subdivision'
pool = Pool()
Party = pool.get('party.party')
Country = pool.get('country.country')
Subdivision = pool.get('country.subdivision')
Address = pool.get('party.address')
party, = Party.create([{
'name': 'Party',
}])
country = Country(name='Country')
country.save()
subdivision = Subdivision(
name='Subdivision', country=country, code='SUB', type='area')
subdivision.save()
address, = Address.create([{
'party': party.id,
'subdivision': subdivision.id,
'country': country.id,
}])
self.assertMultiLineEqual(address.full_address,
"Subdivision\n"
"COUNTRY")
@with_transaction()
def test_address_get_no_type(self):
"Test address_get with no type"
pool = Pool()
Party = pool.get('party.party')
Address = pool.get('party.address')
party, = Party.create([{}])
address1, address2 = Address.create([{
'party': party.id,
'sequence': 1,
}, {
'party': party.id,
'sequence': 2,
}])
address = party.address_get()
self.assertEqual(address, address1)
@with_transaction()
def test_address_get_no_address(self):
"Test address_get with no address"
pool = Pool()
Party = pool.get('party.party')
party, = Party.create([{}])
address = party.address_get()
self.assertEqual(address, None)
@with_transaction()
def test_address_get_inactive(self):
"Test address_get with inactive"
pool = Pool()
Party = pool.get('party.party')
Address = pool.get('party.address')
party, = Party.create([{}])
address1, address2 = Address.create([{
'party': party.id,
'sequence': 1,
'active': False,
}, {
'party': party.id,
'sequence': 2,
'active': True,
}])
address = party.address_get()
self.assertEqual(address, address2)
@with_transaction()
def test_address_get_type(self):
"Test address_get with type"
pool = Pool()
Party = pool.get('party.party')
Address = pool.get('party.address')
party, = Party.create([{}])
address1, address2 = Address.create([{
'party': party.id,
'sequence': 1,
'postal_code': None,
}, {
'party': party.id,
'sequence': 2,
'postal_code': '1000',
}])
address = party.address_get(type='postal_code')
self.assertEqual(address, address2)
@with_transaction()
def test_party_label_report(self):
'Test party label report'
pool = Pool()
Party = pool.get('party.party')
Label = pool.get('party.label', type='report')
party1, = Party.create([{
'name': 'Party 1',
}])
oext, content, _, _ = Label.execute([party1.id], {})
self.assertEqual(oext, 'odt')
self.assertTrue(content)
@with_transaction()
def test_party_without_name(self):
'Create party without name'
pool = Pool()
Party = pool.get('party.party')
party2, = Party.create([{}])
self.assertTrue(party2.id)
code = party2.code
self.assertEqual(party2.rec_name, '[' + code + ']')
@unittest.skipIf(phonenumbers is None, 'requires phonenumbers')
@with_transaction()
def test_phone_number_format(self):
'Test phone number format'
pool = Pool()
Party = pool.get('party.party')
ContactMechanism = pool.get('party.contact_mechanism')
transaction = Transaction()
def create(mtype, mvalue):
party1, = Party.create([{
'name': 'Party 1',
}])
return ContactMechanism.create([{
'party': party1.id,
'type': mtype,
'value': mvalue,
}])[0]
# Test format on create
mechanism = create('phone', '+442083661177')
self.assertEqual(mechanism.value, '+44 20 8366 1177')
self.assertEqual(mechanism.value_compact, '+442083661177')
# Test format on write
mechanism.value = '+442083661178'
mechanism.save()
self.assertEqual(mechanism.value, '+44 20 8366 1178')
self.assertEqual(mechanism.value_compact, '+442083661178')
ContactMechanism.write([mechanism], {
'value': '+442083661179',
})
self.assertEqual(mechanism.value, '+44 20 8366 1179')
self.assertEqual(mechanism.value_compact, '+442083661179')
# Test rejection of a phone type mechanism to non-phone value
with self.assertRaises(UserError):
mechanism.value = 'notaphone@example.com'
mechanism.save()
transaction.rollback()
# Test rejection of invalid phone number creation
with self.assertRaises(UserError):
mechanism = create('phone', 'alsonotaphone@example.com')
transaction.rollback()
# Test acceptance of a non-phone value when type is non-phone
mechanism = create('email', 'name@example.com')
@with_transaction()
def test_set_contact_mechanism(self):
"Test set_contact_mechanism"
pool = Pool()
Party = pool.get('party.party')
party = Party(email='test@example.com')
party.save()
self.assertEqual(party.email, 'test@example.com')
@with_transaction()
def test_set_contact_mechanism_with_value(self):
"Test set_contact_mechanism"
pool = Pool()
Party = pool.get('party.party')
party = Party(email='foo@example.com')
party.save()
party.email = 'bar@example.com'
with self.assertRaises(AccessError):
party.save()
@with_transaction()
def test_contact_mechanism_get_no_usage(self):
"Test contact_mechanism_get with no usage"
pool = Pool()
Party = pool.get('party.party')
ContactMechanism = pool.get('party.contact_mechanism')
party, = Party.create([{}])
contact1, contact2 = ContactMechanism.create([{
'party': party.id,
'sequence': 1,
'type': 'email',
'value': 'test1@example.com',
}, {
'party': party.id,
'sequence': 2,
'type': 'email',
'value': 'test2@example.com',
}])
contact = party.contact_mechanism_get('email')
self.assertEqual(contact, contact1)
@with_transaction()
def test_contact_mechanism_get_many_types(self):
"Test contact_mechanism_get with many types"
pool = Pool()
Party = pool.get('party.party')
ContactMechanism = pool.get('party.contact_mechanism')
party, = Party.create([{}])
contact1, contact2 = ContactMechanism.create([{
'party': party.id,
'sequence': 1,
'type': 'other',
'value': 'test',
}, {
'party': party.id,
'sequence': 2,
'type': 'email',
'value': 'test2@example.com',
}])
contact = party.contact_mechanism_get({'email', 'phone'})
self.assertEqual(contact, contact2)
@with_transaction()
def test_contact_mechanism_get_no_contact_mechanism(self):
"Test contact_mechanism_get with no contact mechanism"
pool = Pool()
Party = pool.get('party.party')
party, = Party.create([{}])
contact = party.contact_mechanism_get()
self.assertEqual(contact, None)
@with_transaction()
def test_contact_mechanism_get_no_type(self):
"Test contact_mechanism_get with no type"
pool = Pool()
Party = pool.get('party.party')
ContactMechanism = pool.get('party.contact_mechanism')
party, = Party.create([{}])
ContactMechanism.create([{
'party': party.id,
'type': 'email',
'value': 'test1@example.com',
}])
contact = party.contact_mechanism_get('phone')
self.assertEqual(contact, None)
@with_transaction()
def test_contact_mechanism_get_any_type(self):
"Test contact_mechanism_get with any type"
pool = Pool()
Party = pool.get('party.party')
ContactMechanism = pool.get('party.contact_mechanism')
party, = Party.create([{}])
email1, = ContactMechanism.create([{
'party': party.id,
'type': 'email',
'value': 'test1@example.com',
}])
contact = party.contact_mechanism_get()
self.assertEqual(contact, email1)
@with_transaction()
def test_contact_mechanism_get_inactive(self):
"Test contact_mechanism_get with inactive"
pool = Pool()
Party = pool.get('party.party')
ContactMechanism = pool.get('party.contact_mechanism')
party, = Party.create([{}])
contact1, contact2 = ContactMechanism.create([{
'party': party.id,
'sequence': 1,
'type': 'email',
'value': 'test1@example.com',
'active': False,
}, {
'party': party.id,
'sequence': 2,
'type': 'email',
'value': 'test2@example.com',
'active': True,
}])
contact = party.contact_mechanism_get()
self.assertEqual(contact, contact2)
@with_transaction()
def test_contact_mechanism_get_usage(self):
"Test contact_mechanism_get with usage"
pool = Pool()
Party = pool.get('party.party')
ContactMechanism = pool.get('party.contact_mechanism')
party, = Party.create([{}])
contact1, contact2 = ContactMechanism.create([{
'party': party.id,
'sequence': 1,
'type': 'email',
'value': 'test1@example.com',
'name': None,
}, {
'party': party.id,
'sequence': 2,
'type': 'email',
'value': 'test2@example.com',
'name': 'email',
}])
contact = party.contact_mechanism_get(usage='name')
self.assertEqual(contact, contact2)
@with_transaction()
def test_tax_identifier_types(self):
"Ensure tax identifier types are in identifier types"
pool = Pool()
Party = pool.get('party.party')
identifiers = dict(IDENTIFIER_TYPES).keys()
tax_identifiers = set(Party.tax_identifier_types())
self.assertLessEqual(tax_identifiers, identifiers)
@with_transaction()
def test_identifier_vat_types(self):
"Ensure VAT identifiers are identifier types"
identifiers = dict(IDENTIFIER_TYPES).keys()
vat_identifiers = set(map(replace_vat, IDENTIFIER_VAT))
self.assertLessEqual(vat_identifiers, identifiers)
@with_transaction()
def test_party_distance(self):
"Test party distance"
pool = Pool()
Party = pool.get('party.party')
A, B, = Party.create([{
'name': 'A',
}, {
'name': 'B',
}])
parties = Party.search([])
self.assertEqual([p.distance for p in parties], [None] * 2)
with Transaction().set_context(related_party=A.id):
parties = Party.search([])
self.assertEqual(
[(p.name, p.distance) for p in parties],
[('A', 0), ('B', None)])
del ModuleTestCase