# Routing → Production Rules → MO Integration Plan

## Current State

### Tables
```
llx_productionrules_routing         — operations per product (op_no, op_code, label, ws)
llx_productionrules_routing_wc      — workstation links per operation
llx_productionrules_routing_confirm — confirmed status per product
```

### Classes
- `ProductionRoutingService::getRoutingByProduct()` — reads routing + workstations (read-only)
- `productionrules.class.php` — generates BOMs and MO operations from *rulesets* (separate tables, separate logic)

Routing and rulesets are currently **separate**. Rulesets have their own `operation` / `workstation` fields per rule step.

---

## Goal

When a product has a **confirmed routing**, use it as the source of operations when creating an MO —
populating `llx_mrp_production` (type=operation) and `llx_mrp_mo_optracking` automatically.

---

## Options

### Option A — Routing as direct MO fallback (recommended)
If a product has a confirmed routing, use it directly to fill MO operations, independently of rulesets.
Works for any product regardless of whether it has a ruleset.

### Option B — Routing embedded in rulesets
Ruleset points to a product; system auto-fetches its confirmed routing.
More complex — requires changes to ruleset flow renderer.

**Recommendation: Option A — simpler, decoupled, immediately useful.**

---

## Implementation Steps

### Step 1 — `applyRoutingToMO($moId, $fkProduct)` in `ProductionRoutingService`

- Fetch confirmed routing for `$fkProduct` (active ops, sorted by op_no)
- For each operation:
  - Insert row in `llx_mrp_production`:
    - `fk_mo` = $moId
    - `fk_product` = service product resolved from op_code (or null)
    - `fk_default_workstation` = first WC from routing_wc
    - `rank` = op_no
    - `label` = routing op label
  - Insert row in `llx_mrp_mo_optracking`:
    - `fk_mo` = $moId
    - `op_rank` = op_no
    - `op_code` = op_code
    - `fk_workstation` = first WC
    - `qty_done` = 0, `status` = 0

### Step 2 — Trigger `MO_VALIDATE` in `core/triggers/interface_productionrules.class.php`

```php
if ($action === 'MO_VALIDATE') {
    $fkProduct = $object->fk_product;
    // check confirmed routing exists
    $confirmed = ProductionRoutingService::isConfirmed($db, $fkProduct, $conf->entity);
    if ($confirmed) {
        ProductionRoutingService::applyRoutingToMO($db, $object->id, $fkProduct, $conf->entity);
    }
}
```

### Step 3 — Optional: preview before apply

Page or AJAX endpoint that shows routing operations that will be added to the MO.
User can click "Apply routing" manually instead of automatic trigger.

### Step 4 — Planning integration

`planning` module reads `llx_mrp_mo_optracking` + `llx_workstation_workstation`.
Once routing populates optracking, planning automatically gets operations + workstations for scheduling.
No changes needed in planning module.

---

## Implementation Order

| # | What | Where |
|---|---|---|
| 1 | `applyRoutingToMO()` + `isConfirmed()` in `ProductionRoutingService` | `class/ProductionRoutingService.class.php` |
| 2 | Trigger `MO_VALIDATE` | `core/triggers/interface_productionrules.class.php` |
| 3 | Manual "Apply routing" button on MO page (optional, for testing/override) | AJAX endpoint + MO page hook |
| 4 | Test on dev with one MO | dev instance |

---

## Open Questions

1. When trigger fires on `MO_VALIDATE` — skip if MO already has operations (re-validate scenario)?
2. Should subcontract ops (`subcontract=1`) be skipped or mapped to a subcon service product?
3. Does each op need a unique service product in `llx_mrp_production`, or is a label-only line sufficient?
4. Conflict with existing ruleset-generated operations — should routing ops be added on top, or replace them?

---

## Key DB Columns Reference

| Table | Key columns |
|---|---|
| `llx_productionrules_routing` | `fk_product`, `op_no`, `op_code`, `label`, `subcontract`, `active` |
| `llx_productionrules_routing_wc` | `fk_routing`, `fk_workstation` |
| `llx_productionrules_routing_confirm` | `fk_product`, `confirmed`, `entity` |
| `llx_mrp_production` | `fk_mo`, `fk_product`, `fk_default_workstation`, `rank`, `label`, `qty` |
| `llx_mrp_mo_optracking` | `fk_mo`, `op_rank`, `op_code`, `fk_workstation`, `qty_done`, `status` |
| `llx_workstation_workstation` | `rowid`, `ref`, `worksation_type`, `nb_operators_required` |
