38960-vm/includes/pages/pharmacy_pos.php
2026-03-21 09:33:13 +00:00

342 lines
14 KiB
PHP

<div class="container-fluid h-100">
<div class="row h-100">
<!-- Left Panel: Product Search -->
<div class="col-md-7 d-flex flex-column h-100">
<div class="card shadow-sm flex-grow-1">
<div class="card-header bg-white py-3">
<div class="input-group input-group-lg">
<span class="input-group-text bg-light border-end-0"><i class="bi bi-search"></i></span>
<input type="text" id="drugSearch" class="form-control border-start-0" placeholder="<?php echo __('search_by_name'); ?> / SKU..." autocomplete="off">
</div>
</div>
<div class="card-body p-0 overflow-auto" style="max-height: calc(100vh - 200px);">
<div id="drugList" class="list-group list-group-flush">
<!-- Populated by JS -->
<div class="text-center py-5 text-muted">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Right Panel: Cart -->
<div class="col-md-5 d-flex flex-column h-100">
<div class="card shadow-sm flex-grow-1 border-primary">
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
<h5 class="mb-0"><i class="bi bi-cart3 me-2"></i> <?php echo __('cart'); ?></h5>
<span id="cartCount" class="badge bg-white text-primary rounded-pill">0</span>
</div>
<div class="card-body p-0 d-flex flex-column">
<div class="table-responsive flex-grow-1" style="max-height: calc(100vh - 350px); overflow-y: auto;">
<table class="table table-striped mb-0">
<thead class="sticky-top bg-light">
<tr>
<th><?php echo __('item'); ?></th>
<th width="80"><?php echo __('qty'); ?></th>
<th width="100" class="text-end"><?php echo __('total'); ?></th>
<th width="50"></th>
</tr>
</thead>
<tbody id="cartTableBody">
<!-- Cart Items -->
</tbody>
</table>
</div>
<div class="p-3 bg-light border-top">
<div class="d-flex justify-content-between mb-2">
<span class="fs-5"><?php echo __('total'); ?>:</span>
<span class="fs-4 fw-bold text-primary" id="cartTotal">0.00</span>
</div>
<button class="btn btn-success w-100 btn-lg" onclick="showCheckout()">
<i class="bi bi-credit-card me-2"></i> <?php echo __('checkout'); ?>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Checkout Modal -->
<div class="modal fade" id="checkoutModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><?php echo __('checkout'); ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="checkoutForm">
<div class="mb-3">
<label class="form-label"><?php echo __('patient'); ?> (<?php echo __('optional'); ?>)</label>
<select class="form-select select2-patient" id="patientSelect" style="width: 100%;">
<option value=""><?php echo __('select_patient'); ?></option>
<!-- Populated via AJAX or PHP if small list -->
</select>
</div>
<!-- Visit ID (Optional linkage) -->
<input type="hidden" id="visitSelect" value="">
<div class="mb-3">
<label class="form-label"><?php echo __('payment_method'); ?></label>
<div class="btn-group w-100" role="group">
<input type="radio" class="btn-check" name="payment_method" id="pay_cash" value="cash" checked>
<label class="btn btn-outline-primary" for="pay_cash"><i class="bi bi-cash me-1"></i> <?php echo __('cash'); ?></label>
<input type="radio" class="btn-check" name="payment_method" id="pay_card" value="card">
<label class="btn btn-outline-primary" for="pay_card"><i class="bi bi-credit-card me-1"></i> <?php echo __('card'); ?></label>
<input type="radio" class="btn-check" name="payment_method" id="pay_insurance" value="insurance">
<label class="btn btn-outline-primary" for="pay_insurance"><i class="bi bi-shield-check me-1"></i> <?php echo __('insurance'); ?></label>
</div>
</div>
<div class="alert alert-info d-flex justify-content-between">
<span><?php echo __('total_amount'); ?>:</span>
<strong id="checkoutTotal">0.00</strong>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo __('cancel'); ?></button>
<button type="button" class="btn btn-primary" onclick="processSale()">
<i class="bi bi-check-lg me-1"></i> <?php echo __('confirm_payment'); ?>
</button>
</div>
</div>
</div>
</div>
<script>
let cart = [];
let debounceTimer;
const searchInput = document.getElementById('drugSearch');
const drugList = document.getElementById('drugList');
const cartTableBody = document.getElementById('cartTableBody');
const cartTotalEl = document.getElementById('cartTotal');
const cartCountEl = document.getElementById('cartCount');
const checkoutTotalEl = document.getElementById('checkoutTotal');
function fetchDrugs(query = '') {
// Show loading if query is manual, or just keep calm
if (query.length > 0) {
// Optional: show loading indicator
}
fetch('api/pharmacy.php?action=search_drugs&q=' + encodeURIComponent(query))
.then(r => r.json())
.then(data => {
drugList.innerHTML = '';
if (data.length === 0) {
drugList.innerHTML = '<div class="list-group-item text-center text-muted"><?php echo __('no_drugs_found'); ?></div>';
return;
}
data.forEach(drug => {
const stock = parseFloat(drug.stock);
// Use batch price if available (current active price), otherwise default
const price = parseFloat(drug.batch_price || drug.default_price || 0);
const isOutOfStock = stock <= 0;
const item = document.createElement('a');
item.className = `list-group-item list-group-item-action d-flex justify-content-between align-items-center ${isOutOfStock ? 'disabled bg-light' : ''}`;
let skuHtml = drug.sku ? `<span class="badge bg-secondary me-2">${drug.sku}</span>` : '';
item.innerHTML = `
<div>
<div class="fw-bold">${drug.name_en}</div>
<small class="text-muted">${skuHtml}${drug.name_ar || ''}</small>
</div>
<div class="text-end">
<div class="fw-bold text-primary">${price.toFixed(2)}</div>
<small class="${isOutOfStock ? 'text-danger' : 'text-success'}">
${isOutOfStock ? '<?php echo __('out_of_stock'); ?>' : '<?php echo __('stock'); ?>: ' + stock}
</small>
</div>
`;
if (!isOutOfStock) {
item.onclick = () => addToCart(drug);
item.style.cursor = 'pointer';
}
drugList.appendChild(item);
});
})
.catch(err => {
console.error(err);
drugList.innerHTML = '<div class="list-group-item text-center text-danger">Error loading items</div>';
});
}
// Initial Load
document.addEventListener('DOMContentLoaded', () => {
fetchDrugs();
renderCart(); // Initialize empty cart view
});
// Search Logic
searchInput.addEventListener('input', function() {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
const query = this.value.trim();
fetchDrugs(query);
}, 300);
});
// Cart Logic
function addToCart(drug) {
const existing = cart.find(i => i.id === drug.id);
if (existing) {
if (existing.quantity >= drug.stock) {
alert('<?php echo __('insufficient_stock'); ?>');
return;
}
existing.quantity++;
} else {
cart.push({
id: drug.id,
name: drug.name_en,
// Use batch price if available
price: parseFloat(drug.batch_price || drug.default_price || 0),
quantity: 1,
max_stock: parseFloat(drug.stock)
});
}
renderCart();
}
function updateQty(id, change) {
const item = cart.find(i => i.id === id);
if (!item) return;
const newQty = item.quantity + change;
if (newQty > item.max_stock) {
alert('<?php echo __('insufficient_stock'); ?>');
return;
}
if (newQty <= 0) {
cart = cart.filter(i => i.id !== id);
} else {
item.quantity = newQty;
}
renderCart();
}
function renderCart() {
cartTableBody.innerHTML = '';
let total = 0;
cart.forEach(item => {
const itemTotal = item.quantity * item.price;
total += itemTotal;
cartTableBody.innerHTML += `
<tr>
<td>
<div class="fw-bold text-truncate" style="max-width: 180px;">${item.name}</div>
<small class="text-muted">${item.price.toFixed(2)}</small>
</td>
<td>
<div class="input-group input-group-sm">
<button class="btn btn-outline-secondary" onclick="updateQty(${item.id}, -1)">-</button>
<input type="text" class="form-control text-center p-0" value="${item.quantity}" readonly>
<button class="btn btn-outline-secondary" onclick="updateQty(${item.id}, 1)">+</button>
</div>
</td>
<td class="text-end fw-bold">${itemTotal.toFixed(2)}</td>
<td class="text-end">
<button class="btn btn-sm btn-link text-danger p-0" onclick="updateQty(${item.id}, -1000)"><i class="bi bi-trash"></i></button>
</td>
</tr>
`;
});
cartTotalEl.textContent = total.toFixed(2);
cartCountEl.textContent = cart.length;
checkoutTotalEl.textContent = total.toFixed(2);
if (cart.length === 0) {
cartTableBody.innerHTML = '<tr><td colspan="4" class="text-center text-muted py-4"><?php echo __('cart_empty'); ?></td></tr>';
}
}
// Checkout Logic
function showCheckout() {
if (cart.length === 0) {
alert('<?php echo __('cart_is_empty'); ?>');
return;
}
const modal = new bootstrap.Modal(document.getElementById('checkoutModal'));
modal.show();
}
function processSale() {
const patientId = $('#patientSelect').val();
const paymentMethod = document.querySelector('input[name="payment_method"]:checked').value;
const payload = {
patient_id: patientId || null,
visit_id: null,
payment_method: paymentMethod,
total_amount: parseFloat(cartTotalEl.textContent),
items: cart.map(i => ({
drug_id: i.id,
quantity: i.quantity,
price: i.price
}))
};
fetch('api/pharmacy.php?action=create_sale', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload)
})
.then(r => r.json())
.then(data => {
if (data.success) {
alert('Sale completed!');
cart = [];
renderCart();
bootstrap.Modal.getInstance(document.getElementById('checkoutModal')).hide();
// Open receipt in new tab
window.open('print_pharmacy_receipt.php?sale_id=' + data.sale_id, '_blank');
} else {
alert(data.error || 'Transaction failed');
}
})
.catch(e => {
console.error(e);
alert('Network error');
});
}
// Initialize Patient Select2
$(document).ready(function() {
$('.select2-patient').select2({
theme: 'bootstrap-5',
dropdownParent: $('#checkoutModal'),
ajax: {
url: 'api/patients.php?action=search', // Need to check if this endpoint exists or similar
dataType: 'json',
delay: 250,
processResults: function (data) {
return {
results: data.map(p => ({id: p.id, text: p.name + ' (' + p.phone + ')'}))
};
},
cache: true
},
placeholder: '<?php echo __('search_patient'); ?>',
minimumInputLength: 1
});
});
</script>