Enhance patient search and enforce refer master validation
This commit is contained in:
parent
780ebaa263
commit
5f443f307a
|
|
@ -111,6 +111,21 @@ class Patient extends BaseController
|
|||
->with('error', $abhaError);
|
||||
}
|
||||
|
||||
$referByName = trim((string) $this->request->getPost('refer_by_name'));
|
||||
$referByError = $this->validatePatientReferByInput($referByName);
|
||||
if ($referByError !== null) {
|
||||
if ($isAjax) {
|
||||
return $this->response->setJSON([
|
||||
'insertid' => 0,
|
||||
'error_text' => $referByError,
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->to(base_url('billing/patient'))
|
||||
->withInput()
|
||||
->with('error', $referByError);
|
||||
}
|
||||
|
||||
$bloodGroup = trim((string) $this->request->getPost('input_blood_group'));
|
||||
if ($bloodGroup === '') {
|
||||
$bloodGroup = 'Not Define';
|
||||
|
|
@ -132,7 +147,7 @@ class Patient extends BaseController
|
|||
'udai' => strtoupper((string) $this->request->getPost('input_udai')),
|
||||
'estimate_dob' => $estimate_dob,
|
||||
];
|
||||
$this->applyPatientReferbyField($data, (string) $this->request->getPost('refer_by_name'));
|
||||
$this->applyPatientReferbyField($data, $referByName);
|
||||
$this->applyPatientAbhaFieldValues($data, $abhaId, $abhaAddress);
|
||||
|
||||
if ($chk_age === 'on') {
|
||||
|
|
@ -220,6 +235,9 @@ class Patient extends BaseController
|
|||
|
||||
$builder = $this->db->table('patient_master');
|
||||
$builder->select("id,p_fname,p_relative,p_rname,gender,add1,city,district,state,zip,mphone1,udai,last_visit,dob,age,age_in_month,estimate_dob");
|
||||
if ($this->db->fieldExists('referby', 'patient_master')) {
|
||||
$builder->select('referby');
|
||||
}
|
||||
if ($abhaField !== null) {
|
||||
$builder->select($abhaField . ' AS abha_id');
|
||||
}
|
||||
|
|
@ -436,7 +454,31 @@ class Patient extends BaseController
|
|||
$sdata = (string) $this->request->getPost('txtsearch');
|
||||
$sdata = preg_replace('/[^A-Za-z0-9 _.@\/-]/', '', trim($sdata ?? ''));
|
||||
|
||||
$advSearchBy = trim((string) $this->request->getPost('adv_search_by'));
|
||||
$advSearchValue = preg_replace('/[^A-Za-z0-9 _.@\/-]/', '', trim((string) $this->request->getPost('adv_search_value')));
|
||||
$advAgeMode = trim((string) $this->request->getPost('adv_age_mode'));
|
||||
$advAgeValue = preg_replace('/[^0-9]/', '', trim((string) $this->request->getPost('adv_age_value')));
|
||||
$advAgeFrom = preg_replace('/[^0-9]/', '', trim((string) $this->request->getPost('adv_age_from')));
|
||||
$advAgeTo = preg_replace('/[^0-9]/', '', trim((string) $this->request->getPost('adv_age_to')));
|
||||
$advAgeTolerance = preg_replace('/[^0-9]/', '', trim((string) $this->request->getPost('adv_age_tolerance')));
|
||||
$advAgeToleranceInt = $advAgeTolerance === '' ? 2 : (int) $advAgeTolerance;
|
||||
if ($advAgeToleranceInt < 0) {
|
||||
$advAgeToleranceInt = 0;
|
||||
}
|
||||
if ($advAgeToleranceInt > 20) {
|
||||
$advAgeToleranceInt = 20;
|
||||
}
|
||||
|
||||
$data['search_query'] = $sdata;
|
||||
$data['advanced_filters'] = [
|
||||
'adv_search_by' => $advSearchBy,
|
||||
'adv_search_value' => $advSearchValue,
|
||||
'adv_age_mode' => $advAgeMode,
|
||||
'adv_age_value' => $advAgeValue,
|
||||
'adv_age_from' => $advAgeFrom,
|
||||
'adv_age_to' => $advAgeTo,
|
||||
'adv_age_tolerance' => (string) $advAgeToleranceInt,
|
||||
];
|
||||
return view('billing/Patient_Search_V', $data);
|
||||
}
|
||||
|
||||
|
|
@ -508,10 +550,29 @@ class Patient extends BaseController
|
|||
// Get search value from DataTables search box or from initial search_query
|
||||
$dtSearch = trim((string) ($request['search']['value'] ?? ''));
|
||||
$initialSearch = trim((string) ($request['search_query'] ?? ''));
|
||||
$advSearchBy = trim((string) ($request['adv_search_by'] ?? ''));
|
||||
$advSearchValue = trim((string) ($request['adv_search_value'] ?? ''));
|
||||
$advAgeMode = trim((string) ($request['adv_age_mode'] ?? ''));
|
||||
$advAgeValue = trim((string) ($request['adv_age_value'] ?? ''));
|
||||
$advAgeFrom = trim((string) ($request['adv_age_from'] ?? ''));
|
||||
$advAgeTo = trim((string) ($request['adv_age_to'] ?? ''));
|
||||
$advAgeTolerance = trim((string) ($request['adv_age_tolerance'] ?? ''));
|
||||
|
||||
// Use DataTables search if provided, otherwise use initial search_query
|
||||
$sdata = $dtSearch !== '' ? $dtSearch : $initialSearch;
|
||||
$sdata = preg_replace('/[^A-Za-z0-9 _.@\/-]/', '', $sdata);
|
||||
$advSearchValue = preg_replace('/[^A-Za-z0-9 _.@\/-]/', '', $advSearchValue);
|
||||
$advAgeValue = preg_replace('/[^0-9]/', '', $advAgeValue);
|
||||
$advAgeFrom = preg_replace('/[^0-9]/', '', $advAgeFrom);
|
||||
$advAgeTo = preg_replace('/[^0-9]/', '', $advAgeTo);
|
||||
$advAgeTolerance = preg_replace('/[^0-9]/', '', $advAgeTolerance);
|
||||
$advAgeToleranceInt = $advAgeTolerance === '' ? 2 : (int) $advAgeTolerance;
|
||||
if ($advAgeToleranceInt < 0) {
|
||||
$advAgeToleranceInt = 0;
|
||||
}
|
||||
if ($advAgeToleranceInt > 20) {
|
||||
$advAgeToleranceInt = 20;
|
||||
}
|
||||
|
||||
// Detect ABHA and legacy UHID columns in patient_master
|
||||
$abhaField = null;
|
||||
|
|
@ -524,6 +585,72 @@ class Patient extends BaseController
|
|||
if (in_array($f, $pmFields, true)) { $oldUhidField = $f; break; }
|
||||
}
|
||||
|
||||
$hasAdvancedFilters = false;
|
||||
$advancedClause = '';
|
||||
|
||||
if ($advSearchBy === 'age') {
|
||||
$ageExpr = "(CASE
|
||||
WHEN p.dob IS NOT NULL AND p.dob != '0000-00-00' THEN TIMESTAMPDIFF(YEAR, p.dob, CURDATE())
|
||||
WHEN p.age REGEXP '^[0-9]+$' THEN CAST(p.age AS UNSIGNED)
|
||||
ELSE NULL
|
||||
END)";
|
||||
|
||||
if ($advAgeMode === 'between' && $advAgeFrom !== '' && $advAgeTo !== '') {
|
||||
$from = (int) $advAgeFrom;
|
||||
$to = (int) $advAgeTo;
|
||||
if ($from > $to) {
|
||||
[$from, $to] = [$to, $from];
|
||||
}
|
||||
$advancedClause = " and {$ageExpr} between {$from} and {$to}";
|
||||
$hasAdvancedFilters = true;
|
||||
} elseif ($advAgeMode === 'approx' && $advAgeValue !== '') {
|
||||
$age = (int) $advAgeValue;
|
||||
$min = max(0, $age - $advAgeToleranceInt);
|
||||
$max = $age + $advAgeToleranceInt;
|
||||
$advancedClause = " and {$ageExpr} between {$min} and {$max}";
|
||||
$hasAdvancedFilters = true;
|
||||
}
|
||||
} elseif ($advSearchValue !== '') {
|
||||
$escapedLike = $this->db->escape('%' . $advSearchValue . '%');
|
||||
$escapedExact = $this->db->escape($advSearchValue);
|
||||
|
||||
switch ($advSearchBy) {
|
||||
case 'patient_uhid':
|
||||
$advancedClause = " and (p.p_code LIKE {$escapedLike}";
|
||||
if (ctype_digit($advSearchValue)) {
|
||||
$advancedClause .= ' or p.id = ' . (int) $advSearchValue;
|
||||
}
|
||||
$advancedClause .= ')';
|
||||
$hasAdvancedFilters = true;
|
||||
break;
|
||||
case 'patient_name':
|
||||
$advancedClause = " and p.p_fname LIKE {$escapedLike}";
|
||||
$hasAdvancedFilters = true;
|
||||
break;
|
||||
case 'relative_name':
|
||||
$advancedClause = " and p.p_rname LIKE {$escapedLike}";
|
||||
$hasAdvancedFilters = true;
|
||||
break;
|
||||
case 'phone':
|
||||
$advancedClause = " and (p.mphone1 = {$escapedExact} or p.mphone1 LIKE {$escapedLike})";
|
||||
$hasAdvancedFilters = true;
|
||||
break;
|
||||
case 'refer_by':
|
||||
if (in_array('referby', $pmFields, true)) {
|
||||
$advancedClause = " and p.referby LIKE {$escapedLike}";
|
||||
$hasAdvancedFilters = true;
|
||||
}
|
||||
break;
|
||||
case 'old_uhid':
|
||||
$oldUhidClause = $this->buildOldUhidSearchClause($advSearchValue, $oldUhidField);
|
||||
if ($oldUhidClause !== '') {
|
||||
$advancedClause = ' and (' . $oldUhidClause . ')';
|
||||
$hasAdvancedFilters = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Build WHERE clause based on search query
|
||||
$searchString = ' 1=1 ';
|
||||
if (strlen($sdata) > 0) {
|
||||
|
|
@ -563,6 +690,25 @@ class Patient extends BaseController
|
|||
}
|
||||
}
|
||||
|
||||
$searchString .= $advancedClause;
|
||||
|
||||
$useLatestThirtyOnly = (strlen($sdata) === 0 && ! $hasAdvancedFilters);
|
||||
if ($useLatestThirtyOnly) {
|
||||
$searchString .= " and (
|
||||
(
|
||||
p.last_visit IS NOT NULL
|
||||
AND p.last_visit != '0000-00-00'
|
||||
AND DATE(p.last_visit) >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
)
|
||||
OR
|
||||
(
|
||||
p.insert_date IS NOT NULL
|
||||
AND p.insert_date != '0000-00-00'
|
||||
AND DATE(p.insert_date) >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
)
|
||||
)";
|
||||
}
|
||||
|
||||
// Count total records (without filtering)
|
||||
$totalSql = "SELECT COUNT(DISTINCT p.id) as total FROM patient_master p";
|
||||
$totalData = (int) $this->db->query($totalSql)->getRow()->total;
|
||||
|
|
@ -606,6 +752,11 @@ class Patient extends BaseController
|
|||
$patientId = (int) ($row->id ?? 0);
|
||||
$age = esc(get_age_1($row->dob ?? null, $row->age ?? '', $row->age_in_month ?? '', $row->estimate_dob ?? '', $row->Last_Visit ?? null));
|
||||
$oldUhid = '';
|
||||
$patientNameCell = esc(($row->p_fname ?? '') . ' {' . ($row->p_rname ?? '') . '}');
|
||||
$referBy = trim((string) ($row->referby ?? ''));
|
||||
if ($referBy !== '') {
|
||||
$patientNameCell .= '<br><small><i>Refer By: ' . esc(ucwords(strtolower($referBy))) . '</i></small>';
|
||||
}
|
||||
if ($oldUhidField !== null && $oldUhidField !== '' && isset($row->{$oldUhidField})) {
|
||||
$oldUhid = (string) $row->{$oldUhidField};
|
||||
}
|
||||
|
|
@ -613,7 +764,7 @@ class Patient extends BaseController
|
|||
$data[] = [
|
||||
$start + $index + 1,
|
||||
$this->formatPatientCodeCell($patientId, (string) ($row->p_code ?? ''), $oldUhid),
|
||||
esc(($row->p_fname ?? '') . ' {' . ($row->p_rname ?? '') . '}'),
|
||||
$patientNameCell,
|
||||
$age,
|
||||
esc($row->Last_Visit ?? ''),
|
||||
esc($row->insurance_status ?? 'Self'),
|
||||
|
|
@ -811,6 +962,16 @@ class Patient extends BaseController
|
|||
]);
|
||||
}
|
||||
|
||||
$referByName = trim((string) $this->request->getPost('refer_by_name'));
|
||||
$existingReferBy = is_array($existingPatient) ? (string) ($existingPatient['referby'] ?? '') : null;
|
||||
$referByError = $this->validatePatientReferByInput($referByName, $existingReferBy);
|
||||
if ($referByError !== null) {
|
||||
return $this->response->setJSON([
|
||||
'update' => 0,
|
||||
'error_text' => $referByError,
|
||||
]);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'mphone1' => $this->request->getPost('input_mphone1'),
|
||||
'p_fname' => strtoupper((string) $this->request->getPost('input_name')),
|
||||
|
|
@ -828,7 +989,7 @@ class Patient extends BaseController
|
|||
'estimate_dob' => $estimate_dob,
|
||||
'blood_group' => $this->request->getPost('input_blood_group'),
|
||||
];
|
||||
$this->applyPatientReferbyField($data, (string) $this->request->getPost('refer_by_name'));
|
||||
$this->applyPatientReferbyField($data, $referByName);
|
||||
|
||||
if ($isAbhaVerifiedLocked) {
|
||||
$data['p_fname'] = (string) ($existingPatient['p_fname'] ?? $data['p_fname']);
|
||||
|
|
@ -891,6 +1052,34 @@ class Patient extends BaseController
|
|||
$data['referby'] = trim(strtoupper($referByName));
|
||||
}
|
||||
|
||||
private function validatePatientReferByInput(string $referByName, ?string $existingReferBy = null): ?string
|
||||
{
|
||||
$referByName = trim($referByName);
|
||||
if ($referByName === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$normalize = static fn (string $v): string => strtoupper(trim(preg_replace('/\s+/', ' ', $v)));
|
||||
$typed = $normalize($referByName);
|
||||
if ($typed === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($existingReferBy !== null && $normalize($existingReferBy) === $typed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$activeRefs = $this->getActiveReferMasters();
|
||||
foreach ($activeRefs as $row) {
|
||||
$label = trim((string) (($row->title ?? '') . ' ' . ($row->f_name ?? '')));
|
||||
if ($label !== '' && $normalize($label) === $typed) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return 'Refer By must be selected from Refer Master list.';
|
||||
}
|
||||
|
||||
public function update_aadhar()
|
||||
{
|
||||
if (!$this->request->isAJAX()) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ class Reffer extends BaseController
|
|||
{
|
||||
public function index(): string
|
||||
{
|
||||
$this->ensureReferMasterAddressColumns();
|
||||
$model = new RefferModel();
|
||||
|
||||
return view('Setting/Reffer/reffer_index', [
|
||||
|
|
@ -18,6 +19,7 @@ class Reffer extends BaseController
|
|||
|
||||
public function create(): string
|
||||
{
|
||||
$this->ensureReferMasterAddressColumns();
|
||||
$model = new RefferModel();
|
||||
|
||||
return view('Setting/Reffer/reffer_add', [
|
||||
|
|
@ -27,6 +29,7 @@ class Reffer extends BaseController
|
|||
|
||||
public function edit(int $id): string
|
||||
{
|
||||
$this->ensureReferMasterAddressColumns();
|
||||
$model = new RefferModel();
|
||||
|
||||
return view('Setting/Reffer/reffer_edit', [
|
||||
|
|
@ -37,6 +40,7 @@ class Reffer extends BaseController
|
|||
|
||||
public function store()
|
||||
{
|
||||
$this->ensureReferMasterAddressColumns();
|
||||
if (! $this->request->isAJAX()) {
|
||||
return $this->response->setJSON(['insertid' => 0, 'error_text' => 'Invalid request']);
|
||||
}
|
||||
|
|
@ -66,6 +70,8 @@ class Reffer extends BaseController
|
|||
'active' => 1,
|
||||
];
|
||||
|
||||
$data = array_merge($data, $this->getReferAddressData());
|
||||
|
||||
$insertId = $model->insert($data);
|
||||
|
||||
return $this->response->setJSON([
|
||||
|
|
@ -76,6 +82,7 @@ class Reffer extends BaseController
|
|||
|
||||
public function update(int $id)
|
||||
{
|
||||
$this->ensureReferMasterAddressColumns();
|
||||
if (! $this->request->isAJAX()) {
|
||||
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid request']);
|
||||
}
|
||||
|
|
@ -105,6 +112,8 @@ class Reffer extends BaseController
|
|||
'active' => 1,
|
||||
];
|
||||
|
||||
$data = array_merge($data, $this->getReferAddressData());
|
||||
|
||||
$updated = $model->updateReffer($data, $id);
|
||||
|
||||
return $this->response->setJSON([
|
||||
|
|
@ -196,4 +205,50 @@ class Reffer extends BaseController
|
|||
'errors' => $errors,
|
||||
]);
|
||||
}
|
||||
|
||||
private function getReferAddressData(): array
|
||||
{
|
||||
$db = db_connect();
|
||||
$data = [];
|
||||
|
||||
if ($db->fieldExists('place', 'refer_master')) {
|
||||
$data['place'] = trim((string) $this->request->getPost('input_place'));
|
||||
}
|
||||
if ($db->fieldExists('city', 'refer_master')) {
|
||||
$data['city'] = trim((string) $this->request->getPost('input_city'));
|
||||
}
|
||||
if ($db->fieldExists('district', 'refer_master')) {
|
||||
$data['district'] = trim((string) $this->request->getPost('input_district'));
|
||||
}
|
||||
if ($db->fieldExists('state', 'refer_master')) {
|
||||
$data['state'] = trim((string) $this->request->getPost('input_state'));
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function ensureReferMasterAddressColumns(): void
|
||||
{
|
||||
try {
|
||||
$db = db_connect();
|
||||
if (! $db->tableExists('refer_master')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$columnsToEnsure = [
|
||||
'place' => "ALTER TABLE refer_master ADD COLUMN place varchar(150) DEFAULT NULL AFTER phone_number",
|
||||
'city' => "ALTER TABLE refer_master ADD COLUMN city varchar(100) DEFAULT NULL AFTER place",
|
||||
'district' => "ALTER TABLE refer_master ADD COLUMN district varchar(100) DEFAULT NULL AFTER city",
|
||||
'state' => "ALTER TABLE refer_master ADD COLUMN state varchar(100) DEFAULT NULL AFTER district",
|
||||
];
|
||||
|
||||
foreach ($columnsToEnsure as $column => $sql) {
|
||||
if (! $db->fieldExists($column, 'refer_master')) {
|
||||
$db->query($sql);
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Fail-open so referral pages still load if schema update is blocked.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,22 @@
|
|||
<?php endforeach ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Place</label>
|
||||
<input class="form-control" name="input_place" placeholder="Place" value="" type="text" autocomplete="off">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">City</label>
|
||||
<input class="form-control" name="input_city" placeholder="City" value="" type="text" autocomplete="off">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">District</label>
|
||||
<input class="form-control" name="input_district" placeholder="District" value="" type="text" autocomplete="off">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">State</label>
|
||||
<input class="form-control" name="input_state" placeholder="State" value="" type="text" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-3">
|
||||
|
|
|
|||
|
|
@ -34,6 +34,22 @@
|
|||
<?php endforeach ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Place</label>
|
||||
<input class="form-control" name="input_place" placeholder="Place" value="<?= esc($data[0]->place ?? '') ?>" type="text" autocomplete="off">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">City</label>
|
||||
<input class="form-control" name="input_city" placeholder="City" value="<?= esc($data[0]->city ?? '') ?>" type="text" autocomplete="off">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">District</label>
|
||||
<input class="form-control" name="input_district" placeholder="District" value="<?= esc($data[0]->district ?? '') ?>" type="text" autocomplete="off">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">State</label>
|
||||
<input class="form-control" name="input_state" placeholder="State" value="<?= esc($data[0]->state ?? '') ?>" type="text" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-3">
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Address</th>
|
||||
<th>Register Date</th>
|
||||
<th>Status</th>
|
||||
<th>Edit</th>
|
||||
|
|
@ -27,9 +28,19 @@
|
|||
<tbody>
|
||||
<?php if (! empty($refer_master)) : ?>
|
||||
<?php foreach ($refer_master as $row) : ?>
|
||||
<?php
|
||||
$addressParts = array_filter([
|
||||
trim((string) ($row->place ?? '')),
|
||||
trim((string) ($row->city ?? '')),
|
||||
trim((string) ($row->district ?? '')),
|
||||
trim((string) ($row->state ?? '')),
|
||||
], static fn ($v) => $v !== '');
|
||||
$addressText = $addressParts === [] ? 'NA' : implode(', ', $addressParts);
|
||||
?>
|
||||
<tr>
|
||||
<td><?= esc(($row->title ?? '') . ' ' . ($row->f_name ?? '')) ?></td>
|
||||
<td><?= esc($row->type_desc ?? 'Others') ?></td>
|
||||
<td><?= esc($addressText) ?></td>
|
||||
<td><?= esc($row->str_dateadd ?? '') ?></td>
|
||||
<td>
|
||||
<?php if (! empty($row->active)) : ?>
|
||||
|
|
@ -45,7 +56,7 @@
|
|||
<?php endforeach ?>
|
||||
<?php else : ?>
|
||||
<tr>
|
||||
<td colspan="5" class="text-center text-muted">No referral clients found.</td>
|
||||
<td colspan="6" class="text-center text-muted">No referral clients found.</td>
|
||||
</tr>
|
||||
<?php endif ?>
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
<script>
|
||||
(function() {
|
||||
var searchQuery = '<?= esc($search_query ?? '', 'js') ?>';
|
||||
var advancedFilters = <?= json_encode($advanced_filters ?? [], JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>;
|
||||
|
||||
if (window.jQuery && $.fn.DataTable) {
|
||||
$('#example1').DataTable({
|
||||
|
|
@ -46,9 +47,16 @@
|
|||
ajax: {
|
||||
url: '<?= base_url('billing/patient/search_ajax') ?>',
|
||||
type: 'GET',
|
||||
data: {
|
||||
search_query: searchQuery
|
||||
}
|
||||
data: function(d) {
|
||||
d.search_query = searchQuery;
|
||||
d.adv_search_by = advancedFilters.adv_search_by || '';
|
||||
d.adv_search_value = advancedFilters.adv_search_value || '';
|
||||
d.adv_age_mode = advancedFilters.adv_age_mode || '';
|
||||
d.adv_age_value = advancedFilters.adv_age_value || '';
|
||||
d.adv_age_from = advancedFilters.adv_age_from || '';
|
||||
d.adv_age_to = advancedFilters.adv_age_to || '';
|
||||
d.adv_age_tolerance = advancedFilters.adv_age_tolerance || '2';
|
||||
}
|
||||
},
|
||||
columns: [
|
||||
{ data: 0, orderable: false },
|
||||
|
|
@ -60,11 +68,12 @@
|
|||
{ data: 6, orderable: false }
|
||||
],
|
||||
order: [[4, 'desc']], // Order by Last Visit (column index 4) DESC
|
||||
pageLength: 25,
|
||||
searching: false,
|
||||
pageLength: 30,
|
||||
lengthMenu: [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]],
|
||||
language: {
|
||||
processing: '<i class="fa fa-spinner fa-spin fa-3x fa-fw"></i><span class="sr-only">Loading...</span>',
|
||||
emptyTable: searchQuery ? 'No patients found matching your search' : 'No patient records available',
|
||||
emptyTable: searchQuery ? 'No patients found matching your search' : 'No patients available for the selected criteria',
|
||||
info: 'Showing _START_ to _END_ of _TOTAL_ patients',
|
||||
infoEmpty: 'Showing 0 to 0 of 0 patients',
|
||||
infoFiltered: '(filtered from _MAX_ total patients)',
|
||||
|
|
|
|||
|
|
@ -315,6 +315,72 @@
|
|||
<button type="submit" class="btn btn-info btn-flat">Go!</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-2 d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||
<small class="text-muted">Blank search shows patients from last 30 days by register date or last visit date.</small>
|
||||
<button type="button" id="toggle_advanced_search" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="bi bi-funnel"></i> Advanced Search
|
||||
</button>
|
||||
</div>
|
||||
<div id="advanced_search_panel" class="card mt-2" style="display:none;">
|
||||
<div class="card-body py-3">
|
||||
<div class="row g-2 align-items-end">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Search By</label>
|
||||
<select class="form-select" name="adv_search_by" id="adv_search_by">
|
||||
<option value="">Select field</option>
|
||||
<option value="patient_uhid">Patient / UHID</option>
|
||||
<option value="patient_name">Patient Name</option>
|
||||
<option value="relative_name">Relative Name</option>
|
||||
<option value="phone">Phone No</option>
|
||||
<option value="age">Age</option>
|
||||
<option value="refer_by">Refer By</option>
|
||||
<option value="old_uhid">OLD UHID</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4" id="adv_value_col">
|
||||
<label class="form-label">Search Value</label>
|
||||
<input type="text" class="form-control" name="adv_search_value" id="adv_search_value" placeholder="Enter value">
|
||||
<datalist id="adv_refer_by_list">
|
||||
<?php foreach (($refer_master ?? []) as $row) : ?>
|
||||
<?php $advReferLabel = trim((string) (($row->title ?? '') . ' ' . ($row->f_name ?? ''))); ?>
|
||||
<?php if ($advReferLabel !== '') : ?>
|
||||
<option value="<?= esc($advReferLabel) ?>"></option>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</datalist>
|
||||
</div>
|
||||
<div class="col-md-4 d-none" id="adv_age_mode_col">
|
||||
<label class="form-label">Age Mode</label>
|
||||
<select class="form-select" name="adv_age_mode" id="adv_age_mode">
|
||||
<option value="approx">Approx</option>
|
||||
<option value="between">Between</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4 d-none" id="adv_age_approx_col">
|
||||
<label class="form-label">Approx Age</label>
|
||||
<input type="number" min="0" max="150" class="form-control" name="adv_age_value" id="adv_age_value" placeholder="e.g. 35">
|
||||
<small class="text-muted d-block mt-1" id="adv_age_effective_hint">Approx range: -</small>
|
||||
</div>
|
||||
<div class="col-md-2 d-none" id="adv_age_tolerance_col">
|
||||
<label class="form-label">Tolerance (+/- Years)</label>
|
||||
<input type="number" min="0" max="20" class="form-control" name="adv_age_tolerance" id="adv_age_tolerance" value="2">
|
||||
</div>
|
||||
<div class="col-md-2 d-none" id="adv_age_from_col">
|
||||
<label class="form-label">Age From</label>
|
||||
<input type="number" min="0" max="150" class="form-control" name="adv_age_from" id="adv_age_from" placeholder="0">
|
||||
</div>
|
||||
<div class="col-md-2 d-none" id="adv_age_to_col">
|
||||
<label class="form-label">Age To</label>
|
||||
<input type="number" min="0" max="150" class="form-control" name="adv_age_to" id="adv_age_to" placeholder="60">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 d-flex justify-content-end">
|
||||
<button type="button" id="btn_advanced_search" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-search"></i> Search Advanced
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="search_loading" class="alert alert-info mt-2" style="display:none;">
|
||||
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
|
|
@ -791,7 +857,144 @@
|
|||
return fallbackText;
|
||||
}
|
||||
|
||||
function syncAdvancedSearchFields() {
|
||||
var searchBy = String($('#adv_search_by').val() || '');
|
||||
var ageMode = String($('#adv_age_mode').val() || 'approx');
|
||||
var isAge = searchBy === 'age';
|
||||
var isReferBy = searchBy === 'refer_by';
|
||||
|
||||
$('#adv_value_col').toggleClass('d-none', isAge);
|
||||
$('#adv_age_mode_col').toggleClass('d-none', !isAge);
|
||||
|
||||
if (!isAge) {
|
||||
$('#adv_search_value').attr('placeholder', isReferBy ? 'Type referral name' : 'Enter value');
|
||||
if (isReferBy) {
|
||||
$('#adv_search_value').attr('list', 'adv_refer_by_list');
|
||||
} else {
|
||||
$('#adv_search_value').removeAttr('list');
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAge) {
|
||||
$('#adv_age_approx_col,#adv_age_tolerance_col,#adv_age_from_col,#adv_age_to_col').addClass('d-none');
|
||||
$('#adv_age_value,#adv_age_from,#adv_age_to').val('');
|
||||
$('#adv_age_tolerance').val('2');
|
||||
$('#adv_age_effective_hint').text('Approx range: -');
|
||||
return;
|
||||
}
|
||||
|
||||
if (ageMode === 'between') {
|
||||
$('#adv_age_approx_col,#adv_age_tolerance_col').addClass('d-none');
|
||||
$('#adv_age_value').val('');
|
||||
$('#adv_age_tolerance').val('2');
|
||||
$('#adv_age_effective_hint').text('Approx range: -');
|
||||
$('#adv_age_from_col,#adv_age_to_col').removeClass('d-none');
|
||||
} else {
|
||||
$('#adv_age_from_col,#adv_age_to_col').addClass('d-none');
|
||||
$('#adv_age_from,#adv_age_to').val('');
|
||||
$('#adv_age_approx_col').removeClass('d-none');
|
||||
$('#adv_age_tolerance_col').removeClass('d-none');
|
||||
syncAgeApproxHint();
|
||||
}
|
||||
}
|
||||
|
||||
function syncAgeApproxHint() {
|
||||
var ageValue = parseInt($('#adv_age_value').val(), 10);
|
||||
var tolerance = parseInt($('#adv_age_tolerance').val(), 10);
|
||||
|
||||
if (isNaN(tolerance) || tolerance < 0) {
|
||||
tolerance = 0;
|
||||
}
|
||||
if (tolerance > 20) {
|
||||
tolerance = 20;
|
||||
}
|
||||
|
||||
if (isNaN(ageValue) || ageValue < 0) {
|
||||
$('#adv_age_effective_hint').text('Approx range: -');
|
||||
return;
|
||||
}
|
||||
|
||||
var minAge = Math.max(0, ageValue - tolerance);
|
||||
var maxAge = ageValue + tolerance;
|
||||
$('#adv_age_effective_hint').text('Approx range: ' + minAge + ' to ' + maxAge + ' years');
|
||||
}
|
||||
|
||||
function getDatalistValues(listId) {
|
||||
return $('#' + listId + ' option').map(function() {
|
||||
return String($(this).val() || '').trim().toUpperCase();
|
||||
}).get().filter(function(v) {
|
||||
return v !== '';
|
||||
});
|
||||
}
|
||||
|
||||
function validateAdvancedReferBy() {
|
||||
var searchBy = String($('#adv_search_by').val() || '');
|
||||
if (searchBy !== 'refer_by') {
|
||||
return true;
|
||||
}
|
||||
|
||||
var typedValue = String($('#adv_search_value').val() || '').trim();
|
||||
if (typedValue === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
var allowedValues = getDatalistValues('adv_refer_by_list');
|
||||
var typedUpper = typedValue.toUpperCase();
|
||||
var isValid = allowedValues.indexOf(typedUpper) !== -1;
|
||||
|
||||
if (!isValid) {
|
||||
if (typeof notify === 'function') {
|
||||
notify('warning', 'Please Attention', 'Refer By must be selected from Refer Master list.');
|
||||
}
|
||||
$('#adv_search_value').focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function validateReferByAgainstMaster(inputSelector, listId) {
|
||||
var typedValue = String($(inputSelector).val() || '').trim();
|
||||
if (typedValue === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
var allowedValues = getDatalistValues(listId);
|
||||
var typedUpper = typedValue.toUpperCase();
|
||||
var isValid = allowedValues.indexOf(typedUpper) !== -1;
|
||||
|
||||
if (!isValid) {
|
||||
if (typeof notify === 'function') {
|
||||
notify('warning', 'Please Attention', 'Refer By must be selected from Refer Master list.');
|
||||
}
|
||||
$(inputSelector).focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$('#toggle_advanced_search').on('click', function() {
|
||||
$('#advanced_search_panel').stop(true, true).slideToggle(120);
|
||||
});
|
||||
|
||||
$('#adv_search_by').on('change', syncAdvancedSearchFields);
|
||||
$('#adv_age_mode').on('change', syncAdvancedSearchFields);
|
||||
$('#adv_age_value,#adv_age_tolerance').on('input change', syncAgeApproxHint);
|
||||
$('#adv_search_value').on('blur', function() {
|
||||
validateAdvancedReferBy();
|
||||
});
|
||||
$('#refer_by_name').on('blur', function() {
|
||||
validateReferByAgainstMaster('#refer_by_name', 'refer_by_name_list');
|
||||
});
|
||||
syncAdvancedSearchFields();
|
||||
|
||||
$('form.form1').on('submit', function(form) {
|
||||
if (!validateReferByAgainstMaster('#refer_by_name', 'refer_by_name_list')) {
|
||||
form.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
$("#RegisterPatient").prop("disabled", true);
|
||||
form.preventDefault();
|
||||
|
||||
|
|
@ -805,24 +1008,44 @@
|
|||
}, 'json');
|
||||
});
|
||||
|
||||
function runPatientSearch(payload, triggerButton, buttonText) {
|
||||
var loadingDiv = $('#search_loading');
|
||||
var originalText = buttonText || triggerButton.text();
|
||||
|
||||
loadingDiv.show();
|
||||
triggerButton.prop('disabled', true);
|
||||
triggerButton.text('Searching...');
|
||||
|
||||
$.post('<?= base_url('billing/patient/search') ?>', payload, function(data) {
|
||||
$('div.searchresult').html(data);
|
||||
loadingDiv.hide();
|
||||
triggerButton.prop('disabled', false);
|
||||
triggerButton.text(originalText);
|
||||
}).fail(function() {
|
||||
loadingDiv.hide();
|
||||
triggerButton.prop('disabled', false);
|
||||
triggerButton.text(originalText);
|
||||
});
|
||||
}
|
||||
|
||||
$('form.form2').on('submit', function(form) {
|
||||
form.preventDefault();
|
||||
var searchBtn = $('form.form2 button[type="submit"]');
|
||||
var loadingDiv = $('#search_loading');
|
||||
|
||||
// Show loading indicator and disable button
|
||||
loadingDiv.show();
|
||||
searchBtn.prop('disabled', true);
|
||||
searchBtn.text('Searching...');
|
||||
|
||||
$.post('<?= base_url('billing/patient/search') ?>', $('form.form2').serialize(), function(data) {
|
||||
$('div.searchresult').html(data);
|
||||
|
||||
// Hide loading indicator and re-enable button
|
||||
loadingDiv.hide();
|
||||
searchBtn.prop('disabled', false);
|
||||
searchBtn.text('Go!');
|
||||
|
||||
// Classic Go search should keep old behavior: only txtsearch + csrf.
|
||||
var basicFields = $('form.form2').serializeArray().filter(function(field) {
|
||||
return field.name.indexOf('adv_') !== 0;
|
||||
});
|
||||
|
||||
var searchBtn = $('form.form2 button[type="submit"]');
|
||||
runPatientSearch($.param(basicFields), searchBtn, 'Go!');
|
||||
});
|
||||
|
||||
$('#btn_advanced_search').on('click', function() {
|
||||
if (!validateAdvancedReferBy()) {
|
||||
return;
|
||||
}
|
||||
var advancedBtn = $(this);
|
||||
runPatientSearch($('form.form2').serialize(), advancedBtn, 'Search Advanced');
|
||||
});
|
||||
|
||||
/* ===================== ABHA CREATION WIZARD ===================== */
|
||||
|
|
|
|||
|
|
@ -243,6 +243,7 @@
|
|||
<label>Refer By</label>
|
||||
<input class="form-control input-sm" name="refer_by_name" id="refer_by_name"
|
||||
list="refer_by_name_list" placeholder="Type to search referral"
|
||||
data-original-value="<?= esc($data[0]->referby ?? '') ?>"
|
||||
value="<?= esc($data[0]->referby ?? '') ?>" autocomplete="off">
|
||||
<datalist id="refer_by_name_list">
|
||||
<?php foreach (($refer_master ?? []) as $row) : ?>
|
||||
|
|
@ -374,11 +375,55 @@ function onchange_title() {
|
|||
}
|
||||
}
|
||||
|
||||
function getReferMasterValues() {
|
||||
return $('#refer_by_name_list option').map(function() {
|
||||
return String($(this).val() || '').trim().toUpperCase();
|
||||
}).get().filter(function(v) {
|
||||
return v !== '';
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeReferValue(v) {
|
||||
return String(v || '').trim().replace(/\s+/g, ' ').toUpperCase();
|
||||
}
|
||||
|
||||
function validateEditReferBy() {
|
||||
var typedValue = String($('#refer_by_name').val() || '').trim();
|
||||
if (typedValue === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
var originalValue = String($('#refer_by_name').attr('data-original-value') || '').trim();
|
||||
if (normalizeReferValue(originalValue) !== '' && normalizeReferValue(originalValue) === normalizeReferValue(typedValue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var allowedValues = getReferMasterValues();
|
||||
var isValid = allowedValues.indexOf(normalizeReferValue(typedValue)) !== -1;
|
||||
if (!isValid) {
|
||||
if (typeof notify === 'function') {
|
||||
notify('warning', 'Please Attention', 'Refer By must be selected from Refer Master list.');
|
||||
}
|
||||
$('#refer_by_name').focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
document.title = 'Pt.:<?=$data[0]->p_fname ?>/<?=$data[0]->id ?>';
|
||||
|
||||
$('#refer_by_name').on('blur', function() {
|
||||
validateEditReferBy();
|
||||
});
|
||||
|
||||
$('#btn_update').click(function() {
|
||||
if (!validateEditReferBy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.post('<?= base_url('billing/patient/update') ?>', $('form.form1').serialize(), function(data) {
|
||||
var resp = data;
|
||||
if (typeof data === 'string') {
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ if (! empty($search_result) && is_array($search_result)) {
|
|||
<?php
|
||||
$profileUrl = base_url('billing/patient/person_record') . '/' . (int) ($row->id ?? 0);
|
||||
$fullName = trim((string) ($row->p_fname ?? ''));
|
||||
$referBy = trim((string) ($row->referby ?? ''));
|
||||
$relative = trim((string) ($row->p_relative ?? '')) . ' ' . trim((string) ($row->p_rname ?? ''));
|
||||
$relative = trim($relative);
|
||||
$addressParts = array_filter([
|
||||
|
|
@ -164,6 +165,9 @@ if (! empty($search_result) && is_array($search_result)) {
|
|||
View Profile
|
||||
</button>
|
||||
</div>
|
||||
<?php if ($referBy !== '') : ?>
|
||||
<div class="small fst-italic text-muted">Refer By: <?= esc(ucwords(strtolower($referBy))) ?></div>
|
||||
<?php endif; ?>
|
||||
<div class="small text-muted"><?= esc($relative !== '' ? $relative : 'Relative: NA') ?></div>
|
||||
<?php if (! empty($matchReasons)) : ?>
|
||||
<div class="mt-1">
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user