enhance load items in pos

This commit is contained in:
Flatlogic Bot 2026-02-26 13:51:13 +00:00
parent 923a8e50e7
commit a052840f3d

151
index.php
View File

@ -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); }
}
}
});