آلة حالة الحجز

كل حجز يمر عبر مجموعة محددة من الحالات. الانتقالات تُفرض في كود التطبيق وتُتحقق بواسطة عمود booking_status (enum مخزّن كـ string عبر HasConversion<string>() في EF Core، مع قيد CHECK على مستوى قاعدة البيانات).

booked
confirmed
completed
المسؤول يؤكد أو يتم تسجيل الدفع                بعد 24 ساعة من انتهاء الجلسة
رمز سبب إلزامي
cancelled
يمكن الوصول إليه من booked أو confirmed
عدم تسجيل حضور بعد الجلسة
no_show
يمكن الوصول إليه من confirmed فقط

4 قنوات للحجز

تدخل الحجوزات إلى النظام عبر أربع قنوات مختلفة، لكل منها تدفقات مستخدم وخصائص أعمال مختلفة.

قناة المسؤول 70.6% من الحجم

المشغّلون ينشئون حجوزات للعملاء الحاضرين والمتصلين هاتفياً عبر لوحة تحكم الويب. هذه القناة هي المهيمنة حالياً وستظل عالية الحجم حتى مع نمو الحجز الذاتي للعملاء.

قناة مدير الملعب تطبيق مدير الملعب

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

قناة العميل 2.5%، المستهدف 15%+

العملاء المصادقون يحجزون ذاتياً عبر تطبيق الجوال. حالياً أصغر قناة، لكن تنميتها أولوية أعمال قصوى. تستخدم الحجز المؤقت للفترات لمنع التعارضات أثناء عملية الحجز.

قناة العقود 6.8%

إنشاء تلقائي من الاتفاقيات المتكررة. حجوزات العقود لديها أقل معدل إلغاء بنسبة 12.7%، مما يجعلها أكثر مصدر إيرادات موثوقية.

إدارة الحجز المؤقت

عندما يبدأ العميل نموذج الحجز في تطبيق الجوال، يُوضع حجز مؤقت على الفترة المختارة لمنع عميل آخر من حجز نفس الوقت بينما يكمل العميل الأول النموذج.

دورة حياة الحجز المؤقت: العميل يفتح نموذج الحجز → يُنشأ الحجز المؤقت → العميل يرسل خلال 5 دقائق → يتحول الحجز المؤقت إلى حجز. إذا تخلى العميل عن النموذج، ينتهي الحجز المؤقت تلقائياً وتعود الفترة متاحة مرة أخرى.

منع التعارض

منع الحجز المزدوج مُطبّق على مستوى قاعدة البيانات باستخدام فهرس UNIQUE على (field_id, date, start_time) مع معاملة serializable أو trigger من نوع INSTEAD OF INSERT يتحقق من عدم وجود نطاقات زمنية متداخلة. SQL Server سيرفض عمليات الإدراج المتعارضة. حتى لو كان هناك خطأ في كود التطبيق، فإن قيد قاعدة البيانات يمنع الحجز المزدوج.

في v2، كان منع التعارض يُعالج بالكامل في كود التطبيق. مولّد العقود تجاوز هذه الفحوصات، مما أدى إلى 4 حوادث تلف بيانات حيث شغل حجزان نفس الملعب والفترة الزمنية. في v3، قاعدة البيانات نفسها تُطبّق هذا الثابت — لا يمكن لأي مسار في كود التطبيق انتهاكه.

الانتقالات التلقائية (المهام الخلفية)

المهام الخلفية تتولى تقدم الحالة التلقائي حتى لا تعلق الحجوزات في حالات قديمة.

الانتقال المحفّز الغرض
confirmedcompleted بعد 24 ساعة من وقت انتهاء الحدث يُعلّم الجلسات تلقائياً كمكتملة بعد حدوثها
bookedcancelled بعد 24 ساعة من تاريخ الجلسة إذا لم يُؤكد يُلغي تلقائياً الحجوزات التي لم تُؤكد أبداً (بدون دفع، بدون إجراء من المسؤول)
مشكلة v2 المحلولة: 34.8% من الحجوزات في v2 كانت عالقة في حالة قديمة — جلسات مؤكدة لا تزال تظهر كـ "قادمة" بعد أسابيع من حدوثها. الانتقالات التلقائية تزيل هذا بالكامل، مما يضمن هدف v3 بنسبة 0% حجوزات قديمة.

الحقول الإلزامية في كل حجز

الحقل النوع الوصف
field_id string (max 15 chars) أي ملعب الحجز له
customer_id string (max 15 chars) من حجز (العميل)
creator_id string (max 15 chars) من أنشأ الحجز (قد يختلف عن العميل — مثلاً، المسؤول يحجز نيابة عن العميل)
date DATE تاريخ الجلسة
start_time TIME وقت بداية الفترة
end_time TIME وقت نهاية الفترة
status string (enum stored as string) الحالة الحالية: booked, confirmed, completed, cancelled, no_show
source string (enum stored as string) القناة: admin, fm, customer, contract, guest
price DECIMAL, NOT NULL, > 0 تسعير إلزامي — في v2 كان 58.3% من الحجوزات بسعر صفر. قيد قاعدة البيانات يمنع هذا.
payment_status string (enum stored as string) حالة الدفع: unpaid, partial, paid, refunded

الإلغاء

الإلغاء عملية محكومة مع توثيق إلزامي لدعم تحليل الأعمال وتقليل معدلات الإلغاء.

هدف الأعمال: معدل الإلغاء في نفس اليوم الحالي هو 27.1%. هدف v3 هو 22%. رموز الأسباب الإلزامية تُمكّن من تحليل أسباب الإلغاء، والبيانات ستقود تغييرات السياسات لتقليل المعدل.