diff --git a/fusion_claims/models/dashboard.py b/fusion_claims/models/dashboard.py
index dabf506c..84f41c2c 100644
--- a/fusion_claims/models/dashboard.py
+++ b/fusion_claims/models/dashboard.py
@@ -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 = (
+ '
No activities assigned.
'
+ )
+ 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''
+ )
+ 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),
+ ])
diff --git a/fusion_claims/tests/test_dashboard.py b/fusion_claims/tests/test_dashboard.py
index 278d63bf..589687e9 100644
--- a/fusion_claims/tests/test_dashboard.py
+++ b/fusion_claims/tests/test_dashboard.py
@@ -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)