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->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_autocomplete', 'Medical::drug_terminology_autocomplete');
$routes->get('product_master/drug_terminology_detail', 'Medical::drug_terminology_detail'); $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('product_master/Product_edit/(:num)', 'Medical::product_edit/$1');
$routes->post('product_master/product_master_update/(:num)', 'Medical::product_master_update/$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->get('Product_master/drug_master_list', 'Medical::drug_master_list');
$routes->post('Product_master/Product_search', 'Medical::product_search'); $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_autocomplete', 'Medical::drug_terminology_autocomplete');
$routes->get('Product_master/drug_terminology_detail', 'Medical::drug_terminology_detail'); $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('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 // Pharmacy Procurement: Purchase Orders (PO) & GRN
$routes->get('Medical/purchase_orders', 'MedicalProcurement::po_list'); $routes->get('Medical/purchase_orders', 'MedicalProcurement::po_list');
$routes->get('Medical/po_new', 'MedicalProcurement::po_new'); $routes->get('Medical/po_new', 'MedicalProcurement::po_new');

View File

@ -6252,6 +6252,12 @@ class Medical extends BaseController
$limit = 10; $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 { try {
$connector = new \App\Libraries\Abdm\EAtriaBridgeConnector(); $connector = new \App\Libraries\Abdm\EAtriaBridgeConnector();
$result = $connector->drugsAutocomplete($q, $type, $limit); $result = $connector->drugsAutocomplete($q, $type, $limit);
@ -6316,11 +6322,17 @@ class Medical extends BaseController
]; ];
} }
return $this->response->setJSON([ $responsePayload = [
'ok' => (int) ($result['ok'] ?? 0), 'ok' => (int) ($result['ok'] ?? 0),
'suggestions' => $suggestions, 'suggestions' => $suggestions,
'error_text' => (string) ($result['error_text'] ?? $result['message'] ?? ''), '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() 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 { try {
$connector = new \App\Libraries\Abdm\EAtriaBridgeConnector(); $connector = new \App\Libraries\Abdm\EAtriaBridgeConnector();
$result = $connector->drugsDetail($type, $identifier); $result = $connector->drugsDetail($type, $identifier);
@ -6409,7 +6427,7 @@ class Medical extends BaseController
$packSizeText = $this->extractDrugText($record, ['pack_size_text', 'pack_size', 'package', 'packing']); $packSizeText = $this->extractDrugText($record, ['pack_size_text', 'pack_size', 'package', 'packing']);
$compositionText = $this->extractDrugText($record, ['composition_text', 'composition', 'generic_name', 'generic']); $compositionText = $this->extractDrugText($record, ['composition_text', 'composition', 'generic_name', 'generic']);
return $this->response->setJSON([ $detailPayload = [
'ok' => (int) ($result['ok'] ?? 0), 'ok' => (int) ($result['ok'] ?? 0),
'selected' => [ 'selected' => [
'label' => $label, 'label' => $label,
@ -6432,7 +6450,13 @@ class Medical extends BaseController
'synced_at' => date('Y-m-d H:i:s'), 'synced_at' => date('Y-m-d H:i:s'),
], ],
'error_text' => (string) ($result['error_text'] ?? $result['message'] ?? ''), '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) public function product_edit($productId = 0)
{ {
if ($deny = $this->ensurePharmacyAccess()) { if ($deny = $this->ensurePharmacyAccess()) {
return $deny; return $deny;
} }
$this->runDrugMasterStandardizationOnce();
$productId = (int) $productId; $productId = (int) $productId;
$productData = []; $productData = [];
$selectedCompanyId = 0;
if ($this->db->tableExists('med_product_master') && $productId > 0) { if ($this->db->tableExists('med_product_master') && $productId > 0) {
$row = $this->db->table('med_product_master')->where('id', $productId)->get()->getRow(); $row = $this->db->table('med_product_master')->where('id', $productId)->get()->getRow();
if ($row) { if ($row) {
$productData = [$row]; $productData = [$row];
$selectedCompanyId = (int) ($row->company_id ?? 0);
} }
} }
@ -7233,8 +7318,11 @@ class Medical extends BaseController
} }
$companies = []; $companies = [];
if ($this->db->tableExists('med_company')) { if ($selectedCompanyId > 0 && $this->db->tableExists('med_company')) {
$companies = $this->db->table('med_company')->orderBy('company_name', 'ASC')->get()->getResult(); $compRow = $this->db->table('med_company')->where('id', $selectedCompanyId)->get()->getRow();
if ($compRow) {
$companies = [$compRow];
}
} }
$categories = []; $categories = [];

View File

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