قيود اليومية لمعامل الـB2B (المعمل الخارجي كعميل)

تقرير شامل: إزاي القيود بتتعمل، بنيتها، وهل هي صحيحة ولا لأ — مبني على فحص الكود + قيود فعلية في الـGL

التاريخ: 3 يونيو 2026  •  الشركة: 4 (تطوير)  •  المصدر: PostLabInvoiceItem · RecordExternalLabPayment · قيود فعلية

✅ التصميم سليم

القيد لكل تحليل عند رفع النتيجة: مدين ذمم العميل / دائن إيراد / دائن ض.ق.م — متوازن والضريبة مفصولة صح.

❌ فيه ثغرات تشغيلية

بنود اتعلّمت "مرحّلة" من غير قيد، فاتورة مدفوعة بدون إيراد مسجّل، والأخطاء بتتبلع في اللوج.

1) إزاي القيود بتتعمل (المسار)

الـB2B مش بيترحّل دفعة واحدة — بيترحّل تحليل تحليل أول ما النتيجة تترفع (release)، عشان رصيد العميل والمطالبة الشهرية يعكسوا الشغل وقت ما يتسلّم فعلاً.

طلب B2Bمن البورتال → فاتورة external_lab_receivable حالتها Draft
رفع نتيجة تحليلListener PostInvoiceItemOnResultRelease
ترحيل البندPostLabInvoiceItem يعمل قيد للبند + يقلب الفاتورة Posted
تحصيل من المعملRecordExternalLabPayment: مدين خزينة / دائن ذمم العميل

ملحوظة: الفوترة على المعمل الخارجي (هو الـPartner اللي عليه الذمم)، مش المريض — متّسق مع اللي اتفقنا عليه قبل كده.

2) بنية القيد الصحيحة (مثال فعلي من الـGL)

قيد فعلي JE#183 لبند فاتورة B2B — متوازن والضريبة (15%) مفصولة في حساب ض.ق.م.

JE#183 · ref LI-2026-00186/85 · partner: Hazem lab (معمل خارجي) · entry_type=lab_invoice · source=lab_invoice_item
الحسابمديندائن
110320 — ذمم العميل (Hazem lab) — Partner AR79.350
110304 — إيراد المختبر (Revenue)69.000
2106 — ضريبة القيمة المضافة (VAT Payable)10.350
✓ متوازن: 79.350 = 69.000 + 10.350  |  الضريبة 10.350 = 15% × 69.000 ✓  |  الذمم على الحساب الفرعي للمعمل ✓

القيد بيستخدم الحساب الفرعي للشريك (AccBpExt.ar_account_id) — يعني رصيد كل معمل بيتتبّع لوحده في كشف حسابه. والـVAT بيتحسب نسبة وتناسب لكل بند.

3) تقييم الصحة

البندالحالةالتفاصيل
توازن القيد (مدين=دائن)صحيحكل الـ7 قيود per-item في الـGL متوازنة
فصل الضريبة (VAT)صحيح7 من 7 قيود فيها سطر ض.ق.م منفصل — مش متحطوط في الإيراد
ذمم على الحساب الفرعي للمعملصحيحبيستخدم AccBpExt.ar_account_id للشريك
قيد التحصيل (الدفع)صحيحمدين خزينة / دائن ذمم العميل
توقيت الترحيليحتاج انتباهالإيراد مايتسجّلش غير لما النتيجة تترفع — لو ما اترفعتش يفضل Draft بلا قيد
بنود "مرحّلة" بدون قيدمكسور4 بنود posted_at متظبّط بس journal_entry_id = NULL
فاتورة مدفوعة بدون إيرادمكسورLI-2026-00211 مدفوعة 276 والإيراد ماتسجّلش
معالجة الأخطاءخطرالـListener بيبلع الاستثناء (Log فقط) — فجوة صامتة
المسار القديم whole-invoiceقنبلة موقوتةcreateExternalLabInboundJE بيحط الإجمالي إيراد بدون فصل VAT

4) الأرقام الفعلية (شركة 4)

19
فواتير B2B
(12 draft · 6 ملغاة · 1 مدفوعة)
7
قيود per-item سليمة في الـGL
35/39
بنود لسه متحرّكتش
(نتايج مترفعتش)
4
بنود "مرحّلة" بدون قيد
276
فاتورة مدفوعة بلا إيراد مسجّل

5) المشاكل بالتفصيل

🔴 حرِجة — فاتورة مدفوعة من غير إيراد مسجّل

الفاتورة LI-2026-00211 حالتها paid (اتقبض 276) لكن بنودها الأربعة journal_entry_id = NULL — يعني المدين (الذمم) والدائن (الإيراد) ماتسجّلوش في الأستاذ خالص، ومع ذلك الكاش اتقبض. النتيجة: الإيراد ناقص في قائمة الدخل، والذمم ممكن تطلع سالبة لما قيد الدفع يخصم من رصيد ماتبنىش.

الدليل: LI-2026-00211 — total=276 · paid=276 · header_JE=NULL · 4 items posted_at set / je=NULL

🔴 حرِجة — الأخطاء بتتبلع بصمت

الـListener PostInvoiceItemOnResultRelease بيلف الترحيل في try/catch ويكتب Log::error بس. يعني لو الترحيل فشل (إعداد حساب ناقص، أو شريك من غير حساب فرعي)، التحليل بيترفع عادي بس من غير قيد — وفجوة الإيراد متبقاش ظاهرة لحد ما يطلع تقرير مالي ناقص.

الدليل: PostInvoiceItemOnResultRelease.php — سطر 58 try / 60 catch (\Throwable)Log::error

🟠 متوسطة — مسار قديم بدون فصل VAT

لسه موجود في الكود PostLabInvoice::createExternalLabInboundJE اللي بيعمل مدين ذمم / دائن إيراد بالإجمالي كله من غير ما يفصل الـVAT. لو أي مسار نده ترحيل الفاتورة-كاملة لـB2B (بدل per-item)، الـ15% ض.ق.م هتتسجّل إيراد بالغلط بدل التزام ضريبي.

الدليل: PostLabInvoice.phpcreateExternalLabInboundJE(): سطرين بس (DR AR full / CR Revenue full) بلا سطر ضريبة

🟠 متوسطة — توقيت تسجيل الإيراد

الإيراد بيتسجّل لكل تحليل وقت رفع النتيجة، مش وقت الفاتورة. ده نموذج محاسبي مقبول (تسجيل عند تأدية الخدمة)، بس معناه إن فواتير كتير بتفضل Draft من غير قيد طول ما نتايجها مترفعتش — 35 من 39 بند حالياً كده. لازم الإدارة تعرف إن الإيراد المعلّق ده طبيعي ومش مفقود.

الدليل: 12 فاتورة draft · 35/39 بند posted_at = NULL

6) المقترح للإصلاح

  1. إصلاح فجوة الـListener الصامتة: بدل ما الخطأ يتبلع، نخلّي البند يتعلّم posting_failed ويطلع في تقرير "بنود فاشلة الترحيل" عشان الحسابات تعالجها — مش بس Log.
  2. منع "مرحّل بدون قيد": نخلّي posted_at مايتحطّش غير بعد ما القيد يتعمل فعلاً (هو كده جوه الـtransaction، بس نتأكد إن مفيش مسار تاني بيحط posted_at لوحده) + سكربت يكتشف البنود posted_at≠NULL & je=NULL ويعيد ترحيلها.
  3. تصحيح الفاتورة المدفوعة بلا إيراد: نعيد ترحيل بنود LI-2026-00211 (وأي زيها) عشان الإيراد والذمم يتسجّلوا قبل ما قيد الدفع يبقى متعلّق.
  4. إصلاح المسار القديم whole-invoice: نضيف فصل الـVAT في createExternalLabInboundJE (DR AR / CR Revenue صافي / CR Tax) عشان لو اتنده مايغلطش — أو نشيله خالص ونعتمد per-item بس.
  5. شاشة متابعة: تقرير "إيراد B2B معلّق" (بنود مترحّلتش لإن نتيجتها مترفعتش) عشان الفرق بين "معلّق طبيعي" و"مفقود بخطأ" يبقى واضح.