Make that the module could be installed and works :-)
This commit is contained in:
parent
92f315cf10
commit
9f66e89be9
12
__init__.py
12
__init__.py
|
@ -1,7 +1,15 @@
|
|||
#This file is part asterisk module for Tryton.
|
||||
#The COPYRIGHT file at the top level of this repository contains
|
||||
#the full copyright notices and license terms.
|
||||
from trytond.pool import Pool
|
||||
from .asterisk import *
|
||||
from .party import *
|
||||
from .user import *
|
||||
|
||||
def register():
|
||||
pass
|
||||
|
||||
Pool.register(
|
||||
AsteriskConfiguration,
|
||||
AsteriskConfigurationCompany,
|
||||
Party,
|
||||
User,
|
||||
module='asterisk', type_='model')
|
||||
|
|
|
@ -0,0 +1,443 @@
|
|||
#This file is part asterisk module for Tryton.
|
||||
#The COPYRIGHT file at the top level of this repository contains
|
||||
#the full copyright notices and license terms.
|
||||
from trytond.model import ModelView, ModelSQL, ModelSingleton, fields
|
||||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
import logging
|
||||
import socket
|
||||
import unicodedata
|
||||
|
||||
__all__ = [ 'AsteriskConfiguration', 'AsteriskConfigurationCompany']
|
||||
|
||||
|
||||
class AsteriskConfiguration(ModelSingleton, ModelSQL, ModelView):
|
||||
'Asterisk Configuration'
|
||||
__name__ = 'asterisk.configuration'
|
||||
name = fields.Function(fields.Char('Asterisk server name', required=True,
|
||||
help="Asterisk server name."), 'get_fields', setter='set_fields')
|
||||
ip_address = fields.Function(fields.Char('Asterisk IP addr. or DNS',
|
||||
required=True,
|
||||
help="IPv4 address or DNS name of the Asterisk server."),
|
||||
'get_fields', setter='set_fields')
|
||||
port = fields.Function(fields.Char('Port', required=True,
|
||||
help="TCP port on which the Asterisk Manager Interface listens. "
|
||||
"Defined in /etc/asterisk/manager.conf on Asterisk."),
|
||||
'get_fields', setter='set_fields')
|
||||
out_prefix = fields.Function(fields.Char('Out prefix',
|
||||
help="Prefix to dial to place outgoing calls. If you don't use a "
|
||||
"prefix to place outgoing calls, leave empty."),
|
||||
'get_fields', setter='set_fields')
|
||||
national_prefix = fields.Function(fields.Char('National prefix',
|
||||
help="Prefix for national phone calls (don't include the 'out"
|
||||
" prefix'). For example, in France, the phone numbers looks like "
|
||||
"'01 41 98 12 42': the National prefix is '0'."),
|
||||
'get_fields', setter='set_fields')
|
||||
international_prefix = fields.Function(fields.Char('International prefix',
|
||||
help="Prefix to add to make international phone calls (don't "
|
||||
"include the 'out prefix'). For example, in France, the "
|
||||
"International prefix is '00'."),
|
||||
'get_fields', setter='set_fields')
|
||||
country_prefix = fields.Function(fields.Char('My country prefix',
|
||||
required=True,
|
||||
help="Prefix to add to make international phone calls (don't "
|
||||
"include the 'out prefix'). For example, the phone prefix for "
|
||||
"France is '33'. If the phone number to dial starts with the 'My "
|
||||
"country prefix', Tryton will remove the country prefix from "
|
||||
"the phone number and add the 'out prefix' followed by the "
|
||||
"'national prefix'. If the phone number to dial doesn't start "
|
||||
"with the 'My country prefix', Tryton will add the 'out "
|
||||
"prefix' followed by the 'international prefix'."),
|
||||
'get_fields', setter='set_fields')
|
||||
national_format_allowed = fields.Function(fields.Boolean(
|
||||
'National format allowed?',
|
||||
help="Do we allow to use click2dial on phone numbers written in "
|
||||
"national format, e.g. 01 41 98 12 42, or only in the "
|
||||
"international format, e.g. +34 1 41 98 12 42 ?"),
|
||||
'get_fields', setter='set_fields')
|
||||
login = fields.Function(fields.Char('AMI login', required=True,
|
||||
help="Login that Tryton will use to communicate with the Asterisk "
|
||||
"Manager Interface. Refer to /etc/asterisk/manager.conf on "
|
||||
"your Asterisk server."),
|
||||
'get_fields', setter='set_fields')
|
||||
password = fields.Function(fields.Char('AMI password', required=True,
|
||||
help="Password that Asterisk will use to communicate with the "
|
||||
"Asterisk Manager Interface. Refer to /etc/asterisk/manager.conf "
|
||||
"on your Asterisk server."),
|
||||
'get_fields', setter='set_fields')
|
||||
context = fields.Function(fields.Char('Dialplan context', required=True,
|
||||
help="Asterisk dialplan context from which the calls will be "
|
||||
"made. Refer to /etc/asterisk/extensions.conf on your Asterisk "
|
||||
"server."),
|
||||
'get_fields', setter='set_fields')
|
||||
wait_time = fields.Function(fields.Integer('Wait time (sec)',
|
||||
required=True,
|
||||
help="Amount of time (in seconds) Asterisk will try to reach the "
|
||||
"user's phone before hanging up."),
|
||||
'get_fields', setter='set_fields')
|
||||
extension_priority = fields.Function(fields.Integer('Extension priority',
|
||||
required=True,
|
||||
help="Priority of the extension in the Asterisk dialplan. Refer "
|
||||
"to /etc/asterisk/extensions.conf on your Asterisk server."),
|
||||
'get_fields', setter='set_fields')
|
||||
alert_info = fields.Function(fields.Char('Alert-Info SIP header',
|
||||
help="Set Alert-Info header in SIP request to user's IP Phone. If "
|
||||
"empty, the Alert-Info header will not be added. You can use "
|
||||
"it to have a special ring tone for click2dial, for example "
|
||||
"you could choose a silent ring tone."),
|
||||
'get_fields', setter='set_fields')
|
||||
|
||||
@classmethod
|
||||
def get_fields(cls, configurations, names):
|
||||
res = {}
|
||||
ConfigurationCompany = Pool().get('asterisk.configuration.company')
|
||||
company_id = Transaction().context.get('company')
|
||||
conf_id = configurations[0].id
|
||||
if company_id:
|
||||
confs = ConfigurationCompany.search([
|
||||
('company', '=', company_id),
|
||||
], limit=1)
|
||||
for conf in confs:
|
||||
for field_name in names:
|
||||
value = getattr(conf, field_name)
|
||||
res[field_name] = {conf_id: value}
|
||||
return res
|
||||
|
||||
@classmethod
|
||||
def set_fields(cls, configurations, name, value):
|
||||
if value:
|
||||
ConfigurationCompany = Pool().get('asterisk.configuration.company')
|
||||
company_id = Transaction().context.get('company')
|
||||
if company_id:
|
||||
configuration = ConfigurationCompany.search([
|
||||
('company', '=', company_id),
|
||||
], limit=1)
|
||||
if not configuration:
|
||||
ConfigurationCompany.create([{
|
||||
'company': company_id,
|
||||
name: value,
|
||||
}])
|
||||
else:
|
||||
ConfigurationCompany.write([configuration[0]], {
|
||||
name: value
|
||||
})
|
||||
|
||||
def _only_digits(self, prefix, can_be_empty):
|
||||
prefix_to_check = self.read([self.id], [prefix])[0]
|
||||
if prefix_to_check:
|
||||
prefix_to_check = prefix_to_check[prefix]
|
||||
if not prefix_to_check:
|
||||
if not can_be_empty:
|
||||
return False
|
||||
else:
|
||||
if not prefix_to_check.isdigit():
|
||||
return False
|
||||
return True
|
||||
|
||||
def _only_digits_port(self):
|
||||
return self._only_digits('port', False)
|
||||
|
||||
def _only_digits_out_prefix(self):
|
||||
return self._only_digits('out_prefix', True)
|
||||
|
||||
def _only_digits_country_prefix(self):
|
||||
return self._only_digits('country_prefix', False)
|
||||
|
||||
def _only_digits_national_prefix(self):
|
||||
return self._only_digits('national_prefix', True)
|
||||
|
||||
def _only_digits_international_prefix(self):
|
||||
return self._only_digits('international_prefix', False)
|
||||
|
||||
def _check_wait_time(self):
|
||||
wait_time_to_check = self.read([self.id], ['wait_time'])[0]\
|
||||
['wait_time']
|
||||
if wait_time_to_check < 1 or wait_time_to_check > 120:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_extension_priority(self):
|
||||
extension_priority_to_check = self.read([self.id],
|
||||
['extension_priority'])[0]['extension_priority']
|
||||
if extension_priority_to_check < 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_port(self):
|
||||
port_to_check = self.read([self.id], ['port'])[0]['port']
|
||||
if int(port_to_check) > 65535 or int(port_to_check) < 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(AsteriskConfiguration, cls).__setup__()
|
||||
cls._constraints += [
|
||||
('_only_digits_port', 'port'),
|
||||
('_only_digits_out_prefix', 'out_prefix'),
|
||||
('_only_digits_country_prefix', 'country_prefix'),
|
||||
('_only_digits_national_prefix', 'national_prefix'),
|
||||
('_only_digits_international_prefix', 'international_prefix'),
|
||||
('_check_wait_time', 'wait_time'),
|
||||
('_check_extension_priority', 'extension_priority'),
|
||||
('_check_port', 'port'),
|
||||
]
|
||||
cls._error_messages.update({
|
||||
'out_prefix': "Use only digits for the 'Out prefix' or leave "
|
||||
"it empty.",
|
||||
'country_prefix': "Use only digits for the 'Country prefix'.",
|
||||
'national_prefix': "Use only digits for the 'National prefix' "
|
||||
"or leave it empty.",
|
||||
'international_prefix': "Use only digits for 'International "
|
||||
"prefix'.",
|
||||
'wait_time': "You should enter a 'Wait time' value between 1 "
|
||||
"and 120 seconds.",
|
||||
'extension_priority': "The 'Extension priority' must be a "
|
||||
"positive value.",
|
||||
'port': 'TCP ports range from 1 to 65535.',
|
||||
'error': 'Error',
|
||||
'invalid_phone': 'Invalid phone number',
|
||||
'invalid_international_format': "The phone number is not "
|
||||
"written in a valid international format. Example of valid "
|
||||
"international format: +33 1 41 98 12 42.",
|
||||
'invalid_national_format': "The phone number is not written "
|
||||
"in a valid national format.",
|
||||
'invalid_format': "The phone number is not written in a valid "
|
||||
"format.",
|
||||
'no_phone_number': "There is no phone number.",
|
||||
'no_asterisk_configuration': "Not available Asterisk Server "
|
||||
"configured for the current user.",
|
||||
'no_channel_type': "There isn't a channel type configured for "
|
||||
"the current user",
|
||||
'no_internal_phone': "There isn't a internal phone number "
|
||||
"configured for the current user",
|
||||
'cant_resolve_dns': "Can't resolve the DNS of the Asterisk "
|
||||
"server:",
|
||||
'connection_failed': "The connection from Tryton to the "
|
||||
"Asterisk server has failed. Please check the configuration "
|
||||
"on Tryton and Asterisk.",
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_port():
|
||||
return '5038'
|
||||
|
||||
@staticmethod
|
||||
def default_out_prefix():
|
||||
return '0'
|
||||
|
||||
@staticmethod
|
||||
def default_national_prefix():
|
||||
return '0'
|
||||
|
||||
@staticmethod
|
||||
def default_international_prefix():
|
||||
return '00'
|
||||
|
||||
@staticmethod
|
||||
def default_extension_priority():
|
||||
return 1
|
||||
|
||||
@staticmethod
|
||||
def default_wait_time():
|
||||
return 5
|
||||
|
||||
@staticmethod
|
||||
def unaccent(text):
|
||||
if isinstance(text, str):
|
||||
text = unicode(text, 'utf-8')
|
||||
return unicodedata.normalize('NFKD', text).encode('ASCII',
|
||||
'ignore')
|
||||
|
||||
@classmethod
|
||||
def reformat_number(cls, tryton_number, ast_server):
|
||||
'''
|
||||
This method transforms the number available in Tryton to the number
|
||||
that Asterisk should dial.
|
||||
'''
|
||||
logger = logging.getLogger('asterisk')
|
||||
|
||||
# Let's call the variable tmp_number now
|
||||
tmp_number = tryton_number
|
||||
logger.debug('Number before reformat = %s' % tmp_number)
|
||||
|
||||
# Check if empty
|
||||
if not tmp_number:
|
||||
cls.raise_user_error(error='invalid_phone',
|
||||
error_description='invalid_format')
|
||||
|
||||
# First, we remove all stupid characters and spaces
|
||||
for i in [' ', '.', '(', ')', '[', ']', '-', '/']:
|
||||
tmp_number = tmp_number.replace(i, '')
|
||||
|
||||
# Before starting to use prefix, we convert empty prefix whose value
|
||||
# is False to an empty string
|
||||
country_prefix = (ast_server.country_prefix or '')
|
||||
national_prefix = (ast_server.national_prefix or '')
|
||||
international_prefix = (ast_server.international_prefix or '')
|
||||
out_prefix = (ast_server.out_prefix or '')
|
||||
|
||||
# International format
|
||||
if tmp_number[0] == '+':
|
||||
# Remove the starting '+' of the number
|
||||
tmp_number = tmp_number.replace('+','')
|
||||
logger.debug('Number after removal of special char = %s' % \
|
||||
tmp_number)
|
||||
|
||||
# At this stage, 'tmp_number' should only contain digits
|
||||
if not tmp_number.isdigit():
|
||||
cls.raise_user_error(error='invalid_phone',
|
||||
error_description='invalid_format_msg')
|
||||
|
||||
logger.debug('Country prefix = ' + country_prefix)
|
||||
if country_prefix == tmp_number[0:len(country_prefix)]:
|
||||
# If the number is a national number,
|
||||
# remove 'my country prefix' and add 'national prefix'
|
||||
tmp_number = (national_prefix) + tmp_number[
|
||||
len(country_prefix):len(tmp_number)]
|
||||
logger.debug('National prefix = %s - Number with national '
|
||||
'prefix = %s' % (national_prefix, tmp_number))
|
||||
else:
|
||||
# If the number is an international number,
|
||||
# add 'international prefix'
|
||||
tmp_number = international_prefix + tmp_number
|
||||
logger.debug('International prefix = %s - Number with '
|
||||
'international prefix = %s' % (international_prefix,
|
||||
tmp_number))
|
||||
# National format, allowed
|
||||
elif ast_server.national_format_allowed:
|
||||
# No treatment required
|
||||
if not tmp_number.isdigit():
|
||||
cls.raise_user_error(error='invalid_phone',
|
||||
error_description='invalid_national_format')
|
||||
|
||||
# National format, disallowed
|
||||
elif not ast_server.national_format_allowed:
|
||||
cls.raise_user_error(error='invalid_phone',
|
||||
error_description='invalid_international_format')
|
||||
# Add 'out prefix' to all numbers
|
||||
tmp_number = out_prefix + tmp_number
|
||||
logger.debug('Out prefix = %s - Number to be sent to Asterisk = %s' % \
|
||||
(out_prefix, tmp_number))
|
||||
return tmp_number
|
||||
|
||||
@classmethod
|
||||
def dial(cls, party, tryton_number):
|
||||
'''
|
||||
Open the socket to the Asterisk Manager Interface (AMI)
|
||||
and send instructions to Dial to Asterisk.
|
||||
'''
|
||||
logger = logging.getLogger('asterisk')
|
||||
User = Pool().get('res.user')
|
||||
user_id = Transaction().user
|
||||
if user_id == 0 and 'user' in Transaction().context:
|
||||
user_id = Transaction().context['user']
|
||||
user = User(user_id)
|
||||
|
||||
# Check if the number to dial is not empty
|
||||
if not tryton_number:
|
||||
cls.raise_user_error(error='error',
|
||||
error_description='no_phone_number')
|
||||
|
||||
# We check if the user has an Asterisk server configured
|
||||
if not user.asterisk_server:
|
||||
cls.raise_user_error(error='error',
|
||||
error_description='no_asterisk_configuration')
|
||||
else:
|
||||
ast_server = user.asterisk_server
|
||||
|
||||
# We check if the current user has a chan type
|
||||
if not user.asterisk_chan_type:
|
||||
cls.raise_user_error(error='error',
|
||||
error_description='no_channel_type')
|
||||
|
||||
# We check if the current user has an internal number
|
||||
if not user.internal_number:
|
||||
cls.raise_user_error(error='error',
|
||||
error_description='no_internal_phone')
|
||||
|
||||
# The user should also have a CallerID, but in Spain that will
|
||||
# be the name of the address that we call.
|
||||
if not user.callerid:
|
||||
#Party = Pool().get('party.party')
|
||||
#callerid = Party.search(party).get_name_for_display
|
||||
callerid = party.display_name
|
||||
else:
|
||||
callerid = user.CallerId
|
||||
|
||||
# Convert the phone number in the format that will be sent to Asterisk
|
||||
ast_number = cls.reformat_number(tryton_number, ast_server)
|
||||
logger.info('User dialing: channel = %s/%s - Callerid = %s' % \
|
||||
(user.asterisk_chan_type, user.internal_number, user.callerid))
|
||||
logger.info('Asterisk server = %s:%s' % \
|
||||
(ast_server.ip_address, ast_server.port))
|
||||
|
||||
# Connect to the Asterisk Manager Interface, using IPv6-ready code
|
||||
try:
|
||||
res = socket.getaddrinfo(ast_server.ip_address, ast_server.port,
|
||||
socket.AF_UNSPEC, socket.SOCK_STREAM)
|
||||
except:
|
||||
logger.error("Can't resolve the DNS of the Asterisk server : %s" %\
|
||||
str(ast_server.ip_address))
|
||||
cls.raise_user_error(error='error',
|
||||
error_description='cant_resolve_dns')
|
||||
for result in res:
|
||||
af, socktype, proto, _, sockaddr = result
|
||||
try:
|
||||
sock = socket.socket(af, socktype, proto)
|
||||
sock.connect(sockaddr)
|
||||
sock.send('Action: login\r\n')
|
||||
sock.send('Events: off\r\n')
|
||||
sock.send('Username: %s\r\n' % str(ast_server.login))
|
||||
sock.send('Secret: %s\r\n\r\n' % str(ast_server.password))
|
||||
sock.send('Action: originate\r\n')
|
||||
sock.send('Channel: %s/%s\r\n' % (str(user.asterisk_chan_type),
|
||||
str(user.internal_number)))
|
||||
sock.send('Timeout: %s\r\n' % str(ast_server.wait_time*1000))
|
||||
sock.send('CallerId: %s\r\n' % cls.unaccent(callerid))
|
||||
sock.send('Exten: %s\r\n' % str(ast_number))
|
||||
sock.send('Context: %s\r\n' % str(ast_server.context))
|
||||
if ast_server.alert_info and user.asterisk_chan_type == 'SIP':
|
||||
sock.send('Variable: SIPAddHeader=Alert-Info: %s\r\n' % \
|
||||
str(ast_server.alert_info))
|
||||
sock.send('Priority: %s\r\n\r\n' % \
|
||||
str(ast_server.extension_priority))
|
||||
sock.send('Action: Logoff\r\n\r\n')
|
||||
sock.close()
|
||||
except:
|
||||
logger.debug("Click2dial failed: unable to connect to "
|
||||
"Asterisk")
|
||||
cls.raise_user_error(error='error',
|
||||
error_description='connection_failed')
|
||||
logger.info("Asterisk Click2Dial from %s to %s" % \
|
||||
(user.internal_number, ast_number))
|
||||
|
||||
|
||||
class AsteriskConfigurationCompany(ModelSQL, ModelView):
|
||||
'Asterisk Configuration Company'
|
||||
__name__ = 'asterisk.configuration.company'
|
||||
|
||||
company = fields.Many2One('company.company', 'Company', readonly=True)
|
||||
name = fields.Char('Asterisk server name')
|
||||
ip_address = fields.Char('Asterisk IP addr. or DNS')
|
||||
port = fields.Char('Port')
|
||||
out_prefix = fields.Char('Out prefix')
|
||||
national_prefix = fields.Char('National prefix')
|
||||
international_prefix = fields.Char('International prefix')
|
||||
country_prefix = fields.Char('My country prefix')
|
||||
national_format_allowed = fields.Boolean('National format allowed?')
|
||||
login = fields.Char('AMI login')
|
||||
#TODO: Make not visible the password
|
||||
password = fields.Char('AMI password')
|
||||
context = fields.Char('Dialplan context')
|
||||
wait_time = fields.Integer('Wait time (sec)')
|
||||
extension_priority = fields.Integer('Extension priority')
|
||||
alert_info = fields.Char('Alert-Info SIP header')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(AsteriskConfigurationCompany, cls).__setup__()
|
||||
cls._sql_constraints += [
|
||||
('company_uniq', 'UNIQUE(company)',
|
||||
'There is already one configuration for this company.'),
|
||||
]
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part asterisk module for Tryton.
|
||||
The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="res.group" id="group_asterisk">
|
||||
<field name="name">Asterisk Administration</field>
|
||||
</record>
|
||||
|
||||
<record model="res.user-res.group" id="user_admin_group_asterisk">
|
||||
<field name="user" ref="res.user_admin"/>
|
||||
<field name="group" ref="group_asterisk"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_asterisk"
|
||||
name="Asterisk"
|
||||
parent="ir.menu_administration"/>
|
||||
|
||||
<!-- asterisk.configuration -->
|
||||
<record model="ir.ui.view" id="asterisk_configuration_view_form">
|
||||
<field name="model">asterisk.configuration</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">asterisk_configuration_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_asterisk_configuration_form">
|
||||
<field name="name">Server</field>
|
||||
<field name="res_model">asterisk.configuration</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window.view" id="act_asterisk_configuration_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="asterisk_configuration_view_form"/>
|
||||
<field name="act_window" ref="act_asterisk_configuration_form"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_asterisk_configuration"
|
||||
name="Asterisk Configuration"
|
||||
parent="menu_asterisk"
|
||||
action="act_asterisk_configuration_form"/>
|
||||
|
||||
<record model="ir.model.access" id="access_asterisk_asterisk_administration">
|
||||
<field name="model" search="[('model', '=', 'asterisk.configuration.company')]"/>
|
||||
<field name="group" ref="group_asterisk"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_asterisk_asterisk">
|
||||
<field name="model" search="[('model', '=', 'asterisk.configuration.company')]"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
|
@ -0,0 +1,14 @@
|
|||
#This file is part asterisk module for Tryton.
|
||||
#The COPYRIGHT file at the top level of this repository contains
|
||||
#the full copyright notices and license terms.
|
||||
from trytond.pool import PoolMeta
|
||||
|
||||
__all__ = ['Party']
|
||||
__metaclass__ = PoolMeta
|
||||
|
||||
class Party:
|
||||
__name__ = 'party.party'
|
||||
|
||||
@property
|
||||
def display_name(self):
|
||||
return self.trade_name or self.name or "UNKNOWN NAME"
|
|
@ -1,6 +1,10 @@
|
|||
[tryton]
|
||||
version=3.0.0
|
||||
depends:
|
||||
company
|
||||
ir
|
||||
party
|
||||
res
|
||||
|
||||
xml:
|
||||
asterisk.xml
|
||||
user.xml
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
#This file is part asterisk module for Tryton.
|
||||
#The COPYRIGHT file at the top level of this repository contains
|
||||
#the full copyright notices and license terms.
|
||||
from trytond.model import fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
|
||||
__all__ = ['User']
|
||||
__metaclass__ = PoolMeta
|
||||
|
||||
|
||||
class User():
|
||||
__name__ = "res.user"
|
||||
internal_number = fields.Char('Internal number',
|
||||
help="User's internal phone number.")
|
||||
callerid = fields.Char('Caller ID',
|
||||
help="Caller ID used for the calls initiated by this user.")
|
||||
asterisk_chan_type = fields.Selection([
|
||||
('SIP', 'SIP'),
|
||||
('IAX2', 'IAX2'),
|
||||
('DAHDI', 'DAHDI'),
|
||||
('Zap', 'Zap'),
|
||||
('Skinny', 'Skinny'),
|
||||
('MGCP', 'MGCP'),
|
||||
('mISDN', 'mISDN'),
|
||||
('H323', 'H323'),
|
||||
], 'Asterisk channel type',
|
||||
help="Asterisk channel type, as used in the Asterisk dialplan. "
|
||||
"If the user has a regular IP phone, the channel type is "
|
||||
"'SIP'.")
|
||||
asterisk_server = fields.Function(fields.Many2One('asterisk.configuration',
|
||||
'Asterisk server',
|
||||
help="Asterisk server on which the user's phone is connected."),
|
||||
getter='get_asterisk_server')
|
||||
|
||||
def get_asterisk_server(self, name=None):
|
||||
ConfigurationCompany = Pool().get('asterisk.configuration.company')
|
||||
if self.company:
|
||||
confs = ConfigurationCompany.search([
|
||||
('company', '=', self.company.id),
|
||||
], limit=1)
|
||||
if confs:
|
||||
return confs[0].id
|
||||
return None
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part asterisk module for Tryton.
|
||||
The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="user_view_form">
|
||||
<field name="model">res.user</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">user_form</field>
|
||||
<field name="inherit" ref="res.user_view_form"/>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part asterisk module for Tryton.
|
||||
The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. -->
|
||||
<form string="Asterisk">
|
||||
<label name="name"/>
|
||||
<field name="name"/>
|
||||
<label name="ip_address"/>
|
||||
<field name="ip_address"/>
|
||||
<label name="port"/>
|
||||
<field name="port"/>
|
||||
<separator string="Prefix" colspan="4" id="prefix"/>
|
||||
<label name="out_prefix"/>
|
||||
<field name="out_prefix"/>
|
||||
<label name="national_prefix"/>
|
||||
<field name="national_prefix"/>
|
||||
<label name="international_prefix"/>
|
||||
<field name="international_prefix"/>
|
||||
<label name="country_prefix"/>
|
||||
<field name="country_prefix"/>
|
||||
<label name="national_format_allowed"/>
|
||||
<field name="national_format_allowed"/>
|
||||
<separator string="AMI" colspan="4" id="ami"/>
|
||||
<label name="login"/>
|
||||
<field name="login"/>
|
||||
<label name="password"/>
|
||||
<field name="password"/>
|
||||
<label name="context"/>
|
||||
<field name="context"/>
|
||||
<label name="wait_time"/>
|
||||
<field name="wait_time"/>
|
||||
<label name="extension_priority"/>
|
||||
<field name="extension_priority"/>
|
||||
<label name="alert_info"/>
|
||||
<field name="alert_info"/>
|
||||
</form>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part asterisk module for Tryton.
|
||||
The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/form/notebook/page[@id='preferences']" position="after">
|
||||
<page string="Asterisk" col="4" id="asterisk">
|
||||
<label name="internal_number"/>
|
||||
<field name="internal_number"/>
|
||||
<label name="callerid"/>
|
||||
<field name="callerid"/>
|
||||
<label name="asterisk_chan_type"/>
|
||||
<field name="asterisk_chan_type"/>
|
||||
<label name="asterisk_server"/>
|
||||
<field name="asterisk_server"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
Loading…
Reference in New Issue