طلبك: أمين المخزن يشوف مخازنه وعمليات مخازنه — وبعض الشركات تحب يشوف كل المخازن، وبعضها بياناته هو بس.
وبعدين وسّعت: ده على مستوى النظام كله، والخزن نفس القصة.
التحليل طلّع حقيقة أهم من الطلب: النظام حاليًا مافيهوش حماية بيانات في المخازن ولا الخزن على الإطلاق —
وفيه آلية نص مبنية بتوحي إنها بتحمي. أمين المخزن دلوقتي بيشوف
كل أذون الصرف في الشركة كلها، وأمين الخزنة بيشوف كل الخزن وأرصدتها.
DataScope بيعمل بالظبط التلات أوضاع اللي وصفتها: own · branch · all.
وهي مطبَّقة فعليًا في ~٣٥ موضع — كلها في المعامل (LIS) والعيادات (Clinic).
وفي المخازن: صفر موضع. وفي المحاسبة والخزن: صفر. وفي المبيعات والمشتريات ونقاط البيع: صفر.
معنى ده عمليًا: شاشة الأدوار عندك فيها قايمة اختيار «نطاق البيانات» — تختار «مخازنه بس» لأمين المخزن، بتتحفظ في قاعدة البيانات، وما بتعملش أي حاجة. زينة بحتة.
| العمود | موجود؟ | بيُستخدم في أي استعلام؟ |
|---|---|---|
| warehouses.manager_id | أيوه — وبتحدّده من الشاشة | ولا مرة. بحثت: بيظهر في fillable والعلاقة وبس |
| petty_cash.custodian_id | أيوه — وبتحدّده من الشاشة | ولا مرة. نفس الحكاية |
دي أخطر نقطة في التحليل كله: إنت بتعيّن أمين على مخزن، وبتعيّن أمين على خزنة، والنظام بيحفظ التعيين ويعرضه — وما بيقيّد بيه حاجة. أي حد شايف الشاشة دي هيفترض إن فيها حماية.
وكمان مفيش جدول ربط — يعني «الأمين ده مسؤول عن ٣ مخازن» مش قابلة للتعبير أصلًا في قاعدة البيانات الحالية. manager_id واحد لكل مخزن، وده الاتجاه الغلط.
الكود في شاشة الأرصدة بالحرف:
data = data.filter((i) => whIds.includes(i.warehouse_id)); // ← بتخبّي this.totalRecords.set(res.meta.total); // ← والعدّاد بيفضل الرقم الكامل
كل الصفوف بتوصل المتصفح فعلًا، وبعدين الواجهة بتخبّي بعضها. أي حد يفتح تبويب الشبكة في المتصفح أو يستخدم توكنه مباشرة يشوف الكل.
ونفس الحاجة في اختيار المخزن نفسه: الدالة اللي اسمها «هات مخازن الفرع الحالي» بتجيب كل المخازن من السيرفر وبتفلتر في المتصفح — والقاعدة دي مكرّرة ~١٠ مرات بقواعد مختلفة (بعضها بيسمح بالمورد اللي مالوش فرع وبعضها لأ).
الجانب الإيجابي: ده دليل إن نية المنتج موجودة صح — حد فكّر إن الأمين يشوف مخازنه. بس نُفِّذت في المكان الوحيد اللي مستحيل تتنفّذ فيه.
اتأكدت بنفسي من InventoryIssueController::index. الشرط الوحيد على الاستعلام:
->where('company_id', auth()->user()->company_id)
وwarehouse_id موجود — بس كـفلتر اختياري المستخدم بيحطه بنفسه، مش قيد. تشيله تشوف الكل.
النتيجة النهائية: أمين مخزن الفرع الأول شايف كل أذون الصرف والاستلام والتحويلات والتسويات والجرد بتاعة كل الفروع وكل المخازن في الشركة — ويقدر يفتحها. الحاجة الوحيدة اللي بتتقيّد له هي المخزن اللي يختاره وهو بيعمل مستند جديد.
الصلاحيات الحالية كلها على مستوى الأفعال: «يقدر يشوف الأرصدة» · «يقدر يعمل إذن صرف». بتقول إيه اللي تعمله، ما بتقولش على أنهي بيانات.
والمطلوب اختيار لكل شركة بين تلات أوضاع:
| الوضع | معناه | الشركة اللي تحتاجه |
|---|---|---|
| أ — موارده بس | يشوف مخازنه/خزنه هو وعملياتها | شركة فيها فروع مستقلة وكل أمين مسؤول عن مخزنه |
| ب — كل الموارد | يشوف كل المخازن/الخزن | شركة صغيرة أو مخزن مركزي، والشفافية مطلوبة |
| ج — بياناته بس | يشوف المستندات اللي هو عملها هو | شركة فيها أكتر من أمين على نفس المخزن ومحتاجة مسؤولية فردية |
والتوسيع اللي إنت قلته يغيّر شكل الحل مش حجمه: لو عملنا المخازن لوحدها، هنعيد نفس الشغل للخزن، وتالت للحسابات البنكية، ورابع لمراكز التكلفة. المطلوب آلية واحدة والمخازن والخزن أول تطبيقين ليها.
DataScopeموجود ومجرَّب ومختبَر:
own · branch · all — على عمود roles.data_scopebranch — قرار صحown ← where(created_by, user.id) · branch ← whereIn(branch_id, فروع المستخدم) · all ← بلا قيدapplyReportBranch() للاستعلامات الخام (التقارير)operatingBranchId() بيوسم السجلات الجديدة بفرع المستخدميعني نص الشغل معمول بجودة. المشكلة إنه مربوط بمودولين بس.
| السلوك | ليه بيفشل مفتوح |
|---|---|
| «الأوسع يكسب» بين الأدوار | لو المستخدم عنده دورين، الأوسع بيكسب. وكل الأدوار القديمة اتحوّلت لـall في migration سابق للتوافق. يعني تحطّ لأمين المخزن دور مقيّد + أي دور قديم ← القيد يتبخّر في صمت. والكود نفسه مكتوب فيه التحذير ده. |
orWhereNull(branch_id) |
السجل اللي مالوش فرع بيبان للكل — تنازل موثَّق وقت الإطلاق، لسه مفتوح. |
| مفيش تعيين = بلا قيد | مستخدم مش معيَّن على أي فرع ← يشوف كل حاجة. القيد اختياري بالتعيين، وغياب التعيين معناه «مفتوح». |
ودي أهم نقطة معمارية في الملف: نقل الآلية دي للخزن بحالتها أسوأ من إننا ما نعملش حاجة — لأنها هتبان إنها بتحمي وهي لأ.
| المورد | ربط بالمستخدم | ربط بالفرع | الحالة |
|---|---|---|---|
| المخازن | manager_id (واحد) | branch_id | العمود ميت · مفيش جدول ربط · تعيين متعدد مستحيل |
| الفروع | branch_user + is_primary | — | الربط الوحيد اللي متعدد ومطبَّق فعلًا — بس في LIS والعيادات |
| الخزن / العهدة | custodian_id (واحد) | branch_id | العمود ميت · شاشة معامل الخزن بتفلتر بالشركة بس |
| الحسابات البنكية | مفيش | bank_account_branch | الجدول موجود — وما بيقيّدش أي قراءة. سابقة شكل مش سابقة تطبيق |
| مراكز التكلفة | مفيش | مفيش | مفيش أي محور تقييد أصلًا |
| نقاط البيع | مفيش | branch_id | اختيار النقطة يدوي بلا ربط بالمستخدم |
| ورديات الكاشير (معامل) | user_id | مطبَّق | المثال الوحيد الصح لوضع «بياناته بس» على السيرفر |
| ورديات نقاط البيع | user_id | branch_id | القايمة بتفلتر بـuser_id اللي العميل بيبعته — مش قيد |
شاشة الخزن في المعامل بتفلتر بـcompany_id وبس. يعني أي كاشير معاه صلاحية العرض بيشوف كل خزن الشركة وأرصدتها.
وفيه عائق أعمق: جدول حركات الخزنة petty_cash_transactions مالوش عمود «مين عمل الحركة» إطلاقًا.
يعني وضع «بياناته بس» غير قابل للبناء للخزن من غير تعديل في قاعدة البيانات. ← قرار رقم ٥.
القناة الوحيدة في النظام كله اللي بتقيّد على السيرفر هي ترويسة X-Branch-Id في المعامل — ومكتوب في الكود صراحةً إنها «تُتجاهل في أي مكان تاني».
من ~٣٨ منتقي مورد في التطبيق، ولا واحد بيتغذّى من مدخل مقيَّد على السيرفر (غير منتقي فروع المعامل). و٢٢ بيتقصّوا في المتصفح — بقواعد مش متطابقة.
وعدم الاتساق داخل نفس الدورة: فاتورة المشتريات بتعرض مخازن مقيّدة، وإذن الاستلام وأمر الشراء في نفس السلسلة يعرضوا كل المخازن. ومرتجع المبيعات مقيّد ومرتجع المشتريات لأ.
TenantAware بيوسم company_id تلقائيًا عند الكتابة — شغّال في كل الموديلات.scopeTenant() جاهز للقراءة — وشبه مستخدم.addGlobalScope عدد مرات استخدامه صفر.
والنتيجة اللي لازم تتقال بصراحة: نفس الفريق اللي كتب DataScope في كل كنترولر عيادات ما كتبهوش ولا مرة في المخازن.
أي آلية تُبنى بنفس الطريقة هتورث نفس المصير: بتشتغل في المكان اللي المطوّر افتكرها فيه بس.
| المطلوب | الموجود | الناقص | الحكم |
|---|---|---|---|
| وضع «موارده بس» | النية موجودة (فلترة المتصفح) · manager_id · custodian_id | جدول ربط متعدد · تطبيق على السيرفر | الأعمدة ميتة والتعيين المتعدد مستحيل |
| وضع «كل الموارد» | ده الوضع الحالي فعليًا | — | موجود ضمنًا |
| وضع «بياناته بس» | created_by موجود ومملوء في ٥ مستندات مخزنية | تطبيق (صفر استعلام بيستخدمه) | جاهز للمخازن · مستحيل للخزن (مفيش عمود) |
| الاختيار لكل شركة | آلية الإعدادات كاملة وجاهزة | تعريف الإعداد وقراءته | سهل — بس بشرط (قرار ٦) |
| تقييد الصفوف | مفيش — الشرط الوحيد الشركة | ~٣٠ مدخل قراءة + ~٢٠ مدخل تعديل | ده جوهر الشغل |
| تقييد الفتح المباشر | مفيش — getById بينجح لأي مورد | نفس التقييد على show والأفعال | تقييد القوايم لوحده يسيب الباب مفتوح |
warehouse_id بس. فآلية DataScope اللي بتقيّد بـbranch_id مالهاش عمود تشاور عليه. محور التقييد هنا المخزن مش الفرع.DataScope بعمود واحد مش قادر يعبّر عن ده.data_scope الموجودوضع على الدور + جداول ربط لكل مورد.
له: الآلية موجودة ومجرَّبة في ٣٥ موضع، وليها نسخة للتقارير، ومختبَرة، وبتوصل للواجهة خلاص.
عليه: قيمة واحدة لكل الموارد — مش قادرة تقول «مخازنه بس بس كل الخزن». والتلات مسارات اللي بتفشل مفتوحة.
scopableجدول واحد متعدّد الأشكال + إعداد وضع لكل نوع مورد.
له: جدول واحد لكل الموارد.
عليه: بيبقى الأسلوب التالت للربط في نظام فيه اتنين خلاص، ومفيش أي أداة حالية بتعرف تبني الاستعلام ده، وبيهمّش الآلية الشغّالة بدل ما يركّب عليها.
نمط (أ) في التطبيق + جداول ربط محدَّدة لكل مورد + إعداد وضع لكل نوع مورد.
| العنصر | من أين | ليه |
|---|---|---|
| دوال التطبيق | نوسّع DataScope الموجودة | مجرَّبة، وليها نسخة تقارير، ومختبَرة — ما نبنيش تانية |
| التعيين | warehouse_user · petty_cash_user … على نمط branch_user | نفس الأسلوب الموجود مرتين خلاص — مش أسلوب تالت |
| الوضع | إعداد لكل نوع مورد عبر آلية الإعدادات | لأن الشركة عايزة تختار لكل مورد على حدة |
| محور التقييد | المورد (المخزن/الخزنة) مش الفرع | مستندات المخازن مالهاش عمود فرع أصلًا |
| ومش نورّث | التلات مسارات المفتوحة | «الأوسع يكسب» و«مالوش فرع يبان للكل» و«مفيش تعيين = مفتوح» — الثلاثة تتقفل |
سألت: فين المكان اللي لو حد ضاف شاشة جديدة السنة الجاية تتحمي تلقائيًا مش لما يفتكر؟
الجواب الصادق: مفيش مكان زي كده في النظام الحالي.
DB::table خام — والـglobal scope ما بيلمسهاش. يعني هيغطّي نص ويسيب نص، وده أخطر من الوضع الحالي لأنه أصعب في الملاحظة.scopeTenant()؟ موجود ومعمول علشان ده بالظبط — والكنترولرات لسه بتكتب الشرط بالإيد. دليل حاسم إن «اختياري» ≠ «افتراضي».index مشتركة.فأفضل حاجة متاحة تلات طبقات: (١) global scope للقراءات العادية + (٢) دالة صريحة للاستعلامات الخام + (٣) اختبار ثابت (invariant) بيمرّ على كل مسار في المودول ويتأكد إن مستخدم مقيَّد بيرجّعله صفوف مفلترة.
والطبقة التالتة هي الوحيدة اللي بتمسك المطوّر الناسي. من غيرها، الطبقتين الأولانيتين هيتحلّلوا بنفس الطريقة اللي scopeTenant() وmanager_id اتحلّلوا بيها. وفي النظام سوابق للاختبارات النوع ده.
| الحالة | المعالجة |
|---|---|
| مستخدم بلا أي تعيين | النهاردة = يشوف كل حاجة. لو الوضع «موارده بس» فالصح إنه ما يشوفش حاجة. ← قرار ١، وهو أهم قرار في الملف |
| مستخدم بدورين | «الأوسع يكسب» بيلغي القيد. لازم يتحوّل لـالأضيق يكسب — أو تعيين مورد يقرّر بغض النظر عن الدور |
| مورد مالوش فرع | النهاردة بيبان للكل. مع التقييد بالمورد المشكلة دي بتختفي أصلًا |
| تحويل بين مخزنين | مخزن مصدر ومخزن وجهة — يبان لو أي واحد منهم في نطاقه؟ ولا الاتنين؟ الأول أنسب عمليًا |
| مستند قديم على مورد اتشال من نطاقه | لازم يفضل مقروء وإلا الأرشيف يقع. النظام عنده سابقة: الفاتورة بتجيب المخزن بالمعرّف علشان الخانة ما تبانش فاضية |
| الإجماليات وبطاقات المجاميع | النهاردة على مستوى الشركة كلها. لازم تتقيّد بنفس النطاق وإلا الأمين يشوف قيمة مخزون مش بتاعته |
| عدّاد الصفحات | النهاردة العدّاد رقم السيرفر الكامل والصفوف مقصوصة — صفحات فاضية. بيتحلّ لوحده لما التقييد ينتقل للسيرفر |
| الوظائف الخلفية والترحيل | فيه عمليات بتجري بلا مستخدم مسجّل — أي تقييد يفترض وجود مستخدم هيقع أو يفلتر كل حاجة. لازم استثناء صريح |
| الصلاحية الجديدة ما بتبانش | النظام بيخزّن المستخدم وصلاحياته في المتصفح عند الدخول — التعيين الجديد مش هيبان غير بعد خروج ودخول |
| المالك والمدير | لازم يعدّوا التقييد صراحةً وإلا الإدارة تتقفل على نفسها |
السابقة موجودة: تعيين المستخدم على الفروع قايمة متعددة جوّه نافذة تعديل المستخدم. المخازن والخزن يبقوا جنبها.
تفصيلة مهمة: قايمة المخازن تعرض مخازن الفروع المختارة فوق بس — ترابط بين خانتين في نفس النافذة. لو حطّينا التعيين في شاشة المخزن نفسه، الترابط ده مش هيبقى ممكن.
| الرقم | التاريخ | المخزن | القيمة |
|---|---|---|---|
| GDN-000046 | 2026-07-31 | مخزن الخام | 4,200 |
| GDN-000047 | 2026-07-31 | مخزن أكتوبر مش بتاعه | 18,900 |
| GDN-000051 | 2026-08-01 | مخزن التام - الإسكندرية مش بتاعه | 66,400 |
| الرقم | التاريخ | المخزن | القيمة |
|---|---|---|---|
| GDN-000046 | 2026-07-31 | مخزن الخام | 4,200 |
| GDN-000052 | 2026-08-02 | مخزن التام | 7,150 |
ملاحظة على الحجم: دي أكبر من أي حزمة عملناها. ~٥٠ مدخل، ٩ موارد، وطبقة أمان. والأهم: حزمة نصّها ماشية أخطر من حزمة ما بدأتش — لأن الشاشة تبان مقيّدة وهي لأ. عشان كده الترتيب ده بيقفل مورد واحد كامل قبل ما يبدأ التاني.
| WP | النطاق | المستودع | ملاحظات |
|---|---|---|---|
| WP1 أساس |
محرّك النطاق — توسيع DataScope ليقيّد بالمورد مش بالفرع، بمحور مورد + عمود منشئ اختياريين، ودعم عمودين (التحويلات)، والأضيق يكسب، ومفيش تعيين = مفيش صفوف. |
BE | بلا أي مدخل مربوط — محرّك واختبارات بس |
| WP2 | التعيين + الإعدادات — warehouse_user وpetty_cash_user على نمط branch_user · إعداد وضع لكل نوع مورد · نقل manager_id/custodian_id الحاليين للجداول الجديدة (بحذر: عمود ميت ممكن يكون فيه بيانات غلط) |
BE | migration · الافتراضي «كل الموارد» = السلوك الحالي |
| WP3 حرج |
اختبار الثبات — يمرّ على كل مسار في المودول ويتأكد إن مستخدم مقيَّد بيرجّعله صفوف مفلترة. ويفشل لو مدخل جديد اتضاف بلا تقييد. | BE | ده الشيء الوحيد اللي بيمنع التحلّل — قبل الربط مش بعده |
| WP4 | ربط المخازن كامل — ~٣٠ مدخل قراءة + ~٢٠ فعل + الإجماليات + show والفتح المباشر. المورد الأول من الأول للآخر. |
BE | أكبر حزمة |
| WP5 | الواجهة — المخازن — التخلّص من فلترة المتصفح · منتقي مقيَّد من السيرفر · بادج النطاق · اختيار واحد = نص · الحالة بره النطاق | FE | بيشيل ١٢ نسخة مكرّرة |
| WP6 | واجهة التعيين — قايمتين في نافذة المستخدم مع ترابط الفرع↔المخزن + شاشة الإعدادات | FE | |
| WP7 | الخزن — نفس الآلية على الخزن والحسابات البنكية. ووضع «بياناته» يحتاج عمود منشئ على حركات الخزنة (قرار ٥) | BE+FE | مالي — الفلوس أغلى من المخزون |
| WP8 | توحيد قواعد الفرع المكرّرة — الـ~١٠ نسخ المتباعدة تبقى نسخة واحدة | FE | قاعدتك: الإصلاح في الطبقة المشتركة |
| WP9 | باقي الموارد — مراكز التكلفة · نقاط البيع · ورديات نقاط البيع | BE+FE | بعد ما النمط يستقر على مورّدين |
النهاردة: يشوف كل حاجة. والوضع لو «مخازنه بس» فالمنطق الأمني إنه ما يشوفش حاجة.
توصيتي: يفشل مقفول — ما يشوفش حاجة، مع رسالة واضحة «مش معيَّن على أي مخزن، كلّم المدير». آلية أمان بتفشل مفتوحة أسوأ من إنها مش موجودة، لأنها تخلّيك تفتكر إنك محمي. والرسالة الواضحة تخلّي الإعداد الناقص يبان في يوم بدل ما يفضل مستور.
وده بيتطلب حرص وقت التنزيل: لو الوضع اتحوّل قبل التعيين، الناس هتلاقي شاشات فاضية. عشان كده الافتراضي «كل المخازن» والتحويل قرار صريح بعد التعيين.
النهاردة الأوسع يكسب، وكل الأدوار القديمة اتحوّلت لـ«الكل». يعني أمين مقيّد + أي دور قديم = القيد اتبخّر في صمت. والكود نفسه محذّر من ده.
توصيتي: الأضيق يكسب — وتعيين المورد هو الحاكم مهما كان الدور. ده الاختيار اللي يخلّي الآلية تفشل مقفولة، وبيمنع أخطر سيناريو: إضافة دور عادي بترفع القيد.
توصيتي: المحرّك عام من الأول (WP1)، والربط المخازن أولًا لوحدها. مورد واحد من الأول للآخر بيثبت النمط ويطلّع المفاجآت على مورد مخزون — أرخص من مفاجأة على الفلوس. وبعدين الخزن تاخد نفس الآلية في وقت أقصر.
تلات اختيارات، وكل واحد بيسرّب قدر مختلف: شاشة فاضية (شكلها عيب) · «بره صلاحيتك» (بيأكّد إن السجل موجود) · «غير موجود».
توصيتي: «غير موجود» — ٤٠٤ بالسلوك الطبيعي للنظام. بيسرّب أقل حاجة (مش فارقة عن سجل متمسوح)، وبيستخدم مسارات الخطأ الموجودة.
بشرط واحد: القوايم ما تعرضش أبدًا لينك هيرجّع ٤٠٤ — وده بيتحقّق لوحده لما تقييد القوايم ينزل على السيرفر.
petty_cash_transactions مالهوش عمود «مين عمل الحركة». فالوضع ده غير قابل للبناء للخزن حاليًا. (المستندات المخزنية الخمسة عندها العمود ومملوء.)
توصيتي: نضيف العمود. عمود منشئ على حركة نقدية مش رفاهية — هو أثر المراجعة الأساسي، وغيابه فجوة في المحاسبة مش في الصلاحيات بس. والحركات القديمة تبقى بلا منشئ (وده صادق: إحنا فعلًا ما نعرفش).
توصيتي: مخفي في الاستخدام اليومي، ظاهر في مكانين بس.
الأمين لازم يفكّر «دي شاشة مخزني»، مش «دي شاشة كل المخازن ناقص اللي ممنوع أشوفه». وسوابق النظام كلها ماشية كده.
بس الإخفاء الكامل بيفشل في نقطتين: (١) الغموض — قايمة مقيّدة بلا أي علامة مش فارقة عن «الشركة عندها مخزن واحد»، فبادج صغير غير تفاعلي بيحلّها. (٢) الإدارة — المدير لازم يشوف الإعداد. وأبدًا ما نعرضش للمستخدم العادي مبدّل للوضع — نعرضله نطاقه كحقيقة بس.
manager_id وcustodian_id فيهم بيانات محتمَلة اتحدّدت من الشاشة على مدى شهور، ومحدش اتأثر بيها لأنها مش بتعمل حاجة.
توصيتي: نحوّلها للجداول الجديدة كنقطة بداية، بس نعرضها للمراجعة قبل ما تبقى قيد. عمود محدش اعتمد عليه ممكن يكون فيه أي حاجة. تحويله لقيد نافذ في السكات = ناس تتقفل بره مخازنها فجأة. نحوّل، نعرض القايمة، وإنت تعتمد.
اللي بتطلبه مبني ومجرَّب — في مودولين من تسعة. DataScope بيعمل التلات أوضاع اللي وصفتها بالظبط، في ~٣٥ موضع، كلهم في المعامل والعيادات. وفي المخازن والخزن: صفر.
والوضع الحالي أسوأ من «مفيش ميزة»: عمود «مسؤول المخزن» وعمود «أمين الخزنة» موجودين وبتحدّدهم من الشاشة وما بيقيّدوا أي حاجة · والفلترة الموجودة في المتصفح بعد ما البيانات وصلت · وقوايم المستندات مش متفلترة إطلاقًا، فأمين المخزن شايف كل أذون الشركة، وأمين الخزنة شايف كل الخزن وأرصدتها.
والتوصية المعمارية: نوسّع محرّك DataScope الموجود (ما نبنيش تاني) + جداول ربط لكل مورد على نمط branch_user الموجود + إعداد وضع لكل نوع مورد — ومن غير ما نورّث التلات مسارات اللي بتفشل مفتوحة.
وحقيقة لازم تتقال: مفيش في النظام مكان يخلّي شاشة جديدة تتحمي تلقائيًا — مفيش global scope ولا طبقة مستودعات، وتقييد الشركة نفسه مكتوب بالإيد ٨ مرات في كنترولر واحد. عشان كده اختبار الثبات (WP3) مش رفاهية — هو الشيء الوحيد اللي بيمنع الآلية تتحلّل زي ما scopeTenant() والعمودين الميتين اتحلّلوا.
تحليل المرحلة ١ — مفيش كود ولا migration اتكتب · مسارَي تحليل متوازيين بموديل Fable + تحقّق مباشر من الكود وقاعدة البيانات · ٦ أغسطس ٢٠٢٦ · moonui2
مستنّي قراراتك — وأهمهم ١ و٢، لأنهم بيحدّدوا هل الآلية تفشل مقفولة ولا مفتوحة.