Merge: self-heal WOs missing recipe/steps (fusion_plating_jobs 19.0.12.6.0; deployed to entech)
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
{
|
||||
'name': 'Fusion Plating - Native Jobs',
|
||||
'version': '19.0.12.5.0',
|
||||
'version': '19.0.12.6.0',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Native plating job model - replaces mrp.production / mrp.workorder bridge.',
|
||||
'author': 'Nexa Systems Inc.',
|
||||
|
||||
@@ -1385,6 +1385,70 @@ class FpJob(models.Model):
|
||||
# - Drops work_role_id (not on fp.job.step yet - Task 2.6+)
|
||||
# - Drops _fp_autofill_default_equipment (not yet on step)
|
||||
# ------------------------------------------------------------------
|
||||
def _fp_resync_recipe_from_so(self):
|
||||
"""Re-resolve the recipe from this job's SO line(s) and build its
|
||||
steps. Heals work orders created before the recipe was set on the
|
||||
SO line (new parts, or the copy-from-quote path): once the
|
||||
estimator sets the process variant on the line, the WO can pull it
|
||||
in. Acts only on jobs with NO steps yet and not terminal, so
|
||||
in-progress work is never disturbed. Idempotent. Returns True when
|
||||
steps were generated.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.step_ids or self.state in ('done', 'cancelled'):
|
||||
return False
|
||||
so = self.sale_order_id
|
||||
if not so:
|
||||
return False
|
||||
recipe = False
|
||||
for line in self.sale_order_line_ids:
|
||||
recipe = so._fp_resolve_recipe_for_line(line)
|
||||
if recipe:
|
||||
break
|
||||
if not recipe:
|
||||
return False
|
||||
if self.recipe_id != recipe:
|
||||
self.recipe_id = recipe.id
|
||||
# Mirror action_confirm's post-recipe sequence so a healed WO
|
||||
# matches a normally-confirmed one (steps, ready, express text).
|
||||
self._generate_steps_from_recipe()
|
||||
pending = self.step_ids.filtered(lambda s: s.state == 'pending')
|
||||
if pending:
|
||||
pending.write({'state': 'ready'})
|
||||
if (self.recipe_id and self.step_ids
|
||||
and 'x_fc_masking_enabled' in self.env['sale.order.line']._fields):
|
||||
for sol in self.sale_order_line_ids:
|
||||
if hasattr(sol, '_fp_apply_express_overrides_to_job'):
|
||||
sol._fp_apply_express_overrides_to_job(self)
|
||||
self.message_post(body=_(
|
||||
'Recipe re-synced from the sale order (%(recipe)s); %(n)d '
|
||||
'step(s) generated.'
|
||||
) % {'recipe': recipe.display_name, 'n': len(self.step_ids)})
|
||||
return True
|
||||
|
||||
def action_fp_resync_recipe_from_so(self):
|
||||
"""Header button: re-pull the recipe from the SO and build steps
|
||||
for work orders that came out empty because the recipe was set on
|
||||
the SO line after the WO was created. Safe and idempotent.
|
||||
"""
|
||||
healed = self.env['fp.job']
|
||||
for job in self:
|
||||
if job._fp_resync_recipe_from_so():
|
||||
healed |= job
|
||||
if not healed:
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'warning',
|
||||
'message': _('Nothing to re-sync: no resolvable recipe '
|
||||
'on the sale order, or the job already has '
|
||||
'steps.'),
|
||||
'sticky': False,
|
||||
},
|
||||
}
|
||||
return True
|
||||
|
||||
def _generate_steps_from_recipe(self):
|
||||
"""Generate fp.job.step records from the assigned recipe.
|
||||
|
||||
|
||||
@@ -25,6 +25,18 @@ class SaleOrderLine(models.Model):
|
||||
for line in self:
|
||||
old_qty_by_id[line.id] = line.product_uom_qty
|
||||
result = super().write(vals)
|
||||
# Recipe set/changed late on the line -> heal the linked WO that
|
||||
# was created empty before the estimator picked the process. Only
|
||||
# not-yet-started jobs (no steps) are touched.
|
||||
if 'x_fc_process_variant_id' in vals:
|
||||
Job = self.env['fp.job']
|
||||
for line in self:
|
||||
jobs = Job.search([
|
||||
('sale_order_line_ids', 'in', line.id),
|
||||
('state', 'not in', ('done', 'cancelled')),
|
||||
])
|
||||
for job in jobs.filtered(lambda j: not j.step_ids):
|
||||
job.sudo()._fp_resync_recipe_from_so()
|
||||
if 'product_uom_qty' not in vals:
|
||||
return result
|
||||
Job = self.env['fp.job']
|
||||
|
||||
@@ -20,6 +20,14 @@
|
||||
<field name="inherit_id" ref="fusion_plating.view_fp_job_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//header" position="inside">
|
||||
<!-- Heal a WO that was created before the recipe was set
|
||||
on the SO line: re-pull the recipe and build steps.
|
||||
Hidden once steps exist or the job is terminal. -->
|
||||
<button name="action_fp_resync_recipe_from_so" type="object"
|
||||
string="Re-sync Recipe from SO"
|
||||
class="btn-secondary"
|
||||
icon="fa-refresh"
|
||||
invisible="state in ('done', 'cancelled') or step_ids"/>
|
||||
<!-- Phase 1 - Tablet redesign. Opens the JobWorkspace OWL
|
||||
client action focused on this WO. Primary entry point
|
||||
for techs before the Landing kanban (Phase 3) ships;
|
||||
|
||||
Reference in New Issue
Block a user