|
| 1 | +--- |
| 2 | +name: correct-shipped-build |
| 3 | +description: >- |
| 4 | + Resolve a shipped-order discrepancy — customer received different items than |
| 5 | + Katana recorded. Pull the customer-facing record, locate the linked Katana |
| 6 | + SO/MO, decide whether a BOM-driven split is needed (when an assembled good |
| 7 | + shipped as its component parts), then apply correct_manufacturing_order + |
| 8 | + correct_sales_order with operator-judgment gates between phases. Verify |
| 9 | + against the source of truth at the end. |
| 10 | +argument-hint: "[order id from source-of-truth system]" |
| 11 | +allowed-tools: |
| 12 | + - mcp__katana-erp__get_sales_order |
| 13 | + - mcp__katana-erp__get_manufacturing_order |
| 14 | + - mcp__katana-erp__get_manufacturing_order_recipe |
| 15 | + - mcp__katana-erp__list_sales_orders |
| 16 | + - mcp__katana-erp__correct_manufacturing_order |
| 17 | + - mcp__katana-erp__correct_sales_order |
| 18 | + - mcp__katana-erp__modify_manufacturing_order |
| 19 | + - mcp__katana-erp__modify_sales_order |
| 20 | + - mcp__katana-erp__check_inventory |
| 21 | + - mcp__katana-erp__create_stock_adjustment |
| 22 | +--- |
| 23 | + |
| 24 | +# /correct-shipped-build — shipped-order discrepancy resolution |
| 25 | + |
| 26 | +## PURPOSE |
| 27 | + |
| 28 | +When a customer received different items than Katana recorded — wrong |
| 29 | +ingredient on a finished good, wrong line item on a sales order, or a |
| 30 | +finished good that shipped as its components — restore Katana to ground |
| 31 | +truth without losing the original close-state metadata (`done_date`, |
| 32 | +`picked_date`, per-production timestamps, fulfillment tracking). |
| 33 | + |
| 34 | +## CRITICAL |
| 35 | + |
| 36 | +- **Ground truth is the customer-facing record, not Katana.** Pull the |
| 37 | + Shopify / e-commerce / fulfillment-system order first; treat it as |
| 38 | + authoritative. Don't assume Katana's recorded fulfillment is right — |
| 39 | + this skill exists because it isn't. |
| 40 | +- **Preview every correction before applying.** Both `correct_manufacturing_order` |
| 41 | + and `correct_sales_order` default to `preview=true` for a reason. The |
| 42 | + operator should read the planned action list and confirm before any |
| 43 | + mutations land. |
| 44 | +- **Both MO and SO may need correction, in this order.** When a finished |
| 45 | + good's recipe was wrong (consumed the wrong ingredient), fix the MO |
| 46 | + first — that's the upstream truth. Then fix the SO if its line items |
| 47 | + also drifted. SO line items can reference variants the MO produced, so |
| 48 | + MO-first is the safer order. |
| 49 | +- **Inventory side-effects belong to a separate decision.** If the |
| 50 | + finished good was disassembled mid-fulfillment — i.e. an assembled |
| 51 | + good was broken into its BOM components and shipped as parts — the |
| 52 | + MO/SO corrections alone don't reshuffle inventory. That's |
| 53 | + `split_via_bom`'s job. Don't conflate the two; decide explicitly |
| 54 | + whether a split is needed. |
| 55 | + |
| 56 | +## STANDARD PATH |
| 57 | + |
| 58 | +### Phase 1 — Pull the source-of-truth record |
| 59 | + |
| 60 | +Ask the operator for the source-system order id (Shopify order, etc.). |
| 61 | +Read the customer-facing record and write down what actually shipped: |
| 62 | +SKUs, quantities, condition / assembly state. This is the **expected |
| 63 | +state** every later phase compares against. |
| 64 | + |
| 65 | +If you have a Chrome tab or other tool already open on the order, use it. |
| 66 | +Otherwise the operator may need to open it manually and read off the |
| 67 | +relevant fields. |
| 68 | + |
| 69 | +### Phase 2 — Locate the linked Katana SO and MO |
| 70 | + |
| 71 | +``` |
| 72 | +list_sales_orders(customer_id=..., delivered_after=..., delivered_before=...) |
| 73 | +``` |
| 74 | + |
| 75 | +Filter by customer name + delivery date window to find the candidate SO. |
| 76 | +Once you have the SO id: |
| 77 | + |
| 78 | +``` |
| 79 | +get_sales_order(order_id=...) |
| 80 | +``` |
| 81 | + |
| 82 | +Inspect: |
| 83 | +- SO line items — what Katana thinks shipped |
| 84 | +- `linked_manufacturing_order_id` on each row — the upstream MO, if any |
| 85 | + |
| 86 | +If a row has a linked MO: |
| 87 | + |
| 88 | +``` |
| 89 | +get_manufacturing_order(order_id=...) |
| 90 | +``` |
| 91 | + |
| 92 | +Note the MO's `variant_id` (the finished good) and the status (`DONE` / |
| 93 | +`PARTIALLY_COMPLETED`). |
| 94 | + |
| 95 | +`get_manufacturing_order` defaults to `include_rows="blocking"`, which |
| 96 | +hides recipe rows whose ingredients are in stock. To inspect the *full* |
| 97 | +recipe before deciding what to correct, fetch the dedicated recipe view: |
| 98 | + |
| 99 | +``` |
| 100 | +get_manufacturing_order_recipe(manufacturing_order_id=...) |
| 101 | +``` |
| 102 | + |
| 103 | +This returns every recipe row regardless of ingredient-availability |
| 104 | +status — that's what you want when figuring out which row consumed the |
| 105 | +wrong variant. |
| 106 | + |
| 107 | +### Phase 3 — Decide whether a split is needed |
| 108 | + |
| 109 | +A **split** means: the finished good produced by the MO was disassembled |
| 110 | +into its BOM components before shipment, and the customer received the |
| 111 | +components instead of the assembled good. |
| 112 | + |
| 113 | +Operator-judgment criteria: |
| 114 | + |
| 115 | +- The customer-confirmed item list (phase 1) lists individual component |
| 116 | + SKUs instead of the assembled SKU. |
| 117 | +- The MO produced a single assembled good, but the SO's line item now |
| 118 | + needs to reference component SKUs. |
| 119 | +- The warehouse / fulfillment notes confirm the split. |
| 120 | + |
| 121 | +If yes, plan to use `split_via_bom` in phase 6. If no, skip phase 6. |
| 122 | + |
| 123 | +If `split_via_bom` is not yet shipped (issue #543 still open at the time |
| 124 | +of writing), document the intended split — finished-good variant id, |
| 125 | +location id, quantity, and the timing — and proceed without it. The |
| 126 | +MO/SO corrections still land cleanly; the inventory reshuffling can be |
| 127 | +applied as a follow-up when the tool ships. |
| 128 | + |
| 129 | +### Phase 4 — Correct the MO (if the recipe was wrong) |
| 130 | + |
| 131 | +If the MO consumed the wrong ingredient (e.g., the wrong variant of a |
| 132 | +component, or an incorrect quantity per unit): |
| 133 | + |
| 134 | +``` |
| 135 | +correct_manufacturing_order( |
| 136 | + id=..., |
| 137 | + ingredient_changes=[ |
| 138 | + { |
| 139 | + old_variant_id: <variant currently on the recipe row>, |
| 140 | + new_variant_id: <variant that was actually consumed>, |
| 141 | + planned_quantity_per_unit: <if changed; otherwise omit>, |
| 142 | + }, |
| 143 | + ... |
| 144 | + ], |
| 145 | + preview=true, |
| 146 | +) |
| 147 | +``` |
| 148 | + |
| 149 | +Review the planned action sequence — revert → recipe edit → recreate |
| 150 | +production → re-close with backdated done_date / production_date. |
| 151 | +Confirm the captured close-state matches reality. Then re-call with |
| 152 | +`preview=false`. |
| 153 | + |
| 154 | +The tool requires the MO to be in `DONE` or `PARTIALLY_COMPLETED` |
| 155 | +status. If it's still `IN_PROGRESS`, use `modify_manufacturing_order` |
| 156 | +directly — there's no close-state to preserve. |
| 157 | + |
| 158 | +### Phase 5 — Correct the SO (if line items were wrong) |
| 159 | + |
| 160 | +If the SO's line items don't match what the customer received: |
| 161 | + |
| 162 | +``` |
| 163 | +correct_sales_order( |
| 164 | + id=..., |
| 165 | + line_changes=[ |
| 166 | + { |
| 167 | + old_variant_id: <variant currently on the row>, |
| 168 | + new_variant_id: <variant actually shipped>, |
| 169 | + quantity: <if changed; must be ≥ already-fulfilled qty>, |
| 170 | + price_per_unit: <if changed>, |
| 171 | + }, |
| 172 | + ... |
| 173 | + ], |
| 174 | + preview=true, |
| 175 | +) |
| 176 | +``` |
| 177 | + |
| 178 | +Preview surfaces the planned action sequence — delete fulfillments → |
| 179 | +revert → row edits → re-add fulfillments with original `picked_date` → |
| 180 | +re-close. Confirm and re-call with `preview=false`. |
| 181 | + |
| 182 | +`correct_sales_order` only updates rows in place. If you need to **add |
| 183 | +or remove** a line item entirely, use `modify_sales_order` directly — |
| 184 | +the close-state restore would otherwise lose the row-id mapping the |
| 185 | +fulfillments depend on. |
| 186 | + |
| 187 | +### Phase 6 — Apply the BOM-driven split (if decided in phase 3) |
| 188 | + |
| 189 | +> **Status: deferred.** The `split_via_bom` tool isn't shipped yet |
| 190 | +> (issue #543). It's intentionally **not** listed in this skill's |
| 191 | +> `allowed-tools` so the skill can load and run today; when the tool |
| 192 | +> ships, add `mcp__katana-erp__split_via_bom` to the allowlist and |
| 193 | +> remove this status callout. |
| 194 | +
|
| 195 | +When `split_via_bom` ships, the call shape will be: |
| 196 | + |
| 197 | +``` |
| 198 | +split_via_bom( |
| 199 | + variant_id=<finished good>, |
| 200 | + location_id=<where the inventory transformation happened>, |
| 201 | + quantity=<how many were split>, |
| 202 | + stock_adjustment_date=<just before the customer's delivery date>, |
| 203 | + preview=true, |
| 204 | +) |
| 205 | +``` |
| 206 | + |
| 207 | +It generates a single mixed-sign stock adjustment: `-quantity` of the |
| 208 | +finished good, `+(quantity × planned_quantity_per_unit)` of each BOM |
| 209 | +ingredient, cost-balanced to net zero. |
| 210 | + |
| 211 | +**Until then:** file a follow-up issue documenting the intended split |
| 212 | +parameters (finished-good variant id, location id, quantity, timing) |
| 213 | +and skip this phase. The MO/SO corrections still leave the records in |
| 214 | +the right shape; only the inventory reshuffling is deferred. |
| 215 | + |
| 216 | +### Phase 7 — Verify |
| 217 | + |
| 218 | +Re-fetch the corrected records: |
| 219 | + |
| 220 | +``` |
| 221 | +get_manufacturing_order(order_id=...) |
| 222 | +get_sales_order(order_id=...) |
| 223 | +``` |
| 224 | + |
| 225 | +Confirm the close-state restored cleanly: status back to its original |
| 226 | +value, dates backdated to the originals, no new "today" timestamps |
| 227 | +sneaking in. |
| 228 | + |
| 229 | +Sanity-check inventory on the affected SKUs: |
| 230 | + |
| 231 | +``` |
| 232 | +check_inventory(skus_or_variant_ids=[...]) |
| 233 | +``` |
| 234 | + |
| 235 | +Expected vs. actual stock levels should reconcile against the customer- |
| 236 | +confirmed shipment. Negative availability is a red flag — it means the |
| 237 | +correction over-corrected somewhere. |
| 238 | + |
| 239 | +### Phase 8 — Summarize |
| 240 | + |
| 241 | +Report to the operator: |
| 242 | + |
| 243 | +- **Source-system order id** — what was being corrected. |
| 244 | +- **Katana SO and MO ids** — links to the corrected records. |
| 245 | +- **Changes applied** — MO recipe rows swapped (with variants), SO line |
| 246 | + items updated, split applied (yes/no, with parameters). |
| 247 | +- **Inventory delta** — affected SKUs and the qty change at each. |
| 248 | +- **Verification** — the inventory-check result and any flagged |
| 249 | + anomalies. |
| 250 | +- **Deferred work** — if `split_via_bom` wasn't available, the |
| 251 | + follow-up issue or manual adjustment that's still needed. |
| 252 | + |
| 253 | +## EDGE CASES |
| 254 | + |
| 255 | +- **No linked MO on the SO** — the SO is for a purchased finished good, |
| 256 | + not a manufactured one. Skip phase 4 entirely; only the SO needs |
| 257 | + correction. |
| 258 | +- **MO is `IN_PROGRESS`** — `correct_manufacturing_order` will refuse. |
| 259 | + The MO hasn't shipped yet, so there's no close-state to preserve. Use |
| 260 | + `modify_manufacturing_order` directly. |
| 261 | +- **Same variant appears on multiple recipe rows / SO rows** — the |
| 262 | + correction tools refuse on ambiguity. Use `modify_manufacturing_order` |
| 263 | + / `modify_sales_order` with the explicit row id, or split the |
| 264 | + correction into two passes that disambiguate by other means. |
| 265 | +- **New row quantity below the already-fulfilled quantity** — |
| 266 | + `correct_sales_order` preflight refuses. Either keep the original |
| 267 | + quantity, or fix the fulfillment record manually first. |
| 268 | +- **Inventory goes negative after corrections** — the corrections |
| 269 | + over-shot. Likely cause: the MO/SO drift was only a partial |
| 270 | + representation of what shipped, and the rest needs a stock adjustment |
| 271 | + or a parallel correction on a sibling SO. Don't paper over with a |
| 272 | + goodwill stock adjustment without understanding the root cause. |
| 273 | +- **Customer received a superset (got extras)** — corrections alone |
| 274 | + won't capture this. Either confirm a picking-error / goodwill add-on |
| 275 | + with the warehouse, or treat the extras as a separate incident. |
| 276 | + |
| 277 | +## RELATED |
| 278 | + |
| 279 | +- PR #536 — `correct_manufacturing_order`, `correct_sales_order` |
| 280 | +- Issue #543 — `split_via_bom` (BOM-driven inventory split, pending) |
| 281 | +- Issue #523 — Closed-record corrections umbrella |
| 282 | +- Issue #532 — `correct_purchase_order` (separate workflow; not used here) |
0 commit comments