perf(pharmacy): optimize product master load with company ajax search and cached abdm drug lookup
This commit is contained in:
parent
40ce7b7a0f
commit
cf25a00ef5
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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 = [];
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user