From 209b1974a71224fe348354b1f8b2d75fa0d07d70 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Sat, 18 Apr 2026 22:41:27 -0400 Subject: [PATCH] feat(plating): seed 5 fresh MOs with mixed states + priorities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../scripts/fp_demo_stage_filler.py | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/fusion_plating/scripts/fp_demo_stage_filler.py b/fusion_plating/scripts/fp_demo_stage_filler.py index 6a762c2c..3d2a699b 100644 --- a/fusion_plating/scripts/fp_demo_stage_filler.py +++ b/fusion_plating/scripts/fp_demo_stage_filler.py @@ -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")