181 lines
9.6 KiB
PHP
181 lines
9.6 KiB
PHP
<?php
|
|
require_once __DIR__ . '/app.php';
|
|
$user = require_roles(['owner', 'manager', 'cashier']);
|
|
$pageTitle = $saleMode === 'normal' ? tr('بيع عادي', 'Normal Sale') : tr('نقاط البيع', 'POS Sale');
|
|
$activeNav = $saleMode === 'normal' ? 'normal' : 'pos';
|
|
$error = '';
|
|
$catalog = catalog();
|
|
$allowedBranches = $user['role'] === 'owner' ? array_keys(branches()) : [$user['branch_code']];
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$branchCode = trim((string) ($_POST['branch_code'] ?? ''));
|
|
$customerName = trim((string) ($_POST['customer_name'] ?? ''));
|
|
$paymentMethod = trim((string) ($_POST['payment_method'] ?? 'cash'));
|
|
$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 cart.');
|
|
} 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 = (float) $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 cart is invalid after product validation.');
|
|
} else {
|
|
$cashierName = current_lang() === 'ar' ? $user['name_ar'] : $user['name_en'];
|
|
$saleId = create_sale([
|
|
'receipt_no' => receipt_code(),
|
|
'sale_mode' => $saleMode,
|
|
'branch_code' => $branchCode,
|
|
'cashier_username' => $user['username'],
|
|
'cashier_name' => $cashierName,
|
|
'role_name' => $user['role'],
|
|
'customer_name' => $customerName !== '' ? $customerName : null,
|
|
'payment_method' => $paymentMethod,
|
|
'items' => $normalized,
|
|
'item_count' => $itemCount,
|
|
'subtotal' => $subtotal,
|
|
'total_amount' => $subtotal,
|
|
'notes' => $notes !== '' ? $notes : null,
|
|
]);
|
|
|
|
set_flash('success', $saleMode === 'normal'
|
|
? tr('تم حفظ البيع العادي بنجاح.', 'Normal sale saved successfully.')
|
|
: tr('تم حفظ عملية POS بنجاح.', 'POS sale saved successfully.'));
|
|
redirect_to('sale.php', ['id' => $saleId]);
|
|
}
|
|
}
|
|
}
|
|
|
|
require __DIR__ . '/header.php';
|
|
?>
|
|
<section class="row g-4">
|
|
<div class="col-xl-7">
|
|
<div class="surface-card h-100">
|
|
<div class="d-flex justify-content-between align-items-center mb-3 gap-3 flex-wrap">
|
|
<div>
|
|
<h3 class="h5 mb-1"><?= h($pageTitle) ?></h3>
|
|
<div class="small text-muted"><?= h(tr('أضف المنتجات إلى السلة ثم احفظ العملية.', 'Add products to the cart and save the transaction.')) ?></div>
|
|
</div>
|
|
<span class="badge text-bg-light border px-3 py-2"><?= h(sale_mode_label($saleMode)) ?></span>
|
|
</div>
|
|
<?php if ($error !== ''): ?>
|
|
<div class="alert alert-warning"><?= h($error) ?></div>
|
|
<?php endif; ?>
|
|
<form method="post" class="d-grid gap-4" id="sale-form" data-sale-form>
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="branch_code"><?= h(tr('الفرع', 'Branch')) ?></label>
|
|
<select class="form-select" id="branch_code" name="branch_code" <?= count($allowedBranches) === 1 ? 'aria-readonly="true"' : '' ?>>
|
|
<?php foreach ($allowedBranches as $branchCode): ?>
|
|
<option value="<?= h($branchCode) ?>"><?= h(branch_label($branchCode)) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="payment_method"><?= h(tr('طريقة الدفع', 'Payment method')) ?></label>
|
|
<select class="form-select" id="payment_method" name="payment_method">
|
|
<option value="cash"><?= h(tr('نقداً', 'Cash')) ?></option>
|
|
<option value="card"><?= h(tr('بطاقة', 'Card')) ?></option>
|
|
<option value="transfer"><?= h(tr('تحويل', 'Transfer')) ?></option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="customer_name"><?= h(tr('اسم العميل', 'Customer name')) ?></label>
|
|
<input class="form-control" id="customer_name" name="customer_name" maxlength="120" placeholder="<?= h(tr('اختياري', 'Optional')) ?>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="notes"><?= h(tr('ملاحظات', 'Notes')) ?></label>
|
|
<input class="form-control" id="notes" name="notes" maxlength="500" placeholder="<?= h(tr('طلب خاص أو ملاحظة داخلية', 'Special request or internal note')) ?>">
|
|
</div>
|
|
</div>
|
|
<input type="hidden" name="cart_json" id="cart_json" value="[]">
|
|
<div>
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h4 class="h6 mb-0"><?= h(tr('الأصناف السريعة', 'Quick products')) ?></h4>
|
|
<span class="small text-muted"><?= h(tr('انقر لإضافة المنتج', 'Tap to add product')) ?></span>
|
|
</div>
|
|
<div class="row g-3">
|
|
<?php foreach ($catalog as $product): ?>
|
|
<div class="col-sm-6 col-lg-4">
|
|
<button class="product-tile w-100 text-start" type="button" data-add-product data-sku="<?= h($product['sku']) ?>" data-name="<?= h(current_lang() === 'ar' ? $product['name_ar'] : $product['name_en']) ?>" data-price="<?= h((string) $product['price']) ?>">
|
|
<span class="product-pill"><?= h(current_lang() === 'ar' ? $product['unit_ar'] : $product['unit_en']) ?></span>
|
|
<div class="fw-semibold mb-1"><?= h(current_lang() === 'ar' ? $product['name_ar'] : $product['name_en']) ?></div>
|
|
<div class="small text-muted mb-2">SKU: <?= h($product['sku']) ?></div>
|
|
<div class="fw-semibold"><?= h(currency((float) $product['price'])) ?></div>
|
|
</button>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-5">
|
|
<div class="surface-card h-100 cart-panel">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h3 class="h5 mb-0"><?= h(tr('السلة', 'Cart')) ?></h3>
|
|
<span class="small text-muted" id="cart-count-label">0 <?= h(tr('قطعة', 'items')) ?></span>
|
|
</div>
|
|
<div class="empty-state compact mb-3" id="cart-empty-state">
|
|
<h4><?= h(tr('السلة فارغة', 'Cart is empty')) ?></h4>
|
|
<p class="mb-0"><?= h(tr('اختر المنتجات من القائمة اليسرى لبدء أول فاتورة.', 'Select products from the left to start the first receipt.')) ?></p>
|
|
</div>
|
|
<div id="cart-lines" class="d-grid gap-2"></div>
|
|
<div class="cart-summary mt-3 pt-3 border-top">
|
|
<div class="d-flex justify-content-between small text-muted mb-2"><span><?= h(tr('المجموع الفرعي', 'Subtotal')) ?></span><span id="cart-subtotal">0.00</span></div>
|
|
<div class="d-flex justify-content-between fw-semibold"><span><?= h(tr('الإجمالي', 'Total')) ?></span><span id="cart-total">0.00</span></div>
|
|
</div>
|
|
<div class="d-grid gap-2 mt-3">
|
|
<button class="btn btn-dark" type="submit" form="sale-form"><?= h(tr('حفظ الفاتورة', 'Save invoice')) ?></button>
|
|
<button class="btn btn-outline-secondary" type="button" data-clear-cart><?= h(tr('تفريغ السلة', 'Clear cart')) ?></button>
|
|
</div>
|
|
<div class="alert alert-light border mt-3 mb-0 small">
|
|
<?= h(tr('بعد الحفظ ستنتقل مباشرة إلى صفحة التفاصيل ويمكنك الطباعة من هناك.', 'After saving, you will go straight to the detail page and can print from there.')) ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<script>
|
|
window.saleCatalog = <?= json_encode(array_values(array_map(static function (array $item): array {
|
|
return [
|
|
'sku' => $item['sku'],
|
|
'name' => current_lang() === 'ar' ? $item['name_ar'] : $item['name_en'],
|
|
'price' => (float) $item['price'],
|
|
];
|
|
}, $catalog)), JSON_UNESCAPED_UNICODE) ?>;
|
|
window.saleLabels = <?= json_encode([
|
|
'currency' => tr('ر.ع', 'OMR'),
|
|
'empty' => tr('السلة فارغة', 'Cart is empty'),
|
|
'remove' => tr('إزالة', 'Remove'),
|
|
], JSON_UNESCAPED_UNICODE) ?>;
|
|
</script>
|
|
<?php require __DIR__ . '/footer.php'; ?>
|