172 lines
9.5 KiB
PHP
172 lines
9.5 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/app.php';
|
|
require_permission('purchases');
|
|
|
|
$errors = [];
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
verify_csrf();
|
|
|
|
$supplierId = (int)($_POST['supplier_id'] ?? 0);
|
|
$productId = (int)($_POST['product_id'] ?? 0);
|
|
$qty = (float)($_POST['qty'] ?? 0);
|
|
$unitCost = (float)($_POST['unit_cost'] ?? 0);
|
|
$status = (string)($_POST['status'] ?? 'received');
|
|
$notes = trim((string)($_POST['notes'] ?? ''));
|
|
|
|
$supplier = fetch_record('supplier', $supplierId);
|
|
$product = fetch_record('product', $productId);
|
|
|
|
if (!$supplier) {
|
|
$errors[] = 'اختر موردًا صحيحًا.';
|
|
}
|
|
if (!$product) {
|
|
$errors[] = 'اختر صنفًا صحيحًا.';
|
|
}
|
|
if ($qty <= 0 || $unitCost < 0) {
|
|
$errors[] = 'أدخل كمية وسعر شراء صالحين.';
|
|
}
|
|
if (!in_array($status, ['draft', 'received'], true)) {
|
|
$errors[] = 'حالة أمر الشراء غير صحيحة.';
|
|
}
|
|
|
|
if (!$errors && $supplier && $product) {
|
|
$productPayload = $product['payload_data'];
|
|
$sku = (string)($productPayload['sku'] ?? $product['code']);
|
|
$subtotal = $qty * $unitCost;
|
|
$vat = $subtotal * 0.15;
|
|
$grand = $subtotal + $vat;
|
|
|
|
db()->beginTransaction();
|
|
try {
|
|
$purchaseId = create_record('purchase_order', 'أمر شراء ' . $supplier['title'], next_code('PO', 'purchase_order'), [
|
|
'supplier_id' => (int)$supplier['id'],
|
|
'supplier_name' => $supplier['title'],
|
|
'product_id' => (int)$product['id'],
|
|
'product_name' => $product['title'],
|
|
'sku' => $sku,
|
|
'unit' => $productPayload['unit'] ?? 'وحدة',
|
|
'qty' => $qty,
|
|
'unit_cost' => $unitCost,
|
|
'subtotal' => $subtotal,
|
|
'vat' => $vat,
|
|
'grand_total' => $grand,
|
|
'notes' => $notes,
|
|
'created_date' => date('Y-m-d H:i'),
|
|
'received_at' => $status === 'received' ? date('Y-m-d H:i') : null,
|
|
'created_by' => current_user()['username'] ?? 'system',
|
|
], $status);
|
|
|
|
$purchase = fetch_record('purchase_order', $purchaseId);
|
|
if ($status === 'received' && $purchase) {
|
|
$stock = adjust_product_stock($product, $qty, 'purchase_receive', $purchase['code'], 'purchase_order', $purchaseId, 'استلام شراء من المورد ' . $supplier['title']);
|
|
$payload = $purchase['payload_data'];
|
|
$payload['stock_after'] = $stock['after'];
|
|
update_record_payload($purchaseId, $payload, 'received');
|
|
}
|
|
|
|
db()->commit();
|
|
set_flash('success', $status === 'received' ? 'تم إنشاء أمر الشراء واستلامه وتحديث المخزون.' : 'تم حفظ أمر الشراء كمسودة.');
|
|
redirect('purchases.php?id=' . $purchaseId);
|
|
} catch (Throwable $e) {
|
|
db()->rollBack();
|
|
$errors[] = 'تعذر حفظ أمر الشراء، حاول مرة أخرى.';
|
|
}
|
|
}
|
|
}
|
|
|
|
$suppliers = supplier_dataset();
|
|
$products = product_dataset();
|
|
$orders = fetch_records('purchase_order');
|
|
$detail = isset($_GET['id']) ? fetch_record('purchase_order', (int)$_GET['id']) : null;
|
|
render_header('أوامر الشراء', 'إنشاء أوامر شراء وربطها بالموردين وتحديث المخزون عند الاستلام.', 'purchases');
|
|
?>
|
|
<div class="row g-4">
|
|
<div class="col-xl-5">
|
|
<div class="panel-card">
|
|
<div class="section-header compact"><div><h1 class="section-title mb-1">إنشاء أمر شراء</h1><p class="section-copy">اختر المورد والصنف ثم قرر هل هو استلام مباشر أم مسودة معلقة.</p></div></div>
|
|
<?php if ($errors): ?>
|
|
<div class="alert alert-danger py-2"><?php foreach ($errors as $error): ?><div><?= e($error) ?></div><?php endforeach; ?></div>
|
|
<?php endif; ?>
|
|
<form method="post" class="vstack gap-3">
|
|
<input type="hidden" name="csrf_token" value="<?= e(csrf_token()) ?>">
|
|
<div>
|
|
<label class="form-label">المورد</label>
|
|
<select class="form-select" name="supplier_id" required>
|
|
<option value="">اختر المورد</option>
|
|
<?php foreach ($suppliers as $supplier): ?>
|
|
<option value="<?= (int)$supplier['id'] ?>"><?= e($supplier['name']) ?> — <?= e($supplier['code']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="form-label">الصنف</label>
|
|
<select class="form-select" name="product_id" required>
|
|
<option value="">اختر الصنف</option>
|
|
<?php foreach ($products as $product): ?>
|
|
<option value="<?= (int)$product['id'] ?>"><?= e($product['name']) ?> — <?= e($product['sku']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="row g-3">
|
|
<div class="col-md-4"><label class="form-label">الكمية</label><input type="number" step="0.01" min="0.01" class="form-control" name="qty" value="1" required></div>
|
|
<div class="col-md-4"><label class="form-label">سعر الشراء</label><input type="number" step="0.01" min="0" class="form-control" name="unit_cost" value="0" required></div>
|
|
<div class="col-md-4"><label class="form-label">الحالة</label><select class="form-select" name="status"><option value="received">استلام الآن</option><option value="draft">مسودة</option></select></div>
|
|
</div>
|
|
<div>
|
|
<label class="form-label">ملاحظات</label>
|
|
<textarea class="form-control" name="notes" rows="3"></textarea>
|
|
</div>
|
|
<button class="btn btn-dark" type="submit">حفظ أمر الشراء</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-7">
|
|
<div class="panel-card mb-4">
|
|
<div class="section-header"><div><h2 class="section-title">سجل أوامر الشراء</h2><p class="section-copy">الأوامر المستلمة ترفع المخزون تلقائيًا، والمسودات تبقى دون تأثير.</p></div></div>
|
|
<?php if ($orders): ?>
|
|
<div class="table-responsive">
|
|
<table class="table align-middle app-table">
|
|
<thead><tr><th>الرقم</th><th>المورد</th><th>الصنف</th><th>الإجمالي</th><th>الحالة</th></tr></thead>
|
|
<tbody>
|
|
<?php foreach ($orders as $order): $payload = $order['payload_data']; ?>
|
|
<tr>
|
|
<td><a class="table-link" href="purchases.php?id=<?= (int)$order['id'] ?>"><?= e($order['code']) ?></a></td>
|
|
<td><?= e($payload['supplier_name'] ?? '') ?></td>
|
|
<td><?= e($payload['product_name'] ?? '') ?><div class="small text-secondary"><?= e((string)($payload['qty'] ?? 0)) ?> <?= e($payload['unit'] ?? '') ?></div></td>
|
|
<td><?= e(format_money((float)($payload['grand_total'] ?? 0))) ?></td>
|
|
<td><span class="badge <?= e(status_badge_class((string)$order['status'])) ?>"><?= e(order_status_label((string)$order['status'])) ?></span></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="empty-inline">لا توجد أوامر شراء بعد.</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<div class="panel-card">
|
|
<div class="section-header compact"><div><h2 class="section-title">تفاصيل أمر الشراء</h2></div></div>
|
|
<?php if ($detail): $payload = $detail['payload_data']; ?>
|
|
<div class="detail-grid mb-3">
|
|
<div class="detail-block"><span class="detail-label">رقم الأمر</span><strong><?= e($detail['code']) ?></strong></div>
|
|
<div class="detail-block"><span class="detail-label">الحالة</span><strong><?= e(order_status_label((string)$detail['status'])) ?></strong></div>
|
|
<div class="detail-block"><span class="detail-label">المورد</span><strong><?= e($payload['supplier_name'] ?? '') ?></strong></div>
|
|
<div class="detail-block"><span class="detail-label">تاريخ الإنشاء</span><strong><?= e($payload['created_date'] ?? '') ?></strong></div>
|
|
</div>
|
|
<div class="subtle-card">
|
|
<div class="summary-row"><span>الصنف</span><strong><?= e($payload['product_name'] ?? '') ?> — <?= e($payload['sku'] ?? '') ?></strong></div>
|
|
<div class="summary-row"><span>الكمية</span><strong><?= e((string)($payload['qty'] ?? 0)) ?> <?= e($payload['unit'] ?? '') ?></strong></div>
|
|
<div class="summary-row"><span>سعر الشراء</span><strong><?= e(format_money((float)($payload['unit_cost'] ?? 0))) ?></strong></div>
|
|
<div class="summary-row"><span>الإجمالي</span><strong><?= e(format_money((float)($payload['grand_total'] ?? 0))) ?></strong></div>
|
|
<div class="summary-row"><span>المخزون بعد الاستلام</span><strong><?= e((string)($payload['stock_after'] ?? '—')) ?></strong></div>
|
|
</div>
|
|
<?php if (!empty($payload['notes'])): ?><div class="mt-3 text-secondary small"><?= e($payload['notes']) ?></div><?php endif; ?>
|
|
<?php else: ?>
|
|
<div class="empty-inline">اختر أمر شراء من الجدول لعرض تفاصيله.</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php render_footer(); ?>
|