[ADD] sms: sms gateway using iap
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
||||
@@ -0,0 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': "Calendar SMS",
|
||||
'summary': 'Send text messages as event reminders',
|
||||
'description': "Send text messages as event reminders",
|
||||
'category': 'Hidden',
|
||||
'version': '1.0',
|
||||
'depends': ['calendar', 'sms'],
|
||||
'data': [
|
||||
'views/calendar_views.xml',
|
||||
],
|
||||
'application': False,
|
||||
'auto_install': True,
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * calendar_sms
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 10.saas~18+e\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-09-20 11:33+0000\n"
|
||||
"PO-Revision-Date: 2017-09-20 11:33+0000\n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: calendar_sms
|
||||
#: model:ir.model,name:calendar_sms.model_calendar_event
|
||||
msgid "Event"
|
||||
msgstr ""
|
||||
|
||||
#. module: calendar_sms
|
||||
#: model:ir.model,name:calendar_sms.model_calendar_alarm
|
||||
msgid "Event alarm"
|
||||
msgstr ""
|
||||
|
||||
#. module: calendar_sms
|
||||
#: code:addons/calendar_sms/models/calendar.py:23
|
||||
#, python-format
|
||||
msgid "Event reminder: %s on %s."
|
||||
msgstr ""
|
||||
|
||||
#. module: calendar_sms
|
||||
#: code:addons/calendar_sms/models/calendar.py:24
|
||||
#, python-format
|
||||
msgid "SMS text message reminder sent !"
|
||||
msgstr ""
|
||||
|
||||
#. module: calendar_sms
|
||||
#: model:ir.actions.act_window,name:calendar_sms.sms_message_send_action_mutli
|
||||
msgid "Send SMS to attendees"
|
||||
msgstr ""
|
||||
|
||||
#. module: calendar_sms
|
||||
#: model:ir.model,name:calendar_sms.model_calendar_alarm_manager
|
||||
msgid "calendar.alarm_manager"
|
||||
msgstr ""
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import calendar
|
||||
@@ -0,0 +1,75 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CalendarEvent(models.Model):
|
||||
_inherit = 'calendar.event'
|
||||
|
||||
def _get_default_sms_recipients(self):
|
||||
""" Method overriden from mail.thread (defined in the sms module).
|
||||
SMS text messages will be sent to attendees that haven't declined the event(s).
|
||||
"""
|
||||
return self.mapped('attendee_ids').filtered(lambda att: att.state != 'declined').mapped('partner_id')
|
||||
|
||||
def _do_sms_reminder(self):
|
||||
""" Send an SMS text reminder to attendees that haven't declined the event """
|
||||
for event in self:
|
||||
sms_msg = _("Event reminder: %s on %s.") % (event.name, event.start_datetime or event.start_date)
|
||||
note_msg = _('SMS text message reminder sent !')
|
||||
event.message_post_send_sms(sms_msg, note_msg=note_msg)
|
||||
|
||||
|
||||
class CalendarAlarm(models.Model):
|
||||
_inherit = 'calendar.alarm'
|
||||
|
||||
type = fields.Selection(selection_add=[('sms', 'SMS Text Message')])
|
||||
|
||||
|
||||
class AlarmManager(models.AbstractModel):
|
||||
_inherit = 'calendar.alarm_manager'
|
||||
|
||||
@api.model
|
||||
def get_next_mail(self):
|
||||
""" Cron method, overriden here to send SMS reminders as well
|
||||
"""
|
||||
result = super(AlarmManager, self).get_next_mail()
|
||||
now = fields.Datetime.now()
|
||||
last_sms_cron = self.env['ir.config_parameter'].get_param('calendar_sms.last_sms_cron', default=now)
|
||||
cron = self.env['ir.model.data'].get_object('calendar', 'ir_cron_scheduler_alarm')
|
||||
|
||||
interval_to_second = {
|
||||
"weeks": 7 * 24 * 60 * 60,
|
||||
"days": 24 * 60 * 60,
|
||||
"hours": 60 * 60,
|
||||
"minutes": 60,
|
||||
"seconds": 1
|
||||
}
|
||||
|
||||
cron_interval = cron.interval_number * interval_to_second[cron.interval_type]
|
||||
events_data = self.get_next_potential_limit_alarm('sms', seconds=cron_interval)
|
||||
|
||||
for event in self.env['calendar.event'].browse(events_data):
|
||||
max_delta = events_data[event.id]['max_duration']
|
||||
|
||||
if event.recurrency:
|
||||
found = False
|
||||
for event_start in event._get_recurrent_date_by_event():
|
||||
event_start = event_start.replace(tzinfo=None)
|
||||
last_found = self.do_check_alarm_for_one_date(event_start, event, max_delta, 0, 'sms', after=last_sms_cron, missing=True)
|
||||
for alert in last_found:
|
||||
event.browse(alert['event_id'])._do_sms_reminder()
|
||||
found = True
|
||||
if found and not last_found: # if the precedent event had an alarm but not this one, we can stop the search for this event
|
||||
break
|
||||
else:
|
||||
event_start = fields.Datetime.from_string(event.start)
|
||||
for alert in self.do_check_alarm_for_one_date(event_start, event, max_delta, 0, 'sms', after=last_sms_cron, missing=True):
|
||||
event.browse(alert['event_id'])._do_sms_reminder()
|
||||
self.env['ir.config_parameter'].set_param('calendar_sms.last_sms_cron', now)
|
||||
return result
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Add action entry in the Action Menu for Events -->
|
||||
<act_window id="sms_message_send_action_mutli"
|
||||
name="Send SMS to attendees"
|
||||
src_model="calendar.event"
|
||||
res_model="sms.send_sms"
|
||||
view_type="form"
|
||||
view_mode="form"
|
||||
key2="client_action_multi"
|
||||
target="new"/>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
||||
from . import wizard
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
{
|
||||
'name': 'SMS gateway',
|
||||
'category': 'Tools',
|
||||
'summary': 'SMS Text Messaging',
|
||||
'description': """
|
||||
This module gives a framework for SMS text messaging
|
||||
----------------------------------------------------
|
||||
|
||||
The service is provided by the In App Purchase Odoo platform.
|
||||
""",
|
||||
'depends': ['base', 'iap', 'mail'],
|
||||
'data': [
|
||||
'wizard/send_sms_views.xml',
|
||||
'views/res_partner_views.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import mail_thread
|
||||
from . import res_partner
|
||||
from . import sms_api
|
||||
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import models, _
|
||||
|
||||
from odoo.addons.iap.models.iap import InsufficientCreditError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MailThread(models.AbstractModel):
|
||||
_inherit = 'mail.thread'
|
||||
|
||||
def _get_default_sms_recipients(self):
|
||||
""" This method will likely need to be overriden by inherited models.
|
||||
:returns partners: recordset of res.partner
|
||||
"""
|
||||
partners = self.env['res.partner']
|
||||
if hasattr(self, 'partner_id'):
|
||||
partners |= self.mapped('partner_id')
|
||||
if hasattr(self, 'partner_ids'):
|
||||
partners |= self.mapped('partner_ids')
|
||||
return partners
|
||||
|
||||
def message_post_send_sms(self, sms_message, numbers=None, partners=None, note_msg=None, log_error=False):
|
||||
""" Send an SMS text message and post an internal note in the chatter if successfull
|
||||
:param sms_message: plaintext message to send by sms
|
||||
:param partners: the numbers to send to, if none are given it will take those
|
||||
from partners or _get_default_sms_recipients
|
||||
:param partners: the recipients partners, if none are given it will take those
|
||||
from _get_default_sms_recipients, this argument
|
||||
is ignored if numbers is defined
|
||||
:param note_msg: message to log in the chatter, if none is given a default one
|
||||
containing the sms_message is logged
|
||||
"""
|
||||
if not numbers:
|
||||
if not partners:
|
||||
partners = self._get_default_sms_recipients()
|
||||
|
||||
# Collect numbers, we will consider the message to be sent if at least one number can be found
|
||||
numbers = list(set([i.mobile for i in partners if i.mobile]))
|
||||
|
||||
if numbers:
|
||||
try:
|
||||
self.env['sms.api']._send_sms(numbers, sms_message)
|
||||
mail_message = note_msg or _('SMS message sent: %s') % sms_message
|
||||
|
||||
except InsufficientCreditError as e:
|
||||
if not log_error:
|
||||
raise e
|
||||
mail_message = _('Insufficient credit, unable to send SMS message: %s') % sms_message
|
||||
else:
|
||||
mail_message = _('No mobile number defined, unable to send SMS message: %s') % sms_message
|
||||
|
||||
for thread in self:
|
||||
thread.message_post(body=mail_message)
|
||||
return False
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
|
||||
def _get_default_sms_recipients(self):
|
||||
""" Override of mail.thread method.
|
||||
SMS recipients on partners are the partners themselves.
|
||||
"""
|
||||
return self
|
||||
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.addons.iap.models import iap
|
||||
|
||||
DEFAULT_ENDPOINT = 'https://iap-sms.odoo.com'
|
||||
|
||||
|
||||
class SmsApi(models.AbstractModel):
|
||||
_name = 'sms.api'
|
||||
|
||||
@api.model
|
||||
def _send_sms(self, numbers, message):
|
||||
""" Send sms
|
||||
"""
|
||||
account = self.env['iap.account'].get('sms')
|
||||
params = {
|
||||
'account_token': account.account_token,
|
||||
'numbers': numbers,
|
||||
'message': message,
|
||||
}
|
||||
endpoint = self.env['ir.config_parameter'].sudo().get_param('sms.endpoint', DEFAULT_ENDPOINT)
|
||||
r = iap.jsonrpc(endpoint + '/iap/message_send', params=params)
|
||||
return True
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Add action entry in the Action Menu for Partners -->
|
||||
<record id="partner_form_send_sms_form_view" model="ir.ui.view">
|
||||
<field name="name">res.partner.form.send.sms</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='phone']" position="replace">
|
||||
<label for="phone"/>
|
||||
<div class="o_row">
|
||||
<field name="phone" widget="phone"/>
|
||||
<button
|
||||
type="action"
|
||||
name="%(sms.send_sms_form_action)d"
|
||||
class="btn-xs btn-link mb4 fa fa-envelope-o"
|
||||
attrs="{'invisible':[('phone', '=', False)]}"
|
||||
context="{'field_name': 'phone'}"
|
||||
/>
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='mobile']" position="replace">
|
||||
<label for="mobile"/>
|
||||
<div class="o_row">
|
||||
<field name="mobile" widget="phone"/>
|
||||
<button
|
||||
type="action"
|
||||
name="%(sms.send_sms_form_action)d"
|
||||
class="btn-xs btn-link mb4 fa fa-envelope-o"
|
||||
attrs="{'invisible':[('mobile', '=', False)]}"
|
||||
context="{'field_name': 'mobile'}"
|
||||
/>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import send_sms
|
||||
@@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import logging
|
||||
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.addons.iap.models import iap
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SendSMS(models.TransientModel):
|
||||
_name = 'sms.send_sms'
|
||||
|
||||
recipients = fields.Char('Recipients', required=True)
|
||||
message = fields.Text('Message', required=True)
|
||||
|
||||
def _get_records(self, model):
|
||||
if self.env.context.get('active_domain'):
|
||||
records = model.search(self.env.context.get('active_domain'))
|
||||
elif self.env.context.get('active_ids'):
|
||||
records = model.browse(self.env.context.get('active_ids', []))
|
||||
else:
|
||||
records = model.browse(self.env.context.get('active_id', []))
|
||||
return records
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
result = super(SendSMS, self).default_get(fields)
|
||||
|
||||
active_model = self.env.context.get('active_model')
|
||||
model = self.env[active_model]
|
||||
|
||||
records = self._get_records(model)
|
||||
if getattr(records, '_get_default_sms_recipients'):
|
||||
partners = records._get_default_sms_recipients()
|
||||
phone_numbers = []
|
||||
no_phone_partners = []
|
||||
for partner in records:
|
||||
number = partner.mobile
|
||||
if number:
|
||||
phone_numbers.append(number)
|
||||
else:
|
||||
no_phone_partners.append(partner.name)
|
||||
if len(partners) > 1:
|
||||
if no_phone_partners:
|
||||
raise UserError(_('Missing mobile number for %s.') % ', '.join(no_phone_partners))
|
||||
result['recipients'] = ', '.join(phone_numbers)
|
||||
return result
|
||||
|
||||
def action_send_sms(self):
|
||||
numbers = self.recipients.split(',')
|
||||
|
||||
active_model = self.env.context.get('active_model')
|
||||
model = self.env[active_model]
|
||||
records = self._get_records(model)
|
||||
if getattr(records, 'message_post_send_sms'):
|
||||
records.message_post_send_sms(self.message, numbers=numbers)
|
||||
else:
|
||||
self.env['sms.api']._send_sms(numbers, self.message)
|
||||
return True
|
||||
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="send_sms_view_form" model="ir.ui.view">
|
||||
<field name="name">sms_send_sms.form</field>
|
||||
<field name="model">sms.send_sms</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Send an SMS">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="recipients"/>
|
||||
<field name="message" widget="text"/>
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<group>
|
||||
<span>
|
||||
<button string="Send" type="object" class="oe_highlight" name="action_send_sms"/>
|
||||
<button string="Cancel" class="oe_link" special="cancel" />
|
||||
</span>
|
||||
</group>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="send_sms_form_action" model="ir.actions.act_window">
|
||||
<field name="name">Send SMS</field>
|
||||
<field name="res_model">sms.send_sms</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<!-- Add action entry in the Action Menu for Partners -->
|
||||
<act_window id="send_sms_action"
|
||||
name="Send SMS"
|
||||
src_model="res.partner"
|
||||
res_model="sms.send_sms"
|
||||
view_type="form"
|
||||
view_mode="form"
|
||||
key2="client_action_multi"
|
||||
target="new"/>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user