تعليقي على الخطة (v2) بعد التحقق من الكود الفعلي · + مواصفة التكامل الدقيقة · + خطة WPs نبني منها
المصدر: تحقّق مباشر من كود moonui2 + خطة v2التاريخ: 2026-07-03الحالة: جاهز للتنفيذ بعد قرارات §5
١ حكمي على الخطة — ✅ صلبة ودقيقة، ننفّذها
الخطة ممتازة — التصميم (سعر مباشر · سطر واحد · تعيين متعدد · قيد واحد عند الاكتمال · إيراد صافي IFRS-15) هو الاختيار الصح: أبسط، محاسبيًا سليم، وبيلغي خطر «ثبات المبلغ» بالكامل. وتحققت من ادعاءاتها الأساسية ضد الكود الفعلي وطلعت كلها مظبوطة (تفاصيل §2). ماعنديش اعتراض على الاتجاه — عندي تحديد أدق لنقطة التكامل الأخطر (§3) وتأييد لقراراتك (§4).
✅ CreateLabRequest (~220-255) بيعمل foreach package->investigations صف لكل تحليل بـsource_package_id → الفوترة per-component.
الباقة مفيهاش وضع فوترة
✅ LabPackage فيها package_price/total_price/price_deductible بس — مفيش billing_mode (نضيفه).
الترحيل «only wired for B2B receivables»
✅ مكتوبة حرفيًا في PostLabInvoiceItem.
٣ 🎯 أخطر نقطة تكامل — تحديدها بدقة (إضافتي الأساسية)
الخطة رصدت إن «الآلة per-item ومحتاجين مسار سطر-باقة». من الكود، دي المواصفة الدقيقة اللي هننفّذها:
CreateLabRequest يتفرّع على billing_mode
←
direct: بند فاتورة واحد للباقة + مكوّنات تنفيذ بلا بنود فاتورة
←
trigger اكتمال جديد يرحّل قيد الباقة الواحد
(أ) تفكيك الفاتورة: في billing_mode=direct → LabInvoiceItemواحد للباقة (package_id، investigation_id=null، السعر المباشر). المكوّنات تتفكّك للتنفيذ (worklist/عينات/نتايج) لكن من غير بنود فاتورة — عشان الـPostInvoiceItemOnResultRelease الحالي (اللي بيطابق بـinvestigation_id) مايرحّلش المكوّنات بالغلط. حاسم
(ب) trigger الاكتمال: handler جديد على LabResultReleased — لو النتيجة تخصّ باقة (source_package_id)، يشوف هل كل مكوّنات الباقة غير الملغاة اترفعت → يرحّل قيد الباقة الواحد عبر posting action جديد على سطر الباقة. الـ listener القديم بيتخطّى سطر الباقة تلقائيًا (مفيش investigation_id) → مفيش ازدواج.
(ج) Idempotency:posted_at/journal_entry_id على سطر الباقة (زي بنود per-item بالظبط). إعادة رفع نتيجة (re-test) بتـtrigger تاني → الحارس يمنع الترحيل المزدوج.
(د) نموذج البيانات:LabInvoiceItem لازم يقبل سطر «باقة»: investigation_id nullable + package_id (أو item_type=package). نتأكد من الـschema.
أكبر خطر (متفق مع الخطة): موثوقية trigger الاكتمال. الفخّان: (1) مكوّن ملغى/QNS مايحبسش الاكتمال للأبد → «اكتمال» = كل المكوّنات غير الملغاة اترفعت. (2) لا ترحيل مزدوج (re-test / رفع آخر نتيجة مرتين) → حارس posted_at. ده لبّ اختبارات م2.
٤ قراراتك (§5) — رأيي
القرار
توصية الخطة
رأيي
السعر لكل شريك
مباشر + override اختياري
أوافق يغطّي «سعر موحّد» و«سعر خاص» بجدول واحد.
توقيت الإيراد
عند الاكتمال
أوافق إيراد وقت التسليم = IFRS-15 صح، ومتّسق مع per-item الحالي (اللي بيرحّل عند الرفع).
CreateLabRequest يتفرّع: direct → بند فاتورة واحد للباقة + مكوّنات تنفيذ بلا بنود فاتورة (بصفر). allocated → زي ما هو.
متوسط
WP3 — المحاسبة (الأخطر)
posting action لسطر الباقة + handler اكتمال على LabResultReleased (كل المكوّنات غير الملغاة مرفوعة → قيد واحد) + idempotency + سياسة الجزئي. + اختبارات: قيد واحد=السعر+VAT · لا ازدواج · ملغى مايحبسش.
حسّاس — تركيز التست هنا
WP4 — FE (موظّف)
شاشة الباقات: وضع الفوترة + تبويب «الشركاء (B2B)» (تعيين + سعر لكل شريك). أو تبويب «الباقات» في شاشة المعمل الخارجي.
متوسط
WP5 — البورتال + المطالبة + تقرير
كتالوج البورتال يعرض باقات الشريك بسعرها؛ الطلب بنفس خدمة BE؛ المطالبة تعرض سطر الباقة؛ تقرير «قيمة التوفير» (مجموع أسعار − السعر المباشر، بلا مساس GL).
متوسط
التتابع: WP1 → WP2 → WP3 (بوابة تست مشدّدة على المحاسبة) → WP4 → WP5. كل WP بمراجعة (native + Codex للمحاسبة/المال). البناء على moonui2 (كوده هو اللي فيه بنية Actions/Listeners دي).