1(function () { 2 'use strict'; 3 4 function create(options) { 5 var ctx = options.context; 6 var state = { key: '', catalog: null, pending: null, sequence: 0, connection: null, 7 reconnectTimer: null, liveAttempts: 0, liveError: '', invalidated: false, stopped: false }; 8 var L = options.label; 9 10 function activeInventory() { 11 return !state.stopped && !ctx.disposed && ctx.entitytype === 'forinventory_item' 12 && ctx.mode !== 'list' && ctx.schema && ctx.schema.uom_conversions === true; 13 } 14 function key() { 15 var scope = ownerScope(); 16 return [ctx.viewId, ctx.companyid, scope.owner_type, scope.product_id, scope.inventory_item_id, ctx.values.uom || ''].join('|'); 17 } 18 function ownerScope() { 19 var internal = ctx.values._uom_owner_type === 'inventory' || !ctx.values.qbo_item_id; 20 var id = ctx.recordid || ctx.values.Id || ctx.values.id || ''; 21 return internal 22 ? { owner_type: id ? 'inventory' : 'global', product_id: '', inventory_item_id: id } 23 : { owner_type: 'finance', product_id: ctx.values.qbo_item_id, inventory_item_id: '' }; 24 } 25 function field(name) { 26 return options.body().querySelector('[data-orca-field="' + name + '"]'); 27 } 28 function unitLabel(name, value) { 29 var fields = ((ctx.schema || {}).sections || []).flatMap(function (section) { return section.fields || []; }); 30 var config = fields.find(function (entry) { return entry.key === name; }); 31 var item = config && (config.options || []).find(function (entry) { return entry.value === value; }); 32 return item ? item.label : value; 33 } 34 function newButton(icon, title, action) { 35 var button = document.createElement('button'); 36 button.type = 'button'; 37 button.className = 'flex h-7 w-7 shrink-0 items-center justify-center rounded-md border-0 p-0 disabled:cursor-not-allowed disabled:opacity-50 ' + options.buttonClass('setup'); 38 button.title = title; 39 button.setAttribute('aria-label', title); 40 var glyph = document.createElement('span'); 41 glyph.className = 'material-symbols-outlined text-base leading-none'; 42 glyph.textContent = icon; 43 button.appendChild(glyph); 44 button.addEventListener('click', action); 45 return button; 46 } 47 function bindPurchase() { 48 var select = field('purchase_uom'); 49 if (!select || select.dataset.orcaUomBound) return; 50 select.dataset.orcaUomBound = '1'; 51 var host = document.createElement('div'); 52 host.className = 'flex h-9 w-full min-w-0 items-center gap-1'; 53 select.before(host); 54 host.appendChild(select); 55 select.classList.add('h-9', 'min-w-0', 'flex-1'); 56 var add = newButton('add', L('Create UoM conversion'), function () { 57 var scope = ownerScope(); 58 if (scope.owner_type === 'global' || !ctx.values.uom || ctx.schema.can_manage_uom !== true) { 59 options.status(L('Save the inventory product before creating a conversion.'), '#f59e0b'); 60 return; 61 } 62 options.openConversion(Object.assign({}, scope, { 63 product_id_display: ctx.values.qbo_item_id_display || '', 64 inventory_item_id_display: ctx.values.name || ctx.values.sku || '', 65 to_uom: ctx.values.uom 66 }), ['owner_type', 'product_id', 'inventory_item_id', 'to_uom']); 67 }); 68 add.dataset.orcaUomCreate = '1'; 69 host.appendChild(add); 70 var retry = newButton('refresh', L('Retry'), function () { 71 state.liveAttempts = 0; 72 clearTimeout(state.reconnectTimer); 73 state.reconnectTimer = null; 74 refresh(true); 75 watch(); 76 }); 77 retry.dataset.orcaUomRetry = '1'; 78 host.appendChild(retry); 79 var stock = field('uom'); 80 if (stock && !stock.dataset.orcaUomBound) { 81 stock.dataset.orcaUomBound = '1'; 82 stock.addEventListener('change', function () { refresh(); }); 83 } 84 } 85 function controls() { 86 var select = field('purchase_uom'); 87 if (!select) return; 88 select.disabled = !!ctx.uomCatalogPending || !!ctx.uomCatalogError || !!ctx.saving; 89 var add = options.body().querySelector('[data-orca-uom-create]'); 90 if (add) { 91 add.disabled = !!ctx.saving || ctx.schema.can_manage_uom !== true || ownerScope().owner_type === 'global' || !ctx.values.uom; 92 add.title = ownerScope().owner_type === 'global' ? L('Save the inventory product before creating a conversion.') : L('Create UoM conversion'); 93 add.setAttribute('aria-label', add.title); 94 } 95 var retry = options.body().querySelector('[data-orca-uom-retry]'); 96 if (retry) retry.classList.toggle('!hidden', !ctx.uomCatalogError && !state.liveError); 97 } 98 function applyCatalog(catalog) { 99 var select = field('purchase_uom'); 100 if (!select) return false;
101 var current = String(ctx.values.purchase_uom || '').trim().toLowerCase(); 102 var units = catalog.purchase_units; 103 if (!Array.isArray(units)) throw new Error(L('Unit conversions are unavailable.')); 104 select.replaceChildren(); 105 var blank = document.createElement('option'); 106 blank.value = ''; 107 blank.textContent = L('Select a compatible purchase unit.'); 108 select.appendChild(blank); 109 units.forEach(function (unit) { 110 var option = document.createElement('option'); 111 option.value = unit.value; 112 option.textContent = unitLabel('purchase_uom', unit.value) || unit.label || unit.value; 113 select.appendChild(option); 114 }); 115 var cleared = current && !units.some(function (unit) { return unit.value === current; }); 116 if (cleared) { 117 ctx.values.purchase_uom = ''; 118 ctx.dirty = true; 119 options.status(L('Purchase unit cleared. Select a compatible unit.'), '#f59e0b'); 120 } 121 select.value = cleared ? '' : current; 122 return !!cleared; 123 } 124 function refresh(force, announce) { 125 if (!activeInventory()) return Promise.resolve(null); 126 bindPurchase(); 127 if (ctx.saving) { state.invalidated = state.invalidated || !!force; return Promise.resolve(null); } 128 var wanted = key(); 129 if (!force && !state.invalidated && state.key === wanted) { 130 if (state.catalog) applyCatalog(state.catalog); 131 controls(); 132 return state.pending ? state.pending.promise : Promise.resolve(state.catalog); 133 } 134 if (state.pending) state.pending.controller.abort(); 135 var sequence = ++state.sequence; 136 var controller = new AbortController(); 137 state.key = wanted; 138 state.catalog = null; 139 state.invalidated = false; 140 ctx.uomCatalogPending = true; 141 ctx.uomCatalogError = ''; 142 controls(); 143 var promise = options.command('uom_catalog', Object.assign(ownerScope(), { 144 stock_uom: ctx.values.uom || '' 145 }), controller.signal).then(function (response) { 146 if (state.stopped || ctx.disposed || sequence !== state.sequence || key() !== wanted) return null; 147 if (!response || response.success === false || !response.values) 148 throw new Error(response && response.message || L('Unit conversions are unavailable.')); 149 state.catalog = response.values; 150 var cleared = applyCatalog(state.catalog); 151 if (announce && !cleared) options.status(L('UoM conversions updated.'), '#22c55e'); 152 return state.catalog; 153 }).catch(function (error) { 154 if (error.name === 'AbortError' || state.stopped || ctx.disposed || sequence !== state.sequence || key() !== wanted) return null; 155 state.catalog = null; 156 ctx.uomCatalogError = error.message || L('Unit conversions are unavailable.'); 157 options.status(ctx.uomCatalogError, '#f87171'); 158 return null; 159 }).finally(function () { 160 if (sequence !== state.sequence) return; 161 state.pending = null; 162 ctx.uomCatalogPending = false; 163 controls(); 164 }); 165 state.pending = { controller: controller, promise: promise }; 166 return promise; 167 } 168 function watch() { 169 if (!activeInventory() || state.connection || state.reconnectTimer || state.liveAttempts >= 3 || !window.signalR) return; 170 var connection = new window.signalR.HubConnectionBuilder().withUrl('/orcaHub') 171 .withAutomaticReconnect([0, 1000, 3000, 10000]).configureLogging(window.signalR.LogLevel.Warning).build(); 172 state.connection = connection; 173 function join() { 174 return connection.invoke('JoinEntityForm', ctx.companyid, 'uom_conversion', '_list', ctx.uiSurface).then(function () { 175 if (state.stopped || state.connection !== connection) return; 176 state.liveAttempts = 0; 177 state.liveError = '';
178 state.invalidated = true; 179 controls(); 180 return refresh(true); 181 }); 182 } 183 function disconnected(error) { 184 if (state.stopped || state.connection !== connection) return; 185 state.connection = null; 186 state.invalidated = true; 187 state.liveError = L('Unit conversion updates are unavailable.'); 188 options.status(state.liveError + (error && error.message ? ' ' + error.message : ''), '#f59e0b'); 189 controls(); 190 connection.stop().catch(function (stopError) { console.warn('Unit conversion subscription cleanup failed', stopError); }); 191 if (++state.liveAttempts < 3) { 192 state.reconnectTimer = setTimeout(function () { 193 state.reconnectTimer = null; 194 watch(); 195 }, state.liveAttempts === 1 ? 1000 : 3000); 196 } 197 } 198 connection.on('entityFormChanged', function (payload) { 199 try { 200 var message = typeof payload === 'string' ? JSON.parse(payload) : payload; 201 if (state.connection !== connection || !message || message.companyid !== ctx.companyid 202 || message.entitytype !== 'uom_conversion' || message.recordid !== '_list') return; 203 state.invalidated = true; 204 refresh(true, true); 205 } catch (error) { console.error('Invalid unit conversion notification', error); } 206 }); 207 connection.onclose(disconnected); 208 connection.onreconnected(function () { 209 join().catch(disconnected); 210 }); 211 connection.start().then(join).catch(disconnected); 212 } 213 function inverse() { 214 var display = options.body().querySelector('[data-orca-field-wrap="inverse_rate"] > div'); 215 if (!display) return; 216 var rate = Number(ctx.values.rate); 217 var from = String(ctx.values.from_uom || ''); 218 var to = String(ctx.values.to_uom || ''); 219 var text = from && to && Number.isFinite(rate) && rate > 0 && Number.isFinite(1 / rate) 220 ? '1 ' + to + ' = ' + String(1 / rate) + ' ' + from : 'N/A'; 221 display.replaceChildren(); 222 var icon = document.createElement('span'); 223 icon.className = 'material-symbols-outlined mr-1 text-sm text-slate-500'; 224 icon.textContent = 'lock'; 225 var value = document.createElement('span'); 226 value.className = 'min-w-0 break-words text-xs'; 227 value.textContent = text; 228 display.append(icon, value); 229 } 230 function sync() { 231 if (state.stopped || ctx.disposed) return; 232 if (activeInventory()) { 233 refresh(); 234 watch(); 235 } else if (ctx.entitytype === 'uom_conversion' && ctx.mode !== 'list') { 236 ['from_uom', 'to_uom', 'rate'].forEach(function (name) { 237 var input = field(name); 238 if (!input || input.dataset.orcaUomBound) return; 239 input.dataset.orcaUomBound = '1'; 240 if (name === 'rate') input.setAttribute('step', 'any'); 241 input.addEventListener('input', inverse); 242 input.addEventListener('change', inverse); 243 }); 244 inverse(); 245 } 246 } 247 function stop() { 248 state.stopped = true; 249 ++state.sequence; 250 clearTimeout(state.reconnectTimer); 251 state.reconnectTimer = null; 252 if (state.pending) state.pending.controller.abort(); 253 if (state.connection) state.connection.stop().catch(function (error) { console.warn('Unit conversion subscription cleanup failed', error); }); 254 state.connection = null; 255 } 256 return { sync: sync, refresh: refresh, stop: stop }; 257 } 258 window.OrcaUomWorkspace = { create: create }; 259})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.