PACT HR Partners

Manager Discipline Tool

Synthetic data

Pick a manager to see where each team member stands today, which occurrences roll off next, and to document a new occurrence at the correct level. Progression runs Verbal, Written, Final, then Termination, and each occurrence counts for 12 months. From September 1, 2026, the new union agreement puts attendance and conduct on separate tracks.

Use your own data
Workforce fileDiscipline file

Files are read in your browser and are not sent anywhere. Columns must match the Halvorsen templates.

Team

Employee

Select someone on the team to see their history and document a new occurrence.

Documentation
`; const nm=`Halvorsen_${pending.e['Legal Name'].replace(/[^A-Za-z]+/g,'_')}_${pending.o.date}.html`;try{await dl.save({filename:nm,data:html})}catch(err){if(err&&err.code!=='declined')alert('The download is not available here. Use Print instead.')}}; ['mgr','asof','show','rules'].forEach(id=>$('#'+id).onchange=()=>{if(id==='mgr')selEmp=null;render()}); async function upload(){const msg=$('#upMsg');const fw=$('#upW').files[0],fdsc=$('#upD').files[0];if(!fw&&!fdsc)return;msg.textContent='Reading…'; try{if(fw){const{rows}=await readXlsx(fw,'Workforce_Data');const r=normRows(rows,['Most Recent Hire Date']);const miss=missingCols(r,['Employee ID','Legal Name','Manager ID','Manager Name','Union Status','Employment Status']);if(miss.length)throw new Error('Workforce file is missing: '+miss.join(', '));roster=r.filter(x=>x['Employment Status']!=='Terminated')} if(fdsc){const{rows}=await readXlsx(fdsc,'Discipline_History');const r=normRows(rows,['Occurrence Date']);const miss=missingCols(r,['Employee ID','Occurrence Date','Disciplinary Level (Issued)','Cumulative']);if(miss.length)throw new Error('Discipline file is missing: '+miss.join(', '));disc=r.filter(x=>x['Occurrence Date'])} EMP=byEmp();msg.textContent=`Loaded ${roster.length} employees and ${disc.length} occurrences.`;init()}catch(err){msg.textContent=err.message}} $('#upW').onchange=upload;$('#upD').onchange=upload; init(); (async()=>{sampleFn=await cap('sample');if(sampleFn){const b=$('#aiBtn');if(b)b.classList.remove('hide')}dl=await cap('downloads');if(dl)$('#docSave').classList.remove('hide')})(); })();