From 9419ae79eb4beed602eaf414823b137b5078cfcb Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sun, 1 Mar 2026 18:25:59 +0000 Subject: [PATCH] Autosave: 20260301-182559 --- debug.log | 3 + fix_schema.php | 30 +++++ index.php | 222 ++++++++++++++++++++++++++++++++++++ price_checker.php | 285 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 540 insertions(+) create mode 100644 fix_schema.php create mode 100644 price_checker.php diff --git a/debug.log b/debug.log index d09aadd..25388d7 100644 --- a/debug.log +++ b/debug.log @@ -1,2 +1,5 @@ [INFO] AI agent editing: index.php 2026-02-26 08:18:16 - Items case hit +2026-03-01 13:00:29 - Items case hit +2026-03-01 13:17:12 - Items case hit +2026-03-01 18:24:40 - Items case hit diff --git a/fix_schema.php b/fix_schema.php new file mode 100644 index 0000000..d19e2ff --- /dev/null +++ b/fix_schema.php @@ -0,0 +1,30 @@ +exec("UPDATE _stock_items SET outlet_id = 1 WHERE outlet_id IS NULL"); + $pdo->exec("UPDATE _stock_categories SET outlet_id = 1 WHERE outlet_id IS NULL"); + $pdo->exec("UPDATE _stock_units SET outlet_id = 1 WHERE outlet_id IS NULL"); + + // 2. Fix _stock_items index + // First, drop old unique index if it exists + try { + $pdo->exec("ALTER TABLE _stock_items DROP INDEX sku"); + } catch (Exception $e) { + echo "Note: index 'sku' might not exist or already dropped: " . $e->getMessage() . "\n"; + } + // Add new unique index (outlet_id, sku) + $pdo->exec("ALTER TABLE _stock_items ADD UNIQUE KEY outlet_sku (outlet_id, sku)"); + + // 3. Fix _stock_categories index + $pdo->exec("ALTER TABLE _stock_categories ADD UNIQUE KEY outlet_cat_name (outlet_id, name_en)"); + + // 4. Fix _stock_units index + $pdo->exec("ALTER TABLE _stock_units ADD UNIQUE KEY outlet_unit_name (outlet_id, name_en)"); + + echo "Database schema updated successfully.\n"; +} catch (Exception $e) { + echo "Error updating database schema: " . $e->getMessage() . "\n"; +} diff --git a/index.php b/index.php index ab02ec1..0a59abe 100644 --- a/index.php +++ b/index.php @@ -2995,6 +2995,127 @@ if (isset($_POST['add_hr_department'])) { $stmt->execute([$id]); redirectWithMessage("Outlet deleted successfully!", "index.php?page=outlets"); } + + // --- Data Synchronization Handler --- + if (isset($_POST['perform_data_sync']) && ($_SESSION['user_role_name'] ?? '') === 'Administrator') { + $source_id = (int)$_POST['source_outlet_id']; + $target_id = (int)$_POST['target_outlet_id']; + + if ($source_id === $target_id) { + redirectWithMessage("Source and target outlet must be different.", "index.php?page=data_sync"); + } + + $db = db(); + $db->beginTransaction(); + try { + // 1. Sync & Map Categories + $cat_map = []; + if (isset($_POST['sync_categories'])) { + $source_cats = $db->prepare("SELECT * FROM _stock_categories WHERE outlet_id = ?"); + $source_cats->execute([$source_id]); + foreach ($source_cats->fetchAll() as $cat) { + $stmt = $db->prepare("INSERT INTO _stock_categories (outlet_id, name_en, name_ar) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE name_ar = VALUES(name_ar)"); + $stmt->execute([$target_id, $cat['name_en'], $cat['name_ar']]); + } + } + + // Build category map based on Name EN (unique within outlet) + $s_cats = $db->prepare("SELECT id, name_en FROM _stock_categories WHERE outlet_id = ?"); + $s_cats->execute([$source_id]); + $source_cats_by_name = $s_cats->fetchAll(PDO::FETCH_KEY_PAIR); + + $t_cats = $db->prepare("SELECT id, name_en FROM _stock_categories WHERE outlet_id = ?"); + $t_cats->execute([$target_id]); + $target_cats_by_name = array_flip($t_cats->fetchAll(PDO::FETCH_KEY_PAIR)); + + foreach ($source_cats_by_name as $sid => $name) { + if (isset($target_cats_by_name[$name])) { + $cat_map[$sid] = $target_cats_by_name[$name]; + } + } + + // 2. Sync & Map Units + $unit_map = []; + if (isset($_POST['sync_units'])) { + $source_units = $db->prepare("SELECT * FROM _stock_units WHERE outlet_id = ?"); + $source_units->execute([$source_id]); + foreach ($source_units->fetchAll() as $unit) { + $stmt = $db->prepare("INSERT INTO _stock_units (outlet_id, name_en, name_ar, short_name_en, short_name_ar) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE name_ar = VALUES(name_ar), short_name_en = VALUES(short_name_en), short_name_ar = VALUES(short_name_ar)"); + $stmt->execute([$target_id, $unit['name_en'], $unit['name_ar'], $unit['short_name_en'], $unit['short_name_ar']]); + } + } + + $s_units = $db->prepare("SELECT id, name_en FROM _stock_units WHERE outlet_id = ?"); + $s_units->execute([$source_id]); + $source_units_by_name = $s_units->fetchAll(PDO::FETCH_KEY_PAIR); + + $t_units = $db->prepare("SELECT id, name_en FROM _stock_units WHERE outlet_id = ?"); + $t_units->execute([$target_id]); + $target_units_by_name = array_flip($t_units->fetchAll(PDO::FETCH_KEY_PAIR)); + + foreach ($source_units_by_name as $sid => $name) { + if (isset($target_units_by_name[$name])) { + $unit_map[$sid] = $target_units_by_name[$name]; + } + } + + // 3. Sync Items + if (isset($_POST['sync_items'])) { + $source_items = $db->prepare("SELECT * FROM _stock_items WHERE outlet_id = ?"); + $source_items->execute([$source_id]); + foreach ($source_items->fetchAll() as $item) { + // Check if item exists in target by SKU + $check_stmt = $db->prepare("SELECT id FROM _stock_items WHERE outlet_id = ? AND sku = ?"); + $check_stmt->execute([$target_id, $item['sku']]); + $target_item_id = $check_stmt->fetchColumn(); + + $t_cat_id = $cat_map[$item['category_id']] ?? null; + $t_unit_id = $unit_map[$item['unit_id']] ?? null; + + if ($target_item_id) { + // Update existing: Keep stock_quantity + $stmt = $db->prepare("UPDATE _stock_items SET + category_id = ?, unit_id = ?, + name_en = ?, name_ar = ?, purchase_price = ?, sale_price = ?, + min_stock_level = ?, expiry_date = ?, image_path = ?, + vat_rate = ?, is_promotion = ?, promotion_start = ?, + promotion_end = ?, promotion_percent = ? + WHERE id = ?"); + $stmt->execute([ + $t_cat_id, $t_unit_id, + $item['name_en'], $item['name_ar'], $item['purchase_price'], $item['sale_price'], + $item['min_stock_level'], $item['expiry_date'], $item['image_path'], + $item['vat_rate'], $item['is_promotion'], $item['promotion_start'], + $item['promotion_end'], $item['promotion_percent'], $target_item_id + ]); + } else { + // Insert new: Set stock_quantity to 0 + $stmt = $db->prepare("INSERT INTO _stock_items + (outlet_id, category_id, unit_id, supplier_id, name_en, name_ar, sku, + purchase_price, sale_price, stock_quantity, min_stock_level, + expiry_date, image_path, vat_rate, is_promotion, promotion_start, + promotion_end, promotion_percent) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?)"); + $stmt->execute([ + $target_id, $t_cat_id, $t_unit_id, $item['supplier_id'], + $item['name_en'], $item['name_ar'], $item['sku'], + $item['purchase_price'], $item['sale_price'], + $item['min_stock_level'], $item['expiry_date'], $item['image_path'], + $item['vat_rate'], $item['is_promotion'], $item['promotion_start'], + $item['promotion_end'], $item['promotion_percent'] + ]); + } + } + } + + $db->commit(); + redirectWithMessage("Data synchronization completed successfully!", "index.php?page=data_sync"); + } catch (Exception $e) { + $db->rollBack(); + redirectWithMessage("Error: " . $e->getMessage(), "index.php?page=data_sync"); + } + } + // --- Cash Register & Session Handlers --- if (isset($_POST['add_cash_register'])) { $name = $_POST['name'] ?? ''; @@ -3542,6 +3663,11 @@ switch ($page) { $stmt->execute(); $data['outlets'] = $stmt->fetchAll(); break; + case 'data_sync': + $stmt = db()->prepare("SELECT * FROM outlets ORDER BY name ASC"); + $stmt->execute(); + $data['outlets'] = $stmt->fetchAll(); + break; case 'suppliers': $where = ["1=1"]; $params = []; @@ -4770,6 +4896,9 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; Outlets + + Data Sync + @@ -4795,6 +4924,11 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; + + + Price Checker + + @@ -5351,6 +5485,77 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; }); + +
+
+

Data Synchronization

+
+ +
+
+
+
+
+ + +
Data will be copied FROM this outlet.
+
+
+ + +
Data will be copied TO this outlet.
+
+ +
+
+
Select Data to Sync
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + + Note: For items, if they already exist in the target outlet (by SKU), their details will be updated but stock quantity will remain unchanged. If they are new, stock quantity will be set to 0. + +
+
+ +
+ +
+
+
+
+
+
+
@@ -10094,6 +10299,23 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
+ +
+ +
diff --git a/price_checker.php b/price_checker.php new file mode 100644 index 0000000..faa026b --- /dev/null +++ b/price_checker.php @@ -0,0 +1,285 @@ +query("SELECT id, name FROM outlets WHERE status = 'active'")->fetchAll(PDO::FETCH_ASSOC); +} catch (Exception $e) { + $outlets = []; +} + +// Simple API endpoint for price lookup +if (isset($_GET['action']) && $_GET['action'] === 'lookup') { + header('Content-Type: application/json'); + $sku = $_GET['sku'] ?? ''; + $outlet_id = $_GET['outlet_id'] ?? ($_COOKIE['preferred_outlet'] ?? null); + + if (empty($sku)) { + echo json_encode(['error' => 'Empty SKU']); + exit; + } + + try { + $sql = "SELECT name_en, name_ar, sale_price, image_path FROM stock_items WHERE sku = ?"; + $params = [$sku]; + + if ($outlet_id) { + $sql .= " AND outlet_id = ?"; + $params[] = $outlet_id; + } + + $sql .= " LIMIT 1"; + + $stmt = db()->prepare($sql); + $stmt->execute($params); + $item = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($item) { + echo json_encode([ + 'success' => true, + 'item' => [ + 'name' => $item['name_en'] ?: $item['name_ar'], + 'price' => number_format($item['sale_price'], 3), + 'image' => $item['image_path'] ?: null + ] + ]); + } else { + echo json_encode(['success' => false, 'message' => 'Item not found']); + } + } catch (Exception $e) { + echo json_encode(['error' => 'Database error']); + } + exit; +} +?> + + + + + + Price Checker + + + + + + + + + + +
+
+

Price Checker

+

Quickly check item prices

+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +

+
+ + OMR +
+
+
+
+ +
+ +
+
+ Loading... +
+
+
+ + + + + + + + +