excel import

This commit is contained in:
Flatlogic Bot 2026-03-18 14:06:55 +00:00
parent a0fa021a47
commit 5e59d3a5bd
2 changed files with 331 additions and 168 deletions

View File

@ -57,3 +57,9 @@
2026-03-18 06:00:05 - Items case hit 2026-03-18 06:00:05 - Items case hit
2026-03-18 14:33:50 - Items case hit 2026-03-18 14:33:50 - Items case hit
2026-03-18 14:34:10 - Items case hit 2026-03-18 14:34:10 - Items case hit
2026-03-18 17:01:37 - Items case hit
2026-03-18 17:07:17 - Items case hit
2026-03-18 17:09:46 - Items case hit
2026-03-18 17:35:42 - Items case hit
2026-03-18 17:36:53 - Items case hit
2026-03-18 17:49:12 - Items case hit

493
index.php
View File

@ -49,6 +49,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
file_put_contents('post_debug.log', date('Y-m-d H:i:s') . " - POST: " . json_encode($_POST) . "\n", FILE_APPEND); file_put_contents('post_debug.log', date('Y-m-d H:i:s') . " - POST: " . json_encode($_POST) . "\n", FILE_APPEND);
} }
require_once 'db/config.php'; require_once 'db/config.php';
require_once 'includes/SimpleXLSX.php';
require_once 'includes/stock_helper.php'; require_once 'includes/stock_helper.php';
// Helper for current outlet // Helper for current outlet
@ -1369,79 +1370,117 @@ function getPromotionalPrice($item) {
redirectWithMessage("Expense recorded!", "index.php?page=expenses"); redirectWithMessage("Expense recorded!", "index.php?page=expenses");
} }
# --- Unified Import Logic (Excel & CSV) ---
# --- Unified Import Logic (Excel & CSV) ---
if (isset($_POST['import_items'])) { if (isset($_POST['import_items'])) {
error_log("Import items triggered. POST: " . print_r($_POST, true)); error_log("Import items triggered.");
$count = 0;
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) { if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
$tmpPath = $_FILES['excel_file']['tmp_name']; $tmpPath = $_FILES['excel_file']['tmp_name'];
error_log("File uploaded to: $tmpPath"); $rows = [];
$firstBytes = file_get_contents($tmpPath, false, null, 0, 4); if ( $xlsx = SimpleXLSX::parse($tmpPath) ) {
if ($firstBytes === "PK\x03\x04") { $rows = $xlsx->rows();
$message = "Error: It looks like you uploaded an Excel (.xlsx) file. Please save it as CSV (UTF-8) and try again.";
} else { } else {
// Check for BOM and skip it $handle = fopen($tmpPath, "r");
if (substr($firstBytes, 0, 3) === "\xEF\xBB\xBF") { $firstLine = fgets($handle); rewind($handle);
$handle = fopen($tmpPath, "r"); $sep = (substr_count($firstLine, ';') > substr_count($firstLine, ',')) ? ';' : ',';
fseek($handle, 3); $bom = fread($handle, 3); if ($bom !== "") rewind($handle);
} else { while (($data = fgetcsv($handle, 0, $sep)) !== FALSE) $rows[] = $data;
$handle = fopen($tmpPath, "r");
}
$firstLine = fgets($handle);
rewind($handle);
if (substr($firstBytes, 0, 3) === "\xEF\xBB\xBF") fseek($handle, 3);
error_log("First line of CSV: " . $firstLine);
$seps = [",", ";", "\t", "|"];
$sep = ",";
$maxCount = 0;
foreach ($seps as $s) {
$count = substr_count($firstLine, $s);
if ($count > $maxCount) {
$maxCount = $count;
$sep = $s;
}
}
error_log("Detected separator: $sep");
$count = 0;
$errors = 0;
$rowNum = 0;
while (($data = fgetcsv($handle, 10000, $sep)) !== FALSE) {
$rowNum++;
if ($rowNum === 1) continue; // Skip header
if (count($data) < 4) { $errors++; continue; }
$sku = trim($data[0]);
$name_en = trim($data[1]);
$name_ar = trim($data[2]);
$price = (float)($data[3] ?? 0);
$qty = (float)($data[4] ?? 0);
$vat_rate = (float)($data[5] ?? 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 = ?, stock_quantity = ?, vat_rate = ? WHERE sku = ?")
->execute([$name_en, $name_ar, $price, $qty, $vat_rate, $sku]);
} else {
db()->prepare("INSERT INTO stock_items (sku, name_en, name_ar, sale_price, stock_quantity, vat_rate) VALUES (?, ?, ?, ?, ?, ?)")
->execute([$sku, $name_en, $name_ar, $price, $qty, $vat_rate]);
}
$count++;
}
fclose($handle); fclose($handle);
redirectWithMessage("Import completed! $count items processed." . ($errors > 0 ? " ($errors rows skipped due to data errors.)" : ""), "index.php?page=items");
} }
} else { if (isset($rows[0][0]) && stripos($rows[0][0], 'sku') !== false) array_shift($rows);
$errCode = $_FILES['excel_file']['error'] ?? 'no file';
$message = "Error: File upload failed. (Error code: $errCode)"; foreach ($rows as $row) {
if (empty($row[0])) continue;
$sku = trim((string)$row[0]);
$name_en = trim((string)($row[1] ?? ''));
$name_ar = trim((string)($row[2] ?? ''));
$sale_price = (float)($row[3] ?? 0);
$purchase_price = (float)($row[4] ?? 0);
$qty = (float)($row[5] ?? 0);
$vat_rate = (float)($row[6] ?? 0);
$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("Import items completed! $count processed.", "index.php?page=items");
} }
} }
if (isset($_POST['import_customers']) || isset($_POST['import_suppliers'])) {
$type = isset($_POST['import_customers']) ? 'customers' : 'suppliers';
$table = $type;
error_log("Import $type triggered.");
$count = 0;
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
$tmpPath = $_FILES['excel_file']['tmp_name'];
$rows = [];
if ( $xlsx = SimpleXLSX::parse($tmpPath) ) {
$rows = $xlsx->rows();
} else {
$handle = fopen($tmpPath, "r");
$firstLine = fgets($handle); rewind($handle);
$sep = (substr_count($firstLine, ';') > substr_count($firstLine, ',')) ? ';' : ',';
$bom = fread($handle, 3); if ($bom !== "") rewind($handle);
while (($data = fgetcsv($handle, 0, $sep)) !== FALSE) $rows[] = $data;
fclose($handle);
}
if (isset($rows[0][0]) && (stripos($rows[0][0], 'name') !== false || stripos($rows[0][0], 'id') !== false)) array_shift($rows);
foreach ($rows as $row) {
if (empty($row[0])) continue;
$name = trim((string)$row[0]);
if (!$name) continue;
$email = trim((string)($row[1] ?? ''));
$phone = trim((string)($row[2] ?? ''));
$tax_id = trim((string)($row[3] ?? ''));
db()->prepare("INSERT INTO $table (name, email, phone, tax_id, created_at) VALUES (?, ?, ?, ?, NOW())")
->execute([$name, $email, $phone, $tax_id]);
$count++;
}
redirectWithMessage("Import $type completed! $count processed.", "index.php?page=$type");
}
}
if (isset($_POST['import_categories'])) {
$count = 0;
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
$tmpPath = $_FILES['excel_file']['tmp_name'];
$rows = [];
if ( $xlsx = SimpleXLSX::parse($tmpPath) ) { $rows = $xlsx->rows(); }
else {
$handle = fopen($tmpPath, "r");
while (($data = fgetcsv($handle)) !== FALSE) $rows[] = $data;
fclose($handle);
}
if (isset($rows[0][0]) && stripos($rows[0][0], 'name') !== false) array_shift($rows);
foreach ($rows as $row) {
if (empty($row[0])) continue;
$name_en = trim((string)$row[0]);
$name_ar = trim((string)($row[1] ?? $name_en));
db()->prepare("INSERT INTO stock_categories (name_en, name_ar) VALUES (?, ?)")
->execute([$name_en, $name_ar]);
$count++;
}
redirectWithMessage("Import categories completed! $count processed.", "index.php?page=categories");
}
}
if (isset($_POST['import_units'])) {
$count = 0;
if (isset($_POST['add_expense_category'])) { if (isset($_POST['add_expense_category'])) {
$name_en = $_POST['name_en'] ?? ''; $name_en = $_POST['name_en'] ?? '';
$name_ar = $_POST['name_ar'] ?? ''; $name_ar = $_POST['name_ar'] ?? '';
@ -1718,9 +1757,7 @@ function getPromotionalPrice($item) {
$message = "Expense recorded!"; $message = "Expense recorded!";
} }
if (isset($_POST['import_items'])) { if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
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']; $tmpPath = $_FILES['excel_file']['tmp_name'];
error_log("File uploaded to: $tmpPath"); error_log("File uploaded to: $tmpPath");
$firstBytes = file_get_contents($tmpPath, false, null, 0, 4); $firstBytes = file_get_contents($tmpPath, false, null, 0, 4);
@ -4997,7 +5034,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<tbody> <tbody>
<?php foreach ($data['items'] as $item): ?> <?php foreach ($data['items'] as $item): ?>
<tr> <tr>
<td><input type="checkbox" class="form-check-input item-checkbox" data-id="<?= $item['id'] ?>" data-sku="<?= htmlspecialchars($item['sku']) ?>" data-name="<?= htmlspecialchars($item['name_en'] . ' - ' . $item['name_ar']) ?>" data-price="<?= number_format((float)$item['sale_price'] * (1 + (float)($item['vat_rate'] ?? 0) / 100), 3) ?>"></td> <td><input type="checkbox" class="form-check-input item-checkbox" data-id="<?= $item['id'] ?>" data-sku="<?= htmlspecialchars($item['sku']) ?>" data-name-ar="<?= htmlspecialchars($item['name_ar']) ?>" data-name-en="<?= htmlspecialchars($item['name_en']) ?>" data-name="<?= htmlspecialchars($item['name_en'] . ' - ' . $item['name_ar']) ?>" data-price="<?= number_format((float)$item['sale_price'] * (1 + (float)($item['vat_rate'] ?? 0) / 100), 3) ?>"></td>
<td> <td>
<?php if ($item['image_path']): ?> <?php if ($item['image_path']): ?>
<img src="<?= htmlspecialchars($item['image_path']) ?>" alt="item" style="width: 40px; height: 40px; object-fit: cover;" class="rounded"> <img src="<?= htmlspecialchars($item['image_path']) ?>" alt="item" style="width: 40px; height: 40px; object-fit: cover;" class="rounded">
@ -5034,7 +5071,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<div class="btn-group btn-group-sm"> <div class="btn-group btn-group-sm">
<button class="btn btn-outline-info" title="View" data-bs-toggle="modal" data-bs-target="#viewItemModal<?= $item['id'] ?>"><i class="bi bi-eye"></i></button> <button class="btn btn-outline-info" title="View" data-bs-toggle="modal" data-bs-target="#viewItemModal<?= $item['id'] ?>"><i class="bi bi-eye"></i></button>
<button class="btn btn-outline-primary" title="Edit" data-bs-toggle="modal" data-bs-target="#editItemModal<?= $item['id'] ?>"><i class="bi bi-pencil"></i></button> <button class="btn btn-outline-primary" title="Edit" data-bs-toggle="modal" data-bs-target="#editItemModal<?= $item['id'] ?>"><i class="bi bi-pencil"></i></button>
<button class="btn btn-outline-dark" title="Barcode" onclick="printItemBarcode('<?= htmlspecialchars($item['sku']) ?>', '<?= htmlspecialchars($item['name_en'] . ' - ' . $item['name_ar']) ?>', '<?= number_format((float)$item['sale_price'] * (1 + (float)($item['vat_rate'] ?? 0) / 100), 3) ?>')"><i class="bi bi-upc"></i></button> <button class="btn btn-outline-dark" title="Barcode" onclick="printItemBarcode('<?= htmlspecialchars($item['sku']) ?>', '<?= htmlspecialchars($item['name_ar']) ?>', '<?= htmlspecialchars($item['name_en']) ?>', '<?= number_format((float)$item['sale_price'] * (1 + (float)($item['vat_rate'] ?? 0) / 100), 3) ?>')"><i class="bi bi-upc"></i></button>
<form method="POST" class="d-inline" onsubmit="return confirm('Are you sure?')"> <form method="POST" class="d-inline" onsubmit="return confirm('Are you sure?')">
<input type="hidden" name="id" value="<?= $item['id'] ?>"> <input type="hidden" name="id" value="<?= $item['id'] ?>">
<button type="submit" name="delete_item" class="btn btn-outline-danger" title="Delete"><i class="bi bi-trash"></i></button> <button type="submit" name="delete_item" class="btn btn-outline-danger" title="Delete"><i class="bi bi-trash"></i></button>
@ -5071,7 +5108,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-outline-dark" onclick="printItemBarcode('<?= htmlspecialchars($item['sku']) ?>', '<?= htmlspecialchars($item['name_en'] . ' - ' . $item['name_ar']) ?>', '<?= number_format((float)$item['sale_price'] * (1 + (float)($item['vat_rate'] ?? 0) / 100), 3) ?>')"><i class="bi bi-printer"></i> Print Barcode</button> <button class="btn btn-outline-dark" onclick="printItemBarcode('<?= htmlspecialchars($item['sku']) ?>', '<?= htmlspecialchars($item['name_ar']) ?>', '<?= htmlspecialchars($item['name_en']) ?>', '<?= number_format((float)$item['sale_price'] * (1 + (float)($item['vat_rate'] ?? 0) / 100), 3) ?>')"><i class="bi bi-printer"></i> Print Barcode</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal" data-en="Close" data-ar="إغلاق">Close</button> <button type="button" class="btn btn-light" data-bs-dismiss="modal" data-en="Close" data-ar="إغلاق">Close</button>
</div> </div>
</div> </div>
@ -8608,104 +8645,202 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<?php elseif ($page === "outlets" && ($_SESSION["user_role_name"] ?? "") === "Administrator"): ?> <?php elseif ($page === "outlets" && ($_SESSION["user_role_name"] ?? "") === "Administrator"): ?>
<?php require "outlets_html.php"; ?> <?php require "outlets_html.php"; ?>
<?php elseif ($page === 'settings'): ?> <?php elseif ($page === 'settings'): ?>
<div class="card p-4"> <div class="row justify-content-center">
<h5 class="mb-4" data-en="Company Profile" data-ar="ملف الشركة">Company Profile</h5> <div class="col-xl-10">
<form method="POST" enctype="multipart/form-data"> <div class="card border-0 shadow-sm rounded-4">
<div class="form-grid-3"> <div class="card-header bg-white py-3 border-bottom-0">
<div class="col-md-6"> <div class="d-flex align-items-center gap-3">
<label class="form-label" data-en="Company Name" data-ar="اسم الشركة">Company Name</label> <div class="rounded-circle bg-primary bg-opacity-10 p-3 text-primary">
<input type="text" name="settings[company_name]" class="form-control" value="<?= htmlspecialchars($data['settings']['company_name'] ?? '') ?>"> <i class="bi bi-building-gear fs-4"></i>
</div>
<div class="col-md-6">
<label class="form-label" data-en="Phone" data-ar="الهاتف">Phone</label>
<input type="text" name="settings[company_phone]" class="form-control" value="<?= htmlspecialchars($data['settings']['company_phone'] ?? '') ?>">
</div>
<div class="col-md-6">
<label class="form-label" data-en="Email" data-ar="البريد الإلكتروني">Email</label>
<input type="email" name="settings[company_email]" class="form-control" value="<?= htmlspecialchars($data['settings']['company_email'] ?? '') ?>">
</div>
<div class="col-md-6">
<label class="form-label" data-en="VAT Number" data-ar="الرقم الضريبي">VAT Number</label>
<input type="text" name="settings[vat_number]" class="form-control" value="<?= htmlspecialchars($data['settings']['vat_number'] ?? '') ?>">
</div>
<div class="col-md-12">
<label class="form-label" data-en="Address" data-ar="العنوان">Address</label>
<textarea name="settings[company_address]" class="form-control" rows="3"><?= htmlspecialchars($data['settings']['company_address'] ?? '') ?></textarea>
</div>
<div class="col-md-4 mt-3">
<label class="form-label" data-en="Timezone" data-ar="المنطقة الزمنية">Timezone</label>
<select name="settings[timezone]" class="form-select">
<?php
$tz_identifiers = DateTimeZone::listIdentifiers();
$current_tz = $data['settings']['timezone'] ?? date_default_timezone_get();
foreach ($tz_identifiers as $tz) {
$selected = ($tz === $current_tz) ? 'selected' : '';
echo "<option value=\"$tz\" $selected>$tz</option>";
}
?>
</select>
</div>
<div class="col-md-4 mt-3">
<label class="form-label" data-en="Allow Selling Out of Stock" data-ar="السماح بالبيع عند نفاذ المخزون">Allow Selling Out of Stock</label>
<select name="settings[allow_zero_stock_sell]" class="form-select">
<option value="0" <?= ($data['settings']['allow_zero_stock_sell'] ?? '1') === '0' ? 'selected' : '' ?> data-en="Disabled" data-ar="معطل">Disabled</option>
<option value="1" <?= ($data['settings']['allow_zero_stock_sell'] ?? '1') === '1' ? 'selected' : '' ?> data-en="Enabled" data-ar="مفعل">Enabled</option>
</select>
</div>
<div class="col-md-4 mt-3">
<label class="form-label" data-en="Company Logo" data-ar="شعار الشركة">Company Logo</label>
<input type="file" name="company_logo" class="form-control" accept="image/*">
<?php if (!empty($data['settings']['company_logo'])): ?>
<div class="mt-2">
<img src="<?= htmlspecialchars($data['settings']['company_logo']) ?>?v=<?= time() ?>" alt="Logo" class="img-thumbnail" style="max-height: 80px;">
</div>
<?php endif; ?>
</div>
<div class="col-md-4">
<label class="form-label" data-en="Favicon" data-ar="أيقونة الموقع">Favicon</label>
<input type="file" name="favicon" class="form-control" accept="image/*">
<?php if (!empty($data['settings']['favicon'])): ?>
<div class="mt-2">
<img src="<?= htmlspecialchars($data['settings']['favicon']) ?>?v=<?= time() ?>" alt="Favicon" class="img-thumbnail" style="max-height: 32px;">
</div>
<?php endif; ?>
</div>
<div class="col-md-4">
<label class="form-label" data-en="Manager Signature" data-ar="توقيع المدير">Manager Signature</label>
<input type="file" name="manager_signature" class="form-control" accept="image/*">
<?php if (!empty($data['settings']['manager_signature'])): ?>
<div class="mt-2">
<img src="<?= htmlspecialchars($data['settings']['manager_signature']) ?>?v=<?= time() ?>" alt="Signature" class="img-thumbnail" style="max-height: 80px;">
</div>
<?php endif; ?>
</div>
<div class="col-md-12 mt-4">
<h5 class="mb-3" data-en="Loyalty Configuration" data-ar="إعدادات الولاء">Loyalty Configuration</h5>
<div class="form-grid-3">
<div class="col-md-4">
<label class="form-label" data-en="Loyalty System" data-ar="نظام الولاء">Loyalty System</label>
<select name="settings[loyalty_enabled]" class="form-select">
<option value="0" <?= ($data['settings']['loyalty_enabled'] ?? '0') === '0' ? 'selected' : '' ?> data-en="Disabled" data-ar="معطل">Disabled</option>
<option value="1" <?= ($data['settings']['loyalty_enabled'] ?? '0') === '1' ? 'selected' : '' ?> data-en="Enabled" data-ar="مفعل">Enabled</option>
</select>
</div> </div>
<div class="col-md-4"> <div>
<label class="form-label" data-en="Points per 1 OMR" data-ar="النقاط لكل 1 ريال">Points per 1 OMR</label> <h5 class="m-0 fw-bold text-dark" data-en="Company Profile & Settings" data-ar="ملف الشركة والإعدادات">Company Profile & Settings</h5>
<input type="number" step="0.01" name="settings[loyalty_points_per_unit]" class="form-control" value="<?= htmlspecialchars($data['settings']['loyalty_points_per_unit'] ?? '1') ?>"> <p class="text-muted small mb-0" data-en="Manage your business identity and system preferences" data-ar="إدارة هوية عملك وتفضيلات النظام">Manage your business identity and system preferences</p>
</div>
<div class="col-md-4">
<label class="form-label" data-en="Points for 1 OMR Discount" data-ar="النقاط لخصم 1 ريال">Points for 1 OMR Discount</label>
<input type="number" step="0.01" name="settings[loyalty_redeem_points_per_unit]" class="form-control" value="<?= htmlspecialchars($data['settings']['loyalty_redeem_points_per_unit'] ?? '100') ?>">
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-12 mt-4"> <div class="card-body p-4">
<button type="submit" name="update_settings" class="btn btn-primary"> <form method="POST" enctype="multipart/form-data">
<i class="bi bi-save"></i> <span data-en="Save Changes" data-ar="حفظ التغييرات">Save Changes</span>
</button> <!-- Company Details Section -->
<div class="mb-5">
<h6 class="fw-bold text-primary mb-3 border-bottom pb-2">
<i class="bi bi-info-circle me-2"></i><span data-en="Company Details" data-ar="تفاصيل الشركة">Company Details</span>
</h6>
<div class="row g-3">
<div class="col-md-6">
<label class="form-label text-muted small fw-semibold" data-en="Company Name" data-ar="اسم الشركة">Company Name</label>
<div class="input-group">
<span class="input-group-text bg-light border-end-0"><i class="bi bi-building"></i></span>
<input type="text" name="settings[company_name]" class="form-control border-start-0 ps-0" value="<?= htmlspecialchars($data['settings']['company_name'] ?? '') ?>" placeholder="e.g. Tech Solutions LLC">
</div>
</div>
<div class="col-md-6">
<label class="form-label text-muted small fw-semibold" data-en="CTR No (Commercial Registration)" data-ar="رقم السجل التجاري">CTR No (Commercial Registration)</label>
<div class="input-group">
<span class="input-group-text bg-light border-end-0"><i class="bi bi-file-text"></i></span>
<input type="text" name="settings[ctr_no]" class="form-control border-start-0 ps-0" value="<?= htmlspecialchars($data['settings']['ctr_no'] ?? '') ?>" placeholder="e.g. 1234567">
</div>
</div>
<div class="col-md-6">
<label class="form-label text-muted small fw-semibold" data-en="VAT Number" data-ar="الرقم الضريبي">VAT Number</label>
<div class="input-group">
<span class="input-group-text bg-light border-end-0"><i class="bi bi-receipt"></i></span>
<input type="text" name="settings[vat_number]" class="form-control border-start-0 ps-0" value="<?= htmlspecialchars($data['settings']['vat_number'] ?? '') ?>" placeholder="e.g. OM123456789">
</div>
</div>
</div>
</div>
<!-- Contact Information Section -->
<div class="mb-5">
<h6 class="fw-bold text-primary mb-3 border-bottom pb-2">
<i class="bi bi-telephone me-2"></i><span data-en="Contact Information" data-ar="معلومات الاتصال">Contact Information</span>
</h6>
<div class="row g-3">
<div class="col-md-6">
<label class="form-label text-muted small fw-semibold" data-en="Phone Number" data-ar="رقم الهاتف">Phone Number</label>
<div class="input-group">
<span class="input-group-text bg-light border-end-0"><i class="bi bi-phone"></i></span>
<input type="text" name="settings[company_phone]" class="form-control border-start-0 ps-0" value="<?= htmlspecialchars($data['settings']['company_phone'] ?? '') ?>" placeholder="+968 9999 9999">
</div>
</div>
<div class="col-md-6">
<label class="form-label text-muted small fw-semibold" data-en="Email Address" data-ar="البريد الإلكتروني">Email Address</label>
<div class="input-group">
<span class="input-group-text bg-light border-end-0"><i class="bi bi-envelope"></i></span>
<input type="email" name="settings[company_email]" class="form-control border-start-0 ps-0" value="<?= htmlspecialchars($data['settings']['company_email'] ?? '') ?>" placeholder="info@example.com">
</div>
</div>
<div class="col-md-12">
<label class="form-label text-muted small fw-semibold" data-en="Physical Address" data-ar="العنوان الفعلي">Physical Address</label>
<div class="input-group">
<span class="input-group-text bg-light border-end-0"><i class="bi bi-geo-alt"></i></span>
<textarea name="settings[company_address]" class="form-control border-start-0 ps-0" rows="2" placeholder="Street, Building, City..."><?= htmlspecialchars($data['settings']['company_address'] ?? '') ?></textarea>
</div>
</div>
</div>
</div>
<!-- System Configuration Section -->
<div class="mb-5">
<h6 class="fw-bold text-primary mb-3 border-bottom pb-2">
<i class="bi bi-sliders me-2"></i><span data-en="System Configuration" data-ar="تكوين النظام">System Configuration</span>
</h6>
<div class="row g-3">
<div class="col-md-6">
<label class="form-label text-muted small fw-semibold" data-en="System Timezone" data-ar="المنطقة الزمنية للنظام">System Timezone</label>
<select name="settings[timezone]" class="form-select">
<?php
$tz_identifiers = DateTimeZone::listIdentifiers();
$current_tz = $data['settings']['timezone'] ?? date_default_timezone_get();
foreach ($tz_identifiers as $tz) {
$selected = ($tz === $current_tz) ? 'selected' : '';
echo "<option value=\"\"$tz\" $selected>$tz</option>";
}
?>
</select>
</div>
<div class="col-md-6">
<label class="form-label text-muted small fw-semibold" data-en="Stock Policy" data-ar="سياسة المخزون">Stock Policy</label>
<select name="settings[allow_zero_stock_sell]" class="form-select">
<option value="0" <?= ($data['settings']['allow_zero_stock_sell'] ?? '1') === '0' ? 'selected' : '' ?> data-en="Prevent selling out of stock" data-ar="منع البيع عند نفاذ المخزون">Prevent selling out of stock</option>
<option value="1" <?= ($data['settings']['allow_zero_stock_sell'] ?? '1') === '1' ? 'selected' : '' ?> data-en="Allow selling out of stock" data-ar="السماح بالبيع عند نفاذ المخزون">Allow selling out of stock</option>
</select>
</div>
</div>
</div>
<!-- Visual Identity Section -->
<div class="mb-5">
<h6 class="fw-bold text-primary mb-3 border-bottom pb-2">
<i class="bi bi-palette me-2"></i><span data-en="Visual Identity" data-ar="الهوية البصرية">Visual Identity</span>
</h6>
<div class="row g-4">
<div class="col-md-4">
<div class="card h-100 border-dashed bg-light text-center p-3">
<label class="form-label fw-semibold mb-2" data-en="Company Logo" data-ar="شعار الشركة">Company Logo</label>
<div class="mb-3 d-flex justify-content-center align-items-center" style="height: 100px;">
<?php if (!empty($data['settings']['company_logo'])): ?>
<img src="<?= htmlspecialchars($data['settings']['company_logo']) ?>?v=<?= time() ?>" alt="Logo" class="img-fluid" style="max-height: 80px;">
<?php else:
?>
<i class="bi bi-image text-muted fs-1"></i>
<?php endif; ?>
</div>
<input type="file" name="company_logo" class="form-control form-control-sm" accept="image/*">
</div>
</div>
<div class="col-md-4">
<div class="card h-100 border-dashed bg-light text-center p-3">
<label class="form-label fw-semibold mb-2" data-en="Website Favicon" data-ar="أيقونة الموقع">Website Favicon</label>
<div class="mb-3 d-flex justify-content-center align-items-center" style="height: 100px;">
<?php if (!empty($data['settings']['favicon'])): ?>
<img src="<?= htmlspecialchars($data['settings']['favicon']) ?>?v=<?= time() ?>" alt="Favicon" class="img-fluid" style="max-height: 32px;">
<?php else:
?>
<i class="bi bi-globe text-muted fs-1"></i>
<?php endif; ?>
</div>
<input type="file" name="favicon" class="form-control form-control-sm" accept="image/*">
</div>
</div>
<div class="col-md-4">
<div class="card h-100 border-dashed bg-light text-center p-3">
<label class="form-label fw-semibold mb-2" data-en="Manager Signature" data-ar="توقيع المدير">Manager Signature</label>
<div class="mb-3 d-flex justify-content-center align-items-center" style="height: 100px;">
<?php if (!empty($data['settings']['manager_signature'])): ?>
<img src="<?= htmlspecialchars($data['settings']['manager_signature']) ?>?v=<?= time() ?>" alt="Signature" class="img-fluid" style="max-height: 80px;">
<?php else:
?>
<i class="bi bi-pen text-muted fs-1"></i>
<?php endif; ?>
</div>
<input type="file" name="manager_signature" class="form-control form-control-sm" accept="image/*">
</div>
</div>
</div>
</div>
<!-- Loyalty Configuration Section -->
<div class="mb-5">
<h6 class="fw-bold text-primary mb-3 border-bottom pb-2">
<i class="bi bi-award me-2"></i><span data-en="Loyalty Program" data-ar="برنامج الولاء">Loyalty Program</span>
</h6>
<div class="row g-3">
<div class="col-md-4">
<label class="form-label text-muted small fw-semibold" data-en="Loyalty Status" data-ar="حالة الولاء">Loyalty Status</label>
<select name="settings[loyalty_enabled]" class="form-select">
<option value="0" <?= ($data['settings']['loyalty_enabled'] ?? '0') === '0' ? 'selected' : '' ?> data-en="Disabled" data-ar="معطل">Disabled</option>
<option value="1" <?= ($data['settings']['loyalty_enabled'] ?? '0') === '1' ? 'selected' : '' ?> data-en="Active" data-ar="نشط">Active</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label text-muted small fw-semibold" data-en="Earning Rule (Points/1 OMR)" data-ar="قاعدة الكسب (نقاط/1 ريال)">Earning Rule (Points/1 OMR)</label>
<div class="input-group">
<span class="input-group-text bg-light border-end-0"><i class="bi bi-arrow-up-circle"></i></span>
<input type="number" step="0.01" name="settings[loyalty_points_per_unit]" class="form-control border-start-0 ps-0" value="<?= htmlspecialchars($data['settings']['loyalty_points_per_unit'] ?? '1') ?>">
</div>
</div>
<div class="col-md-4">
<label class="form-label text-muted small fw-semibold" data-en="Redemption Rule (Points/1 OMR)" data-ar="قاعدة الاسترداد (نقاط/1 ريال)">Redemption Rule (Points/1 OMR)</label>
<div class="input-group">
<span class="input-group-text bg-light border-end-0"><i class="bi bi-arrow-down-circle"></i></span>
<input type="number" step="0.01" name="settings[loyalty_redeem_points_per_unit]" class="form-control border-start-0 ps-0" value="<?= htmlspecialchars($data['settings']['loyalty_redeem_points_per_unit'] ?? '100') ?>">
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-end pt-3">
<button type="submit" name="update_settings" class="btn btn-primary btn-lg rounded-pill px-5 shadow-sm">
<i class="bi bi-check-lg me-2"></i> <span data-en="Save All Changes" data-ar="حفظ جميع التغييرات">Save All Changes</span>
</button>
</div>
</form>
</div> </div>
</div> </div>
</form> </div>
</div> </div>
<?php elseif ($page === 'role_groups'): ?> <?php elseif ($page === 'role_groups'): ?>
@ -12864,6 +12999,8 @@ document.addEventListener('DOMContentLoaded', function() {
<option value="4x10">4 x 10 (40 Labels per sheet)</option> <option value="4x10">4 x 10 (40 Labels per sheet)</option>
<option value="L7651">L7651 (5 x 13 - 65 Labels)</option> <option value="L7651">L7651 (5 x 13 - 65 Labels)</option>
<option value="L4736">L4736 (2 x 7 - 14 Labels)</option> <option value="L4736">L4736 (2 x 7 - 14 Labels)</option>
<option value="L7431">L7431 (6 x 8 - 48 Labels)</option>
<option value="L4716">L4716 (6 x 8 - 48 Labels - Round)</option>
</select> </select>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
@ -12918,6 +13055,13 @@ document.addEventListener('DOMContentLoaded', function() {
.avery-layout-4x10 { grid-template-columns: repeat(4, 1fr); grid-auto-rows: 27mm; } .avery-layout-4x10 { grid-template-columns: repeat(4, 1fr); grid-auto-rows: 27mm; }
.avery-layout-L7651 { grid-template-columns: repeat(5, 1fr); grid-auto-rows: 21mm; } .avery-layout-L7651 { grid-template-columns: repeat(5, 1fr); grid-auto-rows: 21mm; }
.avery-layout-L4736 { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 38mm; } .avery-layout-L4736 { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 38mm; }
.avery-layout-L7431 { grid-template-columns: repeat(6, 1fr); grid-auto-rows: 33mm; }
.avery-layout-L4716 { grid-template-columns: repeat(6, 1fr); grid-auto-rows: 33mm; }
.avery-layout-L4716 .avery-label { border-radius: 50%; }
.avery-layout-L7431 .avery-label, .avery-layout-L4716 .avery-label { padding: 1mm; }
.avery-layout-L7431 .avery-label div, .avery-layout-L4716 .avery-label div { font-size: 8px !important; }
.avery-layout-L7431 .avery-label svg, .avery-layout-L4716 .avery-label svg { height: 20px; }
.avery-layout-L7651 .avery-label { padding: 2mm; } .avery-layout-L7651 .avery-label { padding: 2mm; }
.avery-layout-L7651 .avery-label svg { height: 25px; } .avery-layout-L7651 .avery-label svg { height: 25px; }
@ -13063,6 +13207,8 @@ document.addEventListener('DOMContentLoaded', function() {
checkedItems.forEach(cb => { checkedItems.forEach(cb => {
const sku = cb.dataset.sku; const sku = cb.dataset.sku;
const nameAr = cb.dataset.nameAr || '';
const nameEn = cb.dataset.nameEn || '';
const name = cb.dataset.name; const name = cb.dataset.name;
const price = cb.dataset.price; const price = cb.dataset.price;
const id = cb.dataset.id; const id = cb.dataset.id;
@ -13081,8 +13227,19 @@ document.addEventListener('DOMContentLoaded', function() {
label.className = 'avery-label'; label.className = 'avery-label';
const uniqueId = Math.random().toString(36).substr(2, 9); const uniqueId = Math.random().toString(36).substr(2, 9);
const svgId = `bc-${sku}-${uniqueId}`; const svgId = `bc-${sku}-${uniqueId}`;
let nameHtml = '';
if (nameAr || nameEn) {
const arText = nameAr || name;
const enText = nameEn || '';
nameHtml = `<div style="font-size: 10px; font-weight: bold; direction: rtl; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.1;">${arText}</div>
<div style="font-size: 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.1;">${enText}</div>`;
} else {
nameHtml = `<div style="font-size: 9px; font-weight: bold; margin-bottom: 2px; line-height: 1.1; overflow: hidden; text-overflow: ellipsis; width: 100%;">${name}</div>`;
}
label.innerHTML = ` label.innerHTML = `
<div style="font-size: 9px; font-weight: bold; margin-bottom: 2px; line-height: 1.1; overflow: hidden; text-overflow: ellipsis; width: 100%;">${name}</div> ${nameHtml}
<svg id="${svgId}"></svg> <svg id="${svgId}"></svg>
<div style="font-size: 11px; font-weight: bold; margin-top: 2px;">OMR ${price}</div> <div style="font-size: 11px; font-weight: bold; margin-top: 2px;">OMR ${price}</div>
`; `;
@ -13118,12 +13275,12 @@ document.addEventListener('DOMContentLoaded', function() {
<script> <script>
window.printItemBarcode = function(sku, name, price) { window.printItemBarcode = function(sku, nameAr, nameEn, price) {
if (!sku) { if (!sku) {
Swal.fire('Error', 'This item has no SKU/Barcode assigned.', 'error'); Swal.fire('Error', 'This item has no SKU/Barcode assigned.', 'error');
return; return;
} }
document.getElementById('barcodeLabelName').textContent = name; document.getElementById('barcodeLabelName').innerHTML = '<div style="font-weight:bold; font-size:12px; direction:rtl; margin-bottom:2px;">' + nameAr + '</div><div style="font-size:10px;">' + nameEn + '</div>';
document.getElementById('barcodeLabelPrice').textContent = 'OMR ' + price; document.getElementById('barcodeLabelPrice').textContent = 'OMR ' + price;
JsBarcode("#barcodeSvg", sku, { JsBarcode("#barcodeSvg", sku, {
@ -13144,7 +13301,7 @@ document.addEventListener('DOMContentLoaded', function() {
const height = parseInt(document.getElementById('barcodeHeight').value) || 25; const height = parseInt(document.getElementById('barcodeHeight').value) || 25;
// Get content // Get content
const name = document.getElementById('barcodeLabelName').innerText; const nameHtml = document.getElementById('barcodeLabelName').innerHTML;
const price = document.getElementById('barcodeLabelPrice').innerText; const price = document.getElementById('barcodeLabelPrice').innerText;
const svg = document.getElementById('barcodeSvg').outerHTML; const svg = document.getElementById('barcodeSvg').outerHTML;
@ -13162,7 +13319,7 @@ document.addEventListener('DOMContentLoaded', function() {
for (let i = 0; i < qty; i++) { for (let i = 0; i < qty; i++) {
labelsHtml += ` labelsHtml += `
<div class="label-container"> <div class="label-container">
<div class="label-name">${name}</div> <div class="label-name" style="height: auto; overflow: visible;">${nameHtml}</div>
${svg} ${svg}
<div class="label-price">${price}</div> <div class="label-price">${price}</div>
</div> </div>
@ -13190,7 +13347,7 @@ document.addEventListener('DOMContentLoaded', function() {
padding: 1mm; padding: 1mm;
} }
.label-container:last-child { page-break-after: avoid; } .label-container:last-child { page-break-after: avoid; }
.label-name { font-size: 9px; font-weight: bold; margin-bottom: 2px; line-height: 1.1; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } .label-name { font-weight: bold; margin-bottom: 2px; line-height: 1.1; max-width: 100%; }
.label-price { font-size: 12px; font-weight: bold; margin-top: 2px; } .label-price { font-size: 12px; font-weight: bold; margin-top: 2px; }
svg { max-width: 100%; height: auto; max-height: 70%; display: block; } svg { max-width: 100%; height: auto; max-height: 70%; display: block; }
</style> </style>