update pos
This commit is contained in:
parent
017bae675e
commit
5f4a0b383c
@ -116,7 +116,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (typeof Swal !== 'undefined') {
|
||||
Swal.fire('Empty Cart', 'Please add items before saving.', 'warning');
|
||||
} else {
|
||||
alert('Cart is empty');
|
||||
Swal.fire({icon: 'warning', text: 'Cart is empty'});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -2,4 +2,4 @@
|
||||
# https://curl.se/docs/http-cookies.html
|
||||
# This file was generated by libcurl! Edit at your own risk.
|
||||
|
||||
127.0.0.1 FALSE / FALSE 0 PHPSESSID une8u6o0qtfojm7tp3ppv39knh
|
||||
127.0.0.1 FALSE / FALSE 0 PHPSESSID knhve7lgedl0acegda65ol7h47
|
||||
|
||||
@ -563,7 +563,7 @@ renderInvoice();
|
||||
document.getElementById('smart-sale-form').addEventListener('submit', function(e) {
|
||||
if (Object.keys(invoiceItems).length === 0) {
|
||||
e.preventDefault();
|
||||
alert('<?= h(tr('الرجاء إضافة أصناف للفاتورة أولاً.', 'Please add items to the invoice first.')) ?>');
|
||||
Swal.fire({icon: 'warning', text: '<?= h(tr('الرجاء إضافة أصناف للفاتورة أولاً.', 'Please add items to the invoice first.')) ?>'});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -43,6 +43,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$error = tr('اختر فرعاً صالحاً لهذه الصلاحية.', 'Choose a valid branch for this role.');
|
||||
} elseif (!in_array($paymentMethod, ['cash', 'card', 'transfer', 'pay_later'], true)) {
|
||||
$error = tr('اختر طريقة دفع صحيحة.', 'Choose a valid payment method.');
|
||||
} elseif ($paymentMethod === 'pay_later' && !$customerId) {
|
||||
$error = tr('يجب اختيار عميل مسجل للدفع الآجل.', 'You must select a registered customer for pay later.');
|
||||
} elseif (!is_array($items) || $items === []) {
|
||||
$error = tr('أضف صنفاً واحداً على الأقل إلى الفاتورة.', 'Add at least one item to the invoice.');
|
||||
} else {
|
||||
@ -521,7 +523,7 @@ async function saveNewCustomer() {
|
||||
const name = document.getElementById('ncName').value.trim();
|
||||
const phone = document.getElementById('ncPhone').value.trim();
|
||||
if (!name) {
|
||||
alert('<?= h(tr('الاسم مطلوب', 'Name is required')) ?>');
|
||||
Swal.fire({icon: 'warning', text: '<?= h(tr('الاسم مطلوب', 'Name is required')) ?>'});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -543,10 +545,10 @@ async function saveNewCustomer() {
|
||||
const Toast = Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, timer: 2000 });
|
||||
Toast.fire({ icon: 'success', title: '<?= h(tr('تم إضافة العميل', 'Customer added')) ?>' });
|
||||
} else {
|
||||
alert(data.error);
|
||||
Swal.fire({icon: 'warning', text: data.error});
|
||||
}
|
||||
} catch(err) {
|
||||
alert('Error saving customer');
|
||||
Swal.fire({icon: 'warning', text: 'Error saving customer'});
|
||||
}
|
||||
}
|
||||
|
||||
@ -708,9 +710,14 @@ renderInvoice();
|
||||
|
||||
// Intercept form submission to check if items exist
|
||||
document.getElementById('smart-sale-form').addEventListener('submit', function(e) {
|
||||
const paymentMethod = document.querySelector('select[name="payment_method"]').value;
|
||||
const customerId = document.getElementById('formCustomerId').value;
|
||||
if (Object.keys(invoiceItems).length === 0) {
|
||||
e.preventDefault();
|
||||
alert('<?= h(tr('الرجاء إضافة أصناف للفاتورة أولاً.', 'Please add items to the invoice first.')) ?>');
|
||||
Swal.fire({icon: 'warning', text: '<?= h(tr('الرجاء إضافة أصناف للفاتورة أولاً.', 'Please add items to the invoice first.')) ?>'});
|
||||
} else if (paymentMethod === 'pay_later' && !customerId) {
|
||||
e.preventDefault();
|
||||
Swal.fire({icon: 'warning', text: '<?= h(tr('يجب اختيار عميل مسجل للدفع الآجل.', 'You must select a registered customer for pay later.')) ?>'});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -274,11 +274,12 @@ function catalog(): array
|
||||
{
|
||||
try {
|
||||
$db = db();
|
||||
$stmt = $db->query("SELECT items.*, units.name_ar as u_name_ar, units.name_en as u_name_en FROM items LEFT JOIN units ON items.unit_id = units.id");
|
||||
$stmt = $db->query("SELECT items.*, units.name_ar as u_name_ar, units.name_en as u_name_en FROM items LEFT JOIN units ON items.unit_id = units.id ORDER BY items.created_at DESC, items.id DESC");
|
||||
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
$catalog = [];
|
||||
foreach ($items as $item) {
|
||||
$catalog[$item["sku"]] = [
|
||||
"id" => (int)($item["id"] ?? 0),
|
||||
"sku" => $item["sku"],
|
||||
"name_ar" => $item["name"],
|
||||
"name_en" => $item["name"],
|
||||
@ -289,6 +290,7 @@ function catalog(): array
|
||||
"category_id" => $item["category_id"], "in_catalog" => (int)($item["in_catalog"] ?? 0),
|
||||
"supplier_id" => $item["supplier_id"],
|
||||
"image_url" => $item["image_url"],
|
||||
"created_at" => $item["created_at"] ?? null,
|
||||
"unit_id" => $item["unit_id"],
|
||||
"unit_ar" => $item["u_name_ar"] ?? "قطعة",
|
||||
"unit_en" => $item["u_name_en"] ?? "pcs"
|
||||
|
||||
@ -38,6 +38,24 @@ $isPublic = !isset($user) || !$user;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
function confirmSwal(e, msg) {
|
||||
e.preventDefault();
|
||||
const form = e.target;
|
||||
Swal.fire({
|
||||
title: msg,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '<?= h(tr("نعم", "Yes")) ?>',
|
||||
cancelButtonText: '<?= h(tr("إلغاء", "Cancel")) ?>',
|
||||
confirmButtonColor: '#d33'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script src="assets/js/main.js?v=<?= h(date('YmdHi')) ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -474,7 +474,7 @@ async function saveNewCustomer() {
|
||||
const name = document.getElementById('ncName').value.trim();
|
||||
const phone = document.getElementById('ncPhone').value.trim();
|
||||
if (!name) {
|
||||
alert('<?= h(tr('الاسم مطلوب', 'Name is required')) ?>');
|
||||
Swal.fire({icon: 'warning', text: '<?= h(tr('الاسم مطلوب', 'Name is required')) ?>'});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -495,10 +495,10 @@ async function saveNewCustomer() {
|
||||
const Toast = Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, timer: 2000 });
|
||||
Toast.fire({ icon: 'success', title: '<?= h(tr('تم إضافة العميل', 'Customer added')) ?>' });
|
||||
} else {
|
||||
alert(data.error);
|
||||
Swal.fire({icon: 'warning', text: data.error});
|
||||
}
|
||||
} catch(err) {
|
||||
alert('Error saving customer');
|
||||
Swal.fire({icon: 'warning', text: 'Error saving customer'});
|
||||
}
|
||||
}
|
||||
|
||||
@ -667,7 +667,7 @@ function updateTotals(total, vat) {
|
||||
document.getElementById('smart-sale-form').addEventListener('submit', function(e) {
|
||||
if (Object.keys(invoiceItems).length === 0) {
|
||||
e.preventDefault();
|
||||
alert('<?= h(tr('الرجاء إضافة أصناف للفاتورة أولاً.', 'Please add items to the invoice first.')) ?>');
|
||||
Swal.fire({icon: 'warning', text: '<?= h(tr('الرجاء إضافة أصناف للفاتورة أولاً.', 'Please add items to the invoice first.')) ?>'});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -28,6 +28,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$error = tr('اختر فرعاً صالحاً لهذه الصلاحية.', 'Choose a valid branch for this role.');
|
||||
} elseif (!in_array($paymentMethod, ['cash', 'card', 'transfer', 'pay_later'], true)) {
|
||||
$error = tr('اختر طريقة دفع صحيحة.', 'Choose a valid payment method.');
|
||||
} elseif ($paymentMethod === 'pay_later' && !$customerId) {
|
||||
$error = tr('يجب اختيار عميل مسجل للدفع الآجل.', 'You must select a registered customer for pay later.');
|
||||
} elseif (!is_array($items) || $items === []) {
|
||||
$error = tr('أضف صنفاً واحداً على الأقل إلى الفاتورة.', 'Add at least one item to the invoice.');
|
||||
} else {
|
||||
@ -488,7 +490,7 @@ async function saveNewCustomer() {
|
||||
const name = document.getElementById('ncName').value.trim();
|
||||
const phone = document.getElementById('ncPhone').value.trim();
|
||||
if (!name) {
|
||||
alert('<?= h(tr('الاسم مطلوب', 'Name is required')) ?>');
|
||||
Swal.fire({icon: 'warning', text: '<?= h(tr('الاسم مطلوب', 'Name is required')) ?>'});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -510,10 +512,10 @@ async function saveNewCustomer() {
|
||||
const Toast = Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, timer: 2000 });
|
||||
Toast.fire({ icon: 'success', title: '<?= h(tr('تم إضافة العميل', 'Customer added')) ?>' });
|
||||
} else {
|
||||
alert(data.error);
|
||||
Swal.fire({icon: 'warning', text: data.error});
|
||||
}
|
||||
} catch(err) {
|
||||
alert('Error saving customer');
|
||||
Swal.fire({icon: 'warning', text: 'Error saving customer'});
|
||||
}
|
||||
}
|
||||
|
||||
@ -673,9 +675,14 @@ function updateTotals(total, vat) {
|
||||
|
||||
// Intercept form submission to check if items exist
|
||||
document.getElementById('smart-sale-form').addEventListener('submit', function(e) {
|
||||
const paymentMethod = document.querySelector('select[name="payment_method"]').value;
|
||||
const customerId = document.getElementById('formCustomerId').value;
|
||||
if (Object.keys(invoiceItems).length === 0) {
|
||||
e.preventDefault();
|
||||
alert('<?= h(tr('الرجاء إضافة أصناف للفاتورة أولاً.', 'Please add items to the invoice first.')) ?>');
|
||||
Swal.fire({icon: 'warning', text: '<?= h(tr('الرجاء إضافة أصناف للفاتورة أولاً.', 'Please add items to the invoice first.')) ?>'});
|
||||
} else if (paymentMethod === 'pay_later' && !customerId) {
|
||||
e.preventDefault();
|
||||
Swal.fire({icon: 'warning', text: '<?= h(tr('يجب اختيار عميل مسجل للدفع الآجل.', 'You must select a registered customer for pay later.')) ?>'});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -151,7 +151,7 @@ require __DIR__ . '/includes/header.php';
|
||||
<li><form method="post"><input type="hidden" name="action" value="update_status"><input type="hidden" name="id" value="<?= h($o['id']) ?>"><input type="hidden" name="status" value="rejected"><button class="dropdown-item text-danger fw-bold" type="submit"><?= h(tr('مرفوض', 'Rejected')) ?></button></form></li>
|
||||
</ul>
|
||||
</div>
|
||||
<form method="post" class="d-inline" onsubmit="return confirm('<?= h(tr('هل أنت متأكد من الحذف؟', 'Are you sure you want to delete?')) ?>');">
|
||||
<form method="post" class="d-inline" onsubmit="confirmSwal(event, '<?= h(tr('هل أنت متأكد من الحذف؟', 'Are you sure you want to delete?')) ?>');">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="id" value="<?= h($o['id']) ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger shadow-sm"><i class="bi bi-trash"></i></button>
|
||||
|
||||
76
patch.php
76
patch.php
@ -1,76 +0,0 @@
|
||||
<?php
|
||||
$c = file_get_contents('includes/app.php');
|
||||
$c = str_replace("migrated_sales_cols_", "migrated_sales_cols_v2_", $c);
|
||||
|
||||
// add to auto migration
|
||||
$s1 = <<<S1
|
||||
$stmt2 = \pdo->query("SHOW COLUMNS FROM branches LIKE 'avatar'");
|
||||
if (\$stmt2->rowCount() === 0) {
|
||||
\$pdo->exec("ALTER TABLE branches ADD COLUMN avatar varchar(255) DEFAULT NULL");
|
||||
}
|
||||
@file_put_contents(\$flagFile, '1');
|
||||
S1;
|
||||
|
||||
$r1 = <<<R1
|
||||
$stmt2 = \pdo->query("SHOW COLUMNS FROM branches LIKE 'avatar'");
|
||||
if (\$stmt2->rowCount() === 0) {
|
||||
\$pdo->exec("ALTER TABLE branches ADD COLUMN avatar varchar(255) DEFAULT NULL");
|
||||
}
|
||||
\$stmt3 = \pdo->query("SHOW COLUMNS FROM sales_orders LIKE 'customer_id'");
|
||||
if (\$stmt3->rowCount() === 0) {
|
||||
\$pdo->exec("ALTER TABLE sales_orders ADD COLUMN customer_id int(10) unsigned DEFAULT NULL");
|
||||
}
|
||||
\$stmt4 = \pdo->query("SHOW COLUMNS FROM sales_orders LIKE 'payment_status'");
|
||||
if (\$stmt4->rowCount() === 0) {
|
||||
\$pdo->exec("ALTER TABLE sales_orders ADD COLUMN payment_status varchar(20) NOT NULL DEFAULT 'paid'");
|
||||
}
|
||||
@file_put_contents(\$flagFile, '1');
|
||||
R1;
|
||||
|
||||
$c = str_replace($s1, $r1, $c);
|
||||
|
||||
// add to ensure_sales_table()
|
||||
$s2 = <<<S2
|
||||
customer_name VARCHAR(120) DEFAULT NULL,
|
||||
payment_method VARCHAR(30) NOT NULL,
|
||||
S2;
|
||||
|
||||
$r2 = <<<R2
|
||||
customer_id INT(10) UNSIGNED DEFAULT NULL,
|
||||
customer_name VARCHAR(120) DEFAULT NULL,
|
||||
payment_method VARCHAR(30) NOT NULL,
|
||||
payment_status VARCHAR(20) NOT NULL DEFAULT 'paid',
|
||||
R2;
|
||||
$c = str_replace($s2, $r2, $c);
|
||||
|
||||
// add to create_sale
|
||||
$s3 = <<<S3
|
||||
(receipt_no, sale_mode, branch_code, cashier_username, cashier_name, role_name, customer_name, payment_method, items_json, item_count, subtotal, vat_amount, total_amount, status, notes, sale_date)
|
||||
VALUES
|
||||
(:receipt_no, :sale_mode, :branch_code, :cashier_username, :cashier_name, :role_name, :customer_name, :payment_method, :items_json, :item_count, :subtotal, :vat_amount, :total_amount, :status, :notes, NOW())
|
||||
);
|
||||
S3;
|
||||
|
||||
$r3 = <<<R3
|
||||
(receipt_no, sale_mode, branch_code, cashier_username, cashier_name, role_name, customer_id, customer_name, payment_method, payment_status, items_json, item_count, subtotal, vat_amount, total_amount, status, notes, sale_date)
|
||||
VALUES
|
||||
(:receipt_no, :sale_mode, :branch_code, :cashier_username, :cashier_name, :role_name, :customer_id, :customer_name, :payment_method, :payment_status, :items_json, :item_count, :subtotal, :vat_amount, :total_amount, :status, :notes, NOW())
|
||||
);
|
||||
R3;
|
||||
$c = str_replace($s3, $r3, $c);
|
||||
|
||||
$s4 = <<<S4
|
||||
$stmt->bindValue(':customer_name', $data['customer_name']);
|
||||
$stmt->bindValue(':payment_method', $data['payment_method']);
|
||||
S4;
|
||||
|
||||
$r4 = <<<R4
|
||||
$stmt->bindValue(':customer_id', $data['customer_id'] ?? null, PDO::PARAM_INT);
|
||||
$stmt->bindValue(':customer_name', $data['customer_name']);
|
||||
$stmt->bindValue(':payment_method', $data['payment_method']);
|
||||
$stmt->bindValue(':payment_status', $data['payment_status'] ?? 'paid');
|
||||
R4;
|
||||
$c = str_replace($s4, $r4, $c);
|
||||
|
||||
file_put_contents('includes/app.php', $c);
|
||||
echo "Patched includes/app.php\n";
|
||||
118
patch_import.py
118
patch_import.py
@ -1,118 +0,0 @@
|
||||
import re
|
||||
|
||||
with open("stock.php", "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
# Replace block from `// Handle Import CSV` to `// Handle AJAX actions`
|
||||
start_marker = "// Handle Import CSV"
|
||||
end_marker = "// Handle AJAX actions"
|
||||
start_idx = content.find(start_marker)
|
||||
end_idx = content.find(end_marker)
|
||||
|
||||
if start_idx == -1 or end_idx == -1:
|
||||
print("Could not find import_csv block")
|
||||
exit(1)
|
||||
|
||||
new_import_code = """// Handle Import CSV
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'import_csv') {
|
||||
if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] === UPLOAD_ERR_OK) {
|
||||
require_once __DIR__ . '/includes/SimpleXLSX.php';
|
||||
$pdo = db();
|
||||
$file_path = $_FILES['csv_file']['tmp_name'];
|
||||
$raw_content = file_get_contents($file_path);
|
||||
|
||||
$rows = [];
|
||||
# Check if XLSX (starts with PK)
|
||||
if (str_starts_with($raw_content, 'PK')) {
|
||||
if ( $xlsx = Shuchkin\SimpleXLSX::parse($file_path) ) {
|
||||
$rows = $xlsx->rows();
|
||||
if (count($rows) > 0) {
|
||||
array_shift($rows); # Remove header
|
||||
}
|
||||
} else {
|
||||
header('Location: stock.php?import_error=' . urlencode('خطأ في قراءة ملف الإكسل (XLSX). يرجى التأكد من أن الملف سليم.'));
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
# Treat as CSV
|
||||
# Remove UTF-8 BOM if present
|
||||
if (str_starts_with($raw_content, "\xEF\xBB\xBF")) {
|
||||
$raw_content = substr($raw_content, 3);
|
||||
}
|
||||
|
||||
# Fix encoding for Windows-1256 (common in Arabic Excel exports)
|
||||
if (!mb_check_encoding($raw_content, 'UTF-8')) {
|
||||
$raw_content = mb_convert_encoding($raw_content, 'UTF-8', 'Windows-1256');
|
||||
}
|
||||
|
||||
# Determine delimiter by checking first line
|
||||
$first_line = strtok($raw_content, "\r\n");
|
||||
$delimiter = ',';
|
||||
if ($first_line !== false && substr_count($first_line, ';') > substr_count($first_line, ',')) {
|
||||
$delimiter = ';';
|
||||
}
|
||||
|
||||
$clean_file = tmpfile();
|
||||
fwrite($clean_file, $raw_content);
|
||||
rewind($clean_file);
|
||||
|
||||
$header = fgetcsv($clean_file, 0, $delimiter);
|
||||
while (($row = fgetcsv($clean_file, 0, $delimiter)) !== false) {
|
||||
$rows[] = $row;
|
||||
}
|
||||
fclose($clean_file);
|
||||
}
|
||||
|
||||
$imported = 0; $updated = 0;
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
$stmtInsert = $pdo->prepare("INSERT INTO items (sku, name, price, cost_price, base_stock, vat, category_id, supplier_id, unit_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmtUpdate = $pdo->prepare("UPDATE items SET name=?, price=?, cost_price=?, base_stock=?, vat=?, category_id=?, supplier_id=?, unit_id=? WHERE sku=?");
|
||||
$stmtCheck = $pdo->prepare("SELECT id FROM items WHERE sku=?");
|
||||
|
||||
foreach ($rows as $row) {
|
||||
if (count($row) < 5) continue;
|
||||
$sku = trim((string)$row[0]); $name = trim((string)$row[1]);
|
||||
if ($sku === '' || $name === '') continue;
|
||||
$price = (float)($row[2] ?? 0);
|
||||
$cost_price = (float)($row[3] ?? 0);
|
||||
$base_stock = (int)($row[4] ?? 0);
|
||||
$vat = (float)($row[5] ?? 5);
|
||||
$category_id = !empty($row[6]) ? (int)$row[6] : null;
|
||||
$supplier_id = !empty($row[7]) ? (int)$row[7] : null;
|
||||
$unit_id = !empty($row[8]) ? (int)$row[8] : null;
|
||||
$stmtCheck->execute([$sku]);
|
||||
if ($stmtCheck->fetchColumn()) {
|
||||
$stmtUpdate->execute([$name, $price, $cost_price, $base_stock, $vat, $category_id, $supplier_id, $unit_id, $sku]);
|
||||
$updated++;
|
||||
} else {
|
||||
$stmtInsert->execute([$sku, $name, $price, $cost_price, $base_stock, $vat, $category_id, $supplier_id, $unit_id]);
|
||||
$imported++;
|
||||
}
|
||||
}
|
||||
$pdo->commit();
|
||||
header('Location: stock.php?import_success=1&imported='.$imported.'&updated='.$updated);
|
||||
exit;
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
header('Location: stock.php?import_error='.urlencode($e->getMessage()));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
header('Location: stock.php?import_error=No+file');
|
||||
exit;
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
content = content[:start_idx] + new_import_code + content[end_idx:]
|
||||
|
||||
# Update the modal text to reflect XLSX support
|
||||
content = content.replace("ملاحظة: يدعم النظام ملفات CSV فقط. يرجى حفظ ملف Excel بصيغة (CSV UTF-8).", "ملاحظة: يدعم النظام الآن ملفات Excel (XLSX) بالإضافة إلى CSV.")
|
||||
content = content.replace("Note: The system supports CSV files only. Please save your Excel file as (CSV UTF-8).", "Note: The system now supports Excel (XLSX) files in addition to CSV.")
|
||||
content = content.replace('accept=".csv"', 'accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"')
|
||||
|
||||
with open("stock.php", "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
|
||||
print("Patched successfully")
|
||||
@ -1,31 +0,0 @@
|
||||
import re
|
||||
|
||||
with open("stock.php", "r") as f:
|
||||
content = f.read()
|
||||
|
||||
replacement = """ $valid_categories = $pdo->query("SELECT id FROM categories")->fetchAll(PDO::FETCH_COLUMN);
|
||||
$valid_suppliers = $pdo->query("SELECT id FROM suppliers")->fetchAll(PDO::FETCH_COLUMN);
|
||||
$valid_units = $pdo->query("SELECT id FROM units")->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
if (count($row) < 5) continue;
|
||||
$sku = trim((string)$row[0]); $name = trim((string)$row[1]);
|
||||
if ($sku === '' || $name === '') continue;
|
||||
$price = (float)($row[2] ?? 0);
|
||||
$cost_price = (float)($row[3] ?? 0);
|
||||
$base_stock = (int)($row[4] ?? 0);
|
||||
$vat = (float)($row[5] ?? 5);
|
||||
$category_id = (!empty($row[6]) && in_array((int)$row[6], $valid_categories)) ? (int)$row[6] : null;
|
||||
$supplier_id = (!empty($row[7]) && in_array((int)$row[7], $valid_suppliers)) ? (int)$row[7] : null;
|
||||
$unit_id = (!empty($row[8]) && in_array((int)$row[8], $valid_units)) ? (int)$row[8] : null;"""
|
||||
|
||||
pattern = r" foreach \(\$rows as \$row\) \{[^\}]+?\$unit_id = !empty\(\$row\[8\]\) \? \(int\)\$row\[8\] : null;"
|
||||
|
||||
new_content = re.sub(pattern, replacement, content, flags=re.DOTALL)
|
||||
|
||||
if replacement in new_content:
|
||||
with open("stock.php", "w") as f:
|
||||
f.write(new_content)
|
||||
print("Success")
|
||||
else:
|
||||
print("Failed")
|
||||
@ -1,44 +0,0 @@
|
||||
import re
|
||||
|
||||
with open('stock.php', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Update openItemModal definition
|
||||
content = content.replace(
|
||||
"function openItemModal(sku = '', name = '', price = '', cost_price = '', base_stock = '', vat = '<?= h(get_setting('vat_percentage', 5)) ?>', category_id = '', supplier_id = '', unit_id = '', image_url = '') {",
|
||||
"function openItemModal(sku = '', name = '', price = '', cost_price = '', base_stock = '', vat = '<?= h(get_setting('vat_percentage', 5)) ?>', category_id = '', supplier_id = '', unit_id = '', image_url = '', in_catalog = 0) {"
|
||||
)
|
||||
|
||||
# Add UI toggle in the form
|
||||
switch_html = """
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('الظهور في المتجر (الكتالوج)', 'Visible in Catalog')) ?></label>
|
||||
<div class="form-check form-switch mt-2">
|
||||
<input class="form-check-input" type="checkbox" id="item_in_catalog" style="transform: scale(1.3); margin-left: -1.5em; margin-right: 0.5em;">
|
||||
<label class="form-check-label" for="item_in_catalog" style="margin-right: 2.5em;"><?= h(tr('عرض أونلاين', 'Show Online')) ?></label>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
content = content.replace(
|
||||
'<div class="col-md-6 mb-3">\n <label class="form-label"><?= h(tr(\'التكلفة\', \'Cost Price\')) ?></label>',
|
||||
switch_html + '<div class="col-md-6 mb-3">\n <label class="form-label"><?= h(tr(\'التكلفة\', \'Cost Price\')) ?></label>'
|
||||
)
|
||||
|
||||
# Update Javascript form handling
|
||||
content = content.replace(
|
||||
"document.getElementById('item_unit').value = unit_id;",
|
||||
"document.getElementById('item_unit').value = unit_id;\n document.getElementById('item_in_catalog').checked = (parseInt(in_catalog) === 1);"
|
||||
)
|
||||
|
||||
content = content.replace(
|
||||
"formData.append('unit_id', document.getElementById('item_unit').value);",
|
||||
"formData.append('unit_id', document.getElementById('item_unit').value);\n formData.append('in_catalog', document.getElementById('item_in_catalog').checked ? '1' : '0');"
|
||||
)
|
||||
|
||||
# Replace the onclick in the button where we pass the arguments
|
||||
search_str = "onclick=\"openItemModal('<?= h($row['sku']) ?>', '<?= h(addslashes($row['name'])) ?>', '<?= h($row['price']) ?>', '<?= h($row['cost_price'] ?? 0) ?>', '<?= h($row['base_stock']) ?>', '<?= h($row['vat'] ?? get_setting('vat_percentage', 5)) ?>', '<?= h($row['category_id'] ?? '') ?>', '<?= h($row['supplier_id'] ?? '') ?>', '<?= h($row['unit_id'] ?? '') ?>', '<?= h($row['image_url'] ?? '') ?>')\""
|
||||
replace_str = "onclick=\"openItemModal('<?= h($row['sku']) ?>', '<?= h(addslashes($row['name'])) ?>', '<?= h($row['price']) ?>', '<?= h($row['cost_price'] ?? 0) ?>', '<?= h($row['base_stock']) ?>', '<?= h($row['vat'] ?? get_setting('vat_percentage', 5)) ?>', '<?= h($row['category_id'] ?? '') ?>', '<?= h($row['supplier_id'] ?? '') ?>', '<?= h($row['unit_id'] ?? '') ?>', '<?= h($row['image_url'] ?? '') ?>', '<?= h($row['in_catalog'] ?? 0) ?>')\""
|
||||
content = content.replace(search_str, replace_str)
|
||||
|
||||
with open('stock.php', 'w') as f:
|
||||
f.write(content)
|
||||
121
pos.php
121
pos.php
@ -31,6 +31,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$error = tr('اختر فرعاً صالحاً لهذه الصلاحية.', 'Choose a valid branch for this role.');
|
||||
} elseif (!in_array($paymentMethod, ['cash', 'card', 'transfer', 'pay_later'], true)) {
|
||||
$error = tr('اختر طريقة دفع صحيحة.', 'Choose a valid payment method.');
|
||||
} elseif ($paymentMethod === 'pay_later' && !$customerId) {
|
||||
$error = tr('يجب اختيار عميل مسجل للدفع الآجل.', 'You must select a registered customer for pay later.');
|
||||
} elseif (!is_array($items) || $items === []) {
|
||||
$error = tr('أضف صنفاً واحداً على الأقل إلى السلة.', 'Add at least one item to the cart.');
|
||||
} else {
|
||||
@ -202,17 +204,58 @@ require __DIR__ . '/includes/header.php';
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
}
|
||||
.product-badges {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 0.35rem;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
.product-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
.product-badge-new {
|
||||
background: rgba(13, 202, 240, 0.14);
|
||||
color: #087990;
|
||||
}
|
||||
.product-badge-no-image {
|
||||
background: rgba(108, 117, 125, 0.12);
|
||||
color: #6c757d;
|
||||
}
|
||||
.product-title {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.25rem;
|
||||
margin-bottom: 0.35rem;
|
||||
color: #212529;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
line-height: 1.25;
|
||||
min-height: 2.5em;
|
||||
word-break: break-word;
|
||||
}
|
||||
.product-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.15rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.product-sku,
|
||||
.product-created {
|
||||
font-size: 0.76rem;
|
||||
color: #6c757d;
|
||||
line-height: 1.2;
|
||||
height: 2.4em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.product-price {
|
||||
font-weight: 700;
|
||||
@ -383,22 +426,38 @@ require __DIR__ . '/includes/header.php';
|
||||
$itemPrice = h($item['price']);
|
||||
$itemCat = h($item['category_id'] ?? '');
|
||||
$imageUrl = !empty($item['image_url']) ? h($item['image_url']) : '';
|
||||
$createdAtRaw = (string)($item['created_at'] ?? '');
|
||||
$createdAtStamp = $createdAtRaw !== '' ? strtotime($createdAtRaw) : false;
|
||||
$isRecentlyAdded = $createdAtStamp && $createdAtStamp >= strtotime('-7 days');
|
||||
$createdLabel = $createdAtStamp ? date('Y-m-d', $createdAtStamp) : '';
|
||||
?>
|
||||
<div class="product-card" data-sku="<?= $itemSku ?>" data-name="<?= $itemName ?>" data-price="<?= $itemPrice ?>" data-cat="<?= $itemCat ?>" onclick="addToCart('<?= $itemSku ?>')">
|
||||
<div class="product-card" data-sku="<?= $itemSku ?>" data-name="<?= $itemName ?>" data-price="<?= $itemPrice ?>" data-cat="<?= $itemCat ?>" data-search="<?= h(mb_strtolower($itemName . ' ' . $sku)) ?>" data-created="<?= h($createdAtRaw) ?>" onclick="addToCart('<?= $itemSku ?>')">
|
||||
<div class="product-img-wrapper">
|
||||
<?php if (!empty($imageUrl)):
|
||||
$imgAlt = h(tr('صورة المنتج', 'Product Image'));
|
||||
?>
|
||||
<img src="<?= $imageUrl ?>" alt="<?= $imgAlt ?>" class="product-img" loading="lazy">
|
||||
<?php else:
|
||||
$placeholderText = h(tr('لا توجد صورة', 'No Image'));
|
||||
?>
|
||||
<i class="bi bi-image product-placeholder"></i>
|
||||
<?php else: ?>
|
||||
<i class="bi bi-image product-placeholder" aria-hidden="true"></i>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="product-info">
|
||||
<div class="product-title"><?= $itemName ?></div>
|
||||
<div class="product-price"><?= h(currency($itemPrice)) ?></div>
|
||||
<div class="product-badges">
|
||||
<?php if ($isRecentlyAdded): ?>
|
||||
<span class="product-badge product-badge-new"><?= h(tr('جديد', 'New')) ?></span>
|
||||
<?php endif; ?>
|
||||
<?php if (empty($imageUrl)): ?>
|
||||
<span class="product-badge product-badge-no-image"><?= h(tr('بدون صورة', 'No image')) ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="product-title" title="<?= $itemName ?>"><?= $itemName ?></div>
|
||||
<div class="product-meta">
|
||||
<div class="product-sku" title="<?= $itemSku ?>">SKU: <?= $itemSku ?></div>
|
||||
<?php if ($createdLabel !== ''): ?>
|
||||
<div class="product-created"><?= h(tr('أضيف', 'Added')) ?>: <?= h($createdLabel) ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="product-price"><?= h(currency((float)$item['price'])) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
@ -610,7 +669,7 @@ async function saveNewCustomer() {
|
||||
const name = document.getElementById('ncName').value.trim();
|
||||
const phone = document.getElementById('ncPhone').value.trim();
|
||||
if (!name) {
|
||||
alert('<?= h(tr('الاسم مطلوب', 'Name is required')) ?>');
|
||||
Swal.fire({icon: 'warning', text: '<?= h(tr('الاسم مطلوب', 'Name is required')) ?>'});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -632,10 +691,10 @@ async function saveNewCustomer() {
|
||||
const Toast = Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, timer: 2000 });
|
||||
Toast.fire({ icon: 'success', title: '<?= h(tr('تم إضافة العميل', 'Customer added')) ?>' });
|
||||
} else {
|
||||
alert(data.error);
|
||||
Swal.fire({icon: 'warning', text: data.error});
|
||||
}
|
||||
} catch(err) {
|
||||
alert('Error saving customer');
|
||||
Swal.fire({icon: 'warning', text: 'Error saving customer'});
|
||||
}
|
||||
}
|
||||
|
||||
@ -665,21 +724,17 @@ document.getElementById('posBarcode').addEventListener('keypress', function(e) {
|
||||
});
|
||||
|
||||
function applyFilters() {
|
||||
const q = document.getElementById('posSearch').value.toLowerCase();
|
||||
const q = document.getElementById('posSearch').value.toLowerCase().trim();
|
||||
const activeCat = document.querySelector('.cat-btn.active').dataset.cat;
|
||||
|
||||
document.querySelectorAll('.product-card').forEach(card => {
|
||||
const name = card.dataset.name.toLowerCase();
|
||||
const searchable = (card.dataset.search || card.dataset.name || '').toLowerCase();
|
||||
const cat = card.dataset.cat;
|
||||
|
||||
const matchesSearch = name.includes(q);
|
||||
const matchesSearch = q === '' || searchable.includes(q);
|
||||
const matchesCat = (activeCat === 'all' || cat === activeCat);
|
||||
|
||||
if (matchesSearch && matchesCat) {
|
||||
card.style.display = 'block';
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
card.style.display = (matchesSearch && matchesCat) ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
@ -807,6 +862,11 @@ function submitSale(method) {
|
||||
const branch = document.getElementById('posBranch').value || '<?= h($allowedBranches[0] ?? '') ?>';
|
||||
const customer = document.getElementById('posCustomer').value;
|
||||
const customerId = document.getElementById('posCustomer').dataset.id || '';
|
||||
|
||||
if (method === 'pay_later' && !customerId) {
|
||||
Swal.fire({icon: 'warning', text: '<?= h(tr('يجب اختيار عميل مسجل للدفع الآجل.', 'You must select a registered customer for pay later.')) ?>'});
|
||||
return;
|
||||
}
|
||||
|
||||
const itemsArr = Object.values(cart).map(item => ({
|
||||
sku: item.sku,
|
||||
@ -914,11 +974,24 @@ function resumeOrder(index) {
|
||||
|
||||
// Warn if cart is not empty
|
||||
if (Object.keys(cart).length > 0) {
|
||||
if (!confirm('<?= h(tr('السلة الحالية غير فارغة، هل تريد استبدالها؟', 'Current cart is not empty, replace it?')) ?>')) {
|
||||
return;
|
||||
}
|
||||
Swal.fire({
|
||||
title: '<?= h(tr("تنبيه", "Alert")) ?>',
|
||||
text: '<?= h(tr("السلة الحالية غير فارغة، هل تريد استبدالها؟", "Current cart is not empty, replace it?")) ?>',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '<?= h(tr("نعم", "Yes")) ?>',
|
||||
cancelButtonText: '<?= h(tr("إلغاء", "Cancel")) ?>'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
doResumeOrder(index, order, orders);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
doResumeOrder(index, order, orders);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function doResumeOrder(index, order, orders) {
|
||||
cart = order.cart;
|
||||
document.getElementById('posCustomer').value = order.name.includes('#') ? '' : order.name;
|
||||
if (document.getElementById('posBranch').querySelector(`option[value="${order.branch}"]`)) {
|
||||
|
||||
5363
pos_out.html
Normal file
5363
pos_out.html
Normal file
File diff suppressed because one or more lines are too long
0
pos_out2.html
Normal file
0
pos_out2.html
Normal file
@ -253,9 +253,18 @@ $registerNo = 'REG-01';
|
||||
<span><?= h(tr('الإجمالي', 'Total')) ?></span>
|
||||
<span><?= number_format((float)$sale['total_amount'], 3) ?> <?= h(tr('ر.ع', 'OMR')) ?></span>
|
||||
</div>
|
||||
<?php
|
||||
$pm = $sale['payment_method'];
|
||||
$pmLabel = $pm;
|
||||
if ($pm === 'cash') $pmLabel = tr('نقدي', 'Cash');
|
||||
elseif ($pm === 'card') $pmLabel = tr('بطاقة', 'Card');
|
||||
elseif ($pm === 'transfer') $pmLabel = tr('تحويل', 'Transfer');
|
||||
elseif ($pm === 'pay_later') $pmLabel = tr('آجل', 'Pay Later');
|
||||
else $pmLabel = ucfirst(str_replace('_', ' ', (string)$pm));
|
||||
?>
|
||||
<div class="totals-row">
|
||||
<span><?= h(tr('طريقة الدفع', 'Payment Method')) ?></span>
|
||||
<span><?= h(ucfirst((string)$sale['payment_method'])) ?></span>
|
||||
<span><?= h($pmLabel) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -283,4 +292,4 @@ $registerNo = 'REG-01';
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@ -566,9 +566,7 @@ require __DIR__ . '/includes/header.php';
|
||||
|
||||
<script>
|
||||
function markPaidFromReports(id) {
|
||||
if(confirm("<?= h(tr('هل أنت متأكد من تأكيد دفع هذا الطلب؟', 'Are you sure you want to confirm payment for this order?')) ?>")) {
|
||||
window.location.href = 'sales.php?mark_paid=' + id + '&redirect=reports.php?tab=orders';
|
||||
}
|
||||
Swal.fire({ title: "<?= h(tr('تأكيد الدفع', 'Confirm payment')) ?>", text: "<?= h(tr('هل أنت متأكد من تأكيد دفع هذا الطلب؟', 'Are you sure you want to confirm payment for this order?')) ?>", icon: "warning", showCancelButton: true, confirmButtonText: "<?= h(tr('نعم', 'Yes')) ?>", cancelButtonText: "<?= h(tr('إلغاء', 'Cancel')) ?>" }).then((result) => { if(result.isConfirmed) { window.location.href = 'sales.php?mark_paid=' + id + '&redirect=reports.php?tab=orders'; } });
|
||||
}
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
11
sale.php
11
sale.php
@ -355,8 +355,17 @@ require __DIR__ . '/includes/header.php';
|
||||
<div class="meta-label"><?= h(tr('تاريخ الإصدار', 'Date Issued')) ?>:</div>
|
||||
<div class="meta-val"><?= h(date('Y-m-d H:i', strtotime((string) $sale['sale_date']))) ?></div>
|
||||
|
||||
<?php
|
||||
$pm = $sale['payment_method'];
|
||||
$pmLabel = $pm;
|
||||
if ($pm === 'cash') $pmLabel = tr('نقدي', 'Cash');
|
||||
elseif ($pm === 'card') $pmLabel = tr('بطاقة', 'Card');
|
||||
elseif ($pm === 'transfer') $pmLabel = tr('تحويل', 'Transfer');
|
||||
elseif ($pm === 'pay_later') $pmLabel = tr('آجل', 'Pay Later');
|
||||
else $pmLabel = ucfirst(str_replace('_', ' ', (string)$pm));
|
||||
?>
|
||||
<div class="meta-label"><?= h(tr('طريقة الدفع', 'Payment')) ?>:</div>
|
||||
<div class="meta-val"><?= h(ucfirst((string) $sale['payment_method'])) ?></div>
|
||||
<div class="meta-val"><?= h($pmLabel) ?></div>
|
||||
|
||||
<div class="meta-label"><?= h(tr('الحالة', 'Status')) ?>:</div>
|
||||
<div class="meta-val"><?= ($sale['status'] ?? 'completed') === 'order' ? h(tr('غير مدفوعة', 'Unpaid')) : h(tr('مدفوعة', 'Paid')) ?></div>
|
||||
|
||||
@ -165,7 +165,7 @@ require __DIR__ . '/includes/header.php';
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<?php if ($account['id'] !== $user['id'] && has_permission("users", "del")): ?>
|
||||
<form method="POST" class="d-inline" onsubmit="return confirm('<?= h(tr('هل أنت متأكد؟', 'Are you sure?')) ?>');">
|
||||
<form method="POST" class="d-inline" onsubmit="confirmSwal(event, '<?= h(tr('هل أنت متأكد؟', 'Are you sure?')) ?>');">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="id" value="<?= h($account['id']) ?>">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger rounded-circle shadow-sm ms-1" style="width: 34px; height: 34px; padding: 0;" title="<?= h(tr('حذف', 'Delete')) ?>">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user