Files
tryton/party.py
T

352 lines
12 KiB
Python
Raw Normal View History

2008-10-22 20:07:32 +02:00
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
2010-08-10 11:59:01 -04:00
import logging
2009-02-28 17:00:12 +01:00
from trytond.model import ModelView, ModelSQL, fields
from trytond.wizard import Wizard
2011-08-21 11:20:44 +02:00
from trytond.pyson import Bool, Eval
2010-08-10 11:59:01 -04:00
from trytond.transaction import Transaction
2011-07-03 13:19:00 +02:00
from trytond.pool import Pool
2008-09-21 19:07:19 +02:00
HAS_VATNUMBER = False
VAT_COUNTRIES = [('', '')]
try:
import vatnumber
HAS_VATNUMBER = True
for country in vatnumber.countries():
VAT_COUNTRIES.append((country, country))
except ImportError:
2008-10-22 20:07:32 +02:00
logging.getLogger('party').warning(
2009-03-20 12:22:48 +01:00
'Unable to import vatnumber. VAT number validation disabled.')
2008-09-21 19:07:19 +02:00
2008-02-26 12:41:13 +01:00
STATES = {
2011-08-21 11:20:44 +02:00
'readonly': ~Eval('active', True),
2008-02-26 12:41:13 +01:00
}
2011-08-04 16:57:31 +02:00
DEPENDS = ['active']
2008-02-11 22:05:04 +01:00
2009-02-28 17:00:12 +01:00
class Party(ModelSQL, ModelView):
2008-05-21 18:12:20 +02:00
"Party"
2008-02-11 22:05:04 +01:00
_description = __doc__
2008-10-22 20:07:32 +02:00
_name = "party.party"
2008-03-04 15:43:44 +01:00
2008-10-23 14:04:22 +02:00
name = fields.Char('Name', required=True, select=1,
2011-08-04 16:57:31 +02:00
states=STATES, depends=DEPENDS)
2008-10-23 14:04:22 +02:00
code = fields.Char('Code', required=True, select=1,
2008-08-23 01:35:48 +02:00
readonly=True, order_field="%(table)s.code_length %(order)s, " \
"%(table)s.code %(order)s")
code_length = fields.Integer('Code Length', select=1, readonly=True)
2011-08-04 16:57:31 +02:00
lang = fields.Many2One("ir.lang", 'Language', states=STATES,
depends=DEPENDS)
2008-10-23 14:04:22 +02:00
vat_number = fields.Char('VAT Number', help="Value Added Tax number",
2011-08-04 16:57:31 +02:00
states={
2011-08-21 11:20:44 +02:00
'readonly': ~Eval('active', True),
2011-08-04 16:57:31 +02:00
'required': Bool(Eval('vat_country')),
},
depends=['active', 'vat_country'])
2008-09-21 19:07:19 +02:00
vat_country = fields.Selection(VAT_COUNTRIES, 'VAT Country', states=STATES,
2011-08-04 16:57:31 +02:00
depends=DEPENDS,
2009-03-20 12:22:48 +01:00
help="Setting VAT country will enable validation of the VAT number.",
2008-11-26 17:20:54 +01:00
translate=False)
2010-03-19 15:26:56 +01:00
vat_code = fields.Function(fields.Char('VAT Code',
on_change_with=['vat_number', 'vat_country']), 'get_vat_code',
searcher='search_vat_code')
2008-10-22 20:07:32 +02:00
addresses = fields.One2Many('party.address', 'party',
2011-08-04 16:57:31 +02:00
'Addresses', states=STATES, depends=DEPENDS)
2008-10-23 13:41:06 +02:00
contact_mechanisms = fields.One2Many('party.contact_mechanism', 'party',
2011-08-04 16:57:31 +02:00
'Contact Mechanisms', states=STATES, depends=DEPENDS)
2009-03-05 14:52:31 +01:00
categories = fields.Many2Many('party.party-party.category',
2011-08-04 16:57:31 +02:00
'party', 'category', 'Categories', states=STATES, depends=DEPENDS)
2008-03-10 23:29:25 +01:00
active = fields.Boolean('Active', select=1)
2010-03-11 16:48:37 +01:00
full_name = fields.Function(fields.Char('Full Name'), 'get_full_name')
phone = fields.Function(fields.Char('Phone'), 'get_mechanism')
mobile = fields.Function(fields.Char('Mobile'), 'get_mechanism')
fax = fields.Function(fields.Char('Fax'), 'get_mechanism')
email = fields.Function(fields.Char('E-Mail'), 'get_mechanism')
website = fields.Function(fields.Char('Website'), 'get_mechanism')
2008-03-04 11:50:22 +01:00
def __init__(self):
2008-05-21 18:12:20 +02:00
super(Party, self).__init__()
2008-03-04 11:50:22 +01:00
self._sql_constraints = [
2008-05-07 18:59:25 +02:00
('code_uniq', 'UNIQUE(code)',
2008-05-21 18:12:20 +02:00
'The code of the party must be unique!')
2008-03-04 11:50:22 +01:00
]
2008-07-02 12:28:34 +02:00
self._constraints += [
2009-04-05 14:18:42 +02:00
('check_vat', 'invalid_vat'),
2008-07-02 12:28:34 +02:00
]
2008-08-05 11:36:25 +02:00
self._error_messages.update({
2009-04-05 14:18:42 +02:00
'invalid_vat': 'Invalid VAT number!',
2008-08-05 11:36:25 +02:00
})
2009-01-08 00:07:42 +01:00
self._order.insert(0, ('name', 'ASC'))
2010-08-10 11:59:01 -04:00
def default_active(self):
2008-07-02 12:28:34 +02:00
return True
2010-08-10 11:59:01 -04:00
def default_categories(self):
return Transaction().context.get('categories', [])
2010-08-10 11:59:01 -04:00
def default_addresses(self):
2011-07-03 13:19:00 +02:00
address_obj = Pool().get('party.address')
fields_names = list(x for x in set(address_obj._columns.keys()
+ address_obj._inherit_fields.keys())
if x not in ['id', 'create_uid', 'create_date',
'write_uid', 'write_date'])
2010-08-10 11:59:01 -04:00
return [address_obj.default_get(fields_names)]
2010-06-20 22:33:35 +02:00
2011-03-29 15:17:39 +02:00
def default_lang(self):
2011-07-03 13:19:00 +02:00
config_obj = Pool().get('party.configuration')
2011-03-29 15:17:39 +02:00
config = config_obj.browse(1)
return config.party_lang.id
2010-08-10 11:59:01 -04:00
def on_change_with_vat_code(self, vals):
2010-03-19 15:26:56 +01:00
return (vals.get('vat_country') or '') + (vals.get('vat_number') or '')
2010-08-10 11:59:01 -04:00
def get_vat_code(self, ids, name):
2008-07-02 12:28:34 +02:00
if not ids:
return []
res = {}
2010-08-10 11:59:01 -04:00
for party in self.browse(ids):
2008-07-02 13:52:05 +02:00
res[party.id] = (party.vat_country or '') + (party.vat_number or '')
2008-07-02 12:28:34 +02:00
return res
2008-02-26 16:23:30 +01:00
2010-08-10 11:59:01 -04:00
def search_vat_code(self, name, clause):
res = []
value = clause[2]
for country, _ in VAT_COUNTRIES:
if isinstance(value, basestring) \
and country \
and value.upper().startswith(country):
res.append(('vat_country', '=', country))
value = value[len(country):]
break
res.append(('vat_number', clause[1], value))
return res
2009-01-04 15:33:00 +01:00
2010-08-10 11:59:01 -04:00
def get_full_name(self, ids, name):
2008-10-22 20:07:32 +02:00
if not ids:
return []
res = {}
2010-08-10 11:59:01 -04:00
for party in self.browse(ids):
2008-10-22 20:07:32 +02:00
res[party.id] = party.name
return res
2010-08-10 11:59:01 -04:00
def get_mechanism(self, ids, name):
2008-10-23 13:41:06 +02:00
if not ids:
return []
res = {}
2010-08-10 11:59:01 -04:00
for party in self.browse(ids):
2008-10-23 13:41:06 +02:00
res[party.id] = ''
for mechanism in party.contact_mechanisms:
2010-03-11 16:48:37 +01:00
if mechanism.type == name:
2008-10-23 13:41:06 +02:00
res[party.id] = mechanism.value
break
2008-10-23 13:41:06 +02:00
return res
2010-08-10 11:59:01 -04:00
def create(self, values):
2011-07-03 13:19:00 +02:00
sequence_obj = Pool().get('ir.sequence')
config_obj = Pool().get('party.configuration')
2008-05-07 18:59:25 +02:00
values = values.copy()
2008-06-19 20:16:41 +02:00
if not values.get('code'):
2010-08-10 11:59:01 -04:00
config = config_obj.browse(1)
values['code'] = sequence_obj.get_id(config.party_sequence.id)
values['code_length'] = len(values['code'])
2010-08-10 11:59:01 -04:00
return super(Party, self).create(values)
2010-08-10 11:59:01 -04:00
def write(self, ids, vals):
if vals.get('code'):
vals = vals.copy()
vals['code_length'] = len(vals['code'])
2010-08-10 11:59:01 -04:00
return super(Party, self).write(ids, vals)
2010-08-10 11:59:01 -04:00
def copy(self, ids, default=None):
2011-07-03 13:19:00 +02:00
address_obj = Pool().get('party.address')
int_id = False
if isinstance(ids, (int, long)):
int_id = True
ids = [ids]
if default is None:
default = {}
default = default.copy()
default['code'] = False
default['addresses'] = False
new_ids = []
2010-08-10 11:59:01 -04:00
for party in self.browse(ids):
new_id = super(Party, self).copy(party.id, default=default)
address_obj.copy([x.id for x in party.addresses],
default={
'party': new_id,
2010-08-10 11:59:01 -04:00
})
new_ids.append(new_id)
if int_id:
return new_ids[0]
return new_ids
2008-05-07 18:59:25 +02:00
2010-08-10 11:59:01 -04:00
def search_rec_name(self, name, clause):
2011-03-29 11:13:39 +02:00
ids = self.search([('code',) + clause[1:]], order=[])
if ids:
2010-08-10 11:59:01 -04:00
ids += self.search([('name',) + clause[1:]], order=[])
return [('id', 'in', ids)]
return [('name',) + clause[1:]]
2008-07-28 18:29:20 +02:00
2010-08-10 11:59:01 -04:00
def address_get(self, party_id, type=None):
"""
2008-03-06 01:07:08 +01:00
Try to find an address for the given type, if no type match
the first address is return.
"""
2011-07-03 13:19:00 +02:00
address_obj = Pool().get("party.address")
2008-03-06 01:07:08 +01:00
address_ids = address_obj.search(
2010-08-10 11:59:01 -04:00
[("party", "=", party_id), ("active", "=", True)],
order=[('sequence', 'ASC'), ('id', 'ASC')])
2008-03-06 01:07:08 +01:00
if not address_ids:
return False
2008-03-06 01:07:08 +01:00
default_address = address_ids[0]
if not type:
return default_address
2010-08-10 11:59:01 -04:00
for address in address_obj.browse(address_ids):
2008-03-06 01:07:08 +01:00
if address[type]:
return address.id
return default_address
2008-03-04 15:43:44 +01:00
2010-08-10 11:59:01 -04:00
def check_vat(self, ids):
2008-07-02 12:28:34 +02:00
'''
Check the VAT number depending of the country.
http://sima-pc.com/nif.php
'''
2008-09-21 19:07:19 +02:00
if not HAS_VATNUMBER:
return True
2010-08-10 11:59:01 -04:00
for party in self.browse(ids):
vat_number = party.vat_number
if not party.vat_country:
2008-07-02 12:28:34 +02:00
continue
2008-09-21 19:07:19 +02:00
if not getattr(vatnumber, 'check_vat_' + \
party.vat_country.lower())(vat_number):
#Check if user doesn't have put country code in number
if vat_number.startswith(party.vat_country):
vat_number = vat_number[len(party.vat_country):]
2010-08-10 11:59:01 -04:00
self.write(party.id, {
'vat_number': vat_number,
})
else:
return False
2008-07-02 12:28:34 +02:00
return True
2008-05-21 18:12:20 +02:00
Party()
2009-03-05 14:52:31 +01:00
class PartyCategory(ModelSQL):
'Party - Category'
_name = 'party.party-party.category'
_table = 'party_category_rel'
2009-03-17 13:19:54 +01:00
_description = __doc__
2009-03-05 14:52:31 +01:00
party = fields.Many2One('party.party', 'Party', ondelete='CASCADE',
required=True, select=1)
category = fields.Many2One('party.category', 'Category', ondelete='CASCADE',
required=True, select=1)
PartyCategory()
class CheckVIESNoCheck(ModelView):
'Check VIES - No Check'
_name = 'party.check_vies.no_check'
_description = __doc__
CheckVIESNoCheck()
class CheckVIESCheck(ModelView):
'Check VIES - Check'
_name = 'party.check_vies.check'
_description = __doc__
parties_succeed = fields.Many2Many('party.party', None, None,
2011-08-21 11:20:44 +02:00
'Parties Succeed', readonly=True, states={
'invisible': ~Eval('parties_succeed'),
})
parties_failed = fields.Many2Many('party.party', None, None,
2011-08-21 11:20:44 +02:00
'Parties Failed', readonly=True, states={
'invisible': ~Eval('parties_failed'),
})
CheckVIESCheck()
class CheckVIES(Wizard):
'Check VIES'
_name = 'party.check_vies'
states = {
'init': {
'result': {
'type': 'choice',
'next_state': '_choice',
},
},
'no_check': {
'result': {
'type': 'form',
'object': 'party.check_vies.no_check',
'state': [
('end', 'Ok', 'tryton-ok', True),
],
},
},
'check': {
'actions': ['_check'],
'result': {
'type': 'form',
'object': 'party.check_vies.check',
'state': [
('end', 'Ok', 'tryton-ok', True),
],
},
},
}
def __init__(self):
super(CheckVIES, self).__init__()
self._error_messages.update({
'vies_unavailable': 'The VIES service is unavailable, ' \
'try again later.',
})
2010-08-10 11:59:01 -04:00
def _choice(self, data):
if not HAS_VATNUMBER or not hasattr(vatnumber, 'check_vies'):
return 'no_check'
return 'check'
2010-08-10 11:59:01 -04:00
def _check(self, data):
2011-07-03 13:19:00 +02:00
party_obj = Pool().get('party.party')
res = {
'parties_succeed': [],
'parties_failed': [],
}
2010-08-10 11:59:01 -04:00
parties = party_obj.browse(data['ids'])
for party in parties:
if not party.vat_code:
continue
try:
if not vatnumber.check_vies(party.vat_code):
res['parties_failed'].append(party.id)
else:
res['parties_succeed'].append(party.id)
except Exception, e:
if hasattr(e, 'faultstring') \
and hasattr(e.faultstring, 'find'):
if e.faultstring.find('INVALID_INPUT'):
res['parties_failed'].append(party.id)
continue
if e.faultstring.find('SERVICE_UNAVAILABLE') \
or e.faultstring.find('MS_UNAVAILABLE') \
or e.faultstring.find('TIMEOUT') \
or e.faultstring.find('SERVER_BUSY'):
2010-08-10 11:59:01 -04:00
self.raise_user_error('vies_unavailable')
raise
return res
CheckVIES()