feat(pos/inventory): product search instead of ID in purchase entry modal
- Replace ID Producto input with autocomplete search by name/part number/barcode - Support Enter key for barcode/part number exact match - Keep hidden inventory_id field for API compatibility - Bump inventory.js cache version
This commit is contained in:
@@ -478,16 +478,140 @@
|
||||
// PURCHASE / ENTRADA (purchaseModal)
|
||||
// =====================================================================
|
||||
|
||||
let purchaseSearchTimeout = null;
|
||||
let purchaseSelectedItem = null;
|
||||
|
||||
function showPurchaseModal() {
|
||||
document.getElementById('purchaseModal').classList.add('is-open');
|
||||
setTimeout(function() {
|
||||
var el = document.getElementById('purchaseItemSearch');
|
||||
if (el) el.focus();
|
||||
}, 100);
|
||||
}
|
||||
function showPurchaseModalForItem(itemId) {
|
||||
document.getElementById('purchaseItemId').value = itemId;
|
||||
// Pre-fill by fetching item details
|
||||
apiFetch(API + '/items?page=1&per_page=1').then(function() {
|
||||
// We just need the item detail; use the existing list or fetch by id
|
||||
apiFetch(API + '/items?page=1&per_page=1').then(function() {});
|
||||
});
|
||||
selectPurchaseItem({id: itemId, name: 'Producto #' + itemId});
|
||||
showPurchaseModal();
|
||||
}
|
||||
function closePurchaseModal() {
|
||||
document.getElementById('purchaseModal').classList.remove('is-open');
|
||||
document.getElementById('purchaseResult').innerHTML = '';
|
||||
clearPurchaseSelection();
|
||||
}
|
||||
|
||||
function clearPurchaseSelection() {
|
||||
purchaseSelectedItem = null;
|
||||
var ids = ['purchaseItemId','purchaseItemSearch','purchaseQty','purchaseCost','purchaseInvoice','purchaseNotes'];
|
||||
ids.forEach(function(id) {
|
||||
var el = document.getElementById(id);
|
||||
if (el) el.value = '';
|
||||
});
|
||||
var results = document.getElementById('purchaseItemResults');
|
||||
if (results) results.style.display = 'none';
|
||||
var selected = document.getElementById('purchaseItemSelected');
|
||||
if (selected) selected.textContent = '';
|
||||
}
|
||||
|
||||
function selectPurchaseItem(item) {
|
||||
purchaseSelectedItem = item;
|
||||
document.getElementById('purchaseItemId').value = item.id;
|
||||
document.getElementById('purchaseItemSearch').value = item.name || item.part_number || item.barcode || ('#' + item.id);
|
||||
document.getElementById('purchaseItemResults').style.display = 'none';
|
||||
document.getElementById('purchaseItemSelected').innerHTML =
|
||||
'<strong>' + esc(item.name || '') + '</strong>' +
|
||||
(item.part_number ? ' · No. parte: ' + esc(item.part_number) : '') +
|
||||
(item.barcode ? ' · Barcode: ' + esc(item.barcode) : '');
|
||||
document.getElementById('purchaseQty').focus();
|
||||
}
|
||||
|
||||
function searchPurchaseItems(query) {
|
||||
var resultsEl = document.getElementById('purchaseItemResults');
|
||||
if (!query || query.length < 2) {
|
||||
resultsEl.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
apiFetch(API + '/items?q=' + encodeURIComponent(query) + '&per_page=10').then(function(res) {
|
||||
var items = (res && res.items) || [];
|
||||
if (!items.length) {
|
||||
resultsEl.innerHTML = '<div style="padding:var(--space-3);color:var(--color-text-muted);font-size:var(--text-caption);">Sin resultados</div>';
|
||||
resultsEl.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
resultsEl.innerHTML = items.map(function(it) {
|
||||
return '<div class="purchase-search-result" style="padding:var(--space-3);cursor:pointer;border-bottom:1px solid var(--color-border);" ' +
|
||||
'data-id="' + it.id + '">' +
|
||||
'<div style="font-weight:var(--font-weight-semibold);">' + esc(it.name) + '</div>' +
|
||||
'<div style="font-size:var(--text-caption);color:var(--color-text-muted);">' +
|
||||
(it.part_number ? 'No. parte: ' + esc(it.part_number) + ' · ' : '') +
|
||||
(it.barcode ? 'Barcode: ' + esc(it.barcode) + ' · ' : '') +
|
||||
'Stock: ' + (it.stock || 0) +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
resultsEl.querySelectorAll('.purchase-search-result').forEach(function(row) {
|
||||
row.onclick = function() {
|
||||
var id = parseInt(row.dataset.id);
|
||||
var item = items.find(function(x) { return x.id === id; });
|
||||
if (item) selectPurchaseItem(item);
|
||||
};
|
||||
});
|
||||
resultsEl.style.display = 'block';
|
||||
}).catch(function() {
|
||||
resultsEl.style.display = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function wirePurchaseSearch() {
|
||||
var input = document.getElementById('purchaseItemSearch');
|
||||
var resultsEl = document.getElementById('purchaseItemResults');
|
||||
if (!input) return;
|
||||
|
||||
input.addEventListener('input', function() {
|
||||
if (purchaseSelectedItem && input.value !== purchaseSelectedItem.name) {
|
||||
purchaseSelectedItem = null;
|
||||
document.getElementById('purchaseItemId').value = '';
|
||||
document.getElementById('purchaseItemSelected').textContent = '';
|
||||
}
|
||||
clearTimeout(purchaseSearchTimeout);
|
||||
purchaseSearchTimeout = setTimeout(function() {
|
||||
searchPurchaseItems(input.value.trim());
|
||||
}, 250);
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
// Try exact barcode match first
|
||||
var query = input.value.trim();
|
||||
if (!query) return;
|
||||
apiFetch(API + '/items?q=' + encodeURIComponent(query) + '&per_page=20').then(function(res) {
|
||||
var items = (res && res.items) || [];
|
||||
var exact = items.find(function(it) {
|
||||
return (it.barcode || '').toLowerCase() === query.toLowerCase() ||
|
||||
(it.part_number || '').toLowerCase() === query.toLowerCase();
|
||||
});
|
||||
if (exact) {
|
||||
selectPurchaseItem(exact);
|
||||
} else if (items.length === 1) {
|
||||
selectPurchaseItem(items[0]);
|
||||
} else {
|
||||
searchPurchaseItems(query);
|
||||
}
|
||||
});
|
||||
} else if (e.key === 'Escape') {
|
||||
if (resultsEl) resultsEl.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', function(e) {
|
||||
if (resultsEl && !input.contains(e.target) && !resultsEl.contains(e.target)) {
|
||||
resultsEl.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function recordPurchase() {
|
||||
@@ -506,10 +630,6 @@
|
||||
if (result && result.operation_id) {
|
||||
document.getElementById('purchaseResult').innerHTML = '<span style="color:var(--color-success);">Compra registrada (op #' + result.operation_id + ')</span>';
|
||||
closePurchaseModal();
|
||||
['purchaseItemId','purchaseQty','purchaseCost','purchaseInvoice','purchaseNotes'].forEach(function(id) {
|
||||
var el = document.getElementById(id);
|
||||
if (el) el.value = '';
|
||||
});
|
||||
if (window.loadInventoryStats) window.loadInventoryStats();
|
||||
loadItems(currentPage);
|
||||
} else {
|
||||
@@ -2009,4 +2129,5 @@
|
||||
|
||||
loadItems(1);
|
||||
renderSavedFilters();
|
||||
wirePurchaseSearch();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user