Autosave: 20260419-091830
This commit is contained in:
parent
4f9eddc419
commit
9b7ec271a9
26
api/suppliers.php
Normal file
26
api/suppliers.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../includes/app.php';
|
||||
$user = require_auth();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
header('Content-Type: application/json');
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
$phone = trim($_POST['phone'] ?? '');
|
||||
|
||||
if (!$name) {
|
||||
echo json_encode(['success' => false, 'error' => tr('الاسم مطلوب', 'Name is required')]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare('INSERT INTO suppliers (name, phone) VALUES (?, ?)');
|
||||
$stmt->execute([$name, $phone]);
|
||||
$id = $pdo->lastInsertId();
|
||||
|
||||
echo json_encode(['success' => true, 'supplier' => ['id' => $id, 'name' => $name, 'phone' => $phone]]);
|
||||
} catch (Throwable $e) {
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
@ -230,6 +230,7 @@ function catalog(): array
|
||||
"name_ar" => $item["name"],
|
||||
"name_en" => $item["name"],
|
||||
"price" => (float)$item["price"],
|
||||
"cost_price" => (float)($item["cost_price"] ?? 0),
|
||||
"base_stock" => (int)$item["base_stock"],
|
||||
"vat" => (float)$item["vat"],
|
||||
"category_id" => $item["category_id"],
|
||||
@ -488,6 +489,7 @@ function stock_snapshot(): array
|
||||
'sold' => $used,
|
||||
'available' => max(0, $base - $used),
|
||||
'price' => $item['price'],
|
||||
'cost_price' => $item['cost_price'] ?? 0,
|
||||
'category_id' => $item['category_id'],
|
||||
'supplier_id' => $item['supplier_id'],
|
||||
'image_url' => $item['image_url'],
|
||||
@ -525,3 +527,47 @@ function receipt_code(): string
|
||||
{
|
||||
return 'AR-' . date('ymd-His') . '-' . random_int(100, 999);
|
||||
}
|
||||
|
||||
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)
|
||||
VALUES
|
||||
(:reference_no, :branch_code, :user_username, :user_name, :role_name, :supplier_name, :items_json, :item_count, :subtotal, :total_amount, :status, :notes, NOW())");
|
||||
|
||||
$stmt->bindValue(":reference_no", $data["reference_no"]);
|
||||
$stmt->bindValue(":branch_code", $data["branch_code"]);
|
||||
$stmt->bindValue(":user_username", $data["user_username"]);
|
||||
$stmt->bindValue(":user_name", $data["user_name"]);
|
||||
$stmt->bindValue(":role_name", $data["role_name"]);
|
||||
$stmt->bindValue(":supplier_name", $data["supplier_name"]);
|
||||
$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(":total_amount", $data["total_amount"]);
|
||||
$stmt->bindValue(":status", $data["status"] ?? "completed");
|
||||
$stmt->bindValue(":notes", $data["notes"]);
|
||||
$stmt->execute();
|
||||
|
||||
$purchaseId = (int) db()->lastInsertId();
|
||||
|
||||
// Update stock
|
||||
foreach ($data["items"] as $item) {
|
||||
$qty = (int) $item["qty"];
|
||||
$sku = $item["sku"];
|
||||
$updateStmt = db()->prepare("UPDATE items SET base_stock = base_stock + :qty, cost_price = :price WHERE sku = :sku");
|
||||
$updateStmt->bindValue(":qty", $qty, PDO::PARAM_INT);
|
||||
$updateStmt->bindValue(":price", $item["price"]);
|
||||
$updateStmt->bindValue(":sku", $sku);
|
||||
$updateStmt->execute();
|
||||
}
|
||||
|
||||
db()->commit();
|
||||
return $purchaseId;
|
||||
} catch (Throwable $e) {
|
||||
db()->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,16 +70,8 @@ $isPublic = !isset($user) || !$user;
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'dashboard' ? 'active' : '' ?>" href="<?= h(url_for('index.php')) ?>">
|
||||
<i class="bi bi-speedometer2"></i> <?= h(tr('لوحة التحكم', 'Dashboard')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'pos' ? 'active' : '' ?>" href="<?= h(url_for('pos.php')) ?>">
|
||||
<i class="bi bi-cart-check"></i> <?= h(tr('نقاط البيع', 'POS Sale')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'normal' ? 'active' : '' ?>" href="<?= h(url_for('normal_sale.php')) ?>">
|
||||
<i class="bi bi-receipt"></i> <?= h(tr('بيع عادي', 'Normal Sale')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'sales' ? 'active' : '' ?>" href="<?= h(url_for('sales.php')) ?>">
|
||||
<i class="bi bi-journal-text"></i> <?= h(tr('المبيعات', 'Sales')) ?>
|
||||
</a>
|
||||
|
||||
|
||||
<!-- المخزون (Inventory) - Now First -->
|
||||
<a class="list-group-item list-group-item-action <?= in_array($activeNav, ['stock', 'categories', 'units']) ? '' : 'collapsed' ?>" data-bs-toggle="collapse" href="#collapseStock" role="button" aria-expanded="<?= in_array($activeNav, ['stock', 'categories', 'units']) ? 'true' : 'false' ?>" aria-controls="collapseStock">
|
||||
<div class="d-flex justify-content-between align-items-center w-100">
|
||||
<span><i class="bi bi-box-seam"></i> <?= h(tr('المخزون', 'Inventory')) ?></span>
|
||||
@ -99,10 +91,44 @@ $isPublic = !isset($user) || !$user;
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'purchases' ? 'active' : '' ?>" href="<?= h(url_for('purchases.php')) ?>">
|
||||
<i class="bi bi-bag-plus"></i> <?= h(tr('المشتريات', 'Purchases')) ?>
|
||||
|
||||
<!-- المبيعات (Sales) - Now Collapsible -->
|
||||
<a class="list-group-item list-group-item-action <?= in_array($activeNav, ['sales', 'normal', 'pos']) ? '' : 'collapsed' ?>" data-bs-toggle="collapse" href="#collapseSales" role="button" aria-expanded="<?= in_array($activeNav, ['sales', 'normal', 'pos']) ? 'true' : 'false' ?>" aria-controls="collapseSales">
|
||||
<div class="d-flex justify-content-between align-items-center w-100">
|
||||
<span><i class="bi bi-cart"></i> <?= h(tr('المبيعات', 'Sales')) ?></span>
|
||||
<i class="bi bi-chevron-down toggle-icon" style="transition: transform 0.2s;"></i>
|
||||
</div>
|
||||
</a>
|
||||
<div class="collapse <?= in_array($activeNav, ['sales', 'normal', 'pos']) ? 'show' : '' ?>" id="collapseSales">
|
||||
<div class="list-group list-group-flush" style="background-color: rgba(0,0,0,0.15);">
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'sales' ? 'active' : '' ?>" href="<?= h(url_for('sales.php')) ?>" style="padding-left: 2.5rem; padding-right: 2.5rem;">
|
||||
<i class="bi bi-dot"></i> <?= h(tr('قائمة الفواتير', 'Invoice list')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'normal' ? 'active' : '' ?>" href="<?= h(url_for('normal_sale.php')) ?>" style="padding-left: 2.5rem; padding-right: 2.5rem;">
|
||||
<i class="bi bi-dot"></i> <?= h(tr('فاتورة جديدة', 'New invoice')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'pos' ? 'active' : '' ?>" href="<?= h(url_for('pos.php')) ?>" style="padding-left: 2.5rem; padding-right: 2.5rem;">
|
||||
<i class="bi bi-dot"></i> <?= h(tr('نقاط البيع', 'POS')) ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="list-group-item list-group-item-action <?= in_array($activeNav, ['purchases', 'new_purchase']) ? '' : 'collapsed' ?>" data-bs-toggle="collapse" href="#collapsePurchases" role="button" aria-expanded="<?= in_array($activeNav, ['purchases', 'new_purchase']) ? 'true' : 'false' ?>" aria-controls="collapsePurchases">
|
||||
<div class="d-flex justify-content-between align-items-center w-100">
|
||||
<span><i class="bi bi-bag-plus"></i> <?= h(tr('المشتريات', 'Purchases')) ?></span>
|
||||
<i class="bi bi-chevron-down toggle-icon" style="transition: transform 0.2s;"></i>
|
||||
</div>
|
||||
</a>
|
||||
<div class="collapse <?= in_array($activeNav, ['purchases', 'new_purchase']) ? 'show' : '' ?>" id="collapsePurchases">
|
||||
<div class="list-group list-group-flush" style="background-color: rgba(0,0,0,0.15);">
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'purchases' ? 'active' : '' ?>" href="<?= h(url_for('purchases.php')) ?>" style="padding-left: 2.5rem; padding-right: 2.5rem;">
|
||||
<i class="bi bi-dot"></i> <?= h(tr('قائمة المشتريات', 'Purchase list')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'new_purchase' ? 'active' : '' ?>" href="<?= h(url_for('new_purchase.php')) ?>" style="padding-left: 2.5rem; padding-right: 2.5rem;">
|
||||
<i class="bi bi-dot"></i> <?= h(tr('فاتورة مشتريات جديدة', 'New purchase')) ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'suppliers' ? 'active' : '' ?>" href="<?= h(url_for('suppliers.php')) ?>">
|
||||
<i class="bi bi-truck"></i> <?= h(tr('الموردون', 'Suppliers')) ?>
|
||||
</a>
|
||||
|
||||
664
includes/purchase_form.php
Normal file
664
includes/purchase_form.php
Normal file
@ -0,0 +1,664 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/app.php';
|
||||
$user = require_roles(['owner', 'manager', 'cashier']);
|
||||
$pageTitle = tr('فاتورة مشتريات جديدة', 'New Purchase');
|
||||
$activeNav = 'new_purchase';
|
||||
$error = '';
|
||||
$catalog = catalog();
|
||||
$allowedBranches = $user['role'] === 'owner' ? array_keys(branches()) : [$user['branch_code']];
|
||||
|
||||
try {
|
||||
$customers = $customers = [];
|
||||
$suppliers = db()->query('SELECT id, name, phone FROM suppliers ORDER BY name ASC')->fetchAll();
|
||||
} catch (Throwable $e) {
|
||||
$customers = [];
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$branchCode = trim((string) ($_POST['branch_code'] ?? ''));
|
||||
$supplierName = trim((string) ($_POST['supplier_name'] ?? ''));
|
||||
$paymentMethod = trim((string) ($_POST['payment_method'] ?? 'cash'));
|
||||
$purchaseStatus = trim((string) ($_POST['purchase_status'] ?? 'completed'));
|
||||
$notes = trim((string) ($_POST['notes'] ?? ''));
|
||||
$cartJson = (string) ($_POST['cart_json'] ?? '[]');
|
||||
$items = json_decode($cartJson, true);
|
||||
|
||||
if (!in_array($branchCode, $allowedBranches, true)) {
|
||||
$error = tr('اختر فرعاً صالحاً لهذه الصلاحية.', 'Choose a valid branch for this role.');
|
||||
} elseif (!in_array($paymentMethod, ['cash', 'card', 'transfer'], true)) {
|
||||
$error = tr('اختر طريقة دفع صحيحة.', 'Choose a valid payment method.');
|
||||
} elseif (!is_array($items) || $items === []) {
|
||||
$error = tr('أضف صنفاً واحداً على الأقل إلى الفاتورة.', 'Add at least one item to the invoice.');
|
||||
} else {
|
||||
$normalized = [];
|
||||
$subtotal = 0.0;
|
||||
$itemCount = 0;
|
||||
foreach ($items as $item) {
|
||||
$sku = (string) ($item['sku'] ?? '');
|
||||
$qty = (int) ($item['qty'] ?? 0);
|
||||
if (!isset($catalog[$sku]) || $qty < 1) {
|
||||
continue;
|
||||
}
|
||||
$product = $catalog[$sku];
|
||||
$price = isset($item['price']) ? (float)$item['price'] : (float)($product['cost_price'] ?? $product['price']);
|
||||
$lineTotal = $price * $qty;
|
||||
$normalized[] = [
|
||||
'sku' => $sku,
|
||||
'name_ar' => $product['name_ar'],
|
||||
'name_en' => $product['name_en'],
|
||||
'qty' => $qty,
|
||||
'price' => $price,
|
||||
'line_total' => $lineTotal,
|
||||
];
|
||||
$subtotal += $lineTotal;
|
||||
$itemCount += $qty;
|
||||
}
|
||||
|
||||
if ($normalized === []) {
|
||||
$error = tr('الفاتورة غير صالحة بعد التحقق من الأصناف.', 'The invoice is invalid after product validation.');
|
||||
} else {
|
||||
$cashierName = current_lang() === 'ar' ? $user['name_ar'] : $user['name_en'];
|
||||
$purchaseId = create_purchase([
|
||||
'reference_no' => receipt_code(),
|
||||
'branch_code' => $branchCode,
|
||||
'user_username' => $user['username'],
|
||||
'user_name' => $cashierName,
|
||||
'role_name' => $user['role'],
|
||||
'supplier_name' => $supplierName !== '' ? $supplierName : null,
|
||||
'items' => $normalized,
|
||||
'item_count' => $itemCount,
|
||||
'subtotal' => $subtotal,
|
||||
'total_amount' => $subtotal,
|
||||
'status' => $purchaseStatus,
|
||||
'notes' => $notes !== '' ? $notes : null,
|
||||
]);
|
||||
|
||||
set_flash('success', tr('تم حفظ فاتورة المشتريات بنجاح. وتم تحديث المخزون.', 'Purchase invoice saved successfully and stock updated.'));
|
||||
redirect_to('purchases.php');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
require __DIR__ . '/header.php';
|
||||
?>
|
||||
|
||||
<style>
|
||||
.smart-form-card {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
|
||||
border: 1px solid #edf2f9;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.smart-form-header {
|
||||
padding: 1.5rem 2rem;
|
||||
border-bottom: 1px solid #edf2f9;
|
||||
background-color: #fcfdfd;
|
||||
border-radius: 12px 12px 0 0;
|
||||
}
|
||||
.smart-form-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.form-label {
|
||||
font-weight: 500;
|
||||
color: #495057;
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
.custom-input {
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 8px;
|
||||
padding: 0.6rem 1rem;
|
||||
font-size: 0.95rem;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
.custom-input:focus {
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 0 0 0.25rem rgba(59, 130, 246, 0.25);
|
||||
}
|
||||
.search-wrapper {
|
||||
position: relative;
|
||||
max-width: 600px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 1rem;
|
||||
transform: translateY(-50%);
|
||||
color: #6c757d;
|
||||
}
|
||||
[dir="rtl"] .search-icon {
|
||||
left: auto;
|
||||
right: 1rem;
|
||||
}
|
||||
.search-input {
|
||||
padding-left: 2.5rem;
|
||||
}
|
||||
[dir="rtl"] .search-input {
|
||||
padding-left: 1rem;
|
||||
padding-right: 2.5rem;
|
||||
}
|
||||
.item-search-dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
||||
z-index: 1000;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
display: none;
|
||||
border: 1px solid #edf2f9;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.item-search-dropdown.show { display: block; }
|
||||
.search-item-row {
|
||||
padding: 0.75rem 1rem;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid #edf2f9;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.search-item-row:hover { background: #f8f9fa; }
|
||||
.search-item-row:last-child { border-bottom: none; }
|
||||
|
||||
.table-modern {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
border: 1px solid #edf2f9;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.table-modern th {
|
||||
background: #f8f9fa;
|
||||
padding: 1rem;
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
border-bottom: 1px solid #edf2f9;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.table-modern td {
|
||||
padding: 1rem;
|
||||
vertical-align: middle;
|
||||
border-bottom: 1px solid #edf2f9;
|
||||
}
|
||||
.table-modern tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
.qty-control {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 6px;
|
||||
padding: 0.4rem;
|
||||
}
|
||||
.btn-remove {
|
||||
color: #dc3545;
|
||||
background: rgba(220, 53, 69, 0.1);
|
||||
border: none;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.btn-remove:hover {
|
||||
background: #dc3545;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.totals-box {
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
border: 1px solid #edf2f9;
|
||||
}
|
||||
.totals-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem;
|
||||
color: #495057;
|
||||
}
|
||||
.totals-row.grand-total {
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid #dee2e6;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: #212529;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container-fluid py-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h3 class="fw-bold mb-0 text-dark"><?= h($pageTitle) ?></h3>
|
||||
<a href="sales.php" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="bi bi-arrow-left"></i> <?= h(tr('عودة للمبيعات', 'Back to Sales')) ?>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if ($error !== ''): ?>
|
||||
<div class="alert alert-danger rounded-3 shadow-sm mb-4"><i class="bi bi-exclamation-triangle-fill me-2"></i><?= h($error) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" id="smart-sale-form">
|
||||
<input type="hidden" name="cart_json" id="cart_json" value="[]">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<!-- Items Section -->
|
||||
<div class="smart-form-card">
|
||||
<div class="smart-form-header">
|
||||
<div class="section-title mb-0">
|
||||
<i class="bi bi-cart-plus text-primary"></i> <?= h(tr('عناصر الفاتورة', 'Invoice Items')) ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="smart-form-body">
|
||||
|
||||
<!-- Search Bar -->
|
||||
<div class="search-wrapper">
|
||||
<i class="bi bi-search search-icon"></i>
|
||||
<input type="text" id="itemSearchInput" class="form-control custom-input search-input form-control-lg" placeholder="<?= h(tr('ابحث بالاسم أو الباركود...', 'Search by name or barcode...')) ?>" autocomplete="off">
|
||||
<div id="itemDropdown" class="item-search-dropdown"></div>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="table-responsive">
|
||||
<table class="table-modern" id="invoiceTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="45%"><?= h(tr('المنتج', 'Product')) ?></th>
|
||||
<th width="15%" class="text-center"><?= h(tr('السعر', 'Price')) ?></th>
|
||||
<th width="15%" class="text-center"><?= h(tr('الكمية', 'Qty')) ?></th>
|
||||
<th width="20%" class="text-center"><?= h(tr('الإجمالي', 'Total')) ?></th>
|
||||
<th width="5%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="invoiceLines">
|
||||
<tr id="emptyInvoiceRow">
|
||||
<td colspan="5" class="text-center py-5 text-muted">
|
||||
<i class="bi bi-inbox fs-1 d-block mb-2 text-light"></i>
|
||||
<?= h(tr('لم يتم إضافة أي منتجات بعد.', 'No products added yet.')) ?>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<!-- Settings Section -->
|
||||
<div class="smart-form-card">
|
||||
<div class="smart-form-header">
|
||||
<div class="section-title mb-0">
|
||||
<i class="bi bi-receipt text-primary"></i> <?= h(tr('تفاصيل الفاتورة', 'Invoice Details')) ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="smart-form-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('الفرع', 'Branch')) ?></label>
|
||||
<select class="form-select custom-input" name="branch_code" <?= count($allowedBranches) === 1 ? 'readonly' : '' ?>>
|
||||
<?php foreach ($allowedBranches as $branchCode): ?>
|
||||
<option value="<?= h($branchCode) ?>"><?= h(branch_label($branchCode)) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3 position-relative">
|
||||
<label class="form-label"><?= h(tr('المورد', 'Supplier')) ?></label>
|
||||
<div class="input-group">
|
||||
<input type="text" id="formSupplier" name="supplier_name" class="form-control custom-input" style="border-right-width: 1px;" placeholder="<?= h(tr('بحث (اسم أو هاتف)', 'Search (Name or Phone)')) ?>" autocomplete="off">
|
||||
<button class="btn btn-outline-primary px-3" style="border-radius: 0 8px 8px 0;" type="button" onclick="openNewSupplierModal()" title="<?= h(tr('إضافة مورد', 'Add Supplier')) ?>">
|
||||
<i class="bi bi-person-plus-fill"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="formSupplierDropdown" class="item-search-dropdown w-100" style="top: 100%;"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('نوع العملية', 'Entry Type')) ?></label>
|
||||
<select class="form-select custom-input" name="purchase_status">
|
||||
<option value="completed"><?= h(tr('مكتمل (تم الاستلام)', 'Completed (Received)')) ?></option>
|
||||
<option value="order"><?= h(tr('طلب شراء (قيد الانتظار)', 'Purchase Order (Pending)')) ?></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('طريقة الدفع', 'Payment Method')) ?></label>
|
||||
<select class="form-select custom-input" name="payment_method">
|
||||
<option value="cash"><?= h(tr('نقداً', 'Cash')) ?></option>
|
||||
<option value="card"><?= h(tr('بطاقة ائتمان', 'Credit Card')) ?></option>
|
||||
<option value="transfer"><?= h(tr('تحويل بنكي', 'Bank Transfer')) ?></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label"><?= h(tr('ملاحظات (اختياري)', 'Notes (Optional)')) ?></label>
|
||||
<textarea class="form-control custom-input" name="notes" rows="2" placeholder="<?= h(tr('أي ملاحظات إضافية...', 'Any additional notes...')) ?>"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Summary -->
|
||||
<div class="totals-box mb-4">
|
||||
<div class="totals-row">
|
||||
<span><?= h(tr('المجموع الفرعي', 'Subtotal')) ?></span>
|
||||
<span id="displaySubtotal" class="fw-medium">0.000</span>
|
||||
</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>
|
||||
</div>
|
||||
<div class="totals-row grand-total">
|
||||
<span><?= h(tr('الإجمالي', 'Total')) ?></span>
|
||||
<span id="displayTotal" class="text-primary">0.000 <?= h(tr('ر.ع', 'OMR')) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100 py-2 fs-5 rounded-3 shadow-sm">
|
||||
<i class="bi bi-check-circle me-1"></i> <?= h(tr('حفظ الفاتورة', 'Save Invoice')) ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- New Customer Modal -->
|
||||
<div class="modal fade" id="newSupplierModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-sm">
|
||||
<div class="modal-content border-0 shadow-lg" style="border-radius: 16px;">
|
||||
<div class="modal-header border-0 pb-0">
|
||||
<h5 class="modal-title fw-bold"><?= h(tr('إضافة مورد', 'Add Supplier')) ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-muted small mb-1"><?= h(tr('الاسم', 'Name')) ?> <span class="text-danger">*</span></label>
|
||||
<input type="text" id="ncName" class="form-control rounded-3">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-muted small mb-1"><?= h(tr('رقم الهاتف', 'Phone')) ?></label>
|
||||
<input type="text" id="ncPhone" class="form-control rounded-3" dir="ltr">
|
||||
</div>
|
||||
<div class="d-grid mt-4">
|
||||
<button class="btn btn-primary rounded-pill fw-semibold shadow-sm" onclick="saveNewCustomer()"><?= h(tr('حفظ العميل', 'Save Customer')) ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script>
|
||||
const catalogData = <?= json_encode($catalog, JSON_UNESCAPED_UNICODE) ?>;
|
||||
const catalogArray = Object.values(catalogData);
|
||||
let invoiceItems = {};
|
||||
|
||||
const searchInput = document.getElementById('itemSearchInput');
|
||||
const dropdown = document.getElementById('itemDropdown');
|
||||
const tbody = document.getElementById('invoiceLines');
|
||||
const emptyRow = document.getElementById('emptyInvoiceRow');
|
||||
const cartJson = document.getElementById('cart_json');
|
||||
const currencySuffix = ' <?= h(tr('ر.ع', 'OMR')) ?>';
|
||||
|
||||
// Customers Logic
|
||||
let suppliersData = <?= json_encode($suppliers, JSON_UNESCAPED_UNICODE) ?>;
|
||||
const custInput = document.getElementById('formSupplier');
|
||||
const custDropdown = document.getElementById('formSupplierDropdown');
|
||||
|
||||
custInput.addEventListener('input', function() {
|
||||
const q = this.value.toLowerCase().trim();
|
||||
custDropdown.innerHTML = '';
|
||||
if (q.length < 2) {
|
||||
custDropdown.classList.remove('show');
|
||||
return;
|
||||
}
|
||||
|
||||
const matches = suppliersData.filter(c =>
|
||||
c.name.toLowerCase().includes(q) ||
|
||||
(c.phone && c.phone.toLowerCase().includes(q))
|
||||
).slice(0, 5);
|
||||
|
||||
if (matches.length > 0) {
|
||||
matches.forEach(c => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'search-item-row';
|
||||
div.innerHTML = `<strong>${c.name}</strong> ${c.phone ? '<small class="text-muted ms-2">'+c.phone+'</small>' : ''}`;
|
||||
div.onclick = function() {
|
||||
custInput.value = c.name + (c.phone ? ' - ' + c.phone : '');
|
||||
custDropdown.classList.remove('show');
|
||||
};
|
||||
custDropdown.appendChild(div);
|
||||
});
|
||||
custDropdown.classList.add('show');
|
||||
} else {
|
||||
custDropdown.classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!custInput.contains(e.target) && !custDropdown.contains(e.target)) {
|
||||
custDropdown.classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
let newSupplierModalObj = null;
|
||||
function openNewSupplierModal() {
|
||||
if (!newSupplierModalObj) {
|
||||
newSupplierModalObj = new bootstrap.Modal(document.getElementById('newSupplierModal'));
|
||||
}
|
||||
document.getElementById('ncName').value = '';
|
||||
document.getElementById('ncPhone').value = '';
|
||||
newSupplierModalObj.show();
|
||||
}
|
||||
|
||||
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')) ?>');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('name', name);
|
||||
formData.append('phone', phone);
|
||||
|
||||
try {
|
||||
const res = await fetch('api/suppliers.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
suppliersData.push(data.supplier);
|
||||
custInput.value = data.supplier.name + (data.supplier.phone ? ' - ' + data.supplier.phone : '');
|
||||
newSupplierModalObj.hide();
|
||||
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);
|
||||
}
|
||||
} catch(err) {
|
||||
alert('Error saving customer');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Search logic
|
||||
searchInput.addEventListener('input', function() {
|
||||
const q = this.value.toLowerCase().trim();
|
||||
dropdown.innerHTML = '';
|
||||
|
||||
if (q === '') {
|
||||
dropdown.classList.remove('show');
|
||||
return;
|
||||
}
|
||||
|
||||
const matches = catalogArray.filter(item => {
|
||||
const nameAr = (item.name_ar || '').toLowerCase();
|
||||
const nameEn = (item.name_en || '').toLowerCase();
|
||||
const sku = (item.sku || '').toLowerCase();
|
||||
return nameAr.includes(q) || nameEn.includes(q) || sku.includes(q);
|
||||
}).slice(0, 6);
|
||||
|
||||
if (matches.length > 0) {
|
||||
matches.forEach(item => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'search-item-row d-flex justify-content-between align-items-center';
|
||||
const name = '<?= current_lang() ?>' === 'ar' ? item.name_ar : item.name_en;
|
||||
div.innerHTML = `
|
||||
<div>
|
||||
<div class="fw-medium text-dark">${name}</div>
|
||||
<div class="text-muted small">SKU: ${item.sku}</div>
|
||||
</div>
|
||||
<div class="fw-semibold text-primary">${parseFloat(item.cost_price || item.price).toFixed(3)}</div>
|
||||
`;
|
||||
div.onclick = () => {
|
||||
addItemToInvoice(item.sku);
|
||||
searchInput.value = '';
|
||||
dropdown.classList.remove('show');
|
||||
searchInput.focus();
|
||||
};
|
||||
dropdown.appendChild(div);
|
||||
});
|
||||
dropdown.classList.add('show');
|
||||
} else {
|
||||
dropdown.classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
// Barcode scanner integration on enter
|
||||
searchInput.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
const q = this.value.trim();
|
||||
if(q === '') return;
|
||||
|
||||
const match = catalogArray.find(item => item.sku === q);
|
||||
if (match) {
|
||||
addItemToInvoice(match.sku);
|
||||
searchInput.value = '';
|
||||
dropdown.classList.remove('show');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchInput.contains(e.target) && !dropdown.contains(e.target)) {
|
||||
dropdown.classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
function addItemToInvoice(sku) {
|
||||
if (invoiceItems[sku]) {
|
||||
invoiceItems[sku].qty += 1;
|
||||
} else {
|
||||
const item = catalogData[sku];
|
||||
invoiceItems[sku] = {
|
||||
sku: sku,
|
||||
name: '<?= current_lang() ?>' === 'ar' ? item.name_ar : item.name_en,
|
||||
price: parseFloat(item.cost_price || item.price || 0),
|
||||
qty: 1
|
||||
};
|
||||
}
|
||||
renderInvoice();
|
||||
}
|
||||
|
||||
function changePrice(sku, newPrice) {
|
||||
const price = parseFloat(newPrice);
|
||||
if (!isNaN(price) && price >= 0) {
|
||||
invoiceItems[sku].price = price;
|
||||
}
|
||||
renderInvoice();
|
||||
}
|
||||
|
||||
function changeQty(sku, newQty) {
|
||||
const qty = parseInt(newQty);
|
||||
if (isNaN(qty) || qty < 1) {
|
||||
delete invoiceItems[sku];
|
||||
} else {
|
||||
invoiceItems[sku].qty = qty;
|
||||
}
|
||||
renderInvoice();
|
||||
}
|
||||
|
||||
function removeItem(sku) {
|
||||
delete invoiceItems[sku];
|
||||
renderInvoice();
|
||||
}
|
||||
|
||||
function renderInvoice() {
|
||||
const skus = Object.keys(invoiceItems);
|
||||
if (skus.length === 0) {
|
||||
tbody.innerHTML = '';
|
||||
tbody.appendChild(emptyRow);
|
||||
updateTotals(0);
|
||||
cartJson.value = '[]';
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = '';
|
||||
let totalAmount = 0;
|
||||
const cartData = [];
|
||||
|
||||
skus.forEach(sku => {
|
||||
const item = invoiceItems[sku];
|
||||
const lineTotal = item.qty * item.price;
|
||||
totalAmount += lineTotal;
|
||||
cartData.push({ sku: item.sku, qty: item.qty, price: item.price });
|
||||
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `
|
||||
<td>
|
||||
<div class="fw-medium text-dark">${item.name}</div>
|
||||
<div class="text-muted small">SKU: ${item.sku}</div>
|
||||
</td>
|
||||
<td class="text-center align-middle">
|
||||
<input type="number" step="0.001" class="qty-control mx-auto fw-medium" min="0" value="${item.price.toFixed(3)}" onchange="changePrice('${sku}', this.value)" onkeyup="if(event.key==='Enter') changePrice('${sku}', this.value)">
|
||||
</td>
|
||||
<td class="text-center align-middle">
|
||||
<input type="number" class="qty-control mx-auto fw-medium" min="1" value="${item.qty}" onchange="changeQty('${sku}', this.value)" onkeyup="if(event.key==='Enter') changeQty('${sku}', this.value)">
|
||||
</td>
|
||||
<td class="text-center fw-semibold text-dark align-middle">${lineTotal.toFixed(3)}</td>
|
||||
<td class="text-center align-middle">
|
||||
<button type="button" class="btn-remove mx-auto" onclick="removeItem('${sku}')" title="<?= h(tr('إزالة', 'Remove')) ?>">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
|
||||
updateTotals(totalAmount);
|
||||
cartJson.value = JSON.stringify(cartData);
|
||||
}
|
||||
|
||||
function updateTotals(total) {
|
||||
document.getElementById('displaySubtotal').innerText = total.toFixed(3);
|
||||
document.getElementById('displayTotal').innerText = total.toFixed(3) + currencySuffix;
|
||||
}
|
||||
|
||||
// Intercept form submission to check if items exist
|
||||
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.')) ?>');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php require __DIR__ . '/footer.php'; ?>
|
||||
14
login.php
14
login.php
@ -120,12 +120,6 @@ $accounts = demo_users();
|
||||
<div class="eyebrow text-white-50 mb-3"><?= h(tr('مرحباً بك مجدداً', 'Welcome Back')) ?></div>
|
||||
<h2 class="display-6 fw-bold mb-4"><?= h(current_lang() === 'ar' ? get_setting('company_name_ar', 'نظام إدارة') : get_setting('company_name_en', 'Management System')) ?></h2>
|
||||
<p class="lead opacity-75"><?= h(tr('نظام متكامل لتسجيل المبيعات، وإدارة المخزون، والتقارير في واجهة واحدة.', 'Integrated system for logging sales, managing inventory, and reports in one interface.')) ?></p>
|
||||
|
||||
<div class="mini-grid mt-5 gap-3 d-flex flex-wrap">
|
||||
<div class="stat-chip text-white px-3 py-2 rounded"><strong>3</strong> <span><?= h(tr('أدوار', 'Roles')) ?></span></div>
|
||||
<div class="stat-chip text-white px-3 py-2 rounded"><strong>3</strong> <span><?= h(tr('فروع', 'Branches')) ?></span></div>
|
||||
<div class="stat-chip text-white px-3 py-2 rounded"><strong>2</strong> <span><?= h(tr('لغات', 'Languages')) ?></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 pt-5 border-top border-light border-opacity-25 text-white-50 small">
|
||||
© <?= date('Y') ?> <?= h($projectName) ?>
|
||||
@ -145,7 +139,7 @@ $accounts = demo_users();
|
||||
<img src="<?= h(get_setting('company_logo')) ?>" alt="Logo" class="company-logo">
|
||||
<?php else: ?>
|
||||
<div class="company-logo mx-auto bg-primary bg-opacity-10 text-primary d-flex align-items-center justify-content-center" style="width: 90px; height: 90px; font-size: 2.5rem; font-weight: bold;">
|
||||
<?= h(mb_substr(current_lang() === 'ar' ? get_setting('company_name_ar', 'نظام') : get_setting('company_name_en', 'System'), 0, 1)) ?>
|
||||
<?= h((current_lang() === 'ar' ? 'ن' : 'S')) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<h3 class="fw-bold mb-1"><?= h(tr('تسجيل الدخول', 'Sign in to your account')) ?></h3>
|
||||
@ -169,14 +163,14 @@ $accounts = demo_users();
|
||||
<form method="post" class="mb-5">
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold" for="username"><?= h(tr('اسم المستخدم', 'Username')) ?></label>
|
||||
<input id="username" name="username" class="form-control form-control-lg rounded-3 bg-light border-0" autocomplete="username" required>
|
||||
<input id="username" name="username" class="form-control form-control-lg rounded-3 border border-secondary-subtle" autocomplete="username" placeholder="<?= h(tr('اسم المستخدم', 'Username')) ?>" value="<?= h(array_values($accounts)[0]['username'] ?? '') ?>" required>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<label class="form-label fw-semibold mb-0" for="password"><?= h(tr('كلمة المرور', 'Password')) ?></label>
|
||||
<a href="#" class="text-decoration-none small text-primary fw-medium" data-bs-toggle="modal" data-bs-target="#resetPasswordModal"><?= h(tr('نسيت كلمة المرور؟', 'Forgot password?')) ?></a>
|
||||
</div>
|
||||
<input id="password" name="password" type="password" class="form-control form-control-lg rounded-3 bg-light border-0" autocomplete="current-password" required>
|
||||
<input id="password" name="password" type="password" class="form-control form-control-lg rounded-3 border border-secondary-subtle" autocomplete="current-password" placeholder="********" value="<?= h(array_values($accounts)[0]['password'] ?? '') ?>" required>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-lg w-100 rounded-3 shadow-sm fw-semibold" type="submit"><?= h(tr('دخول', 'Sign in')) ?></button>
|
||||
</form>
|
||||
@ -221,7 +215,7 @@ $accounts = demo_users();
|
||||
<p class="text-muted mb-4"><?= h(tr('أدخل اسم المستخدم أو البريد الإلكتروني وسنقوم بإرسال رابط لإعادة تعيين كلمة المرور الخاصة بك.', 'Enter your username or email and we will send you a link to reset your password.')) ?></p>
|
||||
<div class="mb-3">
|
||||
<label for="reset_username" class="form-label fw-semibold"><?= h(tr('البريد الإلكتروني / اسم المستخدم', 'Email / Username')) ?></label>
|
||||
<input type="text" class="form-control form-control-lg bg-light border-0 rounded-3" id="reset_username" name="reset_username" required>
|
||||
<input type="text" class="form-control form-control-lg rounded-3 border border-secondary-subtle" id="reset_username" name="reset_username" placeholder="user@example.com" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer border-top-0 pt-0 pb-4 px-4">
|
||||
|
||||
2
new_purchase.php
Normal file
2
new_purchase.php
Normal file
@ -0,0 +1,2 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/purchase_form.php';
|
||||
33
patch_header.php
Normal file
33
patch_header.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
$content = file_get_contents('includes/header.php');
|
||||
|
||||
$search = <<<HTML
|
||||
<a class="list-group-item list-group-item-action <?= \$activeNav === 'pos' ? 'active' : '' ?>" href="<?= h(url_for('pos.php')) ?>">
|
||||
<i class="bi bi-cart-check"></i> <?= h(tr('نقاط البيع', 'POS Sale')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= \$activeNav === 'normal' ? 'active' : '' ?>" href="<?= h(url_for('normal_sale.php')) ?>">
|
||||
<i class="bi bi-receipt"></i> <?= h(tr('بيع عادي', 'Normal Sale')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= \$activeNav === 'sales' ? 'active' : '' ?>" href="<?= h(url_for('sales.php')) ?>">
|
||||
<i class="bi bi-journal-text"></i> <?= h(tr('المبيعات', 'Sales')) ?>
|
||||
</a>
|
||||
HTML;
|
||||
|
||||
$replace = <<<HTML
|
||||
<div class="px-3 pt-3 pb-2 text-white-50 text-uppercase small fw-bold">
|
||||
<?= h(tr('المبيعات', 'Sales')) ?>
|
||||
</div>
|
||||
<a class="list-group-item list-group-item-action <?= \$activeNav === 'sales' ? 'active' : '' ?>" href="<?= h(url_for('sales.php')) ?>">
|
||||
<i class="bi bi-journal-text"></i> <?= h(tr('قائمة الفواتير', 'Invoice list')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= \$activeNav === 'normal' ? 'active' : '' ?>" href="<?= h(url_for('normal_sale.php')) ?>">
|
||||
<i class="bi bi-plus-circle"></i> <?= h(tr('فاتورة جديدة', 'New invoice')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= \$activeNav === 'pos' ? 'active' : '' ?>" href="<?= h(url_for('pos.php')) ?>">
|
||||
<i class="bi bi-cart-check"></i> <?= h(tr('نقاط البيع', 'POS')) ?>
|
||||
</a>
|
||||
HTML;
|
||||
|
||||
$newContent = str_replace($search, $replace, $content);
|
||||
file_put_contents('includes/header.php', $newContent);
|
||||
echo "Done";
|
||||
@ -4,45 +4,53 @@ $user = require_roles(['owner', 'manager']);
|
||||
$pageTitle = tr('المشتريات', 'Purchases');
|
||||
$activeNav = 'purchases';
|
||||
|
||||
$allPurchases = purchase_pipeline();
|
||||
|
||||
// Search logic
|
||||
$search = $_GET['q'] ?? '';
|
||||
$filteredPurchases = [];
|
||||
if ($search) {
|
||||
$lowerSearch = strtolower($search);
|
||||
foreach ($allPurchases as $key => $row) {
|
||||
if (
|
||||
str_contains(strtolower((string)$row['supplier']), $lowerSearch) ||
|
||||
str_contains(strtolower((string)$row['reference']), $lowerSearch)
|
||||
) {
|
||||
$filteredPurchases[$key] = $row;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$filteredPurchases = $allPurchases;
|
||||
}
|
||||
|
||||
// Pagination logic
|
||||
$page = max(1, (int)($_GET['p'] ?? 1));
|
||||
$limit = 10;
|
||||
$total = count($filteredPurchases);
|
||||
$totalPages = max(1, ceil($total / $limit));
|
||||
$offset = ($page - 1) * $limit;
|
||||
$purchaseRows = array_slice($filteredPurchases, $offset, $limit, true);
|
||||
|
||||
$params = [];
|
||||
$where = ' WHERE 1=1 ';
|
||||
|
||||
if ($search) {
|
||||
$where .= ' AND (supplier_name LIKE :search OR reference_no LIKE :search) ';
|
||||
$params[':search'] = "%$search%";
|
||||
}
|
||||
|
||||
// Pagination counts
|
||||
$countSql = 'SELECT COUNT(*) FROM purchase_orders' . $where;
|
||||
$countStmt = db()->prepare($countSql);
|
||||
foreach ($params as $key => $value) {
|
||||
$countStmt->bindValue($key, $value);
|
||||
}
|
||||
$countStmt->execute();
|
||||
$total = $countStmt->fetchColumn();
|
||||
$totalPages = max(1, ceil($total / $limit));
|
||||
|
||||
// Fetch Data
|
||||
$sql = 'SELECT * FROM purchase_orders' . $where . ' ORDER BY purchase_date DESC LIMIT :limit OFFSET :offset';
|
||||
$stmt = db()->prepare($sql);
|
||||
foreach ($params as $key => $value) {
|
||||
$stmt->bindValue($key, $value);
|
||||
}
|
||||
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
|
||||
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
$purchaseRows = $stmt->fetchAll();
|
||||
|
||||
require __DIR__ . '/includes/header.php';
|
||||
?>
|
||||
<section class="surface-card mb-4">
|
||||
<div class="row g-4 align-items-center mb-3">
|
||||
<div class="col-lg-8">
|
||||
<h3 class="h5 mb-2"><i class="bi bi-bag-plus me-2"></i><?= h(tr('لوحة استلام الموردين', 'Supplier receiving board')) ?></h3>
|
||||
<p class="text-muted mb-0"><?= h(tr('هذه صفحة تمهيدية منظمة للمشتريات حتى تكون كل وحدة في صفحة مستقلة من البداية.', 'This is a structured starter page for purchases so every module already has a dedicated screen.')) ?></p>
|
||||
<h3 class="h5 mb-2"><i class="bi bi-bag-plus me-2"></i><?= h(tr('قائمة المشتريات', 'Purchase List')) ?></h3>
|
||||
<p class="text-muted mb-0"><?= h(tr('عرض المشتريات من الموردين وتحديث المخزون.', 'View supplier purchases and inventory updates.')) ?></p>
|
||||
</div>
|
||||
<div class="col-lg-4 text-lg-end">
|
||||
<button type="button" class="btn btn-primary" onclick="mockEdit()">
|
||||
<i class="bi bi-plus-lg"></i> <?= h(tr('إضافة أمر شراء', 'Add Purchase Order')) ?>
|
||||
</button>
|
||||
<a href="new_purchase.php" class="btn btn-primary">
|
||||
<i class="bi bi-plus-lg"></i> <?= h(tr('إضافة فاتورة مشتريات', 'Add Purchase Invoice')) ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -62,22 +70,30 @@ 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('الإجمالي', '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('الوصول المتوقع', 'ETA')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('التاريخ', 'Date')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('إجراءات', 'Actions')) ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="border-top-0">
|
||||
<?php if(empty($purchaseRows)): ?>
|
||||
<tr><td colspan="6" class="text-center text-muted py-4"><?= h(tr('لا توجد بيانات', 'No data found')) ?></td></tr>
|
||||
<tr><td colspan="7" class="text-center text-muted py-4"><?= h(tr('لا توجد بيانات', 'No data found')) ?></td></tr>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($purchaseRows as $row): ?>
|
||||
<tr>
|
||||
<td><?= h($row['supplier']) ?></td>
|
||||
<td><?= h($row['reference']) ?></td>
|
||||
<td><?= h(branch_label($row['branch'])) ?></td>
|
||||
<td><span class="badge text-bg-light border"><?= h($row['status']) ?></span></td>
|
||||
<td><?= h($row['eta']) ?></td>
|
||||
<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>
|
||||
<?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>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-success px-3 py-2 rounded-pill"><i class="bi bi-check-circle"></i> <?= h(tr('مكتمل', 'Completed')) ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= h(date('Y-m-d', strtotime((string)$row['purchase_date']))) ?></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary rounded-circle shadow-sm" style="width: 34px; height: 34px; padding: 0;" onclick="mockEdit()" title="<?= h(tr('تعديل', 'Edit')) ?>">
|
||||
<i class="bi bi-pencil"></i>
|
||||
@ -108,8 +124,8 @@ require __DIR__ . '/includes/header.php';
|
||||
<script>
|
||||
function mockEdit() {
|
||||
Swal.fire({
|
||||
title: '<?= h(tr('تعديل (تجريبي)', 'Edit (Demo)')) ?>',
|
||||
text: '<?= h(tr('هذه الميزة غير مفعلة للبيانات التجريبية.', 'This feature is mock data and not active yet.')) ?>',
|
||||
title: '<?= h(tr('تعديل (غير متاح)', 'Edit (Disabled)')) ?>',
|
||||
text: '<?= h(tr('تعديل الفواتير غير متاح حالياً.', 'Editing invoices is currently disabled.')) ?>',
|
||||
icon: 'info',
|
||||
confirmButtonText: '<?= h(tr('حسناً', 'OK')) ?>'
|
||||
});
|
||||
@ -118,7 +134,7 @@ function mockEdit() {
|
||||
function mockDelete() {
|
||||
Swal.fire({
|
||||
title: '<?= h(tr('هل أنت متأكد؟', 'Are you sure?')) ?>',
|
||||
text: '<?= h(tr('لن تتمكن من التراجع عن هذا!', "You won't be able to revert this!")) ?>',
|
||||
text: '<?= h(tr('هل تريد حذف هذه الفاتورة؟ (هذه الميزة غير متاحة حالياً)', "Do you want to delete this invoice? (Currently disabled)")) ?>',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#dc3545',
|
||||
@ -128,13 +144,13 @@ function mockDelete() {
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
Swal.fire(
|
||||
'<?= h(tr('محذوف!', 'Deleted!')) ?>',
|
||||
'<?= h(tr('حساب تجريبي لا يمكن حذفه.', 'Demo account cannot be deleted.')) ?>',
|
||||
'success'
|
||||
'<?= h(tr('مرفوض!', 'Denied!')) ?>',
|
||||
'<?= h(tr('حذف الفاتورة غير مدعوم في هذه النسخة.', 'Deleting invoice is not supported in this version.')) ?>',
|
||||
'error'
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php require __DIR__ . '/includes/footer.php'; ?>
|
||||
<?php require __DIR__ . '/includes/footer.php'; ?>
|
||||
|
||||
2
sale.php
2
sale.php
@ -19,7 +19,7 @@ $companyName = current_lang() === 'ar' ? get_setting('company_name_ar', 'حلو
|
||||
$companyAddress = get_setting('company_address', '');
|
||||
$companyVat = get_setting('company_vat_number', '300123456789012');
|
||||
$companyEmail = 'info@flatlogic.com';
|
||||
$companyPhone = '<?= h(get_setting('company_phone', '')) ?>';
|
||||
$companyPhone = get_setting('company_phone', '');
|
||||
|
||||
require __DIR__ . '/includes/header.php';
|
||||
?>
|
||||
|
||||
127
stock.php
127
stock.php
@ -15,6 +15,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
$sku = $_POST['sku'] ?? '';
|
||||
$name = $_POST['name'] ?? '';
|
||||
$price = (float)($_POST['price'] ?? 0);
|
||||
$cost_price = (float)($_POST['cost_price'] ?? 0);
|
||||
$base_stock = (int)($_POST['base_stock'] ?? 0);
|
||||
$vat = (float)($_POST['vat'] ?? get_setting('vat_percentage', 5));
|
||||
$category_id = !empty($_POST['category_id']) ? (int)$_POST['category_id'] : null;
|
||||
@ -50,8 +51,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql = "UPDATE items SET sku=?, name=?, price=?, base_stock=?, vat=?, category_id=?, supplier_id=?, unit_id=? " . ($image_url ? ", image_url=?" : "") . " WHERE sku=?";
|
||||
$params = [$sku, $name, $price, $base_stock, $vat, $category_id, $supplier_id, $unit_id];
|
||||
$sql = "UPDATE items SET sku=?, name=?, price=?, cost_price=?, base_stock=?, vat=?, category_id=?, supplier_id=?, unit_id=? " . ($image_url ? ", image_url=?" : "") . " WHERE sku=?";
|
||||
$params = [$sku, $name, $price, $cost_price, $base_stock, $vat, $category_id, $supplier_id, $unit_id];
|
||||
if ($image_url) {
|
||||
$params[] = $image_url;
|
||||
}
|
||||
@ -63,8 +64,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
echo json_encode(['success' => false, 'error' => 'SKU already exists']);
|
||||
exit;
|
||||
}
|
||||
$stmt = $pdo->prepare("INSERT INTO items (sku, name, price, base_stock, vat, category_id, supplier_id, unit_id, image_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$sku, $name, $price, $base_stock, $vat, $category_id, $supplier_id, $unit_id, $image_url]);
|
||||
$stmt = $pdo->prepare("INSERT INTO items (sku, name, price, cost_price, base_stock, vat, category_id, supplier_id, unit_id, image_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$sku, $name, $price, $cost_price, $base_stock, $vat, $category_id, $supplier_id, $unit_id, $image_url]);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
@ -201,6 +202,7 @@ require __DIR__ . '/includes/header.php';
|
||||
<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"><?= 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>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('مباع', 'Sold')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('متاح', 'Available')) ?></th>
|
||||
@ -210,7 +212,7 @@ require __DIR__ . '/includes/header.php';
|
||||
</thead>
|
||||
<tbody class="border-top-0">
|
||||
<?php if(empty($stockRows)): ?>
|
||||
<tr><td colspan="9" class="text-center text-muted py-5"><i class="bi bi-inbox fs-1 d-block text-black-50 mb-2"></i> <?= h(tr('لا توجد بيانات', 'No data found')) ?></td></tr>
|
||||
<tr><td colspan="10" class="text-center text-muted py-5"><i class="bi bi-inbox fs-1 d-block text-black-50 mb-2"></i> <?= h(tr('لا توجد بيانات', 'No data found')) ?></td></tr>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($stockRows as $row): ?>
|
||||
<tr style="transition: all 0.2s ease;">
|
||||
@ -226,6 +228,7 @@ require __DIR__ . '/includes/header.php';
|
||||
</td>
|
||||
<td class="fw-bold text-start text-dark"><?= 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>
|
||||
<td class="text-secondary"><?= h((string) $row['sold']) ?></td>
|
||||
<td class="fw-bold text-primary fs-6"><?= h((string) $row['available']) ?></td>
|
||||
@ -237,7 +240,7 @@ require __DIR__ . '/includes/header.php';
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-end pe-3">
|
||||
<button class="btn btn-sm btn-outline-primary rounded-circle shadow-sm" style="width: 34px; height: 34px; padding: 0;" onclick="openItemModal('<?= h($row['sku']) ?>', '<?= h(addslashes($row['name'])) ?>', '<?= h($row['price']) ?>', '<?= 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'] ?? '') ?>')" data-bs-toggle="tooltip" title="<?= h(tr('تعديل', 'Edit')) ?>">
|
||||
<button class="btn btn-sm btn-outline-primary rounded-circle shadow-sm" style="width: 34px; height: 34px; padding: 0;" 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'] ?? '') ?>')" data-bs-toggle="tooltip" title="<?= h(tr('تعديل', 'Edit')) ?>">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger rounded-circle shadow-sm ms-1" style="width: 34px; height: 34px; padding: 0;" onclick="deleteItem('<?= h(addslashes($row['sku'])) ?>')" data-bs-toggle="tooltip" title="<?= h(tr('حذف', 'Delete')) ?>">
|
||||
@ -276,63 +279,77 @@ require __DIR__ . '/includes/header.php';
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="item_original_sku">
|
||||
<input type="hidden" id="item_existing_image_url">
|
||||
<div class="mb-3 text-center">
|
||||
<label class="form-label d-block text-start"><?= h(tr('صورة الصنف', 'Item Picture')) ?></label>
|
||||
<input type="file" class="form-control" id="item_picture" accept="image/*">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('رمز الصنف (SKU)', 'SKU')) ?></label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="item_sku" required maxlength="15">
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="suggestSKU()" title="<?= h(tr('اقتراح رمز', 'Suggest SKU')) ?>">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3 text-center">
|
||||
<label class="form-label d-block text-start"><?= h(tr('صورة الصنف', 'Item Picture')) ?></label>
|
||||
<input type="file" class="form-control" id="item_picture" accept="image/*">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('اسم الصنف', 'Product Name')) ?></label>
|
||||
<input type="text" class="form-control" id="item_name" required>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-4">
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('رمز الصنف (SKU)', 'SKU')) ?></label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="item_sku" required maxlength="15">
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="suggestSKU()" title="<?= h(tr('اقتراح رمز', 'Suggest SKU')) ?>">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('اسم الصنف', 'Product Name')) ?></label>
|
||||
<input type="text" class="form-control" id="item_name" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('السعر', 'Price')) ?></label>
|
||||
<input type="number" step="0.001" class="form-control" id="item_price" required>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('التكلفة', 'Cost Price')) ?></label>
|
||||
<input type="number" step="0.001" class="form-control" id="item_cost_price" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('الرصيد الافتتاحي', 'Opening Stock')) ?></label>
|
||||
<input type="number" class="form-control" id="item_base_stock" required>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('الضريبة (VAT %)', 'VAT %')) ?></label>
|
||||
<input type="number" step="0.001" class="form-control" id="item_vat" value="<?= h(get_setting('vat_percentage', 5)) ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('الوحدة', 'Unit')) ?></label>
|
||||
<select class="form-select" id="item_unit">
|
||||
<option value=""><?= h(tr('-- اختر الوحدة --', '-- Select Unit --')) ?></option>
|
||||
<?php foreach($units as $unit): ?>
|
||||
<option value="<?= h($unit['id']) ?>"><?= h(current_lang() === 'ar' ? $unit['name_ar'] : $unit['name_en']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('التصنيف', 'Category')) ?></label>
|
||||
<select class="form-select" id="item_category">
|
||||
<option value=""><?= h(tr('-- اختر التصنيف --', '-- Select Category --')) ?></option>
|
||||
<?php foreach($categories as $cat): ?>
|
||||
<option value="<?= h($cat['id']) ?>"><?= h(current_lang() === 'ar' ? $cat['name_ar'] : $cat['name_en']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('المورد', 'Supplier')) ?></label>
|
||||
<select class="form-select" id="item_supplier">
|
||||
<option value=""><?= h(tr('-- اختر المورد --', '-- Select Supplier --')) ?></option>
|
||||
<?php foreach($suppliers as $sup): ?>
|
||||
<option value="<?= h($sup['id']) ?>"><?= h($sup['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('التصنيف', 'Category')) ?></label>
|
||||
<select class="form-select" id="item_category">
|
||||
<option value=""><?= h(tr('-- اختر التصنيف --', '-- Select Category --')) ?></option>
|
||||
<?php foreach($categories as $cat): ?>
|
||||
<option value="<?= h($cat['id']) ?>"><?= h(current_lang() === 'ar' ? $cat['name_ar'] : $cat['name_en']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('المورد', 'Supplier')) ?></label>
|
||||
<select class="form-select" id="item_supplier">
|
||||
<option value=""><?= h(tr('-- اختر المورد --', '-- Select Supplier --')) ?></option>
|
||||
<?php foreach($suppliers as $sup): ?>
|
||||
<option value="<?= h($sup['id']) ?>"><?= h($sup['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 mb-3">
|
||||
<label class="form-label"><?= h(tr('الوحدة', 'Unit')) ?></label>
|
||||
<select class="form-select" id="item_unit">
|
||||
<option value=""><?= h(tr('-- اختر الوحدة --', '-- Select Unit --')) ?></option>
|
||||
<?php foreach($units as $unit): ?>
|
||||
<option value="<?= h($unit['id']) ?>"><?= h(current_lang() === 'ar' ? $unit['name_ar'] : $unit['name_en']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@ -404,12 +421,13 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
});
|
||||
|
||||
function openItemModal(sku = '', name = '', 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 = '') {
|
||||
document.getElementById('item_original_sku').value = sku;
|
||||
document.getElementById('item_existing_image_url').value = image_url;
|
||||
document.getElementById('item_sku').value = sku;
|
||||
document.getElementById('item_name').value = name;
|
||||
document.getElementById('item_price').value = price;
|
||||
document.getElementById('item_cost_price').value = cost_price;
|
||||
document.getElementById('item_base_stock').value = base_stock;
|
||||
document.getElementById('item_vat').value = vat;
|
||||
document.getElementById('item_category').value = category_id;
|
||||
@ -446,6 +464,7 @@ async function handleItemSubmit(e) {
|
||||
formData.append('sku', document.getElementById('item_sku').value);
|
||||
formData.append('name', document.getElementById('item_name').value);
|
||||
formData.append('price', document.getElementById('item_price').value);
|
||||
formData.append('cost_price', document.getElementById('item_cost_price').value);
|
||||
formData.append('base_stock', document.getElementById('item_base_stock').value);
|
||||
formData.append('vat', document.getElementById('item_vat').value);
|
||||
formData.append('category_id', document.getElementById('item_category').value);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user