From 0baf4befc0f02d0d4afab0010d25bebd619dfbdd Mon Sep 17 00:00:00 2001 From: "n.siegfried" Date: Mon, 15 Jun 2026 15:20:37 -0700 Subject: [PATCH] Tag material list by discipline for multi-discipline WPs When a package carries 2+ disciplines, the BOM gains a per-line Discipline column (hidden otherwise). Split by Discipline routes each instance only the material lines tagged to it; untagged lines stay on the kept master, and the split summary flags how many were untagged. Rendered doc shows the column when any line is tagged. Co-Authored-By: Claude Opus 4.8 (1M context) --- wp-creation-app.js | 24 ++++++++++++++++++++---- wp-creation-index.html | 2 +- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/wp-creation-app.js b/wp-creation-app.js index 9726421..e5c61b7 100644 --- a/wp-creation-app.js +++ b/wp-creation-app.js @@ -207,12 +207,24 @@ function unitOptions(cur){ if(c && !list.includes(c)) list.unshift(c); return ``+list.map(u=>``).join(''); } +// Discipline column on the BOM appears only for multi-discipline packages, so a +// split hands each instance just its own materials. Keeps a stale value as an +// option (mirrors unitOptions) if the discipline was later removed. +function matDisciplineOptions(cur){ + const list=pkgDisciplines.slice(); const c=cur||''; + if(c && !list.includes(c)) list.unshift(c); + return ``+list.map(d=>``).join(''); +} function buildMaterials(){ + const multi=isMultiDiscipline(); + const th=document.getElementById('mat-disc-th'); if(th) th.style.display=multi?'':'none'; const tb=document.getElementById('material-body'); tb.innerHTML=''; pkgMaterials.forEach((m,i)=>{ const tr=document.createElement('tr'); + const discCell = multi ? `` : ''; tr.innerHTML=` + ${discCell} `; tb.appendChild(tr); }); } @@ -305,7 +317,7 @@ function toggleDiscipline(d,on){ pkgDisciplines.forEach(x=>{ if(!pkgScope[x]) pkgScope[x]=['']; if(!pkgDiscStatus[x]) pkgDiscStatus[x]=getRadio('status')||'Draft'; }); Object.keys(pkgScope).forEach(x=>{ if(!pkgDisciplines.includes(x)) delete pkgScope[x]; }); Object.keys(pkgDiscStatus).forEach(x=>{ if(!pkgDisciplines.includes(x)) delete pkgDiscStatus[x]; }); - buildDisciplinePicker(); renderScope(); track('discipline_toggle',{count:pkgDisciplines.length}); + buildDisciplinePicker(); renderScope(); buildMaterials(); track('discipline_toggle',{count:pkgDisciplines.length}); } // Show the flat work-step list for 0–1 disciplines; per-discipline sections for 2+. @@ -399,6 +411,7 @@ function splitByDiscipline(){ scope:{[d]:steps.length?steps:['']}, discStatus:{[d]: (pkgDiscStatus[d]||base.status||'Draft')}, status: pkgDiscStatus[d]||base.status||'Draft', + materials: (base.materials||[]).filter(m=>m.discipline===d), workSteps: steps, work: steps.join('\n'), instanceOf: masterId, instanceLabel: suffix, parentNumber: baseNumber, split:false, children:undefined, @@ -412,7 +425,9 @@ function splitByDiscipline(){ editingId=masterId; saveStore(); renderSavedList(); track('wp_split',{disciplines:pkgDisciplines.length}); toast('Split into '+children.length+' discipline instances'); - alert('Created '+children.length+' instances:\n\n• '+children.map(c=>c.number+' ('+c.disciplines[0]+')').join('\n• ')+'\n\nThe master '+baseNumber+' is kept as a roll-up. Edit each instance from the Saved Work Packages list.'); + const untagged=(base.materials||[]).filter(m=>!m.discipline).length; + const matNote = untagged ? `\n\nNote: ${untagged} material line${untagged===1?'':'s'} had no discipline tag and stayed on the master only — tag them before splitting to route them to an instance.` : ''; + alert('Created '+children.length+' instances:\n\n• '+children.map(c=>c.number+' ('+c.disciplines[0]+', '+(c.materials?c.materials.length:0)+' material line'+((c.materials&&c.materials.length===1)?'':'s')+')').join('\n• ')+'\n\nThe master '+baseNumber+' is kept as a roll-up.'+matNote); } // ── ASSETS (controls.dev) ──────────────────────────────────────────────────── @@ -690,8 +705,9 @@ function renderPackage(pkg){ Labor Est. Hrs.${cell(pkg.hours)} Package Predecessor${pkg.seq?('After: '+esc(pkg.seq)):'None (no predecessor)'} `; - if(pkg.materials&&pkg.materials.length){ h+=`

4.0 Material List

`; - pkg.materials.forEach(m=>h+=``); h+=`
QtyUnitDescription
${cell(m.qty)}${cell(m.unit)}${cell(m.desc)}
`; } + if(pkg.materials&&pkg.materials.length){ const showDisc=pkg.materials.some(m=>m.discipline); + h+=`

4.0 Material List

${showDisc?'':''}`; + pkg.materials.forEach(m=>h+=`${showDisc?``:''}`); h+=`
QtyUnitDescriptionDiscipline
${cell(m.qty)}${cell(m.unit)}${cell(m.desc)}${cell(m.discipline)}
`; } if(pkg.attachments&&pkg.attachments.length){ h+=`

5.0 Drawings & Attachments

`; pkg.attachments.forEach(a=>h+=``); h+=`
DocumentRevLink / Note
${cell(a.doc)}${cell(a.rev)}${a.link?linkify(a.link):ns()}
`; } h+=`

6.0 Kitting & MIMO

diff --git a/wp-creation-index.html b/wp-creation-index.html index 2f21596..9394494 100644 --- a/wp-creation-index.html +++ b/wp-creation-index.html @@ -114,7 +114,7 @@
Material List
Structured bill of materials. Feeds kitting and the delivery forecast. Unit is from the Acumatica unit list.
-
QtyUnitDescription
+
QtyUnitDescription