تحليل عميق للكود، الـ UI، الـ UX، والـ Backend — مع خريطة طريق للإصلاح
نظام صلاحيات Moon ERP مبني على Spatie Laravel Permission في الـ backend مع طبقة من الـ frontend logic لفلترة العرض.
| ID | اسم الدور | محمي؟ | عدد المستخدمين | عدد الصلاحيات | التوزيع على المديولز |
|---|---|---|---|---|---|
| 1 | owner | ✅ | 4 | 794 | lis 154 · accounting 116 · hrm 112 · webstore 87 · core 69 · sales 51 · purchases 45 · inventory 39 · cmms 22 · qms 22 · crm 21 · pos 20 · production 19 · nphies 8 · einvoicing 7 · reports 2 |
| 2 | admin | ✅ | 2 | 185 | core 69 · accounting 116 |
| 3 | manager | ✅ | 0 | 187 | hrm 112 · accounting 32 · core 27 · inventory 13 · sales 3 |
| 4 | accountant | ✅ | 1 | 207 | accounting 116 · core 69 · cmms 22 ⚠️ |
| 5 | cashier | ✅ | 1 | 23 | pos 14 · core 5 · sales 4 |
| 6 | employee | ✅ | 1 | 20 | hrm 16 · core 4 |
| 8 | hr_manager | ❌ | 0 | 118 | hrm 112 · core 6 |
| 9 | lab_manager | ❌ | 0 | 160 | lis 154 · core 6 |
hasAnyPermission(['core.users']) بيتطابق مع core.users.view, core.users.create, إلخ. ده بيخلي تعريف الصلاحيات على الـ UI level جاي بشكل granular بس بيتم فحصها بشكل مرن. الفرونت ما يحتاج يعرف كل الـ actions على كل resource.
currentUser signal بيتقرأ من الـ store. الـ permission service و الـ guards و الـ topbar/sidebar كلهم بيبصوا للنفس المصدر. ده ممتاز للـ consistency.
AuthService بيخزن الـ user في localStorage كـ fallback لو الـ /me endpoint فشل. ده بيقلل احتمالية فشل الـ login بعد reload.
CanActivateFn (مش class-based) — أحدث نمط في Angular وأكثر testable.
30 route بيلودوا component بدون permissionGuard — أي user مسجل دخوله يقدر يفتحهم بكتابة الـ URL. الفلترة في الـ sidebar/topbar مش هتحميهم.
الحل: إضافة canActivate: [permissionGuard], data: { permissions: ['lis.'] } على كل route LIS وكذا الباقي.
في users.component.ts الـ roleOptions array هاردكود 6 أدوار (owner, admin, manager, accountant, cashier, employee). لكن الـ DB فيها 8 أدوار حالياً + أي أدوار جديدة بيعملها الأدمن.
النتيجة:
hr_manager, lab_manager) لا تظهر في dropdown إنشاء/تعديل المستخدم.الحل: تحميل الأدوار ديناميكياً عبر RolesService.list() مع translateRoleName() للعرض.
الـ Users form بيستخدم p-select (single) للـ role. لكن الـ API بيدعم مصفوفة roles[]. عند التعديل:
role: user.roles?.length ? user.roles[0] : 'employee',
أي user عنده multi-role، التعديل بياخد أول role بس ويفرشه. ده بيدمر باقي الأدوار بدون تنبيه.
الحل: استخدام p-multiSelect + الباك إند يقبل array.
في users.component.html سطر 23:
<p-tag [value]="('ROLES.' + role.toUpperCase()) | translate" ...>
هذا يبحث عن مفاتيح زي ROLES.OWNER, ROLES.ADMIN — غير موجودة في i18n. الترجمات كلها تحت ROLES.ROLE_NAMES.{name} (lowercase).
النتيجة: الـ tags في صفحة المستخدمين هتعرض المفتاح الخام (ROLES.OWNER) بدل "المالك".
الحل: استخدام translateRoleName() الموجودة بالفعل في roles component (نقلها لـ shared utility).
دور accountant في الـ DB عنده 207 perms = accounting 116 + core 69 + cmms 22.
cmms مش متعلق بالمحاسبة — هو نظام إدارة الصيانة. في الغالب seed خاطئ في RolePermissionSeeder. نفس البرابلم اللي حليته في admin role قبل كده.
النتيجة: المحاسب اللي بيدخل بيشوف مديول CMMS في الـ sidebar من غير ما يحتاجه.
الحل: تاسك للباك إند ينظف الـ RolePermissionSeeder ويفصل CMMS عن باقي الأدوار، أو نشيلها يدوياً من الـ DB.
purchasing.* orphan permissionspurchasing. (purchasing.orders.*) مش مسندة لأي دور. الفرونت إند بيستخدم prefix purchasing. في 3 أماكن (sidebar, topbar, app.routes) لكن عملياً مفيهاش حد عنده الصلاحية. purchases.*.
sales.approval-workflows orphansales. — مش granular للـ approval workflows فعلياً. /sales/customer-statements nav بيتطلب sales.reports فقط (بعد الفيكس)، لكن route guard لسه بيقبل ['sales.reports', 'accounting.reports']. النتيجة: مستخدم بـ accounting بس مش هيشوف الرابط في الـ sidebar لكن يقدر يدخل بكتابة URL. /purchases/supplier-statements.permission.service.ts سطر 22: if (!user) return true; // Not loaded yet — allow all to avoid flash
أثناء الـ flash بين فتح الصفحة وتحميل الـ profile، أي مستخدم يُعتبر super admin. كل عناصر الـ sidebar/topbar تظهر له.
المدة قصيرة (~ms) لكن في حالة شبكة بطيئة هتظهر ثواني، فيها يمكن للمستخدم يضغط روابط مش له صلاحية فيها (تعتمد على route guards اللي مش حماية كافية).
الحل: عرض loading skeleton حتى يحمل الـ user، بدل ما نخلي super admin = unloaded.
صفحة /core/users بتعرض الـ roles بس. مفيش زرار "View permissions" يعرض الـ effective permissions (مجموع كل الأدوار + المباشرة). للأدمن الـ debug صعب: "ليه User X مش شايف Module Y؟"
الحل: إضافة dialog "View Permissions" على كل صف user.
NAV_MODULES من nav-items.config.ts (الـ topbar config). لكن السايدبار له menuItems منفصل في الكود — هيكلة مختلفة. sidebar.component.ts:76-430 (354 سطر). الـ topbar / module-nav يستخدمون nav-items.config.ts. التغييرات في مكان واحد لازم تتكرر يدوياً في الثاني → سبب الـ bugs اللي ظهرت في الـ session ده.menuItems من سايدبار، استخدام NAV_MODULES في الاتنين.
core.settings. لكن إنشاء warehouse داخله يتطلب inventory.warehouses.create. لو الادمن مفيش inventory perms (زي السيناريو "core+accounting only")، الـ wizard بيوقع.guard_name: 'web'spatie/laravel-activitylog على الباك إند.name. لو الادمن عمل "Custom_role_5" مفيش حقل بيشرح "هذا الدور للمدراء الإقليمين". description.
تم الاختبار بـ simulator يحاكي منطق الـ sidebar filtering لـ 5 سيناريوهات. ✅ = ظاهر، × = مخفي.
| السيناريو | Dashboard | Core | Accounting | Sales | Purchases | Inventory | HR | Production | CRM | CMMS | QMS | LIS | POS |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Owner (794 perms) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Admin (185: core+accounting) | ✅ | ✅ | ✅ | × | × | × | × | × | × | × | × | × | × |
| Cashier (23 perms) | ✅ | ✅ | × | ✅ | × | × | × | × | × | × | × | × | ✅ |
| Employee (HR self-service) | ✅ | ✅ | × | × | × | × | ✅ | × | × | × | × | × | × |
| Lab Manager (LIS + Core) | ✅ | ✅ | × | × | × | × | × | × | × | × | × | ✅ | × |
| الأولوية | المهمة | المسؤول | الجهد |
|---|---|---|---|
| P1 | إضافة permissionGuard على 30 route غير محمي (LIS, dashboards, guides) | Frontend | 30 دقيقة |
| P1 | توحيد route guards مع nav permissions (customer-statements, supplier-statements) | Frontend | 10 دقائق |
| P1 | إصلاح Users page → role dropdown ديناميكي + multi-role + ترجمة صحيحة | Frontend | 2 ساعات |
| P1 | تنظيف cmms.* من دور accountant (و audit أدوار أخرى) | Backend (تاسك جديد لأحمد) | 30 دقيقة |
| الأولوية | المهمة | المسؤول | الجهد |
|---|---|---|---|
| P2 | توحيد nav config — حذف sidebar.menuItems واستخدام NAV_MODULES | Frontend | 3 ساعات |
| P2 | إضافة "View Permissions" dialog في Users page | Frontend | 1 ساعة |
| P2 | إضافة بحث + فلتر (role/status/branch) في Users page | Frontend | 1 ساعة |
| P2 | تنظيف orphan purchasing.* + إضافة sales.approval-workflows | Backend (تاسك #1533 + جديد) | 1 ساعة |
| P2 | إضافة column description على Role model | Backend (تاسك جديد) | 30 دقيقة |
| الأولوية | المهمة | المسؤول | الجهد |
|---|---|---|---|
| P3 | تفعيل Activity Log على Role + User | Backend | 2 ساعات |
| P3 | Loading skeleton بدل super-admin-while-loading | Frontend | 1 ساعة |
| P3 | Setup wizard scoped permissions / elevated token | Backend | 3 ساعات |
| P3 | "Revoke active sessions" feature | Backend + Frontend | 4 ساعات |