# WP1 — [pivotal / GL] الأثر على أمر الإنتاج لازم يتسجّل في كل الحالات

## Goal
إذن صرف مرتبط بأمر إنتاج (`reference_type = production_order`) لما يتعتمد، لازم **دايمًا** يسجّل أثره على الأمر: `MfgMaterialIssue` + ترحيل WIP + خصم من الكمية المخططة + فك الحجز. حاليًا ده بيحصل **بس لو إعداد `production.material_issue_requires_approval` مفتوح**؛ ولو مقفول (وهو **الافتراضي: `default_value = 'false'`**) الإذن بيخصم المخزون **بلا أي أثر على الأمر ولا على تكلفة التشغيل** = stock↔GL desync صامت.

## الملف
`/home/moonui2/moon-erp-be/Modules/Production/app/Listeners/ApplyMaterialIssueOnApproval.php` — دالة `handle(InventoryIssueApproved $event)`.

## الحرّاس الأربعة الحالية (اقرأهم قبل أي تعديل)
1. **`reference_type !== ProductionOrder` → return.** يفضل زي ما هو.
2. **`IssueMaterials::$applyingInline` → return.** ⚠️ **مهم:** الفلاج ده `static` بيتحط `true` في `IssueMaterials.php:213` وبيترجّع `false` في `:224` — يعني بيلفّ نداء ApproveIssue الداخلي بالظبط. فالحارس ده **بيغطّي فعليًا** حالة «IssueMaterials بيطبّق inline دلوقتي».
3. **`! settings->getBool('production.material_issue_requires_approval')` → return.** ← **ده مصدر الفجوة.** الكومنت بيبرّره بـ«لما يكون OFF، IssueMaterials رحّل الأثر inline في نفس الطلب» — وده صحيح **بس للأذون اللي IssueMaterials عملها**. الإذن اللي المستخدم عمله **يدويًا من شاشة أذون الصرف** عمره ما مرّ على IssueMaterials، فمفيش أي حاجة اترحّلت inline، والحارس بيسكته تمامًا.
4. **idempotency:** `MfgMaterialIssue::where('inventory_issue_id', $issue->id)->exists()` → return. يفضل زي ما هو — ده اللي بيمنع الترحيل المزدوج.

## المطلوب
خلّي الحارس رقم 3 **مايسكتش** الأذون اللي IssueMaterials ما عملهاش. الفرضية الراجحة (تحقّق منها بنفسك): الحارس 2 (inline flag) + الحارس 4 (idempotency) كفاية لمنع الترحيل المزدوج، فالحارس 3 **زائد** ويقدر يتشال أو يتضيّق.
- لو شيلته: أثبت بالاختبارات إن مسار IssueMaterials inline (الإعداد OFF) **مابيرحّلش مرتين**.
- لو ضيّقته: لازم يفرّق بين إذن جاي من IssueMaterials وإذن يدوي بمعيار حقيقي في البيانات (مش تخمين).
**حافظ على:** رمي `ValidationException` لو الأمر مش `canIssue()` (بيعمل rollback جوّه transaction الاعتماد — الكومنت :70-77 بيشرح ليه السكوت هنا خطر) · إعادة بناء السطور من البنود المعتمدة بترتيب `id` asc keyed على `issued_quantity` · تمرير `warehouse_id`/`owner_partner_id` من البند (ISS-9166 WP5) · بوابة الصرف الزائد `production.over_issue_requires_approval`.

## Acceptance
- [ ] إذن صرف **يدوي** مرتبط بأمر إنتاج + الإعداد **OFF** → الاعتماد بيعمل `MfgMaterialIssue` وبيرحّل WIP وبيخصم من المخطط وبيفك الحجز.
- [ ] نفس السيناريو والإعداد **ON** → نفس النتيجة بالظبط (سلوك اليوم).
- [ ] مسار IssueMaterials (الصرف من شاشة أمر الإنتاج) بالإعداد **OFF** → الأثر بيترحّل **مرة واحدة** بالظبط، مش مرتين.
- [ ] نفس المسار بالإعداد **ON** (draft يعتمده أمين المخزن) → مرة واحدة، زي النهارده.
- [ ] أمر مش `canIssue()` وقت الاعتماد → 422 والمخزون **مايتحركش** (rollback).
- [ ] إذن صرف **مش** مرتبط بأمر إنتاج → مايتأثرش إطلاقًا.

## Tests
Pest في `Modules/Production/tests/Feature/` (مثلاً `ManualIssueAgainstOrderTest.php`) تغطّي الست نقاط فوق — خصوصًا **اختبار عدم الترحيل المزدوج** في مسار inline بالإعداد OFF (ده الحارس اللي بتلمسه).
شغّل: `/opt/cpanel/ea-php82/root/usr/bin/php -d memory_limit=1G vendor/bin/pest Modules/Production/tests --filter='Issue|Material'`
⚠️ baseline معروف: Production 573 pass / **10 فشل سابقة** (9× record-output 422 + 1 RuntimeException في ConsignmentFoundationTest) — متتحاسبش عليهم، بس **ماتزوّدش عليهم**.

## Flags
**pivotal — بيمسّ ترحيل WIP/GL** ⇒ بوابة تصميم `architect` إلزامية قبل الإغلاق. **migration: لا.**

## Out of scope
منطق التكاليف/الانحرافات · الصرف الزائد نفسه (له فلوه ISS-9105) · إنشاء الأمر المؤقت (WP2) · أي واجهة.
