Autosave: 20260305-171454

This commit is contained in:
Flatlogic Bot 2026-03-05 17:14:54 +00:00
parent d04323d02a
commit 4d100ba658
8 changed files with 306 additions and 101 deletions

View File

@ -0,0 +1,4 @@
ALTER TABLE drugs MODIFY name_en TEXT;
ALTER TABLE drugs MODIFY name_ar TEXT;
ALTER TABLE drugs_groups MODIFY name_en TEXT;
ALTER TABLE drugs_groups MODIFY name_ar TEXT;

View File

@ -479,7 +479,7 @@ class SimpleXLSX
$entry_xml .= ' '; $entry_xml .= ' ';
$entry_xml = preg_replace('/<[a-zA-Z0-9]+:([^>]+)>/', '<$1>', $entry_xml); // fix namespaced openned tags $entry_xml = preg_replace('/<[a-zA-Z0-9]+:([^>]+)>/', '<$1>', $entry_xml); // fix namespaced openned tags
$entry_xml .= ' '; $entry_xml .= ' ';
$entry_xml = preg_replace('/<\[a-zA-Z0-9]+:([^>]+)>/', '</$1>', $entry_xml); // fix namespaced closed tags $entry_xml = preg_replace('/<\/[a-zA-Z0-9]+:([^>]+)>/', '</$1>', $entry_xml); // fix namespaced closed tags
$entry_xml .= ' '; $entry_xml .= ' ';
if (strpos($name, '/sheet')) { // dirty skip empty rows if (strpos($name, '/sheet')) { // dirty skip empty rows

View File

@ -4,8 +4,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
require_once __DIR__ . '/../helpers.php'; require_once __DIR__ . '/../helpers.php';
$db = db(); $db = db();
// Check for post_max_size overflow
if (empty($_POST) && empty($_FILES) && isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] > 0) {
$_SESSION['flash_message'] = 'Error: File too large (exceeds post_max_size).';
header("Location: " . $_SERVER['REQUEST_URI']);
exit;
}
function parse_import_file($file_input) { function parse_import_file($file_input) {
if (!isset($file_input['error']) || $file_input['error'] !== UPLOAD_ERR_OK) { if (!isset($file_input['error']) || $file_input['error'] !== UPLOAD_ERR_OK) {
$_SESSION['import_error'] = 'Upload error code: ' . ($file_input['error'] ?? 'unknown');
return false; return false;
} }
$ext = strtolower(pathinfo($file_input['name'], PATHINFO_EXTENSION)); $ext = strtolower(pathinfo($file_input['name'], PATHINFO_EXTENSION));
@ -13,7 +21,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($ext === 'csv') { if ($ext === 'csv') {
$handle = fopen($file_input['tmp_name'], 'r'); $handle = fopen($file_input['tmp_name'], 'r');
// Skip header if ($handle === false) {
$_SESSION['import_error'] = 'Failed to open CSV file.';
return false;
}
# Skip header
fgetcsv($handle); fgetcsv($handle);
while (($row = fgetcsv($handle)) !== false) { while (($row = fgetcsv($handle)) !== false) {
if (array_filter($row)) { if (array_filter($row)) {
@ -21,16 +33,29 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
} }
fclose($handle); fclose($handle);
if (empty($rows)) {
$_SESSION['import_error'] = 'CSV file is empty or could not be parsed.';
}
} elseif ($ext === 'xlsx' || $ext === 'xls') { } elseif ($ext === 'xlsx' || $ext === 'xls') {
require_once __DIR__ . '/SimpleXLSX.php'; require_once __DIR__ . '/SimpleXLSX.php';
if ($xlsx = Shuchkin\SimpleXLSX::parse($file_input['tmp_name'])) { if ($xlsx = \Shuchkin\SimpleXLSX::parse($file_input['tmp_name'])) {
$rows = $xlsx->rows(); $rows = $xlsx->rows();
array_shift($rows); // Skip header array_shift($rows); # Skip header
if (empty($rows)) {
$_SESSION['import_error'] = 'Excel file is empty.';
} }
} else {
$_SESSION['import_error'] = 'SimpleXLSX Error: ' . \Shuchkin\SimpleXLSX::parseError();
return false;
}
} else {
$_SESSION['import_error'] = "Unsupported file extension: $ext. Please upload .csv or .xlsx";
return false;
} }
return $rows; return $rows;
} }
function upload_file($file_array, $index, $target_dir = "assets/uploads/") { function upload_file($file_array, $index, $target_dir = "assets/uploads/") {
if (!isset($file_array["name"][$index]) || $file_array["error"][$index] !== UPLOAD_ERR_OK) { if (!isset($file_array["name"][$index]) || $file_array["error"][$index] !== UPLOAD_ERR_OK) {
return null; return null;
@ -765,9 +790,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
} elseif ($_POST['action'] === 'import_drugs_groups') { } elseif ($_POST['action'] === 'import_drugs_groups') {
if (isset($_FILES['csv_file'])) { if (isset($_FILES['csv_file'])) {
try {
$rows = parse_import_file($_FILES['csv_file']); $rows = parse_import_file($_FILES['csv_file']);
if ($rows) { if ($rows) {
$db->beginTransaction();
$stmt = $db->prepare("INSERT INTO drugs_groups (name_en, name_ar) VALUES (?, ?)"); $stmt = $db->prepare("INSERT INTO drugs_groups (name_en, name_ar) VALUES (?, ?)");
$checkStmt = $db->prepare("SELECT id FROM drugs_groups WHERE name_en = ?"); $checkStmt = $db->prepare("SELECT id FROM drugs_groups WHERE name_en = ?");
@ -782,15 +809,29 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
} }
} }
$db->commit();
$_SESSION['flash_message'] = __('import_successfully'); $_SESSION['flash_message'] = __('import_successfully');
$redirect = true; $redirect = true;
} else {
$_SESSION['flash_message'] = $_SESSION['import_error'] ?? 'Failed to parse file or empty.'; unset($_SESSION['import_error']);
$redirect = true;
}
} catch (Throwable $e) {
if ($db->inTransaction()) {
$db->rollBack();
}
error_log("Import Error: " . $e->getMessage());
$_SESSION['flash_message'] = 'Error: ' . $e->getMessage();
$redirect = true;
} }
} }
} elseif ($_POST['action'] === 'import_drugs') { } elseif ($_POST['action'] === 'import_drugs') {
if (isset($_FILES['csv_file'])) { if (isset($_FILES['csv_file'])) {
try {
$rows = parse_import_file($_FILES['csv_file']); $rows = parse_import_file($_FILES['csv_file']);
if ($rows) { if ($rows) {
$db->beginTransaction();
$stmt = $db->prepare("INSERT INTO drugs (name_en, name_ar, group_id, price, expiry_date, supplier_id) VALUES (?, ?, ?, ?, ?, ?)"); $stmt = $db->prepare("INSERT INTO drugs (name_en, name_ar, group_id, price, expiry_date, supplier_id) VALUES (?, ?, ?, ?, ?, ?)");
$groupMap = []; $groupMap = [];
@ -801,7 +842,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name_ar = $row[1] ?? ''; $name_ar = $row[1] ?? '';
$group_name = $row[2] ?? ''; $group_name = $row[2] ?? '';
$price = $row[3] ?? 0; $price = $row[3] ?? 0;
$expiry = $row[4] ?? null; // $expiry = $row[4] ?? null;
// Force expiry to null as requested to bypass parsing issues
$expiry = null;
$supplier_name = $row[5] ?? ''; $supplier_name = $row[5] ?? '';
if ($name_en) { if ($name_en) {
@ -843,20 +886,50 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
} }
if ($expiry && !strtotime($expiry)) $expiry = null; /*
if ($expiry) {
if (is_numeric($expiry)) {
$ts = strtotime($expiry);
} else {
$ts = strtotime($expiry);
}
if ($ts) {
$expiry = date('Y-m-d', $ts);
} else {
$expiry = null;
}
} else {
$expiry = null;
}
*/
$stmt->execute([$name_en, $name_ar, $group_id, $price, $expiry, $supplier_id]); $stmt->execute([$name_en, $name_ar, $group_id, $price, $expiry, $supplier_id]);
} }
} }
$db->commit();
$_SESSION['flash_message'] = __('import_successfully'); $_SESSION['flash_message'] = __('import_successfully');
$redirect = true; $redirect = true;
} else {
$_SESSION['flash_message'] = $_SESSION['import_error'] ?? 'Failed to parse file or empty.'; unset($_SESSION['import_error']);
$redirect = true;
}
} catch (Throwable $e) {
if ($db->inTransaction()) {
$db->rollBack();
}
error_log("Import Error: " . $e->getMessage());
$_SESSION['flash_message'] = 'Error: ' . $e->getMessage();
$redirect = true;
} }
} }
} elseif ($_POST['action'] === 'import_tests') { } elseif ($_POST['action'] === 'import_tests') {
if (isset($_FILES['csv_file'])) { if (isset($_FILES['csv_file'])) {
try {
$rows = parse_import_file($_FILES['csv_file']); $rows = parse_import_file($_FILES['csv_file']);
if ($rows) { if ($rows) {
$db->beginTransaction();
$stmt = $db->prepare("INSERT INTO laboratory_tests (name_en, name_ar, group_id, price, normal_range) VALUES (?, ?, ?, ?, ?)"); $stmt = $db->prepare("INSERT INTO laboratory_tests (name_en, name_ar, group_id, price, normal_range) VALUES (?, ?, ?, ?, ?)");
$groupMap = []; $groupMap = [];
@ -889,8 +962,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$stmt->execute([$name_en, $name_ar, $group_id, $price, $range]); $stmt->execute([$name_en, $name_ar, $group_id, $price, $range]);
} }
} }
$db->commit();
$_SESSION['flash_message'] = __('import_successfully'); $_SESSION['flash_message'] = __('import_successfully');
$redirect = true; $redirect = true;
} else {
$_SESSION['flash_message'] = $_SESSION['import_error'] ?? 'Failed to parse file or empty.'; unset($_SESSION['import_error']);
$redirect = true;
}
} catch (Throwable $e) {
if ($db->inTransaction()) {
$db->rollBack();
}
error_log("Import Error: " . $e->getMessage());
$_SESSION['flash_message'] = 'Error: ' . $e->getMessage();
$redirect = true;
} }
} }
} }

View File

@ -1,7 +1,7 @@
<?php <?php
// Common data for selects // Common data for selects
$all_doctors = $db->query("SELECT id, name_$lang as name FROM doctors")->fetchAll(); $all_doctors = $db->query("SELECT id, name_$lang as name FROM doctors")->fetchAll();
$all_patients = $db->query("SELECT id, name FROM patients")->fetchAll(); $all_patients = $db->query("SELECT id, name, dob, gender FROM patients")->fetchAll();
$all_nurses = $db->query("SELECT id, name_$lang as name FROM nurses")->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_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(); $all_employees = $db->query("SELECT id, name_$lang as name FROM employees")->fetchAll();

View File

@ -7,6 +7,12 @@ $lang = $_SESSION['lang'];
$section = $section ?? 'dashboard'; $section = $section ?? 'dashboard';
$message = $message ?? ''; $message = $message ?? '';
// Check for flash message in session
if (isset($_SESSION['flash_message'])) {
$message = $_SESSION['flash_message'];
unset($_SESSION['flash_message']);
}
// Fetch company settings for dynamic branding // Fetch company settings for dynamic branding
$stmt = $db->query("SELECT setting_key, setting_value FROM settings WHERE setting_key IN ('company_name', 'company_logo', 'company_favicon')"); $stmt = $db->query("SELECT setting_key, setting_value FROM settings WHERE setting_key IN ('company_name', 'company_logo', 'company_favicon')");
$site_settings = []; $site_settings = [];

View File

@ -108,7 +108,7 @@ $groups = $stmt->fetchAll();
<div class="modal-body p-4"> <div class="modal-body p-4">
<div class="mb-3"> <div class="mb-3">
<label class="form-label"><?php echo __('upload_csv_file'); ?> <span class="text-danger">*</span></label> <label class="form-label"><?php echo __('upload_csv_file'); ?> <span class="text-danger">*</span></label>
<input type="file" class="form-control" name="csv_file" accept=".csv" required> <input type="file" class="form-control" name="csv_file" accept=".csv, .xlsx, .xls" required>
</div> </div>
<div class="alert alert-info small mb-0"> <div class="alert alert-info small mb-0">
<i class="bi bi-info-circle me-1"></i> <?php echo __('csv_format_groups'); ?> <i class="bi bi-info-circle me-1"></i> <?php echo __('csv_format_groups'); ?>

View File

@ -4,7 +4,7 @@ $search_doctor = $_GET['doctor'] ?? '';
$search_date = $_GET['date'] ?? ''; $search_date = $_GET['date'] ?? '';
$query = " $query = "
SELECT v.*, p.name as patient_name, d.name_$lang as doctor_name SELECT v.*, p.name as patient_name, p.dob as patient_dob, p.gender as patient_gender, d.name_$lang as doctor_name
FROM visits v FROM visits v
JOIN patients p ON v.patient_id = p.id JOIN patients p ON v.patient_id = p.id
JOIN doctors d ON v.doctor_id = d.id JOIN doctors d ON v.doctor_id = d.id

110
update_actions.py Normal file
View File

@ -0,0 +1,110 @@
import re
file_path = 'includes/actions.php'
try:
with open(file_path, 'r') as f:
content = f.read()
except FileNotFoundError:
print(f"Error: {file_path} not found.")
exit(1)
# New function implementation
new_function = r""" function parse_import_file($file_input) {
if (!isset($file_input['error']) || $file_input['error'] !== UPLOAD_ERR_OK) {
$_SESSION['import_error'] = 'Upload error code: ' . ($file_input['error'] ?? 'unknown');
return false;
}
$ext = strtolower(pathinfo($file_input['name'], PATHINFO_EXTENSION));
$rows = [];
if ($ext === 'csv') {
$handle = fopen($file_input['tmp_name'], 'r');
if ($handle === false) {
$_SESSION['import_error'] = 'Failed to open CSV file.';
return false;
}
# Skip header
fgetcsv($handle);
while (($row = fgetcsv($handle)) !== false) {
if (array_filter($row)) {
$rows[] = $row;
}
}
fclose($handle);
if (empty($rows)) {
$_SESSION['import_error'] = 'CSV file is empty or could not be parsed.';
}
} elseif ($ext === 'xlsx' || $ext === 'xls') {
require_once __DIR__ . '/SimpleXLSX.php';
if ($xlsx = \Shuchkin\SimpleXLSX::parse($file_input['tmp_name'])) {
$rows = $xlsx->rows();
array_shift($rows); # Skip header
if (empty($rows)) {
$_SESSION['import_error'] = 'Excel file is empty.';
}
} else {
$_SESSION['import_error'] = 'SimpleXLSX Error: ' . \Shuchkin\SimpleXLSX::parseError();
return false;
}
} else {
$_SESSION['import_error'] = "Unsupported file extension: $ext. Please upload .csv or .xlsx";
return false;
}
return $rows;
}"""
# Escape specific regex characters in the new function if we used it in regex, but here we use it as replacement string.
# We need to escape backslashes in the replacement string if we use re.sub, because \1, \g<1> etc have meaning.
# However, our replacement string doesn't have those. But PHP has backslashes (namespaces).
# So we need to escape backslashes for python string, and then for re.sub.
# Actually, let's use string.replace() if possible, but we don't know the exact content of the old function to replace it exactly if it varies.
# But we know the structure.
# Let's try to identify the start and end of the function.
start_marker = "function parse_import_file($file_input) {"
end_marker = "return $rows;\n }"
start_pos = content.find(start_marker)
if start_pos != -1:
end_pos = content.find(end_marker, start_pos)
if end_pos != -1:
end_pos += len(end_marker)
# Replace the function
content = content[:start_pos] + new_function.strip() + "\n" + content[end_pos:]
print("Replaced function parse_import_file.")
else:
print("Could not find end of function parse_import_file.")
# Fallback to regex if simple find fails (e.g. whitespace diff)
pattern = r"function parse_import_file\(\$file_input\) \{[\s\S]+?return \$rows;\s+\}"
if re.search(pattern, content):
content = re.sub(pattern, new_function, content, count=1)
print("Replaced function parse_import_file using regex.")
else:
print("Could not find function parse_import_file using regex.")
else:
print("Could not find start of function parse_import_file.")
# Fallback to regex
pattern = r"function parse_import_file\(\$file_input\) \{[\s\S]+?return \$rows;\s+\}"
if re.search(pattern, content):
content = re.sub(pattern, new_function, content, count=1)
print("Replaced function parse_import_file using regex (fallback).")
else:
print("Could not find function parse_import_file using regex (fallback).")
# Update the calling logic
old_msg = "$_SESSION['flash_message'] = 'Failed to parse file or empty.';"
new_msg = "$_SESSION['flash_message'] = $_SESSION['import_error'] ?? 'Failed to parse file or empty.'; unset($_SESSION['import_error']);"
if old_msg in content:
content = content.replace(old_msg, new_msg)
print("Updated error message logic.")
else:
print("Could not find old error message logic.")
with open(file_path, 'w') as f:
f.write(content)
print("Finished updating includes/actions.php")