(int)$customer['id'], 'customer_name' => $customer['title'], 'branch' => $branch, 'product_id' => (int)$product['id'], 'product_name' => $product['title'], 'sku' => $productPayload['sku'] ?? $product['code'], 'unit' => $productPayload['unit'] ?? 'وحدة', 'qty' => $qty, 'unit_price' => $unitPrice, 'subtotal' => $subtotal, 'vat' => $vat, 'grand_total' => $grand, 'notes' => $notes, 'document_type' => $documentType, 'document_label' => sales_document_label($documentType), 'created_date' => date('Y-m-d H:i'), 'created_by' => current_user()['username'] ?? 'system', 'source_document_id' => (int)($source['id'] ?? 0), 'source_document_code' => $source['code'] ?? null, 'source_document_type' => $source['record_type'] ?? null, 'source_document_label' => $source ? sales_document_label((string)$source['record_type']) : null, 'inventory_effect' => $documentType === 'sales_order' ? 'deducted' : 'none', ]; } $errors = []; if ($_SERVER['REQUEST_METHOD'] === 'POST') { verify_csrf(); $action = (string)($_POST['form_action'] ?? 'create_document'); if ($action === 'create_document') { $documentType = (string)($_POST['document_type'] ?? 'sales_order'); $customerId = (int)($_POST['customer_id'] ?? 0); $branch = trim((string)($_POST['branch'] ?? '')); $productId = (int)($_POST['product_id'] ?? 0); $qty = (float)($_POST['qty'] ?? 0); $notes = trim((string)($_POST['notes'] ?? '')); if (!in_array($documentType, ['sales_quote', 'sales_order'], true)) { $errors[] = 'نوع المستند غير صحيح.'; } $customer = fetch_record('customer', $customerId); $product = fetch_record('product', $productId); validate_sales_input($customer, $product, $branch, $qty, $errors); if (!$errors && $customer && $product) { db()->beginTransaction(); try { $payload = build_sales_payload($customer, $product, $branch, $qty, $notes, $documentType); $status = $documentType === 'sales_quote' ? 'draft' : 'confirmed'; $documentId = create_record( $documentType, sales_document_label($documentType) . ' ' . $customer['title'], next_code(sales_document_prefix($documentType), $documentType), $payload, $status ); if ($documentType === 'sales_order') { $document = fetch_sales_document_by_id($documentId); if ($document) { $stock = adjust_product_stock($product, -$qty, 'sales_confirm', $document['code'], 'sales_order', $documentId, 'بيع للعميل ' . $customer['title']); $payload = $document['payload_data']; $payload['stock_after'] = $stock['after']; $payload['inventory_effect'] = 'deducted'; update_record_payload($documentId, $payload, 'confirmed'); } } db()->commit(); set_flash('success', $documentType === 'sales_quote' ? 'تم إنشاء عرض السعر بنجاح.' : 'تم إنشاء أمر البيع وتحديث المخزون بنجاح.'); redirect('sales_orders.php?id=' . $documentId); } catch (Throwable $e) { db()->rollBack(); $errors[] = $e instanceof RuntimeException ? 'المخزون غير كافٍ لتأكيد أمر البيع.' : 'تعذر حفظ المستند، حاول مرة أخرى.'; } } } if ($action === 'convert_document') { $sourceId = (int)($_POST['source_id'] ?? 0); $targetType = (string)($_POST['target_type'] ?? ''); $source = fetch_sales_document_by_id($sourceId); if (!$source) { $errors[] = 'المستند المصدر غير موجود.'; } elseif (!in_array($targetType, sales_document_conversion_targets((string)$source['record_type']), true)) { $errors[] = 'التحويل المطلوب غير مسموح لهذه المرحلة.'; } elseif (sales_document_child_exists($sourceId, $targetType)) { $errors[] = 'تم إنشاء هذا المستند مسبقًا من نفس المصدر.'; } if (!$errors && $source) { $payload = $source['payload_data']; $customer = fetch_record('customer', (int)($payload['customer_id'] ?? 0)); $product = fetch_record('product', (int)($payload['product_id'] ?? 0)); $branch = (string)($payload['branch'] ?? ''); $qty = (float)($payload['qty'] ?? 0); $notes = trim((string)($payload['notes'] ?? '')); validate_sales_input($customer, $product, $branch, $qty, $errors); if (!$errors && $customer && $product) { db()->beginTransaction(); try { $newPayload = build_sales_payload($customer, $product, $branch, $qty, $notes, $targetType, $source); if ($targetType === 'delivery_note') { $newPayload['delivered_at'] = date('Y-m-d H:i'); } if ($targetType === 'sales_invoice') { $newPayload['invoiced_at'] = date('Y-m-d H:i'); $newPayload['payment_status'] = 'unpaid'; } $newId = create_record( $targetType, sales_document_label($targetType) . ' ' . $customer['title'], next_code(sales_document_prefix($targetType), $targetType), $newPayload, 'confirmed' ); if ($targetType === 'sales_order') { $document = fetch_sales_document_by_id($newId); if ($document) { $stock = adjust_product_stock($product, -$qty, 'sales_convert', $document['code'], 'sales_order', $newId, 'تحويل من عرض سعر إلى أمر بيع للعميل ' . $customer['title']); $newPayload = $document['payload_data']; $newPayload['stock_after'] = $stock['after']; $newPayload['inventory_effect'] = 'deducted'; update_record_payload($newId, $newPayload, 'confirmed'); } } db()->commit(); set_flash('success', 'تم إنشاء ' . sales_document_label($targetType) . ' بنجاح.'); redirect('sales_orders.php?id=' . $newId); } catch (Throwable $e) { db()->rollBack(); $errors[] = $e instanceof RuntimeException ? 'المخزون غير كافٍ لإتمام التحويل.' : 'تعذر تنفيذ التحويل، حاول مرة أخرى.'; } } } } } $customers = customer_dataset(); $products = product_dataset(); $documents = fetch_sales_documents(); $detail = isset($_GET['id']) ? fetch_sales_document_by_id((int)$_GET['id']) : null; $detailChildren = $detail ? sales_document_children((int)$detail['id']) : []; $summaryCounts = []; foreach (sales_document_record_types() as $type) { $summaryCounts[$type] = record_count($type); } render_header('المبيعات والوثائق', 'إدارة عروض الأسعار وأوامر البيع وأوامر التسليم والفواتير وربطها في مسار واحد قابل للطباعة.', 'orders'); ?>
عروض الأسعار
مرحلة التسعير قبل خصم المخزون
أوامر البيع
تؤكد البيع وتخصم المخزون
أوامر التسليم
جاهزة للطباعة والتسليم
الفواتير
مرتبطة بالعميل وبالمحاسبة لاحقًا

إنشاء مستند مبيعات

ابدأ بعرض سعر أو أنشئ أمر بيع مباشر. عند تأكيد أمر البيع يتم خصم المخزون تلقائيًا.

عرض السعر لا يغير المخزون، بينما أمر البيع يخصم الكمية مباشرة.
اختر العميل أولًا لمعرفة قيود الأصناف.
المخزون المتاح
الإجمالي قبل الضريبة0.00 ر.س
ضريبة القيمة المضافة 15%0.00 ر.س
الإجمالي النهائي0.00 ر.س

سجل وثائق المبيعات

المسار الآن يدعم: عرض سعر → أمر بيع → أمر تسليم / فاتورة.

الرقمالنوعالعميلالصنفالإجماليالمصدرالطباعة
طباعة
لا توجد وثائق مبيعات بعد.

تفاصيل المستند

رقم المستند
النوع
الحالة
العميل / الفرع
تاريخ الإنشاء
المصدر
الكمية
سعر الوحدة
الإجمالي قبل الضريبة
الضريبة
الإجمالي النهائي
المخزون بعد العملية
حالة الدفع
الوثائق المرتبطة
أنشئ مستندًا جديدًا أو اختر واحدًا من الجدول لعرض تفاصيله.