Autosave: 20260419-143136
This commit is contained in:
parent
c4d7baf9d6
commit
cc0da06fbb
@ -59,7 +59,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$lineTotal = $price * $qty;
|
||||
|
||||
$vatPercent = (float) ($product['vat'] ?? 0);
|
||||
$itemVat = $lineTotal - ($lineTotal / (1 + ($vatPercent / 100)));
|
||||
$itemVat = $lineTotal * ($vatPercent / 100);
|
||||
$totalVat += $itemVat;
|
||||
|
||||
$normalized[] = [
|
||||
@ -389,7 +389,7 @@ require __DIR__ . '/includes/header.php';
|
||||
<span id="displaySubtotal" class="fw-medium">0.000</span>
|
||||
</div>
|
||||
<div class="totals-row">
|
||||
<span><?= h(tr('الضريبة (مشمولة)', 'VAT (Included)')) ?></span>
|
||||
<span><?= h(tr('الضريبة (مضافة)', 'VAT (Added)')) ?></span>
|
||||
<span id="displayVat" class="text-muted">0.000</span>
|
||||
</div>
|
||||
<div class="totals-row grand-total">
|
||||
@ -657,7 +657,7 @@ function renderInvoice() {
|
||||
const lineTotal = item.qty * item.price;
|
||||
|
||||
const vatPercent = parseFloat(catalogData[sku].vat) || 0;
|
||||
const itemVat = lineTotal - (lineTotal / (1 + (vatPercent / 100)));
|
||||
const itemVat = lineTotal * (vatPercent / 100);
|
||||
totalVat += itemVat;
|
||||
|
||||
totalAmount += lineTotal;
|
||||
@ -688,10 +688,11 @@ function renderInvoice() {
|
||||
}
|
||||
|
||||
function updateTotals(total, vat) {
|
||||
const subtotal = total - vat;
|
||||
const subtotal = total;
|
||||
const finalTotal = subtotal + vat;
|
||||
document.getElementById('displaySubtotal').innerText = subtotal.toFixed(3);
|
||||
document.getElementById('displayVat').innerText = vat.toFixed(3);
|
||||
document.getElementById('displayTotal').innerText = total.toFixed(3) + currencySuffix;
|
||||
document.getElementById('displayTotal').innerText = finalTotal.toFixed(3) + currencySuffix;
|
||||
}
|
||||
|
||||
renderInvoice();
|
||||
|
||||
1231
includes/SimpleXLSX.php
Normal file
1231
includes/SimpleXLSX.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -276,9 +276,9 @@ function create_sale(array $data): int
|
||||
ensure_sales_table();
|
||||
|
||||
$stmt = db()->prepare('INSERT INTO sales_orders
|
||||
(receipt_no, sale_mode, branch_code, cashier_username, cashier_name, role_name, customer_name, payment_method, items_json, item_count, subtotal, total_amount, status, notes, sale_date)
|
||||
(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, :total_amount, :status, :notes, NOW())');
|
||||
(: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())');
|
||||
|
||||
$stmt->bindValue(':receipt_no', $data['receipt_no']);
|
||||
$stmt->bindValue(':sale_mode', $data['sale_mode']);
|
||||
@ -291,9 +291,9 @@ function create_sale(array $data): int
|
||||
$stmt->bindValue(':items_json', json_encode($data['items'], JSON_UNESCAPED_UNICODE));
|
||||
$stmt->bindValue(':item_count', $data['item_count'], PDO::PARAM_INT);
|
||||
$stmt->bindValue(':subtotal', $data['subtotal']);
|
||||
$stmt->bindValue(':vat_amount', $data['vat_amount'] ?? 0.0);
|
||||
$stmt->bindValue(':total_amount', $data['total_amount']);
|
||||
$stmt->bindValue(':status', $data['status'] ?? 'completed');
|
||||
$stmt->bindValue(':status', $data['status'] ?? 'completed');
|
||||
$stmt->bindValue(':notes', $data['notes']);
|
||||
$stmt->execute();
|
||||
|
||||
@ -415,6 +415,7 @@ function report_metrics(): array
|
||||
$paymentTotals = [];
|
||||
$productTotals = [];
|
||||
$gross = 0.0;
|
||||
$totalVat = 0.0;
|
||||
|
||||
foreach ($sales as $sale) {
|
||||
$branch = $sale['branch_code'];
|
||||
@ -422,6 +423,7 @@ function report_metrics(): array
|
||||
$payment = $sale['payment_method'];
|
||||
$paymentTotals[$payment] = ($paymentTotals[$payment] ?? 0.0) + (float) $sale['total_amount'];
|
||||
$gross += (float) $sale['total_amount'];
|
||||
$totalVat += (float) $sale['vat_amount'];
|
||||
foreach ($sale['items'] as $item) {
|
||||
$sku = (string) ($item['sku'] ?? '');
|
||||
$qty = (int) ($item['qty'] ?? 0);
|
||||
@ -435,6 +437,7 @@ function report_metrics(): array
|
||||
|
||||
return [
|
||||
'gross' => $gross,
|
||||
'total_vat' => $totalVat,
|
||||
'branch_totals' => $branchTotals,
|
||||
'payment_totals' => $paymentTotals,
|
||||
'product_totals' => $productTotals,
|
||||
@ -467,6 +470,7 @@ function stock_snapshot(): array
|
||||
'cost_price' => $item['cost_price'] ?? 0,
|
||||
'category_id' => $item['category_id'],
|
||||
'supplier_id' => $item['supplier_id'],
|
||||
'unit_id' => $item['unit_id'],
|
||||
'image_url' => $item['image_url'],
|
||||
'vat' => $item['vat'],
|
||||
];
|
||||
@ -508,9 +512,9 @@ function create_purchase(array $data): int
|
||||
db()->beginTransaction();
|
||||
try {
|
||||
$stmt = db()->prepare("INSERT INTO purchase_orders
|
||||
(reference_no, branch_code, user_username, user_name, role_name, supplier_name, items_json, item_count, subtotal, total_amount, status, notes, purchase_date)
|
||||
(reference_no, branch_code, user_username, user_name, role_name, supplier_name, items_json, item_count, subtotal, vat_amount, total_amount, status, notes, purchase_date)
|
||||
VALUES
|
||||
(:reference_no, :branch_code, :user_username, :user_name, :role_name, :supplier_name, :items_json, :item_count, :subtotal, :total_amount, :status, :notes, NOW())");
|
||||
(:reference_no, :branch_code, :user_username, :user_name, :role_name, :supplier_name, :items_json, :item_count, :subtotal, :vat_amount, :total_amount, :status, :notes, NOW())");
|
||||
|
||||
$stmt->bindValue(":reference_no", $data["reference_no"]);
|
||||
$stmt->bindValue(":branch_code", $data["branch_code"]);
|
||||
@ -521,6 +525,7 @@ function create_purchase(array $data): int
|
||||
$stmt->bindValue(":items_json", json_encode($data["items"], JSON_UNESCAPED_UNICODE));
|
||||
$stmt->bindValue(":item_count", $data["item_count"], PDO::PARAM_INT);
|
||||
$stmt->bindValue(":subtotal", $data["subtotal"]);
|
||||
$stmt->bindValue(":vat_amount", $data["vat_amount"] ?? 0.0);
|
||||
$stmt->bindValue(":total_amount", $data["total_amount"]);
|
||||
$stmt->bindValue(":status", $data["status"] ?? "completed");
|
||||
$stmt->bindValue(":notes", $data["notes"]);
|
||||
|
||||
@ -32,6 +32,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
} else {
|
||||
$normalized = [];
|
||||
$subtotal = 0.0;
|
||||
$totalVat = 0.0;
|
||||
$globalVat = (float) get_setting('vat_percentage', 5);
|
||||
$itemCount = 0;
|
||||
foreach ($items as $item) {
|
||||
$sku = (string) ($item['sku'] ?? '');
|
||||
@ -51,6 +53,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
'line_total' => $lineTotal,
|
||||
];
|
||||
$subtotal += $lineTotal;
|
||||
$itemVat = $lineTotal * ($globalVat / 100);
|
||||
$totalVat += $itemVat;
|
||||
$itemCount += $qty;
|
||||
}
|
||||
|
||||
@ -68,7 +72,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
'items' => $normalized,
|
||||
'item_count' => $itemCount,
|
||||
'subtotal' => $subtotal,
|
||||
'total_amount' => $subtotal,
|
||||
'vat_amount' => $totalVat,
|
||||
'total_amount' => $subtotal + $totalVat,
|
||||
'status' => $purchaseStatus,
|
||||
'notes' => $notes !== '' ? $notes : null,
|
||||
]);
|
||||
@ -358,7 +363,7 @@ require __DIR__ . '/header.php';
|
||||
</div>
|
||||
<div class="totals-row">
|
||||
<span><?= h(tr('الضريبة (' . get_setting('vat_percentage', 5) . '%)', 'VAT (' . get_setting('vat_percentage', 5) . '%)')) ?></span>
|
||||
<span class="text-success small"><?= h(tr('مشمولة', 'Included')) ?></span>
|
||||
<span id="displayVat" class="text-muted">0.000</span>
|
||||
</div>
|
||||
<div class="totals-row grand-total">
|
||||
<span><?= h(tr('الإجمالي', 'Total')) ?></span>
|
||||
@ -606,19 +611,22 @@ function renderInvoice() {
|
||||
if (skus.length === 0) {
|
||||
tbody.innerHTML = '';
|
||||
tbody.appendChild(emptyRow);
|
||||
updateTotals(0);
|
||||
updateTotals(0, 0);
|
||||
cartJson.value = '[]';
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = '';
|
||||
let totalAmount = 0;
|
||||
let totalVat = 0;
|
||||
const globalVat = <?= get_setting('vat_percentage', 5) ?>;
|
||||
const cartData = [];
|
||||
|
||||
skus.forEach(sku => {
|
||||
const item = invoiceItems[sku];
|
||||
const lineTotal = item.qty * item.price;
|
||||
totalAmount += lineTotal;
|
||||
totalVat += lineTotal * (globalVat / 100);
|
||||
cartData.push({ sku: item.sku, qty: item.qty, price: item.price });
|
||||
|
||||
const tr = document.createElement('tr');
|
||||
@ -643,13 +651,16 @@ function renderInvoice() {
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
|
||||
updateTotals(totalAmount);
|
||||
updateTotals(totalAmount, totalVat);
|
||||
cartJson.value = JSON.stringify(cartData);
|
||||
}
|
||||
|
||||
function updateTotals(total) {
|
||||
document.getElementById('displaySubtotal').innerText = total.toFixed(3);
|
||||
document.getElementById('displayTotal').innerText = total.toFixed(3) + currencySuffix;
|
||||
function updateTotals(total, vat) {
|
||||
const subtotal = total;
|
||||
const finalTotal = subtotal + vat;
|
||||
document.getElementById('displaySubtotal').innerText = subtotal.toFixed(3);
|
||||
document.getElementById('displayVat').innerText = vat.toFixed(3);
|
||||
document.getElementById('displayTotal').innerText = finalTotal.toFixed(3) + currencySuffix;
|
||||
}
|
||||
|
||||
// Intercept form submission to check if items exist
|
||||
|
||||
@ -45,7 +45,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
$vatPercent = (float) ($product['vat'] ?? 0);
|
||||
// Assuming price is inclusive of VAT:
|
||||
$itemVat = $lineTotal - ($lineTotal / (1 + ($vatPercent / 100)));
|
||||
$itemVat = $lineTotal * ($vatPercent / 100);
|
||||
$totalVat += $itemVat;
|
||||
|
||||
$normalized[] = [
|
||||
@ -77,9 +77,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
'payment_method' => $paymentMethod,
|
||||
'items' => $normalized,
|
||||
'item_count' => $itemCount,
|
||||
'subtotal' => $subtotal - $totalVat,
|
||||
'subtotal' => $subtotal,
|
||||
'vat_amount' => $totalVat,
|
||||
'total_amount' => $subtotal,
|
||||
'total_amount' => $subtotal + $totalVat,
|
||||
'status' => $saleStatus,
|
||||
'notes' => $notes !== '' ? $notes : null,
|
||||
]);
|
||||
@ -370,7 +370,7 @@ require __DIR__ . '/header.php';
|
||||
<span id="displaySubtotal" class="fw-medium">0.000</span>
|
||||
</div>
|
||||
<div class="totals-row">
|
||||
<span><?= h(tr('الضريبة (مشمولة)', 'VAT (Included)')) ?></span>
|
||||
<span><?= h(tr('الضريبة (مضافة)', 'VAT (Added)')) ?></span>
|
||||
<span id="displayVat" class="text-muted">0.000</span>
|
||||
</div>
|
||||
<div class="totals-row grand-total">
|
||||
@ -626,7 +626,7 @@ function renderInvoice() {
|
||||
const lineTotal = item.qty * item.price;
|
||||
|
||||
const vatPercent = parseFloat(catalogData[sku].vat) || 0;
|
||||
const itemVat = lineTotal - (lineTotal / (1 + (vatPercent / 100)));
|
||||
const itemVat = lineTotal * (vatPercent / 100);
|
||||
totalVat += itemVat;
|
||||
|
||||
totalAmount += lineTotal;
|
||||
@ -657,10 +657,11 @@ function renderInvoice() {
|
||||
}
|
||||
|
||||
function updateTotals(total, vat) {
|
||||
const subtotal = total - vat;
|
||||
const subtotal = total;
|
||||
const finalTotal = subtotal + vat;
|
||||
document.getElementById('displaySubtotal').innerText = subtotal.toFixed(3);
|
||||
document.getElementById('displayVat').innerText = vat.toFixed(3);
|
||||
document.getElementById('displayTotal').innerText = total.toFixed(3) + currencySuffix;
|
||||
document.getElementById('displayTotal').innerText = finalTotal.toFixed(3) + currencySuffix;
|
||||
}
|
||||
|
||||
// Intercept form submission to check if items exist
|
||||
|
||||
13
patch_export.php
Normal file
13
patch_export.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
$content = file_get_contents('stock.php');
|
||||
$search = "echo \"\";";
|
||||
$replace = 'echo "\xEF\xBB\xBF";';
|
||||
|
||||
if (strpos($content, $search) !== false) {
|
||||
$content = str_replace($search, $replace, $content);
|
||||
file_put_contents('stock.php', $content);
|
||||
echo "Replaced successfully.\n";
|
||||
} else {
|
||||
echo "Search string not found.\n";
|
||||
}
|
||||
|
||||
69
patch_import.php
Normal file
69
patch_import.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
$content = file_get_contents('stock.php');
|
||||
$search = <<<'REPLACE'
|
||||
if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] === UPLOAD_ERR_OK) {
|
||||
$pdo = db();
|
||||
$file = fopen($_FILES['csv_file']['tmp_name'], 'r');
|
||||
$bom = fread($file, 3);
|
||||
if ($bom !== "") rewind($file);
|
||||
$header = fgetcsv($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=?");
|
||||
while (($row = fgetcsv($file)) !== false) {
|
||||
REPLACE;
|
||||
|
||||
$replace = <<<'REPLACE'
|
||||
if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] === UPLOAD_ERR_OK) {
|
||||
$pdo = db();
|
||||
$file_path = $_FILES['csv_file']['tmp_name'];
|
||||
$raw_content = file_get_contents($file_path);
|
||||
|
||||
// Prevent ZIP / XLSX
|
||||
if (str_starts_with($raw_content, 'PK')) {
|
||||
header('Location: stock.php?import_error=' . urlencode('يرجى حفظ الملف بصيغة CSV وليس كملف إكسل (XLSX)'));
|
||||
exit;
|
||||
}
|
||||
|
||||
// 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);
|
||||
$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=?");
|
||||
while (($row = fgetcsv($clean_file, 0, $delimiter)) !== false) {
|
||||
REPLACE;
|
||||
|
||||
if (strpos($content, $search) !== false) {
|
||||
$content = str_replace($search, $replace, $content);
|
||||
file_put_contents('stock.php', $content);
|
||||
echo "Replaced successfully.\n";
|
||||
} else {
|
||||
echo "Search string not found.\n";
|
||||
}
|
||||
|
||||
118
patch_import.py
Normal file
118
patch_import.py
Normal file
@ -0,0 +1,118 @@
|
||||
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")
|
||||
31
patch_import_fk.py
Normal file
31
patch_import_fk.py
Normal file
@ -0,0 +1,31 @@
|
||||
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")
|
||||
24
pos.php
24
pos.php
@ -34,6 +34,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
} else {
|
||||
$normalized = [];
|
||||
$subtotal = 0.0;
|
||||
$totalVat = 0.0;
|
||||
$itemCount = 0;
|
||||
foreach ($items as $item) {
|
||||
$sku = (string) ($item['sku'] ?? '');
|
||||
@ -53,6 +54,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
'line_total' => $lineTotal,
|
||||
];
|
||||
$subtotal += $lineTotal;
|
||||
$vatPercent = (float) ($product['vat'] ?? 0);
|
||||
$itemVat = $lineTotal * ($vatPercent / 100);
|
||||
$totalVat += $itemVat;
|
||||
$itemCount += $qty;
|
||||
}
|
||||
|
||||
@ -72,7 +76,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
'items' => $normalized,
|
||||
'item_count' => $itemCount,
|
||||
'subtotal' => $subtotal,
|
||||
'total_amount' => $subtotal,
|
||||
'vat_amount' => $totalVat,
|
||||
'total_amount' => $subtotal + $totalVat,
|
||||
'notes' => $notes !== '' ? $notes : null,
|
||||
]);
|
||||
|
||||
@ -433,6 +438,10 @@ require __DIR__ . '/includes/header.php';
|
||||
<span><?= h(tr('المجموع الفرعي', 'Subtotal')) ?></span>
|
||||
<span id="cartSubtotalVal">0.000</span>
|
||||
</div>
|
||||
<div class="summary-row">
|
||||
<span><?= h(tr('الضريبة (مضافة)', 'VAT (Added)')) ?></span>
|
||||
<span id="cartVatVal" class="text-muted">0.000</span>
|
||||
</div>
|
||||
<div class="summary-total">
|
||||
<span><?= h(tr('الإجمالي', 'Total')) ?></span>
|
||||
<span class="text-primary" id="cartTotalVal">0.000</span>
|
||||
@ -694,6 +703,7 @@ function renderCart() {
|
||||
const list = document.getElementById('cartItemsList');
|
||||
const badge = document.getElementById('cartBadgeCount');
|
||||
const subVal = document.getElementById('cartSubtotalVal');
|
||||
const vatVal = document.getElementById('cartVatVal');
|
||||
const totalVal = document.getElementById('cartTotalVal');
|
||||
const btnPay = document.getElementById('btnPay');
|
||||
const btnHold = document.getElementById('btnHold');
|
||||
@ -702,6 +712,7 @@ function renderCart() {
|
||||
list.innerHTML = '';
|
||||
let total = 0;
|
||||
let count = 0;
|
||||
let totalVat = 0;
|
||||
|
||||
const skus = Object.keys(cart);
|
||||
if (skus.length === 0) {
|
||||
@ -713,6 +724,7 @@ function renderCart() {
|
||||
`;
|
||||
badge.innerText = '0';
|
||||
subVal.innerText = `0.000 ${currencyLabel}`;
|
||||
vatVal.innerText = `0.000`;
|
||||
totalVal.innerText = `0.000 ${currencyLabel}`;
|
||||
btnPay.disabled = true;
|
||||
btnHold.disabled = true;
|
||||
@ -723,6 +735,9 @@ function renderCart() {
|
||||
skus.forEach(sku => {
|
||||
const item = cart[sku];
|
||||
const lineTotal = item.price * item.qty;
|
||||
const vatPercent = parseFloat(catalogData[sku].vat) || 0;
|
||||
const itemVat = lineTotal * (vatPercent / 100);
|
||||
totalVat += itemVat;
|
||||
total += lineTotal;
|
||||
count += item.qty;
|
||||
|
||||
@ -746,8 +761,11 @@ function renderCart() {
|
||||
});
|
||||
|
||||
badge.innerText = count;
|
||||
const totalStr = `${total.toFixed(3)} ${currencyLabel}`;
|
||||
subVal.innerText = totalStr;
|
||||
const subtotal = total;
|
||||
const finalTotal = subtotal + totalVat;
|
||||
const totalStr = `${finalTotal.toFixed(3)} ${currencyLabel}`;
|
||||
subVal.innerText = subtotal.toFixed(3) + ' ' + currencyLabel;
|
||||
vatVal.innerText = totalVat.toFixed(3);
|
||||
totalVal.innerText = totalStr;
|
||||
document.getElementById('modalTotalAmount').innerText = totalStr;
|
||||
|
||||
|
||||
@ -248,7 +248,7 @@ $registerNo = 'REG-01';
|
||||
<span><?= number_format((float)$sale['subtotal'], 3) ?></span>
|
||||
</div>
|
||||
<div class="totals-row">
|
||||
<span><?= h(tr('ضريبة القيمة المضافة (مشمولة)', 'VAT (Inclusive)')) ?></span>
|
||||
<span><?= h(tr('ضريبة القيمة المضافة (مضافة)', 'VAT (Added)')) ?></span>
|
||||
<span><?= number_format((float)($sale['vat_amount'] ?? 0), 3) ?></span>
|
||||
</div>
|
||||
<div class="totals-row grand-total">
|
||||
|
||||
@ -70,6 +70,8 @@ require __DIR__ . '/includes/header.php';
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('المورد', 'Supplier')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('المرجع', 'Reference')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الفرع', 'Branch')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('المجموع', 'Subtotal')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الضريبة', 'VAT')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الإجمالي', 'Total Amount')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الحالة', 'Status')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('التاريخ', 'Date')) ?></th>
|
||||
@ -85,7 +87,9 @@ require __DIR__ . '/includes/header.php';
|
||||
<td><?= h($row['supplier_name'] ?: '-') ?></td>
|
||||
<td><?= h($row['reference_no']) ?></td>
|
||||
<td><?= h(branch_label($row['branch_code'])) ?></td>
|
||||
<td class="fw-semibold"><?= h(currency((float)$row['total_amount'])) ?></td>
|
||||
<td class="text-muted"><?= h(currency((float)$row['subtotal'])) ?></td>
|
||||
<td class="text-muted text-danger"><?= h(currency((float)$row['vat_amount'])) ?></td>
|
||||
<td class="fw-bold text-success"><?= h(currency((float)$row['total_amount'])) ?></td>
|
||||
<td>
|
||||
<?php if ($row['status'] === 'order'): ?>
|
||||
<span class="badge bg-warning text-dark px-3 py-2 rounded-pill"><i class="bi bi-clock"></i> <?= h(tr('طلب شراء', 'Order')) ?></span>
|
||||
|
||||
17
reports.php
17
reports.php
@ -142,13 +142,19 @@ require __DIR__ . '/includes/header.php';
|
||||
<th><?= h(tr('الفرع', 'Branch')) ?></th>
|
||||
<th><?= h(tr('طريقة الدفع', 'Payment Method')) ?></th>
|
||||
<th><?= h(tr('الحالة', 'Status')) ?></th>
|
||||
<th class="text-end"><?= h(tr('المجموع', 'Subtotal')) ?></th>
|
||||
<th class="text-end"><?= h(tr('الضريبة', 'VAT')) ?></th>
|
||||
<th class="text-end"><?= h(tr('الإجمالي', 'Total')) ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$subtotalSum = 0;
|
||||
$vatSum = 0;
|
||||
$totalSum = 0;
|
||||
foreach($salesReport as $sale):
|
||||
$subtotalSum += (float) ($sale['subtotal'] ?? 0);
|
||||
$vatSum += (float) ($sale['vat_amount'] ?? 0);
|
||||
$totalSum += (float) $sale['total_amount'];
|
||||
?>
|
||||
<tr>
|
||||
@ -164,6 +170,8 @@ require __DIR__ . '/includes/header.php';
|
||||
<span class="badge bg-success"><i class="bi bi-check-circle"></i> <?= h(tr('مدفوع', 'Paid')) ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-end"><?= h(currency((float)($sale['subtotal'] ?? 0))) ?></td>
|
||||
<td class="text-end"><?= h(currency((float)($sale['vat_amount'] ?? 0))) ?></td>
|
||||
<td class="text-end fw-bold"><?= h(currency((float)$sale['total_amount'])) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
@ -171,6 +179,8 @@ require __DIR__ . '/includes/header.php';
|
||||
<tfoot class="table-dark">
|
||||
<tr>
|
||||
<td colspan="6" class="text-end"><?= h(tr('الإجمالي الكلي', 'Grand Total')) ?></td>
|
||||
<td class="text-end fw-bold fs-6"><?= h(currency($subtotalSum)) ?></td>
|
||||
<td class="text-end fw-bold fs-6"><?= h(currency($vatSum)) ?></td>
|
||||
<td class="text-end fw-bold fs-5"><?= h(currency($totalSum)) ?></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
@ -263,9 +273,10 @@ require __DIR__ . '/includes/header.php';
|
||||
<?php else: ?>
|
||||
|
||||
<section class="row g-3 mb-4 d-print-none">
|
||||
<div class="col-md-4"><article class="metric-card"><div class="eyebrow"><?= h(tr('إجمالي المبيعات', 'Gross sales')) ?></div><div class="metric-value"><?= h(currency((float) $report['gross'])) ?></div><div class="small text-muted"><?= h(tr('حسب نطاق صلاحية المستخدم الحالي', 'Scoped to the current viewer permissions')) ?></div></article></div>
|
||||
<div class="col-md-4"><article class="metric-card"><div class="eyebrow"><?= h(tr('عدد الفواتير', 'Invoices')) ?></div><div class="metric-value"><?= h((string) $report['sales_count']) ?></div><div class="small text-muted"><?= h(tr('إجمالي الفواتير المسجلة', 'Total logged invoices')) ?></div></article></div>
|
||||
<div class="col-md-4"><article class="metric-card"><div class="eyebrow"><?= h(tr('أفضل صنف', 'Top product')) ?></div><div class="metric-value small-metric"><?= h($report['product_totals'] ? product_label((string) array_key_first($report['product_totals'])) : tr('لا يوجد', 'None yet')) ?></div><div class="small text-muted"><?= h(tr('الأكثر مبيعاً حتى الآن', 'Most sold item so far')) ?></div></article></div>
|
||||
<div class="col-md-3"><article class="metric-card"><div class="eyebrow"><?= h(tr('إجمالي المبيعات', 'Gross sales')) ?></div><div class="metric-value"><?= h(currency((float) $report['gross'])) ?></div><div class="small text-muted"><?= h(tr('حسب نطاق صلاحية المستخدم الحالي', 'Scoped to the current viewer permissions')) ?></div></article></div>
|
||||
<div class="col-md-3"><article class="metric-card"><div class="eyebrow"><?= h(tr('إجمالي الضريبة', 'Total VAT')) ?></div><div class="metric-value"><?= h(currency((float) ($report['total_vat'] ?? 0))) ?></div><div class="small text-muted"><?= h(tr('مجموع ضريبة القيمة المضافة', 'Total Value Added Tax')) ?></div></article></div>
|
||||
<div class="col-md-3"><article class="metric-card"><div class="eyebrow"><?= h(tr('عدد الفواتير', 'Invoices')) ?></div><div class="metric-value"><?= h((string) $report['sales_count']) ?></div><div class="small text-muted"><?= h(tr('إجمالي الفواتير المسجلة', 'Total logged invoices')) ?></div></article></div>
|
||||
<div class="col-md-3"><article class="metric-card"><div class="eyebrow"><?= h(tr('أفضل صنف', 'Top product')) ?></div><div class="metric-value small-metric"><?= h($report['product_totals'] ? product_label((string) array_key_first($report['product_totals'])) : tr('لا يوجد', 'None yet')) ?></div><div class="small text-muted"><?= h(tr('الأكثر مبيعاً حتى الآن', 'Most sold item so far')) ?></div></article></div>
|
||||
</section>
|
||||
<section class="row g-4 d-print-none">
|
||||
<div class="col-lg-6">
|
||||
|
||||
2
sale.php
2
sale.php
@ -424,7 +424,7 @@ require __DIR__ . '/includes/header.php';
|
||||
<td class="total-amount"><?= h(number_format((float) $sale['subtotal'], 3)) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="total-label"><?= h(tr('ضريبة القيمة المضافة (مشمولة)', 'VAT (Inclusive)')) ?></td>
|
||||
<td class="total-label"><?= h(tr('ضريبة القيمة المضافة (مضافة)', 'VAT (Added)')) ?></td>
|
||||
<td class="total-amount"><?= number_format((float)($sale['vat_amount'] ?? 0), 3) ?></td>
|
||||
</tr>
|
||||
<tr class="grand-total-row">
|
||||
|
||||
@ -131,6 +131,8 @@ require __DIR__ . '/includes/header.php';
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('النوع', 'Type')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الكاشير', 'Cashier')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('العميل', 'Customer')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('المجموع', 'Subtotal')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الضريبة', 'VAT')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الإجمالي', 'Total')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الحالة', 'Status')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('التاريخ', 'Date')) ?></th>
|
||||
@ -148,7 +150,9 @@ require __DIR__ . '/includes/header.php';
|
||||
<td><span class="badge text-bg-light border"><?= h(sale_mode_label((string) $sale['sale_mode'])) ?></span></td>
|
||||
<td><?= h((string) $sale['cashier_name']) ?></td>
|
||||
<td><?= h((string) ($sale['customer_name'] ?: '-')) ?></td>
|
||||
<td class="fw-semibold"><?= h(currency((float) $sale['total_amount'])) ?></td>
|
||||
<td class="text-muted"><?= h(currency((float) $sale['subtotal'])) ?></td>
|
||||
<td class="text-muted text-danger"><?= h(currency((float) $sale['vat_amount'])) ?></td>
|
||||
<td class="fw-bold text-success"><?= h(currency((float) $sale['total_amount'])) ?></td>
|
||||
<td>
|
||||
<?php if (($sale['status'] ?? 'completed') === 'order'): ?>
|
||||
<span class="badge bg-warning text-dark px-3 py-2 rounded-pill"><i class="bi bi-clock"></i> <?= h(tr('طلب حجز', 'Order')) ?></span>
|
||||
|
||||
76
stock.php
76
stock.php
@ -12,7 +12,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action']) && $_GET['act
|
||||
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
header('Content-Type: text/csv; charset=utf-8');
|
||||
header('Content-Disposition: attachment; filename=stock_export_' . date('Ymd_His') . '.csv');
|
||||
echo "";
|
||||
echo "\xEF\xBB\xBF";
|
||||
$output = fopen('php://output', 'w');
|
||||
fputcsv($output, ['SKU', 'Name', 'Price', 'Cost Price', 'Stock', 'VAT', 'Category ID', 'Supplier ID', 'Unit ID']);
|
||||
foreach ($items as $row) { fputcsv($output, $row); }
|
||||
@ -23,28 +23,76 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action']) && $_GET['act
|
||||
// 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 = fopen($_FILES['csv_file']['tmp_name'], 'r');
|
||||
$bom = fread($file, 3);
|
||||
if ($bom !== "") rewind($file);
|
||||
$header = fgetcsv($file);
|
||||
$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, "")) {
|
||||
$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, "
|
||||
");
|
||||
$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=?");
|
||||
while (($row = fgetcsv($file)) !== false) {
|
||||
|
||||
$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($row[0]); $name = trim($row[1]);
|
||||
$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;
|
||||
$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;
|
||||
$stmtCheck->execute([$sku]);
|
||||
if ($stmtCheck->fetchColumn()) {
|
||||
$stmtUpdate->execute([$name, $price, $cost_price, $base_stock, $vat, $category_id, $supplier_id, $unit_id, $sku]);
|
||||
@ -285,7 +333,7 @@ require __DIR__ . '/includes/header.php';
|
||||
</th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent">SKU</th>
|
||||
<th width="70" class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('صورة', 'Pic')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent text-start"><?= h(tr('الصنف', 'Product')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent text-end pe-4"><?= h(tr('الصنف', 'Product')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('السعر', 'Price')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('التكلفة', 'Cost')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('افتتاحي', 'Opening')) ?></th>
|
||||
@ -314,7 +362,7 @@ require __DIR__ . '/includes/header.php';
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="fw-bold text-start text-dark"><?= h($row['name']) ?></td>
|
||||
<td class="fw-bold text-end text-dark pe-4"><?= h($row['name']) ?></td>
|
||||
<td><span class="badge bg-light text-dark border px-2 py-1"><?= h(currency($row['price'])) ?></span></td>
|
||||
<td><span class="badge bg-light text-secondary border px-2 py-1"><?= h(currency($row['cost_price'] ?? 0)) ?></span></td>
|
||||
<td class="text-secondary"><?= h((string) $row['base_stock']) ?></td>
|
||||
@ -370,7 +418,7 @@ require __DIR__ . '/includes/header.php';
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-warning small">
|
||||
<?= h(tr('ملاحظة: يدعم النظام ملفات CSV فقط. يرجى حفظ ملف Excel بصيغة (CSV UTF-8).', 'Note: The system supports CSV files only. Please save your Excel file as (CSV UTF-8).')) ?>
|
||||
<?= h(tr('ملاحظة: يدعم النظام الآن ملفات Excel (XLSX) بالإضافة إلى CSV.', 'Note: The system now supports Excel (XLSX) files in addition to CSV.')) ?>
|
||||
</div>
|
||||
<p class="text-muted small mb-2">
|
||||
<?= h(tr('يجب أن يحتوي الملف على الأعمدة التالية بالترتيب الدقيق:', 'The file must contain exactly these columns in order:')) ?><br>
|
||||
@ -378,7 +426,7 @@ require __DIR__ . '/includes/header.php';
|
||||
</p>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('ملف الإكسل / CSV', 'Excel / CSV File')) ?></label>
|
||||
<input type="file" class="form-control" name="csv_file" accept=".csv" required>
|
||||
<input type="file" class="form-control" name="csv_file" accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user