1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/voip/models/voip_call.py
2024-12-10 09:04:09 +07:00

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
]