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:
@@ -451,6 +451,112 @@ def _add_paused_wo(env):
|
|||||||
print(f"[6b] Paused-WO marker set on {progress.display_name}")
|
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):
|
def _populate_active_wos(env):
|
||||||
"""Make sure the Manager Desk's three columns all have visible data.
|
"""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('6d. mark one quote sent', _mark_quote_sent)
|
||||||
_safe('6e. populate active WOs', _populate_active_wos)
|
_safe('6e. populate active WOs', _populate_active_wos)
|
||||||
_safe('6f. SO awaiting-manager', _mark_so_awaiting_manager)
|
_safe('6f. SO awaiting-manager', _mark_so_awaiting_manager)
|
||||||
|
_safe('6g. seed fresh MOs', _seed_fresh_mos)
|
||||||
print("=========================================================")
|
print("=========================================================")
|
||||||
print("Done. Re-run anytime — script is idempotent.")
|
print("Done. Re-run anytime — script is idempotent.")
|
||||||
print("=========================================================\n")
|
print("=========================================================\n")
|
||||||
|
|||||||
Reference in New Issue
Block a user