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) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 15:20:37 -07:00
parent d1f0901897
commit b5ea671d32
2 changed files with 21 additions and 5 deletions

View File

@@ -207,12 +207,24 @@ function unitOptions(cur){
if(c && !list.includes(c)) list.unshift(c);
return `<option value="">Unit…</option>`+list.map(u=>`<option ${c===u?'selected':''}>${esc(u)}</option>`).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 `<option value="">—</option>`+list.map(d=>`<option ${c===d?'selected':''}>${esc(d)}</option>`).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 ? `<td><select onchange="pkgMaterials[${i}].discipline=this.value">${matDisciplineOptions(m.discipline)}</select></td>` : '';
tr.innerHTML=`<td><input type="text" value="${(m.qty||'').replace(/"/g,'&quot;')}" placeholder="20" oninput="pkgMaterials[${i}].qty=this.value"></td>
<td><select onchange="pkgMaterials[${i}].unit=this.value">${unitOptions(m.unit)}</select></td>
<td><input type="text" value="${(m.desc||'').replace(/"/g,'&quot;')}" placeholder="3/8 lock washers gold" oninput="pkgMaterials[${i}].desc=this.value"></td>
${discCell}
<td class="center"><button class="row-del" onclick="removeMaterial(${i})">✕</button></td>`;
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 01 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){
<tr><th>Labor Est. Hrs.</th><td>${cell(pkg.hours)}</td></tr>
<tr><th>Package Predecessor</th><td>${pkg.seq?('After: '+esc(pkg.seq)):'None (no predecessor)'}</td></tr>
</tbody></table>`;
if(pkg.materials&&pkg.materials.length){ h+=`<h2>4.0 Material List</h2><table><thead><tr><th style="width:80px">Qty</th><th style="width:80px">Unit</th><th>Description</th></tr></thead><tbody>`;
pkg.materials.forEach(m=>h+=`<tr><td>${cell(m.qty)}</td><td>${cell(m.unit)}</td><td>${cell(m.desc)}</td></tr>`); h+=`</tbody></table>`; }
if(pkg.materials&&pkg.materials.length){ const showDisc=pkg.materials.some(m=>m.discipline);
h+=`<h2>4.0 Material List</h2><table><thead><tr><th style="width:80px">Qty</th><th style="width:80px">Unit</th><th>Description</th>${showDisc?'<th style="width:120px">Discipline</th>':''}</tr></thead><tbody>`;
pkg.materials.forEach(m=>h+=`<tr><td>${cell(m.qty)}</td><td>${cell(m.unit)}</td><td>${cell(m.desc)}</td>${showDisc?`<td>${cell(m.discipline)}</td>`:''}</tr>`); h+=`</tbody></table>`; }
if(pkg.attachments&&pkg.attachments.length){ h+=`<h2>5.0 Drawings & Attachments</h2><table><thead><tr><th>Document</th><th style="width:60px">Rev</th><th>Link / Note</th></tr></thead><tbody>`;
pkg.attachments.forEach(a=>h+=`<tr><td>${cell(a.doc)}</td><td>${cell(a.rev)}</td><td>${a.link?linkify(a.link):ns()}</td></tr>`); h+=`</tbody></table>`; }
h+=`<h2>6.0 Kitting & MIMO</h2><table><tbody>

View File

@@ -114,7 +114,7 @@
<div class="card">
<div class="sub-heading">Material List</div>
<div class="notice">Structured bill of materials. Feeds kitting and the delivery forecast. Unit is from the Acumatica unit list.</div>
<div class="table-wrap"><table><thead><tr><th style="width:90px">Qty</th><th style="width:120px">Unit</th><th>Description</th><th style="width:44px"></th></tr></thead><tbody id="material-body"></tbody></table></div>
<div class="table-wrap"><table><thead><tr><th style="width:90px">Qty</th><th style="width:120px">Unit</th><th>Description</th><th id="mat-disc-th" style="width:140px;display:none">Discipline</th><th style="width:44px"></th></tr></thead><tbody id="material-body"></tbody></table></div>
<div class="material-actions">
<button class="add-btn" onclick="addMaterial()">+ Add Material Line</button>
<button class="add-btn" onclick="document.getElementById('material-import').click()">⤒ Import from Excel/CSV</button>