/** * Shared vendor-dropdown auto-select for the Inventory Create/Edit forms. * * Why this exists: catalog lookup, AI lookup, label scan, and manual manufacturer entry * all need to pick the right Vendor option, and they used to each carry their own copy of * the matching logic. They disagreed on WHAT to match on — the AI path keyed off the * price-derived `vendorName` (which is null unless a price was scraped), so the vendor * only got selected "sometimes". This centralizes the rule: * * For ~95% of powders the manufacturer IS the vendor (Prismatic, Columbia, * All Powder Paints, Tiger, Powder Buy The Pound). So match on the Manufacturer * field first — it's almost always populated — and only fall back to the * AI/catalog-supplied vendor name when the manufacturer is blank. * * Brands sold by more than one distributor (e.g. PPG, KP Pigments) are intentionally * skipped so the user picks the vendor manually rather than getting a wrong guess. */ (function () { 'use strict'; // Brands carried by multiple distributors — never auto-pick a vendor for these. // Lowercase; matched as a substring against the manufacturer name. Extend as needed. const AMBIGUOUS_BRANDS = ['ppg', 'kp pigments', 'kp pigment']; function normalize(s) { return (s || '').toLowerCase().trim(); } function isAmbiguousBrand(name) { const n = normalize(name); return n.length > 0 && AMBIGUOUS_BRANDS.some(b => n.includes(b)); } /** * Selects the vendor dropdown option that best matches a manufacturer/vendor name. * * @param {HTMLSelectElement} vendorSelect the #field-vendor element * @param {string} manufacturerName primary name to match on (the Manufacturer field) * @param {string} fallbackVendorName AI/catalog vendor name, used only if manufacturer is blank * @param {{force?: boolean}} [opts] force=true overrides an existing selection (bad-match retry) * @returns {boolean} true if a vendor option was selected. */ window.matchInventoryVendor = function (vendorSelect, manufacturerName, fallbackVendorName, opts) { opts = opts || {}; if (!vendorSelect) return false; // Don't clobber a choice the user (or a prior fill) already made, unless forcing a re-fill. if (vendorSelect.value && !opts.force) return false; // Manufacturer drives the match; the price-derived vendor name is only a fallback. const name = normalize(manufacturerName) || normalize(fallbackVendorName); if (!name) return false; // Brands sold by multiple distributors stay manual — don't guess. if (isAmbiguousBrand(name)) return false; const match = Array.from(vendorSelect.options).find(function (o) { const t = normalize(o.text); // Skip the placeholder and the "Add new vendor" sentinel; require a real name to // avoid spurious substring hits (e.g. empty option text matches everything). if (!o.value || o.value === '__new__' || t.length < 3) return false; return t.includes(name) || name.includes(t); }); if (match) { vendorSelect.value = match.value; return true; } return false; }; })();