---
name: evolution_jobeditinv_bom_inline_oom
description: "jobeditinv renders showQuoteItemBom inline per row; gate ONLY on $bomModified, never inventory.hasBom, or PHP OOMs on 7.4"
metadata: 
  node_type: memory
  type: project
  originSessionId: 4fabb73a-488a-4da0-b514-d6e6fabb96a6
---

`jobeditinv.inc` `showItems()` echoes `quote::showQuoteItemBom()` **inline for every job
inventory row** (the moreItemInfo popup markup is built server-side per row, not lazily).

Gate it ONLY on `$bomModified` (per-row: a `quoteitems_bom` OR `jobInventory_bom` exists —
i.e. quote-modified or pipe-modifier lines; computed at jobeditinv.inc ~479-481 via
`$bomModified`/`$bomModified2`). Do NOT broaden the gate to `$row['hasBom']=="1"`: that fires
the full base-BOM resolver (several wide `inventory.*` selects) for every manufacturing line,
and on a large job exhausts PHP's 128MB limit → `Allowed memory size exhausted` **fatal on
PHP 7.4** mid-render. The page truncates, so the pipe-modifier wizard's last step (buildDesc +
p2CostTable in [[gst_precision_project]]-unrelated `plugins/pipe/pipetopost2.js`) renders blank
with **nothing in the JS console** (it's a server fatal, not JS).

**Why:** dev is PHP 8 (more headroom, survived); prod/staging is PHP 7.4 (fatals). quotedit was
fine because it doesn't render BOMs inline per row this way. The base-hasBom popup was dead
before anyway (old `$row['hasbom']` key never matched `inventory.hasBom`), so scoping to
`$bomModified` is not a user-facing regression.

**How to apply:** if base-hasBom BOMs need to show too, make the popup lazy (AJAX on open),
don't add per-row inline resolver calls. See also [[evolution_bom_hierarchy]] (resolver chain).
Fixed 2026-07-16.
