/* * Product autocomplete for the add-item row on invoices, quotes and recurring * invoices. * * The three pages used to carry a copy of this each, and they drifted - the * quote and recurring invoice copies were fed a four column product query, so * item.product_name was undefined and selecting a product wrote the literal * string "undefined" into the item name. * * Call with the array emitted by getProductsForAutocomplete(): * * initProductAutocomplete(= $json_products ?? '[]' ?>); */ function initProductAutocomplete(availableProducts) { var nameInput = document.getElementById('name'); if (!nameInput) { return; } // Quote and recurring invoice do not store the product link yet, so treat // the hidden input as optional rather than throwing on every keystroke. var productIdInput = document.getElementById('product_id'); itflowAutocomplete(nameInput, { minLength: 1, source: availableProducts || [], match: function (item, term) { return String(item.label || '').toLowerCase().indexOf(term) !== -1 || String(item.product_name || '').toLowerCase().indexOf(term) !== -1 || String(item.product_code || '').toLowerCase().indexOf(term) !== -1; }, render: function (item) { var esc = itflowEscapeHtml; var typeText = item.type ? item.type.charAt(0).toUpperCase() + item.type.slice(1).toLowerCase() : ""; var showStock = (typeText.toLowerCase() !== "service"); var taxText = (item.tax_percent != null) ? (parseFloat(item.tax_percent) + "%") : "No tax"; var priceText = (item.price != null && item.price !== "") ? String(item.price) : ""; var stockText = (item.available_stock ?? 0); return "