integrations multi outlets

This commit is contained in:
Flatlogic Bot 2026-02-25 12:42:16 +00:00
parent 94afa7ad81
commit bda20a7ffb
21 changed files with 1663 additions and 2252 deletions

View File

@ -1,54 +0,0 @@
<?php
require 'db/config.php';
$db = db();
$tables = [
'users', 'invoices', 'lpos', 'purchases', 'quotations',
'expenses', 'payments', 'purchase_payments', 'pos_transactions',
'customers', 'suppliers', 'stock_items', 'pos_held_carts',
'loyalty_transactions', 'purchase_returns', 'sales_returns', 'pos_payments'
];
try {
// 1. Create function
$db->exec("DROP FUNCTION IF EXISTS current_outlet_id");
$db->exec("CREATE FUNCTION current_outlet_id() RETURNS INT DETERMINISTIC RETURN @session_outlet_id");
foreach ($tables as $t) {
// Ensure table exists and isn't already a view
$stmt = $db->query("SHOW FULL TABLES LIKE '$t'");
$row = $stmt->fetch(PDO::FETCH_NUM);
if (!$row) continue;
if ($row[1] === 'VIEW') continue; // Already processed
// Make sure it has outlet_id
$hasCol = false;
$cols = $db->query("SHOW COLUMNS FROM `$t`")->fetchAll(PDO::FETCH_ASSOC);
foreach ($cols as $c) {
if ($c['Field'] === 'outlet_id') $hasCol = true;
}
if (!$hasCol) {
$db->exec("ALTER TABLE `$t` ADD COLUMN IF NOT EXISTS `outlet_id` int(11) DEFAULT 1 AFTER `id`");
}
// Alter default to 1 if it's NULL
$db->exec("ALTER TABLE `$t` MODIFY `outlet_id` int(11) DEFAULT 1");
// Rename table to _table
$db->exec("RENAME TABLE `$t` TO `_$t`");
// Create view
$db->exec("CREATE VIEW `$t` AS SELECT * FROM `_$t` WHERE outlet_id = current_outlet_id() OR current_outlet_id() IS NULL OR current_outlet_id() = 0");
// Create trigger
$db->exec("CREATE TRIGGER `trg_ins_$t` BEFORE INSERT ON `_$t` FOR EACH ROW BEGIN
IF current_outlet_id() IS NOT NULL AND current_outlet_id() != 0 THEN
SET NEW.outlet_id = current_outlet_id();
END IF;
END");
echo "Processed $t\n";
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +0,0 @@
<?php
require 'db/config.php';
try {
$pdo = db();
$stmt = $pdo->query("SHOW TABLES");
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
foreach ($tables as $table) {
echo "Table: $table\n";
$stmt2 = $pdo->query("DESCRIBE `$table`");
$cols = $stmt2->fetchAll(PDO::FETCH_ASSOC);
foreach ($cols as $col) {
echo " - " . $col['Field'] . " (" . $col['Type'] . ")\n";
}
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}

View File

@ -1,6 +0,0 @@
<?php
require 'db/config.php';
$stmt = db()->query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME = 'outlet_id' AND TABLE_SCHEMA = DATABASE()");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['TABLE_NAME'] . "\n";
}

View File

@ -1,47 +0,0 @@
<?php
require 'db/config.php';
try {
$pdo = db();
// Create outlets table
$pdo->exec("CREATE TABLE IF NOT EXISTS `outlets` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`address` text,
`phone` varchar(50),
`status` enum('active','inactive') DEFAULT 'active',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
// Insert a default Main Branch if table is empty
$stmt = $pdo->query("SELECT COUNT(*) FROM outlets");
if ($stmt->fetchColumn() == 0) {
$pdo->exec("INSERT INTO outlets (name, address, status) VALUES ('Main Branch', 'Headquarters', 'active')");
}
// Add outlet_id to tables
$tablesToUpdate = [
'users',
'invoices',
'purchases',
'lpos',
'quotations',
'expenses',
'payments'
];
foreach ($tablesToUpdate as $table) {
$stmt = $pdo->query("SHOW COLUMNS FROM `$table` LIKE 'outlet_id'");
if ($stmt->rowCount() == 0) {
$pdo->exec("ALTER TABLE `$table` ADD COLUMN `outlet_id` int(11) NULL DEFAULT 1 AFTER `id`");
echo "Added outlet_id to $table\n";
} else {
echo "outlet_id already exists in $table\n";
}
}
echo "Migration completed successfully.";
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}

View File

@ -1,2 +1,7 @@
2026-02-25 09:56:17 - Items case hit
2026-02-25 09:56:38 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true}
2026-02-25 10:03:48 - Items case hit
2026-02-25 11:48:14 - Items case hit
2026-02-25 11:49:27 - Items case hit
2026-02-25 11:51:57 - Items case hit
2026-02-25 12:41:41 - Items case hit

181
diff.txt
View File

@ -1,181 +0,0 @@
diff --git a/index.php b/index.php
index b15ee60..3f88b32 100644
--- a/index.php
+++ b/index.php
@@ -35,6 +35,15 @@ if ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || (isset($_SERVER[
}
session_start();
+if (isset($_GET['action']) && $_GET['action'] === 'switch_outlet') {
+ $target = (int)$_GET['id'];
+ if (($_SESSION['user_role_name'] ?? '') === 'Administrator') {
+ $_SESSION['outlet_id'] = $target === 0 ? null : $target;
+ }
+ header("Location: " . ($_SERVER['HTTP_REFERER'] ?? 'index.php'));
+ exit;
+}
+
if (isset($_GET['action']) && $_GET['action'] === 'download_items_template') {
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=items_import_template.csv');
@@ -3014,6 +3023,11 @@ $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 = [];
@@ -4184,6 +4198,26 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</div>
</div>
<?php endif; ?>
+ <?php
+ if (($_SESSION['user_role_name'] ?? '') === 'Administrator'):
+ $outlets = db()->query("SELECT * FROM outlets WHERE status = 'active'")->fetchAll(PDO::FETCH_ASSOC);
+ $cur_out = $_SESSION['outlet_id'] ?? 0;
+ $cur_name = 'All Outlets';
+ foreach ($outlets as $o) { if ($o['id'] == $cur_out) $cur_name = $o['name']; }
+ ?>
+ <div class="dropdown me-3">
+ <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown">
+ <i class="bi bi-shop"></i> <span class="d-none d-md-inline"><?= htmlspecialchars($cur_name) ?></span>
+ </button>
+ <ul class="dropdown-menu shadow-sm border-0">
+ <li><a class="dropdown-item <?= $cur_out == 0 ? 'active' : '' ?>" href="index.php?action=switch_outlet&id=0">All Outlets</a></li>
+ <li><hr class="dropdown-divider"></li>
+ <?php foreach ($outlets as $o): ?>
+ <li><a class="dropdown-item <?= $cur_out == $o['id'] ? 'active' : '' ?>" href="index.php?action=switch_outlet&id=<?= $o['id'] ?>"><?= htmlspecialchars($o['name']) ?></a></li>
+ <?php endforeach; ?>
+ </ul>
+ </div>
+ <?php endif; ?>
<div class="dropdown me-3">
<button class="btn btn-outline-secondary btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="bi bi-palette"></i> <span><?= $lang === 'ar' ? 'المظهر' : 'Theme' ?></span>
@@ -4488,6 +4522,121 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<h1 class="display-4 fw-bold text-muted text-center mt-3"><?= htmlspecialchars($data['settings']['company_name'] ?? 'Company Name') ?></h1>
</div>
<?php endif; ?>
+ <?php elseif ($page === 'outlets' && ($_SESSION['user_role_name'] ?? '') === 'Administrator'): ?>
+ <div class="card border-0 shadow-sm rounded-4 mb-4">
+ <div class="card-header bg-white border-bottom-0 pt-4 pb-0 px-4 d-flex justify-content-between align-items-center">
+ <h5 class="fw-bold mb-0"><i class="bi bi-shop text-primary me-2"></i> Manage Outlets</h5>
+ <button class="btn btn-primary rounded-pill px-3 py-2" data-bs-toggle="modal" data-bs-target="#addOutletModal">
+ <i class="bi bi-plus-lg me-1"></i> Add Outlet
+ </button>
+ </div>
+ <div class="card-body p-4">
+ <div class="table-responsive">
+ <table class="table table-hover align-middle mb-0">
+ <thead class="table-light">
+ <tr>
+ <th>ID</th>
+ <th>Name</th>
+ <th>Address</th>
+ <th>Phone</th>
+ <th>Status</th>
+ <th>Created At</th>
+ <th class="text-end">Actions</th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach ($data['outlets'] as $o): ?>
+ <tr>
+ <td>#<?= $o['id'] ?></td>
+ <td class="fw-semibold text-dark"><?= htmlspecialchars($o['name']) ?></td>
+ <td class="text-muted small"><?= htmlspecialchars($o['address'] ?: '-') ?></td>
+ <td><?= htmlspecialchars($o['phone'] ?: '-') ?></td>
+ <td><span class="badge bg-<?= $o['status'] === 'active' ? 'success' : 'secondary' ?> bg-opacity-10 text-<?= $o['status'] === 'active' ? 'success' : 'secondary' ?> rounded-pill px-2"><?= ucfirst($o['status']) ?></span></td>
+ <td class="small text-muted"><?= htmlspecialchars($o['created_at']) ?></td>
+ <td class="text-end">
+ <button class="btn btn-light btn-sm rounded-circle me-1" onclick="editOutlet(<?= htmlspecialchars(json_encode($o)) ?>)" title="Edit">
+ <i class="bi bi-pencil"></i>
+ </button>
+ <?php if ($o['id'] !== 1): ?>
+ <form method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this outlet?');">
+ <input type="hidden" name="id" value="<?= $o['id'] ?>">
+ <button type="submit" name="delete_outlet" class="btn btn-light btn-sm rounded-circle text-danger" title="Delete">
+ <i class="bi bi-trash"></i>
+ </button>
+ </form>
+ <?php endif; ?>
+ </td>
+ </tr>
+ <?php endforeach; ?>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+
+ <!-- Add/Edit Modal -->
+ <div class="modal fade" id="addOutletModal" tabindex="-1">
+ <div class="modal-dialog modal-dialog-centered">
+ <div class="modal-content border-0 shadow rounded-4">
+ <form method="POST">
+ <input type="hidden" name="id" id="outlet_id">
+ <div class="modal-header border-bottom-0 pb-0">
+ <h5 class="modal-title fw-bold" id="outletModalTitle">Add Outlet</h5>
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
+ </div>
+ <div class="modal-body p-4">
+ <div class="mb-3">
+ <label class="form-label small fw-semibold text-muted">Name</label>
+ <input type="text" name="name" id="outlet_name" class="form-control rounded-3" required>
+ </div>
+ <div class="mb-3">
+ <label class="form-label small fw-semibold text-muted">Phone</label>
+ <input type="text" name="phone" id="outlet_phone" class="form-control rounded-3">
+ </div>
+ <div class="mb-3">
+ <label class="form-label small fw-semibold text-muted">Address</label>
+ <textarea name="address" id="outlet_address" class="form-control rounded-3" rows="2"></textarea>
+ </div>
+ <div class="mb-0">
+ <label class="form-label small fw-semibold text-muted">Status</label>
+ <select name="status" id="outlet_status" class="form-select rounded-3">
+ <option value="active">Active</option>
+ <option value="inactive">Inactive</option>
+ </select>
+ </div>
+ </div>
+ <div class="modal-footer border-top-0 pt-0">
+ <button type="button" class="btn btn-light rounded-pill px-4" data-bs-dismiss="modal">Cancel</button>
+ <button type="submit" name="add_outlet" id="outletSubmitBtn" class="btn btn-primary rounded-pill px-4">Save Outlet</button>
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+ <script>
+ function editOutlet(o) {
+ document.getElementById('outlet_id').value = o.id;
+ document.getElementById('outlet_name').value = o.name;
+ document.getElementById('outlet_phone').value = o.phone || '';
+ document.getElementById('outlet_address').value = o.address || '';
+ document.getElementById('outlet_status').value = o.status;
+ document.getElementById('outletModalTitle').innerText = 'Edit Outlet';
+ document.getElementById('outletSubmitBtn').name = 'edit_outlet';
+ document.getElementById('outletSubmitBtn').innerText = 'Update Outlet';
+ new bootstrap.Modal(document.getElementById('addOutletModal')).show();
+ }
+ document.getElementById('addOutletModal').addEventListener('hidden.bs.modal', function () {
+ document.getElementById('outlet_id').value = '';
+ document.getElementById('outlet_name').value = '';
+ document.getElementById('outlet_phone').value = '';
+ document.getElementById('outlet_address').value = '';
+ document.getElementById('outlet_status').value = 'active';
+ document.getElementById('outletModalTitle').innerText = 'Add Outlet';
+ document.getElementById('outletSubmitBtn').name = 'add_outlet';
+ document.getElementById('outletSubmitBtn').innerText = 'Save Outlet';
+ });
+ </script>
+
<?php elseif ($page === 'customers' || $page === 'suppliers'): ?>
<div class="card p-4">
<div class="d-flex justify-content-between align-items-center mb-4">

1231
includes/SimpleXLSX.php Normal file

File diff suppressed because it is too large Load Diff

450
index.php
View File

@ -1317,14 +1317,216 @@ function getPromotionalPrice($item) {
redirectWithMessage("Expense recorded!", "index.php?page=expenses");
}
if (isset($_POST['import_customers'])) {
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
$tmpPath = $_FILES['excel_file']['tmp_name'];
$firstBytes = file_get_contents($tmpPath, false, null, 0, 4);
$count = 0; $errors = 0;
if ($firstBytes === "PK\x03\x04") {
require_once __DIR__ . '/includes/SimpleXLSX.php';
if ($xlsx = \Shuchkin\SimpleXLSX::parse($tmpPath)) {
foreach ($xlsx->rows() as $i => $data) {
if ($i === 0) continue;
$name = trim($data[0] ?? '');
if (!$name) { $errors++; continue; }
$email = trim($data[1] ?? '');
$phone = trim($data[2] ?? '');
$tax_id = trim($data[3] ?? '');
$balance = (float)($data[4] ?? 0);
db()->prepare("INSERT INTO customers (name, email, phone, tax_id, balance) VALUES (?, ?, ?, ?, ?)")
->execute([$name, $email, $phone, $tax_id, $balance]);
$count++;
}
}
} else {
// CSV fallback
$handle = fopen($tmpPath, "r");
fgetcsv($handle); // skip header
while (($data = fgetcsv($handle)) !== FALSE) {
$name = trim($data[0] ?? '');
if (!$name) { $errors++; continue; }
$email = trim($data[1] ?? '');
$phone = trim($data[2] ?? '');
$tax_id = trim($data[3] ?? '');
$balance = (float)($data[4] ?? 0);
db()->prepare("INSERT INTO customers (name, email, phone, tax_id, balance) VALUES (?, ?, ?, ?, ?)")
->execute([$name, $email, $phone, $tax_id, $balance]);
$count++;
}
fclose($handle);
}
redirectWithMessage("Customers imported! $count processed." . ($errors ? " ($errors skipped)" : ""), "index.php?page=customers");
}
}
if (isset($_POST['import_suppliers'])) {
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
$tmpPath = $_FILES['excel_file']['tmp_name'];
$firstBytes = file_get_contents($tmpPath, false, null, 0, 4);
$count = 0; $errors = 0;
if ($firstBytes === "PK\x03\x04") {
require_once __DIR__ . '/includes/SimpleXLSX.php';
if ($xlsx = \Shuchkin\SimpleXLSX::parse($tmpPath)) {
foreach ($xlsx->rows() as $i => $data) {
if ($i === 0) continue;
$name = trim($data[0] ?? '');
if (!$name) { $errors++; continue; }
$email = trim($data[1] ?? '');
$phone = trim($data[2] ?? '');
$tax_id = trim($data[3] ?? '');
$balance = (float)($data[4] ?? 0);
db()->prepare("INSERT INTO suppliers (name, email, phone, tax_id, balance) VALUES (?, ?, ?, ?, ?)")
->execute([$name, $email, $phone, $tax_id, $balance]);
$count++;
}
}
} else {
// CSV fallback
$handle = fopen($tmpPath, "r");
fgetcsv($handle); // skip header
while (($data = fgetcsv($handle)) !== FALSE) {
$name = trim($data[0] ?? '');
if (!$name) { $errors++; continue; }
$email = trim($data[1] ?? '');
$phone = trim($data[2] ?? '');
$tax_id = trim($data[3] ?? '');
$balance = (float)($data[4] ?? 0);
db()->prepare("INSERT INTO suppliers (name, email, phone, tax_id, balance) VALUES (?, ?, ?, ?, ?)")
->execute([$name, $email, $phone, $tax_id, $balance]);
$count++;
}
fclose($handle);
}
redirectWithMessage("Suppliers imported! $count processed." . ($errors ? " ($errors skipped)" : ""), "index.php?page=suppliers");
}
}
if (isset($_POST['import_categories'])) {
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
$tmpPath = $_FILES['excel_file']['tmp_name'];
$firstBytes = file_get_contents($tmpPath, false, null, 0, 4);
$count = 0; $errors = 0;
if ($firstBytes === "PK\x03\x04") {
require_once __DIR__ . '/includes/SimpleXLSX.php';
if ($xlsx = \Shuchkin\SimpleXLSX::parse($tmpPath)) {
foreach ($xlsx->rows() as $i => $data) {
if ($i === 0) continue;
$name_en = trim($data[0] ?? '');
if (!$name_en) { $errors++; continue; }
$name_ar = trim($data[1] ?? '');
db()->prepare("INSERT INTO stock_categories (name_en, name_ar) VALUES (?, ?)")
->execute([$name_en, $name_ar]);
$count++;
}
}
} else {
$handle = fopen($tmpPath, "r");
fgetcsv($handle); // skip header
while (($data = fgetcsv($handle)) !== FALSE) {
$name_en = trim($data[0] ?? '');
if (!$name_en) { $errors++; continue; }
$name_ar = trim($data[1] ?? '');
db()->prepare("INSERT INTO stock_categories (name_en, name_ar) VALUES (?, ?)")
->execute([$name_en, $name_ar]);
$count++;
}
fclose($handle);
}
redirectWithMessage("Categories imported! $count processed.", "index.php?page=stock_categories");
}
}
if (isset($_POST['import_units'])) {
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
$tmpPath = $_FILES['excel_file']['tmp_name'];
$firstBytes = file_get_contents($tmpPath, false, null, 0, 4);
$count = 0; $errors = 0;
if ($firstBytes === "PK\x03\x04") {
require_once __DIR__ . '/includes/SimpleXLSX.php';
if ($xlsx = \Shuchkin\SimpleXLSX::parse($tmpPath)) {
foreach ($xlsx->rows() as $i => $data) {
if ($i === 0) continue;
$name_en = trim($data[0] ?? '');
if (!$name_en) { $errors++; continue; }
$name_ar = trim($data[1] ?? '');
$short_name_en = trim($data[2] ?? '');
$short_name_ar = trim($data[3] ?? '');
db()->prepare("INSERT INTO stock_units (name_en, name_ar, short_name_en, short_name_ar) VALUES (?, ?, ?, ?)")
->execute([$name_en, $name_ar, $short_name_en, $short_name_ar]);
$count++;
}
}
} else {
$handle = fopen($tmpPath, "r");
fgetcsv($handle);
while (($data = fgetcsv($handle)) !== FALSE) {
$name_en = trim($data[0] ?? '');
if (!$name_en) { $errors++; continue; }
$name_ar = trim($data[1] ?? '');
$short_name_en = trim($data[2] ?? '');
$short_name_ar = trim($data[3] ?? '');
db()->prepare("INSERT INTO stock_units (name_en, name_ar, short_name_en, short_name_ar) VALUES (?, ?, ?, ?)")
->execute([$name_en, $name_ar, $short_name_en, $short_name_ar]);
$count++;
}
fclose($handle);
}
redirectWithMessage("Units imported! $count processed.", "index.php?page=stock_units");
}
}
if (isset($_POST['import_items'])) {
error_log("Import items triggered. POST: " . print_r($_POST, true));
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
$tmpPath = $_FILES['excel_file']['tmp_name'];
error_log("File uploaded to: $tmpPath");
$firstBytes = file_get_contents($tmpPath, false, null, 0, 4);
if ($firstBytes === "PK\x03\x04") {
$message = "Error: It looks like you uploaded an Excel (.xlsx) file. Please save it as CSV (UTF-8) and try again.";
// EXCEL IMPORT VIA SimpleXLSX
require_once __DIR__ . '/includes/SimpleXLSX.php';
if ($xlsx = \Shuchkin\SimpleXLSX::parse($tmpPath)) {
$rows = $xlsx->rows();
$rowNum = 0;
$count = 0;
$errors = 0;
foreach ($rows as $data) {
$rowNum++;
if ($rowNum === 1) continue; // Skip header
$sku = trim($data[0] ?? '');
$name_en = trim($data[1] ?? '');
$name_ar = trim($data[2] ?? '');
$sale_price = (float)($data[3] ?? 0);
$purchase_price = (float)($data[4] ?? 0);
$qty = (float)($data[5] ?? 0);
$vat_rate = (float)($data[6] ?? 0);
if (!$sku && !$name_en) { $errors++; continue; }
$check = db()->prepare("SELECT id FROM stock_items WHERE sku = ?");
$check->execute([$sku]);
if ($check->fetch()) {
db()->prepare("UPDATE stock_items SET name_en = ?, name_ar = ?, sale_price = ?, purchase_price = ?, stock_quantity = ?, vat_rate = ? WHERE sku = ?")
->execute([$name_en, $name_ar, $sale_price, $purchase_price, $qty, $vat_rate, $sku]);
} else {
db()->prepare("INSERT INTO stock_items (sku, name_en, name_ar, sale_price, purchase_price, stock_quantity, vat_rate) VALUES (?, ?, ?, ?, ?, ?, ?)")
->execute([$sku, $name_en, $name_ar, $sale_price, $purchase_price, $qty, $vat_rate]);
}
$count++;
}
redirectWithMessage("Excel Import completed! $count items processed." . ($errors > 0 ? " ($errors rows skipped.)" : ""), "index.php?page=items");
} else {
$message = "Error parsing Excel file: " . \Shuchkin\SimpleXLSX::parseError();
}
} else {
// Check for BOM and skip it
if (substr($firstBytes, 0, 3) === "\xEF\xBB\xBF") {
@ -1666,14 +1868,216 @@ function getPromotionalPrice($item) {
$message = "Expense recorded!";
}
if (isset($_POST['import_customers'])) {
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
$tmpPath = $_FILES['excel_file']['tmp_name'];
$firstBytes = file_get_contents($tmpPath, false, null, 0, 4);
$count = 0; $errors = 0;
if ($firstBytes === "PK\x03\x04") {
require_once __DIR__ . '/includes/SimpleXLSX.php';
if ($xlsx = \Shuchkin\SimpleXLSX::parse($tmpPath)) {
foreach ($xlsx->rows() as $i => $data) {
if ($i === 0) continue;
$name = trim($data[0] ?? '');
if (!$name) { $errors++; continue; }
$email = trim($data[1] ?? '');
$phone = trim($data[2] ?? '');
$tax_id = trim($data[3] ?? '');
$balance = (float)($data[4] ?? 0);
db()->prepare("INSERT INTO customers (name, email, phone, tax_id, balance) VALUES (?, ?, ?, ?, ?)")
->execute([$name, $email, $phone, $tax_id, $balance]);
$count++;
}
}
} else {
// CSV fallback
$handle = fopen($tmpPath, "r");
fgetcsv($handle); // skip header
while (($data = fgetcsv($handle)) !== FALSE) {
$name = trim($data[0] ?? '');
if (!$name) { $errors++; continue; }
$email = trim($data[1] ?? '');
$phone = trim($data[2] ?? '');
$tax_id = trim($data[3] ?? '');
$balance = (float)($data[4] ?? 0);
db()->prepare("INSERT INTO customers (name, email, phone, tax_id, balance) VALUES (?, ?, ?, ?, ?)")
->execute([$name, $email, $phone, $tax_id, $balance]);
$count++;
}
fclose($handle);
}
redirectWithMessage("Customers imported! $count processed." . ($errors ? " ($errors skipped)" : ""), "index.php?page=customers");
}
}
if (isset($_POST['import_suppliers'])) {
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
$tmpPath = $_FILES['excel_file']['tmp_name'];
$firstBytes = file_get_contents($tmpPath, false, null, 0, 4);
$count = 0; $errors = 0;
if ($firstBytes === "PK\x03\x04") {
require_once __DIR__ . '/includes/SimpleXLSX.php';
if ($xlsx = \Shuchkin\SimpleXLSX::parse($tmpPath)) {
foreach ($xlsx->rows() as $i => $data) {
if ($i === 0) continue;
$name = trim($data[0] ?? '');
if (!$name) { $errors++; continue; }
$email = trim($data[1] ?? '');
$phone = trim($data[2] ?? '');
$tax_id = trim($data[3] ?? '');
$balance = (float)($data[4] ?? 0);
db()->prepare("INSERT INTO suppliers (name, email, phone, tax_id, balance) VALUES (?, ?, ?, ?, ?)")
->execute([$name, $email, $phone, $tax_id, $balance]);
$count++;
}
}
} else {
// CSV fallback
$handle = fopen($tmpPath, "r");
fgetcsv($handle); // skip header
while (($data = fgetcsv($handle)) !== FALSE) {
$name = trim($data[0] ?? '');
if (!$name) { $errors++; continue; }
$email = trim($data[1] ?? '');
$phone = trim($data[2] ?? '');
$tax_id = trim($data[3] ?? '');
$balance = (float)($data[4] ?? 0);
db()->prepare("INSERT INTO suppliers (name, email, phone, tax_id, balance) VALUES (?, ?, ?, ?, ?)")
->execute([$name, $email, $phone, $tax_id, $balance]);
$count++;
}
fclose($handle);
}
redirectWithMessage("Suppliers imported! $count processed." . ($errors ? " ($errors skipped)" : ""), "index.php?page=suppliers");
}
}
if (isset($_POST['import_categories'])) {
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
$tmpPath = $_FILES['excel_file']['tmp_name'];
$firstBytes = file_get_contents($tmpPath, false, null, 0, 4);
$count = 0; $errors = 0;
if ($firstBytes === "PK\x03\x04") {
require_once __DIR__ . '/includes/SimpleXLSX.php';
if ($xlsx = \Shuchkin\SimpleXLSX::parse($tmpPath)) {
foreach ($xlsx->rows() as $i => $data) {
if ($i === 0) continue;
$name_en = trim($data[0] ?? '');
if (!$name_en) { $errors++; continue; }
$name_ar = trim($data[1] ?? '');
db()->prepare("INSERT INTO stock_categories (name_en, name_ar) VALUES (?, ?)")
->execute([$name_en, $name_ar]);
$count++;
}
}
} else {
$handle = fopen($tmpPath, "r");
fgetcsv($handle); // skip header
while (($data = fgetcsv($handle)) !== FALSE) {
$name_en = trim($data[0] ?? '');
if (!$name_en) { $errors++; continue; }
$name_ar = trim($data[1] ?? '');
db()->prepare("INSERT INTO stock_categories (name_en, name_ar) VALUES (?, ?)")
->execute([$name_en, $name_ar]);
$count++;
}
fclose($handle);
}
redirectWithMessage("Categories imported! $count processed.", "index.php?page=stock_categories");
}
}
if (isset($_POST['import_units'])) {
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
$tmpPath = $_FILES['excel_file']['tmp_name'];
$firstBytes = file_get_contents($tmpPath, false, null, 0, 4);
$count = 0; $errors = 0;
if ($firstBytes === "PK\x03\x04") {
require_once __DIR__ . '/includes/SimpleXLSX.php';
if ($xlsx = \Shuchkin\SimpleXLSX::parse($tmpPath)) {
foreach ($xlsx->rows() as $i => $data) {
if ($i === 0) continue;
$name_en = trim($data[0] ?? '');
if (!$name_en) { $errors++; continue; }
$name_ar = trim($data[1] ?? '');
$short_name_en = trim($data[2] ?? '');
$short_name_ar = trim($data[3] ?? '');
db()->prepare("INSERT INTO stock_units (name_en, name_ar, short_name_en, short_name_ar) VALUES (?, ?, ?, ?)")
->execute([$name_en, $name_ar, $short_name_en, $short_name_ar]);
$count++;
}
}
} else {
$handle = fopen($tmpPath, "r");
fgetcsv($handle);
while (($data = fgetcsv($handle)) !== FALSE) {
$name_en = trim($data[0] ?? '');
if (!$name_en) { $errors++; continue; }
$name_ar = trim($data[1] ?? '');
$short_name_en = trim($data[2] ?? '');
$short_name_ar = trim($data[3] ?? '');
db()->prepare("INSERT INTO stock_units (name_en, name_ar, short_name_en, short_name_ar) VALUES (?, ?, ?, ?)")
->execute([$name_en, $name_ar, $short_name_en, $short_name_ar]);
$count++;
}
fclose($handle);
}
redirectWithMessage("Units imported! $count processed.", "index.php?page=stock_units");
}
}
if (isset($_POST['import_items'])) {
error_log("Import items triggered. POST: " . print_r($_POST, true));
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
$tmpPath = $_FILES['excel_file']['tmp_name'];
error_log("File uploaded to: $tmpPath");
$firstBytes = file_get_contents($tmpPath, false, null, 0, 4);
if ($firstBytes === "PK\x03\x04") {
$message = "Error: It looks like you uploaded an Excel (.xlsx) file. Please save it as CSV (UTF-8) and try again.";
// EXCEL IMPORT VIA SimpleXLSX
require_once __DIR__ . '/includes/SimpleXLSX.php';
if ($xlsx = \Shuchkin\SimpleXLSX::parse($tmpPath)) {
$rows = $xlsx->rows();
$rowNum = 0;
$count = 0;
$errors = 0;
foreach ($rows as $data) {
$rowNum++;
if ($rowNum === 1) continue; // Skip header
$sku = trim($data[0] ?? '');
$name_en = trim($data[1] ?? '');
$name_ar = trim($data[2] ?? '');
$sale_price = (float)($data[3] ?? 0);
$purchase_price = (float)($data[4] ?? 0);
$qty = (float)($data[5] ?? 0);
$vat_rate = (float)($data[6] ?? 0);
if (!$sku && !$name_en) { $errors++; continue; }
$check = db()->prepare("SELECT id FROM stock_items WHERE sku = ?");
$check->execute([$sku]);
if ($check->fetch()) {
db()->prepare("UPDATE stock_items SET name_en = ?, name_ar = ?, sale_price = ?, purchase_price = ?, stock_quantity = ?, vat_rate = ? WHERE sku = ?")
->execute([$name_en, $name_ar, $sale_price, $purchase_price, $qty, $vat_rate, $sku]);
} else {
db()->prepare("INSERT INTO stock_items (sku, name_en, name_ar, sale_price, purchase_price, stock_quantity, vat_rate) VALUES (?, ?, ?, ?, ?, ?, ?)")
->execute([$sku, $name_en, $name_ar, $sale_price, $purchase_price, $qty, $vat_rate]);
}
$count++;
}
redirectWithMessage("Excel Import completed! $count items processed." . ($errors > 0 ? " ($errors rows skipped.)" : ""), "index.php?page=items");
} else {
$message = "Error parsing Excel file: " . \Shuchkin\SimpleXLSX::parseError();
}
} else {
// Check for BOM and skip it
if (substr($firstBytes, 0, 3) === "\xEF\xBB\xBF") {
@ -2254,7 +2658,7 @@ if (isset($_POST['add_hr_department'])) {
$group_id = (int)($_POST['group_id'] ?? 0) ?: null;
if ($username && $password) {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$stmt = db()->prepare("INSERT INTO users (outlet_id, username, password, email, phone, group_id) VALUES (?, ?, ?, ?, ?, ?)");
$stmt = db()->prepare("INSERT INTO users (username, password, email, phone, group_id) VALUES (?, ?, ?, ?, ?)");
try {
$stmt->execute([$username, $hashed_password, $email, $phone, $group_id]);
$message = "User added successfully!";
@ -10844,8 +11248,8 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<div class="modal-body">
<div class="alert alert-info py-2">
<div class="mb-2">
<small data-en="Please upload a CSV file with the following columns: SKU, English Name, Arabic Name, Sale Price, Cost Price." data-ar="يرجى رفع ملف CSV بالأعمدة التالية: الباركود، الاسم الإنجليزي، الاسم العربي، سعر البيع، سعر التكلفة.">
Please upload a CSV file with the following columns: SKU, English Name, Arabic Name, Sale Price, Cost Price.
<small data-en="Please upload an Excel (.xlsx) or CSV file with the following columns: SKU, English Name, Arabic Name, Sale Price, Cost Price." data-ar="يرجى رفع ملف إكسل (.xlsx) أو CSV بالأعمدة التالية: الباركود، الاسم الإنجليزي، الاسم العربي، سعر البيع، سعر التكلفة.">
Please upload an Excel (.xlsx) or CSV file with the following columns: SKU, English Name, Arabic Name, Sale Price, Cost Price.
</small>
</div>
<a href="index.php?action=download_items_template" class="btn btn-sm btn-primary">
@ -10853,8 +11257,8 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</a>
</div>
<div class="mb-3">
<label class="form-label" data-en="Choose CSV File" data-ar="اختر ملف CSV">Choose CSV File</label>
<input type="file" name="excel_file" class="form-control" accept=".csv" required>
<label class="form-label" data-en="Choose File" data-ar="اختر الملف">Choose File</label>
<input type="file" name="excel_file" class="form-control" accept=".csv, .xlsx" required>
</div>
</div>
<div class="modal-footer">
@ -10877,13 +11281,13 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<form method="POST" enctype="multipart/form-data">
<div class="modal-body">
<div class="alert alert-info py-2">
<small data-en="Please upload a CSV file with the following columns: Name, Email, Phone, Tax ID, Balance." data-ar="يرجى رفع ملف CSV بالأعمدة التالية: الاسم، البريد الإلكتروني، الهاتف، الرقم الضريبي، الرصيد.">
Please upload a CSV file with the following columns: Name, Email, Phone, Tax ID, Balance.
<small data-en="Please upload an Excel (.xlsx) or CSV file with the following columns: Name, Email, Phone, Tax ID, Balance." data-ar="يرجى رفع ملف إكسل (.xlsx) أو CSV بالأعمدة التالية: الاسم، البريد الإلكتروني، الهاتف، الرقم الضريبي، الرصيد.">
Please upload an Excel (.xlsx) or CSV file with the following columns: Name, Email, Phone, Tax ID, Balance.
</small>
</div>
<div class="mb-3">
<label class="form-label" data-en="Choose CSV File" data-ar="اختر ملف CSV">Choose CSV File</label>
<input type="file" name="excel_file" class="form-control" accept=".csv" required>
<label class="form-label" data-en="Choose File" data-ar="اختر الملف">Choose File</label>
<input type="file" name="excel_file" class="form-control" accept=".csv, .xlsx" required>
</div>
</div>
<div class="modal-footer">
@ -10906,13 +11310,13 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<form method="POST" enctype="multipart/form-data">
<div class="modal-body">
<div class="alert alert-info py-2">
<small data-en="Please upload a CSV file with the following columns: Name, Email, Phone, Tax ID, Balance." data-ar="يرجى رفع ملف CSV بالأعمدة التالية: الاسم، البريد الإلكتروني، الهاتف، الرقم الضريبي، الرصيد.">
Please upload a CSV file with the following columns: Name, Email, Phone, Tax ID, Balance.
<small data-en="Please upload an Excel (.xlsx) or CSV file with the following columns: Name, Email, Phone, Tax ID, Balance." data-ar="يرجى رفع ملف إكسل (.xlsx) أو CSV بالأعمدة التالية: الاسم، البريد الإلكتروني، الهاتف، الرقم الضريبي، الرصيد.">
Please upload an Excel (.xlsx) or CSV file with the following columns: Name, Email, Phone, Tax ID, Balance.
</small>
</div>
<div class="mb-3">
<label class="form-label" data-en="Choose CSV File" data-ar="اختر ملف CSV">Choose CSV File</label>
<input type="file" name="excel_file" class="form-control" accept=".csv" required>
<label class="form-label" data-en="Choose File" data-ar="اختر الملف">Choose File</label>
<input type="file" name="excel_file" class="form-control" accept=".csv, .xlsx" required>
</div>
</div>
<div class="modal-footer">
@ -10935,13 +11339,13 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<form method="POST" enctype="multipart/form-data">
<div class="modal-body">
<div class="alert alert-info py-2">
<small data-en="Please upload a CSV file with the following columns: Name (EN), Name (AR)." data-ar="يرجى رفع ملف CSV بالأعمدة التالية: الاسم (إنجليزي)، الاسم (عربي).">
Please upload a CSV file with the following columns: Name (EN), Name (AR).
<small data-en="Please upload an Excel (.xlsx) or CSV file with the following columns: Name (EN), Name (AR)." data-ar="يرجى رفع ملف إكسل (.xlsx) أو CSV بالأعمدة التالية: الاسم (إنجليزي)، الاسم (عربي).">
Please upload an Excel (.xlsx) or CSV file with the following columns: Name (EN), Name (AR).
</small>
</div>
<div class="mb-3">
<label class="form-label" data-en="Choose CSV File" data-ar="اختر ملف CSV">Choose CSV File</label>
<input type="file" name="excel_file" class="form-control" accept=".csv" required>
<label class="form-label" data-en="Choose File" data-ar="اختر الملف">Choose File</label>
<input type="file" name="excel_file" class="form-control" accept=".csv, .xlsx" required>
</div>
</div>
<div class="modal-footer">
@ -10964,13 +11368,13 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<form method="POST" enctype="multipart/form-data">
<div class="modal-body">
<div class="alert alert-info py-2">
<small data-en="Please upload a CSV file with the following columns: Name (EN), Name (AR), Short (EN), Short (AR)." data-ar="يرجى رفع ملف CSV بالأعمدة التالية: الاسم (إنجليزي)، الاسم (عربي)، الاختصار (إنجليزي)، الاختصار (عربي).">
Please upload a CSV file with the following columns: Name (EN), Name (AR), Short (EN), Short (AR).
<small data-en="Please upload an Excel (.xlsx) or CSV file with the following columns: Name (EN), Name (AR), Short (EN), Short (AR)." data-ar="يرجى رفع ملف إكسل (.xlsx) أو CSV بالأعمدة التالية: الاسم (إنجليزي)، الاسم (عربي)، الاختصار (إنجليزي)، الاختصار (عربي).">
Please upload an Excel (.xlsx) or CSV file with the following columns: Name (EN), Name (AR), Short (EN), Short (AR).
</small>
</div>
<div class="mb-3">
<label class="form-label" data-en="Choose CSV File" data-ar="اختر ملف CSV">Choose CSV File</label>
<input type="file" name="excel_file" class="form-control" accept=".csv" required>
<label class="form-label" data-en="Choose File" data-ar="اختر الملف">Choose File</label>
<input type="file" name="excel_file" class="form-control" accept=".csv, .xlsx" required>
</div>
</div>
<div class="modal-footer">

View File

@ -1,65 +0,0 @@
431
481
486
498
514
518
781
828
832
855
916
932
989
1003
1062
1110
1125
1183
1215
1221
1258
1264
1290
1306
1370
1387
1393
1451
1466
1527
1559
1565
1602
1608
1641
1655
1733
1757
1762
1848
1867
1900
1945
1974
2040
2045
2091
2096
2126
2173
2177
2187
2221
2226
2248
2277
2335
2400
2413
2454
2487
2523
2559
2592
2628

View File

@ -1,90 +0,0 @@
$content = file_get_contents('index.php');
$search = " 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);
}";
$replace = " default:
if (can('dashboard_view')) {
\$oW = ((\$_SESSION['outlet_id'] ?? 1) == 0) ? \"\" : \"WHERE outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);
\$oA = ((\$_SESSION['outlet_id'] ?? 1) == 0) ? \"\" : \"AND outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);
\$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 \$oW\")->fetchColumn() ?: 0) + (db()->query(\"SELECT SUM(net_amount) FROM pos_transactions WHERE status = 'completed' \$oA\")->fetchColumn() ?: 0),
'total_received' => (db()->query(\"SELECT SUM(amount) FROM payments \$oW\")->fetchColumn() ?: 0) + (db()->query(\"SELECT SUM(amount) FROM pos_payments \$oW\")->fetchColumn() ?: 0),
'total_purchases' => db()->query(\"SELECT SUM(total_with_vat) FROM purchases \$oW\")->fetchColumn() ?: 0,
'total_paid' => db()->query(\"SELECT SUM(amount) FROM purchase_payments \$oW\")->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 \$oW 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 \$oW GROUP BY label ORDER BY label ASC LIMIT 5\")->fetchAll(PDO::FETCH_ASSOC);
}";
if (strpos($content, $search) !== false) {
$content = str_replace($search, $replace, $content);
echo "Dashboard patched successfully\n";
file_put_contents('index.php', $content);
} else {
echo "Could not find target to patch\n";
}
$content = file_get_contents('index.php');
$replacements = [
[
"case 'sales':\n case 'purchases':\n \$type = (\$page === 'sales') ? 'sale' : 'purchase';\n \$table = (\$type === 'purchase') ? 'purchases' : 'invoices';\n \$cust_supplier_col = (\$type === 'purchase') ? 'supplier_id' : 'customer_id';\n \$cust_supplier_table = (\$type === 'purchase') ? 'suppliers' : 'customers';\n \n \$where = [\"1=1\"];",
"case 'sales':\n case 'purchases':\n \$type = (\$page === 'sales') ? 'sale' : 'purchase';\n \$table = (\$type === 'purchase') ? 'purchases' : 'invoices';\n \$cust_supplier_col = (\$type === 'purchase') ? 'supplier_id' : 'customer_id';\n \$cust_supplier_table = (\$type === 'purchase') ? 'suppliers' : 'customers';\n \n \$where = [];\n if ((\$_SESSION['outlet_id'] ?? 1) != 0) \$where[] = \"v.outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);\n else \$where[] = \"1=1\";"
],
[
"case 'quotations':\n \$where = [\"1=1\"];",
"case 'quotations':\n \$where = [];\n if ((\$_SESSION['outlet_id'] ?? 1) != 0) \$where[] = \"q.outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);\n else \$where[] = \"1=1\";"
],
[
"case 'lpos':\n \$where = [\"1=1\"];",
"case 'lpos':\n \$where = [];\n if ((\$_SESSION['outlet_id'] ?? 1) != 0) \$where[] = \"q.outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);\n else \$where[] = \"1=1\";"
],
[
"case 'expenses':\n \$where = [\"1=1\"];",
"case 'expenses':\n \$where = [];\n if ((\$_SESSION['outlet_id'] ?? 1) != 0) \$where[] = \"e.outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);\n else \$where[] = \"1=1\";"
],
[
"case 'payments':",
"case 'payments':\n \$where = [];\n if ((\$_SESSION['outlet_id'] ?? 1) != 0) \$where[] = \"p.outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);\n else \$where[] = \"1=1\";"
]
];
foreach ($replacements as $rep) {
if (strpos($content, $rep[0]) !== false) {
$content = str_replace($rep[0], $rep[1], $content);
echo "Patched successfully\n";
} else {
echo "Could not find target to patch\n";
}
}
file_put_contents('index.php', $content);

View File

@ -1,56 +0,0 @@
<?php
$content = file_get_contents('index.php');
$search = " 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);
}";
$replace = " default:
if (can('dashboard_view')) {
\$oW = ((\$_SESSION['outlet_id'] ?? 1) == 0) ? \"\" : \"WHERE outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);
\$oA = ((\$_SESSION['outlet_id'] ?? 1) == 0) ? \"\" : \"AND outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);
\$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 \$oW\")->fetchColumn() ?: 0) + (db()->query(\"SELECT SUM(net_amount) FROM pos_transactions WHERE status = 'completed' \$oA\")->fetchColumn() ?: 0),
'total_received' => (db()->query(\"SELECT SUM(amount) FROM payments \$oW\")->fetchColumn() ?: 0) + (db()->query(\"SELECT SUM(amount) FROM pos_payments \$oW\")->fetchColumn() ?: 0),
'total_purchases' => db()->query(\"SELECT SUM(total_with_vat) FROM purchases \$oW\")->fetchColumn() ?: 0,
'total_paid' => db()->query(\"SELECT SUM(amount) FROM purchase_payments \$oW\")->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 \$oW 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 \$oW GROUP BY label ORDER BY label ASC LIMIT 5\")->fetchAll(PDO::FETCH_ASSOC);
}";
if (strpos($content, $search) !== false) {
$content = str_replace($search, $replace, $content);
echo "Dashboard patched successfully\n";
file_put_contents('index.php', $content);
} else {
echo "Could not find target to patch\n";
}

View File

@ -1,68 +0,0 @@
<?php
$content = file_get_contents('index.php');
$patterns = [
// Invoices (POS)
[
'INSERT INTO invoices (transaction_no, customer_id, invoice_date, payment_type, total_amount, vat_amount, total_with_vat, paid_amount, status, register_session_id, is_pos, discount_amount, loyalty_points_redeemed, created_by)',
'INSERT INTO invoices (outlet_id, transaction_no, customer_id, invoice_date, payment_type, total_amount, vat_amount, total_with_vat, paid_amount, status, register_session_id, is_pos, discount_amount, loyalty_points_redeemed, created_by)',
'?, ?, ?, ?, ?, ?, ?, ?, ?, \'paid\', ?, 1, ?, ?, ?',
'".(int)($_SESSION["outlet_id"] ?? 1).", ?, ?, ?, ?, ?, ?, ?, ?, \'paid\', ?, 1, ?, ?, ?'
],
// Quotations
[
'INSERT INTO quotations (customer_id, quotation_date, valid_until, status, total_amount, vat_amount, total_with_vat)',
'INSERT INTO quotations (outlet_id, customer_id, quotation_date, valid_until, status, total_amount, vat_amount, total_with_vat)',
'?, ?, ?, ?, ?, ?, ?',
'".(int)($_SESSION["outlet_id"] ?? 1).", ?, ?, ?, ?, ?, ?, ?'
],
// Lpos
[
'INSERT INTO lpos (supplier_id, lpo_date, delivery_date, status, total_amount, vat_amount, total_with_vat, terms_conditions)',
'INSERT INTO lpos (outlet_id, supplier_id, lpo_date, delivery_date, status, total_amount, vat_amount, total_with_vat, terms_conditions)',
'?, ?, ?, \'pending\', ?, ?, ?, ?',
'".(int)($_SESSION["outlet_id"] ?? 1).", ?, ?, ?, \'pending\', ?, ?, ?, ?'
],
// Invoices (General)
[
'INSERT INTO invoices (customer_id, invoice_date, status, payment_type, total_amount, vat_amount, total_with_vat, paid_amount)',
'INSERT INTO invoices (outlet_id, customer_id, invoice_date, status, payment_type, total_amount, vat_amount, total_with_vat, paid_amount)',
'?, ?, \'unpaid\', \'credit\', ?, ?, ?, 0',
'".(int)($_SESSION["outlet_id"] ?? 1).", ?, ?, \'unpaid\', \'credit\', ?, ?, ?, 0'
],
// Purchases (General)
[
'INSERT INTO purchases (supplier_id, invoice_date, status, payment_type, total_amount, vat_amount, total_with_vat, paid_amount)',
'INSERT INTO purchases (outlet_id, supplier_id, invoice_date, status, payment_type, total_amount, vat_amount, total_with_vat, paid_amount)',
'?, ?, \'unpaid\', \'credit\', ?, ?, ?, 0',
'".(int)($_SESSION["outlet_id"] ?? 1).", ?, ?, \'unpaid\', \'credit\', ?, ?, ?, 0'
],
// Expenses
[
'INSERT INTO expenses (category_id, amount, expense_date, reference_no, description)',
'INSERT INTO expenses (outlet_id, category_id, amount, expense_date, reference_no, description)',
'(int)$_POST[\'category_id\'], $amt, $date, $_POST[\'reference_no\'] ?? \'\', $desc',
'".(int)($_SESSION["outlet_id"] ?? 1).", (int)$_POST[\'category_id\'], $amt, $date, $_POST[\'reference_no\'] ?? \'\', $desc'
],
// Users
[
'INSERT INTO users (username, password, email, phone, group_id)',
'INSERT INTO users (outlet_id, username, password, email, phone, group_id)',
'?, ?, ?, ?, ?',
'".(int)($_SESSION["outlet_id"] ?? 1).", ?, ?, ?, ?, ?'
]
];
foreach ($patterns as $p) {
if (strpos($content, $p[0]) !== false) {
$content = str_replace($p[0], $p[1], $content);
$content = str_replace($p[2], $p[3], $content);
echo "Patched " . explode(' ', $p[0])[2] . "\n";
}
}
// Don't forget invoice_items insertion
$p1 = 'INSERT INTO invoice_items (invoice_id, item_id, quantity, unit_price, vat_amount, total_price) VALUES (?, ?, ?, ?, ?, ?)';
$p2 = 'INSERT INTO invoice_items (invoice_id, item_id, quantity, unit_price, vat_amount, total_price) VALUES (?, ?, ?, ?, ?, ?)';
// Actually wait, invoice_items doesn't have outlet_id... No wait, does it?
file_put_contents('index.php', $content);

View File

@ -1,78 +0,0 @@
<?php
$content = file_get_contents('index.php');
$replacements = [
// Invoices (POS)
[
'INSERT INTO invoices (transaction_no, customer_id, invoice_date, payment_type, total_amount, vat_amount, total_with_vat, paid_amount, status, register_session_id, is_pos, discount_amount, loyalty_points_redeemed, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, \'paid\', ?, 1, ?, ?, ?)',
'INSERT INTO invoices (outlet_id, transaction_no, customer_id, invoice_date, payment_type, total_amount, vat_amount, total_with_vat, paid_amount, status, register_session_id, is_pos, discount_amount, loyalty_points_redeemed, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, \'paid\', ?, 1, ?, ?, ?)'
],
[
'execute([$transaction_no, $customer_id, date(\'Y-m-d\'), \'pos\', $total_amount, $tax_amount, $net_amount, $net_amount, $session_id, $discount_amount, $loyalty_redeemed, $_SESSION[\'user_id\']])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), $transaction_no, $customer_id, date(\'Y-m-d\'), \'pos\', $total_amount, $tax_amount, $net_amount, $net_amount, $session_id, $discount_amount, $loyalty_redeemed, $_SESSION[\'user_id\']])'
],
// Quotations
[
'INSERT INTO quotations (customer_id, quotation_date, valid_until, status, total_amount, vat_amount, total_with_vat) VALUES (?, ?, ?, \'pending\', ?, ?, ?)',
'INSERT INTO quotations (outlet_id, customer_id, quotation_date, valid_until, status, total_amount, vat_amount, total_with_vat) VALUES (?, ?, ?, ?, \'pending\', ?, ?, ?)'
],
[
'execute([$_POST[\'customer_id\'], $_POST[\'date\'], $_POST[\'valid_until\'], $totals[\'total\'], $totals[\'tax\'], $totals[\'net\']])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), $_POST[\'customer_id\'], $_POST[\'date\'], $_POST[\'valid_until\'], $totals[\'total\'], $totals[\'tax\'], $totals[\'net\']])'
],
// Lpos
[
'INSERT INTO lpos (supplier_id, lpo_date, delivery_date, status, total_amount, vat_amount, total_with_vat, terms_conditions) VALUES (?, ?, ?, \'pending\', ?, ?, ?, ?)',
'INSERT INTO lpos (outlet_id, supplier_id, lpo_date, delivery_date, status, total_amount, vat_amount, total_with_vat, terms_conditions) VALUES (?, ?, ?, ?, \'pending\', ?, ?, ?, ?)'
],
[
'execute([$_POST[\'supplier_id\'], $_POST[\'date\'], $_POST[\'delivery_date\'], $totals[\'total\'], $totals[\'tax\'], $totals[\'net\'], $_POST[\'terms\'] ?? \'\'])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), $_POST[\'supplier_id\'], $_POST[\'date\'], $_POST[\'delivery_date\'], $totals[\'total\'], $totals[\'tax\'], $totals[\'net\'], $_POST[\'terms\'] ?? \'\'])'
],
// Invoices (General)
[
'INSERT INTO invoices (customer_id, invoice_date, status, payment_type, total_amount, vat_amount, total_with_vat, paid_amount) VALUES (?, ?, \'unpaid\', \'credit\', ?, ?, ?, 0)',
'INSERT INTO invoices (outlet_id, customer_id, invoice_date, status, payment_type, total_amount, vat_amount, total_with_vat, paid_amount) VALUES (?, ?, ?, \'unpaid\', \'credit\', ?, ?, ?, 0)'
],
[
'execute([$_POST[\'customer_id\'], $_POST[\'date\'], $totals[\'total\'], $totals[\'tax\'], $totals[\'net\']])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), $_POST[\'customer_id\'], $_POST[\'date\'], $totals[\'total\'], $totals[\'tax\'], $totals[\'net\']])'
],
// Purchases (General)
[
'INSERT INTO purchases (supplier_id, invoice_date, status, payment_type, total_amount, vat_amount, total_with_vat, paid_amount) VALUES (?, ?, \'unpaid\', \'credit\', ?, ?, ?, 0)',
'INSERT INTO purchases (outlet_id, supplier_id, invoice_date, status, payment_type, total_amount, vat_amount, total_with_vat, paid_amount) VALUES (?, ?, ?, \'unpaid\', \'credit\', ?, ?, ?, 0)'
],
[
'execute([$_POST[\'supplier_id\'], $_POST[\'date\'], $totals[\'total\'], $totals[\'tax\'], $totals[\'net\']])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), $_POST[\'supplier_id\'], $_POST[\'date\'], $totals[\'total\'], $totals[\'tax\'], $totals[\'net\']])'
],
// Expenses
[
'INSERT INTO expenses (category_id, amount, expense_date, reference_no, description) VALUES (?, ?, ?, ?, ?)',
'INSERT INTO expenses (outlet_id, category_id, amount, expense_date, reference_no, description) VALUES (?, ?, ?, ?, ?, ?)'
],
[
'execute([(int)$_POST[\'category_id\'], $amt, $date, $_POST[\'reference_no\'] ?? \'\', $desc])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), (int)$_POST[\'category_id\'], $amt, $date, $_POST[\'reference_no\'] ?? \'\', $desc])'
],
// Users
[
'INSERT INTO users (username, password, email, phone, group_id) VALUES (?, ?, ?, ?, ?)',
'INSERT INTO users (outlet_id, username, password, email, phone, group_id) VALUES (?, ?, ?, ?, ?, ?)'
],
[
'execute([$_POST[\'username\'], password_hash($_POST[\'password\'], PASSWORD_DEFAULT), $_POST[\'email\'] ?? \'\', $_POST[\'phone\'] ?? \'\', (int)$_POST[\'group_id\']])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), $_POST[\'username\'], password_hash($_POST[\'password\'], PASSWORD_DEFAULT), $_POST[\'email\'] ?? \'\', $_POST[\'phone\'] ?? \'\', (int)$_POST[\'group_id\']])'
]
];
foreach ($replacements as $p) {
if (strpos($content, $p[0]) !== false) {
$content = str_replace($p[0], $p[1], $content);
echo "Replaced OK.\n";
} else {
echo "Not found: " . substr($p[0], 0, 40) . "\n";
}
}
file_put_contents('index.php', $content);

View File

@ -1,76 +0,0 @@
<?php
$content = file_get_contents('index.php');
$patterns = [
// Invoices (POS)
[
'INSERT INTO invoices (transaction_no, customer_id, invoice_date, payment_type, total_amount, vat_amount, total_with_vat, paid_amount, status, register_session_id, is_pos, discount_amount, loyalty_points_redeemed, created_by)',
'INSERT INTO invoices (outlet_id, transaction_no, customer_id, invoice_date, payment_type, total_amount, vat_amount, total_with_vat, paid_amount, status, register_session_id, is_pos, discount_amount, loyalty_points_redeemed, created_by)',
'execute([$transaction_no, $customer_id, date(\'Y-m-d\'), \'pos\', $total_amount, $tax_amount, $net_amount, $net_amount, $session_id, $discount_amount, $loyalty_redeemed, $_SESSION[\'user_id\']])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), $transaction_no, $customer_id, date(\'Y-m-d\'), \'pos\', $total_amount, $tax_amount, $net_amount, $net_amount, $session_id, $discount_amount, $loyalty_redeemed, $_SESSION[\'user_id\']])'
],
// Quotations
[
'INSERT INTO quotations (customer_id, quotation_date, valid_until, status, total_amount, vat_amount, total_with_vat)',
'INSERT INTO quotations (outlet_id, customer_id, quotation_date, valid_until, status, total_amount, vat_amount, total_with_vat)',
'execute([$_POST[\'customer_id\'], $_POST[\'date\'], $_POST[\'valid_until\'], \'pending\', $totals[\'total\'], $totals[\'tax\'], $totals[\'net\']])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), $_POST[\'customer_id\'], $_POST[\'date\'], $_POST[\'valid_until\'], \'pending\', $totals[\'total\'], $totals[\'tax\'], $totals[\'net\']])'
],
// Lpos
[
'INSERT INTO lpos (supplier_id, lpo_date, delivery_date, status, total_amount, vat_amount, total_with_vat, terms_conditions)',
'INSERT INTO lpos (outlet_id, supplier_id, lpo_date, delivery_date, status, total_amount, vat_amount, total_with_vat, terms_conditions)',
'execute([$_POST[\'supplier_id\'], $_POST[\'date\'], $_POST[\'delivery_date\'], \'pending\', $totals[\'total\'], $totals[\'tax\'], $totals[\'net\'], $_POST[\'terms\'] ?? \'\'])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), $_POST[\'supplier_id\'], $_POST[\'date\'], $_POST[\'delivery_date\'], \'pending\', $totals[\'total\'], $totals[\'tax\'], $totals[\'net\'], $_POST[\'terms\'] ?? \'\'])'
],
// Invoices (General)
[
'INSERT INTO invoices (customer_id, invoice_date, status, payment_type, total_amount, vat_amount, total_with_vat, paid_amount)',
'INSERT INTO invoices (outlet_id, customer_id, invoice_date, status, payment_type, total_amount, vat_amount, total_with_vat, paid_amount)',
'execute([$_POST[\'customer_id\'], $_POST[\'date\'], \'unpaid\', \'credit\', $totals[\'total\'], $totals[\'tax\'], $totals[\'net\'], 0])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), $_POST[\'customer_id\'], $_POST[\'date\'], \'unpaid\', \'credit\', $totals[\'total\'], $totals[\'tax\'], $totals[\'net\'], 0])'
],
// Purchases (General)
[
'INSERT INTO purchases (supplier_id, invoice_date, status, payment_type, total_amount, vat_amount, total_with_vat, paid_amount)',
'INSERT INTO purchases (outlet_id, supplier_id, invoice_date, status, payment_type, total_amount, vat_amount, total_with_vat, paid_amount)',
'execute([$_POST[\'supplier_id\'], $_POST[\'date\'], \'unpaid\', \'credit\', $totals[\'total\'], $totals[\'tax\'], $totals[\'net\'], 0])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), $_POST[\'supplier_id\'], $_POST[\'date\'], \'unpaid\', \'credit\', $totals[\'total\'], $totals[\'tax\'], $totals[\'net\'], 0])'
],
// Expenses
[
'INSERT INTO expenses (category_id, amount, expense_date, reference_no, description)',
'INSERT INTO expenses (outlet_id, category_id, amount, expense_date, reference_no, description)',
'execute([(int)$_POST[\'category_id\'], $amt, $date, $_POST[\'reference_no\'] ?? \'\', $desc])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), (int)$_POST[\'category_id\'], $amt, $date, $_POST[\'reference_no\'] ?? \'\', $desc])'
],
// Users
[
'INSERT INTO users (username, password, email, phone, group_id)',
'INSERT INTO users (outlet_id, username, password, email, phone, group_id)',
'execute([$_POST[\'username\'], password_hash($_POST[\'password\'], PASSWORD_DEFAULT), $_POST[\'email\'] ?? \'\', $_POST[\'phone\'] ?? \'\', (int)$_POST[\'group_id\']])',
'execute([(int)($_SESSION["outlet_id"] ?? 1), $_POST[\'username\'], password_hash($_POST[\'password\'], PASSWORD_DEFAULT), $_POST[\'email\'] ?? \'\', $_POST[\'phone\'] ?? \'\', (int)$_POST[\'group_id\']])'
]
];
foreach ($patterns as $i => $p) {
if (strpos($content, $p[0]) !== false) {
$content = str_replace($p[0], $p[1], $content);
$content = str_replace($p[2], $p[3], $content);
echo "Patched #$i \n";
// Let's also add the ? to the query string
$q0 = "VALUES (";
$q1 = "VALUES (?, ";
// We only want to add it inside the specific INSERT query
$insert_start = strpos($content, $p[1]);
if ($insert_start !== false) {
$values_start = strpos($content, 'VALUES (', $insert_start);
if ($values_start !== false) {
$content = substr_replace($content, 'VALUES (?, ', $values_start, 9);
}
}
}
}
file_put_contents('index.php', $content);

View File

@ -1,36 +0,0 @@
<?php
$content = file_get_contents('index.php');
$replacements = [
[
"case 'sales':\n case 'purchases':\n \$type = (\$page === 'sales') ? 'sale' : 'purchase';\n \$table = (\$type === 'purchase') ? 'purchases' : 'invoices';\n \$cust_supplier_col = (\$type === 'purchase') ? 'supplier_id' : 'customer_id';\n \$cust_supplier_table = (\$type === 'purchase') ? 'suppliers' : 'customers';\n \n \$where = [\"1=1\"];",
"case 'sales':\n case 'purchases':\n \$type = (\$page === 'sales') ? 'sale' : 'purchase';\n \$table = (\$type === 'purchase') ? 'purchases' : 'invoices';\n \$cust_supplier_col = (\$type === 'purchase') ? 'supplier_id' : 'customer_id';\n \$cust_supplier_table = (\$type === 'purchase') ? 'suppliers' : 'customers';\n \n \$where = [];\n if ((\$_SESSION['outlet_id'] ?? 1) != 0) \$where[] = \"v.outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);\n else \$where[] = \"1=1\";"
],
[
"case 'quotations':\n \$where = [\"1=1\"];",
"case 'quotations':\n \$where = [];\n if ((\$_SESSION['outlet_id'] ?? 1) != 0) \$where[] = \"q.outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);\n else \$where[] = \"1=1\";"
],
[
"case 'lpos':\n \$where = [\"1=1\"];",
"case 'lpos':\n \$where = [];\n if ((\$_SESSION['outlet_id'] ?? 1) != 0) \$where[] = \"q.outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);\n else \$where[] = \"1=1\";"
],
[
"case 'expenses':\n \$where = [\"1=1\"];",
"case 'expenses':\n \$where = [];\n if ((\$_SESSION['outlet_id'] ?? 1) != 0) \$where[] = \"e.outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);\n else \$where[] = \"1=1\";"
],
[
"case 'payments':",
"case 'payments':\n \$where = [];\n if ((\$_SESSION['outlet_id'] ?? 1) != 0) \$where[] = \"p.outlet_id = \" . (int)(\$_SESSION['outlet_id'] ?? 1);\n else \$where[] = \"1=1\";"
]
];
foreach ($replacements as $rep) {
if (strpos($content, $rep[0]) !== false) {
$content = str_replace($rep[0], $rep[1], $content);
echo "Patched successfully\n";
} else {
echo "Could not find target to patch\n";
}
}
file_put_contents('index.php', $content);

View File

@ -1,13 +0,0 @@
<?php
$content = file_get_contents('index.php');
$tables = ['users', 'invoices', 'lpos', 'purchases', 'quotations', 'expenses', 'payments', 'purchase_payments', 'pos_transactions', 'customers', 'suppliers', 'stock_items', 'pos_held_carts', 'loyalty_transactions', 'purchase_returns', 'sales_returns'];
foreach ($tables as $t) {
// Regex to match: INSERT INTO table (...) VALUES (...)
// and replace with INSERT INTO table (outlet_id, ...) VALUES (current_outlet_id(), ...)
// Note: since this is PDO prepare, we can't just inject current_outlet_id() directly into VALUES if it's parametrized, but we CAN inject the value if we just use a session variable call directly in SQL, or `?` and prepend the param.
// Wait, simpler: "INSERT INTO $t (outlet_id, " . "(current_outlet_id())" is safe enough?
// Let's look at a simpler regex.
}

View File

@ -1,25 +0,0 @@
<?php
require 'db/config.php';
$db = db();
try {
$db->exec("DROP FUNCTION IF EXISTS current_outlet_id");
$db->exec("CREATE FUNCTION current_outlet_id() RETURNS INT DETERMINISTIC RETURN @session_outlet_id");
$db->exec("RENAME TABLE invoices TO _invoices");
$db->exec("CREATE VIEW invoices AS SELECT * FROM _invoices WHERE outlet_id = current_outlet_id() OR current_outlet_id() IS NULL");
$db->exec("SET @session_outlet_id = 1");
$db->exec("INSERT INTO invoices (customer_id, invoice_date, status, total_amount, vat_amount, total_with_vat, paid_amount) VALUES (1, '2026-01-01', 'unpaid', 100, 5, 105, 0)");
echo "Insert OK\n";
$stmt = $db->query("SELECT * FROM invoices ORDER BY id DESC LIMIT 1");
print_r($stmt->fetch(PDO::FETCH_ASSOC));
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
$db->exec("DROP VIEW IF EXISTS invoices");
$db->exec("RENAME TABLE _invoices TO invoices");
$db->exec("DELETE FROM invoices WHERE customer_id=1 AND total_amount=100");
$db->exec("DROP FUNCTION IF EXISTS current_outlet_id");

View File

@ -1,21 +0,0 @@
<?php
require 'db/config.php';
$db = db();
try {
$db->exec("RENAME TABLE invoices TO _invoices");
$db->exec("CREATE FUNCTION current_outlet_id() RETURNS INT DETERMINISTIC RETURN @session_outlet_id");
$db->exec("CREATE VIEW invoices AS SELECT * FROM _invoices WHERE outlet_id = current_outlet_id() OR current_outlet_id() IS NULL");
$db->exec("SET @session_outlet_id = 1");
$db->exec("INSERT INTO invoices (customer_id, invoice_date, status, total_amount, vat_amount, total_with_vat, paid_amount) VALUES (1, '2026-01-01', 'unpaid', 100, 5, 105, 0)");
$id = $db->lastInsertId();
echo "Last Insert ID: " . $id . "\n";
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
$db->exec("DROP VIEW IF EXISTS invoices");
$db->exec("RENAME TABLE _invoices TO invoices");
$db->exec("DELETE FROM invoices WHERE customer_id=1 AND total_amount=100");
$db->exec("DROP FUNCTION IF EXISTS current_outlet_id");

View File

@ -1,23 +0,0 @@
<?php
require 'db/config.php';
$db = db();
try {
$db->exec("RENAME TABLE invoices TO _invoices");
$db->exec("CREATE VIEW invoices AS SELECT * FROM _invoices WHERE outlet_id = @session_outlet_id OR @session_outlet_id IS NULL");
$db->exec("SET @session_outlet_id = 1");
// Test INSERT
$db->exec("INSERT INTO invoices (customer_id, invoice_date, status, total_amount, vat_amount, total_with_vat, paid_amount) VALUES (1, '2026-01-01', 'unpaid', 100, 5, 105, 0)");
echo "Insert OK\n";
// Check if outlet_id is populated. It won't be, because the view doesn't auto-fill!
// We still need the trigger for INSERT.
$stmt = $db->query("SELECT * FROM invoices ORDER BY id DESC LIMIT 1");
print_r($stmt->fetch(PDO::FETCH_ASSOC));
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
// rollback
$db->exec("DROP VIEW IF EXISTS invoices");
$db->exec("RENAME TABLE _invoices TO invoices");
$db->exec("DELETE FROM invoices WHERE customer_id=1 AND total_amount=100");

View File

@ -1,372 +0,0 @@
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->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'];
--
$page_num = isset($_GET["p"]) ? (int)$_GET["p"] : 1;
if ($page_num < 1) $page_num = 1;
$offset = ($page_num - 1) * $limit;
switch ($page) {
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);
--
$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);
--
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
--
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'])) {
--
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'])) {
--
$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'];
--
$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
--
$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
--
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['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 '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';
}