Pharmacy: add ABDM drug terminology autocomplete + canonical mapping
This commit is contained in:
parent
12cacc3a9c
commit
0558ac5a2e
|
|
@ -85,10 +85,14 @@ $routes->get('Product_master/medicine_category_Sub', 'Medical::medicine_category
|
||||||
$routes->post('Product_master/medicine_category_Update', 'Medical::medicine_category_update');
|
$routes->post('Product_master/medicine_category_Update', 'Medical::medicine_category_update');
|
||||||
$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_detail', 'Medical::drug_terminology_detail');
|
||||||
$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_detail', 'Medical::drug_terminology_detail');
|
||||||
$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('Medical/Purchase', 'Medical::purchase');
|
$routes->get('Medical/Purchase', 'Medical::purchase');
|
||||||
|
|
|
||||||
|
|
@ -5071,6 +5071,226 @@ class Medical extends BaseController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function drug_terminology_autocomplete()
|
||||||
|
{
|
||||||
|
if ($deny = $this->ensurePharmacyAccess()) {
|
||||||
|
return $deny;
|
||||||
|
}
|
||||||
|
|
||||||
|
$q = trim((string) ($this->request->getGet('q') ?? $this->request->getPost('q') ?? ''));
|
||||||
|
$q = preg_replace('/[^A-Za-z0-9_ \-]/', '', $q);
|
||||||
|
if (mb_strlen($q) < 2) {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'ok' => 1,
|
||||||
|
'suggestions' => [],
|
||||||
|
'error_text' => '',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = strtolower(trim((string) ($this->request->getGet('type') ?? $this->request->getPost('type') ?? 'generic')));
|
||||||
|
$allowedTypes = ['generic', 'brand', 'product', 'substance'];
|
||||||
|
if (! in_array($type, $allowedTypes, true)) {
|
||||||
|
$type = 'generic';
|
||||||
|
}
|
||||||
|
|
||||||
|
$limit = (int) ($this->request->getGet('limit') ?? $this->request->getPost('limit') ?? 10);
|
||||||
|
if ($limit <= 0 || $limit > 50) {
|
||||||
|
$limit = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$connector = new \App\Libraries\Abdm\EAtriaBridgeConnector();
|
||||||
|
$result = $connector->drugsAutocomplete($q, $type, $limit);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'ok' => 0,
|
||||||
|
'suggestions' => [],
|
||||||
|
'error_text' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $this->extractDrugResultList($result);
|
||||||
|
$suggestions = [];
|
||||||
|
foreach ($items as $row) {
|
||||||
|
if (! is_array($row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$label = $this->extractDrugText($row, ['label', 'name', 'display', 'term', 'title', 'drug_name']);
|
||||||
|
$identifier = $this->extractDrugText($row, ['identifier', 'id', 'code', 'concept_id', 'value']);
|
||||||
|
$itemType = strtolower($this->extractDrugText($row, ['type', 'entity_type', 'category']));
|
||||||
|
if ($itemType === '') {
|
||||||
|
$itemType = $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($label === '' && $identifier === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$suggestions[] = [
|
||||||
|
'label' => $label !== '' ? $label : $identifier,
|
||||||
|
'identifier' => $identifier,
|
||||||
|
'type' => $itemType,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'ok' => (int) ($result['ok'] ?? 0),
|
||||||
|
'suggestions' => $suggestions,
|
||||||
|
'error_text' => (string) ($result['error_text'] ?? $result['message'] ?? ''),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function drug_terminology_detail()
|
||||||
|
{
|
||||||
|
if ($deny = $this->ensurePharmacyAccess()) {
|
||||||
|
return $deny;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = strtolower(trim((string) ($this->request->getGet('type') ?? $this->request->getPost('type') ?? 'generic')));
|
||||||
|
$identifier = trim((string) ($this->request->getGet('identifier') ?? $this->request->getPost('identifier') ?? ''));
|
||||||
|
if ($identifier === '') {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'ok' => 0,
|
||||||
|
'selected' => null,
|
||||||
|
'error_text' => 'Identifier is required',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$connector = new \App\Libraries\Abdm\EAtriaBridgeConnector();
|
||||||
|
$result = $connector->drugsDetail($type, $identifier);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'ok' => 0,
|
||||||
|
'selected' => null,
|
||||||
|
'error_text' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$record = $this->extractDrugResultObject($result);
|
||||||
|
$label = $this->extractDrugText($record, ['label', 'name', 'display', 'term', 'title', 'drug_name']);
|
||||||
|
$generic = $this->extractDrugText($record, ['generic_name', 'genericname', 'generic', 'salt', 'molecule']);
|
||||||
|
$hsnCode = $this->extractDrugText($record, ['hsn_code', 'hsn', 'hscode']);
|
||||||
|
$formulation = $this->extractDrugText($record, ['formulation', 'dosage_form', 'drug_form']);
|
||||||
|
$packing = $this->extractDrugText($record, ['packing', 'pack_size', 'package']);
|
||||||
|
$itemType = strtolower($this->extractDrugText($record, ['type', 'entity_type', 'category']));
|
||||||
|
if ($itemType === '') {
|
||||||
|
$itemType = $type;
|
||||||
|
}
|
||||||
|
$canonicalId = $this->extractDrugText($record, ['identifier', 'id', 'code', 'concept_id', 'value']);
|
||||||
|
if ($canonicalId === '') {
|
||||||
|
$canonicalId = $identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
$payloadJson = json_encode($record, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
if (! is_string($payloadJson)) {
|
||||||
|
$payloadJson = '{}';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'ok' => (int) ($result['ok'] ?? 0),
|
||||||
|
'selected' => [
|
||||||
|
'label' => $label,
|
||||||
|
'generic_name' => $generic,
|
||||||
|
'hsn_code' => $hsnCode,
|
||||||
|
'formulation' => $formulation,
|
||||||
|
'packing' => $packing,
|
||||||
|
'type' => $itemType,
|
||||||
|
'identifier' => $canonicalId,
|
||||||
|
'payload_json' => $payloadJson,
|
||||||
|
'synced_at' => date('Y-m-d H:i:s'),
|
||||||
|
],
|
||||||
|
'error_text' => (string) ($result['error_text'] ?? $result['message'] ?? ''),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $result
|
||||||
|
* @return array<int, array<string, mixed>>
|
||||||
|
*/
|
||||||
|
private function extractDrugResultList(array $result): array
|
||||||
|
{
|
||||||
|
$candidate = $result['data'] ?? $result['result'] ?? $result;
|
||||||
|
if (! is_array($candidate)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_is_list($candidate)) {
|
||||||
|
return array_values(array_filter($candidate, static fn ($v) => is_array($v)));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (['items', 'suggestions', 'results', 'data', 'records'] as $key) {
|
||||||
|
if (isset($candidate[$key]) && is_array($candidate[$key]) && array_is_list($candidate[$key])) {
|
||||||
|
return array_values(array_filter($candidate[$key], static fn ($v) => is_array($v)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->extractDrugText($candidate, ['identifier', 'id', 'code', 'concept_id', 'value']) !== '') {
|
||||||
|
return [$candidate];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $result
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
private function extractDrugResultObject(array $result): array
|
||||||
|
{
|
||||||
|
$candidate = $result['data'] ?? $result['result'] ?? $result;
|
||||||
|
if (! is_array($candidate)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_is_list($candidate)) {
|
||||||
|
$first = $candidate[0] ?? [];
|
||||||
|
return is_array($first) ? $first : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (['item', 'result', 'record', 'data'] as $key) {
|
||||||
|
if (isset($candidate[$key]) && is_array($candidate[$key])) {
|
||||||
|
return $candidate[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $data
|
||||||
|
* @param array<int, string> $keys
|
||||||
|
*/
|
||||||
|
private function extractDrugText(array $data, array $keys): string
|
||||||
|
{
|
||||||
|
$flat = [];
|
||||||
|
$walker = static function ($node) use (&$walker, &$flat): void {
|
||||||
|
if (! is_array($node)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($node as $k => $v) {
|
||||||
|
if (is_array($v)) {
|
||||||
|
$walker($v);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (is_scalar($v) || $v === null) {
|
||||||
|
$flat[strtolower((string) $k)] = trim((string) $v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$walker($data);
|
||||||
|
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$val = trim((string) ($flat[strtolower($key)] ?? ''));
|
||||||
|
if ($val !== '') {
|
||||||
|
return $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
public function product_edit($productId = 0)
|
public function product_edit($productId = 0)
|
||||||
{
|
{
|
||||||
if ($deny = $this->ensurePharmacyAccess()) {
|
if ($deny = $this->ensurePharmacyAccess()) {
|
||||||
|
|
@ -5149,6 +5369,15 @@ class Medical extends BaseController
|
||||||
$coldStorage = trim((string) ($this->request->getPost('input_cold_storage') ?? ''));
|
$coldStorage = trim((string) ($this->request->getPost('input_cold_storage') ?? ''));
|
||||||
$companyId = (int) ($this->request->getPost('input_company_name') ?? 0);
|
$companyId = (int) ($this->request->getPost('input_company_name') ?? 0);
|
||||||
$relatedDrugId = (int) ($this->request->getPost('related_drug_id') ?? 0);
|
$relatedDrugId = (int) ($this->request->getPost('related_drug_id') ?? 0);
|
||||||
|
$abdmDrugType = trim((string) ($this->request->getPost('abdm_drug_type') ?? ''));
|
||||||
|
$abdmDrugIdentifier = trim((string) ($this->request->getPost('abdm_drug_identifier') ?? ''));
|
||||||
|
$abdmDrugDisplay = trim((string) ($this->request->getPost('abdm_drug_display') ?? ''));
|
||||||
|
$abdmDrugGeneric = trim((string) ($this->request->getPost('abdm_drug_generic') ?? ''));
|
||||||
|
$abdmDrugPayloadJson = trim((string) ($this->request->getPost('abdm_drug_payload_json') ?? ''));
|
||||||
|
$abdmDrugLastSyncedAt = trim((string) ($this->request->getPost('abdm_drug_last_synced_at') ?? ''));
|
||||||
|
if (strlen($abdmDrugPayloadJson) > 65000) {
|
||||||
|
$abdmDrugPayloadJson = substr($abdmDrugPayloadJson, 0, 65000);
|
||||||
|
}
|
||||||
|
|
||||||
$errors = [];
|
$errors = [];
|
||||||
if ($itemName === '' || mb_strlen($itemName) < 3) {
|
if ($itemName === '' || mb_strlen($itemName) < 3) {
|
||||||
|
|
@ -5193,6 +5422,12 @@ class Medical extends BaseController
|
||||||
'shelf_no' => $shelfNo,
|
'shelf_no' => $shelfNo,
|
||||||
'cold_storage' => $coldStorage,
|
'cold_storage' => $coldStorage,
|
||||||
'company_id' => $companyId,
|
'company_id' => $companyId,
|
||||||
|
'abdm_drug_type' => $abdmDrugType,
|
||||||
|
'abdm_drug_identifier' => $abdmDrugIdentifier,
|
||||||
|
'abdm_drug_display' => $abdmDrugDisplay,
|
||||||
|
'abdm_drug_generic' => $abdmDrugGeneric,
|
||||||
|
'abdm_drug_payload_json' => $abdmDrugPayloadJson,
|
||||||
|
'abdm_drug_last_synced_at' => $abdmDrugLastSyncedAt,
|
||||||
'ban_flag_id' => $flag('chk_ban_flag_id'),
|
'ban_flag_id' => $flag('chk_ban_flag_id'),
|
||||||
'batch_applicable' => $flag('chk_batch_applicable'),
|
'batch_applicable' => $flag('chk_batch_applicable'),
|
||||||
'is_continue' => $flag('chk_is_continue'),
|
'is_continue' => $flag('chk_is_continue'),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Database\Migrations;
|
||||||
|
|
||||||
|
use CodeIgniter\Database\Migration;
|
||||||
|
|
||||||
|
class AddAbdmDrugTerminologyFieldsToMedProductMaster extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('med_product_master')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $this->db->getFieldNames('med_product_master') ?? [];
|
||||||
|
$add = [];
|
||||||
|
|
||||||
|
if (! in_array('abdm_drug_type', $fields, true)) {
|
||||||
|
$add['abdm_drug_type'] = ['type' => 'VARCHAR', 'constraint' => 30, 'null' => true, 'default' => null];
|
||||||
|
}
|
||||||
|
if (! in_array('abdm_drug_identifier', $fields, true)) {
|
||||||
|
$add['abdm_drug_identifier'] = ['type' => 'VARCHAR', 'constraint' => 120, 'null' => true, 'default' => null];
|
||||||
|
}
|
||||||
|
if (! in_array('abdm_drug_display', $fields, true)) {
|
||||||
|
$add['abdm_drug_display'] = ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true, 'default' => null];
|
||||||
|
}
|
||||||
|
if (! in_array('abdm_drug_generic', $fields, true)) {
|
||||||
|
$add['abdm_drug_generic'] = ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true, 'default' => null];
|
||||||
|
}
|
||||||
|
if (! in_array('abdm_drug_payload_json', $fields, true)) {
|
||||||
|
$add['abdm_drug_payload_json'] = ['type' => 'MEDIUMTEXT', 'null' => true];
|
||||||
|
}
|
||||||
|
if (! in_array('abdm_drug_last_synced_at', $fields, true)) {
|
||||||
|
$add['abdm_drug_last_synced_at'] = ['type' => 'DATETIME', 'null' => true, 'default' => null];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($add !== []) {
|
||||||
|
$this->forge->addColumn('med_product_master', $add);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional index for fast traceability lookup.
|
||||||
|
$indexes = $this->db->query('SHOW INDEX FROM med_product_master')->getResultArray();
|
||||||
|
$hasIdx = false;
|
||||||
|
foreach ($indexes as $idx) {
|
||||||
|
if (strcasecmp((string) ($idx['Key_name'] ?? ''), 'idx_abdm_drug_identifier') === 0) {
|
||||||
|
$hasIdx = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $hasIdx && in_array('abdm_drug_identifier', $this->db->getFieldNames('med_product_master') ?? [], true)) {
|
||||||
|
try {
|
||||||
|
$this->db->query('ALTER TABLE med_product_master ADD INDEX idx_abdm_drug_identifier (abdm_drug_identifier)');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// Ignore index creation failures to keep migration non-blocking.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('med_product_master')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->db->query('ALTER TABLE med_product_master DROP INDEX idx_abdm_drug_identifier');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $this->db->getFieldNames('med_product_master') ?? [];
|
||||||
|
foreach ([
|
||||||
|
'abdm_drug_type',
|
||||||
|
'abdm_drug_identifier',
|
||||||
|
'abdm_drug_display',
|
||||||
|
'abdm_drug_generic',
|
||||||
|
'abdm_drug_payload_json',
|
||||||
|
'abdm_drug_last_synced_at',
|
||||||
|
] as $column) {
|
||||||
|
if (in_array($column, $fields, true)) {
|
||||||
|
$this->forge->dropColumn('med_product_master', $column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -539,4 +539,47 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
|
||||||
{
|
{
|
||||||
return $this->post('/v3/hip/link/sms-notify', $payload);
|
return $this->post('/v3/hip/link/sms-notify', $payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Drug Terminology (CDCI)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public function drugsAutocomplete(string $q, string $type = 'generic', int $limit = 10): array
|
||||||
|
{
|
||||||
|
$query = [
|
||||||
|
'q' => $q,
|
||||||
|
'type' => $type,
|
||||||
|
'limit' => max(1, min(50, $limit)),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($this->hfrId !== '') {
|
||||||
|
$query['hfr_id'] = $this->hfrId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->get('/v3/drugs/autocomplete', $query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function drugsDetail(string $type, string $identifier): array
|
||||||
|
{
|
||||||
|
$query = [
|
||||||
|
'type' => $type,
|
||||||
|
'identifier' => $identifier,
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($this->hfrId !== '') {
|
||||||
|
$query['hfr_id'] = $this->hfrId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->get('/v3/drugs/detail', $query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function drugsVersion(): array
|
||||||
|
{
|
||||||
|
$query = [];
|
||||||
|
if ($this->hfrId !== '') {
|
||||||
|
$query['hfr_id'] = $this->hfrId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->get('/v3/drugs/version', $query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,12 @@ $shelfNo = (string) ($product->shelf_no ?? '');
|
||||||
$coldStorage = (string) ($product->cold_storage ?? '');
|
$coldStorage = (string) ($product->cold_storage ?? '');
|
||||||
$companyId = (int) ($product->company_id ?? 0);
|
$companyId = (int) ($product->company_id ?? 0);
|
||||||
$relatedDrugId = (int) ($product->related_drug_id ?? 0);
|
$relatedDrugId = (int) ($product->related_drug_id ?? 0);
|
||||||
|
$abdmDrugType = (string) ($product->abdm_drug_type ?? '');
|
||||||
|
$abdmDrugIdentifier = (string) ($product->abdm_drug_identifier ?? '');
|
||||||
|
$abdmDrugDisplay = (string) ($product->abdm_drug_display ?? '');
|
||||||
|
$abdmDrugGeneric = (string) ($product->abdm_drug_generic ?? '');
|
||||||
|
$abdmDrugPayloadJson = (string) ($product->abdm_drug_payload_json ?? '');
|
||||||
|
$abdmDrugLastSyncedAt = (string) ($product->abdm_drug_last_synced_at ?? '');
|
||||||
|
|
||||||
$flag = static function ($v): string {
|
$flag = static function ($v): string {
|
||||||
return ((int) $v === 1) ? 'checked' : '';
|
return ((int) $v === 1) ? 'checked' : '';
|
||||||
|
|
@ -25,16 +31,47 @@ $flag = static function ($v): string {
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">Product <?= $productId > 0 ? '<small class="text-muted">#' . $productId . '</small>' : '' ?></div>
|
<div class="card-header">Product <?= $productId > 0 ? '<small class="text-muted">#' . $productId . '</small>' : '' ?></div>
|
||||||
<div class="card-body pt-3">
|
<div class="card-body pt-3">
|
||||||
|
<style>
|
||||||
|
#abdm-drug-suggest {
|
||||||
|
max-height: 260px;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 1055;
|
||||||
|
display: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#abdm-drug-selected {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<div id="product-msg"></div>
|
<div id="product-msg"></div>
|
||||||
|
|
||||||
<form id="product-form" class="row g-2" method="post" action="javascript:void(0)">
|
<form id="product-form" class="row g-2" method="post" action="javascript:void(0)">
|
||||||
<?= csrf_field() ?>
|
<?= csrf_field() ?>
|
||||||
<input type="hidden" id="product_id" name="product_id" value="<?= esc((string) $productId) ?>">
|
<input type="hidden" id="product_id" name="product_id" value="<?= esc((string) $productId) ?>">
|
||||||
<input type="hidden" id="related_drug_id" name="related_drug_id" value="<?= esc((string) $relatedDrugId) ?>">
|
<input type="hidden" id="related_drug_id" name="related_drug_id" value="<?= esc((string) $relatedDrugId) ?>">
|
||||||
|
<input type="hidden" id="abdm_drug_type" name="abdm_drug_type" value="<?= esc($abdmDrugType) ?>">
|
||||||
|
<input type="hidden" id="abdm_drug_identifier" name="abdm_drug_identifier" value="<?= esc($abdmDrugIdentifier) ?>">
|
||||||
|
<input type="hidden" id="abdm_drug_display" name="abdm_drug_display" value="<?= esc($abdmDrugDisplay) ?>">
|
||||||
|
<input type="hidden" id="abdm_drug_generic" name="abdm_drug_generic" value="<?= esc($abdmDrugGeneric) ?>">
|
||||||
|
<input type="hidden" id="abdm_drug_payload_json" name="abdm_drug_payload_json" value="<?= esc($abdmDrugPayloadJson) ?>">
|
||||||
|
<input type="hidden" id="abdm_drug_last_synced_at" name="abdm_drug_last_synced_at" value="<?= esc($abdmDrugLastSyncedAt) ?>">
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label">Product Name</label>
|
<label class="form-label">Product Name</label>
|
||||||
<input class="form-control" name="input_item_name" id="input_item_name" type="text" value="<?= esc($itemName) ?>">
|
<div class="position-relative">
|
||||||
|
<input class="form-control" name="input_item_name" id="input_item_name" type="text" value="<?= esc($itemName) ?>" autocomplete="off">
|
||||||
|
<div id="abdm-drug-suggest" class="list-group position-absolute shadow-sm"></div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center gap-2 mt-1">
|
||||||
|
<small class="text-muted">ABDM Drug Search</small>
|
||||||
|
<select id="abdm_drug_search_type" class="form-select form-select-sm" style="width:auto;">
|
||||||
|
<option value="generic" selected>Generic</option>
|
||||||
|
<option value="brand">Brand</option>
|
||||||
|
<option value="product">Product</option>
|
||||||
|
<option value="substance">Substance</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="abdm-drug-selected" class="text-success mt-1"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label class="form-label">Formulation</label>
|
<label class="form-label">Formulation</label>
|
||||||
|
|
@ -131,6 +168,9 @@ $flag = static function ($v): string {
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
|
var suggestTimer = null;
|
||||||
|
var lastQuery = '';
|
||||||
|
|
||||||
if (window.jQuery && $.fn.select2) {
|
if (window.jQuery && $.fn.select2) {
|
||||||
$('#med_cat_id').select2({
|
$('#med_cat_id').select2({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
|
@ -142,6 +182,177 @@ $flag = static function ($v): string {
|
||||||
$('#product-msg').html(html || '');
|
$('#product-msg').html(html || '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showSelectedDrugSummary() {
|
||||||
|
var id = $('#abdm_drug_identifier').val() || '';
|
||||||
|
var label = $('#abdm_drug_display').val() || '';
|
||||||
|
var type = $('#abdm_drug_type').val() || '';
|
||||||
|
var generic = $('#abdm_drug_generic').val() || '';
|
||||||
|
var syncedAt = $('#abdm_drug_last_synced_at').val() || '';
|
||||||
|
|
||||||
|
if (!id && !label) {
|
||||||
|
$('#abdm-drug-selected').html('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parts = [];
|
||||||
|
if (label) { parts.push(label); }
|
||||||
|
if (generic) { parts.push('Generic: ' + generic); }
|
||||||
|
if (type) { parts.push('Type: ' + type); }
|
||||||
|
if (id) { parts.push('Identifier: ' + id); }
|
||||||
|
if (syncedAt) { parts.push('Synced: ' + syncedAt); }
|
||||||
|
|
||||||
|
$('#abdm-drug-selected').text(parts.join(' | '));
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSuggestionBox() {
|
||||||
|
$('#abdm-drug-suggest').hide().empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyDrugDetail(detail) {
|
||||||
|
if (!detail || typeof detail !== 'object') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var label = detail.label || '';
|
||||||
|
var generic = detail.generic_name || '';
|
||||||
|
var hsnCode = detail.hsn_code || '';
|
||||||
|
var formulation = detail.formulation || '';
|
||||||
|
var packing = detail.packing || '';
|
||||||
|
|
||||||
|
if (label) {
|
||||||
|
$('#input_item_name').val(label);
|
||||||
|
$('#abdm_drug_display').val(label);
|
||||||
|
}
|
||||||
|
if (generic && !$('#input_genericname').val()) {
|
||||||
|
$('#input_genericname').val(generic);
|
||||||
|
}
|
||||||
|
if (hsnCode && !$('#input_HSNCODE').val()) {
|
||||||
|
$('#input_HSNCODE').val(hsnCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formulation) {
|
||||||
|
var $f = $('#input_formulation');
|
||||||
|
var exists = $f.find('option').filter(function () {
|
||||||
|
return String($(this).val()).toLowerCase() === String(formulation).toLowerCase();
|
||||||
|
}).length > 0;
|
||||||
|
if (exists) {
|
||||||
|
$f.val(formulation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packing && !$('#input_packing_type').val()) {
|
||||||
|
$('#input_packing_type').val(packing);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#abdm_drug_type').val(detail.type || '');
|
||||||
|
$('#abdm_drug_identifier').val(detail.identifier || '');
|
||||||
|
$('#abdm_drug_generic').val(generic);
|
||||||
|
$('#abdm_drug_payload_json').val(detail.payload_json || '{}');
|
||||||
|
$('#abdm_drug_last_synced_at').val(detail.synced_at || '');
|
||||||
|
showSelectedDrugSummary();
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchDrugDetail(type, identifier) {
|
||||||
|
if (!identifier) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.getJSON('<?= base_url('product_master/drug_terminology_detail') ?>', {
|
||||||
|
type: type || 'generic',
|
||||||
|
identifier: identifier
|
||||||
|
}, function (resp) {
|
||||||
|
if (!resp || Number(resp.ok || 0) !== 1 || !resp.selected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
applyDrugDetail(resp.selected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSuggestions(items) {
|
||||||
|
var $box = $('#abdm-drug-suggest');
|
||||||
|
$box.empty();
|
||||||
|
|
||||||
|
if (!Array.isArray(items) || items.length === 0) {
|
||||||
|
clearSuggestionBox();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
items.forEach(function (item) {
|
||||||
|
var label = item.label || '';
|
||||||
|
var type = item.type || '';
|
||||||
|
var identifier = item.identifier || '';
|
||||||
|
if (!label && !identifier) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $a = $('<a href="#" class="list-group-item list-group-item-action py-1 px-2"></a>');
|
||||||
|
$a.text(label + (type ? ' [' + type + ']' : '') + (identifier ? ' (' + identifier + ')' : ''));
|
||||||
|
$a.on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
clearSuggestionBox();
|
||||||
|
if (label) {
|
||||||
|
$('#input_item_name').val(label);
|
||||||
|
}
|
||||||
|
fetchDrugDetail(type, identifier);
|
||||||
|
});
|
||||||
|
$box.append($a);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($box.children().length > 0) {
|
||||||
|
$box.show();
|
||||||
|
} else {
|
||||||
|
clearSuggestionBox();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchDrugSuggestions() {
|
||||||
|
var q = ($('#input_item_name').val() || '').trim();
|
||||||
|
var type = ($('#abdm_drug_search_type').val() || 'generic').trim();
|
||||||
|
|
||||||
|
if (q.length < 2) {
|
||||||
|
clearSuggestionBox();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (q === lastQuery) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastQuery = q;
|
||||||
|
|
||||||
|
$.getJSON('<?= base_url('product_master/drug_terminology_autocomplete') ?>', {
|
||||||
|
q: q,
|
||||||
|
type: type,
|
||||||
|
limit: 10
|
||||||
|
}, function (resp) {
|
||||||
|
if (!resp || Number(resp.ok || 0) !== 1) {
|
||||||
|
clearSuggestionBox();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
renderSuggestions(resp.suggestions || []);
|
||||||
|
}).fail(function () {
|
||||||
|
clearSuggestionBox();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#input_item_name').off('input').on('input', function () {
|
||||||
|
if (suggestTimer) {
|
||||||
|
clearTimeout(suggestTimer);
|
||||||
|
}
|
||||||
|
suggestTimer = setTimeout(fetchDrugSuggestions, 250);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#abdm_drug_search_type').off('change').on('change', function () {
|
||||||
|
lastQuery = '';
|
||||||
|
fetchDrugSuggestions();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).off('click.abdmDrug').on('click.abdmDrug', function (evt) {
|
||||||
|
if (!$(evt.target).closest('#abdm-drug-suggest, #input_item_name').length) {
|
||||||
|
clearSuggestionBox();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
showSelectedDrugSummary();
|
||||||
|
|
||||||
$('#btn_update_stock').off('click').on('click', function () {
|
$('#btn_update_stock').off('click').on('click', function () {
|
||||||
$.post('<?= base_url('product_master/product_master_update') ?>/' + ($('#product_id').val() || '0'), $('#product-form').serialize(), function (data) {
|
$.post('<?= base_url('product_master/product_master_update') ?>/' + ($('#product_id').val() || '0'), $('#product-form').serialize(), function (data) {
|
||||||
if (!data || typeof data !== 'object') {
|
if (!data || typeof data !== 'object') {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user