forked from Mapan/odoo17e
169 lines
7.1 KiB
Python
169 lines
7.1 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import api, fields, models, _
|
|
from odoo.osv import expression
|
|
|
|
|
|
class VoipCall(models.Model):
|
|
_name = "voip.call"
|
|
_description = """A phone call handled using the VoIP application"""
|
|
|
|
phone_number = fields.Char(required=True, readonly=True)
|
|
direction = fields.Selection(
|
|
[
|
|
("incoming", "Incoming"),
|
|
("outgoing", "Outgoing"),
|
|
],
|
|
default="outgoing",
|
|
readonly=True,
|
|
)
|
|
state = fields.Selection(
|
|
[
|
|
("aborted", "Aborted"),
|
|
("calling", "Calling"),
|
|
("missed", "Missed"),
|
|
("ongoing", "Ongoing"),
|
|
("rejected", "Rejected"),
|
|
("terminated", "Terminated"),
|
|
],
|
|
default="calling",
|
|
index=True,
|
|
)
|
|
end_date = fields.Datetime()
|
|
start_date = fields.Datetime()
|
|
# Since activities are deleted from the database once marked as done, the
|
|
# activity name is saved here in order to be preserved.
|
|
activity_name = fields.Char(help="The name of the activity related to this phone call, if any.")
|
|
partner_id = fields.Many2one("res.partner", "Contact", index=True)
|
|
user_id = fields.Many2one("res.users", "Responsible", default=lambda self: self.env.uid, index=True)
|
|
|
|
@api.depends("state", "partner_id.name")
|
|
def _compute_display_name(self):
|
|
def get_name(call):
|
|
if call.activity_name:
|
|
return call.activity_name
|
|
if call.state == "aborted":
|
|
return _("Aborted call to %(phone_number)s", phone_number=call.phone_number)
|
|
if call.state == "missed":
|
|
return _("Missed call from %(phone_number)s", phone_number=call.phone_number)
|
|
if call.state == "rejected":
|
|
if call.direction == "incoming":
|
|
return _("Rejected call from %(phone_number)s", phone_number=call.phone_number)
|
|
return _("Rejected call to %(phone_number)s", phone_number=call.phone_number)
|
|
if call.partner_id:
|
|
if call.direction == "incoming":
|
|
return _("Call from %(correspondent)s", correspondent=call.partner_id.name)
|
|
return _("Call to %(correspondent)s", correspondent=call.partner_id.name)
|
|
if call.direction == "incoming":
|
|
return _("Call from %(phone_number)s", phone_number=call.phone_number)
|
|
return _("Call to %(phone_number)s", phone_number=call.phone_number)
|
|
|
|
for call in self:
|
|
call.display_name = get_name(call)
|
|
|
|
@api.model
|
|
def create_and_format(self, res_id: int = None, res_model: str = None, **kwargs) -> list:
|
|
"""Creates a call from the provided values and returns it formatted for
|
|
use in JavaScript. If a record is provided via its id and model,
|
|
introspects it for a recipient.
|
|
"""
|
|
if res_id and res_model:
|
|
related_record = self.env[res_model].browse(res_id)
|
|
kwargs["partner_id"] = next(
|
|
iter(related_record._mail_get_partners(introspect_fields=True)[related_record.id]),
|
|
self.env["res.partner"],
|
|
).id
|
|
return self.create(kwargs)._format_calls()
|
|
|
|
@api.model
|
|
def get_recent_phone_calls(self, search_terms: str = None, offset: int = 0, limit: int = None) -> list:
|
|
domain = [("user_id", "=", self.env.uid)]
|
|
if search_terms:
|
|
search_fields = ["phone_number", "partner_id.name", "activity_name"]
|
|
search_domain = expression.OR([[(field, "ilike", search_terms)] for field in search_fields])
|
|
domain += search_domain
|
|
return self.search(domain, offset=offset, limit=limit, order="create_date DESC")._format_calls()
|
|
|
|
@api.model
|
|
def _get_number_of_missed_calls(self) -> int:
|
|
domain = [("user_id", "=", self.env.uid), ("state", "=", "missed")]
|
|
last_seen_phone_call = self.env.user.last_seen_phone_call
|
|
if last_seen_phone_call:
|
|
domain += [("id", ">", last_seen_phone_call.id)]
|
|
return self.search_count(domain)
|
|
|
|
def abort_call(self) -> list:
|
|
self.state = "aborted"
|
|
return self._format_calls()
|
|
|
|
def start_call(self) -> list:
|
|
self.start_date = fields.Datetime.now()
|
|
self.state = "ongoing"
|
|
return self._format_calls()
|
|
|
|
def end_call(self, activity_name: str = None) -> list:
|
|
self.end_date = fields.Datetime.now()
|
|
self.state = "terminated"
|
|
if activity_name:
|
|
self.activity_name = activity_name
|
|
return self._format_calls()
|
|
|
|
def reject_call(self) -> list:
|
|
self.state = "rejected"
|
|
return self._format_calls()
|
|
|
|
def miss_call(self) -> list:
|
|
self.state = "missed"
|
|
return self._format_calls()
|
|
|
|
def get_contact_info(self):
|
|
self.ensure_one()
|
|
number = self.phone_number
|
|
# Internal extensions could theoretically be one or two digits long.
|
|
# phone_mobile_search doesn't handle numbers that short: do a regular
|
|
# search for the exact match:
|
|
if len(number) < 3:
|
|
domain = ["|", ("phone", "=", number), ("mobile", "=", number)]
|
|
# 00 and + both denote an international prefix. phone_mobile_search will
|
|
# match both indifferently.
|
|
elif number.startswith(("+", "00")):
|
|
domain = [("phone_mobile_search", "=", number)]
|
|
# USA: Calls between different area codes are usually prefixed with 1.
|
|
# Conveniently, the country code for the USA also happens to be 1, so we
|
|
# just need to add the + symbol to format it like an international call
|
|
# and match what's supposed to be stored in the database.
|
|
elif number.startswith("1"):
|
|
domain = [("phone_mobile_search", "=", f"+{number}")]
|
|
# 0 is the national prefix recommended by the ITU-T. This means that a
|
|
# number starting with 0 will identify a national call in most
|
|
# countries. We don't know the country code, so we can't convert the
|
|
# number to the international format: Replace the prefix with a
|
|
# wildcard.
|
|
elif number.startswith("0"):
|
|
domain = [("phone_mobile_search", "=like", f"%{number[1:]}")]
|
|
# The phone number doesn't match any expected format. Could it be some
|
|
# kind of internal extension? Try looking for an exact match:
|
|
else:
|
|
domain = [("phone_mobile_search", "=", number)]
|
|
partner = self.env["res.partner"].search(domain, limit=1)
|
|
if not partner:
|
|
return False
|
|
self.partner_id = partner
|
|
return self.partner_id._format_contacts()[0]
|
|
|
|
def _format_calls(self) -> list:
|
|
return [
|
|
{
|
|
"id": call.id,
|
|
"creationDate": call.create_date,
|
|
"direction": call.direction,
|
|
"displayName": call.display_name,
|
|
"endDate": call.end_date,
|
|
"partner": call.partner_id._format_contacts()[0] if call.partner_id else False,
|
|
"phoneNumber": call.phone_number,
|
|
"startDate": call.start_date,
|
|
"state": call.state,
|
|
}
|
|
for call in self
|
|
]
|