feat(fusion_claims): add dashboard activities and bottlenecks
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -149,3 +149,61 @@ class FusionClaimsDashboard(models.TransientModel):
|
||||
ar_invoices = Move.search(ar_domain)
|
||||
rec.kpi_ar_count = len(ar_invoices)
|
||||
rec.kpi_ar_amount = sum(ar_invoices.mapped('amount_total'))
|
||||
|
||||
# =========================================================================
|
||||
# Activities (left column)
|
||||
# =========================================================================
|
||||
my_activities_count = fields.Integer(compute='_compute_activities')
|
||||
my_activities_html = fields.Html(compute='_compute_activities', sanitize=False)
|
||||
|
||||
def _compute_activities(self):
|
||||
Activity = self.env['mail.activity'].sudo()
|
||||
domain = [
|
||||
('user_id', '=', self.env.user.id),
|
||||
('res_model', 'in', ['sale.order', 'account.move', 'fusion.technician.task']),
|
||||
]
|
||||
for rec in self:
|
||||
activities = Activity.search(domain, order='date_deadline asc', limit=10)
|
||||
rec.my_activities_count = Activity.search_count(domain)
|
||||
if not activities:
|
||||
rec.my_activities_html = (
|
||||
'<p class="o_fc_empty">No activities assigned.</p>'
|
||||
)
|
||||
continue
|
||||
from datetime import date
|
||||
today = date.today()
|
||||
rows = []
|
||||
for act in activities:
|
||||
overdue = act.date_deadline and act.date_deadline < today
|
||||
row_class = 'o_fc_activity_row o_fc_activity_overdue' if overdue else 'o_fc_activity_row'
|
||||
deadline_text = act.date_deadline.strftime('%b %d') if act.date_deadline else '—'
|
||||
url = f'/odoo/{act.res_model.replace(".", "_")}/{act.res_id}'
|
||||
rows.append(
|
||||
f'<div class="{row_class}">'
|
||||
f'<a href="{url}"><b>{act.summary or act.activity_type_id.name or "Activity"}</b></a>'
|
||||
f'<span class="o_fc_activity_deadline">{deadline_text}</span>'
|
||||
f'</div>'
|
||||
)
|
||||
rec.my_activities_html = '\n'.join(rows)
|
||||
|
||||
# =========================================================================
|
||||
# Bottlenecks (left column)
|
||||
# =========================================================================
|
||||
bottleneck_no_pod_count = fields.Integer(compute='_compute_secondary_counts')
|
||||
bottleneck_no_response_count = fields.Integer(compute='_compute_secondary_counts')
|
||||
|
||||
def _compute_secondary_counts(self):
|
||||
from datetime import date, timedelta
|
||||
SO = self.env['sale.order'].sudo()
|
||||
cutoff_14d_ago = date.today() - timedelta(days=14)
|
||||
for rec in self:
|
||||
base = rec._role_filter_domain()
|
||||
|
||||
rec.bottleneck_no_pod_count = SO.search_count(base + [
|
||||
('x_fc_adp_application_status', 'in', ['approved', 'approved_deduction']),
|
||||
('x_fc_proof_of_delivery', '=', False),
|
||||
])
|
||||
rec.bottleneck_no_response_count = SO.search_count(base + [
|
||||
('x_fc_adp_application_status', 'in', ['submitted', 'resubmitted']),
|
||||
('x_fc_claim_submission_date', '<', cutoff_14d_ago),
|
||||
])
|
||||
|
||||
@@ -160,3 +160,51 @@ class TestFusionClaimsDashboard(TransactionCase):
|
||||
dashboard_rep = self.Dashboard.with_user(self.salesrep).create({})
|
||||
self.assertEqual(dashboard_rep.kpi_ready_count, 0,
|
||||
"Salesrep must not see manager's invoice")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Task 4 — Activities + bottlenecks
|
||||
# -------------------------------------------------------------------------
|
||||
def test_my_activities_count_zero_when_none(self):
|
||||
dashboard = self.Dashboard.with_user(self.manager).create({})
|
||||
self.assertEqual(dashboard.my_activities_count, 0)
|
||||
|
||||
def test_my_activities_count_picks_up_user_activity(self):
|
||||
so = self.env['sale.order'].with_context(skip_status_validation=True).create({
|
||||
'partner_id': self.partner.id,
|
||||
'user_id': self.manager.id,
|
||||
'x_fc_sale_type': 'adp',
|
||||
})
|
||||
self.env['mail.activity'].create({
|
||||
'res_model_id': self.env['ir.model']._get('sale.order').id,
|
||||
'res_id': so.id,
|
||||
'res_model': 'sale.order',
|
||||
'user_id': self.manager.id,
|
||||
'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id,
|
||||
'summary': 'Test activity',
|
||||
})
|
||||
dashboard = self.Dashboard.with_user(self.manager).create({})
|
||||
self.assertEqual(dashboard.my_activities_count, 1)
|
||||
self.assertIn('Test activity', dashboard.my_activities_html or '')
|
||||
|
||||
def test_bottleneck_no_pod_count(self):
|
||||
self.env['sale.order'].with_context(skip_status_validation=True).create({
|
||||
'partner_id': self.partner.id,
|
||||
'user_id': self.manager.id,
|
||||
'x_fc_sale_type': 'adp',
|
||||
'x_fc_adp_application_status': 'approved',
|
||||
})
|
||||
dashboard = self.Dashboard.with_user(self.manager).create({})
|
||||
self.assertEqual(dashboard.bottleneck_no_pod_count, 1)
|
||||
|
||||
def test_bottleneck_no_response_count(self):
|
||||
from datetime import date, timedelta
|
||||
old_date = date.today() - timedelta(days=20)
|
||||
self.env['sale.order'].with_context(skip_status_validation=True).create({
|
||||
'partner_id': self.partner.id,
|
||||
'user_id': self.manager.id,
|
||||
'x_fc_sale_type': 'adp',
|
||||
'x_fc_adp_application_status': 'submitted',
|
||||
'x_fc_claim_submission_date': old_date,
|
||||
})
|
||||
dashboard = self.Dashboard.with_user(self.manager).create({})
|
||||
self.assertEqual(dashboard.bottleneck_no_response_count, 1)
|
||||
|
||||
Reference in New Issue
Block a user