تشخيص · Phase 1 · بلا كود

فاتورة الشراء بتطلع من غير أي بنود

«ألاقي فيها اسم المورد بس، مفيهاش أي منتج من المنتجات اللي وافقت عليها وحددت عددهم»
الخطورة عالية [FIN] بتمسّ مبالغ وقيود الشاشة أوامر الشراء ← إنشاء فاتورة مش انحدار من تعديل امبارح moonui2 · 2026-08-16
الخلاصة في سطر
الحوار في الواجهة بيحسب الكمية المتاحة للفوترة بـالمطلوب ناقص المفوتر، والسيرفر في الوضع المراقَب بيحسبها بـالمستلَم ناقص المفوتر. أمر الشراء ٢١ مستلم منه صفر — فالحوار عرض ٣٠ والسيرفر شاف صفر، وبدل ما يرفض، أنشأ فاتورة فاضية.

١ الأعراض

المتوقع: فاتورة فيها البنود اللي اخترتها بالكميات اللي كتبتها.
الحاصل: الفاتورة بتتعمل فعلاً، فيها المورد والفرع والعملة — وجدول البنود فاضي والإجمالي صفر.

خطوات التكرار: أوامر الشراء ← أمر confirmed لسه ما اتستلمش ← زرار «إنشاء فاتورة» ← الحوار بيعرض البنود وكمياتها ← تأكيد ← افتح الفاتورة: مفيش بنود.

مين بيتأثر: أي شركة في الوضع المراقَب (procurement_mode = controlled) أو grn_mode = grn / grn_quality. الشركات في الوضع المباشر (direct) ما بتتأثرش إطلاقًا.

٢ خط التتبّع

الحلقةالموضعاللي اتلاحظ
الداتاpurchase_bills #19 BILL-2026-00018 · من أمر ٢١ · صفر بند · الإجمالي 0.000 · اتعملت 2026-08-15 13:02
الأمرpurchase_orders #21 confirmed · receive_status = pending · بند واحد: مطلوب ٣٠ · مستلَم 0 · مفوتر 0
الواجهةorders.component.ts :: openCreateBill() بتجيب المستند كامل بـgetById وبتفلتر على i.remaining_bill_qty > 0 وبتملّي الكمية بيه
الموردPurchaseOrderItemResource:37 remaining_bill_qty => remainingBillQuantity()
الحسابPurchaseOrderItem::remainingBillQuantity():123 quantity − billed_quantity = ٣٠ ← ده اللي الحوار عرضه
السيرفرPurchaseBillController::createFromOrder() في غير الوضع المباشر: received_quantity − billed_quantity = صفرcontinue لكل بند
النتيجةالهيدر اتكتب، ولا بند اتكتب، ومفيش أي رفض
الحوار يعرض ٣٠ المستخدم يحدد ويأكّد السيرفر يحسب صفر continue على كل بند فاتورة فاضية اتحفظت

٣ السبب الجذري

السبب الأساسي — طرفان بيحسبوا نفس الرقم بقاعدتين مختلفتين

في الوضع المراقَب القاعدة المحاسبية سليمة: ما تفوترش حاجة ما استلمتهاش. المشكلة إن الواجهة ما اتعلّمتش القاعدة دي أبدًا — هي بتسأل عن remaining_bill_qty وده محسوب على المطلوب، من غير أي اعتبار لوضع الاستلام.

السبب المساهم — إنشاء مستند فاضي بدل الرفض

لما كل البنود تتخطّى، الدالة بتكمل وتحفظ الهيدر. مفيش أي فحص «الخطة طلعت فاضية → ارفض». ده ثالث حالة من نفس العيلة النهاردة: التحويل الفاضي لطلب الشراء، وأمر الشراء بلا سطور، ودي.

٤ ليه حصلت — وهل هي انحدار؟

لأ، مش انحدار من تعديل امبارح. الفرضية الأولى كانت إن كوميت b7d73ea3e (الكميات المطلوبة) هو السبب. الدليل نفاها: قاعدة received_quantity − billed_quantity جت من 3d453860f بتاريخ 2026-07-09 — قبل تعديل امبارح بشهر. تعديل امبارح ضاف طبقة فوقها وما سببش ده.

الآلية: فجوة تصميم بين طرفين اتغيّر أحدهما ولم يُبلَّغ الآخر. لما الوضع المراقَب اتبنى في يوليو، السيرفر اتعلّم «فوتر على المستلَم»، والواجهة فضلت على «فوتر على المطلوب». ومحدش لاحظ لأن الفرق ما بيظهرش غير لما تحاول تفوتر قبل الاستلام.

٥ كل الأبعاد

البُعدالنتيجة
الداتافاتورة واحدة فاضية على الديف (#19). مسودة وإجماليها صفر، فمفيش قيد اترحّل ولا رصيد اتأثر. الإصلاح مش محتاج ترحيل بيانات — بس الفاتورة الفاضية دي تتحذف يدويًا.
الإعداداتهي محور العطل. direct سليم تمامًا · grn وgrn_quality والوضع المراقَب متأثرين.
الصلاحياتلا علاقة — مش متوقف على دور.
المال [FIN]الفاتورة الفاضية ما بتفسدش الدفاتر (صفر، ومسودة). الخطر إنها بتوهم المستخدم إن الفوترة تمّت، فيدوّر على فلوس مش موجودة.
i18nلا علاقة.
حالات حدّيةاستلام جزئي: الحوار هيعرض المطلوب كله والسيرفر هيقصّه للمستلَم — فاتورة أقل من المتوقع من غير أي رسالة. أخطر من الحالة الفاضية لأنها مش ملحوظة.

أشقّاء كامنون

٦ الحلول

خيار ١ — الواجهة تسأل السيرفر بدل ما تحسب (المرشَّح)

يتضاف حقل remaining_bill_qty_effective على مورد بند الأمر يحترم وضع الاستلام، والحوار يفلتر ويملّي منه. + حارس رفض على السيرفر: لو الخطة طلعت فاضية → 422 برسالة «لازم تستلم البضاعة الأول».

المكسب: بيصلح الفئة مش الحالة — الحوار مش هيعرض حاجة السيرفر هيرفضها، لا فاضية ولا جزئية. التكلفة: حقل جديد في المورد + تعديل الحوار.

خيار ٢ — حارس الرفض بس

422 لما الخطة تطلع فاضية، والحوار زي ما هو.
المكسب: سطرين. العيب: الحالة الجزئية تفضل صامتة — تختار ٣٠ وتاخد ١٠ من غير ما حد يقولك.

خيار ٣ — توحيد القاعدة على «المطلوب» في الطرفين

مرفوض: بيكسر قاعدة محاسبية مقصودة (ما تفوترش قبل ما تستلم) وبيخالف حارس المطابقة الثلاثية.

٧ خطة الإصلاح

WPالنطاقالريبوالاختبارعلم
١حارس الرفض: خطة فاضية → 422 برسالة «استلم الأول» (ar+en)BEأحمر-قبل-أخضر: أمر مستلَم صفر → 422 ومفيش فاتورة اتعملت[FIN]
٢remaining_bill_qty_effective على مورد بند الأمر، محسوب بنفس قاعدة السيرفرBEالحقل = صفر في المراقَب بلا استلام · = المتبقي في المباشر
٣الحوار يفلتر ويملّي من الحقل الجديد، ورسالة واضحة لو مفيش حاجة قابلة للفوترةFEبناء نضيف + فحص يدوي على أمر غير مستلَم
٤حذف الفاتورة الفاضية #19 من الديفdata

٨ قرارات تحتاجك

  1. الخيار ١ ولا ٢؟ ترشيحي ١ — الحالة الجزئية الصامتة أخطر من الفاضية لأنها بتعدّي من غير ما حد ياخد باله.
  2. الفاتورة الفاضية #19 — أحذفها من الديف؟ (مسودة بصفر، حذفها آمن.)
  3. مسح الأشقّاء — أعمل مسح لباقي مسارات «إنشاء مستند من مستند» بنفس النمط دلوقتي ولا بعدين؟