202 lines
7.6 KiB
Python
202 lines
7.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2026 Nexa Systems Inc.
|
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
from odoo import api, fields, models
|
|
|
|
|
|
class RcCallDashboard(models.TransientModel):
|
|
_name = 'rc.call.dashboard'
|
|
_description = 'RingCentral Call Dashboard'
|
|
_rec_name = 'name'
|
|
|
|
name = fields.Char(default='Call Dashboard', readonly=True)
|
|
|
|
total_count = fields.Integer(compute='_compute_stats')
|
|
inbound_count = fields.Integer(compute='_compute_stats')
|
|
outbound_count = fields.Integer(compute='_compute_stats')
|
|
answered_count = fields.Integer(compute='_compute_stats')
|
|
missed_count = fields.Integer(compute='_compute_stats')
|
|
avg_duration = fields.Float(compute='_compute_stats', string='Avg Duration (min)')
|
|
success_rate = fields.Float(compute='_compute_stats', string='Success Rate (%)')
|
|
|
|
today_count = fields.Integer(compute='_compute_time_stats', string='Today')
|
|
week_count = fields.Integer(compute='_compute_time_stats', string='This Week')
|
|
month_count = fields.Integer(compute='_compute_time_stats', string='This Month')
|
|
|
|
voicemail_count = fields.Integer(compute='_compute_voicemail_stats')
|
|
unread_voicemail_count = fields.Integer(compute='_compute_voicemail_stats')
|
|
|
|
recent_call_ids = fields.Many2many(
|
|
'rc.call.history',
|
|
compute='_compute_recent_calls',
|
|
)
|
|
|
|
recent_voicemail_ids = fields.Many2many(
|
|
'rc.voicemail', 'rc_dashboard_recent_vm_rel',
|
|
compute='_compute_voicemail_lists',
|
|
)
|
|
older_voicemail_ids = fields.Many2many(
|
|
'rc.voicemail', 'rc_dashboard_older_vm_rel',
|
|
compute='_compute_voicemail_lists',
|
|
)
|
|
|
|
@api.depends_context('uid')
|
|
def _compute_stats(self):
|
|
Call = self.env['rc.call.history']
|
|
for rec in self:
|
|
all_calls = Call.search([])
|
|
rec.total_count = len(all_calls)
|
|
rec.inbound_count = Call.search_count([('direction', '=', 'inbound')])
|
|
rec.outbound_count = Call.search_count([('direction', '=', 'outbound')])
|
|
rec.answered_count = Call.search_count([('status', '=', 'answered')])
|
|
rec.missed_count = Call.search_count([('status', 'in', ('missed', 'no_answer'))])
|
|
|
|
durations = all_calls.mapped('duration')
|
|
rec.avg_duration = round(sum(durations) / len(durations) / 60, 1) if durations else 0.0
|
|
rec.success_rate = round(
|
|
(rec.answered_count / rec.total_count * 100) if rec.total_count else 0.0, 1
|
|
)
|
|
|
|
@api.depends_context('uid')
|
|
def _compute_time_stats(self):
|
|
Call = self.env['rc.call.history']
|
|
now = datetime.utcnow()
|
|
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
week_start = today_start - timedelta(days=today_start.weekday())
|
|
month_start = today_start.replace(day=1)
|
|
|
|
for rec in self:
|
|
rec.today_count = Call.search_count([('start_time', '>=', today_start)])
|
|
rec.week_count = Call.search_count([('start_time', '>=', week_start)])
|
|
rec.month_count = Call.search_count([('start_time', '>=', month_start)])
|
|
|
|
@api.depends_context('uid')
|
|
def _compute_recent_calls(self):
|
|
for rec in self:
|
|
rec.recent_call_ids = self.env['rc.call.history'].search([], limit=20)
|
|
|
|
@api.depends_context('uid')
|
|
def _compute_voicemail_lists(self):
|
|
VM = self.env['rc.voicemail']
|
|
one_week_ago = datetime.utcnow() - timedelta(days=7)
|
|
for rec in self:
|
|
rec.recent_voicemail_ids = VM.search([
|
|
('received_date', '>=', one_week_ago),
|
|
], limit=50)
|
|
rec.older_voicemail_ids = VM.search([
|
|
('received_date', '<', one_week_ago),
|
|
], limit=100)
|
|
|
|
@api.depends_context('uid')
|
|
def _compute_voicemail_stats(self):
|
|
VM = self.env['rc.voicemail']
|
|
for rec in self:
|
|
rec.voicemail_count = VM.search_count([])
|
|
rec.unread_voicemail_count = VM.search_count(
|
|
[('read_status', '=', 'Unread')],
|
|
)
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# Navigation actions
|
|
# ──────────────────────────────────────────────────────────
|
|
|
|
def action_open_all(self):
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': 'Call History',
|
|
'res_model': 'rc.call.history',
|
|
'view_mode': 'list,form',
|
|
}
|
|
|
|
def action_open_inbound(self):
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': 'Inbound Calls',
|
|
'res_model': 'rc.call.history',
|
|
'view_mode': 'list,form',
|
|
'domain': [('direction', '=', 'inbound')],
|
|
}
|
|
|
|
def action_open_outbound(self):
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': 'Outbound Calls',
|
|
'res_model': 'rc.call.history',
|
|
'view_mode': 'list,form',
|
|
'domain': [('direction', '=', 'outbound')],
|
|
}
|
|
|
|
def action_open_missed(self):
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': 'Missed Calls',
|
|
'res_model': 'rc.call.history',
|
|
'view_mode': 'list,form',
|
|
'domain': [('status', 'in', ('missed', 'no_answer'))],
|
|
}
|
|
|
|
def action_open_graph(self):
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': 'Call Analytics',
|
|
'res_model': 'rc.call.history',
|
|
'view_mode': 'graph,pivot,list',
|
|
}
|
|
|
|
def action_open_voicemails(self):
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': 'Voicemails',
|
|
'res_model': 'rc.voicemail',
|
|
'view_mode': 'list,form',
|
|
}
|
|
|
|
def action_open_unread_voicemails(self):
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': 'Voicemails',
|
|
'res_model': 'rc.call.dashboard',
|
|
'view_mode': 'form',
|
|
'view_id': self.env.ref(
|
|
'fusion_ringcentral.view_rc_voicemail_dashboard_form',
|
|
).id,
|
|
'target': 'current',
|
|
}
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# Quick action buttons (Call / Message / Fax)
|
|
# ──────────────────────────────────────────────────────────
|
|
|
|
def action_quick_call(self):
|
|
"""Open the RingCentral dialer via the Embeddable widget."""
|
|
return {
|
|
'type': 'ir.actions.act_window_close',
|
|
'infos': {
|
|
'rc_action': 'call',
|
|
'rc_phone_number': '',
|
|
},
|
|
}
|
|
|
|
def action_quick_sms(self):
|
|
"""Open the RingCentral SMS compose via the Embeddable widget."""
|
|
return {
|
|
'type': 'ir.actions.act_window_close',
|
|
'infos': {
|
|
'rc_action': 'sms',
|
|
'rc_phone_number': '',
|
|
},
|
|
}
|
|
|
|
def action_quick_fax(self):
|
|
"""Open the Send Fax wizard."""
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': 'Send Fax',
|
|
'res_model': 'fusion_faxes.send.fax.wizard',
|
|
'view_mode': 'form',
|
|
'target': 'new',
|
|
}
|