'use strict'; (() => { if (typeof inventoryPage !== 'function') return; const purchaseOrderReadRoles = new Set(['owner','admin','accountant','office_manager','operations_manager']); const purchaseOrderWriteRoles = new Set(['owner','admin','office_manager','operations_manager']); const purchaseOrderApproveRoles = new Set(['owner','admin']); const baseInventoryPage = inventoryPage; const style = document.createElement('style'); style.textContent = ` .po-card{margin-top:16px}.po-head{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;flex-wrap:wrap}.po-head h3{margin:0}.po-head p{margin:5px 0 0;color:var(--muted);max-width:760px}.po-boundary{margin:14px 0;padding:12px 14px;border:1px solid #cfe7df;border-radius:10px;background:#f4fbf8;color:#335b50;font-size:12px;line-height:1.5}.po-row-actions{display:flex;gap:6px;flex-wrap:wrap}.po-row-actions .btn{padding:7px 9px;font-size:12px}.po-status-note{display:block;margin-top:4px;color:var(--muted);font-size:11px}.po-empty{padding:24px 10px;text-align:center;color:var(--muted)} @media(max-width:700px){.po-head .actions{width:100%}.po-head .actions .btn{flex:1}.po-row-actions{min-width:190px}} `; document.head.append(style); const statusLabel = status => ({draft:'Draft',pending_approval:'Pending approval',ordered:'Ordered',shipped:'Shipped',received:'Received',cancelled:'Cancelled'})[status] || String(status || 'Unknown'); const statusTone = status => ({received:'good',ordered:'info',shipped:'info',pending_approval:'warn',cancelled:'bad',draft:''})[status] || ''; const expectedLabel = value => value ? new Date(value).toLocaleString() : 'Not set'; function purchaseOrderActions(row) { if (!purchaseOrderWriteRoles.has(user?.role)) return 'Read only'; const id = esc(row.id); if (row.status === 'draft') return `
`; if (row.status === 'pending_approval') { const approve = purchaseOrderApproveRoles.has(user?.role) ? `` : 'Owner/admin approval required to mark ordered'; return `
${approve}
`; } if (row.status === 'ordered') return `
`; if (row.status === 'shipped') return `
`; return 'Final state'; } function purchaseOrderRows(items) { if (!items.length) return '
No purchase orders yet. Create a draft when supplies need procurement tracking.
'; return items.map(row => `${esc(row.vendor_name || 'Unnamed vendor')}Created ${new Date(row.created_at).toLocaleString()}${row.created_by_name ? ` by ${esc(row.created_by_name)}` : ''}${pill(statusLabel(row.status),statusTone(row.status))}${money(row.total || 0)}${esc(expectedLabel(row.expected_at))}${esc(String(row.id).slice(0,8).toUpperCase())}${purchaseOrderActions(row)}`).join(''); } inventoryPage = async function() { const page = await baseInventoryPage(); if (!purchaseOrderReadRoles.has(user?.role)) return page; let payload; try { payload = await api('/api/purchase-orders'); } catch (error) { return page + `

Purchase orders

Procurement tracking is temporarily unavailable. Existing inventory and job-cost controls are unchanged.

`; } const items = Array.isArray(payload?.items) ? payload.items : []; const canCreate = purchaseOrderWriteRoles.has(user?.role); return page + `

Purchase orders

Move supply purchases from draft through approval, shipment, and receipt with an audited internal status trail.

${canCreate ? '' : ''}
Internal procurement tracking only. Creating or changing a purchase order does not send anything to a vendor, move money, post an accounting expense, or automatically increase inventory. Record the real expense and inventory receipt through their reviewed workflows when those events actually occur.
${purchaseOrderRows(items)}
VendorStatusTracked totalExpectedPO refActions
`; }; window.openPurchaseOrderForm = () => { if (!purchaseOrderWriteRoles.has(user?.role)) return toast('You do not have permission to create purchase orders.', true); $('#purchaseOrderModal')?.remove(); document.body.insertAdjacentHTML('beforeend', ``); }; window.savePurchaseOrder = async () => { if (!purchaseOrderWriteRoles.has(user?.role)) return toast('You do not have permission to create purchase orders.', true); const vendorName = String($('#po_vendor')?.value || '').trim(); const total = Number($('#po_total')?.value); const expectedValue = String($('#po_expected')?.value || '').trim(); if (!vendorName) return toast('Enter a vendor name.', true); if (!Number.isFinite(total) || total < 0) return toast('Enter a non-negative purchase-order total.', true); let expectedAt = null; if (expectedValue) { const expected = new Date(expectedValue); if (Number.isNaN(expected.getTime())) return toast('Enter a valid expected date and time.', true); expectedAt = expected.toISOString(); } try { await api('/api/purchase-orders', { method:'POST', body:JSON.stringify({ vendorName, total, expectedAt }) }); $('#purchaseOrderModal')?.remove(); toast('Draft purchase order created.'); await render('supplies'); } catch (error) { toast(error.message, true); } }; window.changePurchaseOrderStatus = async (id, status) => { if (!purchaseOrderWriteRoles.has(user?.role)) return toast('You do not have permission to change purchase orders.', true); if (status === 'ordered' && !purchaseOrderApproveRoles.has(user?.role)) return toast('Owner or admin approval is required.', true); const messages = { pending_approval:'Submit this draft for owner/admin approval?', draft:'Return this purchase order to draft?', ordered:'Approve and mark this purchase order ordered? This updates internal tracking only; FacilityOps HQ will not contact the vendor or move money.', shipped:'Mark this purchase order shipped in internal tracking?', received:'Mark this purchase order received? This does not automatically increase inventory; reconcile received stock separately.', cancelled:'Cancel this purchase order in internal tracking?' }; if (!confirm(messages[status] || `Change purchase order to ${status}?`)) return; try { await api(`/api/purchase-orders/${encodeURIComponent(id)}/status`, { method:'POST', body:JSON.stringify({ status }) }); toast(`Purchase order marked ${statusLabel(status).toLowerCase()}.`); await render('supplies'); } catch (error) { toast(error.message, true); } }; })();