# -*- coding: utf-8 -*- # Copyright 2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) # # /fp/jobs/process_tree — JSON endpoint that returns the recipe tree # for a given fp.job, with each node tagged by the matching # fp.job.step (if any) and its current state. from odoo import http from odoo.http import request class FpJobProcessTreeController(http.Controller): @http.route('/fp/jobs/process_tree', type='jsonrpc', auth='user', website=False) def fp_jobs_process_tree(self, job_id, **kwargs): Job = request.env['fp.job'] job = Job.browse(int(job_id)).exists() if not job: return {'error': 'Job not found'} # Map recipe_node_id -> step step_by_node = {s.recipe_node_id.id: s for s in job.step_ids if s.recipe_node_id} def serialize(node): step = step_by_node.get(node.id) return { 'id': node.id, 'name': node.name, 'node_type': node.node_type, 'sequence': node.sequence, 'step_id': step.id if step else None, 'step_state': step.state if step else None, 'step_assigned_user': step.assigned_user_id.name if step and step.assigned_user_id else None, 'duration_expected': step.duration_expected if step else node.estimated_duration, 'duration_actual': step.duration_actual if step else 0.0, 'children': [serialize(c) for c in node.child_ids.sorted('sequence')], } return { 'job_name': job.name, 'partner': job.partner_id.name, 'state': job.state, 'qty': job.qty, 'recipe_name': job.recipe_id.name if job.recipe_id else '', 'progress_pct': job.step_progress_pct, 'tree': serialize(job.recipe_id) if job.recipe_id else None, }