تقرير شامل: إزاي القيود بتتعمل، بنيتها، وهل هي صحيحة ولا لأ — مبني على فحص الكود + قيود فعلية في الـGL
القيد لكل تحليل عند رفع النتيجة: مدين ذمم العميل / دائن إيراد / دائن ض.ق.م — متوازن والضريبة مفصولة صح.
بنود اتعلّمت "مرحّلة" من غير قيد، فاتورة مدفوعة بدون إيراد مسجّل، والأخطاء بتتبلع في اللوج.
الـB2B مش بيترحّل دفعة واحدة — بيترحّل تحليل تحليل أول ما النتيجة تترفع (release)، عشان رصيد العميل والمطالبة الشهرية يعكسوا الشغل وقت ما يتسلّم فعلاً.
external_lab_receivable حالتها DraftPostInvoiceItemOnResultReleasePostLabInvoiceItem يعمل قيد للبند + يقلب الفاتورة PostedRecordExternalLabPayment: مدين خزينة / دائن ذمم العميلملحوظة: الفوترة على المعمل الخارجي (هو الـPartner اللي عليه الذمم)، مش المريض — متّسق مع اللي اتفقنا عليه قبل كده.
قيد فعلي JE#183 لبند فاتورة B2B — متوازن والضريبة (15%) مفصولة في حساب ض.ق.م.
| الحساب | مدين | دائن |
|---|---|---|
| 110320 — ذمم العميل (Hazem lab) — Partner AR | 79.350 | — |
| 110304 — إيراد المختبر (Revenue) | — | 69.000 |
| 2106 — ضريبة القيمة المضافة (VAT Payable) | — | 10.350 |
القيد بيستخدم الحساب الفرعي للشريك (AccBpExt.ar_account_id) — يعني رصيد كل معمل بيتتبّع لوحده في كشف حسابه. والـVAT بيتحسب نسبة وتناسب لكل بند.
| البند | الحالة | التفاصيل |
|---|---|---|
| توازن القيد (مدين=دائن) | صحيح | كل الـ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 |
الفاتورة 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لسه موجود في الكود PostLabInvoice::createExternalLabInboundJE اللي بيعمل مدين ذمم / دائن إيراد بالإجمالي كله من غير ما يفصل الـVAT. لو أي مسار نده ترحيل الفاتورة-كاملة لـB2B (بدل per-item)، الـ15% ض.ق.م هتتسجّل إيراد بالغلط بدل التزام ضريبي.
PostLabInvoice.php — createExternalLabInboundJE(): سطرين بس (DR AR full / CR Revenue full) بلا سطر ضريبةالإيراد بيتسجّل لكل تحليل وقت رفع النتيجة، مش وقت الفاتورة. ده نموذج محاسبي مقبول (تسجيل عند تأدية الخدمة)، بس معناه إن فواتير كتير بتفضل Draft من غير قيد طول ما نتايجها مترفعتش — 35 من 39 بند حالياً كده. لازم الإدارة تعرف إن الإيراد المعلّق ده طبيعي ومش مفقود.
posted_at = NULLposting_failed ويطلع في تقرير "بنود فاشلة الترحيل" عشان الحسابات تعالجها — مش بس Log.posted_at مايتحطّش غير بعد ما القيد يتعمل فعلاً (هو كده جوه الـtransaction، بس نتأكد إن مفيش مسار تاني بيحط posted_at لوحده) + سكربت يكتشف البنود posted_at≠NULL & je=NULL ويعيد ترحيلها.LI-2026-00211 (وأي زيها) عشان الإيراد والذمم يتسجّلوا قبل ما قيد الدفع يبقى متعلّق.createExternalLabInboundJE (DR AR / CR Revenue صافي / CR Tax) عشان لو اتنده مايغلطش — أو نشيله خالص ونعتمد per-item بس.