تدقيق فني عميق · 5 محاور متوازية

تقرير تدقيق موديول التصنيع (Production) — Moon ERP

تحليل شامل لموديول التصنيع في برنامج حسابات moonui: نقاط القوة، الباجز، الأمان، الأداء، والنواقص — بأدلة من الكود (file:line) وخطة عمل مرتّبة بالأولوية.

2026-06-15 · Backend ~31.5K LOC (16 controllers · 32 models · 38 migrations · 14 actions) · Frontend ~9.6K LOC (19 screens)
قويالحكم العام: مبني فعلياً
3مشاكل حرجة (Critical)
8+مشاكل عالية (High)
5فجوات GMP فارما (P0)
~41Kإجمالي سطور الكود
⚖️

٠ — الحكم العام

موديول حقيقي وناضج، مش بروتوتايب — بس فيه عيوب سلامة بيانات مركّزة في التكلفة والـ MRP، وفجوات GMP مهمة للفارما.

ده نظام MRP-II حقيقي ومبني باحتراف: دورة كاملة (MRP ← أمر إنتاج ← صرف خامات ← تأكيد ← استلام ← تكلفة ← قيود GL)، آلة MRP زمنية فعلية بـ BOM explosion و pegging، تصنيع بالأمر للغير (toll) شغّال، وتتبّع تشغيلات (genealogy). صفر TODO/FIXME و18 من 19 شاشة موصولة بالـ backend.

طبقة المستندات (صرف/استلام/تأكيد/إقفال) ممتازة ومحصّنة: كل عملية متعددة الخطوات داخل DB::transaction مع lockForUpdate، والقيود لها idempotency_key. العيوب الحقيقية مركّزة في 3 أماكن: حسابات التكلفة/الانحرافات، تزامن الـ MRP، ومسارَي إنشاء (Order/BOM) بدون transaction.

أخطر نقطة للفارما: مفيش بوابة جودة (QC hold) قبل استلام المنتج التام أو قبل صرف الخامة — حالة الجودة متحطوطة 'released' ثابتة، ومفيش quarantine/rejected. ده أكبر عائق GMP.
💪

١ — نقاط القوة (مبني كويس — متعملش فيه)

الحاجات دي صلبة ومتعتمد عليها — متعيد بناءها.
🏛️

٢ — المعمارية

التقسيم سليم، بس فيه "god classes" وكنترولرز سمينة وأنماط غير متسقة.

التقسيم

Controller ← Action ← Model (منطق الكتابة في Actions، مفيش service layer للكتابة — مجلد Services/ شبه فاضي). الأحداث/المستمعين منظّمين كويس لقيود الـ GL. التعامل مع الـ transactions متسق في كل الـ actions المعدِّلة.

الضعف المعماري

HIGH God-classes — ملفات أكبر بكتير من حد 800 سطر
RunMrp.php = 1,149 LOC · ConfirmOperation.php = 1,005 LOC · ProductionOrderController.php = 832 LOC · orders.component.ts = 1,487 LOC

آلة الـ MRP كلها في class واحد (GC + demand + netting + scheduling + lot-sizing + explosion + pegging). ConfirmOperation أوركستراتور متخفي في صورة action (بيستدعي Inventory + GL + HRM piecework). محتاجين تفكيك لـ classes أصغر (DemandCollector / NettingEngine / LotSizer / BomExploder / Pegger).

MED أنماط غير متسقة

غالبية الموديلز enums حقيقية، بس MfgMpsHeader/Line وMfgVarianceRecord بيستخدموا const strings (عُرفين متنافسين). مفيش query scopes — فلترة where('company_id') متكتوبة يدوي في كل مكان. وصول مباشر لموديولات تانية عبر app(...) بدل anti-corruption boundary.

🐞

٣ — الباجز وسلامة البيانات

العيوب الحقيقية مركّزة في التكلفة/الانحرافات والـ MRP ومسارَي إنشاء بدون transaction.
CRITICAL C1 — معادلة انحراف حجم التكلفة الثابتة (FOVV) غلط رياضياً
ComputeOrderVariances.php:374-375

بيقارن ميزانية الفترة كاملة بالمطبّق على أمر واحد، فأي أمر إنتاجه أقل من حجم الفترة بيطلع انحراف ≈ الميزانية الشهرية كلها كـ "غير مواتٍ". الصح: (budgetedVolume − produced) × stdFixedRate. الأثر: انحراف OH ثابت غلط على كل أمر، وتقارير إدارية مشوّهة. (مفيش تست يغطي المسار).

CRITICAL C2 — إنشاء أمر الإنتاج بدون transaction
ProductionOrderController.php:134-178 (store)

توليد رقم تسلسلي ← إنشاء الأمر ← N خامات ← N عمليات ← تحديث التكاليف المخططة — كله مش داخل DB::transaction. أي فشل في النص بيسيب أمر يتيم نص-مكتمل برقم مستهلك وتكاليف ناقصة. كل مسارات الإنشاء الأخرى عاملة transaction — ده الوحيد المنسي.

CRITICAL C3 — إنشاء الـ BOM بنفس المشكلة (بدون transaction)
BomController.php:73-99 (store)

إنشاء BOM ← مكوّنات + بدائل ← عمليات، بدون transaction (عكس clone/setDefault بتوعه). BOM نص-مكتمل بيغذّي أوامر إنتاج لاحقة → فساد بيانات أساسية.

#المشكلةالمكانالخطورة
H1تشغيلات MRP متزامنة بتفسد بعض — الـ purge على مستوى الشركة كلها مش مستوى الـ run، ومفيش قفل "run واحد نشط"RunMrp.php:161-184HIGH
H2توليد run_number بـ count()+1 = race conditionRunMrp.php:1143HIGH
H3هيدر الـ run والـ CRP برّه transaction الـ netting — run "فاشل" بيسيب planned orders حيّةRunMrp.php:100,138HIGH
H4تسوية الـ overhead check-then-act بدون قفل (TOCTOU) → ترحيل مزدوج آخر الشهرSettleAppliedOverhead.php:58-83HIGH
H5حفظ الانحرافات غير transactional وغير idempotent → سجلات مكرّرة عند إعادة الحسابComputeOrderVariances.php:591HIGH
H6recordOutput بيخلّي الكمية المنتجة تكبر بلا سقف مقابل الخطة → تكلفة وحدة و yield مشوّهينProductionOrderController.php:538HIGH
H7مفيش تحويل وحدات قياس (UoM) في الـ MRP/BOM explosion — لو مكوّن بوحدة مختلفة عن وحدة المخزون → فساد كميات صامتRunMrp.php:1018, BomExplosionService.php:103HIGH
H8المطبّق مقابل الفعلي للـ overhead متقاسين بمستوى/نطاق مختلف → انحراف تسوية وهميSettleAppliedOverhead.php:129-232HIGH
M1roll-up التكلفة المعيارية بيدمج عمل/OH التجميعة الفرعية في leg "الخامة" للأب → تصنيف انحراف غلطRollUpStandardCost.php:159MED
M2قاعدة min_max بتغطّي العجز ناقص بصمت بدون split/exceptionRunMrp.php:925MED
M3period_order (POQ) لا-عملية وeoq = fixed بس (stubs)RunMrp.php:916MED
M9per_page بلا سقف في 3 endpoints → full-table hydrate / DoSProductionOrderController.php:93MED
L1cancel/unrelease بيبلعوا أخطاء تحرير الحجز وبيغيّروا الحالة برضه → تسريب حجزProductionOrderController.php:387LOW
أكبر فجوة في التستات: مفيش ولا تست تزامن حقيقي — كل الـ lockForUpdate (خط الدفاع الأساسي) متستّت بشكل تسلسلي على connection واحد. لو شِلت كل الـ lockForUpdate() مش هيفشل ولا تست. كمان الانحرافات متستّتة في اتجاه واحد بس (كلها غير مواتية).
🔐

٤ — الأمان

الوضع العام قوي — الفجوات ضيقة ومحدّدة، بس فيها مخاطرة عبور-المستأجرين.
HIGH H1 — تعيين مستخدم من شركة تانية عبر owner_user_id (IDOR)
VarianceController.php:118

owner_user_id متحقّق منه كرقم موجب بس — مش مقيّد بشركة المستخدم. مهاجم بيـ PATCH انحراف بـ owner_user_id لمستخدم في شركة تانية → فساد ملكية + كشف بيانات. الكنترولر التاني (ConfirmationController:74) بيعمل scoping صح لنفس النوع — يعني سهو. الإصلاح سطر واحد: Rule::exists('users','id')->where('company_id',$companyId).

#المشكلةالمكانالخطورة
M1قواعد exists غير مقيّدة بالشركة في الـ BOM (product/unit/center) → ربط كيانات شركة تانيةBomController.php:162,165,205MED
M2نفس المشكلة في صرف الخامة/استلام التام/فاتورة toll (warehouse/customer/tax)ProductionOrderController.php:584+MED
L1403 بدل 404 على موارد شركة تانية → كشف وجود الـ ID (enumeration)10 controllersLOW
مشكلة منهجية (التصميم): مفيش Global Tenant Scope — عزل القراءة 100% يدوي. الموديول دلوقتي عامله صح في كل مكان، بس مفيش شبكة أمان: أي query مستقبلي ينسى where('company_id') هيسرّب بصمت. التوصية: اعمل CompanyScope عام (addGlobalScope) يشيل الاعتماد على إن كل مطوّر يفتكر.

٥ — الأداء

المشاكل بتظهر مع الحجم (40+ منتج، آلاف الأوامر): MRP متزامن، N+1، فهارس ناقصة.
CRITICAL تشغيل الـ MRP متزامن (synchronous) بيحجز الـ request
MrpController.php:61 → RunMrp.php:91,111,138

كل آلة الـ MRP + الـ explosion + الـ pegging + الـ CRP بتشتغل جوه request واحد وtransaction واحدة. مع 40+ منتج → دقايق بتحجز PHP worker وقفل طويل. الحل: ادفعها لـ queued job، ورجّع الـ run فوراً، وخلّي الـ cockpit يـ poll.

CRITICAL resolveDefaultBom() بيتعاد استعلامه لكل منتج بدون memoization (N+1)
RunMrp.php:1043 (متنادى من :425, :642, :557)

نفس الـ BOM SELECT بيتعمل مئات المرات في الـ run. + supplySchedule() بيعمل 4-5 استعلامات منفصلة لكل منتج. الحل: memoize الـ BOM والـ supply لكل منتج طول الـ run.

فهارس ناقصة (إضافة migration واحدة آمنة تحلّها)

الجدولالفهرس الناقصالأثر
production_orders(company_id,product_id,status) + (company_id,planned_end_date)CRIT full scan في توريد MRP
bill_of_materials(company_id,product_id,is_active,is_default)CRIT أسخن lookup في MRP
mfg_mrp_planned_orders(company_id,product_id), (mrp_run_id,due_date)HIGH
bom_componentsproduct_id (where-used / LLC)HIGH

الواجهة الأمامية

🧩

٦ — النواقص (فارما GMP + MRP-II)

النواقص دي "حذف نطاق" مش stubs نص-مكتملة — الـ core شغّال.

P0 — حرجة لمصنع أدوية / toll

P1 — فجوات MRP-II وظيفية

🗺️

٧ — خطة العمل المقترحة (بالأولوية)

رتّبتها بالأثر/المخاطرة — ابدأ من فوق لتحت.
الموجةالشغلليه دلوقتي
١ — إصلاحات حرجةصلّح C1 (معادلة FOVV)، لفّ C2/C3 في transaction، وحطّ سقف للكمية المنتجة (H6)دي بتفسد بيانات مالية/مخزون فعلياً دلوقتي
٢ — تزامن MRP/OHقفل "run واحد نشط" + run-scoped purge (H1)، transaction حول الـ run+CRP (H3)، idempotency للـ overhead والانحرافات (H4/H5)تمنع فساد التخطيط والترحيل المزدوج
٣ — أمانscope الـ owner_user_id (H1) وكل قواعد exists (M1/M2)، وزوّد CompanyScope عامإصلاحات صغيرة، تقفل عبور-المستأجرين
٤ — أداءاعمل queue للـ MRP/rollUp/convertAll، memoize الـ BOM/supply، migration الفهارس، وصلّح N+1 في الصرف/التأكيدضروري قبل ما الداتا تكبر
٥ — GMP فارماحالة batch (quarantine/released/rejected) + بوابة QC قبل الاستلام/الصرف، حقول الصلاحية/الـ potency، وملكية على الـ batchمتطلبات تنظيمية لمصنع أدوية
٦ — اكتمال + جودة كودsubcontracting، rework حقيقي، تستات تزامن، وتفكيك الـ god-classes (RunMrp/ConfirmOperation)قيمة طويلة المدى وقابلية صيانة
الخلاصة: الأساس قوي جداً ومتبنيش من جديد. ركّز على إصلاحات سلامة البيانات (الموجة ١-٢) الأول لأنها بتأثر على الأرقام المالية دلوقتي، وبعدين GMP الفارما لو ده مصنع أدوية فعلي.
تقرير تدقيق موديول التصنيع — Moon ERP · 5 محاور (معمارية · جودة · أمان · أداء · اكتمال) · أدلة من الكود · 2026-06-15