# -*- coding: utf-8 -*- # Copyright 2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) # Part of the Fusion Plating product family. # # Sub 12 Phase D — counts endpoint for the Unified Quality Dashboard. from odoo import fields, http from odoo.http import request class FpQualityDashboardController(http.Controller): @http.route('/fp/quality/dashboard/counts', type='jsonrpc', auth='user', methods=['POST']) def counts(self): """Return per-tab open + overdue counts for the dashboard. "Overdue" definition: - Hold: state='on_hold' for > 3 days - Check: state='pending' for > 1 day - NCR: state in (open, containment, disposition) AND reported >7d - CAPA: due_date < today AND state not in (effective, closed) - RMA: state='received' for > 5 days (triage past due) OR state in (authorised, shipped_to_us) for > 14 days """ env = request.env today = fields.Date.context_today(env.user) now = fields.Datetime.now() Hold = env['fusion.plating.quality.hold'] Check = env['fusion.plating.quality.check'] Ncr = env['fusion.plating.ncr'] Capa = env['fusion.plating.capa'] Rma = env['fusion.plating.rma'] d3 = fields.Datetime.subtract(now, days=3) d1 = fields.Datetime.subtract(now, days=1) d7 = fields.Datetime.subtract(now, days=7) d5 = fields.Datetime.subtract(now, days=5) d14 = fields.Datetime.subtract(now, days=14) return { 'holds': { 'open': Hold.search_count( [('state', 'in', ('on_hold', 'under_review'))]), 'overdue': Hold.search_count([ ('state', 'in', ('on_hold', 'under_review')), ('create_date', '<', d3), ]), }, 'checks': { 'open': Check.search_count([('state', '=', 'pending')]), 'overdue': Check.search_count([ ('state', '=', 'pending'), ('create_date', '<', d1), ]), }, 'ncrs': { 'open': Ncr.search_count([ ('state', 'in', ('open', 'containment', 'disposition')), ]), 'overdue': Ncr.search_count([ ('state', 'in', ('open', 'containment', 'disposition')), ('reported_date', '<', d7), ]), }, 'capas': { 'open': Capa.search_count([ ('state', 'not in', ('effective', 'closed')), ]), 'overdue': Capa.search_count([ ('state', 'not in', ('effective', 'closed')), ('due_date', '<', today), ('due_date', '!=', False), ]), }, 'rmas': { 'open': Rma.search_count([ ('state', 'not in', ('closed', 'cancelled')), ]), 'overdue': Rma.search_count([ '|', '&', ('state', '=', 'received'), ('create_date', '<', d5), '&', ('state', 'in', ('authorised', 'shipped_to_us')), ('create_date', '<', d14), ]), }, }