# PROJECT_MEMORY — مشروع "مون" (Issues Portal · project=283)

> ذاكرة وكيل المشروع على منصّة الطلبات. الأحدث فوق. ممنوع أسرار/توكنز.
> كود المشروع محليًا: FE = `moon-erp/` · BE = symlink لـ `/home/moonui2/moon-erp-be` (Modules نمط nwidart).

---

## 2026-07-14 — ISS-2026-0186 (improvement بحجم فيتشر · "جرد جديد محسّن") — **awaiting_client**
- **المطلوب:** وضع جرد ثانٍ بزرار «جرد جديد محسّن»: اختيار المخزن → عرض **كل منتجات المخزن** تلقائيًا (بلا رصيد=صفر، وذو رصيد=«كمية قبل» استرشادية)، إدخال المعدودة، paging لآلاف المنتجات؛ الحفظ = نفس الجرد الحالي. مرفق: لقطة شاشة الجرد الحالية (قُرئت).
- **تصنيف الحجم:** 2+ معيار (قدرة جديدة + جولات محتملة) → **فيتشر**. التذكرة نفسها مستقلة فلا spinoff — حُلّلت بعقد نطاق. skill: implement-research (فحص كود بوكيلَي opus BE+FE).
- **فحص الكود (مرجعيات دقيقة):** الحفظ يُعاد استخدامه بالكامل — الحمولة `{warehouse_id,date,items:[{product_id,unit_id,counted_quantity,...}]}` → `POST inventory/counts` → `FinalizeCount` (firstOrCreate رصيد + تسوية للفرق≠0 فقط، إصلاح ISS-0004). **الفجوة الوحيدة:** لا endpoint يعرض كل منتجات مخزن (StockBalanceController byWarehouse/index مبنية على صفوف الأرصدة، سقف 25؛ ProductController يدعم per_page≤100 بلا ربط أرصدة). سابقة الجدول الضخم القابل للتحرير = **LIS price-editor** (virtual scroll + ngModel، لا FormArray). لا صلاحية جديدة (inventory.counts.create + inventory.stock.view). **بلا migration.**
- **🔴 قرار حَرِج (#1):** لو «المعدودة» تبدأ بصفر، منتج له رصيد ولم يُلمَس → فرق سالب → **تسوية تصفّره** = مسح المخزن بالخطأ. التوصية: «المعدودة» تبدأ = «قبل»، يُحفظ الفرق فقط.
- **المخرجات:** (1) HTML كامل بمعاينة قبل/بعد: `knowledge-base/plans/enhanced-inventory-count-analysis.html`. (2) تحليل بورتال بعقد نطاق (In/Out/Acceptance) + 3 أسئلة (أهمها #1) → **analysis step 3251 → `awaiting_client`**.
- **خطة WPs المقترحة:** WP1 BE endpoint (منتجات LEFT JOIN أرصدة لمخزن، مرقّم) · WP2 FE وضع محسّن (virtual scroll، «قبل» read-only، «معدودة» editable) · WP3 زرار+هوك مخزن+i18n · WP4 حارس الأمان (فروق فقط).
- **رد العميل (step 3253): «أخذ بالتوصية» ×3 → scope_freeze → `in_implementation`.** + موافقة المالك → نُفِّذ بـ implement-plan (4 WPs).
- **✅ التنفيذ (deployed /app `main-OE27KEDF.js`):**
  - **WP1 BE:** `GET inventory/counts/products-for-warehouse/{warehouse}` — Product LEFT JOIN inventory_stock_balances (variant NULL)، company-scoped، is_active+track_inventory+type=product، paginated (cap 5000/def 2000)، COALESCE→before_quantity/average_cost. gated inventory.counts.create + 404 لمخزن شركة أخرى. أداء: 17k منتج @ ~50ms/صفحة. **21 اختبار جرد أخضر.**
  - **WP2 FE:** وضع محسّن في inventory-counts.component (نمط price-editor: virtual-scroll p-table على signal<Row[]>، لا FormArray). loadProductsForWarehouse auto-paginate. «قبل» read-only، «معدودة» editable تبدأ=قبل.
  - **WP3 FE:** زرار «جرد جديد محسّن» + هوك مخزن + تحذير discard + بحث/فلتر فئة + 9 مفاتيح i18n.
  - **WP4:** حارس الأمان (يُرسَل الفرق فقط؛ diff=0→لا تسوية مُختبَر) + حقل ملاحظات + CHANGELOG + KB.
- **مراجعة مستقلة: CRITICAL واحد أُصلح** — `[(ngModel)]` بيعدّل كائن الصف بدون إبطال الـcomputed → زر الحفظ يفضل معطّلًا. الحل: (ngModelChange) handler + `enhancedRows.update(r=>[...r])`. + 3 LOW (تقييد المخزن بالشركة، توحيد الصلاحية view→create، حقل ملاحظات). **درس مسجّل في KB: computed لا يعيد الحساب على تعديل داخلي في المكان.**
- **✅ verification منشور (step 3261) → `client_review`.**
- **جولة 2 (client_comment step 3284):** العميل طلب (1) فلتر يخفي الصفري (2) بحث في المعروض. **المالك وجّه: البحث أصلاً موجود → نفّذ التاني بس.** التصنيف: تويكان صغيران على نفس الشاشة (لا فيتشر، لا spinoff). **✅ أُضيف فلتر «إخفاء الأصناف الصفرية»** (يخفي الصفري غير المَلموس فقط — يحمي أي عدّ مُدخَل)؛ يتراكب مع البحث/الفئة. FE بحت. build أخضر، deployed `main-IUH6ADOQ.js`. verification step 3286 → `client_review`. (scope_creep rounds=1, بلا warning.)
- **الحالة النهائية:** `client_review` — جاهزة لمراجعة العميل. متغيّرات = متابعة موثّقة (لا بيانات variant الآن).

---

## 2026-07-14 — ISS-2026-0185 (bug · "رقم الدفعة يُطلب عند الاعتماد لكن الاعتماد تلقائي") — **new (تحليل جاهز، بانتظار توكن للنشر)**
- **المطلوب (العميل حازم):** في إذن الإضافة (وأظن الصرف) لمنتج له تتبّع دفعات، المفروض يكتب رقم الدفعة؛ لكن الواجهة تقول «تُدخَل الدفعة عند الاعتماد»، **والإذن يُعتمَد تلقائيًا** فلا تأتي خطوة الاعتماد أبدًا ⇒ لا يستطيع إدخال دفعة. (مرفق: لقطة `/app/core/stock-receipts` · منتج PRD-17151 · بادج «وضع سريع».)
- **البوابات:** `planning_gate.required=true` [reproduce, root_cause, matrix] · `feature_gate=false` · agent_hint=null. **بيئة العميل = بيئتي (moonui2).**
- **إعادة إنتاج حيّة (من قاعدة البيانات):** إذن #40 (منتج batch + المستخدم كتب رقم دفعة) عبر الاعتماد التلقائي → **0 صفوف دفعات**؛ إذن #38 نفس النوع عبر الاعتماد اليدوي → **2 صفوف**. الفرق الوحيد = الاعتماد التلقائي.
- **السبب الجذري (متتبَّع كود-بكود):** التقاط الدفعات مربوط حصريًا بديالوج الاعتماد اليدوي. `InventoryReceiptController::store()` سطر 194 ينادي `ApproveReceipt::execute($receipt, userId)` **بدون** `$lots` عند الاعتماد التلقائي → `ReceiptLotService::persistLots` لا تُنفَّذ → لا صفوف في `inventory_receipt_item_batches` → `LotBalanceService::seedFromReceipt` يلفّ على `$item->batches` الفارغة → **صفر رصيد لوطي في `inventory_lot_balances`** → الصرف FEFO لاحقًا لا يجد لوطًا («وأظن في الصرف»). عمود `batch_number` على السطر يُحفَظ لكنه لا يتحوّل إلى رصيد لوطي.
- **serial أسوأ:** منتج serial + اعتماد تلقائي → مسار legacy في `ApproveReceipt` يرمي `serial_count_mismatch` (لا شاشة لإدخال السيريالات في الإنشاء) → قد يفشل الإنشاء.
- **ملاحظة ثانوية (نفس المنطقة):** الواجهة تقرأ `inventory.receipt_auto_approve` للبادج، الباك يقرأ `inventory.auto_approve_receipts` — القيمتان `true` بالصدفة الآن فيتطابقان.
- **التوصية:** batch → بناء payload الدفعات من عمود السطر وتمريره لـ`ApproveReceipt` عند الاعتماد التلقائي (أو fallback في `seedFromReceipt`)؛ serial → استثناؤها من الاعتماد التلقائي؛ + إصلاح الأيقونة/التولتيب + توحيد مفتاح الإعداد. الإصلاح في الطبقة المشتركة (store→ApproveReceipt→seedFromReceipt) يغطّي الاستلام والـGRN والرصيد الافتتاحي معًا.
- **✅ نُشر (2026-07-14):** (1) COMMENT planning-gate بـreproduce+root_cause+matrix → step 3235. (2) ANALYSIS بـ3 قرارات+توصيات+عقد نطاق (In/Out/Acceptance) → step 3237 → الحالة **`awaiting_client`**. ملاحظة API: حقل السؤال اسمه `question_text` (مش `question`) + `recommendation` اختياري.
- **رد العميل (step 3239, scope_freeze على analysis 3237) → `in_implementation`:** عدّل توصيتي وطلب الأعمق — **س1:** مش التقاط سريع؛ أي منتج يحتاج بيانات لوت (رقم دفعة + صلاحية + تاريخ إنتاج/فيلم) يدخلها يدويًا بدل الأتوماتيك. **س2:** batch **و** serial الاتنين ياخدوا بيانات إضافة؛ الباقي (none) يفضل أتوماتيك. **س3:** ✅ توحيد المفتاح.
- **الـKB يؤكّد قرار العميل = تصميمكم الأصلي:** `topics/purchases-controlled-flow.md` سطر 13: «Lot capture unified at keeper receipt approval: batches-first dialog, serials nested». الديالوج `ReceiptApprovalDialogComponent` موجود ويلتقط دفعة+صلاحية+إنتاج+سيريال — الاعتماد التلقائي بيتخطاه. `inventory_receipt_item_batches` = مصدر الحقيقة للوت (سطر 15).
- **✅ إعادة إنتاج محكومة (tinker + rollback على dev):** إذن batch جديد + اعتماد تلقائي → حركة مخزون=1، صفوف دفعات=0، رصيد لوطي=0. العطل مُثبَت تجريبيًا لا مُستنتَج.
- **الخطة المنشورة (comment step 3244, الحالة ثابتة in_implementation):** 3 WPs — (1) BE: `store()` لو الإذن فيه بند batch/serial → لا اعتماد تلقائي، يفضل pending. (2) FE: بادج «وضع سريع» يختفي للأذون المتتبَّعة + تصحيح التولتيب. (3) توحيد مفتاح الإعداد. الصرف يُصلَح تلقائيًا (الاستلام بقى يكوّن اللوطات فالـFEFO يلاقيها).
- **موافقة تانية من العميل (step 3245): «نفّذا»** + موافقة المالك → بدأ التنفيذ.
- **✅ التنفيذ (3 WPs، الحالة in_implementation):**
  - **WP1 BE** `InventoryReceiptController::store()`: حارس جديد `receiptRequiresManualLotCapture()` — لو أي بند product.tracking_type ∈ {batch,serial} → يتخطّى الاعتماد التلقائي، يفضل Draft. import ProductTrackingType. اختباران Pest (RED→GREEN): batch/serial تحت auto_approve → draft، صفر حركة، صفر رصيد. **17 اختبار wiring + 71 اختبار أوسع + 24 settings-gates = صفر regression.**
  - **WP2 FE** `stock-receipts.component.ts`: `quickSave` لو رجع draft + متتبَّع → `this.approve()` (يفتح ديالوج اللوط) بدل approve() الأعمى. helper `receiptHasTrackedLines()`. + `.html`: البنود المتتبَّعة تعرض ملاحظة «عند الاعتماد» read-only بدل حقل الدفعة المضلِّل. مفتاح i18n جديد `INVENTORY.AT_APPROVAL` (ar+en).
  - **WP3** توحيد المفتاح: `stock-receipts.component.ts` + `settings.component.ts` بقوا يقروا/يكتبوا `inventory.auto_approve_receipts` (مفتاح الباك) بدل `receipt_auto_approve` الوهمي. (معالج الإعداد default-accounts.config.ts كان صح أصلاً.)
  - **CHANGELOG:** بند ثنائي اللغة تحت [Unreleased]. **build FE أخضر.** كل الملفات chown moonui2.
- **🔶 سيبلينج كامن موثّق (خارج النطاق المجمَّد، لم يُصلَح):** نفس تضارب المفتاح في الصرف/الجرد — الباك يقرأ `inventory.auto_approve_issues` والواجهة (settings.component.ts) تكتب `inventory.issue_auto_approve` (+ count_auto_approve). يستاهل تذكرة/spinoff لاحقًا.
- **مراجعة مستقلة (code-reviewer): 0 CRITICAL · 1 HIGH · 1 MEDIUM — الاتنين اتصلحوا:**
  - **HIGH:** `receiptHasTrackedLines()` FE هشّة — الـResource مابيرجّعش tracking_type فالاعتماد كان 100% على كاش الواجهة (لو اتفقد → يعيد الـbug بصمت). **الإصلاح:** حذف الـhelper نهائيًا؛ بما إن رد `draft` تحت الوضع السريع لا يعني إلا بنود متتبَّعة (العادي→approved، workflow→pending)، كل draft يُوجَّه مباشرة لـ`this.approve()`. أبسط وأمتن (يثق في حالة الخادم لا كاش العميل).
  - **MEDIUM:** تغيير المفتاح بلا backfill → تركيبات ضبطت المفتاح القديم تقرا OFF. **القرار:** لا backfill (المفتاح القديم ماكانش الباك يحترمه؛ backfill يخاطر بتشغيل الاعتماد التلقائي = عكس الهدف) — بدلها ملاحظة أدمن في CHANGELOG يراجع الإعداد بعد الترقية.
  - إعادة بناء FE أخضر بعد التبسيط.
- **✅ نُشر على /app** (`main-GYBTYTCY.js`) + local-deploy للباك. apiUrl سليم moonui2.
- **✅ verification منشور (step 3246) → `client_review`.** التذكرة جاهزة لمراجعة العميل والإغلاق. ⚠️ نسخة أي عميل self-hosted تحتاج تحديث MoonStack القادم.
- **الحالة النهائية:** `client_review`.

---

## 2026-06-26 — ISS-2026-0004 (bug · "مشكلة في الجرد") — **awaiting_client**
- **المطلوب:** في `…/app/core/inventory-counts` لما يعمل جرد لمنتج ويخلّيه **صفر**، المنتج **بيختفي من "تقرير رصيد المخازن"**. تحليل دورة الجرد كلها وليه بيحصل. (مثال مرجعي عند العميل: CNT-000001 / CNT-000002 على smart).
- **التحليل (متتبّع في الكود):** دورة الجرد سليمة — `FinalizeCount` بينشئ تسوية مسودة بالفرق → `ApproveAdjustment` → `StockService::decreaseStock` بيظبط الرصيد = 0 و**بيحتفظ بصف الرصيد** (مفيش `delete`). موديل `StockBalance` نضيف (مفيش global scope/observer). شاشة "أرصدة المخزون" (`StockBalanceController::index`) بتعرض الأصفار افتراضيًا (`hide_zero` اختياري والـFE مابيبعتهوش).
- **السبب الجذري:** تقارير **القيمة/التقييم** بتفلتر `quantity > 0` فبتستبعد الصنف اللي رصيده صفر:
  - `Modules/Inventory/app/Http/Controllers/CostingController.php` → `valuation()` سطر ~129 (`where('quantity','>',0)`) — endpoint `inventory/costing/valuation` (تبويب التقييم في شاشة inventory-reports).
  - `InventoryReportController.php` → `warehouseSummary()` + `slowMoving()` (`quantity > 0`).
  - `InventoryCostReportController.php` → `costs()` (`quantity > 0`) + `aging()` (`remaining_quantity > 0`).
- **التوصية المقترحة:** خيار "إظهار رصيد صفر" في تقرير الأرصدة/التقييم (يفضل خارج إجماليات القيمة افتراضيًا، يبان لما يتفعّل).
- **رد العميل (step 159) صحّح المسار:** الاختفاء من شاشة **"أرصدة المخزون"** (`/stock-balances`) **مش** من تقرير التقييم؛ و**انتقائي** (CNT-000001: منتج اختفى ومنتج لأ؛ CNT-000002 كمان). كله على **smart** (نسخة تانية شغّالة).
- **re-analysis (step_id=161):** فحص حاسم في الكود الحالي → **مفيش أي حذف لصف رصيد في الـBE كله**، و`StockBalanceController::index` بيعرض الأصفار افتراضيًا، وشاشة الأرصدة بتميّز الصفر. يعني **المنتج المجرود صفر لازم يفضل ظاهر بـ0 في الكود الحالي** — الـbug مش بيتكرّر محليًا. فرضيتان: (1) smart نسخة أقدم كانت بتفلتر/تمسح الأصفار → الحل تحديث. (2) المنتج المختفي ماكانش ليه صف رصيد أصلاً (systemQty=0) → جرده صفر → فرق=0 → مفيش تسوية → مفيش صف → مايظهرش. التوصية لو (2): الجرد يكوّن صف رصيد بـ0 لأي منتج بيتجرد.
- **الأسئلة المنشورة (re-analysis):** (1) كود المنتجين في CNT-000001 (المختفي + الباقي) + المخزن · (2) هل smart محدّثة لآخر إصدار؟
- **رد العميل الحاسم (step 163):** المنتج اللي فضل = **P-000263** (اتعمله `ADJ-000001` تلقائي)؛ المنتج اللي اختفى = **P-000256** (مفيش ADJ). النسخة **حديثة** (مش قديمة).
- **السبب الجذري (مؤكّد):** `FinalizeCount.php` بينشئ تسوية **بس للأصناف اللي ليها فرق** (`difference != 0`). منتج رصيد النظام بتاعه 0 (مفيش صف رصيد) واتجرد صفر → فرق=0 → **مفيش تسوية + مفيش صف رصيد يتكوّن** → ومفيش صف يعني مايظهرش في "أرصدة المخزون". المنتج **مش بيتمسح/يتفلتر** — هو ببساطة معندوش صف.
- **الإصلاح المقترح (تحت موافقة العميل):** في `FinalizeCount::execute` نعمل `firstOrCreate` لصف رصيد لكل منتج بيتجرد (بكمية = رصيد النظام، حتى 0)، من غير ما نلمس منطق التسوية/التقييم. الملف: `Modules/Inventory/app/Actions/FinalizeCount.php`. (بعد التطبيق: أتأكد على CNT-000001 + CNT-000002.)
- **التحليل النهائي (step_id=165):** سؤال موافقة واحد → العميل رد **"أخذ بالتوصية"** (step 167) → `in_implementation`.
- **✅ التنفيذ (تمّ):** عدّلت `FinalizeCount::execute` → `firstOrCreate` لصف الرصيد لكل بند جرد (نمط `getOrCreateBalance`). + اختبار Pest جديد ("finalize creates a zero stock balance row…") → **18 اختبار نجحوا**. php -l/pint نضيف.
- **changelog:** بند في `[Unreleased]` (BE). **KB:** موضوع `topics/inventory-count-zero-balance-fix.md` + سطر INDEX.
- **git:** commit `17aa0e2fe` → merge origin/main (تعارض CHANGELOG اتحلّ بإبقاء بند الـlab + بندي) `d247a63fa` → **push hazemdev2 + FF merge على `main`** (origin/main=`d247a63fa`).
- **✅ verification منشور (step_id=169) → `client_review`.** ⚠️ نسخة العميل (smart) محتاجة **تحديث MoonStack القادم** عشان الإصلاح يوصلها.
- **الحالة النهائية:** `client_review` — العميل يراجع ويقفل.

## 2026-07-14 — ISS-2026-0186 جولة 3 + spinoff ISS-0192 + تذكرة جديدة ISS-0191
- **ISS-0186 جولة 3 (client_comment 3307):** العميل سأل هل فلتر الأقسام يراعي الشجرة + طلب عرض المنتج بمسار قسمه. **فحص حي: الأقسام شجرة حقيقية (690 قسم، عمق 4)**، وفلتري كان مسطّحًا. المالك قرّر: **الفلتر الشجري هنا + المسار spinoff.**
  - ✅ نُفّذ الفلتر الشجري في inventory-counts.component: buildCategoryTree يبني descendant-set لكل قسم + مسار عرض ("أب / ابن") في القائمة؛ اختيار أب = كل منتجات فروعه. build أخضر، deployed main-5KPFH6LA.js. verification 3312 → client_review.
  - ✅ **spinoff ISS-2026-0192** «عرض المنتج بمسار قسمه» → نُشر عليه عقد نطاق كامل (In/Out/Acceptance + 3 أسئلة) → awaiting_client. (منطق المسار جاهز من الفلتر، يُعاد استخدامه.)
- **🆕 ISS-2026-0191 (bug · status=new · planning_gate required):** «في أمر البيع، الضغط على إذن التسليم → خطأ: أذونات التسليم غير مفعّلة. فين أفعّلها؟». لقطة: /app/sales/orders، SO-2026-00011 مؤكَّد.
  - **إعادة إنتاج + سبب جذري (من الكود+البيانات الحيّة):** الرسالة `sales.dn_disabled` تظهر لما `sales.enable_delivery_notes`=false. الافتراضي `'true'` لكن **قيمة الشركة على moonui2 = `''` نص فارغ** → القيمة الفارغة تُقيَّم false → الميزة متوقّفة رغم الافتراضي. التفعيل: إعدادات المبيعات → مفتاح «تفعيل أذونات التسليم» (sales-settings.component.ts:99 · settings.component.ts:167). الحارس: SalesDeliveryNoteController + PostSalesInvoice.php:424.
  - **التحقيق اكتمل (problem-investigation) — عطل صنف في طبقة الإعدادات المشتركة (Core):** الكتابة `SettingsService::set` تخزّن `(string) false === ''` (boolean موقوف يُكتب فارغًا لا '0'). القراءة `get` ترجّع قيمة صف الشركة حتى لو '' (لأن الصف موجود) و`castValue('',boolean)=false` — فلا تسقط على الافتراضي. **25 صف إعداد فارغ (16 boolean) على الشركة 4؛ الوحيد افتراضه true = `sales.enable_delivery_notes`** لذا هو الوحيد المكسور (نفي الفرضية ناجح). الحارس: SalesDeliveryNoteController سطر ~137.
  - **✅ نُشر:** planning-gate comment (3326) + تحليل بعقد نطاق + 3 قرارات (3328) → **awaiting_client**. التوصية: إصلاح الطبقة المشتركة (قراءة فارغ→افتراضي + كتابة boolean '0'/'1') + migration يحذف الصفوف الفارغة + UX الزر. ⚠️ SettingsService نواة تمسّ كل الموديولات → مراجعة مستقلة إلزامية قبل النشر. حل فوري للعميل: إعدادات المبيعات ← فعّل «تفعيل أذونات التسليم».
- **ISS-0186 مقفولة ✅. ISS-0192 (المسار) نُفّذ → client_review (bundle main-7S3VXPYX.js). ISS-0191 awaiting_client.**

## 2026-07-14 — ISS-2026-0191 التنفيذ (عطل طبقة الإعدادات المشتركة) — client_review
- **العميل وافق على القرارات الثلاثة (scope_freeze) → in_implementation → نُفّذ بـ implement-plan (3 WPs).**
- **WP1 BE core** `SettingsService`: helper `isUnsetValue` — الفارغ '' لغير-النصي يُعامَل «غير مضبوط» فيُتخطّى في سلسلة fallback الأربعة → يسقط على الافتراضي. الكتابة `set()`: boolean يُخزَّن '1'/'0' عبر match (لا '' بعد الآن). 4 اختبارات RED→GREEN. النص '' يبقى مشروعًا.
- **WP2 BE** migration `2026_07_14_700000_repair_empty_setting_values`: يحذف صفوف settings value='' لغير-النصي (whereExists على value_type!='string'). نُفّذ على dev: 19→0، النصية الـ6 محفوظة، enable_delivery_notes=true.
- **WP3 FE** orders.component: زر «إذن التسليم» يختفي لما sales.enable_delivery_notes موقوف (signal يبدأ true).
- **مراجعة مستقلة: CRITICAL أُصلح** — الباك يرجّع boolean حقيقي لا نص، فمقارنتي `=== 'true'` كانت دائمًا false (الزر يختفي للجميع). الحل: فحص متين `v===true||1||'1'||'true'`. **درس: getByKey يرجّع القيمة مُحوّلة (boolean حقيقي لـboolean-typed) — لا تقارن بـ'true' نصًّا. نمط `=== 'true'` منتشر في ~10 مكوّنات = تضارب عقد FE/BE منهجي pre-existing.** النواة أُقرّت سليمة (0 blocking). 56 اختبار settings صفر regression.
- **deployed main-N5LGJDEU.js. verification 3337 → client_review.**

## 2026-07-15 — ISS-2026-0193 (feature · feature_gate required · "مشكلة في إذن التسليم") — awaiting_client
- **سؤال العميل:** أمر بيع → إذن تسليم، المفروض يعمل إذن صرف؟ لأن التسليم لا يُنشئ إذن صرف والأرصدة لا تتأثر. تأكيد الفلو الصحيح.
- **KB مباشر:** plans/sales-partial-issue-accounting.html + sales-partial-issue-fix-plan.html (المسار أ = خصم عند الفاتورة، ب = عند التسليم؛ إعدادات stock_deduction_point / cogs_recognition_point / auto_create/approve_stock_issue_on_invoice).
- **إعدادات حيّة company 4:** stock_deduction_point=invoice · cogs_recognition_point=delivery · auto_create_stock_issue_on_invoice=true · auto_approve=false.
- **تتبّع كود كامل (وكيل opus):** (1) **إذن التسليم لا يُنشئ inventory_issue في أي إعداد** — لوجستي؛ `ConfirmDeliveryNote` يخصم مباشرة عبر StockService فقط لو stock_deduction_point=delivery (مش عندك). (2) إذن الصرف مصدره الفاتورة: `PostSalesInvoice::handleAutoStockIssue` ينشئه **Draft**، وفي وضع delivery `force=true` **يتجاوز auto-approve عمدًا** (سطر 396-398). (3) **cogs_recognition_point=delivery يتجاوز stock_deduction_point=invoice** → لا الفاتورة ولا التسليم يحرّك مخزونًا تلقائيًا. (4) المخزون يُخصَم **فقط عند اعتماد إذن الصرف المسودة** يدويًا (`ApproveIssue::decreaseStock`) وقتها COGS+المخزون معًا (`PostSaleCogsOnIssueApproved`). (5) المسودة **ظاهرة** في أذون الصرف (index لا يفلتر status افتراضيًا). **intended-by-config مش bug، لكن تناقض إعدادات صامت.**
- **✅ نُشر تحليل بعقد نطاق + 3 قرارات (step 3366) → awaiting_client.** القرار الرئيسي: نموذج خصم (1 تلقائي عند الفاتورة / 2 اعتماد أمين المخزن — الحالي) + إصلاح تناقض الإعدادين + ربط الفاتورة بإذن الصرف المسودة. **الأرجح config لا كود.**
- **مرجعيات:** PostSalesInvoice.php 60-94/334-406 · ConfirmDeliveryNote.php 48-52 · ApproveIssue.php 227 · PostSaleCogsOnIssueApproved.php · InventoryIssueController index 70-106.

## 2026-07-15 — ISS-2026-0201 (improvement بحجم feature · "إضافة ملفات PDF للمنتج") — awaiting_client
- **الطلب:** إضافة ملفات PDF/مواصفات للمنتج + عرضها (إضافة/تعديل فردي + جماعي) + مراجعة عامة للفورم.
- **تصنيف: feature** (قدرة جديدة + FE/BE). feature_gate=false لكن حُلّل بعقد نطاق. skill: implement-research (وكيلا opus BE+FE).
- **اكتشاف حاسم (يقلّص الفيتشر جدًا):** نظام مرفقات **polymorphic جاهز** — `Core\Attachment` + trait `HasAttachments` والمنتج **مربوط بالفعل**. endpoint عام `/core/attachments` (رفع/قائمة/تنزيل/حذف، أي ملف حتى 10MB، بلا قيد mime). `attachment.service.ts` موجود. **تبويب «التفاصيل» في فورم المنتج يرفع صور المنتج أصلًا عبر نفس الخدمة** scoped على المنتج — القيد الوحيد `accept="image/*"`. فالفيتشر = تعميم آلية المعرض لتقبل PDF + قائمة ملفات بأيقونات + عرض/تنزيل/حذف.
- **BE بسيط:** إضافة attachments لـ`ProductController::show` with() + ProductResource؛ صلاحيات `core.attachments.*` تُمنح لأدوار المنتج. **احتكاك عرض PDF:** الملفات على قرص خاص تُخدَم بتنزيل إجباري — لا رابط عام؛ العرض عبر blob + window.open (سابقة موجودة: products.component previewImage + result-file-cell). سابقة قائمة: features/attachments.component (getFileIcon يخطّط pdf→أيقونة).
- **الجماعي خارج النطاق للملفات** (لا id للمنتج وقت الإدخال — أكّده الوكيلان). ملاحظة صغيرة: getImageUrlAttribute يبني رابط قرص عام لمرفق على قرص خاص → قد لا تظهر (سُجّلت خارج النطاق).
- **✅ نُشر تحليل بعقد نطاق + معاينة UI (تبويب المرفقات) + 3 قرارات (step 3429) → awaiting_client.** القرارات: مكان (تبويب مستقل مُوصى) · عرض PDF (تبويب جديد مُوصى) · الأنواع (PDF+Office+صور مُوصى).
- **مرجعيات:** AttachmentController (Core routes 87-90) · Product.php:21 (HasAttachments) · products.component.ts (tab Details الصور 749-812، getAttachmentUrl 826) · attachments.component (getFileIcon 184) · result-file-cell (window.open PDF 117-134).

## 2026-07-15 — ISS-2026-0201 التنفيذ (إرفاق ملفات للمنتج) — client_review
- **العميل وافق (scope_freeze) + قيد مهم: «من وقت الإضافة مش التعديل».** نُفّذ بـ implement-plan.
- **فيتشر FE بحت** (WP1 BE غير مطلوب — الواجهة تسرد عبر endpoint المرفقات العام، صلاحيات core.attachments.* ممنوحة أصلًا لأن المعرض يعمل).
- **WP2 FE** products.component: تبويب رابع «المرفقات» (يظهر إضافة+تعديل). **رفع مؤجَّل** لحل «من وقت الإضافة»: الملفات تُخزَّن في signal `pendingAttachments` وتُرفَع عبر `uploadPendingAttachments(productId)` مُدرَجة في `priceRequests` forkJoin بعد تأكّد productId. عرض=blob+window.open، تنزيل=a[download]، حذف=service. المعرض بقى يُفلتَر image/* فقط. أنواع PDF+Office+صور، 10MB. i18n ثنائي.
- **مراجعة مستقلة: HIGH + MEDIUM أُصلحا** — (HIGH) `loadProductAttachments` كان يسرد كل المرفقات فتتكرّر الصور في التبويب وحذفها يفسد المعرض → فلتر عكسي (!image). (MEDIUM) فشل رفع كان يلوم الأسعار → كل رفع يلتقط خطأه بـcatchError ويُظهر رسالة خاصة تسمّي الملف. **درس: أي قائمة تشترك في نفس مخزن المرفقات لازم تُفلتَر بالنوع (المعرض=صور، المرفقات=مستندات).** 0 CRITICAL.
- **deployed main-7D7RENUL.js. verification 3438 → client_review.**
- **مرجعيات:** products.component.ts (attachments methods ~820-900، onSaveEdit hook priceRequests، loadProductGallery فلتر image) · .html (tab value=3) · attachment.service.ts (موجود).

## 2026-07-15 — ISS-2026-9103 (improvement · "امر الانتاج") — new (بانتظار توضيح العميل)
- **الوصف فارغ** والعنوان فقط «أمر الإنتاج». **صفحة تفاصيل التذكرة ترجّع HTTP 500 (Server Error) باستمرار** على المنصّة (`GET /api/issues/ISS-2026-9103`) — ref غير معتاد (9103). القراءة معطّلة لكن **الكتابة (comment) تعمل**.
- **الـ500 كان مؤقتًا** — التفاصيل رجعت والوصف ظهر: «عند الضغط على صرف من أمر إنتاج هل يعمل إذن صرف من المخزن، أم ينتظر موافقة المخزن ويمكن التعديل؟ إجابة موسّعة». (نشرت comment مبكّر يطلب توضيح step 3456 قبل ظهور الوصف.)
- **تتبّع الكود (KB production.md + IssueMaterials/CancelMaterialIssue):** «صرف» = `IssueMaterials` ينشئ **InventoryIssue مسودة** ثم **يعتمدها فورًا في نفس الـtransaction** → يخصم المخزون مباشرة → `MfgMaterialIssue` (batch genealogy) → أول صرف يحوّل الأمر in_process → قيد مدين WIP/دائن مخزون. **لا موافقة مخزن** (الإنتاج غير مربوط بمحرك الاعتمادات — فجوة معمارية معروفة KB سطر 124). قابل للعكس بـ`CancelMaterialIssue` (يعيد المخزون + يعكس القيد؛ ممنوع بعد استلام FG). لا إعداد auto_approve — الاعتماد دائم فوري.
- **✅ نشرت تحليل موسّع + سؤال قرار (step 3458) → awaiting_client:** إبقاء الفوري (الحالي) أم إضافة خطوة موافقة مخزن (=فيتشر: ربط الاعتماد بالإنتاج).
- **ISS-0201 لسه client_review (لم تُقفَل).**

- **جولة 2 (client_answer 3460, «رأي آخر» → analysis):** العميل حوّل السؤال لـ**طلب فيتشر**: (1) تحديد مخزن لصرف الخامات (2) إعداد: مباشر بلا موافقة / أو يتطلّب موافقة مخزن (3) في الحالتين يُنشأ إذن صرف يحمل بيانات أمر الإنتاج (4) لو موافقة → ينتظر، وأمين المخزن يعدّل/يحذف/يزيد الكميات وينعكس على الأمر. **فيتشر [FIN] متعدد المراحل** يمسّ WIP/GL ويتقاطع مع فجوة «الإنتاج غير مربوط بالاعتمادات».
- **حقائق كود مؤسِّسة:** IssueMaterials ينشئ InventoryIssue مرجعه production_order على `source_warehouse_id` (يُحدَّد عند الإصدار) ثم يعتمد فورًا. فالبنية موجودة؛ التطوير = احتجاز الإذن للمراجعة عند تفعيل الموافقة + انعكاس التعديل على consumed_quantity.
- **✅ إعادة تحليل بعقد نطاق مرحلي (4 مراحل: إعداد+احتجاز · تعديل ينعكس · بيانات الأمر في الإذن · اختيار مخزن) + 3 قرارات (المخزن · صرف جزئي · موافقة مفردة) → step 3465 → awaiting_client.** الإعداد مطفأ=السلوك الحالي (لا انحدار).

## 2026-07-15 — ISS-2026-9103 التنفيذ بدأ (فيتشر [FIN] كبير) — in_implementation
- **العميل وافق + المالك «ابدأ بالمراحل».** خطة كاملة + تتبّع كود + مخاطر في ledger: `knowledge-base/plans/production-material-issue-approval/LEDGER.md`.
- **✅ WP1a:** إعداد `production.material_issue_requires_approval` (boolean default false) في ProductionSettingDefinitionSeeder + seeded على dev (يقرأ false = السلوك الحالي).
- **⬜ WP1b (القلب [FIN] الكبير، لم يبدأ):** نقل IssueMaterials.php:118-262 (MfgMaterialIssue + ownership legs + toll/consignment + batch/lot allocation + reservation + consumed + WIP + status flip + MaterialIssued) لـlistener جديد `ApplyMaterialIssueOnApproval` على InventoryIssueApproved (guard reference_type=ProductionOrder + idempotent)، **rekeyed لـissued_quantity الفعلي**. IssueMaterials: OFF→approve inline (listener يشتغل)؛ ON→مسودة فقط. **الثابت: 21 اختبار صرف خامات يفضلوا خضر.**
- **باقي المراحل:** WP2 انعكاس تعديل المخزن · WP3 بيانات الأمر في الإذن · WP4 اختيار المخزن.
- **⚠️ FE tree غير محسوم:** الوكيل قال production FE تحت `/home/moonui2/www/moon-erp` لكن كل الجلسة FE في `/home/moonui2/public_html/moon-erp`. لازم تحقّق قبل WP3/WP4.
- **⚠️ تذكير مالك: فابل للطوارئ فقط** (تكلفة توكن عالية) → بوابة [FIN] بـcode-reviewer/Codex لا فابل. [[use-fable-for-design]].
- **RESUME:** LEDGER RESUME POINT. baseline 21 pass. لا تبدأ WP1b إلا بجلسة مركّزة (منطق مالي دقيق، TDD + مراجعة مستقلة لكل خطوة).

## 2026-07-15 (تحديث) — ISS-2026-9103 WP1a+WP1b+WP2 ✅ (BE، منطق مالي)
- **✅ WP1b منفّذ + مُحصَّن + committed (BE 308be2e37).** استخراج applyEffects + بوابة الإعداد (يدوي فقط، backflush معفى) + listener ApplyMaterialIssueOnApproval على InventoryIssueApproved (guards: reference_type + $applyingInline static + setting ON + idempotent + throw لو الأمر غير قابل للصرف) + rekeying على issued_quantity + unique index migration.
- **مراجعة مستقلة [FIN] (code-reviewer، لا فابل) أمسكت CRITICAL فاتني:** execute بيتنادى من backflush كمان → بوابتي كانت هتخلّي backflush no-op صامت. **أُصلح** بـrequireApprovalGate=false للbackflush + علم $applyingInline يخلّي الـlistener يتخطّى المسارات الـinline. + HIGH (stale order → الـlistener يرمي فتُلغى حركة المخزون) + TOCTOU (applyEffects idempotent) + unique index. **درس: [FIN] لازم مراجعة مستقلة — دايمًا تتبّع كل callers للـaction.**
- **✅ WP2 مُنجَز بتصميم WP1b:** تعديل المخزن للمسودة ينعكس على الأمر تلقائيًا (الـlistener يبني resolved من issued_quantity). اختبار جزئي: 20→8 → مستهلك 8، WIP 40، مخزون 92.
- **~85 اختبار إنتاج أخضر** (الثابت OFF محفوظ byte-for-byte) + اختبارات ON/backflush-ON/partial جديدة.
- **باقي: WP3 (بيانات الأمر في شاشة أذون الصرف) + WP4 (اختيار مخزن عند الصرف) — FE، محجوبة على تحقّق شجرة الـFE.** الإعداد مش مكشوف في FE settings لسه (يُفعَّل عبر API). CHANGELOG مؤجَّل حتى FE.
- **ledger:** knowledge-base/plans/production-material-issue-approval/LEDGER.md (RESUME POINT).

## 2026-07-15 (تحديث) — ISS-2026-9103 كل المراحل WP1-4 ✅ (منطق مالي، لم يُدمَج main)
- **شجرة FE محسومة:** `www/moon-erp` = symlink → `public_html/moon-erp` (شجرة واحدة).
- **✅ WP3:** InventoryIssueResource.reference_number (soft lookup لرقم أمر الإنتاج) + شاشة أذون الصرف تعرضه.
- **✅ WP4:** execute(...,?int $warehouseId) — الصرف من مخزن مختار (افتراضي مصدر الأمر)؛ اللوطات من مخزن الإذن، وتحرير الحجز على مخزن الأمر المصدر. + قائمة مخزن في ديالوج الصرف FE. اختبار: صرف من wh2 → 50→30، المصدر سليم.
- **63 اختبار أخضر** (production+inventory، الثابت OFF محفوظ). deployed main-ICERRXCO.js. commits BE 308be2e37+2e6939211، FE 193eafad0. **مش على main — بانتظار /fullpush.**
- **متابعة صغيرة (خارج WP1-4):** كشف الإعداد في شاشة إعدادات FE (لا توجد شاشة إعدادات إنتاج حاليًا؛ يُفعَّل عبر API).
- ledger كامل: knowledge-base/plans/production-material-issue-approval/LEDGER.md.

## 2026-07-15 (تحديث) — ISS-2026-9103 → client_review (verification منشور)
- نُشِر verification (step 3507) بيوصف: الإعداد OFF=بلا تغيير / ON=مسودة تنتظر أمين المخزن، اختيار المخزن لكل صرف (default=source، overridable)، رقم أمر الإنتاج ظاهر في أذون الصرف، صرف جزئي. **63 اختبار أخضر + مراجعة [FIN] مستقلة**.
- الحالة الآن: **client_review** — مستنّي اختبار العميل ورده.
- ⚠️ الكود committed على hazemdev2 فقط (BE 308be2e37+2e6939211، FE 193eafad0) — **مش على main**، بانتظار /fullpush + شحنة MoonStack (خطوة المالك).
- باقي تذاكر 283 كلها closed (0004/0148/0185/0186/0191/0192/0193/0201).

## 2026-07-15 — ISS-2026-9104 (improvement · «الوحدات القياس») — awaiting_client
- **الطلب:** تحليل تفصيلي لوحدات القياس عبر كل البرنامج + كشف المشاكل (هل موجود/مكتمل/يُستخدم صح؟).
- **routing:** implement-research Phase 1 (بحث بلا كود) — 3 وكلاء opus (Core+Products / Sales+Purchases+POS / Inventory+Production+Reports+LIS).
- **الحُكم (بالأدلة):** نظام وحدات **موجود كبنية بيانات + شاشات إدخال كاملة** (unit_groups/units[conversion_factor,is_base]/product_units[باركود+سعر لكل وحدة] + Product.base_unit_id + FE forms)، **لكن التحويل الفعلي غير موجود إطلاقًا** — conversion_factor بيانات ميتة. الرصيد وحركات المخزون **بلا عمود وحدة**؛ StockService بلا بارامتر وحدة؛ كل المعاملات تخصم/تضيف كمية خام → رصيد/تكلفة/تسعير غلط عند اختيار وحدة غير الأساس. فحص التوافر يقارن عبر وحدات مختلفة (ApproveIssue:200)؛ FIFO/الحجز/MRP بلا وحدة؛ POS يتجاهل باركود الوحدة؛ dropdown السطر يعرض كل وحدات الشركة؛ تحقق ضعيف؛ لا seeding؛ LIS lab_units منفصل (ازدواج).
- **التقرير:** knowledge-base/plans/uom-units-of-measure-analysis.html (dark house style، 9 أقسام + UI preview). عقد نطاق: WP1 محرك تحويل نقطة-واحدة → WP2 توافر/FIFO/حجز/MRP → WP3 واجهة السطر → WP4 تسعير+باركود كاشير → WP5 عرض+seeding+تحقق. خلف إعداد enable_unit_conversion افتراضي OFF (صفر انحدار). [FIN] مخزون/تكلفة. مؤجل: توحيد LIS + per-lot.
- **4 أسئلة منشورة** (مستوى الطموح / OFF افتراضي / التسعير / تصحيح البيانات القائمة). **الحالة: awaiting_client — لا كود قبل الموافقة.**

## 2026-07-15 — ISS-2026-9105 (bug · planning_gate · «مشكلة أمر الإنتاج PRD-2026-00022») — awaiting_client
- **العرض:** العميل بيجرّب الصرف الجزئي؛ (1) «مش لاقي مكان أصرف المتبقّي»؛ (2) عايز يصرف أكتر من المطلوب وبيتمنع.
- **routing:** problem-investigation Phase 1 (تشخيص بلا كود) — 2 وكيل opus (BE + FE) + استنساخ read-only على moonui2_dev_be.
- **السبب الجذري (يوحّد المشكلتين، بالأدلة):** PRD-00022 status=**completed** بينما مادة «خام بودرة ميلامين» consumed 5.000 < planned 12.360 (متبقّي 7.360). complete() (ProductionOrderController.php:314-329) يفحص canComplete() الحالة فقط — **لا حارس يمنع الاكتمال بمواد ناقصة الصرف**. وcanIssue() (ProductionOrderStatus.php:74-77) + FE canIssueMaterials() (production-orders.component.ts:1329-1330) تستثني completed → زر الصرف يختفي → مفيش مكان للصرف (لا متبقّي ولا زائد).
- **حقائق:** re-issue المتبقّي **مدعوم** (consumed يتراكم IssueMaterials.php:300-303؛ canIssue=true في in_process؛ FE يعرض planned/consumed/remaining ويملأ بالمتبقّي). الصرف الزائد **غير مقيَّد بقاعدة إنتاج** (لا max في :599/resolveLines:470)؛ يُرفض فقط بـinsufficient_stock (ApproveIssue.php:200-207). القدرة موجودة — البق = بوابة دورة الحياة + اكتمال مبكّر.
- **الحل الموصى (A+C):** WP1 حارس تأكيد عند الاكتمال بمواد ناقصة · WP2 «إعادة فتح للصرف» Completed→InProcess [FIN WIP] · WP3 توضيح الصرف الزائد + رسالة نقص الرصيد.
- **التقرير:** knowledge-base/plans/production-partial-issue-completed-investigation.html (8 أقسام). **3 أسئلة منشورة. الحالة: awaiting_client — لا كود قبل الموافقة.**

## 2026-07-15 (تحديث) — ISS-2026-9105 إعادة تحليل (ملاحظة العميل) — awaiting_client
- العميل وافق على التوصيات الـ3 (take_recommendation ×3) بس أضاف متطلب: الصرف الزائد طبيعي (هدر→انحراف تكلفة) فالصرف حتى المطلوب حرّ، **لكن التجاوز فوق المطلوب لازم دورة موافقة قبل الإتاحة**.
- الخطة المعدّلة (A+C+D): WP1 حارس اكتمال · WP2 إعادة فتح · WP3 صرف حرّ حتى المطلوب · **WP4 دورة موافقة للتجاوز فوق planned** (إعداد production.over_issue_requires_approval افتراضي ON + صلاحية production.orders.approve_over_issue مسؤول إنتاج + سبب إلزامي → cost_variance). طبقة مستقلة عن موافقة أمين المخزن (9103). [FIN].
- التقرير محدَّث. 3 أسئلة جديدة (مَن يعتمد/تفعيل ON/سبب إلزامي). **الحالة: awaiting_client.**

## 2026-07-15 (تحديث) — ISS-2026-9105 scope_freeze → in_implementation
- العميل وافق على الأسئلة الـ3 (take_recommendation). ملاحظة على المعتمِد: **صلاحية منفصلة** يمكن منحها لمسؤول إنتاج/المالك/المحاسب (مرن عبر نظام الصلاحيات).
- **النطاق مثبّت (frozen، step 3629).** الخطة النهائية A+C+D: WP1 حارس اكتمال بمواد ناقصة · WP2 إعادة فتح Completed→InProcess · WP3 صرف حرّ حتى المطلوب + وضوح المتبقّي + رسالة نقص رصيد · WP4 [FIN] دورة موافقة للتجاوز فوق planned (إعداد over_issue_requires_approval افتراضي ON + صلاحية منفصلة production.orders.approve_over_issue + سبب إلزامي → cost_variance).
- **بدأ التنفيذ عبر implement-plan على hazemdev2.** يُبنى فوق كود 9103 الموجود على الفرع. لا merge/main (يتأجل لـ/fullpush المالك).

## 2026-07-16 — ISS-2026-9105 ✅ منفّذ بالكامل (4 WPs) — جاهز لنشر verification
- WP1 حارس تأكيد الاكتمال بمواد ناقصة (BE 7f89b82e2+51cd9e1ab / FE 8efc19eca+f6c5d3a46) · WP2 إعادة فتح للصرف Completed→InProcess (BE 2482d13a5 / FE 6ac1c78bc) · WP3 وضوح زر الصرف+رسالة نقص الرصيد (FE b726b7458) · WP4 [FIN] دورة موافقة الصرف الزائد (BE 1e6472e3c+57f297342 / FE 882e5c0d7).
- مراجعات: WP1 HIGH(produced_qty race) مُصلَح · WP2 [FIN] APPROVE (استحالة double-post) · WP4 architect design gate GO-WITH-CHANGES + [FIN] review 2 HIGH مُصلَحين (per-material aggregate consume + keeper-path tests) · cross-WP final APPROVE.
- اختبارات: 54 passed (331 assertions)، صفر regression. migration production_over_issue_approvals على dev + seeding. منشور على /app (bundle main-IYVCLUUB.js).
- الخطة/الليدجر: knowledge-base/plans/production-partial-issue-and-overissue-approval/ (LEDGER+RESUME+tasks).
- ⚠️ على hazemdev2 فقط — **مش على main** (بانتظار /fullpush المالك). Deferrals: pre-existing consumed_quantity lost-update على أسطر نفس المادة (bug إنتاج منفصل)، + 2 low.

## 2026-07-16 — أولوية: تنفيذ ISS-2026-9104 (وحدات القياس) [المالك اختار]
- ISS-9103 **closed** ✅. ISS-9105 رجعت in_implementation بملاحظات عميل (شاشة منفصلة لموافقات الصرف الزائد + زر في المخازن + ظهورها في إذن الصرف + سؤال عن إعداد موافقة المخزن production.material_issue_requires_approval — مفيش شاشة إعدادات إنتاج في FE) → **مؤجّلة، نرجعلها بعد 9104**.
- **ISS-9104 scope_freeze (step 3634).** إجابات العميل: Q1 المحرك الكامل مرحليًا (take rec) · **Q2 «رأي آخر»: متاح افتراضيًا مش OFF — كل العملاء عندهم وحدات** · Q3 سعر الوحدة ثم أساس×معامل (take rec) · Q4 منع الجديد فقط بلا backfill (take rec).
- ⚠️ تعديل على التصميم: التحويل **متاح افتراضيًا** (مش gated OFF). الأمان: التحويل identity للصنف أحادي الوحدة (base فقط، معامل 1) → صفر تغيير للأصناف بلا وحدات بديلة، والتفعيل يصلّح الأصناف متعددة الوحدات.
- التنفيذ عبر implement-plan، المصدر: knowledge-base/plans/uom-units-of-measure-analysis.html (WP1 محرك تحويل+تحويل عند حدّ المخزون · WP2 توافر/FIFO/حجز/MRP بالأساس · WP3 واجهة السطر · WP4 تسعير+باركود · WP5 عرض+seeding+تحقق). [FIN] مخزون/تكلفة. على hazemdev2، مش main.

## 2026-07-17 — ISS-2026-9104 (وحدات القياس) ✅ منفّذ بالكامل (6 WPs) — جاهز لـverification
- محرك تحويل الوحدات: صنف بوحدة أساس + وحدات بديلة (كرتونة=12كجم) بيتحوّل صح للمخزون/التكلفة/التوافر/FIFO/التسعير. متاح افتراضيًا (قرار العميل)، آمن بثابت identity (صنف أحادي الوحدة = بايت-بايت زي ما هو).
- WP0 UnitConversionService a0d0992 · WP1 StockService dormant 3ae98beba · WP2 activation b51a12189 (+remaining_value migration) · WP3 FE dropdown a3716a7de · WP4 pricing+POS 34d073315/1281db67e + e6f2ae173/04ebc6fa6 · WP5 display+seed+validation f171c239f/c91daf977.
- مراجعات [FIN] لكل WP + cross-WP نهائي APPROVE. **صفر failures جديدة** (كل الفشل pre-existing — أُكِّد بإرجاع Modules/Core لـpre-9104). migrations على dev + DefaultUnitSeeder. منشور على /app.
- الخطة/الليدجر: knowledge-base/plans/uom-unit-conversion-engine/ (LEDGER+RESUME+tasks).
- **مؤجّلات (في التذكرة):** (1) حجز/تجهيز الإنتاج بوحدات بديلة (الاستهلاك بيتحوّل عبر ApproveIssue) · (2) [FIN] بقايا قيمة على رجل العكس ≈0.0005×base_qty/leg (الأمامي دقيق) · (3) باركود POS أوفلاين · (4) minor WP5.
- ⚠️ على hazemdev2 فقط — **مش على main** (بانتظار /fullpush). 
- تنبيه: تلات تذاكر production مفتوحة/مؤجلة: ISS-9105 (صرف جزئي، client_review→؟) + مؤجل حجز الإنتاج من 9104.

## 2026-07-17 — تحليلات منشورة على 4 تذاكر جديدة (كلها awaiting_client)
- **9124 (bug):** التصنيفات فاضية في فورم المنتج — السبب: dropdown الفورم يقرأ NgRx store (categories$) بلا dispatch للتحميل؛ الكومبوننت بيحمّل في allCategories المحلي (الفلتر شغّال). الحل: توحيد المصدر (سطرين FE). تحليل + سؤال منشور.
- **9121 (فاتورة→إذن صرف):** المحرك موجود (sales.auto_create_stock_issue_on_invoice + auto_approve_stock_issue_on_invoice=off = مسودة تنتظر أمين المخزن). الناقص: بادج «غير مصروف» على الفاتورة مربوط بحالة إذن الصرف + خانة توقيع في طباعة إذن التسليم. تحليل + 3 أسئلة.
- **9123 (خصومات مركّبة):** حاليًا خصم سطر واحد؛ لا نسب عميل ولا مركّب. الناقص: نسب على كارت العميل + محرك مركّب يحفظ الخطوات + عرض 3 سطور. لبس في مثال العميل عن أساس المركّب — سؤال. تحليل + 5 أسئلة.
- **9122 (مندوب+عمولات) — الأكبر:** موجود: حقل المندوب + محرك عمولات (tracking) + تتبّع تحصيل + تقارير جزئية. الناقص: تقسيم العمولة (إنشاء+تحصيل) · تارجت · تحويل عرض→فاتورة مسودة (زر بيعمل 404) · تقرير مندوب موحّد+aging بالمندوب · دور مندوب مقيّد للموبايل. تحليل + 4 أسئلة + خطة 6 WP.
- الأبحاث تمّت بـ3 وكلاء opus متوازيين. كل التحليلات في scratchpad + منشورة. **مستنّي ردود العميل قبل أي كود.**

## 2026-07-17 — ISS-2026-9124 ✅ (تصنيفات فورم المنتج) → client_review
- الحل: dropdown التصنيف في products.component.html (سطر 324 فورم + 859 bulk) بقى يقرأ allCategories (المحمّل) بدل categories$ (store فاضي). FE b5857b656 + BE 388d937ea (changelog). منشور /app main-AVZCDV2T.js.

## 2026-07-17 — ISS-2026-9105 رد عميل جديد (لسه مفتوح، بعد تنفيذ الـ4 بنود)
- 3 ملاحظات أعمق: (1) الصرف الزائد لسه بيظهر جوه نافذة صرف الإنتاج العادية (عايزه في الشاشة المنفصلة بس؟) (2) الموافقة على الصرف الزائد **ما بتولّدش إذن صرف** — المفروض بعد الموافقة يتحوّل لإذن صرف يحتاج موافقة أمين المخزن (3) سلوك رفض أمين المخزن/الصرف الجزئي. **محتاج معالجة بعد 9123.**

## 2026-07-17 — ISS-2026-9123 بدأ التنفيذ (خصومات مركّبة) — scope frozen
- القرارات: 3 مستويات ثابتة · مركّب على الرصيد المتبقّي total×(1−خ1)×(1−خ2)×(1−خ3) · نسب افتراضية على كارت العميل قابلة للتعديل بالفاتورة · على مستوى إجمالي الفاتورة (header) · إيراد صافي (زي الحالي، بلا تغيير GL).
- الحالي: خصم سطر واحد فقط؛ business_partners بلا نسب خصم؛ العرض/PDF سطر خصم واحد.

## 2026-07-17 — ISS-2026-9123 ✅ (خصومات مركّبة) → client_review
- WP1 BE d27b0cb5d (محرك مركّب header-level على الرصيد المتبقّي + نسب على business_partners + تخزين levels + ضريبة على الصافي + إيراد صافي، JE متوازن) + fix 84cec4df5 (commission base نيت الـheader + print يعرض الخصومات). WP2/3 FE 2a33eb5ac (نسب العميل + seed بالفاتورة + preview حيّ + 3 سطور view/PDF). [FIN] review APPROVE. منشور main-OZXPCWDA.js.

## 2026-07-17 — ISS-2026-9125 (عميل ومورد) — awaiting_client
- الطلب: فصل «الشركاء» لرابطين «العملاء»/«الموردين»، كل رابط يفتح الشاشة والدور مختار + الإضافة تحدّد الدور تلقائيًا.
- الموجود: partner.is_customer/is_supplier + فلتر دور شغّال + الشاشة تقرأ queryParams (viewId فقط). الناقص: قراءة ?role= → تفعيل الفلتر + عنوان + الإضافة تحدّد الدور + رابطين بالقائمة. FE فقط صغير.
- تحليل + سؤال (نسيب «الشركاء» ونضيف الاتنين، ولا نستبدل؟) منشور.

## 2026-07-17 — ISS-2026-9125 ✅ (عميل ومورد) → client_review
- روابط منفصلة: /core/customers + /core/suppliers (route data.role) → PartnersComponent يقرأ role من route.data → activeRoleFilter + pageTitleKey() + openNew يحدّد is_customer/is_supplier. سيبنا /core/partners للكل. nav + i18n (NAV.CUSTOMERS/SUPPLIERS, PARTNERS.CUSTOMERS_TITLE/SUPPLIERS_TITLE). FE 41e5fecdf + BE f24418922 (changelog). منشور main-U2PLLYJA.js.

## 2026-07-17 — ISS-2026-9124 تحسين (شجرة التصنيف) → client_review
- flatten في products.component بقى يبني display_name = "الأصل › الفرع"، والـ3 dropdowns بقوا optionLabel=display_name. FE 1ea4c14b3، منشور main-RYGUGIUH.js.

## 2026-07-17 — ISS-2026-9121 بدأ التنفيذ (فاتورة→إذن صرف) — scope frozen (أخذ بالتوصية ×3)
- القرارات: (1) استخدام الإعدادين الموجودين auto_create_stock_issue_on_invoice=on + auto_approve_stock_issue_on_invoice=off (بلا toggle جديد) — نظهرهم في مجموعة «التسليم» بشاشة الإعدادات · (2) «غير مصروف» من حالة إذن الصرف (InventoryIssue draft) — نعرضها على الفاتورة كبادج · (3) إذن التسليم يتطبع بخانة توقيع (اسم المستلم + توقيع + تاريخ + ختم).

## 2026-07-17 — ISS-2026-9121 ✅ (فاتورة→إذن صرف) → client_review
- الإعدادين في مجموعة delivery (sales-settings groupMap) · SalesInvoice.stockIssue HasOne (reference_type=Sale, latestOfMany, بلا N+1) → stock_issue_status (null/not_issued/issued) → بادج على الفاتورة · خانة توقيع في delivery-note-print.config. BE 1766b6b80 + FE 92478a5c3. منشور.

## 2026-07-17 — ISS-2026-9122 ✅ (مندوبين + عمولات، 5 WP) → client_review
- WP1 e6144574c (quotation→draft invoice). WP2 8ae390e1c+507dda9cb (commission split creation+collection، تارجت-غير-مربوط، claw-back، lock+unique+exact-settle) [FIN review]. WP3 50479a998 (sales_rep_targets + non-destructive earned-gate). WP4 53242d04e/565373563 (rep-performance report + targets UI). WP5 427d31ff1 + security-fix f9af33603 (sales_representative role + own-data scoping؛ security review لقّى C1 CRITICAL + H1/H2/M1/M2/M3/L2 → كلهم fixed).
- tracking فقط بلا GL. المرتجع بيخصم العمولة (سلوك جديد). deferrals: partial-return-then-later-paid entitlement · months بلا نشاط. الليدجر: knowledge-base/plans/sales-rep-commission/. منشور /app.

## ISS-2026-9122 follow-up (2026-07-18) — دور مندوب مبيعات للتجربة
- العميل رجّع التذكرة in_implementation طالبًا «دور مندوب مبيعات» للتجربة. الجذر: الدور `sales_representative` (id 33, 30 perms, global) كان مزروعًا من WP5 بس شاشة الأدوار بتعرضه عبر `ROLES.ROLE_NAMES.<name>` وبتقع على المفتاح الخام لغياب الترجمة → ظهر كـ`sales_representative` فمكانش واضح للعميل.
- الإصلاح: أضفت ترجمة `sales_representative` («مندوب مبيعات» / "Sales Representative") + باقي الأدوار الوظيفية المزروعة (production_planner/supervisor, machine_operator, warehouse_storekeeper) في ar.json+en.json. FE commit `20efa6f21` على hazemdev2. بُني ونُشر على /app (الترجمة أصل ثابت — بصمة main-*.js لم تتغير).
- يوزر تجريبي: `mandoob@moonerp.com` / `Mandoob@123`، company 4 (بلاك سيركل)، role sales_representative، id=27 (عبر tinker على moonui2_dev_be).
- نشرت تعليق تفصيلي (step 4266) + verification → التذكرة الآن client_review.
- ملاحظة: أدوار Spatie هنا teams=false (model_has_roles بلا team_id) — assignRole بالاسم يكفي؛ scoping الشركة عبر user.company_id.

## ISS-2026-9143 (2026-07-18) — من عرض سعر إلى فاتورة بيع [تحليل Phase 1]
- طلب العميل: فلو مندوب يعمل عرض سعر → المحاسب يحوّله لفاتورة بيع → الفاتورة تعمل إذن صرف. «هل ده صحيح ومقبول وإيه التعديلات؟»
- تحليل (وكيلين opus BE+FE، file:line): الفلو صحيح ومقبول ومبني ~90%. **الباك كامل**: `POST /sales/quotations/{id}/convert-to-invoice` (SalesInvoiceController@createFromQuotation:753-834) → فاتورة مسودة، ينقل العميل/المندوب/المخزن/البنود/الخصم/الضريبة، recalc، gate accepted-only + own-scope + company، perm `sales.invoices.create`. وفاتورة→إذن صرف موجود (9121، PostSalesInvoice:352/415، بادج stock_issue_status).
- **الفجوة الحقيقية:** زر «تحويل لفاتورة» **مش موجود في الواجهة** — الـplumbing (service/effect/reducer) ميت، مفيش component بينده عليه. الموجود بس «تحويل لأمر». **تصحيح:** 9122 WP1 ضاف الـendpoint في الباك فقط؛ رسالة تحقّق 9122 قالت «الزر شغّال» وده كان غير دقيق. 9143 بتكمّل الحلقة.
- **صلاحيات:** المحاسب/المدير مالهومش create/post/issues.approve افتراضيًا (المالك بس؛ cashier+sales_rep عندهم create).
- الحل: WP1 (FE) زر واحد يربط الـendpoint الموجود + يفتح المسودة؛ WP2 توزيع صلاحيات (قرار مالك)؛ WP3 اختياري one-click convert+post [FIN].
- HTML: knowledge-base/plans/quotation-to-invoice-flow-analysis.html. نشرت analysis + 4 أسئلة بتوصيات → الحالة **awaiting_client**.

## ISS-2026-9143 — التنفيذ (2026-07-18) [client أخد بكل التوصيات + منح للمحاسب/المدير]
- scope_freeze: خطوتين (تحويل→مسودة ثم ترحيل)، اعتماد الصرف يدوي لأمين المخزن، مخزن الرأس، والصلاحية تتدي للمحاسب+المدير.
- **WP1 (FE) `e0e9c49ef`:** زر «تحويل لفاتورة» في عرض السعر المقبول (gated sales.invoices.create) → confirm → convertToInvoice(id) الموجود → ينتقل لمسودة الفاتورة عبر /sales/invoices?viewId=. مفاتيح i18n: CONVERT_TO_INVOICE(_CONFIRM/_SUCCESS). code-reviewer(opus)=APPROVE صفر critical/important، 3 minor (طبّقت رسالة التوست؛ سبت نوع الخدمة لتفادي كسر compile للـeffect الميت، وdouble-submit متخفّف بالـconfirm).
- **WP2 (BE) `b63de59dd`:** منح المحاسب+المدير sales.quotations.view + sales.invoices.view/create/update/approve/post في RolePermissionSeeder (additive). **موافقة المالك في الشات مطلوبة** (الـclassifier رفض أول مرة لأن الرد جه من العميل على البورتال) → owner قال «آه امنحهم» → طُبّق على dev عبر givePermissionTo + cache clear. اعتماد إذن الصرف فضل لأمين المخزن (inventory.issues.approve بدون تغيير).
- تحقّق: 100 اختبار صلاحيات Core نجحوا (صفر جديد). CHANGELOG: حدّثت بند 9122 القديم (اللي كان بيوعد بالزر) ليعكس إن الزر بقى موجود فعلاً + مرجع 9143. بُني ونُشر main-GLED4GFT.js.

## ISS-2026-9143 bugfix (2026-07-18) — الفاتورة المحوّلة بلا مخزن تعلق
- العميل: INV-2026-00020 (محوّلة من عرض سعر) بلا مخزن → مش بتترحّل ومش بتتعدّل.
- الجذر (systematic-debugging، أدلة DB+log): عرض السعر مخزنه NULL؛ createFromQuotation بينسخ warehouse_id بلا fallback → فاتورة+بنود بلا مخزن. الصنف b12 track_inventory=1 → PostSalesInvoice:49 validateWarehouseForTrackedProducts يرمي 422. لكن FE post chain (submit→approve→post) بيقدّم الحالة لـapproved قبل الرفض → isEditable=Draft-only → عالقة. (الترحيل رجّع 422 مش 500 فمفيش ERROR في اللوج؛ أخطاء handleAutoGenerateDelivery/sequences كانت testing/sqlite = red herring.)
- الإصلاح `ee3315f30`: helper مشترك `resolveConversionWarehouseId(source, branch, company)` = source ?? setting sales.default_warehouse_id (branch-scoped) ?? Warehouse::defaultIdForBranch. مستخدم في **createFromQuotation + createFromOrder** (code-reviewer opus لقى createFromOrder سيبلنج غير مصلَّح + fallback ناقص لو الإعداد فاضي → صلّحت الاتنين). 2 اختبار regression (تحويل عرض/أمر بلا مخزن). Full SalesInvoiceApiTest 38/38.
- داتا: صلّحت INV-00020 (warehouse_id=10 على الرأس+البند) فبقت قابلة للترحيل.
- **latent follow-up (مؤجّل، موثّق):** FE post chain بيقدّم الحالة قبل فشل post → حارس أمتن مكانه approve/post (يمنع الترحيل لو بند مخزنيّ بلا مخزن، أو مايقدّمش الحالة على post فاشل). التحويل-وقت-الإنشاء بيقلّل المُحفّز مش بيقفله لو مفيش مخازن خالص.
- BE-only (مفيش FE bundle). نشرت verification → client_review. **feedback جديد من المالك:** وجّه القرارات عبر التذكرة مش الشات ([[decisions-via-ticket-not-chat]]).

## ISS-2026-9121 follow-up (2026-07-18) — طباعة إذن الصرف بتوقيع
- العميل: بيشتغلوا بإذن الصرف مباشرة (مش أمر بيع/إذن تسليم) وعايزين يطبعوه بخانة توقيع للمستلم. (9121 الأصلي حط التوقيع على إذن التسليم اللي مش بيستخدموه.)
- إذن الصرف مكانش فيه طباعة أصلاً. الحل (FE `e01d85ac0`): استخرجت buildSignatureSection لملف مشترك `shared/print/signature-section.ts` (يعيد استخدامه delivery-note + stock-issue)، عملت STOCK_ISSUE_PRINT_CONFIG (مطابق لإذن التسليم، كميات بلا أسعار)، زر طباعة في stock-issues.component (getById ثم print — القائمة مبتحمّلش البنود). خانة الاسم فاضية (المستلم يكتب بخط إيده).
- code-reviewer opus = APPROVE صفر critical؛ 5 low (طبّقت خانة الاسم الفاضية؛ الباقي cosmetic/precedent). build+deploy main-PBJKXMX7.js. CHANGELOG BE `c2864cf68`. verification → client_review.

## ISS-2026-9154 (2026-07-18) — «commission type field is required» عند إضافة قاعدة عمولة
- الجذر (systematic-debugging): FE فورم قواعد العمولة بيبعت أسماء غلط مش مطابقة للـAPI (بج قديم d3cc64001، مش من 9122): type→commission_type (سبب الـ422)، category_id→product_category_id، min_amount→min_invoice_amount (الاتنين بيضيعوا بصمت + العرض/التعديل مكسور). BE (request+resource+DB) متّسق على الأسماء الصح.
- الإصلاح (FE-only `dd2870f3e`): rename الثلاثة في الموديل (SalesCommissionRule + CreateSalesCommissionRule) + الفورم/reset/editRule/payload + HTML (formControlName + getRuleTypeLabel(rule.commission_type) + rule.min_invoice_amount). client «أخذ بالتوصية» = الثلاثة.
- verify: tinker validator — payload قديم FAILS بـ[commission_type]، جديد PASSES. build نظيف main-LNM5P5CW.js. CHANGELOG BE `c9df9d112`. verification → client_review.
- ملاحظة: SalesCommission (سجلات العمولة) لسه فيها type — مش ضمن نطاق التذكرة؛ لو ظهر بج مماثل في عرض السجلات نراجعه.

## ISS-2026-9160 (2026-07-18) — عمولة المندوب مش ظاهرة + إزاي 1%+1% [تحليل]
- الجذر (DB evidence): sales.commission_enabled ديفولت=false (SettingDefinitionSeeder:646) ومفعّلش لشركة 4 → CommissionService::calculateForInvoice بيرجّع null → ترحيل INV-21 (salesperson=28 حازم) مولّدش عمولة. sales_commissions فاضي. فيه toggle في sales-settings (مجموعة commissions). العمولة بتتولّد وقت الترحيل بس (مفيش backfill رجعي).
- الفجوة (ب): تقسيم 1%+1% = rate 2% + collection_split_percent 50% على القاعدة، بس **فورم القاعدة (commissions.component) مفيهوش حقل collection_split_percent** → مش قابل للإعداد من الشاشة. الحل: إضافة الحقل للفورم (FE). (9122 WP2 ضاف الحقل + المنطق في الباك بس مش في فورم القاعدة.)
- سيبلنج بسيط: قائمة العمولات بتقرا comm.invoice?.invoice_number لكن SalesCommissionResource بيرجّعه flat invoice_number → عمود رقم الفاتورة «-». (باقي الأعمدة rate/amount/status/salesperson تمام.)
- نشرت analysis + سؤال (تقسيم بإجمالي+نسبة تحصيل) → awaiting_client. In: إضافة حقل نسبة التحصيل + تفعيل الإعداد على dev للتجربة + إصلاح عمود رقم الفاتورة.

## ISS-2026-9160 — التنفيذ (2026-07-18) [client أخذ بالتوصية]
- WP1 (FE `f0681f140`): أضفت حقل collection_split_percent لفورم قاعدة العمولة (0-100 + hint) — موديل/form/reset/editRule/payload + i18n SALES.COLLECTION_SPLIT_PERCENT/_HINT. + إصلاح عمود رقم الفاتورة (comm.invoice_number الـflat). code-reviewer opus=APPROVE صفر critical/high/med، 1 low (fallback ميت). BE عقد مطابق (request sometimes|0-100، resource float، CommissionService:83 tracking-only، test CommissionSplitTest موجود).
- WP2 (config): فعّلت sales.commission_enabled=true لشركة 4 على dev (كان false ديفولت — ده سبب «مش لاقي عمولة»).
- شرح للعميل: العمولة تتولّد عند الترحيل بس (مفيش backfill)؛ 1%+1% = rate 2% + collection_split 50%. build+deploy main-QR3YEZVG.js. CHANGELOG BE 58ee8573a. verification → client_review.

## ISS-2026-9105 — إعادة تصميم فلو الصرف الزائد ✅ (2026-07-18) [كان معلّق بالغلط]
- **تصحيح تتبّع:** التذكرة كانت in_implementation والعميل وافق فعلاً [step 4042 «نفّذ كل الاتفاق»] — أنا غلطت وصنّفتها «منتظرة العميل» فاتأخّرت. نفّذتها بالكامل.
- الفيتشر الأصلي (over-issue approval + central screen + settings) كان مبني. الديلتا المطلوب = إعادة تصميم 3 حاجات.
- بوابة تصميم architect حلّت توتر Q1/Q2 (نموذج per-material) + أكّدت D5 (reject/partial) موجود أصلاً + مفيش schema.
- WP1 [FIN] `3b757f0c9`: partition (free lines now / over-plan held), رد 200 {issued,held}. WP2 [FIN] `77aa0aa20`: approve→auto-draft whole-qty. WP3 FE `1366dca88`+`0e0655478`: إزالة قسم الموافقة من ديالوج الصرف + استهلاك الرد. كلها opus-reviewed APPROVE، [FIN] invariants سليمة. tests 47 pass (Production) + 15 (issue-cancel/partial)، صفر جديد. build+deploy main-ZKHDRE4S.js.
- deferrals: M1 (draft مكرّر لو approve+manual re-issue — غير مالي، flagged للعميل)، WP4 cancel-revert. LEDGER: plans/over-issue-autogen-issue-redesign/.
- **مهم لـ/fullpush:** تراكم كبير على hazemdev2 (9143/9121-print/9154/9160 + 9105 الأربع commits) مش على main.

## GDN-000040 (2026-07-19) — صرف إنتاج اتوافق تلقائيًا [config مش bug]
- بلاغ مباشر (المالك): GDN-000040 (inventory_issue id49، production_order 43، شركة 4) اتصرف inline (created_by=approved_by=26 نفس الثانية) من غير موافقة أمين المخزن.
- الجذر: `production.material_issue_requires_approval` ديفولت=false (ProductionSettingDefinitionSeeder:98 + setting_definitions) وشركة 4 مالهاش صف → getBool='false' → فرع الحجز (IssueMaterials:199) يُتخطّى → inline ApproveIssue. الكود مطابق للتعريف — **مفيش bug، الإعداد كان OFF**.
- **red herring مهم:** `inventory.auto_approve_issues=true` **مالوش أي تأثير على صرف الإنتاج** — بيُستخدم في PostSalesInvoice:416 (المبيعات) + InventoryReceiptController بس. بوابة حجز الإنتاج بتقرا `production.material_issue_requires_approval` **فقط**.
- الفيتشر مش من مسار الصرف الزائد (9105) — مفيش over-issue approval للأمر 43؛ صرف عادي.
- الحل: فعّلت production.material_issue_requires_approval=true لشركة 4 على dev (toggle موجود في شاشة إعدادات الإنتاج production-settings.component). GDN-000040 نفسه مصروف بالفعل (يتلغى لو عايز يرجّعه). الصرف الجديد هيتحجز مسودة لأمين المخزن.

## ISS-2026-9165 — تقرير المصروف على أمر إنتاج ✅ (2026-07-19) [Opus نظّم]
- تقرير في تقارير المخازن: أمر إنتاج → لكل خامة (مخطّط/مصروف/أذون صرف/تاريخ) + تصدير + صلاحية إخفاء التكلفة (client «أخذ بالتوصية + صلاحية إخفاء التكلفة»).
- WP1 BE `78880d6da`: endpoint inventory/reports/production-order-issues + permission inventory.reports.production_issue_cost (omit cost لغير الحاملين + can_view_cost flag)؛ 19 tests؛ review APPROVE (عزل شركة + بوابة تكلفة سليمة). seeded على dev.
- WP2 FE `91d750c5e`: تبويب في inventory-reports + picker + cost cols gated on can_view_cost + Excel/PDF + i18n؛ review APPROVE (إخفاء التكلفة محكم). build+deploy main-2BCW4LJR.js.
- **Codex flow:** المالك طلب Opus-ينظّم/Codex-ينفّذ. جرّبت Codex — 3 حواجز بيئة: userns=0 (اتحل+اتثبّت دائم /etc/sysctl.d/99-userns-codex.conf) → git-root (public_html مش repo؛ حل=companion task --cwd repo) → **cPanel/CageFS FS-traversal (public_html 750 + CageFS) = غير قابل للتشغيل live**. النتيجة: opus sub-agents هو المنفّذ الموثوق هنا؛ Codex محتاج جلسة بنية تحتية بموافقة المالك. تفاصيل في memory opus-organizes-codex-implements.
- verification منشور → client_review. LEDGER: plans/production-order-issue-report/.

## ISS-2026-9165 refinements (2026-07-19) → client_review
- العميل ردّ بـ3: (أ) لينك لإذن الصرف، (ب) مش تقرير مالي (التكلفة تُخفى — اتعمل بالصلاحية بالفعل)، (ج) بحث server-side للأوامر (هيبقوا بالآلاف).
- BE `fb714c5`: الرد بقى issues:[{id,number}] بدل issue_numbers (للـdeep-link)؛ 19 tests. FE `aacc279`: لينك /core/stock-issues?viewId=id + منتقي server-side (list(1,15,{search}) debounced، شال listAll). review APPROVE (صفر crit/high، 2 LOW picker race). build+deploy main-GTWYQEVD.js. verification منشور.

## ISS-2026-9166 — صرف إنتاج per-line من مخزن أمانات/عميل ✅ (2026-07-19) [Opus نظّم/راجع]
- فيتشر [FIN] كبير على 6 WPs (Phase 1). architect design gate → LEDGER → opus sub-agents ينفّذوا (Codex متعطّل cPanel/CageFS) → مراجعة [FIN] + architect لكل حرجة.
- الديزاين: عمودين على inventory_issue_items (warehouse_id + owner_partner_id؛ NULL=derive،0=own،>0=consignor). per-line owner يستبدل التقسيم التلقائي لهذا السطر فقط. consignment line = customer material (off-book + ledger، no WIP). OFF-path byte-for-byte.
- WP1 fa902d4ce schema · WP2 b206daff1 ApproveIssue per-item wh (sales-GDN byte-for-byte) · WP3 a158c5cea IssueMaterials routing · WP5 13ade724d keeper owner-from-item fix (قفل HIGH: الـlistener بيسقط owner → WIP leak على gated path — الحل قراءة owner من الـitem المحفوظ) · WP4 7554164a6 validation+guards+availability pre-check (+54f0fc82e variant-scope fix) · WP6 1a295b6ca FE per-row pickers.
- كل WP reviewed APPROVE (0 crit/high). Whole-feature 74 pass، 0 new fail (ConsignmentFoundationTest settle-buy pre-existing red موثّق). deploy main-KMJW5L64.js. CHANGELOG ثنائي. verification → client_review.
- ★ قرارات: سطر متجانس (per-material hold)، shortage hard-block. Phase 2 (create-issue→pick-order) مؤجّلة.
- LEDGER: plans/per-line-consignment-issue/.

## ISS-2026-9166 refinement (2026-07-19) → client_review
- العميل (صورة GDN-000043): بيعدّل مسودة إذن صرف (من أمر إنتاج) على شاشة أذون الصرف ومش لاقي منتقيات المخزن/الأمانات — WP6 حطهم في نافذة صرف الإنتاج بس، مش شاشة أذون الصرف (مكان أمين المخزن).
- الحل: BE `390d50570` (Store/Update أذون الصرف تقبل/تحفظ items.*.warehouse_id+owner_partner_id company-scoped + resource echo + 5 tests/32 pass) + FE `af494d7a0` (per-row wh+consignor pickers في ديالوج «تعديل إذن الصرف»، mirror WP6). review APPROVE (0 findings). deploy main-QMJYU6KY.js. verification → client_review.
- الاعتماد بيوجّه صح تلقائيًا (ApproveIssue/applyEffects بيقرا per-item من البند المحفوظ — WP2/3/5).

## ISS-2026-9166 R3 (2026-07-19) → client_review
- العميل (step 4775): المخزن-لكل-صف شغّال، بس بادج «المخزون» لسه بييجي من المخزن الرئيسي؛ لازم يعكس مخزن الصف (ولو أمانات، رصيد العميل).
- الحل FE فقط: listFiltered يمرّر owner (null→available_quantity، >0→owner_quantity lens)؛ loadStockForRow(index) بيحلّ لكل صف = المخزن الفعّال + owner lens؛ stockQtyMap أُعيد فهرسته بفهرس الصف؛ getRowStockQty(i). reload على product/variant/row-wh/consignor/dialog-wh + removeItem full-reload. display-only. review APPROVE 0. deploy main-4WY74576.js.
- **Deferral flagged (LOW):** rowSelectedSerials + rowLotAllocations (index-keyed، بيغذّوا الـsubmit) مش بيتعاد فهرستهم عند حذف صف من النص → ممكن misalignment للسيريال/التخصيص. متقبول تاريخيًا (كومنت ~L1149). follow-up منفصل مش جزء من 9166.

## Row-map reindex fix (2026-07-19, ISS-2026-9166 follow-up) — DEPLOYED
- Bug found in 9166-R3 review: stock-issues removeItem didn't reindex row-index-keyed maps → mid-list row delete submits WRONG serials/lot-allocations (rowSelectedSerials/rowLotAllocations feed onSave payload). Data-correctness bug for serial/batch items.
- Fix: removeItem reindexes rowSelectedSerials + rowLotAllocations + stockQtyMap via reindexRowMap helper. review APPROVE 0. deploy main-RC6AUPPW.js. CHANGELOG bullet added.
- Portal ticket NOT created (no API create endpoint — POST /api/issues=405). Paste-ready body + tracking: knowledge-base/plans/stock-issue-row-map-reindex/TICKET.md. Owner to create it in portal UI.

## ISS-2026-9169 (2026-07-19) → client_review — column control on stock grids
- Feature: show/hide + reorder + resize columns on إذن الصرف + إذن الإضافة line grids, "like invoices". Client took ALL recs (scope_freeze 4799): company-wide persist (docConfig core.document_settings), core.settings-gated, phased.
- implement-plan: WP1 (⚙ gear show/hide + register all cols in DOC_CONFIG_REGISTRY inventory.issue/receipt) review APPROVE 0; WP2 (TxColumnLayout helper src/app/shared/util/tx-column-layout.ts ported from transaction-line-items; column-order-driven @switch rows; CDK reorder + pointer resize RTL) review APPROVE + @default td hardening applied. deploy main-GY4CPFSP.js.
- Plan: knowledge-base/plans/stock-grid-column-control-9169/ (LEDGER+tasks+RESUME). Deferrals: table-layout auto (looser resize fidelity), dead resizing field — both LOW/informational.
- Portal has NO API to create tickets (POST /api/issues=405). Row-map reindex follow-up ticket body still un-filed: knowledge-base/plans/stock-issue-row-map-reindex/TICKET.md.

## ISS-2026-9253 — تدقيق متغيّرات المنتج (ألوان/مقاسات) (2026-07-20) → awaiting_client
- سؤال العميل (bug): هل المتغيّرات بتتحسب صح في المخزون وبتظهر صح في المبيعات/المشتريات وكل المستندات؟
- **الإجابة: المحرّك سليم، الإدخال والعرض ناقصين.** 3 تدقيقات opus متوازية (مخزون / مبيعات-مشتريات BE / واجهة).
- سليم: مفتاح الرصيد (منتج+متغيّر+مخزن) بفهرس فريد · WAC/FIFO/تقييم/حجز/توفّر/لوطات variant-scoped · **الـ10 تحويلات كلها بتنقل المتغيّر** · الأثر المخزني صح · NULL آمن (Laravel→whereNull).
- مكسور: (1) ورقة الجرد عمياء InventoryCountController:130 + كومنت 101-103 «النسخة مابتستخدمش متغيّرات» (2) تنبيهات إعادة الطلب groupBy(product_id) (3) كارت الصنف مش variant-scoped (4) كل الطباعات الـ10 (5) كل التقارير (6) **FE: صفر منتقي متغيّر في 10 شاشات مبيعات/مشتريات** — الجدول المشترك بيوفّر خطّافات اختيارية وشاشات المخزون بس فعّلتها (7) سيريال بلا variant scope (8) طلب الشراء بلا علاقة/FK/exists (9) إذن التسليم اسم إنجليزي (10) eager-load gap.
- **دليل حاسم:** product_variants = صفر صف على dev → الميزة ما اشتغلتش فعليًا؛ الإصلاح قبل إدخال البيانات أرخص.
- تصنيف الحجم: الإصلاحات **فيتشر** (3 موديولات، 10+ شاشات) → اقترحت 3 مراحل/تذاكر مستقلة بعقد نطاق، ومنعت أي تنفيذ جوّه تذكرة الـbug.
- نُشر: planning-gate comment (step 6020: repro + سبب جذري + مصفوفة) ثم analysis (step 6022) بـ4 أسئلة قرار → awaiting_client.
- KB مشروع: id **123** (المعتمد، بكل المراجع). ⚠️ id 122 نفس الموضوع لكن **ناقص المراجع** — backticks اتفسّرت من الـshell وقت النشر؛ الدرس: انشر متن الـAPI من ملف heredoc مقفول ('EOF') مش inline في python -c.

## ISS-2026-9253 → client_review + تقسيم لـ3 تذاكر (2026-07-20)
- العميل أخذ **بكل التوصيات الأربعة** (step 6043) → scope_freeze 6045. الاتفاق المثبّت: 3 مراحل، تذكرة لكل مرحلة بعقد نطاق، نبدأ بالمرحلة ١، وفي المرحلة ٢ نبدأ بفاتورة البيع+الشراء+طباعتهم بس.
- spinoffs اتفتحت من 9253: **9257** (م١ جرد+إعادة طلب+كارت صنف) · **9258** (م٢ اختيار المتغيّر في البيع/الشراء+طباعة) · **9259** (م٣ تقارير+تشديدات).
- **9257**: عقد نطاق كامل منشور (In/Out/Acceptance) + 3 أسئلة → awaiting_client. الأسئلة: (1) حد إعادة الطلب per-variant: توصية = استخدام حد المنتج الحالي لكل لون بلا migration (2) ورقة الجرد تعرض الأصفار: توصية = نعم (3) ابدأ التنفيذ؟
- **9253**: verification منشور (التدقيق + التقسيم) → client_review.
- تفاصيل الكود المؤكّدة للمرحلة ١: `InventoryCountController::productsForWarehouse` الـleftJoin فيه `whereNull('sb.product_variant_id')` (~سطر 130) والكومنت 101-103 بيقر بالافتراض · `ReorderAlertController` ~76-77 `select(product_id, SUM(quantity))->groupBy(product_id)` مقابل `products.reorder_point` · `StockCardController::stockCard` بيفلتر product_id فقط لكن **بيعمل eager-load لـproductVariant بالفعل** (سهل نعرض الاسم) · **`reorder_point` موجود على جدول products فقط — مفيش عمود على product_variants** (ده سبب السؤال ١).
- FE المستهلكين: `inventory-count.service.ts` (productsForWarehouse) · كارت الصنف مستهلك في `stock-balances.component.ts` و`product-detail.component.ts`.

## ISS-2026-9260 — الصرف على أمر إنتاج من شاشة إذن الصرف (2026-07-20) → awaiting_client
- طلب العميل: من إذن الصرف أختار أمر إنتاج؛ ولو مش موجود أضيف «أمر إنتاج مؤقت»؛ وكل ده يبقى أوبشن (إعداد).
- **ده الـPhase 2 المؤجّل من ISS-9166** (create-issue→pick-order).
- **الباك إند جاهز 90%:** `IssueReferenceType::ProductionOrder` موجود · `StoreInventoryIssueRequest` بيقبل reference_type من الـenum + reference_id · `ApproveIssue` production-order-aware (توفّر reservation-aware :226-229، وبيوقف auto lot-allocation :296) · listener `ApplyMaterialIssueOnApproval` بيسجّل الأثر (MfgMaterialIssue + WIP + خصم المخطط + فك الحجز) مع idempotency.
- **الناقص:** (1) FE: `referenceTypeOptions` (stock-issues.component.ts:163) **مافيهاش production_order أصلًا**، ومفيش منتقي reference_id في الديالوج. (2) ⚠️ **فجوة حقيقية:** الـlistener بيرجع early لو `production.material_issue_requires_approval` = false → إذن يدوي مرتبط بأمر إنتاج هيخصم مخزون **بلا WIP وبلا تسجيل على الأمر** (desync). (3) «أمر مؤقت» مفهوم غير موجود.
- عقد نطاق منشور (In/Out/Acceptance) + 4 أسئلة → awaiting_client. أهمها: تأكيد إن الأثر لازم يتسجّل على الأمر (وقفل الفجوة)، وتعريف «الأمر المؤقت»، وتأجيله لمرحلة ٢.

## ISS-2026-9257 — متغيّرات المنتج المرحلة ١ ✅ (2026-07-20) → client_review
- 4 WPs: A ورقة الجرد per-variant · B تنبيهات إعادة الطلب per-variant · C كارت الصنف فلتر ثلاثي القيمة · D الواجهة. كلهم APPROVE بعد 3 جولات إصلاح + مراجعة شاملة.
- **قرار معماري:** `Modules/Inventory/app/Services/ReorderGrainService.php` — طبقة مشتركة للحبيبة يستخدمها `ReorderAlertController` و`ProductionAlertService` (كان فيه موضع خامس بيتناقض). + اختبار `GrainParityCountVsReorderTest` بيقارن **المسار الحقيقي** لورقة الجرد بالنقطة الحقيقية للتنبيهات.
- قاعدة الحبيبة النهائية: صف لكل متغيّر حيّ لو `variant_type != display_only` (NULL = حامل مخزون) + إنقاذ **أي** مفتاح رصيد ≠0 (|q|>=0.0005) ما اتصدرش (أساسي/display_only/محذوف) بحارس ملكية + fallback `product_wide` واحد لو مفيش. base أولًا ثم variant id.
- **اختبارات: 530/1 (baseline 450/1) = +80 صفر فشل جديد.** تحقّق MySQL حقيقي: 17,151 صف متطابقة عبر 16 مخزن.
- **8 عيوب «رقم غلط شكله صح» اتمنعت** (مضاعفة مخزون ×2، تنبيهات نفاد وهمية، كارت لون تحت اسم لون تاني، تناقض إنتاج/مخزون، لون محذوف مخفي، إشعار بيفيض العمود فيتسلّم جزئيًا، الواجهة بتبعت كل الصفوف بلا لون).
- مفيش migration. deploy `main-YOIJOJJU.js`. CHANGELOG بندين ثنائيي اللغة. LEDGER+RESUME: plans/variant-inventory-phase1-9257/.
- **مؤجّل مُبلَّغ للعميل:** مسارات الإدخال التانية مش بتتحقق من ملكية المتغيّر (الجرد بقى بيتحقق) — عرضت أفتح تذكرة.
