feat: add scan doc list modal and print medicine formulation prefix
This commit is contained in:
parent
2154af394c
commit
30643c87f8
|
|
@ -477,12 +477,14 @@ $routes->post('Opd/confirm_payment', 'Opd::confirm_payment');
|
|||
$routes->post('Opd/opd_discount_update/(:num)', 'Opd::opd_discount_update/$1');
|
||||
$routes->post('Opd/opd_cancel/(:num)', 'Opd::opd_cancel/$1');
|
||||
$routes->match(['get', 'post'], 'Opd/opd_load_doc/(:num)', 'Opd::opd_load_doc/$1');
|
||||
$routes->match(['get', 'post'], 'Opd/opd_file_list/(:num)', 'Opd::opd_file_list/$1');
|
||||
$routes->match(['get', 'post'], 'Opd/opd_file_last_list', 'Opd::opd_file_last_list');
|
||||
$routes->match(['get', 'post'], 'Opd/opd_file_last_list/(:num)', 'Opd::opd_file_last_list/$1');
|
||||
$routes->match(['get', 'post'], 'Opd/save_image/(:num)', 'Opd::save_image/$1');
|
||||
$routes->post('Opd/scan_ai_process_file', 'Opd::scan_ai_process_file');
|
||||
$routes->post('Opd/scan_ai_assist', 'Opd::scan_ai_assist');
|
||||
$routes->match(['get', 'post'], 'Opd/opd_file_hide/(:num)', 'Opd::opd_file_hide/$1');
|
||||
$routes->match(['get', 'post'], 'Opd/opd_file_delete/(:num)', 'Opd::opd_file_delete/$1');
|
||||
$routes->get('Opd/invoice/(:num)', 'Opd::invoice/$1');
|
||||
$routes->get('Opd/invoice_print/(:num)', 'Opd::invoice_print/$1');
|
||||
$routes->get('Opd/opd_lettre_print/(:num)', 'Opd::opd_lettre_print/$1');
|
||||
|
|
|
|||
|
|
@ -1924,6 +1924,30 @@ class Opd extends BaseController
|
|||
foreach ($rxMeds as $med) {
|
||||
$i++;
|
||||
$name = trim((string) ($med['med_name'] ?? $med['drug_name'] ?? $med['medicine_name'] ?? $med['item_name'] ?? ''));
|
||||
$formulationRaw = trim((string) ($med['formulation'] ?? $med['dosage_form'] ?? $med['form'] ?? $med['medicine_form'] ?? ''));
|
||||
$formulationPrefixMap = [
|
||||
'tablet' => 'Tab',
|
||||
'tab' => 'Tab',
|
||||
'capsule' => 'Cap',
|
||||
'cap' => 'Cap',
|
||||
'syrup' => 'Syp',
|
||||
'syp' => 'Syp',
|
||||
'injection' => 'Inj',
|
||||
'inj' => 'Inj',
|
||||
'drop' => 'Drop',
|
||||
'drops' => 'Drops',
|
||||
'cream' => 'Cream',
|
||||
'ointment' => 'Oint',
|
||||
'gel' => 'Gel',
|
||||
'lotion' => 'Lotion',
|
||||
'powder' => 'Powder',
|
||||
'sachet' => 'Sachet',
|
||||
];
|
||||
$formulationKey = strtolower($formulationRaw);
|
||||
$formulationPrefix = $formulationPrefixMap[$formulationKey] ?? $formulationRaw;
|
||||
if ($formulationPrefix !== '' && $name !== '' && preg_match('/^' . preg_quote($formulationPrefix, '/') . '\b/i', $name) !== 1) {
|
||||
$name = trim($formulationPrefix . ' ' . $name);
|
||||
}
|
||||
$generic = trim((string) ($med['genericname'] ?? $med['generic_name'] ?? $med['salt_name'] ?? ''));
|
||||
$dose = trim((string) ($med['dosage_label'] ?? $med['dosage'] ?? $med['drug_dose'] ?? $med['dose'] ?? ''));
|
||||
$freq = trim((string) ($med['dosage_freq_label'] ?? $med['dosage_freq'] ?? $med['drug_freq'] ?? $med['frequency'] ?? ''));
|
||||
|
|
@ -3257,7 +3281,9 @@ class Opd extends BaseController
|
|||
$mapFreqLocal = [];
|
||||
$mapWhereLocal = [];
|
||||
$medGenericMap = [];
|
||||
$medFormulationMap = [];
|
||||
$missingGenericMedIds = [];
|
||||
$missingFormulationMedIds = [];
|
||||
|
||||
if ($this->db->tableExists('opd_dose_shed')) {
|
||||
$f = $this->db->getFieldNames('opd_dose_shed');
|
||||
|
|
@ -3380,20 +3406,40 @@ class Opd extends BaseController
|
|||
$missingGenericMedIds[$medId] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$formulationInline = trim((string) ($medRow['formulation'] ?? ($medRow['dosage_form'] ?? '')));
|
||||
if ($formulationInline === '') {
|
||||
$medId = (int) ($medRow['med_id'] ?? 0);
|
||||
if ($medId > 0) {
|
||||
$missingFormulationMedIds[$medId] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($medRow);
|
||||
|
||||
if (!empty($missingGenericMedIds) && $this->db->tableExists('opd_med_master')) {
|
||||
if ((!empty($missingGenericMedIds) || !empty($missingFormulationMedIds)) && $this->db->tableExists('opd_med_master')) {
|
||||
$masterFields = $this->db->getFieldNames('opd_med_master');
|
||||
$masterIdField = $this->resolveFirstField($masterFields, ['id']);
|
||||
$masterGenericField = $this->resolveFirstField($masterFields, ['genericname', 'generic_name']);
|
||||
$masterSaltField = $this->resolveFirstField($masterFields, ['salt_name', 'sal_name', 'salt', 'saltname']);
|
||||
$masterFormulationField = $this->resolveFirstField($masterFields, ['formulation', 'dosage_form', 'form']);
|
||||
$labelField = $masterSaltField ?? $masterGenericField;
|
||||
$select = [];
|
||||
if ($masterIdField !== null) {
|
||||
$select[] = $masterIdField . ' as med_id';
|
||||
}
|
||||
if ($labelField !== null) {
|
||||
$select[] = $labelField . ' as generic_fallback';
|
||||
}
|
||||
if ($masterFormulationField !== null) {
|
||||
$select[] = $masterFormulationField . ' as formulation_fallback';
|
||||
}
|
||||
|
||||
if ($masterIdField !== null && $labelField !== null) {
|
||||
if ($masterIdField !== null && !empty($select)) {
|
||||
$neededIds = array_unique(array_map('intval', array_merge(array_keys($missingGenericMedIds), array_keys($missingFormulationMedIds))));
|
||||
$rows = $this->db->table('opd_med_master')
|
||||
->select($masterIdField . ' as med_id,' . $labelField . ' as generic_fallback')
|
||||
->whereIn($masterIdField, array_map('intval', array_keys($missingGenericMedIds)))
|
||||
->select(implode(',', $select))
|
||||
->whereIn($masterIdField, $neededIds)
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
|
|
@ -3403,6 +3449,10 @@ class Opd extends BaseController
|
|||
if ($id > 0 && $label !== '') {
|
||||
$medGenericMap[$id] = $label;
|
||||
}
|
||||
$formulation = trim((string) ($r['formulation_fallback'] ?? ''));
|
||||
if ($id > 0 && $formulation !== '') {
|
||||
$medFormulationMap[$id] = $formulation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3415,6 +3465,14 @@ class Opd extends BaseController
|
|||
$medRow['genericname'] = $fallback;
|
||||
}
|
||||
}
|
||||
|
||||
$formulationInline = trim((string) ($medRow['formulation'] ?? ($medRow['dosage_form'] ?? '')));
|
||||
if ($formulationInline === '') {
|
||||
$formulationFallback = $medFormulationMap[(int) ($medRow['med_id'] ?? 0)] ?? '';
|
||||
if ($formulationFallback !== '') {
|
||||
$medRow['formulation'] = $formulationFallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($medRow);
|
||||
}
|
||||
|
|
@ -4193,6 +4251,61 @@ class Opd extends BaseController
|
|||
return view('billing/opd_scan_last_list', ['opd_file_list' => $list]);
|
||||
}
|
||||
|
||||
public function opd_file_list(int $opdid)
|
||||
{
|
||||
if (! $this->db->tableExists('file_upload_data')) {
|
||||
return $this->response->setBody('<div class="text-muted">No scan table found.</div>');
|
||||
}
|
||||
|
||||
$fields = $this->db->getFieldNames('file_upload_data');
|
||||
$safeSelect = ['id', 'full_path', 'file_ext', 'insert_date'];
|
||||
if (in_array('show_type', $fields, true)) {
|
||||
$safeSelect[] = 'show_type';
|
||||
}
|
||||
|
||||
$rows = $this->db->table('file_upload_data')
|
||||
->select(implode(',', $safeSelect))
|
||||
->where('opd_id', (int) $opdid)
|
||||
->where('show_type', 0)
|
||||
->orderBy('id', 'ASC')
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
$today = date('Y-m-d');
|
||||
$slides = [];
|
||||
foreach ($rows as $row) {
|
||||
$rawPath = (string) ($row['full_path'] ?? '');
|
||||
$normalizedPath = str_replace('\\', '/', $rawPath);
|
||||
$publicPath = $normalizedPath;
|
||||
$uploadPos = strpos($normalizedPath, '/uploads/');
|
||||
if ($uploadPos !== false) {
|
||||
$publicPath = substr($normalizedPath, $uploadPos);
|
||||
}
|
||||
|
||||
$ext = strtolower((string) ($row['file_ext'] ?? pathinfo($publicPath, PATHINFO_EXTENSION)));
|
||||
if ($ext !== '' && $ext[0] === '.') {
|
||||
$ext = substr($ext, 1);
|
||||
}
|
||||
|
||||
$insertDate = (string) ($row['insert_date'] ?? '');
|
||||
$insertYmd = $insertDate !== '' ? date('Y-m-d', strtotime($insertDate)) : '';
|
||||
$canDeleteToday = $insertYmd === $today;
|
||||
|
||||
$slides[] = [
|
||||
'id' => (int) ($row['id'] ?? 0),
|
||||
'path' => $publicPath,
|
||||
'is_pdf' => $ext === 'pdf',
|
||||
'insert_date' => $insertDate !== '' ? date('d-m-Y H:i', strtotime($insertDate)) : '',
|
||||
'can_delete_today' => $canDeleteToday ? 1 : 0,
|
||||
];
|
||||
}
|
||||
|
||||
return view('billing/opd_scan_carousel_list', [
|
||||
'opdid' => (int) $opdid,
|
||||
'slides' => $slides,
|
||||
]);
|
||||
}
|
||||
|
||||
public function scan_ai_assist()
|
||||
{
|
||||
if (! $this->request->isAJAX()) {
|
||||
|
|
@ -4319,6 +4432,59 @@ class Opd extends BaseController
|
|||
return $this->opd_file_last_list((int) ($row['opd_id'] ?? 0));
|
||||
}
|
||||
|
||||
public function opd_file_delete(int $fileId)
|
||||
{
|
||||
if (! $this->db->tableExists('file_upload_data')) {
|
||||
return $this->response->setJSON([
|
||||
'update' => 0,
|
||||
'error_text' => 'File table not found',
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
|
||||
$row = $this->db->table('file_upload_data')
|
||||
->select('id,opd_id,full_path,insert_date')
|
||||
->where('id', $fileId)
|
||||
->get(1)
|
||||
->getRowArray();
|
||||
|
||||
if (empty($row)) {
|
||||
return $this->response->setJSON([
|
||||
'update' => 0,
|
||||
'error_text' => 'File not found',
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
|
||||
$insertDate = (string) ($row['insert_date'] ?? '');
|
||||
$isToday = $insertDate !== '' && date('Y-m-d', strtotime($insertDate)) === date('Y-m-d');
|
||||
if (! $isToday) {
|
||||
return $this->response->setJSON([
|
||||
'update' => 0,
|
||||
'error_text' => 'Delete allowed only for current date uploads',
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
|
||||
$fullPath = (string) ($row['full_path'] ?? '');
|
||||
if ($fullPath !== '' && is_file($fullPath)) {
|
||||
@unlink($fullPath);
|
||||
}
|
||||
|
||||
$this->db->table('file_upload_data')->where('id', (int) $fileId)->delete();
|
||||
|
||||
return $this->response->setJSON([
|
||||
'update' => 1,
|
||||
'opd_id' => (int) ($row['opd_id'] ?? 0),
|
||||
'error_text' => 'Scan deleted',
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function insertOpdFileUploadRecord(string $fullPath, string $publicPath, int $pid, int $opdid, int $caseId, string $uploadBy, int $uploadById, string $mimeType = 'image/jpeg', int $isImageFile = 1, string $fileExt = '.jpg'): int
|
||||
{
|
||||
if (!$this->db->tableExists('file_upload_data')) {
|
||||
|
|
|
|||
|
|
@ -197,6 +197,58 @@
|
|||
});
|
||||
});
|
||||
|
||||
$(document).off('click.opdscanlistopen', '.btn-opd-scan-list').on('click.opdscanlistopen', '.btn-opd-scan-list', function() {
|
||||
// Open modal with slide-by-slide scan document list for this OPD.
|
||||
var opdid = parseInt($(this).data('opdid') || '0', 10);
|
||||
if (!opdid || !modalObj) {
|
||||
return;
|
||||
}
|
||||
|
||||
$('#testentryLabel').text('Scan Document List');
|
||||
$('#testentry-bodyc').html('<div class="text-muted">Loading...</div>');
|
||||
modalObj.show();
|
||||
|
||||
$.post('/Opd/opd_file_list/' + opdid, {}, function(html) {
|
||||
$('#testentry-bodyc').html(html || '<div class="text-danger">No scan documents found.</div>');
|
||||
}).fail(function() {
|
||||
$('#testentry-bodyc').html('<div class="text-danger">Unable to load scan document list.</div>');
|
||||
});
|
||||
});
|
||||
|
||||
window.deleteOpdScanFromList = function(fileId, opdid) {
|
||||
// Delete only allowed for current-date uploads; server enforces this rule.
|
||||
if (!fileId || !opdid) {
|
||||
return;
|
||||
}
|
||||
if (!window.confirm('Delete this scan document?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var csrf = getCsrfPair();
|
||||
var payload = {};
|
||||
payload[csrf.name] = csrf.value;
|
||||
|
||||
$.post('/Opd/opd_file_delete/' + fileId, payload, function(resp) {
|
||||
if (resp && resp.csrfName && resp.csrfHash) {
|
||||
updateCsrf(resp);
|
||||
}
|
||||
|
||||
if (!resp || parseInt(resp.update || 0, 10) !== 1) {
|
||||
var msg = (resp && resp.error_text) ? resp.error_text : 'Unable to delete document.';
|
||||
$('#testentry-bodyc').prepend('<div class="alert alert-danger py-1 mb-2">' + $('<div>').text(msg).html() + '</div>');
|
||||
return;
|
||||
}
|
||||
|
||||
$.post('/Opd/opd_file_list/' + opdid, {}, function(html) {
|
||||
$('#testentry-bodyc').html(html || '<div class="text-muted">No scan documents found.</div>');
|
||||
}).fail(function() {
|
||||
$('#testentry-bodyc').prepend('<div class="alert alert-danger py-1 mb-2">Unable to refresh scan list.</div>');
|
||||
});
|
||||
}, 'json').fail(function() {
|
||||
$('#testentry-bodyc').prepend('<div class="alert alert-danger py-1 mb-2">Unable to delete document.</div>');
|
||||
});
|
||||
};
|
||||
|
||||
$(document).off('click.opdstatus', '.btn-opd-status').on('click.opdstatus', '.btn-opd-status', function() {
|
||||
var opdId = $(this).data('opd-id');
|
||||
var opdStatus = $(this).data('opd-status');
|
||||
|
|
|
|||
|
|
@ -44,9 +44,9 @@ if ($authUser) {
|
|||
<button type="button" class="btn btn-outline-secondary btn-sm btn-opd-scan" title="Upload Scan" data-opdid="<?= esc((int) ($row->opd_id ?? 0)) ?>">
|
||||
Upload
|
||||
</button>
|
||||
<a class="btn btn-outline-dark btn-sm" title="Profile" href="javascript:load_form('/billing/patient/person_record/<?= esc((int) ($row->id ?? 0)) ?>/0','Patient Profile');">
|
||||
Profile
|
||||
</a>
|
||||
<button type="button" class="btn btn-outline-dark btn-sm btn-opd-scan-list" title="Scan Document List" data-opdid="<?= esc((int) ($row->opd_id ?? 0)) ?>">
|
||||
Scan Doc List
|
||||
</button>
|
||||
<?php if (($tabType ?? '') === 'waiting') : ?>
|
||||
<button type="button" class="btn btn-outline-success btn-sm btn-opd-status" data-opd-id="<?= esc((int) ($row->opd_id ?? 0)) ?>" data-opd-status="2" title="Visit Done">
|
||||
Visit Done
|
||||
|
|
|
|||
47
app/Views/billing/opd_scan_carousel_list.php
Normal file
47
app/Views/billing/opd_scan_carousel_list.php
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<?php $items = $slides ?? []; ?>
|
||||
<?php if (empty($items)) : ?>
|
||||
<div class="text-muted">No scan documents found.</div>
|
||||
<?php else : ?>
|
||||
<div id="opdScanCarousel" class="carousel slide" data-bs-ride="false">
|
||||
<div class="carousel-indicators">
|
||||
<?php foreach ($items as $index => $item) : ?>
|
||||
<button type="button" data-bs-target="#opdScanCarousel" data-bs-slide-to="<?= (int) $index ?>" class="<?= $index === 0 ? 'active' : '' ?>" <?= $index === 0 ? 'aria-current="true"' : '' ?> aria-label="Slide <?= (int) ($index + 1) ?>"></button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="carousel-inner bg-light rounded border" style="min-height:420px;">
|
||||
<?php foreach ($items as $index => $item) : ?>
|
||||
<div class="carousel-item <?= $index === 0 ? 'active' : '' ?>" style="padding:14px;">
|
||||
<div class="text-center">
|
||||
<?php if (!empty($item['is_pdf'])) : ?>
|
||||
<iframe src="<?= esc($item['path']) ?>" style="width:100%;height:460px;border:1px solid #dee2e6;"></iframe>
|
||||
<div class="mt-2"><a class="btn btn-outline-secondary btn-sm" target="_blank" href="<?= esc($item['path']) ?>">Open PDF in new tab</a></div>
|
||||
<?php else : ?>
|
||||
<img src="<?= esc($item['path']) ?>" alt="Scan Document" class="img-fluid rounded" style="max-height:520px;">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mt-2">
|
||||
<div class="small text-muted">
|
||||
Uploaded: <?= esc($item['insert_date'] ?? '') ?>
|
||||
</div>
|
||||
<?php if ((int) ($item['can_delete_today'] ?? 0) === 1) : ?>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm" onclick="deleteOpdScanFromList(<?= (int) ($item['id'] ?? 0) ?>, <?= (int) ($opdid ?? 0) ?>)">Delete</button>
|
||||
<?php else : ?>
|
||||
<span class="badge bg-secondary">Delete allowed only for today upload</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<button class="carousel-control-prev" type="button" data-bs-target="#opdScanCarousel" data-bs-slide="prev">
|
||||
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">Previous</span>
|
||||
</button>
|
||||
<button class="carousel-control-next" type="button" data-bs-target="#opdScanCarousel" data-bs-slide="next">
|
||||
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">Next</span>
|
||||
</button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
Loading…
Reference in New Issue
Block a user