fix(pharmacy): resolve CSRF, schema auto-migration, and error handling for mobile draft basket
This commit is contained in:
parent
9f6d9d3799
commit
97ca466d2f
|
|
@ -94,6 +94,14 @@ class Filters extends BaseFilters
|
||||||
'stt-proxy/*',
|
'stt-proxy/*',
|
||||||
'billing/charges',
|
'billing/charges',
|
||||||
'billing/charges/*',
|
'billing/charges/*',
|
||||||
|
'Medical/mobile_save_draft',
|
||||||
|
'Medical/mobile_save_audit',
|
||||||
|
'Medical/stock_audit_reconcile',
|
||||||
|
'Medical/mobile_*',
|
||||||
|
'medical/mobile_save_draft',
|
||||||
|
'medical/mobile_save_audit',
|
||||||
|
'medical/stock_audit_reconcile',
|
||||||
|
'medical/mobile_*',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
// 'honeypot',
|
// 'honeypot',
|
||||||
|
|
|
||||||
|
|
@ -55,18 +55,29 @@ $routes->get('Medical/invoice_print/(:num)', 'Medical::invoice_print/$1');
|
||||||
$routes->get('Medical/invoice_print/(:num)/(:num)', 'Medical::invoice_print/$1/$2');
|
$routes->get('Medical/invoice_print/(:num)/(:num)', 'Medical::invoice_print/$1/$2');
|
||||||
$routes->get('Medical/Invoice_Med_Draft', 'Medical::Invoice_Med_Draft');
|
$routes->get('Medical/Invoice_Med_Draft', 'Medical::Invoice_Med_Draft');
|
||||||
$routes->get('Medical/mobile', 'Medical::mobile');
|
$routes->get('Medical/mobile', 'Medical::mobile');
|
||||||
|
$routes->get('medical/mobile', 'Medical::mobile');
|
||||||
$routes->get('app/central-pharmacy', 'Medical::mobile');
|
$routes->get('app/central-pharmacy', 'Medical::mobile');
|
||||||
$routes->get('app/main-pharmacy', 'Medical::mobile');
|
$routes->get('app/main-pharmacy', 'Medical::mobile');
|
||||||
$routes->get('Medical/mobile_patient_search', 'Medical::mobilePatientSearch');
|
$routes->get('Medical/mobile_patient_search', 'Medical::mobilePatientSearch');
|
||||||
|
$routes->get('medical/mobile_patient_search', 'Medical::mobilePatientSearch');
|
||||||
$routes->get('Medical/mobile_patient_prescription/(:num)', 'Medical::mobilePatientPrescription/$1');
|
$routes->get('Medical/mobile_patient_prescription/(:num)', 'Medical::mobilePatientPrescription/$1');
|
||||||
|
$routes->get('medical/mobile_patient_prescription/(:num)', 'Medical::mobilePatientPrescription/$1');
|
||||||
$routes->get('Medical/mobile_medicine_search', 'Medical::mobileMedicineSearch');
|
$routes->get('Medical/mobile_medicine_search', 'Medical::mobileMedicineSearch');
|
||||||
|
$routes->get('medical/mobile_medicine_search', 'Medical::mobileMedicineSearch');
|
||||||
$routes->post('Medical/mobile_save_draft', 'Medical::mobileSaveDraft');
|
$routes->post('Medical/mobile_save_draft', 'Medical::mobileSaveDraft');
|
||||||
|
$routes->post('medical/mobile_save_draft', 'Medical::mobileSaveDraft');
|
||||||
$routes->get('Medical/mobile_recent_drafts', 'Medical::mobileRecentDrafts');
|
$routes->get('Medical/mobile_recent_drafts', 'Medical::mobileRecentDrafts');
|
||||||
|
$routes->get('medical/mobile_recent_drafts', 'Medical::mobileRecentDrafts');
|
||||||
$routes->get('Medical/mobile_audit_racks', 'Medical::mobileAuditRacks');
|
$routes->get('Medical/mobile_audit_racks', 'Medical::mobileAuditRacks');
|
||||||
|
$routes->get('medical/mobile_audit_racks', 'Medical::mobileAuditRacks');
|
||||||
$routes->get('Medical/mobile_audit_items', 'Medical::mobileAuditItems');
|
$routes->get('Medical/mobile_audit_items', 'Medical::mobileAuditItems');
|
||||||
|
$routes->get('medical/mobile_audit_items', 'Medical::mobileAuditItems');
|
||||||
$routes->post('Medical/mobile_save_audit', 'Medical::mobileSaveAudit');
|
$routes->post('Medical/mobile_save_audit', 'Medical::mobileSaveAudit');
|
||||||
|
$routes->post('medical/mobile_save_audit', 'Medical::mobileSaveAudit');
|
||||||
$routes->get('Medical/stock_audit_report', 'Medical::stockAuditReport');
|
$routes->get('Medical/stock_audit_report', 'Medical::stockAuditReport');
|
||||||
|
$routes->get('medical/stock_audit_report', 'Medical::stockAuditReport');
|
||||||
$routes->post('Medical/stock_audit_reconcile', 'Medical::stockAuditReconcile');
|
$routes->post('Medical/stock_audit_reconcile', 'Medical::stockAuditReconcile');
|
||||||
|
$routes->post('medical/stock_audit_reconcile', 'Medical::stockAuditReconcile');
|
||||||
$routes->get('Medical/list_med_inv/(:num)', 'Medical::list_med_inv/$1');
|
$routes->get('Medical/list_med_inv/(:num)', 'Medical::list_med_inv/$1');
|
||||||
$routes->get('Medical/list_med_orginv/(:num)', 'Medical::list_med_orginv/$1');
|
$routes->get('Medical/list_med_orginv/(:num)', 'Medical::list_med_orginv/$1');
|
||||||
$routes->get('Medical/list_med_orginv/(:num)/(:num)', 'Medical::list_med_orginv/$1/$2');
|
$routes->get('Medical/list_med_orginv/(:num)/(:num)', 'Medical::list_med_orginv/$1/$2');
|
||||||
|
|
|
||||||
|
|
@ -15491,6 +15491,8 @@ class Medical extends BaseController
|
||||||
|
|
||||||
public function mobile()
|
public function mobile()
|
||||||
{
|
{
|
||||||
|
$this->ensureMobileStoreColumns();
|
||||||
|
|
||||||
$hospitalSetting = [];
|
$hospitalSetting = [];
|
||||||
if ($this->db->tableExists('hospital_setting')) {
|
if ($this->db->tableExists('hospital_setting')) {
|
||||||
$hospitalSetting = $this->db->table('hospital_setting')->get()->getRowArray() ?: [];
|
$hospitalSetting = $this->db->table('hospital_setting')->get()->getRowArray() ?: [];
|
||||||
|
|
@ -15503,6 +15505,7 @@ class Medical extends BaseController
|
||||||
'hospitalSetting' => $hospitalSetting,
|
'hospitalSetting' => $hospitalSetting,
|
||||||
'sessionUser' => $sessionUser,
|
'sessionUser' => $sessionUser,
|
||||||
'userId' => $userId,
|
'userId' => $userId,
|
||||||
|
'footerVersion' => '26.117',
|
||||||
'pageTitle' => 'Central Pharmacy Mobile & Picker'
|
'pageTitle' => 'Central Pharmacy Mobile & Picker'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -15696,7 +15699,10 @@ class Medical extends BaseController
|
||||||
|
|
||||||
public function mobileSaveDraft()
|
public function mobileSaveDraft()
|
||||||
{
|
{
|
||||||
$json = $this->request->getJSON(true) ?: $this->request->getPost();
|
$this->ensureMobileStoreColumns();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$json = (method_exists($this->request, 'getJSON') ? $this->request->getJSON(true) : null) ?: $this->request->getPost();
|
||||||
if (empty($json) && !empty($this->request->getBody())) {
|
if (empty($json) && !empty($this->request->getBody())) {
|
||||||
$json = json_decode($this->request->getBody(), true) ?: [];
|
$json = json_decode($this->request->getBody(), true) ?: [];
|
||||||
}
|
}
|
||||||
|
|
@ -15807,10 +15813,25 @@ class Medical extends BaseController
|
||||||
'total_items' => count($items),
|
'total_items' => count($items),
|
||||||
'total_amount' => round($totalGross, 2)
|
'total_amount' => round($totalGross, 2)
|
||||||
]);
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
log_message('error', 'mobileSaveDraft error: ' . $e->getMessage());
|
||||||
|
return $this->response->setStatusCode(500)->setJSON([
|
||||||
|
'status' => 0,
|
||||||
|
'message' => 'Server error creating draft: ' . $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mobileRecentDrafts()
|
public function mobileRecentDrafts()
|
||||||
{
|
{
|
||||||
|
$this->ensureMobileStoreColumns();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$invFields = $this->db->getFieldNames('invoice_med_master') ?? [];
|
||||||
|
$hasDraftSource = in_array('draft_source', $invFields, true);
|
||||||
|
$hasPickerName = in_array('picker_name', $invFields, true);
|
||||||
|
$hasBedNo = in_array('bed_no', $invFields, true);
|
||||||
|
|
||||||
$builder = $this->db->table('invoice_med_master m');
|
$builder = $this->db->table('invoice_med_master m');
|
||||||
$builder->select('
|
$builder->select('
|
||||||
m.id,
|
m.id,
|
||||||
|
|
@ -15819,21 +15840,25 @@ class Medical extends BaseController
|
||||||
m.inv_name AS patient_name,
|
m.inv_name AS patient_name,
|
||||||
m.ipd_id,
|
m.ipd_id,
|
||||||
m.ipd_code,
|
m.ipd_code,
|
||||||
m.bed_no,
|
' . ($hasBedNo ? 'm.bed_no' : "'' AS bed_no") . ',
|
||||||
m.net_amount,
|
m.net_amount,
|
||||||
m.bill_final,
|
m.bill_final,
|
||||||
m.draft_source,
|
' . ($hasDraftSource ? 'm.draft_source' : "'' AS draft_source") . ',
|
||||||
m.picker_name,
|
' . ($hasPickerName ? 'm.picker_name' : "'' AS picker_name") . ',
|
||||||
m.inv_date,
|
m.inv_date,
|
||||||
m.inv_insert_datetime,
|
m.inv_insert_datetime,
|
||||||
COUNT(i.id) AS total_items
|
(SELECT COUNT(*) FROM inv_med_item i WHERE i.inv_med_id = m.id) AS total_items
|
||||||
', false);
|
', false);
|
||||||
$builder->join('inv_med_item i', 'i.inv_med_id = m.id', 'left');
|
|
||||||
$builder->groupStart();
|
$builder->groupStart();
|
||||||
|
if ($hasDraftSource) {
|
||||||
$builder->where('m.draft_source', 'mobile_picker');
|
$builder->where('m.draft_source', 'mobile_picker');
|
||||||
$builder->orWhere('m.inv_date', date('Y-m-d'));
|
$builder->orWhere('m.inv_date', date('Y-m-d'));
|
||||||
|
} else {
|
||||||
|
$builder->where('m.inv_date', date('Y-m-d'));
|
||||||
|
}
|
||||||
$builder->groupEnd();
|
$builder->groupEnd();
|
||||||
$builder->groupBy('m.id');
|
|
||||||
$builder->orderBy('m.id', 'DESC');
|
$builder->orderBy('m.id', 'DESC');
|
||||||
$builder->limit(30);
|
$builder->limit(30);
|
||||||
|
|
||||||
|
|
@ -15860,6 +15885,14 @@ class Medical extends BaseController
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->response->setJSON(['status' => 1, 'drafts' => $list]);
|
return $this->response->setJSON(['status' => 1, 'drafts' => $list]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
log_message('error', 'mobileRecentDrafts error: ' . $e->getMessage());
|
||||||
|
return $this->response->setStatusCode(500)->setJSON([
|
||||||
|
'status' => 0,
|
||||||
|
'message' => 'Error retrieving drafts: ' . $e->getMessage(),
|
||||||
|
'drafts' => []
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mobileAuditRacks()
|
public function mobileAuditRacks()
|
||||||
|
|
@ -15981,7 +16014,9 @@ class Medical extends BaseController
|
||||||
|
|
||||||
public function mobileSaveAudit()
|
public function mobileSaveAudit()
|
||||||
{
|
{
|
||||||
$json = $this->request->getJSON(true) ?: $this->request->getPost();
|
$this->ensureMobileStoreColumns();
|
||||||
|
|
||||||
|
$json = (method_exists($this->request, 'getJSON') ? $this->request->getJSON(true) : null) ?: $this->request->getPost();
|
||||||
if (empty($json) && !empty($this->request->getBody())) {
|
if (empty($json) && !empty($this->request->getBody())) {
|
||||||
$json = json_decode($this->request->getBody(), true) ?: [];
|
$json = json_decode($this->request->getBody(), true) ?: [];
|
||||||
}
|
}
|
||||||
|
|
@ -16167,4 +16202,154 @@ class Medical extends BaseController
|
||||||
|
|
||||||
return $this->response->setJSON(['status' => 1, 'message' => 'Stock inventory adjusted and audit marked as RECONCILED.']);
|
return $this->response->setJSON(['status' => 1, 'message' => 'Stock inventory adjusted and audit marked as RECONCILED.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function ensureMobileStoreColumns(): void
|
||||||
|
{
|
||||||
|
static $ensured = false;
|
||||||
|
if ($ensured) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$forge = \Config\Database::forge();
|
||||||
|
$fields = $this->db->getFieldNames('invoice_med_master') ?? [];
|
||||||
|
$missing = [];
|
||||||
|
|
||||||
|
if (!in_array('draft_source', $fields, true)) {
|
||||||
|
$missing['draft_source'] = [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 30,
|
||||||
|
'null' => true,
|
||||||
|
'default' => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (!in_array('picker_name', $fields, true)) {
|
||||||
|
$missing['picker_name'] = [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 100,
|
||||||
|
'null' => true,
|
||||||
|
'default' => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (!in_array('bed_no', $fields, true)) {
|
||||||
|
$missing['bed_no'] = [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 50,
|
||||||
|
'null' => true,
|
||||||
|
'default' => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($missing)) {
|
||||||
|
$forge->addColumn('invoice_med_master', $missing);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->db->tableExists('mst_stock_audit')) {
|
||||||
|
$forge->addField([
|
||||||
|
'audit_id' => [
|
||||||
|
'type' => 'INT',
|
||||||
|
'constraint' => 11,
|
||||||
|
'unsigned' => true,
|
||||||
|
'auto_increment' => true,
|
||||||
|
],
|
||||||
|
'store_id' => [
|
||||||
|
'type' => 'INT',
|
||||||
|
'constraint' => 11,
|
||||||
|
'default' => 1,
|
||||||
|
],
|
||||||
|
'item_id' => [
|
||||||
|
'type' => 'INT',
|
||||||
|
'constraint' => 11,
|
||||||
|
'default' => 0,
|
||||||
|
],
|
||||||
|
'batch_id' => [
|
||||||
|
'type' => 'INT',
|
||||||
|
'constraint' => 11,
|
||||||
|
'default' => 0,
|
||||||
|
],
|
||||||
|
'batch_no' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 50,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'expiry_date' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 30,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'audit_type' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 50,
|
||||||
|
'default' => 'OK',
|
||||||
|
],
|
||||||
|
'system_qty' => [
|
||||||
|
'type' => 'INT',
|
||||||
|
'constraint' => 11,
|
||||||
|
'default' => 0,
|
||||||
|
],
|
||||||
|
'physical_qty' => [
|
||||||
|
'type' => 'INT',
|
||||||
|
'constraint' => 11,
|
||||||
|
'default' => 0,
|
||||||
|
],
|
||||||
|
'variation_qty' => [
|
||||||
|
'type' => 'INT',
|
||||||
|
'constraint' => 11,
|
||||||
|
'default' => 0,
|
||||||
|
],
|
||||||
|
'rate' => [
|
||||||
|
'type' => 'DECIMAL',
|
||||||
|
'constraint' => '12,2',
|
||||||
|
'default' => 0.00,
|
||||||
|
],
|
||||||
|
'total_value' => [
|
||||||
|
'type' => 'DECIMAL',
|
||||||
|
'constraint' => '12,2',
|
||||||
|
'default' => 0.00,
|
||||||
|
],
|
||||||
|
'remarks' => [
|
||||||
|
'type' => 'TEXT',
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'rack_no' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 30,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'shelf_no' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 30,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'reconciled_status' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 30,
|
||||||
|
'default' => 'PENDING',
|
||||||
|
],
|
||||||
|
'conducted_by' => [
|
||||||
|
'type' => 'INT',
|
||||||
|
'constraint' => 11,
|
||||||
|
'default' => 0,
|
||||||
|
],
|
||||||
|
'conducted_by_name' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 100,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'created_at' => [
|
||||||
|
'type' => 'DATETIME',
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$forge->addKey('audit_id', true);
|
||||||
|
$forge->addKey(['store_id', 'item_id', 'batch_id']);
|
||||||
|
$forge->createTable('mst_stock_audit', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ensured = true;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
log_message('error', 'ensureMobileStoreColumns error: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -384,6 +384,13 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- System & License Footer -->
|
||||||
|
<footer class="text-center py-3 mt-4 mb-4 text-muted" style="font-size: 11px; border-top: 1px solid #e2e8f0;">
|
||||||
|
© 2017 - 2026 <strong><a href="https://www.e-atria.in" target="_blank" rel="noopener" class="text-decoration-none text-primary">E-Atria</a></strong>. All Rights Reserved |
|
||||||
|
<a href="<?= base_url('software-use-license.html') ?>" target="_blank" rel="noopener" class="text-decoration-none text-secondary">Software Use License</a> |
|
||||||
|
Version: <?= esc($footerVersion ?? '26.117') ?>
|
||||||
|
</footer>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Floating Bottom Bar for Basket -->
|
<!-- Floating Bottom Bar for Basket -->
|
||||||
|
|
@ -400,7 +407,7 @@
|
||||||
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="showBasketDrawer();" title="View Picked Items">
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="showBasketDrawer();" title="View Picked Items">
|
||||||
<i class="bi bi-list-check"></i> Review
|
<i class="bi bi-list-check"></i> Review
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-primary btn-sm fw-bold px-3 shadow-sm" id="btn_send_draft" onclick="sendDraftToCounter();">
|
<button type="button" class="btn btn-primary btn-sm fw-bold px-3 shadow-sm btn-send-draft-action" id="btn_send_draft" onclick="sendDraftToCounter();">
|
||||||
<i class="bi bi-send-fill me-1"></i> Send to Counter
|
<i class="bi bi-send-fill me-1"></i> Send to Counter
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -424,7 +431,7 @@
|
||||||
<div class="small text-muted">Total Estimated Amount</div>
|
<div class="small text-muted">Total Estimated Amount</div>
|
||||||
<h5 class="fw-bold text-primary mb-0" id="basket_modal_total">₹0.00</h5>
|
<h5 class="fw-bold text-primary mb-0" id="basket_modal_total">₹0.00</h5>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-primary fw-bold" onclick="sendDraftToCounter();">
|
<button type="button" class="btn btn-primary fw-bold btn-send-draft-action" id="btn_modal_confirm_draft" onclick="sendDraftToCounter();">
|
||||||
<i class="bi bi-send-fill me-1"></i> Confirm & Send Draft
|
<i class="bi bi-send-fill me-1"></i> Confirm & Send Draft
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -465,7 +472,19 @@
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// STATE & GLOBAL VARIABLES
|
// STATE & GLOBAL VARIABLES
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
var baseUrl = '<?= base_url() ?>';
|
var baseUrl = '<?= rtrim(base_url(), '/') ?>/';
|
||||||
|
var csrfHeader = '<?= csrf_header() ?>';
|
||||||
|
var csrfTokenName = '<?= csrf_token() ?>';
|
||||||
|
var csrfHash = '<?= csrf_hash() ?>';
|
||||||
|
|
||||||
|
// Global AJAX setup: inject CSRF token header & X-Requested-With
|
||||||
|
jQuery.ajaxSetup({
|
||||||
|
headers: {
|
||||||
|
'<?= csrf_header() ?>': '<?= csrf_hash() ?>',
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var selectedPatient = null;
|
var selectedPatient = null;
|
||||||
var cartItems = [];
|
var cartItems = [];
|
||||||
var html5QrCodeScanner = null;
|
var html5QrCodeScanner = null;
|
||||||
|
|
@ -809,9 +828,10 @@
|
||||||
picker_name: operatorName,
|
picker_name: operatorName,
|
||||||
items: cartItems
|
items: cartItems
|
||||||
};
|
};
|
||||||
|
payload[csrfTokenName] = csrfHash;
|
||||||
|
|
||||||
var btn = jQuery('#btn_send_draft');
|
var btns = jQuery('.btn-send-draft-action');
|
||||||
btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span> Sending...');
|
btns.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span> Sending Draft...');
|
||||||
|
|
||||||
jQuery.ajax({
|
jQuery.ajax({
|
||||||
url: baseUrl + 'Medical/mobile_save_draft',
|
url: baseUrl + 'Medical/mobile_save_draft',
|
||||||
|
|
@ -820,7 +840,10 @@
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(res) {
|
success: function(res) {
|
||||||
btn.prop('disabled', false).html('<i class="bi bi-send-fill me-1"></i> Send to Counter');
|
btns.prop('disabled', false);
|
||||||
|
jQuery('#btn_send_draft').html('<i class="bi bi-send-fill me-1"></i> Send to Counter');
|
||||||
|
jQuery('#btn_modal_confirm_draft').html('<i class="bi bi-send-fill me-1"></i> Confirm & Send Draft');
|
||||||
|
|
||||||
if (res.status === 1) {
|
if (res.status === 1) {
|
||||||
var m = bootstrap.Modal.getInstance(document.getElementById('basketModal'));
|
var m = bootstrap.Modal.getInstance(document.getElementById('basketModal'));
|
||||||
if (m) m.hide();
|
if (m) m.hide();
|
||||||
|
|
@ -837,9 +860,19 @@
|
||||||
alert(res.message || 'Failed to save draft bill.');
|
alert(res.message || 'Failed to save draft bill.');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function() {
|
error: function(xhr, status, error) {
|
||||||
btn.prop('disabled', false).html('<i class="bi bi-send-fill me-1"></i> Send to Counter');
|
btns.prop('disabled', false);
|
||||||
alert("Failed to communicate with hospital server. Please check WiFi/intranet connection.");
|
jQuery('#btn_send_draft').html('<i class="bi bi-send-fill me-1"></i> Send to Counter');
|
||||||
|
jQuery('#btn_modal_confirm_draft').html('<i class="bi bi-send-fill me-1"></i> Confirm & Send Draft');
|
||||||
|
|
||||||
|
var errDetail = "HTTP " + xhr.status + " (" + (xhr.statusText || error || 'Connection Error') + ")";
|
||||||
|
try {
|
||||||
|
var parsed = JSON.parse(xhr.responseText);
|
||||||
|
if (parsed && parsed.message) {
|
||||||
|
errDetail += "\n\nServer message: " + parsed.message;
|
||||||
|
}
|
||||||
|
} catch(e) {}
|
||||||
|
alert("Failed to communicate with hospital server.\n\n" + errDetail + "\n\nPlease check WiFi/intranet connection or contact IT support.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -996,6 +1029,7 @@
|
||||||
shelf_no: shelfNo,
|
shelf_no: shelfNo,
|
||||||
audited_by_name: operatorName
|
audited_by_name: operatorName
|
||||||
};
|
};
|
||||||
|
payload[csrfTokenName] = csrfHash;
|
||||||
|
|
||||||
jQuery.ajax({
|
jQuery.ajax({
|
||||||
url: baseUrl + 'Medical/mobile_save_audit',
|
url: baseUrl + 'Medical/mobile_save_audit',
|
||||||
|
|
@ -1014,6 +1048,10 @@
|
||||||
} else {
|
} else {
|
||||||
alert(res.message || 'Error recording audit.');
|
alert(res.message || 'Error recording audit.');
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
var errDetail = "HTTP " + xhr.status + " (" + (xhr.statusText || error || 'Connection Error') + ")";
|
||||||
|
alert("Failed to save audit count.\n\n" + errDetail);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1059,6 +1097,16 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
container.html(html);
|
container.html(html);
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
var errDetail = "HTTP " + xhr.status + " (" + (xhr.statusText || error || 'Connection Error') + ")";
|
||||||
|
container.html('<div class="text-center py-4 text-danger">' +
|
||||||
|
'<i class="bi bi-exclamation-triangle fs-3 d-block mb-1"></i>' +
|
||||||
|
'Failed to load recent drafts: ' + escapeHtml(errDetail) + '<br>' +
|
||||||
|
'<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="loadRecentDrafts();">' +
|
||||||
|
'<i class="bi bi-arrow-repeat"></i> Retry' +
|
||||||
|
'</button>' +
|
||||||
|
'</div>');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user