return$db->selectOne("SELECT * FROM treasuries WHERE code = 'SUB_MEM' AND is_active = 1");
}
publicstaticfunctiongetByCode(string$code):?array
{
$db=App::getInstance()->db();
return$db->selectOne("SELECT * FROM treasuries WHERE code = ? AND is_active = 1",[$code]);
}
publicstaticfunctiongetMainTreasury():?array
publicstaticfunctiongetMainTreasury():?array
{
{
$db=App::getInstance()->db();
$db=App::getInstance()->db();
...
@@ -164,9 +176,10 @@ final class TreasuryService
...
@@ -164,9 +176,10 @@ final class TreasuryService
$where.=" AND pr.status IN ('pending','processing')";
$where.=" AND pr.status IN ('pending','processing')";
}
}
// Filter by sports-related payment types for sub-treasury
if($treasury['code']==='SUB_SA'){
if($treasury['type']==='sub'){
$where.=" AND pr.payment_type IN ('activity_subscription','hourly_booking','sports_registration')";
$where.=" AND pr.payment_type IN ('activity_subscription','hourly_booking','sports_registration')";
}elseif($treasury['code']==='SUB_MEM'){
$where.=" AND pr.payment_type IN ('form_fee','membership_fee','down_payment','addition_fee','annual_subscription','divorce_fee','death_fee','waiver_fee','separation_fee','fine','carnet_replacement','seasonal_fee')";
"SELECT COUNT(*) as count FROM payment_requests WHERE status IN ('pending','processing') AND is_voided = 0 AND payment_type IN ('activity_subscription','hourly_booking','sports_registration')"
"SELECT COUNT(*) as count FROM payment_requests WHERE status IN ('pending','processing') AND is_voided = 0 AND payment_type IN {$typeFilter}"
['title'=>'محتويات التقرير','body'=>'يعرض: إجمالي المحصل (نقدي/شيكات/فيزا)، عدد الإيصالات، الدفعات الملغاة.'],
['title'=>'محتويات التقرير','body'=>'يعرض: إجمالي المحصل (نقدي/شيكات/فيزا)، عدد الإيصالات، الدفعات الملغاة.'],
['title'=>'الطباعة والأرشفة','body'=>'اضغط <span class="field">طباعة</span> أو <span class="field">تصدير PDF</span> للحفظ.'],
['title'=>'الطباعة والأرشفة','body'=>'اضغط <span class="field">طباعة</span> أو <span class="field">تصدير PDF</span> للحفظ.'],
],
],
'cashier.session-open'=>[
['title'=>'فتح صفحة خزنة العضويات','body'=>'من القائمة الجانبية: <span class="field">خزنة العضويات</span> > <span class="field">طابور الدفع</span>.'],
['title'=>'التأكد من عدم وجود وردية مفتوحة','body'=>'إذا كانت هناك وردية مفتوحة بالفعل — يظهر شريط أخضر بمعلومات الوردية. لا تحتاج لفتح وردية جديدة.<span class="info">لا يمكن فتح أكثر من وردية واحدة في نفس الوقت لنفس الموظف على نفس الخزنة.</span>'],
['title'=>'فتح الوردية','body'=>'إذا لم تكن هناك وردية مفتوحة — يظهر شريط أصفر مع زر <span class="field">فتح وردية</span>. اضغط الزر لبدء الوردية.<span class="success">بعد فتح الوردية يمكنك البدء في تحصيل المدفوعات من الطابور. يتم تسجيل وقت الفتح ورقم وردية فريد.</span>'],
],
'cashier.session-close'=>[
['title'=>'مراجعة الإجمالي','body'=>'من شريط الوردية أعلى الصفحة: تأكد من صحة إجمالي التحصيلات وعدد الإيصالات المعروضة.'],
['title'=>'إغلاق الوردية','body'=>'اضغط <span class="field">إغلاق الوردية</span>. سيطلب النظام تأكيد الإغلاق.<span class="warn">بعد الإغلاق لا يمكن تحصيل أي دفعة جديدة حتى فتح وردية أخرى.</span>'],
['title'=>'بعد الإغلاق','body'=>'يتم حفظ إجمالي التحصيلات ووقت الإغلاق. يمكنك الآن إنشاء تسوية لترحيل المبلغ للخزنة الرئيسية.<span class="info">الورديات المغلقة تظهر في تقرير الورديات مع تفاصيل كل وردية.</span>'],
],
'cashier.settlement-flow'=>[
['title'=>'متى تُنشئ تسوية؟','body'=>'بعد إغلاق وردية أو أكثر — المبالغ المحصلة تبقى في عهدة أمين الخزنة الفرعية. التسوية هي عملية تحويل هذه المبالغ رسمياً للخزنة الرئيسية.'],
['title'=>'استلام المبلغ','body'=>'أمين الخزنة الرئيسية يستلم المبلغ ويؤكد الاستلام بالضغط على <span class="field">تأكيد الاستلام</span>.<span class="success">عند التأكيد: تنتقل العهدة من الخزنة الفرعية للرئيسية ويُسجل القيد المحاسبي تلقائياً.</span>'],
['title'=>'متابعة التسوية','body'=>'من <span class="field">التسويات</span> يمكنك متابعة حالة كل تسوية: معلقة، مستلمة، أو مرفوضة.<span class="warn">التسوية المرفوضة تحتاج مراجعة وإعادة إرسال — المبلغ يبقى في عهدة الخزنة الفرعية.</span>'],
],
'cashier.custody-tracking'=>[
['title'=>'ما هي العهدة؟','body'=>'العهدة هي المبلغ النقدي الموجود فعلياً لدى أمين الخزنة. تزيد بالتحصيل وتنقص بالتسوية أو إلغاء دفعة.'],
['title'=>'عرض رصيد العهدة','body'=>'من شريط الوردية: يظهر <span class="field">العهدة</span> بالمبلغ الحالي. أو من صفحة الخزنة الرئيسية > <span class="field">رصيد العهدة</span>.'],
['title'=>'سجل الحركات','body'=>'كل عملية تحصيل أو تسوية أو إلغاء تُسجل في <span class="field">سجل حركات العهدة</span> (treasury_custody_log) مع: النوع، المبلغ، المرجع، الوقت.<span class="info">هذا السجل يُستخدم في المراجعة المالية ولا يمكن تعديله أو حذفه.</span>'],
],
// ── SALES ──
// ── SALES ──
'sales.pos-transaction'=>[
'sales.pos-transaction'=>[
['title'=>'فتح نقطة البيع','body'=>'من <span class="field">المبيعات</span> > <span class="field">عملية بيع جديدة</span>.'],
['title'=>'فتح نقطة البيع','body'=>'من <span class="field">المبيعات</span> > <span class="field">عملية بيع جديدة</span>.'],
...
@@ -518,6 +539,17 @@ final class TutorialRegistry
...
@@ -518,6 +539,17 @@ final class TutorialRegistry
['title'=>'كتابة محضر الفض','body'=>'أدخل <span class="field">سبب الإخلاء</span> بالتفصيل. هذا يُسجل كمحضر رسمي.<span class="warn">عملية الإخلاء نهائية ولا يمكن التراجع عنها — يتم تحرير اللوكر للتأجير مرة أخرى.</span>'],
['title'=>'كتابة محضر الفض','body'=>'أدخل <span class="field">سبب الإخلاء</span> بالتفصيل. هذا يُسجل كمحضر رسمي.<span class="warn">عملية الإخلاء نهائية ولا يمكن التراجع عنها — يتم تحرير اللوكر للتأجير مرة أخرى.</span>'],
['title'=>'تأكيد الإخلاء','body'=>'اضغط <span class="field">تأكيد الإخلاء</span>. يتحول الإيجار لحالة «تم الإخلاء» واللوكر يصبح متاحاً للتأجير.<span class="success">يتم حفظ تاريخ الإخلاء والموظف المسؤول تلقائياً.</span>'],
['title'=>'تأكيد الإخلاء','body'=>'اضغط <span class="field">تأكيد الإخلاء</span>. يتحول الإيجار لحالة «تم الإخلاء» واللوكر يصبح متاحاً للتأجير.<span class="success">يتم حفظ تاريخ الإخلاء والموظف المسؤول تلقائياً.</span>'],
],
],
'facilities.locker-payment'=>[
['title'=>'إنشاء الإيجار أولاً','body'=>'عند إنشاء إيجار لوكر جديد، النظام يُنشئ طلب دفع تلقائياً في طابور الخزنة الفرعية (SUB_SA).<span class="info">المبلغ يُحدد يدوياً عند إنشاء الإيجار حسب نوع اللوكر ومدة الإيجار.</span>'],
['title'=>'الدفع من خزنة الأنشطة','body'=>'ينتقل أمين الخزنة لطابور دفع الأنشطة: <span class="field">خزنة الأنشطة</span> > <span class="field">طابور الدفع</span>. يظهر الطلب بنوع «إيجار لوكر».'],
['title'=>'تحصيل المبلغ','body'=>'اختر الطلب > حدد طريقة الدفع > <span class="field">تحصيل</span>. يتم تسجيل الدفع وربطه بالوردية المفتوحة.<span class="success">بعد التحصيل يتحول الإيجار لحالة «فعال» تلقائياً ويُطبع إيصال.</span>'],
],
'facilities.locker-renewal'=>[
['title'=>'متى يُجدد الإيجار؟','body'=>'قبل انتهاء الإيجار الحالي أو خلال فترة السماح (3 شهور). بعد فترة السماح — يتحول لحالة «بانتظار الإخلاء» ولا يمكن التجديد.'],
['title'=>'اختيار مدة التجديد','body'=>'حدد <span class="field">مدة التجديد</span> (شهر، 6 شهور، سنة). النظام يحسب تاريخ الانتهاء الجديد من تاريخ الانتهاء الحالي.<span class="info">المبلغ يُحدد يدوياً — يمكنك تطبيق خصم أو تسعيرة مختلفة.</span>'],
['title'=>'الدفع والتفعيل','body'=>'بعد الحفظ يُنشأ طلب دفع في طابور الخزنة. بعد التحصيل → يُمدد تاريخ الانتهاء ويبقى الإيجار فعالاً.<span class="success">يتم تسجيل تاريخ التجديد والموظف المسؤول تلقائياً.</span>'],
['title'=>'إضافة صلاحية فردية','body'=>'اضغط <span class="field">إضافة</span>. اختر الصلاحية المحددة من القائمة.'],
['title'=>'إضافة صلاحية فردية','body'=>'اضغط <span class="field">إضافة</span>. اختر الصلاحية المحددة من القائمة.'],
['title'=>'الحفظ','body'=>'اضغط <span class="field">حفظ</span>. الصلاحية الفردية تُضاف بالإضافة للصلاحيات من الأدوار.<span class="warn">الصلاحيات الفردية تتقدم على الأدوار — استخدمها بحذر.</span>'],
['title'=>'الحفظ','body'=>'اضغط <span class="field">حفظ</span>. الصلاحية الفردية تُضاف بالإضافة للصلاحيات من الأدوار.<span class="warn">الصلاحيات الفردية تتقدم على الأدوار — استخدمها بحذر.</span>'],
['title'=>'أمين خزنة العضويات','body'=>'يمكنه: فتح/إغلاق وردية، تحصيل دفعات العضويات، إلغاء طلب دفع، بدء تسوية، عرض العهدة.<span class="info">لا يمكنه: إلغاء دفعة مكتملة (void) أو استلام تسويات أو إيداع في البنك.</span>'],
['title'=>'أمين خزنة الأنشطة','body'=>'يمكنه: فتح/إغلاق وردية، تحصيل اشتراكات رياضية وحجوزات ساعية، بدء تسوية، عرض التقارير.<span class="info">يعمل على الخزنة الفرعية SUB_SA — لا يرى طابور العضويات.</span>'],
['title'=>'أمين الخزنة الرئيسية','body'=>'يمكنه: استلام التسويات من الخزن الفرعية، إنشاء إيداعات بنكية، تأكيد الإيداع، عرض الأرصدة.<span class="warn">هذا الدور لا يُحصّل مدفوعات مباشرة من الأعضاء — دوره إداري/مالي فقط.</span>'],
],
'roles.sports-roles'=>[
['title'=>'الأدوار المتاحة','body'=>'النظام يوفر دورين لإدارة الأنشطة الرياضية:<ul><li><strong>موظف الأنشطة الرياضية</strong> — العمليات اليومية</li><li><strong>مدير الأنشطة الرياضية</strong> — إدارة كاملة + تقارير + إعفاءات</li></ul>'],
['title'=>'موظف الأنشطة','body'=>'يمكنه: تسجيل اللاعبين، إدارة المجموعات والحجوزات، توليد الاشتراكات، تسجيل الحضور، إدارة قوائم الانتظار، تأجير اللوكرات.<span class="info">لا يمكنه: إعفاء من اشتراك، إدارة الأسعار، اعتماد عقود.</span>'],
['title'=>'مدير الأنشطة','body'=>'يمتلك كل صلاحيات الموظف بالإضافة إلى: إعفاء من الرسوم، إدارة التسعير، اعتماد العقود، الموافقة الطبية، إخلاء لوكرات، تقارير العمليات.<span class="success">هذا هو الدور المناسب لمدير قسم الأنشطة الرياضية.</span>'],
<divclass="tut-step"><divclass="tut-step-num">1</div><h3class="tut-step-title">فتح تسجيل اللاعبين</h3><divclass="tut-step-body">من صفحة المجموعة > <spanclass="field">تسجيل لاعب</span>.</div></div>
<divclass="tut-step"><divclass="tut-step-num">1</div><h3class="tut-step-title">فتح تسجيل اللاعبين</h3><divclass="tut-step-body">من صفحة المجموعة > <spanclass="field">تسجيل لاعب</span>.</div></div>
<divclass="tut-step"><divclass="tut-step-num">2</div><h3class="tut-step-title">البحث عن اللاعب</h3><divclass="tut-step-body">ابحث بالاسم أو الرقم التسلسلي أو الرقم القومي.</div></div>
<divclass="tut-step"><divclass="tut-step-num">2</div><h3class="tut-step-title">البحث عن اللاعب</h3><divclass="tut-step-body">ابحث بالاسم أو الرقم التسلسلي أو الرقم القومي.</div></div>
<divclass="tut-step"><divclass="tut-step-num">3</div><h3class="tut-step-title">التحقق التلقائي</h3><divclass="tut-step-body">النظام يتحقق من:<ul><li>الحالة الطبية: لازم تكون <spanclass="field">fit</span> أو <spanclass="field">conditional</span></li><li>السعة: المجموعة لسه فيها مكان</li><li>العمر: ضمن الفئة العمرية للبرنامج</li><li>عدم التكرار: مش مسجل بالفعل</li></ul><spanclass="warn">إذا اللاعب حالته الطبية "pending" أو "unfit" أو "expired"، التسجيل مرفوض تلقائياً.</span></div></div>
<divclass="tut-step"><divclass="tut-step-num">3</div><h3class="tut-step-title">التحقق التلقائي</h3><divclass="tut-step-body">النظام يتحقق من:<ul><li>الحالة الطبية: لازم تكون <spanclass="field">fit</span> أو <spanclass="field">conditional</span></li><li>السعة: المجموعة لسه فيها مكان</li><li>العمر: ضمن الفئة العمرية للبرنامج</li><li>عدم التكرار: مش مسجل بالفعل</li></ul><spanclass="warn">إذا اللاعب حالته الطبية "pending" أو "unfit" أو "expired"، التسجيل مرفوض تلقائياً.</span></div></div>
<divclass="tut-step"><divclass="tut-step-num">4</div><h3class="tut-step-title">نتيجة التسجيل</h3><divclass="tut-step-body">إذا كل الشروط تمام → يتم التسجيل. إذا المجموعة ممتلئة → يُعرض خيار "قائمة الانتظار".<spanclass="success">بعد التسجيل بنجاح، اللاعب يظهر في كشف الحضور اليومي ويتم توليد اشتراك شهري له.</span></div></div>
<divclass="tut-step"><divclass="tut-step-num">4</div><h3class="tut-step-title">نتيجة التسجيل</h3><divclass="tut-step-body">إذا كل الشروط تمام → يُسجل اللاعب بحالة <spanclass="field">pending_payment</span> (في انتظار الدفع). إذا المجموعة ممتلئة → يُعرض خيار "قائمة الانتظار".<spanclass="info">التسجيل لا يُفعّل مباشرة — يجب دفع أول اشتراك من خزنة الأنشطة أولاً. انظر: <ahref="/tutorials/sports-activity/enrollment-payment"style="color:#1E40AF;font-weight:600;">الدفع الإجباري عند التسجيل</a>.</span><spanclass="success">بعد التحصيل: اللاعب يظهر في كشف الحضور اليومي ويتم توليد اشتراك شهري له.</span></div></div>
<divclass="tut-nav">
<divclass="tut-nav">
<ahref="/tutorials/sports-activity/create-group"><idata-lucide="arrow-right"style="width:14px;height:14px;"></i> إنشاء مجموعة تدريبية</a>
<ahref="/tutorials/sports-activity/create-group"><idata-lucide="arrow-right"style="width:14px;height:14px;"></i> إنشاء مجموعة تدريبية</a>
<div><h1>الدفع الإجباري عند التسجيل</h1><p>لا يتم تفعيل التسجيل بدون دفع أول اشتراك من خزنة الأنشطة</p></div>
</div>
<divclass="tut-step"><divclass="tut-step-num">1</div><h3class="tut-step-title">كيف يعمل النظام؟</h3><divclass="tut-step-body">عند تسجيل لاعب في مجموعة، لا يُفعّل التسجيل مباشرة. بدلاً من ذلك:<ul><li>يُسجل اللاعب بحالة <spanclass="field">pending_payment</span> (في انتظار الدفع)</li><li>يُنشئ النظام <strong>طلب دفع</strong> تلقائياً في طابور خزنة الأنشطة</li><li>المبلغ = رسم الاشتراك الشهري للمجموعة (حسب نوع اللاعب: عضو أو غير عضو)</li></ul><spanclass="info">هذا يضمن عدم تفعيل أي تسجيل بدون سداد — لا استثناءات إلا عبر الإعفاء الرسمي.</span></div></div>
<divclass="tut-step"><divclass="tut-step-num">2</div><h3class="tut-step-title">حالة اللاعب بعد التسجيل</h3><divclass="tut-step-body">في صفحة المجموعة، اللاعب يظهر بخلفية صفراء وبادج <spanclass="field">في انتظار الدفع</span>.<ul><li>لا يُحسب ضمن العدد الفعلي للمجموعة (السعة)</li><li>لا يظهر في كشوف الحضور</li><li>لا يُولد له اشتراك شهري</li></ul><spanclass="warn">إذا أُلغي طلب الدفع بدون تحصيل — التسجيل يبقى معلقاً ولا يُحذف تلقائياً.</span></div></div>
<divclass="tut-step"><divclass="tut-step-num">3</div><h3class="tut-step-title">تحصيل الدفعة</h3><divclass="tut-step-body">أمين خزنة الأنشطة يفتح طابور الدفع ويرى الطلب بنوع <spanclass="field">sports_registration</span>. يقوم بالتحصيل بالطريقة المعتادة (نقدي/فيزا/شيك).<divclass="tut-diagram">تسجيل لاعب → طلب دفع (pending) → تحصيل من الخزنة → تفعيل التسجيل (active)</div></div></div>
<divclass="tut-step"><divclass="tut-step-num">4</div><h3class="tut-step-title">التفعيل التلقائي</h3><divclass="tut-step-body">بعد التحصيل الناجح، النظام يقوم تلقائياً بـ:<ul><li>تغيير حالة التسجيل من <spanclass="field">pending_payment</span> إلى <spanclass="field">active</span></li><li>زيادة عدد اللاعبين الفعليين في المجموعة</li><li>إنشاء أول اشتراك شهري بحالة <spanclass="field">paid</span></li><li>طباعة إيصال الدفع</li></ul><spanclass="success">من هذه اللحظة، اللاعب يظهر في كشوف الحضور ويُولد له اشتراك شهري كل شهر.</span></div></div>
<divclass="tut-step"><divclass="tut-step-num">5</div><h3class="tut-step-title">ماذا لو أُلغيت الدفعة؟</h3><divclass="tut-step-body">إذا تم إلغاء (void) دفعة تسجيل بعد التحصيل:<ul><li>يعود التسجيل لحالة <spanclass="field">pending_payment</span></li><li>يتم خفض عدد اللاعبين في المجموعة</li><li>يحتاج اللاعب لدفعة جديدة لإعادة التفعيل</li></ul><spanclass="warn">الإلغاء يُرجع حالة التسجيل فقط — لا يحذف سجل اللاعب من المجموعة.</span></div></div>
<divclass="tut-nav">
<ahref="/tutorials/sports-activity/enroll-player"><idata-lucide="arrow-right"style="width:14px;height:14px;"></i> تسجيل لاعب في مجموعة</a>
<ahref="/tutorials/sports-activity/sports-payment-queue">طابور دفع الأنشطة الرياضية <idata-lucide="arrow-left"style="width:14px;height:14px;"></i></a>
<div><h1>طابور دفع الأنشطة الرياضية</h1><p>عرض ومعالجة طلبات الدفع في خزنة الأنشطة الفرعية</p></div>
</div>
<divclass="tut-step"><divclass="tut-step-num">1</div><h3class="tut-step-title">فتح الطابور</h3><divclass="tut-step-body">من القائمة الجانبية: <spanclass="field">خزنة الأنشطة</span> > <spanclass="field">طابور الدفع</span>. يظهر جميع طلبات الدفع المعلقة والجارية المرتبطة بالأنشطة الرياضية.<spanclass="info">الطابور يتحدث تلقائياً كل 30 ثانية. أنواع الطلبات: تسجيل رياضي، اشتراك نشاط، حجز ساعة، إيجار لوكر.</span></div></div>
<divclass="tut-step"><divclass="tut-step-num">2</div><h3class="tut-step-title">التأكد من وجود وردية مفتوحة</h3><divclass="tut-step-body">قبل التحصيل — يجب أن تكون هناك وردية مفتوحة. إذا لم تكن موجودة:<ul><li>يظهر شريط أصفر: «لا توجد وردية مفتوحة»</li><li>اضغط <spanclass="field">فتح وردية</span> لبدء العمل</li></ul><spanclass="warn">لا يمكن تحصيل أي دفعة بدون وردية مفتوحة — الزر «تحصيل» لن يعمل.</span></div></div>
<divclass="tut-step"><divclass="tut-step-num">3</div><h3class="tut-step-title">اختيار طلب الدفع</h3><divclass="tut-step-body">اضغط <spanclass="field">تحصيل</span> بجانب الطلب المطلوب. تُفتح صفحة التحصيل بمعلومات:<ul><li>اسم اللاعب/العضو</li><li>نوع الدفعة (تسجيل رياضي، اشتراك شهري، حجز ساعة)</li><li>المبلغ المطلوب</li><li>تفاصيل إضافية (المجموعة، الوحدة، التاريخ)</li></ul></div></div>
<divclass="tut-step"><divclass="tut-step-num">4</div><h3class="tut-step-title">اختيار طريقة الدفع والتحصيل</h3><divclass="tut-step-body">حدد <spanclass="field">طريقة الدفع</span>: نقدي، فيزا، شيك، أو تحويل بنكي. لكل طريقة حقول إضافية (رقم الشيك، مرجع التحويل...). ثم اضغط <spanclass="field">تأكيد التحصيل</span>.<divclass="tut-diagram">طلب دفع (pending) → تحصيل → إيصال + قيد محاسبي + تفعيل الخدمة</div><spanclass="success">بعد التحصيل: يُطبع الإيصال تلقائياً، ويُربط المبلغ بالوردية الحالية وعهدة أمين الخزنة.</span></div></div>
<divclass="tut-step"><divclass="tut-step-num">5</div><h3class="tut-step-title">إغلاق الوردية والتسوية</h3><divclass="tut-step-body">بعد الانتهاء من التحصيلات:<ul><li>اضغط <spanclass="field">إغلاق الوردية</span> — يُحفظ إجمالي التحصيلات</li><li>أنشئ <spanclass="field">تسوية</span> لترحيل المبالغ للخزنة الرئيسية</li><li>أمين الخزنة الرئيسية يستلم ويؤكد</li></ul><spanclass="info">شريط الوردية يعرض دائماً: رقم الوردية، عدد الإيصالات، إجمالي التحصيلات، ورصيد العهدة الحالي.</span></div></div>
<divclass="tut-nav">
<ahref="/tutorials/sports-activity/enrollment-payment"><idata-lucide="arrow-right"style="width:14px;height:14px;"></i> الدفع الإجباري عند التسجيل</a>