fusion_plating_bridge_mrp: per-job recipe overrides with config wizard (v19.0.2.0.0)

Links recipes to manufacturing orders via x_fc_recipe_id on mrp.production.
New model fusion.plating.job.node.override stores per-job opt-in/out
decisions for optional recipe steps.

Config wizard (fp.recipe.config.wizard) shows all optional nodes as a
checklist — opt-in steps default unchecked, opt-out steps default checked.
Planner toggles and confirms. "Overrides" stat button on MO form opens wizard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-12 15:36:03 -04:00
parent cb57585b5a
commit 4185b149bd
10 changed files with 319 additions and 1 deletions

View File

@@ -11,4 +11,5 @@ from . import fp_portal_job
from . import fp_quality_hold
from . import fp_delivery
from . import fp_batch
from . import fp_job_node_override
from . import account_move

View File

@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import fields, models
class FpJobNodeOverride(models.Model):
"""Per-job override for optional recipe steps.
When a recipe is assigned to a manufacturing order, nodes with
opt_in_out != 'disabled' can be toggled on or off for that specific
job. Opt-in nodes default to excluded; opt-out nodes default to
included. The planner changes these via the configuration wizard.
"""
_name = 'fusion.plating.job.node.override'
_description = 'Fusion Plating — Job Node Override'
_order = 'node_sequence, id'
production_id = fields.Many2one(
'mrp.production',
string='Manufacturing Order',
required=True,
ondelete='cascade',
index=True,
)
node_id = fields.Many2one(
'fusion.plating.process.node',
string='Recipe Step',
required=True,
ondelete='cascade',
)
node_name = fields.Char(
related='node_id.name',
string='Step Name',
readonly=True,
)
node_type = fields.Selection(
related='node_id.node_type',
string='Type',
readonly=True,
)
node_sequence = fields.Integer(
related='node_id.sequence',
string='Sequence',
readonly=True,
store=True,
)
opt_in_out = fields.Selection(
related='node_id.opt_in_out',
string='Default',
readonly=True,
)
included = fields.Boolean(
string='Included',
default=True,
help='Whether this optional step is active for this job.',
)
_sql_constraints = [
('unique_production_node',
'unique(production_id, node_id)',
'Each recipe step can only have one override per job.'),
]

View File

@@ -3,7 +3,8 @@
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import api, fields, models
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class MrpProduction(models.Model):
@@ -27,6 +28,44 @@ class MrpProduction(models.Model):
string='Portal Job',
help='The portal job linked to this manufacturing order.',
)
x_fc_recipe_id = fields.Many2one(
'fusion.plating.process.node',
string='Recipe',
domain=[('node_type', '=', 'recipe')],
help='Process recipe template for this manufacturing order.',
tracking=True,
)
x_fc_override_ids = fields.One2many(
'fusion.plating.job.node.override',
'production_id',
string='Recipe Overrides',
)
x_fc_override_count = fields.Integer(
string='Overrides',
compute='_compute_override_count',
)
@api.depends('x_fc_override_ids')
def _compute_override_count(self):
for rec in self:
rec.x_fc_override_count = len(rec.x_fc_override_ids)
def action_configure_recipe_steps(self):
"""Open the wizard to configure opt-in/out steps for this job."""
self.ensure_one()
if not self.x_fc_recipe_id:
raise UserError(_('Please select a recipe first.'))
return {
'type': 'ir.actions.act_window',
'name': f'Configure Steps — {self.x_fc_recipe_id.name}',
'res_model': 'fp.recipe.config.wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_recipe_id': self.x_fc_recipe_id.id,
},
}
# ------------------------------------------------------------------
# GAP 2: SO confirm → MO confirm → auto-create Portal Job