تحليل معماري — المرحلة ١ · بلا كود · تحليل بموديل Fable

صلاحيات على مستوى البيانات: مين يشوف بيانات مين

طلبك: أمين المخزن يشوف مخازنه وعمليات مخازنه — وبعض الشركات تحب يشوف كل المخازن، وبعضها بياناته هو بس. وبعدين وسّعت: ده على مستوى النظام كله، والخزن نفس القصة.

التحليل طلّع حقيقة أهم من الطلب: النظام حاليًا مافيهوش حماية بيانات في المخازن ولا الخزن على الإطلاق — وفيه آلية نص مبنية بتوحي إنها بتحمي. أمين المخزن دلوقتي بيشوف كل أذون الصرف في الشركة كلها، وأمين الخزنة بيشوف كل الخزن وأرصدتها.

موارد قابلة للتقييد: ٩ مداخل محتاجة تقييد: ~٥٠ آلية موجودة وشغّالة في: مودولين من ٩ ٦ أغسطس ٢٠٢٦ · moonui2 الحالة: مستنّي قراراتك

٠ الخلاصة التنفيذية — أربع حقائق

١. الآلية اللي إنت بتطلبها موجودة ومبنية وشغّالة — بس في مودولين بس

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 موجود — بس كـفلتر اختياري المستخدم بيحطه بنفسه، مش قيد. تشيله تشوف الكل.

النتيجة النهائية: أمين مخزن الفرع الأول شايف كل أذون الصرف والاستلام والتحويلات والتسويات والجرد بتاعة كل الفروع وكل المخازن في الشركة — ويقدر يفتحها. الحاجة الوحيدة اللي بتتقيّد له هي المخزن اللي يختاره وهو بيعمل مستند جديد.

مواضع التقييد في المخازن
0
مواضع التقييد في الخزن
0
مواضع شغّالة (LIS + عيادات)
~35
أعمدة ملكية ميتة
2
global scopes في النظام
0

١ المشكلة

الصلاحيات الحالية كلها على مستوى الأفعال: «يقدر يشوف الأرصدة» · «يقدر يعمل إذن صرف». بتقول إيه اللي تعمله، ما بتقولش على أنهي بيانات.

والمطلوب اختيار لكل شركة بين تلات أوضاع:

الوضعمعناهالشركة اللي تحتاجه
أ — موارده بسيشوف مخازنه/خزنه هو وعملياتهاشركة فيها فروع مستقلة وكل أمين مسؤول عن مخزنه
ب — كل الموارديشوف كل المخازن/الخزنشركة صغيرة أو مخزن مركزي، والشفافية مطلوبة
ج — بياناته بسيشوف المستندات اللي هو عملها هوشركة فيها أكتر من أمين على نفس المخزن ومحتاجة مسؤولية فردية

والتوسيع اللي إنت قلته يغيّر شكل الحل مش حجمه: لو عملنا المخازن لوحدها، هنعيد نفس الشغل للخزن، وتالت للحسابات البنكية، ورابع لمراكز التكلفة. المطلوب آلية واحدة والمخازن والخزن أول تطبيقين ليها.

٢ الوضع الحالي — بالكود

٢-١ الآلية الموجودة: DataScope

موجود ومجرَّب ومختبَر:

يعني نص الشغل معمول بجودة. المشكلة إنه مربوط بمودولين بس.

🔴 لكن فيه تلات طرق الآلية دي بتفشل بيهم مفتوحة

السلوكليه بيفشل مفتوح
«الأوسع يكسب» بين الأدوار لو المستخدم عنده دورين، الأوسع بيكسب. وكل الأدوار القديمة اتحوّلت لـ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_idbranch_idالقايمة بتفلتر بـuser_id اللي العميل بيبعته — مش قيد

٢-٣ الخزن تحديدًا — لأن الفلوس أغلى

شاشة الخزن في المعامل بتفلتر بـcompany_id وبس. يعني أي كاشير معاه صلاحية العرض بيشوف كل خزن الشركة وأرصدتها.

وفيه عائق أعمق: جدول حركات الخزنة petty_cash_transactions مالوش عمود «مين عمل الحركة» إطلاقًا. يعني وضع «بياناته بس» غير قابل للبناء للخزن من غير تعديل في قاعدة البيانات. ← قرار رقم ٥.

٢-٤ سلسلة القراءة النهاردة — بالكامل

أمين المخزن يفتح الشاشة حارس المسار: عنده الصلاحية؟ ✔ السيرفر: فلتر بالشركة بس كل صفوف الشركة بتوصل المتصفح الواجهة تخبّي بعضها (في شاشتين بس)

القناة الوحيدة في النظام كله اللي بتقيّد على السيرفر هي ترويسة X-Branch-Id في المعامل — ومكتوب في الكود صراحةً إنها «تُتجاهل في أي مكان تاني».

٢-٥ إحصاء منتقيات الموارد في التطبيق كله

مصدرها مقيَّد من السيرفر
1
تفلتر في المتصفح
22
مش مقيّدة إطلاقًا
15+

من ~٣٨ منتقي مورد في التطبيق، ولا واحد بيتغذّى من مدخل مقيَّد على السيرفر (غير منتقي فروع المعامل). و٢٢ بيتقصّوا في المتصفح — بقواعد مش متطابقة.

وعدم الاتساق داخل نفس الدورة: فاتورة المشتريات بتعرض مخازن مقيّدة، وإذن الاستلام وأمر الشراء في نفس السلسلة يعرضوا كل المخازن. ومرتجع المبيعات مقيّد ومرتجع المشتريات لأ.

٢-٦ نمط تعدّد الشركات — وده اللي بيحدّد مصير أي آلية جديدة

والنتيجة اللي لازم تتقال بصراحة: نفس الفريق اللي كتب DataScope في كل كنترولر عيادات ما كتبهوش ولا مرة في المخازن. أي آلية تُبنى بنفس الطريقة هتورث نفس المصير: بتشتغل في المكان اللي المطوّر افتكرها فيه بس.

٣ المطلوب

  1. تقييد على مستوى الصف لكل مورد: المخازن والخزن أولًا، والباقي بنفس الآلية.
  2. تعيين متعدد: الأمين ده مسؤول عن مخزن أو أكتر.
  3. الوضع اختيار لكل شركة ولكل نوع مورد: موارده / كل الموارد / بياناته.
  4. التقييد يشمل الصفوف — مش المنتقيات بس. وده اللي ناقص تمامًا النهاردة.
  5. ويشمل الفتح المباشر بالرابط — مش القوايم بس.

٤ الفجوة

المطلوبالموجودالناقصالحكم
وضع «موارده بس»النية موجودة (فلترة المتصفح) · manager_id · custodian_idجدول ربط متعدد · تطبيق على السيرفرالأعمدة ميتة والتعيين المتعدد مستحيل
وضع «كل الموارد»ده الوضع الحالي فعليًاموجود ضمنًا
وضع «بياناته بس»created_by موجود ومملوء في ٥ مستندات مخزنيةتطبيق (صفر استعلام بيستخدمه)جاهز للمخازن · مستحيل للخزن (مفيش عمود)
الاختيار لكل شركةآلية الإعدادات كاملة وجاهزةتعريف الإعداد وقراءتهسهل — بس بشرط (قرار ٦)
تقييد الصفوفمفيش — الشرط الوحيد الشركة~٣٠ مدخل قراءة + ~٢٠ مدخل تعديلده جوهر الشغل
تقييد الفتح المباشرمفيش — getById بينجح لأي موردنفس التقييد على show والأفعالتقييد القوايم لوحده يسيب الباب مفتوح

عوائق بنيوية لازم تتعرف قبل التصميم

  1. مستندات المخازن مالهاش عمود فرع إطلاقًا — عندها warehouse_id بس. فآلية DataScope اللي بتقيّد بـbranch_id مالهاش عمود تشاور عليه. محور التقييد هنا المخزن مش الفرع.
  2. التحويلات عندها مخزنين (من / إلى) — وتصميم DataScope بعمود واحد مش قادر يعبّر عن ده.
  3. الأرصدة والحركات مالهاش «مين عملها» — فوضع «بياناته بس» غير قابل للتطبيق عليها مبدئيًا، وتقييد المخزن هو الوحيد اللي له معنى.
  4. حركات الخزنة مالهاش عمود منشئ — وضع «بياناته» للخزن محتاج تعديل قاعدة بيانات.

٥ المعمارية — الحكم بين خيارين

(أ) نعمّم data_scope الموجود

وضع على الدور + جداول ربط لكل مورد.

له: الآلية موجودة ومجرَّبة في ٣٥ موضع، وليها نسخة للتقارير، ومختبَرة، وبتوصل للواجهة خلاص.

عليه: قيمة واحدة لكل الموارد — مش قادرة تقول «مخازنه بس بس كل الخزن». والتلات مسارات اللي بتفشل مفتوحة.

(ب) جدول ربط عام scopable

جدول واحد متعدّد الأشكال + إعداد وضع لكل نوع مورد.

له: جدول واحد لكل الموارد.

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

التوصية: لا ده ولا ده بالكامل — تركيبة

نمط (أ) في التطبيق + جداول ربط محدَّدة لكل مورد + إعداد وضع لكل نوع مورد.

العنصرمن أينليه
دوال التطبيقنوسّع DataScope الموجودةمجرَّبة، وليها نسخة تقارير، ومختبَرة — ما نبنيش تانية
التعيينwarehouse_user · petty_cash_user … على نمط branch_userنفس الأسلوب الموجود مرتين خلاص — مش أسلوب تالت
الوضعإعداد لكل نوع مورد عبر آلية الإعداداتلأن الشركة عايزة تختار لكل مورد على حدة
محور التقييدالمورد (المخزن/الخزنة) مش الفرعمستندات المخازن مالهاش عمود فرع أصلًا
ومش نورّثالتلات مسارات المفتوحة«الأوسع يكسب» و«مالوش فرع يبان للكل» و«مفيش تعيين = مفتوح» — الثلاثة تتقفل

🔴 المكان اللي المطوّر لازم يقع فيه في الفخ — والحقيقة المرّة

سألت: فين المكان اللي لو حد ضاف شاشة جديدة السنة الجاية تتحمي تلقائيًا مش لما يفتكر؟

الجواب الصادق: مفيش مكان زي كده في النظام الحالي.

فأفضل حاجة متاحة تلات طبقات: (١) global scope للقراءات العادية + (٢) دالة صريحة للاستعلامات الخام + (٣) اختبار ثابت (invariant) بيمرّ على كل مسار في المودول ويتأكد إن مستخدم مقيَّد بيرجّعله صفوف مفلترة.

والطبقة التالتة هي الوحيدة اللي بتمسك المطوّر الناسي. من غيرها، الطبقتين الأولانيتين هيتحلّلوا بنفس الطريقة اللي scopeTenant() وmanager_id اتحلّلوا بيها. وفي النظام سوابق للاختبارات النوع ده.

٦ الحالات الحدّية

الحالةالمعالجة
مستخدم بلا أي تعيينالنهاردة = يشوف كل حاجة. لو الوضع «موارده بس» فالصح إنه ما يشوفش حاجة. ← قرار ١، وهو أهم قرار في الملف
مستخدم بدورين«الأوسع يكسب» بيلغي القيد. لازم يتحوّل لـالأضيق يكسب — أو تعيين مورد يقرّر بغض النظر عن الدور
مورد مالوش فرعالنهاردة بيبان للكل. مع التقييد بالمورد المشكلة دي بتختفي أصلًا
تحويل بين مخزنينمخزن مصدر ومخزن وجهة — يبان لو أي واحد منهم في نطاقه؟ ولا الاتنين؟ الأول أنسب عمليًا
مستند قديم على مورد اتشال من نطاقهلازم يفضل مقروء وإلا الأرشيف يقع. النظام عنده سابقة: الفاتورة بتجيب المخزن بالمعرّف علشان الخانة ما تبانش فاضية
الإجماليات وبطاقات المجاميعالنهاردة على مستوى الشركة كلها. لازم تتقيّد بنفس النطاق وإلا الأمين يشوف قيمة مخزون مش بتاعته
عدّاد الصفحاتالنهاردة العدّاد رقم السيرفر الكامل والصفوف مقصوصة — صفحات فاضية. بيتحلّ لوحده لما التقييد ينتقل للسيرفر
الوظائف الخلفية والترحيلفيه عمليات بتجري بلا مستخدم مسجّل — أي تقييد يفترض وجود مستخدم هيقع أو يفلتر كل حاجة. لازم استثناء صريح
الصلاحية الجديدة ما بتبانشالنظام بيخزّن المستخدم وصلاحياته في المتصفح عند الدخول — التعيين الجديد مش هيبان غير بعد خروج ودخول
المالك والمديرلازم يعدّوا التقييد صراحةً وإلا الإدارة تتقفل على نفسها

٧ معاينة الواجهة

٧-١ تعيين الأمين على مخازنه — في شاشة المستخدم

السابقة موجودة: تعيين المستخدم على الفروع قايمة متعددة جوّه نافذة تعديل المستخدم. المخازن والخزن يبقوا جنبها.

جديد نافذة تعديل مستخدم — قايمتين جديدتين تحت الفروع
تعديل مستخدم — محمود أمينحفظ · إلغاء
الأدوار
أمين مخزن
الفروع (موجودة)
الفرع الرئيسي ★ فرع أكتوبر
المخازن المسؤول عنها ◄ جديد
مخزن الخام مخزن التام
تعرض مخازن الفروع المختارة فوق بس
الخزن المسؤول عنها ◄ جديد
خزنة الفرع الرئيسي

تفصيلة مهمة: قايمة المخازن تعرض مخازن الفروع المختارة فوق بس — ترابط بين خانتين في نفس النافذة. لو حطّينا التعيين في شاشة المخزن نفسه، الترابط ده مش هيبقى ممكن.

٧-٢ الوضع لكل شركة — في الإعدادات

جديد الإعدادات ← المخزون — إعداد لكل نوع مورد
الإعدادات ← المخزون🔒 يتطلب صلاحية إدارة إعدادات المخزون
نطاق بيانات المخازن
مخازنه المعيَّن عليها بس ▾
يحدّد أي مخازن وأي عمليات يشوفها المستخدم. «كل المخازن» = السلوك الحالي.
نطاق بيانات الخزن
كل الخزن ▾
مستقل عن المخازن — شركة تقدر تقيّد المخازن وتسيب الخزن مفتوحة

٧-٣ شاشة الأمين — قبل / بعد

قبل أمين مخزن الخام بيشوف كل أذون الشركة
أذون الصرفالمخزن: الكل ▾
الرقمالتاريخالمخزنالقيمة
GDN-0000462026-07-31مخزن الخام4,200
GDN-0000472026-07-31مخزن أكتوبر مش بتاعه18,900
GDN-0000512026-08-01مخزن التام - الإسكندرية مش بتاعه66,400
⚠ الشرط الوحيد على الاستعلام هو الشركة — وقيم مخازن تانية ظاهرة له
بعد السيرفر بيرجّع مخازنه بس — والشاشة بتقول ده بوضوح
أذون الصرفمخزن الخام · مخزن التام المخزن: الكل ▾
الرقمالتاريخالمخزنالقيمة
GDN-0000462026-07-31مخزن الخام4,200
GDN-0000522026-08-02مخزن التام7,150
البادج فوق مش فلتر — بيان بنطاقه، علشان الشاشة المقيّدة ما تتلخبطش مع «الشركة عندها مخزن واحد».
والعدّاد والإجماليات بقت على نطاقه — مش على الشركة.

٧-٤ مورد واحد؟ ما نسألوش

قبل قايمة بخيار واحد
المخزن *
مخزن الخام ▾
قايمة فيها اختيار واحد — كل يوم
بعد نص ثابت مش قايمة معطّلة
المخزن
مخزن الخام
القايمة المعطّلة شكلها «باظت» — النص شكله «حقيقة»

٨ خطة التنفيذ

ملاحظة على الحجم: دي أكبر من أي حزمة عملناها. ~٥٠ مدخل، ٩ موارد، وطبقة أمان. والأهم: حزمة نصّها ماشية أخطر من حزمة ما بدأتش — لأن الشاشة تبان مقيّدة وهي لأ. عشان كده الترتيب ده بيقفل مورد واحد كامل قبل ما يبدأ التاني.

WPالنطاقالمستودعملاحظات
WP1
أساس
محرّك النطاق — توسيع DataScope ليقيّد بالمورد مش بالفرع، بمحور مورد + عمود منشئ اختياريين، ودعم عمودين (التحويلات)، والأضيق يكسب، ومفيش تعيين = مفيش صفوف. BEبلا أي مدخل مربوط — محرّك واختبارات بس
WP2 التعيين + الإعداداتwarehouse_user وpetty_cash_user على نمط branch_user · إعداد وضع لكل نوع مورد · نقل manager_id/custodian_id الحاليين للجداول الجديدة (بحذر: عمود ميت ممكن يكون فيه بيانات غلط) BEmigration · الافتراضي «كل الموارد» = السلوك الحالي
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
مستنّي قراراتك — وأهمهم ١ و٢، لأنهم بيحدّدوا هل الآلية تفشل مقفولة ولا مفتوحة.