perf(pharmacy): optimize product master load with company ajax search and cached abdm drug lookup

This commit is contained in:
DevSoftTech Server 2026-10-27 01:23:34 +05:30
parent 40ce7b7a0f
commit cf25a00ef5
3 changed files with 159 additions and 25 deletions

View File

@ -142,13 +142,17 @@ $routes->get('product_master/drug_master_list', 'Medical::drug_master_list');
$routes->post('product_master/Product_search', 'Medical::product_search');
$routes->get('product_master/drug_terminology_autocomplete', 'Medical::drug_terminology_autocomplete');
$routes->get('product_master/drug_terminology_detail', 'Medical::drug_terminology_detail');
$routes->get('product_master/company_search_ajax', 'Medical::companySearchAjax');
$routes->get('product_master/Product_edit/(:num)', 'Medical::product_edit/$1');
$routes->post('product_master/product_master_update/(:num)', 'Medical::product_master_update/$1');
$routes->get('Product_master/drug_master_list', 'Medical::drug_master_list');
$routes->post('Product_master/Product_search', 'Medical::product_search');
$routes->get('Product_master/drug_terminology_autocomplete', 'Medical::drug_terminology_autocomplete');
$routes->get('Product_master/drug_terminology_detail', 'Medical::drug_terminology_detail');
$routes->get('Product_master/company_search_ajax', 'Medical::companySearchAjax');
$routes->get('Product_master/Product_edit/(:num)', 'Medical::product_edit/$1');
$routes->get('Medical/company_search_ajax', 'Medical::companySearchAjax');
$routes->get('medical/company_search_ajax', 'Medical::companySearchAjax');
// Pharmacy Procurement: Purchase Orders (PO) & GRN
$routes->get('Medical/purchase_orders', 'MedicalProcurement::po_list');
$routes->get('Medical/po_new', 'MedicalProcurement::po_new');

View File

@ -6252,6 +6252,12 @@ class Medical extends BaseController
$limit = 10;
}
$cacheKey = 'abdm_drug_ac_' . md5($type . '_' . strtolower($q) . '_' . $limit);
$cached = cache($cacheKey);
if (is_array($cached) && ! empty($cached) && ($cached['ok'] ?? 0) === 1) {
return $this->response->setJSON($cached);
}
try {
$connector = new \App\Libraries\Abdm\EAtriaBridgeConnector();
$result = $connector->drugsAutocomplete($q, $type, $limit);
@ -6316,11 +6322,17 @@ class Medical extends BaseController
];
}
return $this->response->setJSON([
$responsePayload = [
'ok' => (int) ($result['ok'] ?? 0),
'suggestions' => $suggestions,
'error_text' => (string) ($result['error_text'] ?? $result['message'] ?? ''),
]);
];
if (($responsePayload['ok'] ?? 0) === 1 && ! empty($suggestions)) {
cache()->save($cacheKey, $responsePayload, 86400);
}
return $this->response->setJSON($responsePayload);
}
public function drug_terminology_detail()
@ -6339,6 +6351,12 @@ class Medical extends BaseController
]);
}
$cacheKey = 'abdm_drug_dt_' . md5($type . '_' . $identifier);
$cached = cache($cacheKey);
if (is_array($cached) && ! empty($cached) && ($cached['ok'] ?? 0) === 1) {
return $this->response->setJSON($cached);
}
try {
$connector = new \App\Libraries\Abdm\EAtriaBridgeConnector();
$result = $connector->drugsDetail($type, $identifier);
@ -6409,7 +6427,7 @@ class Medical extends BaseController
$packSizeText = $this->extractDrugText($record, ['pack_size_text', 'pack_size', 'package', 'packing']);
$compositionText = $this->extractDrugText($record, ['composition_text', 'composition', 'generic_name', 'generic']);
return $this->response->setJSON([
$detailPayload = [
'ok' => (int) ($result['ok'] ?? 0),
'selected' => [
'label' => $label,
@ -6432,7 +6450,13 @@ class Medical extends BaseController
'synced_at' => date('Y-m-d H:i:s'),
],
'error_text' => (string) ($result['error_text'] ?? $result['message'] ?? ''),
]);
];
if (($detailPayload['ok'] ?? 0) === 1 && ! empty($detailPayload['selected'])) {
cache()->save($cacheKey, $detailPayload, 86400);
}
return $this->response->setJSON($detailPayload);
}
/**
@ -7207,21 +7231,82 @@ class Medical extends BaseController
}
}
public function companySearchAjax()
{
if ($deny = $this->ensurePharmacyAccess()) {
return $deny;
}
$term = trim((string) ($this->request->getGet('q') ?? $this->request->getPost('q') ?? ''));
$id = (int) ($this->request->getGet('id') ?? $this->request->getPost('id') ?? 0);
if (! $this->db->tableExists('med_company')) {
return $this->response->setJSON(['results' => []]);
}
$builder = $this->db->table('med_company');
$companyFields = $this->db->getFieldNames('med_company') ?? [];
$codeCol = in_array('company_code', $companyFields, true) ? 'company_code' : (in_array('code', $companyFields, true) ? 'code' : null);
if ($id > 0) {
$row = $builder->where('id', $id)->get()->getRowArray();
if (! $row) {
return $this->response->setJSON(['results' => []]);
}
return $this->response->setJSON([
'results' => [
[
'id' => (int) $row['id'],
'text' => (string) ($row['company_name'] ?? ''),
'code' => $codeCol ? (string) ($row[$codeCol] ?? '') : ''
]
]
]);
}
if ($term !== '') {
$builder->groupStart();
$builder->like('company_name', $term, 'after');
$builder->orLike('company_name', $term);
if ($codeCol) {
$builder->orLike($codeCol, $term);
}
$builder->groupEnd();
}
$rows = $builder->select('id, company_name' . ($codeCol ? ", {$codeCol} AS company_code" : ''))
->orderBy('company_name', 'ASC')
->limit(30)
->get()
->getResultArray();
$results = [];
foreach ($rows as $r) {
$results[] = [
'id' => (int) $r['id'],
'text' => (string) ($r['company_name'] ?? ''),
'code' => (string) ($r['company_code'] ?? '')
];
}
return $this->response->setJSON(['results' => $results]);
}
public function product_edit($productId = 0)
{
if ($deny = $this->ensurePharmacyAccess()) {
return $deny;
}
$this->runDrugMasterStandardizationOnce();
$productId = (int) $productId;
$productData = [];
$selectedCompanyId = 0;
if ($this->db->tableExists('med_product_master') && $productId > 0) {
$row = $this->db->table('med_product_master')->where('id', $productId)->get()->getRow();
if ($row) {
$productData = [$row];
$selectedCompanyId = (int) ($row->company_id ?? 0);
}
}
@ -7233,8 +7318,11 @@ class Medical extends BaseController
}
$companies = [];
if ($this->db->tableExists('med_company')) {
$companies = $this->db->table('med_company')->orderBy('company_name', 'ASC')->get()->getResult();
if ($selectedCompanyId > 0 && $this->db->tableExists('med_company')) {
$compRow = $this->db->table('med_company')->where('id', $selectedCompanyId)->get()->getRow();
if ($compRow) {
$companies = [$compRow];
}
}
$categories = [];

View File

@ -94,7 +94,7 @@ $flag = static function ($v): string {
<div class="col-md-3">
<label class="form-label">Company</label>
<select name="input_company_name" id="input_company_name" class="form-select">
<option value="0">Select</option>
<option value="0">Select company</option>
<?php foreach (($med_company ?? []) as $row): ?>
<?php $companyCode = (string) ($row->company_code ?? ($row->manufacturer_code ?? ($row->code ?? ''))); ?>
<option value="<?= (int) ($row->id ?? 0) ?>" data-company-code="<?= esc($companyCode) ?>" <?= ((int) ($row->id ?? 0) === $companyId) ? 'selected' : '' ?>><?= esc($row->company_name ?? '') ?></option>
@ -263,8 +263,25 @@ window.closeDrugMasterSubView = window.closeDrugMasterSubView || function () {
$('#input_company_name').select2({
theme: 'bootstrap4',
width: '100%',
placeholder: 'Select company',
allowClear: true
placeholder: 'Search & select company...',
allowClear: true,
ajax: {
url: '<?= base_url('product_master/company_search_ajax') ?>',
dataType: 'json',
delay: 200,
data: function (params) {
return {
q: params.term || ''
};
},
processResults: function (data) {
return {
results: data.results || []
};
},
cache: true
},
minimumInputLength: 0
});
}
@ -742,7 +759,7 @@ window.closeDrugMasterSubView = window.closeDrugMasterSubView || function () {
}
function setCompanyByName(companyName) {
var needle = normalizeCompanyText(companyName).toLowerCase();
var needle = normalizeCompanyText(companyName);
if (!needle) {
return;
}
@ -751,7 +768,7 @@ window.closeDrugMasterSubView = window.closeDrugMasterSubView || function () {
$c.find('option').each(function () {
var text = normalizeCompanyText($(this).text()).toLowerCase();
if (text === needle) {
if (text === needle.toLowerCase()) {
matchedVal = $(this).val();
return false;
}
@ -761,19 +778,16 @@ window.closeDrugMasterSubView = window.closeDrugMasterSubView || function () {
return;
}
$c.find('option').each(function () {
var text = normalizeCompanyText($(this).text()).toLowerCase();
if (!text || text === 'select') {
return;
}
if (text === needle || text.indexOf(needle) >= 0 || needle.indexOf(text) >= 0) {
matchedVal = $(this).val();
return false;
$.getJSON('<?= base_url('product_master/company_search_ajax') ?>', { q: needle }, function (res) {
if (res && res.results && res.results.length > 0) {
var first = res.results[0];
var option = new Option(first.text, first.id, true, true);
if (first.code) {
$(option).attr('data-company-code', first.code);
}
$c.append(option).trigger('change');
}
});
if (matchedVal !== '') {
$c.val(matchedVal).trigger('change');
}
}
function setCompanyByCode(companyCode) {
@ -792,7 +806,19 @@ window.closeDrugMasterSubView = window.closeDrugMasterSubView || function () {
});
if (matchedVal !== '') {
$c.val(matchedVal).trigger('change');
return;
}
$.getJSON('<?= base_url('product_master/company_search_ajax') ?>', { q: needle }, function (res) {
if (res && res.results && res.results.length > 0) {
var first = res.results[0];
var option = new Option(first.text, first.id, true, true);
if (first.code) {
$(option).attr('data-company-code', first.code);
}
$c.append(option).trigger('change');
}
});
}
function setFormulationByCode(formulationCode) {
@ -1003,11 +1029,18 @@ window.closeDrugMasterSubView = window.closeDrugMasterSubView || function () {
showSelectedDrugSummary();
}
var clientDetailCache = {};
function fetchDrugDetail(type, identifier) {
if (!identifier) {
return;
}
var cKey = (type || 'brand') + ':' + identifier;
if (clientDetailCache[cKey]) {
applyDrugDetail(clientDetailCache[cKey]);
return;
}
$.getJSON('<?= base_url('product_master/drug_terminology_detail') ?>', {
type: type || 'brand',
identifier: identifier
@ -1015,6 +1048,7 @@ window.closeDrugMasterSubView = window.closeDrugMasterSubView || function () {
if (!resp || Number(resp.ok || 0) !== 1 || !resp.selected) {
return;
}
clientDetailCache[cKey] = resp.selected;
applyDrugDetail(resp.selected);
});
}
@ -1143,6 +1177,7 @@ window.closeDrugMasterSubView = window.closeDrugMasterSubView || function () {
}
}
var clientDrugCache = {};
function fetchDrugSuggestions() {
var q = ($('#input_item_name').val() || '').trim();
var type = ($('#abdm_drug_search_type').val() || 'brand').trim();
@ -1157,6 +1192,12 @@ window.closeDrugMasterSubView = window.closeDrugMasterSubView || function () {
}
lastQuery = q;
var cacheKey = type + ':' + q.toLowerCase();
if (clientDrugCache[cacheKey]) {
renderSuggestions(clientDrugCache[cacheKey]);
return;
}
if (suggestXhr && typeof suggestXhr.abort === 'function') {
suggestXhr.abort();
suggestXhr = null;
@ -1172,6 +1213,7 @@ window.closeDrugMasterSubView = window.closeDrugMasterSubView || function () {
clearSuggestionBox();
return;
}
clientDrugCache[cacheKey] = resp.suggestions || [];
renderSuggestions(resp.suggestions || []);
}).fail(function (_xhr, status) {
suggestXhr = null;
@ -1186,7 +1228,7 @@ window.closeDrugMasterSubView = window.closeDrugMasterSubView || function () {
if (suggestTimer) {
clearTimeout(suggestTimer);
}
suggestTimer = setTimeout(fetchDrugSuggestions, 1000);
suggestTimer = setTimeout(fetchDrugSuggestions, 250);
});
$('#input_item_name').off('keydown.abdmDrug').on('keydown.abdmDrug', function (evt) {