feat(plating): seed 5 fresh MOs with mixed states + priorities

Stage filler gains step 6g — spins up five new manufacturing orders
so the Manager Desk has a busy shop floor instead of the single
in-flight MO that came out of the base seeder.

Plan, in order of creation:

  WH/MO/00012  HOT     Cyclone Manufacturing      qty 25  unassigned
  WH/MO/00013  Urgent  Westin Manufacturing       qty 60  unassigned
  WH/MO/00014  Normal  Honeywell Aerospace        qty 18  auto-routed
  WH/MO/00015  Normal  Amphenol Canada            qty 40  routed + first WO started
  WH/MO/00016  Normal  Magellan Aerospace         qty 32  auto-routed

Each MO is created via mrp.production.create() and confirmed through
the bridge_mrp action_confirm() override, which auto-creates the
portal job and generates ~9 WOs from the recipe with role-aware
auto-routing. Post-create the script stamps priority on every WO and
optionally clears assignments (HOT + Urgent) or starts the first WO
(MO_00015) for variety.

Recipe lookup was previously by code='ENP-ALUM-BASIC' which silently
failed because the seed file uses code='ENP_ALUM_BASIC' (underscores)
while the display name has dashes. Switched to "first available
recipe of node_type=recipe" so the script works regardless of which
spelling is canonical.

Idempotent — bails early if there are already five-plus active MOs,
so re-runs don't keep stacking new jobs.

Verified on entech: Manager Desk now shows
  - 6 active MOs (was 1)
  - 23 unassigned active WOs (was 2)
  - 30 active+assigned WOs (was 6)
  - 2 WOs in progress now
  - all 7 operators with open queue (Marie 2, James 1, Carlos 8, etc.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-18 22:41:27 -04:00
parent 2ce7bd3665
commit 209b1974a7

View File

@@ -451,6 +451,112 @@ def _add_paused_wo(env):
print(f"[6b] Paused-WO marker set on {progress.display_name}")
def _seed_fresh_mos(env):
"""Spin up five new MOs in mixed states so the Manager Desk and the
Tablet Station have a busy shop floor to look at, not just one
in-flight job.
Each MO generates ~9 WOs from the ENP-ALUM-BASIC recipe via the
bridge_mrp action_confirm() override (auto-create portal job +
auto-route workers by role). After confirmation we tweak the
result so the demo has visible variety:
- MO_A HOT priority, all WOs unassigned (Needs a Worker pile)
- MO_B Urgent, all WOs unassigned (Needs a Worker pile)
- MO_C Normal, auto-routed (team queues)
- MO_D Normal, auto-routed + first WO started (live workload)
- MO_E Normal, auto-routed (team queues)
Idempotent: runs only when fewer than 13 active MOs exist (3 baseline
+ 5 created here + a small buffer).
"""
Production = env['mrp.production']
SO = env['sale.order']
Product = env['product.product']
Recipe = env['fusion.plating.process.node']
active_mo_count = Production.search_count([('state','not in',('done','cancel'))])
if active_mo_count >= 5:
print(f"[6g] Already have {active_mo_count} active MOs — skipping")
return
product = Product.search([('default_code', '=', 'FP-WIDGET')], limit=1)
# Recipe code stores underscores (ENP_ALUM_BASIC) while the
# display name uses dashes — historical drift between the two
# data files. Just take the first available recipe so the seed
# works regardless of which spelling is canonical today.
recipe = Recipe.search([('node_type', '=', 'recipe')], limit=1)
if not product or not recipe:
print("[6g] Missing product/recipe — skipping (need FP-WIDGET + a recipe)")
return
# Customer + qty + priority + post-create tweaks for each MO.
customer_names = [
'Cyclone Manufacturing Inc.',
'Westin Manufacturing Ltd',
'Honeywell Aerospace Toronto',
'Amphenol Canada Corp.',
'Magellan Aerospace',
]
plan = [
# (customer_idx, qty, priority, unassign_all, start_first)
(0, 25, '2', True, False), # HOT, unassigned
(1, 60, '1', True, False), # Urgent, unassigned
(2, 18, '0', False, False), # Normal, auto-routed
(3, 40, '0', False, True), # Normal, auto-routed, first WO started
(4, 32, '0', False, False), # Normal, auto-routed
]
created = 0
for cust_idx, qty, prio, unassign_all, start_first in plan:
partner = env['res.partner'].search(
[('name', '=', customer_names[cust_idx])], limit=1,
)
# Sale order origin lookup — fall back to a synthetic ref if no
# SO exists for this customer (just so the demo card has text).
so = SO.search([('partner_id', '=', partner.id)], limit=1) if partner else None
origin = so.name if so else f'DEMO-{customer_names[cust_idx][:6].upper()}'
vals = {
'product_id': product.id,
'product_qty': qty,
'product_uom_id': product.uom_id.id,
'origin': origin,
'x_fc_recipe_id': recipe.id,
'company_id': env.company.id,
}
# Some installs put partner_id on mrp.production; use it if present.
if partner and 'partner_id' in Production._fields:
vals['partner_id'] = partner.id
try:
mo = Production.create(vals)
mo.action_confirm() # bridge override generates WOs + routes them
except Exception as exc:
print(f" !! MO create failed for {customer_names[cust_idx]}: {exc!r}")
continue
# Stamp priority on every generated WO so the Manager Desk
# cards show the badge.
mo.workorder_ids.write({'x_fc_priority': prio})
if unassign_all:
mo.workorder_ids.write({'x_fc_assigned_user_id': False})
if start_first:
first = mo.workorder_ids.sorted('sequence')[:1]
if first and first.state in ('ready', 'pending', 'waiting'):
try:
first.button_start()
except Exception:
# Cert gate or similar — not fatal for the demo seed.
pass
created += 1
print(f"[6g] Created {created} fresh MOs (HOT/Urgent unassigned + 3 auto-routed)")
def _populate_active_wos(env):
"""Make sure the Manager Desk's three columns all have visible data.
@@ -630,6 +736,7 @@ _safe('6c. add quote requests', _add_quote_requests)
_safe('6d. mark one quote sent', _mark_quote_sent)
_safe('6e. populate active WOs', _populate_active_wos)
_safe('6f. SO awaiting-manager', _mark_so_awaiting_manager)
_safe('6g. seed fresh MOs', _seed_fresh_mos)
print("=========================================================")
print("Done. Re-run anytime — script is idempotent.")
print("=========================================================\n")