19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:31:39 +01:00
parent 5df8c07b59
commit daa394e8b0
2114 changed files with 564841 additions and 299642 deletions

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import controllers
from . import models
from . import tools
from . import wizard

View file

@ -3,8 +3,8 @@
{
'name': 'SMS gateway',
'version': '2.4',
'category': 'Hidden/Tools',
'version': '3.0',
'category': 'Sales/Sales',
'summary': 'SMS Text Messaging',
'description': """
This module gives a framework for SMS text messaging
@ -19,15 +19,19 @@ The service is provided by the In App Purchase Odoo platform.
'phone_validation'
],
'data': [
'data/iap_service_data.xml',
'data/ir_cron_data.xml',
'wizard/sms_account_code_views.xml',
'wizard/sms_account_phone_views.xml',
'wizard/sms_account_sender_views.xml',
'wizard/sms_composer_views.xml',
'wizard/sms_template_preview_views.xml',
'wizard/sms_resend_views.xml',
'wizard/sms_template_reset_views.xml',
'views/ir_actions_server_views.xml',
'views/mail_notification_views.xml',
'views/res_config_settings_views.xml',
'views/res_partner_views.xml',
'views/iap_account_views.xml',
'views/sms_sms_views.xml',
'views/sms_template_views.xml',
'security/ir.model.access.csv',
@ -40,20 +44,13 @@ The service is provided by the In App Purchase Odoo platform.
'installable': True,
'auto_install': True,
'assets': {
'mail.assets_messaging': [
'sms/static/src/models/*.js',
],
'mail.assets_discuss_public': [
'sms/static/src/components/sms_button/*',
],
'web.assets_backend': [
'sms/static/src/js/fields_phone_widget.js',
'sms/static/src/components/*/*',
'sms/static/src/**/*',
],
'web.qunit_suite_tests': [
'sms/static/tests/sms_widget_test.js',
'sms/static/tests/qunit_suite_tests/**/*.js',
'web.assets_unit_tests': [
'sms/static/tests/**/*',
],
},
'author': 'Odoo S.A.',
'license': 'LGPL-3',
}

View file

@ -0,0 +1,3 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import main

View file

@ -0,0 +1,49 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import re
from odoo import _
from odoo.exceptions import UserError
from odoo.http import Controller, request, route
_logger = logging.getLogger(__name__)
class SmsController(Controller):
@route('/sms/status', type='jsonrpc', auth='public')
def update_sms_status(self, message_statuses):
"""Receive a batch of delivery reports from IAP
:param message_statuses:
[
{
'sms_status': status0,
'uuids': [uuid00, uuid01, ...],
}, {
'sms_status': status1,
'uuids': [uuid10, uuid11, ...],
},
...
]
"""
all_uuids = []
for uuids, iap_status in ((status['uuids'], status['sms_status']) for status in message_statuses):
self._check_status_values(uuids, iap_status, message_statuses)
if sms_trackers_sudo := request.env['sms.tracker'].sudo().search([('sms_uuid', 'in', uuids)]):
if state := request.env['sms.sms'].IAP_TO_SMS_STATE_SUCCESS.get(iap_status):
sms_trackers_sudo._action_update_from_sms_state(state)
else:
sms_trackers_sudo._action_update_from_provider_error(iap_status)
all_uuids += uuids
request.env['sms.sms'].sudo().search([('uuid', 'in', all_uuids), ('to_delete', '=', False)]).to_delete = True
return 'OK'
@staticmethod
def _check_status_values(uuids, iap_status, message_statuses):
"""Basic checks to avoid unnecessary queries and allow debugging."""
if (not uuids or not iap_status or not re.match(r'^\w+$', iap_status)
or any(not re.match(r'^[0-9a-f]{32}$', uuid) for uuid in uuids)):
_logger.warning('Received ill-formatted SMS delivery report event: \n%s', message_statuses)
raise UserError(_("Bad parameters"), status=400)

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="iap_service_sms" model="iap.service">
<field name="name">SMS</field>
<field name="technical_name">sms</field>
<field name="description">Send SMS to your contacts directly from your database.</field>
<field name="unit_name">Credits</field>
<field name="integer_balance">False</field>
</record>
</data>
</odoo>

View file

@ -6,9 +6,7 @@
<field name="state">code</field>
<field name="code">model._process_queue()</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_number">24</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
</record>
</data></odoo>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,22 +1,22 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sms
#
#
# Translators:
# Qaidjohar Barbhaya, 2025
#
# Qaidjohar Barbhaya, 2023
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Project-Id-Version: Odoo Server 16.0beta\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-06 20:36+0000\n"
"POT-Creation-Date: 2023-05-16 13:49+0000\n"
"PO-Revision-Date: 2022-09-22 05:55+0000\n"
"Last-Translator: Qaidjohar Barbhaya, 2025\n"
"Last-Translator: Qaidjohar Barbhaya, 2023\n"
"Language-Team: Gujarati (https://app.transifex.com/odoo/teams/41243/gu/)\n"
"Language: gu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: gu\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: sms
@ -32,30 +32,19 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_template.py:0
#, python-format
msgid "%s (copy)"
msgstr "%s (copy)"
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_composer.py:0
#, python-format
msgid "%s invalid recipients"
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid ""
"%s out of the %s selected SMS Text Messages have successfully been resent."
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#, python-format
msgid ", fits in"
msgid "%s out of the %s selected SMS Text Messages have successfully been resent."
msgstr ""
#. module: sms
@ -65,6 +54,11 @@ msgid ""
" <span class=\"o_stat_text\">Context Action</span>"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid "<span class=\"o_stat_text\">Preview</span>"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid ""
@ -74,9 +68,7 @@ msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_preview_form
msgid ""
"<span class=\"text-warning\" attrs=\"{'invisible': [('no_record', '=', "
"False)]}\">No records</span>"
msgid "<span class=\"text-warning\" attrs=\"{'invisible': [('no_record', '=', False)]}\">No records</span>"
msgstr ""
#. module: sms
@ -105,16 +97,13 @@ msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid ""
"Add a contextual action on the related model to open a sms composer with "
"this template"
msgid "Add a contextual action on the related model to open a sms composer with this template"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/notification_group/notification_group.xml:0
#, python-format
msgid "An error occurred when sending an SMS."
#: code:addons/sms/static/src/core/notification_group_model_patch.js:0
msgid "An error occurred when sending an SMS"
msgstr ""
#. module: sms
@ -125,9 +114,7 @@ msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_reset_view_form
msgid ""
"Are you sure you want to reset these sms templates to their original "
"configuration? Changes and translations will be lost."
msgid "Are you sure you want to reset these sms templates to their original configuration? Changes and translations will be lost."
msgstr ""
#. module: sms
@ -135,11 +122,6 @@ msgstr ""
msgid "Attachment Count"
msgstr "Attachment Count"
#. module: sms
#: model:ir.model,name:sms.model_base
msgid "Base"
msgstr "Base"
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__sms_sms__failure_type__sms_blacklist
msgid "Blacklisted"
@ -170,7 +152,6 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_api.py:0
#, python-format
msgid "Buy credits."
msgstr ""
@ -332,6 +313,12 @@ msgstr ""
msgid "Email Thread"
msgstr "Email Thread"
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/phone_field/phone_field.js:0
msgid "Enable SMS"
msgstr ""
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__sms_sms__state__error
msgid "Error"
@ -354,15 +341,12 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__phone_sanitized
msgid ""
"Field used to store sanitized phone number. Helps speeding up searches and "
"comparisons."
msgid "Field used to store sanitized phone number. Helps speeding up searches and comparisons."
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_template__template_fs
msgid ""
"File from where the template originates. Used to reset broken template."
msgid "File from where the template originates. Used to reset broken template."
msgstr ""
#. module: sms
@ -378,7 +362,6 @@ msgstr "Followers (Partners)"
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_composer.py:0
#, python-format
msgid "Following numbers are not correctly encoded: %s"
msgstr ""
@ -424,6 +407,7 @@ msgstr "If checked, new messages require your attention."
#: model:ir.model.fields,help:sms.field_calendar_event__message_has_sms_error
#: model:ir.model.fields,help:sms.field_crm_team__message_has_sms_error
#: model:ir.model.fields,help:sms.field_crm_team_member__message_has_sms_error
#: model:ir.model.fields,help:sms.field_discuss_channel__message_has_sms_error
#: model:ir.model.fields,help:sms.field_fleet_vehicle__message_has_sms_error
#: model:ir.model.fields,help:sms.field_fleet_vehicle_log_contract__message_has_sms_error
#: model:ir.model.fields,help:sms.field_fleet_vehicle_log_services__message_has_sms_error
@ -435,13 +419,14 @@ msgstr "If checked, new messages require your attention."
#: model:ir.model.fields,help:sms.field_hr_job__message_has_sms_error
#: model:ir.model.fields,help:sms.field_hr_leave__message_has_sms_error
#: model:ir.model.fields,help:sms.field_hr_leave_allocation__message_has_sms_error
#: model:ir.model.fields,help:sms.field_hr_plan_employee_activity__message_has_sms_error
#: model:ir.model.fields,help:sms.field_loyalty_card__message_has_sms_error
#: model:ir.model.fields,help:sms.field_lunch_supplier__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_blacklist__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_channel__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread_blacklist__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread_cc__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread_main_attachment__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread_phone__message_has_sms_error
#: model:ir.model.fields,help:sms.field_maintenance_equipment__message_has_sms_error
#: model:ir.model.fields,help:sms.field_maintenance_equipment_category__message_has_sms_error
@ -450,6 +435,7 @@ msgstr "If checked, new messages require your attention."
#: model:ir.model.fields,help:sms.field_phone_blacklist__message_has_sms_error
#: model:ir.model.fields,help:sms.field_product_product__message_has_sms_error
#: model:ir.model.fields,help:sms.field_product_template__message_has_sms_error
#: model:ir.model.fields,help:sms.field_rating_mixin__message_has_sms_error
#: model:ir.model.fields,help:sms.field_res_partner__message_has_error
#: model:ir.model.fields,help:sms.field_res_partner__message_has_sms_error
#: model:ir.model.fields,help:sms.field_res_users__message_has_sms_error
@ -458,9 +444,7 @@ msgstr "If checked, some messages have a delivery error."
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__phone_sanitized_blacklisted
msgid ""
"If the sanitized phone number is on the blacklist, the contact won't receive"
" mass mailing sms anymore, from any list"
msgid "If the sanitized phone number is on the blacklist, the contact won't receive mass mailing sms anymore, from any list"
msgstr ""
#. module: sms
@ -475,18 +459,12 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__mobile_blacklisted
msgid ""
"Indicates if a blacklisted sanitized phone number is a mobile number. Helps "
"distinguish which number is blacklisted when there is both a "
"mobile and phone field in a model."
msgid "Indicates if a blacklisted sanitized phone number is a mobile number. Helps distinguish which number is blacklisted when there is both a mobile and phone field in a model."
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__phone_blacklisted
msgid ""
"Indicates if a blacklisted sanitized phone number is a phone number. Helps "
"distinguish which number is blacklisted when there is both a "
"mobile and phone field in a model."
msgid "Indicates if a blacklisted sanitized phone number is a phone number. Helps distinguish which number is blacklisted when there is both a mobile and phone field in a model."
msgstr ""
#. module: sms
@ -503,7 +481,6 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_composer.py:0
#, python-format
msgid "Invalid recipient number. Please update it."
msgstr ""
@ -527,17 +504,6 @@ msgstr ""
msgid "Language"
msgstr "Language"
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer____last_update
#: model:ir.model.fields,field_description:sms.field_sms_resend____last_update
#: model:ir.model.fields,field_description:sms.field_sms_resend_recipient____last_update
#: model:ir.model.fields,field_description:sms.field_sms_sms____last_update
#: model:ir.model.fields,field_description:sms.field_sms_template____last_update
#: model:ir.model.fields,field_description:sms.field_sms_template_preview____last_update
#: model:ir.model.fields,field_description:sms.field_sms_template_reset____last_update
msgid "Last Modified on"
msgstr "Last Modified on"
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__write_uid
#: model:ir.model.fields,field_description:sms.field_sms_resend__write_uid
@ -570,18 +536,13 @@ msgstr ""
msgid "Mail Thread SMS"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_main_attachment_id
msgid "Main Attachment"
msgstr "Main Attachment"
#. module: sms
#: model:ir.model,name:sms.model_mail_message
#: model:ir.model.fields,field_description:sms.field_sms_composer__body
#: model:ir.model.fields,field_description:sms.field_sms_resend__mail_message_id
#: model_terms:ir.ui.view,arch_db:sms.sms_tsms_view_form
msgid "Message"
msgstr ""
msgstr "સંદેશ"
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_has_error
@ -596,12 +557,8 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_mail_mail__message_type
#: model:ir.model.fields,help:sms.field_mail_message__message_type
msgid ""
"Message type: email for email message, notification for system message, "
"comment for other messages such as user replies"
msgstr ""
"Message type: email for email message, notification for system message, "
"comment for other messages such as user replies"
msgid "Message type: email for email message, notification for system message, comment for other messages such as user replies"
msgstr "Message type: email for email message, notification for system message, comment for other messages such as user replies"
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_ids
@ -661,8 +618,8 @@ msgstr "Number of errors"
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__message_needaction_counter
msgid "Number of messages requiring action"
msgstr ""
msgid "Number of messages which requires an action"
msgstr "Number of messages which requires an action"
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__message_has_error_counter
@ -671,9 +628,7 @@ msgstr "Number of messages with delivery error"
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_composer__res_ids_count
msgid ""
"Number of recipients that will receive the SMS if sent in mass mode, without"
" applying the Active Domain value"
msgid "Number of recipients that will receive the SMS if sent in mass mode, without applying the Active Domain value"
msgstr ""
#. module: sms
@ -683,16 +638,8 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_template__lang
msgid ""
"Optional translation language (ISO code) to select when sending out an "
"email. If not set, the english version will be used. This should usually be "
"a placeholder expression that provides the appropriate language, e.g. {{ "
"object.partner_id.lang }}."
msgstr ""
"Optional translation language (ISO code) to select when sending out an "
"email. If not set, the english version will be used. This should usually be "
"a placeholder expression that provides the appropriate language, e.g. {{ "
"object.partner_id.lang }}."
msgid "Optional translation language (ISO code) to select when sending out an email. If not set, the english version will be used. This should usually be a placeholder expression that provides the appropriate language, e.g. {{ object.partner_id.lang }}."
msgstr "Optional translation language (ISO code) to select when sending out an email. If not set, the english version will be used. This should usually be a placeholder expression that provides the appropriate language, e.g. {{ object.partner_id.lang }}."
#. module: sms
#: model:ir.model,name:sms.model_sms_sms
@ -721,9 +668,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_composer__recipient_single_number_itf
msgid ""
"Phone number of the recipient. If changed, it will be recorded on "
"recipient's profile."
msgid "Phone number of the recipient. If changed, it will be recorded on recipient's profile."
msgstr ""
#. module: sms
@ -746,11 +691,6 @@ msgstr ""
msgid "Post on a document"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid "Preview"
msgstr "Preview"
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_preview_form
msgid "Preview of"
@ -766,6 +706,11 @@ msgstr ""
msgid "Put in queue"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__rating_ids
msgid "Ratings"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.mail_resend_message_view_form
msgid "Reason"
@ -851,8 +796,7 @@ msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_button/sms_button.xml:0
#: code:addons/sms/static/src/models/message_view.js:0
#: code:addons/sms/static/src/models/message_view.js:0
#: code:addons/sms/static/src/core/notification_model_patch.js:0
#: model:ir.actions.act_window,name:sms.sms_sms_action
#: model:ir.model.fields,field_description:sms.field_mail_notification__sms_id
#: model:ir.model.fields.selection,name:sms.selection__ir_actions_server__sms_method__sms
@ -860,14 +804,12 @@ msgstr ""
#: model:ir.model.fields.selection,name:sms.selection__mail_notification__notification_type__sms
#: model:ir.ui.menu,name:sms.sms_sms_menu
#: model_terms:ir.ui.view,arch_db:sms.sms_tsms_view_form
#, python-format
msgid "SMS"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#, python-format
msgid "SMS ("
msgstr ""
@ -881,6 +823,7 @@ msgstr ""
#: model:ir.model.fields,field_description:sms.field_calendar_event__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_crm_team__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_crm_team_member__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_discuss_channel__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_fleet_vehicle__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_fleet_vehicle_log_contract__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_fleet_vehicle_log_services__message_has_sms_error
@ -892,13 +835,14 @@ msgstr ""
#: model:ir.model.fields,field_description:sms.field_hr_job__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_hr_leave__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_hr_leave_allocation__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_hr_plan_employee_activity__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_loyalty_card__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_lunch_supplier__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_blacklist__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_channel__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread_blacklist__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread_cc__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread_main_attachment__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread_phone__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_maintenance_equipment__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_maintenance_equipment_category__message_has_sms_error
@ -907,6 +851,7 @@ msgstr ""
#: model:ir.model.fields,field_description:sms.field_phone_blacklist__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_product_product__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_product_template__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_rating_mixin__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_res_partner__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_res_users__message_has_sms_error
msgid "SMS Delivery error"
@ -914,8 +859,7 @@ msgstr "SMS Delivery error"
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/models/notification_group.js:0
#, python-format
#: code:addons/sms/static/src/messaging_menu/messaging_menu_patch.js:0
msgid "SMS Failures"
msgstr ""
@ -932,8 +876,6 @@ msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#, python-format
msgid "SMS Pricing"
msgstr ""
@ -977,7 +919,6 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_template_reset.py:0
#, python-format
msgid "SMS Templates have been reset"
msgstr ""
@ -988,7 +929,6 @@ msgstr ""
#. module: sms
#: model:ir.actions.server,name:sms.ir_cron_sms_scheduler_action_ir_actions_server
#: model:ir.cron,cron_name:sms.ir_cron_sms_scheduler_action
msgid "SMS: SMS Queue Manager"
msgstr ""
@ -1024,20 +964,16 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_template.py:0
#, python-format
msgid "Send SMS (%s)"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_button/sms_button.js:0
#: code:addons/sms/static/src/js/fields_phone_widget.js:0
#: code:addons/sms/static/src/js/fields_phone_widget.js:0
#: model:ir.actions.act_window,name:sms.res_partner_act_window_sms_composer_multi
#: model:ir.actions.act_window,name:sms.res_partner_act_window_sms_composer_single
#: model:ir.actions.act_window,name:sms.sms_composer_action_form
#: model:ir.model.fields.selection,name:sms.selection__ir_actions_server__state__sms
#, python-format
msgid "Send SMS Text Message"
msgstr ""
@ -1086,8 +1022,7 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/ir_actions_server.py:0
#, python-format
msgid "Sending SMS can only be done on a not transient mail.thread model"
msgid "Sending SMS can only be done on a mail.thread or a transient model"
msgstr ""
#. module: sms
@ -1118,9 +1053,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_template__sidebar_action_id
msgid ""
"Sidebar action to make this template available on records of the related "
"document model"
msgid "Sidebar action to make this template available on records of the related document model"
msgstr ""
#. module: sms
@ -1146,7 +1079,6 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid "Success"
msgstr ""
@ -1178,14 +1110,12 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid "The SMS Text Messages could not be resent."
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_api.py:0
#, python-format
msgid "The number you're trying to reach is not correctly formatted."
msgstr ""
@ -1198,14 +1128,12 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid "There are no SMS Text Messages to resend."
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.res_partner_view_form
msgid ""
"This phone number is blacklisted for SMS Marketing. Click to unblacklist."
msgid "This phone number is blacklisted for SMS Marketing. Click to unblacklist."
msgstr ""
#. module: sms
@ -1259,7 +1187,6 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid "Warning"
msgstr "Warning"
@ -1287,37 +1214,31 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_resend.py:0
#, python-format
msgid "You do not have access to the message and/or related document."
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_api.py:0
#, python-format
msgid "You don't have an eligible IAP account."
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_api.py:0
#, python-format
msgid "You don't have enough credits on your IAP account."
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.js:0
#, python-format
msgid ""
"Your SMS Text Message must include at least one non-whitespace character"
msgid "Your SMS Text Message must include at least one non-whitespace character"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#, python-format
msgid "characters"
msgid "characters, fits in"
msgstr ""
#. module: sms
@ -1347,9 +1268,7 @@ msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_composer_view_form
msgid ""
"recipients have an invalid phone number and will not receive this text "
"message."
msgid "recipients have an invalid phone number and will not receive this text message."
msgstr ""
#. module: sms

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,23 +1,18 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sms
#
# Translators:
# Kristófer Arnþórsson, 2024
# jonasyngvi, 2025
#
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Project-Id-Version: Odoo Server 16.0beta\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-06 20:36+0000\n"
"POT-Creation-Date: 2023-05-16 13:49+0000\n"
"PO-Revision-Date: 2022-09-22 05:55+0000\n"
"Last-Translator: jonasyngvi, 2025\n"
"Language-Team: Icelandic (https://app.transifex.com/odoo/teams/41243/is/)\n"
"Language-Team: Icelandic (https://www.transifex.com/odoo/teams/41243/is/)\n"
"Language: is\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: is\n"
"Plural-Forms: nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);\n"
#. module: sms
@ -33,32 +28,19 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_template.py:0
#, python-format
msgid "%s (copy)"
msgstr ""
"%s (afrit)\n"
" "
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_composer.py:0
#, python-format
msgid "%s invalid recipients"
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid ""
"%s out of the %s selected SMS Text Messages have successfully been resent."
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#, python-format
msgid ", fits in"
msgid "%s out of the %s selected SMS Text Messages have successfully been resent."
msgstr ""
#. module: sms
@ -68,6 +50,11 @@ msgid ""
" <span class=\"o_stat_text\">Context Action</span>"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid "<span class=\"o_stat_text\">Preview</span>"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid ""
@ -77,9 +64,7 @@ msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_preview_form
msgid ""
"<span class=\"text-warning\" attrs=\"{'invisible': [('no_record', '=', "
"False)]}\">No records</span>"
msgid "<span class=\"text-warning\" attrs=\"{'invisible': [('no_record', '=', False)]}\">No records</span>"
msgstr ""
#. module: sms
@ -97,7 +82,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_needaction
msgid "Action Needed"
msgstr "Aðgerða þörf"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_base_automation__state
@ -108,16 +93,13 @@ msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid ""
"Add a contextual action on the related model to open a sms composer with "
"this template"
msgid "Add a contextual action on the related model to open a sms composer with this template"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/notification_group/notification_group.xml:0
#, python-format
msgid "An error occurred when sending an SMS."
#: code:addons/sms/static/src/core/notification_group_model_patch.js:0
msgid "An error occurred when sending an SMS"
msgstr ""
#. module: sms
@ -128,20 +110,13 @@ msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_reset_view_form
msgid ""
"Are you sure you want to reset these sms templates to their original "
"configuration? Changes and translations will be lost."
msgid "Are you sure you want to reset these sms templates to their original configuration? Changes and translations will be lost."
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_attachment_count
msgid "Attachment Count"
msgstr "Fjöldi viðhengja"
#. module: sms
#: model:ir.model,name:sms.model_base
msgid "Base"
msgstr "Grunnur"
msgstr ""
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__sms_sms__failure_type__sms_blacklist
@ -173,7 +148,6 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_api.py:0
#, python-format
msgid "Buy credits."
msgstr ""
@ -192,12 +166,12 @@ msgstr ""
#: model_terms:ir.ui.view,arch_db:sms.sms_template_reset_view_form
#: model_terms:ir.ui.view,arch_db:sms.sms_tsms_view_form
msgid "Cancel"
msgstr "Eyða"
msgstr "Hætta við"
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__sms_sms__state__canceled
msgid "Canceled"
msgstr "Hætt við"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_preview_form
@ -224,7 +198,7 @@ msgstr ""
#: model_terms:ir.ui.view,arch_db:sms.mail_resend_message_view_form
#: model_terms:ir.ui.view,arch_db:sms.sms_composer_view_form
msgid "Close"
msgstr "Loka"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__composition_mode
@ -235,12 +209,12 @@ msgstr ""
#: model:ir.model,name:sms.model_res_partner
#: model_terms:ir.ui.view,arch_db:sms.sms_tsms_view_form
msgid "Contact"
msgstr "Hafa samband"
msgstr "Tengiliður"
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid "Content"
msgstr "Innihald"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__create_uid
@ -262,12 +236,12 @@ msgstr "Búið til af"
#: model:ir.model.fields,field_description:sms.field_sms_template_preview__create_date
#: model:ir.model.fields,field_description:sms.field_sms_template_reset__create_date
msgid "Created on"
msgstr "Búið til þann"
msgstr "Stofnað þann"
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_sms__partner_id
msgid "Customer"
msgstr "Viðskiptavinur"
msgstr ""
#. module: sms
#: model:sms.template,name:sms.sms_template_demo_0
@ -282,7 +256,7 @@ msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_preview_form
msgid "Discard"
msgstr "Hætta við"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__display_name
@ -293,7 +267,7 @@ msgstr "Hætta við"
#: model:ir.model.fields,field_description:sms.field_sms_template_preview__display_name
#: model:ir.model.fields,field_description:sms.field_sms_template_reset__display_name
msgid "Display Name"
msgstr "Birtingarnafn"
msgstr "Nafn"
#. module: sms
#: model:ir.model,name:sms.model_mail_followers
@ -333,12 +307,18 @@ msgstr ""
#. module: sms
#: model:ir.model,name:sms.model_mail_thread
msgid "Email Thread"
msgstr "Email Thread"
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/phone_field/phone_field.js:0
msgid "Enable SMS"
msgstr ""
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__sms_sms__state__error
msgid "Error"
msgstr "Villa"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_resend_recipient__failure_type
@ -357,31 +337,27 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__phone_sanitized
msgid ""
"Field used to store sanitized phone number. Helps speeding up searches and "
"comparisons."
msgid "Field used to store sanitized phone number. Helps speeding up searches and comparisons."
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_template__template_fs
msgid ""
"File from where the template originates. Used to reset broken template."
msgid "File from where the template originates. Used to reset broken template."
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_follower_ids
msgid "Followers"
msgstr "Fylgjendur"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_partner_ids
msgid "Followers (Partners)"
msgstr "Fylgjendur (samstarfsaðilar)"
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_composer.py:0
#, python-format
msgid "Following numbers are not correctly encoded: %s"
msgstr ""
@ -393,7 +369,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__has_message
msgid "Has Message"
msgstr "Hefur skilaboð"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_mail_mail__has_sms_error
@ -415,18 +391,19 @@ msgstr ""
#: model:ir.model.fields,field_description:sms.field_sms_template_preview__id
#: model:ir.model.fields,field_description:sms.field_sms_template_reset__id
msgid "ID"
msgstr "Auðkenni (ID)"
msgstr "Auðkenni"
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__message_needaction
msgid "If checked, new messages require your attention."
msgstr "Ef hakað er við krefjast ný skilaboð athygli þinnar."
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_account_analytic_account__message_has_sms_error
#: model:ir.model.fields,help:sms.field_calendar_event__message_has_sms_error
#: model:ir.model.fields,help:sms.field_crm_team__message_has_sms_error
#: model:ir.model.fields,help:sms.field_crm_team_member__message_has_sms_error
#: model:ir.model.fields,help:sms.field_discuss_channel__message_has_sms_error
#: model:ir.model.fields,help:sms.field_fleet_vehicle__message_has_sms_error
#: model:ir.model.fields,help:sms.field_fleet_vehicle_log_contract__message_has_sms_error
#: model:ir.model.fields,help:sms.field_fleet_vehicle_log_services__message_has_sms_error
@ -438,13 +415,14 @@ msgstr "Ef hakað er við krefjast ný skilaboð athygli þinnar."
#: model:ir.model.fields,help:sms.field_hr_job__message_has_sms_error
#: model:ir.model.fields,help:sms.field_hr_leave__message_has_sms_error
#: model:ir.model.fields,help:sms.field_hr_leave_allocation__message_has_sms_error
#: model:ir.model.fields,help:sms.field_hr_plan_employee_activity__message_has_sms_error
#: model:ir.model.fields,help:sms.field_loyalty_card__message_has_sms_error
#: model:ir.model.fields,help:sms.field_lunch_supplier__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_blacklist__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_channel__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread_blacklist__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread_cc__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread_main_attachment__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread_phone__message_has_sms_error
#: model:ir.model.fields,help:sms.field_maintenance_equipment__message_has_sms_error
#: model:ir.model.fields,help:sms.field_maintenance_equipment_category__message_has_sms_error
@ -453,17 +431,16 @@ msgstr "Ef hakað er við krefjast ný skilaboð athygli þinnar."
#: model:ir.model.fields,help:sms.field_phone_blacklist__message_has_sms_error
#: model:ir.model.fields,help:sms.field_product_product__message_has_sms_error
#: model:ir.model.fields,help:sms.field_product_template__message_has_sms_error
#: model:ir.model.fields,help:sms.field_rating_mixin__message_has_sms_error
#: model:ir.model.fields,help:sms.field_res_partner__message_has_error
#: model:ir.model.fields,help:sms.field_res_partner__message_has_sms_error
#: model:ir.model.fields,help:sms.field_res_users__message_has_sms_error
msgid "If checked, some messages have a delivery error."
msgstr "Ef hakað er við hafa sum skilaboð sendingarvillu."
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__phone_sanitized_blacklisted
msgid ""
"If the sanitized phone number is on the blacklist, the contact won't receive"
" mass mailing sms anymore, from any list"
msgid "If the sanitized phone number is on the blacklist, the contact won't receive mass mailing sms anymore, from any list"
msgstr ""
#. module: sms
@ -478,18 +455,12 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__mobile_blacklisted
msgid ""
"Indicates if a blacklisted sanitized phone number is a mobile number. Helps "
"distinguish which number is blacklisted when there is both a "
"mobile and phone field in a model."
msgid "Indicates if a blacklisted sanitized phone number is a mobile number. Helps distinguish which number is blacklisted when there is both a mobile and phone field in a model."
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__phone_blacklisted
msgid ""
"Indicates if a blacklisted sanitized phone number is a phone number. Helps "
"distinguish which number is blacklisted when there is both a "
"mobile and phone field in a model."
msgid "Indicates if a blacklisted sanitized phone number is a phone number. Helps distinguish which number is blacklisted when there is both a mobile and phone field in a model."
msgstr ""
#. module: sms
@ -506,14 +477,13 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_composer.py:0
#, python-format
msgid "Invalid recipient number. Please update it."
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_is_follower
msgid "Is Follower"
msgstr "Er fylgjandi"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__recipient_single_valid
@ -530,17 +500,6 @@ msgstr ""
msgid "Language"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer____last_update
#: model:ir.model.fields,field_description:sms.field_sms_resend____last_update
#: model:ir.model.fields,field_description:sms.field_sms_resend_recipient____last_update
#: model:ir.model.fields,field_description:sms.field_sms_sms____last_update
#: model:ir.model.fields,field_description:sms.field_sms_template____last_update
#: model:ir.model.fields,field_description:sms.field_sms_template_preview____last_update
#: model:ir.model.fields,field_description:sms.field_sms_template_reset____last_update
msgid "Last Modified on"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__write_uid
#: model:ir.model.fields,field_description:sms.field_sms_resend__write_uid
@ -573,11 +532,6 @@ msgstr ""
msgid "Mail Thread SMS"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_main_attachment_id
msgid "Main Attachment"
msgstr "Aðal viðhengi"
#. module: sms
#: model:ir.model,name:sms.model_mail_message
#: model:ir.model.fields,field_description:sms.field_sms_composer__body
@ -589,7 +543,7 @@ msgstr "Skilaboð"
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_has_error
msgid "Message Delivery error"
msgstr "Villa við afhendingu skilaboða"
msgstr ""
#. module: sms
#: model:ir.model,name:sms.model_mail_notification
@ -599,15 +553,13 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_mail_mail__message_type
#: model:ir.model.fields,help:sms.field_mail_message__message_type
msgid ""
"Message type: email for email message, notification for system message, "
"comment for other messages such as user replies"
msgid "Message type: email for email message, notification for system message, comment for other messages such as user replies"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_ids
msgid "Messages"
msgstr "Skilaboð"
msgstr ""
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__mail_notification__failure_type__sms_number_missing
@ -623,7 +575,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_template__name
msgid "Name"
msgstr "Nafn"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_template_preview__no_record
@ -653,28 +605,26 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_needaction_counter
msgid "Number of Actions"
msgstr "Fjöldi aðgerða"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_has_error_counter
msgid "Number of errors"
msgstr "Fjöldi villna"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__message_needaction_counter
msgid "Number of messages requiring action"
msgstr "Fjöldi skeyta sem krefjast aðgerða"
msgid "Number of messages which requires an action"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__message_has_error_counter
msgid "Number of messages with delivery error"
msgstr "Fjöldi skeyta með sendingarvillu"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_composer__res_ids_count
msgid ""
"Number of recipients that will receive the SMS if sent in mass mode, without"
" applying the Active Domain value"
msgid "Number of recipients that will receive the SMS if sent in mass mode, without applying the Active Domain value"
msgstr ""
#. module: sms
@ -684,11 +634,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_template__lang
msgid ""
"Optional translation language (ISO code) to select when sending out an "
"email. If not set, the english version will be used. This should usually be "
"a placeholder expression that provides the appropriate language, e.g. {{ "
"object.partner_id.lang }}."
msgid "Optional translation language (ISO code) to select when sending out an email. If not set, the english version will be used. This should usually be a placeholder expression that provides the appropriate language, e.g. {{ object.partner_id.lang }}."
msgstr ""
#. module: sms
@ -699,7 +645,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_resend_recipient__partner_id
msgid "Partner"
msgstr "Félagi"
msgstr ""
#. module: sms
#: model:ir.model,name:sms.model_mail_thread_phone
@ -714,13 +660,11 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_resend_recipient__sms_number
msgid "Phone Number"
msgstr "Símanúmer"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_composer__recipient_single_number_itf
msgid ""
"Phone number of the recipient. If changed, it will be recorded on "
"recipient's profile."
msgid "Phone number of the recipient. If changed, it will be recorded on recipient's profile."
msgstr ""
#. module: sms
@ -743,11 +687,6 @@ msgstr ""
msgid "Post on a document"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid "Preview"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_preview_form
msgid "Preview of"
@ -763,10 +702,15 @@ msgstr ""
msgid "Put in queue"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__rating_ids
msgid "Ratings"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.mail_resend_message_view_form
msgid "Reason"
msgstr "Ástæða"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.mail_resend_message_view_form
@ -787,7 +731,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_resend__recipient_ids
msgid "Recipients"
msgstr ""
msgstr "Viðtakendurf"
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__numbers
@ -843,13 +787,12 @@ msgstr ""
#: model_terms:ir.ui.view,arch_db:sms.sms_sms_view_tree
#: model_terms:ir.ui.view,arch_db:sms.sms_tsms_view_form
msgid "Retry"
msgstr "Reyna"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_button/sms_button.xml:0
#: code:addons/sms/static/src/models/message_view.js:0
#: code:addons/sms/static/src/models/message_view.js:0
#: code:addons/sms/static/src/core/notification_model_patch.js:0
#: model:ir.actions.act_window,name:sms.sms_sms_action
#: model:ir.model.fields,field_description:sms.field_mail_notification__sms_id
#: model:ir.model.fields.selection,name:sms.selection__ir_actions_server__sms_method__sms
@ -857,14 +800,12 @@ msgstr "Reyna"
#: model:ir.model.fields.selection,name:sms.selection__mail_notification__notification_type__sms
#: model:ir.ui.menu,name:sms.sms_sms_menu
#: model_terms:ir.ui.view,arch_db:sms.sms_tsms_view_form
#, python-format
msgid "SMS"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#, python-format
msgid "SMS ("
msgstr ""
@ -878,6 +819,7 @@ msgstr ""
#: model:ir.model.fields,field_description:sms.field_calendar_event__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_crm_team__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_crm_team_member__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_discuss_channel__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_fleet_vehicle__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_fleet_vehicle_log_contract__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_fleet_vehicle_log_services__message_has_sms_error
@ -889,13 +831,14 @@ msgstr ""
#: model:ir.model.fields,field_description:sms.field_hr_job__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_hr_leave__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_hr_leave_allocation__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_hr_plan_employee_activity__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_loyalty_card__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_lunch_supplier__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_blacklist__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_channel__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread_blacklist__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread_cc__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread_main_attachment__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread_phone__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_maintenance_equipment__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_maintenance_equipment_category__message_has_sms_error
@ -904,15 +847,15 @@ msgstr ""
#: model:ir.model.fields,field_description:sms.field_phone_blacklist__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_product_product__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_product_template__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_rating_mixin__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_res_partner__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_res_users__message_has_sms_error
msgid "SMS Delivery error"
msgstr "SMS sendingarvilla"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/models/notification_group.js:0
#, python-format
#: code:addons/sms/static/src/messaging_menu/messaging_menu_patch.js:0
msgid "SMS Failures"
msgstr ""
@ -929,8 +872,6 @@ msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#, python-format
msgid "SMS Pricing"
msgstr ""
@ -974,7 +915,6 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_template_reset.py:0
#, python-format
msgid "SMS Templates have been reset"
msgstr ""
@ -985,7 +925,6 @@ msgstr ""
#. module: sms
#: model:ir.actions.server,name:sms.ir_cron_sms_scheduler_action_ir_actions_server
#: model:ir.cron,cron_name:sms.ir_cron_sms_scheduler_action
msgid "SMS: SMS Queue Manager"
msgstr ""
@ -1021,20 +960,16 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_template.py:0
#, python-format
msgid "Send SMS (%s)"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_button/sms_button.js:0
#: code:addons/sms/static/src/js/fields_phone_widget.js:0
#: code:addons/sms/static/src/js/fields_phone_widget.js:0
#: model:ir.actions.act_window,name:sms.res_partner_act_window_sms_composer_multi
#: model:ir.actions.act_window,name:sms.res_partner_act_window_sms_composer_single
#: model:ir.actions.act_window,name:sms.sms_composer_action_form
#: model:ir.model.fields.selection,name:sms.selection__ir_actions_server__state__sms
#, python-format
msgid "Send SMS Text Message"
msgstr ""
@ -1083,14 +1018,13 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/ir_actions_server.py:0
#, python-format
msgid "Sending SMS can only be done on a not transient mail.thread model"
msgid "Sending SMS can only be done on a mail.thread or a transient model"
msgstr ""
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__sms_sms__state__sent
msgid "Sent"
msgstr "Sent"
msgstr ""
#. module: sms
#: model:ir.model,name:sms.model_ir_actions_server
@ -1115,9 +1049,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_template__sidebar_action_id
msgid ""
"Sidebar action to make this template available on records of the related "
"document model"
msgid "Sidebar action to make this template available on records of the related document model"
msgstr ""
#. module: sms
@ -1143,7 +1075,6 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid "Success"
msgstr ""
@ -1175,14 +1106,12 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid "The SMS Text Messages could not be resent."
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_api.py:0
#, python-format
msgid "The number you're trying to reach is not correctly formatted."
msgstr ""
@ -1195,14 +1124,12 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid "There are no SMS Text Messages to resend."
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.res_partner_view_form
msgid ""
"This phone number is blacklisted for SMS Marketing. Click to unblacklist."
msgid "This phone number is blacklisted for SMS Marketing. Click to unblacklist."
msgstr ""
#. module: sms
@ -1256,19 +1183,18 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid "Warning"
msgstr "Aðvörun"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__website_message_ids
msgid "Website Messages"
msgstr "Skilaboð á vefsíðu"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__website_message_ids
msgid "Website communication history"
msgstr "Samskiptasaga vefsíðu"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_ir_model__is_mail_thread_sms
@ -1284,37 +1210,31 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_resend.py:0
#, python-format
msgid "You do not have access to the message and/or related document."
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_api.py:0
#, python-format
msgid "You don't have an eligible IAP account."
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_api.py:0
#, python-format
msgid "You don't have enough credits on your IAP account."
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.js:0
#, python-format
msgid ""
"Your SMS Text Message must include at least one non-whitespace character"
msgid "Your SMS Text Message must include at least one non-whitespace character"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#, python-format
msgid "characters"
msgid "characters, fits in"
msgstr ""
#. module: sms
@ -1344,9 +1264,7 @@ msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_composer_view_form
msgid ""
"recipients have an invalid phone number and will not receive this text "
"message."
msgid "recipients have an invalid phone number and will not receive this text message."
msgstr ""
#. module: sms

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,27 +1,21 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sms
#
# * sms
#
# Translators:
# Sitthykun LY <ly.sitthykun@gmail.com>, 2023
# AN Souphorn <ansouphorn@gmail.com>, 2023
# Samkhann Seang <seangsamkhann@gmail.com>, 2023
# Sengtha Chay <sengtha@gmail.com>, 2023
# Chan Nath <channath@gmail.com>, 2023
# Lux Sok <sok.lux@gmail.com>, 2025
#
# Sengtha Chay <sengtha@gmail.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-06 20:36+0000\n"
"PO-Revision-Date: 2022-09-22 05:55+0000\n"
"Last-Translator: Lux Sok <sok.lux@gmail.com>, 2025\n"
"Language-Team: Khmer (https://app.transifex.com/odoo/teams/41243/km/)\n"
"POT-Creation-Date: 2023-05-16 13:49+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Sengtha Chay <sengtha@gmail.com>, 2018\n"
"Language-Team: Khmer (https://www.transifex.com/odoo/teams/41243/km/)\n"
"Language: km\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: km\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. module: sms
@ -37,30 +31,19 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_template.py:0
#, python-format
msgid "%s (copy)"
msgstr "%s (ចម្លង)"
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_composer.py:0
#, python-format
msgid "%s invalid recipients"
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid ""
"%s out of the %s selected SMS Text Messages have successfully been resent."
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#, python-format
msgid ", fits in"
msgid "%s out of the %s selected SMS Text Messages have successfully been resent."
msgstr ""
#. module: sms
@ -70,6 +53,11 @@ msgid ""
" <span class=\"o_stat_text\">Context Action</span>"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid "<span class=\"o_stat_text\">Preview</span>"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid ""
@ -79,9 +67,7 @@ msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_preview_form
msgid ""
"<span class=\"text-warning\" attrs=\"{'invisible': [('no_record', '=', "
"False)]}\">No records</span>"
msgid "<span class=\"text-warning\" attrs=\"{'invisible': [('no_record', '=', False)]}\">No records</span>"
msgstr ""
#. module: sms
@ -99,51 +85,41 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_needaction
msgid "Action Needed"
msgstr "តម្រូវការសកម្មភាព"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_base_automation__state
#: model:ir.model.fields,field_description:sms.field_ir_actions_server__state
#: model:ir.model.fields,field_description:sms.field_ir_cron__state
msgid "Action To Do"
msgstr "តាមដានសកម្មភាព"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid ""
"Add a contextual action on the related model to open a sms composer with "
"this template"
msgid "Add a contextual action on the related model to open a sms composer with this template"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/notification_group/notification_group.xml:0
#, python-format
msgid "An error occurred when sending an SMS."
#: code:addons/sms/static/src/core/notification_group_model_patch.js:0
msgid "An error occurred when sending an SMS"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_template__model_id
#: model:ir.model.fields,field_description:sms.field_sms_template_preview__model_id
msgid "Applies to"
msgstr "ដាក់​ពាក្យ​ទៅ"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_reset_view_form
msgid ""
"Are you sure you want to reset these sms templates to their original "
"configuration? Changes and translations will be lost."
msgid "Are you sure you want to reset these sms templates to their original configuration? Changes and translations will be lost."
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_attachment_count
msgid "Attachment Count"
msgstr "ចំនួនឯកសារភ្ជាប់"
#. module: sms
#: model:ir.model,name:sms.model_base
msgid "Base"
msgstr "មូលដ្ឋាន"
msgstr ""
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__sms_sms__failure_type__sms_blacklist
@ -165,17 +141,16 @@ msgstr ""
#: model:ir.model.fields,field_description:sms.field_sms_template__body
#: model:ir.model.fields,field_description:sms.field_sms_template_preview__body
msgid "Body"
msgstr "រូបរាង"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.mail_resend_message_view_form
msgid "Buy credits"
msgstr "ការទិញដោយជំពាក់"
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_api.py:0
#, python-format
msgid "Buy credits."
msgstr ""
@ -199,7 +174,7 @@ msgstr "លុបចោល"
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__sms_sms__state__canceled
msgid "Canceled"
msgstr "លុបចោល"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_preview_form
@ -226,7 +201,7 @@ msgstr ""
#: model_terms:ir.ui.view,arch_db:sms.mail_resend_message_view_form
#: model_terms:ir.ui.view,arch_db:sms.sms_composer_view_form
msgid "Close"
msgstr "បិទ"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__composition_mode
@ -242,7 +217,7 @@ msgstr "ទំនាក់ទំនង"
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid "Content"
msgstr "មាតិកា"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__create_uid
@ -269,7 +244,7 @@ msgstr "បង្កើតនៅ"
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_sms__partner_id
msgid "Customer"
msgstr "អតិថិជន"
msgstr ""
#. module: sms
#: model:sms.template,name:sms.sms_template_demo_0
@ -284,7 +259,7 @@ msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_preview_form
msgid "Discard"
msgstr "បោះបង់"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__display_name
@ -305,7 +280,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__res_id
msgid "Document ID"
msgstr "អត្ត. ឯកសារ"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__res_ids
@ -320,12 +295,12 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__res_model
msgid "Document Model Name"
msgstr "ឈ្មោះឯកសារគំរូ"
msgstr ""
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__sms_sms__failure_type__sms_duplicate
msgid "Duplicate"
msgstr "ត្រួត"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.mail_resend_message_view_form
@ -335,17 +310,23 @@ msgstr ""
#. module: sms
#: model:ir.model,name:sms.model_mail_thread
msgid "Email Thread"
msgstr "អ៊ីម៉ែលខ្សែស្រឡាយ"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/phone_field/phone_field.js:0
msgid "Enable SMS"
msgstr ""
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__sms_sms__state__error
msgid "Error"
msgstr "កំហុស"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_resend_recipient__failure_type
msgid "Error Message"
msgstr "សារកំហុស"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_sms__failure_type
@ -355,35 +336,31 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_mail_notification__failure_type
msgid "Failure type"
msgstr "ប្រភេទបរាជ័យ។"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__phone_sanitized
msgid ""
"Field used to store sanitized phone number. Helps speeding up searches and "
"comparisons."
msgid "Field used to store sanitized phone number. Helps speeding up searches and comparisons."
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_template__template_fs
msgid ""
"File from where the template originates. Used to reset broken template."
msgid "File from where the template originates. Used to reset broken template."
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_follower_ids
msgid "Followers"
msgstr "អ្នកតាម"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_partner_ids
msgid "Followers (Partners)"
msgstr "អ្នកដើរតាម (ដៃគូរ)"
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_composer.py:0
#, python-format
msgid "Following numbers are not correctly encoded: %s"
msgstr ""
@ -417,18 +394,19 @@ msgstr ""
#: model:ir.model.fields,field_description:sms.field_sms_template_preview__id
#: model:ir.model.fields,field_description:sms.field_sms_template_reset__id
msgid "ID"
msgstr "អត្តសញ្ញាណ"
msgstr "ID"
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__message_needaction
msgid "If checked, new messages require your attention."
msgstr "ប្រសិនបើបានគូសធីកសារថ្មីទាមទារការយកចិត្តទុកដាក់របស់អ្នក។"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_account_analytic_account__message_has_sms_error
#: model:ir.model.fields,help:sms.field_calendar_event__message_has_sms_error
#: model:ir.model.fields,help:sms.field_crm_team__message_has_sms_error
#: model:ir.model.fields,help:sms.field_crm_team_member__message_has_sms_error
#: model:ir.model.fields,help:sms.field_discuss_channel__message_has_sms_error
#: model:ir.model.fields,help:sms.field_fleet_vehicle__message_has_sms_error
#: model:ir.model.fields,help:sms.field_fleet_vehicle_log_contract__message_has_sms_error
#: model:ir.model.fields,help:sms.field_fleet_vehicle_log_services__message_has_sms_error
@ -440,13 +418,14 @@ msgstr "ប្រសិនបើបានគូសធីកសារថ្មី
#: model:ir.model.fields,help:sms.field_hr_job__message_has_sms_error
#: model:ir.model.fields,help:sms.field_hr_leave__message_has_sms_error
#: model:ir.model.fields,help:sms.field_hr_leave_allocation__message_has_sms_error
#: model:ir.model.fields,help:sms.field_hr_plan_employee_activity__message_has_sms_error
#: model:ir.model.fields,help:sms.field_loyalty_card__message_has_sms_error
#: model:ir.model.fields,help:sms.field_lunch_supplier__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_blacklist__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_channel__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread_blacklist__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread_cc__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread_main_attachment__message_has_sms_error
#: model:ir.model.fields,help:sms.field_mail_thread_phone__message_has_sms_error
#: model:ir.model.fields,help:sms.field_maintenance_equipment__message_has_sms_error
#: model:ir.model.fields,help:sms.field_maintenance_equipment_category__message_has_sms_error
@ -455,17 +434,16 @@ msgstr "ប្រសិនបើបានគូសធីកសារថ្មី
#: model:ir.model.fields,help:sms.field_phone_blacklist__message_has_sms_error
#: model:ir.model.fields,help:sms.field_product_product__message_has_sms_error
#: model:ir.model.fields,help:sms.field_product_template__message_has_sms_error
#: model:ir.model.fields,help:sms.field_rating_mixin__message_has_sms_error
#: model:ir.model.fields,help:sms.field_res_partner__message_has_error
#: model:ir.model.fields,help:sms.field_res_partner__message_has_sms_error
#: model:ir.model.fields,help:sms.field_res_users__message_has_sms_error
msgid "If checked, some messages have a delivery error."
msgstr "ប្រសិនបើបានគូសធីកសារខ្លះមានកំហុសបញ្ជូន។"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__phone_sanitized_blacklisted
msgid ""
"If the sanitized phone number is on the blacklist, the contact won't receive"
" mass mailing sms anymore, from any list"
msgid "If the sanitized phone number is on the blacklist, the contact won't receive mass mailing sms anymore, from any list"
msgstr ""
#. module: sms
@ -476,22 +454,16 @@ msgstr ""
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__sms_sms__state__outgoing
msgid "In Queue"
msgstr "នៅក្នុងជួរ"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__mobile_blacklisted
msgid ""
"Indicates if a blacklisted sanitized phone number is a mobile number. Helps "
"distinguish which number is blacklisted when there is both a "
"mobile and phone field in a model."
msgid "Indicates if a blacklisted sanitized phone number is a mobile number. Helps distinguish which number is blacklisted when there is both a mobile and phone field in a model."
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__phone_blacklisted
msgid ""
"Indicates if a blacklisted sanitized phone number is a phone number. Helps "
"distinguish which number is blacklisted when there is both a "
"mobile and phone field in a model."
msgid "Indicates if a blacklisted sanitized phone number is a phone number. Helps distinguish which number is blacklisted when there is both a mobile and phone field in a model."
msgstr ""
#. module: sms
@ -508,14 +480,13 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_composer.py:0
#, python-format
msgid "Invalid recipient number. Please update it."
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_is_follower
msgid "Is Follower"
msgstr "ត្រូវតាមអ្នក"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__recipient_single_valid
@ -530,18 +501,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_template__lang
msgid "Language"
msgstr "ភាសារ"
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer____last_update
#: model:ir.model.fields,field_description:sms.field_sms_resend____last_update
#: model:ir.model.fields,field_description:sms.field_sms_resend_recipient____last_update
#: model:ir.model.fields,field_description:sms.field_sms_sms____last_update
#: model:ir.model.fields,field_description:sms.field_sms_template____last_update
#: model:ir.model.fields,field_description:sms.field_sms_template_preview____last_update
#: model:ir.model.fields,field_description:sms.field_sms_template_reset____last_update
msgid "Last Modified on"
msgstr "កាលបរិច្ឆេតកែប្រែចុងក្រោយ"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__write_uid
@ -575,43 +535,34 @@ msgstr ""
msgid "Mail Thread SMS"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_main_attachment_id
msgid "Main Attachment"
msgstr "ឯកសារភ្ជាប់សំខាន់"
#. module: sms
#: model:ir.model,name:sms.model_mail_message
#: model:ir.model.fields,field_description:sms.field_sms_composer__body
#: model:ir.model.fields,field_description:sms.field_sms_resend__mail_message_id
#: model_terms:ir.ui.view,arch_db:sms.sms_tsms_view_form
msgid "Message"
msgstr "សារ"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_has_error
msgid "Message Delivery error"
msgstr "កំហុសក្នុងការផ្ញើសារ"
msgstr ""
#. module: sms
#: model:ir.model,name:sms.model_mail_notification
msgid "Message Notifications"
msgstr "ការជូនដំណឹងសារ"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_mail_mail__message_type
#: model:ir.model.fields,help:sms.field_mail_message__message_type
msgid ""
"Message type: email for email message, notification for system message, "
"comment for other messages such as user replies"
msgid "Message type: email for email message, notification for system message, comment for other messages such as user replies"
msgstr ""
"ប្រភេទសារ: សារអេឡិចត្រូនិចសម្រាប់សារអ៊ីម៉ែល, ការជូនដំណឹងសម្រាប់ប្រព័ន្ធសារ, "
"មតិយោបល់សម្រាប់សារផ្សេងទៀតដូចជាការឆ្លើយតបរបស់អ្នកប្រើ"
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_ids
msgid "Messages"
msgstr "សារ"
msgstr ""
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__mail_notification__failure_type__sms_number_missing
@ -622,12 +573,12 @@ msgstr ""
#. module: sms
#: model:ir.model,name:sms.model_ir_model
msgid "Models"
msgstr "ម៉ូត"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_template__name
msgid "Name"
msgstr "ឈ្មោះ"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_template_preview__no_record
@ -637,7 +588,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_resend_recipient__notification_id
msgid "Notification"
msgstr "សេចក្តីជូនដំណឹង"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_mail_notification__notification_type
@ -647,7 +598,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_sms__number
msgid "Number"
msgstr "តួរលេខ"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_composer__number_field_name
@ -657,7 +608,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_needaction_counter
msgid "Number of Actions"
msgstr "ចំនួនសកម្មភាព"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__message_has_error_counter
@ -666,19 +617,17 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__message_needaction_counter
msgid "Number of messages requiring action"
msgid "Number of messages which requires an action"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__message_has_error_counter
msgid "Number of messages with delivery error"
msgstr "ចំនួនសារដែលមានកំហុសឆ្គងនៃការដឹកជញ្ជូន"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_composer__res_ids_count
msgid ""
"Number of recipients that will receive the SMS if sent in mass mode, without"
" applying the Active Domain value"
msgid "Number of recipients that will receive the SMS if sent in mass mode, without applying the Active Domain value"
msgstr ""
#. module: sms
@ -688,11 +637,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_template__lang
msgid ""
"Optional translation language (ISO code) to select when sending out an "
"email. If not set, the english version will be used. This should usually be "
"a placeholder expression that provides the appropriate language, e.g. {{ "
"object.partner_id.lang }}."
msgid "Optional translation language (ISO code) to select when sending out an email. If not set, the english version will be used. This should usually be a placeholder expression that provides the appropriate language, e.g. {{ object.partner_id.lang }}."
msgstr ""
#. module: sms
@ -703,7 +648,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_resend_recipient__partner_id
msgid "Partner"
msgstr "ដៃគូ"
msgstr ""
#. module: sms
#: model:ir.model,name:sms.model_mail_thread_phone
@ -718,13 +663,11 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_resend_recipient__sms_number
msgid "Phone Number"
msgstr "លេខទូរស័ព្ទ"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_composer__recipient_single_number_itf
msgid ""
"Phone number of the recipient. If changed, it will be recorded on "
"recipient's profile."
msgid "Phone number of the recipient. If changed, it will be recorded on recipient's profile."
msgstr ""
#. module: sms
@ -747,11 +690,6 @@ msgstr ""
msgid "Post on a document"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
msgid "Preview"
msgstr "ពីមុន"
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_preview_form
msgid "Preview of"
@ -767,16 +705,21 @@ msgstr ""
msgid "Put in queue"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__rating_ids
msgid "Ratings"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.mail_resend_message_view_form
msgid "Reason"
msgstr "ហេតុផល"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.mail_resend_message_view_form
#: model_terms:ir.ui.view,arch_db:sms.sms_composer_view_form
msgid "Recipient"
msgstr "ការទទួល"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_resend_recipient__partner_name
@ -811,7 +754,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_template__model
msgid "Related Document Model"
msgstr "គំរូឯកសារដែលទាក់ទង"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_template_view_form
@ -852,8 +795,7 @@ msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_button/sms_button.xml:0
#: code:addons/sms/static/src/models/message_view.js:0
#: code:addons/sms/static/src/models/message_view.js:0
#: code:addons/sms/static/src/core/notification_model_patch.js:0
#: model:ir.actions.act_window,name:sms.sms_sms_action
#: model:ir.model.fields,field_description:sms.field_mail_notification__sms_id
#: model:ir.model.fields.selection,name:sms.selection__ir_actions_server__sms_method__sms
@ -861,27 +803,26 @@ msgstr ""
#: model:ir.model.fields.selection,name:sms.selection__mail_notification__notification_type__sms
#: model:ir.ui.menu,name:sms.sms_sms_menu
#: model_terms:ir.ui.view,arch_db:sms.sms_tsms_view_form
#, python-format
msgid "SMS"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#, python-format
msgid "SMS ("
msgstr ""
#. module: sms
#: model:ir.model,name:sms.model_sms_api
msgid "SMS API"
msgstr "SMS API"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_account_analytic_account__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_calendar_event__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_crm_team__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_crm_team_member__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_discuss_channel__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_fleet_vehicle__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_fleet_vehicle_log_contract__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_fleet_vehicle_log_services__message_has_sms_error
@ -893,13 +834,14 @@ msgstr "SMS API"
#: model:ir.model.fields,field_description:sms.field_hr_job__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_hr_leave__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_hr_leave_allocation__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_hr_plan_employee_activity__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_loyalty_card__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_lunch_supplier__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_blacklist__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_channel__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread_blacklist__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread_cc__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread_main_attachment__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_mail_thread_phone__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_maintenance_equipment__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_maintenance_equipment_category__message_has_sms_error
@ -908,6 +850,7 @@ msgstr "SMS API"
#: model:ir.model.fields,field_description:sms.field_phone_blacklist__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_product_product__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_product_template__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_rating_mixin__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_res_partner__message_has_sms_error
#: model:ir.model.fields,field_description:sms.field_res_users__message_has_sms_error
msgid "SMS Delivery error"
@ -915,8 +858,7 @@ msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/models/notification_group.js:0
#, python-format
#: code:addons/sms/static/src/messaging_menu/messaging_menu_patch.js:0
msgid "SMS Failures"
msgstr ""
@ -933,10 +875,8 @@ msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#, python-format
msgid "SMS Pricing"
msgstr "តម្លៃ សារ SMS"
msgstr ""
#. module: sms
#: model:ir.model,name:sms.model_sms_resend
@ -978,7 +918,6 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_template_reset.py:0
#, python-format
msgid "SMS Templates have been reset"
msgstr ""
@ -989,7 +928,6 @@ msgstr ""
#. module: sms
#: model:ir.actions.server,name:sms.ir_cron_sms_scheduler_action_ir_actions_server
#: model:ir.cron,cron_name:sms.ir_cron_sms_scheduler_action
msgid "SMS: SMS Queue Manager"
msgstr ""
@ -1015,30 +953,26 @@ msgstr ""
#: model_terms:ir.ui.view,arch_db:sms.sms_sms_view_tree
#: model_terms:ir.ui.view,arch_db:sms.sms_tsms_view_form
msgid "Send Now"
msgstr "ការផ្ញើរឥឡូវ"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_composer_view_form
msgid "Send SMS"
msgstr "ផ្ញើរសារ SMS"
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_template.py:0
#, python-format
msgid "Send SMS (%s)"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_button/sms_button.js:0
#: code:addons/sms/static/src/js/fields_phone_widget.js:0
#: code:addons/sms/static/src/js/fields_phone_widget.js:0
#: model:ir.actions.act_window,name:sms.res_partner_act_window_sms_composer_multi
#: model:ir.actions.act_window,name:sms.res_partner_act_window_sms_composer_single
#: model:ir.actions.act_window,name:sms.sms_composer_action_form
#: model:ir.model.fields.selection,name:sms.selection__ir_actions_server__state__sms
#, python-format
msgid "Send SMS Text Message"
msgstr ""
@ -1055,7 +989,7 @@ msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_composer_view_form
msgid "Send an SMS"
msgstr "ផ្ញើរសារ SMSមួយ"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.ir_actions_server_view_form
@ -1087,25 +1021,24 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/ir_actions_server.py:0
#, python-format
msgid "Sending SMS can only be done on a not transient mail.thread model"
msgid "Sending SMS can only be done on a mail.thread or a transient model"
msgstr ""
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__sms_sms__state__sent
msgid "Sent"
msgstr "បានផ្ងើរ"
msgstr ""
#. module: sms
#: model:ir.model,name:sms.model_ir_actions_server
msgid "Server Action"
msgstr "សកម្មភាពម៉ាស៊ីនបម្រើ"
msgstr ""
#. module: sms
#: model:ir.model.fields.selection,name:sms.selection__mail_notification__failure_type__sms_server
#: model:ir.model.fields.selection,name:sms.selection__sms_sms__failure_type__sms_server
msgid "Server Error"
msgstr "កំហុសម៉ាស៊ីន"
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.mail_resend_message_view_form
@ -1119,9 +1052,7 @@ msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_sms_template__sidebar_action_id
msgid ""
"Sidebar action to make this template available on records of the related "
"document model"
msgid "Sidebar action to make this template available on records of the related document model"
msgstr ""
#. module: sms
@ -1147,14 +1078,13 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid "Success"
msgstr "ជោគជ័យ"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_template_reset__template_ids
msgid "Template"
msgstr "គំរូ"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_sms_template__template_fs
@ -1174,19 +1104,17 @@ msgstr ""
#. module: sms
#: model:ir.actions.act_window,name:sms.sms_template_action
msgid "Templates"
msgstr "គម្រូ"
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid "The SMS Text Messages could not be resent."
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_api.py:0
#, python-format
msgid "The number you're trying to reach is not correctly formatted."
msgstr ""
@ -1199,14 +1127,12 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid "There are no SMS Text Messages to resend."
msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.res_partner_view_form
msgid ""
"This phone number is blacklisted for SMS Marketing. Click to unblacklist."
msgid "This phone number is blacklisted for SMS Marketing. Click to unblacklist."
msgstr ""
#. module: sms
@ -1218,7 +1144,7 @@ msgstr ""
#: model:ir.model.fields,field_description:sms.field_mail_mail__message_type
#: model:ir.model.fields,field_description:sms.field_mail_message__message_type
msgid "Type"
msgstr "ប្រភេទ"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_base_automation__state
@ -1260,19 +1186,18 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_sms.py:0
#, python-format
msgid "Warning"
msgstr "ព្រមាន"
msgstr ""
#. module: sms
#: model:ir.model.fields,field_description:sms.field_res_partner__website_message_ids
msgid "Website Messages"
msgstr "សារវែបសាយ"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_res_partner__website_message_ids
msgid "Website communication history"
msgstr "ប្រវត្តិទំនាក់ទំនងវែបសាយ"
msgstr ""
#. module: sms
#: model:ir.model.fields,help:sms.field_ir_model__is_mail_thread_sms
@ -1288,37 +1213,31 @@ msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/wizard/sms_resend.py:0
#, python-format
msgid "You do not have access to the message and/or related document."
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_api.py:0
#, python-format
msgid "You don't have an eligible IAP account."
msgstr ""
#. module: sms
#. odoo-python
#: code:addons/sms/models/sms_api.py:0
#, python-format
msgid "You don't have enough credits on your IAP account."
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.js:0
#, python-format
msgid ""
"Your SMS Text Message must include at least one non-whitespace character"
msgid "Your SMS Text Message must include at least one non-whitespace character"
msgstr ""
#. module: sms
#. odoo-javascript
#: code:addons/sms/static/src/components/sms_widget/fields_sms_widget.xml:0
#, python-format
msgid "characters"
msgid "characters, fits in"
msgstr ""
#. module: sms
@ -1348,9 +1267,7 @@ msgstr ""
#. module: sms
#: model_terms:ir.ui.view,arch_db:sms.sms_composer_view_form
msgid ""
"recipients have an invalid phone number and will not receive this text "
"message."
msgid "recipients have an invalid phone number and will not receive this text message."
msgstr ""
#. module: sms

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,15 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import iap_account
from . import ir_actions_server
from . import ir_model
from . import mail_followers
from . import mail_message
from . import mail_notification
from . import mail_thread
from . import mail_thread_phone
from . import models
from . import res_partner
from . import sms_api
from . import res_company
from . import sms_sms
from . import sms_template
from . import sms_tracker

View file

@ -0,0 +1,35 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models, _
class IapAccount(models.Model):
_inherit = 'iap.account'
sender_name = fields.Char(help="This is the name that will be displayed as the sender of the SMS.", readonly=True)
def action_open_registration_wizard(self):
return {
'type': 'ir.actions.act_window',
'target': 'new',
'name': _('Register Account'),
'view_mode': 'form',
'res_model': 'sms.account.phone',
'context': {'default_account_id': self.id},
}
def action_open_sender_name_wizard(self):
return {
'type': 'ir.actions.act_window',
'target': 'new',
'name': _('Choose your sender name'),
'view_mode': 'form',
'res_model': 'sms.account.sender',
'context': {'default_account_id': self.id},
}
def _get_account_info(self, account_id, balance, information):
res = super()._get_account_info(account_id, balance, information)
if account_id.service_name == 'sms':
res['sender_name'] = information.get('sender_name')
return res

View file

@ -2,16 +2,14 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class ServerActions(models.Model):
class IrActionsServer(models.Model):
""" Add SMS option in server actions. """
_name = 'ir.actions.server'
_inherit = ['ir.actions.server']
_inherit = 'ir.actions.server'
state = fields.Selection(selection_add=[
('sms', 'Send SMS Text Message'),
('sms', 'Send SMS'), ('followers',),
], ondelete={'sms': 'cascade'})
# SMS
sms_template_id = fields.Many2one(
@ -21,11 +19,28 @@ class ServerActions(models.Model):
domain="[('model_id', '=', model_id)]",
)
sms_method = fields.Selection(
selection=[('sms', 'SMS'), ('comment', 'Post as Message'), ('note', 'Post as Note')],
string='Send as (SMS)',
selection=[('sms', 'SMS (without note)'), ('comment', 'SMS (with note)'), ('note', 'Note only')],
string='Send SMS As',
compute='_compute_sms_method',
readonly=False, store=True,
help='Choose method for SMS sending:\nSMS: mass SMS\nPost as Message: log on document\nPost as Note: mass SMS with archives')
readonly=False, store=True)
def _name_depends(self):
return [*super()._name_depends(), "sms_template_id"]
def _generate_action_name(self):
self.ensure_one()
if self.state == 'sms' and self.sms_template_id:
return _('Send %(template_name)s', template_name=self.sms_template_id.name)
return super()._generate_action_name()
@api.depends('state')
def _compute_available_model_ids(self):
mail_thread_based = self.filtered(lambda action: action.state == 'sms')
if mail_thread_based:
mail_models = self.env['ir.model'].search([('is_mail_thread', '=', True), ('transient', '=', False)])
for action in mail_thread_based:
action.available_model_ids = mail_models.ids
super(IrActionsServer, self - mail_thread_based)._compute_available_model_ids()
@api.depends('model_id', 'state')
def _compute_sms_template_id(self):
@ -45,11 +60,30 @@ class ServerActions(models.Model):
if other:
other.sms_method = 'sms'
def _check_model_coherency(self):
super()._check_model_coherency()
for action in self:
if action.state == 'sms' and (action.model_id.transient or not action.model_id.is_mail_thread):
raise ValidationError(_("Sending SMS can only be done on a not transient mail.thread model"))
@api.model
def _warning_depends(self):
return super()._warning_depends() + [
'model_id',
'state',
'sms_template_id',
]
def _get_warning_messages(self):
self.ensure_one()
warnings = super()._get_warning_messages()
if self.state == 'sms':
if self.model_id.transient or not self.model_id.is_mail_thread:
warnings.append(_("Sending SMS can only be done on a not transient mail.thread model"))
if self.sms_template_id and self.sms_template_id.model_id != self.model_id:
warnings.append(
_('SMS template model of %(action_name)s does not match action model.',
action_name=self.name
)
)
return warnings
def _run_action_sms_multi(self, eval_context=None):
# TDE CLEANME: when going to new api with server action, remove action

View file

@ -18,24 +18,23 @@ class IrModel(models.Model):
for model in self:
if model.is_mail_thread:
ModelObject = self.env[model.model]
potential_fields = ModelObject._sms_get_number_fields() + ModelObject._sms_get_partner_fields()
potential_fields = ModelObject._phone_get_number_fields() + ModelObject._mail_get_partner_fields()
if any(fname in ModelObject._fields for fname in potential_fields):
model.is_mail_thread_sms = True
continue
model.is_mail_thread_sms = False
def _search_is_mail_thread_sms(self, operator, value):
if operator != 'in':
return NotImplemented
thread_models = self.search([('is_mail_thread', '=', True)])
valid_models = self.env['ir.model']
for model in thread_models:
if model.model not in self.env:
continue
ModelObject = self.env[model.model]
potential_fields = ModelObject._sms_get_number_fields() + ModelObject._sms_get_partner_fields()
potential_fields = ModelObject._phone_get_number_fields() + ModelObject._mail_get_partner_fields()
if any(fname in ModelObject._fields for fname in potential_fields):
valid_models |= model
search_sms = (operator == '=' and value) or (operator == '!=' and not value)
if search_sms:
return [('id', 'in', valid_models.ids)]
return [('id', 'not in', valid_models.ids)]
return [('id', 'in', valid_models.ids)]

View file

@ -4,23 +4,23 @@
from odoo import models
class Followers(models.Model):
_inherit = ['mail.followers']
class MailFollowers(models.Model):
_inherit = 'mail.followers'
def _get_recipient_data(self, records, message_type, subtype_id, pids=None):
recipients_data = super()._get_recipient_data(records, message_type, subtype_id, pids=pids)
if message_type != 'sms' or not (pids or records):
return super(Followers, self)._get_recipient_data(records, message_type, subtype_id, pids=pids)
return recipients_data
if pids is None and records:
records_pids = dict(
(record.id, record._sms_get_default_partners().ids)
for record in records
(rec_id, partners.ids)
for rec_id, partners in records._mail_get_partners().items()
)
elif pids and records:
records_pids = dict((record.id, pids) for record in records)
else:
records_pids = {0: pids if pids else []}
recipients_data = super(Followers, self)._get_recipient_data(records, message_type, subtype_id, pids=pids)
for rid, rdata in recipients_data.items():
sms_pids = records_pids.get(rid) or []
for pid, pdata in rdata.items():

View file

@ -1,11 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from operator import itemgetter
from odoo import exceptions, fields, models
from odoo.tools import groupby
from odoo import fields, models
class MailMessage(models.Model):
@ -14,9 +9,9 @@ class MailMessage(models.Model):
gateway. """
_inherit = 'mail.message'
message_type = fields.Selection(selection_add=[
('sms', 'SMS')
], ondelete={'sms': lambda recs: recs.write({'message_type': 'email'})})
message_type = fields.Selection(
selection_add=[('sms', 'SMS')],
ondelete={'sms': lambda recs: recs.write({'message_type': 'comment'})})
has_sms_error = fields.Boolean(
'Has SMS error', compute='_compute_has_sms_error', search='_search_has_sms_error')
@ -29,26 +24,9 @@ class MailMessage(models.Model):
message.has_sms_error = message in sms_error_from_notification
def _search_has_sms_error(self, operator, operand):
if operator == '=' and operand:
return ['&', ('notification_ids.notification_status', '=', 'exception'), ('notification_ids.notification_type', '=', 'sms')]
raise NotImplementedError()
def message_format(self, format_reply=True):
""" Override in order to retrieves data about SMS (recipient name and
SMS status)
TDE FIXME: clean the overall message_format thingy
"""
message_values = super(MailMessage, self).message_format(format_reply=format_reply)
all_sms_notifications = self.env['mail.notification'].sudo().search([
('mail_message_id', 'in', [r['id'] for r in message_values]),
('notification_type', '=', 'sms')
])
msgid_to_notif = defaultdict(lambda: self.env['mail.notification'].sudo())
for notif in all_sms_notifications:
msgid_to_notif[notif.mail_message_id.id] += notif
for message in message_values:
customer_sms_data = [(notif.id, notif.res_partner_id.display_name or notif.sms_number, notif.notification_status) for notif in msgid_to_notif.get(message['id'], [])]
message['sms_ids'] = customer_sms_data
return message_values
if operator != 'in':
return NotImplemented
return [('notification_ids', 'any', [
('notification_status', '=', 'exception'),
('notification_type', '=', 'sms'),
])]

View file

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
from odoo import api, fields, models
class MailNotification(models.Model):
@ -10,12 +10,35 @@ class MailNotification(models.Model):
notification_type = fields.Selection(selection_add=[
('sms', 'SMS')
], ondelete={'sms': 'cascade'})
sms_id = fields.Many2one('sms.sms', string='SMS', index='btree_not_null', ondelete='set null')
sms_id_int = fields.Integer('SMS ID', index='btree_not_null')
# Used to give links on form view without foreign key. In most cases, you'd want to use sms_id_int or sms_tracker_ids.sms_uuid.
sms_id = fields.Many2one('sms.sms', string='SMS', store=False, compute='_compute_sms_id')
sms_tracker_ids = fields.One2many('sms.tracker', 'mail_notification_id', string="SMS Trackers")
sms_number = fields.Char('SMS Number', groups='base.group_user')
failure_type = fields.Selection(selection_add=[
('sms_number_missing', 'Missing Number'),
('sms_number_format', 'Wrong Number Format'),
('sms_credit', 'Insufficient Credit'),
('sms_country_not_supported', 'Country Not Supported'),
('sms_registration_needed', 'Country-specific Registration Required'),
('sms_server', 'Server Error'),
('sms_acc', 'Unregistered Account')
('sms_acc', 'Unregistered Account'),
# delivery report errors
('sms_expired', 'Expired'),
('sms_invalid_destination', 'Invalid Destination'),
('sms_not_allowed', 'Not Allowed'),
('sms_not_delivered', 'Not Delivered'),
('sms_rejected', 'Rejected'),
])
@api.depends('sms_id_int', 'notification_type')
def _compute_sms_id(self):
self.sms_id = False
sms_notifications = self.filtered(lambda n: n.notification_type == 'sms' and bool(n.sms_id_int))
if not sms_notifications:
return
existing_sms_ids = self.env['sms.sms'].sudo().search([
('id', 'in', sms_notifications.mapped('sms_id_int')), ('to_delete', '!=', True)
]).ids
for sms_notification in sms_notifications.filtered(lambda n: n.sms_id_int in set(existing_sms_ids)):
sms_notification.sms_id = sms_notification.sms_id_int

View file

@ -3,8 +3,7 @@
import logging
from odoo import api, models, fields
from odoo.addons.phone_validation.tools import phone_validation
from odoo import api, Command, models, fields
from odoo.addons.sms.tools.sms_tools import sms_content_to_rendered_html
from odoo.tools import html2plaintext
@ -34,16 +33,17 @@ class MailThread(models.AbstractModel):
AND msg.message_type != 'user_notification'
GROUP BY msg.res_id
""", {'author_id': self.env.user.partner_id.id, 'model_name': self._name, 'res_ids': tuple(self.ids)})
res.update(self._cr.fetchall())
res.update(self.env.cr.fetchall())
for record in self:
record.message_has_sms_error = bool(res.get(record._origin.id, 0))
@api.model
def _search_message_has_sms_error(self, operator, operand):
return ['&', ('message_ids.has_sms_error', operator, operand), ('message_ids.author_id', '=', self.env.user.partner_id.id)]
if operator != 'in':
return NotImplemented
return ['&', ('message_ids.has_sms_error', '=', True), ('message_ids.author_id', '=', self.env.user.partner_id.id)]
@api.returns('mail.message', lambda value: value.id)
def message_post(self, *args, body='', message_type='notification', **kwargs):
# When posting an 'SMS' `message_type`, make sure that the body is used as-is in the sms,
# and reformat the message body for the notification (mainly making URLs clickable).
@ -136,30 +136,25 @@ class MailThread(models.AbstractModel):
)
def _notify_thread(self, message, msg_vals=False, **kwargs):
recipients_data = super(MailThread, self)._notify_thread(message, msg_vals=msg_vals, **kwargs)
self._notify_thread_by_sms(message, recipients_data, msg_vals=msg_vals, **kwargs)
# Main notification method. Override to add support of sending SMS notifications.
scheduled_date = self._is_notification_scheduled(kwargs.get('scheduled_date'))
recipients_data = super()._notify_thread(message, msg_vals=msg_vals, **kwargs)
if not scheduled_date:
self._notify_thread_by_sms(message, recipients_data, msg_vals=msg_vals, **kwargs)
return recipients_data
def _notify_thread_by_sms(self, message, recipients_data, msg_vals=False,
sms_content=None, sms_numbers=None, sms_pid_to_number=None,
resend_existing=False, put_in_queue=False, **kwargs):
put_in_queue=False, **kwargs):
""" Notification method: by SMS.
:param message: ``mail.message`` record to notify;
:param recipients_data: list of recipients information (based on res.partner
records), formatted like
[{'active': partner.active;
'id': id of the res.partner being recipient to notify;
'groups': res.group IDs if linked to a user;
'notif': 'inbox', 'email', 'sms' (SMS App);
'share': partner.partner_share;
'type': 'customer', 'portal', 'user;'
}, {...}].
See ``MailThread._notify_get_recipients``;
:param msg_vals: dictionary of values used to create the message. If given it
may be used to access values related to ``message`` without accessing it
directly. It lessens query count in some optimized use cases by avoiding
access message content in db;
:param record message: <mail.message> record being notified. May be
void as 'msg_vals' superseeds it;
:param list recipients_data: list of recipients data based on <res.partner>
records formatted like a list of dicts containing information. See
``MailThread._notify_get_recipients()``;
:param dict msg_vals: values dict used to create the message, allows to
skip message usage and spare some queries if given;
:param sms_content: plaintext version of body, mainly to avoid
conversion glitches by splitting html and plain text content formatting
@ -167,20 +162,19 @@ class MailThread(models.AbstractModel):
If not given, `msg_vals`'s `body` is used and converted from html to plaintext;
:param sms_numbers: additional numbers to notify in addition to partners
and classic recipients;
:param pid_to_number: force a number to notify for a given partner ID
:param sms_pid_to_number: force a number to notify for a given partner ID
instead of taking its mobile / phone number;
:param resend_existing: check for existing notifications to update based on
mailed recipient, otherwise create new notifications;
:param put_in_queue: use cron to send queued SMS instead of sending them
directly;
"""
msg_vals = msg_vals or {}
sms_pid_to_number = sms_pid_to_number if sms_pid_to_number is not None else {}
sms_numbers = sms_numbers if sms_numbers is not None else []
sms_create_vals = []
sms_all = self.env['sms.sms'].sudo()
# pre-compute SMS data
body = sms_content or html2plaintext(msg_vals['body'] if msg_vals and 'body' in msg_vals else message.body)
body = sms_content or html2plaintext(msg_vals['body'] if 'body' in msg_vals else message.body)
sms_base_vals = {
'body': body,
'mail_message_id': message.id,
@ -192,21 +186,18 @@ class MailThread(models.AbstractModel):
partner_ids = [r['id'] for r in partners_data]
if partner_ids:
for partner in self.env['res.partner'].sudo().browse(partner_ids):
number = sms_pid_to_number.get(partner.id) or partner.mobile or partner.phone
sanitize_res = phone_validation.phone_sanitize_numbers_w_record([number], partner)[number]
number = sanitize_res['sanitized'] or number
number = sms_pid_to_number.get(partner.id) or partner.phone
sms_create_vals.append(dict(
sms_base_vals,
partner_id=partner.id,
number=number
number=partner._phone_format(number=number) or number,
))
# notify from additional numbers
if sms_numbers:
sanitized = phone_validation.phone_sanitize_numbers_w_record(sms_numbers, self)
tocreate_numbers = [
value['sanitized'] or original
for original, value in sanitized.items()
self._phone_format(number=sms_number) or sms_number
for sms_number in sms_numbers
]
existing_partners_numbers = {vals_dict['number'] for vals_dict in sms_create_vals}
sms_create_vals += [dict(
@ -218,55 +209,34 @@ class MailThread(models.AbstractModel):
) for n in tocreate_numbers if n not in existing_partners_numbers]
# create sms and notification
existing_pids, existing_numbers = [], []
if sms_create_vals:
sms_all |= self.env['sms.sms'].sudo().create(sms_create_vals)
if resend_existing:
existing = self.env['mail.notification'].sudo().search([
'|', ('res_partner_id', 'in', partner_ids),
'&', ('res_partner_id', '=', False), ('sms_number', 'in', sms_numbers),
('notification_type', '=', 'sms'),
('mail_message_id', '=', message.id)
])
for n in existing:
if n.res_partner_id.id in partner_ids and n.mail_message_id == message:
existing_pids.append(n.res_partner_id.id)
if not n.res_partner_id and n.sms_number in sms_numbers and n.mail_message_id == message:
existing_numbers.append(n.sms_number)
notif_create_values = [{
'author_id': message.author_id.id,
'mail_message_id': message.id,
'res_partner_id': sms.partner_id.id,
'sms_number': sms.number,
'notification_type': 'sms',
'sms_id': sms.id,
'sms_id_int': sms.id,
'sms_tracker_ids': [Command.create({'sms_uuid': sms.uuid})] if sms.state == 'outgoing' else False,
'is_read': True, # discard Inbox notification
'notification_status': 'ready' if sms.state == 'outgoing' else 'exception',
'failure_type': '' if sms.state == 'outgoing' else sms.failure_type,
} for sms in sms_all if (sms.partner_id and sms.partner_id.id not in existing_pids) or (not sms.partner_id and sms.number not in existing_numbers)]
} for sms in sms_all]
if notif_create_values:
self.env['mail.notification'].sudo().create(notif_create_values)
if existing_pids or existing_numbers:
for sms in sms_all:
notif = next((n for n in existing if
(n.res_partner_id.id in existing_pids and n.res_partner_id.id == sms.partner_id.id) or
(not n.res_partner_id and n.sms_number in existing_numbers and n.sms_number == sms.number)), False)
if notif:
notif.write({
'notification_type': 'sms',
'notification_status': 'ready',
'sms_id': sms.id,
'sms_number': sms.number,
})
if sms_all and not put_in_queue:
sms_all.filtered(lambda sms: sms.state == 'outgoing').send(auto_commit=False, raise_exception=False)
sms_all.filtered(lambda sms: sms.state == 'outgoing').send(raise_exception=False)
return True
def _get_notify_valid_parameters(self):
return super()._get_notify_valid_parameters() | {
'put_in_queue', 'sms_numbers', 'sms_pid_to_number', 'sms_content',
}
@api.model
def notify_cancel_by_type(self, notification_type):
super().notify_cancel_by_type(notification_type)

View file

@ -1,16 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class PhoneMixin(models.AbstractModel):
_inherit = 'mail.thread.phone'
def _sms_get_number_fields(self):
""" Add fields coming from mail.thread.phone implementation. """
phone_fields = self._phone_get_number_fields()
sms_fields = super(PhoneMixin, self)._sms_get_number_fields()
for fname in (f for f in sms_fields if f not in phone_fields):
phone_fields.append(fname)
return phone_fields

View file

@ -5,36 +5,8 @@ from odoo.addons.phone_validation.tools import phone_validation
class BaseModel(models.AbstractModel):
_inherit = 'base'
def _sms_get_partner_fields(self):
""" This method returns the fields to use to find the contact to link
whensending an SMS. Having partner is not necessary, having only phone
number fields is possible. However it gives more flexibility to
notifications management when having partners. """
fields = []
if hasattr(self, 'partner_id'):
fields.append('partner_id')
if hasattr(self, 'partner_ids'):
fields.append('partner_ids')
return fields
def _sms_get_default_partners(self):
""" This method will likely need to be overridden by inherited models.
:returns partners: recordset of res.partner
"""
partners = self.env['res.partner']
for fname in self._sms_get_partner_fields():
partners = partners.union(*self.mapped(fname)) # ensure ordering
return partners
def _sms_get_number_fields(self):
""" This method returns the fields to use to find the number to use to
send an SMS on a record. """
if 'mobile' in self:
return ['mobile']
return []
def _sms_get_recipients_info(self, force_field=False, partner_fallback=True):
"""" Get SMS recipient information on current record set. This method
""" Get SMS recipient information on current record set. This method
checks for numbers and sanitation in order to centralize computation.
Example of use cases
@ -45,32 +17,52 @@ class BaseModel(models.AbstractModel):
customer, force its number to found field number or fallback on customer fields;
:param force_field: either give a specific field to find phone number, either
generic heuristic is used to find one based on ``_sms_get_number_fields``;
generic heuristic is used to find one based on :meth:`_phone_get_number_fields`;
:param partner_fallback: if no value found in the record, check its customer
values based on ``_sms_get_default_partners``;
values based on :meth:`_mail_get_partners`;
:rtype: dict[int, dict[str, Any]]
:return: a dictionnary with the following structure:
.. code-block:: python
{
record.id: {
# a res.partner recordset that is the customer (void or
# singleton) linked to the recipient.
# See _mail_get_partners;
'partner': ...,
# sanitized number to use (coming from record's field
# or partner's phone fields). Set to False if number
# impossible to parse and format;
'sanitized': ...,
# original number before sanitation;
'number': ...,
# whether the number comes from the customer phone
# fields. If False it means number comes from the
# record itself, even if linked to a customer;
'partner_store': ...,
# field in which the number has been found (generally
# mobile or phone, see _phone_get_number_fields);
'field_store': ...,
}
for record in self
}
:return dict: record.id: {
'partner': a res.partner recordset that is the customer (void or singleton)
linked to the recipient. See ``_sms_get_default_partners``;
'sanitized': sanitized number to use (coming from record's field or partner's
phone fields). Set to False is number impossible to parse and format;
'number': original number before sanitation;
'partner_store': whether the number comes from the customer phone fields. If
False it means number comes from the record itself, even if linked to a
customer;
'field_store': field in which the number has been found (generally mobile or
phone, see ``_sms_get_number_fields``);
} for each record in self
"""
result = dict.fromkeys(self.ids, False)
tocheck_fields = [force_field] if force_field else self._sms_get_number_fields()
tocheck_fields = [force_field] if force_field else self._phone_get_number_fields()
for record in self:
all_numbers = [record[fname] for fname in tocheck_fields if fname in record]
all_partners = record._sms_get_default_partners()
all_partners = record._mail_get_partners()[record.id]
valid_number = False
valid_number, fname = False, False
for fname in [f for f in tocheck_fields if f in record]:
valid_number = phone_validation.phone_sanitize_numbers_w_record([record[fname]], record)[record[fname]]['sanitized']
valid_number = record._phone_format(fname=fname)
if valid_number:
break
@ -85,13 +77,13 @@ class BaseModel(models.AbstractModel):
elif all_partners and partner_fallback:
partner = self.env['res.partner']
for partner in all_partners:
for fname in self.env['res.partner']._sms_get_number_fields():
valid_number = phone_validation.phone_sanitize_numbers_w_record([partner[fname]], record)[partner[fname]]['sanitized']
for fname in self.env['res.partner']._phone_get_number_fields():
valid_number = partner._phone_format(fname=fname)
if valid_number:
break
if not valid_number:
fname = 'mobile' if partner.mobile else ('phone' if partner.phone else 'mobile')
fname = 'phone'
result[record.id] = {
'partner': partner,

View file

@ -0,0 +1,11 @@
from odoo import models
from odoo.addons.sms.tools.sms_api import SmsApi
class ResCompany(models.Model):
_inherit = 'res.company'
def _get_sms_api_class(self):
self.ensure_one()
return SmsApi

View file

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class ResPartner(models.Model):
_name = 'res.partner'
_inherit = ['mail.thread.phone', 'res.partner']
def _sms_get_default_partners(self):
""" Override of mail.thread method.
SMS recipients on partners are the partners themselves.
"""
return self
def _phone_get_number_fields(self):
""" This method returns the fields to use to find the number to use to
send an SMS on a record. """
return ['mobile', 'phone']

View file

@ -1,79 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, api, exceptions, models
from odoo.addons.iap.tools import iap_tools
DEFAULT_ENDPOINT = 'https://iap-sms.odoo.com'
class SmsApi(models.AbstractModel):
_name = 'sms.api'
_description = 'SMS API'
@api.model
def _contact_iap(self, local_endpoint, params):
if not self.env.registry.ready: # Don't reach IAP servers during module installation
raise exceptions.AccessError("Unavailable during module installation.")
account = self.env['iap.account'].get('sms')
params['account_token'] = account.account_token
endpoint = self.env['ir.config_parameter'].sudo().get_param('sms.endpoint', DEFAULT_ENDPOINT)
# TODO PRO, the default timeout is 15, do we have to increase it ?
return iap_tools.iap_jsonrpc(endpoint + local_endpoint, params=params)
@api.model
def _send_sms(self, numbers, message):
""" Send a single message to several numbers
:param numbers: list of E164 formatted phone numbers
:param message: content to send
:raises ? TDE FIXME
"""
params = {
'numbers': numbers,
'message': message,
}
return self._contact_iap('/iap/message_send', params)
@api.model
def _send_sms_batch(self, messages):
""" Send SMS using IAP in batch mode
:param messages: list of SMS to send, structured as dict [{
'res_id': integer: ID of sms.sms,
'number': string: E164 formatted phone number,
'content': string: content to send
}]
:return: return of /iap/sms/1/send controller which is a list of dict [{
'res_id': integer: ID of sms.sms,
'state': string: 'insufficient_credit' or 'wrong_number_format' or 'success',
'credit': integer: number of credits spent to send this SMS,
}]
:raises: normally none
"""
params = {
'messages': messages
}
return self._contact_iap('/iap/sms/2/send', params)
@api.model
def _get_sms_api_error_messages(self):
""" Returns a dict containing the error message to display for every known error 'state'
resulting from the '_send_sms_batch' method.
We prefer a dict instead of a message-per-error-state based method so we only call
the 'get_credits_url' once, to avoid extra RPC calls. """
buy_credits_url = self.sudo().env['iap.account'].get_credits_url(service_name='sms')
buy_credits = '<a href="%s" target="_blank">%s</a>' % (
buy_credits_url,
_('Buy credits.')
)
return {
'unregistered': _("You don't have an eligible IAP account."),
'insufficient_credit': ' '.join([_('You don\'t have enough credits on your IAP account.'), buy_credits]),
'wrong_number_format': _("The number you're trying to reach is not correctly formatted."),
}

View file

@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import threading
from uuid import uuid4
from odoo import api, fields, models, tools, _
from odoo.addons.sms.tools.sms_api import SmsApi
from odoo.tools.urls import urljoin as url_join
_logger = logging.getLogger(__name__)
@ -15,93 +17,111 @@ class SmsSms(models.Model):
_rec_name = 'number'
_order = 'id DESC'
IAP_TO_SMS_STATE = {
'success': 'sent',
IAP_TO_SMS_STATE_SUCCESS = {
'processing': 'process',
'success': 'pending',
# These below are not returned in responses from IAP API in _send but are received via webhook events.
'sent': 'pending',
'delivered': 'sent',
}
IAP_TO_SMS_FAILURE_TYPE = { # TODO RIGR remove me in master
'insufficient_credit': 'sms_credit',
'wrong_number_format': 'sms_number_format',
'country_not_supported': 'sms_country_not_supported',
'server_error': 'sms_server',
'unregistered': 'sms_acc'
}
BOUNCE_DELIVERY_ERRORS = {'sms_invalid_destination', 'sms_not_allowed', 'sms_rejected'}
DELIVERY_ERRORS = {'sms_expired', 'sms_not_delivered', *BOUNCE_DELIVERY_ERRORS}
uuid = fields.Char('UUID', copy=False, readonly=True, default=lambda self: uuid4().hex,
help='Alternate way to identify a SMS record, used for delivery reports')
number = fields.Char('Number')
body = fields.Text()
partner_id = fields.Many2one('res.partner', 'Customer')
mail_message_id = fields.Many2one('mail.message', index=True)
state = fields.Selection([
('outgoing', 'In Queue'),
('sent', 'Sent'),
('process', 'Processing'),
('pending', 'Sent'),
('sent', 'Delivered'), # As for notifications and traces
('error', 'Error'),
('canceled', 'Canceled')
('canceled', 'Cancelled')
], 'SMS Status', readonly=True, copy=False, default='outgoing', required=True)
failure_type = fields.Selection([
("unknown", "Unknown error"),
('sms_number_missing', 'Missing Number'),
('sms_number_format', 'Wrong Number Format'),
('sms_country_not_supported', 'Country Not Supported'),
('sms_registration_needed', 'Country-specific Registration Required'),
('sms_credit', 'Insufficient Credit'),
('sms_server', 'Server Error'),
('sms_acc', 'Unregistered Account'),
# mass mode specific codes
# mass mode specific codes, generated internally, not returned by IAP.
('sms_blacklist', 'Blacklisted'),
('sms_duplicate', 'Duplicate'),
('sms_optout', 'Opted Out'),
], copy=False)
sms_tracker_id = fields.Many2one('sms.tracker', string='SMS trackers', compute='_compute_sms_tracker_id')
to_delete = fields.Boolean(
'Marked for deletion', default=False,
help='Will automatically be deleted, while notifications will not be deleted in any case.'
)
_uuid_unique = models.Constraint(
'unique(uuid)',
'UUID must be unique',
)
@api.model_create_multi
def create(self, vals_list):
self.env.ref('sms.ir_cron_sms_scheduler_action')._trigger()
return super().create(vals_list)
@api.depends('uuid')
def _compute_sms_tracker_id(self):
self.sms_tracker_id = False
existing_trackers = self.env['sms.tracker'].search([('sms_uuid', 'in', self.filtered('uuid').mapped('uuid'))])
tracker_ids_by_sms_uuid = {tracker.sms_uuid: tracker.id for tracker in existing_trackers}
for sms in self.filtered(lambda s: s.uuid in tracker_ids_by_sms_uuid):
sms.sms_tracker_id = tracker_ids_by_sms_uuid[sms.uuid]
def action_set_canceled(self):
self.state = 'canceled'
notifications = self.env['mail.notification'].sudo().search([
('sms_id', 'in', self.ids),
# sent is sent -> cannot reset
('notification_status', 'not in', ['canceled', 'sent']),
])
if notifications:
notifications.write({'notification_status': 'canceled'})
if not self._context.get('sms_skip_msg_notification', False):
notifications.mail_message_id._notify_message_notification_update()
self._update_sms_state_and_trackers('canceled')
def action_set_error(self, failure_type):
self.state = 'error'
self.failure_type = failure_type
notifications = self.env['mail.notification'].sudo().search([
('sms_id', 'in', self.ids),
# sent can be set to error due to IAP feedback
('notification_status', '!=', 'exception'),
])
if notifications:
notifications.write({'notification_status': 'exception', 'failure_type': failure_type})
if not self._context.get('sms_skip_msg_notification', False):
notifications.mail_message_id._notify_message_notification_update()
self._update_sms_state_and_trackers('error', failure_type=failure_type)
def action_set_outgoing(self):
self.write({
'state': 'outgoing',
'failure_type': False
})
notifications = self.env['mail.notification'].sudo().search([
('sms_id', 'in', self.ids),
# sent is sent -> cannot reset
('notification_status', 'not in', ['ready', 'sent']),
])
if notifications:
notifications.write({'notification_status': 'ready', 'failure_type': False})
if not self._context.get('sms_skip_msg_notification', False):
notifications.mail_message_id._notify_message_notification_update()
self._update_sms_state_and_trackers('outgoing', failure_type=False)
def send(self, unlink_failed=False, unlink_sent=True, auto_commit=False, raise_exception=False):
def send(self, unlink_failed=False, unlink_sent=True, raise_exception=False):
""" Main API method to send SMS.
This contacts an external server. If the transaction fails, it may be
retried which can result in sending multiple SMS messages!
:param unlink_failed: unlink failed SMS after IAP feedback;
:param unlink_sent: unlink sent SMS after IAP feedback;
:param auto_commit: commit after each batch of SMS;
:param raise_exception: raise if there is an issue contacting IAP;
"""
self = self.filtered(lambda sms: sms.state == 'outgoing')
for batch_ids in self._split_batch():
self.browse(batch_ids)._send(unlink_failed=unlink_failed, unlink_sent=unlink_sent, raise_exception=raise_exception)
# auto-commit if asked except in testing mode
if auto_commit is True and not getattr(threading.current_thread(), 'testing', False):
self._cr.commit()
domain = [('state', '=', 'outgoing'), ('to_delete', '!=', True)]
to_send = self.try_lock_for_update().filtered_domain(domain)
for sms_api, sms in to_send._split_by_api():
for batch_ids in sms._split_batch():
self.browse(batch_ids).with_context(sms_api=sms_api)._send(
unlink_failed=unlink_failed,
unlink_sent=unlink_sent,
raise_exception=raise_exception,
)
def _split_by_api(self):
yield SmsApi(self.env), self
def resend_failed(self):
sms_to_send = self.filtered(lambda sms: sms.state == 'error')
sms_to_send = self.filtered(lambda sms: sms.state == 'error' and not sms.to_delete)
sms_to_send.state = 'outgoing'
notification_title = _('Warning')
notification_type = 'danger'
@ -112,7 +132,7 @@ class SmsSms(models.Model):
if success_sms > 0:
notification_title = _('Success')
notification_type = 'success'
notification_message = _('%s out of the %s selected SMS Text Messages have successfully been resent.', success_sms, len(self))
notification_message = _('%(count)s out of the %(total)s selected SMS Text Messages have successfully been resent.', count=success_sms, total=len(self))
else:
notification_message = _('The SMS Text Messages could not be resent.')
else:
@ -128,90 +148,93 @@ class SmsSms(models.Model):
}
@api.model
def _process_queue(self, ids=None):
""" Send immediately queued messages, committing after each message is sent.
This is not transactional and should not be called during another transaction!
def _process_queue(self):
""" CRON job to send queued SMS messages. """
domain = [('state', '=', 'outgoing'), ('to_delete', '!=', True)]
:param list ids: optional list of emails ids to send. If passed no search
is performed, and these ids are used instead.
"""
domain = [('state', '=', 'outgoing')]
batch_size = self._get_send_batch_size()
records = self.search(domain, limit=batch_size, order='id').try_lock_for_update()
if not records:
return
filtered_ids = self.search(domain, limit=10000).ids # TDE note: arbitrary limit we might have to update
if ids:
ids = list(set(filtered_ids) & set(ids))
else:
ids = filtered_ids
ids.sort()
records._send(unlink_failed=False, unlink_sent=True, raise_exception=False)
self.env['ir.cron']._commit_progress(len(records), remaining=self.search_count(domain) if len(records) == batch_size else 0)
res = None
try:
# auto-commit except in testing mode
auto_commit = not getattr(threading.current_thread(), 'testing', False)
res = self.browse(ids).send(unlink_failed=False, unlink_sent=True, auto_commit=auto_commit, raise_exception=False)
except Exception:
_logger.exception("Failed processing SMS queue")
return res
def _get_send_batch_size(self):
return int(self.env['ir.config_parameter'].sudo().get_param('sms.session.batch.size', 500))
def _get_sms_company(self):
return self.mail_message_id.record_company_id or self.env.company
def _split_batch(self):
batch_size = int(self.env['ir.config_parameter'].sudo().get_param('sms.session.batch.size', 500))
for sms_batch in tools.split_every(batch_size, self.ids):
yield sms_batch
batch_size = self._get_send_batch_size()
yield from tools.split_every(batch_size, self.ids)
def _send(self, unlink_failed=False, unlink_sent=True, raise_exception=False):
""" This method tries to send SMS after checking the number (presence and
formatting). """
iap_data = [{
'res_id': record.id,
'number': record.number,
'content': record.body,
} for record in self]
"""Send SMS after checking the number (presence and formatting)."""
sms_api = self.env.context.get('sms_api')
if not sms_api:
company = self._get_sms_company()
company.ensure_one() # This should always be the case since the grouping is done in `send`
sms_api = company._get_sms_api_class()(self.env)
return self._send_with_api(
sms_api,
unlink_failed=unlink_failed,
unlink_sent=unlink_sent,
raise_exception=raise_exception,
)
def _send_with_api(self, sms_api, unlink_failed=False, unlink_sent=True, raise_exception=False):
"""Send SMS after checking the number (presence and formatting)."""
messages = [{
'content': body,
'numbers': [{'number': sms.number, 'uuid': sms.uuid} for sms in body_sms_records],
} for body, body_sms_records in self.grouped('body').items()]
delivery_reports_url = url_join(self[0].get_base_url(), '/sms/status')
try:
iap_results = self.env['sms.api']._send_sms_batch(iap_data)
results = sms_api._send_sms_batch(messages, delivery_reports_url=delivery_reports_url)
except Exception as e:
_logger.info('Sent batch %s SMS: %s: failed with exception %s', len(self.ids), self.ids, e)
if raise_exception:
raise
self._postprocess_iap_sent_sms(
[{'res_id': sms.id, 'state': 'server_error'} for sms in self],
unlink_failed=unlink_failed, unlink_sent=unlink_sent)
results = [{'uuid': sms.uuid, 'state': 'server_error'} for sms in self]
else:
_logger.info('Send batch %s SMS: %s: gave %s', len(self.ids), self.ids, iap_results)
self._postprocess_iap_sent_sms(iap_results, unlink_failed=unlink_failed, unlink_sent=unlink_sent)
_logger.info('Send batch %s SMS: %s: gave %s', len(self.ids), self.ids, results)
def _postprocess_iap_sent_sms(self, iap_results, failure_reason=None, unlink_failed=False, unlink_sent=True):
todelete_sms_ids = []
if unlink_failed:
todelete_sms_ids += [item['res_id'] for item in iap_results if item['state'] != 'success']
if unlink_sent:
todelete_sms_ids += [item['res_id'] for item in iap_results if item['state'] == 'success']
results_uuids = [result['uuid'] for result in results]
all_sms_sudo = self.env['sms.sms'].sudo().search([('uuid', 'in', results_uuids)]).with_context(sms_skip_msg_notification=True)
for state in self.IAP_TO_SMS_STATE.keys():
sms_ids = [item['res_id'] for item in iap_results if item['state'] == state]
if sms_ids:
if state != 'success' and not unlink_failed:
self.env['sms.sms'].sudo().browse(sms_ids).write({
'state': 'error',
'failure_type': self.IAP_TO_SMS_STATE[state],
})
if state == 'success' and not unlink_sent:
self.env['sms.sms'].sudo().browse(sms_ids).write({
'state': 'sent',
'failure_type': False,
})
notifications = self.env['mail.notification'].sudo().search([
('notification_type', '=', 'sms'),
('sms_id', 'in', sms_ids),
('notification_status', 'not in', ('sent', 'canceled')),
])
if notifications:
notifications.write({
'notification_status': 'sent' if state == 'success' else 'exception',
'failure_type': self.IAP_TO_SMS_STATE[state] if state != 'success' else False,
'failure_reason': failure_reason if failure_reason else False,
})
self.mail_message_id._notify_message_notification_update()
for (iap_state, failure_reason), results_group in tools.groupby(results, key=lambda result: (result['state'], result.get('failure_reason'))):
sms_sudo = all_sms_sudo.filtered(lambda s: s.uuid in {result['uuid'] for result in results_group})
if success_state := self.IAP_TO_SMS_STATE_SUCCESS.get(iap_state):
sms_sudo.sms_tracker_id._action_update_from_sms_state(success_state)
to_delete = {'to_delete': True} if unlink_sent else {}
sms_sudo.write({'state': success_state, 'failure_type': False, **to_delete})
else:
failure_type = sms_api.PROVIDER_TO_SMS_FAILURE_TYPE.get(iap_state, 'unknown')
if failure_type != 'unknown':
sms_sudo.sms_tracker_id._action_update_from_sms_state('error', failure_type=failure_type, failure_reason=failure_reason)
else:
sms_sudo.sms_tracker_id.with_context(sms_known_failure_reason=failure_reason)._action_update_from_provider_error(iap_state)
to_delete = {'to_delete': True} if unlink_failed else {}
sms_sudo.write({'state': 'error', 'failure_type': failure_type, **to_delete})
if todelete_sms_ids:
self.browse(todelete_sms_ids).sudo().unlink()
all_sms_sudo._handle_call_result_hook(results)
all_sms_sudo.mail_message_id._notify_message_notification_update()
def _update_sms_state_and_trackers(self, new_state, failure_type=None):
"""Update sms state update and related tracking records (notifications, traces)."""
self.write({'state': new_state, 'failure_type': failure_type})
# Use sudo on mail.notification to allow writing other users' notifications; rights are already checked by sms write
self.sms_tracker_id.sudo()._action_update_from_sms_state(new_state, failure_type=failure_type)
def _handle_call_result_hook(self, results):
"""Further process SMS sending API results."""
pass
@api.autovacuum
def _gc_device(self):
self.env.cr.execute("DELETE FROM sms_sms WHERE to_delete = TRUE")
_logger.info("GC'd %d sms marked for deletion", self.env.cr.rowcount)

View file

@ -4,9 +4,9 @@
from odoo import api, fields, models, _
class SMSTemplate(models.Model):
class SmsTemplate(models.Model):
"Templates for sending SMS"
_name = "sms.template"
_name = 'sms.template'
_inherit = ['mail.render.mixin', 'template.reset.mixin']
_description = 'SMS Templates'
@ -14,8 +14,8 @@ class SMSTemplate(models.Model):
@api.model
def default_get(self, fields):
res = super(SMSTemplate, self).default_get(fields)
if not fields or 'model_id' in fields and not res.get('model_id') and res.get('model'):
res = super().default_get(fields)
if 'model_id' in fields and not res.get('model_id') and res.get('model'):
res['model_id'] = self.env['ir.model']._get(res['model']).id
return res
@ -41,15 +41,13 @@ class SMSTemplate(models.Model):
# CRUD
# ------------------------------------------------------------
@api.returns('self', lambda value: value.id)
def copy(self, default=None):
default = dict(default or {},
name=_("%s (copy)", self.name))
return super(SMSTemplate, self).copy(default=default)
def copy_data(self, default=None):
vals_list = super().copy_data(default=default)
return [dict(vals, name=self.env._("%s (copy)", template.name)) for template, vals in zip(self, vals_list)]
def unlink(self):
self.sudo().mapped('sidebar_action_id').unlink()
return super(SMSTemplate, self).unlink()
return super().unlink()
def action_create_sidebar_action(self):
ActWindow = self.env['ir.actions.act_window']

View file

@ -0,0 +1,83 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class SmsTracker(models.Model):
"""Relationship between a sent SMS and tracking records such as notifications and traces.
This model acts as an extension of a `mail.notification` or a `mailing.trace` and allows to
update those based on the SMS provider responses both at sending and when later receiving
sent/delivery reports (see `SmsController`).
SMS trackers are supposed to be created manually when necessary, and tied to their related
SMS through the SMS UUID field. (They are not tied to the SMS records directly as those can
be deleted when sent).
Note: Only admins/system user should need to access (a fortiori modify) these technical
records so no "sudo" is used nor should be required here.
"""
_name = 'sms.tracker'
_description = "Link SMS to mailing/sms tracking models"
SMS_STATE_TO_NOTIFICATION_STATUS = {
'canceled': 'canceled',
'process': 'process',
'error': 'exception',
'outgoing': 'ready',
'sent': 'sent',
'pending': 'pending',
}
sms_uuid = fields.Char('SMS uuid', required=True)
mail_notification_id = fields.Many2one('mail.notification', ondelete='cascade', index='btree_not_null')
_sms_uuid_unique = models.Constraint(
'unique(sms_uuid)',
'A record for this UUID already exists',
)
def _action_update_from_provider_error(self, provider_error):
"""
:param str provider_error: value returned by SMS service provider (IAP) or any string.
If provided, notification values will be derived from it.
(see ``_get_tracker_values_from_provider_error``)
"""
failure_reason = self.env.context.get("sms_known_failure_reason") # TODO RIGR in master: pass as param instead of context
failure_type = f'sms_{provider_error}'
error_status = None
if failure_type not in self.env['sms.sms'].DELIVERY_ERRORS:
failure_type = 'unknown'
failure_reason = failure_reason or provider_error
elif failure_type in self.env['sms.sms'].BOUNCE_DELIVERY_ERRORS:
error_status = "bounce"
self._update_sms_notifications(error_status or 'exception', failure_type=failure_type, failure_reason=failure_reason)
return error_status, failure_type, failure_reason
def _action_update_from_sms_state(self, sms_state, failure_type=False, failure_reason=False):
notification_status = self.SMS_STATE_TO_NOTIFICATION_STATUS[sms_state]
self._update_sms_notifications(notification_status, failure_type=failure_type, failure_reason=failure_reason)
def _update_sms_notifications(self, notification_status, failure_type=False, failure_reason=False):
# canceled is a state which means that the SMS sending order should not be sent to the SMS service.
# `process`, `pending` are sent to IAP which is not revertible (as `sent` which means "delivered").
notifications_statuses_to_ignore = {
'canceled': ['canceled', 'process', 'pending', 'sent'],
'ready': ['ready', 'process', 'pending', 'sent'],
'process': ['process', 'pending', 'sent'],
'pending': ['pending', 'sent'],
'bounce': ['bounce', 'sent'],
'sent': ['sent'],
'exception': ['exception'],
}[notification_status]
notifications = self.mail_notification_id.filtered(
lambda n: n.notification_status not in notifications_statuses_to_ignore
)
if notifications:
notifications.write({
'notification_status': notification_status,
'failure_type': failure_type,
'failure_reason': failure_reason,
})
if not self.env.context.get('sms_skip_msg_notification'):
notifications.mail_message_id._notify_message_notification_update()

View file

@ -4,8 +4,11 @@ access_sms_sms_system,access.sms.sms.system,model_sms_sms,base.group_system,1,1,
access_sms_template_all,access.sms.template.all,model_sms_template,,0,0,0,0
access_sms_template_user,access.sms.template.user,model_sms_template,base.group_user,1,0,0,0
access_sms_template_system,access.sms.template.system,model_sms_template,base.group_system,1,1,1,1
access_sms_tracker_all,access.sms.tracker.all,model_sms_tracker,,0,0,0,0
access_sms_tracker_system,access.sms.tracker.system,model_sms_tracker,base.group_system,1,1,1,1
access_sms_composer,access.sms.composer,model_sms_composer,base.group_user,1,1,1,0
access_sms_resend_recipient,access.sms.resend.recipient,model_sms_resend_recipient,base.group_user,1,1,1,0
access_sms_resend,access.sms.resend,model_sms_resend,base.group_user,1,1,1,0
access_sms_template_preview,access.sms.template.preview,model_sms_template_preview,base.group_user,1,1,1,0
access_sms_template_reset,access.sms.template.reset,model_sms_template_reset,mail.group_mail_template_editor,1,1,1,1
access_sms_account_registration_phone_number_wizard_system,access.sms.account.phone.system,model_sms_account_phone,base.group_system,1,1,1,1
access_sms_account_verification_code_wizard_system,access.sms.account.code.system,model_sms_account_code,base.group_system,1,1,1,1
access_sms_account_sender_name_wizard_system,access.sms.account.sender.system,model_sms_account_sender,base.group_system,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
4 access_sms_template_all access.sms.template.all model_sms_template 0 0 0 0
5 access_sms_template_user access.sms.template.user model_sms_template base.group_user 1 0 0 0
6 access_sms_template_system access.sms.template.system model_sms_template base.group_system 1 1 1 1
7 access_sms_tracker_all access.sms.tracker.all model_sms_tracker 0 0 0 0
8 access_sms_tracker_system access.sms.tracker.system model_sms_tracker base.group_system 1 1 1 1
9 access_sms_composer access.sms.composer model_sms_composer base.group_user 1 1 1 0
access_sms_resend_recipient access.sms.resend.recipient model_sms_resend_recipient base.group_user 1 1 1 0
access_sms_resend access.sms.resend model_sms_resend base.group_user 1 1 1 0
10 access_sms_template_preview access.sms.template.preview model_sms_template_preview base.group_user 1 1 1 0
11 access_sms_template_reset access.sms.template.reset model_sms_template_reset mail.group_mail_template_editor 1 1 1 1
12 access_sms_account_registration_phone_number_wizard_system access.sms.account.phone.system model_sms_account_phone base.group_system 1 1 1 1
13 access_sms_account_verification_code_wizard_system access.sms.account.code.system model_sms_account_code base.group_system 1 1 1 1
14 access_sms_account_sender_name_wizard_system access.sms.account.sender.system model_sms_account_sender base.group_system 1 1 1 1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before After
Before After

View file

@ -1,41 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 70 70">
<defs>
<mask id="mask" x="0" y="0" width="70" height="70" maskUnits="userSpaceOnUse">
<g id="b">
<path id="a" d="M4,0H65c4,0,5,1,5,5V65c0,4-1,5-5,5H4c-3,0-4-1-4-5V5C0,1,1,0,4,0Z" fill="#fff" fill-rule="evenodd"/>
</g>
</mask>
<linearGradient id="linear-gradient" x1="-1172.36" y1="477.94" x2="-1173.36" y2="476.94" gradientTransform="matrix(70, 0, 0, -70, 82134.99, 33455.73)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#7cc098"/>
<stop offset="1" stop-color="#5f8a71"/>
</linearGradient>
</defs>
<g mask="url(#mask)">
<g>
<path d="M0,0H70V70H0Z" fill-rule="evenodd" fill="url(#linear-gradient)"/>
<path d="M4,1H65c2.67,0,4.33.67,5,2V0H0V3C.67,1.67,2,1,4,1Z" fill="#fff" fill-opacity="0.38" fill-rule="evenodd"/>
<path d="M4,69H65c2.67,0,4.33-1,5-3v4H0V66A3.92,3.92,0,0,0,4,69Z" fill-opacity="0.38" fill-rule="evenodd"/>
<path d="M4,69a3.66,3.66,0,0,1-4-4V34.17L20.65,13.52l24.78-.66,2,3.78v9.44l-6.11,6.11,2.41,2.11-1.09,1.09,1.31,2L47.32,34l1.52,0,3.8-3.83,1.87,12-7.08,7.1-.56,7.6L34.81,69Z" fill-opacity="0.15" fill-rule="evenodd"/>
<g>
<g opacity="0.4" style="isolation: isolate">
<path d="M21.59,39.06,23,39a2.29,2.29,0,0,0,.49,1,1.5,1.5,0,0,0,1.05.33,1.87,1.87,0,0,0,1-.27.87.87,0,0,0,.33-.66A.64.64,0,0,0,25.7,39a1.88,1.88,0,0,0-.49-.33l-1.15-.33a4.49,4.49,0,0,1-1.59-.71,1.82,1.82,0,0,1-.66-1.42,1.52,1.52,0,0,1,.33-1,1.83,1.83,0,0,1,.88-.71,3.31,3.31,0,0,1,1.37-.22,3.2,3.2,0,0,1,2,.6,2.05,2.05,0,0,1,.72,1.54l-1.43,0a1.3,1.3,0,0,0-.38-.77,1.44,1.44,0,0,0-.88-.22,1.73,1.73,0,0,0-1,.28.51.51,0,0,0-.22.44.52.52,0,0,0,.22.44,3.47,3.47,0,0,0,1.32.49,7.23,7.23,0,0,1,1.53.49,2,2,0,0,1,.77.71,2.34,2.34,0,0,1,.27,1.16A2.3,2.3,0,0,1,27,40.6a2.08,2.08,0,0,1-.94.76,4.1,4.1,0,0,1-1.53.28A3.3,3.3,0,0,1,22.47,41,3.1,3.1,0,0,1,21.59,39.06Z"/>
<path d="M28.39,41.36V34.29h2.13l1.26,4.83L33,34.29h2.14v7.07H33.87V35.83L32.5,41.36H31.13l-1.37-5.53v5.53Z"/>
<path d="M36.17,39.06,37.54,39A2.29,2.29,0,0,0,38,40a1.56,1.56,0,0,0,1,.28,1.77,1.77,0,0,0,1-.28.83.83,0,0,0,.33-.65.61.61,0,0,0-.16-.44,2.07,2.07,0,0,0-.49-.33c-.17-.06-.55-.17-1.16-.33A3.37,3.37,0,0,1,37,37.53a1.88,1.88,0,0,1-.65-1.43,1.51,1.51,0,0,1,.33-1,1.93,1.93,0,0,1,.87-.72A3.52,3.52,0,0,1,39,34.18a3.15,3.15,0,0,1,2,.61,2,2,0,0,1,.71,1.53l-1.42.06a1.25,1.25,0,0,0-.39-.77,1.33,1.33,0,0,0-.87-.22,1.81,1.81,0,0,0-1,.27.52.52,0,0,0-.22.44.51.51,0,0,0,.22.44,3.09,3.09,0,0,0,1.32.49,7.13,7.13,0,0,1,1.53.5,2,2,0,0,1,.77.71,2.44,2.44,0,0,1,.27,1.15,2.29,2.29,0,0,1-.33,1.15,1.8,1.8,0,0,1-.93.77,4.36,4.36,0,0,1-1.53.27A3.42,3.42,0,0,1,37,41,3,3,0,0,1,36.17,39.06Z"/>
<path d="M57.14,37.2l-4.55-4.93a1.37,1.37,0,0,0-2.36,1.06v2.74H45.3v4h4.93v3a1.37,1.37,0,0,0,2.36,1.06l4.55-4.93A1.54,1.54,0,0,0,57.14,37.2Z"/>
<path d="M18.09,36.07H10a1.78,1.78,0,0,0-1.69,1.34,2,2,0,0,0,1.62,2.66h8.18Z"/>
<path d="M43.41,14.88H20.11a2,2,0,0,0-2,2V28.09h2.3V21.28a.27.27,0,0,1,.3-.3h22.1a.27.27,0,0,1,.3.3v6.81h2.3V16.88A2,2,0,0,0,43.41,14.88Zm-8.5,4.7h-6.1a.47.47,0,0,1-.5-.5c0-.3.1-.5.4-.5h6.1a.47.47,0,0,1,.5.5A.46.46,0,0,1,34.91,19.58Z"/>
<path d="M43.11,47.38v6.9a.27.27,0,0,1-.3.3H20.71a.27.27,0,0,1-.3-.3v-6.9h-2.3v10.2a2,2,0,0,0,2,2h23.3a2,2,0,0,0,2-2V47.38Zm-11.3,11.1a1.5,1.5,0,1,1,1.5-1.5A1.47,1.47,0,0,1,31.81,58.48Z"/>
</g>
<g style="isolation: isolate">
<path d="M23.61,37,25,36.93a2.3,2.3,0,0,0,.5,1,1.48,1.48,0,0,0,1,.33,1.85,1.85,0,0,0,1-.27.87.87,0,0,0,.33-.66.62.62,0,0,0-.17-.44,1.88,1.88,0,0,0-.49-.33l-1.15-.33a5,5,0,0,1-1.59-.71,1.84,1.84,0,0,1-.66-1.43,1.51,1.51,0,0,1,.33-1,1.87,1.87,0,0,1,.88-.71,3.31,3.31,0,0,1,1.37-.22,3.23,3.23,0,0,1,2,.6,2.06,2.06,0,0,1,.71,1.53l-1.43.06a1.27,1.27,0,0,0-.38-.77,1.42,1.42,0,0,0-.88-.22,1.7,1.7,0,0,0-1,.28.48.48,0,0,0-.22.43.48.48,0,0,0,.22.44,3.13,3.13,0,0,0,1.31.5,6.81,6.81,0,0,1,1.54.49,1.87,1.87,0,0,1,.76.71,2.33,2.33,0,0,1,.28,1.15A2.27,2.27,0,0,1,29,38.57a2.13,2.13,0,0,1-.93.77,4.37,4.37,0,0,1-1.54.27,3.32,3.32,0,0,1-2.08-.6A3.15,3.15,0,0,1,23.61,37Z" fill="#fff"/>
<path d="M30.41,39.34V32.27h2.14l1.26,4.82,1.26-4.82H37.2v7.07H35.89V33.81l-1.37,5.53H33.15l-1.37-5.53v5.53Z" fill="#fff"/>
<path d="M38.19,37l1.37-.11a2.4,2.4,0,0,0,.49,1,1.57,1.57,0,0,0,1.05.27,1.87,1.87,0,0,0,1-.27.85.85,0,0,0,.33-.66.64.64,0,0,0-.17-.44,1.66,1.66,0,0,0-.49-.32c-.17-.06-.55-.17-1.15-.33a3.27,3.27,0,0,1-1.59-.72,1.82,1.82,0,0,1-.66-1.42,1.55,1.55,0,0,1,.33-1,1.83,1.83,0,0,1,.88-.71A3.48,3.48,0,0,1,41,32.16a3.2,3.2,0,0,1,2,.6,2.07,2.07,0,0,1,.72,1.54l-1.43.05a1.24,1.24,0,0,0-.38-.76,1.37,1.37,0,0,0-.88-.22,1.81,1.81,0,0,0-1,.27.5.5,0,0,0-.21.44.51.51,0,0,0,.21.44,3.3,3.3,0,0,0,1.32.49,7.64,7.64,0,0,1,1.53.49,1.92,1.92,0,0,1,.77.72,2.32,2.32,0,0,1-.05,2.3,1.89,1.89,0,0,1-.93.77,4.42,4.42,0,0,1-1.54.27,3.36,3.36,0,0,1-2.08-.6A3,3,0,0,1,38.19,37Z" fill="#fff"/>
<path d="M59.16,35.18l-4.54-4.93a1.37,1.37,0,0,0-2.36,1.06V34H47.32v4h4.94v3a1.37,1.37,0,0,0,2.36,1.06l4.54-4.93A1.54,1.54,0,0,0,59.16,35.18Z" fill="#fff"/>
<path d="M20.11,34H12a1.79,1.79,0,0,0-1.7,1.35A2,2,0,0,0,11.94,38h8.17Z" fill="#fff"/>
<path d="M45.43,12.86H22.13a2,2,0,0,0-2,2V26.07h2.3V19.26a.27.27,0,0,1,.3-.3h22.1a.27.27,0,0,1,.3.3v6.81h2.3V14.86A2,2,0,0,0,45.43,12.86Zm-8.5,4.7h-6.1a.47.47,0,0,1-.5-.5c0-.3.1-.5.4-.5h6.1a.47.47,0,0,1,.5.5A.46.46,0,0,1,36.93,17.56Z" fill="#fff"/>
<path d="M45.13,45.35v6.91a.27.27,0,0,1-.3.3H22.73a.27.27,0,0,1-.3-.3V45.35h-2.3V55.56a2,2,0,0,0,2,2h23.3a2,2,0,0,0,2-2V45.35ZM33.83,56.46a1.5,1.5,0,1,1,1.5-1.5A1.47,1.47,0,0,1,33.83,56.46Z" fill="#fff"/>
</g>
</g>
</g>
</g>
</svg>
<svg width="50" height="50" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><path d="M13 8a4 4 0 0 1 4-4h16a4 4 0 0 1 4 4v34a4 4 0 0 1-4 4H17a4 4 0 0 1-4-4V8Z" fill="#1AD3BB"/><path d="M37 19v18a9 9 0 1 1 0-18Z" fill="#1A6F66"/><path d="M13 31V13a9 9 0 1 1 0 18Z" fill="#005E7A"/><path d="M13 13H4v9a9 9 0 0 0 9 9V13Z" fill="#985184"/><path d="M37 37h9v-9a9 9 0 0 0-9-9v18Z" fill="#FC868B"/></svg>

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 405 B

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-inherit="mail.NotificationGroup" t-inherit-mode="extension">
<xpath expr="//*[hasclass('o_NotificationGroup_inlineText')]" position="inside">
<t t-if="notificationGroupView.notificationGroup.notification_type === 'sms'">
An error occurred when sending an SMS.
</t>
</xpath>
</t>
</templates>

View file

@ -1,10 +1,9 @@
/** @odoo-module **/
import { _t } from "@web/core/l10n/translation";
import { patch } from "@web/core/utils/patch";
import { PhoneField } from "@web/views/fields/phone/phone_field";
import { PhoneField, phoneField, formPhoneField } from "@web/views/fields/phone/phone_field";
import { SendSMSButton } from '@sms/components/sms_button/sms_button';
patch(PhoneField, "sms.PhoneField", {
patch(PhoneField, {
components: {
...PhoneField.components,
SendSMSButton
@ -17,10 +16,21 @@ patch(PhoneField, "sms.PhoneField", {
...PhoneField.props,
enableButton: { type: Boolean, optional: true },
},
extractProps: ({ attrs }) => {
return {
enableButton: attrs.options.enable_sms,
placeholder: attrs.placeholder,
};
},
});
const patchDescr = () => ({
extractProps({ options }) {
const props = super.extractProps(...arguments);
props.enableButton = options.enable_sms;
return props;
},
supportedOptions: [{
label: _t("Enable SMS"),
name: "enable_sms",
type: "boolean",
default: true,
}],
});
patch(phoneField, patchDescr());
patch(formPhoneField, patchDescr());

View file

@ -3,7 +3,7 @@
<t t-inherit="web.PhoneField" t-inherit-mode="extension">
<xpath expr="//div[hasclass('o_phone_content')]//a" position="after">
<t t-if="props.enableButton and props.value.length > 0">
<t t-if="props.enableButton and props.record.data[props.name].length > 0">
<SendSMSButton t-props="props" />
</t>
</xpath>
@ -11,7 +11,7 @@
<t t-inherit="web.FormPhoneField" t-inherit-mode="extension">
<xpath expr="//div[hasclass('o_phone_content')]" position="inside">
<t t-if="props.enableButton and props.value.length > 0">
<t t-if="props.enableButton and props.record.data[props.name].length > 0">
<SendSMSButton t-props="props" />
</t>
</xpath>

View file

@ -1,41 +1,44 @@
/** @odoo-module **/
import { _t } from "@web/core/l10n/translation";
import { user } from "@web/core/user";
import { useService } from "@web/core/utils/hooks";
const { Component , status } = owl;
import { Component, status } from "@odoo/owl";
export class SendSMSButton extends Component {
static template = "sms.SendSMSButton";
static props = ["*"];
setup() {
this.action = useService("action");
this.user = useService("user");
this.title = this.env._t("Send SMS Text Message");
this.title = _t("Send SMS");
}
get phoneHref() {
return "sms:" + this.props.value.replace(/\s+/g, "");
return "sms:" + this.props.record.data[this.props.name].replace(/\s+/g, "");
}
async onClick() {
await this.props.record.save();
this.action.doAction({
type: "ir.actions.act_window",
target: "new",
name: this.title,
res_model: "sms.composer",
views: [[false, "form"]],
context: {
...this.user.context,
default_res_model: this.props.record.resModel,
default_res_id: this.props.record.resId,
default_number_field_name: this.props.name,
default_composition_mode: 'comment',
}
}, {
onClose: () => {
if (status(this) !== "destroyed") {
this.props.record.load();
this.props.record.model.notify();
}
this.action.doAction(
{
type: "ir.actions.act_window",
target: "new",
name: this.title,
res_model: "sms.composer",
views: [[false, "form"]],
context: {
...user.context,
default_res_model: this.props.record.resModel,
default_res_id: this.props.record.resId,
default_number_field_name: this.props.name,
default_composition_mode: "comment",
dialog_size: "medium",
},
},
});
{
onClose: () => {
if (status(this) === "destroyed") {
return;
}
this.props.record.load();
},
}
);
}
};
SendSMSButton.template = "sms.SendSMSButton";
}

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="sms.SendSMSButton" owl="1">
<t t-name="sms.SendSMSButton">
<a
t-att-title="title"
t-att-href="phoneHref"

View file

@ -1,52 +1,38 @@
/** @odoo-module **/
import basic_fields from 'web.basic_fields';
import { patch } from "@web/core/utils/patch";
import { EmojisTextField} from '@mail/views/fields/emojis_text_field/emojis_text_field';
import { _t } from "@web/core/l10n/translation";
import {
EmojisTextField,
emojisTextField,
} from "@mail/views/web/fields/emojis_text_field/emojis_text_field";
import { useService } from "@web/core/utils/hooks";
import { registry } from "@web/core/registry";
const DynamicPlaceholderFieldMixin = basic_fields.DynamicPlaceholderFieldMixin;
/**
* SmsWidget is a widget to display a textarea (the body) and a text representing
* the number of SMS and the number of characters. This text is computed every
* time the user changes the body.
*/
export class SmsWidget extends EmojisTextField {
static template = "sms.SmsWidget";
setup() {
super.setup();
this._emojiAdded = () => this.props.record.update({ [this.props.name]: this.targetEditElement.el.value });
this.notification = useService('notification');
}
get encoding() {
return this._extractEncoding(this.props.value || '');
return this._extractEncoding(this.props.record.data[this.props.name] || '');
}
get nbrChar() {
const content = this._getValueForSmsCounts(this.props.value || '');
const content = this._getValueForSmsCounts(this.props.record.data[this.props.name] || "");
return content.length + (content.match(/\n/g) || []).length;
}
get nbrCharExplanation() {
return '';
return "";
}
get nbrSMS() {
return this._countSMS(this.nbrChar, this.encoding);
}
/**
* Open a Model Field Selector in order to select fields
* and create a dynamic placeholder string with or without
* a default text value.
*
* @public
* @param {String} baseModel
* @param {Array} chain
*
*/
async openDynamicPlaceholder(baseModel, chain = []) {
const modelSelector = await this._openNewModelSelector(baseModel, chain);
modelSelector.$el.css('margin-top', 4);
}
//--------------------------------------------------------------------------
// Private: SMS
//--------------------------------------------------------------------------
@ -109,13 +95,14 @@ export class SmsWidget extends EmojisTextField {
* @private
*/
async onBlur() {
var content = this.props.value || '';
await super.onBlur();
var content = this.props.record.data[this.props.name] || '';
if( !content.trim().length && content.length > 0) {
this.notification.add(
this.env._t("Your SMS Text Message must include at least one non-whitespace character"),
_t("Your SMS Text Message must include at least one non-whitespace character"),
{ type: 'danger' },
)
await this.props.update(content.trim());
await this.props.record.update({ [this.props.name]: content.trim() });
}
}
@ -124,18 +111,19 @@ export class SmsWidget extends EmojisTextField {
* @private
*/
async onInput(ev) {
await this.props.update(this.targetEditElement.el.value);
super.onInput(...arguments);
const key = ev.originalEvent ? ev.originalEvent.data : '';
if (this.props.dynamicPlaceholder && key === this.DYNAMIC_PLACEHOLDER_TRIGGER_KEY) {
const baseModel = this.recordData && this.recordData.mailing_model_real ? this.recordData.mailing_model_real : undefined;
if (baseModel) {
this.openDynamicPlaceholder(baseModel);
}
}
await this.props.record.update({ [this.props.name]: this.targetEditElement.el.value });
}
}
export const smsWidget = {
...emojisTextField,
component: SmsWidget,
additionalClasses: [
...(emojisTextField.additionalClasses || []),
"o_field_text",
"o_field_text_emojis",
],
};
SmsWidget.template = 'sms.SmsWidget';
SmsWidget.additionalClasses = [...(EmojisTextField.additionalClasses || []), 'o_field_text'];
patch(SmsWidget.prototype, 'sms_widget_dynamic_placeholder_field_mixin', DynamicPlaceholderFieldMixin);
registry.category("fields").add("sms_widget", SmsWidget);
registry.category("fields").add("sms_widget", smsWidget);

View file

@ -1,17 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="sms.SmsWidget" t-inherit="mail.EmojisTextField" t-inherit-mode="primary" owl="1">
<xpath expr="//textarea[1]" position="attributes">
<attribute name="t-on-blur">onBlur</attribute>
</xpath>
<t t-name="sms.SmsWidget" t-inherit="mail.EmojisTextField" t-inherit-mode="primary">
<xpath expr="//div[hasclass('o_field_input_buttons')]/button[hasclass('fa-magic')]" position="replace"/>
<xpath expr="/*[last()]/*[last()]" position="after">
<div class="o_sms_container">
<span class="text-muted o_sms_count">
<t t-out="nbrChar"/> characters<t t-out="nbrCharExplanation"/>, fits in <t t-out="nbrSMS"/> SMS (<t t-out="encoding"/>)
<div class="o_sms_container mt-3">
<span class="text-muted o_sms_count me-1">
<t t-out="nbrChar"/>/<t t-out="160 * nbrSMS" /> <t t-out="nbrCharExplanation" /> | <t t-out="nbrSMS"/> SMS (<t t-out="encoding"/>)
<a href="https://iap-services.odoo.com/iap/sms/pricing" target="_blank"
title="SMS Pricing" aria-label="SMS Pricing" class="fa fa-lg fa-info-circle"/>
title="SMS Pricing" aria-label="SMS Pricing" class="fa fa-lg fa-info-circle align-middle"/>
</span>
<button t-if="props.dynamicPlaceholder"
class="btn btn-link py-0 border-0"
title="Insert Field"
t-on-click="onDynamicPlaceholderOpen"
><span class="fa fa-magic me-1"/><span>Insert Field</span></button>
</div>
</xpath>
</t>

View file

@ -0,0 +1,23 @@
import { Failure } from "@mail/core/common/failure_model";
import { _t } from "@web/core/l10n/translation";
import { patch } from "@web/core/utils/patch";
patch(Failure.prototype, {
get iconSrc() {
if (this.type === "sms") {
return "/sms/static/img/sms_failure.svg";
}
return super.iconSrc;
},
get body() {
if (this.type === "sms") {
if (this.notifications.length === 1 && this.lastMessage?.thread) {
return _t("An error occurred when sending an SMS on “%(record_name)s”", {
record_name: this.lastMessage.thread.display_name,
});
}
return _t("An error occurred when sending an SMS");
}
return super.body;
},
});

View file

@ -0,0 +1,50 @@
import { Notification } from "@mail/core/common/notification_model";
import { _t } from "@web/core/l10n/translation";
import { patch } from "@web/core/utils/patch";
/** @type {import("models").Notification} */
const notificationPatch = {
get failureMessage() {
switch (this.failure_type) {
case "sms_number_missing":
return _t("Missing Number");
case "sms_number_format":
return _t("Wrong Number Format");
case "sms_credit":
return _t("Insufficient Credit");
case "sms_country_not_supported":
return _t("Country Not Supported");
case "sms_registration_needed":
return _t("Country-specific Registration Required");
case "sms_server":
return _t("Server Error");
case "sms_acc":
return _t("Unregistered Account");
case "sms_expired":
return _t("Expired");
case "sms_invalid_destination":
return _t("Invalid Destination");
case "sms_not_allowed":
return _t("Not Allowed");
case "sms_not_delivered":
return _t("Not Delivered");
case "sms_rejected":
return _t("Rejected");
default:
return super.failureMessage;
}
},
get icon() {
if (this.notification_type === "sms") {
return "fa fa-mobile";
}
return super.icon;
},
get label() {
if (this.notification_type === "sms") {
return _t("SMS");
}
return super.label;
},
};
patch(Notification.prototype, notificationPatch);

View file

@ -1,99 +0,0 @@
odoo.define('sms.fields', function (require) {
"use strict";
var basic_fields = require('web.basic_fields');
var core = require('web.core');
var session = require('web.session');
var _t = core._t;
/**
* Override of FieldPhone to add a button calling SMS composer if option activated (default)
*/
var Phone = basic_fields.FieldPhone;
Phone.include({
/**
* By default, enable_sms is activated
*
* @override
*/
init() {
this._super.apply(this, arguments);
this.enableSMS = 'enable_sms' in this.attrs.options ? this.attrs.options.enable_sms : true;
// reinject in nodeOptions (and thus in this.attrs) to signal the property
this.attrs.options.enable_sms = this.enableSMS;
},
/**
* When the send SMS button is displayed, $el becomes a div wrapping
* the original links.
* This method makes sure we always focus the phone number
*
* @override
*/
getFocusableElement() {
if (this.enableSMS && this.mode === 'readonly') {
return this.$el.filter('.' + this.className).find('a');
}
return this._super.apply(this, arguments);
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Open SMS composer wizard
*
* @private
*/
_onClickSMS: function (ev) {
ev.preventDefault();
ev.stopPropagation();
var context = session.user_context;
context = _.extend({}, context, {
default_res_model: this.model,
default_res_id: parseInt(this.res_id),
default_number_field_name: this.name,
default_composition_mode: 'comment',
});
var self = this;
return this.do_action({
title: _t('Send SMS Text Message'),
type: 'ir.actions.act_window',
res_model: 'sms.composer',
target: 'new',
views: [[false, 'form']],
context: context,
}, {
on_close: function () {
self.trigger_up('reload');
}});
},
/**
* Add a button to call the composer wizard
*
* @override
* @private
*/
_renderReadonly: function () {
var def = this._super.apply(this, arguments);
if (this.enableSMS && this.value) {
var $composerButton = $('<a>', {
title: _t('Send SMS Text Message'),
href: '',
class: 'ms-3 d-inline-flex align-items-center o_field_phone_sms',
html: $('<small>', {class: 'fw-bold ms-1', html: 'SMS'}),
});
$composerButton.prepend($('<i>', {class: 'fa fa-mobile'}));
$composerButton.on('click', this._onClickSMS.bind(this));
this.$el = this.$el.add($composerButton);
}
return def;
},
});
return Phone;
});

View file

@ -0,0 +1,32 @@
import { MessagingMenu } from "@mail/core/public_web/messaging_menu";
import { _t } from "@web/core/l10n/translation";
import { patch } from "@web/core/utils/patch";
patch(MessagingMenu.prototype, {
openFailureView(failure) {
if (failure.type === "email") {
return super.openFailureView(failure);
}
this.env.services.action.doAction({
name: _t("SMS Failures"),
type: "ir.actions.act_window",
view_mode: "kanban,list,form",
views: [
[false, "kanban"],
[false, "list"],
[false, "form"],
],
target: "current",
res_model: failure.resModel,
domain: [["message_has_sms_error", "=", true]],
context: { create: false },
});
this.dropdown.close();
},
getFailureNotificationName(failure) {
if (failure.type === "sms") {
return _t("SMS Failure: %(modelName)s", { modelName: failure.modelName });
}
return super.getFailureNotificationName(...arguments);
},
});

View file

@ -1,26 +0,0 @@
/** @odoo-module **/
import { registerPatch } from '@mail/model/model_core';
registerPatch({
name: 'Message',
recordMethods: {
/**
* @override
*/
openResendAction() {
if (this.message_type === 'sms') {
this.env.services.action.doAction(
'sms.sms_resend_action',
{
additionalContext: {
default_mail_message_id: this.id,
},
},
);
} else {
this._super(...arguments);
}
},
},
});

Some files were not shown because too many files have changed in this diff Show more