39528-vm/manufacturing.php
2026-04-09 09:46:40 +00:00

242 lines
16 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/app.php';
require_permission('manufacturing');
$errors = [];
$qualityOptions = manufacturing_quality_options();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
verify_csrf();
$rawProductId = (int)($_POST['raw_product_id'] ?? 0);
$finishedProductId = (int)($_POST['finished_product_id'] ?? 0);
$finishedQty = (float)($_POST['finished_qty'] ?? 0);
$actualRawQty = (float)($_POST['actual_raw_qty'] ?? 0);
$qualityStatus = (string)($_POST['quality_status'] ?? 'accepted');
$status = (string)($_POST['status'] ?? 'completed');
$notes = trim((string)($_POST['notes'] ?? ''));
$rawProduct = fetch_record('product', $rawProductId);
$finishedProduct = fetch_record('product', $finishedProductId);
if (!$rawProduct || (($rawProduct['payload_data']['category'] ?? '') !== 'مواد خام')) {
$errors[] = 'اختر مادة خام صحيحة لعملية التصنيع.';
}
if (!$finishedProduct || (($finishedProduct['payload_data']['category'] ?? '') === 'مواد خام')) {
$errors[] = 'اختر منتجًا نهائيًا صحيحًا.';
}
if ($rawProductId > 0 && $rawProductId === $finishedProductId) {
$errors[] = 'لا يمكن أن تكون المادة الخام والمنتج النهائي نفس الصنف.';
}
if ($finishedQty <= 0 || $actualRawQty <= 0) {
$errors[] = 'أدخل كميات صحيحة للإنتاج والاستهلاك الفعلي.';
}
if (!array_key_exists($qualityStatus, $qualityOptions)) {
$errors[] = 'حالة الجودة غير صحيحة.';
}
if (!in_array($status, ['draft', 'completed'], true)) {
$errors[] = 'حالة أمر التصنيع غير صحيحة.';
}
if (!$errors && $rawProduct && $finishedProduct) {
$rawPayload = $rawProduct['payload_data'];
$finishedPayload = $finishedProduct['payload_data'];
$producedQty = $qualityStatus === 'rejected' ? 0.0 : $finishedQty;
db()->beginTransaction();
try {
$manufacturingId = create_record('manufacturing_order', 'أمر تصنيع ' . $finishedProduct['title'], next_code('MO', 'manufacturing_order'), [
'raw_product_id' => (int)$rawProduct['id'],
'raw_product_name' => $rawProduct['title'],
'raw_sku' => $rawPayload['sku'] ?? $rawProduct['code'],
'raw_unit' => $rawPayload['unit'] ?? 'وحدة',
'finished_product_id' => (int)$finishedProduct['id'],
'finished_product_name' => $finishedProduct['title'],
'finished_sku' => $finishedPayload['sku'] ?? $finishedProduct['code'],
'finished_unit' => $finishedPayload['unit'] ?? 'وحدة',
'finished_qty' => $finishedQty,
'actual_raw_qty' => $actualRawQty,
'produced_qty' => $producedQty,
'quality_status' => $qualityStatus,
'quality_label' => manufacturing_quality_label($qualityStatus),
'conversion_ratio' => $actualRawQty > 0 ? round($finishedQty / $actualRawQty, 4) : 0,
'notes' => $notes,
'created_date' => date('Y-m-d H:i'),
'completed_at' => $status === 'completed' ? date('Y-m-d H:i') : null,
'created_by' => current_user()['username'] ?? 'system',
], $status);
$order = fetch_record('manufacturing_order', $manufacturingId);
if ($status === 'completed' && $order) {
$rawStock = adjust_product_stock($rawProduct, -$actualRawQty, 'manufacturing_consume', $order['code'], 'manufacturing_order', $manufacturingId, 'استهلاك خامات لأمر التصنيع ' . $order['code']);
$payload = $order['payload_data'];
$payload['raw_stock_after'] = $rawStock['after'];
if ($producedQty > 0) {
$finishedStock = adjust_product_stock($finishedProduct, $producedQty, 'manufacturing_output', $order['code'], 'manufacturing_order', $manufacturingId, 'إضافة إنتاج نهائي لأمر التصنيع ' . $order['code']);
$payload['finished_stock_after'] = $finishedStock['after'];
} else {
$currentFinishedStock = (float)($finishedPayload['stock_qty'] ?? 0);
$payload['finished_stock_after'] = $currentFinishedStock;
}
update_record_payload($manufacturingId, $payload, 'completed');
}
db()->commit();
set_flash('success', $status === 'completed' ? 'تم إكمال أمر التصنيع وتحديث المخزون تلقائيًا.' : 'تم حفظ أمر التصنيع كمسودة.');
redirect('manufacturing.php?id=' . $manufacturingId);
} catch (Throwable $e) {
db()->rollBack();
$errors[] = 'تعذر حفظ أمر التصنيع، تأكد من توفر المخزون الخام ثم حاول مرة أخرى.';
}
}
}
$rawMaterials = raw_material_dataset();
$finishedProducts = finished_product_dataset();
$orders = fetch_records('manufacturing_order');
$detail = isset($_GET['id']) ? fetch_record('manufacturing_order', (int)$_GET['id']) : null;
$todayCount = today_record_count('manufacturing_order');
$completedCount = 0;
$draftCount = 0;
$totalProducedQty = 0.0;
foreach ($orders as $order) {
$payload = $order['payload_data'];
if (($order['status'] ?? '') === 'completed') {
$completedCount++;
}
if (($order['status'] ?? '') === 'draft') {
$draftCount++;
}
$totalProducedQty += (float)($payload['produced_qty'] ?? 0);
}
render_header('التصنيع', 'تسجيل أوامر تصنيع تخصم الخامات وتضيف المنتجات النهائية تلقائيًا مع متابعة الجودة.', 'manufacturing');
?>
<div class="row g-3 mb-4">
<div class="col-6 col-xl-3"><div class="stat-card"><div class="stat-label">أوامر التصنيع</div><div class="stat-value"><?= e((string)count($orders)) ?></div><div class="stat-note">إجمالي السجل</div></div></div>
<div class="col-6 col-xl-3"><div class="stat-card"><div class="stat-label">اليوم</div><div class="stat-value"><?= e((string)$todayCount) ?></div><div class="stat-note">أوامر اليوم</div></div></div>
<div class="col-6 col-xl-3"><div class="stat-card"><div class="stat-label">مكتملة</div><div class="stat-value"><?= e((string)$completedCount) ?></div><div class="stat-note">تم ترحيلها للمخزون</div></div></div>
<div class="col-6 col-xl-3"><div class="stat-card stat-card-soft"><div class="stat-label">الإنتاج الناتج</div><div class="stat-value"><?= e((string)$totalProducedQty) ?></div><div class="stat-note">إجمالي الكمية المقبولة</div></div></div>
</div>
<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 (!$rawMaterials || !$finishedProducts): ?>
<div class="empty-inline mb-3">يلزم وجود مادة خام ومنتج نهائي واحد على الأقل في صفحة الأصناف قبل بدء التصنيع.</div>
<?php endif; ?>
<?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="raw_product_id" required>
<option value="">اختر مادة خام</option>
<?php foreach ($rawMaterials as $product): ?>
<option value="<?= (int)$product['id'] ?>"><?= e($product['name']) ?> — <?= e($product['sku']) ?> (<?= e((string)$product['stock_qty']) ?>)</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label class="form-label">المنتج النهائي</label>
<select class="form-select" name="finished_product_id" required>
<option value="">اختر منتجًا نهائيًا</option>
<?php foreach ($finishedProducts as $product): ?>
<option value="<?= (int)$product['id'] ?>"><?= e($product['name']) ?> — <?= e($product['sku']) ?> (<?= e((string)$product['stock_qty']) ?>)</option>
<?php endforeach; ?>
</select>
</div>
<div class="row g-3">
<div class="col-md-6"><label class="form-label">الكمية المنتجة</label><input type="number" step="0.01" min="0.01" class="form-control" name="finished_qty" value="1" required></div>
<div class="col-md-6"><label class="form-label">الاستهلاك الفعلي للخامة</label><input type="number" step="0.01" min="0.01" class="form-control" name="actual_raw_qty" value="1" required></div>
</div>
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">الجودة</label>
<select class="form-select" name="quality_status">
<?php foreach ($qualityOptions as $key => $label): ?>
<option value="<?= e($key) ?>"><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label">الحالة</label>
<select class="form-select" name="status">
<option value="completed">إكمال الآن</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" <?= (!$rawMaterials || !$finishedProducts) ? 'disabled' : '' ?>>حفظ أمر التصنيع</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="manufacturing.php?id=<?= (int)$order['id'] ?>"><?= e($order['code']) ?></a></td>
<td><?= e($payload['finished_product_name'] ?? '') ?><div class="small text-secondary"><?= e((string)($payload['finished_qty'] ?? 0)) ?> <?= e($payload['finished_unit'] ?? '') ?></div></td>
<td><?= e($payload['raw_product_name'] ?? '') ?><div class="small text-secondary"><?= e((string)($payload['actual_raw_qty'] ?? 0)) ?> <?= e($payload['raw_unit'] ?? '') ?></div></td>
<td><?= e((string)($payload['produced_qty'] ?? 0)) ?> <?= e($payload['finished_unit'] ?? '') ?><div class="small text-secondary"><?= e(manufacturing_quality_label((string)($payload['quality_status'] ?? 'accepted'))) ?></div></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><p class="section-copy">يعرض أثر العملية على المخزون الفعلي لكل صنف.</p></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(manufacturing_quality_label((string)($payload['quality_status'] ?? 'accepted'))) ?></strong></div>
<div class="detail-block"><span class="detail-label">المنشئ</span><strong><?= e($payload['created_by'] ?? '') ?></strong></div>
</div>
<div class="subtle-card mb-3">
<div class="summary-row"><span>المادة الخام</span><strong><?= e($payload['raw_product_name'] ?? '') ?> — <?= e($payload['raw_sku'] ?? '') ?></strong></div>
<div class="summary-row"><span>الاستهلاك الفعلي</span><strong><?= e((string)($payload['actual_raw_qty'] ?? 0)) ?> <?= e($payload['raw_unit'] ?? '') ?></strong></div>
<div class="summary-row"><span>المخزون بعد الخصم</span><strong><?= e((string)($payload['raw_stock_after'] ?? '—')) ?></strong></div>
</div>
<div class="subtle-card">
<div class="summary-row"><span>المنتج النهائي</span><strong><?= e($payload['finished_product_name'] ?? '') ?> — <?= e($payload['finished_sku'] ?? '') ?></strong></div>
<div class="summary-row"><span>الكمية المنتجة</span><strong><?= e((string)($payload['finished_qty'] ?? 0)) ?> <?= e($payload['finished_unit'] ?? '') ?></strong></div>
<div class="summary-row"><span>الكمية المرحلة فعليًا</span><strong><?= e((string)($payload['produced_qty'] ?? 0)) ?> <?= e($payload['finished_unit'] ?? '') ?></strong></div>
<div class="summary-row"><span>المخزون بعد الإضافة</span><strong><?= e((string)($payload['finished_stock_after'] ?? '—')) ?></strong></div>
</div>
<div class="detail-grid mt-3">
<div class="detail-block"><span class="detail-label">تاريخ الإنشاء</span><strong><?= e($payload['created_date'] ?? '') ?></strong></div>
<div class="detail-block"><span class="detail-label">تاريخ الإكمال</span><strong><?= e($payload['completed_at'] ?? '—') ?></strong></div>
<div class="detail-block"><span class="detail-label">نسبة التحويل</span><strong><?= e((string)($payload['conversion_ratio'] ?? 0)) ?></strong></div>
<div class="detail-block"><span class="detail-label">مسودات</span><strong><?= e((string)$draftCount) ?></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(); ?>