📦

المخزون والمشتريات: شريان التصنيع (Inventory & Purchases)

وحدتا Modules\Inventory وModules\Purchases هما الجاران الأكثر التصاقاً بوحدة التصنيع المقترحة: منهما تُحجَز المواد، وإليهما تُصرَف للإنتاج، ومنهما تُستلَم المنتجات النهائية، وعبرهما يُطلِق نظام تخطيط الاحتياجات (MRP) طلبات الشراء. هذا القسم يوثّق ما هو جاهز للاستخدام كما هو، وما ينقص ويجب بناؤه، مع تحديد نقاط الربط البرمجية بدقّة.

سيِّد الأصناف ووحدات القياس (Item Master & UoM)

سيِّد المنتجات مملوك لوحدة Modules\Core ومشترَك بين كل الوحدات؛ المخزون والمشتريات يشيران إليه بمفتاح خارجي فقط. البنية الأساسية لوحدات القياس والتحويلات قوية وجاهزة لإعادة الاستخدام في قوائم المكوّنات (BOM) ومسارات التصنيع.

التتبّع بالدُفعة والتسلسل (Batch / Lot / Serial)

التتبّع بالرقم التسلسلي (Serial) مكتمل ومُدار بجدول product_serials مع دورة حياة مُلزَمة عند الاستلام والصرف. أمّا التتبّع بالدُفعة (Batch/Lot) فهو ناقص جوهرياً: لا يوجد جدول رئيسي للدُفعات ولا أرصدة على مستوى الدُفعة؛ batch_number وexpiry_date مجرّد حقول نصّية على بنود الاستلام/الصرف. أرصدة المخزون وطبقات التكلفة مفهرسة بـ(منتج/متغيّر/مستودع) فقط دون الدُفعة.

فجوة التصنيع يحتاج نَسَب الدُفعات (أي دُفعات المواد الخام دخلت في أي دُفعة منتج نهائي) والصرف وفق الأقرب انتهاءً (FEFO) — وهذا غير ممكن في النموذج الحالي ويتطلّب جدول أرصدة دُفعات وخدمة مخزون واعية بالدُفعة. استنتاج

خدمة المخزون المركزية (StockService)

كل حركات المخزون تمرّ عبر Modules\Inventory\Services\StockService. تدعم الخدمة طريقتَي تقييم: الوارد أولاً صادر أولاً (FIFO) عبر طبقات التكلفة، والمتوسّط المرجّح (WAC) عبر الرصيد. الطريقة تُضبَط على مستوى الشركة بإعداد inventory.valuation_method (الافتراضي weighted_avg).

تدفّق الاستلام والشراء (GRN & Receipt Flow)

النمط المرجعي الذي يجب أن يحاكيه التصنيع لاستلام المنتجات النهائية موجود في PurchaseGrnController::approve(): يُنشئ مستند استلام InventoryReceipt بحالة مسوّدة ثم يستدعي ApproveReceipt::execute() الذي يزيد المخزون فعلياً ويُنشئ طبقة التكلفة. أوضاع الاستلام (purchases.grn_mode): مباشر، أو باستلام، أو باستلام مع فحص جودة.

  1. توليد رقم المستند عبر SequenceService::generateNext($company,'inventory','receipt').
  2. إنشاء InventoryReceipt بحالة Draft وبنوده مع تمرير batch_number وexpiry_date وserial_numbers.
  3. استدعاء app(ApproveReceipt::class)->execute($receipt, $userId) لزيادة المخزون.
  4. تحديث كميات الاستلام على أمر الشراء وإعادة احتساب حالته.

طلب الشراء ← أمر الشراء (مدخل MRP)

سيُنشئ تخطيط الاحتياجات (MRP) طلبات شراء آلياً. الطلب موجود في purchase_requests (مع needed_by وpriority وcost_center_id وconverted_to_order_id)، ويتحوّل إلى أمر شراء عبر مسار convertFromRequest الذي يَسِم الطلب بحالة Converted. حدود إعادة الطلب مخزّنة على المنتج، لكن وحدة التنبيهات ReorderAlertController تنبيهية فقط ولا تُنشئ طلبات شراء آلياً.

فجوة إنشاء طلب الشراء يقع اليوم داخل المتحكّم فقط؛ يُنصَح باستخراج إجراء Purchases\Actions\CreatePurchaseRequest ليستدعيه MRP داخلياً دون المرور عبر واجهة HTTP. استنتاج

الترحيل المحاسبي وتكلفة البضاعة المباعة (GL & COGS)

نقطة جوهرية: وحدة المخزون لا تُرحِّل أي قيود محاسبية — حركة المخزون منفصلة عن الترحيل المحاسبي. الوحدة المستهلِكة هي من تكتب القيد دائماً عبر البوّابة الوحيدة Modules\Accounting\Actions\CreateJournalEntry:

الاستدعاءات البرمجية الدقيقة لوحدة التصنيع

الغرضالاستدعاء / الجدولالحالة
حجز المواد عند إطلاق أمر الإنتاجزيادة inventory_stock_balances.reserved_quantity (عبر إجراء جديد ReserveOrderComponents)يُبنى
صرف المواد للإنتاج (خام ← تحت التشغيل)ApproveIssue::execute($issue)StockService::decreaseStock + قيد مدين WIP/دائن المخزون عبر CreateJournalEntryجاهز جزئياً
استلام المنتج النهائي (تحت التشغيل ← نهائي)InventoryReceipt (Draft) ← ApproveReceipt::execute($receipt) + قيد مدين المخزون النهائي/دائن WIPجاهز
إطلاق طلبات الشراء من MRPإنشاء PurchaseRequest + بنوده، ثم convertFromRequestPurchaseOrderجاهز جزئياً
تكلفة الصرف للمعاينةStockService::getIssueCost() / getProductCost()جاهز
مواقع الورشة وتحت التشغيلتمثيلها كسجلّات warehouses (لا يوجد نموذج مواقع/أرفف فرعية)يُبنى
2وحدتان متاخمتان
5دوال StockService عامة
FIFO+WACطريقتا تقييم
9فجوات يجب سدّها

ملخّص الفجوات الجوهرية للمجلس