feat(quality_dashboard): banner with overdue + critical (Task 4)
_fetch_banner_candidates collects (overdue) OR (critical-customer + open) records per type. _critical_customer_ids reuses partner.x_fc_rush and partner.x_fc_vip flags when defined (gracefully no-ops when absent). _critical_badge returns RUSH/VIP/AEROSPACE/AS9100 label when the banner reason is critical-customer (no badge when overdue). _build_banner ranks: overdue first by oldest, then critical-customer by oldest, takes top 6, reports total_matching. build() now collects banner candidates from every section in one pass + invokes _build_banner once. Tests cover overdue hold pickup, 6-cap with overflow count, and all_clear when DB is empty. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -109,3 +109,64 @@ class TestDashboardSnapshotItems(TransactionCase):
|
||||
self.assertEqual(item['open_action']['res_model'],
|
||||
'fusion.plating.quality.hold')
|
||||
self.assertEqual(item['open_action']['res_id'], hold.id)
|
||||
|
||||
|
||||
class TestDashboardSnapshotBanner(TransactionCase):
|
||||
"""Banner population + ranking."""
|
||||
|
||||
def _build(self):
|
||||
from odoo.addons.fusion_plating_quality.controllers.fp_quality_dashboard \
|
||||
import FpQualityDashboardSnapshot
|
||||
return FpQualityDashboardSnapshot(self.env).build()
|
||||
|
||||
def test_banner_picks_up_overdue_hold(self):
|
||||
from datetime import datetime, timedelta
|
||||
if 'fusion.plating.quality.hold' not in self.env:
|
||||
self.skipTest('fusion.plating.quality.hold not installed')
|
||||
partner = self.env['res.partner'].create({'name': 'Cust'})
|
||||
hold = self.env['fusion.plating.quality.hold'].create({
|
||||
'partner_id': partner.id, 'state': 'on_hold', 'reason': 'old',
|
||||
})
|
||||
# Backdate create_date past the 3-day overdue threshold
|
||||
old = datetime.now() - timedelta(days=5)
|
||||
self.env.cr.execute(
|
||||
"UPDATE fusion_plating_quality_hold SET create_date = %s WHERE id = %s",
|
||||
(old, hold.id),
|
||||
)
|
||||
hold.invalidate_recordset(['create_date'])
|
||||
|
||||
snap = self._build()
|
||||
self.assertFalse(snap['banner']['all_clear'])
|
||||
self.assertEqual(snap['banner']['total_matching'], 1)
|
||||
self.assertEqual(len(snap['banner']['items']), 1)
|
||||
item = snap['banner']['items'][0]
|
||||
self.assertEqual(item['type'], 'hold')
|
||||
self.assertEqual(item['urgency'], 'overdue')
|
||||
|
||||
def test_banner_caps_at_6_with_overflow_count(self):
|
||||
from datetime import datetime, timedelta
|
||||
if 'fusion.plating.quality.hold' not in self.env:
|
||||
self.skipTest('fusion.plating.quality.hold not installed')
|
||||
partner = self.env['res.partner'].create({'name': 'Cust'})
|
||||
Hold = self.env['fusion.plating.quality.hold']
|
||||
# 8 overdue holds
|
||||
old = datetime.now() - timedelta(days=5)
|
||||
for i in range(8):
|
||||
h = Hold.create({
|
||||
'partner_id': partner.id, 'state': 'on_hold',
|
||||
'reason': f'overdue {i}',
|
||||
})
|
||||
self.env.cr.execute(
|
||||
"UPDATE fusion_plating_quality_hold SET create_date = %s WHERE id = %s",
|
||||
(old, h.id),
|
||||
)
|
||||
snap = self._build()
|
||||
self.assertEqual(len(snap['banner']['items']), 6)
|
||||
self.assertEqual(snap['banner']['total_matching'], 8)
|
||||
self.assertFalse(snap['banner']['all_clear'])
|
||||
|
||||
def test_banner_all_clear_when_zero(self):
|
||||
snap = self._build()
|
||||
# Empty DB — no overdue, no critical
|
||||
self.assertTrue(snap['banner']['all_clear'])
|
||||
self.assertEqual(snap['banner']['items'], [])
|
||||
|
||||
Reference in New Issue
Block a user