INFORMATIQUE EDUCATION · SIS MOBILE

تطبيق الجامعة — دليل الفريق الكامل

إحنا فين دلوقتي، وإيه اللي اتعمل، وليه اتعمل كده، وإيه اللي فاضل عشان نخلّص. دليل عملي للموبايل تيم والباك إند تيم — شاشة شاشة، بأرقام حقيقية من داتابيز شغالة.

33شاشة متغطّية
191إندبوينت متوثّق
167منهم شغال على الداتابيز
9شغال بالـ Sandbox
144/196خاصية متعملة (73%)
34كوليكشن Postman

★ الجديد ٢ — الحضور، حجز الساعات المكتبية، رسالة للسكاشن، الخطط الدراسية

طلبات نادر الخمسة: اتعمل فيها إيه، إنت كموبايل تنادي إيه وتقرا أنهي حقل، وإيه اللي بيتظبط من الأدمن. كله متنزّل على السيرفر (109.123.243.32:9300)، ومعروض في الديمو (:9301) بنفس الريكوستات. الجولة اللي قبلها (ميتنج 30 سبتمبر) لسه تحت في الجديد ١.

🛡️

كل الحقول القديمة زي ما هي — اللي اتضاف حقول وروابط جديدة بس، ومفيش عمود ولا جدول اتضاف للداتابيز: الحجوزات وجلسات الـ QR متخزّنة في داتا السيرفس (JSON جنب بروفايل الأدمن)، والإعدادات في الأدمن.

⚠️

تغيير في المعنى لازم تاخد بالك منه — كشف الحضور: قبل كده «مالوش سطر غياب» كان معناه «حاضر». دلوقتي زي البورتال بالظبط: الحاضر ليه سطر (ATTEND_FLG = 1)، والمتأخر ليه سطر تأخير، واللي مالوش أي سطر يبقى «لسه ماتاخدش» مش «حاضر». يعني: ماتستنتجش الحالة من EdStudAbsenceId (الحاضر بقى عنده رقم) — اقرا AttendanceState اللي بقى جاي مع كل طالب. وعلى Innovation الاعتماد اتلغى من الأدمن: ApprovalRequired: false ← خبّي زرار «اعتماد»، وكل علامة نهائية أول ما تتحفظ.

#المطلوباتعمل إيهالموبايل يعمل إيه⚙️ من الأدمنفين
1الدكتور يبعت رسالة لكل طلبة المقرر — ولو عنده أكتر من سكشن يختاررسالة واحدة بتتبعت لكل طالب في سكاشن الدكتور هو بس في المقرر (أو السكاشن اللي اختارها)، والطالب ياخدها مرة واحدة حتى لو في سكشنين. academicstaff/SendMessageToCourse بـ EdCourseId + ScScheduleDtlIds (فاضية = كل سكاشنه). السكاشن من academicstaff/GetMySections.مفيش تفاصيل ↓
2جدول محاضرات الدكتور يظهر فيه حجوزات الساعات المكتبيةجدول الدكتور وجدول الطالب الاتنين بيرجّعوا الحجوزات الجاية جنب المحاضرات. اقرا officeHourBookings[] الجديدة في رد academicstaff/GetStaffSchedule وstudents/schedule.مفيش تفاصيل ↓
3لو طالب حجز ميعاد، الدكتور مايقدرش يحذفه أو يعدّله — والحجوزات ترجع مع المواعيدحجز كامل: الطالب يشوف المواعيد المتاحة ويحجز/يلغي، الدكتور يشوف مين حاجز وإمتى وعن إيه، ويلغي بسبب بيوصل للطالب. الميعاد المحجوز بيرفض التعديل والحذف. دكتور: Bookings[] وHasBookings في academicstaff/GetMyOfficeHours، وإلغاء بـ academicstaff/CancelOfficeHourBooking. طالب: students/GetOfficeHourOpenings وstudents/BookOfficeHour.Attendance & office hours ← Office-hour booking تفاصيل ↓
4بيانات الجامعة/الكلية/البرنامج ترجع معاها الخطط الدراسيةكل برنامج في الشجرة معاه Plan: الحزم (إجباري/اختياري…) بساعاتها ومقرراتها — من غير لوجين. GET Info/University (فيها الخطط)، أو ?plans=false للشجرة خفيفة + GET Info/AcademicPlan?programId= لبرنامج واحد.University ← Show study plans تفاصيل ↓
5الحضور والغيابزي صفحة البورتال: محدش حاضر افتراضيًا · حاضر / متأخر / غياب / عذر · «حضور الكل» · QR بيتغيّر كل شوية بفترة سماح · كود رقمي للي مش عارف يمسح · مصدر كل علامة (يدوي / QR / كود) · «قفل الحضور» = إخفاء الـ QR بس · كل علامة بتتحفظ فورًا (الاعتماد اتلغى). التفاصيل نقطة نقطة تحت.Attendance & office hours ← Register approval + Lecture QR تفاصيل ↓

5 · الحضور والغياب — نقطة نقطة

المطلوبتنادي إيهبيرجع / بيعمل إيه
مفيش حالة افتراضيةStaff/GetAbsenceDetails كل طالب معاه AttendanceState: NotTaken · Present · Late · Absent · Excused، وAttendanceSource: Manual · QR · Code · Location، وLateMinutes. وفوق: PresentCount · LateCount · AbsentCount · ExcusedCount · NotTakenCount · ApprovalRequired. ابدأ كل الأزرار مش متدوسة لما الحالة NotTaken.
حاضر / غياب / متأخر / عذرStaff/SaveAttendanceSheet كل عنصر في MARKS: STATE = present · late (+ LATE_MINUTES) · absent · excused · clear (يرجّعه «لسه»). ابعت اللي اتغيّر بس. القيم القديمة (present/absent/excused) شغالة زي ما هي.
كل علامة بتتحفظ فورًا— مفيش خطوة «اعتماد» بعد الحفظ لما ApprovalRequired: false (Innovation دلوقتي). ممكن تحفظ مع كل دوسة (طالب واحد في MARKS) — مفيش حاجة بتستنى.
زرار «حضور الكل»Staff/MarkAllPresent
{SC_SCHEDULE_DTL_ID, SC_SCHEDULE_DTL_DAY_ID, ABS_DATE}
بيعلّم «حاضر» على اللي لسه مالوش علامة بس — اللي مسح أو اتعلّم غياب/عذر بيفضل زي ما هو. الرد {marked, alreadyMarked}. ينفع من شاشة الـ QR ومن الكشف.
QR بيتغيّر كل فترة + سماحacademicstaff/GetLectQRCode الرد زاد: refreshSeconds · nextRefreshAt · sessionCode · sessionOpen، والـ qrPayload فيه QR_TOKEN وQR_WINDOW. نادي تاني عند nextRefreshAt واعرض الكود الجديد. الكود القديم بيفضل شغال لحد فترة السماح (لكاميرا بطيئة). الفترتين من الأدمن.
الطالب بيمسحstudents/GetStudentAttend ابعت QR_TOKEN وQR_WINDOW من الـ payload زي ما هم جنب الحقول القديمة. كود انتهى ← details = «الكود ده انتهى — امسح الكود اللي على شاشة الدكتور دلوقتي». والمسح بيسجّل «حاضر» في الكشف على طول (مصدره QR).
كود الجلسة للي مش عارف يمسحstudents/AttendByCode
{ED_STUD, CODE}
6 أرقام بتظهر تحت الـ QR (sessionCode) وثابتة طول المحاضرة. نفس شروط المسح (اليوم، وقت المحاضرة، مسجّل في السكشن). الرد زي رد المسح، ومصدره Code.
قفل الحضور = قفل جلسة الـ QR بسacademicstaff/SetQrSession
{SC_SCHEDULE_DTL_ID, SC_SCHEDULE_DTL_DAY_ID, OPEN}
OPEN: false ← الكود يختفي والمسح والكود الرقمي بيترفضوا («الحضور للمحاضرة دي اتقفل»). مش بيكتب غياب لحد. يفتح تاني بـ OPEN: true أو GetLectQRCode بـ OPEN: true. (Staff/CloseLectureAttendance القديم لسه موجود لو حد عايز «غياب لكل اللي مامسحش» بعد المحاضرة — اختياري.)
مصدر الحضورStaff/GetAbsenceDetails AttendanceSource لكل طالب — اعرضه شارة صغيرة جنب الاسم (يدوي / QR / كود / موقع). متخزّن في ABS_NOTES بنفس عرف الـ SIS (QrCode).

3 · حجز الساعات المكتبية

مينتنادي إيهبيرجع / بيعمل إيه
طالبstudents/GetOfficeHourOpenings
{EdStudId, SaStfMemberId} (0 = كل دكاترته)
تحت Data: BookingEnabled · Openings[] (SaOfficeHourId, StaffNameAr, Date, DayAr, FromTime, ToTime, Capacity, Booked, Available, MyBookingId) · MyBookings[] · CancelledByLecturer[] (فيها CancelReason — اعرضها للطالب، عشان الإلغاء مايبقاش في صمت).
طالبstudents/BookOfficeHour
{EdStudId, SaOfficeHourId, Date, Topic}
Data.Status + Data.Booking، أو Data.Error بسبب واضح (مش دكتورك، اليوم مش يوم الميعاد، فات، مليان، أبعد من المسموح…). الإلغاء: نفس الرابط بـ {Cancel: true, BookingId}.
دكتورacademicstaff/GetMyOfficeHours كل ميعاد في Items[] بقى معاه Bookings[] (BookingId, StudentNameAr, Date, Topic…) وHasBookings. لو true ← اقفل «تعديل/حذف».
دكتورacademicstaff/SaveOfficeHour على ميعاد محجوز بيرفض: «This office hour is booked by N student(s) (next on …); cancel the booking first.»
دكتورacademicstaff/CancelOfficeHourBooking
{BookingId, Reason}
بيلغي الحجز، والسبب بيوصل للطالب في CancelledByLecturer.

2 · الحجوزات في الجدول

رد academicstaff/GetStaffSchedule (الدكتور) وstudents/schedule (الطالب) زاد فيه officeHourBookings[] — نفس شكل الحجز فوق (Date, DayAr, FromTime, ToTime, StudentNameAr / StaffNameAr, Topic). اعرضها في يومها جنب المحاضرات بلون مختلف. جدول الدكتور بيرجع Ok حتى لو عنده حجوزات بس ومفيش محاضرات.

1 · رسالة لطلبة المقرر / سكاشن مختارة

4 · الخطط الدراسية مع بيانات الجامعة

جرّبها بنفسك

عايز تجرّبالحسابتتوقّع
QR بيتغيّر + كود الجلسة + حضور الكلatef.dahesh ← BAS112 (2136 / 3239) كود جديد كل 30 ثانية، 6 أرقام تحت الكود، «حضور الكل» يرجّع marked. الطلبة: ahmed.elsayed.24010214 · mohamed.rabeea.24010230
الحجزطالب: basmala.nabih.25010008 · دكتور: atef.dahesh بسملة حاجزة الثلاثاء 6 أكتوبر 10:00 «مراجعة الميدتيرم» ← عند الدكتور الميعاد «محجوز» ومش بيتحذف.
رسالة لسكشنatef.daheshBAS112 ← سكشن 2136 ← Sent: 27
الخططمن غير حسابGET Info/AcademicPlan?programId=10

كل الأسامي اللي فوق بالظبط زي الـ JSON اللي بيرجع. الديمو (:9301) بينادي نفس الروابط دي — افتح الـ DevTools وشوف الريكوست.

★ الجديد ١ — مخرجات ميتنج 30 سبتمبر (اتنفّذت 1 أكتوبر)

كل بند اتقال في الميتنج، اتعمل فيه إيه، إنت كموبايل تعمل إيه، واتشرح فين في الدليل. للبيزنس: نفس النقط بكلام من غير كود ومعاها إعدادات الأدمن في شرح البيزنس. كله متنزّل على السيرفر (109.123.243.32:9300) ومتجرّب على داتابيز Innovation.

🛡️

مفيش حاجة شغالين عليها اتكسرت. كل التغييرات إضافة حقول — مفيش حقل اتشال ولا اتغيّر اسمه ولا معناه. ده مش كلام: قبل ما ننزّل سجّلنا شكل رد 25 إندبوينت من السيرفر القديم، وبعد التعديل قارنّاهم حقل حقل (tools/contract-snapshot.js). النتيجة: حقول جديدة بس. ومفيش ولا عمود ولا جدول اتضاف للداتابيز — أي إعداد جديد متخزّن في بروفايل الأدمن.

#المطلوب في الميتنجاتعمل إيهالموبايل يعمل إيه⚙️ من الأدمنفين في الدليل
1المقر والسكاشن: أيام التدريس وموقع التدريسكل سكشن في students/schedule وacademicstaff/GetStaffSchedule بقى بيرجّع الأيام والوقت والقاعة والمبنى والمقر — بنفس أسامي حقول شاشة التسجيل. اعرض DaysAr · FromTime–ToTime · CampusAr · HallAr، أو اعمل سطر لكل عنصر في meetings[].مفيش الجدول الدراسي · شاشات الدكتور
2المتطلبات الدراسية: الدرجة / «مسجل» / «-----»students/AcadPlan: كل مقرر معاه COURSE_STATUS وDISPLAY_GRADE_AR جاهز للعرض. اعرض DISPLAY_GRADE_AR في عمود الدرجة بدل Course_Grading_AR.مفيش خطة الدراسة
3D- وبعدين مسجّله تاني ← الحالي بس «مسجل». D- وبعدين A ← الأفضل ولا الأخير؟المسجّل حاليًا بيغلب أي محاولة قديمة ← «مسجل». واللي خلص أكتر من مرة: القاعدة متسجّلة على لائحة الطالب في الـ SIS (الأعلى / الأحدث / الأحدث بحد أقصى) — نفس منطق تقرير الخطة في النظام القديم. مش قرار في الكود. ولا حاجة زيادة — DISPLAY_GRADE_AR بيجيب الصح. GRADE_RULE وATTEMPTS لو حابب تعرض «معاد».من الـ SIS مش الأدمن: قاعدة الإعادة على لائحة الطالب خطة الدراسة
4التقويم الأكاديمي مش بيرجّع داتاالسبب: قاعدة قديمة بتعرض بس الأنشطة المعلّم عليها «للطلاب»، والمسجّل هنا بيعلّم «يظهر». بقت إعداد في الأدمن ← 48 بند للربيع. POST semester/semsterCalnd بـ EdAcadYearId وEdCodeSemesterId من اللوجين.Academic rules ← «Academic calendar shows» = Shown
الإعداد بالصورة ↓
التقويم الأكاديمي
5الساعات المكتبية فاضية في دليل الأساتذةكانت فاضية لأن مفيش مواعيد للربيع، والدكتور كان بيدخل على الصيفي فمواعيده بتروح هناك. اتصلّح ترم الدكتور، واتحطّت مواعيد تجريبية لـ 9 دكاترة، والمواعيد بقت جوّه الدليل نفسه. من getInstructorsAndAdvisorForAsk: Instructors[].officeHours[] وAcademicAdvisior.officeHours[].Academic rules ← «A lecturer’s term…» مفتوح
الإعداد بالصورة ↓
دليل الأساتذة
6بيانات صفحة الأدمناللينك: http://109.123.243.32:9300/Admin · اليوزر: admin · الباسورد عند محمد عطية (مش هيتكتب في دليل منشور). والأدمن نفسه اتنظّم — شوف الفصل. —Overview + Setup
الإعداد بالصورة ↓
لوحة الإدارة
7ريكوست من غير Authorization يرجّع بيانات الجامعة (عشان الستور)GET Info/University: بيانات الجامعة من الأدمن + الشجرة الأكاديمية لايف من الـ SIS (8 كليات بأقسامها وبرامجها). نادِيه قبل اللوجين، من غير هيدر Authorization.Public info & contact ← الاسم واللوجو والنبذة وسياسة الخصوصية
الإعداد بالصورة ↓
عن الجامعة
8تواصل معنا — عامة ولا إرسال إيميل؟قرار: بيانات عامة بس دلوقتي. GET Info/ContactUs من غير توكن: كروت المكاتب (تليفون، إيميل، واتساب، مواعيد). اعرض الكروت بأزرار tel: / mailto: / واتساب.Public info & contact ← Contact cards
الإعداد بالصورة ↓
تواصل معنا
9اللوجين بـ 3 طرق: افتراضي · Active Directory · Azureالـ 3 شغالين والأدمن بيختار. ① الافتراضي: «أي باسورد» أو باسورد موحّد. ② AD: كان TODO بيرفض دايمًا ← بقى LDAP bind حقيقي + زرار تجربة. ③ Azure: كان بيفك التوكن من غير ما يتأكد منه (أي حد يزوّر توكن لأي طالب) ← بقى بيتحقق من التوقيع والـ tenant والـ audience والصلاحية. ① و② ← auth/login زي ما هو. ③ ← students/LoginAZ بالـ id_token (مش توكن Graph).Sign-in methods ← الطريقة + «Token checking» (فلاج التيست)
الإعداد بالصورة ↓
تسجيل الدخول · دليل دخول مايكروسوفت

⚙️ إعدادات الأدمن لكل نقطة — بالصورة

كل اللي تحت في http://109.123.243.32:9300/Admin ← Setup. غيّر الخانة، واضغط Save changes تحت — الحفظ بيحفظ التاب اللي غيّرته بس (عليه ●) ومش بيلمس أي إعداد تاني. النقط 1 و2 و3 مالهاش إعداد: بتشتغل لكل الجامعات من بيانات الـ SIS.

وكمان اتصلّح في السكة

الحاجةكانت بتعمل إيهدلوقتي
جدول الدكتور (GetStaffSchedule) «data not found» لأي دكتور سكاشنه مش في «مجموعة سكاشن» — وده السبب إن الاتنين اللي كانوا مع التيست جدولهم فاضي. واللي بيرجع، كل سكشن مكرر 3 مرات. سطر لكل سكشن. للتيست: atef.dahesh (7 سكاشن في الصيفي، 12 في الربيع)، وكمان الدكاترة 66 و160 و231 عندهم 11–12 سكشن في الربيع.
ترم الدكتور في اللوجين كان ممكن ييجي على ترم مالوش فيه تدريس. الترم الحالي، ولو مالوش فيه سكاشن ← آخر ترم بيدرّس فيه.
academicstaff/scheduledays بـ EdStudId = 0 كان بيرجّع جدول كل طلبة الترم لأي دكتور. بيرجّع فاضي — الجدول بتاع طالب واحد.
students/GetOfficeHours كان بيعرض «الأيام المفضّلة للتدريس» كأنها ساعات مكتبية، والوقت بتاريخ وهمي. ساعات مكتبية بس، وFromTime/ToTime بـ HH:mm.
الساعات المكتبية للدكتور في auth/me الخاصية كانت متعرّفة «للطالب بس» فمش ظاهرة للدكتور. ظاهرة للاتنين.

📷 جرّب الحضور بالـ QR النهاردة

الدكتور atef.dahesh بقى عنده محاضرتين يوم الخميس (بيانات تجريبية مضافة جنب جدوله الأصلي — مالمسناش مواعيده):

المحاضرةالسكشن / الجلسةالمسح شغال لحدالقفل (الغياب) يشتغل منطلبة للتجربة
BAS112 — 27 طالب2136 / 3239اليوم كله07:00 صباح الجمعة (مصر) ahmed.elsayed.24010214 · mohamed.rabeea.24010230 · basmala.nabih.25010008 (omar.mahmoud.25010184 اتجرّب بيه ومسجّل حضور خلاص)
BAS115 — 27 طالب2143 / 32409 مساءً (مصر)بعد 9 مساءً (مصر) abdlahady.abdlahady.23010040 · mona.esmaeel.24010002 · malak.awad.24010043

الدورة: ① الدكتور يدخل ← محاضراته ← يختار المحاضرة ← يعرض الكود (academicstaff/GetLectQRCode). ② الطالب يمسح (students/GetStudentAttend) ← «تم تسجيل الحضور بنجاح»؛ مرة تانية ← «حضورك مسجّل بالفعل»؛ طالب مش في السكشن ← «إنت مش مسجّل في السكشن ده». ②½ جديد كشف الدكتور (Staff/GetAbsenceDetails) بيقول لكل طالب Scanned / ScannedAt وفوقه ScannedCount — والمسح بقى بيسجّل «حاضر» في الكشف على طول، والباقي «لسه» (AttendanceState: NotTaken) — شوف الجديد ٢. ③ اختياري بعد ما المحاضرة تخلص: Staff/CloseLectureAttendance ← كل اللي مامسحش بيتسجّل غياب، والرد بيقول كام حضر وكام اتسجّل غياب. (إخفاء الكود نفسه بقى academicstaff/SetQrSession.) قبل نهاية المحاضرة القفل بيترفض برسالة «it can be closed after 14:00» — اتصلّح (كان بيقول «اتقفلت» ومايكتبش ولا غياب).

🕐

ساعة السيرفر مش ساعة الموبايل. كل مواعيد المحاضرات (المسح والقفل وجدول النهاردة) بتتحسب بساعة سيرفر الداتابيز، وسيرفر Innovation_Demo على توقيت شرق أمريكا — 7 ساعات ورا مصر. عشان كده المواعيد في الجدول فوق مكتوبة بتوقيت مصر بعد التحويل. على سيرفر جامعة حقيقية في مصر الفرق ده مش هيبقى موجود — بس لو حد قال «الكود بيقول مش وقته» وهو في المحاضرة، أول حاجة تتشاف هي ساعة السيرفر.

حسابات التيست — تجرّب إيه بإيه

عايز تجرّبالحسابتتوقّع
الخطة الدراسية العاديةmahmoued.eprahim.2301006249 ناجح بالدرجة، 39 «-----»
مقرر راسب ومسجّل تانيahmed.ghanem.25060211BSM112/BSM113/MEC112 ← «مسجل»، وATTEMPTS: 2
الساعات المكتبية في الدليلmahmoued.eprahim.2301006212 دكتور كلهم معاهم مواعيد
جدول الدكتور + ساعاتهatef.dahesh7 سكاشن بالمقر، وميعادين مكتبيين
بيانات الجامعةمن غير حسابGET /Info/University

الباسورد على Innovation لسه «أي حاجة» — بقصد، عشان شغلكم. لو الجامعة عايزة باسورد موحّد، ده من الأدمن من غير ما حد يغيّر كود.

⏳

لسه مستني: داتابيز BOX_HILL_Stage (74.208.186.99) — البورت 1433 مقفول من عندنا ومن السيرفر. محتاج حد يفتحه للـ IP 109.123.243.32. أول ما يتفتح: نسخة تانية من السيرفس على بورت 9303 ببروفايل BoxHill، من غير ما نلمس نسخة Innovation اللي إنتوا شغالين عليها — والتيست تيم يشوف من النظام القديم اللي بيعمله الموبايل.

★ شرح البيزنس — كل نقطة اتعمل فيها إيه، وإعداداتها

للبيزنس والتيست تيم: من غير كود. كل نقطة من ميتنج 30 سبتمبر — المطلوب، اللي بقى بيحصل، الإعداد اللي بيتحكم فيها من لوحة الإدارة (فين، اختياراته، الافتراضي، ومتظبط على إيه دلوقتي)، وإزاي تتأكد بنفسك. لوحة الإدارة: http://109.123.243.32:9300/Admin ← Setup. الديمو: http://109.123.243.32:9301.

🧭

قاعدة عامة: المنتج واحد لكل الجامعات، والفرق بينهم إعدادات مش كود. أي حاجة جامعة عايزاها بشكل مختلف (زي الدرجة الأفضل ولا الأخيرة) إما بتيجي من بيانات الـ SIS بتاعتها، أو من إعداد في الأدمن. ومفيش أي تعديل حصل في داتابيز الـ SIS — لا أعمدة ولا جداول.

1 المقر والسكاشن — أيام وموقع التدريس

المطلوب: كل مقرر/سكشن يبان عليه بيتدرّس أنهي أيام، الساعة كام، وفين (المقر والمبنى والقاعة).

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

⚙️ مفيش إعداد — بيشتغل كده لكل الجامعات، والبيانات من الـ SIS نفسه.

✅ تتأكد إزاي: ادخل الديمو بالطالب ← «المقررات الدراسية»: تحت اسم المقرر هتلاقي مثلًا «الأربعاء · 09:00–12:00 · Main Campus · …». لو المقرر مالوش ميعاد متسجّل في الـ SIS مش هيظهر سطر.

2 المتطلبات الدراسية — الدرجة / «مسجل» / «-----»

المطلوب: في الخطة الدراسية: لو الطالب خلّص المقرر تظهر درجته، لو مسجّله دلوقتي تظهر كلمة «مسجل»، ولو عمره ما سجّله تظهر «-----».

اللي بقى بيحصل: بالظبط كده. كل مقرر في الخطة بقى عليه واحدة من التلاتة، ولو المقرر اتاخد أكتر من مرة بيبان عليه «معاد». والدرجة مابتظهرش غير لما النتيجة تتعتمد في الـ SIS.

⚙️ مفيش إعداد — بيشتغل كده لكل الجامعات، والبيانات من الـ SIS نفسه.

✅ تتأكد إزاي: الديمو ← «خطة الدراسة» ← دوس على أي مسار: قدام كل مقرر A- أو B+… أو «مسجل» أو «-----».

3 الإعادة — D- وبعدين سجّله تاني / D- وبعدين A

المطلوب: (أ) طالب جاب D- وسجّل المقرر تاني الترم ده ← يظهر الحالي بس «مسجل». (ب) جاب D- وبعدين A ← يظهر الأفضل ولا الأخير حسب اللائحة، والرجوع للبيزنس في النظام القديم.

اللي بقى بيحصل: (أ) اتعمل: المحاولة الحالية بتغلب أي محاولة قديمة. (ب) رجعنا للنظام القديم ولقينا إن القرار متسجّل على لائحة كل طالب في الـ SIS نفسه — كل لائحة ليها «قاعدة الإعادة»: تاخد الأعلى، أو الأحدث، أو الأحدث بحد أقصى للتقدير. والمنتج بيقرا القاعدة دي ويطبّقها، بنفس منطق تقرير الخطة في النظام القديم. يعني مش إحنا اللي بنقرر، والجامعة لو غيّرت لائحتها في الـ SIS التطبيق بيمشي معاها لوحده.

⚙️ الإعداد في الـ SIS مش في الأدمن
المكان شاشة «سياسة إعادة المقررات» الخاصة باللائحة في النظام القديم
الاختيارات الأحدث · الأعلى · الأحدث بحد أقصى للتقدير
على Innovation كل لائحة وقاعدتها — مثلًا لائحة على «الأعلى» وأخرى على «الأحدث بحد أقصى»

✅ تتأكد إزاي: بالطالب ahmed.ghanem.25060211: مقررات راسب فيها في الربيع ومسجّلها في الصيفي (BSM112، BSM113، MEC112) بتظهر «مسجل» ومعاها «معاد».

4 التقويم الأكاديمي مش بيرجّع داتا

المطلوب: التقويم يرجّع مواعيد الفصل.

اللي بقى بيحصل: اتصلّح. السبب: القاعدة القديمة بتعرض على الموبايل بس الأنشطة اللي المسجّل علّم عليها «للطلاب»، والمسجّل عندنا بيعلّم «يظهر» بس — فالتقويم كان فاضي رغم إن فيه أنشطة. بقى إعداد، والتقويم دلوقتي فيه مواعيد الربيع كلها (48 نشاط لكل الكليات، و6 لكلية الطالب).

⚙️ إعداد في الأدمن
المكان Setup ← Academic rules ← «Academic calendar shows»
الاختيارات «للطلاب» بس (القديم) · «Shown» = اللي عليه «يظهر» أو «للطلاب» · «All» = كل أنشطة الفصل
الافتراضي «للطلاب» بس — عشان أي جامعة قديمة ماتتغيّرش من غير ما تختار
على Innovation Shown

✅ تتأكد إزاي: الديمو ← الخدمات ← «التقويم الأكاديمي»: العنوان بيقول «من الـ SIS · N نشاط» والأنشطة بتواريخها الحقيقية. ولو حبيت تشوف الفرق: غيّر الإعداد لـ «للطلاب» واحفظ ← التقويم هيفضى.

5 الساعات المكتبية في دليل أعضاء هيئة التدريس

المطلوب: دليل الأساتذة يعرض الساعات المكتبية لكل دكتور.

اللي بقى بيحصل: بقت جوّه الدليل نفسه: كل دكتور (والمرشد الأكاديمي) تحت اسمه أيامه ومواعيده. كانت فاضية لسببين: مفيش مواعيد متسجّلة للربيع أصلًا، والدكتور كان بيدخل على ترم الصيفي فأي ميعاد يسجّله من الموبايل كان بيروح للصيفي والطالب (اللي على الربيع) مش شايفه. اتصلّح ترم الدكتور (نقطة 10)، واتحطّت مواعيد تجريبية لـ 9 دكاترة عشان التيست. ودكتور مالوش مواعيد بيظهر عنده «لا توجد ساعات مكتبية».

⚙️ مفيش إعداد — بيشتغل كده لكل الجامعات، والبيانات من الـ SIS نفسه.

✅ تتأكد إزاي: الديمو ← «دليل أعضاء هيئة التدريس»: كل كارت تحته «الساعات المكتبية» (الإثنين 10:00–12:00…). المواعيد التجريبية مكتوب عليها «بيانات تجريبية 2026-10-01».

6 بيانات صفحة الأدمن

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

اللي بقى بيحصل: اللينك: http://109.123.243.32:9300/Admin · اليوزر: admin · الباسورد: عند محمد عطية (مابيتكتبش في دليل منشور).
والصفحات اتنظّمت: Overview بتقول في سطر لكل جزء الجامعة دي متظبطة على إيه، ولو فيه مشكلة بتحذّر وتوديك على مكانها. وSetup بقى منيو جانبي بترتيب ما بتظبط عميل جديد (الجامعة ← الاتصال والدخول ← الأكاديمي ← التكاملات ← الإدارة). والحفظ بيحفظ اللي غيّرته بس (عليه علامة ●) — مفيش إعداد تاني بيتلمس بالغلط، وكل حفظ بياخد نسخة احتياطي من القديم.

⚙️ مفيش إعداد — بيشتغل كده لكل الجامعات، والبيانات من الـ SIS نفسه.

✅ تتأكد إزاي: ادخل الأدمن ← Overview: هتلاقي «Sign-in: User name + ANY password» ومعاه تحذير، و«Academic rules: … calendar: Shown».

7 ريكوست من غير Authorization يرجّع بيانات الجامعة (عشان الستور)

المطلوب: مراجع الـ App Store / Google Play بيفتح التطبيق من غير حساب — لازم يلاقي بيانات الجامعة.

اللي بقى بيحصل: في شاشة الدخول فيه زرار «عن الجامعة» بيفتح من غير حساب: اسم الجامعة ولوجو ونبذة وتواصل عام، وكل الكليات بأقسامها وبرامجها. البيانات من مصدرين زي ما اتقال في الميتنج: الكليات والأقسام والبرامج من الـ SIS لايف (الشجرة الأكاديمية)، والباقي (اللوجو، النبذة، الإيميل…) بيتكتب مرة في الأدمن لأن الـ SIS مفيهوش.

⚙️ إعداد في الأدمن
المكان Setup ← Public info & contact
الاختيارات الاسم المختصر · اللوجو · الألوان · النبذة (عربي/إنجليزي) · الموقع · الإيميل · التليفون · العنوان · دبوس الخريطة · لينك سياسة الخصوصية · لينكات السوشيال · «Include the academic tree» (إظهار الكليات)
الافتراضي فاضية — والكليات ظاهرة
على Innovation الكليات ظاهرة (8)، والباقي لسه محتاج يتكتب

✅ تتأكد إزاي: الديمو ← شاشة الدخول ← «عن الجامعة»: 8 كليات، دوس على كلية تشوف أقسامها وبرامجها. قبل الرفع على الستور: لينك سياسة الخصوصية لازم يتكتب — صفحة Overview بتحذّر لحد ما يتكتب.

8 تواصل معنا

المطلوب: بيانات عامة ولا إرسال إيميل للموظفين؟ ومن غير Authorization عشان مراجعة الستور.

اللي بقى بيحصل: القرار: بيانات عامة بس دلوقتي. شاشة «تواصل معنا» (من شاشة الدخول ومن غير حساب) بتعرض كارت لكل مكتب — القبول، شؤون الطلاب، الدعم الفني… — بأزرار اتصال وإيميل وواتساب ومواعيد العمل. الإرسال من التطبيق للموظفين اتأجّل لأنه من غير حساب ومحتاج حماية من الإزعاج.

⚙️ إعداد في الأدمن
المكان Setup ← Public info & contact ← «Contact cards»
الاختيارات كارت لكل مكتب: الاسم (عربي/إنجليزي) · التليفون · الإيميل · الواتساب · مواعيد العمل
الافتراضي مفيش كروت
على Innovation لسه محتاجة تتكتب — الشاشة بتقول «الجامعة لسه مكتبتش بيانات التواصل»

✅ تتأكد إزاي: اكتب كارت واحد في الأدمن واحفظ ← الديمو ← «تواصل معنا» هيظهر فيها الكارت بأزراره.

9 اللوجين بـ 3 طرق

المطلوب: (1) الدخول الافتراضي باليوزر والباسورد الافتراضي، (2) Active Directory، (3) Microsoft Azure — ومراجعة هل شغالين.

اللي بقى بيحصل: قبل: (1) كان بيقبل أي باسورد. (2) كان موجود في الأدمن بس مش متنفّذ — بيرفض أي حد دايمًا. (3) كان بيقبل توكن مايكروسوفت من غير ما يتأكد إنه حقيقي — يعني أي حد يقدر يزوّر دخول باسم أي طالب (ونفس المشكلة في النظام القديم).
دلوقتي الـ 3 شغالين والأدمن بيختار: (1) إما «أي باسورد» للديمو، أو «باسورد موحّد» تكتبه (زي 123). (2) التطبيق بيسأل الـ Active Directory بتاع الجامعة هل الباسورد صح — وفي الأدمن زرار «Test sign-in» تجرّب بحساب حقيقي. (3) التوكن بيتراجع مع مايكروسوفت فعلًا (التوقيع، والجامعة، والتطبيق، والصلاحية)؛ وتقدر تسيب اليوزر والباسورد شغالين جنبه أو تقفلهم.

⚙️ إعداد في الأدمن
المكان Setup ← Sign-in methods
الاختيارات «Main sign-in method»: 1 · 2 · 3 — ولـ 1: «أي باسورد» / «باسورد موحّد». ولـ 2: الـ domain controller والدومين وزرار الاختبار. ولـ 3: دومين الجامعة، الـ Client ID، «Verify» (الصح) أو «Decode only» (القديم — بيظهر أحمر)، و«Also accept user name + password».
الافتراضي 1 · أي باسورد
على Innovation 1 · أي باسورد — عن قصد عشان شغل الموبايل تيم والتيست ما يقفش. Overview وReadiness بيحذّروا منها.

✅ تتأكد إزاي: (1) غيّر لـ «باسورد موحّد» واكتب 123 واحفظ ← الدخول بأي باسورد غير 123 بيترفض «password not correct». (رجّعه بعد التجربة.) (3) من غير Azure: Sandbox ← «Microsoft sign-in» بيدّي توكن تجريبي — التفاصيل في دليل دخول مايكروسوفت. (2) محتاج بيانات الـ AD من الجامعة.

10 ترم الدكتور + جدوله (اتصلّح في السكة)

المطلوب: عضو هيئة تدريس يكون عنده جدول — الاتنين اللي مع التيست جدولهم فاضي.

اللي بقى بيحصل: جدول الدكتور كان بيرجّع «data not found» لأي دكتور سكاشنه مش متربطة بـ «مجموعة سكاشن» في الـ SIS — ودول أغلب الدكاترة. اتصلّح، واتصلّح كمان إن كل سكشن كان بيتكرر 3 مرات. وكمان الدكتور بقى بيدخل على الترم اللي بيدرّس فيه: الترم الحالي، ولو مالوش فيه ولا سكشن ← آخر ترم درّس فيه.

⚙️ إعداد في الأدمن
المكان Setup ← Academic rules ← «A lecturer’s term is the one they teach in»
الاختيارات مفتوح / مقفول
الافتراضي مفتوح
على Innovation مفتوح

✅ تتأكد إزاي: ادخل بـ atef.dahesh ← جدوله فيه سكاشن بأيامها ومقرها. ودكاترة تانيين عندهم جداول في الربيع: الأرقام 66 و160 و231.

طلبات نادر (الجولة التانية) — للبيزنس

التفاصيل التقنية للموبايل في الجديد ٢.

11 رسالة من الدكتور لطلبة المقرر — أو لسكاشن يختارها

المطلوب: الدكتور يبعت رسالة لكل طلبة المقرر، ولو بيدرّس أكتر من سكشن يختار أنهي.

اللي بقى بيحصل: من «مقرراتي» ← «رسالة للطلبة»: السكاشن بتظهر بعدد طلبتها (كلها متعلّمة افتراضيًا)، يكتب مرة ويبعت. الرسالة بتوصل لكل طالب مرة واحدة حتى لو في سكشنين، وبتروح لطلبة سكاشن الدكتور نفسه بس — مايقدرش يبعت لسكشن مش بتاعه.

⚙️ مفيش إعداد — بيشتغل كده لكل الجامعات، والبيانات من الـ SIS نفسه.

✅ تتأكد إزاي: ادخل بـ atef.dahesh ← مقرراتي ← BAS112 ← رسالة للطلبة ← اختار سكشن ← ابعت ← «اتبعتت لـ N طالب»؛ وادخل بطالب من السكشن تلاقيها في الشات.

12 حجز الساعات المكتبية — ومايتحذفش وهو محجوز

المطلوب: الطالب يحجز ميعاد من الساعات المكتبية؛ لو محجوز الدكتور مايقدرش يحذفه أو يعدّله؛ والحجوزات تظهر مع المواعيد وفي جدول الدكتور.

اللي بقى بيحصل: الطالب بيشوف المواعيد المتاحة لدكاترته ومرشده للأسبوعين الجايين ويحجز (ويكتب الموضوع) أو يلغي. الدكتور بيشوف تحت كل ميعاد مين حاجز وإمتى وعن إيه، والميعاد المحجوز مقفول للتعديل والحذف لحد ما الحجز يتلغي. الدكتور يقدر يلغي حجز بسبب — والطالب بيشوف الإلغاء والسبب. الحجوزات بتظهر في جدول الدكتور وجدول الطالب.

⚙️ إعداد في الأدمن
المكان Setup ← Attendance & office hours ← «Office-hour booking»
الاختيارات الحجز مفتوح/مقفول · عدد الطلبة في الميعاد · الحجز لحد كام يوم قدّام · الطالب يلغي لحد كام دقيقة قبل
الافتراضي مفتوح · 1 · 14 يوم · 60 دقيقة
على Innovation نفس الافتراضي

✅ تتأكد إزاي: طالب basmala.nabih.25010008 حاجز عند atef.dahesh الثلاثاء 6 أكتوبر ← ادخل بالدكتور ← ساعاتي المكتبية ← الميعاد «محجوز» وزرار الحذف مقفول ← «إلغاء» بسبب ← الطالبة تشوف السبب.

13 الخطط الدراسية مع بيانات الجامعة

المطلوب: بيانات الجامعة والكلية والبرنامج ترجع معاها الخطط الدراسية.

اللي بقى بيحصل: شاشة «عن الجامعة» (من غير حساب) بقت تعرض لكل برنامج خطته: المجموعات (متطلبات جامعة، كلية، إجباري، اختياري) بعدد ساعاتها ومقرراتها بالكود والساعات. الخطة = الخطة الحالية للبرنامج في الـ SIS.

⚙️ إعداد في الأدمن
المكان Setup ← University ← «Show study plans»
الاختيارات مفتوح / مقفول
الافتراضي مفتوح
على Innovation مفتوح

✅ تتأكد إزاي: افتح الديمو من غير لوجين ← عن الجامعة ← كلية ← «الخطة الدراسية» جنب البرنامج.

14 الحضور زي صفحة البورتال: مفيش افتراضي، 4 حالات، حضور الكل، والمصدر

المطلوب: مفيش حالة افتراضية؛ زرار «حضور الكل»؛ الحالات حاضر / غياب / متأخر / عذر؛ تسجيل مصدر الحضور؛ كل علامة تتحفظ فورًا لأن الاعتماد هيتلغي.

اللي بقى بيحصل: الكشف بيفتح ومحدش متعلّم — اللي مالوش علامة «لسه» (مش «حاضر»)، عشان محاضرة الدكتور نسي ياخد غيابها ماتطلعش «الكل حضر». الحالات الأربعة زي البورتال بالظبط (المتأخر بعدد دقايقه). «حضور الكل» بيعلّم اللي لسه بس، ومش بيلمس اللي مسح أو اتعلّم غياب/عذر. جنب كل طالب مصدر علامته: يدوي / QR / كود / موقع. وكل علامة بتتسجّل في الـ SIS أول ما تتحفظ — والاعتماد اتقفل من الأدمن على Innovation.

⚙️ إعداد في الأدمن
المكان Setup ← Attendance & office hours ← «Register approval»
الاختيارات مفتوح = الدكتور بيعتمد كل كشف (والكشوف بتتقفل بالترتيب) · مقفول = كل علامة نهائية لما تتحفظ
الافتراضي مفتوح (زي النظام القديم)
على Innovation مقفول — اتقفل 1 أكتوبر حسب قرار إلغاء الاعتماد

✅ تتأكد إزاي: atef.dahesh ← الحضور ← BAS112 ← الكل «لسه» ← دوس «حضور الكل» ← العدّاد يتغيّر، وافتح نفس اليوم في البورتال تلاقيهم حاضرين.

15 QR بيتغيّر كل شوية + كود للي مش عارف يمسح + قفل الحضور

المطلوب: آلية تجديد الـ QR كل فترة بفترة سماح من الأدمن؛ كود للجلسة؛ قفل الحضور = قفل جلسة الـ QR (إظهار/إخفاء).

اللي بقى بيحصل: الكود على شاشة الدكتور بيتغيّر لوحده كل 30 ثانية، فصورة الكود اللي اتبعتت على جروب مابتشتغلش بعد دقيقة. الكود القديم بيفضل مقبول 15 ثانية بعد ما يتغيّر (لكاميرا بطيئة). تحت الـ QR رقم من 6 أرقام ثابت طول المحاضرة للي موبايله مش بيمسح. «اقفل الحضور» بيخفي الكود وبيرفض أي مسح أو كود بعدها — ومش بيسجّل غياب لحد؛ «افتح تاني» بيرجّعه.

⚙️ إعداد في الأدمن
المكان Setup ← Attendance & office hours ← «Lecture QR»
الاختيارات كود جديد كل (ثانية؛ 0 = ثابت) · فترة السماح · إظهار كود الجلسة · قبول الـ QR الثابت القديم
الافتراضي 30 · 15 · مفتوح · مفتوح
على Innovation نفس الافتراضي — «قبول القديم» يتقفل لما كل نسخ الأبلكيشن تبعت الكود المتغيّر

✅ تتأكد إزاي: atef.dahesh ← الحضور بالـ QR ← BAS112 ← اعرض الكود ← العدّاد بيعد ← طالب يكتب الرقم ← «اتسجّل» ← «اقفل الحضور» ← طالب تاني يكتب الرقم ← «الحضور للمحاضرة دي اتقفل».

⏳

مستني: داتابيز BOX_HILL — البورت مقفول؛ أول ما يتفتح للـ IP 109.123.243.32 هتطلع نسخة على 9303 ببروفايل BoxHill من غير ما نلمس نسخة Innovation.

1 المقدمة — إحنا فين بالظبط؟

اقرا الجزء ده الأول. هيوفر عليك ساعات من الأسئلة.

خلينا نبدأ من الأول. في 3 حاجات منفصلة، ولازم تفرق بينهم:

1️⃣ البروتوتايب (Vercel)

تطبيق React اتعمل على university-pwlm.vercel.app. 27 شاشة كاملة، تصميم نهائي، عربي RTL.

بس… كل الداتا اللي فيه متكتبة بالإيد في الكود. مفيش ولا ريكوست واحد للسيرفر. ده «فيلم» للتطبيق مش تطبيق.

2️⃣ السيرفس (Informatique.SIS.MobileService)

باك إند .NET 10 بيتكلم مع داتابيز الـ SIS مباشرة بـ Dapper. مفيش stored procedures بتتنشر — كل الـ SQL جوّه الكود.

فيه ~90 إندبوينت. كلهم متنقلين — اللي مش بيرجّع داتا بيرجّع كده لأن الداتا نفسها مش موجودة للحالة اللي جرّبناها، مش لأن الكود ناقص.

3️⃣ الديمو (MobileDemo)

أخدنا نفس كود البروتوتايب بالظبط (استخرجناه من البِلد المنشور) وحطينا تحته طبقة داتا بتنادي السيرفس.

يعني اللي بتشوفه دلوقتي على الشاشة بيانات طالب حقيقي من الداتابيز، مش عيّنة.

✅

الخلاصة: الواجهة خلصت. الباك إند نصه خلص. اللي فاضل هو ربط الباقي + كتابة اللي لسه متكتبش. التيوتوريال ده بيقولك بالظبط إيه اللي خلص وإيه اللي لأ، شاشة شاشة.

🧪

جديد: كان فيه 7 خصائص واقفة مش بسبب كود — بسبب إننا مستنيين بيانات فوري وTeams وMoodle وFirebase. دلوقتي فيه Sandbox جوّه السيرفس بيقلّد البوابات دي بنفس بروتوكولها الحقيقي، فمبقاش فيه ولا خاصية واحدة Blocked، ورحلة الدفع كاملة بقت شغالة من التطبيق للداتابيز. الموبايل تيم يقدر يبني كل الشاشات دي النهاردة. اقرا القسم 10.

طب مين اللي جرّب ده؟

كل الأرقام اللي في التيوتوريال ده مش تخمين — إحنا شغّلنا السيرفس على داتابيز Innovation_Demo الحقيقية (984 جدول) وناديّنا كل إندبوينت واحد واحد وسجّلنا رد كل واحد. الطالب اللي بنجرّب بيه:

🎓

محمود محمد ابراهيم عوض — كلية العلاج الطبيعي · المستوى الثالث · المعدل التراكمي 2.42 · 101 ساعة منجزة

يوزر: mahmoued.eprahim.23010062 — وأي باسورد بيعدّي (هنشرح ليه في جزء الإعدادات).

2 الصورة الكبيرة

إزاي الـ 3 حتت بيتكلموا مع بعض.

┌─────────────────────────────────────────────────────────────────────┐
│  الموبايل / MobileDemo            (React, عربي RTL)                 │
│  ─────────────────────────────────────────────────────────────────  │
│  app/api.js     ← بينادي السيرفس ويفك الـ envelope القديم           │
│  app/data.jsx   ← بيحوّل رد السيرفس لشكل الشاشات بالظبط              │
│  screens-*.jsx  ← الشاشات (نفس كود البروتوتايب)                     │
└────────────────────────────┬────────────────────────────────────────┘
                             │  HTTP  (JWT في هيدر Authorization)
                             ▼
┌─────────────────────────────────────────────────────────────────────┐
│  Informatique.SIS.MobileService                     (.NET 10)       │
│  ─────────────────────────────────────────────────────────────────  │
│  EndPoints/       ← المسارات القديمة زي ما هي (students/login…)      │
│  FeatureGate      ← لو الخدمة مقفولة → يرد رسالة، ما يقعش أبدًا       │
│  Business/        ← الـ handlers + الـ SQL جوّه الكود                 │
│  ClientProfile    ← كل سلوك العميل من ملف JSON واحد                │
└────────────────────────────┬────────────────────────────────────────┘
                             │  Dapper (SQL نصّي، مفيش SP بيتنشر)
                             ▼
┌─────────────────────────────────────────────────────────────────────┐
│  داتابيز العميل (Innovation_Demo)          984 جدول                 │
└─────────────────────────────────────────────────────────────────────┘

3 قواعد لازم تفهمها قبل ما تكتب سطر

📜 الـ Envelope القديم

السيرفس دايمًا بيرد HTTP 200 — حتى في الغلط. الغلط بيتعرف من جوّه الـ body:

{ "result": "Ok",    "details": "NO ERROR", ... }
{ "result": "ERROR", "details": "data not found" }

وكمان: أي string فاضي بيرجع "" مش null. ده عشان التطبيقات القديمة المنشورة تفضل شغالة.

🎛️ الـ Feature Gate

كل خدمة ليها حالة: Active (شغالة) / Disabled (مقفولة بالإيد) / Blocked (مفتوحة بس ناقصها إعدادات أو جداول).

لو Blocked، الإندبوينت بيرد رسالة مهذّبة بدل ما يضرب 500. يعني إعداد ناقص عمره ما هيوقّع السيرفس.

🗄️ مفيش حاجة بتتنشر على الداتابيز

السيرفس ما بيعملش deploy لأي SP ولا view ولا سكريبت. بيعمل مسح على sys.objects ويتأكد إن الجداول اللي محتاجها موجودة.

لو ناقص جدول → الخدمة تبقى Blocked ومكتوب الجدول الناقص بالاسم. مش هتكتشفها في الإنتاج.

💡

للموبايل تيم: بعد اللوجين على طول نادي GET Info/Capabilities. هيرجّعلك حالة كل خدمة على التركيب ده، فتخفي الشاشات اللي مش متاحة بدل ما الزرار يضرب غلط في وش المستخدم.

3 شغّل النظام عندك

من الصفر لحد ما تشوف بيانات طالب حقيقي — 3 خطوات.

  1. شغّل السيرفس

    من فولدر Informatique.SIS.MobileService. لازم تحدد مكان ملف البروفايل:

    # Windows CMD
    set ClientProfile__Path=C:\path\to\clientprofile.json
    dotnet run --project Informatique.SIS.MobileService.WebApi ^
               --no-launch-profile --urls http://localhost:5099

    اتأكد إنه شغال: http://localhost:5099/health لازم يرد {"status":"up","setupMode":false}

  2. شغّل الديمو

    من فولدر MobileDemo — محتاج Node 18 أو أحدث بس:

    node server.js
    # أو دوس دبل كليك على start.bat

    افتح http://localhost:4173/ — واختار حساب من الكروت اللي في شاشة الدخول.

  3. افتح لوحة الإدارة

    http://localhost:5099/Admin — منها تظبط البروفايل وتشغّل فحص الجاهزية.

  4. شغّل الـ Sandbox جديد

    من Profile → تاب Sandbox. ده اللي بيخلّي الدفع والإرشاد والمنصة التعليمية والإشعارات تشتغل من غير بيانات اعتماد حقيقية. ظبّط اللينكين دول:

    Browser base URL   http://localhost:5099
    Loopback base URL  http://127.0.0.1:5099

    اتأكد: http://localhost:5099/health لازم يرد "sandbox":["payments","meetings","lms","push"]، وصفحة التحكّم على http://localhost:5099/__sandbox.

🔀

ليه فيه سيرفر للديمو أصلاً؟ السيرفس مافيهوش CORS. بدل ما نغيّر سيرفس إنتاجي عشان ديمو، server.js بيعمل proxy لـ /api/* على السيرفس — فالمتصفح يفضل شايف كل حاجة من نفس الـ origin. مغيّرناش حاجة في السيرفس عشان الديمو يشتغل.

الحسابات الجاهزة

اليوزرمين دهليه مفيد
mahmoued.eprahim.23010062محمود محمد ابراهيم عوض — طالبأغنى حساب: 8 مقررات، 14 حصة، 3 سنين ترانسكريبت، غياب، رسوم
ahmed.said.23010035أحمد سعيد — طالبللمقارنة بين طالبين
Hassana.Gamalحسناء جمال — عضو هيئة تدريسلتجربة شاشات الدكتور (50 طالب تحت الإشراف)

الباسورد: أي حاجة. مش بيتشيك عليه — اعرف ليه.

كوليكشن تسجيل المقررات — كل عملية في فولدر

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

1  تسجيل مقرر جديد        دخول → المسموح → المتاح → سجّل ✍️ → 🔎 تأكيد
2  تغيير سكشن             دخول → مقرراتي → السكاشن المرشّحة → انقل ✍️ → 🔎 تأكيد
3  حذف سكشن               دخول → مقرراتي → حذف ✍️ → 🔎 المقرر لسه مسجّل
4  حجز مقعد + الخروج منه   دخول → سكشن مليان → احجز ✍️ → 🔎 → اخرج ✍️ → 🔎
5  إسقاط مقرر               دخول → مقرراتي → احذف ✍️ → 🔎 الحالة بقت 6
6  تراجع عن الإسقاط         دخول → مقرراتي → السكاشن → تراجع ✍️ → 🔎 رجع وفي سكشن
7  قراءات مرجعية          نداءات مابتكتبش حاجة

⬇️ حمّل كوليكشن تسجيل المقررات (٧ فولدرات · ٣٥ ريكوست)

تقدر تشغّل فولدر واحد بس. في Postman: كليك يمين على الفولدر ← Run folder. ومن الطرفية:
newman run workflow-course-registration.postman_collection.json --folder "5 · إسقاط مقرر"
يعني لو بتشتغل على شاشة الإسقاط بس، مالكش دعوة بالستة التانية.
اتشغّل فعلاً قبل ما ينزل: كل فولدر لوحده صفر فشل، والكوليكشن كله ٣٥ ريكوست · ٤١ تحقّق · صفر فشل على السيرفر المنشور — والنسخة اللي بتنزّلها هي اللي اتجرّبت. وأي رفض من اللائحة (متطلب سابق / سقف ساعات / سعة سكشن / نافذة مقفولة) بيتعامل معاه على إنه رد صح ويتطلب إن يكون معاه سبب — مش على إنه فشل.
🔎 معناها القراءة بعد الكتابة. عملية بترد Status: true ومابتظهرش في أي شاشة هي أخطر عطل ممكن — مافيش فرق بينها وبين النجاح وقت النداء. عشان كده كل فولدر بيقفل بقراءة بتتأكد.

العناوين — HTTPS

التلات خدمات بقى ليها دومينات بشهادات Let's Encrypt حقيقية، عن طريق sslip.io اللي بيحوّل أي <ip>.sslip.io للـ IP نفسه — يعني من غير ما نشتري دومين ولا نظبط DNS.

الخدمةالعنوانكان
الـ API https://api.109.123.243.32.sslip.io :9300
الديمو https://demo.109.123.243.32.sslip.io :9301
الدليل ده https://docs.109.123.243.32.sslip.io :9302

البورتات القديمة لسه شغالة زي ما هي — الدومينات إضافة مش بديل. الـ baseUrl في كل كوليكشنات الـ Postman بقى على دومين الـ API، وتقدر ترجّعه http://localhost:5099 لو بتجرّب على جهازك.

تفصيلة مهمة للي هيصيانها: بورت 80 على السيرفر ده مش بتاعنا — فيه Nextcloud بتاع عميل تاني شغال عليه. عشان كده الشهادات بتتصدر عن طريق TLS-ALPN-01 على بورت 443 لوحده، مش HTTP-01 اللي بيحتاج بورت 80. الـ proxy (Caddy) بيشتغل كـ مشروع compose مستقل عشان أي up عليه ما يقدرش يلمس حاويات حد تاني.

حساب واحد بيشغّل كل الشاشات

كل شاشات الطالب في الديمو وفي الدليل ماشية على حساب واحد بس — من تسجيل الدخول لحد آخر شاشة، من غير ما حد يضطر يغيّر المستخدم في نص العرض.

الحسابمينبيغطي إيه
mahmoued.eprahim.23010062 طالب علاج طبيعي · ED_STUD_ID 73 ٨٢ إندبوينت — كل شاشات الطالب من غير استثناء، وفيها التسجيل والإسقاط والتراجع
atef.dahesh عضو هيئة تدريس شاشات الدكتور (كشوف الحضور، الـ QR، الجدول) — دور تاني، مش بديل

ده مكانش الوضع قبل كده: التسجيل كان محتاج حساب طالب تاني، لأن جدول كلية العلاج الطبيعي للترم ده كان متعمول بالكامل بس ماتعتمدش، وMOB_GET_STD_CRS_REG_PLN_SEC مابيرجّعش سكشن إلا لو عرضها معتمد. بعد اعتماد الجداول وفتح السكاشن للتسجيل الذاتي (ONLINE_FLG = 1 — الطالب مابيشوفش غيرها) بقى عند الطالب ٣٠ مقرر من خطته و١٢٩ سكشن، وقدامه ٨ مقررات متاحة للإضافة بسكاشن مفتوحة — فالعرض يتكرر أكتر من مرة من غير ما الليستة تفضى.

اتأكدنا منه بالتشغيل مش بالكلام: شغّلنا الـ ١٠٨ إندبوينت اللي الدليل بيوثّقها على الخدمة المنشورة — صفر ٤٠١، وصفر إندبوينت مكتوب عليه «شغّال» ورجع فاضي. وكوليكشن دورة التسجيل الكاملة عدّى ١٠ ريكوستات و٢٢ تحقّق من غير ولا فشل، كله بالحساب ده لوحده.

4 مفتاح الرموز

هتشوف الرموز دي في كل الجداول اللي جاية — افهمها مرة واحدة.

حالة الإندبوينت

● شغال
جرّبناه ورجّع داتا حقيقيةالموبايل يقدر يستهلكه دلوقتي حالًا.
○ شغال / فاضي
الكود سليم، الداتا مش موجودةرجّع 200 بقايمة فاضية. غالبًا محتاج إدخال بيانات من الباك أوفيس، مش شغل برمجة.
🔒 مقفول
الـ feature مقفولالكود متكتب وشغال، بس ناقص إعدادات في البروفايل.
🧪 شغال بالـ Sandbox
شغال دلوقتي، بس على بوابة مزيّفةالكود بتاعنا هو الكود النهائي — البوابة اللي على الطرف التاني هي المزيّفة. الموبايل يقدر يبني عليه حالًا، وللإنتاج محتاج بيانات حقيقية بس. التفاصيل.
🔑 محتاج بيانات
محتاج بيانات اعتماد خارجيةزي مفتاح فوري أو SMTP أو tenant بتاع مايكروسوفت.
◐ لسه متنقلش
مفيش منها دلوقتيالحالة دي كانت للإندبوينت اللي مساره موجود والـ SQL لأ. بعد مراجعة كل إندبوينت على السيرفس الشغال، مبقاش فيه ولا واحد — سايبينها في المفتاح عشان لو رجعت في المستقبل.
✕ مش موجود
مفيش إندبوينت خالصلازم يتبني من الصفر. الشاشة دلوقتي شغالة على داتا وهمية.

حالة الخاصية في الباك إند

متعمل
الباك إند بيدعمها بالكامل. الموبايل يقدر يعتمد عليها.
نص نص
جزء متعمل وجزء لأ — اقرا عمود «محتاجة إيه».
ناقص
مفيش دعم خالص. الشاشة بتعرض داتا وهمية.
⚠️

فرق مهم جدًا: «شغال / فاضي» مش زي «مش موجود». الأولى معناها الكود خلص وعايز بيانات بس (زي جدول الامتحانات — محتاج الكنترول يدخّله). التانية معناها لازم حد يقعد يكتب كود. ما تخلطش بينهم لما تقدّر الوقت.

عمود «الحساب» في جداول الإندبوينتات

كل رقم في الدليل ده جه من نداء حقيقي، والنداء مابيرجّعش داتا غير لما يتعمل بالحساب الصح. عشان كده كل إندبوينت مكتوب جنبه اتنادى بحساب مين:

العلامةالحسابيعني إيه
🎓 طالب mahmoued.eprahim.23010062 توكن طالب. ED_STUD_ID = 73
👨‍🏫 دكتور atef.dahesh توكن عضو هيئة تدريس. SA_STF_MEMBER_ID = 148 · SE_USER_ID = 2991
🌐 من غير توكن — بيشتغل قبل الدخول (Info/Capabilities، اللوجين نفسه، صورة الطالب)
أهم حاجة تاخد بالك منها: لو نادِيت إندبوينت بتاع دكتور بتوكن طالب، الرد هيبقى data not found — وده شكله زي إندبوينت باظ وهو مش باظ. الحساب غلط مش الإندبوينت. الباسورد أي حاجة (LegacyTrust)، بس نوع الحساب فارق.

الردود محفوظة جوّه الكوليكشن

كل ريكويست في الـ Postman جايّ معاه الرد الحقيقي بتاعه محفوظ كـ Example — تقدر تفتحه وتشوف شكل الرد من غير ما تبعت حاجة. الردود دي مش مكتوبة بالإيد: اتسجّلت من تشغيل فعلي لكل إندبوينت على السيرفر المنشور، بالحساب المكتوب جنبه.

اسم الـ Exampleمعناه
رد حقيقي — فيه داتاالحالة الطبيعية
رد حقيقي — ملف/صورةزي صورة الطالب وكود الـ QR
رفض قاعدة شغلانية (رد صح)«عندك طلب مفتوح بالفعل» — المنتج شغال صح
رد ناجح من غير داتاعمليات كتابة مالهاش ريسبونس
الرد لما الخاصية مقفولةبيقول الناقص إيه بالظبط
قاعدة ثابتة: أي إندبوينت بنشغّله، رده بيتحفظ معاه في الكوليكشن. فلو رد عندك اختلف عن الـ Example، الفرق ده معناه حاجة — داتا اتغيّرت أو حساب غلط أو توكن خلص.

5 الشاشات — واحدة واحدة

33 شاشة. لكل واحدة: صورتها، بتشتغل إزاي، جدول الإندبوينتات اللي محتاجاها، وجدول الخصائص بإيه المتعمل وإيه الناقص وإيه المطلوب — وكوليكشن Postman جاهز للتحميل.

📮

الـ Postman: كل شاشة ليها كوليكشن لوحدها. جوّه كل كوليكشن فيه ريكوست اسمه 0) Login — شغّله الأول وهو هيملأ التوكن وكل الـ ids (رقم الطالب، السنة، الفصل…) لوحده في متغيرات الكوليكشن، وبعدها كل الريكوستات تشتغل من غير ما تكتب أي رقم بإيدك.

وفيه كمان كوليكشن واحد بكل الشاشات لو تحب تستوردها مرة واحدة.

01 🔐

تسجيل الدخول

Login
7/8 خاصية متعملة · 88%
Postman · 10 ريكوست
login
login
login-manual
login-manual

دي أول شاشة الطالب بيشوفها. فيها طريقتين للدخول: زرار «المتابعة عبر مايكروسوفت» (SSO)، أو «الدخول بالرقم الجامعي» (يوزر وباسورد). الشاشة كمان بتعرض حسابات جاهزة للتجربة عشان الديمو يشتغل من غير ما حد يحفظ رقم جامعي.

بيشتغل إزاي؟

الموبايل بيبعت auth/login — إندبوينت واحد للاتنين. السيرفس بيعمل 3 حاجات: (1) يتأكد من الباسورد حسب auth.mode، (2) يجيب هوية الطالب من SE_USER ⨝ SE_USER_ACCNT ⨝ ENTITY_MAIN، (3) يحدد السنة والفصل الحاليين. الرد بيرجع UserMainData + التوكن. التوكن ده لازم يتخزن ويتبعت في كل ريكوست بعد كده في هيدر Authorization: Bearer <token>.

الفرع بيتحدد من الرد مش من الشاشة: في UserMainData فيه فلاج IsStudent. لو true الفرونت إند يمشي في مسار الطالب، ولو false يمشي في مسار عضو هيئة التدريس. مفيش سؤال للمستخدم «انت طالب ولا دكتور؟» — السيرفس هو صاحب الإجابة دي، والمستخدم مالوش إنه يدّعي دور.
students/login وacademicstaff/login لسه شغالين كأسماء قديمة لنفس الهاندلر بالظبط، وكل واحد فيهم بيقبل الاتنين — فاختيار المسار حسب الدور مجرد وهم. استخدم auth/login.
خد بالك: الرد بيحط نفس الرقم في ED_STUD_ID وSA_STAFF_MEMBER_ID، فالحقل اللي مالوش معنى للدور الحالي لازم يتجاهَل مش يتصدّق.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST auth/login ● شغال 🌐 من غير توكن ⭐ ده الإندبوينت الرسمي — للطالب وللدكتور. الرد فيه IsStudent وهو اللي بيحدد المسار
POST auth/login ● شغال 🌐 من غير توكن ⭐ نفس الإندبوينت بحساب دكتور — IsStudent: false
POST students/login ● شغال 🌐 من غير توكن اسم قديم لنفس الهاندلر — بيقبل الطالب والدكتور. متسيبه للتوافق بس
POST academicstaff/login ● شغال 🌐 من غير توكن اسم قديم كمان — نفس الهاندلر بالظبط
POST students/LoginAZ 🔑 محتاج بيانات 🌐 من غير توكن جديد ⭐ دخول مايكروسوفت — التوكن بقى بيتراجع فعلًا (التوقيع، الـ tenant، الـ audience، الصلاحية). ابعت الـ id_token مش توكن Graph. مقفول على Innovation لأن طريقة الدخول الرئيسية «1» — شوف دليل دخول مايكروسوفت
POST __sandbox/identity/token 🧪 شغال بالـ Sandbox 🌐 من غير توكن جديد 🧪 توكن مايكروسوفت تجريبي موقّع من الـ Sandbox — ابعته لـ students/LoginAZ وجرّب الدورة كلها من غير tenant. شغال بس لما «Microsoft sign-in» مفتوح في تاب Sandbox
POST students/GetForgetPasswordCode 🔑 محتاج بيانات 🌐 من غير توكن بيولّد كود ويبعته إيميل — محتاج بيانات SMTP صح
POST students/ResetPasswordUsingCode ● شغال 🌐 من غير توكن تغيير الباسورد الفعلي على الـ AD لسه متكتبش
POST auth/me ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ جديد ده اللي بيقول لك الشخص ده يشوف إيه — بيرجّع home وscreens[] بـ allowed/reason وfeatures[] بتاعة دوره بس. شوف فصل الشاشات المسموحة
GET Info/Capabilities ● شغال 🌐 من غير توكن حالة التركيب نفسه — بيتنادى قبل شاشة الدخول ومش عارف مين بيسأل. مش بديل لـ auth/me

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
إندبوينت دخول واحد + فلاج IsStudent متعمل ⭐ auth/login بيخدم الاتنين، والرد بيقول IsStudent. الديمو حذف اختيار «طالب / عضو هيئة تدريس» من شاشة الدخول خالص — المستخدم مايتسألش عن حاجة السيرفس عارفها. —
① الدخول الافتراضي (يوزر + باسورد افتراضي) متعمل جديد الـ SIS مفيهوش باسوردات أصلًا، فالطريقة دي ليها وضعين من الأدمن: «أي باسورد» (زي النسخ القديمة — وده اللي على Innovation دلوقتي عشان شغلكم مايقفش) أو «باسورد موحّد» زي 123 بتاع Lotus. لو الباسورد غلط الرد: details: "password not correct". —
② Active Directory متعمل جديد اتعمل فعلًا (كان TODO بيرفض دايمًا): LDAP bind بـ user@domain زي الكود القديم بالظبط، وفي الأدمن زرار «Test sign-in» يجرّب بحساب حقيقي. لو الـ DC مش بيرد، الرد بييجي في ثواني: «The university directory could not be reached». بيانات الـ domain controller من الجامعة
③ Microsoft (Azure) متعمل جديد التوكن بيتراجع مقابل مفاتيح الـ tenant — التوقيع والـ issuer والـ audience والصلاحية. توكن Graph بيترفض برسالة بتقول تبعت إيه بداله. اتجرّب بـ 6 حالات على الـ Sandbox. App Registration من الجامعة + الموبايل يبعت id_token
الاتنين مع بعض متعمل جديد لو الرئيسي مايكروسوفت، الأدمن يقدر يسيب اليوزر والباسورد شغالين جنبه (بـ ① أو ②) أو يقفلهم. مقفولين ← auth/login بيرد «Sign in with your Microsoft account.» —
تحديد السنة والفصل متعمل 4 أوضاع (GlobalCurrent / StudentLastSemester / FacultyCurrentYear / ByUserType). ⚠️ في الداتابيز دي CURRENT_YEAR لسه على 2023/24 والفصل المفتوح 2025/26 — فمستخدمين ByUserType
JWT للموبايل متعمل HS256، المدة من البروفايل، فيه claims: seUserId / edStudId / userType. —
نسيت الباسورد جاهز · مستني بيانات بيولّد الكود ويحفظه، لكن الإيميل بيقع. بيانات SMTP حقيقية في تبويب E-mail
02 🏠

الرئيسية

Home
5/7 خاصية متعملة · 71%
Postman · 8 ريكوست
home
home

قلب التطبيق. بتعرض: تحية باسم الطالب، محاضرات النهارده على خط زمني، المحاضرة اللي شغالة دلوقتي بزرار تسجيل حضور، اختصارات سريعة، آخر الإعلانات، والخدمات المتصلة.

بيشتغل إزاي؟

الشاشة دي مش بتنادي إندبوينت خاص بيها — بتتبني من الداتا اللي اتحمّلت بعد اللوجين. الجدول بيتقسم بالأيام (GS_CODE_WEEK_DAY_ID: 2=السبت … 7=الخميس)، وبنقارن ساعة الموبايل بـ FROM_TIME/TO_TIME عشان نعرف المحاضرة past / current / upcoming.
ملاحظة مهمة: النهارده بره فترة الفصل، فالتطبيق بيعرض أقرب يوم دراسي جاي بدل ما يعرض يوم فاضي.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/scheduledays ● شغال 🎓 طالب
mahmoued.eprahim.23010062
مصدر محاضرات النهارده (قاعة، دكتور، مجموعة)
POST students/get ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ كارت الطالب — وفيه STUD_PHOTO اترجع: لينك كامل جاهز للـ <img>، مش اسم ملف. الخدمة بتركّبه من إعداد Media.StudentPhotoBaseUrl، ولو مفيش مجلد صور متظبّط بترجّع صورة افتراضية من عندها عشان الشاشة ماتبانش مكسورة
POST students/GetMobileNotifications ● شغال 🎓 طالب
mahmoued.eprahim.23010062
عدّاد الجرس
POST students/schedulePerday ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ محاضرات النهارده — محتاج SC_SCHEDULE_DTL_Date يغطّي النهاردة
GET Info/Capabilities ● شغال 🌐 من غير توكن بيحدد الخدمات المتصلة اللي تتعرض
POST students/GetStudentAttend ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ ✍️ الطالب بيمسح كود المحاضرة. لازم يكون مسجّل في السكشن ده — والمثال هنا سكشن الطالب نفسه. بيرفض بسبب واضح لو الجلسة مش موجودة، أو مش يوم المحاضرة، أو برّه الوقت، أو مش مسجّل. ومسح مرتين = سطر واحد · جديد ٢ ابعت QR_TOKEN وQR_WINDOW من الـ payload؛ المسح بيسجّل «حاضر» في الكشف على طول
POST students/AttendByCode ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ٢ للي مش عارف يمسح: الـ 6 أرقام اللي تحت الـ QR
POST students/CheckInByLocation ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ ✍️ جديد الحضور بالموقع — الطريق التاني الموازي للـ QR. التطبيق بيبعت القراية بس، والسيرفر هو اللي بيحدد المحاضرة من جدول الطالب ويتأكد إنه جوه الحرم. جرّبناه على الداتابيز الحقيقية: من جوه الحدود اتسجّل حضور في SUR311، ومن ميدان التحرير رجّع «إنت بره حرم الجامعة بحوالي 1639 متر»، وتاني ضغطة = نفس السطر

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
محاضرات النهارده متعمل 14 محاضرة راجعة فعليًا للطالب التجريبي. —
تحديد المحاضرة الحالية متعمل بيتحسب في الموبايل من ساعة الجهاز — مفيش إندبوينت مطلوب. —
تسجيل الحضور بالموقع (GPS) متعمل ⭐ جديد زرار تاني جنب الـ QR: التطبيق بياخد إحداثيات الجهاز ويبعتها، والسيرفر بيرد اتسجّل ولا لأ وليه. مابتبعتش رقم محاضرة — السيرفر بيلاقيها لوحده، ومافيش أي حساب في التطبيق. الطريقتين مستقلين والطالب يختار. التفاصيل في شاشة تسجيل الحضور بالموقع. والبصمة اتحذفت من الشاشة — كانت رسمة لتحقق مش موجود أصلاً. إذن الموقع في التطبيق + الجامعة ترسم حدودها مرة واحدة
الحضور بالـ QR متعمل ⭐ جديد اتعمل كامل: عرض الكود، المسح بكل التحققات، وقفل المحاضرة اللي بيسجّل غياب اللي مامسحش. شوف فصل الحضور. —
الإعلانات ناقص الإعلانات اللي في الشاشة ثابتة في الكود. إندبوينت إعلانات (ممكن يتبني على SV_MESSAGE) أو استخدام الإشعارات
عدّاد الإشعارات متعمل الإندبوينت شغال، بس صندوق الطالب ده فاضي. إرسال إشعارات من الباك أوفيس
الخدمات المتصلة (Teams/Moodle) جاهز · مستني بيانات بيتعرضوا حسب Info/Capabilities، وعليهم علامة «غير مفعّل». بيانات Teams و Moodle في تبويب Integrations
03 📅

الجدول الدراسي

Schedule
5/6 خاصية متعملة · 83%
Postman · 5 ريكوست
schedule
schedule

الجدول الأسبوعي كامل من السبت للخميس، وكل يوم بيعرض محاضراته بالتوقيت والقاعة والدكتور. فيه كمان تبويب لجدول الاختبارات، وعرض شهري.

بيشتغل إزاي؟

students/scheduledays بيرجع صف لكل حصة في الأسبوع (مش لكل مقرر). التطبيق بيجمّعهم بـ GS_CODE_WEEK_DAY_ID ويرتّبهم بـ FROM_TIME. students/schedule بيرجع المقررات المسجلة نفسها (مستخدم في شاشة المقررات).
جديد كل سكشن بقى بيقول بيتدرّس إمتى وفين — في students/schedule وفي academicstaff/GetStaffSchedule، بنفس أسامي حقول شاشة التسجيل: DaysAr · FromTime · ToTime · HallAr · BuildingAr · CampusAr (المقر) للسطر الواحد، وmeetings[] لكل يوم لوحده (DayAr, FromTime, HallAr, CampusAr, TeachingMethodAr) — لأن المعمل ممكن يبقى في قاعة غير المحاضرة. سكشن مالوش مواعيد متسجّلة بيرجع meetings: [].

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/scheduledays ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ الأساسي — 14 حصة بالتفاصيل
POST students/schedule ● شغال 🎓 طالب
mahmoued.eprahim.23010062
المقررات المسجلة (كود، ساعات، مجموعة) جديد + الأيام والمقر والقاعة وmeetings[] · جديد ٢ + officeHourBookings[] — حجوزات الطالب الجاية
POST students/GetExamSchdule ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ جدول الامتحانات بالقاعات — بيحتاج IS_EXAM_APPROVED=1 وتوزيع لجان للطالب
POST semester/semsterCalnd ● شغال 🎓 طالب
mahmoued.eprahim.23010062
اتصلّح التقويم الأكاديمي — 48 بند للربيع
POST academicstaff/GetStaffSchedule ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ اتصلّح جدول الدكتور — سطر لكل سكشن (مش مكرر 3 مرات) + SC_SCHEDULE_DTL_ID وmeetings[] · جديد ٢ + officeHourBookings[] — حجوزات الساعات المكتبية الجاية

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
الجدول الأسبوعي متعمل شغال بالكامل بأسماء القاعات والدكاترة والمجموعات. —
جدول الاختبارات متعمل الإندبوينت والـ feature شغالين، بس ED_EXAM_SCHDL فاضي للطالب ده. إدخال جدول الامتحانات من الباك أوفيس
العرض الشهري ناقص الأيام اللي عليها محاضرات/امتحانات ثابتة في الكود (مايو 2026). اشتقاقها من scheduledays + جدول الامتحانات — شغل موبايل مش باك إند
التقويم الأكاديمي متعمل اتصلّح بيرجّع 48 بند للربيع. شوف شاشة التقويم — السبب كان قاعدة «يظهر للطالب» القديمة. —
جدول الدكتور متعمل اتصلّح كان بيرد «data not found» لأي دكتور سكاشنه مش جوّه «مجموعة سكاشن» (وده أغلب الدكاترة). وكل سكشن كان بيتكرر 3 مرات. الاتنين اتصلّحوا في الكود. —
أيام التدريس والمقر لكل سكشن متعمل جديد DaysAr / CampusAr / meetings[] في الجدولين — نفس شكل شاشة التسجيل. —
04 📚

المقررات الدراسية

Courses
7/7 خاصية متعملة · 100%
Postman · 7 ريكوست
courses
courses
course-detail
course-detail

قايمة مقررات الفصل الحالي، وكل مقرر بيعرض الكود والساعات ونسبة الحضور والتقدّم. الضغط على مقرر بيفتح صفحة تفاصيله.

بيشتغل إزاي؟

التطبيق بيدمج 3 مصادر: students/schedule (المقررات المسجلة)، students/getabs (نسبة الغياب لكل مقرر)، students/semresult (الدرجة لو ظهرت). الدمج بيحصل بـ ED_COURSE_ID.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/schedule ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ المقررات المسجلة · جديد ٢ + officeHourBookings[] — حجوزات الطالب الجاية
POST students/getabs ● شغال 🎓 طالب
mahmoued.eprahim.23010062
نسبة الحضور لكل مقرر
POST students/GetCourseworkMarks ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ⭐ درجات أعمال الفصل — الدرجات اللي بتتجمّع خلال الترم لكل مقرر ولكل عنصر تقييم (امتحان نصفي، عملي، مشروع…) ومن كام. دي شاشة StudSemWork في منيو الـ SIS نفسه، وكانت ناقصة تمامًا: كان عندنا النتيجة النهائية والسجل الأكاديمي بس. لو ماتبعتش سنة/فصل بيرجّع فصل الطالب الحالي
POST students/semresult ● شغال 🎓 طالب
mahmoued.eprahim.23010062
الدرجة والحالة
POST Faculties/CoursesCatalogNew ● شغال 🎓 طالب
mahmoued.eprahim.23010062
وصف المقرر والمصروفات
POST lms/MyCourses 🧪 شغال بالـ Sandbox 🎓 طالب
mahmoued.eprahim.23010062
جديد مقررات Moodle بنسبة الإنجاز — 4 مواد راجعة
POST lms/Assignments 🧪 شغال بالـ Sandbox 🎓 طالب
mahmoued.eprahim.23010062
جديد التكليفات ومواعيد التسليم — مرتّبة بالأقرب

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
قايمة المقررات متعمل 8 مقررات راجعة فعليًا. —
نسبة الحضور لكل مقرر متعمل من getabs — بيرجع كمان حدود الإنذار (15%) والحرمان (25%) من إعدادات العميل نفسه. —
الدرجة الحالية متعمل شغال، بس درجات الفصل ده لسه ما اعتُمدتش (GRADING_AR فاضي). اعتماد النتيجة من الكنترول
نسبة التقدّم في المقرر متعمل ⭐ الموبايل بقى بياخد نسبة الإنجاز الحقيقية من Moodle لكل مقرر، وبيرجع لتقدّم الفصل بس للمقررات اللي Moodle مايعرفهاش — والشاشة بتقول ده صراحةً، بدل ما رقم تقريبي يبان كأنه داتا. —
تكليفات ومواعيد تسليم (Moodle) متعمل جديد MoodleLmsProvider شغال ومتجرَّب على الـ Sandbox: بيطابق الطالب، يجيب مواده وتكليفاته، ويرتّبهم بالأقرب تسليمًا. اتصلّح هنا باج: الكود كان بيقرا STUD_FACULTY_CODE من جدول ED_STUD وهو أصلاً على ED_STUD_FACULTY، فالشاشة كلها كانت بترمي 500. baseUrl + wsToken من أدمن Moodle الحقيقي
اسم الدكتور في الكارت متعمل الموبايل بيدمج schedule مع scheduledays — التاني هو اللي فيه اسم الدكتور. الدمج اتعمل والكارت بيعرض الاسم. —
محتوى المقرر والمراجع متعمل ⭐ COURSE_CONTENTS_AR بقى معروض في صفحة المقرر. —
05 🏆

نتيجة الفصل

Grades
4/5 خاصية متعملة · 80%
Postman · 3 ريكوست
grades
grades

السجل الأكاديمي كامل: كل فصل بمعدله ومعدله التراكمي والساعات، وجوّاه درجات كل مقرر، مع منتقي فصول ورسم لتطور المعدل.

بيشتغل إزاي؟

students/transcript بيرجع شجرة: سنة ← فصل ← مقررات. كل صف مقرر جواه كمان SEM_GPA وACCUM_GPA وACCUM_CH، فالتطبيق بياخد أول صف في الفصل كمصدر لإحصائيات الفصل كله.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/transcript ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ 3 سنين × فصولها × مقرراتها
POST students/semresult ● شغال 🎓 طالب
mahmoued.eprahim.23010062
نتيجة الفصل الحالي بس
GET students/GetSTudentTranscriptApi ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⚠️ GET مش POST · بيحتاج RESULT_APPROVE_FLG=1

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
السجل الأكاديمي الكامل متعمل 3 سنين دراسية راجعة بالمقررات والدرجات والنقاط. —
المعدل الفصلي والتراكمي متعمل جاي جاهز من الـ SQL — مش محسوب في الموبايل. —
تطور المعدل (رسم) متعمل بيتبني من نفس بيانات الترانسكريبت. —
حارس «النتيجة معتمدة؟» متعمل ⭐ القرار اتاخد من البوابة نفسها: فلتر الاعتماد في CurrentCourseResultResults.aspx.cs متعلّق عليه كومنت — يعني البوابة بتعرض النتيجة سواء اتعمدت أو لأ. فالموبايل بيعرضها كمان وبيعلّم عليها «غير معتمدة» بدل ما يخفيها. اللي فعلاً متقفل على الاعتماد هو السجل الرسمي وده مظبوط. —
تحميل الترانسكريبت PDF ناقص مفيش أي إندبوينت لده. إندبوينت يولّد PDF أو يوصل لتقرير الـ SIS
06 📍

تسجيل الحضور بالموقع

Location check-in
6/7 خاصية متعملة · 86%
Postman · 4 ريكوست
طريقتين جنب بعض — كود أو موقع
طريقتين جنب بعض — كود أو موقع
من جوّه الحرم: اتسجّل
من جوّه الحرم: اتسجّل
من برّه: اترفض بالمسافة
من برّه: اترفض بالمسافة

الطالب بيسجّل حضوره لإنه موجود في الجامعة فعلاً. التطبيق بيبعت إحداثيات الجهاز، والسيرفر بيحكم. طريق موازي للـ QR — مش بديل ليه ولا خطوة قبله.

بيشتغل إزاي؟

إنت بتبعت 3 أرقام وبس — LAT وLNG وACC (دقة القراية بالمتر زي ما الجهاز إداهالك). مابتبعتش رقم مقرر ولا رقم محاضرة: السيرفر بيدوّر لوحده في جدول الطالب على المحاضرة الشغالة دلوقتي، فالزرار واحد في كل الشاشات ومفيش حاجة تظبّطها قبل ما تبعت.
وماتعملش أي حساب عندك: حدود الجامعة عمرها ما بتوصل للتطبيق — مفيش مكتبة geofence ولا مسافات ولا حدود متخزّنة عندك. إنت بتبعت قراية والسيرفر بيحكم.
الرد: نجاح فيه messageAr جاهزة للعرض + session (اسم المقرر) + presenceTime + locationAr. وalreadyRecorded: true معناها ضغط تاني — <u>ده مش error</u>، اعرضه بشكل النجاح.
الرفض بيرجع HTTP 200 بـ result: "ERROR" وdetails رسالة عربي جاهزة للطالب — اعرضها زي ما هي.
الأذونات: iOS NSLocationWhenInUseUsageDescription، أندرويد ACCESS_FINE_LOCATION (الـ COARSE لوحده السيرفر هيرفض دقته). اقرا قراية جديدة (maximumAge = 0) بدقة عالية وtimeout ~15 ثانية، ولو المستخدم رفض الإذن سيبله الـ QR شغال. وفي الويب/WebView على http المتصفح بيمنع الموقع خالص.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
GET Info/Capabilities ● شغال 🌐 من غير توكن فيه campus.checkInEnabled — التطبيق بيخفي الزرار لو الجامعة مافعّلتش الخاصية أو مرسمتش حدود
POST students/CheckInByLocation ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ ✍️ جديد من جوّه الحدود: اتسجّل حضور في SUR311 والسيرفس هو اللي اختار المحاضرة. تاني ضغطة = نفس السطر
POST students/CheckInByLocation ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ من ميدان التحرير: «إنت بره حرم الجامعة بحوالي 1639 متر»
POST students/CheckInByLocation ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ قراية دقتها ±800 متر: «إشارة الموقع ضعيفة» — بيترفض قبل ما يبص للحدود أصلاً

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
الزرار يبان امتى متعمل من Info/Capabilities → campus.checkInEnabled. لو false ماتعرضهوش أصلاً بدل ما الطالب يدوس وياخد رفض. —
قراية الموقع متعمل إذن + قراية جديدة بدقة عالية. أول قراية بتيجي وحشة وبعد ثانيتين بتتحسّن — استنى الكويسة قبل ما تبعت. ربط الـ GPS في التطبيق الحقيقي
التحقق نفسه متعمل ⭐ كله في السيرفر: دقة القراية، جوه الحدود ولا لأ، وأنهي محاضرة. مفيش أي منطق عندك — والحدود مابتتبعتش للتطبيق عن قصد. —
رسائل الرفض متعمل أربع حالات وكلها بتيجي جاهزة بالعربي في details: بره الحرم (وبالمسافة)، إشارة ضعيفة، مفيش محاضرة دلوقتي، الخاصية مقفولة. —
ضغط مرتين متعمل السيرفر مابيسجّلش مرتين وبيرجّع أول وقت — بس اقفل الزرار وقت الانتظار للـ UX. —
حدود الجامعة متعمل بترسمها الجامعة مرة واحدة من لوحة الإدارة (تبويب Campus map) — مش شغلك. لو مرسموش، الخاصية بتيجي مقفولة في Capabilities. الأدمن يرسمها
تخزين مسار الطالب ناقص مش متعمل عن قصد. الإحداثيات بتتفحص وتترمي — مفيش سجل بمكان الطالب. لو الجامعة طلبت ده، بيبقى قرار وشغل جديد. قرار من الجامعة
07 📋

الحضور والغياب

Attendance
4/5 خاصية متعملة · 80%
Postman · 2 ريكوست
attendance
attendance

أهم شاشة للطالب عمليًا. بتعرض نسبة الغياب في كل مقرر بالألوان (أخضر/أصفر/أحمر)، وكام يوم فاضل قبل الإنذار، وتواريخ الغياب بالتفصيل.

بيشتغل إزاي؟

students/getabs بيرجع لكل مقرر: StudAbs (عدد مرات الغياب)، StudAbsPrcnt (النسبة)، وقايمة absences بالتواريخ. والأهم: بيرجع كمان ABS_RATE_WARNINIG_1 وABS_RATE_COURSE_FAIL — يعني حدود الإنذار والحرمان بتيجي من إعدادات الجامعة نفسها مش ثابتة في الموبايل.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/getabs ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ 8 مقررات بتواريخ الغياب والحدود
POST academicstaff/getabs ● شغال 👨‍🏫 دكتور
atef.dahesh
نفس الإندبوينت لمسار الموظفين

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
نسبة الغياب لكل مقرر متعمل شغال بالكامل بداتا حقيقية. —
حدود الإنذار والحرمان متعمل بتيجي من الداتابيز (15% إنذار / 25% حرمان هنا). —
تواريخ الغياب بالتفصيل متعمل مع نوع الحصة (محاضرة/عملي) وهل معذور. —
وضع نظام الساعات مقابل السنوي متعمل الفلاج isModular بيبدّل الاستعلام تلقائيًا. —
تقديم عذر غياب ناقص مفيش إندبوينت خالص. إندبوينت رفع عذر + مرفق + دورة موافقة
08 ⏰

جدول الاختبارات

Exams
2/3 خاصية متعملة · 67%
Postman · 1 ريكوست
exams
exams

عدّاد لأقرب امتحان، وقايمة الامتحانات بالتاريخ والوقت والقاعة ورقم الجلوس.

بيشتغل إزاي؟

students/GetExamSchdule بيقرا من ED_EXAM_SCHDL. الشاشة محكومة بـ feature examSchedule.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/GetExamSchdule ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ جدول الامتحانات بالقاعات — بيحتاج IS_EXAM_APPROVED=1 وتوزيع لجان للطالب

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
جدول الامتحانات متعمل الكود والـ feature والجدول موجودين — بس ED_EXAM_SCHDL فاضي. إدخال الجدول من الكنترول — مش شغل برمجة
رقم الجلوس والقاعة متعمل الأعمدة موجودة في الـ SQL. نفس الكلام — بيانات
تذكير قبل الامتحان ناقص مفيش جدولة إشعارات. مهمة مجدولة تبعت push قبل الامتحان بيوم
09 👤

الملف الشخصي

Profile
2/5 خاصية متعملة · 40%
Postman · 5 ريكوست
profile
profile

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

بيشتغل إزاي؟

students/get بيرجع صف واحد فيه ~35 عمود — ده الـ «كارت» بتاع الطالب. بيستخدم dbo.fn_GetStudContactMethod عشان يجيب الإيميل والموبايل.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/get ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ كل بيانات الطالب
POST academicstaff/get ● شغال 👨‍🏫 دكتور
atef.dahesh
بيانات الدكتور
GET Files/UserImages/{name} ● شغال 🌐 من غير توكن صورة الطالب — بيقرا من فولدر Files/UserImages، اسم الملف هو الكود الجامعي + .jpg
POST students/GetContactMethods ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد 🔒 وسائل التواصل — مقفول افتراضيًا (studentContactEdit)
POST students/SaveContactMethod ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد 🔒 ✍️ إضافة/تعديل/حذف — مقفول افتراضيًا

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
بيانات الطالب متعمل كله راجع: الاسم، الكود، الكلية، المستوى، GPA 2.42، 101 ساعة، المرشد. —
كارت الموظف متعمل academicstaff/get بيرجّع بيانات الدكتور. نفس القصة: الحساب المجرّب هو اللي مكانش عليه داتا. —
صورة الطالب ناقص Files/UserImages لسه متعملش. ستريم للملف + مسار الصور في الإعدادات
تعديل البيانات (موبايل/إيميل) ناقص العرض بس، مفيش تعديل. إندبوينت تحديث + دورة موافقة
تغيير اللغة ناقص التطبيق عربي بس دلوقتي (SHOW_EN = false). تفعيل الفلاج + مراجعة النصوص الإنجليزية
10 🪪

البطاقة الجامعية

Digital ID
2/4 خاصية متعملة · 50%
Postman · 2 ريكوست
qrid
qrid

بطاقة الطالب الرقمية بالاسم والكلية والرقم الجامعي وكود QR للدخول والمكتبة.

بيشتغل إزاي؟

دلوقتي البطاقة بتتبني من students/get، والـ QR شكل بس مش قابل للمسح — نمط ثابت مشتق من بيانات الطالب. الإندبوينت الحقيقي students/GetStudentQRCode موجود ومربوط بـ feature qrCard بس بيرجع «data not found».

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/get ● شغال 🎓 طالب
mahmoued.eprahim.23010062
بيانات البطاقة
POST students/GetStudentQRCode ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⚠️ بيرجّع صورة PNG مش JSON · UserName بياخد اسم الدخول أو الكود الجامعي، ولو بعتّه بودي فاضي بيرجّع كارنيه صاحب التوكن

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
بيانات البطاقة متعمل الاسم والكلية والرقم كلهم حقيقيين. —
كود QR حقيقي متعمل students/GetStudentQRCode بيرجّع صورة PNG فعلية. كان بيرد «data not found» لما تبعتله اسم الدخول لأنه كان بيدوّر بـالكود الجامعي بس — دلوقتي بيقبل الاتنين، وبودي فاضي بيرجّع كارنيه صاحب التوكن. —
صلاحية مؤقتة للكود ناقص العدّاد «صالح 04:58» ديكور في الموبايل. كود موقّع بوقت انتهاء + إندبوينت تحقق للبوابة
إضافة لـ Apple/Google Wallet ناقص الزرار موجود بس مش موصّل. توليد pass موقّع — شغل كبير ومحتاج شهادات
11 💬

المحادثات

Chat
8/13 خاصية متعملة · 62%
Postman · 11 ريكوست
chat
chat
chat-thread
chat-thread

شات نص، فرد لفرد، بين الطالب والدكتور. الطالب بيكلّم مرشده أو دكاترة مقرراته، والدكتور بيكلّم طلبة إشرافه أو طلبة مقرراته. الطرفين بيشوفوا نفس المحادثة بالظبط.

بيشتغل إزاي؟

٣ شاشات، وكل واحدة نداء واحد:

١) شاشة «ابعت لمين» (اختيار جهة اتصال جديدة)
الطالب ينادي getInstructorsAndAdvisorForAsk → بيرجّع AcademicAdvisior (المرشد) وInstructors (دكاترة مقرراته الترم ده).
الدكتور ينادي getStudentsForAsk → بيرجّع Advisees (طلبة إشرافه) وCourses (مقرراته) وCourseStudents (طلبة المقررات).

٢) شاشة الصندوق (المحادثات القايمة)
الطالب: GetStudMailInBox · الدكتور: GetStaffMailInBox.
كل صف = محادثة. فيه اسم الطرف التاني، عدد الرسايل، تاريخ آخر رسالة، وحقول لازم تخزّنها وتبعتها زي ما هي لما تفتح المحادثة أو تبعت رد.

٣) شاشة المحادثة نفسها
GetaskById بيرجّع courses[] — مصفوفة رسايل مرتّبة بالتاريخ. وAddAsk بيبعت رسالة جديدة.

ــــــــــــــــــــ

🎨 إزاي ترسم الفقاعة يمين ولا شمال
كل رسالة في courses[] معاها AdvChat:
AdvChat: true ← الرسالة من الدكتور / المرشد
AdvChat: false ← الرسالة من الطالب
خلاص، مفيش مقارنة IDs ولا حسابات. الفلاج ده دايمًا موجود في كل رسالة.
(فيه كمان FROM_STUD — ده القديم، وبيرجع null في الرسايل المكتوبة من بوابة الويب. متعتمدش عليه، استخدم AdvChat.)

⚠️ أهم حاجة: AdvChat في الريكوست ≠ AdvChat في الرسالة
الاتنين اسمهم واحد بس معناهم مختلف:
• جوّه الرسالة → مين كتبها (اقراه بس)
• في بودي AddAsk → قناة المحادثة (اكتبه بس)
القاعدة الذهبية: الـ AdvChat اللي هتبعته في AddAsk خده زي ما هو من صف الصندوق (أو من صف جهة الاتصال). متحطّش قيمة ثابتة من عندك — ده اللي كان بيخلّي المحادثة تفتح فاضية.

📋 الحقول اللي بتنتقل من الصندوق للمحادثة
من صف الصندوق خُد: SA_STF_MEMBER_ID (أو ED_STUD_ID لو إنت في تطبيق الدكتور) + ED_ACAD_YEAR_ID + ED_CODE_SEMESTER_ID + AS_FACULTY_INFO_ID + AdvChat — وابعتهم زي ما هم في GetaskById وAddAsk. مفيش أي رقم تحسبه إنت.

🔒 حاجات السيرفر بيتجاهلها من الريكوست
رقمك إنت (رقم الطالب لو داخل بحساب طالب، رقم الدكتور لو داخل بحساب دكتور) بيتاخد من التوكن مش من البودي. فمهما بعت رقم تاني، السيرفر هيستخدم بتاعك. سيبه في البودي عادي (مفيش حاجة هتقع)، بس متتوقعش إنك تقرا محادثة حد تاني بيه.

🔁 مفيش إشعارات لسه
لما الطرف التاني يبعت، مفيش push بيوصل. لو عايز الشاشة تتحدّث، اعمل polling على GetaskById وإنت فاتح المحادثة.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/getInstructorsAndAdvisorForAsk ● شغال 🎓 طالب
mahmoued.eprahim.23010062
👤 شاشة «ابعت لمين» — الطالب. الرد: AcademicAdvisior (أوبچكت واحد، ممكن يرجع فاضي) + Instructors[] (15 صف هنا). الدكتور الواحد ممكن يتكرر لو بيدّي أكتر من مقرر — اعمل distinct على SA_STF_MEMBER_ID لو عايز قايمة أشخاص
POST academicstaff/getStudentsForAsk ● شغال 👨‍🏫 دكتور
atef.dahesh
👨‍🏫 شاشة «ابعت لمين» — الدكتور. ⭐ جديد. الرد فيه ٣ قوايم: Advisees[] (25) + Courses[] (7، كل مقرر بعدد طلبته) + CourseStudents[] (920). كل صف طالب جايب AdvChat وMessagesCount والسنة/الترم/الكلية → افتح المحادثة منه على طول
POST academicstaff/getStudentsForAsk ● شغال 👨‍🏫 دكتور
atef.dahesh
⚡ نفس النداء بس بمقرر واحد. CourseStudents بترجع 26 بدل 920. الطريقة المفضّلة: النداء الأول من غير EdCourseId واعرض Advisees + Courses بس، وأول ما يدوس على مقرر نادي تاني بالـ id بتاعه
POST students/GetStudMailInBox ● شغال 🎓 طالب
mahmoued.eprahim.23010062
📥 صندوق الطالب. صف لكل دكتور. اعرض StaffNameAr/En وcount وLastMessageDate، وحطّ شارة «مرشدك» لو IsAdvisor=true. خزّن SA_STF_MEMBER_ID وAdvChat للخطوة اللي بعدها
POST students/GetStaffMailInBox ● شغال 👨‍🏫 دكتور
atef.dahesh
📥 صندوق الدكتور. صف لكل طالب — StudentNameAr/En. خزّن ED_STUD_ID وAdvChat والسنة/الترم/الكلية من نفس الصف
POST students/GetaskById ● شغال 🎓 طالب
mahmoued.eprahim.23010062
💬 شاشة المحادثة — عين الطالب. saStaffMemberId = الدكتور اللي بتكلمه (مش إنت). الرد courses[] مرتّب بالتاريخ، وكل رسالة فيها AdvChat (مين كتبها) وRE_DTL (النص) وRE_DATE
POST academicstaff/GetaskById ● شغال 👨‍🏫 دكتور
atef.dahesh
💬 نفس المحادثة — عين الدكتور. بيرجّع نفس المصفوفة بالظبط. يعني تقدر تستخدم نفس الـ model ونفس الـ widget في التطبيقين
POST students/AddAsk ● شغال 🎓 طالب
mahmoued.eprahim.23010062
✍️ الطالب بيبعت. FromStud: true ثابت من تطبيق الطالب. AdvChat خده من الصندوق. الرد courses: true يعني اتبعتت — بعدها نادي GetaskById تاني عشان تجيب الرسالة بتاريخها
POST academicstaff/AddAsk ● شغال 👨‍🏫 دكتور
atef.dahesh
✍️ الدكتور بيرد. FromStud: false ثابت من تطبيق الدكتور — ده اللي بيخلّي الفقاعة تطلع للطالب على إنها منك
POST students/SendMessageToCourse ● شغال 👨‍🏫 دكتور
atef.dahesh
📢 إعلان لكل طلبة مقرر. نداء واحد بيبعت لكل مسجّل. بيوصل كل طالب كـ رسالة شخصية في صندوقه (مش جروب) — فمتستغربش لو الصندوق كبر فجأة · جديد ٢ اختار سكاشن: ScScheduleDtlIds (فاضية = كل سكاشن الدكتور). بيروح لطلبة سكاشن الدكتور بس، ولكل طالب مرة واحدة. الرد Sent وSections — التفاصيل
POST academicstaff/SendMessageToCourse ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ٢ نفس الإعلان من حساب الدكتور، لسكاشن يختارها (من academicstaff/GetMySections)

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
قايمة «ابعت لمين» للطالب متعمل المرشد + دكاترة المقررات المسجّلة الترم ده، باسم المقرر. —
قايمة «ابعت لمين» للدكتور متعمل ⭐ جديد. academicstaff/getStudentsForAsk — طلبة الإشراف + المقررات + طلبة المقررات في نداء واحد. قبل كده الدكتور كان لازم يستنى الطالب يبعت الأول. —
صندوق المحادثات متعمل الطرفين. صف لكل محادثة بالاسم والعدد وتاريخ آخر رسالة. —
قراءة المحادثة متعمل نفس المصفوفة بالظبط من الناحيتين — نفس الـ model في التطبيقين. —
إرسال رسالة متعمل من الطالب ومن الدكتور. بتوصل للطرف التاني فورًا من غير أي موافقة في النص. —
مصدر الرسالة (يمين/شمال) متعمل اقرا AdvChat من الرسالة: true = الدكتور/المرشد، false = الطالب. موجود في كل رسالة من غير استثناء. —
شارة «مرشدك» / «طالب إشراف» متعمل IsAdvisor في صندوق الطالب، وIsAdvisee في قايمة الدكتور. —
إعلان لكل طلبة مقرر متعمل SendMessageToCourse شغال فعلًا وبيكتب — بيوصل لكل طالب كرسالة شخصية في صندوقه، مش كجروب. —
عدّاد رسايل غير مقروءة ناقص مفيش عمود «مقروء» في الـ SIS. count اللي راجع = إجمالي الرسايل مش الجديد. إما عمود جديد في الداتابيز، أو تخزّن آخر LastMessageDate شفتها محليًا في التطبيق وتقارن
إشعار عند وصول رد ناقص مفيش push. الطرف التاني مش هيعرف غير لما يفتح التطبيق. ربط AddAsk بالـ push — شغل باك اند. مؤقتًا: polling وإنت فاتح المحادثة
مؤشر «بيكتب» / علامات التسليم ناقص مفيش. الشات ده REST عادي مش realtime. WebSocket / SignalR — قرار منتج
مجموعات وملفات وصور ومكالمات ناقص الشات نص فقط وفرد لفرد. جداول جديدة + تخزين ملفات — قرار منتج
تعديل / مسح رسالة ناقص مفيش إندبوينت. موجود في بوابة الويب بس. إندبوينتين جداد
12 🔔

الإشعارات

Notifications
4/5 خاصية متعملة · 80%
Postman · 7 ريكوست
notifications
notifications

صندوق الإشعارات: المقروء وغير المقروء، مع إمكانية التعليم كمقروء أو الإسقاط.

بيشتغل إزاي؟

GetMobileNotifications بيقرا SV_USER_MESSAGE ⨝ SV_MESSAGE. الضغط على إشعار بينادي SetReadedNotification، والإسقاط بينادي SetDeletedNotification — الاتنين بيكتبوا في الداتابيز.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/GetMobileNotifications ● شغال 🎓 طالب
mahmoued.eprahim.23010062
الصندوق — فيه رسايل دلوقتي
POST students/SetReadedNotification ● شغال 🎓 طالب
mahmoued.eprahim.23010062
✍️ تعليم كمقروء
POST students/SetDeletedNotification ● شغال 🎓 طالب
mahmoued.eprahim.23010062
✍️ حذف
GET students/GetTokenHelp ● شغال 🎓 طالب
mahmoued.eprahim.23010062
أنواع الأجهزة واللغات
POST students/SaveToken ● شغال 🎓 طالب
mahmoued.eprahim.23010062
تسجيل توكن الإشعارات — SE_USER_ID بتاع صاحب الطلب نفسه
POST students/SendNotification 🧪 شغال بالـ Sandbox 🎓 طالب
mahmoued.eprahim.23010062
إرسال — بيشتغل بالـ Sandbox والرسالة بتتسجّل في الصندوق
POST students/SendNotificationSIS 🔑 محتاج بيانات 🎓 طالب
mahmoued.eprahim.23010062
من الباك أوفيس (X-Api-Key)

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
قراءة الصندوق متعمل شغال وفيه رسايل دلوقتي — بعتنا إشعار من SendNotification وظهر في الصندوق. —
تعليم كمقروء / حذف متعمل بيكتبوا في الداتابيز فعليًا. —
تسجيل جهاز للـ push متعمل SaveToken بيعمل upsert بالتوكن. الموبايل يبعت توكن FCM الحقيقي بعد اللوجين
إرسال Push فعلي متعمل الكود موجود (FcmLegacyKey و FcmV1) وشغال دلوقتي بالـ Sandbox — الرسايل بتروح لـ outbox تقدر تقراه من /__sandbox/control/outbox. الجرس نفسه مش هيرن غير بـ Firebase حقيقي، بس صندوق الوارد جوّه التطبيق شغال بالكامل. اتصلّح هنا باجان: MSG_SUBJECT عموده 50 حرف والكود كان بيحشر فيه JSON باللغتين (كل الإشعارات كانت بتفشل)، والسنة والفصل بقوا بيتجابوا لوحدهم لو المرسل مبعتهمش. push.fcmServerKey الحقيقي أو ملف الـ service account
هوك الباك أوفيس جاهز · مستني بيانات محمي بـ X-Api-Key ومقفول دلوقتي. تفعيل feature backOfficeNotifications + مفتاح
13 💳

الرسوم والمدفوعات

Payments
6/9 خاصية متعملة · 67%
Postman · 7 ريكوست
payments
payments
topup
topup

الشاشة بتعرض ملخص الحساب المالي (شغال بداتا حقيقية)، وبعدين رحلة دفع من 4 خطوات: اختيار الرسوم ← المبلغ ← تأكيد ← طريقة الدفع. الرحلة دي بقت شغالة من الأول للآخر دلوقتي على البوابة المزيّفة بتاعة الـ Sandbox — الطالب بياخد كود دفع حقيقي الشكل، ولما يتسدّد الإشعار بيرجع موقّع والرسوم بتتسجّل مدفوعة في الداتابيز.

بيشتغل إزاي؟

الاستعلام شغال، والدفع بقى شغال بالـ Sandbox.
students/GetTotalFeesInfo بيرجع الأرصدة والفواتير مقسّمة (دراسية / أخرى / غرامات) — وده راجع فعليًا: 240,000 مدفوعة، خصم 5,600، متبقي 0.

الدفع الأونلاين اتبنى جديد بالكامل. واكتشاف مهم: الداتابيز أصلاً فيها جداول تكامل الدفع بتاعة الـ SIS — INTEG_Fawry_Payment فيها 2,570 رقم فوري حقيقي، وOnlinePaymentRequests فيها لينكات دفع MasterCard، وFEE_STUD_VOUCHER فيها 18,235 سند. فالكود الجديد بيكتب في نفس الجداول دي مش في جداول جديدة، عشان شاشات التسوية في الباك أوفيس تفضل شغالة.

الدورة اللي اتجرّبت فعليًا: التطبيق ينادي CreatePayment ← البوابة ترجّع كود 9 أرقام ولينك ← الطالب يدفع ← البوابة تبعت POST /payments/callback/Fawry موقّع ← السيرفس يتحقق من التوقيع ويحدّث OnlinePaymentRequests وINTEG_Fawry_Payment ← التطبيق يشوف Paid. وجرّبنا كمان نبعت نفس الإشعار 3 مرات: اتسجّل مرة واحدة بس.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/GetTotalFeesInfo ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ ملخص الحساب — شغال
POST students/fees ● شغال 🎓 طالب
mahmoued.eprahim.23010062
الرسوم مجمّعة بالعملة
POST students/PaymentMethods 🧪 شغال بالـ Sandbox 🎓 طالب
mahmoued.eprahim.23010062
جديد طرق الدفع المتاحة — بيرجّع kiosk + card
POST students/CreatePayment 🧪 شغال بالـ Sandbox 🎓 طالب
mahmoued.eprahim.23010062
جديد بدء الدفع — بيرجّع كود منافذ ولينك دفع
POST students/PaymentStatus 🧪 شغال بالـ Sandbox 🎓 طالب
mahmoued.eprahim.23010062
جديد متابعة الحالة — بيسأل البوابة مباشرة
POST payments/callback/Fawry 🧪 شغال بالـ Sandbox 🎓 طالب
mahmoued.eprahim.23010062
جديد إشعار السداد — بيتحقق من التوقيع، و idempotent (متجرَّب بـ replay)
POST students/EfinancePay 🔑 محتاج بيانات 🎓 طالب
mahmoued.eprahim.23010062
eFinance (السويس) — الـ SQL اتصلّح، بس محتاج صف رقم 7 في GS_CODE_PAY_METHOD (عميل eFinance)

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
ملخص الحساب المالي متعمل الإجماليات والمدفوع والخصم والمتبقي — كله حقيقي. —
بنود الرسوم بالتفصيل متعمل الإندبوينت بيرجع StudyFees/OtherFees/FineFees، بس الطالب ده سدّد كل حاجة فالقوايم فاضية. طالب عليه متأخرات للتجربة
فوري (رقم مرجعي) متعمل جديد FawryPaymentProvider متكتب ومتجرَّب من الأول للآخر على بوابة الـ Sandbox: توقيع SHA-256، إنشاء الرقم، استعلام الحالة، وتحقق من إشعار V1/V2. merchantCode + securityKey من فوري — الكود نفسه مش هيتغيّر
محافظ (فودافون كاش / أورنج / اتصالات) متعمل جديد PaymobPaymentProvider متكتب ومتجرَّب: توكن → أوردر → مفتاح دفع → دفع بالمحفظة، وتحقق HMAC-SHA512. تظبيط payments.provider = Paymob + apiKey وhmacSecret وwalletIntegrationId
فيزا / ماستر كارد متعمل جديد MpgsPaymentProvider — نفس عيلة اللينكات الموجودة في الداتابيز، ومتجرَّب على الـ Sandbox. merchantId + apiPassword من البنك
إشعار السداد (callback) متعمل جديد متكتب وآمن ومتجرَّب: بيتحقق من التوقيع، والتحديث idempotent — بعتنا نفس الإشعار 3 مرات واتسجّل مرة واحدة. هنا لقينا باج خطير: الـ body مكانش بيتقري لو البوابة بعتت بـ chunked encoding، يعني كل إشعارات السداد كانت هتتفقد بصمت. callbackBaseUrl عام بـ https + تسجيله عند البوابة
المحفظة الجامعية ناقص الرصيد دلوقتي في localStorage في الموبايل — مش حقيقي. إندبوينتات رصيد وشحن + قرار: جدول جديد ولا بند رسوم
سند سداد (Voucher) جاهز · مستني بيانات السند في شاشة الموبايل لسه وهمي، بس EfinancePay بينشئ سند حقيقي في FEE_STUD_VOUCHER — وده الكود اللي المفروض يتنقل لمسار الدفع الجديد. (اتصلّح فيه إن ED_CODE_SEMESTER_ID كان ناقص من الـ INSERT أصلاً، فمكانش بيشتغل خالص.) ربط CreatePayment بإنشاء السند + بنوده
إيصال ومطبوعات ناقص مفيش. إندبوينت إيصال PDF
14 🎓

خطة الدراسة

Study Plan
6/8 خاصية متعملة · 75%
Postman · 3 ريكوست
study-plan
study-plan
study-plan-grades
study-plan-grades

مسار التخرج: نسبة الإنجاز، الساعات المتبقية، والمقررات مقسّمة على حزم (متطلبات جامعة / كلية / تخصص / اختياري) وكل مقرر عليه علامة تم أو لأ.

بيشتغل إزاي؟

students/AcadPlan بيرجع الحزم (PKG_HDR_TITLE_AR) وجوّه المقررات بدرجاتها. إجمالي الساعات المنجزة والمطلوبة بييجي من كارت الطالب (ACCUM_CH وFULLFILLED_CH) لأنها الأدق.

جديد اعرض عمود الدرجة من DISPLAY_GRADE_AR على طول — جاهز للطباعة: <table style="margin:8px 0"><tr><th>COURSE_STATUS</th><th>DISPLAY_GRADE_AR</th><th>معناها</th></tr> <tr><td>Passed / Failed</td><td>الدرجة (A-، F…)</td><td>الدرجة اللي بتتحسب حسب لائحة الطالب</td></tr> <tr><td>Registered</td><td>مسجل</td><td>مسجّله الترم ده ولسه مالوش درجة معتمدة — حتى لو أخده قبل كده وجاب D-: بيظهر الحالي بس</td></tr> <tr><td>NotTaken</td><td>-----</td><td>عمره ما سجّله</td></tr> <tr><td>Withdrawn / Incomplete / Transferred…</td><td>رمز الحالة (W، I، P)</td><td>حصل له حاجة غير الدرجة</td></tr></table> مقرر اتعاد وخلص (D- وبعدين A): مش إحنا اللي بنقرر — كل لائحة في الـ SIS متسجّل عليها قاعدة الإعادة (الأعلى / الأحدث / الأحدث بحد أقصى). GRADE_RULE بيقول اتطبّق أنهي (Best / Latest)، وATTEMPTS عدد المحاولات، وIS_REPEATED لو أكتر من محاولة.
Course_Grading_AR القديم زي ما هو (آخر محاولة بس) — ماتعتمدش عليه في العمود ده.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/AcadPlan ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ 5 حزم بمقرراتها — جديد COURSE_STATUS + DISPLAY_GRADE_AR (الدرجة / مسجل / -----)
POST students/get ● شغال 🎓 طالب
mahmoued.eprahim.23010062
إجمالي الساعات
GET students/GetSTudentPaln 🔒 مقفول 🎓 طالب
mahmoued.eprahim.23010062
نسخة KU من نفس الشاشة — بديل لـ students/AcadPlan مش إضافة ليه، والشاشة بتستخدم AcadPlan. مقفول بفيتشر kuStudyPlanReport لأنه بيرجّع صفر صفوف على داتابيز Innovation (اتجرّب على 8 طلبة). مش KU-only بالتصميم — أي تركيب داتاه متوافقة يفتحه ويشتغل. ولو فتحته لازم تبعت المدى كامل: edStudId لوحده بيخلّي مدى السنين فاضي والرد بيرجع فاضي مهما كان

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
تقرير الخطة نسخة KU جاهز · مستني بيانات students/GetSTudentPaln بديل قديم لنفس الشاشة، متقفل بفيتشر kuStudyPlanReport. بيرجّع صفر صفوف على Innovation، فبدل ما يرد «نجاح فاضي» — وده شكله زي إندبوينت باظ — بقى بيقول صراحةً إنه مقفول على التركيب ده. تركيب داتاه متوافقة يفتح الفيتشر ويشتغل
حزم الخطة الدراسية متعمل 5 حزم راجعة بالمقررات والدرجات. —
الساعات المنجزة والمتبقية متعمل 101 من 200 ساعة = 51%. —
المقررات المتبقية لكل حزمة متعمل المقرر اللي مالوش درجة = لسه. —
الدرجة / مسجل / ----- متعمل جديد من الميتنج: DISPLAY_GRADE_AR جاهز للعرض. اتجرّب على طالب شايل مقررات متعادة (ahmed.ghanem.25060211): اللي راسب في الربيع ومسجّله في الصيفي بيظهر «مسجل». —
الأفضل ولا الأخير عند الإعادة متعمل جديد بيتقرأ من لائحة الطالب نفسها في الـ SIS (نفس منطق تقرير الخطة القديم) — مش قرار في الكود. GRADE_RULE بيقول اتطبّق إيه. —
التخرج المتوقع ناقص كان مكتوب «يونيو 2028» ثابت — اتحذف. حساب من الساعات المتبقية ÷ متوسط الفصل — قرار سياسة
إنذارات أكاديمية متعمل ⭐ بقى بيتعرض كبانر فوق نتيجة الفصل. —
15 👥

دليل الأساتذة

Faculty Directory
3/5 خاصية متعملة · 60%
Postman · 4 ريكوست
faculty
faculty

دليل دكاترة الطالب: المرشد الأكاديمي + دكاترة المقررات المسجلة، مع أزرار تواصل.

بيشتغل إزاي؟

نفس مصدر شاشة المحادثات — getInstructorsAndAdvisorForAsk. يعني الدليل مخصّص للطالب (دكاترته هو)، مش دليل الكلية كلها.
جديد كل دكتور بقى جاي معاه officeHours[] — والمرشد كمان (courses.AcademicAdvisior.officeHours). كل ميعاد: DayAr · FromTime · ToTime (HH:mm) · Notes. يعني الشاشة مش محتاجة نداء تاني لكل دكتور. المواعيد بتاعة ترم الطالب الحالي؛ دكتور مادخلش مواعيد ← officeHours: [] ← اكتب «لا توجد ساعات مكتبية».

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/getInstructorsAndAdvisorForAsk ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ 12 دكتور + المرشد — جديد كل واحد معاه officeHours[]
POST students/GetOfficeHours ● شغال 🎓 طالب
mahmoued.eprahim.23010062
نفس المواعيد بشكل كروت (دكتور ← مقرراته ← مواعيده). جديد FromTime/ToTime بـ HH:mm، ومبقاش بيرجّع «الأيام المفضّلة للتدريس» على إنها ساعات مكتبية · جديد ٢ والحجز من students/GetOfficeHourOpenings / students/BookOfficeHour
POST students/GetOfficeHourOpenings ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ٢ المواعيد المتاحة للحجز (Openings[]) + MyBookings[] + CancelledByLecturer[] — التفاصيل
POST students/BookOfficeHour ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ٢ حجز ميعاد؛ الإلغاء بـ {Cancel: true, BookingId}

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
دكاترة الطالب متعمل بأسماء المقررات اللي بيدرّسوها له. —
المرشد الأكاديمي متعمل راجع من نفس الإندبوينت + كارت الطالب. —
الإيميل والتليفون والمكتب ناقص الإندبوينت بيرجع الاسم والـ ID بس. إضافة أعمدة التواصل للـ SQL من SA_STF_MEMBER
الساعات المكتبية متعمل ⭐ اتصلّحت كانت فاضية لسببين: مفيش مواعيد متسجّلة للربيع (اتحطّت بيانات تجريبية لـ 9 دكاترة)، والدكتور كان بيدخل على ترم الصيفي فأي ميعاد يسجّله من الموبايل كان بيروح للصيفي والطالب مش شايفه. ودلوقتي جوّه الدليل نفسه. الدكاترة يدخلوا مواعيدهم الحقيقية
دليل الكلية كاملة ناقص دلوقتي دكاترة الطالب بس. إندبوينت بحث في كل هيئة التدريس
16 ➕

تسجيل المقررات

Course Registration
registration
registration

إضافة وإسقاط المقررات في فترة التسجيل: المقررات المتاحة، السلة، حدود الساعات، وموافقة المرشد.

بيشتغل إزاي؟

مجموعة CourseReg/* اتعملت أصلاً لـ KU. الإندبوينتات كلها موجودة والـ feature courseRegistration مفعّل والجداول المطلوبة كلها موجودة — بس الـ SQL بتاع الكتابة (تسجيل / حذف / تراجع) كلها شغالة ومتحقّقة — بترجّع نتيجة العملية وسببها لما ترفض.
v2/MyCourses وGetNewCourses بيردّوا 200 بقايمة فاضية لأن باب التسجيل مقفول دلوقتي (مش لأنهم بايظين).

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST CourseReg/GetAllowedActions ● شغال 🎓 طالب (تسجيل)
menna.elsayed.25010015
جديد ⭐ ابدأ بيه — بيقولك الأزرار المسموحة دلوقتي: نوافذ الأجندة الخمسة، وكل واحدة بتسمح بإيه، وسقف الساعات والمحمّل منه. الموبايل مالوش داعي يعرف قواعد الـ SIS، بس يقرا الفلاجز
POST CourseReg/v2/MyCourses ● شغال 🎓 طالب (تسجيل)
menna.elsayed.25010015
جديد ⭐ تاب «مقرراتي» — كل تسجيلات الفصل بكل الحالات (مسجّل · محذوف · منسحب · انتظار). كل مقرر معاه State ككلمة مش رقم، وTeachingMethods = كل طرق تدريسه وحالة الطالب في كل واحدة (REGISTERED · NOT_REGISTERED · NO_SECTIONS_OFFERED) — حتى للمقرر المحذوف. وSections فضلت مسطّحة وكل سكشن شايل طريقة تدريسه، فالربط شغلة التطبيق
POST CourseReg/v2/AvailableCourses ● شغال 🎓 طالب (تسجيل)
menna.elsayed.25010015
جديد ⭐ تاب «المتاح» — مش «المقررات اللي تقدر تسجّلها» بل المقرر × طريقة التدريس اللي لسه ينفع تتضاف. المقرر اللي الطالب ماسكه بيظهر هنا كمان بطرق التدريس الناقصة بس (Mode: ADD_METHOD)، والمحذوف مش بيظهر فيها خالص — التراجع عن الإسقاط من تاب «مقرراتي»، لأنه عملية على تسجيل قايم مش إضافة. وأول ما آخر فراغ يتملي المقرر بيختفي من التاب — عشان كده التابين بيكمّلوا بعض ومابيتقاطعوش
POST CourseReg/RegisterNewCourse ● شغال 🎓 طالب (تسجيل)
menna.elsayed.25010015
⭐ ✍️ تسجيل شغّال ومتجرَّب — ولو مقاعد السكشن خلصت والطابور فيه مكان بيسجّل في الطابور تلقائياً (حالة 7 + WAIT_NO) زي البورتال بالظبط. — بيرجّع Status: true والمقرر بيظهر في القائمة بحالة 1. ScScheduleDtlId هي السكشن اللي الطالب هيتحط فيها لما المقرر مش متجمّع في مجموعة سكاشن
POST CourseReg/DropCourse ● شغال 🎓 طالب (تسجيل)
menna.elsayed.25010015
⭐ ✍️ حذف شغّال ومتجرَّب — الحالة بتبقى 6 والصف مابيتمسحش. edStudCourseRegId اختياري — لو بعتّ EdCourseId لوحده الخدمة بتلاقي التسجيل من فصل الطالب نفسه. ومربوط بنافذة الإسقاط (6) في أجندة الفصل
POST CourseReg/UnDropCourse ● شغال 🎓 طالب (تسجيل)
menna.elsayed.25010015
⭐ ✍️ تراجع شغّال ومتجرَّب — الحالة بترجع 1. ده الطريق الوحيد لإرجاع مقرر مسقَط، مش تسجيل جديد.
اتغيّر مابقاش يختار سكشن لوحده: الحذف بيمسح صفوف ED_STUD_GROUP فيزيكال ومفيش جدول بيفتكرها، فأي اختيار تلقائي تخمين — والقديم كان بيفلتر على IS_CLOSED بس، من غير فحص مقاعد ولا تعارض. بيرجّع دلوقتي TeachingMethods وNeedsSection وNextAction على البيكر. عايز القديم؟ AssignSection: true — وبقى آمن: واحد لكل طريقة تدريس، وبس اللي فيه مقعد ومافيهوش تعارض
POST CourseReg/GetSectionsToReplace ● شغال 🎓 طالب (تسجيل)
menna.elsayed.25010015
جديد ⭐ بيكر السكاشن — بيجاوب على الحالتين: «أنقل لفين؟» و«أختار إيه؟». الرد مقسّم أربع قوايم + Summary، وكل صف بيقول Mode: REPLACE (عنده سكشن من النوع ده) ولا PICK (مالوش — بعد حذف مثلاً) وجنبه الـ actions بالإندبوينت الصح. وفوق: NeedsSection وMissingTeachingMethods = النوع الناقص بالاسم.
جديد ابعت EdCodeCourseTeachingId عشان تفتح طريقة تدريس بعينها — ده السؤال الوحيد اللي ماكانش ينفع يتسأل، لأن طريقة التدريس اللي الطالب مالوش فيها سكشن مفيهاش ED_STUD_GROUP نستنتج منه. وبترجّع كمان TeachingMethods وRequiresUnDrop+NextAction للمقرر المحذوف.
اتغيّر بترجّع كل سكاشن الطريقة — اللي مايتسجلش بيرجع في BlockedSections ومعاه BlockedReason بمصطلحات البورتال (NOT_ALLOW_ONLINE · INTERNAL · IS_CLOSED · EXAM_CONFLICT · SCHEDULE_CONFLICT · FULL) بدل ما تختفي. ودي اللي كانت بتخفي على الطالب سكشنه هو لما شؤون التسجيل بتقفل التسجيل الإلكتروني عليه
POST CourseReg/DeleteSection ● شغال 🎓 طالب (تسجيل)
menna.elsayed.25010015
جديد ✍️ حذف سكشن واحد من مقرر بيتدرّس على أكتر من سكشن (محاضرة/عملي/إكلينيكي) — التسجيل نفسه بيفضل قايم. مفتوح بنافذة تسجيل السكاشن (70). والرد بيقولك الفراغ اللي عمله: NeedsSection وMissingTeachingMethod (النوع الناقص وكام مرشّح ليه) وNextAction فيها نداء البيكر جاهز
POST CourseReg/SetSection ● شغال 🎓 طالب (تسجيل)
menna.elsayed.25010015
⭐ ✍️ «حطّني في السكشن دي» — النداء الوحيد للسكاشن. AddSection وReplaceSection اتشالوا: كانوا اسمين للنداء ده نفسه. الخدمة بتشوف إن الطالب عنده سكشن من نوع التدريس ده ولا لأ، وتقرّر نقل ولا إضافة، وترجّع Action: REPLACED · ADDED · UNCHANGED (هو فيها أصلاً). الفرق ده بيعتمد على حال الداتابيز لحظة الدوسة مش على الشاشة، عشان كده الخدمة هي اللي بتقرّره. EdStudGroupId اختياري — وماينفعش إلا في مقرر فيه أكتر من سكشن من نفس النوع.
جديد لو المقعد راح وانت بتختار، الرفض بيرجّع Reason: SEAT_TAKEN ومعاه Alternatives = بدايل نفس طريقة التدريس، حيّة، بأزرارها — من غير نداء تاني
POST CourseReg/CancelWaitList ● شغال 🎓 طالب (تسجيل)
menna.elsayed.25010015
✍️ الخروج من قائمة الانتظار — AddToWaitList اتشال: الدخول للطابور بقى جوّه SetSection وRegisterNewCourse زي البورتال، فمافيش زرار تاني. — الصف بيتمسح خالص مش بيتعلّم كمسقَط، لأنه أصلاً ماكانش تسجيل
POST Faculties/CoursesCatalogNew ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ 85 مقرر بالمصروفات
POST EdSemesterAgenda/GetAllByEdSemesterOpen ● شغال 🎓 طالب
mahmoued.eprahim.23010062
مواعيد فتح وقفل التسجيل

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
كتالوج المقررات متعمل 85 مقرر بالوصف والساعات والمصروفات. —
مواعيد فتح التسجيل متعمل EdSemesterAgenda بيرجع 6 أنشطة بتواريخها. —
عرض المسجَّل حاليًا متعمل ⭐ 8 مقررات. كان فاضي لسببين مع بعض: فترة التسجيل كانت قافلة، والهاندلر كان بيبعت 0 مكان NULL لفلتر الحالة. —
تسجيل مقرر متعمل CourseReg/RegisterNewCourse شغال ومتحقق — بيرجّع نتيجة العملية وسببها لما ترفض. —
إسقاط مقرر متعمل CourseReg/DropCourse شغال ومتحقق — بيرجّع سبب الرفض لما العملية مترفض. —
التراجع عن الإسقاط متعمل CourseReg/UnDropCourse شغال ومتحقق — بيرجّع المقرر من غير ما يختار سكشن، ومعاه طرق التدريس والخطوة اللي بعدها. —
حدود الساعات والمتطلبات السابقة متعمل اتعمل بمحرّك الـ SIS نفسه. المتطلب السابق: CHK_STUD_CRS_PREREQ بيتنده مرة لكل مقرر، والنتيجة بتقفل كل سكاشنه: CanRegister=false وBlockedReason=PREREQ_NOT_MET وPrereqProblem فيه مفتاح الرسالة بتاع الـ SIS. والتسجيل بيترفض قبل ما يكتب. سقف الساعات: GetAcadLoadMinMax — التسجيل والتراجع وحجز المقعد كلهم بيشيكوا عليه، وMinCreditHours/MaxCreditHours/CurrentCreditHours راجعين في GetAllowedActions عشان تحذّر قبل الرفض. —
موافقة المرشد ناقص 🚫 في الموبايل بس. مفيش دورة موافقة مرشد على التسجيل في الـ SIS — الموافقة اللي موجودة هي على الانسحاب (WithdrawCoursesApprv). قرار: هل الجامعة عايزة دورة موافقة على التسجيل أصلاً؟ لو أيوه لازم تتعمل في البوابة الأول
17 🛡️

الإرشاد الأكاديمي

Advising
4/5 خاصية متعملة · 80%
Postman · 4 ريكوست
advising
advising

بيانات المرشد الأكاديمي، حجز موعد (حضوري أو عبر Teams)، ومتابعة المواعيد السابقة.

بيشتغل إزاي؟

اسم المرشد بييجي حقيقي من كارت الطالب وgetInstructorsAndAdvisorForAsk. لكن المواعيد نفسها كلها في الموبايل — مفيش جدول ولا إندبوينت. الجديد: meetings/Create بيعمل لينك Teams حقيقي لما الإعدادات تكون موجودة.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/getInstructorsAndAdvisorForAsk ● شغال 🎓 طالب
mahmoued.eprahim.23010062
المرشد
POST students/get ● شغال 🎓 طالب
mahmoued.eprahim.23010062
ACAD_ADV_AR
POST meetings/Create 🧪 شغال بالـ Sandbox 🎓 طالب
mahmoued.eprahim.23010062
جديد لينك Teams — بيرجّع joinWebUrl
POST academicstaff/GetStudentUnderSuperVision ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ 25 طالب تحت إشراف الدكتور

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
بيانات المرشد متعمل الاسم حقيقي من الداتابيز. —
الطلاب تحت الإشراف (شاشة الدكتور) متعمل 50 طالب راجعين فعليًا. —
حجز موعد متعمل ⭐ اتعمل — GetAppointmentSlots + BookAppointment. المتاح جاي من البروسيجر ED_STUD_SEM_TIME_AVAILABLE (أهلية + سعة)، وقاعدة «حجز واحد لكل نشاط» متطبّقة. الأجندة نفسها الإدارة بتعرّفها. —
لينك Teams للموعد متعمل جديد TeamsMeetingProvider متكتب بالكامل بـ client credentials، وبيرجّع joinWebUrl فعليًا دلوقتي على Graph المزيّف بتاع الـ Sandbox — فشاشة الإرشاد تتبني النهاردة. tenantId/clientId/clientSecret/organizer + صلاحية OnlineMeetings.ReadWrite.All + application access policy — الكود نفسه مش هيتغيّر
ملاحظات وتوصيات المرشد ناقص مفيش. ممكن تستخدم الشات الموجود بدل جدول جديد
18 📄

الطلبات والوثائق

Requests & Documents
1/5 خاصية متعملة · 20%
Postman · 5 ريكوست
requests
requests

طلب وثائق رسمية: بيان درجات، إفادة قيد، خطاب تجنيد… مع متابعة حالة الطلب.

بيشتغل إزاي؟

الشاشة دي كلها بروتوتايب. مفيش أي إندبوينت. الكاتالوج والحالات والمسار الزمني كله ثابت في الموبايل. الـ feature serviceRequests معرّف في الكتالوج ومتطلباته (GS_ATTCHD_DOC, GS_CODE_DOC_TYPE) متحققة في الداتابيز — بس مفيش handlers.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/get ● شغال 🎓 طالب
mahmoued.eprahim.23010062
بيانات الطالب في الطلب
POST students/GetClearanceRequests ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ إخلاء الطرف بالتوقيعات الناقصة — ده طلب الـ SIS الحقيقي
POST students/GetCampusRequests ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ الخزانة وملصق العربية وطلبات الخصم
POST students/AddCampusRequest ● شغال 🎓 طالب
mahmoued.eprahim.23010062
✍️ تقديم طلب خزانة / ملصق
POST students/ServiceRequestTypes ✕ مش موجود 🎓 طالب
mahmoued.eprahim.23010062
🚫 مش موجود في الـ SIS. كتالوج الوثائق (بيان درجات، إفادة قيد…) مالوش فورم في البوابة — شوف تحليل الفجوة

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
كتالوج الوثائق ناقص ثابت في الموبايل. جدول أنواع الطلبات + رسومها + إندبوينت
تقديم طلب ناقص الفورم شغال في الموبايل بس مش بيبعت. إندبوينت إنشاء + جدول طلبات + رقم طلب
متابعة الحالة متعمل ⭐ بقى حقيقي — المسار الزمني بيتبني من حالة الـ SIS نفسها (توقيعات إخلاء الطرف، تسليم مفتاح الخزانة، طباعة الملصق، اعتماد الخصم). —
الدفع مقابل الوثيقة ناقص مربوط بالمدفوعات. ربط الطلب بسند سداد
استلام الوثيقة PDF ناقص مفيش. توليد/رفع الوثيقة + تحميل
19 🚪

الانسحاب والاعتذار

Withdrawals
3/5 خاصية متعملة · 60%
Postman · 5 ريكوست
withdrawal
withdrawal

الاعتذار عن مقرر، أو الانسحاب من الجامعة كلها (بتاريخ ومرفقات).

بيشتغل إزاي؟

بروتوتايب بالكامل. الـ feature withdrawalRequests معرّف ومتطلباته (ED_STUD_COURSE_REG, ED_STUD_SEMESTER) موجودة، بس مفيش handlers. المقررات اللي بتظهر للاعتذار بتيجي من students/schedule الحقيقي.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/schedule ● شغال 🎓 طالب
mahmoued.eprahim.23010062
المقررات المتاحة للاعتذار
POST students/GetCourseWithdrawals ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ طلبات الانسحاب + المقررات المؤهلة + الأسباب
POST students/RequestCourseWithdrawal ● شغال 🎓 طالب
mahmoued.eprahim.23010062
✍️ انسحاب من مقرر — طلب بس؛ حالة التسجيل مبتتغيّرش غير باعتماد العميد
POST students/GetEnrollmentChanges ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ الانسحاب من الجامعة وتغيير القيد + الحالات المتاحة والأسباب وحد التأجيل
POST students/RequestEnrollmentChange ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ✍️ تقديم انسحاب من الجامعة / تأجيل / إعادة قيد — الحالة القديمة بتتقرا من قيد الطالب مش من الطلب

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
اختيار المقرر متعمل المقررات حقيقية من الجدول. —
تقديم اعتذار عن مقرر متعمل ⭐ students/RequestCourseWithdrawal — بيسجّل طلب في ED_STUD_COURSE_CHNG. وED_STUD_COURSE_REG مبتتلمسش: العميد هو اللي بيعتمد وبيغيّر الحالة. —
انسحاب من الجامعة متعمل ⭐ اتعمل — students/RequestEnrollmentChange (انسحاب رسمي / نهائي / تأجيل / إعادة قيد). الحالة القديمة بتتقرا من قيد الطالب مش من الطلب، وAPPROVE_FLG=0 — قيد الطالب مبيتغيّرش غير بقرار شؤون التسجيل. قاعدة «مفيش طلبين معلّقين» وحد التأجيل من ENT_POLICY_EXCUSE متطبّقين. —
رفع مستندات ناقص الزرار ديكور — رفع الملفات نفسه مش متعمل في السيرفس. رفع ملفات + GS_ATTCHD_DOC (الجدول موجود)
حساب المسترد ماليًا ناقص مفيش. قواعد الاسترداد حسب تاريخ الانسحاب
20 📢

استطلاع الرأي

Survey
4/4 خاصية متعملة · 100%
Postman · 6 ريكوست
survey
survey

تقييم المقررات والدكاترة: أسئلة تقييم بنجوم وأسئلة نصية.

بيشتغل إزاي؟

مجموعة Survey/* كاملة ومتكتبة — قايمة المقررات، الاستبيانات، الأسئلة، وحفظ الإجابات (تقييم ونص). كلها بترجع 200 بقوايم فاضية لأن مفيش استبيان منشور للفصل ده — مش لأنها بايظة.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST Survey/GetCoursesUnderSurvey ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ المقررات المفتوح عليها تقييم (7)
POST Survey/GetMadatorySurvy ● شغال 🎓 طالب
mahmoued.eprahim.23010062
التقييم الإجباري (REG_FLG)
POST Survey/GetSurveyByCourse ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⚠️ الباراميتر اسمه EdCourseId مش ED_COURSE_ID
POST Survey/GetSurvyQuestions ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ 40 سؤال
POST Survey/InsertItemAnswer ● شغال 🎓 طالب
mahmoued.eprahim.23010062
✍️ إجابة سؤال تقييم — SV_COURSE_EVAL_ITEM_ID لازم يكون سؤال حقيقي من GetSurvyQuestions، وإلا الـ FK بيرفض
POST Survey/InsertTextAnswer ● شغال 🎓 طالب
mahmoued.eprahim.23010062
✍️ إجابة نصية — SV_COURSE_EVAL_EXT_ID من نفس الاستبيان

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
قايمة الاستبيانات متعمل الكود كامل — بس مفيش استبيان منشور. نشر استبيان من الباك أوفيس — مش شغل برمجة
الأسئلة ومقاييس التقييم متعمل بيرجع EvalItems وEvalTEXT. نفس الكلام — بيانات
حفظ الإجابات متعمل بيكتب في SV_STUD_COURSE_EVAL. —
الاستبيان الإجباري قبل النتيجة متعمل GetMadatorySurvy موجود. ربطه في الموبايل: منع دخول النتيجة قبل التقييم
21 🗓️

التقويم الأكاديمي

Academic Calendar
3/4 خاصية متعملة · 75%
Postman · 4 ريكوست
events
events

مواعيد الفصل الرسمية: بداية ونهاية التسجيل، الامتحانات، الإجازات.

بيشتغل إزاي؟

الريكوست المطلوب: POST semester/semsterCalnd بـ { "EdAcadYearId": 16, "EdCodeSemesterId": 2 } (السنة والفصل من UserMainData بتاع اللوجين). ابعت AsFacultyInfoId لو عايز كلية الطالب بس — 0 أو من غيره = كل الكليات. الرد items[]: ACTV_AR (اسم النشاط) · FROM_DATE · TO_DATE · ENT_DESCR_AR (الكلية) · SEM_AR.
اتصلّح كان بيرد «data not found» لأن القاعدة القديمة بتعرض بس الأنشطة اللي مسجّل الـ «للطلاب» بتاعها — والمسجّل هنا بيعلّم «يظهر» بس. بقت إعداد في الأدمن (Academic rules ← «Academic calendar shows»)، وInnovation عليه «Shown» ← 48 بند.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST semester/semsterCalnd ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ اتصلّح التقويم الأكاديمي — 48 بند للربيع
POST EdSemesterAgenda/GetAllByEdSemesterOpen ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ 6 أنشطة بتواريخها
GET EdSemesterOpen/GetAll ● شغال 🎓 طالب
mahmoued.eprahim.23010062
30 فصل بتواريخ البداية والنهاية
GET EdAcademicYear/GetAll ● شغال 🎓 طالب
mahmoued.eprahim.23010062
7 سنين دراسية

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
أنشطة الفصل متعمل EdSemesterAgenda بيرجع داتا حقيقية. ربطه في الموبايل بدل الداتا الثابتة
تواريخ الفصول متعمل 30 فصل بـ FROM_DATE / TO_DATE. —
تقويم الكلية متعمل اتصلّح 48 بند للربيع. أي جامعة تانية تختار من الأدمن: «للطلاب» بس (القديم) أو «الظاهر» أو «الكل». —
إضافة للتقويم الشخصي ناقص مفيش. توليد ملف ICS — شغل موبايل
22 🏛️

حجز القاعات

Room Booking
0/4 خاصية متعملة · 0%
Postman · 3 ريكوست
booking
booking

حجز قاعات مذاكرة أو مشاريع بالتاريخ والوقت وعدد الأفراد.

بيشتغل إزاي؟

بروتوتايب بالكامل. بس فيه اكتشاف حلو: الداتابيز فيها موديول سكن كامل (ACCOM_* — 28 جدول) فيه قاعات وحجوزات ودورة موافقات جاهزة. الـ feature roomBooking معرّف على الجداول دي ومتطلباته متحققة.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST campus/Rooms ✕ مش موجود 🎓 طالب
mahmoued.eprahim.23010062
⚠️ لسه — القاعات المتاحة
POST campus/BookRoom ✕ مش موجود 🎓 طالب
mahmoued.eprahim.23010062
⚠️ لسه — حجز
POST campus/MyBookings ✕ مش موجود 🎓 طالب
mahmoued.eprahim.23010062
⚠️ لسه — حجوزاتي

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
قايمة القاعات ناقص ثابتة في الموبايل. استعلام على ACCOM_ROOM + ACCOM_CDE_ROOM_TYPE
المتاح في وقت معين ناقص مفيش. استعلام تعارض مع ACCOM_STUD_BOOK
الحجز ناقص مفيش. insert في ACCOM_STUD_BOOK + ACCOM_CDE_BOOK_STATUS
الموافقة على الحجز ناقص مفيش. ACCOM_WF_REQUEST_APPROVE موجود وجاهز للاستخدام
23 🗺️

الأنشطة والرحلات

Trips & Events
0/4 خاصية متعملة · 0%
Postman · 3 ريكوست
trips
trips

الرحلات والفعاليات الطلابية، والتسجيل فيها بعدد المشاركين.

بيشتغل إزاي؟

بروتوتايب بالكامل. الداتابيز فيها ED_STUD_ACTIVITY، والـ feature studentActivities معرّف عليه ومتطلباته متحققة — بس مفيش handlers.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST campus/Activities ✕ مش موجود 🎓 طالب
mahmoued.eprahim.23010062
⚠️ لسه — الفعاليات
POST campus/JoinActivity ✕ مش موجود 🎓 طالب
mahmoued.eprahim.23010062
⚠️ لسه — التسجيل
POST campus/MyActivities ✕ مش موجود 🎓 طالب
mahmoued.eprahim.23010062
⚠️ لسه — اشتراكاتي

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
كتالوج الفعاليات ناقص ثابت في الموبايل. استعلام على ED_STUD_ACTIVITY + جدول تعريف الفعاليات
طلب مشاركة ناقص مش بيتحفظ. insert + حالة + منع التكرار
تعديل/إلغاء الطلب ناقص في الموبايل بس. update/delete مع شرط إن الطلب لسه معلّق
الدفع للرحلة ناقص مفيش. ربط بالمدفوعات
24 🧭

خدمات الحرم الجامعي

Services Hub
2/3 خاصية متعملة · 67%
Postman · 1 ريكوست
services
services

صفحة تجميعية لكل الخدمات (14 خدمة) مقسّمة لمجموعات، مع بحث.

بيشتغل إزاي؟

مفيش إندبوينت — دي شاشة تنقّل بس. لكن المفروض تستخدم Info/Capabilities عشان تخفي الخدمات اللي التركيب ده مش بيدعمها.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
GET Info/Capabilities ● شغال 🌐 من غير توكن ⭐ لإخفاء الخدمات غير المتاحة

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
قايمة الخدمات متعمل شاشة تنقّل — مش محتاجة باك إند. —
إخفاء الخدمات غير المفعّلة متعمل ⭐ كروت الخدمات بقت بتتفلتر بـ Info/Capabilities — الخدمة المقفولة مابتتعرضش أصلاً، بدل ما تتعرض وتفشل عند الضغط. —
شارة عدد (طلبات معلّقة مثلاً) ناقص الأرقام ثابتة. إندبوينت عدّادات مجمّع
25 📣

الإعلانات

Announcements
0/3 خاصية متعملة · 0%
Postman · 2 ريكوست
announcements
announcements

إعلانات الكلية والإدارات، مقسّمة بالتصنيف مع تمييز العاجل.

بيشتغل إزاي؟

كل الإعلانات ثابتة في الموبايل. مفيش إندبوينت إعلانات في السيرفس. أقرب حاجة موجودة هي جداول الرسايل SV_MESSAGE / SV_USER_MESSAGE اللي بتستخدمها الإشعارات.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/GetMobileNotifications ● شغال 🎓 طالب
mahmoued.eprahim.23010062
أقرب بديل موجود
POST students/Announcements ✕ مش موجود 🎓 طالب
mahmoued.eprahim.23010062
⚠️ لسه

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
قايمة الإعلانات ناقص ثابتة في الكود. إما إندبوينت إعلانات جديد، أو استخدام SV_MESSAGE بتصنيف
التصنيفات والعاجل ناقص ثابتة. أعمدة تصنيف وأولوية
استهداف (كلية/مستوى) ناقص مفيش. قواعد استهداف في الباك أوفيس
26 ✨

المساعد الذكي

AI Assistant
2/3 خاصية متعملة · 67%
Postman · 1 ريكوست
assistant
assistant

مساعد بيرد على أسئلة الطالب الشائعة وبيوديه للشاشة المناسبة.

بيشتغل إزاي؟

دلوقتي مطابقة نوايا بسيطة في الموبايل (كلمات مفتاحية → رد جاهز + زرار تنقّل). مفيش أي ذكاء اصطناعي حقيقي ولا إندبوينت.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST assistant/Ask ✕ مش موجود 🎓 طالب
mahmoued.eprahim.23010062
⚠️ لسه

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
الردود الجاهزة متعمل شغالة في الموبايل، والتحية بقت باسم الطالب الحقيقي. —
التنقّل للشاشات متعمل شغال. —
ذكاء اصطناعي حقيقي ناقص مفيش. إندبوينت يوصّل لـ LLM + سياق الطالب (جدوله/درجاته) + قرار خصوصية مهم: إيه اللي يتبعت للموديل
27 👨‍🏫

شاشات عضو هيئة التدريس

Staff Screens
11/11 خاصية متعملة · 100%
Postman · 21 ريكوست
لوحة الدكتور
لوحة الدكتور
طلابي تحت الإشراف
طلابي تحت الإشراف
مقرراتي
مقرراتي
رصد الدرجات
رصد الدرجات

التطبيق بيشتغل بدورين. لما تدخل بحساب دكتور، الشاشات بتتغير: مقرراته، طلابه، أخذ الحضور، والطلاب تحت إشرافه.

بيشتغل إزاي؟

الدور بيتحدد من IsStudent في رد اللوجين. الإندبوينتات بتاعة الموظفين شغالة جزئيًا: GetStudentUnderSuperVision بيرجع 50 طالب، وGetInstructorSectionSolts بيرجع حصص فعلية.
الحضور بالـ QR بقى كامل من الآخر للآخر — عرض الكود، المسح، وقفل المحاضرة اللي بيحوّل «مامسحش» لغياب فعلي. التفاصيل في فصل الحضور.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST academicstaff/login ● شغال 🌐 من غير توكن دخول الدكتور
POST academicstaff/GetStudentUnderSuperVision ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ 25 طالب تحت إشراف الدكتور
POST Staff/GetInstructorSectionSolts ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ حصص السكشن
POST academicstaff/GetStaffCourse ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ 7 مقررات
POST academicstaff/GetStaffSchedule ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ اتصلّح جدول الدكتور — كان «data not found» لأي دكتور سكاشنه مش في «مجموعة سكاشن». سطر لكل سكشن ومعاه SC_SCHEDULE_DTL_ID والأيام والمقر وmeetings[] · جديد ٢ + officeHourBookings[] — حجوزات الساعات المكتبية الجاية
POST academicstaff/GetTeachingLoad ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ⭐ العبء التدريسي — كل مقرر بيدرّسه وكام سكشن وكام طالب، والإجماليات (٧ مقررات · ١٢ سكشن · ٤٨٥ طالب). دي Faculty Academic Load في منيو عضو هيئة التدريس. رقم عضو هيئة التدريس بيتاخد من التوكن مش من الـ body، والساعات بتتحسب مرة لكل مقرر مش لكل سكشن
POST academicstaff/GetMyOfficeHours ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ساعاتي المكتبية — الطالب كان يقدر يشوفها (students/GetOfficeHours) وصاحبها ماكانش يقدر يعدّلها من الموبايل · جديد ٢ كل ميعاد معاه Bookings[] وHasBookings — المحجوز مايتعدّلش ولا يتحذف
POST academicstaff/CancelOfficeHourBooking ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ٢ الدكتور يلغي حجز بسبب — بيوصل للطالب في CancelledByLecturer
POST academicstaff/SaveOfficeHour ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ✍️ إضافة/تعديل/حذف ميعاد مكتبي واحد. ابعت SaOfficeHourId عشان تعدّل، وDelete: true عشان تحذف. مربوط بصاحبه: رقم ميعاد بتاع دكتور تاني بيترفض بـ «مش بتاعك»
POST Staff/GetInstructorCourseSections ● شغال 👨‍🏫 دكتور
atef.dahesh
سكاشن المقرر — بيرجّع SectionId مش SC_SCHEDULE_DTL_ID
POST students/GetStudentsForCourse ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ 52 طالب — كشف السكشن، بيتنادى بحساب الدكتور رغم إن المسار بادئ بـ students/
POST Staff/GetAbsenceDetails ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ كشف الغياب — 26 طالب. لازم سكشن في الفصل المفتوح ويوم يوافق يوم المحاضرة، وإلا بيرجّع رسالة مش كشف. جديد كل طالب معاه Scanned وScannedAt (مسح كود المحاضرة؟ وإمتى)، والكشف معاه ScannedCount — قبل قفل المحاضرة الكل بيظهر «حاضر» لأن الغياب لسه ماتكتبش، فدي الطريقة الوحيدة تعرف مين مسح فعلًا · جديد ٢ لكل طالب AttendanceState (NotTaken افتراضي) وAttendanceSource وLateMinutes، وفوق العدّادات وApprovalRequired — التفاصيل
POST Staff/SetAbsence ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ تسجيل غياب — بيرفض اليوم المستقبلى واليوم اللى مش يوم الحصة
POST Staff/SaveAttendanceSheet ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ⭐ ✍️ الكشف كله في نداء واحد — 30 طالب = ريكوست واحد مش 30. STATE: present / absent / excused، و«حاضر» معناها إلغاء الغياب. الرد بيقول saved / unchanged / failed · جديد ٢ STATE بقى: present · late (+LATE_MINUTES) · absent · excused · clear. «حاضر» بقى بيكتب سطر حضور زي البورتال
POST Staff/MarkAllPresent ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ٢ «حضور الكل» — للي لسه مالوش علامة بس. الرد {marked, alreadyMarked}
POST Staff/ApproveAbsence ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ اعتماد الغياب — بيفرض اعتماد المحاضرة السابقة الأول
POST Staff/GetGradeEntrySheet ● شغال 👨‍🏫 دكتور
atef.dahesh
🔒 المكوّنات المسموح للدكتور يرصدها + كشف الطلاب (26 طالب). الصلاحية من SE_USER_CRS_ASSESS بالـ SE_USER_ID بتاع الدكتور
POST Staff/SaveGrade ● شغال 👨‍🏫 دكتور
atef.dahesh
🔒 ✍️ رصد درجة. المكوّن والتسجيل لازم يكونوا من كشف الدكتور نفسه — مكوّن مصرّح لدكتور تاني بيترفض. والدرجة المعتمدة مبتتغيّرش
POST academicstaff/GetLectQRCode ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ⭐ الدكتور بيعرض كود المحاضرة — بيرفض لو مش بيدرّس السكشن، وبيكتب في QRCode_Log · جديد ٢ الكود بيتغيّر: refreshSeconds / nextRefreshAt + sessionCode وsessionOpen؛ OPEN: true يفتح جلسة مقفولة
POST academicstaff/SetQrSession ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ٢ «اقفل الحضور» = إخفاء الكود ورفض أي مسح بعدها (مش بيكتب غياب)؛ OPEN: true يفتحه
POST Staff/CloseLectureAttendance ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ⭐ ✍️ قفل المحاضرة — هنا بيتسجّل غياب اللي مامسحش. مابيشتغلش قبل ما ميعاد المحاضرة ينتهي — ودلوقتي بيقول كده: result: ERROR + «can be closed after HH:mm» بدل «اتقفلت» من غير ولا غياب. التجربة النهاردة: جرّب الـ QR · جديد ٢ اختياري — قفل الحضور نفسه بقى academicstaff/SetQrSession

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
دخول الدكتور متعمل شغال ونفس شكل رد الطالب. —
ترم الدكتور متعمل جديد الدكتور بيدخل على الترم الحالي، ولو مالوش فيه ولا سكشن ← آخر ترم بيدرّس فيه (مش ترم جاي). قبل كده كان ممكن يدخل على ترم فاضي فالجدول فاضي والساعات المكتبية تتسجّل في ترم الطلبة مش فيه. ED_ACAD_YEAR_ID/ED_CODE_SEMESTER_ID في رد اللوجين هم اللي تبعتهم. —
الطلاب تحت الإشراف متعمل 50 طالب راجعين. —
حصص السكشن متعمل راجعة بالأيام والفترات. —
مقررات الدكتور متعمل ⭐ 7 مقررات لحساب atef.dahesh. الـ join كان سليم — الحساب اللي كان بيتجرّب (31) هو اللي مالوش تحميل تدريس. —
أخذ الحضور متعمل شغال على ED_STUD_ABSENCE — insert/update/delete مع تحقق اليوم. وغلطة الكود القديم (تبديل EdStudId و EdCourseId) اتصلّحت مش اتنقلت. —
اعتماد الغياب متعمل شغال وبيفرض اعتماد المحاضرة السابقة الأول — وده بقى إعداد في تبويب Academic بدل ما يكون فرق بين راوتين. —
كشف الحضور اليدوي متعمل ⭐ جديد شاشة كاملة للدكتور: 3 قوايم + تاريخ، الكشف بأسامي الطلاب ونِسَب غيابهم، تعليم حاضر / بعذر / غائب، وحفظ بنداء واحد. الطريقة التالتة للحضور — مش محتاجة كود ولا GPS. التفاصيل في شاشة كشف الحضور اليدوي. —
عرض كود الـ QR متعمل ⭐ جديد بيتحقق إن الدكتور فعلاً بيدرّس السكشن (SC_SCHEDULE_DTL_EXT_STF)، وبيرجّع الـ payload مش صورة — الرسم شغل الكلاينت. وبيكتب في QRCode_Log اللي التطبيق القديم عمره ما كتب فيه. —
قفل المحاضرة وتحويل الحضور لغياب متعمل ⭐ جديد ده كان الجزء الناقص خالص. بينادي Integ_QRCodeInsertStudentAbsence بتاع المنتج. اتجرّب: 27 مسجّل، واحد مسح، 26 اتسجّلوا غياب، واللي مسح مادخلش فيهم. شوف فصل الحضور. —
رصد الدرجات متعمل ⭐ اتعمل ومقفول افتراضيًا (staffGradeEntry). الصلاحية من SE_USER_CRS_ASSESS (مين يرصد إيه ولحد إمتى)، الدرجة المعتمدة بتترفض، وكل تعديل بينزل في الـ audit trail الموجود. قرار الجامعة تفتحه
28 📋

كشف الحضور اليدوي

Manual attendance
11/11 خاصية متعملة · 100%
Postman · 6 ريكوست
من شاشة المحاضرة: كود ولا كشف يدوي؟
من شاشة المحاضرة: كود ولا كشف يدوي؟
سكاشن الدكتور — دوس على واحد
سكاشن الدكتور — دوس على واحد
الكشف بأسامي وأرقام ونِسَب حقيقية
الكشف بأسامي وأرقام ونِسَب حقيقية
الدكتور بيعلّم — والعدّادات بتتحرك
الدكتور بيعلّم — والعدّادات بتتحرك
بعد الحفظ — النسبة اتحدّثت والتحذير طلع
بعد الحفظ — النسبة اتحدّثت والتحذير طلع
ولو القاعدة رفضت: بتقول أنهي محاضرة وبتوديك ليها
ولو القاعدة رفضت: بتقول أنهي محاضرة وبتوديك ليها

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

بيشتغل إزاي؟

دوسة واحدة. الشاشة بتفتح على سكاشن الدكتور من academicstaff/GetMySections — نداء واحد بيرجّع كل سكشن ومعاه الـ IDs ومواعيده وعدد طلابه. يدوس على سكشن ← الكشف يفتح على طول.
ليه GetMySections ومش GetStaffSchedule؟ الجدول كان تقرير من غير IDs؛ دلوقتي بيرجّع SC_SCHEDULE_DTL_ID وmeetings[].ScScheduleDtlDayId، بس GetMySections لسه الأنسب للكشف لأنه بيرجّع Slots بالترتيب اللي الكشف محتاجه.
الموعد مش السكشن: Slots هي مواعيد السكشن في الأسبوع، والحضور بيتسجّل على الموعد. فلما يدوس، اختار الموعد اللي DayEn بتاعه يساوي يوم التاريخ المختار، ولو مفيش رجّع التاريخ لآخر مرة الموعد حصل فيها. السكشن اللي ليه أكتر من موعد بيبان بأزرار يوم.
الكشف (Staff/GetAbsenceDetails) بيرجّع data = قايمة الطلاب. من كل سطر: EdStudId (هتبعته تاني وقت الحفظ)، الاسم والرقم، StudAbsPrcnt وAbsWarnStatusId (تحذير لما يعدّي الحد)، وEdStudAbsenceId — لو مش فاضي يبقى الطالب متعلّم عليه غياب قبل كده.
الحالة الابتدائية: مافيش EdStudAbsenceId ← حاضر · فيه وExcuseFlag=1 ← بعذر · فيه من غير عذر ← غائب.
تلات حالات وبس: present / absent / excused. مفيش «متأخر» — النظام مايعرفهاش. و«حاضر» معناها إلغاء الغياب مش حالة بتتخزن.
الحفظ (Staff/SaveAttendanceSheet): ابعت كل الطلاب ومعاهم ED_STUD_ABSENCE_ID اللي جه من الكشف (أو 0). الرد فيه saved / unchanged / failed وerrors. وبعد الحفظ اقرا الكشف تاني، لأن السطور الجديدة بقى ليها أرقام والنِسَب اتغيّرت.
وسلسلة «اعتمد اللي قبلها» بقت بتتحل لوحدها: الكشف بيفتح، والخدمة بتعتمد المحاضرات المستنية بالترتيب من الأقدم وبترجّعهم في AutoApprovedDates — اعرضهم للدكتور. ولو الجامعة قافلة الإعداد ده، بيرجع BlockedReason (كود ثابت) ومعاه PendingApprovals = السلسلة كلها بتواريخها وسلوتاتها، بدل ما تكتشفها رفض ورا رفض.
والاعتماد غير الحفظ: Staff/ApproveAbsence زرار منفصل، وماتعرضهوش غير لما الرد يقول AllowApprove: true — لأنه بيقفل اليوم.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST academicstaff/GetMySections ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ جديد نداء واحد بيبني الشاشة كلها — 12 سكشن للدكتور التجريبي، كل واحد بالـ IDs وعدد الطلاب وSlots (اليوم والوقت والقاعة وآخر تاريخ اتعمد). رقم الدكتور من التوكن. ابعت EdCourseId لو عايز مقرر واحد بس
POST academicstaff/GetStaffSchedule ● شغال 👨‍🏫 دكتور
atef.dahesh
جدول الدكتور للعرض — بقى فيه SC_SCHEDULE_DTL_ID وmeetings[]، بس للكشف استخدم GetMySections · جديد ٢ + officeHourBookings[] — حجوزات الساعات المكتبية الجاية
POST Staff/GetAbsenceDetails ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ الكشف — 26 طالب بأسامي وأكواد ونِسَب غياب. لو رجع فاضي اقرا MessageAr · جديد ٢ لكل طالب AttendanceState (NotTaken افتراضي) وAttendanceSource وLateMinutes، وفوق العدّادات وApprovalRequired — التفاصيل
POST Staff/SaveAttendanceSheet ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ⭐ ✍️ الكشف كله في نداء واحد. بيرجّع saved / unchanged / failed وقايمة بالطلاب اللي مانفعوش — الحفظ الجزئي بيتقال مش بيتخبّى · جديد ٢ STATE بقى: present · late (+LATE_MINUTES) · absent · excused · clear. «حاضر» بقى بيكتب سطر حضور زي البورتال
POST Staff/MarkAllPresent ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ٢ «حضور الكل» — للي لسه مالوش علامة بس. الرد {marked, alreadyMarked}
POST Staff/ApproveAbsence ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ ✍️ اعتماد المحاضرة — بيقفل اليوم. والمحاضرات المستنية قبلها بتتعتمد معاها بالترتيب من الأقدم (إعداد Approve the backlog automatically، مفتوح افتراضيًا) وApprovedDates بتقول اللي اتعمد. ابعت ApprovePrevious: false لو عايز تعرض السلسلة للدكتور وتاخد موافقته الأول

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
قايمة السكاشن متعمل ⭐ جديد academicstaff/GetMySections — نداء واحد بدل 3 قوايم و3 نداءات. كل سكشن بمواعيده وعدد طلابه، ودوسة واحدة بتفتح الكشف. —
اختيار الموعد تلقائي متعمل الحضور بيتسجّل على الموعد مش السكشن. الشاشة بتختار الموعد اللي يومه يساوي يوم التاريخ، والسكشن اللي ليه أكتر من موعد بيبان بأزرار يوم. —
فتح الكشف متعمل ⭐ 26 طالب بأسامي وأكواد ونِسَب غياب، والتعليم المحفوظ بيتقري من EdStudAbsenceId وExcuseFlag. —
تلات حالات متعمل present / absent / excused. مفيش «متأخر» — كانت في البروتوتايب والنظام مايعرفهاش. —
التعليم محلي متعمل كل ضغطة بتغيّر الـ state في الشاشة بس — مفيش نداء لحد ما يدوس حفظ. العدّادات فوق بتتحسب عندك. —
حفظ بنداء واحد متعمل ⭐ جديد SaveAttendanceSheet — 30 طالب = ريكوست واحد مش 30. اتجرّب: واحد غائب + واحد بعذر = saved 2 · unchanged 1 · failed 0. —
الحفظ الجزئي متعمل لو failed > 0، errors بتقولك مين بالظبط وليه — علّم السطور دي بدل ما تقول «تم الحفظ». —
الكشف الفاضي متعمل ⭐ BlockedReason كود ثابت (مش نص عربي تقارن عليه): محاضرة سابقة مش معتمدة / اليوم اتعمد / أجازة / التاريخ مش يوم المحاضرة. —
سلسلة الاعتماد بتتحل لوحدها متعمل ⭐ جديد فتح الكشف بيعتمد المحاضرات المستنية بالترتيب من الأقدم وبيرجّعهم في AutoApprovedDates — الترتيب لسه مفروض، بس مابقاش مشوار على الدكتور. ولو الجامعة قافلة الإعداد: PendingApprovals = السلسلة كلها (تاريخ + ScTchngSlotId بتاعها + عدد التعليمات عليها) عشان تعرضها وتاخد موافقة. —
الاعتماد متعمل زرار منفصل، وبيظهر بس لما AllowApprove: true. بيقفل اليوم فخليه فعل واضح مش جزء من الحفظ. —
الدخول من شاشة المحاضرة متعمل لمس محاضرة في الجدول بيفتح اختيار بين «اعرض كود المحاضرة» و«كشف الحضور اليدوي». —
29 📷

الحضور بالـ QR

QR Attendance
5/5 خاصية متعملة · 100%
Postman · 9 ريكوست
الدكتور بيعرض الكود
الدكتور بيعرض الكود
الطالب بيمسح
الطالب بيمسح
والطريق التاني: الحضور بالموقع
والطريق التاني: الحضور بالموقع

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

بيشتغل إزاي؟

الكود اللي في الصورة كود QR حقيقي مرسوم في الكلاينت من الـ payload اللي رجع من academicstaff/GetLectQRCode — السيرفس بيرجّع بيانات مش صورة، عشان الرسم شغل الكلاينت والصورة ماينفعش تتكاش ولا يتأكد منها في تست.
شاشة الطالب في الديمو بتلزق الـ payload بدل ما تفتح الكاميرا (مفيش كاميرا جوه الديمو)، لكن كل حاجة بعد كده هي النداء الحقيقي بنفس القواعد — كل رفض في الشاشة جاي من السيرفس.
التفاصيل الكاملة في فصل الحضور.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST academicstaff/GetLectQRCode ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ الـ payload + وقت الصلاحية · جديد ٢ الكود بيتغيّر: refreshSeconds / nextRefreshAt + sessionCode وsessionOpen؛ OPEN: true يفتح جلسة مقفولة
POST academicstaff/SetQrSession ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ٢ «اقفل الحضور» = إخفاء الكود ورفض أي مسح بعدها (مش بيكتب غياب)؛ OPEN: true يفتحه
POST students/GetStudentAttend ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ ✍️ الطالب بيمسح كود المحاضرة. لازم يكون مسجّل في السكشن ده — والمثال هنا سكشن الطالب نفسه. بيرفض بسبب واضح لو الجلسة مش موجودة، أو مش يوم المحاضرة، أو برّه الوقت، أو مش مسجّل. ومسح مرتين = سطر واحد · جديد ٢ ابعت QR_TOKEN وQR_WINDOW من الـ payload؛ المسح بيسجّل «حاضر» في الكشف على طول
POST students/AttendByCode ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ٢ للي مش عارف يمسح: الـ 6 أرقام اللي تحت الـ QR
POST students/CheckInByLocation ● شغال 🎓 طالب
mahmoued.eprahim.23010062
⭐ ✍️ جديد الطريق التاني: الطالب يسجّل بموقعه بدل ما يمسح كود. بيوصل لنفس المكان بالظبط، فقفل المحاضرة بيتعامل معاه زي المسح — شوف شاشة تسجيل الحضور بالموقع
POST Staff/CloseLectureAttendance ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ ✍️ القفل — بيسجّل غياب اللي مامسحش ولا سجّل بالموقع · جديد ٢ اختياري — قفل الحضور نفسه بقى academicstaff/SetQrSession
POST academicstaff/GetStaffCourse ● شغال 👨‍🏫 دكتور
atef.dahesh
⭐ 7 مقررات
POST Staff/GetInstructorCourseSections ● شغال 👨‍🏫 دكتور
atef.dahesh
سكاشن المقرر — بيرجّع SectionId مش SC_SCHEDULE_DTL_ID
POST Staff/GetInstructorSectionSolts ● شغال 👨‍🏫 دكتور
atef.dahesh
المحاضرات — الرد أوبجكت فيه slots، مش قائمة

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
عرض كود المحاضرة متعمل بيتحقق إن الدكتور بيدرّس السكشن، وبيكتب في QRCode_Log. —
رسم الـ QR في الكلاينت متعمل الديمو بيرسمه SVG بمكتبة qrcode-generator — كود حقيقي بيتقري بالكاميرا. الموبايل يستخدم مكتبة QR بتاعته
مسح الطالب متعمل خمس حالات رفض مختلفة، والمسح مرتين بيسيب سطر واحد. ربط الكاميرا في التطبيق الحقيقي
قفل المحاضرة متعمل ⭐ بينادي Integ_QRCodeInsertStudentAbsence. اتجرّب: 27 مسجّل، واحد مسح، 26 غياب. —
الحضور بالموقع (GPS) متعمل ⭐ جديد طريق تاني موازي للكود، بيكتب نفس السطر. الحدود بتترسم على خريطة في تبويب Campus map وبتتخزن في الـ Client Profile — صفر تعديل على داتابيز العميل. البصمة اتحذفت خالص: مفيش تسجيل أجهزة ولا بصمة في أي جدول، ودي كانت رسمة لتحقق مش موجود. الأدمن يرسم حدود الحرم
30 ➕

إضافة وإسقاط · سداد · اسأل المرشد

Add/Drop, Payment, Ask
إضافة وإسقاط
إضافة وإسقاط
سداد eFinance
سداد eFinance
اسأل المرشد
اسأل المرشد

تلات مسارات كتابة كانت لسه بروتوتايب: إضافة/إسقاط المقررات، طلب كود سداد eFinance، وأسئلة المرشد. دلوقتي التلاتة بينادوا الـ SIS فعلاً.

بيشتغل إزاي؟

الإضافة والإسقاط بيكتبوا في ED_STUD_COURSE_REG على طول، والباقي بيكمّل من البوابة. خد بالك من تلات حاجات: CourseReg/v2/MyCourses وGetNewCourses بياخدوا ED_STUD_ID كنص — الرقم بيتقري 0 في صمت؛ والتسجيل بيعدّي على قواعد اللائحة جوّه الـ SIS فالرفض رد حقيقي مش عطل؛ والإسقاط مش بيرجّع المقرر لقائمة «المتاح للتسجيل» لأن الـ SIS بيستنى «تراجع عن الإسقاط» مش تسجيل جديد. التفاصيل في فصل لما الإندبوينت يرجع فاضي.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST CourseReg/DropCourse ● شغال 🎓 طالب (تسجيل)
menna.elsayed.25010015
جديد ✍️ إسقاط مقرر
POST CourseReg/UnDropCourse ● شغال 🎓 طالب (تسجيل)
menna.elsayed.25010015
جديد ✍️ تراجع عن الإسقاط
POST CourseReg/RegisterNewCourse ● شغال 🎓 طالب (تسجيل)
menna.elsayed.25010015
جديد ✍️ تسجيل مقرر — الـ XML كان بيتبعت كنص فالتسجيل عمره ما اشتغل؛ اتصلّح
POST students/EfinancePayList ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد أكواد السداد المفتوحة
POST Questions/GetQuestions ● شغال 🎓 طالب
mahmoued.eprahim.23010062
أسئلة الطالب للمرشد — لازم تبعت AsFacultyInfoId، الاستعلام بيفلتر بيه وبدونه بيرجّع «data not found»
POST Questions/GetAnswers ● شغال 🎓 طالب
mahmoued.eprahim.23010062
ردود سؤال — الـ id بييجي من GetQuestions
POST Questions/AddQuestion ● شغال 🎓 طالب
mahmoued.eprahim.23010062
✍️ سؤال جديد. خد بالك: الرد بيرجّع result فيه جملة نجاح مش كلمة Ok — لو التطبيق بيتأكد من result === 'Ok' هيقراها غلط
POST Questions/AddAnswer ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ✍️ رد على السؤال

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
عرض المقررات المسجّلة متعمل 8 مقررات بالساعات والحالة. —
إسقاط مقرر متعمل ⭐ شغال — الصف بيتغيّر لحالة «مسقَط» على طول. —
تراجع عن الإسقاط متعمل ⭐ شغال. ده الطريق الصح لإرجاع مقرر مسقَط — مش تسجيل جديد. —
تسجيل مقرر جديد متعمل ⭐ الباج اتصلّح (الـ XML كان nvarchar). النداء بيوصل لقواعد اللائحة، واللي بيرفض هو المنتج نفسه. —
سداد eFinance جاهز · مستني بيانات قائمة الأكواد شغالة. إصدار كود جديد محتاج بيانات بوابة الدفع. بيانات eFinance في تبويب Integrations
اسأل المرشد متعمل ⭐ سؤال + رد، الاتنين بيكتبوا في SV_STUD_ASK_ADV. —
31 📁

شؤون الطلاب — المخالفات والطلبات

Student Affairs
11/13 خاصية متعملة · 85%
Postman · 17 ريكوست
شؤون الطلاب
شؤون الطلاب
المخالفات
المخالفات
الشكاوى
الشكاوى
ساعات المكتب
ساعات المكتب
حجز موعد
حجز موعد
سجلاتي
سجلاتي
إخلاء الطرف
إخلاء الطرف
تغيير القيد
تغيير القيد
السكن
السكن
طلبات الحرم
طلبات الحرم
وسائل الاتصال
وسائل الاتصال

الشاشات دي اتبنت من المنيو نفسه (SE_MODULE / SE_FORM) مش من البروتوتايب — كل واحدة فيها مأخوذة من الويب فورم بتاعها في البوابة القديمة. بتغطّي: المخالفات والعقوبات، إخلاء الطرف، تغيير القيد والانسحاب، الشكاوى، ساعات مكتب الدكاترة، وحجز المواعيد.

بيشتغل إزاي؟

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

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
POST students/GetViolations ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ⭐ المخالفات والعقوبات — عبر GetStudentViolations (بيتحوّل لـ C_)
POST students/GetClearanceRequests ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ⭐ إخلاء الطرف — بيوضّح أنهي إدارة لسه ماوقّعتش
POST students/GetEnrollmentChanges ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد تغيير القيد والانسحاب الرسمي
POST students/GetOfficeHours ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ساعات المكتب — كارت لكل دكتور مش صف لكل مقرر · جديد ٢ والحجز من students/GetOfficeHourOpenings / students/BookOfficeHour
POST students/GetOfficeHourOpenings ● شغال 👨‍🏫 دكتور
atef.dahesh
جديد ٢ المواعيد المتاحة للحجز (Openings[]) + MyBookings[] + CancelledByLecturer[] — التفاصيل
POST students/BookOfficeHour ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ٢ حجز ميعاد؛ الإلغاء بـ {Cancel: true, BookingId}
POST students/GetComplaints ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد شكاويّ + قوايم الأنواع
POST students/AddComplaint ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ✍️ تقديم شكوى — الموظف بيرد من البوابة
POST students/GetAppointmentSlots ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد المواعيد المتاحة — من ED_STUD_SEM_TIME_AVAILABLE
POST students/BookAppointment ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ✍️ حجز / إلغاء — حجز واحد لكل نشاط
POST students/GetMyRecords ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد الأنشطة والتدريب ومذكرات المحاضرات
POST students/GetCampusRequests ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد الخزائن وملصق العربية وطلبات الخصم
POST students/AddCampusRequest ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ✍️ طلب خزانة / ملصق — العميد بيسلّم المفتاح ويطبع الملصق
POST students/GetHousingRequests ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد السكن + أنواع الغرف + النافذة الزمنية
POST students/AddHousingRequest ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ✍️ طلب سكن — بيرفض برّه النافذة
POST students/GetCourseWithdrawals ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد طلبات الانسحاب + المقررات المؤهلة
POST students/RequestCourseWithdrawal ● شغال 🎓 طالب
mahmoued.eprahim.23010062
جديد ✍️ انسحاب من مقرر — بيسجّل طلب بس؛ حالة التسجيل مبتتغيّرش غير باعتماد العميد

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
المخالفات والعقوبات متعمل عرض فقط — بما فيه التظلم لو الموظف سجّله. —
تقديم تظلم من الموبايل ناقص مقصود إنه مش موجود. فورم التظلم بتاع عميد شؤون الطلاب (14) وشؤون التسجيل (27) — الطالب مالوش صلاحية يقدّمه من البوابة أصلاً. مايتعملش إلا لو البوابة نفسها إدّت الطالب الصلاحية
إخلاء الطرف متعمل بيعرض التوقيعات الناقصة بالاسم. —
تغيير القيد والانسحاب متعمل عرض. ملاحظة: APPROVE_FLG عليه DEFAULT 0 فالداتابيز مش بتفرّق بين "لسه" و"مرفوض". —
ساعات المكتب متعمل مجمّعة لكل دكتور بمقرراته. —
الشكاوى متعمل تقديم من الموبايل والرد من البوابة — دورة كاملة. —
حجز المواعيد متعمل حجز وإلغاء بقاعدة حجز واحد لكل نشاط وفحص السعة. —
الخزائن وملصق العربية متعمل الطالب بيطلب من الموبايل، والعميد بيسلّم المفتاح ويطبع الملصق من البوابة. فيه منع تكرار: طلب خزانة مفتوح واحد، وملصق واحد لكل عربية في السنة. —
طلبات الخصم متعمل عرض فقط — النسبة والموافقة قرار مالي البوابة هي اللي بتاخده. —
الأنشطة والتدريب ومذكرات المحاضرات متعمل عرض فقط — الصفوف دي بتتكتب من البوابة مش من التطبيق. —
السكن الجامعي متعمل دورة تلاتية: الطالب يطلب (2) · العميد ينفّذ (14) · المالية توافق وتسعّر (25). وبنافذة زمنية زي الفورم بالظبط. —
الانسحاب من مقرر متعمل الطالب بيسجّل طلب في ED_STUD_COURSE_CHNG بس — ED_STUD_COURSE_REG مبتتلمسش. العميد هو اللي بيعتمد وبيغيّر الحالة من 1 لـ 2/4. —
كتالوج طلبات المستندات في البروتوتايب ناقص مش موجود في الـ SIS أصلاً. شاشة الطلبات في البروتوتايب اتصمّمت حوالين بيان درجات وإفادة قيد وخطاب تعريف — ومفيش فورم في البوابة يكمّل الطلبات دي. الشاشة دلوقتي بتعرض طلبات الـ SIS الحقيقية. قرار إداري: هل الجامعة عايزة الخدمات دي أصلاً؟ لو أيوه لازم تتعمل في البوابة الأول
32 🏛️

عن الجامعة (قبل الدخول)

About the University
3/3 خاصية متعملة · 100%
Postman · 3 ريكوست
public-about
public-about

جديد شاشة بتتفتح من غير حساب — عشان مراجع الـ App Store / Google Play، والطالب الجديد، وولي الأمر. بتعرض اسم الجامعة ولوجو وألوانها ونبذة، وأسامي الكليات والأقسام والبرامج.

بيشتغل إزاي؟

GET Info/University — من غير Authorization. الرد: University { nameAr · nameEn · shortNameAr · logoUrl · primaryColor · aboutAr · website · email · phone · addressAr · location {lat,lng} · privacyPolicyUrl · termsUrl · bylawUrl · social[] {type,url} } + Faculties[] (شجرة: Type = Faculty ← Department ← Program، كل واحد NameAr/NameEn/Children[]) + Contacts[].
مصدرين: بيانات الجامعة والتواصل بتتكتب مرة في الأدمن (تاب «Public info & contact») لأن الـ SIS مفيهوش حاجة منها؛ والشجرة الأكاديمية بتتقري لايف من الـ SIS فمبتختلفش أبدًا عن اللي المسجّل عامله. حقل فاضي بييجي "" — اخفي السطر بتاعه. الرد عليه كاش 5 دقايق.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
GET Info/University ● شغال 🌐 من غير توكن ⭐ جديد من غير توكن — بيانات الجامعة + 8 كليات بأقسامها وبرامجها · جديد ٢ كل برنامج معاه Plan (الخطة الدراسية)؛ ?plans=false من غيرها
GET Info/AcademicPlan?programId=10 ● شغال 🌐 من غير توكن جديد ٢ خطة برنامج واحد، من غير توكن — التفاصيل
GET Info/ByLawPage ● شغال 🌐 من غير توكن لينك اللائحة (نص) — وبقى كمان جوّه University.bylawUrl

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
الشجرة الأكاديمية متعمل جديد 8 كليات ← أقسام ← برامج، الفعّال والظاهر بس. —
الاسم واللوجو والألوان والنبذة متعمل جديد من الأدمن. حد من الجامعة يكتبهم في تاب Public info
سياسة الخصوصية متعمل الستورين بيطلبوا لينك — privacyPolicyUrl. صفحة Overview في الأدمن بتحذّر لو فاضي. اللينك من الجامعة
33 ☎️

تواصل معنا

Contact Us
1/2 خاصية متعملة · 50%
Postman · 2 ريكوست
public-contact
public-contact

جديد أرقام وإيميلات المكاتب اللي الطالب بيحتاجها (القبول، شؤون الطلاب، الدعم الفني) — من غير حساب، عشان مراجعة الستور.

بيشتغل إزاي؟

GET Info/ContactUs — من غير توكن. الرد: Contacts[] كارت لكل مكتب: TitleAr · TitleEn · Phone · Email · WhatsApp · HoursAr · NotesAr — ومعاه University (التليفون والإيميل والعنوان والخريطة العامة).
زرار «اتصال» ← tel:، «إيميل» ← mailto:، «واتساب» ← https://wa.me/، «الموقع» ← location.
قرار البيزنس: دلوقتي بيانات عامة بس — مفيش إرسال رسالة من التطبيق. الإرسال للموظفين (زي صفحة النظام القديم) محتاج حماية من الإزعاج لأنه من غير توكن، فاتأجّل.

الإندبوينتات اللي الشاشة دي محتاجاها

الطريقةالمسارالحالةالحسابملاحظات
GET Info/ContactUs ● شغال 🌐 من غير توكن ⭐ جديد من غير توكن — كروت التواصل + البيانات العامة
POST Info/ContactUs/Send ✕ مش موجود 🌐 من غير توكن إرسال رسالة للجامعة — اتأجّل بقرار (بيانات عامة بس دلوقتي)

الخصائص — إيه المتعمل وإيه الناقص

الخاصيةالباك إندالوضع الحاليمحتاجة إيه؟
كروت التواصل متعمل جديد بتتكتب في الأدمن (جدول «Contact cards»). بيانات المكاتب من الجامعة
إرسال رسالة ناقص اتأجّل بقرار. قرار بيزنس + حماية من الإزعاج

6 الـ Client Profile — قلب النظام كله

لو فهمت الجزء ده، فهمت السيرفس كله. الجزء ده هو سبب وجود المشروع أصلاً.

القصة من الأول

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

🎯

الفكرة الجديدة: كود واحد فيه كل الخصائص بتاعة الـ 17 نسخة. والاختلاف بين جامعة وجامعة بيتحدد من ملف JSON واحد اسمه clientprofile.json. مفيش if (client == "KU") في الكود خالص.

الملف ده فيه إيه؟

{
  "client":            // اسم وكود الجامعة
  "connectionStrings": // الاتصال بالداتابيز (كتابة + قراءة اختيارية)
  "auth":              // طريقة الدخول + إعدادات الـ JWT
  "academic":          // نظام ساعات ولا سنوي + إزاي نحدد الفصل الحالي
  "features":          // { "اسم الخدمة": true/false } لكل خدمة
  "payments":          // بوابة الدفع وبياناتها  ← جديد
  "wallet":            // المحفظة الجامعية        ← جديد
  "meetings":          // Microsoft Teams          ← جديد
  "lms":               // Moodle                   ← جديد
  "campus":            // حدود حرم الجامعة على الخريطة   ← جديد
  "push":              // إعدادات FCM للإشعارات
  "mail":              // SMTP (لكود نسيت الباسورد)
  "infoPages":         // لينكات اللائحة ورسالة الرئيس
  "backOffice":        // مفتاح API للباك أوفيس
  "admin":             // يوزر وباسورد لوحة الإدارة
  "sandbox":           // تقليد التكاملات وقت التطوير  ← جديد
}
🔐

الأسرار بتتشفّر لوحدها. باسورد الداتابيز، مفتاح الـ JWT، مفتاح فوري، توكن Moodle… كلهم بيتحفظوا مشفّرين بـ DPAPI وبيبدأوا بـ ENC:. ما تحطش سر في الملف بإيدك — استخدم لوحة الإدارة، هي اللي بتشفّر.

لوحة الإدارة اتنظّمت

روح http://109.123.243.32:9300/Admin (أو http://localhost:5099/Admin عندك). اليوزر admin، والباسورد عند محمد عطية. أول مرة على سيرفر جديد (لو مفيش بروفايل) هتدخل على طول في «وضع الإعداد» وتقدر تبدأ من preset جاهز. فوق فيه 4 صفحات: Overview · Setup · Readiness · Sandbox control (لو شغال) — و«Sign out».

Overview
Overview — سطر لكل جزء بيقول متظبط على إيه (الدخول، الداتابيز، القواعد الأكاديمية…) ومعاه التحذير المهم لو فيه، وكل سطر عليه «Change →» بيوديك على التاب اللي بيغيّره. وتحته الخصائص، والـ Blocked الأول.
Setup
Setup — التابات متجمّعة في منيو جانبي بترتيب ما بتظبط عميل جديد: الجامعة ← الاتصال والدخول ← الأكاديمي ← التكاملات ← الإدارة. تحت كل اسم سطر بيقول التاب ده بتاع إيه.
💾

«Save changes» بيحفظ التابات اللي غيّرتها بس — عليها علامة ● برتقالي. قبل كده الفورم كله كان بيتحفظ مرة واحدة، فغلطة في تاب كانت بتمنع حفظ تاب تاني، وأي إعداد الفورم مايعرفوش كان بيرجع للافتراضي مع أول حفظ في أي حتة. دلوقتي باقي البروفايل بيفضل زي ما هو بالحرف، والرسالة بتقول «Saved: Academic rules» عشان تعرف اتحفظ إيه. ونسخة من البروفايل القديم بتتحفظ في profile-history كل مرة.

التابات واحد واحد

① Identity — هوية الجامعة

تبويب Identity

كود الجامعة واسمها بالعربي والإنجليزي والدولة. الكود ده بيظهر في /health وInfo/Capabilities، والاسم في Info/University.

② Public info & contact — اللي بيظهر قبل الدخول جديد

تبويب Public info

الاسم المختصر، اللوجو، الألوان، النبذة، الموقع والإيميل والتليفون والعنوان ودبوس الخريطة، ولينك سياسة الخصوصية (الستورين بيطلبوه)، وكروت التواصل (مكتب لكل كارت: تليفون، إيميل، واتساب، مواعيد) ولينكات السوشيال. ده اللي بيطلع في Info/University وInfo/ContactUs من غير توكن. الكليات والأقسام مابتتكتبش هنا — بتتقري لايف من الـ SIS.

③ SIS database — الاتصال

تبويب Database

سيرفر وداتابيز ويوزر وباسورد. فيه خانة اختيارية لـ read replica — لو حطيتها، كل القراءات هتروح عليها والكتابات بس على الأساسي. لو سيبتها فاضية، الاتنين على نفس الاتصال.

④ Sign-in methods — الدخول بـ 3 طرق اتعمل

تبويب Sign-in methods

كل الطرق بتنتهي بنفس الحاجة: اليوزر لازم يكون موجود في الـ SIS، والتطبيق بياخد نفس التوكن ونفس UserMainData. اللي بيختلف إزاي الباسورد بيتثبت:

الطريقةالموبايل بيناديبتعمل إيهالحالة
1 · يوزر + باسورد افتراضيauth/login الـ SIS مفيهوش باسوردات، فليها وضعين: «أي باسورد» (زي النسخ القديمة) أو «باسورد موحّد» تكتبه هنا (زي 123 بتاع Lotus) — بيتخزن hash. للتيست والديمو. ● شغال
2 · Active Directoryauth/login LDAP bind بـ user@domain على الـ domain controller — الـ AD هو اللي بيقول الباسورد صح ولا لأ. تحط الـ DC (المسار القديم ينفع زي ما هو) والدومين، وفيه «Test sign-in» تجرّب بحساب حقيقي من غير ما تحفظ. ● شغال
3 · Microsoftstudents/LoginAZ الدومين، الـ Tenant ID (اختياري)، والـ audience = Client ID بتاع app الموبايل. «Token checking»: Verify (الصح) أو «Decode only» (القديم — بيظهر أحمر). وتقدر تسيب يوزر + باسورد شغالين جنبه (بـ 1 أو 2) أو تقفلهم. تفاصيل الموبايل في دليل دخول مايكروسوفت. ● شغال
🔓

Innovation لسه على «1 · أي باسورد» — بقصد، عشان شغل الموبايل تيم والتيست مايقفش. Overview وReadiness بيحذّروا منها. قبل أي جامعة حقيقية: 2 أو 3، أو على الأقل باسورد موحّد على سيرفر تيست.

⑤ Academic rules — القواعد الأكاديمية

تبويب Academic

حاجتين:

  • النظام: YearBased (سنوي) ولا Modular (ساعات معتمدة). ده بيغيّر استعلام الغياب مثلاً.
  • تحديد الفصل الحالي: وده أهم إعداد وأكترهم بيتنسي. 4 أوضاع:
الوضعبيحدد الفصل إزاي
GlobalCurrentمن ED_ACAD_YEAR.CURRENT_YEAR = 1 + ED_SEMESTER_OPEN.IS_CURRENT = 1
StudentLastSemesterمن آخر فصل الطالب نفسه (ED_STUD_SEMESTER.IS_LAST = 1)
FacultyCurrentYearسنة الكلية (ED_ACAD_YEAR_FACULTY)
ByUserTypeطالب → آخر فصله · موظف → السنة العامة
🐞

دي بالظبط اللي وقعتنا: في Innovation_Demo الـ CURRENT_YEAR لسه على 2023/2024 بينما الفصل المفتوح فعليًا 2025/2026 ربيع. فلما جربنا GlobalCurrent اللوجين رجّع ED_CODE_SEMESTER_ID = null وكل الشاشات وقعت. حوّلناها ByUserType واشتغل كل حاجة.

الدرس: أول حاجة تعملها مع عميل جديد — اتأكد إن اللوجين بيرجّع سنة وفصل صح. لو null، غيّر الوضع ده.

جديد وجنبهم إعدادين من الميتنج:

الإعدادالافتراضيبيعمل إيه
A lecturer's term is the one they teach inمفتوح الدكتور بيدخل على الترم الحالي، ولو مالوش فيه سكاشن ← آخر ترم بيدرّس فيه (مش ترم جاي). من غيره الدكتور كان ممكن يدخل على ترم فاضي: جدوله فاضي، والساعات المكتبية اللي يسجّلها تروح لترم الطلبة مش فيه.
Academic calendar shows«للطلاب» (القديم) إيه اللي يظهر في semester/semsterCalnd: الأنشطة المعلّم عليها «للطلاب» بس، ولا «الظاهر»، ولا الكل. كتير من المسجّلين بيعلّموا «يظهر» بس — والقاعدة القديمة ساعتها بترجّع تقويم فاضي. Innovation على «Shown».

وتحتيها سياسة الحضور — إعدادين:

الإعدادالافتراضيبيعمل إيه
Lectures must be approved in orderمفتوح كشف حضور محاضرة مابيفتحش طالما فيه محاضرة أقدم في نفس السكشن لسه مش معتمدة. القاعدة بتمنع إن كشف الترم كله يتملي بأثر رجعي آخر السنة — ومش كل جامعة بتطبّقها، فالقرار بقى إعداد.
Approve the backlog automatically جديد مفتوح لما الدكتور يفتح (أو يعتمد) كشف وفيه محاضرات أقدم مستنية، بتتعتمد لوحدها بالترتيب من الأقدم — كل واحدة بتعدّي على نفس الشيك. الترتيب لسه مفروض، بس بقى مش مشوار يدوي على الدكتور. اقفله لو الجامعة عايزة كل محاضرة تتأكد بإيد.
📱

اللي يخصّك كموبايل: مع الإعداد التاني مفتوح (الافتراضي)، Staff/GetAbsenceDetails بيفتح عادي وبيرجّع AutoApprovedDates = المحاضرات اللي اعتمدها بالنيابة عنك. اعرضها للدكتور — «تم اعتماد ٣ محاضرات سابقة» — مش لأنها مشكلة، لكن لأن اعتماد محاضرة كلام على قاعة، ومن حقه يعرف إنه اتقال باسمه.

ولو الجامعة قفلته: هيرجع كشف فاضي بـ BlockedReason: "PreviousLectureNotApproved" ومعاه PendingApprovals (السلسلة كلها). ده مش عطل — اعرض العدد وزرار يعتمدها. شوف شاشة كشف الحضور اليدوي.

⑥ Campus map — الجامعة على الخريطة

تبويب Campus map

هنا الجامعة بترسم حدودها على الخريطة. ودي الحاجة الوحيدة اللي خاصية «تسجيل الحضور بالموقع» بتشتغل عليها: من غير حدود مرسومة، الخاصية بتفضل Blocked وبتقفل نفسها — مش بتعتبر الدنيا كلها حرم جامعي.

📱

لو إنت مبرمج موبايل: الصفحة دي مش شغلك، ومحتاج تعرف منها حاجة واحدة — الحدود دي عمرها ما بتنزل للتطبيق. إنت بتبعت إحداثيات الجهاز والسيرفر بيحكم. اللي يخصّك هو campus.checkInEnabled في Info/Capabilities: لو false، يبقى الجامعة لسه مرسمتش حدودها ↩ ماتعرضش الزرار.

الأدمن بيعملها إزاي:

نتيجة البحث عن الجامعة
بيدوّر باسم الجامعة — والبحث بيرجّع النتيجة ومعاها «Use this boundary»
الحدود اتحطّت على الخريطة
ضغطة واحدة = حدود جامعة عين شمس الحقيقية بـ 186 نقطة
  1. يدوّر على الجامعة بالاسم. كتير من الجامعات حدودها متراسمة في OpenStreetMap خلاص، وساعتها بياخدها بضغطة بدل ما يرسمها.
  2. أو يرسم بنفسه: «Draw a fence» وكل كليك نقطة (3 على الأقل)، وللمواقع الصغيرة «Add a circle here».
  3. التعديل: يسحب النقطة يحرّكها، كليك يمين يحذفها، وكليك على الضلع بيزوّد نقطة.

الجامعة ممكن تكون أكتر من مكان — حرم رئيسي، ومستشفى تعليمي، ومبنى مستأجر — فدي قايمة، والطالب اللي جوّه أي واحدة منهم بيعتبر في الجامعة.

الإعدادمعناهبيبان للموبايل؟
Tolerance (25م) سماحية برّه الحدود — الموبايل وهو لازق في السور بيقول إنه برّه بمتر أو اتنين. لأ
Max accuracy (100م) أوحش دقة السيرفر هيقبلها. القراية اللي بتقول «±500 متر» بتوصف برج المحمول مش الطالب. أيوه — campus.maxAccuracyMeters
Grace (10 دقايق) قد إيه بدري يقدر يسجّل وقد إيه بعد ما تخلص المحاضرة. أيوه — campus.checkInGraceMinutes
🗺️

مكتبة الخريطة متحطوطة جوه المشروع نفسه مش جاية من CDN — عشان اللوحة تشتغل على سيرفر على شبكة مقفولة. اللي محتاج نت فعلاً هو صور الخريطة والبحث، ولو مفيش نت فيه «Advanced — the fences as coordinates» تحت الخريطة تعدّل منها بإيدك.

★ دخول مايكروسوفت — دليل الموبايل تيم

إيه اللي تبعته لـ students/LoginAZ، إيه الأخطاء اللي ممكن ترجع وتعمل فيها إيه، وإزاي تجرّب النهارده من غير Azure.

التوكن الصح

التطبيق بيعمل Sign-in بمايكروسوفت (MSAL) وبياخد توكن. مش أي توكن ينفع:

التوكنينفع؟ليه
id_token (scopes: openid profile email)✔ ده اللي تبعته الـ aud بتاعه = الـ Client ID بتاع app registration الموبايل، والسيرفس يقدر يتأكد من توقيعه.
access token لـ API بتاعنا (scope api://<api-client-id>/access_as_user)✔ ينفع لو الجامعة عملت app registration للـ API نفسه.
access token لـ Microsoft Graph (User.Read…)✕ بيترفض ده اللي الكود القديم كان بياخده. مايكروسوفت نفسها بتقول محدش يقدر يتأكد من توقيعه غير Graph — فالرد بيقول تبعت إيه بداله.
POST /students/LoginAZ
Content-Type: application/json

{ "JWTAccessToken": "<id_token>" }

الرد نفس رد auth/login بالظبط: UserMainData + token.data — وكمل عادي. اسم اليوزر في الـ SIS = الجزء قبل @ في الإيميل (زي النظام القديم).

لو رجع result: "ERROR"

details بيبدأ بـمعناهتعمل إيه
This is a Microsoft Graph token…بعت توكن Graphابعت الـ id_token
The Microsoft token has expired…التوكن خلصاعمل sign-in تاني (silent)
The Microsoft token is for a different application…الـ Client ID مش المسجّل في الأدمنكلّم الباك إند — إعداد
…issued by a different tenant / Token UPN is not from tenant…حساب من جامعة/tenant تانياعرض «الحساب ده مش تبع الجامعة»
User not found.التوكن سليم بس اليوزر مش في الـ SISاعرض «حسابك مش مفعّل على النظام»
Azure AD login is not enabled on this installation.الجامعة دي مش على مايكروسوفتاخفي الزرار (Info/Capabilities)
🚩

عايز تجرّب بالتوكن اللي معاك دلوقتي (حتى لو Graph)؟ الأدمن يغيّر «Token checking» لـ Decode only على سيرفر التيست — التوكن هيتقري زي النظام القديم من غير تحقق. للتيست بس، وبيظهر أحمر لحد ما يرجع Verify. التفاصيل والصورة في فلاج التيست.

جرّب النهارده من غير Azure — الـ Sandbox

فيه «مايكروسوفت مزيّفة» جوّه السيرفس بتنشر مفاتيح وبتمضي توكنات حقيقية (RS256)، والسيرفس بيتأكد منها بنفس الكود اللي هيتأكد بيه من مايكروسوفت الحقيقية. الأدمن يفتح: Sign-in methods ← «3 · Microsoft sign-in»، و Sandbox ← «Microsoft sign-in». بعدها:

POST /__sandbox/identity/token
{ "userName": "mahmoued.eprahim.23010062" }
→ { "id_token": "eyJ…", "expires_in": 3600 }

POST /students/LoginAZ
{ "JWTAccessToken": "eyJ…" }          → result: Ok

# جرّب الأخطاء:
{ "userName": "…", "ExpiresInMinutes": -5 }     → «has expired»
{ "userName": "…", "Audience": "someone-else" } → «for a different application»

اتجرّبت 6 حالات: سليم ✔، توكن متلعب فيه (غيّرنا الاسم لدكتور) ✕، منتهي ✕، تطبيق تاني ✕، Graph ✕، ويوزر + باسورد جنبه ✔. على Innovation الطريقة الرئيسية لسه «1» عشان شغلكم الحالي مايتأثرش.

المطلوب من الجامعة (مرة واحدة)

  1. App registration للموبايل على Azure (Redirect URI بتاع MSAL) ← الـ Client ID.
  2. في الأدمن: Sign-in methods ← «3»، الدومين (زي ku.edu.bh)، والـ Client ID في «Accepted audience(s)»، و Token checking = Verify.
  3. Readiness يبقى أخضر على «Authentication (Microsoft)».

7 الـ Features — إزاي تفتح وتقفل الخدمات

النظام اللي بيمنع الإعدادات الناقصة إنها توقّع السيرفس.

تبويب Features
كل خدمة في المنتج موجودة في كل تركيب — الفرق بس مفتوحة ولا لأ

الـ 3 حالات

Disabled مقفولة

حد قفلها بالإيد من التبويب ده. الإندبوينت بيرد «الخدمة دي مش متاحة».

Active شغالة

مفتوحة + كل الإعدادات المطلوبة موجودة + كل الجداول المطلوبة موجودة في الداتابيز.

Blocked مفتوحة بس ناقصة

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

إزاي كل خاصية بتعرّف نفسها؟

في ملف FeatureCatalog.cs، كل خاصية بتقول محتاجة إيه:

new() {
  Key = "onlinePaymentFawry",
  DisplayName = "Fawry payment reference",

  // الجداول اللي الـ SQL بتاعها بيلمسها
  RequiredTables = ["INTEG_Fawry_Payment", "FEE_STUD_VOUCHER", ...],

  // خدمات تانية لازم تكون شغالة الأول
  DependsOn = ["payments"],

  // الإعدادات الإجبارية — بترجع اللي ناقص بالاسم
  ConfigRequirements = p => {
    if (string.IsNullOrWhiteSpace(p.Payments.Fawry.MerchantCode))
        missing.Add("payments.fawry.merchantCode");
    ...
  }
}
✅

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

Feature onlinePaymentFawry is BLOCKED: Missing configuration: payments.fawry.merchantCode | Missing configuration: payments.fawry.securityKey | Depends on feature 'Online payments' which is Blocked.

8 Readiness — الفحص قبل ما تسلّم

الصفحة اللي بتقولك: هل التركيب ده جاهز فعلاً ولا لأ.

صفحة Readiness
فحص حقيقي — مش مجرد قراءة إعدادات

بيفحص إيه؟

  1. الاتصال بالداتابيز

    بيفتح اتصال فعلي ويقولك اتصل بإيه وعلى أنهي سيرفر.

  2. مسح الـ Schema

    بيقرا sys.objects ويجيب كل الجداول والفانكشنز (1001 جدول و359 فانكشن هنا) ويقارنها باللي الخصائص محتاجاه.

  3. الـ Schema الأساسي

    الجداول اللي أي تركيب لازم يكون فيه (اللوجين وكارت الطالب). لو ناقصة، الداتابيز دي مش SIS أصلاً.

  4. اكتمال الخصائص

    كل خاصية Blocked بتظهر بسبب واضح.

  5. سلامة الدخول

    بيحذّر لو لسه على LegacyTrust.

  6. الـ Sandbox شغّال ولا لأ 🧪 جديد

    لو فيه أي تكامل متقلّد، بيظهر صف أصفر مكتوب عليه SANDBOX بدل الأخضر، والنتيجة فوق بتبقى READY (sandboxed) مش READY. الفكرة إن الصفحة تفضل تعدّي وانت بتطوّر — بس عمرها ما تدّيك إحساس زائف إنك جاهز للإنتاج.

  7. الاتصال بالخدمات الخارجية ● جديد

    وده الجزء المهم: بيتصل فعلاً بالبوابات والخدمات — مش بيقرا الإعدادات وبس.

🔬

مثال حقيقي من تشغيل على بيانات فوري الحقيقية: الفحص راح لسيرفر فوري التجريبي فعلاً ورجّع:

Payments: Fawry — OK — atfawry.fawrystaging.com reachable (HTTP 200)

وكمان قالنا اللينك اللي المفروض نسجّله عندهم: Register https://mobile.innovation.edu.eg/payments/callback/Fawry with the gateway.

ولما الـ Sandbox بيبقى شغّال، نفس الصف بيبقى أصفر ومكتوب فيه صراحةً this is the built-in fake gateway, not Fawry — عشان محدش يقرا الأخضر غلط.

🧩

حتة ذكية في فحص Teams: عشان Teams يشتغل لازم حاجتين مع بعض: (1) صلاحية OnlineMeetings.ReadWrite.All بموافقة الأدمن، و(2) application access policy على حساب المنظّم. لو ناقص أي واحدة فيهم، Graph بيرد 403 في الحالتين — فمش هتعرف مين الناقص. الفحص عندنا بيفرّق بينهم ويقولك بالظبط تعمل إيه.

9 بوابات الدفع و Teams و Moodle

الجزء ده كله جديد — اتعمل من الصفر. اقراه كويس لو انت في الباك إند تيم.

الاكتشاف اللي غيّر التصميم

قبل ما نكتب أي كود دفع، بصينا في الداتابيز الأول. ولقينا حاجة مهمة جدًا:

🔍

الداتابيز مش فاضية من ناحية الدفع. الـ SIS القديم كان بيكتب فيها فعلاً:

الجدولعدد الصفوفبيحتوي إيه
INTEG_Fawry_Payment2,570أرقام فوري المرجعية الحقيقية + الحالة (UNPAID/PAID)
OnlinePaymentRequests10سجل طلبات الدفع + لينكات دفع MasterCard حقيقية (test-nbe.gateway.mastercard.com/pbl/PAYLINK…)
PaymentMethodAccountConfigurations2حسابات البنوك: «Misr MasterCard» و«NBE MasterCard»
FEE_STUD_VOUCHER18,235سندات السداد — ده اللي المفروض الدفع يسدّده
GS_CODE_PAY_METHOD7نقدي · تحويل بنكي · كارت فيزا · شيك · فوري · إيداع · أهلي
🎯

القرار: الكود الجديد بيكتب في نفس الجداول دي — مش في جداول جديدة. ليه؟ عشان شاشات التسوية والتقارير في الباك أوفيس تفضل شغالة زي ما هي. والجداول دي بقت RequiredTables على الخصائص، يعني لو عميل داتابيزه قديمة ومفيهاش الجداول دي → الخاصية تبقى Blocked تلقائيًا ومكتوب الجدول الناقص.

تبويب Payments
تبويب المدفوعات — كل بوابة وحقولها، وبتظهر حسب البوابة المختارة

البوابات المتاحة

🟠 فوري (Fawry)

بيدي الطالب رقم مرجعي يدفع بيه من أي منفذ فوري أو من تطبيق myFawry، وفوري بتبعتلنا إشعار سيرفر-لـ-سيرفر لما يتسدد.

  • التوقيع SHA-256 على ترتيب حقول محدد + المفتاح السري
  • بيدعم كمان CARD و MWALLET لو فوري فعّلتهم للتاجر
  • بيكتب في INTEG_Fawry_Payment

محتاج: merchantCode + securityKey من فوري

🔴 Paymob — والمحافظ

ده اللي بيدينا فودافون كاش وأورنج كاش واتصالات كاش — مش بوابة منفصلة لكل واحدة، دي integration id مختلف عند Paymob.

  • 3 نداءات: توكن ← أوردر ← مفتاح دفع
  • التوكن بيعيش 60 دقيقة وبيتخزن عندنا
  • المبالغ بالـ قروش مش الجنيه — غلطة هنا = خصم 100 ضعف
  • الإشعار بيتحقق بـ HMAC-SHA512 على ترتيب حقول ثابت

محتاج: apiKey + hmacSecret + walletIntegrationId

💳 MPGS — فيزا/ماستر

نفس عيلة اللينكات الموجودة في الداتابيز أصلاً. صفحة دفع مستضافة عند البنك.

  • الدخول Basic auth بـ merchant.{merchantId}
  • Credimax البحريني نفس المنتج بالظبط

محتاج: merchantId + apiPassword من البنك

🔔 إشعار السداد (Callback)

لما البوابة تسدّد، بتبعتلنا على /payments/callback/{provider}. 3 حاجات مهمة اتعملت:

  • التحقق من التوقيع — أي إشعار مزوّر بيترفض
  • Idempotent — البوابات بتعيد الإرسال لحد ما تاخد 200، فالتحديث مشروط بإن الصف لسه مش مدفوع
  • أكواد HTTP حقيقية — مش الـ envelope القديم. 401 = ما تعيدش، 500 = أعِد
تبويب Integrations
تبويب التكاملات — Teams و Moodle، وكل حقل مكتوب جنبه بتجيبه منين

Microsoft Teams

بيعمل لينك اجتماع لمواعيد الإرشاد. الإعداد له خطوتين في Azure والاتنين لازم:

  1. App registration + صلاحية

    سجّل تطبيق في Entra، وأضف صلاحية OnlineMeetings.ReadWrite.All كـ Application (مش Delegated) — وبموافقة الأدمن.

  2. Application access policy

    ده اللي الناس بتنساه. من PowerShell:

    New-CsApplicationAccessPolicy -Identity mobile-meetings `
        -AppIds "<client-id>" -Description "University mobile"
    Grant-CsApplicationAccessPolicy -PolicyName mobile-meetings `
        -Identity "<organizer-object-id>"

    من غيرها Graph هيرد 403 حتى لو الصلاحية موجودة.

Moodle

بيعرض مقررات الطالب وتكليفاته جنب بيانات الـ SIS. كل حاجة بتمشي على إندبوينت واحد:

{baseUrl}/webservice/rest/server.php?wstoken=…&wsfunction=…&moodlewsrestformat=json
⚠️

حتتين بيوقعوا الناس في Moodle:

(1) Moodle بيرد HTTP 200 حتى في الغلط — الغلط بيبان جوّه الـ body في حقل exception. فما تحكمش على النجاح من الـ status code.

(2) التوكن لازم يبقى لـ role عنده صلاحية يقرا بيانات مستخدمين تانيين، مش بيانات صاحب التوكن بس — وإلا كل استعلام هيرجع accessexception.

ملخص: كل تكامل ومحتاج إيه

التكاملالكودمحتاج إيه عشان يشتغلمين يجيبه
فوريمتعملmerchantCode, securityKey, callbackBaseUrl عامتعاقد مع فوري
Paymob (محافظ + كروت)متعملapiKey, hmacSecret, walletIntegrationId, cardIntegrationId, iframeIdحساب Paymob
MPGS / CredimaxمتعملmerchantId, apiPasswordالبنك المستحوذ
Microsoft Teamsمتعملtenant + app + secret + organizer + access policyأدمن Azure بتاع الجامعة
MoodleمتعملbaseUrl + wsToken + REST مفعّلأدمن Moodle
FCM (إشعارات)متعملfcmServerKey أو ملف service accountFirebase بتاع الجامعة
SMTP (نسيت الباسورد)نص نصhost + user + password صحIT بتاع الجامعة
Active Directoryناقصالكود نفسه لسه متكتبش + بيانات الدومينباك إند تيم
📌

حالة كل التكاملات دلوقتي: الكود متكتب ومتوثّق ومربوط بالـ Readiness، ومفيش بيانات اعتماد حقيقية عندنا لسه — بس ده مبقاش بيوقّف الشغل. فيه Sandbox جوّه السيرفس بيقلّد البوابات دي بنفس البروتوكول بتاعها، فكل الخصائص بتبقى Active والموبايل تيم يشتغل دلوقتي. تفاصيله في القسم اللي بعده.

10 الـ Sandbox — نشتغل دلوقتي من غير ما نستنى البوابات

المشكلة: كل اللي واقفنا تكاملات خارجية. الحل: نقلّد البوابة نفسها، مش نقلّد كودنا.

المشكلة بالظبط

لو فتحت صفحة الـ Readiness قبل الشغل ده، كنت هتلاقي الكلام ده:

الخاصيةواقفة ليهمين بيجيب الحل
الدفع أونلاينmerchantCode و securityKey بتوع فوريتعاقد مع فوري
إشعارات السدادلازم callbackBaseUrl عام بـ httpsدومين + شهادة
اجتماعات تيمزtenant + app + secret + access policyأدمن Azure بتاع الجامعة
MoodlebaseUrl + wsTokenأدمن الـ LMS
الإشعاراتمفتاح FirebaseFirebase بتاع الجامعة

مفيش حاجة فيهم دي بتتحل بكود. كلها ورق وتعاقدات ممكن تاخد أسابيع. ومعنى كده إن الموبايل تيم يقعد مستني — وده مش مقبول.

الحل: نقلّد البوابة، مش نقلّد نفسنا

أسهل حاجة كانت إننا نحط في الكود if (sandbox) return fakeData; — ودي أسوأ حاجة ممكن نعملها. ليه؟ لأنك ساعتها بتختبر فرع كود مش هيشتغل في الحقيقة أبداً، ويوم ما البيانات الحقيقية تيجي تكتشف إن التوقيع غلط والـ HMAC غلط والـ callback مش بيتقري — كل ده يوم الإطلاق.

🎯

اللي عملناه بدل كده: كل تكامل بيتكلم HTTP على baseUrl متظبّط من الـ profile. الـ Sandbox بيقوّم سيرفر مزيّف جوّه نفس الخدمة بيتكلم نفس بروتوكول الشركة الحقيقية، وبيوجّه الـ baseUrl عليه.

يعني FawryPaymentProvider لسه بيبني توقيع SHA-256 حقيقي، وPaymobPaymentProvider لسه بيعمل الأربع نداءات ويتحقق من الـ HMAC-SHA512، وGraph لسه بيعمل client-credentials. مفيش سطر واحد اتغيّر في أي provider، ومفيش أي فرع كود جديد.

يوم الإطلاق = تغيير baseUrl والبيانات. خلاص. نفس الكود اللي جرّبناه هو اللي هيشتغل.

تشغّله إزاي

من لوحة الإدارة: Profile → تاب Sandbox → اختار تشغّل إيه:

تاب الـ Sandbox في لوحة الإدارة
تاب الـ Sandbox — 4 سويتشات، وطريقة السداد، ولينكين
السويتشبيقلّد إيهبيفكّ إيه
paymentsفوري / Paymob / MPGS + صفحة دفع + callbacks موقّعة5 خصائص دفع
meetingsEntra token + Graph onlineMeetingsلينكات تيمز في الإرشاد
lmsMoodle web services + مواد وتكليفات جاهزةشاشة المنصة التعليمية
pushFCM — الرسايل بتروح لـ outbox تقدر تقراهالإشعارات وصندوق الوارد
identity جديدمايكروسوفت مزيّفة — بتنشر مفاتيح وبتمضي توكنات دخول تجريبية (POST /__sandbox/identity/token)دخول مايكروسوفت

محتاج كمان تحدّد مكان الخدمة: Browser base URL (اللي الموبايل بيوصله) وLoopback base URL (اللي الخدمة بتوصل بيه لنفسها — جوّه دوكر بيبقى http://127.0.0.1:8080).

🔑

حاجة مهمة عن البيانات: الـ Sandbox بيفرّق بين نوعين:

• البيانات السرية (merchant code, security key, token) — بيكتبها بس لو الخانة فاضية، فبيانات حقيقية دخّلها حد عمرها ما هتتمسح.
• الـ baseUrl — بياخده دايمًا، حتى لو فيه لينك حقيقي. لأن الـ host هو المفصلة اللي الفكرة كلها قايمة عليها: لو سبناه على فوري الحقيقية وحطينا بيانات مزيّفة، النتيجة تركيبة مستحيل تشتغل، والرفض هيبان كإنه باج في كودنا. اللينك القديم بيتحفظ في sandbox.overriddenBaseUrls وبيرجع مكانه أول ما تقفل الـ Sandbox.

وده اتكشف وقت النشر فعلاً: السيرفر على الـ VPS كان فيه لينك فوري حقيقي، فالـ Sandbox ساب اللينك وغيّر البيانات بس — وكل عملية دفع رجعت Something went wrong لحد ما اتصلّحت.

المبلغ هو اللي بيحدد النتيجة

عشان مطوّر الموبايل يجرّب حالة فشل من غير أي أدوات — الكسور في المبلغ بتتحكم في السيناريو (نفس فكرة كروت الاختبار عند Stripe):

المبلغبيحصل إيهبتختبر بيه إيه
1500.00طبيعي — يفضل معلّق لحد ما تدفعالمسار السعيد
1500.11البوابة ترفض من الأصلرسالة الرفض في الشاشة
1500.22يتعمل، وبعدين يفشل لوحدهأصعب حالة — الفشل المتأخر
1500.33ينتهي صلاحيته من غير دفعالكود اللي بيتلغي
1500.44يتلغيالإلغاء
1500.99البوابة متردّش خالصالـ timeout

صفحة التحكّم: /__sandbox

بتشوف فيها كل عملية شغّالة، وتضغط زرار تخليها مدفوعة أو مرفوضة أو منتهية.

صفحة التحكّم في الـ Sandbox
صفحة التحكّم — كل عملية، وحالتها، وكام إشعار اتبعت لها

وكل ده متاح كمان JSON لو عايز تأتمته:

GET  /__sandbox/control/transactions        // كل العمليات
POST /__sandbox/control/transactions/{ref}/PAID
POST /__sandbox/control/transactions/{ref}/replay
GET  /__sandbox/control/outbox              // الإشعارات اللي اتبعتت
POST /__sandbox/control/reset
🔁

ليه Replay مهم؟ البوابات الحقيقية بتبعت نفس إشعار السداد أكتر من مرة لحد ما تاخد 200. لو الكود بتاعنا مش idempotent، الطالب هيتحسبله السداد مرتين. الطريقة الوحيدة إنك تتأكد إنه سليم هي إنك تبعت الإشعار مرتين بنفسك — وده اللي الزرار ده بيعمله.

جرّبناه فعلاً: أول إشعار رجّع Duplicate: false، والتاني والتالت رجّعوا Duplicate: true، والقيد اتكتب مرة واحدة بس. ✅

الدورة كاملة — اللي اتجرّب فعلاً

التطبيق  → students/CreatePayment
السيرفس  → البوابة المزيّفة (بتوقيع SHA-256 حقيقي)
البوابة  → بترجّع كود دفع 9 أرقام + لينك صفحة الدفع
الطالب   → يفتح الصفحة ويضغط "ادفع"
البوابة  → POST /payments/callback/Fawry  (إشعار موقّع بجد)
السيرفس  → يتحقق من التوقيع → يحدّث OnlinePaymentRequests + INTEG_Fawry_Payment
التطبيق  → students/PaymentStatus → Paid
🐛

الـ Sandbox طلّع 7 باجات حقيقية كانت هتظهر يوم الإطلاق بس — 4 منهم كانوا تعطيل كامل للخاصية:

1. الـ callback مكانش بيتقري لو البوابة بعتت بـ chunked encoding — كل إشعارات السداد كانت هتتفقد بصمت.
2. INTEG_Fawry_Payment عموده identity والكود كان بيكتب فيه رقم بإيده (SQL 544).
3. الـ provider كان بيرمي exception لو فوري بعتت null في مبلغ عملية لسه متدفعتش.
4. شاشة الـ LMS كانت بتقرا STUD_FACULTY_CODE من جدول غلط.
5. MSG_SUBJECT عموده 50 حرف والكود كان بيحشر فيه JSON باللغتين — الإشعارات كانت بتفشل كلها.
6. الإشعار كان بيفشل لو المرسل مبعتش أرقام السنة والفصل — بقى بيجيبهم لوحده.
7. EfinancePay كان ناسي عمود ED_CODE_SEMESTER_ID من الـ INSERT أصلاً — يعني الإندبوينت ده عمره ما اشتغل.

دي بالظبط الحاجات اللي مكانش فيه طريقة تكتشفها من غير ما تشغّل الدورة كاملة. التفاصيل والدروس المستفادة في القسم 12.

🚨

قبل ما ننزل production: اقفل الـ Sandbox من نفس التاب، وشغّل Readiness تاني. الصفوف اللي هتولّع أحمر دي هي الشغل اللي فاضل بالظبط.

الـ Sandbox بيعلن عن نفسه في كل مكان — /health، Info/Capabilities، صف أصفر في Readiness، هيدر X-Sandbox على كل رد مزيّف، وشريط أصفر فوق شاشة التطبيق. بس مفيش حاجة بتمنعك إنك تنزله غلط — فخلي بالك.

11 الـ SIS هو مصدر الحقيقة — قواعد لازم تشتغل بيها

الشاشات القديمة (الـ webforms) في Innovation/ هي مصدر الحقيقة لكل workflow. الموبايل اتصمّم من رؤية البروتوتايب، وساعات الرؤية دي ما تتطابقش مع الـ workflow الحقيقي. الفصل ده بيشرح القواعد الخمسة اللي لازم تمشي عليها، وكل واحدة فيهم غلطها بيعدّي في صمت — مش بيرمي exception، بيرجّع داتا شكلها صح.

قاعدة 1 الموبايل بيعمل نص الدورة، والبوابة بتكمّلها

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

مثال حقيقي حصلالاكتشاف
زرار "قدّم تظلم" على المخالفات
كنا حاطين فيلد canAppeal في الـ API
رجعنا للمنيو: الطالب (user type 2) عنده StudViol_view.aspx عرض وRPT_StudViolation.aspx تقرير — وبس. فورم التظلم AppealProblem.aspx بتاع عميد شؤون الطلاب (14) وشؤون التسجيل (27). يعني الطالب أصلاً مالوش صلاحية يقدّم تظلم من البوابة — الفيلد كان بيخترع flow مالوش استكمال عند الموظف. اتحذف، وبقى hasAppeal عرض بس: الموظف يسجّل التظلم من بوابة العميد، والطالب يشوف النتيجة على الموبايل.
حجز المواعيد الطالب بيحجز بس (STUD_RSRV_REG_TIME.aspx)، لكن الأجندة نفسها (ED_SEM_AGENDA_DTL / _TIME) الإدارة هي اللي بتعرّفها. فالموبايل بيحجز ويلغي — وعمره ما بيعمل أجندة.
إزاي تتأكد قبل ما تبني: دوّر على الفورم في SE_FORM، شوف الـ SE_MODULE بتاعه راجع لأنهي SE_CODE_USER_TYPE_ID. لو كل خطوات الـ cycle بتاعة الموظف — الموبايل يعرض بس.

قاعدة 2 اسم الـ stored procedure بيتغيّر وقت النداء

المنتج بيسمّي البروسيجر باسم منطقي، وبعدين التركيبة بتاعة كل عميل ممكن تستبدله بنسخة مخصّصة. ده بيحصل في StoredProcedureProvider.GetCommandWrapper:

الاسم موجود فيالبادئةاللي بيتنفّذ فعلاً
CustomProcedures.xmlC_dbo.C_GetStudentViolations
ProceduresUpdated.xmlU_dbo.U_<name>
ولا واحد—dbo.<name> زي ما هو
الفخ: صفحة بتقرا dbo.GetStudentViolations بتنفّذ dbo.C_GetStudentViolations، والاسم الأصلي مش موجود في الداتابيز خالص. إحنا كنا مستخدمين C_ED_STUD_VIOL_Students — بروسيجر موجود فعلاً وبيرجّع مخالفات، فشكله شغال تمام — بس مش اللي المنتج بينادي عليه، وبيرجّع أعمدة مختلفة (من غير ED_STUD_VIOL_APPEAL_ID ولا EXE_FLG_REG). الغلط ده مبيرميش exception، عشان كده خطير.
القاعدة: ممنوع تكتب اسم بـ C_ أو U_ بإيدك، وممنوع تعدّل أو تعمل stored procedure. استخدم ISisProcedures.ResolveAsync("<الاسم المنطقي>") — بيجرّب C_ ثم U_ ثم الاسم العادي وياخد أول واحد موجود. وRequiredProcedures في الـ FeatureCatalog بتحذف الاسم المنطقي، والـ Readiness بيدوّر على أي نسخة من التلاتة.
لاقينا نفس المشكلة في مسار الغياب الـ Modular اللي كان موجود من الأول: C_GetAttendanceFWforStudent كان مكتوب بالبادئة، وGetAllABSOfStud من غيرها — كل واحد صح على الداتابيز دي بس غلط على عميل تركيبته مختلفة.

قاعدة 3 "مفيش فلتر" ساعات بيبقى 0 مش NULL

أغلب شروط البروسيجرز مكتوبة @p is null or @p = 0 or col = @p — فـ NULL تمام. بس فيه شروط مكتوبة @p = 0 or col = @p بس، ومن غير is null.

لو بعتّ NULL للشرط ده، الـ WHERE كله بيبقى false، والطالب بيشوف سجله فاضي بدل مخالفاته. الصفحة القديمة بتبعت أصفار عشان كده بالظبط. دايمًا اقرا جسم البروسيجر قبل ما تقرر تبعت إيه.

قاعدة 4 الأعمدة الرقمية مش سلاسل نصية

أعمدة زي EXE_FLG_REG وDrop_Flg وAPPROVE_FLG نوعها numeric، فبترجع 1.0 مش "1".

مقارنة value.ToString() == "1" بتخلي كل عقوبة منفّذة تبان غير منفّذة. اعمل decimal.TryParse وقارن != 0.
وكمان: APPROVE_FLG عليه DEFAULT 0 — يعني طلب لسه محدش بصّله متخزّن بنفس شكل طلب مرفوض، والداتابيز مش قادرة تفرّق بينهم. عشان كده بنكتب NotApproved مش Rejected: ما ينفعش نحط على شاشة الطالب قرار محدش خده.

قاعدة 5 اسم الـ property في الكود القديم مش اسم العمود

الكود القديم بيستخدم ORM مولّد (nettiers)، وده بيحوّل أسماء الأعمدة لأسماء properties بيحذف منها الشرط السفلي. فلو قريت ObjStud.GsCountryInfoId1 في الكود ونقلتها زي ما هي، هتكتب GS_COUNTRY_INFO_ID1 — والعمود الحقيقي اسمه GS_COUNTRY_INFO_ID_1.

دي وقعت معانا فعلاً في طلب ملصق العربية: Invalid column name 'GS_COUNTRY_INFO_ID1'. خد الأعمدة من INFORMATION_SCHEMA.COLUMNS، مش من أسماء الـ properties.

مهم جداول أكواد فاضية عليها Foreign Key

الفورم القديم بيكتب قيم ثابتة (زي SV_CDE_REQ_STATUS_ID = 1) — وجدول الأكواد نفسه ممكن يكون فاضي في داتابيز العميل.

في Innovation_Demo جدول SV_CDE_REQ_STATUS كان فاضي وعليه FK، يعني تقديم أي شكوى كان هيقع بـ FK violation. الهاندلر بتاعنا بيسيب العمود NULL لو مفيش صف بدل ما يقع — نفس مبدأ "الإعداد الناقص بيهبّط الخاصية، ما بيكسرهاش".

12 تحليل الفجوة — الديمو مقابل الـ SIS

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

إزاي اتعملت: منيو الـ SIS مخزّن في SE_MODULE وSE_FORM. الطالب هو SE_CODE_USER_TYPE_ID = 2. فلو حاجة مالهاش SE_FORM في منيو الطالب — يبقى الطالب مالوش صلاحيتها. ولو ليها فورم بس خطوات تانية بتاعة موظف — يبقى دوره جزئي.

🚫 القائمة الأولى — مش موجود في الـ SIS أصلاً

دول مش «لسه متعملوش» — دول مفيش لهم فورم في البوابة يكمّل الدورة. لو بنينا نص الموبايل منهم، هنعمل سجلات محدش يقدر يعالجها. الشاشات دي عليها علامة غير موجود في الـ SIS في التطبيق نفسه.

الشاشة / الخاصيةالبروتوتايب متخيّل إيهالدليل من الـ SIS
كتالوج الوثائق
بيان درجات، إفادة قيد، خطاب تعريف، شهادة تخرج، بدل فاقد كارنيه
تقديم طلب + دفع رسوم + متابعة + استلام PDF مفيش أي SE_FORM لطلب وثيقة في منيو الطالب. وحدة «طلباتي» الحقيقية في الـ SIS حاجة تانية خالص (إخلاء طرف، خزانة، ملصق، خصم، تغيير قيد).
حجز القاعات قايمة قاعات + المتاح في وقت + حجز + موافقة جداول ACCOM_* بتاعة السكن مش حجز قاعات، ومفيش فورم حجز قاعة في منيو الطالب.
الأنشطة والرحلات
كتالوج فعاليات، طلب مشاركة، دفع
يتصفّح فعاليات ويشترك ويدفع ED_STUD_ACTIVITY سجل بعدي لنشاط الطالب (الموظف بيكتبه)، مش كتالوج ولا اشتراك. مفيش فورم اشتراك في فعالية.
المحفظة الجامعيةرصيد + شحن + دفع منها مفيش جدول محفظة في الـ SIS. المدفوعات كلها بتعدّي على FEE_STUD_VOUCHER.
المساعد الذكيشات ذكاء اصطناعي مش جزء من الـ SIS — feature تطبيق محتاج قرار وخدمة منفصلة.
مجموعات وملفات ومكالمات في الشاتشات كامل SV_STUD_ASK_ADV رسائل نصية بين طالب ومرشد بس.
Apple/Google Wallet · تذكير قبل الامتحان · إضافة للتقويم الشخصي تكامل مع الجهاز مميزات جهاز مش داتا SIS — بتتعمل في التطبيق نفسه.

⚠️ القائمة التانية — موجود بس دور الطالب مختلف عن الديمو

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

الحاجةالديمو بيفترضالحقيقة في الـ SISاتعمل إيه
التظلم على المخالفةالطالب يقدّم تظلم الطالب عنده StudViol_view عرض والتقرير وبس. AppealProblem.aspx بتاع عميد شؤون الطلاب (14) وشؤون التسجيل (27). ✅ اتحذف canAppeal وبقى hasAppeal عرض بس
الأنشطة والتدريبالطالب يشترك ويعدّل سجل بيتكتب من البوابة؛ التدريب بيتدار من القسم (RPT_StudTraining لأنواع 15/16/24/27). ✅ اتعمل عرض بس
مذكرات المحاضرات— الدكتور بيرفعها على المقرر؛ الطالب بيشوف مقرراته المسجّلة بس. ✅ اتعمل عرض بس
طلبات الخصم— الطالب يقدّم، بس DISC_PERCENT وUPDATED_PERCENT والموافقة قرار مالي البوابة بتاخده. ✅ اتعمل عرض بس
الخزانة وملصق العربيةمش موجودين في الديمو أصلاً فورم واحد منشور للطالب (2) وللعميد (14) — دورة هجينة مثالية. ✅ اتعمل كتابة؛ التسليم والطباعة من البوابة
السكن الجامعيمش موجود في الديمو ٣ أدوار: الطالب يطلب (2)، العميد ينفّذ (14)، المالية توافق وتسعّر (25) — وبنافذة زمنية. ✅ اتعمل كتابة بنفس شرط النافذة
رصد الدرجات (شاشة الدكتور)الدكتور يرصد من الموبايل موجود في البوابة، وأخطر إجراء أكاديمي في المنتج. ✅ اتعمل ومقفول افتراضيًا — staffGradeEntry

✅ القائمة التالتة — ينفع يتعمل

دول ليهم فورم في منيو الطالب، وليهم استكمال واضح عند الموظف. كلهم خلصوا — مفيش حاجة في القائمة دي مستنية كود. (والاتنين المقفولين فوق كمان اتعملوا، بس بيتفتحوا بقرار.)

خلص ١٣ مجموعة قدرات شغالة على الداتا الحقيقية

القدرةالفورمدور الطالب
المخالفات والعقوباتStudViol_viewعرض
إخلاء الطرفPerStudClrncReqSearchعرض + التوقيعات الناقصة
تغيير القيد والانسحابStudEnrollChangeSearchعرض
ساعات المكتبOfficeHoursعرض
الشكاوىStudentComplainكتابة — الموظف يرد
حجز المواعيدSTUD_RSRV_REG_TIMEكتابة — الإدارة تعرّف الأجندة
الأنشطة والتدريب والمذكراتStudActivity · StudTraining · StudLecturesNotesعرض
الخزانة وملصق العربيةToolBoxOrderReg · CARSTIKR_Transكتابة — العميد ينفّذ
طلبات الخصمFeeStudentDiscRequestعرض
السكن الجامعيAccomOrderDetailsكتابة بنافذة زمنية
الانسحاب من مقررWithdrawCoursesReqكتابة — طلب بس، العميد يعتمد
الانسحاب من الجامعة والتأجيلStudEnrollChngReqكتابة — طلب بس، شؤون التسجيل تعتمد
أخذ الحضور واعتماد الغيابشاشات الدكتوركتابة — بقواعد اليوم والحصة السابقة

مقفول اتعمل بالكامل — بس بيتفتح بقرار

الاتنين دول كانوا «محتاجين قرار». دلوقتي اتعملوا بالكامل وبقواعد الـ SIS نفسها، ومقفولين افتراضيًا. يعني البيزنس متطبّق، والجامعة هي اللي تقرر تفتح أو تقفل من Admin → Profile → Features — من غير ما حد يلمس كود. لما يكونوا مقفولين، الإندبوينت بيرد «الخدمة غير متاحة» زي أي feature مقفولة.

الشغلانةالفورم والدورةالميكانيزم
تعديل بيانات التواصل
studentContactEdit
ContactMethod.aspx — الطالب (2)، بس تحت «Hidden Pages» الطالب يضيف/يعدّل/يمسح موبايله وإيميله وعنوانه. ليه مقفول: الجامعة مخفياه من المنيو عن قصد — بعض الجامعات بتخلي المسجّل هو اللي يغيّرهم لأنهم بيتحكموا في الإشعارات الرسمية. كل جملة SQL متقيّدة بالطالب نفسه، فـ id مزوّر ميوصلش لسجل طالب تاني.
رصد الدرجات
staffGradeEntry
شاشات الدكتور الدكتور يرصد درجة مكوّن تقييم. الصلاحية مش من عندنا: SE_USER_CRS_ASSESS هي اللي بتقول مين يرصد أنهي مكوّن لأنهي مقرر ولحد إمتى (FROM_DATE/TO_DATE). الدرجة المعتمدة (Approve_Flg) بتترفض، والدرجة السالبة بتترفض، وكل تعديل بينزل في AUDT_ED_STUD_COURSE_REG_ASSESS — اتأكدنا: غيّرنا درجة والـ audit سجّلها.
الخلاصة: الفرق بين القائمة الأولى والتالتة مش تقني — القائمة الأولى محتاجة الجامعة تعمل الفورم في البوابة الأول، وبعدين الموبايل ياخد نص الطالب منه. من غير كده أي شغل موبايل فيها هيولّد داتا محدش هيشوفها.

13 مسارات عضو هيئة التدريس

الستاف مش «طالب بصلاحيات زيادة» — هويّته مختلفة، والإندبوينت بتاعة بروفايله مختلفة، وحتى الفصل الدراسي بيتحسبله بطريقة تانية. الفصل ده بيشرح الفروق دي والسيناريوهات الشغالة فعلًا، بأرقام من تشغيل حقيقي.

١) اللوجن — إزاي تعرف هو مين

الراوت مش هو اللي بيحدد النوع. استخدم auth/login. students/login وacademicstaff/login أسماء قديمة لنفس الهاندلر بالظبط، وكل واحد فيهم بيقبل الاتنين. النوع بيتحدد من الداتابيز: IsStudent = (SE_CODE_USER_TYPE_ID == 2). يعني لو التطبيق نادى الراوت «الغلط» الرد هيفضل صح.
فخ في الرد: اللوجن بيرجّع نفس الرقم في ED_STUD_ID وSA_STAFF_MEMBER_ID:
طالب: ED_STUD_ID 73 · SA_STAFF_MEMBER_ID 73
ستاف: ED_STUD_ID 31 · SA_STAFF_MEMBER_ID 31
ده شكل MOB_LOGIN القديم. الحقل اللي مالوش معنى للدور الحالي لازم يتتجاهل — متبنيش أي منطق على إن الرقمين مختلفين، لأنهم مش مختلفين.
الخطوةالطالبالستاف
اللوجن auth/login — إندبوينت واحد، والدور بيتحدد من IsStudent في الرد
البروفايل students/get
بيرجّع CardProfile
academicstaff/get
بيرجّع مصفوفة فيها SA_STF_MEMBER_ID · STF_CODE · STF_FULL_NAME_AR/EN · AS_FACULTY_INFO_ID · SA_CODE_JOB_STATUS_ID
الـ id المستخدمED_STUD_ID SA_STAFF_MEMBER_ID

٢) حساب التجربة — atef.dahesh

استخدم الحساب ده في كل تجارب الستاف. اخترناه بعد ما قيّمنا كل أعضاء هيئة التدريس في الداتابيز على تغطية البيانات، وهو الأعلى: SA_STF_MEMBER_ID = 148 · SE_USER_ID = 2991 · السنة 16 الفصل 2. (الباسورد أي حاجة — LegacyTrust لسه شغال.)
الإندبوينتالبوديبيرجّع
auth/login{"UserName":"atef.dahesh","Password":"x"}IsStudent = false
academicstaff/get{"ED_STUD_ID":"148"}بيانات الدكتور
academicstaff/GetStaffCourse{"saStaffMemberId":148}7 مقررات
academicstaff/GetStudentUnderSuperVision{"EdAcadYearId":16,"EdCodeSemesterId":2,"saStaffMemberId":148}25 طالب
Staff/GetInstructorCourseSections{"saStaffMemberId":148,"EdCourseId":33}2 سكشن
students/GetStudentsForCourse{"EdCourseId":33}388 طالب
Staff/GetInstructorSectionSolts{"SC_SCHEDULE_DTL_ID":866}الحصص + أيامها
Staff/GetGradeEntrySheet{"SE_USER_ID":"2991","ED_COURSE_ID":"33","ED_CODE_COURSE_ASSESS_ID":"55"}21 مكوّن · 194 طالب
Staff/SetAbsence{"EdStudId":1262,"EdCourseId":33,"ScTchngSlotId":1253,"AbsDate":"2026-07-28","IsAbsence":1}«تم الحفظ» — 2026-07-28 يوم ثلاثاء وده يوم الحصة
Staff/GetAbsenceDetails{"EdCourseId":6,"SC_SCHEDULE_DTL_ID":1678,"ScTchngSlotId":2729,"AbsDate":"<أقرب يوم حد>"}26 طالب + الاعتماد مفتوح
academicstaff/GetLectQRCode{"SA_STF_MEMBER_ID":148,"SC_SCHEDULE_DTL_ID":1678,"SC_SCHEDULE_DTL_DAY_ID":2729}الـ payload + وقت الصلاحية
Staff/CloseLectureAttendance{"SC_SCHEDULE_DTL_ID":1678,"SC_SCHEDULE_DTL_DAY_ID":2729,"STF_CODE":"70","SESSION_DATE":"<نفس اليوم>"}حاضر + عدد اللي اتسجّلوا غياب
حاجتين اتظبطوا في الداتا عشان الحساب ده يشتغل بالكامل: الفصل المفتوح كان منتهي 2026-07-31 فاتمدّ؛ وED_ABSENCE_APPROVE كان فاضي تمامًا فسلسلة «اعتمد المحاضرة السابقة الأول» مكانش ليها بداية — اتعمل اعتماد للمحاضرات السابقة. ودي قواعد حقيقية من المنتج مش قيود اخترعناها.
الجلسة المرجعية للحضور: سكشن 1678 · محاضرة 2729 · يوم الحد · مقرر 6 · 27 مسجّل. اختارناها لأنها في الفصل المفتوح — السكشن اللي كنا بنجرّب عليها قبل كده كانت بتاعة الفصل الأول، وده كان بيخلّي كشف الغياب يرجع فاضي لسبب مالوش علاقة بالكود. التفاصيل في فصل الحضور.
خد بالك: الجلسة دي وقتها متوسّع لليوم كله عشان تنفع تتجرّب في أي ساعة — باقي الجلسات مواعيدها ضيّقة عشان رفض «الكود مابقاش صالح» يفضل قابل للتجربة.

٥) مين بيشوف إيه — خريطة الشاشات لكل طرف

ده كان باج حقيقي اتصلّح. الديمو كان بيوَدّي الدكتور على الرئيسية بتاعة الطالب بعد الدخول، والـ tab bar كان بيعرضله «الخدمات» و«الجدول» بتوع الطالب — يعني نص التطبيق الغلط كان على بُعد ضغطة. السبب إن التطبيق كان بيتعامل مع الدور كـ«ثيم» مش كـحدود.

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

الطرفبيهبط علىالـ tabs بتاعتهشاشاته
الطالب
IsStudent = true
home الرئيسية · الخدمات · الجدول · الملف الجدول، المقررات، الدرجات، الحضور، الاختبارات، خطة الدراسة، التسجيل، الإرشاد، الطلبات، الانسحاب، المدفوعات، الاستطلاع، شؤون الطلاب بكل شاشاتها، ومسح كود الحضور.
عضو هيئة التدريس
IsStudent = false
staff-home الرئيسية · مقرراتي · الحضور · الملف لوحة الدكتور، الطلاب تحت الإشراف، مقرراته، رصد الدرجات، وعرض كود الحضور وقفل المحاضرة.
مشترك — — الملف الشخصي، البطاقة الجامعية، الإشعارات، المحادثات، المساعد.
الحدود متفرضة في الراوتر مش في الـ UI بس. لو حد وصل بأي طريقة لشاشة مش بتاعة دوره (deep link، زرار قديم، الكونسول)، التطبيق بيعرض شاشة «الشاشة دي مش لدورك» بدل ما يرندر شاشة هتسأل بمعرّف غلط. ده مقصود إنه يبان — الديمو بيعلّم الفصل بين الطرفين، مش بيخبّيه.

٣) السيناريوهات الشغالة

السيناريوالإندبوينتالنتيجة الفعلية
بروفايليacademicstaff/get ✅ بيانات الدكتور كاملة
طلابي تحت الإشرافacademicstaff/GetStudentUnderSuperVision ✅ ٢٥ طالب
مقرراتيacademicstaff/GetStaffCourse ✅ ٧ مقررات
أخذ الحضورStaff/SetAbsence ✅ بيرفض اليوم المستقبلى واليوم اللى مش يوم الحصة
اعتماد الغيابStaff/ApproveAbsence ✅ بيفرض اعتماد المحاضرة السابقة الأول
رصد الدرجات 🔒Staff/GetGradeEntrySheet · Staff/SaveGrade ✅ ٢١ مكوّن مسموح · كشف ١٩٤ طالب — مقفول افتراضيًا (staffGradeEntry)
صلاحية الرصد مش من عندنا. SE_USER_CRS_ASSESS هي اللي بتقول مين يرصد أنهي مكوّن لـأنهي مقرر ولحد إمتى (FROM_DATE/TO_DATE). الشاشة بتعرض المسموح بس، والدرجة المعتمدة (Approve_Flg) بتترفض، وكل تعديل بينزل في AUDT_ED_STUD_COURSE_REG_ASSESS — اتأكدنا: غيّرنا درجة والـ audit سجّلها.

٤) باجين اتصلحوا وإنت محتاج تعرفهم

باج 1 الفصل الدراسي كان بيرجع NULL للستاف

أي لوجن ستاف كان بيولّد ١١ ريكوست فاشل (400) على طول. السبب داتا مش كود: ED_ACAD_YEAR.CURRENT_YEAR فلاج بيتحط بالإيد، ولسه مأشّر على 2023/24 والفصل المفتوح فعلًا 2025/26. فالـ resolver بيقرا السنة المأشّرة، مبيلاقيش ED_SEMESTER_OPEN ليها، ويرجّع سنة من غير فصل.

الطالب مبيتأثرش لأن صف ED_STUD_SEMESTER بتاعه هو المرجع. الستاف مالوش صف زي ده، فالانحراف وقع عليه هو بس.
الحل: لو السنة المأشّرة مفيهاش فصل مفتوح، الـ resolver بياخد السنة اللي فيها فصل مفتوح فعلًا (IS_CURRENT = 1) — دي الحقيقة التشغيلية — ويكتب warning بالسنتين عشان الفلاج يتصلّح مش يتلف حواليه.
النتيجة: من ١١ ريكوست فاشل لـ صفر، والطلاب تحت الإشراف من صف واحد لـ ٥٠ على الحساب اللي جرّبناه ساعتها.

باج 2 الـ API كان مفتوح من غير توكن

السؤال «هل بروفايل الستاف محتاج توكن زي كارت الطالب؟» كشف إن ٧٧ من ٨٨ كلاس إندبوينت مكانش عليهم [Authorize] أصلًا.

يعني أي حد يبعت {"ED_STUD_ID":"31"} من غير لوجن يقرا بروفايل أي دكتور، أو غياب أي طالب، أو مصروفاته، أو جدوله.
الحل مكانش إضافة [Authorize] ٧٧ مرة — ده بيسيب نفس الفخ لأي إندبوينت جديد. بقى deny by default (SetFallbackPolicy): الراوت محمي لأن اللي كتبه معملش حاجة، مش مكشوف لأنه نسي حاجة. والعام منهم بيقول كده صراحة ومعاه السبب.
عام عن قصدليه
students/login · LoginAZلسه مفيش توكن
نسيت الباسورد + إعادة التعييننفس السبب، وبالتعريف
Info/Capabilitiesالتطبيق بيقراها قبل شاشة اللوجن
Files/UserImages/{name}بتتحط في <img> واللي مبيبعتش bearer token
payments/callback/{provider}البوابة مش مستخدم مسجّل دخول
الساندبوكس · /healthبيقلّدوا بوابة · فحص صحة الكونتينر
للموبايل تيم: بعد التغيير ده، أي نداء من غير Authorization: Bearer هيرجع 401 ما عدا القايمة اللي فوق. خد التوكن من token.data في رد اللوجن وابعته في كل ريكوست بعد كده.

14 الحضور — الدورة كاملة بتلات طرق

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

الدورة تلات خطوات، اتنين بني آدمين

#مينالإندبوينتبيحصل إيه
1الدكتور academicstaff/GetLectQRCode بيعرض كود المحاضرة على الشاشة. بيتكتب سطر في QRCode_Log.
2الطالب students/GetStudentAttend بيمسح الكود جوه وقت المحاضرة. بيتكتب سطر في Integ_QRCode_Presence.
3الدكتور Staff/CloseLectureAttendance بيقفل المحاضرة. هنا بس بيتنادى إجراء المنتج Integ_QRCodeInsertStudentAbsence اللي بيسجّل غياب لكل طالب مسجّل في السكشن ومامسحش في ED_STUD_ABSENCE.

لو الخطوة 3 ماحصلتش، المسح بيفضل مجرد سطر في جدول لوج. ده بالظبط اللي كان حاصل: Integ_QRCode_Presence فيه 39,196 سطر حضور متسجّل، وED_STUD_ABSENCE مافيهوش ولا سطر جاي منهم.

اللي كان ناقص التطبيق القديم كان بيعمل الخطوة الوسطانية بس

رجعنا لتطبيق Lotus/ القديم وقرينا الكود. كان فيه GetStudentAttend بس، وكان بيتحقق من حاجة واحدة: الساعة. مش أكتر.

الفحصالقديمدلوقتي
الجلسة موجودة أصلاً؟لأأيوه — بيرفض بسبب واضح
النهاردة يوم المحاضرة؟لأأيوه — كود يوم التلات مايشتغلش يوم الحد
جوه وقت المحاضرة؟أيوهأيوه
الطالب مسجّل في السكشن دي؟لأأيوه — ED_STUD_GROUP
مسح قبل كده؟لأ — سطر جديد كل مرةأيوه — سطر واحد مهما مسح
الدكتور ده بيدرّس السكشن ده؟مفيش إصدار كود أصلاًأيوه — SC_SCHEDULE_DTL_EXT_STF

وفيه تفصيلة تستاهل تتحكي: القديم كان بيعمل استعلام مكافحة غش — بيدوّر على نفس الجهاز (UDID) متسجّل لطالب تاني — وعمره ما بصّ في النتيجة. الاستعلام كان بيتنفّذ وبيترمي. مش باج بيرمي exception، ده باج بيسكت.

قرار السيرفس بيرجّع الـ payload مش صورة

القديم كان بيرجّع JPEG فيه الكود مرسوم. إحنا بنرجّع الـ JSON نفسه، والرسم شغل الكلاينت. السبب: الصورة مينفعش تتكاش، ومينفعش تتأكد منها في تست، ومينفعش الموبايل يغيّر حجمها أو لونها. الديمو بيرسمها SVG بمكتبة qrcode-generator — كود حقيقي بيتقري بالكاميرا.

{"STUD_FACULTY_CODE":"70","saStaffMemberId":148,"STF_CODE":"70",
 "SC_SCHEDULE_DTL_ID":1678,"SC_SCHEDULE_DTL_DAY_ID":2729,"ED_COURSE_ID_STAFF":6}

تجربة حقيقية شغّلناها على الداتابيز

الجلسة المرجعية: سكشن 1678 / محاضرة 2729 / يوم الحد / الفصل المفتوح.

الخطوةالنتيجة
عدد المسجّلين في السكشن27
الدكتور عرض الكودOk — وQRCode_Log اتكتب
دكتور تاني حاول يعرض كود نفس السكشناترفض — مش بيدرّسها
طالب مسجّل مسحتم تسجيل الحضور
مسح تاني"حضورك مسجّل بالفعل" — وفضل سطر واحد
طالب مش في السكشن مسحاترفض — "إنت مش مسجّل في السكشن ده"
مسح بعد ما المحاضرة خلصتاترفض — الكود مابقاش صالح
الدكتور قفل المحاضرةحاضر 1 · اتسجّل غياب 26
اللي مسح اتسجّل غايب؟لأ — وده بيت القصيد

مهم القفل مش بيشتغل قبل ما المحاضرة تخلص

إجراء المنتج نفسه فيه شرط GETDATE() > TO_TIME. يعني لو ناديت القفل والمحاضرة لسه شغالة، هيرجّع 0 غياب — مش عطل، ده الإجراء بيرفض إنه يحكم على محاضرة لسه ما انتهتش. وكمان بيتخطّى أيام الأجازات من GS_AGENDA_HOLIDAY.

جديد الطريقة التانية: الحضور بالموقع

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

شيت تسجيل الحضور بطريقتين
زرارين، والاتنين حقيقيين
الحضور اتسجّل بالموقع
من جوّه الحرم — والرد نفسه بيقولك اسم المقرر والمكان والوقت
رفض الحضور برّه الحرم
من برّه — والرسالة جاهزة للعرض بالعربي

شغلك في التطبيق: 4 خطوات وخلاص

  1. اتأكد الخاصية مفتوحة أصلاً — من Info/Capabilities اقرا campus.checkInEnabled. لو false ماتعرضش الزرار من أوله.
  2. اطلب إذن الموقع واقرا قراية جديدة (مش مخزّنة).
  3. ابعت 3 أرقام بس.
  4. اعرض رد السيرفر زي ما هو.
POST students/CheckInByLocation
Authorization: Bearer <token>

{ "ED_STUD_ID": 73, "LAT": 30.026, "LNG": 31.206, "ACC": 12 }

ACC هي دقة القراية بالمتر اللي الجهاز نفسه بيديهالك (accuracy / horizontalAccuracy). ابعتها زي ما هي — السيرفر بيستعملها عشان يرفض القرايات الوحشة. لو مش عارفها ابعت 0.

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

الرد لما يتسجّل

{
  "result": "Ok",
  "recorded": true,
  "alreadyRecorded": false,
  "presenceTime": "2026-08-09T14:09:27",
  "session": { "COURSE_CODE": "SUR311", "COURSE_DESCR_AR": "العلاج الطبيعي للأورام",
               "FROM_TIME": "...", "TO_TIME": "..." },
  "location": "Main campus", "locationAr": "الحرم الرئيسي",
  "distanceMeters": 0,
  "messageAr": "تم تسجيل الحضور من الحرم الرئيسي"
}
  • اعرض messageAr زي ما هي، واسم المقرر من session، والوقت من presenceTime.
  • alreadyRecorded: true يعني ضغط تاني — ده مش error. اعرضله «حضورك مسجّل بالفعل» بنفس شكل النجاح.
  • locationAr للعرض في واجهة عربي، وlocation لو الواجهة إنجليزي.

الرد لما يترفض

{ "result": "ERROR",
  "details": "إنت بره حرم الجامعة بحوالي 1639 متر — لازم تكون جوه عشان تسجّل حضورك" }

خد بالك: الرفض بيرجع بـ HTTP 200 زي باقي الـ API القديم — الفرق في result. وdetails رسالة جاهزة للطالب بالعربي: اعرضها زي ما هي، ماتترجمهاش وماتحطش مكانها رسالة من عندك.

الطالب شايف إيهإنت تعمل إيه
«إنت بره حرم الجامعة بحوالي … متر» اعرض الرسالة وسيب زرار «جرّب تاني». وممكن تفكّره بالـ QR كطريق تاني.
«إشارة الموقع ضعيفة (±… متر)» قوله يطلع في مكان مفتوح ويجرّب. متعملش retry أوتوماتيك في لوب.
«مفيش محاضرة مسجّلة ليك في الوقت ده» الزرار مش هيفيد دلوقتي — عادي، مش عطل.
«تسجيل الحضور بالموقع مش مفعّل في جامعتك» يبقى ماكانش المفروض تعرض الزرار — راجع Capabilities.
🚫

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

حاجات هتقابلك وإنت بتنفّذ

الحاجةالتفاصيل
الأذونات iOS: NSLocationWhenInUseUsageDescription · Android: ACCESS_FINE_LOCATION. الـ COARSE لوحده مش كفاية — دقته بالكيلومترات والسيرفر هيرفضها.
قراية جديدةمش cached: maximumAge = 0، دقة عالية (desiredAccuracy: best)، وtimeout حوالي 15 ثانية. الأول قراية بتيجي وحشة وبعد ثانيتين بتتحسّن — استنى الكويسة.
المستخدم رفض الإذناعرضله إزاي يفتحها من الإعدادات، وسيبله الـ QR شغال — الطريقتين مستقلين.
ويب / WebView على http المتصفح بيمنع الموقع خالص على أي origin مش https (ما عدا localhost)، وبيرجّعها كأن المستخدم رافض. ده بالظبط اللي بيحصل على لينك الديمو.
التجربةحط إحداثيات وهمية من الـ simulator (iOS: Features → Location → Custom) أو الـ emulator (Extended controls → Location) وجرّب من جوه ومن برّه.
ضغط مرتينمش مشكلة — السيرفر مابيسجّلش مرتين وبيرجّع أول وقت. بس اقفل الزرار وقت الانتظار عشان الـ UX.
اتجرّب فعلًا على الداتابيزالنتيجة
من جوه الحدود والطالب عنده محاضرة شغالة اتسجّل حضور في SUR311 — والسيرفر هو اللي اختار المحاضرة
ضغط تاني«حضورك مسجّل بالفعل» — ومافيش تسجيل مكرر
من ميدان التحرير (1.6 كم)اترفض — «إنت بره حرم الجامعة بحوالي 1639 متر»
قراية دقتها ±800 متراترفض — «إشارة الموقع ضعيفة»

جديد الطريقة التالتة: الكشف اليدوي (شاشة دكتور)

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

سكاشن الدكتور
سكاشن الدكتور بمواعيدها وعدد طلابها — دوس على سكشن والكشف يفتح
كشف الحضور اليدوي
26 طالب بأسامي وأكواد ونِسَب غياب — كلها جاية من الرد
الدكتور بيعلّم
التعليم محلّي في الشاشة — العدّادات فوق بتتحرك من غير أي نداء
بعد الحفظ
حفظ واحد للكشف كله — والنِسَب بتتحدّث بعده

الشاشة ماشية إزاي

  1. اعرض سكاشن الدكتور: academicstaff/GetMySections — نداء واحد بيرجّع كل سكاشنه ومعاها الـ IDs ومواعيدها وعدد طلابها.
  2. هو يدوس على سكشن ← الكشف يفتح على طول من Staff/GetAbsenceDetails؛ كل اللي الإندبوينت ده محتاجه موجود في السطر اللي اتضغط عليه.
  3. علّم محليًا — من غير أي نداء لكل ضغطة.
  4. احفظ الكشف كله في نداء واحد: Staff/SaveAttendanceSheet.
📱

ليه إندبوينت جديد ومش GetStaffSchedule؟ جدول الدكتور بيعرض نفس القايمة بالظبط، بس كان تقرير من غير IDs (دلوقتي بيرجّع SC_SCHEDULE_DTL_ID وmeetings[]، بس لسه مش مرتّب على مواعيد الكشف). GetMySections هو نفس القايمة بالـ IDs — وقبل كده كانت الشاشة 3 قوايم و3 نداءات (مقرر ← سكشن ← محاضرة) قبل ما تقدر تطلب الكشف أصلاً.

POST academicstaff/GetMySections
{ }                       // أو { "EdCourseId": 6 } لمقرر واحد بس

{ "sections": [
  { "EdCourseId": 6, "CourseCode": "BAS103", "CourseDescrAr": "علم الأنسجة I",
    "ScScheduleDtlId": 1678, "GroupDescrAr": "01", "Students": 27,
    "Slots": [ { "ScScheduleDtlDayId": 2729, "DayAr": "الأحد", "DayEn": "Sunday",
                 "FromTime": "11:00", "ToTime": "13:00",
                 "TeachingAr": "محاضرة", "HallAr": "مدرج 2",
                 "LastApprovedDate": "2026-07-26T00:00:00" } ] } ] }

رقم الدكتور بيتاخد من التوكن مش من الـ body، فمستحيل ترجع سكاشن حد تاني. وSlots هي مواعيد السكشن في الأسبوع: السكشن اللي بيتقابل مرتين ليه موعدين، والحضور بيتسجّل على الموعد مش على السكشن. فلما يدوس على سكشن، اختار الموعد اللي DayEn بتاعه يساوي يوم التاريخ المختار — ولو مفيش، رجّع التاريخ لآخر مرة الموعد ده حصل فيها.

افتح الكشف

POST Staff/GetAbsenceDetails
{ "EdCourseId": 6, "SC_SCHEDULE_DTL_ID": 1678, "ScTchngSlotId": 2729,
  "AbsDate": "2026-08-09", "SortBy": 2 }

الرد فيه data = قايمة الطلاب. اللي يهمك من كل سطر:

الحقلتعمل بيه إيه
EdStudIdمفتاح السطر — هتبعته تاني وقت الحفظ.
StudFullNameAr · StudCodeالاسم والرقم الجامعي.
EdStudAbsenceIdلو مش فاضي يبقى الطالب متعلّم عليه غياب قبل كده. ابعته زي ما هو وقت الحفظ (لو فاضي ابعت 0).
ExcuseFlagبيفرّق بين «غائب» و«بعذر» في التعليم المحفوظ.
StudAbsPrcnt · AbsWarnStatusIdنسبة غيابه، وعلامة تحذير لما يعدّي الحد (555 = تمام).

الحالة الابتدائية لكل طالب: مافيش EdStudAbsenceId ← حاضر · فيه وExcuseFlag = 1 ← بعذر · فيه من غير عذر ← غائب.

تلات حالات وبس

حاضر · بعذر · غائب. مفيش «متأخر» — النظام مايعرفهاش أصلاً، فمتحطهاش في الـ UI. و«حاضر» معناها إلغاء الغياب مش حالة بتتخزن: لو الطالب كان متعلّم عليه غياب ورجّعته حاضر، الحفظ بيحذف الغياب.

احفظ

POST Staff/SaveAttendanceSheet
{ "ED_COURSE_ID": 6, "SC_SCHEDULE_DTL_ID": 1678, "SC_SCHEDULE_DTL_DAY_ID": 2729,
  "ABS_DATE": "2026-08-09",
  "MARKS": [
    { "ED_STUD_ID": 2122, "ED_STUD_ABSENCE_ID": 0,     "STATE": "absent"  },
    { "ED_STUD_ID": 3011, "ED_STUD_ABSENCE_ID": 0,     "STATE": "excused" },
    { "ED_STUD_ID": 1830, "ED_STUD_ABSENCE_ID": 91350, "STATE": "present" }
  ] }

STATE واحدة من: present · absent · excused. ابعت كل الطلاب — السيرفر بيعرف لوحده مين اتغيّر ومين لأ.

{ "result":"Ok", "saved":2, "unchanged":1, "failed":0,
  "isSaved": true, "errors": [], "messageAr": "تم حفظ الكشف" }
  • failed > 0 يعني حفظ جزئي: errors فيها EdStudId + سبب لكل طالب مانفعش. علّم السطور دي بالأحمر بدل ما تقول «اتحفظ».
  • بعد الحفظ اقرا الكشف تاني. السطور الجديدة بقى ليها أرقام (EdStudAbsenceId) والنِسَب اتغيّرت — من غير إعادة قراءة، تاني حفظ هيبعت أرقام قديمة.

لو الكشف رجع فاضي — مش عطل

الكشف بيرفض يفتح
الرفض بيقول السبب وبيدي زرار للخطوة الجاية

الجامعة عندها قواعد بتمنع فتح الكشف في حالات. الرد بيقولك السبب بـكود ثابت — اتعامل معاه بالكود، مش بمقارنة نص عربي هيتغيّر أول ما حد يحسّن الصياغة:

BlockedReasonتعمل إيه
None + AutoApprovedDates فيها تواريخ الكشف فتح عادي، والخدمة اعتمدت المحاضرات المستنية قبل ما تفتحه. اعرض للدكتور إن ده حصل: «تم اعتماد ٣ محاضرات سابقة».
PreviousLectureNotApproved (بيظهر بس لو الجامعة قافلة الاعتماد التلقائي) جنبه PendingApprovalDate = تاريخ المحاضرة اللي لازم تتعمد الأول، وPendingApprovalSlotId = السلوت اللي بيفتحها — خد الاتنين مع بعض لما تفتحها. وفيه PendingApprovals = السلسلة كلها من الأقدم للأحدث، عشان تعرض «فيه ٤ محاضرات ناقصة» بدل ما تكتشفها واحدة واحدة.
AlreadyApprovedاليوم اتقفل — اعرض للقراية بس.
Holidayأجازة رسمية.
NotThisWeekdayالتاريخ مش يوم المحاضرة — رجّعه للتاريخ الصح.
Noneالكشف مفتوح عادي.

وMessageAr موجودة برضه للعرض المباشر، وLastApproveDate بتقول آخر محاضرة اتعمدت.

الاعتماد ≠ الحفظ

Staff/ApproveAbsence زرار منفصل، وماتعرضهوش غير لما الرد يقول AllowApprove: true. الحفظ بيعدّل الكشف، والاعتماد بيقفل اليوم ويخلّي الغياب رسمي — فخليه فعل واضح ومقصود، مش جزء من زرار الحفظ.

سلسلة «اعتمد اللي قبلها» — بقت بتتحل لوحدها

دي كانت أكتر حاجة وقّفت الستاف: تعتمد محاضرة، يقول لك «اعتمد اللي قبلها»؛ تفتح اللي قبلها، يقول لك نفس الكلام على اللي قبلها؛ وهكذا — من غير ما حد يقولك السلسلة طولها كام ولا يدّيك طريقة تخلّصها في مرة واحدة. القاعدة نفسها صح (عشان محدش يملا الترم بأثر رجعي في آخر يوم)، بس هي قاعدة ترتيب — مش مشوار المفروض الدكتور يمشيه بإيده.

الافتراضي دلوقتي: الدكتور يفتح الكشف ويشتغل. إعداد Approve the backlog automatically (مفتوح افتراضيًا في الـ Client Profile) بيخلّي Staff/GetAbsenceDetails يعتمد المحاضرات المستنية بالترتيب من الأقدم وبعدين يفتح كشف النهاردة عادي. كل واحدة بتعدّي على نفس الشيك بتاع الـ SIS — مافيش قاعدة اتكسرت، اللي اتشال هو المشوار.

والرد بيرجّع AutoApprovedDates — التواريخ اللي اتعمدت بالنيابة. اعرضها: «تم اعتماد ٣ محاضرات سابقة». اعتماد محاضرة كلام على قاعة، والدكتور من حقه يعرف إنه اتقال باسمه — وخصوصًا إن كشف فاضي معناه «محدش غاب».

ولو الجامعة قفلت الإعداد ده، بترجع الحالة الصارمة: الرفض بيوصل ومعاه السلسلة كلها، وانت اللي بتقرّر تعرضها إزاي:

في الردمعناه
BlockedReason: "PreviousLectureNotApproved" الاعتماد اترفض — وIsSaved = false.
PendingApprovals[] كل المحاضرات الواقفة في السكة، من الأقدم للأحدث. كل واحدة فيها AbsDate وScTchngSlotId بتاعها هي (مش بتاع المحاضرة اللي انت فيها) وMarked = عدد التعليمات المتسجّلة عليها.
ApprovePrevious (في الطلب — اختيارية) ماتبعتهاش وهياخد إعداد الجامعة (الافتراضي: يعتمد السلسلة وبعدين اليوم ده، نداء واحد). true = اعتمدها حتى لو الإعداد مقفول. false = ماتعتمدش، رجّع لي السلسلة الأول. والرد بيرجّع ApprovedDates[] باللي اتعمد فعلًا.
إمتى تبعت ApprovePrevious: false؟ لما تكون عايز تسأل الدكتور الأول — «فيه ٤ محاضرات قبلها مش معتمدة، تعتمدهم كلهم؟». وMarked = 0 على أي واحدة فيهم معناها كشفها فاضي، يعني اعتمادها بيقول «محدش غاب» — ودي المعلومة اللي بتخلّي السؤال ده يستاهل يتسأل.
واللي اتصلّح تحت في نفس الشغلانة: الشيك بتاع «المحاضرة السابقة» كان محسوب بطريقتين مختلفتين — شاشة الكشف بتنده MOB_GetPervSectionDate والاعتماد بينده GetPervSectionDate. بقى حساب واحد جوّه الخدمة، فالشاشة والزرار مابقوش يختلفوا على أنهي محاضرة واقفة. وكمان: المقارنة بقت على التاريخ مش على الـ datetime بحاله — صف اعتماد متسجّل ومعاه وقت كان بيبقى موجود والشيك مش شايفه، يعني تعتمد المحاضرة وتفضل «مش معتمدة».
💡

فيه كمان Staff/SetAbsence اللي بيحفظ طالب واحد — البوابة بتستعمله كده. للموبايل استعمل SaveAttendanceSheet: 30 طالب = ريكوست واحد مش 30 على شبكة قاعة محاضرات، ولو النت قطع في النص مش هتلاقي نص كشف متسجّل.

اتجرّب فعلًا على الداتابيزالنتيجة
حفظ: واحد غائب، واحد بعذر، والباقي حاضر saved 2 · unchanged 1 · failed 0
إعادة قراءة الكشفالتعليم رجع زي ما هو والنسبة اتحدّثت لوحدها
رجّعنا الاتنين «حاضر»الغياب اتحذف
تاريخ في المستقبلاترفض — «لا يمكن الحفظ فى يوم مستقبلى»

لغز اتحل ليه كشف الغياب كان بيرجع فاضي

Staff/GetAbsenceDetails كان بيرجّع كشف فاضي على سكشن فيها طلبة، وكان مفهوم إنه باج في الكود. طلع مش باج — طلع تلات قواعد شغالة صح فوق بعض:

القاعدةالرسالةمعناها
المحاضرة السابقة لازم تكون معتمدةالمحاضرة السابقة غير معتمدة مش هينفع تسجّل غياب النهاردة قبل ما تعتمد اللي فات — MOB_GetPervSectionDate.
ماينفعش تعتمد مرتينتم الاعتماد مسبقا اليوم اللي اتعمد اتقفل.
السكشن لازم تكون في الفصل المفتوح— السكشن اللي كنا بنجرّب عليها كانت بتاعة الفصل الأول والفصل المفتوح هو التاني، فدالة "المحاضرة السابقة" كانت بترجع تاريخ من يناير. غيّرنا الجلسة المرجعية لسكشن في الفصل المفتوح — الكشف رجع 26 طالب بأسمائهم والاعتماد مفتوح.

الدرس: الكشف الفاضي هنا مش دايمًا باج. اقرا الرسالة اللي راجعة قبل ما تدوّر في الكود.

15 لما الإندبوينت يرجع فاضي — الداتا اللي لازم تكون موجودة

كان في 23 إندبوينت بيرجّعوا «شغال بس فاضي». دلوقتي كلهم بيرجّعوا داتا. الفصل ده بيقول لكل واحد فيهم ليه كان فاضي — لأن نفس السبب هيتكرر على أي داتابيز جديدة، والفرق بين «الإندبوينت باظ» و«الداتا مش متظبطة» هو أهم فرق هتحتاجه وانت بتجرّب.

أولاً أربع باجات حقيقية — كانوا بيرجّعوا نجاح فاضي

دول مكانوش بيرموا exception ولا بيرجّعوا رسالة غلط. كانوا بيرجّعوا result: Ok وليستة فاضية — وعشان كده فضلوا متسجّلين في الدليل على إنهم «شغالين» شهور.

الإندبوينتالسبب
CourseReg/v2/MyCourses الهاندلر كان بيبعت 0 لفلتر حالة المقرر. الـ SQL كاتب «كل الحالات» كده: @ED_CODE_COURSE_STATUS_ID IS NULL OR @… = <column> — يعني 0 معناها «هات الصفوف اللي حالتها 0»، وده رقم مفيش صف بيحمله. بقت NULL ← رجعت 8 مقررات.
CourseReg/RegisterNewCourse كان بيبعت الـ XML كنص عادي، فـ Dapper بيبعته nvarchar و SQL Server بيرفض .nodes() عليه: «The XMLDT method 'nodes' can only be invoked on columns of type xml». يعني التسجيل عمره ما اشتغل. بقى DbType.Xml.
students/GetSTudentTranscriptApi اتنين مع بعض: التقرير بيقرا @RegHeadFlg وعمره ما عرّفه، ونسخة GetCourseRepeatFlg في الداتابيز دي بتاخد باراميتر تالت التقرير مش بيبعته. الأولاني بقى NULL، والتاني بيتحل بنفس طريقة ChkCrsInPlan: نسأل sys.parameters الداتابيز بتاخد كام باراميتر ونكمّل — من غير ما نلمس الفانكشن.

ثانياً سويتشات الإدارة — الداتا موجودة بس مقفولة

الإندبوينتاللي كان ناقص
students/GetExamSchdule جدول الامتحانات كان موجود (133 صف) بس ED_OFFERING.IS_EXAM_APPROVED كان 1 من 12، والطالب مكانش موزّع على لجنة في CO_EXAM_PLACE_STUD. الاتنين اتظبطوا ← 8 امتحانات بالقاعات.
Survey/* (٤ إندبوينت) مكانش في ولا استبيان مربوط بمقررات الطالب. اتعمل تقييم لكل مقرر من مقرراته بنفس الـ 40 سؤال بتوع الاستبيان الموجود، واتفتحت فترة التقييم (PUB_FLG + EVAL_DATE/EVAL_DUE_DATE).
CourseReg/v2/AvailableCourses فترة التسجيل في أجندة الفصل كانت قافلة من 2026-07-30. الإندبوينت كان بيقول كده صراحةً: «التسجيل غير متاح حاليا» — رد صح مش عطل.
students/schedulePerday SC_SCHEDULE_DTL_Date كان فاضي تمامًا، والاستعلام بيشترط إن النهاردة يقع جوّه المدى ده. اتحطّ مدى الفصل لكل سكشن ← رجعت محاضرات النهارده.
academicstaff/GetStaffSchedule التقرير بينتهي بـ WHERE SC_SECTION_GROUP_HDR.IS_ACTIVE = 1 على جدول مربوط بـ LEFT JOIN — وده بيحوّل الـ LEFT لـ INNER في صمت. أي سكشن مش متربطة بمجموعة سكاشن مفعّلة بتختفي من التقرير خالص. اتصلّح في الكود: السكشن بتتشال بس لو المجموعة بتاعتها متوقفة، والتكرار (36 صف لـ 12 سكشن) اتلمّ.
students/GetSTudentTranscriptApi محتاج كمان ED_STUD_SEMESTER.RESULT_APPROVE_FLG = 1 — يعني نتيجة الفصل معتمدة، وده شرط عشان السجل الأكاديمي يطلع رسمي.
Files/UserImages/{name} بيخدم ملف من على الديسك. الفولدر كان فاضي، فالرد الوحيد الممكن كان 404.

ثالثاً باراميتر غلط في الدليل نفسه — مش في الكود

دي أسهل حاجة تضيّع وقتك: الإندبوينت شغال تمام، بس البودي اللي بتبعته مش بيوصل.

الإندبوينتالغلطالصح
CourseReg/v2/MyCoursesEdStudId: 73 ED_STUD_ID: "73" — نص. الرقم بيتقري 0 في صمت ويطلع «مفيش فصل نشط».
Survey/GetSurveyByCourseED_COURSE_IDEdCourseId
students/GetStudentQRCodeاسم الدخولبقى بياخد الاتنين — اسم الدخول أو الكود الجامعي، وكمان بودي فاضي (بيقرا صاحب التوكن). الرد صورة PNG مش JSON.
students/GetSTudentTranscriptApiPOSTGET بـ query string
إندبوينتات الدكتورsaStaffMemberId: 31148 — حساب atef.dahesh هو اللي عليه داتا

401 التوكن بيخلص بعد ٢٤ ساعة

لو فجأة كل الريكويستات بقت 401 وانت مغيّرتش حاجة — التوكن خلص مش الإندبوينتات باظت. التوكن عمره ٢٤ ساعة بالظبط. كوليكشن سايبه مفتوح من امبارح هيرد 401 على كل حاجة، وشكل ده زي إن جزء من الـ API اتكسر.

عشان ده مايحصلش تاني، كوليكشن الـ Postman فيه سكريبت قبل كل ريكويست بيبص على تاريخ انتهاء التوكن، ولو خلص (أو قرّب يخلص بدقيقة) بيعمل لوجين لوحده ويجدّد token وstaffToken. يعني مش محتاج تفتكر تشغّل اللوجين.
ولو بتجرّب من غير Postman: التوكن الفاضي والتوكن المنتهي والتوكن الغلط — كلهم 401.

مهم جدًا رفض القاعدة بييجي كـ result: ERROR

لما الـ SIS يرفض عملية لسبب شغلاني — «عندك طلب خزانة مفتوح بالفعل»، «الكود ده مش بتاع النهاردة»، «عندك حجز في النشاط ده» — الرد بييجي result: ERROR ومعاه جملة مفهومة للمستخدم في details. ده المنتج شغال صح، مش عطل. التطبيق لازم يعرض الجملة دي زي ما هي بدل ما يقول «حصل خطأ غير متوقع».

الرديعني إيه
result: "Ok"تمام
جملة نجاح في result نجاح كمان. Questions/AddQuestion بيرجّع «Success New Question has been Added». لو بتتأكد من result === 'Ok' بس، هتقراها غلط
result: "ERROR" + جملة عربي/إنجليزي مفهومة قاعدة رفضت. اعرض الجملة للمستخدم
result: "ERROR" + «An unexpected error occurred» ده اللي يستاهل تبلّغ عنه
details: "data not found" مفيش نتايج — غالبًا باراميتر ناقص مش عطل

تحقق شامل كل الإندبوينتات اتشغّلت فعلاً

الجدول ده مش تقدير — اتولّد من تشغيل كل إندبوينت في الدليل، كل واحد بالحساب المكتوب جنبه، على السيرفر المنشور.

الحالة المكتوبةاللي رجع فعلاً
شغال (87) 75 رجّعوا داتا · 2 رجّعوا صورة · 7 رفضتهم قاعدة شغلانية (رد صح) · 3 عمليات كتابة مالهاش ريسبونس (SaveToken، AddQuestion، AddAnswer)
Sandbox (7)كلهم رجّعوا داتا من البوابة المزيّفة
محتاج بيانات (4)كل واحد بيقول بالظبط الناقص إيه
مقفول (1)تقرير خطة KU — بيقول إنه مش متاح على التركيب ده
مش موجود (9)لسه متعملوش — مستنيين الجامعة
صفر 401 وصفر إندبوينت مكتوب «شغال» ورجع فاضي. لو لقيت واحد بيخالف الجدول ده، ابدأ بالتوكن وبالحساب — الاتنين دول سبب أغلب المفاجآت.

سكاشن فاضية sections: [] — سببين مختلفين تمامًا

الموبايل تيم لقوا GetNewCourses بيرجّع مقرر ومعاه sections: []. مقرر تقدر تسجّله من غير سكشن تختارها — ده مالوش معنى. طلعوا سببين، واحد اتصلّح والتاني حدود داتا.

السببالتفاصيل
عدّاد المقاعد بايظ
اتصلّح
SC_SCHEDULE_DTL.NO_STUDENTS عدّاد بيتحدّث مع كل تسجيل، وكان مايتحدّثش — فـكل سكاشن الفصل المفتوح (565) كانت قارية «مليانة»، والاستعلام بيستبعد المليان. اتحسب من الحقيقة (ED_STUD_GROUP): 484 سكشن اتصلّحت، و480 بقى فيها أماكن.
خطة الكلية ناقصة
حدود داتا
حتى بعد التصليح، طلبة العلاج الطبيعي (كلية 1) لسه بيرجّعوا سكاشن فاضية لأي مقرر، وطلبة الهندسة (كلية 6) بيرجّعوا تمام: 6 مقررات و18 سكشن. يعني الإندبوينت سليم — خطة/حزم كلية 1 في الداتابيز دي مش بتوصل لسكاشن. (استبعدنا: السعة، النوع، حجز السكاشن، فلاجات السكشن، سنة الحزمة، ومجموعات السكاشن.)
اتحلّت — والدليل كله بقى على طالب واحد: السبب الحقيقي ماكانش في الإندبوينت ولا في الحزم: جدول كلية العلاج الطبيعي للترم ده كان متعمول بالكامل بس ماتعتمدش (ED_OFFERING.IS_APPROVED = 0)، وMOB_GET_STD_CRS_REG_PLN_SEC مابيشوفش سكشن إلا لو عرضها معتمد. اعتمدنا الجداول (٥ عروض)، وفتحنا السكاشن المبنية أصلاً للتسجيل الذاتي (ONLINE_FLG = 1 — الطالب مابيشوفش غيرها)، فبقى عند الطالب ٣٠ مقرر من خطته و١٢٩ سكشن. يعني كل الشاشات دلوقتي بتشتغل بحساب واحد: mahmoued.eprahim.23010062 — وده الحساب اللي الديمو كله ماشي عليه.

القاعدة اقرا الرسالة قبل ما تدوّر في الكود

الـ SIS بيتكلم. «التسجيل غير متاح حاليا»، «المحاضرة السابقة غير معتمدة»، «تم الاعتماد مسبقا»، «Student has no active semester» — كل واحدة فيهم قاعدة شغالة صح، مش باج. الحاجات اللي كانت محتاجة إصلاح فعلي كانت هي بالظبط اللي ماقالتش حاجة ورجّعت ليستة فاضية.

16 بعد اللوجين — إزاي تعرف الشاشات والخصائص المسموحة؟

السؤال ده بيتسأل كتير وكانت إجابته ناقصة: auth/login بيقول لك مين ده، وInfo/Capabilities بيقول لك الجامعة اشترت إيه — لكن ولا واحد فيهم كان بيقول الشخص ده بالذات يشوف أنهي شاشات. فكل تطبيق كان بيكتب خريطة أدوار عنده، والخرايط دي بتفرق عن السيرفس مع الوقت. دلوقتي في نداء واحد بيجاوب.

نداءين الترتيب الصح بعد الدخول

#النداءبيجاوب على
1 POST auth/login مين ده؟ الرد فيه IsStudent + التوكن.
true ← مسار الطالب · false ← مسار عضو هيئة التدريس.
2 POST auth/me جديد وده يشوف إيه؟ بالتوكن. بيرجّع home اللي تفتحها، وscreens[] كل شاشة ومعاها allowed، وfeatures[] المتعلقة بالدور ده بس.

ماتسألش المستخدم هو مين. مفيش «انت طالب ولا دكتور؟» في شاشة الدخول — دي حاجة السيرفس صاحب الإجابة فيها، والمستخدم مالوش إنه يدّعي دور. وكمان ماتبنيش خريطة أدوار في التطبيق: أي خريطة عندك هي نسخة، والنسخ بتفرق عن الأصل.

الرد شكل auth/me

{
  "result": "Ok",
  "isStudent": true,
  "role": "student",
  "home": "home",                      // ← افتح دي بعد اللوجين
  "allowedScreens": ["home","schedule","courses", ...],
  "screens": [
    { "key":"grades",      "titleAr":"نتيجة الفصل", "audience":"Student",
      "feature": null,                  "allowed": true,  "reason": null },
    { "key":"payments",    "titleAr":"الرسوم والمدفوعات", "audience":"Student",
      "feature":"payments",             "allowed": false, "reason":"FeatureBlocked" },
    { "key":"staff-grades","titleAr":"رصد الدرجات", "audience":"Staff",
      "feature":"staffGradeEntry",      "allowed": false, "reason":"OtherRole" }
  ],
  "features": [ { "key":"surveys", "status":"Active", "available":true, "audience":"Student" }, ... ]
}
reasonيعني إيهتعمل إيه في التطبيق
nullمسموحةاعرضها عادي
OtherRoleالشاشة بتاعة الطرف التاني ماتعرضهاش خالص — مش موجودة في تطبيق الشخص ده
FeatureDisabledالجامعة قافلة الخاصية دي اخفيها — مش مشتراة/مش مفعّلة
FeatureBlockedمفعّلة بس ناقصها إعدادات أو دعم من الداتابيز اخفيها كمان. السبب التفصيلي للأدمن مش للتطبيق

لاحظ الفرق بين OtherRole وFeatureDisabled: الأولانية معناها مش بتاعتك أصلاً، والتانية معناها بتاعتك بس مقفولة. لو خلطتهم، هتقول لطالب «الخاصية دي مقفولة» عن شاشة أصلاً مالهاش أي معنى بالنسبة له.

مثال حقيقي نفس النداء بحسابين مختلفين

طالب — mahmoued.eprahim.23010062دكتور — atef.dahesh
homehomestaff-home
شاشات مسموحة27 من 328 من 32
خصائص ظاهرة له319
OtherRole staff-home · staff-courses · staff-advisees · staff-qr · staff-grades كل شاشات الطالب
FeatureBlocked بيتغيّر من تركيب لتركيب. الأرقام فوق من سيرفر التجربة. على تركيب ناقصه بيانات الدفع أو الـ push مثلاً، شاشات زي payments وnotifications بترجع FeatureBlocked والعدد بيقل. وده بالظبط سبب وجود النداء ده — ماتفترضش رقم، اسأل.

الدكتور بيشوف 9 خصائص مش 31 — لأن اللي مش بتاعته مابيتبعتلوش أصلاً. رصد الدرجات مش «خاصية مقفولة» عند الطالب، هي مش خاصية بتاعته.

ليه كده ده نفس اللي البوابة بتعمله

الـ SIS نفسه بيحل نفس المشكلة بنفس الطريقة: صفوف SE_FORM مربوطة بـ SE_CODE_USER_TYPE_ID، فمنيو البوابة جاي من السيرفر مش متكتب في الصفحات. auth/me هو نفس الفكرة للموبايل — قايمة الشاشات داتا، مش حاجة كل كلاينت يخترعها لوحده. لو الجامعة قفلت خاصية بكرة، التطبيق بيعرف من غير نسخة جديدة.

وInfo/Capabilities؟ لسه ليه دور — بس دور تاني

Info/Capabilitiesauth/me
محتاج توكن؟لأ — بيتنادى قبل شاشة الدخولأيوه
بيعرف مين بيسأل؟لأأيوه
بيرجّع إيه التركيب: العميل، الـ sandbox، بوابة الدفع، وحالة كل الخصائص الشاشات والخصائص بتاعة الشخص ده
استخدمه لـ بانر «بيانات تجريبية»، وشكل شاشة الدخول (SSO ظاهر ولا لأ) المنيو، الـ tabs، والراوتر بعد الدخول

الخلاصة تلات سطور تحطهم في التطبيق

const login = await post('auth/login', { UserName, Password });   // مين ده
const me    = await post('auth/me', {}, login.token.data);        // يشوف إيه
open(me.home);                                                    // افتح رئيسيته

// وبعد كده، قبل ما تعرض أي شاشة:
const s = me.screens.find(x => x.key === target);
if (!s || !s.allowed) return;   // reason بيقولك ليه
الديمو بيعمل كده بالظبط. خريطة الأدوار اللي كانت متكتبة جوّه الديمو بقت fallback بس، لسيرفس قديم مابيردش على auth/me. اللي بيتحكم فعليًا هو رد السيرفس.

17 التسجيل — الحكاية من الأول

ده الفصل اللي تقعد تقراه قبل ما تبدأ تبني. مافيش فيه أسماء جداول ولا استعلامات — فيه إيه اللي بيحصل، وبأي ترتيب، والرد بيقولك إيه، وانت تعمل بيه إيه.

الفكرة إحنا بنسجّل إيه بالظبط؟

خد بالك من الحتة دي لأن الفصل كله مبني عليها: الطالب مابيتسجّلش في «مقرر». الطالب بيتسجّل في سكشن من المقرر.

المقرر هو «العلاج الطبيعي للجراحة». السكشن هو الميعاد والمكان والدكتور — «LEC1 يوم السبت ٢:٠٠ إلى ٣:٠٠». المقرر ممكن يكون ليه عشر سكاشن، والطالب بياخد واحد.

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

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

١ رحلة التسجيل — أربع خطوات بالترتيب

الخطوة صفر: هو أصلاً مسموح له يسجّل النهاردة؟

قبل ما تجيب أي داتا، اسأل السؤال ده. مواعيد التسجيل في الـ SIS اسمها أجندة الفصل، وبتختلف من كلية لكلية ومن فصل لفصل — فماينفعش التطبيق يحفظ تواريخ.

0
إيه المسموح دلوقتي؟
POST CourseReg/GetAllowedActions
{ }
بيرجّع Agenda فيها خمس نوافذ: Register · Add · Drop · Withdraw · RegisterSection — كل واحدة true/false. ومعاها MaxCreditHours وCurrentCreditHours.
استخدمها ترسم الشاشة قبل ما تجيب حاجة. لو Agenda.AnyOpen = false اعرض رسالة واحدة «مفيش تسجيل دلوقتي» وخلاص — أحسن من خمس أزرار رمادية.

وفيه فلاج مهم: Agenda.IsActiveStudent. لو false يبقى الطالب موقوف مالياً — وساعتها مفيش أي عملية تنفع مهما كانت النوافذ مفتوحة.

الخطوة الأولى: المقررات اللي مسموح له بيها

1
المقررات المتاحة
POST CourseReg/v2/AvailableCourses
{ }
بيرجّع Courses[] — مش كل مقررات الجامعة، دي المقررات اللي خطة الطالب بتسمح له بيها الفصل ده.

الـ SIS بيعمل الفلترة دي بنفسه: مستوى الطالب، خطته، اللي نجح فيه، اللي رسب فيه ولازم يعيده. انت مش هتفلتر حاجة.

القايمة بترجع فاضية؟ ده مش بالضرورة باج. GetNewCourses بيرجّع المقررات اللي خطة الطالب لسه فيها مكان ليها ومتاحة بسكاشن الترم ده. الاتنين مع بعض. فالقايمة بتبقى فاضية في تلات حالات كلها سليمة:
الحالةتعرفها إزايتعرض للطالب
خلّص خطته طالب متقدّم خد أغلب مقررات خطته، واللي فاضل مش متاح الترم ده «مفيش مقررات زيادة متاحة على خطتك دلوقتي»
وصل سقف الساعات CurrentCreditHours قرّبت من MaxCreditHours «وصلت الحد الأقصى — اسقط مقرر الأول»
الأجندة مقفولة Agenda.Register وAdd الاتنين false «فترة التسجيل مقفولة»
اقرا GetAllowedActions الأول — منها تعرف أنهي حالة من التلاتة، وتعرض رسالة تفرق، بدل «مفيش مقررات» اللي مابتقولش للطالب يعمل إيه.
الطالب اللي الكوليكشن ماشي عليه
الحساب اللي باقي الدليل شغّال بيه (mahmoued.eprahim.23010062) طالب سنة متقدمة: خد ٤٩ من ٧٩ مقرر في خطته، واللي فاضل مفيش منه حاجة معروضة الترم ده — يعني قايمة التسجيل بتاعته فاضية بحق وحقيق، والباقي كله شغّال عليه.

عشان كده فولدرات التسجيل في الكوليكشن بتدخل بحساب تاني: menna.elsayed.25010015 — عنده ٥ مقررات مسجّلة و٢٧ متاح و14/21 ساعة، يعني الدورة كاملة تمشي عليه. نفس الباسورد، ومتغيّر regUserName في الكوليكشن هو اللي بيحدّده.
وهنا أول حاجة لازم تعرضها: الشروط. كل مقرر راجع ومعاه prereqProblem. لو فاضية يبقى تمام. لو فيها كلام، ده سبب مانع جاي من الـ SIS نفسه — يعني المقرر باين في القايمة بس فيه مشكلة تمنع التسجيل.

وماتعرضش النص ده للطالب — هو مفتاح داخلي زي ExamCourseConfilict. الترجمة اللي تعرضها موجودة في flags.BlockedReason على السكشن (جدول الرسايل في آخر الفصل).

الخطوة التانية: سكاشن المقرر

السكاشن جايّة جوّه نفس الرد — مش محتاج نداء تاني. كل مقرر جوّاه sections[]، وكل سكشن جوّاه flags.

Courses[] → {
  ED_COURSE_ID, COURSE_CODE, COURSE_DESCR_AR, CREDIT_HOURS,
  prereqProblem,
  sections: [ {
      SC_SCHEDULE_DTL_ID,          ← ده اللي هتبعته وانت بتسجّل
      GROUP_ORDER_DESCR_EN,        ← اسم السكشن: "LEC1"
      TCHNG_AR,                    ← طريقة التدريس: "محاضرة"
      Dayes, FROM_TIME, TO_TIME,   ← الميعاد
      NO_STUDENTS, NO_STUD_WAIT,   ← السعة وسعة الانتظار
      IS_CLOSED, WAITLIST_FLG,
      flags: { … }                 ← القرار جاهز
  } ]
}

الخطوة التالتة: حالات السكشن — دي أهم حتة في الفصل

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

الحالةبتعرفها إزايتعرض إيهالزرار
✅ فاضي ومتاح flags.CanRegister = true الميعاد + SeatsLeft «فاضل ٣٠ مكان» «سجّل»
🎟️ مليان بس فيه طابور flags.CanAddSeat = true
(IsFull وWaitLeft > 0)
«مليان — فاضل WaitLeft مكان في الانتظار» «احجز مقعد»
🚫 مليان وخلاص IsFull = true وWaitLeft = 0 «مليان» — رمادي مفيش
⏰ متعارض HasClash أو HasExamClash «بيتعارض مع محاضرة تانية» مفيش
🔒 مقفول IsClosed = true «مقفول من الكلية» مفيش
القاعدة الذهبية: CanRegister وCanAddSeat عمرهم ما بيبقوا true مع بعض. واحد منهم بس هو اللي شغّال — فالكارت بيرسم زرار واحد، مش اتنين.

ولو الاتنين false، بص على flags.BlockedReason واعرضه. ماترمّدش الزرار وتسكت.
عدّاد الأماكن — خد بالك من قيمة واحدة: SeatsLeft = -1 مش معناها صفر، معناها السكشن من غير سقف أصلاً (بياخد أي عدد). اعرضها «مفتوح» مش «فاضل -1 مكان». نفس الكلام على WaitLeft = -1.

الخطوة الرابعة: سجّل — وبعدين اتأكد

2
سجّل المقرر
POST CourseReg/RegisterNewCourse
{ EdCourseId, ScScheduleDtlId, CourseCode, CreditHours }
الرد: Status: true — أو false ومعاه Error بالسبب.
⚠️ ابعت ScScheduleDtlId دايمًا. من غيره الطالب هيتسجّل في المقرر ومش هيبقى في أي سكشن — يعني مالوش ميعاد ولا هيظهر في جدوله. دي الحالة اللي اتكلمنا عنها فوق، بس حصلت بالغلط.
3
اتأكد إنه بان
POST CourseReg/v2/MyCourses
{ }
دوّر على المقرر في Courses[] — لازم تلاقيه بـ ED_CODE_COURSE_STATUS_ID = 1 وsections[] مش فاضية.
ليه الخطوة دي مش رفاهية؟ عملية بترد true ومابتظهرش في القايمة هي أخطر عطل ممكن — مافيش فرق بينها وبين النجاح وقت النداء، وماتبانش غير لما الطالب يقول «أنا سجّلت ومش لاقي المقرر». اقرا بعد كل كتابة.

٢ المقرر اللي ليه أكتر من طريقة تدريس

خد SUR310 من الديمو. الطالب مسجّل فيه بـ تلات سكاشن:

SUR310 → sections: [
  { GROUP_DESCR_EN: "LEC",        TCHNG_AR: "محاضرة"  }
  { GROUP_DESCR_EN: "SEC 1",      TCHNG_AR: "عملى"    }
  { GROUP_DESCR_EN: "HOSPITAL 1", TCHNG_AR: "اكلينكى" }
]

ده الطبيعي مش استثناء — ٤٨٪ من التسجيلات في الداتابيز فيها أكتر من سكشن. وبيترتّب عليه تلات حاجات في الشاشة:

  • اعرض السكاشن التلاتة تحت المقرر، وكل واحد باسم طريقة تدريسه (TCHNG_AR) — مش رقم لوحده.
  • زرار «حذف» لكل سكشن، مش زرار واحد للمقرر. لو بعتّ المقرر لوحده، الخدمة هي اللي تختار — وغالبًا مش اللي الطالب دوس عليه.
  • «غيّر السكشن» بيبدّل زي بزي: المحاضرة بمحاضرة والعملي بعملي. الخدمة بترجّع لك المرشّحين من نفس النوع بس — وكل مرشّح كاتب ReplacesEdStudGroupId يعني هيقف مكان أنهي سكشن بالظبط.
وده كان بيسبّب باج حقيقي: قبل كده كانت الخدمة ممكن تعرض سكشن معمل كبديل لـمحاضرة — ولو الطالب داس، محاضرته كانت تتنقل جوّه ميعاد معمل. دلوقتي مقفولة.

وكل سكشن بيجيلك ومعاه ميعاده ومكانه

«إنت في SEC 1» معلومة ناقصة. الطالب فاتح الشاشة عشان يعرف إمتى وفين. عشان كده كل سكشن في sections[] بيجي ومعاه:

sections[] → {
  GROUP_DESCR_EN: "SEC 1",  DESCR_AR: "عملى",

  DaysAr / DaysEn:  "الأحد"                    ← اليوم (أو الأيام)
  FromTime / ToTime: "11:00" / "13:00"         ← أول بداية وآخر نهاية
  HallAr / HallEn:   "معمل 107"                ← القاعة
  BuildingAr / …:    "مبني كلية العلاج الطبيعي" ← المبنى
  CampusAr / …:      "الحرم الجامعى"            ← الفرع

  meetings: [ … ]   ← التفصيل يوم بيوم
}
ليه فيه meetings ومعاها ملخّص فوقيها؟ لأن السكشن بيتقابل أكتر من مرة في الأسبوع، والمرات دي مش زي بعض — المعمل ممكن يكون في قاعة مختلفة ومبنى مختلف كل يوم.

· في صف القايمة اعرض الملخّص (DaysAr + الميعاد + القاعة) — سطر واحد يكفي.
· في شاشة التفاصيل لُفّ على meetings واعرض كل يوم بقاعته.

والملخّص أمين: لو السكشن في أكتر من قاعة، HallAr بيرجّعهم كلهم مفصولين بفاصلة — مش بيختار واحدة ويسكت.
مثال حقيقي من الديمو: BSM131 سكشن 01 بيتقابل يومين: السبت ٩:٠٠–١٠:٤٠ والأربعاء ١١:٠٠–١٣:٠٠، الاتنين في Lecture hall E1.1 بمبنى الهندسة. فالملخّص بيقول «السبت، الأربعاء · 09:00–13:00» — وmeetings فيها السطرين بالظبط.
ولو السكشن لسه مالوش جدول؟ المفاتيح بتيجي موجودة بقيمة null وmeetings بتبقى [] — يعني ماتشيكش على وجود الحقل، شيك على قيمته. واعرض «الميعاد لسه متحطّش» بدل ما تسيب الصف فاضي.

٣ حذف سكشن — مش إسقاط المقرر

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

1
حذف السكشن
POST CourseReg/DeleteSection
{ EdCourseId, EdStudGroupId }
ابعت EdStudGroupId بتاع السكشن اللي الطالب دوس عليه — جايلك في sections[].ED_STUD_GROUP_ID. والرد بيقولك الفراغ اللي عمله: NeedsSection، وMissingTeachingMethod (محاضرة؟ عملي؟)، وNextAction فيها النداء الجاي جاهز.
2
اختار البديل
POST CourseReg/GetSectionsToReplace
{ EdCourseId }
نفس البيكر — بس دلوقتي العنوان اللي بيرجع «اختار سكشن» مش «انقل لسكشن تاني»، وكل مرشّح جايلك بـ Mode: "PICK".
3
خد السكشن
POST CourseReg/SetSection
{ EdCourseId, ScScheduleDtlId }
نفس النداء في كل الحالات. الخدمة هي اللي بتقرّر: عنده سكشن من النوع ده؟ يبقى نقل. مالوش؟ يبقى إضافة. والرد بيقولك عمل إيه في Action: ADDED · REPLACED · UNCHANGED (كان فيها أصلاً).
ماتقررش انت — ماتقدرش أصلاً. الفرق بين «نقل» و«إضافة» بيعتمد على إن الطالب عنده دلوقتي سكشن من النوع ده ولا لأ — وده حال الداتابيز لحظة الدوسة، مش حال الشاشة اللي رسمت الزرار. عشان كده البيكر بقى بيوجّه كل أزراره على CourseReg/SetSection بنفس الـ body، والفرق فضل في الكلام بس (Mode بيقولك تكتب «انقل هنا» ولا «خد السكشن ده»).

ReplaceSection وAddSection لسه شغّالين وبنفس الـ contract — بس بقوا نفس النداء، فمابقاش فيه واحد فيهم بيرفض نص الشغلانة اللي بتاعة التاني.

وبعد خطوة ١ الطالب بيبقى في الحالة اللي قلنا عليها في الأول: مسجّل في المقرر ومن غير ميعاد. وده اللي لازم تعرضه:

اللي بيرجعيعني إيهتعرض إيه
NeedsSection: trueناقصه سكشن من نوع معيّن علامة «من غير سكشن محاضرة» + زرار «اختار سكشن»
MissingTeachingMethodالنوع الناقص وكام مرشّح ليه سمّي النوع في الرسالة — «اختار محاضرة تاني»
RemainingSections: 0مفيش ولا ميعاد خالص علامة «من غير سكشن» على صف المقرر
flags.CanDrop: trueالمقرر لسه مسجّل «أسقط المقرر» يفضل ظاهر كمان
وفي شاشة «مقرراتي» مش محتاج تحسبها بنفسك: v2/MyCourses بقى بيدّي على كل مقرر needsSection وmissingTeachingMethods، والـ actions فيها pickSection جاهز. ودي بتشتغل كمان على المقرر اللي ليه محاضرة وعملي واتحذف العملي بس — لسه عنده سكشن، وبرضو ناقص.
ولو الطالب عايز يإسقاط المقرر كله؟ يبقى الزرار الصح هو «أسقط المقرر» — العملية اللي بعد كده. «حذف السكشن» عمرها ما هتإسقاط مقرر.

٤ أسقط المقرر (Drop) — ومعناه إيه

الإسقاط معناه: المقرر بيتلغي من فصل الطالب، وساعاته بترجع له فيقدر يسجّل غيره. بس فيه حتة مهمة: المقرر مابيتمسحش — بيتعلّم عليه إنه «مسقَط».

1
احذف
POST CourseReg/DropCourse
{ EdCourseId }
شرطه flags.CanDrop = true — يعني نافذة الإسقاط مفتوحة والمقرر نشط.
2
اقرا تاني
POST CourseReg/v2/MyCourses
{ }
المقرر لسه في القايمة بـ flags.IsDropped = true وED_CODE_COURSE_STATUS_ID = 6.
ماتحذفوش من الشاشة! ده مقصود إنه يفضل ظاهر. اعرضه بعلامة «مسقَط»، وحوّل زراره لـ «تراجع» — لأن ده الطريق الوحيد لإرجاعه (شوف اللي بعده).

وفي رسالة التأكيد قبل الإسقاط، قول للطالب إنه بيترجع. ده بيريّحه، وبيمنع سؤال الدعم الفني اللي بييجي بعد كده على طول.

٥ التراجع عن الإسقاط — ومنين بتجيبه

سؤالك: «الطالب لو حب يرجّعه بيجيبه منين؟» الإجابة: مش من قايمة المتاح للتسجيل.

المقرر المسقَط مش بيرجع في GetNewCourses. استعلام الخطة بيستبعد أي مقرر ليه تسجيل في الفصل ده مهما كانت حالته. فلو التطبيق حاول يسجّله من جديد، مش هيلاقيه أصلاً.

الطريق الوحيد هو التراجع، وبتلاقيه في نفس قايمة مقرراتي.

إزاي تعرف إن المقرر ينفع يرجع؟ من الفلاج:

1
مقرراتي
POST CourseReg/v2/MyCourses
{ }
دوّر على flags.CanUnDrop = true — دي المقررات اللي تقدر ترجّعها. (بتبقى true لما نافذة الإضافة تكون مفتوحة والمقرر حالته مسقَطة.)
2
السكاشن اللي أرجع لها
POST CourseReg/GetSectionsToReplace
{ EdCourseId }
لأن الإسقاط حذف السكاشن، فالتراجع محتاج يعرف هيقعد فين. الرد فيه السكاشن المتاحة بنفس الـ flags اللي اتعوّدت عليها.
3
تراجع
POST CourseReg/UnDropCourse
{ EdCourseId, ScScheduleDtlId }
ابعت السكشن اللي اختاره — زي التسجيل بالظبط.
4
اتأكد
POST CourseReg/v2/MyCourses
{ }
المقرر رجع IsDropped = false وفي سكشن.
ليه بتختار سكشن تاني؟ لأن الإسقاط حذف السكشن القديم فعلاً. وممكن السكشن اللي كان فيه اتملى في الوقت ده — فالتراجع مش «رجوع بالزمن»، ده رجوع بمكان جديد. قول كده في الشاشة.

٦ غيّر السكشن — نفس المقرر، ميعاد تاني

الطالب مسجّل في المقرر ومبسوط، بس الميعاد مش مناسبه. مش عايز يسيب المقرر، عايز يقعد في سكشن تاني من نفس المقرر.

وهنا فيه سؤالين مختلفين، وخد بالك من الفرق لأنه بيلخبط: «هل ينفع أغيّر أصلاً؟» غير «أغيّر لأنهي سكشن؟». الأول بيتجاوب من فلاج على المقرر، والتاني محتاج نداء لوحده.

1
هل ينفع أصلاً؟
POST CourseReg/v2/MyCourses
{ }
المقرر اللي عليه flags.CanReplaceSection = true هو اللي ينفع. ومعاه alternativeSectionCount — فيه كام سكشن بديل.
العدد ده مش عدد سكاشن المقرر. ده عدد السكاشن من نفس نوع السكشن اللي هو فيه واللي هو مش فيها. يعني لو المقرر فيه محاضرة واحدة وخمس معامل، والطالب بيسأل عن محاضرته — العدد هيبقى صفر، والزرار مش هيظهر. وده صح: مفيش محاضرة تانية يروح لها.
2
أغيّر لأنهي سكشن؟
POST CourseReg/GetSectionsToReplace
{ EdCourseId, EdStudGroupId }
ابعت EdStudGroupId بتاع السكشن اللي الطالب دوس عليه. الرد بييجي مقسّم على أربع قوايم جاهزة + Summary بالأعداد.
القايمةمعناهاالزرار المناسب
Sections فاضي ومفيهوش تعارض — تقدر تروح له دلوقتي «انقل هنا»
WaitListSections مليان بس الطابور مفتوح «احجز مقعد»
ConflictingSections فيه مكان، بس بيتعارض مع محاضرة تانية في جدوله «شوف التعارض»
FullSections مليان ومفيش انتظار رمادي، من غير زرار
الأعداد مطابقة للقوايم — بالتصميم. Summary.Available = طول Sections بالظبط، وTotal = مجموع الأربعة. فتقدر تكتب «فيه ٢ متاحين و٣ متعارضين» من Summary على طول من غير ما تلف على المصفوفات.
وكل صف بيقولك هو «نقل» ولا «اختيار» — Mode. REPLACE = الطالب عنده سكشن من النوع ده والصف ده يقف مكانه («انقل هنا»)، وPICK = مالوش سكشن من النوع ده خالص («خد السكشن ده»). الفرق في الكلام بس — النداء واحد: CourseReg/SetSection، والـ actions بتاعة الصف جايالك بيه وبالـ Body جاهز.

وفوق القوايم فيه NeedsSection وMissingTeachingMethods: لو true، العنوان اللي الخدمة بترجّعه بيبقى «اختار سكشن» بدل «انقل لسكشن تاني» — اعرضه زي ما هو.

التعارض بيجيلك باسم صاحبه — دي أهم حتة

لما سكشن يبقى متعارض، الرد مابيقولكش «متعارض» وخلاص. بيقولك متعارض مع مين:

"Conflict": {
  "Type":              "TIME_CLASH",
  "WithCourseCode":    "BSM131",       ← المقرر اللي بيتعارض معاه
  "WithSectionNameEn": "01",           ← وسكشنه
  "WithDaysAr":        "الأربعاء, السبت",
  "WithFromTime":      "09:00",
  "WithToTime":        "13:00",
  "WithEdStudGroupId": 135172,         ← سكشن الطالب نفسه في المقرر ده
  "CanResolve":        true            ← يعني هو يقدر يحلّها بنفسه
}

يعني الرسالة اللي تعرضها مش «مينفعش». الرسالة هي: «السكشن ده بيتعارض مع محاضرة BSM131 يوم الأربعاء ٩–١»، وجنبها زرار «روح احذف سكشن BSM131» — وWithEdStudGroupId هو الرقم اللي هتبعته. الطالب يحلّها ويرجع يكمّل. ده الفرق بين رسالة خطأ ورسالة فيها حل.

CanResolve هو اللي بيقرّر تعرض الزرار ولا لأ. true = التعارض مع حاجة الطالب مسجّل فيها فيقدر يحلّها. false = حاجة مالوش فيها يد، اعرض السبب وبس من غير ما توعده بحل.
3
انقل
POST CourseReg/SetSection
{ EdCourseId, ScScheduleDtlId }
نفس النداء بتاع الاختيار — الخدمة بتشوف إن الطالب عنده سكشن من نوع ده فتنقله. الـ EdStudGroupId اختياري ومالوش لازمة غير في حالة واحدة: مقرر الطالب فيه أكتر من سكشن من نفس النوع. والرد بيقول Action: "REPLACED".
وحتى بعد ما تدوس، النقل ممكن يترفض. حد تاني ممكن يكون خد المكان في اللحظة دي. لو رفض بسبب تعارض، الرد بيرجّع نفس بلوك Conflict — فاعرضه بنفس شاشتك، مش برسالة «حصل خطأ».
ولو الرد CanReplace: false؟ الـ Reason بيقول ليه، وكل واحد فيهم رسالة مختلفة للطالب: NO_ALTERNATIVE (مفيش سكشن تاني من نوعه) · ALL_FULL (فيه بدايل بس كلها مليانة) · ALL_CONFLICT (كلها متعارضة — وده اللي ينفع يعمله حاجة) · NONE_AVAILABLE (خليط) · GROUPED_REGISTRATION (السكشن مربوط بمجموعة، التغيير من شؤون التسجيل).

التلاتة الأولانيين دول كانوا كلهم قايمة فاضية قبل كده — والطالب ماكانش يعرف يستنى ولا يفضّي جدوله ولا ياخد باله إن مفيش أصلاً.

٧ احجز مقعد — لما السكشن يبقى مليان

السكشن اللي عايزه مليان. بدل ما التطبيق يقوله «مش متاح» ويقفل الموضوع، الـ SIS بيسمح إنه يقف في الطابور — ولما حد يسيب مكانه، مسؤول التسجيل بيدخّله.

الزرار بيظهر لما flags.CanAddSeat = true — وده بيبقى true في حالة واحدة بس: السكشن مليان والطابور مفتوح وفيه مكان.

وعشان كده CanRegister وCanAddSeat عمرهم ما بيبقوا true مع بعض. لو فيه مكان → «سجّل». لو مليان والطابور مفتوح → «احجز مقعد». الكارت بيرسم زرار واحد.

وقبل ما يدوس، وريه هو واقف فين:

الفيلدمعناه
WaitCapacityطول الطابور اللي الكلية سامحة بيه
WaitTakenواقف فيه كام واحد دلوقتي
WaitLeftفاضل كام مكان · و-1 يعني الطابور من غير سقف
2
احجز
POST CourseReg/SetSection
{ EdCourseId, ScScheduleDtlId }
الرد فيه WaitNo — رقم دور الطالب. اعرضه له، ده أهم حاجة عايز يعرفها.
3
اتأكد
POST CourseReg/v2/MyCourses
{ }
المقرر بيظهر في «مقرراتي» بـ flags.IsWaiting = true وED_CODE_COURSE_STATUS_ID = 7.
وخد بالك: ده مش تسجيل. الطالب مش في المقرر — هو في طابور المقرر. يعني:
· ساعاته مش محسوبة عليه في السقف
· المحاضرة مش هتظهر في جدوله
· ومحدش بيضمن له إنه هيدخل

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

٨ اخرج من الانتظار — ألغِ الحجز

الطالب غيّر رأيه، أو لقى سكشن تاني فاضي، أو الدور طويل ومش مستني. عايز يسيب الطابور.

الزرار بيظهر لما flags.CanCancelWait = true — يعني المقرر حالته انتظار (7).

1
اخرج من الطابور
POST CourseReg/CancelWaitList
{ EdCourseId }
2
اتأكد
POST CourseReg/v2/MyCourses
{ }
المقرر بيختفي من القايمة خالص.
وهنا فرق مهم عن الإسقاط: المقرر بيختفي فعلاً — مش بيفضل بعلامة زي المقرر المسقَط.

وده منطقي: ده ماكانش تسجيل من الأصل عشان يتساب له أثر. ده كان دور في طابور، والطالب خرج منه. فمافيش «تراجع» هنا — لو غيّر رأيه تاني، بيحجز من الأول (ولو الطابور كان اتملى في الوقت ده، يبقى فات عليه).
قول له كده في رسالة التأكيد. «لو خرجت من الطابور، مكانك هيروح لغيرك ومش هتقدر ترجع لنفس الدور» — دي معلومة الطالب لازم يعرفها قبل ما يدوس، ومش هيعرفها لوحده.

٩ بدّل مقرر بمقرر — العملية الوحيدة اللي فيها اتنين مع بعض

الطالب عايز يستغنى عن مقرر ويحط غيره مكانه. مثلاً سقف ساعاته اتملى، أو لقى مقرر أنسب لخطته.

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

وساعتها القديم بقى مسقَط، وهو مش هيلاقيه في «المتاح للتسجيل» عشان يرجّعه (شوف قسم التراجع فوق). يعني حطّيته في مشكلة.
1
المقرر اللي هيستغنى عنه
POST CourseReg/v2/MyCourses
{ }
أي مقرر عليه flags.CanDrop = true ينفع يتبدّل.
2
المقرر الجديد وسكشنه
POST CourseReg/v2/AvailableCourses
{ }
دوّر على سكشن عليه flags.CanRegister — مش على مقرر. المقرر ممكن يكون متاح وكل سكاشنه مليانة.
3
بدّل
POST CourseReg/ReplaceCourse
{ FromEdCourseId, ToEdCourseId, ScScheduleDtlId, CourseCode, CreditHours }
نداء واحد. يا الاتنين يحصلوا يا ولا واحد.
4
اتأكد
POST CourseReg/v2/MyCourses
{ }
القديم بقى IsDropped = true، والجديد ظهر مسجّل وفي سكشن.
القديم بيتعمله إسقاط، مش مسح. يعني بيفضل ظاهر بعلامة «مسقَط» وعليه زرار «تراجع». وده مقصود: لو الطالب ندم على التبديل، لسه قدامه طريق.
وحاجة لطيفة في حساب الساعات: الخدمة بتحسب الفرق مش وزن المقرر الجديد لوحده. يعني لو بيبدّل مقرر ٣ ساعات بمقرر ٣ ساعات، مافيش زيادة — فالتبديل مابيترفضش حتى لو الطالب واصل للسقف. وده صح: هو مش بيزوّد حمله، هو بيغيّره.
الرفضات اللي الخدمة بتشوفها قبل ما تلمس حاجة:
· نفس المقرر على الجهتين → «ده تغيير سكشن، استخدم ReplaceSection»
· المقرر القديم مش مسجّل أصلاً
· المقرر الجديد مسجّل بالفعل
· السكشن المبعوت بتاع مقرر تاني
· ومن غير سكشن خالص → «لازم تختار سكشن»

وكلهم قبل أي كتابة — يعني بعد أي رفض فيهم، المقرر القديم لسه مكانه بالظبط. فيه فولدر في الكوليكشن بيجرّب الخمسة دول واحدة واحدة وبعدين بيعمل التبديل الحقيقي، عشان تشوف بعينك إن «يا الاتنين يا ولا واحد» شغّالة.
ورفض معيّن هتقابله: لو المقرر الجديد ليه متطلب مصاحب (مقرر لازم يتاخد معاه) والطالب مش مسجّل فيه، الخدمة بترفض وبتقولك اسم المقرر الناقص — ومابتسجّلوش نيابة عنه. وده مقصود: التسجيل التلقائي يعني الطالب اتحط في سكشن ماشفهوش. اعرض له الاسم وسيبه هو يسجّله ويختار ميعاده.

١٠ اطلب انسحاب — العملية الوحيدة اللي مابتنفّذش

الطالب بدأ المقرر فعلاً، حضر محاضرات، وبعدين لقى نفسه مش قادر يكمّله. مش عايز إسقاط — الإسقاط ليه ميعاد وفات. هو عايز ينسحب.

وده مختلف عن كل اللي فات في حاجة جوهرية: كل العمليات اللي فاتت بتحصل على طول. الانسحاب لأ.

الطالب بيقدّم طلب. والجامعة هي اللي بتنفّذه بعد الموافقة.

الزرار بيظهر لما المقرر يكون نشط (حالة 1) وAgenda.Withdraw مفتوحة. مش على المقرر المسقَط ولا اللي في الانتظار.

1
اطلب
POST students/RequestCourseWithdrawal
{ ED_COURSE_ID, NOTES, CANCEL: false }
الرد فيه requestId — رقم الطلب.
⚠️ أخطر فخ في الفصل كله: لو عملت refresh لـ«مقرراتي» بعد النداء ده وانت مستني المقرر يتشال أو يتعلّم عليه — مش هيحصل حاجة. المقرر هيفضل نشط زي ما هو، وهتفتكر إن النداء فشل وهو نجح.

الصح: بعد النجاح اقفل الشيت، ووَدّي الطالب على شاشة «طلباتي»، وماتلمسش صف المقرر خالص.
2
الطلب فين
POST students/GetCourseWithdrawals
{ }
ده مكان عرض الطلب — شاشة «طلباتي»، مش شاشة التسجيل.
3
ألغِ الطلب
POST students/RequestCourseWithdrawal
{ ED_COURSE_ID, CANCEL: true }
الطالب يقدر يسحب طلبه طول ما هو لسه تحت الموافقة. بعد الموافقة خلاص.

وطيب لما الجامعة توافق، بيحصل إيه؟

الإسقاطالانسحاب
مين بينفّذهالطالب على طول الجامعة بعد الموافقة
الساعاتبترجع له بتفضل محسوبة كـ attempted
في السجلمفيش أثر — كأنه ما سجّلش بيفضل بتقدير W
بيترجع؟آه — «تراجع» لأ بعد الموافقة
وعشان كده حطّه في شاشة تانية خالص. قرار دائم بيسيب علامة في سجل الطالب للأبد — ماينفعش يبقى جنب زرار بيترجع. وخلّي رسالة التأكيد صريحة: «المقرر هيفضل في سجلك بتقدير W».

١١ display وactions — الرد جاهز للرسم

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

أربع تطبيقات بيعملوا ده = أربع فرص إن نفس الزرار يتسمّى باسمين مختلفين، وواحد فيهم هيغلط في الـ body على مقرر ليه أكتر من سكشن.

عشان كده كل صف بقى جايله كمان display (النصوص جاهزة) و actions (الأزرار، وكل زرار معاه الإندبوينت والـ body بالظبط). والحقول القديمة كلها مكانها زي ما هي — ده إضافة، مش تغيير.

شكل display

"display": {
  "Title":     "SUR310",
  "Subtitle":  "العلاج الطبيعى للجراحة العامة",
  "Meta":      "4 ساعة",
  "Place":     "معمل 107 · مبني كلية العلاج الطبيعي",   ← على السكشن
  "Badge":     "مسقَط",
  "BadgeTone": "danger",     ← neutral · success · warning · danger
  "Note":      "فترة الإسقاط مقفولة"
}
BadgeTone معنى، مش لون. الخدمة بتقولك الشارة معناها إيه — والألوان تفضل عندك في التصميم. ماينفعش الـ API يبعت #FEE2E2.

وشكل actions — وده الجزء اللي بيوفّر شغل حقيقي

"actions": [
  { "Key": "replaceSection", "Label": "غيّر السكشن", "Tone": "secondary",
    "Enabled": true,
    "Endpoint": "CourseReg/GetSectionsToReplace",
    "Body": { "EdCourseId": 46, "EdStudGroupId": 0 } },

  { "Key": "deleteSection",  "Label": "احذف SEC 1 · عملى", "Tone": "secondary",
    "Endpoint": "CourseReg/DeleteSection",
    "Body": { "EdCourseId": 46, "EdStudGroupId": 124534 } },   ← السكشن بالظبط

  { "Key": "drop", "Label": "أسقط المقرر", "Tone": "danger",
    "Endpoint": "CourseReg/DropCourse",
    "Body": { "EdCourseId": 46 } }
]

يعني التطبيق بقى بيعمل ده وخلاص:

actions.map(a => <Button tone={a.Tone} disabled={!a.Enabled}
                    onClick={() => post(a.Endpoint, a.Body)}>
                    {a.Label}
                  </Button>)
وده بيقفل أكتر باج اتكرر معانا: المقرر اللي ليه تلات سكاشن بيجيله تلات أزرار حذف، كل واحد فيه EdStudGroupId بتاع سكشنه. مافيش تخمين، ومافيش «الخدمة تختار».
الـ Key ثابت — اتعامل بيه مش بالنص. register · addSeat · cancelWait · drop · undrop · replaceSection · deleteSection · pickSection.

معظمهم بتنده الـ Endpoint على طول. اتنين بس بيفتحوا شاشة بدل ما يبعتوا: replaceSection وpickSection — دول بيرجّعوا بيكر السكاشن.
واللغة؟ ابعت "LANG": "en" في الـ body وكل نصوص display و actions هتيجي إنجليزي. من غيره بتيجي عربي. الحقول الخام (_AR و_EN) بتيجي الاتنين دايمًا — اللغة بتأثر على النصوص المركّبة بس.
والـ flags لسه مكانها. لو محتاج تبني شكل مختلف عن اللي الخدمة اقترحته، الفلاجز والحقول الخام كلها موجودة. display وactions اختصار، مش قيد.

١٢ جدول الرسايل — اعرض اللي جايلك

كل رفض بيجي ومعاه سبب بالاسم في flags.BlockedReason أو في Error. ترجمهم مرة واحدة واعرضهم بدل «حصل خطأ»:

السبباعرض للطالبوبعدين؟
WINDOW_CLOSEDالفترة مقفولة دلوقتيمفيش — استنى
SECTION_FULLالسكشن مليانشوف لو الطابور مفتوح
SECTION_CLOSEDالسكشن مقفول من الكليةاختار سكشن تاني
TIME_CLASHبيتعارض مع محاضرة تانيةاعرض مع مين ووَدّيه يحذفه
EXAM_CLASHامتحانه مع امتحان مقرر تانياعرض مع مين
MAX_LOADهتعدّي حد الساعاتيإسقاط مقرر الأول
ALREADY_REGISTEREDمسجّل فيه بالفعلمفيش
INCOMPLETE_COURSEفيه محاولة سابقة لسه مفتوحةشؤون التسجيل
PREREQ_NOT_METالمتطلب السابق مش مكتملمفيش
STUDENT_BLOCKEDحسابك موقوفشؤون الطلاب
وأهم عادة تكتسبها من الفصل ده كله: ماتحسبش، اقرا. ماتحسبش أماكن، ولا تعارضات، ولا مواعيد فتح. كل ده جايلك محسوب في flags — وأي شرط تكتبه انت هيختلف عن الجامعة أول ما مسؤول التسجيل يغيّر ميعاد.

وبعد أي عملية كتابة، اقرا القايمة تاني واتعامل مع اللي راجع — ماتعدّلش النسخة اللي عندك من عندك.

18 دورة تسجيل المقررات — بالتفصيل

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

الدورة كل عملية في فولدر لوحدها

الكوليكشن كان سكريبت واحد مترتب: تضغط Run ويمشي الدورة كلها. ده أثبت إنها شغالة، بس مش الشكل اللي يفيد حد بيبني شاشة واحدة — كان لازم يقرا ١٧ خطوة عشان يلاقي الأربعة اللي تخصّه، ولو حب يشغّلهم لوحدهم لازم يعرف أنهي خطوة قبلهم بتحطّ أنهي متغيّر.

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

#الفولدرالخطوات
1تسجيل مقرر جديد دخول ← المسموح ← المتاح وسكاشنه ← سجّل ← 🔎 تأكيد إنه في القايمة بحالة 1 وبسكشن
2تغيير سكشن دخول ← مقرراتي ← GetSectionsToReplace ← انقل ← 🔎 تأكيد إن السكشن اتغيّر
3حذف سكشن دخول ← مقرراتي ← حذف ← 🔎 تأكيد إن المقرر لسه مسجّل والفلاجز اتقفلت
4حجز مقعد + الخروج منه دخول ← سكشن مليان ← احجز ← 🔎 حالة 7 ← اخرج ← 🔎 اختفى
5إسقاط مقرر دخول ← مقرراتي ← احذف ← 🔎 تأكيد إن الحالة بقت 6 والصف موجود
6التراجع عن الإسقاط دخول ← مقرراتي ← السكاشن ← تراجع ← 🔎 تأكيد إنه رجع 1 وفي سكشن
7بدّل مقرر بمقرر — كل الحالات دخول ← مقرراتي ← المتاح ← ٤ رفضات متوقّعة ← 🔎 لسه محصلش حاجة ← بدّل ← 🔎 القديم مسقَط والجديد مسجّل ← ترجيع
8طلب انسحاب دخول ← مقرراتي ← اطلب ← 🔎 مقرراتي ما اتغيّرتش ← ألغِ الطلب
9قراءات مرجعية النداءات اللي بتقرا بس — شغّلها في أي وقت من غير ما تغيّر حاجة
🔎 معناها القراءة بعد الكتابة. عملية بترد Status: true ومابتظهرش في أي شاشة هي أخطر عطل في الخدمة دي: مافيش فرق بينها وبين النجاح وقت النداء، وماتبانش غير لما الطالب يقول «أنا سجّلت ومش لاقي المقرر». عشان كده كل فولدر بيقفل بقراءة بتتأكد إن اللي اتكتب بان فعلًا.
متشغّل ومتأكد منه: كل فولدر لوحده صفر فشل، والكوليكشن كله ٥٧ ريكوست · ٨٨ تحقّق · صفر فشل. وفولدر «التراجع عن الإسقاط» بيإسقاط مقرر بنفسه لو مالقاش مسقَط — عشان يفضل قابل للتشغيل لوحده.
الدورة كلها بحساب الطالب — مفيش ولا نداء لإندبوينت بتاع دكتور. كانت فيه خطوة بتنادي Staff/GetInstructorSectionSolts عشان «تفاصيل السكشن»، واتحذفت: دي نظرة الدكتور للسكشن، والطالب أصلاً بياخد كل التفاصيل جوّه رد «المتاح للتسجيل». لو لقيت نفسك بتنادي مسار بادئ بـ Staff/ في مسار طالب — غالبًا الداتا موجودة عندك بالفعل.
مفيش رقم متكتب بالإيد غير اسم المستخدم. المقرر والسكشن والسكشن البديلة كلهم بيتكتشفوا وقت التشغيل من الفلاجز — السكريبت مابيحسبش أماكن ولا تعارضات ولا مواعيد، بيقرا flags وخلاص. ورقم الطالب من التوكن: الإندبوينتات الجديدة مافيهاش EdStudId أصلاً.

الحالة المقرر بيتنقل بين حالتين — ومابيتمسحش أبدًا

1مسجّل
— DropCourse →
6مسقَط
← UnDropCourse —

الإسقاط مش بيمسح الصف — بيغيّر حالته لـ 6. وده مش تفصيلة شكلية: استعلام الخطة بيستبعد أي مقرر ليه صف تسجيل في الفصل ده مهما كانت حالته، فالمقرر المسقَط مش بيرجع تاني في «المتاح للتسجيل». الطريق الوحيد لإرجاعه هو UnDropCourse. لو التطبيق حاول يسجّله من جديد، مش هيلاقيه أصلاً في القايمة.

دول الحالتين اللي الطالب بيحرّكهم بنفسه من التطبيق. فيه حالتين كمان بتوصلك في الرد — 2 و4 (انسحاب) — بس دول مش من الموبايل: الجامعة هي اللي بتحطّهم. الفرق ده كله في اللي بعده.

أهم فرق «حذف السكشن» ≠ «أسقط المقرر» ≠ «انسحب»

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

✂️
حذف السكشن«مش عايز الميعاد ده»
الزرار
✕ صغيّرة جنب السكشن نفسها — مش جنب المقرر
اعرضه لما
flags.CanDeleteSection
بتنادي
CourseReg/DeleteSection
ومعاها EdStudGroupId بتاع السكشن
بعد النجاح الشاشة تبقى إيه
المقرر يفضل مكانه في «مقرراتي». لو دي كانت آخر سكشن، sections ترجع فاضية. والرد نفسه بيقول NeedsSection وMissingTeachingMethod وNextAction
الخطوة اللي بعدها
GetSectionsToReplace ← AddSection — البيكر بيرجّع مرشّحين بـ Mode: PICK
الطالب يفهم إيه
«لسه مسجّل في المقرر، بس محتاج يختار سكشن»
➜ عمليّة خفيفة — ماتخوّفش الطالب في رسالة التأكيد
🗑️
أسقط المقرر«مش عايز المقرر أصلاً»
الزرار
«أسقط» على صف المقرر
اعرضه لما
flags.CanDrop
بتنادي
CourseReg/DropCourse
بعد النجاح الشاشة تبقى إيه
المقرر يفضل في القايمة بس بـ flags.IsDropped = true — علّم عليه، وحوّل زراره لـ «تراجع» (flags.CanUnDrop)
الطالب يفهم إيه
«المقرر اتلغى وساعاته رجعتلي» — ويقدر يسجّل غيره
➜ بيترجع — قوله كده في رسالة التأكيد عشان يرتاح
📝
انسحب«بدأت ومش قادر أكمّل»
الزرار
«اطلب انسحاب» — في شاشة الطلبات، مش في شاشة التسجيل
اعرضه لما
المقرر نشط ونافذة الانسحاب مفتوحة (Agenda.Withdraw)
بتنادي
students/RequestCourseWithdrawal
بعد النجاح الشاشة تبقى إيه
مافيش أي حاجة تتغيّر في «مقرراتي». الطلب بيروح للجامعة — اعرضه في «طلباتي» بحالة تحت الموافقة
الطالب يفهم إيه
«قدّمت طلب، ولسه مستني الرد» — والمقرر هيبان في سجلّه بتقدير W
➜ قرار دائم — التأكيد هنا لازم يبقى واضح وصريح
أخطر واحدة فيهم للموبايل: الانسحاب مش بيغيّر حاجة على طول. لو ناديت RequestCourseWithdrawal وبعدها عملت refresh لـ «مقرراتي» وانت مستني المقرر يتحذف أو يتعلّم عليه — مش هيحصل، والشاشة هتبان كأن النداء فشل.

الانسحاب طلب، مش تنفيذ. حد في الجامعة لازم يوافق عليه الأول، وساعتها بس المقرر بيتغيّر. يعني بعد النجاح: اقفل الشيت، ووَدّي الطالب على «طلباتي» — وماتلمسش صف المقرر. والطالب يقدر يلغي طلبه طول ما هو لسه تحت الموافقة (نفس النداء بـ Cancel: true).
الفكرة في سطر: حذف السكشن = غيّرت رأيي في ميعاد السكشن · احذف = مش عايز المقرر، وينفع أرجع · انسحب = بدأت ومش قادر أكمّل، وهيفضل في سجلّي.

سؤال جه من الموبايل تيم: «حذفت السكشن والمقرر لسه راجع — ليه؟»

ده صح، ومقصود. «حذف السكشن» بيحذف السكشن بس — مش المقرر. التسجيل نفسه مالوش دعوة، فالمقرر بيرجع في LoadRequest عادي بـ sections: [].

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

لو عايز المقرر يتحذف فعلًا، الزرار الصح هو «أسقط المقرر» (DropCourse) مش «حذف السكشن».
يبقى تعرض إيه بعد ما السكشن تتحذف؟ ماتحسبش حاجة — الفلاجز بتتغيّر لوحدها وبتقولك:
الحقلبيبقىيعني إيه على الشاشة
sections[] اعرض الصف بعلامة «من غير سكشن» + زرار «اختر سكشن»
flags.CanDeleteSectionfalse خبّي زرار «حذف السكشن» — مافيش حاجة تتحذف
flags.CanReplaceSectionfalse خبّي «غيّر السكشن» — مافيش سكشن حالية أصلاً
flags.CanDroptrue «أسقط المقرر» هو اللي يفضل ظاهر
فخ وانت بتجرّب على Postman: DeleteSection بيقبل EdStudGroupId (الصح — السكشن بالظبط)، أو EdCourseId لوحده كـتسهيل. ولو بعتّ EdCourseId بس، بتتحذف أول سكشن فيهم — فمقرر فيه محاضرة + عملي هتتحذف منه واحدة والتانية تفضل، وهتفتكر إن النداء «ما اشتغلش».

القاعدة: ابعت EdStudGroupId اللي جايلك من sections[].ED_STUD_GROUP_ID — وانت أصلاً عارفه، لأن الطالب دوس على السكشن ده بإيده.
ومفيش «امسح المقرر خالص» — بقصد. أقصى حاجة الطالب يقدر يعملها من التطبيق هي «أسقط»، واللي بيترجع. المسح النهائي بيضيّع تاريخ الطالب وسجل التغييرات اللي الجامعة بتشتغل عليه — فده شغل موظف التسجيل مش شغل تليفون. ماتدوّرش على إندبوينت للمسح النهائي، مش موجود.
خلاصة عملية للشاشة: اعرض «حذف السكشن» جنب كل سكشن، و«أسقط المقرر» جنب المقرر، و«اطلب انسحاب» في مكان تاني خالص (شاشة الطلبات) — عشان محدش يدوس عليه بالغلط. وبعد أي عملية فيهم، اقرا LoadRequest تاني واتعامل مع اللي راجع بدل ما تعدّل القايمة من عندك.

الباجات تلاتة فوق بعض — والتسجيل عمره ما اشتغل

كل واحدة فيهم كانت بتخبّي اللي بعدها، والأولانية كانت بتتنكّر في هيئة قاعدة شغلانية.

1 الـ XML كان بيتقري في namespace تاني
إجراء الإدخال بيقرا مستند <Courses> تحت WITH XMLNAMESPACES (DEFAULT 'http://www.w3.org/2005/Atom') — في خمس جمل مختلفة. والـ XPath لما يتنفّذ مع namespace افتراضي مابيطابقش غير العناصر اللي جوّه الـ namespace ده.

المستند اللي كان بيتبعت من غير namespace، فـnodes() رجّع صفر صفوف، والـINSERT ماأدخلش حاجة، والإجراء خلص من غير أي خطأ.
كان<Courses><Course>…
بقى<Courses xmlns="http://www.w3.org/2005/Atom">…
وده اللي خلّى السيرفس يقول «الطلب مرفوض من قواعد اللائحة» — واللائحة أصلاً ماتسألتش.
2 صفر مش قيمة صالحة في مفتاح أجنبي
بعد ما الإدخال اشتغل، الجملة اللي بعده وقعت على FK_ED_STUD_COURSE_REG_SC_SECTION_GROUP_HDR: الكود كان بيكتب GroupId في SC_SECTION_GROUP_HDR_ID على طول، و0 مش مفتاح موجود — 0 معناها «مفيش مجموعة»، يعني NULL. دلوقتي مابيتكتبش غير لما تكون فيه مجموعة فعلاً.
3 الطالب كان هيتسجّل من غير سكشن
السكاشن هنا غالبًا مش متجمّعة في مجموعات — GetNewCourses بيرجّع SC_SCHEDULE_DTL_ID على كل سكشن، وSC_SECTION_GROUP_HDR_ID بيبقى null في أغلب الحالات. يعني من غير مجموعة، مكانش فيه حاجة تحطّ الطالب فيها، وكان هيتسجّل في المقرر ومش مربوط بأي سكشن.

بقى فيه ScScheduleDtlId في الطلب — السكشن اللي الطالب اختارها فعلاً.
الدرس: أخطر رد هو النجاح الفاضي. لو الإجراء رجع من غير خطأ ومن غير ما يعمل حاجة، الرسالة اللي فوقيها التطبيق («مرفوض من اللائحة») بتبقى تفسير مخترع لحاجة محصلتش أصلاً. لما تشوف رفض، اسأل الأول: القاعدة دي فعلاً اتنفّذت؟

جرّبها نزّل الكوليكشن وشغّل اللي يخصّك

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

⬇️ حمّل كوليكشن تسجيل المقررات (٩ فولدرات · ٥٧ ريكوست · ٨٨ تحقّق)

شغّل فولدر واحد بس: في Postman كليك يمين على الفولدر ← Run folder. أو من الطرفية:
newman run workflow-course-registration.postman_collection.json --folder "2 · تغيير سكشن مقرر مسجّل"
الحسابليه هو بالذات
mahmoued.eprahim.23010062
طالب علاج طبيعي · ED_STUD_ID 73
نفس الحساب اللي كل الشاشات التانية ماشية عليه — الدليل كله بقى على طالب واحد عشان العرض يمشي من غير ما حد يغيّر تسجيل الدخول في النص. الطالب ده مسجّل ١٠ مقررات الترم ده، وقدامه مقررات راسب فيها متاحة للإعادة بسكاشن مفتوحة — فالدورة الكاملة (عرض ← تسجيل ← حذف ← تراجع) بتكمل بيه.
اتشغّل قبل ما ينزل، على السيرفر المنشور: 11 ريكويست · 23 تحقق · صفر فشل.
6  registered MEC111 into section 1764
7  now registered in 7 course(s); new one present: true
8  dropped MEC111
9  can go back to 5 section(s)
10 restored MEC111
11 final: 7 rows — 6 active, 1 dropped

في التطبيق نفس الدورة على الشاشة

إضافة وإسقاط المقررات
إضافة وإسقاط — المسجّل والمتاح في شاشة واحدة

الشاشة بتعرض المسجّل بزرار «أسقط»، والمتاح بزرار «سجّل»، والمسقَط بيتعلّم عليه وزراره بيتغيّر لـ «تراجع عن الإسقاط» — لأن ده الطريق الصح في الـ SIS. وأي رفض من اللائحة بيتعرض بنصّه للمستخدم بدل «حصل خطأ».

اتظبط باقي بيزنس التسجيل زي ما هو في الـ SIS

الجزء ده اتعمل بعد مراجعة كود البورتال نفسه (UcRegisteredCourses، UCRegisterAddReplaceCourse، AddStudCourseChngReq) — مش من شكل الريكوست القديم.

باجّين الموبايل تيم بلّغوا عنهم

اللي كان بيحصلالسبباتعمل إيه
إسقاط مقرر بـ EdCourseId لوحده بيرجّع Status: true والمقرر مايتحذفش الخدمة كانت بتنفّذ UPDATE … WHERE ED_STUD_COURSE_REG_ID = 0 — صفر صف اتغيّر، ومع ذلك بترد نجاح، وكمان بتسجّل صف تغيير لحذف ماحصلش. لو رقم التسجيل مش مبعوت، الخدمة بتلاقيه من فصل الطالب نفسه. ولو مفيش حاجة تتحذف بترفض بوضوح بدل ما تدّعي النجاح. وده قفل كمان ثغرة صلاحيات: قبل كده أي حد كان يقدر يبعت رقم تسجيل بتاع طالب تاني ويتنفّذ.
شاشة «مقرراتي» مابترجّعش لا السكشن اللي هو فيها ولا السكاشن المتاحة الاستعلام بيرجّع صف لكل سكشن، والكود كان بياخد أول صف بس (g.First()) — فمقرر فيه محاضرة + عملي + إكلينيكي كان بيوصل كـ ٣ صفوف ويتقفل على واحد. كل مقرر دلوقتي بيجيب سكاشنه تحت sections، وبـ IncludeAvailableSections: true بيجيب كمان availableSections — السكاشن اللي ينفع يغيّر ليها.

خمس عمليات زوّدناها من الـ SIS

الإندبوينتبيعمل إيهالنافذة اللي بتحكمه
CourseReg/SetSection جديد «حطّني في السكشن دي» — نداء واحد لكل الحالات. الخدمة بتشوف إن الطالب عنده سكشن من نوع التدريس ده ولا لأ، وتقرّر نقل ولا إضافة، وترجّع Action. ReplaceSection و AddSection بقوا نفس النداء ده بأسمائهم القديمة. النقل: الإضافة (5) · الإضافة: (70) أو (5)
CourseReg/ReplaceSection
بقى CourseReg/SetSection
تغيير سكشن مقرر مسجّل. بيتأكد إن السكشن بتاع نفس المقرر ومن نفس نوع التدريس، وإن فيها مكان، وإنها ماتتعارضش مع الجدول — والتأكد من التعارض بيتعمل مرتين: قبل النقل وبعده جوّه نفس الترانزاكشن. الإضافة (5) — زي البورتال، مش نافذة الإسقاط
CourseReg/DeleteSection حذف سكشن واحد من مقرر متدرّس على أكتر من سكشن، والتسجيل يفضل قايم. والرد بيسمّي النوع اللي بقى ناقص وكام مرشّح ليه، ومعاه نداء البيكر جاهز. تسجيل السكاشن (70)
CourseReg/AddSection
بقى CourseReg/SetSection
ضم سكشن لتسجيل قايم — الرجعة من الحذف. نفس ضمانات النقل (نفس المقرر · مفتوحة · فيها مكان · ماتتعارضش)، بس مافيش صف بينتقل: بيتزاد صف جديد لنوع تدريس الطالب مالوش فيه سكشن. والساعات ماتتغيّرش — ده مش تسجيل مقرر. تسجيل السكاشن (70) أو الإضافة (5)
CourseReg/AddToWaitList
بقى جوّه SetSection وRegisterNewCourse
حجز مقعد في سكشن مليان: تسجيل بحالة 7 (انتظار) ومعاه رقم الدور. مسؤول التسجيل هو اللي بيحوّله لـ 1 لما يفضى مكان.
مابقاش نداء لوحده — زي البورتال، التسجيل العادي هو اللي بيحطّك في الطابور لما المقاعد تخلص والطابور يبقى فيه مكان.
الإضافة (5) · وبيترفض لو فيه منع في SE_BLOCK_TRANS
CourseReg/CancelWaitList خروج من الطابور — الصف بيتمسح خالص، لأنه أصلاً ماكانش تسجيل. —
وانت كموبايل: نداء واحد للسكشن. CourseReg/SetSection بالـ body { EdCourseId, ScScheduleDtlId } — مش محتاج تعرف هي نقل ولا إضافة، ولا تبعت EdStudGroupId إلا في المقرر اللي فيه أكتر من سكشن من نفس النوع. والبيكر أصلاً بيبعتلك الـ Endpoint والـ Body جاهزين في actions.

قواعد كانت ناقصة واتضافت

  • الإسقاط مربوط بنافذة الإسقاط (نشاط 6) في أجندة الفصل — البورتال بيرفض بـ «dropdateout». قبل كده الموبايل كان بيسمح بالإسقاط في أي وقت.
  • صف التغيير بقى بسبب 1 («رغبة الطالب») وAPPROVE_FLG فاضية في الإسقاط — بالظبط زي البورتال، اللي بيملاها في الإضافة والتراجع بس.
  • التراجع بيكتب صف التغيير بتاعه (نوع 5)، عشان سجلّ التغييرات عند المسجّل يبان فيه الرجوع مش الإسقاط بس.
  • سقف الساعات بيتشيك قبل التراجع وقبل حجز المقعد (GetAcadLoadMinMax) — زي ما البورتال بيعمل بالظبط.
فرق مقصود واحد: البورتال بيعمل «تغيير السكشن» بإنه يمسح صف السكشن، ويمسح صف التسجيل كله، ويدخّلهم من أول وجديد — يعني ED_STUD_COURSE_REG_ID بيتغيّر وتاريخ الصف القديم بيضيع. التطبيق ماسك الرقم ده وهو ماشي بين الشاشات، والمقرر أصلاً مش بيتغيّر، فإحنا بنحرّك صف السكشن الموجود بدل كده — وده نفس اللي الـ SIS بيعمله لما مسؤول التسجيل بيطلّع طالب من قائمة الانتظار.

للموبايل الأزرار جايالك جاهزة — ماتحسبهاش بنفسك

قواعد إيه اللي مسموح إمتى كلها في الـ SIS: أجندة الفصل (وبتختلف من كلية لكلية ومن فصل لفصل)، وأماكن السكشن، وحالة التسجيل. أي كلاينت يحاول يستنتجها بنفسه هيختلف عن البورتال أول ما مسؤول التسجيل يزوّد أو يقفل نافذة. عشان كده الخدمة بترد بالقرار نفسه مش بالمكوّنات.

١ — على مستوى الشاشة: CourseReg/GetAllowedActions

الحقلمعناه
Agenda.Register / Add / Drop / Withdraw / RegisterSection نوافذ الأجندة الخمسة (الأنشطة 3 / 5 / 6 / 7 / 70) — مفتوحة النهاردة ولا لأ
Agenda.IsActiveStudent false لو الطالب موقوف مالياً — ساعتها مفيش أي إجراء مهما كانت النوافذ مفتوحة
Agenda.AnyOpen مفيش أي حاجة متاحة خالص — اعرض رسالة واحدة بدل خمس أزرار مقفولة
CanRegisterCourses · CanDropCourses · CanUnDropCourses
CanChangeSections · CanJoinWaitList
الإجابة الجاهزة لكل شاشة — النافذة + حالة الطالب متجمّعين
MaxCreditHours · CurrentCreditHours سقف اللائحة والمحمّل منه — عشان تحذّر قبل ما التسجيل يترفض

ونفس بلوك Agenda بيرجع كمان مع LoadRequest و GetNewCourses وGetUnDropCourse، فمش محتاج نداء زيادة وانت فاتح الشاشة.

٢ — على مستوى كل سكشن: flags

كل سكشن في sections وavailableSections جايّة ومعاها:

الفلاجيعني إيه للزرار
CanRegisterاعرض زرار «سجّل»
CanAddSeat اعرض زرار «احجز مقعد» — وده بيبقى true في حالة واحدة بس: السكشن مليانة وقائمة الانتظار مفتوحة وفيها مكان. يعني الزرارين عمرهم ما بيظهروا مع بعض.
IsFull · SeatsTotal · SeatsTaken · SeatsLeft عدّاد الأماكن. SeatsLeft = -1 يعني السكشن من غير سقف
WaitCapacity · WaitTaken · WaitLeft طول طابور الانتظار وفاضل فيه كام
HasClash · HasExamClash تعارض مع محاضرة تانية أو مع امتحان في جدول الطالب
BlockedReason ليه الزرار مقفول — واحدة من: WINDOW_CLOSED · SECTION_FULL · SECTION_CLOSED · TIME_CLASH · EXAM_CLASH · MAX_LOAD · INCOMPLETE_COURSE · ALREADY_REGISTERED · PREREQ_NOT_MET · STUDENT_BLOCKED. اعرض السبب بدل ما ترمّد الزرار وخلاص.
PrereqProblem رد محرّك القواعد بنصّه لما يكون فيه مشكلة. ماتعرضهوش للطالب — هو مفتاح داخلي (زي ExamCourseConfilict)، وBlockedReason هو الترجمة بتاعته. موجود للتشخيص بس.
شكل الحالتين جنب بعض (مقرر INT404 في الديمو — سكشن مليان وسكشن فاضية):
sec 2132  CanRegister=false  CanAddSeat=true   IsFull=true   seats 1/1   wait 0/10  BlockedReason=SECTION_FULL
sec 2133  CanRegister=true   CanAddSeat=false  IsFull=false  seats 0/30  wait 0/10  BlockedReason=null

٣ — على مستوى كل مقرر مسجّل: flags

الفلاجالزرارالنافذة اللي بتحكمه
CanDrop«أسقط المقرر»الإسقاط (6) + الحالة نشطة
CanUnDrop«تراجع عن الإسقاط»الإضافة (5) + الحالة مسقَطة
CanReplaceSection«غيّر السكشن» الإضافة (5) + عنده سكشن + فيه بديل فعلاً (alternativeSectionCount > 0)
CanDeleteSection«حذف السكشن»تسجيل السكاشن (70)
needsSection (على المقرر نفسه) «اختار سكشن» — زرار أساسي ناقصه سكشن من نوع بيتدرّس، اسمه في missingTeachingMethods
CanCancelWait«اخرج من الانتظار»الحالة انتظار (7)
IsDropped · IsWaitingشكل الصف نفسه—

يعني الصف في القايمة بيوصلك وهو عارف أزراره: CanDrop بقى false يوم ما نافذة الإسقاط تتقفل، من غير ما تعدّل سطر واحد في التطبيق.

مهم رقم الطالب بيتاخد من التوكن — مش من الـ body

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

اللي كان بيحصل فعلًا (متجرَّب على الخدمة المنشورة): تدخل بحساب الطالب 73، وتبعت { "LANG": "ar" } لـ CourseReg/v2/MyCourses — ويرجعلك مقررات الطالب 2084 التمانية. نفس الشيء على الإندبوينتات اللي بتكتب.

اتظبطت في مكان واحد مش في ٢٧ إندبوينت: فيلتر عام بيستبدل رقم الطالب في أي موديل بالرقم اللي في التوكن، بعد ما الريكوست يتقري وقبل ما أي هاندلر يشوفه.

الحالةاللي بيحصل دلوقتي
بعتّ رقمك أنت (الطبيعي) مافيش أي فرق — الحقل لسه في العقد وشغال زي ما هو
بعتّ رقم طالب تاني بتوكن طالب بيترد عليك داتاك انت — الرقم بيتتجاهل
بعتّ رقم طالب بتوكن دكتور بيشتغل عادي — الدكتور بيتعامل مع طلبة مش هو، وصلاحيته جاية من دوره

يعني عمليًا: ماتعتمدش على الرقم اللي بتبعته عشان تعرض داتا حد تاني — مش هيشتغل. ولو محتاج تعرض بيانات طالب لموظف، لازم توكن الموظف.

اتعمل سقف الساعات والمتطلبات السابقة — بمحرّك الـ SIS نفسه

الاتنين دول كانوا مكتوبين في جدول النواقص إن «التحقق كله لسه» — وده كان صح لما راجعناه على البورتال. دلوقتي الاتنين شغالين، ومش بمنطق كتبناه إحنا: بنستخدم نفس الإجراءات اللي البورتال بيستخدمها.

القاعدةمصدرها في الـ SISبتتنفّذ فين
المتطلب السابق CHK_STUD_CRS_PREREQ — بيرجّع مفتاح مشكلة (زي ExamCourseConfilict) أو فاضي يعني مسموح مرة لكل مقرر في GetNewCourses و GetUnDropCourse وavailableSections، وتاني مرة في RegisterNewCourse قبل الكتابة
سقف الساعات GetAcadLoadMinMax — بيرجّع «الحد الأدنى - الأقصى - عدد المقررات» من لائحة الطالب RegisterNewCourse · UnDropCourse · AddToWaitList
المتطلب بتاع المقرر، مش بتاع السكشن. عشان كده لما يترفض بيقفل كل سكاشن المقرر مرة واحدة — وده بالظبط اللي البورتال بيعمله (بيخفي الصف كله). الفلاجز بترجع كده:
CanRegister=false  CanAddSeat=false  BlockedReason=PREREQ_NOT_MET  PrereqProblem=ExamCourseConfilict
PrereqProblem هو مفتاح رسالة الـ SIS نفسه — ترجمه في التطبيق زي ما البورتال بيترجمه.
متجرَّب على الخدمة المنشورة:
register BAS111 (prereq not met) → Status:false · "prerequisites have not been met" · key=ExamCourseConfilict
register ORT407 (allowed)         → Status:true
register NUR502 (5h) at 24/30     → Status:true   → 29
register NUR504 (4h) at 29/30     → Status:false  · "would take the student to 33.00, over the 30 allowed"
الحد الأدنى: بنعرضه، بس مابنمنعش بيه الإسقاط. الـ SIS بيمنع الانسحاب اللي بينزّل الطالب تحت الحد الأدنى، وبيحذّر في طلب الإسقاط — لكن الإسقاط المباشر الشيك بتاعه متعلّق عليه كومنت في البورتال بطلب من الجامعة. إحنا مشينا ورا البورتال مش ورا نص اللائحة، وMinCreditHours راجع في GetAllowedActions عشان التطبيق يحذّر لوحده لو حب.

اتأكدنا كل عملية كتابة بتظهر فعلًا في القايمة اللي المفروض تعرضها

أخطر عطل في الخدمة دي كان: عملية بترد Status: true ومابتظهرش في أي شاشة — مافيش فرق بينها وبين النجاح وقت النداء، وماتبانش غير لما الطالب يقول «أنا سجّلت المقرر ومش لاقيه». عشان كده كل عملية كتابة اتشغّلت بين قراءتين للإندبوينت اللي التطبيق بيعمله refresh بعدها، والقراءتين اتقارنوا.

العمليةالقايمة اللي بتظهر فيهاالنتيجة
RegisterNewCourseLoadRequest ✅ المقرر ظهر بحالة 1
ReplaceSectionLoadRequest ✅ السكشن الجديدة هي اللي بتترجع
DeleteSectionLoadRequest ✅ sections بقت فاضية والمقرر لسه مسجّل — وneedsSection = true ومعاه زرار «اختار سكشن»
AddSectionLoadRequest ✅ السكشن رجعت والمقرر بقى كامل — واتجرّبت على مقرر MEC111 لطالب تسجيله كان من غير سكشن: زوّدنا محاضرة، والنداء التاني على نفس النوع اترفض صح («استعمل ReplaceSection»)
DropCourseLoadRequest ✅ الحالة بقت 6
UnDropCourseLoadRequest ✅ رجعت 1 ومعاها سكشن
AddToWaitListLoadRequest ✅ ظهر بحالة 7
CancelWaitListLoadRequest ✅ الصف اختفى
AddComplaintGetComplaints ✅ 35 ← 36
AddQuestionGetQuestions ✅ 26 ← 27
AddAnswerGetAnswers ✅ 23 ← 24
SaveContactMethodGetContactMethods ✅ 38 ← 39
AddCampusRequestGetCampusRequests ⚖️ رفض «عندك طلب مفتوح» — والطلب موجود في القايمة
RequestCourseWithdrawalGetCourseWithdrawals ⚖️ نفس الحالة
RequestEnrollmentChangeGetEnrollmentChanges ⚖️ نفس الحالة
AddHousingRequestGetHousingRequests ⚖️ نفس الحالة — واتأكدنا منها بالكامل (تحت)
BookAppointmentGetMyRecords ⚖️ نفس الحالة
صفر عملية كتبت وماظهرتش. و⚖️ معناها إن العملية اترفضت بقاعدة «عندك طلب مفتوح بالفعل» — وده مش تهرّب من الاختبار: القاعدة بتقرا نفس المكان اللي القايمة بتقرا منه، والطلب الموجود بيظهر في القايمة فعلًا. وعشان نتأكد ١٠٠٪ جرّبنا واحدة منهم كاملة — طلب السكن:
list = 4  →  delete the open request  →  list = 3  →  AddHousingRequest → Ok  →  list = 4
يعني الرفض بيحصل لأن الصف موجود، والصف ظاهر، ولما يتحذف العملية بتشتغل وبتبان.

مراجعة منيو الـ SIS كله — إيه اللي يستاهل يبقى موبايل؟

رجعنا لمنيو الـ SIS نفسه (SE_MODULE / SE_FORM) لكل نوع مستخدم، مش لتخميناتنا:

نوع المستخدمموديولاتشاشات
مسؤول التسجيل41448
رئيس القسم23144
عضو هيئة تدريس14109
الطالب11102
عميد الكلية · شؤون الطلاب · الشؤون المالية · القبول … —579

معظم ده مش شغل موبايل — اعتمادات بالجملة، بناء الجداول والعروض، اعتماد النتائج، تقارير، إعدادات لائحة. دي شاشات مكتب: بتتعمل مرة في الترم، على شاشة كبيرة، وبتتطلب تركيز. اللي يستاهل الموبايل هو اللي صغير ومتكرر وبيحصل وانت واقف.

اللي اتعمل

الشاشة في الـ SISالإندبوينتليه موبايل
طالب · Academic Affairs →
Semester Activity Marks
students/GetCourseworkMarks الدرجات اللي بتتجمّع خلال الترم. كان عندنا النتيجة النهائية والسجل بس — يعني الطالب يشوف نتيجته بعد ما تخلص، وعمره ما يشوف هو واقف فين. ١٤٢٬٥٣٩ صف داتا حقيقية، وبيقرا اللي شيت الدرجات بتاع الدكتور بيكتبه.
دكتور · My Academic Duties →
Faculty Academic Load
academicstaff/GetTeachingLoad «أنا بدرّس قد إيه؟» — سؤال بيتسأل في الممشى. كان عندنا الجدول وقائمة المقررات، ومحدش فيهم بيرد عليه.
دكتور · My Academic Duties →
Office Hours
academicstaff/GetMyOfficeHours
academicstaff/SaveOfficeHour
الطالب كان بيقراها وصاحبها ماكانش بيقدر يعدّلها — بالعكس. تأجيل ميعاد ساعة واحدة حاجة بتحصل بين محاضرتين، ومابتتعملش لأنها بتحتاج فتح البورتال.
متجرَّب على الخدمة المنشورة بحسابات حقيقية:
coursework marks  → 26 marks, e.g. INT302 Clinical Exam 20/20 · Final Exam 26.5/30
teaching load     → 7 courses · 12 sections · 485 students · 19 credit hours
office hours      → add → visible · move → visible · delete → gone
                    and the STUDENT's GetOfficeHours now shows what the lecturer saved

اللي مااتعملش — وبسبب واضح

الشاشةليه اتساب
تغيير كلمة السر
(My Preferences)
التنصيب ده شغّال بـ LegacyTrust — مابيتحققش من كلمة السر أصلًا، وSE_USER مافيهاش عمود كلمة سر. الإندبوينت كان هيكتب في حتة محدش بيقراها — شاشة بتكدب.
استطلاعات الرأي (Poll) الجداول موجودة وفاضية تمامًا (SV_POLL_HDR/DTL/STUD_POLL = صفر صف).
ملاحظات المحاضرات · ملصق السيارة ·
الهوايات · التدريب · الإعفاءات والخصومات
مفيش جداول ليها خالص في السكيما دي.
الاعتمادات بالجملة · اعتماد النتائج ·
بناء العروض والجداول · التقارير
شغل مكتب بطبيعته — مرة في الترم، على شاشة كبيرة.

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

إعداد صورة الطالب — لينك جاهز مش اسم ملف

الخدمة القديمة كانت بترجّع STUD_PHOTO اسم ملف بس، وكل كلاينت يخمّن اللي قبله. ده كان ماشي لما الصور كانت جنب الخدمة — دلوقتي الصور متنشورة على سيرفر ملفات الجامعة، فاللي قبل الاسم بقى إعداد، وتركيب اللينك مكانه الخدمة مش كل تطبيق لوحده.

الإعداد — قسم Media في البروفايل

المفتاحمعناه
StudentPhotoBaseUrl جذر مجلد الصور العام، مثلاً https://files.university.edu/students
StudentPhotoPattern شكل اسم الملف تحت الجذر ده. الافتراضي {id}.jpg — و{id} رقم الطالب، {code} الكود الجامعي، لأن فيه تنصيبات بتسمّي الملف بده وفيه بتسمّيه بده
DefaultPhotoUrl اللي يترجع لما مفيش مجلد منشور. سيبه فاضي وهتترجع الصورة الافتراضية بتاعة الخدمة
اللي بيوصلك:
// with Media.StudentPhotoBaseUrl configured
"STUD_PHOTO": "https://files.university.edu/students/73.jpg"

// with nothing configured — still a usable link, never an empty string
"STUD_PHOTO": "https://api.109.123.243.32.sslip.io/Files/UserImages/default.jpg"
نفس الحاجة في GetStudentUnderSuperVision وكشف طلاب المقرر — كانوا بيرجّعوا "<code>.jpg" وبقوا لينك كامل.
تفصيلة اتكشفت وإحنا بنجرّب: أول تشغيل رجّع اللينك بـ http:// على هوست https. السبب إن الـ proxy بينده الخدمة على HTTP جوّه، فـ Request.Scheme بيقول «http» رغم إن المستخدم جاي على TLS — والمتصفح بيرفض صورة http في صفحة https (mixed content)، يعني الصورة كانت هتختفي من غير أي رسالة. اللينك بقى بيتبني من X-Forwarded-Proto لما الـ proxy يبعته.

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

إزاي تغيير السكشن — الدورة الكاملة

flags.CanReplaceSection بيقولك إن المقرر ينفع تتغيّر سكشنه. اللي بعده نداء واحد بيدّيك كل حاجة: السكاشن اللي تقدر تروحلها دلوقتي، واللي مليانة، واللي متعارضة — ومتعارضة مع مين بالظبط.

#النداءبتاخد منه إيه
1CourseReg/v2/MyCourses المقررات المسجّلة. اللي فيها flags.CanReplaceSection = true هي اللي ينفع تتغيّر. وalternativeSectionCount بيقولك فيه كام سكشن بديل — والرقم ده مطابق لإجمالي اللي هيرجع في الخطوة اللي بعدها
2CourseReg/GetSectionsToReplace
{ EdCourseId, EdStudGroupId }
السكاشن مقسومة على ٤ قوايم جاهزة + Summary بالأعداد. ابعت EdStudGroupId بتاع السكشن اللي الطالب دوس عليه — مهم جدًا في المقررات اللي فيها محاضرة + عملي + إكلينيكي
3CourseReg/SetSection
{ EdCourseId, ScScheduleDtlId }
✍️ النقل — أو الإضافة لو مالوش سكشن من النوع ده؛ الخدمة بتقرّر وبترجّع Action. رقم التسجيل مابيتغيّرش — السكشن بس. ولو اترفض بسبب تعارض، بيرجّع Conflict فيه مع مين
4CourseReg/v2/MyCourses 🔎 اقرا تاني واتعامل مع اللي راجع

الأربع قوايم — كل واحدة ليها زرار مختلف

القايمةمعناهاالزرار المناسب
Sections فاضية ومفيهاش تعارض — تقدر تروحلها دلوقتي «انقل هنا» ← ReplaceSection
WaitListSections مليانة بس قائمة الانتظار مفتوحة «احجز مقعد» ← AddToWaitList
ConflictingSections فيها مكان، بس بتتعارض مع محاضرة تانية في جدول الطالب «شوف التعارض» ← تعرض Conflict وتوديه يحذفه
FullSections مليانة ومفيش انتظار — مفيش حاجة تتعمل اعرضها رمادي، من غير زرار
الأعداد مطابقة للقوايم — بالتصميم. Summary.Available = طول Sections بالظبط، وTotal = مجموع الأربعة. تقدر تكتب «فيه ٢ متاحين و٣ متعارضين» من Summary على طول من غير ما تلف على المصفوفات.

وفيه رقمين زايدين بيقطعوا عرض القوايم: WithTimeClash (كام سكشن فيها تعارض — حتى لو مليانة) و ResolvableConflicts (كام تعارض منهم الطالب يقدر يحلّه بنفسه). لو التانية أكبر من صفر، ساعتها بس اعرض رسالة «فضّي جدولك وارجع».

التعارض بيجيلك باسم صاحبه — دي أهم حاجة في النداء ده

كل سكشن في ConflictingSections جايّة ومعاها بلوك Conflict. رد حقيقي من الديمو (مقرر SUR310، السكشن الإكلينيكية):

"Conflict": {
  "Type":               "TIME_CLASH",
  "WithCourseCode":     "BSM131",
  "WithSectionNameEn":  "01",
  "WithDaysEn":         "Saturday, Wednesday",
  "WithFromTime":       "09:00",
  "WithToTime":         "13:00",
  "WithEdStudGroupId":  135172,
  "CanResolve":         true
}

(WithDaysAr جايّة معاها بالعربي، وWithEdStudGroupId ده رقم سكشن الطالب نفسه في المقرر المتعارض — مش رقم السكشن اللي هو رايحلها.)

يعني تقدر تكتب للطالب بالحرف: «السكشن ده بتتعارض مع محاضرة BSM131 يوم الأربعاء ٩–١»، وتحط جنبها زرار «روح احذف سكشن BSM131» — وWithEdStudGroupId هو الرقم اللي هتبعته لـ DeleteSection أو GetSectionsToReplace بتاع المقرر ده. بعدها يرجع ويكمّل. ده الفرق بين رسالة خطأ ورسالة فيها حل.

CanResolve هو اللي بيقرّر تعرض الزرار ولا لأ. true يعني التعارض مع حاجة الطالب مسجّل فيها فيقدر يحذفه. false يعني حاجة مالوش فيها يد — اعرض السبب وبس، من غير ما توعده بحل.

مقرر بأكتر من نوع تدريس — ابعت EdStudGroupId

SUR310 مسجّل فيه الطالب بـ ٣ سكاشن: محاضرة + عملي + إكلينيكي. «غيّر السكشن» لوحدها كلمة مالهاش معنى — أنهي واحدة فيهم؟ عشان كده الرد بيختلف:

اللي بعتّهالرد
من غير EdStudGroupId ٢ بدايل — واحد للعملي وواحد للإكلينيكي، وكل واحد فيهم كاتب ReplacesEdStudGroupId يعني هيقف مكان أنهي سكشن
سكشن المحاضرة NO_ALTERNATIVE — مفيش محاضرة تانية للمقرر ده
سكشن العملي بديل واحد (SEC 2) — مليان 28/28
سكشن الإكلينيكي بديل واحد (HOSPITAL 2) — مليان، وكمان متعارض مع BSM131
ليه ده مهم مش بس شكليًا: الخدمة بقت ماتعرضش عليك سكشن من نوع تاني. قبل كده كان ينفع تيجي سكشن معمل كبديل لـمحاضرة — ولو الطالب دوس عليها كانت محاضرته تتنقل جوّه ميعاد معمل. دلوقتي المحاضرة بتتبدّل بمحاضرة، والمعمل بمعمل، وبس.
لو الرد CanReplace: false، الـReason بيقول ليه: NO_ALTERNATIVE (مفيش سكشن تاني من نوعها) · ALL_FULL (فيه بدايل بس كلها مليانة) · ALL_CONFLICT (كلها متعارضة — وده اللي ينفع تعمله حاجة) · NONE_AVAILABLE (خليط) · GROUPED_REGISTRATION · WINDOW_CLOSED · STUDENT_BLOCKED · NOT_REGISTERED · NO_SECTION.

اعرض السبب زي ما هو — «مفيش سكاشن تانية» و«كلها مليانة» و«كلها متعارضة» تلات رسايل مختلفة للطالب، وكانوا كلهم قبل كده قايمة فاضية.
وحتى بعد ما تدوس، النقل ممكن يترفض: ReplaceSection بيتأكد من التعارض تاني وقت الكتابة (لأن حد تاني ممكن يكون خد المكان في الوقت ده). لو رفض بسبب تعارض بيرجّع نفس بلوك Conflict — فاعرضه بنفس الشاشة بتاعتك، مش برسالة «حصل خطأ».
حالة الخدمة بترفض فيها التغيير من أصله: GROUPED_REGISTRATION

فيه تسجيلات بتتعمل على مجموعة سكاشن — الجدول بيربط المحاضرة بالعملي بتاعها عشان يتاخدوا مع بعض. في الداتا دي ٣١٨٥ تسجيل من ٥٧٦٦٧ (٥٫٥٪) من النوع ده.

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

في التطبيق: اعرض «السكشن ده مربوطة بمجموعة — التغيير من شؤون التسجيل». والرفض ده متحطّ في النداءين (البيكر وReplaceSection) عشان محدش يعدّي عليه بنداء مباشر.
السكشن الحالي مش هتلاقيها في القوايم الأربعة — وده مقصود. هي في CurrentSections لوحدها، عشان تعرضها فوق كـ«إنت هنا دلوقتي». وأي سكشن تاني من نفس المقرر مابتتحسبش تعارض لو هي اللي خارج منها — كانت دي بتخلّي كل البدايل تبان متعارضة والقايمة تطلع فاضية.

قاعدة هوية المستخدم من التوكن — للطالب وللدكتور

مش محتاج تبعت رقم الطالب ولا رقم عضو هيئة التدريس. الخدمة بتاخدهم من التوكن، وأي رقم في الـ body بيتتجاهل.

التوكنفيه إيهبيتحط فين
طالب edStudId أي حقل اسمه ED_STUD_ID / EdStudId في أي ريكوست
دكتور stfMemberId أي حقل اسمه SaStfMemberId / saStaffMemberId
الدكتور اللي بيتعامل مع طالب مش متأثر. رقم الطالب في كشف الدرجات أو كشف الحضور هو مين اللي بيتعمله الإجراء، مش مين اللي بيعمله — وده بيفضل من الـ body زي ما هو. اللي بيتثبّت من التوكن هو رقم الدكتور نفسه.

الإندبوينتات الجديدة حذفت الحقل من العقد أصلاً (JsonIgnore) — يعني مش هتلاقيه في الـ body: ReplaceSection، DeleteSection، AddToWaitList، CancelWaitList، GetCourseworkMarks. القديمة سايبة الحقل عشان الكود القديم مايقعش، بس مابتصدّقهوش. حقل بتبعته ويتغيّر في السكوت بيوحي إنه مهم — وهو مش مهم.

حاجة اتكشفت وإحنا بنتأكد من ده — وكانت غلط عندنا: SE_USER_ACCNT.USER_TABLE_ID بيشاور على ED_STUD لو الحساب طالب، وعلى SA_STF_MEMBER لو موظف. والأرقام بتتقاطع: الرقم 148 في الداتابيز دي هو الدكتور اللي بنجرّب بيه، وكمان رقم طالب لشخص تاني خالص. والرقم 73 هو الطالب بتاعنا وكمان رقم دكتورة. عشان كده الرقم دلوقتي بيتأكد من الجدول اللي نوع المستخدم بيقوله قبل ما يتحط في التوكن — يا إما يشاور على شخص حقيقي من النوع الصح، يا إما مايترجعش أصلاً.
وحاجة تانية: المستخدم ممكن يكون عنده أكتر من حساب (لقينا مسؤول تسجيل عنده ٢٨)، و USER_TABLE_ID بيتملي على حساب واحد بس منهم والباقي فاضي. كنا بناخد أول صف من غير ترتيب، فالرقم كان بيرجع فاضي لأي حد عنده أكتر من حساب: ٢٧٩ من ٤٩١ صف موظف بس اللي كان بيتحل. بعد ما رتّبنا «اللي فيه رقم الأول» بقى ٢١١ من ٢٢٢ مستخدم.

19 التسجيل المطوّر — تابين ووحدة القياس بقت «طريقة التدريس»

التسجيل في الـ SIS مش لكل مقرر — هو لكل مقرر × كل طريقة تدريس. الطالب اللي مسجّل تمارين BS100 ومش مسجّل محاضرتها هو مسجّل ومش مسجّل في نفس الوقت، والقايمة الواحدة القديمة كانت تقدر تقول واحدة بس من الاتنين. الشاشة دلوقتي تابين، ووحدة القياس فيهم طريقة التدريس.

القاعدة التابين بيكمّلوا بعض، مابيتقاطعوش

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

التابالإندبوينتفيه إيه
مقرراتيCourseReg/v2/MyCourses كل تسجيلات الفصل بكل الحالات — مسجّل، محذوف، منسحب، في الانتظار. كل مقرر معاه TeachingMethods (كل طرق تدريسه وحالة الطالب في كل واحدة) وSections مسطّحة زي ما هي
المتاحCourseReg/v2/AvailableCourses كل حاجة لسه ينفع تتضاف — مقرر كامل، أو النص الناقص من مقرر الطالب ماسكه بالفعل. المحذوف مش منهم: إرجاعه مش إضافة

على الشاشة طريقة التدريس سطر، مش صندوق

أول نسخة من الشاشة عملت كل طريقة تدريس صندوق بشارة حالة وزرارين. على مقرر بيتدرّس بتلات طرق ده طلّع تلات صناديق متداخلة، ستة أزرار، «غيّر السكشن» مكتوبة مرتين ومفيش على الزرار حاجة تقول بيغيّر أنهي واحدة، وشارتين — «مفيش سكاشن معروضة» و«مافيش سكشن» — شكلهم واحد بالعربي ومعناهم عكس بعض (واحدة مشكلة الكلية، والتانية مشكلة الطالب). الكارت طلع أطول من الموبايل والطالب مش عارف يدوس على إيه.

دلوقتي: سطر واحد لكل طريقة تدريس، والسطر نفسه هو الزرار، ونصّ السطر هو حالته — إما «اختار سكشن» بالبرتقالي، أو ميعاد ومكان السكشن اللي هو فيه. مفيش شارات على السطور خالص. وكل الإجراءات نزلت دوسة واحدة لجوه — في الـ sheet اللي بتفتح على طريقة التدريس، وهي كمان المكان الوحيد اللي «احذف السكشن» فيه مالهاش غير معنى واحد. وإسقاط المقرر كله بقى لينك أحمر صغير مش زرار عريض بينافس الحاجة اللي الطالب جاي يعملها.

تاب مقرراتي
مقرراتي — الناقص فوق: NUR203 شارته «ناقص سكشن» وسطر «نظري» بيقول «⚠ اختار سكشن». وتحته المكتمل: CLN202 بميعاد ومكان سكشنه
الـ sheet
الدوسة على السطر — sheet لطريقة تدريس واحدة بس، وكل سكشن بشريط مقاعده وزرار واحد جاي من الخدمة نفسها
حالات السكشن الأربعة
الحالات الأربعة في شاشة واحدة — «04» فاضل فيها ٣ مقاعد فالرقم برتقالي · «01» مليانة ٢٠/٢٠ فالشريط أحمر والزرار بقى «احجز مقعد» · «03» فاضية بس بتتعارض، فالرسالة بتسمّي المقرر والميعاد والزرار بيحذف اللي متعارض مش بيقول «مرفوض» وخلاص
تاب المتاح
المتاح — NUR203 راجع هنا بطريقة التدريس الناقصة بس وعليه «ناقص»؛ والمقررات الجديدة من غير شارة و«اختار سكشن» بالأزرق لأن مفيش حاجة ناقصة فيها — هي بس لسه ماتسجّلتش
مقرر محذوف
المحذوف — سطرين وزرار: الشارة، وجملة بتقول هيحتاج أنهي طرق تدريس، و«تراجع عن الإسقاط». مفيش سطور طرق تدريس، لأن مفيش حاجة فيها ينفع تتدوس قبل ما المقرر يرجع

الشكل السكاشن فضلت مسطّحة — والربط شغلة التطبيق

كان الشكل البديهي إننا نحطّ السكاشن جوّه طرق التدريس. ده كان غلط: بيقرّر تصميم الشاشة نيابةً عن التطبيق، وشاشة عايزة جدول عادي هتفكّ الشجرة الأول عشان ترسمه. فالسكشن فضل في Sections مسطّحة، وكل سكشن شايل TeachingMethodId بتاعه، والربط سطر واحد عند الموبايل — وده اللي الديمو بيعمله بالظبط.

الربط: سكاشن طريقة التدريس هي اللي الـ id بتاعها موجود في TeachingMethods[].RegisteredSectionIds، وصفوف الطالب اللي بيتشال منها في RegisteredEdStudGroupIds.

الحالات أربع كلمات بس — وكل واحدة بتودّي لمكان مختلف

Stateيعنيالخطوة اللي بعدها
REGISTEREDالطالب ماسك سكشن من النوع ده غيّر السكشن (SetSection) أو احذفه (DeleteSection)
NOT_REGISTEREDمش ماسك ولا واحد، وفيه سكاشن يختار منها افتح البيكر على طريقة التدريس دي — ده اللي التاب اتعمل عشانه
NO_SECTIONS_OFFEREDالمقرر بيتدرّس كده بس مفيش سكشن معروض خالص مفيش — دي مش مشكلة الطالب، وماينفعش يتعرض عليه زرار
UNKNOWNالـ offering مش شايل رابط طريقة تدريس أصلاً مفيش — أحسن ماتقولش حاجة عن النوع من إنك تقول حاجة غلط

والكورس المحذوف بقى بيرجّع طرق تدريسه. الاستعلام القديم كان فيه ED_CODE_COURSE_STATUS_ID = 1، فالمقرر المحذوف كان بيرجع من غير أي طرق تدريس — وده بالظبط الطالب اللي على وشك يرجّعه ومحتاج يشوف هيحتاج إيه بعد كده.

البيكر «افتح المعمل» — سؤال ماكانش ينفع يتسأل

GetSectionsToReplace كان بياخد السكشن اللي هيتبدّل ويستنتج منه طريقة التدريس. طب وطريقة التدريس اللي الطالب مش ماسك فيها ولا سكشن؟ مفيش ED_STUD_GROUP نستنتج منه، فالرد كان بيرجع المقرر كله. بقى ياخد EdCodeCourseTeachingId على طول.

الحقل الجديدبيعمل إيه
EdCodeCourseTeachingId يحصر الرد على طريقة تدريس واحدة — سواء الطالب ماسك فيها سكشن أو لأ
TeachingMethodId نفس السؤال بالـ id التاني، للعروض اللي مش شايلة رابط تدريس
TeachingMethods كل طرق تدريس المقرر وحالة الطالب فيها — عشان بيكر جاي من نوتيفيكيشن مايحتاجش ينده الكارت كمان
RequiresUnDrop + NextAction المقرر محذوف: السكاشن بتترجع كاملة (الطالب بيقرّر يرجّعه ولا لأ) بس كل الأزرار متقفلة والزرار الشغال الوحيد هو التراجع عن الإسقاط

وكمان: لما كنت تبعت EdStudGroupId، صفوف الـ PICK كانت بتتشال. يعني الشاشة اللي بتقولك «أنا ببدّل السكشن ده» كانت بتخفي عنك طرق التدريس الناقصة. دلوقتي بتفضل.

التراجع الـ UnDrop بقى ميختارش السكشن

الحذف بيمسح صفوف ED_STUD_GROUP فيزيكال، ومفيش أي جدول في الـ SIS بيفتكر السكاشن دي كانت إيه — ED_STUD_COURSE_CHNG على مستوى المقرر بس، مفيهاش SC_SCHEDULE_DTL_ID. يعني مفيش حاجة اسمها «تراجع عن حذف سكشن»؛ اللي بيحصل هو تسجيل من جديد. وبعد كام يوم يكون السكشن اتملى من طلبة تانيين أصلاً.

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

بقى: UnDropCourse بيرجّع المقرر بس، وبيرجّع معاه TeachingMethods وNeedsSection وNextAction على البيكر. اللي عايز الاختيار التلقائي يبعت AssignSection: true — وساعتها بقى آمن: واحد لكل طريقة تدريس، وبس اللي فيه مقعد ومافيهوش تعارض.

إصلاحات تلاتة اتشحنوا للكل — مش ورا flag

التلاتة من نفس العيلة: كانوا بيدّوا نتيجة تبان ناجحة والداتا وراها غلط. حاجة زي دي ماينفعش تفضل هي السلوك الافتراضي.

#كان بيحصل إيهبقى
1 فحص «المقرر مسجّل بالفعل» في RegisterGuards مكتوب ISNULL(...) <> 6 — يعني المقرر المحذوف بيعدّي ويتعمله صف ED_STUD_COURSE_REG تاني لنفس المقرر في نفس الفصل، والساعات تتحسب مرتين بيترفض بـ Reason: COURSE_DROPPED ورسالة بتقول اعمل تراجع. وتاب «المتاح» بيعرض تراجع مش سجّل على المقرر ده
2 الاختيار التلقائي في التراجع، بالتفصيل اللي فوق اتشال من الافتراضي، وبقى آمن لما يتطلب صراحةً
3 فحص المقعد كان برّه الترانزاكشن في AddSection وReplaceSection — التعارض كان بيتفحص مرتين (قبل وجوّه) والمقعد مرة واحدة برّه. طالبين شايفين «فاضل مقعد واحد» يعدّوا الاتنين ويكتبوا الاتنين → السكشن فوق سعته فحص المقعد نزل جوّه الترانزاكشن بعد الكتابة، وRollback لو راح. والرفض بيرجّع Alternatives — بدايل نفس طريقة التدريس، حيّة دلوقتي، بأزرارها — فالطالب يشوف «المقعد راح، دول المتاح» في نداء واحد
نتيجة لازمة: الموبايل مايكاشّش لستة السكاشن ولا SectionsWithSeats. الأرقام دي بتبوظ مع الوقت — كل فتح للبيكر = نداء جديد.

القوانين السكشن بيتعرض ومقفول بسبب — مش بيتخفي

دورة العمل (التابين وطرق التدريس) تصميم جديد. لكن قوانين التسجيل مش تصميم — مرجعها الويب فورمز، وأول نسخة من الشاشة اجتهدت فيها بدل ما تقراها. البورتال في UcRegisteredCourses وUCRegisterAddReplaceCourseDirect_SEC بيعرض كل السكاشن للطالب وبيقفل اللي مايتسجلش ويكتب السبب — وإحنا كنا بنخفيها.

الفرق مش شكلي: أكتر من نص سكاشن العملي والمحاضرات على الداتابيز دي ONLINE_FLG = 0 (شؤون التسجيل بتقعّد الدفعة يدوي). الطالب اللي قاعد في واحدة منها كان بيفتح البيكر فيلاقيه فاضي — لا سكشنه ظاهر ولا فيه زرار يحذفه. اتقفل عليه في سكشن من الشاشة اللي وظيفتها تطلّعه منه.

الشرطBlockedReasonالكلمة في البورتال
ONLINE_FLG = 0 (للطالب بس)NOT_ALLOW_ONLINENotAllowOnline
INTERNAL_FLG = 1 (للطالب بس)INTERNALInternal
IS_CLOSED = 1IS_CLOSEDIsClosed
تعارض امتحانEXAM_CONFLICTExamConflict
SEC_CONF_SC_SCHEDULE_DTL_ID مش nullSCHEDULE_CONFLICTScheduleConflict
المقاعد والانتظار خلصوا الاتنينFULLFull
الترتيب زي البورتال: التلاتة الأولى بوابات قاطعة، وبعدين الامتحان يغلب التعارض، والتعارض يغلب «مليان» — لأن التعارض حاجة الطالب ممكن يعمل فيها حاجة.

الانتظار زرار واحد — والـ SIS يقرر مقعد ولا طابور

«مليان» عند البورتال معناها NO_STUDENTS = Reg_Stud_Count و NO_STUD_WAIT = Wait_Stud_Count. يعني المقاعد خلصت والطابور فيه مكان = مش مليان: الصف يفضل شغّال، والتسجيل بيكتب WAIT_NO = عدد اللي في الطابور + 1.

اتشال: زرار «احجز مقعد» المنفصل. كان اختراع من ناحية الموبايل، وكان بيخلّي الطالب يختار بين حاجتين مش اختيار — هو مايقدرش يختار مقعد مش موجود. بقى نفس الزرار وبس الجملة بتتغيّر: «سجّل في قايمة الانتظار». وCourseReg/AddToWaitList اتشال كـ endpoint — الهاندلر بتاعه فضل جوّه RegisterNewCourse عشان المقرر اللي الطالب مش ماسكه أصلاً: الطابور هناك معناه تسجيل بحالة 7، مش صف سكشن.

الامتحانات القاعدة التلاتية — بالحرف

مكتوبة كده بالظبط في أربع كنترولز في البورتال:

OperationType != Replace && EXP_FOR_GRAD != 1 && EXM_CONF_ED_EXAM_SCHDL_ID != null

يعني تعارض الامتحان بيمنع السكشن بس لو العملية مش استبدال و الطالب مش متوقع تخرجه. الاستبدال معفى لأن امتحان المقرر أصلاً على جدول الطالب — النقل بين سكاشنه مش هيغيّر ده. والمتوقع تخرجه معفى عشان اللائحة تفضّل يمتحن متعارض على إنه يستنى ترم كمان.

المصاحب القاعدة حقيقية والأتمتة بتاعتها مكسورة

ED_COURSE_COREQ أربع أعمدة بس، والزوج جروب فيه صف IS_MAIN = 1 وجنبه IS_MAIN = 0. على الداتابيز دي ٣١ زوج، كلهم تمريض/إكلينيكي (NUR203↔CLN202، NUR201↔CLN201 …).

البورتال بيحاول يسجّل المصاحب تلقائياً — والكود غلط: بيبعت objEdCourseCoreq.EdCourseCoreqId، وده المفتاح الأساسي، في مكان رقم المقرر (المفاتيح ١..٦٢ وأرقام المقررات بتبدأ من ٤٣٢). الفحص اللي فوقه بيستخدم EdCourseId الصح، فالنية واضحة والتنفيذ مكسور.

اللي عملناه: بنطبّق الفحص ومابنكتبش. المقرر بيرجع ومعاه CoRequisites وCoRequisiteNote — «لازم يتاخد مع CLN201 — سجّله معاه» — والطالب يسجّله بنفسه. مافيش صف بيتكتب البورتال مش قادر يكتبه. ١٢٩ طالب على الترم الحالي عندهم نص زوج ناقص.

استثناء تجاوز التعارض بقى فلاج في البروفايل

البورتال بيقارن كود العميل بـ "ecu" / "baha" / "ucb" في السورس عشان يقرر هل السكشن المتعارض ينفع يتسجّل. نفس القرار بقى داتا: registration.allowConflictOverride في الـ Client Profile، مقفول افتراضياً. تشغيله لجامعة جديدة بقى تعديل بروفايل مش تعديل كود.

وNO_STUD_MAX ماتنفذتش عن قصد: مافيهاش أي استخدام في مسار التسجيل في البورتال — بتظهر بس في شاشات تجهيز السكشن وبتتقارن بسعة القاعة — وعلى الداتابيز دي هي مطابقة لـ NO_STUDENTS في كل الـ ١٨٥٤ سكشن. تنفيذها كان هيدي نفس الرقم بالظبط وكود يوهم إن فيه سعتين.

بيانات الاختبار سكاشن مزروعة — وإزاي تشيلها

الترم الحالي على Innovation_Demo كان بيعرض سكشن واحد لكل (مقرر × طريقة تدريس) في كل الحالات تقريباً، فتلات حالات من أربعة في البيكر ماكانش ينفع توصلها أصلاً: اختيار حقيقي بين بدائل، تعارض في الجدول، وسكشن مليان بطابور. مش عيب في الكود — الداتا هي اللي مافيهاش الحالات دي.

فاتزرعت ٧ سكاشن على خمس أزواج (مقرر × طريقة تدريس) بيمسّوا طلبة الاختبار فعلاً، و سعة سكشنين قايمين اتنزّلت لعدد المسجلين فيهم بالظبط عشان يقروا «مليان» — واحد بطابور مفتوح وواحد من غير، لأن الاتنين ردّين مختلفين على الشاشة. مافيش تسجيلات طلبة اتلمست، ومافيش جدول أي سكشن الطالب قاعد فيه اتغيّر.

الطالبالحالة اللي بيوصلها
abdelrahman.baker.24040153 الأربعة في شاشة واحدة — NUR203 نظري: متاح واسع · متاح بـ٣ مقاعد · مليان بطابور · متعارض مع CLN202. وCLN202 ميداني فيه بديل واحد لـ«انقل هنا»
ziad.shehata.25030040 CF127 معمل — سكشنه القديم مش معروض أونلاين، فبقى فيه بديل معروض
moaz.ibrahim.25060107 BSM114 محاضرة — أول سكشن معروض للطالب
abdelrahman.elsayed.24010068 BAS210 عملى — أول سكشن معروض للطالب
mohamed.mohamed.24040217 UNI101 محذوف ومعمله مليان من غير طابور — «مليان» من غير زرار، وفوقيهم «تراجع عن الإسقاط»
حسابين بس في شاشة الدخول — ومحمّلين. تسع حسابات بحالة لكل واحد معناها تسع دخلات عشان تشوف تسع حاجات، وده تعب على اللي بيجرّب مش تسهيل. فبقوا اتنين، وبينهم كل حاجة:
· abdelrahman.baker.24040153 (تمريض) — وحده فيه كل حاجة تقريباً: مسجل · محذوف · منسحب · مقرر مسجّل من غير سكشن · متطلب مصاحب ناقص · والثمانية أسباب في البيكر (متاح · انتظار · مليان · تعارض جدول · تعارض امتحان · مش متاح إلكتروني · لطلبة القسم · مقفول).
· ziad.shehata.25030040 (علاج طبيعي) — مقرر ناقصه معمل وراه ٥ سكاشن بحالات مختلفة · استبدال · محذوف · منسحب.
بترجع بجدول واحد. كل صف مزروع متعلّم NOTES = 'MOBILE-DEMO-SEED-20260910'، وكل الـ ids والقيم القديمة مسجّلة في ZZ_MOBILE_DEMO_SEED_20260910: امسح صفوف INSERTED_*، رجّع OldValues لصفوف CHANGED_CAP، وامسح الجدول. مافيش بروسيدجر ولا سكيما اتغيّرت.

القديم مجمّد، شغال، ومحدّش بيناديه

LoadRequest وGetNewCourses و GetUnDropCourse وGetCourseSections ماتغيّرش فيهم حرف — التطبيق المنشور شغال زي ما هو. اتعمل راوت جديد بدل ما نزوّد flag على GetNewCourses لسبب واحد: الـ flag ممكن ما يتبعتش، وساعتها الهجرة تبقى نُص هجرة ومحدّش واخد باله. الراوت الجديد إما اتنده أو لأ.

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

20 مرجع البرمجة — التسجيل المطوّر

الفصل اللي فات بيقول ليه الشاشة بقت كده. الفصل ده مكتوب لواحد بيكتب الشاشة: ترسم إيه، وتبعت إيه، وتعمل إيه بالردّ. كل الحقول والأرقام اللي هنا مقروءة من ردّ السيرفس الشغّالة — مش من الذاكرة.

ابدأ من هنا إنت بتبني إيه بالظبط

شاشة واحدة، تابين، وثلاث نداءات مش أكتر. كل اللي بعد كده تفاصيل.

اللي بترسمهجاي منين
تاب «مقرراتي» — كروت المقررات اللي الطالب مسجّلها (بكل الحالات) POST CourseReg/v2/MyCourses
تاب «المتاح» — كروت اللي لسه ينفع يتضاف POST CourseReg/v2/AvailableCourses
الشيت اللي بيفتح لما يدوس على سطر طريقة تدريس POST CourseReg/GetSectionsToReplace

وأهم فكرة في الموضوع كله:

🧩
الكارت مش وحدة التسجيل — السطر هو. المقرر الواحد ممكن يتدرّس محاضرة ومعمل، والطالب ممكن يكون ماسك المحاضرة ومش ماسك المعمل. يعني هو «مسجّل» و«مش مسجّل» في نفس الوقت.

عشان كده الكارت جوّاه سطر لكل طريقة تدريس (TeachingMethods[])، وكل سطر ليه حالته وزراره. الكارت نفسه بيحمل اسم المقرر وحالته العامة بس.

ولو عايز تعرف إنت بتشتغل صح ولا لأ، دي أربع قواعد تختصر الدليل كله:

#القاعدةليه
١ماتحسبش حاجة بنفسك. المقاعد والتعارضات والصلاحيات كلها محسوبة وجايالك جاهزة التعارض بيتحسب من جدول الطالب كله — التطبيق ماعندوش نُصّ المعلومة دي
٢الزرار جايلك ومعاه نداءه. إنت بترسم Label وتبعت Endpoint وBody زي ما هما أكتر مكان اتغلط فيه قبل كده هو تركيب الـ body باليد على مقرر بأكتر من طريقة
٣صدّق Data.Status — مش result ولا الـ HTTP الرفض بييجي بـ 200 و"result": "Ok"
٤بعد أي كتابة، نادِ التابين. مش واحد الكتابة الواحدة بتغيّر الاتنين: سجّلت من «المتاح» → ظهر في «مقرراتي» واختفى من «المتاح»

الردّ شكل اللي هيوصلك — بالحقل

النداءالـ body اللي بتبعته
POST CourseReg/v2/MyCourses { "LANG": "ar" }
POST CourseReg/v2/AvailableCourses { "LANG": "ar" }
POST CourseReg/GetSectionsToReplace { "EdCourseId": 528, "EdCodeCourseTeachingId": 33, "LANG": "ar" }
POST CourseReg/GetAllowedActions{ }
🔑
رقم الطالب مابيتبعتش. الخدمة بتاخده من التوكن، وأي EdStudId تحطه في الـ body بيتكتب فوقه. فمتبعتوش أصلاً — في القراءة والكتابة.

«مقرراتي»:

Data
  Agenda        { Register, Add, Drop, Withdraw, RegisterSection, IsActiveStudent, AnyOpen }
  IncompleteCount : int            <- البادچ اللي على التاب
  Courses[]
    EdCourseId, EdStudCourseRegId, CourseCode, CourseNameAr, CourseNameEn, CreditHours
    State     : "ACTIVE" | "DROPPED" | "WITHDRAWN" | "WAITING" | ...
    StateLabelAr / StateLabelEn    <- الكلمة الجاهزة للعرض
    StatusId  : 1|2|6|7|...        <- لو محتاج تفرّع برقم
    IsComplete : bool              <- ماسك سكشن من كل طريقة تدريس؟
    TeachingMethods[]              <- سطور الكارت
    MissingTeachingMethods[]       <- نفس الشكل، الناقص بس
    Sections[]                     <- اللي هو قاعد فيها، مسطّحة
    CoRequisites[] , CoRequisiteNote
    Flags   { CanDrop, CanUnDrop, CanReplaceSection, CanDeleteSection, IsDropped, BlockedReason }
    Display { Title, Subtitle, Place, Meta, Badge, BadgeTone, Note }
    Actions[]                      <- الأزرار
  Error : string|null

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

Data
  PartialCount : int               <- مقررات مسجّلة لسه ناقصها طريقة
  Courses[]
    Mode : "REGISTER_COURSE"       <- ماسكهوش خالص
         | "ADD_METHOD"            <- ماسكه، ودي الطريقة الناقصة بس
    PrereqProblem                  <- في REGISTER_COURSE بس
    Sections[]                     <- SectionOption كامل (مش مسطّح)

الشيت (البيكر) — الردّ سلال، مش لستة واحدة:

Data
  CurrentSections[]      <- اللي هو فيها من الطريقة دي (ممكن تكون فاضية)
  Sections[]             <- يتاخدوا دلوقتي
  WaitListSections[]     <- يتاخدوا برضه — وبيحطّوه في الطابور
  ConflictingSections[]  <- مطفيين
  FullSections[]         <- مطفيين
  BlockedSections[]      <- مطفيين
  TeachingMethods[] , MissingTeachingMethods[] , NeedsSection
  RequiresUnDrop , NextAction , ExamClashes[] , Reason , Summary , Display
💡
ارسمها لستة واحدة على جزئين: فوق [...Sections, ...WaitListSections] (دول اللي زرارهم شغّال)، وتحتيهم تحت عنوان «مش متاحة دلوقتي» [...ConflictingSections, ...FullSections, ...BlockedSections] مطفيّين ومكتوب تحت كل واحد سببه. ده اللي الديمو بيعمله.

أهم سؤال ليه مقرر أنا مسجّله بيظهر في «المتاح»؟

لأن وحدة التسجيل مش المقرر — هي المقرر × طريقة التدريس. الطالب اللي ماسك نظري UNI101 ومش ماسك المعمل هو مسجّل ومش مسجّل في نفس الوقت، والنص الناقص ده لازم يكون ليه مكان في تاب الإضافة — مش مستني الطالب يعرف لوحده إنه يرجع لتاب «مقرراتي».

فتاب «المتاح» بيرجّع حاجتين مختلفتين، ومعاهم أربع علامات بتفرّق بينهم:

الحقلمقرر جديد بالكاملمقرر ماسكه وناقصه طريقة
ModeREGISTER_COURSEADD_METHOD
EdStudCourseRegIdمش موجود موجود — ده تسجيله الحالي
Display.Badgeمافيش «ناقص» بـ BadgeTone: "warning"
Display.Subtitleمافيش «لسه ناقص: معمل»
TeachingMethods[]كل طرق تدريس المقرر الناقصة بس — المحاضرة اللي هو ماسكها مش راجعة
Sections[].Actions[0] → CourseReg/RegisterNewCourse → CourseReg/SetSection
// كارت تاب «المتاح» — فرّق بين الاتنين وخلاص
if (c.Mode === 'ADD_METHOD') {
  return <Card title={c.Display.Title}
               badge={c.Display.Badge}          // «ناقص»
               hint={'إنت مسجّل في المقرر ده بالفعل — ' + c.Display.Subtitle}
               methods={c.TeachingMethods} />;  // الناقصة بس
}
return <Card title={c.Display.Title} badge="جديد" methods={c.TeachingMethods} />;
🧮
وعندك PartialCount في نفس الردّ = عدد المقررات اللي من النوع التاني. ينفع تحطه كعدّاد جنب عنوان في الشاشة («ناقصك سكشن في مقررين»)، أو تستخدمه تتأكد إنك بترسم الصح.
🔁
والتابين بيكمّلوا بعض، مابيتقاطعوش: نفس المقرر بيبان في «مقرراتي» بالطريقة اللي هو ماسكها، وفي «المتاح» بالطريقة الناقصة بس. وأول ما يكمّل آخر طريقة ناقصة بيختفي من «المتاح» خالص وIsComplete بيبقى true.

والمقرر المحذوف مش في المتاح أصلًا — إرجاعه من «مقرراتي» بس.

السطر TeachingMethod — الحاجة اللي بترسمها أكتر من غيرها

الحقليعني إيهبتعمل بيه إيه في الشاشة
TeachingMethodAr / Enمحاضرة · معمل · عملي · إكلينيكي… عنوان السطر
State REGISTERED · NOT_REGISTERED · NO_SECTIONS_OFFERED · UNKNOWN شكل السطر والدوسة — الجدول اللي بعده
EdCodeCourseTeachingId مفتاح «المقرر × الطريقة» ده اللي بتبعته للشيت، وبتربط بيه سكاشن تاب المتاح
TeachingMethodId نوع الطريقة (١ محاضرة · ٢ تمارين · ٣ معمل · ٤ عملي · ١٢ إكلينيكي · ٢١ نظري · ٢٢ ميداني) للأيقونة، وللربط لو الأولاني رجع null
RegisteredSectionIds[] أرقام السكاشن اللي هو فيها من الطريقة دي بتفلتر بيها Sections عشان تكتب ميعاده ومكانه تحت اسم الطريقة
RegisteredEdStudGroupIds[] صفوف تسجيله الحذف بيشتغل عليها — بس إنت مش محتاج تلمسها، الزرار جاهز
SectionsAvailableToTake كام سكشن فيه مكان ومش اللي هو قاعد فيه الرقم اللي تكتبه: «اختار سكشن (٣ متاح)»
SectionsOfferedكل سكاشن الطريقة دي صفر = الكلية ما نزّلتش سكاشن. مش غلط عندك
IsRegistered · CanPick اختصارين محسوبين من State لو مش عايز تكتب switch
⚠️
الأرقام دي مابتحسبش التعارض. «٣ متاح» يعني فيه تلات سكاشن فيهم مكان — مش إن الطالب يقدر ياخد التلاتة. الشيت هو اللي بيجاوب على ده بالظبط لما يتفتح، لأن حساب التعارض لكل سكشن على حدة غالي جدًا على لستة كاملة.

وده شكل رسم الكارت في الكود:

{course.TeachingMethods.map(k => {
  const held = course.Sections.filter(s => k.RegisteredSectionIds.includes(s.SC_SCHEDULE_DTL_ID));
  if (k.State === 'NO_SECTIONS_OFFERED')
    return <Row title={k.TeachingMethodAr} note="الكلية لسه ما نزّلتش سكاشن" disabled />;
  if (k.State === 'REGISTERED')
    return <Row title={k.TeachingMethodAr} ok
                subtitle={held[0]?.display?.Subtitle} place={held[0]?.display?.Place}
                onPress={() => openSheet(course.EdCourseId, k.EdCodeCourseTeachingId)} />;
  return <Row title={k.TeachingMethodAr}
              note={'اختار سكشن (' + k.SectionsAvailableToTake + ' متاح)'}
              onPress={() => openSheet(course.EdCourseId, k.EdCodeCourseTeachingId)} />;
})}

السكشن SectionOption — اللي بيتعرض في الشيت

الحقليعني إيه
ScScheduleDtlIdرقم السكشن — المفتاح اللي كل حاجة ماشية عليه
Status AVAILABLE · WAITLIST · CONFLICT · EXAM_CONFLICT · FULL · BLOCKED · CURRENT
Blocked + BlockedReason مايتاخدش، والسبب بكود
GoesToWaitList المقاعد خلصت والطابور مفتوح — السكشن لسه يتاخد. مش زرار تاني
Mode PICK (اختيار جديد) · REPLACE (هينقل من سكشنه) · CURRENT (هو فيه)
Display النص جاهز: Title · Subtitle (اليوم والميعاد) · Place (القاعة والمبنى) · Meta (المقاعد) · Badge + BadgeTone · Note (جملة السبب أو التعارض)
Actions[]الزرار — أو زرار مطفي ومعاه DisabledReason
Conflict لو فيه تعارض: WithCourseCode · WithSectionNameAr · WithDaysAr · WithFromTime/ToTime — لو حابب تبني الجملة بشكلك
SeatsTotal / SeatsTaken / SeatsLeft المقاعد. SeatsLeft = -1 يعني بلا سقف — متعرضش رقم
WaitCapacity / WaitTaken / WaitLeftالطابور، ونفس قاعدة الـ -1
OnlineFlg · InternalFlg · IsClosed فلاجات الظهور. مهمة في حالة واحدة — اقرا التحذير اللي تحت
IsCurrent · EdStudGroupIdهو فيها دلوقتي، وصفّ تسجيله
ReplacesEdStudGroupIdهتحلّ محلّ أنهي سكشن
✅
التعارض بيسمّي المقرر. Display.Note بييجي جاهز كده: «بيتعارض مع NUR203 — سكشن ٢ (الأحد ٠٩:٠٠–١١:٠٠)». اعرضه زي ما هو. «فيه تعارض» لوحدها معلومة ملهاش لازمة — الطالب قدامه أربع سكاشن ومحتاج يعرف أنهي اختيار بيحلّ المشكلة.
🚪
حالة تستاهل تحذير في الشاشة: السكشن اللي الطالب قاعد فيه بييجي دايمًا Blocked: false (لازم كده، وإلا ماكنتش هترسمه). بص على OnlineFlg: لو false — أو InternalFlg بـ true أو IsClosed بـ true — يبقى شؤون الطلبة هي اللي حطّته هنا، والشيت عمره ما هيدّيه زرار يرجع بيه.

يعني «احذف السكشن» أو «أسقط المقرر» هنا باب في اتجاه واحد. ماتمنعش العملية — بس حذّره قبل التأكيد.

قرار السطر ← ترسمه إزاي والدوسة تودّي فين

Stateشكل السطرالدوسةاللي في الشيت
REGISTERED اسم الطريقة + ميعاد سكشنه ومكانه + علامة ✓ خضرا بتفتح الشيت سكشنه فوق، والبدايل تحته، وزرار احذف السكشن
NOT_REGISTERED اسم الطريقة + «اختار سكشن (٣ متاح)» + شيفرون بتفتح الشيت لستة السكاشن، وكل واحد وزراره
NO_SECTIONS_OFFERED اسم الطريقة + «الكلية لسه ما نزّلتش سكاشن» — رمادي مافيش — السطر مش زرار —
UNKNOWNزي NOT_REGISTERED من غير رقم بتفتح الشيتالشيت بيحسم
⚠️
NO_SECTIONS_OFFERED وNOT_REGISTERED لازم يبانوا مختلفين. الأولانية مشكلة عند الكلية والطالب ملوش فيها حاجة؛ التانية دوره هو. أول نسخة من الشاشة عرضتهم بنفس الشكل، فالطالب فضل يدوس على سطر مالوش جوّه حاجة.

قرار السكشن ← زرار ولا جملة

Statusالشكلالزرارالجملة تحته
AVAILABLEعادي شغّال — «خد السكشن ده» / «انقل هنا» / «سجّل»—
WAITLISTعادي + بادچ «الطابور» شغّال — «سجّل في قايمة الانتظار» «المقاعد خلصت — هتتحطّ في الطابور»
CURRENTمميّز بإطار + بادچ «إنت هنا دلوقتي» احذف السكشن—
CONFLICTمطفيمقفول «بيتعارض مع NUR203 — سكشن ٢ (الأحد ٠٩:٠٠–١١:٠٠)»
EXAM_CONFLICTمطفيمقفول «امتحانه في نفس ميعاد امتحان BS100»
FULLمطفيمقفول «اتملى والطابور اتقفل»
BLOCKEDمطفيمقفول حسب BlockedReason — الجدول اللي بعده
🎯
إنت مش محتاج تنفّذ الجدول ده بإيدك أصلًا: Actions[0].Enabled بيقولك الزرار شغّال ولا لأ، وDisabledReason وDisplay.Note فيهم الجملة. الجدول هنا عشان تفهم ليه السكشن طلع كده.

وBlockedReason بيتقري إزاي:

الكودالجملة اللي بتتعرضالطالب يعمل إيه
NOT_ALLOW_ONLINE «السكشن ده مش متاح للتسجيل الإلكتروني — التسجيل فيه من شؤون التسجيل» يروح شؤون الطلبة. مافيش حاجة في التطبيق
INTERNAL«السكشن ده لطلبة القسم» مش ليه — يختار غيره
IS_CLOSED«السكشن مقفول» يختار غيره
FULL«اتملى والطابور اتقفل» يختار غيره
SCHEDULE_CONFLICTجملة التعارض بالاسم يشيل سكشن المقرر التاني أو يختار غيره
EXAM_CONFLICTجملة الامتحان بالاسم مافيش حلّ من الشاشة — لازم يغيّر مقرر
🚫
السكشن المقفول بيتعرض، مابيتخفيش. ده مقصود: الطالب اللي مالقاش سكشن بيتصل بشؤون الطلبة؛ الطالب اللي شافه ومكتوب تحته السبب مابيتصلش.

قرار الكارت ← المسموح فيه إيه

Stateيبان فيالكارت شكله إيه
ACTIVE (1) مقرراتي + المتاح (لو ناقصه طريقة) كارت كامل: سطور طرق التدريس + لينك «أسقط المقرر» أحمر صغيّر
WAITING (7)مقرراتي بادچ «في الانتظار» + زرار «اخرج من الانتظار»
DROPPED (6)مقرراتي بس كارت مضغوط: سطرين وزرار «تراجع عن الإسقاط». من غير سطور طرق تدريس
WITHDRAWN (2)مقرراتي كارت للعرض بس — مافيش أزرار خالص
(4 · 5 · 9) انسحاب إجباري / إلغاء / بعذرمقرراتي للعرض بس
(3 · 12 · 15) غير مكتمل / معافى / مستمعمقرراتي للعرض بس
↩️
التراجع عن الإسقاط من «مقرراتي» بس. المقرر المحذوف مابيظهرش خالص في «المتاح»، لا كمقرر ولا كطريقة ناقصة. أول ما يرجع ACTIVE بيبان هناك عادي لو ناقصه سكشن.

وممكن تلاقي CanUnDrop: false ومعاه BlockedReason: "MAX_LOAD" — إرجاعه هيعدّي سقف الساعات. الزرار ساعتها مطفي والسبب مكتوب، مش يتضغط ويترفض.

المتطلب المصاحب مقررين بيتسجّلوا مع بعض

اللايحة بتربط مقررات ببعضها: NUR203 مع CLN202، NUR201 مع CLN201… ٣١ زوج، كل زوج مقررين بالظبط. لما الطالب يسجّل نص الزوج، السيرفس بتسجّل النص التاني معاه في نفس العملية.

🧩
المصاحب بيتسجّل من غير سكشن — عن قصد. المقرر بيبقى مسجّل وIsComplete بتاعه false، والطالب يختار سكاشنه من البيكر زي أي مقرر ناقص. ودي حالة إنت بترسمها أصلاً — نفس حالة المقرر اللي رجع من الإسقاط.

قبل الدوسة: كارت المقرر في تاب «المتاح» بيجيلك ومعاه الخطة كاملة.

"CoRequisitePlan": {
  "WillRegister": [                  <- اللي هيتسجّل معاه
    { "EdCourseId": 437, "CourseCode": "NUR105", "CreditHours": 2,
      "IsRegistered": false, "IsDropped": false,
      "RegisteredBefore": false,     <- خده في ترم فات؟ يبقى معفي
      "PassedBefore": false, "IsOffered": true }
  ],
  "PairCreditHours": 4,              <- المقرر + المصاحب
  "CreditRoom": 4,                   <- الفاضل تحت السقف (null = مفيش سقف)
  "Fits": true,                      <- false يعني الزرار مطفي
  "BlockedReason": null,
  "Note": "هيتسجّل معاه NUR105 (المجموع 4.00 ساعة) — تختار سكاشنه بعد كده."
}
✅
والزرار نفسه بيحمل الحكم: لو Fits: false بييجي Enabled: false ومعاه DisabledReason. ولو ماشي، بييجي Confirm — وهي الجملة اللي تعرضها في تأكيد قبل ما تبعت. إنت مش محتاج تحسب ولا تجمع ساعات.

بعد الكتابة: الردّ بيقول اللي حصل بالاسم.

POST CourseReg/RegisterNewCourse  ->  Data
  { "Status": true,
    "CoRegistered": [ { "CourseCode": "NUR105", ... } ],
    "Note": "اتسجّل معاه NUR105 — اختار سكاشنه دلوقتي." }
الحالةالكارت بيقول إيهالزرار
المصاحب هيتسجّل عادي «هيتسجّل معاه NUR105 (المجموع ٤ ساعة)» شغّال + Confirm
الاتنين مع بعض بيعدّوا السقف «المجموع ٧ ساعة وإنت فاضلك ٥ — اسقط مقرر الأول» مطفي · COREQ_MAX_LOAD
المصاحب متطلبه السابق مش مكتمل «لازم يتاخد مع NUR103، ومتطلبه السابق مش مكتمل» حسب الفلاج · COREQ_PREREQ_NOT_MET
المصاحب مش مطروح الفصل ده «لازم يتاخد مع CLN201، وهو مش مطروح — راجع شؤون الطلبة» حسب الفلاج · COREQ_NOT_OFFERED
الطالب خد المصاحب في ترم فات مافيش خطة أصلاً — القاعدة «يتاخدوا مع بعض» مش «يتاخدوا تاني» شغّال عادي

والإسقاط والتراجع بيمشوا مع بعض برضه:

العمليةاللي بيحصلاللي بيوصلك
DropCourse بيسقط الاتنين في عملية واحدة CoRegistered[] + Note: «اتسقط معاه NUR105»
وقبلها زرار drop بييجي ومعاه Confirm بنفس الكلام
UnDropCourse بيرجّع الاتنين، والسقف بيتحسب على المجموع CoRestored[] + Note: «رجع معاه NUR105 — اختار سكاشنه هو كمان»
قايمة الانتظار مابيسجّلش المصاحب — مقعد لسه ماجاش Note: «هيتسجّل معاه NUR105 أول ما تاخد مقعد»
🎛️
فلاجين في بروفايل العميل (شاشة الأدمن ← Course registration)، لأن دي قرارات الجامعة مش قرارنا:
• المصاحب مش مطروح → «سجّل وحذّر» (الافتراضي) أو «ارفض».
• إسقاط/إرجاع نص الزوج → «حرّك الاتنين» (الافتراضي) أو «حرّك اللي اختاره وحذّر».

الفلاج التاني بيحكم الاتجاهين عن قصد — جامعة بتسمح بإسقاط نص زوج وبترفض إرجاع نصه بتقول للطالب قاعدتين مختلفتين.
🧮
ليه ده مهم عندنا بالذات: الزوج بيكلّف ٦ أو ٧ ساعات والسقف ٩ — يعني «المقرر ده ومصاحبه مش هيدخلوا» هي الحالة الطبيعية مش الاستثناء. عشان كده الرسالة بتسمّي الاتنين وبتقول المجموع والفاضل في جملة واحدة.

الأكشنز الزرار جايلك ومعاه نداءه

كل زرار في الشاشة بييجي من السيرفس بالشكل ده. اللي بتعمله: ترسم Label، ولو Enabled = false تطفّيه وتكتب DisabledReason، ولو اتضغط تبعت Endpoint بالـ Body زي ما هو.

{
  "Key": "pickSection",
  "Label": "خد السكشن ده",
  "Tone": "primary",
  "Enabled": true,
  "DisabledReason": "",
  "Endpoint": "CourseReg/SetSection",
  "Body": { "EdCourseId": 528, "ScScheduleDtlId": 2279, "EdStudGroupId": 0 }
}
async function run(action) {
  if (!action.Enabled) return;                       // مطفي = متبعتش
  const res = await post(action.Endpoint, action.Body);
  const d = res.Data || {};
  if (d.Status !== true) return showRefusal(d);      // القاعدة الذهبية تحت
  if (d.Reason === 'PLACED_ON_WAITLIST') toast('تمّ — إنت في قايمة الانتظار');
  await Promise.all([loadMyCourses(), loadAvailable()]);   // التابين، مش واحد
}
Keyبيعمل إيهبيروح فين
registerتسجيل مقرر جديد + سكشنه CourseReg/RegisterNewCourse
pickSection على الكارت يفتح الشيت — قراءة مش كتابة CourseReg/GetSectionsToReplace
pickSection جوّه الشيت ياخد سكشن لطريقة فاضيةCourseReg/SetSection
replaceSectionينقل من سكشن لسكشن الشيت من الكارت، وSetSection جوّه
deleteSectionيشيل السكشن والمقرر يفضل مسجّل CourseReg/DeleteSection
dropيسقط المقرر كلهCourseReg/DropCourse
undropيتراجع عن الإسقاطCourseReg/UnDropCourse
cancelWaitيخرج من الطابورCourseReg/CancelWaitList
replaceCourseيبدّل مقرر بمقررCourseReg/ReplaceCourse
❓
وConfirm لو موجود، اعرضه واستنى موافقة قبل ما تبعت. ده بيتحط على الأكشن اللي بيعمل حاجة الليبل مابيقولهاش — «الإسقاط ده هيسقط CLN202 معاه»، «التسجيل ده هيسجّل NUR105 كمان». السيرفس هي اللي بتقرر أنهي أكشن يستاهل سؤال.
🧷
نفس الـ Key ممكن يوّدي لإندبوينتين — pickSection على الكارت بيفتح الشيت، وجوّه الشيت بيكتب. اتبع Endpoint دايمًا، وسيب Key للأيقونة والتفريع بس.
🛑
ليه متبنيش الـ body بنفسك؟ SetSection بيعمل «اختيار» ولا «نقل» حسب EdStudGroupId اللي بعتّه. المقرر اللي بيتدرّس بتلات طرق عنده تلات صفوف مختلفة، ولو بعتّ صفّ المحاضرة وإنت واقف على المعمل، هيتشال المحاضرة — والسيرفس هتنفّذ اللي بعتّه بالظبط. الزرار الجاهز بيلغي المشكلة دي خالص.

القاعدة الذهبية صدّق Data.Status — مش result

المغلّف القديم بيرجّع "result": "Ok" وHTTP 200 حتى لما العملية ترفض. لو التطبيق بيتأكد من الـ HTTP بس، التسجيل اللي اترفض هيبان ناجح للطالب — وده حصل فعلاً.

// رفض حقيقي — بـ HTTP 200
{
  "result": "Ok",
  "Data": {
    "Status": false,
    "Error": "This would take the student to 11.00 credit hours, over the 9.00 limit",
    "Reason": "MAX_LOAD",
    "Conflict": null,
    "Alternatives": []
  }
}
✅
if (res.Data.Status !== true) → اعرض الرفض. وError جملة إنجليزي من محرّك التسجيل — متعرضهاش للطالب. Reason هو الكود اللي بتترجمه، والجدول اللي تحت فيه الترجمة جاهزة.
🧭
وفي حالتين الرفض بييجي ومعاه حل: Conflict بيقول اتعارض مع أنهي مقرر وسكشن، وAlternatives[] بيدّيك سكاشن بديلة من نفس طريقة التدريس لسه فيها مكان — وكل واحد معاه Display وActions جاهزين. اعرضهم في نفس الديالوج وخلّي الطالب يختار من غير ما يقفل ويفتح تاني.
🎟️
وحالة نجاح شكلها غريب: Status: true + Reason: "PLACED_ON_WAITLIST" = نجحت بس بشكل مختلف — المقعد راح والطالب اتحطّ في الطابور. الرسالة لازم تقول كده، مش «تم التسجيل» ساكت.

مرجع كل أكواد الرفض — Reason

Reasonحصل إيهتكتب للطالب إيهوتعمل إيه
MAX_LOADهيعدّي سقف الساعات «العدد ده هيوصّلك ١١ ساعة والسقف ٩ — اسقط مقرر الأول» اعرض ساعاته الحالية والسقف جنب الرسالة
SEAT_TAKENآخر مقعد راح وهو بيضغط «المقعد راح دلوقتي حالاً» اعرض Alternatives فورًا — دي الحالة اللي اتعملت عشانها
SECTION_FULLمقاعد وطابور خلصوا «السكشن اتملى والطابور اتقفل»اعرض Alternatives
SCHEDULE_CONFLICT · TIME_CLASH تعارض مواعيد «بيتعارض مع NUR203 — سكشن ٢ (الأحد ٠٩:٠٠–١١:٠٠)» Conflict فيه الاسم والميعاد
EXAM_CONFLICTتعارض امتحانات «امتحانه في نفس ميعاد امتحان BS100» مافيش حلّ من الشاشة
SECTION_NOT_AVAILABLE السكشن مش متاح إلكتروني / مقفول / لطلبة القسم «السكشن ده مش متاح للتسجيل الإلكتروني» اعمل ريفريش للشيت — البيانات اتغيّرت
COURSE_DROPPEDالمقرر محذوف «المقرر ده محذوف — ارجعه من «مقرراتي» الأول» ودّيه لتاب مقرراتي
ALREADY_REGISTEREDمسجّل فيه بالفعل «إنت مسجّل في المقرر ده»ريفريش — شاشتك قديمة
NOT_DROPPED · NOT_REGISTERED طلب على مقرر مش في الحالة دي «المقرر مش محذوف / مش في تسجيلاتك»ريفريش
WINDOW_CLOSEDفترة العملية مقفولة «فترة التسجيل مقفولة دلوقتي» ماكانش المفروض توصل هنا — اطفي الزرار من Agenda
STUDENT_BLOCKEDالطالب موقوف «حسابك موقوف عن التسجيل — راجع شؤون الطلبة» اقفل الشاشة كلها من Agenda.IsActiveStudent
GROUPED_REGISTRATIONالكلية مسجّلة المجموعة كلها «تسجيل السكاشن للمقرر ده بيتم من الكلية»اقفل الشيت واكتب السبب
COREQ_MAX_LOADالمقرر ومصاحبه مع بعض بيعدّوا السقف «NUR203 لازم يتاخد مع CLN202 — المجموع ٧ ساعة وإنت فاضلك ٥» ماكانش المفروض توصل هنا: الزرار بييجي مطفي من تاب المتاح
COREQ_NOT_OFFEREDالمصاحب مش مطروح الفصل ده «لازم يتاخد مع CLN201، وهو مش مطروح — راجع شؤون الطلبة» بيظهر بس لو الجامعة مفعّلة «ارفض»
COREQ_PREREQ_NOT_METمتطلب المصاحب مش مكتمل «لازم يتاخد مع NUR103 ومتطلبه السابق مش مكتمل» زيها
COREQ_REFUSEDالمصاحب اترفض وقت الكتابة «المقرر المصاحب اترفض، فالاتنين ماتسجلوش» مفيش نص تسجيل — العملية اترجعت بالكامل
SECTION_REQUIREDتسجيل من غير سكشن —باج عندك — الزرار الجاهز عمره ما بيعمل كده
PLACED_ON_WAITLISTنجاح — اتحط في الطابور «تمّ — إنت في قايمة الانتظار» ريفريش — والمقرر هيبان WAITING لو كان تسجيل مقرر جديد

النوافذ اطفي الزرار قبل ما يترفض

كل عملية ليها فترة مفتوحة في تقويم الفصل. Agenda بيرجع مع التابين فمفيش نداء زيادة. الزرار اللي فترته مقفولة يتعرض مطفي ومكتوب سببه — مش يتضغط ويترفض.

الفلاجبيفتح إيه في الشاشة
Registerتسجيل مقرر جديد
Addالإضافة بعد بداية الفصل — والتراجع عن الإسقاط
RegisterSectionاختيار وتغيير وحذف السكاشن
Dropالإسقاط
Withdrawالانسحاب (طلب، مش تنفيذ فوري)
IsActiveStudentلو false اقفل كل حاجة
AnyOpenلو false اعرض «مفيش فترة تسجيل مفتوحة» بدل الشاشة
🧮
ولو محتاج ساعات الطالب والسقف عشان تعرضهم فوق الشاشة، هما في CourseReg/GetAllowedActions باسم CurrentCreditHours وMaxCreditHours.

الورك فلوهات تسعة مسارات — كل واحد بنداءاته بالترتيب

دول كل المسارات اللي الشاشة بتعملها. كل خطوة مكتوب جنبها الإندبوينت، والأخضر معناه كتابة. وبعد أي كتابة القاعدة واحدة: اقرا Data.Status، وبعدين نادِ التابين.

ونفس التسعة موجودين في كوليكشن الـ Postman بنفس الترتيب ونفس الأسامي — فتقدر تشغّل اللي بتقراه وتشوف الردّ الحقيقي قبل ما تكتب سطر.

ورك فلو ١ · تسجيل مقرر جديد

من تاب «المتاح» — مقرر الطالب ماسكهوش خالص، والمود REGISTER_COURSE.

1
هات المتاح
POST CourseReg/v2/AvailableCourses { "LANG": "ar" }
المقرر جايلك ومعاه TeachingMethods وSections — مافيش نداء تاني لعرض سكاشنه.
بيدّيCourses[] (Mode = REGISTER_COURSE)
2
اعرض طرق تدريسه
من الرد نفسه — من غير نداء
اربط Sections بطرق التدريس بـ EdCodeCourseTeachingId. السكاشن مسطّحة عمداً عشان الربط يبقى عندكم.
3
الطالب يختار سكشن ويدوس
POST CourseReg/RegisterNewCourse (من action.Body)
ابعت action.Body زي ما هو. لو السكشن GoesToWaitList السيرفس بتحوّله لتسجيل انتظار لوحدها وترجّع PLACED_ON_WAITLIST.
بيدّيStatus · Reason · Conflict · Alternatives
4
اقرا الردّ
Data.Status === false ? اعرض الرفض : كمّل
أشهر رفض هنا: MAX_LOAD وSCHEDULE_CONFLICT وSEAT_TAKEN.
5
ريفريش للتابين
v2/MyCourses + v2/AvailableCourses
لو المقرر بيتدرّس بأكتر من طريقة، هيفضل ظاهر في «المتاح» — بس بالطرق الناقصة بس، وهيبان في «مقرراتي» بـ IsComplete = false.

ورك فلو ٢ · مقرر ناقصه طريقة تدريس

الطالب ماسك المحاضرة ومش ماسك المعمل. الحالة دي ليها مدخلين بيوصّلوا لنفس البيكر.

1
من «مقرراتي»: السطر اللي حالته NOT_REGISTERED
POST CourseReg/v2/MyCourses
أو من «المتاح»: نفس المقرر بـ Mode = ADD_METHOD.
بيدّيMissingTeachingMethods[] · IncompleteCount
2
افتح البيكر على الطريقة دي بالظبط
POST CourseReg/GetSectionsToReplace { EdCourseId, EdCodeCourseTeachingId }
ابعت EdCodeCourseTeachingId. من غيره البيكر بيرجّع سكاشن المقرر كله وبيبان إنه بيعرض سكاشن طريقة تانية.
بيدّيSections · WaitListSections · Conflicting · Full · Blocked
3
اعرض السلال
المتاح فوق · المقفول تحت مطفي بسببه
CurrentSections هنا هترجع فاضية — طبيعي، هو مش ماسك حاجة من النوع ده.
4
يدوس «خد السكشن ده»
POST CourseReg/SetSection { EdCourseId, ScScheduleDtlId, EdStudGroupId: 0 }
EdStudGroupId = 0 هي اللي بتقول «ده اختيار جديد مش نقل».
بيدّيStatus · Reason
5
ريفريش
التابين
لو دي كانت آخر طريقة ناقصة: IsComplete بقى true، والمقرر اختفى من «المتاح» خالص.

ورك فلو ٣ · تغيير سكشن مسجّل

نفس طريقة التدريس، سكشن تاني. عملية واحدة — مش حذف وبعده إضافة.

1
السطر حالته REGISTERED
POST CourseReg/v2/MyCourses
بيدّيRegisteredSectionIds · RegisteredEdStudGroupIds
2
افتح البيكر
POST CourseReg/GetSectionsToReplace { EdCourseId, EdCodeCourseTeachingId }
سكشنه الحالي بيرجع في CurrentSections بـ Status = CURRENT، والباقي بـ Mode = REPLACE ومعاهم ReplacesEdStudGroupId.
بيدّيCurrentSections[] + باقي السلال
3
يدوس «انقل هنا»
POST CourseReg/SetSection { EdCourseId, ScScheduleDtlId, EdStudGroupId: القديم }
كله جوّه ترانزاكشن واحدة: لو الجديد اترفض، القديم مابيتشالش. مافيش لحظة الطالب فيها من غير سكشن.
بيدّيStatus · Reason · Alternatives
4
لو اترفض بـ SEAT_TAKEN
اعرض Alternatives[] في نفس الديالوج
دي حالة «آخر مقعد راح وإنت بتضغط» — والبدايل جاية من نفس طريقة التدريس ومعاها أزرارها.
5
ريفريش
التابين

ورك فلو ٤ · حذف السكشن ← اختيار بديل

حذف السكشن مش إسقاط المقرر. المقرر بيفضل مسجّل، وطريقة التدريس بتبقى فاضية.

1
يدوس «احذف السكشن»
POST CourseReg/DeleteSection { EdCourseId, EdStudGroupId }
زرار لكل سكشن، مش واحد للمقرر. المقرر اللي له محاضرة ومعمل هتلاقي على كارته زرارين حذف، وكل واحد فيه الـ body بتاع السكشن بتاعه. اربط كل زرار بسطره.
بيدّيStatus · Display
2
ريفريش «مقرراتي»
POST CourseReg/v2/MyCourses
IncompleteCount زاد واحد — البادچ على التاب بيتحرّك.
بيدّيState = NOT_REGISTERED · IsComplete = false
3
افتح البيكر واختار بديل
GetSectionsToReplace ← SetSection
من هنا الحكاية بقت ورك فلو رقم ٢ بالظبط.

ورك فلو ٥ · سكشن مليان ← قايمة الانتظار

مفيش زرار «احجز مقعد». نفس زرار التسجيل — واللي بيتغيّر هو الليبل بس.

1
السكشن بيرجع في WaitListSections
POST CourseReg/GetSectionsToReplace
«مليان» عندنا يعني المقاعد والطابور خلصوا — دي حالة FULL ودي بس اللي بتطفّي الزرار.
بيدّيStatus = WAITLIST · GoesToWaitList = true
2
اعرض نفس الزرار بليبل مختلف
action.Label = "سجّل في قايمة الانتظار"
الليبل جايلك جاهز من السيرفس. الجملة تحته: «المقاعد خلصت — هتتحطّ في الطابور».
3
يدوس
POST CourseReg/SetSection (أو RegisterNewCourse لو مقرر جديد)
الردّ بيرجع Status: true ومعاه Reason — يعني نجح، بس في الطابور مش في مقعد.
بيدّيStatus = true · Reason = PLACED_ON_WAITLIST
4
الرسالة
«تمّ — إنت في قايمة الانتظار»
متقولش «تم التسجيل» ساكت. ده نجاح بشكل مختلف، والطالب لازم يعرف. واقرا الرسالة من Reason، مش من حالة المقرر — للسبب اللي تحت.
5
وحالة المقرر بعديها بتفرق
حسب إنت سجّلت مقرر ولا نقلت سكشن
مقرر جديد دخل الطابور → التسجيل نفسه بيتكتب بحالة ٧، فبيرجع WAITING وعليه زرار إلغاء الانتظار. لكن نقل سكشن جوّه مقرر هو ماسكه خلاص → رقم الدور بيتكتب على صفّ السكشن والمقرر بيفضل ACTIVE.
6
الخروج من الطابور
POST CourseReg/CancelWaitList { EdCourseId }
الزرار بيبان على المقرر اللي حالته WAITING في «مقرراتي» — يعني الشكل الأول بس.

ورك فلو ٦ · إسقاط مقرر

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

1
الزرار من Flags.CanDrop
POST CourseReg/v2/MyCourses
في الشاشة ده لينك أحمر صغيّر تحت الكارت، مش زرار كبير جنب أزرار السكاشن.
2
يدوس
POST CourseReg/DropCourse { EdCourseId }
الإسقاط بيشيل سكاشنه كلها معاه — فلما يرجع، بيرجع من غير أي سكشن. وخلّي بالك: لو سكشنه OnlineFlg: false يبقى مش هيعرف يرجع مكانه — حذّره قبل التأكيد.
بيدّيStatus · Error
3
ريفريش
التابين
المقرر اختفى من «المتاح» خالص، وفي «مقرراتي» بقى سطرين وزرار واحد.
بيدّيState = DROPPED

ورك فلو ٧ · التراجع عن الإسقاط

من «مقرراتي» بس. مافيش أي مدخل تاني ليه.

1
الكارت المحذوف
POST CourseReg/v2/MyCourses
لو CanUnDrop = false وBlockedReason = "MAX_LOAD" — الزرار مطفي والسبب مكتوب. إرجاعه كان هيعدّي السقف.
بيدّيFlags.CanUnDrop · Flags.BlockedReason
2
يدوس «تراجع عن الإسقاط»
POST CourseReg/UnDropCourse { EdCourseId }
الإندبوينت ده كتابة. كان في زرار بيروح لـ GetUnDropCourse — إندبوينت قراءة اتشال خلاص — فالكارت كان بيفضل محذوف من غير أي رسالة.
بيدّيStatus · TeachingMethods · NeedsSection · NextAction
3
المقرر رجع ACTIVE من غير سكاشن
من الرد: NeedsSection = true
وده مقصود: السكاشن بتتاخد من البيكر اللي بيعرف مين لسه فيه مقعد النهاردة. عايز السيرفس تختار؟ ابعت AssignSection: true — بتختار واحد لكل طريقة، بشرط يكون معروض وفيه مقعد ومفيش تعارض، وبتسيبها لو مالقتش.
4
كمّل عادي
GetSectionsToReplace ← SetSection لكل طريقة
من هنا هو مقرر عادي — نفس ورك فلو ٢.

ورك فلو ٨ · تبديل مقرر بمقرر

يسيب مقرر مسجّل وياخد مكانه واحد تاني — في عملية واحدة، عشان مايقعش في نُصّ الطريق من غير الاتنين.

1
هات المقرر الجديد وسكشنه
POST CourseReg/v2/AvailableCourses
بيدّيToEdCourseId · ScScheduleDtlId · CourseCode · CreditHours
2
نفّذ التبديل
POST CourseReg/ReplaceCourse { FromEdCourseId, ToEdCourseId, ScScheduleDtlId, GroupId, CourseCode, CreditHours }
لو التسجيل الجديد اترفض، القديم مابيتسابش.
بيدّيStatus · Error
3
ريفريش
التابين

ورك فلو ٩ · قراءات مرجعية

حاجات بتحتاجها مرة على الشاشة، مش مع كل دوسة.

1
النوافذ المفتوحة
POST CourseReg/GetAllowedActions { }
نفس المحتوى بيرجع جوّه Agenda في التابين — فالنداء ده للشريط العلوي بس، ومش لازم.
بيدّيRegister · Add · Drop · Withdraw · RegisterSection
2
سكاشن طريقة تدريس
POST CourseReg/GetSectionsToReplace { EdCourseId, EdCodeCourseTeachingId }
ده البيكر. هو المكان الوحيد اللي بيحسب التعارض والمقاعد لكل سكشن على حدة.

تشيك ليست عشر حاجات عدّيها قبل ما تقفل التاسك

الغلطالصح
قراية result أو الـ HTTP status بس Data.Status !== true = رفض، حتى مع 200
عرض Error الإنجليزي للطالب ترجم Reason من جدول الأكواد
بناء الـ body باليد ابعت action.Body زي ما هو
فتح الشيت من غير EdCodeCourseTeachingId ابعته دايمًا — من غيره بترجع سكاشن المقرر كله جوّه سطر المعمل
زرار «احجز مقعد» منفصل نفس الزرار — الليبل بيتغيّر لوحده لما GoesToWaitList
إخفاء السكشن المقفول اعرضه مطفي ومكتوب تحته سببه
«فيه تعارض» من غير اسم Display.Note فيه اسم المقرر والسكشن والميعاد
زرار حذف واحد للمقرر زرار لكل سكشن — المقرر ممكن يكون فيه أكتر من صفّ
«تراجع عن الإسقاط» في تاب المتاح من «مقرراتي» بس — والمحذوف أصلًا مش بيظهر في المتاح
ريفريش لتاب واحد بعد الكتابة التابين — الكتابة الواحدة بتغيّر الاتنين

جرّب حسابات فيها الحالات كلها

أي باسورد بتعدّي — اللوجين بيتأكد من الهوية بس. الاتنين الأولانيين متحطوطين في شاشة دخول الديمو عشان متكتبش حاجة، والتالت موجود في كوليكشن الـ Postman.

الحسابينفع تجرّب بيه إيه
abdelrahman.baker.24040153
تمريض · في الديمو
الحالات: مسجّل · محذوف (٢) · منسحب (٢) · مقرر ناقصه معمل · ملاحظة مقرر مصاحب على NUR203.
وفي الشيت بتاع «نظري» بتاع NUR203 هتلاقي في شاشة واحدة: سكشنه الحالي، واتنين متاحين، وواحد WAITLIST زراره شغّال، والتلاتة المقفولين — NOT_ALLOW_ONLINE وINTERNAL وIS_CLOSED. ده أحسن مكان تظبط عليه شكل السكشن المطفي
ziad.shehata.25030040
علاج طبيعي · في الديمو
السكاشن: ٦ مقررات مسجّلة، تلاتة منهم محاضرة + معمل فينفع تجرّب «غيّر السكشن» و«احذف السكشن» على السطر الصح.
وعنده مقرر محذوف زراره مطفي بـ MAX_LOAD — لأنه أصلًا فوق سقف الساعات (١٢ من ٩). ده الشكل اللي لازم تتعامل معاه: زرار مطفي وسبب مكتوب
mohamed.mohamed.24040217
تمريض · في الـ Postman
التسجيل الجديد: ٦ ساعات من ٩، وقدامه مقررات في «المتاح» — فده الحساب اللي ينفع تجرّب عليه تسجيل مقرر فعلاً ينجح، وقايمة الانتظار، والتراجع عن الإسقاط
🧪
خد بالك وإنت بتجرّب: مافيش في النظام حاجة بتمسح التسجيل. أقرب حاجة للتراجع هي الإسقاط — والمقرر المسقوط بيخرج من تاب «المتاح» ومابيرجعش غير من «مقرراتي». يعني كل مرة تسجّل مقرر وتسقطه، بيقلّ واحد من المتاح على الحساب ده.

21 خطة الإنهاء — إيه اللي فاضل بالترتيب

46 خاصية ناقصة تمامًا و0 نص نص. بس مش كلهم نفس الأهمية ولا نفس الحجم — ده الترتيب اللي إحنا نرشّحه ولييه.

مرحلة 1 اللي بيمنع النزول للإنتاج أصلاً

من غير دول، التطبيق ما ينفعش ينزل — مش مسألة خصائص، دي مسألة أمان وصحة.

الشغلانةليه دلوقتيمين
التحقق الحقيقي من الباسورد
ActiveDirectory أو AzureAd
● الكود خلص
اتعمل الـ LDAP bind والتحقق من توكن مايكروسوفت شغالين (شاشة الدخول). اللي فاضل تركيب مش كود: بيانات الـ domain controller، أو App Registration على Azure، والموبايل يبعت id_token (الدليل). وInnovation لسه على «أي باسورد» بقصد عشان التيست. تركيب + موبايل
مراجعة تحديد الفصل لكل عميل لو غلط، اللوجين يرجّع فصل null وكل الشاشات تفضى. اتأكد من الـ 4 أوضاع مع كل داتابيز. باك إند + تركيب
ربط Info/Capabilities في الموبايل عشان الشاشات غير المتاحة تختفي بدل ما الزرار يضرب غلط قدام المستخدم. موبايل

مرحلة 2 الفلوس — أكبر قيمة وأكبر خطر

الكود متكتب ومتجرَّب من الأول للآخر على الـ Sandbox. اللي فاضل بيانات حقيقية + الحتة اللي بتربط الدفع بسند السداد. وخلي بالك: البيانات دي مبقتش بتوقّف الشغل — الموبايل يقدر يبني ويجرّب دلوقتي.

الشغلانةالوضعمحتاج إيه
بيانات بوابة واحدة على الأقل🔑 بيانات اختاروا بوابة (فوري أنسب للجامعات المصرية) وهاتوا الـ merchant code والمفتاح. الكود جاهز واتجرّب فعليًا — التغيير الوحيد هو الـ baseUrl والبيانات.
ربط الدفع بسند السداد✕ ناقص دلوقتي بنسجّل محاولة الدفع بس. لازم إنشاء FEE_STUD_VOUCHER + _ITEM عند بدء الدفع، وتسوية البنود لما يتسدد. دي أهم حتة ناقصة في المدفوعات.
المحفظة الجامعية✕ ناقص الرصيد دلوقتي في localStorage في الموبايل — وهمي تمامًا. محتاج قرار أول: جدول جديد ولا بند في FEE_STUD_FEE_ITEM؟
عنوان عام للـ callback🔑 بيانات السيرفس لازم يكون متاح على https عام عشان البوابة تعرف تبلّغنا بالسداد. مسار الاستقبال نفسه اتجرّب: التوقيع بيتحقق، والتحديث idempotent.
إقفال الـ Sandbox🧪 قبل الإنتاج اقفله من Profile → Sandbox وشغّل Readiness تاني. الصفوف اللي هتولّع أحمر دي هي الشغل اللي فاضل بالظبط. و/health لازم يرجّع "sandbox":[].

مرحلة 3 نقل الـ SQL اللي لسه

دي شغلانات واضحة المعالم: الإندبوينت موجود، الـ DTOs موجودة، والـ SP القديم موصوف في الكتالوج. مجرد نقل.

الشغلانةالشاشةمصدر الـ SQL
تسجيل / حذف / تراجع المقرراتتسجيل المقررات MOB_Insert_STD_CRS_REG_PLN + MOB_ED_STUD_GROUP_Insert — جوّه transaction واحدة
تسجيل واعتماد الغيابشاشات الدكتور ED_STUD_ABSENCE + Mob_ConfirmAttendance. خد بالك: الكود القديم كان بيبدّل EdStudId وEdCourseId بالغلط
جدول الدكتورالجدول EF join موصوف في staff spec
تغيير الباسورد على ADالدخول مربوط بمرحلة 1
صورة الطالبالملف الشخصي ستريم ملف بسيط
كود QR الحقيقيالبطاقة مكتبة QRCoder مضافة أصلاً — الـ handler بيرجّع فاضي، محتاج مراجعة

مرحلة 4 الخدمات الجديدة بالكامل

دي شاشات شغالة على داتا وهمية 100%. الخصائص معرّفة في الكتالوج ومتطلباتها من الجداول متحققة — بس مفيش handlers.

الخدمةالجداول الموجودة فعلاًالشغل المطلوب
الطلبات والوثائقGS_ATTCHD_DOC, GS_CODE_DOC_TYPE كتالوج أنواع + إنشاء طلب + حالات + ربط بالدفع
الانسحاب والاعتذارED_STUD_COURSE_REG, ED_STUD_SEMESTER إنشاء طلب + دورة موافقات + حساب المسترد
مواعيد الإرشادSV_STUD_ASK_ADV, SA_STF_MEMBER أوقات متاحة + حجز + إلغاء (+ لينك Teams — ده جاهز)
حجز القاعاتACCOM_ROOM, ACCOM_STUD_BOOK, ACCOM_WF_REQUEST_APPROVE 🎁 موديول السكن كامل موجود (28 جدول) بدورة موافقات جاهزة — استغلوه
الأنشطة والرحلاتED_STUD_ACTIVITY كتالوج + تسجيل + إلغاء
الإعلاناتSV_MESSAGE (بديل مقترح) قرار أول: جدول جديد ولا نستخدم الرسايل بتصنيف؟
تسجيل الحضور من الموبايلED_STUD_ABSENCE زرار «تسجيل حضوري» في الشاشة الرئيسية مش بيكتب حاجة. محتاج تحقق مكان/وقت

الأشياء اللي مش شغل برمجة — بيانات بس

📥

الحاجات دي بتظهر «فاضية» في التطبيق، بس الكود بتاعها خلص. محتاجة إدخال بيانات من الباك أوفيس:

  • جدول الامتحانات — ED_EXAM_SCHDL فاضي
  • الاستبيانات — مفيش استبيان منشور للفصل
  • الإشعارات — محدش بعت إشعار للطالب
  • الساعات المكتبية الحقيقية — اتحطّت بيانات تجريبية لـ 9 دكاترة؛ الحقيقي يدخلوه الدكاترة
  • بيانات «عن الجامعة» و«تواصل معنا» — بتتكتب مرة في الأدمن
  • بنود الرسوم المتأخرة — الطالب التجريبي سدّد كل حاجة، فمفيش بنود تظهر

لما تقدّر الوقت، ما تحسبش دول كشغل برمجة.

22 حاجات لازم تعرفها قبل ما تبدأ

دي حاجات اكتشفناها بالتجربة، وهتوفر عليك وقت كتير.

🔑 التوكن في كل ريكوست

ما عدا اللوجين، كل إندبوينت محتاج:

Authorization: Bearer <token>

التوكن بييجي من token.data في رد اللوجين.

📛 أسماء الحقول مش متسقة

الإرث القديم سايب لخبطة في التسمية — خد بالك:

  • ED_STUD_ID في إندبوينتات
  • EdStudId في إندبوينتات تانية
  • saStaffMemberId بحرف صغير في الأول

دايمًا راجع الكوليكشن بتاع الشاشة قبل ما تكتب.

🕐 الوقت والتاريخ

FROM_TIME بيرجع "09:00:00" — مش تاريخ كامل.

أيام الأسبوع بـ GS_CODE_WEEK_DAY_ID: 1=الجمعة، 2=السبت، 3=الأحد، 4=الإثنين، 5=الثلاثاء، 6=الأربعاء، 7=الخميس. لاحظ إن الأسبوع بيبدأ من السبت.

🈳 "" مش null

السيرفس بيحوّل أي string فاضي لـ "" عشان التطبيقات القديمة.

يعني if (x == null) مش هتشتغل — استخدم فحص فاضي.

📄 «data not found» مش error

لما مفيش نتايج، السيرفس بيرد result: "ERROR" وdetails: "data not found".

ده مش عطل — ده «مفيش داتا». في api.js عندنا بنحوّلها null والشاشة تعرض حالة فاضية.

💸 المبالغ بالقروش في Paymob

Paymob بياخد amount_cents. 1500 جنيه = 150000.

غلطة هنا = خصم 100 ضعف من الطالب. التحويل بيحصل في مكان واحد بس في الكود عن قصد.

🔁 البوابات بتعيد الإرسال

لو ما رجّعتش 200 للـ callback، البوابة هتعيد. فالتحديث لازم يكون idempotent.

عملناها بـ UPDATE … WHERE PaymentStatus <> 1 — العدد الراجع بيقولنا لو دي أول مرة.

🧪 بيانات الطالب التجريبي

الطالب 73 سدّد كل مصروفاته — فشاشة الرسوم مش هتوريك بنود متأخرة.

لو عايز تجرّب الدفع، دوّر على طالب عليه متأخرات في FEE_STUD_FEE_ITEM.

7 باجات اتصلّحت — واللي تتعلّمه منها

دول مش تاريخ. كل واحد فيهم فخ ممكن تقع فيه تاني وانت بتضيف إندبوينت جديد، وكلهم اتكشفوا لما شغّلنا الدورة كاملة على الـ Sandbox مش بالقراءة.

الباجكان هيحصل إيهالدرس
الـ callback مكانش بيقرا الـ body
كان بيشرط وجود Content-Length
أي بوابة بتبعت بـ chunked → الـ body يرجع فاضي → التوقيع يفشل → كل إشعارات السداد تتزقّ في الزبالة على إنها مزوّرة، من غير أي صوت اقرا الـ stream على طول. Content-Length مش مضمون
INTEG_Fawry_Payment عموده identity الكود كان بيحسب MAX(id)+1 ويكتبه → SQL 544 → أرقام فوري مبتتسجّلش → التسوية في الباك أوفيس عمياء العميل ممكن يكون identity وممكن لأ. اسأل الـ schema بـ COLUMNPROPERTY
TryGetDecimal بيرمي exception على null فوري بتبعت paymentAmount: null لعملية لسه متدفعتش → استعلام الحالة يضرب اتأكد من ValueKind الأول. الـ TryGet* مش آمنة زي ما اسمها بيوحي
STUD_FACULTY_CODE من جدول غلط الكود كان بيقراه من ED_STUD وهو على ED_STUD_FACULTY → شاشة المنصة التعليمية كلها 500 الرقم الجامعي على التسجيل في الكلية مش على الطالب. خد اللي IS_LAST = 1
MSG_SUBJECT عموده 50 حرف الكود كان بيحط فيه {"TitleEn":…,"TitleAr":…} → SQL 8152 → كل الإشعارات كانت بتفشل، وحتى لو عدّت التطبيق كان هيعرض JSON خام شوف عرض العمود قبل ما تكتب فيه. ومتحطش JSON في عمود المستخدم بيقراه
الإشعار بيطلب سنة وفصل والمرسل مش بيبعتهم أعمدة NOT NULL → الإرسال يفشل لأي عميل مش عارف الأرقام دي لو السيرفس يقدر يجيب القيمة بنفسه، يجيبها. متحمّلش المستدعي معرفة داخلية
EfinancePay ناسي ED_CODE_SEMESTER_ID من الـ INSERT عمود NOT NULL مش موجود أصلاً في قايمة الأعمدة → الإندبوينت ده عمره ما اشتغل البارامتر موجود في الـ SQL ≠ البارامتر مستخدم. راجع قايمة الأعمدة نفسها
💡

الخلاصة المهمة: ولا واحد من السبعة دول كان ممكن يتكشف بقراءة الكود أو بـ unit test. كلهم ظهروا لأن الدورة اتشغّلت كاملة: تطبيق ← سيرفس ← بوابة ← إشعار موقّع ← داتابيز. ده السبب الحقيقي لوجود الـ Sandbox.

ملفات مهمة تعرف مكانها

الملففيه إيه
Application/Features/FeatureCatalog.csتعريف كل خاصية ومتطلباتها — ابدأ من هنا لو بتضيف خاصية
Application/Infrastructure/ClientProfile/ClientProfile.csشكل ملف الإعدادات كله
Application/Business/<Feature>/الـ handlers والـ SQL — كل SQL جوّه const string جنب الـ handler بتاعه
WebApi/EndPoints/المسارات — مقسّمة بالموضوع
docs/legacy-specs/ENDPOINT-CATALOG.mdكل مسار قديم + الـ SP اللي اتنقل منه — مرجعك لما تنقل SQL
MobileDemo/app/api.jsعميل السيرفس في الموبايل
MobileDemo/app/data.jsxتحويل رد السيرفس لشكل الشاشات — مثال حي لكل حقل بيتقرا منين
Application/Infrastructure/Sandbox/جديد الـ overlay اللي بيملا البيانات المزيّفة، وحالة البوابات، وباعت الإشعارات
WebApi/EndPoints/Sandbox/جديد البوابات المزيّفة نفسها (فوري / Paymob / MPGS / Graph / Moodle / FCM) + صفحة الدفع وصفحة التحكّم
docs/SANDBOX.mdجديد شرح الـ Sandbox بالتفصيل بالإنجليزي

قواعد الكود (لو هتضيف)

📐
  • Vertical slice: إندبوينت في WebApi + handler quad في Application
  • الـ SQL يبقى const string جنب الـ handler — بارامترات، أعمدة محددة
  • عرّف الخاصية في FeatureCatalog بجداولها وإعداداتها، وحُط [FeatureGate] على الإندبوينت
  • لو بتضيف تكامل خارجي: خلي الـ host جاي من البروفايل (BaseUrl) عشان الـ Sandbox يقدر يوجّهه، واعمل النسخة المزيّفة تحت WebApi/EndPoints/Sandbox/. ممنوع if (sandbox) جوّه أي provider.
  • ممنوع: EF · استدعاء stored procedures · if (client == "KU")