Initial commit
This commit is contained in:
201
fusion_ringcentral/models/rc_call_dashboard.py
Normal file
201
fusion_ringcentral/models/rc_call_dashboard.py
Normal file
@@ -0,0 +1,201 @@
|
||||
# -*- 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',
|
||||
}
|
||||
Reference in New Issue
Block a user