diff --git a/edit_sale.php b/edit_sale.php
index 8714123..31f34d6 100644
--- a/edit_sale.php
+++ b/edit_sale.php
@@ -20,7 +20,7 @@ $pageTitle = tr('تعديل فاتورة', 'Edit Invoice') . ' #' . h($editSale[
$activeNav = 'sales';
$error = '';
$catalog = catalog();
-$allowedBranches = $user['role'] === 'owner' ? array_keys(branches()) : [$user['branch_code']];
+$allowedBranches = get_user_branches($user);
try {
$customers = db()->query('SELECT id, name, phone FROM customers ORDER BY name ASC')->fetchAll();
diff --git a/expenses.php b/expenses.php
index be24b46..dd59f5d 100644
--- a/expenses.php
+++ b/expenses.php
@@ -22,7 +22,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if ($action === 'create' && has_permission('expenses', 'add')) {
- $branch_code = $isOwner ? ($_POST['branch_code'] ?? null) : $userBranch;
+ $pb = $_POST['branch_code'] ?? ''; $branch_code = can_access_branch($pb) ? $pb : $userBranch; if ($pb === '' && $user['role'] === 'owner') { $branch_code = null; } else if ($branch_code === '') { $branch_code = null; }
$stmt = $pdo->prepare('INSERT INTO expenses (branch_code, category_id, amount, expense_date, description, created_by) VALUES (?, ?, ?, ?, ?, ?)');
$stmt->execute([
$branch_code === '' ? null : $branch_code,
@@ -35,7 +35,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
set_flash('success', tr('تمت إضافة المصروف بنجاح', 'Expense added successfully'));
redirect_to('expenses.php');
} elseif ($action === 'edit' && has_permission('expenses', 'edit')) {
- $branch_code = $isOwner ? ($_POST['branch_code'] ?? null) : $userBranch;
+ $pb = $_POST['branch_code'] ?? ''; $branch_code = can_access_branch($pb) ? $pb : $userBranch; if ($pb === '' && $user['role'] === 'owner') { $branch_code = null; } else if ($branch_code === '') { $branch_code = null; }
$stmt = $pdo->prepare('UPDATE expenses SET branch_code = ?, category_id = ?, amount = ?, expense_date = ?, description = ? WHERE id = ?');
$stmt->execute([
$branch_code === '' ? null : $branch_code,
@@ -68,9 +68,17 @@ if ($search) {
$params[] = "%$search%";
}
-if (!$isOwner && $userBranch) {
- $where .= ' AND (e.branch_code = ? OR e.branch_code IS NULL)';
- $params[] = $userBranch;
+if (!$isOwner) {
+ $ubranches = get_user_branches($user);
+ if (!empty($ubranches)) {
+ $inQuery = implode(',', array_fill(0, count($ubranches), '?'));
+ $where .= " AND (e.branch_code IN ($inQuery) OR e.branch_code IS NULL)";
+ foreach ($ubranches as $ub) {
+ $params[] = $ub;
+ }
+ } else {
+ $where .= " AND e.branch_code IS NULL";
+ }
}
$totalStmt = $pdo->prepare("SELECT COUNT(*) FROM expenses e WHERE $where");
diff --git a/includes/app.php b/includes/app.php
index 27fe2c7..ed95c85 100644
--- a/includes/app.php
+++ b/includes/app.php
@@ -201,6 +201,35 @@ function require_roles(array $roles): array
return $user;
}
+function get_user_branches($user): array
+{
+ if (!$user) return [];
+ if ($user['role'] === 'owner') return array_keys(branches());
+ $list = [$user['branch_code']];
+ if (!empty($user['allowed_branches'])) {
+ $extra = explode(',', $user['allowed_branches']);
+ foreach ($extra as $b) {
+ $b = trim($b);
+ if ($b) $list[] = $b;
+ }
+ }
+ return array_unique($list);
+}
+
+function get_user_branches_assoc($user): array
+{
+ if (!$user) return [];
+ $all = branches();
+ $allowed = get_user_branches($user);
+ $res = [];
+ foreach ($allowed as $b) {
+ if (isset($all[$b])) {
+ $res[$b] = $all[$b];
+ }
+ }
+ return $res;
+}
+
function can_access_branch(string $branchCode): bool
{
$user = current_user();
@@ -212,7 +241,8 @@ function can_access_branch(string $branchCode): bool
return true;
}
- return $user['branch_code'] === $branchCode;
+ $allowed = get_user_branches($user);
+ return in_array($branchCode, $allowed, true);
}
function catalog(): array
@@ -337,8 +367,18 @@ function base_sales_query_filters(array &$params, ?string $mode = null, ?string
$user = current_user();
if ($user && $user['role'] !== 'owner') {
- $sql .= ' AND branch_code = :viewer_branch ';
- $params[':viewer_branch'] = $user['branch_code'];
+ $ubranches = get_user_branches($user);
+ if (empty($ubranches)) {
+ $sql .= ' AND 1=0 '; // No branches allowed
+ } else {
+ $namedParams = [];
+ foreach ($ubranches as $i => $ub) {
+ $key = ':v_branch_' . $i;
+ $namedParams[] = $key;
+ $params[$key] = $ub;
+ }
+ $sql .= ' AND branch_code IN (' . implode(', ', $namedParams) . ') ';
+ }
}
return $sql;
diff --git a/includes/purchase_form.php b/includes/purchase_form.php
index 21ae9de..ffdc3b4 100644
--- a/includes/purchase_form.php
+++ b/includes/purchase_form.php
@@ -5,7 +5,7 @@ $pageTitle = tr('فاتورة مشتريات جديدة', 'New Purchase');
$activeNav = 'new_purchase';
$error = '';
$catalog = catalog();
-$allowedBranches = $user['role'] === 'owner' ? array_keys(branches()) : [$user['branch_code']];
+$allowedBranches = get_user_branches($user);
try {
$customers = $customers = [];
diff --git a/includes/sale_form.php b/includes/sale_form.php
index e7b04c2..a06394e 100644
--- a/includes/sale_form.php
+++ b/includes/sale_form.php
@@ -5,7 +5,7 @@ $pageTitle = $saleMode === 'normal' ? tr('إنشاء فاتورة ضريبية',
$activeNav = $saleMode === 'normal' ? 'normal' : 'pos';
$error = '';
$catalog = catalog();
-$allowedBranches = $user['role'] === 'owner' ? array_keys(branches()) : [$user['branch_code']];
+$allowedBranches = get_user_branches($user);
try {
$customers = db()->query('SELECT id, name, phone FROM customers ORDER BY name ASC')->fetchAll();
diff --git a/patch_export.php b/patch_export.php
deleted file mode 100644
index 17e0445..0000000
--- a/patch_export.php
+++ /dev/null
@@ -1,13 +0,0 @@
-" href="= h(url_for('pos.php')) ?>">
- = h(tr('نقاط البيع', 'POS Sale')) ?>
-
-
- = h(tr('بيع عادي', 'Normal Sale')) ?>
-
-
- = h(tr('المبيعات', 'Sales')) ?>
-
-HTML;
-
-$replace = <<
- = h(tr('المبيعات', 'Sales')) ?>
-
-
- = h(tr('قائمة الفواتير', 'Invoice list')) ?>
-
-
- = h(tr('فاتورة جديدة', 'New invoice')) ?>
-
-
- = h(tr('نقاط البيع', 'POS')) ?>
-
-HTML;
-
-$newContent = str_replace($search, $replace, $content);
-file_put_contents('includes/header.php', $newContent);
-echo "Done";
diff --git a/patch_import.php b/patch_import.php
deleted file mode 100644
index 290ac7c..0000000
--- a/patch_import.php
+++ /dev/null
@@ -1,69 +0,0 @@
-beginTransaction();
- try {
- $stmtInsert = $pdo->prepare("INSERT INTO items (sku, name, price, cost_price, base_stock, vat, category_id, supplier_id, unit_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
- $stmtUpdate = $pdo->prepare("UPDATE items SET name=?, price=?, cost_price=?, base_stock=?, vat=?, category_id=?, supplier_id=?, unit_id=? WHERE sku=?");
- $stmtCheck = $pdo->prepare("SELECT id FROM items WHERE sku=?");
- while (($row = fgetcsv($file)) !== false) {
-REPLACE;
-
-$replace = <<<'REPLACE'
- if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] === UPLOAD_ERR_OK) {
- $pdo = db();
- $file_path = $_FILES['csv_file']['tmp_name'];
- $raw_content = file_get_contents($file_path);
-
- // Prevent ZIP / XLSX
- if (str_starts_with($raw_content, 'PK')) {
- header('Location: stock.php?import_error=' . urlencode('يرجى حفظ الملف بصيغة CSV وليس كملف إكسل (XLSX)'));
- exit;
- }
-
- // Remove UTF-8 BOM if present
- if (str_starts_with($raw_content, "\xEF\xBB\xBF")) {
- $raw_content = substr($raw_content, 3);
- }
-
- // Fix encoding for Windows-1256 (common in Arabic Excel exports)
- if (!mb_check_encoding($raw_content, 'UTF-8')) {
- $raw_content = mb_convert_encoding($raw_content, 'UTF-8', 'Windows-1256');
- }
-
- // Determine delimiter by checking first line
- $first_line = strtok($raw_content, "\r\n");
- $delimiter = ',';
- if ($first_line !== false && substr_count($first_line, ';') > substr_count($first_line, ',')) {
- $delimiter = ';';
- }
-
- $clean_file = tmpfile();
- fwrite($clean_file, $raw_content);
- rewind($clean_file);
-
- $header = fgetcsv($clean_file, 0, $delimiter);
- $imported = 0; $updated = 0;
- $pdo->beginTransaction();
- try {
- $stmtInsert = $pdo->prepare("INSERT INTO items (sku, name, price, cost_price, base_stock, vat, category_id, supplier_id, unit_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
- $stmtUpdate = $pdo->prepare("UPDATE items SET name=?, price=?, cost_price=?, base_stock=?, vat=?, category_id=?, supplier_id=?, unit_id=? WHERE sku=?");
- $stmtCheck = $pdo->prepare("SELECT id FROM items WHERE sku=?");
- while (($row = fgetcsv($clean_file, 0, $delimiter)) !== false) {
-REPLACE;
-
-if (strpos($content, $search) !== false) {
- $content = str_replace($search, $replace, $content);
- file_put_contents('stock.php', $content);
- echo "Replaced successfully.\n";
-} else {
- echo "Search string not found.\n";
-}
-
diff --git a/pos.php b/pos.php
index 2694133..f946e77 100644
--- a/pos.php
+++ b/pos.php
@@ -6,7 +6,7 @@ $pageTitle = tr('نقاط البيع', 'Smart POS');
$activeNav = 'pos';
$error = '';
$catalog = catalog();
-$allowedBranches = $user['role'] === 'owner' ? array_keys(branches()) : [$user['branch_code']];
+$allowedBranches = get_user_branches($user);
try {
$pdo = db();
diff --git a/reports.php b/reports.php
index 3321ae9..290fa68 100644
--- a/reports.php
+++ b/reports.php
@@ -109,13 +109,29 @@ if ($tab === 'sales') {
$params[':date_to'] = $dateTo;
if ($user['role'] !== 'owner') {
- $whereConditions[] = "(e.branch_code = :ubranch OR e.branch_code IS NULL)";
- $params[':ubranch'] = $user['branch_code'];
- if ($branchFilter && $branchFilter === $user['branch_code']) {
+ $ubranches = get_user_branches($user);
+ if ($branchFilter && $branchFilter === 'general') {
+ $whereConditions[] = "e.branch_code IS NULL";
+ } elseif ($branchFilter && in_array($branchFilter, $ubranches, true)) {
$whereConditions[] = "e.branch_code = :branch";
$params[':branch'] = $branchFilter;
- } elseif ($branchFilter && $branchFilter === 'general') {
- $whereConditions[] = "e.branch_code IS NULL";
+ } else {
+ if (empty($ubranches)) {
+ $whereConditions[] = "e.branch_code IS NULL";
+ } else {
+ $inQuery = implode(',', array_fill(0, count($ubranches), '?'));
+ // We must use numbered placeholders if mixing with named placeholders?
+ // PDO might not like mixing ? and :name.
+ // Let's create named placeholders for in query.
+ $namedParams = [];
+ foreach($ubranches as $i => $ub) {
+ $key = ':ubranch_' . $i;
+ $namedParams[] = $key;
+ $params[$key] = $ub;
+ }
+ $inQuery = implode(', ', $namedParams);
+ $whereConditions[] = "(e.branch_code IN ($inQuery) OR e.branch_code IS NULL)";
+ }
}
} else {
if ($branchFilter) {
@@ -215,7 +231,7 @@ require __DIR__ . '/includes/header.php';