feat(jobs): step sequences are 1, 2, 3, ... not 10, 20, 30, ...

User feedback: operators kept asking why their work order said "Step 10"
for the first row. The 10-spacing was originally there to allow midpoint
inserts (insert sequence 15 between 10 and 20 without renumbering).
Tradeoff is operator confusion, and recipe authors rarely insert in the
middle anyway. Switching to 1-based contiguous sequences.

Files changed (every step-sequence allocation in the codebase):

fusion_plating_jobs/models/fp_job.py
  _generate_steps_from_recipe — seq_counter starts at 1, increments by 1.
  This is the path that builds fp.job.step records, so new jobs now show
  Step 1, 2, 3, ... in the work order.

fusion_plating_bridge_mrp/models/mrp_production.py
  Same change for the legacy MRP bridge so customers still on
  mrp.production also get 1-based numbering.

fusion_plating/controllers/recipe_controller.py
  - create_node: max_seq + 1
  - reorder_nodes: idx + 1
  - swap renumber: i (was i * 10)
  - paste-import renumber: i (was i * 10)
  - move_node: max_seq + 1
  - _copy_subtree (recipe duplicate/import): i (was i * 10)

fusion_plating/controllers/simple_recipe_controller.py
  - _sequence_for_position rewritten — always renumbers siblings to
    keep them contiguous. Returns pos + 1 for the inserted node.
    Old code used midpoint-with-fallback-to-renumber (10/20/30 spacing).
  - step_reorder: i (was i * 10)
  - library_input_add + step_add_input: existing_max + 1

What this DOESN'T do
  Existing fp.job.step records keep their old sequences (10, 20, ...).
  Re-confirm the SO to spawn a fresh job if you want the clean 1-based
  numbering on a current test job. No data migration — we're in dev
  and the user explicitly said test data is disposable.

What this DOES do
  Every NEW job created from this commit forward shows Step 1, 2, 3, ...
  Every NEW recipe step inserted via the simple editor / tree editor
  also gets sequence 1, 2, 3, ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-03 22:58:21 -04:00
parent e37eab9f23
commit 32d48ea44d
7 changed files with 39 additions and 33 deletions

View File

@@ -5,7 +5,7 @@
{
'name': 'Fusion Plating',
'version': '19.0.18.12.0',
'version': '19.0.18.12.1',
'category': 'Manufacturing/Plating',
'summary': 'Core plating / metal finishing ERP: facilities, processes, tanks, baths, jobs, operators.',
'description': """

View File

@@ -53,7 +53,7 @@ class FpRecipeController(http.Controller):
'name': name,
'node_type': node_type,
'parent_id': parent.id,
'sequence': max_seq + 10,
'sequence': max_seq + 1,
}
if vals:
data.update(vals)
@@ -132,7 +132,7 @@ class FpRecipeController(http.Controller):
Node = request.env['fusion.plating.process.node']
try:
for idx, nid in enumerate(node_ids):
Node.browse(int(nid)).write({'sequence': (idx + 1) * 10})
Node.browse(int(nid)).write({'sequence': idx + 1})
return {'ok': True}
except Exception as exc:
_logger.exception('Recipe reorder failed')
@@ -195,7 +195,7 @@ class FpRecipeController(http.Controller):
if a_seq == b_seq:
# Sequences collided — renumber everyone cleanly, then swap
for i, s in enumerate(siblings, 1):
s.sequence = i * 10
s.sequence = i
a_seq, b_seq = node.sequence, other.sequence
node.sequence, other.sequence = b_seq, a_seq
return {'ok': True}
@@ -260,7 +260,7 @@ class FpRecipeController(http.Controller):
vals['sequence'] = base_seq
new_node = Node.create(vals)
for i, child in enumerate(src_node.child_ids.sorted('sequence'), 1):
_copy_subtree(child, new_node, i * 10)
_copy_subtree(child, new_node, i)
return new_node
# Phase 1 — create every copied top-level child, tracking their
@@ -308,8 +308,8 @@ class FpRecipeController(http.Controller):
+ existing_top[anchor_idx:]
)
for i, node in enumerate(final_order, 1):
if node.sequence != i * 10:
node.sequence = i * 10
if node.sequence != i:
node.sequence = i
return {
'ok': True,
@@ -341,7 +341,7 @@ class FpRecipeController(http.Controller):
max_seq = max((c.sequence for c in parent.child_ids), default=0)
node.write({
'parent_id': parent.id,
'sequence': max_seq + 10,
'sequence': max_seq + 1,
})
return {'ok': True}
except Exception as exc:

View File

@@ -271,7 +271,7 @@ class SimpleRecipeController(http.Controller):
'template_id': tpl.id,
'name': (payload or {}).get('name') or 'New Prompt',
'input_type': (payload or {}).get('input_type') or 'text',
'sequence': existing_max + 10,
'sequence': existing_max + 1,
'required': bool((payload or {}).get('required')),
})
return {'ok': True, 'input_id': rec.id,
@@ -356,25 +356,26 @@ class SimpleRecipeController(http.Controller):
return {'id': new_node.id, 'sequence': new_node.sequence}
def _sequence_for_position(self, recipe, position):
"""Return the sequence value for a NEW step inserted at
`position` among the recipe's existing children.
Always renumbers existing siblings so the result is contiguous
1, 2, 3, ... matching what the operator sees on the work order.
(Pre-Sub 13c we used 10-spacing to allow midpoint inserts —
operators kept asking why their first step said "Step 10".)
"""
siblings = recipe.child_ids.sorted('sequence')
if not siblings:
return 10
if position >= len(siblings):
return siblings[-1].sequence + 10
if position <= 0:
return max(1, siblings[0].sequence - 10)
before = siblings[position - 1].sequence
after = siblings[position].sequence
if after - before > 1:
return (before + after) // 2
# Sequences are tightly packed (gap == 1 → midpoint == after,
# which collides). Renumber siblings to 10/20/30… first, then
# the new step lands cleanly between renumbered neighbours.
return 1
pos = max(0, min(position, len(siblings)))
# Make room: siblings before `pos` keep their 1-based index;
# siblings at or after `pos` shift up by one so the new step
# lands at sequence (pos + 1).
for idx, sib in enumerate(siblings):
new_seq = (idx + 1) * 10
if sib.sequence != new_seq:
sib.sequence = new_seq
return position * 10 + 5
target = idx + 1 if idx < pos else idx + 2
if sib.sequence != target:
sib.sequence = target
return pos + 1
def _copy_inputs_from_template(self, tpl, new_node):
NodeInput = request.env['fusion.plating.process.node.input']
@@ -412,7 +413,7 @@ class SimpleRecipeController(http.Controller):
def step_reorder(self, node_ids):
Node = request.env['fusion.plating.process.node']
for i, nid in enumerate(node_ids, start=1):
Node.browse(nid).write({'sequence': i * 10})
Node.browse(nid).write({'sequence': i})
return {'ok': True}
# -------------------------------------------------------------- template
@@ -521,7 +522,7 @@ class SimpleRecipeController(http.Controller):
'input_type': (payload or {}).get('input_type') or 'text',
'kind': 'step_input',
'collect': True,
'sequence': existing_max + 10,
'sequence': existing_max + 1,
'required': bool((payload or {}).get('required')),
})
return {'ok': True, 'input_id': rec.id}