أزرار «التحويل / الإنشاء عبر المستندات» والصلاحيات

هل زر «تحويل عرض السعر لأمر بيع» (وأخواته) مربوط بصلاحية إنشاء المستند الهدف؟ — فحص Opus لكل زر (FE) وكل endpoint (BE). تحليل فقط، بدون كود.

moonui2 · hazemdev2فحص Opus (BE+FE)Phase 1 — بانتظار المالك

١. المشكلة

المالك عايز يمنع مستخدم (حازم) من تحويل عرض السعر لأمر بيع عن طريق سحب صلاحية «إنشاء أمر بيع». لكن زر «التحويل» (السهم الأزرق) بيفضل ظاهر رغم سحب الصلاحية. السؤال: هل الزر مربوط بصلاحية؟ وهل فيه صلاحية «إنشاء أمر بيع» أصلًا؟ ونفس الشيء لباقي أزرار التحويل/الإنشاء.

٢. الوضع الحالي — خبر مطمئن

الباك محمي بالكامل ✅ — والصلاحية موجودة فعلًا. كل زر تحويل/إنشاء الـendpoint بتاعه بيفرض صلاحية إنشاء المستند الهدف. تحويل-لأمر تحديدًا بيتطلب sales.orders.create بالظبط (SalesOrderController.php:33). فلو المستخدم مالوش الصلاحية → الـAPI بيرجّع 403. الثغرة الوحيدة: زر الفرونت مش بيختفي (مفيش *appCan)، فبيدوسه ويجيله رسالة خطأ بدل ما الزر يكون مخفي.

الباك (BE) — كل تحويل/إنشاء بيفرض صلاحية المستند الهدف

العمليةالـendpoint / الميثودالصلاحية المفروضة
عرض سعر → أمر بيعSalesOrderController::convertFromQuotation :33sales.orders.create
أمر بيع → فاتورةSalesInvoiceController::createFromOrder :41sales.invoices.create
أمر بيع → إذن تسليمSalesDeliveryNoteController::createFromOrder :28sales.delivery_notes.create
أمر شراء → إذن استلام (GRN)PurchaseGrnController::store :46purchases.grns.create
أمر شراء → فاتورة شراءPurchaseBillController::createFromOrder :43purchases.bills.create
GRN → فاتورة شراءPurchaseBillController::createFromGrn :43purchases.bills.create
طلب شراء → أمر شراءPurchaseOrderController::convertFromRequest :57purchases.requests.convert (صلاحية تحويل مخصّصة — مش orders.create)

الفرونت (FE) — الأزرار مش مربوطة بصلاحية

الزرالمكانمربوط بـ*appCan؟
عرض سعر → أمر بيع (السهم الأزرق) — حالتكquotations.component.html:141❌ مكشوف (المفروض sales.orders.create)
أمر بيع → إذن تسليمsales/orders…html:138❌ مكشوف
أمر بيع → فاتورةsales/orders…html:148❌ مكشوف
أمر شراء → GRNpurchases/orders…html:145❌ مكشوف
أمر شراء → فاتورةpurchases/orders…html:158❌ مكشوف
GRN → فاتورةgrns.component.html:82❌ مكشوف
طلب شراء → أمر شراءrequests…html:103✓ مربوط (purchases.requests.convert)

٣. الفجوة (الإجابة الدقيقة على حالتك)

لو شِلت sales.orders.create من حازم:
  • الباك: ✅ بيرفض التحويل (403) — البضاعة محمية فعلًا، مفيش أمر بيع هيتعمل.
  • الفرونت: ❌ الزر مش بيختفي — فحازم بيشوفه، بيدوسه، وييجيله رسالة خطأ (مزعج، بس مفيش خرق أمني).
يعني: المنع بيشتغل فعليًّا (الباك)، بس تجربة المستخدم وحشة (الزر ظاهر لحد ما يتضغط). الإصلاح فرونت بحت.
تصحيح لملاحظة سابقة في الـKB: كنا سجّلنا إن الـendpoints دي «مكشوفة» — الفحص أثبت العكس: كلها بتفرض صلاحية المستند الهدف (لأن كل ميثود تحويل موجودة في كونترولر المستند الهدف). الثغرة الوحيدة أزرار الفرونت.

٤. الملفات المتأثرة

ملاحظة: الأزرار markup لكل شاشة (مفيش مكوّن تحويل مشترك)، فالإصلاح per-button — بس الآلية المشتركة هي توجيه *appCan نفسه.

٥. الحالات الحدّية

٦. خطة التنفيذ

1
إخفاء أزرار التحويل/الإنشاء الـ٦ حسب صلاحية المستند الهدف — إضافة *appCan="'<target>.create'" على كل زر (فرونت فقط). صغير ومباشر، بناء أخضر، اختبار يدوي بدور بدون sales.orders.create.
2
(اختياري — قرار مالك) إعادة ربط تحويل طلب الشراء لأمر شراء بـpurchases.orders.create بدل/إضافةً لـpurchases.requests.convert.
3
(اختياري — خارج النطاق) فحص تباين اسم create-delivery + الكود الميت createGrn — إصلاحات نظافة منفصلة.

٧. قرارات تحتاج المالك

القرارالخياراتالتوصية
نطاق الإصلاح(أ) الـ٦ أزرار [موصى] · (ب) بس تحويل-لأمر (حالتك)(أ) — نفس الفجوة في الستة، نقفلها كلها مرة واحدة.
تحويل طلب الشراء(أ) سيبها requests.convert · (ب) اربطها بـorders.create كمان(أ) — متّسقة أصلًا؛ (ب) لو عايز نفس صلاحية إنشاء الأمر تتحكّم.

٨. معاينة (WP1 — يمسّ الـUI)

مثال — صف عرض سعر «مقبول» لمستخدم مالوش sales.orders.create:

قبل — الزر ظاهر (ويدّي 403 عند الضغط) ❌
QT-2026-00006 · مقبول
↦ تحويل لأمرعرض
بعد — زر التحويل مخفي ✅
QT-2026-00006 · مقبول
↦ تحويل لأمرعرض

التطبيق: *appCan="'sales.orders.create'" على زر التحويل. لمّا تسحب الصلاحية من دور حازم → الزر يختفي (والباك أصلًا بيرفض). نفس النمط للـ٦ أزرار.

تحليل قراءة-فقط · فحص Opus لـ BE+FE بمراجع دقيقة · لم يُعدَّل أي كود. بانتظار موافقة المالك قبل أي تنفيذ.