updating billing
This commit is contained in:
parent
43f495d39b
commit
01f56287c6
274
api/billing.php
Normal file
274
api/billing.php
Normal file
@ -0,0 +1,274 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
$db = db();
|
||||
|
||||
$action = $_POST['action'] ?? $_GET['action'] ?? '';
|
||||
|
||||
if ($action === 'get_bill_details') {
|
||||
$visit_id = $_GET['visit_id'] ?? 0;
|
||||
$bill_id_param = $_GET['bill_id'] ?? 0;
|
||||
|
||||
if (!$visit_id && !$bill_id_param) {
|
||||
echo json_encode(['success' => false, 'error' => 'Visit ID or Bill ID required']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($bill_id_param) {
|
||||
$stmt = $db->prepare("SELECT visit_id FROM bills WHERE id = ?");
|
||||
$stmt->execute([$bill_id_param]);
|
||||
$visit_id = $stmt->fetchColumn();
|
||||
|
||||
if (!$visit_id) {
|
||||
echo json_encode(['success' => false, 'error' => 'Bill not found']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Get Visit & Patient Info
|
||||
$stmt = $db->prepare("
|
||||
SELECT v.*, p.name as patient_name, p.insurance_company_id, ic.name_en as insurance_name
|
||||
FROM visits v
|
||||
JOIN patients p ON v.patient_id = p.id
|
||||
LEFT JOIN insurance_companies ic ON p.insurance_company_id = ic.id
|
||||
WHERE v.id = ?
|
||||
");
|
||||
$stmt->execute([$visit_id]);
|
||||
$visit = $stmt->fetch();
|
||||
|
||||
if (!$visit) {
|
||||
echo json_encode(['success' => false, 'error' => 'Visit not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 2. Get or Create Bill
|
||||
$stmt = $db->prepare("SELECT * FROM bills WHERE visit_id = ?");
|
||||
$stmt->execute([$visit_id]);
|
||||
$bill = $stmt->fetch();
|
||||
|
||||
if (!$bill) {
|
||||
$stmt = $db->prepare("INSERT INTO bills (patient_id, visit_id, status, created_at) VALUES (?, ?, 'Pending', NOW())");
|
||||
$stmt->execute([$visit['patient_id'], $visit_id]);
|
||||
$bill_id = $db->lastInsertId();
|
||||
|
||||
// Re-fetch
|
||||
$stmt = $db->prepare("SELECT * FROM bills WHERE id = ?");
|
||||
$stmt->execute([$bill_id]);
|
||||
$bill = $stmt->fetch();
|
||||
} else {
|
||||
$bill_id = $bill['id'];
|
||||
}
|
||||
|
||||
// --- AUTO-ADD ITEMS FROM OTHER DEPARTMENTS ---
|
||||
// Fetch existing items to prevent duplicates
|
||||
$stmt = $db->prepare("SELECT description FROM bill_items WHERE bill_id = ?");
|
||||
$stmt->execute([$bill_id]);
|
||||
$existing_items = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
// Helper to check and add
|
||||
$added_count = 0;
|
||||
|
||||
// 1. X-Rays
|
||||
$stmt = $db->prepare("
|
||||
SELECT xt.name_en, xt.price
|
||||
FROM xray_inquiry_items xii
|
||||
JOIN xray_inquiries xi ON xii.inquiry_id = xi.id
|
||||
JOIN xray_tests xt ON xii.xray_id = xt.id
|
||||
WHERE xi.visit_id = ?
|
||||
");
|
||||
$stmt->execute([$visit_id]);
|
||||
$xrays = $stmt->fetchAll();
|
||||
|
||||
foreach ($xrays as $x) {
|
||||
$desc = "X-Ray: " . $x['name_en'];
|
||||
if (!in_array($desc, $existing_items)) {
|
||||
$stmt = $db->prepare("INSERT INTO bill_items (bill_id, description, amount) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$bill_id, $desc, $x['price']]);
|
||||
$existing_items[] = $desc; // Update local cache
|
||||
$added_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Labs
|
||||
$stmt = $db->prepare("
|
||||
SELECT lt.name_en, lt.price
|
||||
FROM inquiry_tests it
|
||||
JOIN laboratory_inquiries li ON it.inquiry_id = li.id
|
||||
JOIN laboratory_tests lt ON it.test_id = lt.id
|
||||
WHERE li.visit_id = ?
|
||||
");
|
||||
$stmt->execute([$visit_id]);
|
||||
$labs = $stmt->fetchAll();
|
||||
|
||||
foreach ($labs as $l) {
|
||||
$desc = "Lab: " . $l['name_en'];
|
||||
if (!in_array($desc, $existing_items)) {
|
||||
$stmt = $db->prepare("INSERT INTO bill_items (bill_id, description, amount) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$bill_id, $desc, $l['price']]);
|
||||
$existing_items[] = $desc;
|
||||
$added_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Drugs (Prescriptions)
|
||||
$stmt = $db->prepare("
|
||||
SELECT vp.drug_name, d.price
|
||||
FROM visit_prescriptions vp
|
||||
LEFT JOIN drugs d ON (vp.drug_name = d.name_en OR vp.drug_name = d.name_ar)
|
||||
WHERE vp.visit_id = ?
|
||||
");
|
||||
$stmt->execute([$visit_id]);
|
||||
$drugs = $stmt->fetchAll();
|
||||
|
||||
foreach ($drugs as $d) {
|
||||
$price = $d['price'] ?: 0; // Default to 0 if not found
|
||||
$desc = "Pharmacy: " . $d['drug_name'];
|
||||
if (!in_array($desc, $existing_items)) {
|
||||
$stmt = $db->prepare("INSERT INTO bill_items (bill_id, description, amount) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$bill_id, $desc, $price]);
|
||||
$existing_items[] = $desc;
|
||||
$added_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// If items were added, update the bill total
|
||||
if ($added_count > 0) {
|
||||
updateBillTotal($db, $bill_id);
|
||||
// Re-fetch bill to get updated total if needed (though calculate total below handles it)
|
||||
$stmt = $db->prepare("SELECT * FROM bills WHERE id = ?");
|
||||
$stmt->execute([$bill_id]);
|
||||
$bill = $stmt->fetch();
|
||||
}
|
||||
// ---------------------------------------------
|
||||
|
||||
// 3. Get Bill Items (Fresh)
|
||||
$stmt = $db->prepare("SELECT * FROM bill_items WHERE bill_id = ?");
|
||||
$stmt->execute([$bill['id']]);
|
||||
$items = $stmt->fetchAll();
|
||||
|
||||
// 4. Calculate Totals (if not synced)
|
||||
$total = 0;
|
||||
foreach ($items as $item) {
|
||||
$total += $item['amount'];
|
||||
}
|
||||
|
||||
// Return Data
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'visit' => $visit,
|
||||
'bill' => $bill,
|
||||
'items' => $items,
|
||||
'calculated_total' => $total,
|
||||
'has_insurance' => !empty($visit['insurance_company_id']),
|
||||
'insurance_name' => $visit['insurance_name']
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
elseif ($action === 'add_item') {
|
||||
$bill_id = $_POST['bill_id'] ?? 0;
|
||||
$description = $_POST['description'] ?? '';
|
||||
$amount = $_POST['amount'] ?? 0;
|
||||
|
||||
if (!$bill_id || !$description || !$amount) {
|
||||
echo json_encode(['success' => false, 'error' => 'Missing fields']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO bill_items (bill_id, description, amount) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$bill_id, $description, $amount]);
|
||||
|
||||
// Update Bill Total
|
||||
updateBillTotal($db, $bill_id);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
elseif ($action === 'remove_item') {
|
||||
$item_id = $_POST['item_id'] ?? 0;
|
||||
|
||||
if (!$item_id) {
|
||||
echo json_encode(['success' => false, 'error' => 'Item ID required']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
// Get bill_id first
|
||||
$stmt = $db->prepare("SELECT bill_id FROM bill_items WHERE id = ?");
|
||||
$stmt->execute([$item_id]);
|
||||
$row = $stmt->fetch();
|
||||
|
||||
if ($row) {
|
||||
$stmt = $db->prepare("DELETE FROM bill_items WHERE id = ?");
|
||||
$stmt->execute([$item_id]);
|
||||
updateBillTotal($db, $row['bill_id']);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
elseif ($action === 'update_totals') {
|
||||
$bill_id = $_POST['bill_id'] ?? 0;
|
||||
$insurance_covered = $_POST['insurance_covered'] ?? 0;
|
||||
$patient_payable = $_POST['patient_payable'] ?? 0;
|
||||
|
||||
try {
|
||||
$stmt = $db->prepare("UPDATE bills SET insurance_covered = ?, patient_payable = ? WHERE id = ?");
|
||||
$stmt->execute([$insurance_covered, $patient_payable, $bill_id]);
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
elseif ($action === 'complete_payment') {
|
||||
$bill_id = $_POST['bill_id'] ?? 0;
|
||||
$payment_method = $_POST['payment_method'] ?? 'Cash';
|
||||
$notes = $_POST['notes'] ?? '';
|
||||
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
|
||||
// Update Bill
|
||||
$stmt = $db->prepare("UPDATE bills SET status = 'Paid', payment_method = ?, notes = ? WHERE id = ?");
|
||||
$stmt->execute([$payment_method, $notes, $bill_id]);
|
||||
|
||||
// Get Visit ID
|
||||
$stmt = $db->prepare("SELECT visit_id FROM bills WHERE id = ?");
|
||||
$stmt->execute([$bill_id]);
|
||||
$bill = $stmt->fetch();
|
||||
|
||||
// Update Visit
|
||||
if ($bill && $bill['visit_id']) {
|
||||
$stmt = $db->prepare("UPDATE visits SET status = 'Completed', checkout_time = NOW() WHERE id = ?");
|
||||
$stmt->execute([$bill['visit_id']]);
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
$db->rollBack();
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
function updateBillTotal($db, $bill_id) {
|
||||
$stmt = $db->prepare("SELECT SUM(amount) FROM bill_items WHERE bill_id = ?");
|
||||
$stmt->execute([$bill_id]);
|
||||
$total = $stmt->fetchColumn() ?: 0;
|
||||
|
||||
$stmt = $db->prepare("UPDATE bills SET total_amount = ? WHERE id = ?");
|
||||
$stmt->execute([$total, $bill_id]);
|
||||
}
|
||||
16
check_details_schema.php
Normal file
16
check_details_schema.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
$db = db();
|
||||
|
||||
$tables = ['xray_inquiry_items', 'inquiry_tests', 'visit_prescriptions', 'laboratory_inquiries'];
|
||||
|
||||
foreach ($tables as $table) {
|
||||
try {
|
||||
$stmt = $db->query("SHOW CREATE TABLE $table");
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
echo $row['Create Table'] . "\n\n";
|
||||
} catch (Exception $e) {
|
||||
echo "Table $table not found: " . $e->getMessage() . "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
16
check_prices_schema.php
Normal file
16
check_prices_schema.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
$db = db();
|
||||
|
||||
$tables = ['laboratory_tests', 'xray_tests', 'drugs'];
|
||||
|
||||
foreach ($tables as $table) {
|
||||
try {
|
||||
$stmt = $db->query("SHOW CREATE TABLE $table");
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
echo $row['Create Table'] . "\n\n";
|
||||
} catch (Exception $e) {
|
||||
echo "Table $table not found or error: " . $e->getMessage() . "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
16
check_schema_billing.php
Normal file
16
check_schema_billing.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
$db = db();
|
||||
|
||||
$tables = ['services', 'xray_inquiries', 'laboratory_inquiries', 'visit_prescriptions', 'bill_items'];
|
||||
|
||||
foreach ($tables as $table) {
|
||||
try {
|
||||
$stmt = $db->query("SHOW CREATE TABLE $table");
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
echo $row['Create Table'] . "\n\n";
|
||||
} catch (Exception $e) {
|
||||
echo "Table $table not found or error: " . $e->getMessage() . "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,13 @@ $lang = $_SESSION['lang'];
|
||||
|
||||
require_once __DIR__ . '/includes/actions.php';
|
||||
require_once __DIR__ . '/includes/common_data.php';
|
||||
require_once __DIR__ . '/includes/layout/header.php';
|
||||
|
||||
if (!isset($_GET['ajax_search'])) {
|
||||
require_once __DIR__ . '/includes/layout/header.php';
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/includes/pages/dashboard.php';
|
||||
require_once __DIR__ . '/includes/layout/footer.php';
|
||||
|
||||
if (!isset($_GET['ajax_search'])) {
|
||||
require_once __DIR__ . '/includes/layout/footer.php';
|
||||
}
|
||||
3
db/migrations/20260321_add_billing_columns.sql
Normal file
3
db/migrations/20260321_add_billing_columns.sql
Normal file
@ -0,0 +1,3 @@
|
||||
ALTER TABLE bills ADD COLUMN IF NOT EXISTS insurance_covered DECIMAL(10,2) DEFAULT 0.00;
|
||||
ALTER TABLE bills ADD COLUMN IF NOT EXISTS patient_payable DECIMAL(10,2) DEFAULT 0.00;
|
||||
ALTER TABLE bills ADD COLUMN IF NOT EXISTS total_amount DECIMAL(10,2) DEFAULT 0.00;
|
||||
2
db/migrations/20260321_add_payment_method_to_bills.sql
Normal file
2
db/migrations/20260321_add_payment_method_to_bills.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE bills ADD COLUMN IF NOT EXISTS payment_method ENUM('Cash', 'Card', 'Insurance', 'Online', 'Other') DEFAULT 'Cash';
|
||||
ALTER TABLE bills ADD COLUMN IF NOT EXISTS notes TEXT;
|
||||
2
db/migrations/20260321_add_status_to_visits.sql
Normal file
2
db/migrations/20260321_add_status_to_visits.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE visits ADD COLUMN IF NOT EXISTS status ENUM('CheckIn', 'In Progress', 'Completed', 'Cancelled') DEFAULT 'CheckIn';
|
||||
ALTER TABLE visits ADD COLUMN IF NOT EXISTS checkout_time DATETIME NULL;
|
||||
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
// Common data for selects
|
||||
$all_doctors = $db->query("SELECT id, name_$lang as name FROM doctors")->fetchAll();
|
||||
$all_patients = $db->query("SELECT id, name, dob, gender FROM patients")->fetchAll();
|
||||
$all_patients = $db->query("SELECT id, name, phone, civil_id, dob, gender FROM patients")->fetchAll();
|
||||
$all_nurses = $db->query("SELECT id, name_$lang as name FROM nurses")->fetchAll();
|
||||
$all_departments = $db->query("SELECT id, name_$lang as name FROM departments")->fetchAll();
|
||||
$all_employees = $db->query("SELECT id, name_$lang as name FROM employees")->fetchAll();
|
||||
@ -9,13 +9,14 @@ $all_positions = $db->query("SELECT id, name_$lang as name FROM positions")->fet
|
||||
$all_insurance = $db->query("SELECT id, name_$lang as name FROM insurance_companies")->fetchAll();
|
||||
$all_test_groups = $db->query("SELECT id, name_$lang as name FROM test_groups")->fetchAll();
|
||||
$all_tests = $db->query("SELECT id, name_$lang as name, price, normal_range FROM laboratory_tests")->fetchAll();
|
||||
$all_services = $db->query("SELECT id, name_$lang as name, price FROM services WHERE is_active = 1")->fetchAll();
|
||||
|
||||
// X-Ray Data
|
||||
$all_xray_groups = $db->query("SELECT id, name_$lang as name FROM xray_groups")->fetchAll();
|
||||
$all_xrays = $db->query("SELECT id, name_$lang as name, price FROM xray_tests")->fetchAll();
|
||||
|
||||
// Drugs Data
|
||||
$all_drugs = $db->query("SELECT id, name_$lang as name, default_dosage, default_instructions FROM drugs")->fetchAll();
|
||||
$all_drugs = $db->query("SELECT id, name_$lang as name, default_dosage, default_instructions, price FROM drugs")->fetchAll();
|
||||
|
||||
$scheduled_appointments = $db->query("
|
||||
SELECT a.id, p.name as patient_name, a.start_time, a.patient_id, a.doctor_id
|
||||
|
||||
@ -1,13 +1,85 @@
|
||||
<?php
|
||||
// --- AJAX HANDLER FOR PATIENT SEARCH ---
|
||||
if (isset($_GET['ajax_search'])) {
|
||||
$search_query = $_GET['search'] ?? '';
|
||||
$limit = 10;
|
||||
|
||||
$where = "WHERE 1=1";
|
||||
$params = [];
|
||||
if ($search_query) {
|
||||
$where .= " AND (p.name LIKE ? OR p.phone LIKE ? OR p.civil_id LIKE ?)";
|
||||
$params[] = "%$search_query%";
|
||||
$params[] = "%$search_query%";
|
||||
$params[] = "%$search_query%";
|
||||
}
|
||||
|
||||
$query = "
|
||||
SELECT p.*, ic.name_$lang as insurance_name
|
||||
FROM patients p
|
||||
LEFT JOIN insurance_companies ic ON p.insurance_company_id = ic.id
|
||||
$where
|
||||
ORDER BY p.id DESC
|
||||
LIMIT $limit";
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute($params);
|
||||
$patients = $stmt->fetchAll();
|
||||
|
||||
ob_start();
|
||||
if (empty($patients)): ?>
|
||||
<tr><td colspan="5" class="text-center py-3 text-muted"><?php echo __('no_patients_found'); ?></td></tr>
|
||||
<?php else:
|
||||
foreach ($patients as $p): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="fw-bold"><?php echo htmlspecialchars($p['name']); ?></div>
|
||||
<small class="text-muted"><?php echo $p['dob']; ?> (<?php echo calculate_age($p['dob']); ?>)</small>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($p['phone']); ?></td>
|
||||
<td>
|
||||
<span class="badge <?php echo $p['insurance_name'] ? 'bg-primary' : 'bg-secondary'; ?>">
|
||||
<?php echo $p['insurance_name'] ?: __('not_insured'); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-outline-info" onclick="showReceptionistVisitModal(<?php echo $p['id']; ?>)" title="<?php echo __('add_visit'); ?>">
|
||||
<i class="bi bi-clipboard-plus"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="showEditPatientModal(<?php echo htmlspecialchars(json_encode($p, JSON_UNESCAPED_UNICODE)); ?>)" title="<?php echo __('edit'); ?>">
|
||||
<i class="bi bi-pencil-square"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach;
|
||||
endif;
|
||||
$html = ob_get_clean();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['html' => $html]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fetch Stats
|
||||
$total_patients = $db->query("SELECT COUNT(*) FROM patients")->fetchColumn();
|
||||
$today_appointments = $db->query("SELECT COUNT(*) FROM appointments WHERE DATE(start_time) = CURDATE()")->fetchColumn();
|
||||
$today_appointments_count = $db->query("SELECT COUNT(*) FROM appointments WHERE DATE(start_time) = CURDATE()")->fetchColumn();
|
||||
$total_visits = $db->query("SELECT COUNT(*) FROM visits")->fetchColumn();
|
||||
$total_revenue = $db->query("SELECT SUM(total_amount) FROM bills WHERE status = 'Paid'")->fetchColumn() ?: 0;
|
||||
$pending_revenue = $db->query("SELECT SUM(total_amount) FROM bills WHERE status = 'Pending'")->fetchColumn() ?: 0;
|
||||
$total_xrays = $db->query("SELECT COUNT(*) FROM xray_inquiries")->fetchColumn();
|
||||
$total_labs = $db->query("SELECT COUNT(*) FROM laboratory_inquiries")->fetchColumn();
|
||||
|
||||
// Fetch Running Visits (Not Completed/Cancelled)
|
||||
$running_visits_sql = "
|
||||
SELECT v.*, p.name as patient_name, d.name_$lang as doctor_name, n.name_$lang as nurse_name
|
||||
FROM visits v
|
||||
JOIN patients p ON v.patient_id = p.id
|
||||
LEFT JOIN doctors d ON v.doctor_id = d.id
|
||||
LEFT JOIN nurses n ON v.nurse_id = n.id
|
||||
WHERE v.status IN ('CheckIn', 'In Progress')
|
||||
ORDER BY v.visit_date ASC";
|
||||
$running_visits = $db->query($running_visits_sql)->fetchAll();
|
||||
|
||||
// Initial Patients Load
|
||||
$patients_sql = "
|
||||
SELECT p.*, ic.name_$lang as insurance_name
|
||||
FROM patients p
|
||||
@ -15,13 +87,15 @@ $patients_sql = "
|
||||
ORDER BY p.id DESC LIMIT 5";
|
||||
$patients = $db->query($patients_sql)->fetchAll();
|
||||
|
||||
// Today's Appointments
|
||||
$appointments_sql = "
|
||||
SELECT a.*, p.name as patient_name, d.name_$lang as doctor_name
|
||||
SELECT a.*, p.name as patient_name, d.name_$lang as doctor_name, n.name_$lang as nurse_name
|
||||
FROM appointments a
|
||||
JOIN patients p ON a.patient_id = p.id
|
||||
JOIN doctors d ON a.doctor_id = d.id
|
||||
ORDER BY a.start_time DESC
|
||||
LIMIT 5";
|
||||
LEFT JOIN doctors d ON a.doctor_id = d.id
|
||||
LEFT JOIN nurses n ON a.nurse_id = n.id
|
||||
WHERE DATE(a.start_time) = CURDATE()
|
||||
ORDER BY a.start_time ASC";
|
||||
$appointments = $db->query($appointments_sql)->fetchAll();
|
||||
?>
|
||||
|
||||
@ -37,7 +111,7 @@ $appointments = $db->query($appointments_sql)->fetchAll();
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card stat-card h-100">
|
||||
<i class="bi bi-calendar-check"></i>
|
||||
<h3><?php echo $today_appointments; ?></h3>
|
||||
<h3><?php echo $today_appointments_count; ?></h3>
|
||||
<p class="text-muted mb-0"><?php echo __('today_appointments'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
@ -86,7 +160,7 @@ $appointments = $db->query($appointments_sql)->fetchAll();
|
||||
<button class="btn btn-success btn-sm" data-bs-toggle="modal" data-bs-target="#bookAppointmentModal">
|
||||
<i class="bi bi-calendar-plus"></i> <?php echo __('book_appointment'); ?>
|
||||
</button>
|
||||
<button class="btn btn-info btn-sm text-white" data-bs-toggle="modal" data-bs-target="#recordVisitModal">
|
||||
<button class="btn btn-info btn-sm text-white" onclick="showReceptionistVisitModal()">
|
||||
<i class="bi bi-clipboard-plus"></i> <?php echo __('add_visit'); ?>
|
||||
</button>
|
||||
<button class="btn btn-warning btn-sm text-white" data-bs-toggle="modal" data-bs-target="#addXrayInquiryModal">
|
||||
@ -97,31 +171,153 @@ $appointments = $db->query($appointments_sql)->fetchAll();
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tables Section -->
|
||||
<div class="row">
|
||||
<div class="col-lg-6 mb-4">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="mb-0 fw-bold text-white"><i class="bi bi-people-fill me-2"></i> <?php echo __('patients'); ?></h6>
|
||||
<!-- Running Visits Table -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center bg-primary text-white">
|
||||
<h6 class="mb-0 fw-bold"><i class="bi bi-play-circle-fill me-2"></i> <?php echo __('running_visits'); ?></h6>
|
||||
<span class="badge bg-white text-primary"><?php echo count($running_visits); ?></span>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th><?php echo __('name'); ?></th>
|
||||
<th><?php echo __('age'); ?></th>
|
||||
<th><?php echo __('phone'); ?></th>
|
||||
<th><?php echo __('insurance'); ?></th>
|
||||
<th><?php echo __('time'); ?></th>
|
||||
<th><?php echo __('patient'); ?></th>
|
||||
<th><?php echo __('doctor'); ?></th>
|
||||
<th><?php echo __('status'); ?></th>
|
||||
<th class="text-end"><?php echo __('actions'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($running_visits as $v): ?>
|
||||
<tr>
|
||||
<td><?php echo date('Y-m-d H:i', strtotime($v['visit_date'])); ?></td>
|
||||
<td>
|
||||
<div class="fw-bold"><?php echo htmlspecialchars($v['patient_name']); ?></div>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
if ($v['doctor_name']) echo htmlspecialchars($v['doctor_name']);
|
||||
elseif ($v['nurse_name']) echo htmlspecialchars($v['nurse_name']) . ' (' . __('nurse') . ')';
|
||||
else echo '-';
|
||||
?>
|
||||
</td>
|
||||
<td><span class="badge bg-warning text-dark"><?php echo __($v['status']); ?></span></td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-outline-success" onclick="showCheckoutModal(<?php echo $v['id']; ?>)" title="<?php echo __('checkout_payment'); ?>">
|
||||
<i class="bi bi-cash-stack"></i> <?php echo __('checkout_payment'); ?>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; if (empty($running_visits)): ?>
|
||||
<tr><td colspan="5" class="text-center py-4 text-muted"><?php echo __('no_running_visits'); ?></td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<div class="row">
|
||||
<!-- Today's Appointments -->
|
||||
<div class="col-lg-6 mb-4">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0 fw-bold text-white"><i class="bi bi-calendar-event-fill me-2"></i> <?php echo __('today_appointments'); ?></h6>
|
||||
<span class="badge bg-white text-primary"><?php echo count($appointments); ?></span>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead class="table-light sticky-top">
|
||||
<tr>
|
||||
<th><?php echo __('time'); ?></th>
|
||||
<th><?php echo __('patient'); ?></th>
|
||||
<th><?php echo __('doctor'); ?></th>
|
||||
<th><?php echo __('status'); ?></th>
|
||||
<th class="text-end"><?php echo __('actions'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($appointments as $a): ?>
|
||||
<tr>
|
||||
<td><?php echo date('H:i', strtotime($a['start_time'])); ?></td>
|
||||
<td>
|
||||
<div class="fw-bold"><?php echo htmlspecialchars($a['patient_name']); ?></div>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
if ($a['doctor_name']) echo htmlspecialchars($a['doctor_name']);
|
||||
elseif ($a['nurse_name']) echo htmlspecialchars($a['nurse_name']) . ' (' . __('nurse') . ')';
|
||||
else echo '-';
|
||||
?>
|
||||
</td>
|
||||
<td><span class="badge <?php echo $a['status'] === 'Completed' ? 'bg-success' : 'bg-secondary'; ?>"><?php echo __($a['status']); ?></span></td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-outline-info" onclick="showReceptionistVisitModal(<?php echo $a['patient_id']; ?>)" title="<?php echo __('add_visit'); ?>">
|
||||
<i class="bi bi-clipboard-plus"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick='showDashboardEditModal(<?php echo json_encode($a, JSON_HEX_APOS | JSON_HEX_QUOT); ?>)' title="<?php echo __('edit'); ?>">
|
||||
<i class="bi bi-pencil-square"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; if (empty($appointments)): ?>
|
||||
<tr><td colspan="5" class="text-center py-4 text-muted"><?php echo __('no_appointments_today'); ?></td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Patient List / Search -->
|
||||
<div class="col-lg-6 mb-4">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header py-2">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0 fw-bold text-white"><i class="bi bi-people-fill me-2"></i> <?php echo __('patients'); ?></h6>
|
||||
<div class="input-group input-group-sm" style="width: 200px;">
|
||||
<input type="text" id="dashboardPatientSearch" class="form-control" placeholder="<?php echo __('search'); ?>...">
|
||||
<button class="btn btn-light" type="button"><i class="bi bi-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<thead class="table-light sticky-top">
|
||||
<tr>
|
||||
<th><?php echo __('patient'); ?></th>
|
||||
<th><?php echo __('phone'); ?></th>
|
||||
<th><?php echo __('insurance'); ?></th>
|
||||
<th class="text-end"><?php echo __('actions'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dashboardPatientsTable">
|
||||
<?php foreach ($patients as $p): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($p['name']); ?></td>
|
||||
<td><?php echo calculate_age($p['dob']); ?></td>
|
||||
<td>
|
||||
<div class="fw-bold"><?php echo htmlspecialchars($p['name']); ?></div>
|
||||
<small class="text-muted"><?php echo $p['dob']; ?> (<?php echo calculate_age($p['dob']); ?>)</small>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($p['phone']); ?></td>
|
||||
<td><span class="badge <?php echo $p['insurance_name'] ? 'bg-primary' : 'bg-secondary'; ?>"><?php echo $p['insurance_name'] ?: __('not_insured'); ?></span></td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-outline-info" onclick="showReceptionistVisitModal(<?php echo $p['id']; ?>)" title="<?php echo __('add_visit'); ?>">
|
||||
<i class="bi bi-clipboard-plus"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="showEditPatientModal(<?php echo htmlspecialchars(json_encode($p, JSON_UNESCAPED_UNICODE)); ?>)" title="<?php echo __('edit'); ?>">
|
||||
<i class="bi bi-pencil-square"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; if (empty($patients)): ?>
|
||||
<tr><td colspan="4" class="text-center py-4 text-muted">No patients found.</td></tr>
|
||||
@ -132,37 +328,572 @@ $appointments = $db->query($appointments_sql)->fetchAll();
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 mb-4">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="mb-0 fw-bold text-white"><i class="bi bi-calendar-event-fill me-2"></i> <?php echo __('appointments'); ?></h6>
|
||||
</div>
|
||||
|
||||
<!-- Dashboard Appointment Edit Modal -->
|
||||
<div class="modal fade" id="dashboardAppointmentModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content border-0 shadow">
|
||||
<div class="modal-header border-0 pb-0">
|
||||
<h5 class="modal-title fw-bold text-secondary" id="dashModalTitle">Edit Appointment</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body pt-3">
|
||||
<input type="hidden" id="dash_apt_id">
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-muted"><?php echo __('patient'); ?></label>
|
||||
<select id="dash_apt_patient_id" class="form-select select2-modal-dash">
|
||||
<?php foreach ($all_patients as $p): ?>
|
||||
<option value="<?php echo $p['id']; ?>" data-address="<?php echo htmlspecialchars($p['address'] ?? ''); ?>"><?php echo htmlspecialchars($p['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small text-muted">Visit Type</label>
|
||||
<select id="dash_apt_visit_type" class="form-select" onchange="dashboard_toggleAddressField()">
|
||||
<option value="Clinic">Clinic Visit</option>
|
||||
<option value="Home">Home Visit</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small text-muted">Provider Type</label>
|
||||
<select id="dash_apt_provider_type" class="form-select" onchange="dashboard_toggleProviderField()">
|
||||
<option value="Doctor">Doctor</option>
|
||||
<option value="Nurse">Nurse</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 d-none" id="dash_div_address">
|
||||
<label class="form-label small text-muted">Address (For Home Visit)</label>
|
||||
<textarea id="dash_apt_address" class="form-control" rows="2" placeholder="Enter patient address..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" id="dash_div_doctor">
|
||||
<label class="form-label small text-muted"><?php echo __('doctor'); ?></label>
|
||||
<select id="dash_apt_doctor_id" class="form-select select2-modal-dash">
|
||||
<option value="">Select Doctor</option>
|
||||
<?php foreach ($all_doctors as $d): ?>
|
||||
<option value="<?php echo $d['id']; ?>"><?php echo htmlspecialchars($d['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 d-none" id="dash_div_nurse">
|
||||
<label class="form-label small text-muted"><?php echo __('nurse'); ?></label>
|
||||
<select id="dash_apt_nurse_id" class="form-select select2-modal-dash">
|
||||
<option value="">Select Nurse</option>
|
||||
<?php foreach ($all_nurses as $n): ?>
|
||||
<option value="<?php echo $n['id']; ?>"><?php echo htmlspecialchars($n['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label small text-muted"><?php echo __('date'); ?> & <?php echo __('time'); ?></label>
|
||||
<input type="datetime-local" id="dash_apt_start_time" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label small text-muted"><?php echo __('status'); ?></label>
|
||||
<select id="dash_apt_status" class="form-select">
|
||||
<option value="Scheduled">Scheduled</option>
|
||||
<option value="Completed">Completed</option>
|
||||
<option value="Cancelled">Cancelled</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-muted"><?php echo __('reason'); ?></label>
|
||||
<textarea id="dash_apt_reason" class="form-control" rows="2"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer border-0 bg-light rounded-bottom">
|
||||
<div class="d-flex justify-content-between w-100">
|
||||
<button type="button" class="btn btn-outline-danger shadow-sm" onclick="dashboard_deleteAppointment()">
|
||||
<i class="bi bi-trash"></i> <?php echo __('delete'); ?>
|
||||
</button>
|
||||
<div>
|
||||
<button type="button" class="btn btn-secondary shadow-sm me-2" data-bs-dismiss="modal"><?php echo __('cancel'); ?></button>
|
||||
<button type="button" class="btn btn-primary shadow-sm px-4" onclick="dashboard_saveAppointment()">
|
||||
<i class="bi bi-check2-circle me-1"></i> <?php echo __('save'); ?>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php echo __('patient'); ?></th>
|
||||
<th><?php echo __('doctor'); ?></th>
|
||||
<th><?php echo __('date'); ?></th>
|
||||
<th><?php echo __('status'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($appointments as $a): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($a['patient_name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($a['doctor_name']); ?></td>
|
||||
<td><?php echo date('M d, H:i', strtotime($a['start_time'])); ?></td>
|
||||
<td><span class="badge <?php echo $a['status'] === 'Completed' ? 'bg-success' : 'bg-secondary'; ?>"><?php echo __($a['status']); ?></span></td>
|
||||
</tr>
|
||||
<?php endforeach; if (empty($appointments)): ?>
|
||||
<tr><td colspan="4" class="text-center py-4 text-muted">No appointments found.</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Receptionist Quick Visit Modal -->
|
||||
<div class="modal fade" id="receptionistVisitModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<form action="dashboard.php" method="POST">
|
||||
<input type="hidden" name="action" value="record_visit">
|
||||
<div class="modal-content border-0 shadow">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title fw-bold text-secondary"><?php echo __('new_visit'); ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?php echo __('patient'); ?></label>
|
||||
<select name="patient_id" id="quick_visit_patient_id" class="form-select select2-modal-quick" required>
|
||||
<option value=""><?php echo __('select'); ?>...</option>
|
||||
<?php foreach ($all_patients as $p): ?>
|
||||
<option value="<?php echo $p['id']; ?>">
|
||||
<?php echo htmlspecialchars($p['name']) . ($p['phone'] ? ' - ' . htmlspecialchars($p['phone']) : '') . ($p['civil_id'] ? ' (' . htmlspecialchars($p['civil_id']) . ')' : ''); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?php echo __('doctor'); ?></label>
|
||||
<select name="doctor_id" id="quick_visit_doctor_id" class="form-select select2-modal-quick" required>
|
||||
<option value=""><?php echo __('select'); ?>...</option>
|
||||
<?php foreach ($all_doctors as $d): ?>
|
||||
<option value="<?php echo $d['id']; ?>"><?php echo htmlspecialchars($d['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="quick_visit_token" name="generate_token" value="1" checked>
|
||||
<label class="form-check-label" for="quick_visit_token"><?php echo __('issue_token'); ?></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer bg-light">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo __('cancel'); ?></button>
|
||||
<button type="submit" class="btn btn-primary px-4"><?php echo __('save'); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Checkout/Billing Modal -->
|
||||
<div class="modal fade" id="checkoutModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content border-0 shadow">
|
||||
<div class="modal-header border-0 pb-0">
|
||||
<h5 class="modal-title fw-bold text-secondary"><?php echo __('checkout_payment'); ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body pt-3">
|
||||
<input type="hidden" id="checkout_bill_id">
|
||||
|
||||
<div class="alert alert-light border d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<div class="small text-muted"><?php echo __('patient'); ?></div>
|
||||
<div class="fw-bold" id="checkout_patient_name">-</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<div class="small text-muted"><?php echo __('insurance'); ?></div>
|
||||
<div class="fw-bold text-primary" id="checkout_insurance_name">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Items -->
|
||||
<div class="card bg-light border-0 mb-3">
|
||||
<div class="card-body p-2">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<select id="checkout_service_select" class="form-select form-select-sm select2-modal-checkout">
|
||||
<option value=""><?php echo __('select_service'); ?>...</option>
|
||||
<?php foreach ($all_services as $s): ?>
|
||||
<option value="<?php echo $s['name']; ?>" data-price="<?php echo $s['price']; ?>"><?php echo htmlspecialchars($s['name']); ?> ($<?php echo $s['price']; ?>)</option>
|
||||
<?php endforeach; ?>
|
||||
<?php foreach ($all_tests as $t): ?>
|
||||
<option value="<?php echo $t['name']; ?>" data-price="<?php echo $t['price']; ?>"><?php echo htmlspecialchars($t['name']); ?> ($<?php echo $t['price']; ?>)</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<input type="text" id="checkout_custom_item" class="form-control form-control-sm" placeholder="Custom Item Description">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<input type="number" id="checkout_item_price" class="form-control form-control-sm" placeholder="Price">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button class="btn btn-primary btn-sm w-100" onclick="addBillItem()"><i class="bi bi-plus-lg"></i> Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Items Table -->
|
||||
<div class="table-responsive mb-3">
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<th class="text-end" width="100">Amount</th>
|
||||
<th width="50"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="checkout_items_table">
|
||||
<!-- Items go here -->
|
||||
</tbody>
|
||||
<tfoot class="table-light fw-bold">
|
||||
<tr>
|
||||
<td class="text-end">Total</td>
|
||||
<td class="text-end" id="checkout_total_amount">$0.00</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Payment Details -->
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small text-muted">Insurance Coverage %</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="number" id="checkout_insurance_percent" class="form-control" value="0" min="0" max="100" onchange="calculateTotals()">
|
||||
<span class="input-group-text">%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small text-muted">Insurance Pays</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" id="checkout_insurance_amount" class="form-control" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small text-muted fw-bold text-dark">Patient Pays</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" id="checkout_patient_amount" class="form-control fw-bold" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small text-muted">Payment Method</label>
|
||||
<select id="checkout_payment_method" class="form-select">
|
||||
<option value="Cash">Cash</option>
|
||||
<option value="Card">Card</option>
|
||||
<option value="Online">Online</option>
|
||||
<option value="Insurance">Insurance Only</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small text-muted">Notes</label>
|
||||
<input type="text" id="checkout_notes" class="form-control" placeholder="Optional notes...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer border-0 bg-light rounded-bottom">
|
||||
<button type="button" class="btn btn-outline-dark me-auto" onclick="printBill()"><i class="bi bi-printer"></i> <?php echo __('print_bill'); ?></button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo __('close'); ?></button>
|
||||
<button type="button" class="btn btn-success px-4" onclick="completePayment()">
|
||||
<i class="bi bi-check2-circle me-1"></i> Pay & Close Visit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const searchInput = document.getElementById('dashboardPatientSearch');
|
||||
const tableBody = document.getElementById('dashboardPatientsTable');
|
||||
let timeout = null;
|
||||
|
||||
searchInput.addEventListener('input', function() {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(function() {
|
||||
const query = searchInput.value;
|
||||
fetch('dashboard.php?ajax_search=1&search=' + encodeURIComponent(query))
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
tableBody.innerHTML = data.html;
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}, 300);
|
||||
});
|
||||
|
||||
// Initialize Select2
|
||||
$('#dashboardAppointmentModal').on('shown.bs.modal', function() {
|
||||
$('.select2-modal-dash').each(function() {
|
||||
$(this).select2({ dropdownParent: $('#dashboardAppointmentModal'), theme: 'bootstrap-5', width: '100%' });
|
||||
});
|
||||
});
|
||||
|
||||
$('#receptionistVisitModal').on('shown.bs.modal', function() {
|
||||
$('.select2-modal-quick').each(function() {
|
||||
$(this).select2({ dropdownParent: $('#receptionistVisitModal'), theme: 'bootstrap-5', width: '100%' });
|
||||
});
|
||||
});
|
||||
|
||||
$('#checkoutModal').on('shown.bs.modal', function() {
|
||||
$('.select2-modal-checkout').each(function() {
|
||||
$(this).select2({ dropdownParent: $('#checkoutModal'), theme: 'bootstrap-5', width: '100%' });
|
||||
|
||||
// Helper for filling inputs on selection
|
||||
$(this).on('select2:select', function(e) {
|
||||
var data = e.params.data;
|
||||
// Since select2 data doesn't keep data attributes easily without extra work,
|
||||
// we'll access the underlying option
|
||||
var element = $(this).find('option:selected');
|
||||
var price = element.data('price');
|
||||
var text = element.text();
|
||||
|
||||
if (price) {
|
||||
document.getElementById('checkout_custom_item').value = text.split(' ($')[0];
|
||||
document.getElementById('checkout_item_price').value = price;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Auto-fill address
|
||||
$('#dash_apt_patient_id').on('change', function() {
|
||||
if (document.getElementById('dash_apt_visit_type').value === 'Home') {
|
||||
var addressField = document.getElementById('dash_apt_address');
|
||||
if (!addressField.value) {
|
||||
var selectedOption = this.options[this.selectedIndex];
|
||||
if (selectedOption && selectedOption.dataset.address) {
|
||||
addressField.value = selectedOption.dataset.address;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ... existing functions ...
|
||||
|
||||
function dashboard_toggleAddressField() {
|
||||
var type = document.getElementById('dash_apt_visit_type').value;
|
||||
var div = document.getElementById('dash_div_address');
|
||||
if (type === 'Home') div.classList.remove('d-none'); else div.classList.add('d-none');
|
||||
}
|
||||
|
||||
function dashboard_toggleProviderField() {
|
||||
var type = document.getElementById('dash_apt_provider_type').value;
|
||||
var divDoc = document.getElementById('dash_div_doctor');
|
||||
var divNurse = document.getElementById('dash_div_nurse');
|
||||
if (type === 'Nurse') { divDoc.classList.add('d-none'); divNurse.classList.remove('d-none'); }
|
||||
else { divDoc.classList.remove('d-none'); divNurse.classList.add('d-none'); }
|
||||
}
|
||||
|
||||
function showDashboardEditModal(data) {
|
||||
document.getElementById('dash_apt_id').value = data.id;
|
||||
$('#dash_apt_patient_id').val(data.patient_id).trigger('change');
|
||||
document.getElementById('dash_apt_visit_type').value = data.visit_type || 'Clinic';
|
||||
document.getElementById('dash_apt_address').value = data.address || '';
|
||||
dashboard_toggleAddressField();
|
||||
|
||||
var providerType = data.nurse_id ? 'Nurse' : 'Doctor';
|
||||
document.getElementById('dash_apt_provider_type').value = providerType;
|
||||
dashboard_toggleProviderField();
|
||||
|
||||
if (providerType === 'Doctor') $('#dash_apt_doctor_id').val(data.doctor_id).trigger('change');
|
||||
else $('#dash_apt_nurse_id').val(data.nurse_id).trigger('change');
|
||||
|
||||
var start = new Date(data.start_time);
|
||||
if (!isNaN(start)) {
|
||||
var offset = start.getTimezoneOffset() * 60000;
|
||||
document.getElementById('dash_apt_start_time').value = (new Date(start.getTime() - offset)).toISOString().slice(0, 16);
|
||||
} else {
|
||||
document.getElementById('dash_apt_start_time').value = data.start_time.replace(' ', 'T').slice(0, 16);
|
||||
}
|
||||
|
||||
document.getElementById('dash_apt_status').value = data.status;
|
||||
document.getElementById('dash_apt_reason').value = data.reason || '';
|
||||
new bootstrap.Modal(document.getElementById('dashboardAppointmentModal')).show();
|
||||
}
|
||||
|
||||
function showReceptionistVisitModal(patientId) {
|
||||
$('#quick_visit_patient_id').val(patientId || '').trigger('change');
|
||||
$('#quick_visit_doctor_id').val('').trigger('change');
|
||||
new bootstrap.Modal(document.getElementById('receptionistVisitModal')).show();
|
||||
}
|
||||
|
||||
function dashboard_saveAppointment() {
|
||||
var id = document.getElementById('dash_apt_id').value;
|
||||
var providerType = document.getElementById('dash_apt_provider_type').value;
|
||||
var data = {
|
||||
action: 'update',
|
||||
id: id,
|
||||
patient_id: document.getElementById('dash_apt_patient_id').value,
|
||||
doctor_id: providerType === 'Doctor' ? document.getElementById('dash_apt_doctor_id').value : null,
|
||||
nurse_id: providerType === 'Nurse' ? document.getElementById('dash_apt_nurse_id').value : null,
|
||||
visit_type: document.getElementById('dash_apt_visit_type').value,
|
||||
address: document.getElementById('dash_apt_address').value,
|
||||
start_time: document.getElementById('dash_apt_start_time').value,
|
||||
status: document.getElementById('dash_apt_status').value,
|
||||
reason: document.getElementById('dash_apt_reason').value
|
||||
};
|
||||
|
||||
fetch('api/appointments.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) })
|
||||
.then(r => r.json()).then(res => { if(res.success) window.location.reload(); else alert(res.error); });
|
||||
}
|
||||
|
||||
function dashboard_deleteAppointment() {
|
||||
var id = document.getElementById('dash_apt_id').value;
|
||||
if (id && confirm('Delete?')) {
|
||||
fetch('api/appointments.php', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({action:'delete', id:id}) })
|
||||
.then(r => r.json()).then(res => { if(res.success) window.location.reload(); else alert(res.error); });
|
||||
}
|
||||
}
|
||||
|
||||
// --- BILLING FUNCTIONS ---
|
||||
let currentBillId = 0;
|
||||
let currentTotal = 0;
|
||||
|
||||
function fetchBillDetails(id, type = 'visit') {
|
||||
const param = type === 'visit' ? `visit_id=${id}` : `bill_id=${id}`;
|
||||
return fetch(`api/billing.php?action=get_bill_details&${param}`)
|
||||
.then(r => r.json());
|
||||
}
|
||||
|
||||
function showCheckoutModal(visitId) {
|
||||
fetchBillDetails(visitId, 'visit')
|
||||
.then(data => {
|
||||
if (!data.success) { alert(data.error); return; }
|
||||
|
||||
updateCheckoutModalUI(data);
|
||||
new bootstrap.Modal(document.getElementById('checkoutModal')).show();
|
||||
});
|
||||
}
|
||||
|
||||
function updateCheckoutModalUI(data) {
|
||||
currentBillId = data.bill.id;
|
||||
document.getElementById('checkout_bill_id').value = currentBillId;
|
||||
document.getElementById('checkout_patient_name').innerText = data.visit.patient_name;
|
||||
document.getElementById('checkout_insurance_name').innerText = data.insurance_name || 'None';
|
||||
|
||||
// Auto-set generic insurance percent if insured (e.g., 80% coverage)
|
||||
// Only set if value is 0 (initial load) to avoid overwriting user input on refresh
|
||||
if (data.has_insurance && data.bill.status === 'Pending' && parseFloat(data.bill.insurance_covered) === 0) {
|
||||
// Do not force overwrite if user might have changed it, but for now kept simple
|
||||
// document.getElementById('checkout_insurance_percent').value = 0;
|
||||
}
|
||||
|
||||
renderBillItems(data.items);
|
||||
}
|
||||
|
||||
function renderBillItems(items) {
|
||||
const tbody = document.getElementById('checkout_items_table');
|
||||
tbody.innerHTML = '';
|
||||
let total = 0;
|
||||
|
||||
items.forEach(item => {
|
||||
total += parseFloat(item.amount);
|
||||
tbody.innerHTML += `
|
||||
<tr>
|
||||
<td>${item.description}</td>
|
||||
<td class="text-end">$${parseFloat(item.amount).toFixed(2)}</td>
|
||||
<td class="text-center">
|
||||
<button class="btn btn-sm btn-link text-danger p-0" onclick="removeBillItem(${item.id})"><i class="bi bi-x-lg"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
currentTotal = total;
|
||||
document.getElementById('checkout_total_amount').innerText = '$' + total.toFixed(2);
|
||||
calculateTotals();
|
||||
}
|
||||
|
||||
function calculateTotals() {
|
||||
const percent = parseFloat(document.getElementById('checkout_insurance_percent').value) || 0;
|
||||
const insuranceAmount = (currentTotal * percent) / 100;
|
||||
const patientAmount = currentTotal - insuranceAmount;
|
||||
|
||||
document.getElementById('checkout_insurance_amount').value = insuranceAmount.toFixed(2);
|
||||
document.getElementById('checkout_patient_amount').value = patientAmount.toFixed(2);
|
||||
}
|
||||
|
||||
function addBillItem() {
|
||||
const desc = document.getElementById('checkout_custom_item').value;
|
||||
const price = document.getElementById('checkout_item_price').value;
|
||||
|
||||
if (!desc || !price) { alert('Please enter description and price'); return; }
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'add_item');
|
||||
formData.append('bill_id', currentBillId);
|
||||
formData.append('description', desc);
|
||||
formData.append('amount', price);
|
||||
|
||||
fetch('api/billing.php', { method: 'POST', body: formData })
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
refreshBillItems();
|
||||
// Clear inputs
|
||||
document.getElementById('checkout_custom_item').value = '';
|
||||
document.getElementById('checkout_item_price').value = '';
|
||||
$('#checkout_service_select').val('').trigger('change'); // Reset Select2
|
||||
} else { alert(data.error); }
|
||||
});
|
||||
}
|
||||
|
||||
function removeBillItem(itemId) {
|
||||
if (!confirm('Remove item?')) return;
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'remove_item');
|
||||
formData.append('item_id', itemId);
|
||||
|
||||
fetch('api/billing.php', { method: 'POST', body: formData })
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) refreshBillItems();
|
||||
else alert(data.error);
|
||||
});
|
||||
}
|
||||
|
||||
function refreshBillItems() {
|
||||
fetchBillDetails(currentBillId, 'bill')
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
updateCheckoutModalUI(data);
|
||||
} else {
|
||||
alert(data.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function completePayment() {
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'complete_payment');
|
||||
formData.append('bill_id', currentBillId);
|
||||
formData.append('payment_method', document.getElementById('checkout_payment_method').value);
|
||||
formData.append('notes', document.getElementById('checkout_notes').value);
|
||||
|
||||
const insurance = document.getElementById('checkout_insurance_amount').value;
|
||||
const patient = document.getElementById('checkout_patient_amount').value;
|
||||
|
||||
const updateData = new FormData();
|
||||
updateData.append('action', 'update_totals');
|
||||
updateData.append('bill_id', currentBillId);
|
||||
updateData.append('insurance_covered', insurance);
|
||||
updateData.append('patient_payable', patient);
|
||||
|
||||
fetch('api/billing.php', { method: 'POST', body: updateData })
|
||||
.then(() => {
|
||||
return fetch('api/billing.php', { method: 'POST', body: formData });
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert('Payment Recorded!');
|
||||
window.location.reload();
|
||||
} else { alert(data.error); }
|
||||
});
|
||||
}
|
||||
|
||||
function printBill() {
|
||||
if (currentBillId) {
|
||||
window.open('print_bill.php?bill_id=' + currentBillId, '_blank');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
10
lang.php
10
lang.php
@ -320,6 +320,10 @@ $translations = [
|
||||
'select_provider_type' => 'Select Provider Type',
|
||||
'select_nurse' => 'Select Nurse',
|
||||
'holiday' => 'Holiday',
|
||||
'checkout_payment' => 'Checkout Payment',
|
||||
'checkout' => 'Checkout',
|
||||
'print_bill' => 'Print Bill',
|
||||
'invoice' => 'Invoice',
|
||||
],
|
||||
'ar' => [
|
||||
'attachment' => 'المرفق',
|
||||
@ -583,7 +587,7 @@ $translations = [
|
||||
'civil_id' => 'الرقم المدني',
|
||||
'nationality' => 'الجنسية',
|
||||
'city' => 'المدينة',
|
||||
'services' => 'الخدمات',
|
||||
'services' => 'Services',
|
||||
'add_service' => 'إضافة خدمة',
|
||||
'edit_service' => 'تعديل خدمة',
|
||||
'delete_service' => 'حذف خدمة',
|
||||
@ -643,5 +647,9 @@ $translations = [
|
||||
'select_provider_type' => 'اختر نوع مقدم الخدمة',
|
||||
'select_nurse' => 'اختر الممرضة',
|
||||
'holiday' => 'إجازة',
|
||||
'checkout_payment' => 'الدفع والمغادرة',
|
||||
'checkout' => 'الدفع',
|
||||
'print_bill' => 'طباعة الفاتورة',
|
||||
'invoice' => 'فاتورة',
|
||||
]
|
||||
];
|
||||
7
list_tables.php
Normal file
7
list_tables.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
$db = db();
|
||||
$stmt = $db->query("SHOW TABLES");
|
||||
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
echo implode("\n", $tables);
|
||||
|
||||
204
print_bill.php
Normal file
204
print_bill.php
Normal file
@ -0,0 +1,204 @@
|
||||
<?php
|
||||
require 'db/config.php';
|
||||
require 'helpers.php';
|
||||
|
||||
// Enable error reporting for debugging
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$db = db();
|
||||
$bill_id = $_GET['bill_id'] ?? 0;
|
||||
|
||||
if (!$bill_id) {
|
||||
throw new Exception("Invalid Bill ID");
|
||||
}
|
||||
|
||||
// Fetch Bill Details
|
||||
$stmt = $db->prepare("SELECT * FROM bills WHERE id = ?");
|
||||
$stmt->execute([$bill_id]);
|
||||
$bill = $stmt->fetch();
|
||||
|
||||
if (!$bill) {
|
||||
throw new Exception("Bill not found");
|
||||
}
|
||||
|
||||
// Fetch Visit and Patient Details
|
||||
$stmt = $db->prepare("
|
||||
SELECT
|
||||
v.*,
|
||||
p.name as patient_name,
|
||||
p.phone as patient_phone,
|
||||
p.civil_id,
|
||||
d.name_en as doctor_name_en,
|
||||
d.name_ar as doctor_name_ar
|
||||
FROM visits v
|
||||
JOIN patients p ON v.patient_id = p.id
|
||||
LEFT JOIN doctors d ON v.doctor_id = d.id
|
||||
WHERE v.id = ?
|
||||
");
|
||||
$stmt->execute([$bill['visit_id']]);
|
||||
$visit = $stmt->fetch();
|
||||
|
||||
// Fetch Bill Items
|
||||
$stmt = $db->prepare("SELECT * FROM bill_items WHERE bill_id = ?");
|
||||
$stmt->execute([$bill_id]);
|
||||
$items = $stmt->fetchAll();
|
||||
|
||||
// Fetch Company Settings (Logo, Address, etc.)
|
||||
$stmt = $db->query("SELECT * FROM settings WHERE id = 1");
|
||||
$settings = $stmt->fetch();
|
||||
|
||||
$lang = $_SESSION['lang'] ?? 'en';
|
||||
} catch (Exception $e) {
|
||||
die("Error: " . $e->getMessage());
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<?php echo $lang; ?>" dir="<?php echo $lang == 'ar' ? 'rtl' : 'ltr'; ?>">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Invoice #<?php echo $bill_id; ?></title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Times New Roman', Times, serif; color: #333; }
|
||||
.invoice-header { border-bottom: 2px solid #ddd; padding-bottom: 20px; margin-bottom: 30px; }
|
||||
.invoice-footer { border-top: 2px solid #ddd; padding-top: 20px; margin-top: 50px; }
|
||||
.company-logo { max-height: 80px; }
|
||||
.invoice-title { font-size: 2rem; color: #555; text-transform: uppercase; letter-spacing: 2px; }
|
||||
|
||||
.table thead th { border-bottom: 2px solid #333; background-color: #f8f9fa; }
|
||||
.table-bordered td, .table-bordered th { border-color: #dee2e6; }
|
||||
|
||||
@media print {
|
||||
.no-print { display: none !important; }
|
||||
body { padding: 20px; background: white; }
|
||||
.card { border: none !important; box-shadow: none !important; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="window.print()">
|
||||
|
||||
<div class="container my-5">
|
||||
<div class="no-print mb-4 text-end">
|
||||
<button onclick="window.print()" class="btn btn-primary"><i class="bi bi-printer"></i> Print</button>
|
||||
<button onclick="window.close()" class="btn btn-secondary">Close</button>
|
||||
</div>
|
||||
|
||||
<div class="card p-4">
|
||||
<!-- Header -->
|
||||
<div class="invoice-header">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-6">
|
||||
<?php if (!empty($settings['company_logo'])): ?>
|
||||
<img src="<?php echo htmlspecialchars($settings['company_logo']); ?>" alt="Logo" class="company-logo mb-2">
|
||||
<?php else: ?>
|
||||
<h2 class="fw-bold m-0"><?php echo htmlspecialchars($settings['company_name'] ?? 'Hospital Name'); ?></h2>
|
||||
<?php endif; ?>
|
||||
<div class="small text-muted">
|
||||
<?php echo htmlspecialchars($settings['company_address'] ?? '123 Medical Center St.'); ?><br>
|
||||
Phone: <?php echo htmlspecialchars($settings['company_phone'] ?? '+123 456 7890'); ?><br>
|
||||
Email: <?php echo htmlspecialchars($settings['company_email'] ?? 'info@hospital.com'); ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 text-end">
|
||||
<h1 class="invoice-title"><?php echo __('invoice'); ?></h1>
|
||||
<p class="lead mb-0">#INV-<?php echo str_pad($bill_id, 6, '0', STR_PAD_LEFT); ?></p>
|
||||
<p class="text-muted small">Date: <?php echo date('d M Y', strtotime($bill['created_at'])); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Patient & Visit Details -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-6">
|
||||
<h6 class="text-uppercase text-muted small fw-bold mb-2">Bill To:</h6>
|
||||
<h5 class="fw-bold mb-1"><?php echo htmlspecialchars($visit['patient_name']); ?></h5>
|
||||
<p class="mb-0 text-muted">
|
||||
<?php if ($visit['patient_phone']) echo 'Phone: ' . htmlspecialchars($visit['patient_phone']) . '<br>'; ?>
|
||||
<?php if ($visit['civil_id']) echo 'ID: ' . htmlspecialchars($visit['civil_id']); ?>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-6 text-end">
|
||||
<h6 class="text-uppercase text-muted small fw-bold mb-2">Visit Details:</h6>
|
||||
<p class="mb-0">
|
||||
<strong>Doctor:</strong> <?php echo htmlspecialchars($visit['doctor_name_' . $lang] ?? $visit['doctor_name_en']); ?><br>
|
||||
<strong>Visit ID:</strong> #<?php echo $visit['id']; ?><br>
|
||||
<strong>Status:</strong> <span class="badge bg-light text-dark border"><?php echo $bill['status']; ?></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Items Table -->
|
||||
<table class="table table-bordered mb-4">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center" width="50">#</th>
|
||||
<th><?php echo __('description'); ?></th>
|
||||
<th class="text-end" width="150"><?php echo __('amount'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php $i = 1; foreach ($items as $item): ?>
|
||||
<tr>
|
||||
<td class="text-center"><?php echo $i++; ?></td>
|
||||
<td><?php echo htmlspecialchars($item['description']); ?></td>
|
||||
<td class="text-end">$<?php echo number_format($item['amount'], 2); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="2" class="text-end fw-bold">Total Amount</td>
|
||||
<td class="text-end fw-bold">$<?php echo number_format($bill['total_amount'], 2); ?></td>
|
||||
</tr>
|
||||
<?php if ($bill['insurance_covered'] > 0): ?>
|
||||
<tr>
|
||||
<td colspan="2" class="text-end text-success">Insurance Covered</td>
|
||||
<td class="text-end text-success">-$<?php echo number_format($bill['insurance_covered'], 2); ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<tr>
|
||||
<td colspan="2" class="text-end fw-bold fs-5">Patient Due</td>
|
||||
<td class="text-end fw-bold fs-5">$<?php echo number_format($bill['patient_payable'], 2); ?></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<!-- Payment Info -->
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<?php if ($bill['status'] == 'Paid'): ?>
|
||||
<div class="alert alert-success d-inline-block py-2 px-3">
|
||||
<i class="bi bi-check-circle-fill me-1"></i> <strong>PAID</strong> via <?php echo htmlspecialchars($bill['payment_method']); ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-warning d-inline-block py-2 px-3">
|
||||
<i class="bi bi-clock me-1"></i> <strong>PENDING</strong>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($bill['notes'])): ?>
|
||||
<p class="text-muted small mt-2"><strong>Notes:</strong> <?php echo htmlspecialchars($bill['notes']); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="col-md-6 text-end">
|
||||
<!-- Signature or Thank you -->
|
||||
<br><br>
|
||||
<p class="fw-bold mb-0">_________________________</p>
|
||||
<p class="small text-muted">Authorized Signature</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="invoice-footer text-center small text-muted">
|
||||
<p class="mb-1">Thank you for your visit!</p>
|
||||
<p>Generated on <?php echo date('Y-m-d H:i:s'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user