Work Order Form Generator

Work Order Form Generator

JobFlow

Work Order Creator
Job & Client Information
Parts & Materials (Cost)
Labor & Summary
$0.00

WO ID: ${data.id} | Date: ${new Date().toLocaleDateString()}

`; // 2. Client & Tech Info Table html += `

I. Job & Technician Data

`; html += `
Client:${data.client}
Address:${data.address}
Service Rep:${data.rep}
`; // 3. Work Description html += `

II. Work Performed

`; html += `

${data.desc}

`; // 4. Materials Table html += `

III. Parts & Labor Breakdown

`; // Materials Table let matTable = ``; data.materials.forEach(m => { matTable += ``; }); matTable += ``; matTable += `
Item / Part # Qty Unit Price Line Total
${m.item} ${m.qty} ${formatted(m.price)} ${formatted(m.total)}
MATERIALS SUB-TOTAL${formatted(materialsData.totalCost)}
`; html += matTable; // Labor and Totals html += `
Labor Hours:${data.hours} @ ${formatted(data.rate)}
Labor Cost:${formatted(data.financials.labor)}
GRAND SUBTOTAL:${formatted(data.financials.subtotal)}
Sales Tax (8.0%):${formatted(data.financials.tax)}
TOTAL DUE:${formatted(data.financials.total)}
`; // 5. Observations html += `

IV. Technician Observations

`; html += `

${data.obs}

`; // 6. Signatures html += `
Technician Signature: ______________________
Client Approval Signature: ______________________
`; container.innerHTML = html; } function wofgSwitchTab(tabId) { document.querySelectorAll('.wofg-tab-btn').forEach(b => b.classList.remove('active')); document.querySelectorAll('.wofg-content').forEach(c => c.classList.remove('active')); const idx = tabId === 'builder' ? 0 : 1; document.querySelectorAll('.wofg-tab-btn')[idx].classList.add('active'); document.getElementById('wofg-' + tabId).classList.add('active'); if (tabId === 'preview') { wofgRenderReport(); } } function wofgLoadExample() { if(!confirm("Overwrite current data with example HVAC repair job?")) return; document.getElementById('inp-id').value = "WO-2025-0101"; document.getElementById('inp-client').value = "MegaTech Office Park"; document.getElementById('inp-address').value = "123 Industrial Way, Chicago, IL 60601"; document.getElementById('inp-rep').value = "A. Martinez"; document.getElementById('inp-rate').value = "85.00"; document.getElementById('inp-hours').value = "3.5"; document.getElementById('inp-desc').value = "Diagnosed motor failure on HVAC unit 4 (Rooftop). Disassembled unit, replaced main blower motor and drive belt (belt was cracked). Reassembled, tested pressures, and verified unit cycling correctly."; document.getElementById('inp-obs').value = "Unit running efficiently (EER 10.5). Recommend filter replacement next month. Client signed off on completed work at 16:30."; // Clear and fill materials document.getElementById('wofg-materials-rows').innerHTML = ''; wofgAddMaterialRow("Blower Motor (Model #XJ-100)", 1, 350.00); wofgAddMaterialRow("Drive Belt (3/4 x 52 inch)", 1, 18.50); wofgAddMaterialRow("Capacitor, 40/5 uF", 1, 22.00); wofgAddMaterialRow("Fuses (10 Amp)", 2, 1.50); wofgRenderReport(); wofgSwitchTab('preview'); } /* --- PDF Generation --- */ async function wofgGeneratePDF() { wofgRenderReport(); // Final render check const materialsData = wofgGetMaterialsData(); const data = { id: document.getElementById('inp-id').value || "WO-0000", client: document.getElementById('inp-client').value || "N/A Client", address: document.getElementById('inp-address').value || "N/A Address", rep: document.getElementById('inp-rep').value || "N/A Rep", rate: parseFloat(document.getElementById('inp-rate').value) || 0, hours: parseFloat(document.getElementById('inp-hours').value) || 0, desc: document.getElementById('inp-desc').value || "No job description provided.", obs: document.getElementById('inp-obs').value || "No observations recorded.", financials: { labor: 0, subtotal: 0, tax: 0, total: 0 } // Recalculated for PDF }; const laborCost = data.hours * data.rate; const subtotal = laborCost + materialsData.totalCost; const taxRate = 0.08; const tax = subtotal * taxRate; const grandTotal = subtotal + tax; data.financials = { labor: laborCost, subtotal: subtotal, tax: tax, total: grandTotal }; const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'mm', 'a4'); const orange = [230, 126, 34]; const slate = [44, 62, 80]; let y = 20; // 1. Header (WO ID & Date) doc.setFillColor(...slate); doc.rect(0, 0, 210, 20, 'F'); doc.setTextColor(255, 255, 255); doc.setFontSize(16); doc.text("WORK ORDER / JOB REPORT", 14, 13); doc.setFontSize(10); doc.text(`WO ID: ${data.id}`, 14, 17); doc.text(`Date: ${new Date().toLocaleDateString()}`, 150, 17); // 2. Job Metadata doc.setTextColor(0, 0, 0); doc.setFontSize(10); doc.setFont("helvetica", "bold"); doc.text("CLIENT:", 14, y + 10); doc.text("REPRESENTATIVE:", 105, y + 10); y += 15; doc.setFont("helvetica", "normal"); doc.text(data.client, 14, y); doc.text(data.rep, 105, y); y += 5; doc.text(data.address, 14, y); y += 10; // 3. Work Performed doc.setFont("helvetica", "bold"); doc.setTextColor(...slate); doc.setFontSize(12); doc.text("WORK PERFORMED / DESCRIPTION", 14, y); y += 5; doc.setFont("times", "normal"); doc.setFontSize(10); doc.setTextColor(50, 50, 50); const splitDesc = doc.splitTextToSize(data.desc, 180); doc.text(splitDesc, 14, y + 5); y += (splitDesc.length * 5) + 10; // 4. Financial Table doc.setFont("helvetica", "bold"); doc.setTextColor(...slate); doc.setFontSize(12); doc.text("FINANCIAL BREAKDOWN", 14, y); y += 5; // Materials Table const matTableBody = materialsData.materials.map(m => [ m.item, m.qty, `$${m.price}`, `$${m.total}` ]); doc.autoTable({ startY: y, head: [['Item / Part #', 'Qty', 'Unit Price', 'Line Total']], body: matTableBody, theme: 'grid', headStyles: { fillColor: orange, textColor: 255, fontSize: 9 }, styles: { fontSize: 8.5 }, columnStyles: { 2: { halign: 'right' }, 3: { halign: 'right', fontStyle: 'bold' } } }); y = doc.lastAutoTable.finalY; // Totals Summary const totalsTable = [ ['Materials Subtotal', `$${materialsData.totalCost.toFixed(2)}`], ['Labor Hours', `${data.hours.toFixed(1)} @ $${data.rate.toFixed(2)}/hr`], ['Labor Cost', `$${data.financials.labor.toFixed(2)}`], ['GRAND SUBTOTAL', `$${data.financials.subtotal.toFixed(2)}`], ['Sales Tax (8.0%)', `$${data.financials.tax.toFixed(2)}`], [{content: 'TOTAL DUE', styles: { fontStyle: 'bold', fillColor: orange, textColor: 255 }}, {content: `$${data.financials.total.toFixed(2)}`, styles: { fontStyle: 'bold', fillColor: orange, textColor: 255, halign: 'right' }}] ]; doc.autoTable({ startY: y, body: totalsTable, theme: 'plain', styles: { fontSize: 9, cellPadding: 3 }, columnStyles: { 0: { cellWidth: 35, halign: 'right' }, 1: { halign: 'right', cellWidth: 40 } }, margin: { left: 120 } }); y = doc.lastAutoTable.finalY + 15; // 5. Observations doc.setFont("helvetica", "bold"); doc.text("TECHNICIAN OBSERVATIONS / NOTES", 14, y); y += 5; doc.setFont("times", "normal"); doc.setFontSize(10); doc.text(doc.splitTextToSize(data.obs, 180), 14, y + 5); y += 20; // 6. Signatures doc.setFontSize(10); doc.text("Technician Signature: ______________________", 14, y + 10); doc.text("Client Approval Signature: ______________________", 110, y + 10); doc.save(`WorkOrder_${data.id}.pdf`); }
Scroll to Top