enhance load items in pos
This commit is contained in:
parent
923a8e50e7
commit
a052840f3d
151
index.php
151
index.php
@ -393,17 +393,20 @@ if (isset($_GET['action']) || isset($_POST['action'])) {
|
||||
}
|
||||
|
||||
if ($action === 'search_items') {
|
||||
file_put_contents('search_debug.log', date('Y-m-d H:i:s') . " - search_items call: q=" . ($_GET['q'] ?? '') . "\n", FILE_APPEND);
|
||||
header('Content-Type: application/json');
|
||||
$q = $_GET['q'] ?? '';
|
||||
if (strlen($q) < 1) {
|
||||
echo json_encode([]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$searchTerm = "%$q%";
|
||||
$stmt = db()->prepare("SELECT * FROM stock_items WHERE name_en LIKE ? OR name_ar LIKE ? OR sku LIKE ? LIMIT 15");
|
||||
// Optimization: Fetch items with promotional prices calculated
|
||||
$stmt = db()->prepare("SELECT * FROM stock_items WHERE name_en LIKE ? OR name_ar LIKE ? OR sku LIKE ? LIMIT 50");
|
||||
$stmt->execute([$searchTerm, $searchTerm, $searchTerm]);
|
||||
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($items as &$item) {
|
||||
$item['original_price'] = (float)$item['sale_price'];
|
||||
$item['sale_price'] = getPromotionalPrice($item);
|
||||
}
|
||||
echo json_encode($items);
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -6233,7 +6236,8 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
||||
$registers = db()->query("SELECT * FROM cash_registers WHERE status = 'active'")->fetchAll();
|
||||
|
||||
$allow_zero_stock_sell = ($data['settings']['allow_zero_stock_sell'] ?? '1') === '1';
|
||||
$sql = "SELECT * FROM stock_items ORDER BY name_en ASC";
|
||||
// Optimization: Only load top 50 products initially to prevent heavy load
|
||||
$sql = "SELECT * FROM stock_items ORDER BY id DESC LIMIT 50";
|
||||
$products_raw = db()->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
$products = [];
|
||||
foreach ($products_raw as $p) {
|
||||
@ -7249,38 +7253,80 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
||||
}
|
||||
};
|
||||
|
||||
document.querySelectorAll('.product-card').forEach(card => {
|
||||
card.addEventListener('click', () => {
|
||||
const product = {
|
||||
id: parseInt(card.dataset.id),
|
||||
nameEn: card.dataset.nameEn,
|
||||
nameAr: card.dataset.nameAr,
|
||||
price: parseFloat(card.dataset.price),
|
||||
stock_quantity: parseFloat(card.dataset.stockQuantity),
|
||||
vatRate: parseFloat(card.dataset.vatRate) || 0
|
||||
};
|
||||
cart.add(product);
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('productSearch').addEventListener('input', (e) => {
|
||||
const q = e.target.value.toLowerCase();
|
||||
function attachProductClickListeners() {
|
||||
document.querySelectorAll('.product-grid .product-card').forEach(card => {
|
||||
const name = card.dataset.nameEn.toLowerCase() + ' ' + card.dataset.nameAr.toLowerCase();
|
||||
const sku = card.dataset.sku.toLowerCase();
|
||||
if (name.includes(q) || sku.includes(q)) {
|
||||
card.style.display = 'flex';
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
card.onclick = () => {
|
||||
const product = {
|
||||
id: parseInt(card.dataset.id),
|
||||
nameEn: card.dataset.nameEn,
|
||||
nameAr: card.dataset.nameAr,
|
||||
price: parseFloat(card.dataset.price),
|
||||
sku: card.dataset.sku,
|
||||
stock_quantity: parseFloat(card.dataset.stockQuantity),
|
||||
vatRate: parseFloat(card.dataset.vatRate) || 0
|
||||
};
|
||||
cart.add(product);
|
||||
};
|
||||
});
|
||||
}
|
||||
attachProductClickListeners();
|
||||
|
||||
let searchTimeout = null;
|
||||
document.getElementById('productSearch').addEventListener('input', (e) => {
|
||||
clearTimeout(searchTimeout);
|
||||
const q = e.target.value.trim();
|
||||
searchTimeout = setTimeout(async () => {
|
||||
try {
|
||||
const resp = await fetch('index.php?action=search_items&q=' + encodeURIComponent(q));
|
||||
const products = await resp.json();
|
||||
renderProductGrid(products);
|
||||
} catch (err) { console.error(err); }
|
||||
}, 300);
|
||||
});
|
||||
|
||||
document.getElementById('barcodeInput').addEventListener('keypress', (e) => {
|
||||
function renderProductGrid(products) {
|
||||
const grid = document.getElementById('productGrid');
|
||||
const lang = document.documentElement.lang || 'en';
|
||||
if (products.length === 0) {
|
||||
grid.innerHTML = '<div class="text-center w-100 p-5 text-muted"><i class="bi bi-search mb-3 d-block" style="font-size: 3rem;"></i> No products found.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
grid.innerHTML = products.map(p => {
|
||||
const salePrice = parseFloat(p.sale_price);
|
||||
const originalPrice = parseFloat(p.original_price || p.sale_price);
|
||||
const img = p.image_path ? `<img src="${p.image_path}" alt="${p.name_en}">` : `
|
||||
<div class="bg-light d-flex align-items-center justify-content-center rounded mb-2" style="height: 120px;">
|
||||
<i class="bi bi-box-seam text-muted" style="font-size: 3rem;"></i>
|
||||
</div>`;
|
||||
const promoHtml = salePrice < originalPrice ? `<span class="text-muted smaller text-decoration-line-through">OMR ${originalPrice.toFixed(3)}</span>` : '';
|
||||
|
||||
return `
|
||||
<div class="product-card" data-id="${p.id}" data-name-en="${p.name_en}" data-name-ar="${p.name_ar}" data-price="${p.sale_price}" data-sku="${p.sku}" data-stock-quantity="${p.stock_quantity}" data-vat-rate="${p.vat_rate}">
|
||||
${img}
|
||||
<div class="mb-1 product-name">
|
||||
<div class="fw-bold text-truncate" title="${p.name_en}">${p.name_en}</div>
|
||||
<div class="small text-muted fw-normal text-truncate" title="${p.name_ar}" dir="rtl">${p.name_ar}</div>
|
||||
</div>
|
||||
<div class="small text-muted mb-2">${p.sku}</div>
|
||||
<div class="d-flex justify-content-between align-items-center mt-auto">
|
||||
<div class="d-flex flex-column">
|
||||
${promoHtml}
|
||||
<span class="price text-primary fw-bold">OMR ${salePrice.toFixed(3)}</span>
|
||||
</div>
|
||||
<span class="badge bg-light text-dark small">${parseFloat(p.stock_quantity)} left</span>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
attachProductClickListeners();
|
||||
}
|
||||
|
||||
document.getElementById('barcodeInput').addEventListener('keypress', async (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
const barcode = e.target.value.trim();
|
||||
if (!barcode) return;
|
||||
|
||||
// First check local DOM
|
||||
const card = Array.from(document.querySelectorAll('.product-card')).find(c => c.dataset.sku === barcode);
|
||||
if (card) {
|
||||
const product = {
|
||||
@ -7294,24 +7340,31 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
||||
};
|
||||
cart.add(product);
|
||||
e.target.value = '';
|
||||
Swal.fire({
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
icon: 'success',
|
||||
title: 'Added: ' + product.nameEn,
|
||||
showConfirmButton: false,
|
||||
timer: 1000
|
||||
});
|
||||
Swal.fire({ toast: true, position: 'top-end', icon: 'success', title: 'Added: ' + product.nameEn, showConfirmButton: false, timer: 1000 });
|
||||
} else {
|
||||
Swal.fire({
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
icon: 'error',
|
||||
title: 'Product not found',
|
||||
showConfirmButton: false,
|
||||
timer: 1500
|
||||
});
|
||||
e.target.select();
|
||||
// Not in DOM, check server
|
||||
try {
|
||||
const resp = await fetch('index.php?action=search_items&q=' + encodeURIComponent(barcode));
|
||||
const products = await resp.json();
|
||||
const found = products.find(p => p.sku === barcode);
|
||||
if (found) {
|
||||
const product = {
|
||||
id: found.id,
|
||||
nameEn: found.name_en,
|
||||
nameAr: found.name_ar,
|
||||
price: parseFloat(found.sale_price),
|
||||
sku: found.sku,
|
||||
stock_quantity: parseFloat(found.stock_quantity),
|
||||
vatRate: parseFloat(found.vat_rate) || 0
|
||||
};
|
||||
cart.add(product);
|
||||
e.target.value = '';
|
||||
Swal.fire({ toast: true, position: 'top-end', icon: 'success', title: 'Added: ' + product.nameEn, showConfirmButton: false, timer: 1000 });
|
||||
} else {
|
||||
Swal.fire({ toast: true, position: 'top-end', icon: 'error', title: 'Product not found', showConfirmButton: false, timer: 1500 });
|
||||
e.target.select();
|
||||
}
|
||||
} catch (err) { console.error(err); }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user