";
} else {
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=' . $filename);
$output = fopen('php://output', 'w');
// Add UTF-8 BOM for Excel
fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF));
}
$headers = [];
$rows = [];
if ($type === 'sales' || $type === 'purchases') {
$table = ($type === 'sales') ? 'invoices' : 'purchases';
$cust_table = ($type === 'sales') ? 'customers' : 'suppliers';
$cust_col = ($type === 'sales') ? 'customer_id' : 'supplier_id';
$where = ["1=1"];
$params = [];
if (!empty($_GET['search'])) {
$s = $_GET['search'];
$clean_id = preg_replace('/[^0-9]/', '', $s);
if ($clean_id !== '') {
$where[] = "(v.id LIKE ? OR c.name LIKE ? OR v.id = ?)";
$params[] = "%$s%";
$params[] = "%$s%";
$params[] = $clean_id;
} else {
$where[] = "(v.id LIKE ? OR c.name LIKE ?)";
$params[] = "%$s%";
$params[] = "%$s%";
}
}
if (!empty($_GET['customer_id'])) { $where[] = "v.$cust_col = ?"; $params[] = $_GET['customer_id']; }
if (!empty($_GET['start_date'])) { $where[] = "v.invoice_date >= ?"; $params[] = $_GET['start_date']; }
if (!empty($_GET['end_date'])) { $where[] = "v.invoice_date <= ?"; $params[] = $_GET['end_date']; }
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT v.id, c.name as customer_name, v.invoice_date, v.payment_type, v.status, v.total_with_vat, v.paid_amount, (v.total_with_vat - v.paid_amount) as balance
FROM $table v LEFT JOIN $cust_table c ON v.$cust_col = c.id
WHERE $whereSql ORDER BY v.id DESC");
$stmt->execute($params);
$headers = ['Invoice ID', 'Customer/Supplier', 'Date', 'Payment', 'Status', 'Total', 'Paid', 'Balance'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'customers' || $type === 'suppliers') {
$table = ($type === 'suppliers') ? 'suppliers' : 'customers';
$where = ["1=1"];
$params = [];
if (!empty($_GET['search'])) { $where[] = "(name LIKE ? OR email LIKE ? OR phone LIKE ? OR tax_id LIKE ?)"; $params[] = "%{$_GET['search']}%"; $params[] = "%{$_GET['search']}%"; $params[] = "%{$_GET['search']}%"; $params[] = "%{$_GET['search']}%"; }
if (!empty($_GET['start_date'])) { $where[] = "DATE(created_at) >= ?"; $params[] = $_GET['start_date']; }
if (!empty($_GET['end_date'])) { $where[] = "DATE(created_at) <= ?"; $params[] = $_GET['end_date']; }
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT id, name, email, phone, tax_id, balance, created_at FROM $table WHERE $whereSql ORDER BY id DESC");
$stmt->execute($params);
$headers = ['ID', 'Name', 'Email', 'Phone', 'Tax ID', 'Balance', 'Created At'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'items') {
$where = ["1=1"];
$params = [];
if (!empty($_GET['search'])) { $where[] = "(i.name_en LIKE ? OR i.name_ar LIKE ? OR i.sku LIKE ?)"; $params[] = "%{$_GET['search']}%"; $params[] = "%{$_GET['search']}%"; $params[] = "%{$_GET['search']}%"; }
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT i.sku, i.name_en, i.name_ar, c.name_en as category, i.purchase_price, i.sale_price, i.stock_quantity, i.vat_rate
FROM stock_items i LEFT JOIN stock_categories c ON i.category_id = c.id
WHERE $whereSql ORDER BY i.id DESC");
$stmt->execute($params);
$headers = ['SKU', 'Name (EN)', 'Name (AR)', 'Category', 'Purchase Price', 'Sale Price', 'Quantity', 'VAT %'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'expenses') {
$where = ["1=1"];
$params = [];
$stmt = db()->prepare("SELECT e.id, c.name_en as category, e.amount, e.expense_date, e.reference_no, e.description
FROM expenses e JOIN expense_categories c ON e.category_id = c.id
ORDER BY e.expense_date DESC");
$stmt->execute();
$headers = ['ID', 'Category', 'Amount', 'Date', 'Reference', 'Description'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'quotations') {
$stmt = db()->prepare("SELECT q.id, c.name as customer_name, q.quotation_date, q.total_with_vat, q.status
FROM quotations q JOIN customers c ON q.customer_id = c.id
ORDER BY q.id DESC");
$stmt->execute();
$headers = ['Quotation #', 'Customer', 'Date', 'Total', 'Status'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'lpos') {
$stmt = db()->prepare("SELECT q.id, s.name as supplier_name, q.lpo_date, q.total_with_vat, q.status
FROM lpos q JOIN suppliers s ON q.supplier_id = s.id
ORDER BY q.id DESC");
$stmt->execute();
$headers = ['LPO #', 'Supplier', 'Date', 'Total', 'Status'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'categories') {
$stmt = db()->query("SELECT id, name_en, name_ar FROM stock_categories ORDER BY id DESC");
$headers = ['ID', 'Name (EN)', 'Name (AR)'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'units') {
$stmt = db()->query("SELECT id, name_en, name_ar, short_name_en, short_name_ar FROM stock_units ORDER BY id DESC");
$headers = ['ID', 'Name (EN)', 'Name (AR)', 'Short (EN)', 'Short (AR)'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'sales_returns') {
$stmt = db()->query("SELECT sr.id, sr.invoice_id, c.name as customer, sr.return_date, sr.total_amount FROM sales_returns sr LEFT JOIN customers c ON sr.customer_id = c.id ORDER BY sr.id DESC");
$headers = ['Return ID', 'Invoice ID', 'Customer', 'Date', 'Amount'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'purchase_returns') {
$stmt = db()->query("SELECT pr.id, pr.purchase_id, s.name as supplier, pr.return_date, pr.total_amount FROM purchase_returns pr LEFT JOIN suppliers s ON pr.supplier_id = s.id ORDER BY pr.id DESC");
$headers = ['Return ID', 'Purchase ID', 'Supplier', 'Date', 'Amount'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'hr_employees') {
$stmt = db()->query("SELECT e.id, e.name, d.name as department, e.position, e.salary, e.status FROM hr_employees e LEFT JOIN hr_departments d ON e.department_id = d.id ORDER BY e.id DESC");
$headers = ['ID', 'Name', 'Department', 'Position', 'Salary', 'Status'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'hr_departments') {
$stmt = db()->query("SELECT id, name FROM hr_departments ORDER BY id DESC");
$headers = ['ID', 'Name'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'hr_attendance') {
$stmt = db()->query("SELECT a.attendance_date, e.name, a.status, a.clock_in, a.clock_out FROM hr_attendance a JOIN hr_employees e ON a.employee_id = e.id ORDER BY a.attendance_date DESC, e.name ASC");
$headers = ['Date', 'Employee', 'Status', 'In', 'Out'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'hr_payroll') {
$stmt = db()->query("SELECT p.payroll_month, p.payroll_year, e.name, p.basic_salary, p.bonus, p.deductions, p.net_salary, p.status FROM hr_payroll p JOIN hr_employees e ON p.employee_id = e.id ORDER BY p.payroll_year DESC, p.payroll_month DESC");
$headers = ['Month', 'Year', 'Employee', 'Salary', 'Bonus', 'Deductions', 'Net', 'Status'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'users') {
$stmt = db()->query("SELECT u.id, u.username, u.email, g.name as role, u.status FROM users u LEFT JOIN role_groups g ON u.group_id = g.id ORDER BY u.id DESC");
$headers = ['ID', 'Username', 'Email', 'Role', 'Status'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
}
if ($format === 'excel') {
echo "
";
foreach ($headers as $h) echo "
" . htmlspecialchars($h) . "
";
echo "
";
foreach ($rows as $row) {
echo "
";
foreach ($row as $val) echo "
" . htmlspecialchars((string)$val) . "
";
echo "
";
}
echo "
";
} else {
fputcsv($output, $headers);
foreach ($rows as $row) fputcsv($output, $row);
fclose($output);
}
exit;
}
// Global data for modals
$data['categories'] = db()->query("SELECT * FROM stock_categories ORDER BY name_en ASC")->fetchAll();
$data['units'] = db()->query("SELECT * FROM stock_units ORDER BY name_en ASC")->fetchAll();
$data['suppliers'] = db()->query("SELECT * FROM suppliers ORDER BY name ASC")->fetchAll();
$data['accounts'] = db()->query("SELECT * FROM acc_accounts ORDER BY code ASC")->fetchAll();
$data['customers_list'] = db()->query("SELECT * FROM customers ORDER BY name ASC")->fetchAll();
$customers = $data['customers_list']; // For backward compatibility in some modals
$settings_raw = db()->query("SELECT * FROM settings")->fetchAll();
$data['settings'] = [];
foreach ($settings_raw as $s) {
$data['settings'][$s['key']] = $s['value'];
}
$limit = isset($_GET["limit"]) ? max(5, (int)$_GET["limit"]) : 20;
$page_num = isset($_GET["p"]) ? (int)$_GET["p"] : 1;
if ($page_num < 1) $page_num = 1;
$offset = ($page_num - 1) * $limit;
switch ($page) {
case 'outlets':
$stmt = db()->prepare("SELECT * FROM outlets ORDER BY id DESC");
$stmt->execute();
$data['outlets'] = $stmt->fetchAll();
break;
case 'suppliers':
$where = ["1=1"];
$params = [];
if (!empty($_GET['search'])) {
$where[] = "(name LIKE ? OR email LIKE ? OR phone LIKE ? OR tax_id LIKE ?)";
$params[] = "%{$_GET['search']}%";
$params[] = "%{$_GET['search']}%";
$params[] = "%{$_GET['search']}%";
$params[] = "%{$_GET['search']}%";
}
if (!empty($_GET['start_date'])) {
$where[] = "DATE(created_at) >= ?";
$params[] = $_GET['start_date'];
}
if (!empty($_GET['end_date'])) {
$where[] = "DATE(created_at) <= ?";
$params[] = $_GET['end_date'];
}
$whereSql = implode(" AND ", $where);
$countStmt = db()->prepare("SELECT COUNT(*) FROM suppliers WHERE $whereSql");
$countStmt->execute($params);
$total_records = (int)$countStmt->fetchColumn();
$data['total_pages'] = ceil($total_records / $limit);
$data['current_page'] = $page_num;
$stmt = db()->prepare("SELECT * FROM suppliers WHERE $whereSql ORDER BY id DESC LIMIT $limit OFFSET $offset");
$stmt->execute($params);
$data['customers'] = $stmt->fetchAll(); // Keep 'customers' key for template compatibility if needed, or update template
break;
case 'customers':
$where = ["1=1"];
$params = [];
if (!empty($_GET['search'])) {
$where[] = "(name LIKE ? OR email LIKE ? OR phone LIKE ? OR tax_id LIKE ?)";
$params[] = "%{$_GET['search']}%";
$params[] = "%{$_GET['search']}%";
$params[] = "%{$_GET['search']}%";
$params[] = "%{$_GET['search']}%";
}
if (!empty($_GET['start_date'])) {
$where[] = "DATE(created_at) >= ?";
$params[] = $_GET['start_date'];
}
if (!empty($_GET['end_date'])) {
$where[] = "DATE(created_at) <= ?";
$params[] = $_GET['end_date'];
}
$whereSql = implode(" AND ", $where);
$countStmt = db()->prepare("SELECT COUNT(*) FROM customers WHERE $whereSql");
$countStmt->execute($params);
$total_records = (int)$countStmt->fetchColumn();
$data['total_pages'] = ceil($total_records / $limit);
$data['current_page'] = $page_num;
$stmt = db()->prepare("SELECT * FROM customers WHERE $whereSql ORDER BY id DESC LIMIT $limit OFFSET $offset");
$stmt->execute($params);
$data['customers'] = $stmt->fetchAll();
break;
case 'categories':
// Already fetched globally
break;
case 'units':
// Already fetched globally
break;
case 'items':
file_put_contents('debug.log', date('Y-m-d H:i:s') . " - Items case hit\n", FILE_APPEND);
$where = ["1=1"];
$params = [];
if (!empty($_GET['search'])) {
$where[] = "(i.name_en LIKE ? OR i.name_ar LIKE ? OR i.sku LIKE ?)";
$params[] = "%{$_GET['search']}%";
$params[] = "%{$_GET['search']}%";
$params[] = "%{$_GET['search']}%";
}
$whereSql = implode(" AND ", $where);
$countStmt = db()->prepare("SELECT COUNT(*) FROM stock_items i
LEFT JOIN stock_categories c ON i.category_id = c.id
LEFT JOIN stock_units u ON i.unit_id = u.id
LEFT JOIN suppliers s ON i.supplier_id = s.id WHERE $whereSql");
$countStmt->execute($params);
$total_records = (int)$countStmt->fetchColumn();
$data['total_pages'] = ceil($total_records / $limit);
$data['current_page'] = $page_num;
$stmt = db()->prepare("SELECT i.*, c.name_en as cat_en, c.name_ar as cat_ar, u.short_name_en as unit_en, u.short_name_ar as unit_ar, s.name as supplier_name
FROM stock_items i
LEFT JOIN stock_categories c ON i.category_id = c.id
LEFT JOIN stock_units u ON i.unit_id = u.id
LEFT JOIN suppliers s ON i.supplier_id = s.id
WHERE $whereSql
ORDER BY i.id DESC LIMIT $limit OFFSET $offset");
$stmt->execute($params);
$data['items'] = $stmt->fetchAll();
break;
case 'quotations':
$where = ["1=1"];
$params = [];
if (!empty($_GET['search'])) {
$s = $_GET['search'];
$clean_id = preg_replace('/[^0-9]/', '', $s);
if ($clean_id !== '') {
$where[] = "(q.id LIKE ? OR c.name LIKE ? OR q.id = ?)";
$params[] = "%$s%";
$params[] = "%$s%";
$params[] = $clean_id;
} else {
$where[] = "(q.id LIKE ? OR c.name LIKE ?)";
$params[] = "%$s%";
$params[] = "%$s%";
}
}
if (!empty($_GET['customer_id'])) {
$where[] = "q.customer_id = ?";
$params[] = $_GET['customer_id'];
}
if (!empty($_GET['start_date'])) {
$where[] = "q.quotation_date >= ?";
$params[] = $_GET['start_date'];
}
if (!empty($_GET['end_date'])) {
$where[] = "q.quotation_date <= ?";
$params[] = $_GET['end_date'];
}
$whereSql = implode(" AND ", $where);
$countStmt = db()->prepare("SELECT COUNT(*) FROM quotations q JOIN customers c ON q.customer_id = c.id WHERE $whereSql");
$countStmt->execute($params);
$total_records = (int)$countStmt->fetchColumn();
$data['total_pages'] = ceil($total_records / $limit);
$data['current_page'] = $page_num;
$stmt = db()->prepare("SELECT q.*, c.name as customer_name
FROM quotations q
JOIN customers c ON q.customer_id = c.id
WHERE $whereSql
ORDER BY q.id DESC
LIMIT $limit OFFSET $offset");
$stmt->execute($params);
$data['quotations'] = $stmt->fetchAll();
break;
case 'lpos':
$where = ["1=1"];
$params = [];
if (!empty($_GET['search'])) {
$s = $_GET['search'];
$clean_id = preg_replace('/[^0-9]/', '', $s);
if ($clean_id !== '') {
$where[] = "(q.id LIKE ? OR s.name LIKE ? OR q.id = ?)";
$params[] = "%$s%";
$params[] = "%$s%";
$params[] = $clean_id;
} else {
$where[] = "(q.id LIKE ? OR s.name LIKE ?)";
$params[] = "%$s%";
$params[] = "%$s%";
}
}
if (!empty($_GET['supplier_id'])) {
$where[] = "q.supplier_id = ?";
$params[] = $_GET['supplier_id'];
}
if (!empty($_GET['start_date'])) {
$where[] = "q.lpo_date >= ?";
$params[] = $_GET['start_date'];
}
if (!empty($_GET['end_date'])) {
$where[] = "q.lpo_date <= ?";
$params[] = $_GET['end_date'];
}
$whereSql = implode(" AND ", $where);
$countStmt = db()->prepare("SELECT COUNT(*) FROM lpos q JOIN suppliers s ON q.supplier_id = s.id WHERE $whereSql");
$countStmt->execute($params);
$total_records = (int)$countStmt->fetchColumn();
$data['total_pages'] = ceil($total_records / $limit);
$data['current_page'] = $page_num;
$stmt = db()->prepare("SELECT q.*, s.name as supplier_name
FROM lpos q
JOIN suppliers s ON q.supplier_id = s.id
WHERE $whereSql
ORDER BY q.id DESC
LIMIT $limit OFFSET $offset");
$stmt->execute($params);
$data['lpos'] = $stmt->fetchAll();
break;
case 'payment_methods':
$data['payment_methods'] = db()->query("SELECT * FROM payment_methods ORDER BY id DESC")->fetchAll();
break;
case 'settings':
// Already fetched globally
break;
case 'my_profile':
$stmt = db()->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$data['user'] = $stmt->fetch();
break;
case 'sales':
case 'purchases':
$type = ($page === 'sales') ? 'sale' : 'purchase';
$table = ($type === 'purchase') ? 'purchases' : 'invoices';
$cust_supplier_col = ($type === 'purchase') ? 'supplier_id' : 'customer_id';
$cust_supplier_table = ($type === 'purchase') ? 'suppliers' : 'customers';
$where = ["1=1"];
$params = [];
if (!empty($_GET['search'])) {
$s = $_GET['search'];
$clean_id = preg_replace('/[^0-9]/', '', $s);
if ($clean_id !== '') {
$where[] = "(v.id LIKE ? OR c.name LIKE ? OR v.id = ?)";
$params[] = "%$s%";
$params[] = "%$s%";
$params[] = $clean_id;
} else {
$where[] = "(v.id LIKE ? OR c.name LIKE ?)";
$params[] = "%$s%";
$params[] = "%$s%";
}
}
if (!empty($_GET['customer_id'])) {
$where[] = "v.$cust_supplier_col = ?";
$params[] = $_GET['customer_id'];
}
if (!empty($_GET['start_date'])) {
$where[] = "v.invoice_date >= ?";
$params[] = $_GET['start_date'];
}
if (!empty($_GET['end_date'])) {
$where[] = "v.invoice_date <= ?";
$params[] = $_GET['end_date'];
}
$whereSql = implode(" AND ", $where);
$countStmt = db()->prepare("SELECT COUNT(*) FROM $table v LEFT JOIN $cust_supplier_table c ON v.$cust_supplier_col = c.id WHERE $whereSql");
$countStmt->execute($params);
$total_records = (int)$countStmt->fetchColumn();
$data['total_pages'] = ceil($total_records / $limit);
$data['current_page'] = $page_num;
$stmt = db()->prepare("SELECT v.*, c.name as customer_name, c.tax_id as customer_tax_id, c.phone as customer_phone
FROM $table v
LEFT JOIN $cust_supplier_table c ON v.$cust_supplier_col = c.id
WHERE $whereSql
ORDER BY v.id DESC LIMIT $limit OFFSET $offset");
$stmt->execute($params);
$data['invoices'] = $stmt->fetchAll();
foreach ($data['invoices'] as &$inv) {
$inv['total_in_words'] = numberToWordsOMR($inv['total_with_vat']);
if ($type === 'sale') {
$item_stmt = db()->prepare("SELECT ii.*, i.name_en, i.name_ar, i.vat_rate FROM invoice_items ii LEFT JOIN stock_items i ON ii.item_id = i.id WHERE ii.invoice_id = ?");
$item_stmt->execute([$inv['id']]);
$inv['items'] = $item_stmt->fetchAll(PDO::FETCH_ASSOC);
} else {
$item_stmt = db()->prepare("SELECT pi.*, i.name_en, i.name_ar, i.vat_rate FROM purchase_items pi LEFT JOIN stock_items i ON pi.item_id = i.id WHERE pi.purchase_id = ?");
$item_stmt->execute([$inv['id']]);
$inv['items'] = $item_stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
unset($inv);
$items_list_raw = db()->query("SELECT id, name_en, name_ar, sale_price, purchase_price, stock_quantity, vat_rate, is_promotion, promotion_start, promotion_end, promotion_percent FROM stock_items ORDER BY name_en ASC")->fetchAll(PDO::FETCH_ASSOC);
foreach ($items_list_raw as &$item) {
$item['sale_price'] = getPromotionalPrice($item);
}
$data['items_list'] = $items_list_raw;
$data['customers_list'] = db()->query("SELECT id, name FROM $cust_supplier_table ORDER BY name ASC")->fetchAll();
if ($type === 'sale') {
$data['sales_invoices'] = db()->query("SELECT id, invoice_date, total_with_vat FROM invoices ORDER BY id DESC")->fetchAll();
} else {
$data['purchase_invoices'] = db()->query("SELECT id, invoice_date, total_with_vat FROM purchases ORDER BY id DESC")->fetchAll();
}
break;
case 'sales_returns':
$where = ["1=1"];
$params = [];
if (!empty($_GET['search'])) {
$s = $_GET['search'];
$clean_id = preg_replace('/[^0-9]/', '', $s);
if ($clean_id !== '') {
$where[] = "(sr.id LIKE ? OR c.name LIKE ? OR sr.invoice_id LIKE ? OR sr.id = ? OR sr.invoice_id = ?)";
$params[] = "%$s%";
$params[] = "%$s%";
$params[] = "%$s%";
$params[] = $clean_id;
$params[] = $clean_id;
} else {
$where[] = "(sr.id LIKE ? OR c.name LIKE ? OR sr.invoice_id LIKE ?)";
$params[] = "%$s%";
$params[] = "%$s%";
$params[] = "%$s%";
}
}
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT sr.*, c.name as customer_name, i.total_with_vat as invoice_total
FROM sales_returns sr
LEFT JOIN customers c ON sr.customer_id = c.id
LEFT JOIN invoices i ON sr.invoice_id = i.id
WHERE $whereSql
ORDER BY sr.id DESC");
$stmt->execute($params);
$data['returns'] = $stmt->fetchAll();
$data['sales_invoices'] = db()->query("SELECT id, invoice_date, total_with_vat FROM invoices ORDER BY id DESC")->fetchAll();
break;
case 'purchase_returns':
$where = ["1=1"];
$params = [];
if (!empty($_GET['search'])) {
$s = $_GET['search'];
$clean_id = preg_replace('/[^0-9]/', '', $s);
if ($clean_id !== '') {
$where[] = "(pr.id LIKE ? OR c.name LIKE ? OR pr.purchase_id LIKE ? OR pr.id = ? OR pr.purchase_id = ?)";
$params[] = "%$s%";
$params[] = "%$s%";
$params[] = "%$s%";
$params[] = $clean_id;
$params[] = $clean_id;
} else {
$where[] = "(pr.id LIKE ? OR c.name LIKE ? OR pr.purchase_id LIKE ?)";
$params[] = "%$s%";
$params[] = "%$s%";
$params[] = "%$s%";
}
}
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT pr.*, c.name as supplier_name, i.total_with_vat as invoice_total
FROM purchase_returns pr
LEFT JOIN suppliers c ON pr.supplier_id = c.id
LEFT JOIN purchases i ON pr.purchase_id = i.id
WHERE $whereSql
ORDER BY pr.id DESC");
$stmt->execute($params);
$data['returns'] = $stmt->fetchAll();
$data['purchase_invoices'] = db()->query("SELECT id, invoice_date, total_with_vat FROM purchases ORDER BY id DESC")->fetchAll();
break;
case 'customer_statement':
case 'supplier_statement':
$isCustomer = ($page === 'customer_statement');
$entityTable = $isCustomer ? 'customers' : 'suppliers';
$invoiceTable = $isCustomer ? 'invoices' : 'purchases';
$paymentTable = $isCustomer ? 'payments' : 'purchase_payments';
$fkColumn = $isCustomer ? 'customer_id' : 'supplier_id';
$invFkColumn = $isCustomer ? 'invoice_id' : 'purchase_id';
$data['entities'] = db()->query("SELECT id, name, balance FROM $entityTable ORDER BY name ASC")->fetchAll();
$entity_id = (int)($_GET['entity_id'] ?? 0);
if ($entity_id) {
$data['selected_entity'] = db()->query("SELECT * FROM $entityTable WHERE id = $entity_id")->fetch();
$start_date = $_GET['start_date'] ?? date('Y-m-01');
$end_date = $_GET['end_date'] ?? date('Y-m-d');
$stmt = db()->prepare("SELECT 'invoice' as trans_type, id, invoice_date as trans_date, total_with_vat as amount, status, id as ref_no
FROM $invoiceTable
WHERE $fkColumn = ? AND invoice_date BETWEEN ? AND ?");
$stmt->execute([$entity_id, $start_date, $end_date]);
$invoices = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt = db()->prepare("SELECT 'payment' as trans_type, p.id, p.payment_date as trans_date, p.amount, p.payment_method, p.$invFkColumn as ref_no
FROM $paymentTable p
JOIN $invoiceTable i ON p.$invFkColumn = i.id
WHERE i.$fkColumn = ? AND p.payment_date BETWEEN ? AND ?");
$stmt->execute([$entity_id, $start_date, $end_date]);
$payments = $stmt->fetchAll(PDO::FETCH_ASSOC);
$transactions = array_merge($invoices, $payments);
usort($transactions, function($a, $b) {
return strtotime($a['trans_date']) <=> strtotime($b['trans_date']);
});
$data['transactions'] = $transactions;
}
break;
case 'expense_categories':
$data['expense_categories'] = db()->query("SELECT * FROM expense_categories ORDER BY name_en ASC")->fetchAll();
break;
case 'expenses':
$where = ["1=1"];
$params = [];
if (!empty($_GET['category_id'])) {
$where[] = "e.category_id = ?";
$params[] = $_GET['category_id'];
}
if (!empty($_GET['start_date'])) {
$where[] = "e.expense_date >= ?";
$params[] = $_GET['start_date'];
}
if (!empty($_GET['end_date'])) {
$where[] = "e.expense_date <= ?";
$params[] = $_GET['end_date'];
}
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT e.*, c.name_en as cat_en, c.name_ar as cat_ar
FROM expenses e
LEFT JOIN expense_categories c ON e.category_id = c.id
WHERE $whereSql
ORDER BY e.expense_date DESC, e.id DESC");
$stmt->execute($params);
$data['expenses'] = $stmt->fetchAll();
break;
case 'role_groups':
$data['role_groups'] = db()->query("SELECT * FROM role_groups ORDER BY name ASC")->fetchAll();
break;
case 'users':
$data['users'] = db()->query("SELECT u.*, g.name as group_name FROM users u LEFT JOIN role_groups g ON u.group_id = g.id ORDER BY u.username ASC")->fetchAll();
$data['role_groups'] = db()->query("SELECT id, name FROM role_groups ORDER BY name ASC")->fetchAll();
break;
case 'backups':
$data['backups'] = BackupService::getBackups();
$stmt = db()->prepare("SELECT * FROM settings WHERE `key` IN ('backup_limit', 'backup_auto_enabled', 'backup_time')");
$stmt->execute();
$data['backup_settings'] = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
break;
case 'accounting':
$data['journal_entries'] = db()->query("SELECT je.*,
(SELECT SUM(debit) FROM acc_ledger WHERE journal_entry_id = je.id) as total_debit
FROM acc_journal_entries je
ORDER BY je.entry_date DESC, je.id DESC LIMIT 100")->fetchAll();
$data['accounts'] = db()->query("SELECT * FROM acc_accounts ORDER BY code ASC")->fetchAll();
if (isset($_GET['action']) && $_GET['action'] === 'get_entry_details') {
header('Content-Type: application/json');
$id = (int)$_GET['id'];
$stmt = db()->prepare("SELECT l.*, a.name_en, a.code FROM acc_ledger l JOIN acc_accounts a ON l.account_id = a.id WHERE l.journal_entry_id = ?");
$stmt->execute([$id]);
echo json_encode($stmt->fetchAll());
exit;
}
if (isset($_GET['view']) && $_GET['view'] === 'trial_balance') {
$data['trial_balance'] = db()->query("SELECT a.code, a.name_en, SUM(l.debit) as total_debit, SUM(l.credit) as total_credit
FROM acc_accounts a
LEFT JOIN acc_ledger l ON a.id = l.account_id
GROUP BY a.id
HAVING total_debit > 0 OR total_credit > 0
ORDER BY a.code ASC")->fetchAll();
}
if (isset($_GET['view']) && $_GET['view'] === 'profit_loss') {
$data['revenue_accounts'] = db()->query("SELECT code, name_en, name_ar FROM acc_accounts WHERE type = 'revenue' AND parent_id IS NOT NULL ORDER BY code ASC")->fetchAll();
$data['expense_accounts'] = db()->query("SELECT code, name_en, name_ar FROM acc_accounts WHERE type = 'expense' AND parent_id IS NOT NULL ORDER BY code ASC")->fetchAll();
}
if (isset($_GET['view']) && $_GET['view'] === 'balance_sheet') {
$data['asset_accounts'] = db()->query("SELECT code, name_en, name_ar FROM acc_accounts WHERE type = 'asset' AND parent_id IS NOT NULL ORDER BY code ASC")->fetchAll();
$data['liability_accounts'] = db()->query("SELECT code, name_en, name_ar FROM acc_accounts WHERE type = 'liability' AND parent_id IS NOT NULL ORDER BY code ASC")->fetchAll();
$data['equity_accounts'] = db()->query("SELECT code, name_en, name_ar FROM acc_accounts WHERE type = 'equity' AND parent_id IS NOT NULL ORDER BY code ASC")->fetchAll();
}
if (isset($_GET['view']) && $_GET['view'] === 'vat_report') {
$start = $_GET['start_date'] ?? date('Y-m-01');
$end = $_GET['end_date'] ?? date('Y-m-d');
$data['vat_report'] = getVatReport($start, $end);
$data['start_date'] = $start;
$data['end_date'] = $end;
}
if (isset($_GET['view']) && $_GET['view'] === 'coa') {
$data['coa'] = db()->query("SELECT a.*, p.name_en as parent_name
FROM acc_accounts a
LEFT JOIN acc_accounts p ON a.parent_id = p.id
ORDER BY a.code ASC")->fetchAll();
}
break;
case 'expense_report':
$start_date = $_GET['start_date'] ?? date('Y-m-01');
$end_date = $_GET['end_date'] ?? date('Y-m-d');
$category_id = $_GET['category_id'] ?? '';
$where = "WHERE e.expense_date BETWEEN ? AND ?";
$params = [$start_date, $end_date];
if ($category_id !== '') {
$where .= " AND e.category_id = ?";
$params[] = $category_id;
}
$stmt = db()->prepare("SELECT c.name_en, c.name_ar, SUM(e.amount) as total
FROM expenses e
JOIN expense_categories c ON e.category_id = c.id
$where
GROUP BY c.id
ORDER BY total DESC");
$stmt->execute($params);
$data['report_by_category'] = $stmt->fetchAll();
$stmt = db()->prepare("SELECT SUM(amount) FROM expenses e $where");
$stmt->execute($params);
$data['total_expenses'] = $stmt->fetchColumn() ?: 0;
$data['expense_categories'] = db()->query("SELECT * FROM expense_categories ORDER BY name_en ASC")->fetchAll();
break;
case 'expiry_report':
$where = ["expiry_date IS NOT NULL"];
$params = [];
$filter = $_GET['filter'] ?? 'all';
if ($filter === 'expired') {
$where[] = "expiry_date <= CURDATE()";
} elseif ($filter === 'near_expiry') {
$where[] = "expiry_date > CURDATE() AND expiry_date <= DATE_ADD(CURDATE(), INTERVAL 30 DAY)";
}
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT i.*, c.name_en as cat_en, c.name_ar as cat_ar
FROM stock_items i
LEFT JOIN stock_categories c ON i.category_id = c.id
WHERE $whereSql
ORDER BY i.expiry_date ASC");
$stmt->execute($params);
$data['expiry_items'] = $stmt->fetchAll();
break;
case 'low_stock_report':
$stmt = db()->prepare("SELECT i.*, c.name_en as cat_en, c.name_ar as cat_ar, s.name as supplier_name
FROM stock_items i
LEFT JOIN stock_categories c ON i.category_id = c.id
LEFT JOIN suppliers s ON i.supplier_id = s.id
WHERE i.stock_quantity <= i.min_stock_level
ORDER BY (i.min_stock_level - i.stock_quantity) DESC");
$stmt->execute();
$data['low_stock_items'] = $stmt->fetchAll();
break;
case 'cashflow_report':
$start_date = $_GET['start_date'] ?? date('Y-m-01');
$end_date = $_GET['end_date'] ?? date('Y-m-d');
// Fetch Cash & Bank Account IDs
$cash_accounts = db()->query("SELECT id FROM acc_accounts WHERE code IN (1100, 1200)")->fetchAll(PDO::FETCH_COLUMN);
$cash_ids_str = implode(',', $cash_accounts);
if (!empty($cash_ids_str)) {
// Opening Balance
$stmt = db()->prepare("SELECT SUM(debit - credit) FROM acc_ledger l JOIN acc_journal_entries je ON l.journal_entry_id = je.id WHERE l.account_id IN ($cash_ids_str) AND je.entry_date < ?");
$stmt->execute([$start_date]);
$data['opening_balance'] = $stmt->fetchColumn() ?: 0;
// Transactions in range
$stmt = db()->prepare("SELECT
je.entry_date,
je.description,
l.debit as inflow,
l.credit as outflow,
a.name_en as other_account,
a.type as other_type
FROM acc_ledger l
JOIN acc_journal_entries je ON l.journal_entry_id = je.id
LEFT JOIN acc_ledger l2 ON l2.journal_entry_id = je.id AND l2.id != l.id
LEFT JOIN acc_accounts a ON l2.account_id = a.id
WHERE l.account_id IN ($cash_ids_str)
AND je.entry_date BETWEEN ? AND ?
ORDER BY je.entry_date ASC, je.id ASC");
$stmt->execute([$start_date, $end_date]);
$data['cash_transactions'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
} else {
$data['opening_balance'] = 0;
$data['cash_transactions'] = [];
}
break;
case 'hr_departments':
$data['departments'] = db()->query("SELECT * FROM hr_departments ORDER BY id DESC")->fetchAll();
break;
case 'hr_employees':
$data['employees'] = db()->query("SELECT e.*, d.name as dept_name FROM hr_employees e LEFT JOIN hr_departments d ON e.department_id = d.id ORDER BY e.id DESC")->fetchAll();
$data['departments'] = db()->query("SELECT * FROM hr_departments ORDER BY name ASC")->fetchAll();
break;
case 'hr_attendance':
$date = $_GET['date'] ?? date('Y-m-d');
$data['attendance_date'] = $date;
$data['employees'] = db()->query("SELECT e.id, e.name, d.name as dept_name, a.status, a.clock_in, a.clock_out
FROM hr_employees e
LEFT JOIN hr_departments d ON e.department_id = d.id
LEFT JOIN hr_attendance a ON e.id = a.employee_id AND a.attendance_date = '$date'
WHERE e.status = 'active' ORDER BY e.name ASC")->fetchAll();
break;
case 'hr_payroll':
$month = (int)($_GET['month'] ?? date('m'));
$year = (int)($_GET['year'] ?? date('Y'));
$data['month'] = $month;
$data['year'] = $year;
$data['payroll'] = db()->query("SELECT p.*, e.name as emp_name FROM hr_payroll p JOIN hr_employees e ON p.employee_id = e.id WHERE p.payroll_month = $month AND p.payroll_year = $year ORDER BY p.id DESC")->fetchAll();
$data['employees'] = db()->query("SELECT id, name, salary FROM hr_employees WHERE status = 'active' ORDER BY name ASC")->fetchAll();
break;
case 'loyalty_history':
$where = ["1=1"];
$params = [];
if (!empty($_GET['customer_id'])) {
$where[] = "lt.customer_id = ?";
$params[] = (int)$_GET['customer_id'];
}
if (!empty($_GET['type'])) {
$where[] = "lt.transaction_type = ?";
$params[] = $_GET['type'];
}
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT lt.*, c.name as customer_name, c.loyalty_tier, c.loyalty_points
FROM loyalty_transactions lt
JOIN customers c ON lt.customer_id = c.id
WHERE $whereSql
ORDER BY lt.created_at DESC");
$stmt->execute($params);
$data['loyalty_transactions'] = $stmt->fetchAll();
break;
case 'devices':
$data['devices'] = db()->query("SELECT * FROM hr_biometric_devices ORDER BY id DESC")->fetchAll();
break;
case 'scale_devices':
$data['scale_devices'] = db()->query("SELECT * FROM pos_devices ORDER BY id DESC")->fetchAll();
break;
case 'cash_registers':
$data['cash_registers'] = db()->query("SELECT * FROM cash_registers ORDER BY id DESC")->fetchAll();
break;
case 'register_sessions':
$where = ["1=1"];
$params = [];
// Filter by user if provided and user has permission
if (isset($_GET['user_id']) && !empty($_GET['user_id'])) {
if (can('users_view')) {
$where[] = "s.user_id = ?";
$params[] = $_GET['user_id'];
}
}
if (!can('users_view')) {
$where[] = "s.user_id = ?";
$params[] = $_SESSION['user_id'];
}
// Filter by date range
if (isset($_GET['date_from']) && !empty($_GET['date_from'])) {
$where[] = "s.opened_at >= ?";
$params[] = $_GET['date_from'] . ' 00:00:00';
}
if (isset($_GET['date_to']) && !empty($_GET['date_to'])) {
$where[] = "s.opened_at <= ?";
$params[] = $_GET['date_to'] . ' 23:59:59';
}
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT s.*, r.name as register_name, u.username
FROM register_sessions s
LEFT JOIN cash_registers r ON s.register_id = r.id
LEFT JOIN users u ON s.user_id = u.id
WHERE $whereSql
ORDER BY s.id DESC");
$stmt->execute($params);
$data['sessions'] = $stmt->fetchAll();
$data['cash_registers'] = db()->query("SELECT * FROM cash_registers WHERE status = 'active'")->fetchAll();
$data['users'] = db()->query("SELECT id, username FROM users ORDER BY username ASC")->fetchAll();
break;
default:
if (can('dashboard_view')) {
$data['customers'] = db()->query("SELECT * FROM customers ORDER BY id DESC LIMIT 5")->fetchAll();
$data['stats'] = [
'total_customers' => db()->query("SELECT COUNT(*) FROM customers")->fetchColumn(),
'total_items' => db()->query("SELECT COUNT(*) FROM stock_items")->fetchColumn(),
'total_sales' => (db()->query("SELECT SUM(total_with_vat) FROM invoices")->fetchColumn() ?: 0) + (db()->query("SELECT SUM(net_amount) FROM pos_transactions WHERE status = 'completed'")->fetchColumn() ?: 0),
'total_received' => (db()->query("SELECT SUM(amount) FROM payments")->fetchColumn() ?: 0) + (db()->query("SELECT SUM(amount) FROM pos_payments")->fetchColumn() ?: 0),
'total_purchases' => db()->query("SELECT SUM(total_with_vat) FROM purchases")->fetchColumn() ?: 0,
'total_paid' => db()->query("SELECT SUM(amount) FROM purchase_payments")->fetchColumn() ?: 0,
'expired_items' => db()->query("SELECT COUNT(*) FROM stock_items WHERE expiry_date IS NOT NULL AND expiry_date <= CURDATE()")->fetchColumn(),
'near_expiry_items' => db()->query("SELECT COUNT(*) FROM stock_items WHERE expiry_date IS NOT NULL AND expiry_date > CURDATE() AND expiry_date <= DATE_ADD(CURDATE(), INTERVAL 30 DAY)")->fetchColumn(),
'low_stock_items_count' => db()->query("SELECT COUNT(*) FROM stock_items WHERE stock_quantity <= min_stock_level")->fetchColumn(),
];
$data['stats']['total_receivable'] = $data['stats']['total_sales'] - $data['stats']['total_received'];
$data['stats']['total_payable'] = $data['stats']['total_purchases'] - $data['stats']['total_paid'];
// Sales Chart Data
$data['monthly_sales'] = db()->query("SELECT DATE_FORMAT(invoice_date, '%M %Y') as label, SUM(total_with_vat) as total FROM invoices GROUP BY DATE_FORMAT(invoice_date, '%Y-%m') ORDER BY invoice_date ASC LIMIT 12")->fetchAll(PDO::FETCH_ASSOC);
$data['yearly_sales'] = db()->query("SELECT YEAR(invoice_date) as label, SUM(total_with_vat) as total FROM invoices GROUP BY label ORDER BY label ASC LIMIT 5")->fetchAll(PDO::FETCH_ASSOC);
}
break;
}
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
?>
= __('accounting') ?> - Admin Panel
0): ?>
prepare("SELECT li.*, i.name_en, i.name_ar, i.vat_rate
FROM lpo_items li
JOIN stock_items i ON li.item_id = i.id
WHERE li.lpo_id = ?");
$items->execute([$q['id']]);
$q['items'] = $items->fetchAll(PDO::FETCH_ASSOC);
?>
query("SELECT code FROM acc_accounts WHERE type='revenue' AND parent_id IS NOT NULL")->fetchAll() as $a) $rev += getAccountBalance($a['code']);
$exp = 0; foreach(db()->query("SELECT code FROM acc_accounts WHERE type='expense' AND parent_id IS NOT NULL")->fetchAll() as $a) $exp += getAccountBalance($a['code']);
$earnings = $rev - $exp;
$total_equity += $earnings;
?>
Manage daily opening and closing of cash registers.
prepare("SELECT s.*, r.name as register_name FROM register_sessions s JOIN cash_registers r ON s.register_id = r.id WHERE s.user_id = ? AND s.status = 'open'");
$active_session->execute([$_SESSION['user_id']]);
$session = $active_session->fetch();
?>
Current Open Register: = htmlspecialchars($session['register_name']) ?> |
Opened At: = $session['opened_at'] ?> |
Opening Balance: OMR = number_format((float)$session['opening_balance'], 3) ?>
prepare("SELECT
SUM(CASE WHEN LOWER(payment_method) = 'cash' THEN amount ELSE 0 END) as cash_total,
SUM(CASE WHEN LOWER(payment_method) IN ('card', 'credit card', 'visa', 'mastercard') THEN amount ELSE 0 END) as card_total,
SUM(CASE WHEN LOWER(payment_method) = 'credit' THEN amount ELSE 0 END) as credit_total,
SUM(CASE WHEN LOWER(payment_method) LIKE '%transfer%' OR LOWER(payment_method) LIKE '%bank%' THEN amount ELSE 0 END) as transfer_total,
SUM(amount) as total_sales
FROM (
SELECT p.payment_method, p.amount FROM pos_payments p JOIN pos_transactions t ON p.transaction_id = t.id WHERE t.register_session_id = ? AND t.status = 'completed'
UNION ALL
SELECT p.payment_method, p.amount FROM payments p JOIN invoices i ON p.invoice_id = i.id WHERE i.register_session_id = ? AND i.status = 'paid' AND i.is_pos = 1
) as combined_payments");
$stats_stmt->execute([$s['id'], $s['id']]);
$st = $stats_stmt->fetch();
$c_total = (float)($st['cash_total'] ?? 0);
$cd_total = (float)($st['card_total'] ?? 0);
$cr_total = (float)($st['credit_total'] ?? 0);
$tr_total = (float)($st['transfer_total'] ?? 0);
$t_sales = (float)($st['total_sales'] ?? 0);
$row_expected_cash = (float)$s['opening_balance'] + $c_total;
?>
prepare("SELECT SUM(vat_amount) as total_tax, SUM(discount_amount) as total_discount, SUM(total_net) as total_net FROM (
SELECT tax_amount as vat_amount, discount_amount, net_amount as total_net FROM pos_transactions WHERE register_session_id = ? AND status = 'completed'
UNION ALL
SELECT vat_amount, discount_amount, total_with_vat as total_net FROM invoices WHERE register_session_id = ? AND status = 'paid' AND is_pos = 1
) as combined_totals");
$extra_stmt->execute([$s['id'], $s['id']]);
$extra = $extra_stmt->fetch();
?>
Actual Cash:OMR = number_format((float)$s['cash_in_hand'], 3) ?>
Balance:OMR = number_format($shortage, 3) ?>
Transaction Details
Time
Order #
Customer
Items
Method
Amount
prepare("SELECT i.*, c.name as customer_name, GROUP_CONCAT(p.payment_method SEPARATOR ', ') as methods FROM invoices i LEFT JOIN payments p ON i.id = p.invoice_id LEFT JOIN customers c ON i.customer_id = c.id WHERE i.register_session_id = ? AND i.status = 'paid' AND i.is_pos = 1 GROUP BY i.id ORDER BY i.created_at DESC");
$txs_stmt->execute([$s['id']]);
$txs = $txs_stmt->fetchAll();
foreach ($txs as $tx):
?>
prepare("SELECT si.name_en, ii.quantity FROM invoice_items ii JOIN stock_items si ON ii.item_id = si.id WHERE ii.invoice_id = ?");
$items_stmt->execute([$tx['id']]);
$items = $items_stmt->fetchAll();
foreach ($items as $item) {
echo "" . htmlspecialchars($item['name_en']) . " x " . (float)$item['quantity'] . "";
}
?>
= htmlspecialchars($tx['methods'] ?: '---') ?>
= number_format($tx['total_with_vat'], 3) ?>
prepare("SELECT t.*, c.name as customer_name, GROUP_CONCAT(p.payment_method SEPARATOR ', ') as methods FROM pos_transactions t LEFT JOIN pos_payments p ON t.id = p.transaction_id LEFT JOIN customers c ON t.customer_id = c.id WHERE t.register_session_id = ? AND t.status = 'completed' GROUP BY t.id ORDER BY t.created_at DESC");
$txs_stmt->execute([$s['id']]);
$txs = $txs_stmt->fetchAll();
foreach ($txs as $tx):
?>