excel import
This commit is contained in:
parent
a0fa021a47
commit
5e59d3a5bd
@ -57,3 +57,9 @@
|
||||
2026-03-18 06:00:05 - Items case hit
|
||||
2026-03-18 14:33:50 - 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
493
index.php
@ -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);
|
||||
}
|
||||
require_once 'db/config.php';
|
||||
require_once 'includes/SimpleXLSX.php';
|
||||
require_once 'includes/stock_helper.php';
|
||||
|
||||
// Helper for current outlet
|
||||
@ -1369,79 +1370,117 @@ function getPromotionalPrice($item) {
|
||||
redirectWithMessage("Expense recorded!", "index.php?page=expenses");
|
||||
}
|
||||
|
||||
|
||||
# --- Unified Import Logic (Excel & CSV) ---
|
||||
# --- Unified Import Logic (Excel & CSV) ---
|
||||
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) {
|
||||
$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.";
|
||||
$rows = [];
|
||||
if ( $xlsx = SimpleXLSX::parse($tmpPath) ) {
|
||||
$rows = $xlsx->rows();
|
||||
} else {
|
||||
// Check for BOM and skip it
|
||||
if (substr($firstBytes, 0, 3) === "\xEF\xBB\xBF") {
|
||||
$handle = fopen($tmpPath, "r");
|
||||
fseek($handle, 3);
|
||||
} else {
|
||||
$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++;
|
||||
}
|
||||
$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);
|
||||
redirectWithMessage("Import completed! $count items processed." . ($errors > 0 ? " ($errors rows skipped due to data errors.)" : ""), "index.php?page=items");
|
||||
}
|
||||
} else {
|
||||
$errCode = $_FILES['excel_file']['error'] ?? 'no file';
|
||||
$message = "Error: File upload failed. (Error code: $errCode)";
|
||||
if (isset($rows[0][0]) && stripos($rows[0][0], 'sku') !== false) array_shift($rows);
|
||||
|
||||
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'])) {
|
||||
$name_en = $_POST['name_en'] ?? '';
|
||||
$name_ar = $_POST['name_ar'] ?? '';
|
||||
@ -1718,9 +1757,7 @@ function getPromotionalPrice($item) {
|
||||
$message = "Expense recorded!";
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
@ -4997,7 +5034,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
||||
<tbody>
|
||||
<?php foreach ($data['items'] as $item): ?>
|
||||
<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>
|
||||
<?php if ($item['image_path']): ?>
|
||||
<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">
|
||||
<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-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?')">
|
||||
<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>
|
||||
@ -5071,7 +5108,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
||||
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@ -8608,104 +8645,202 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
||||
<?php elseif ($page === "outlets" && ($_SESSION["user_role_name"] ?? "") === "Administrator"): ?>
|
||||
<?php require "outlets_html.php"; ?>
|
||||
<?php elseif ($page === 'settings'): ?>
|
||||
<div class="card p-4">
|
||||
<h5 class="mb-4" data-en="Company Profile" data-ar="ملف الشركة">Company Profile</h5>
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
<div class="form-grid-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" data-en="Company Name" data-ar="اسم الشركة">Company Name</label>
|
||||
<input type="text" name="settings[company_name]" class="form-control" value="<?= htmlspecialchars($data['settings']['company_name'] ?? '') ?>">
|
||||
</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 class="row justify-content-center">
|
||||
<div class="col-xl-10">
|
||||
<div class="card border-0 shadow-sm rounded-4">
|
||||
<div class="card-header bg-white py-3 border-bottom-0">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="rounded-circle bg-primary bg-opacity-10 p-3 text-primary">
|
||||
<i class="bi bi-building-gear fs-4"></i>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label" data-en="Points per 1 OMR" data-ar="النقاط لكل 1 ريال">Points per 1 OMR</label>
|
||||
<input type="number" step="0.01" name="settings[loyalty_points_per_unit]" class="form-control" value="<?= htmlspecialchars($data['settings']['loyalty_points_per_unit'] ?? '1') ?>">
|
||||
</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>
|
||||
<h5 class="m-0 fw-bold text-dark" data-en="Company Profile & Settings" data-ar="ملف الشركة والإعدادات">Company Profile & Settings</h5>
|
||||
<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>
|
||||
</div>
|
||||
<div class="col-md-12 mt-4">
|
||||
<button type="submit" name="update_settings" class="btn btn-primary">
|
||||
<i class="bi bi-save"></i> <span data-en="Save Changes" data-ar="حفظ التغييرات">Save Changes</span>
|
||||
</button>
|
||||
<div class="card-body p-4">
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
|
||||
<!-- 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>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?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="L7651">L7651 (5 x 13 - 65 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>
|
||||
</div>
|
||||
<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-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-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 svg { height: 25px; }
|
||||
@ -13063,6 +13207,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
checkedItems.forEach(cb => {
|
||||
const sku = cb.dataset.sku;
|
||||
const nameAr = cb.dataset.nameAr || '';
|
||||
const nameEn = cb.dataset.nameEn || '';
|
||||
const name = cb.dataset.name;
|
||||
const price = cb.dataset.price;
|
||||
const id = cb.dataset.id;
|
||||
@ -13081,8 +13227,19 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
label.className = 'avery-label';
|
||||
const uniqueId = Math.random().toString(36).substr(2, 9);
|
||||
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 = `
|
||||
<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>
|
||||
<div style="font-size: 11px; font-weight: bold; margin-top: 2px;">OMR ${price}</div>
|
||||
`;
|
||||
@ -13118,12 +13275,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
|
||||
<script>
|
||||
window.printItemBarcode = function(sku, name, price) {
|
||||
window.printItemBarcode = function(sku, nameAr, nameEn, price) {
|
||||
if (!sku) {
|
||||
Swal.fire('Error', 'This item has no SKU/Barcode assigned.', 'error');
|
||||
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;
|
||||
|
||||
JsBarcode("#barcodeSvg", sku, {
|
||||
@ -13144,7 +13301,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const height = parseInt(document.getElementById('barcodeHeight').value) || 25;
|
||||
|
||||
// Get content
|
||||
const name = document.getElementById('barcodeLabelName').innerText;
|
||||
const nameHtml = document.getElementById('barcodeLabelName').innerHTML;
|
||||
const price = document.getElementById('barcodeLabelPrice').innerText;
|
||||
const svg = document.getElementById('barcodeSvg').outerHTML;
|
||||
|
||||
@ -13162,7 +13319,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
for (let i = 0; i < qty; i++) {
|
||||
labelsHtml += `
|
||||
<div class="label-container">
|
||||
<div class="label-name">${name}</div>
|
||||
<div class="label-name" style="height: auto; overflow: visible;">${nameHtml}</div>
|
||||
${svg}
|
||||
<div class="label-price">${price}</div>
|
||||
</div>
|
||||
@ -13190,7 +13347,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
padding: 1mm;
|
||||
}
|
||||
.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; }
|
||||
svg { max-width: 100%; height: auto; max-height: 70%; display: block; }
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user