1. 12 Sep, 2026 13 commits
    • DevPilot's avatar
      fix(accounting, sa): missing LC/LG show screens, debtors report repeating每... · 2a77ae3a
      DevPilot authored
      fix(accounting, sa): missing LC/LG show screens, debtors report repeating每 debtor, player link fix screen
      
      - شاشتي عرض الاعتماد المستندي وخطاب الضمان مكانوش موجودين أصلاً (View not
        found) — أي حد يدوس «عرض» كان بياخد استثناء. اتعملوا كاملين: البيانات
        والغطاء والعمولة والمستندات، وكمان فورم تحديث الحالة اللي كان متعرّف في
        الكنترولر من غير أي واجهة توصّله (التجديد بيطلب تاريخ انتهاء جديد).
      
      - تقرير المدينين كان بيطلع سطر لكل مطالبة، فالعضو الواحد يتكرر 53 مرة
        (1219 سطر لكل المدينين). بقى سطر واحد لكل مدين بإجمالي مديونيته وتوزيع
        التقادم، وبالضغط عليه بتتفتح تفاصيل مطالباته — من غير ما نخسر أي تفصيلة.
      
      - «صلّح الربط» في شاشة الفجوات كان بيفتح فورم فيه خانة رقم مجرّدة، والأسوأ
        إن الفورم أصلاً بيبعت على /sa/players/{id}/update واللي مش موجود كـ
        route — يعني الحفظ ما كانش بيشتغل خالص. دلوقتي:
        • الفورم بيبعت على المسار الصح.
        • خانة «العضو المرتبط» بقت بحث بالاسم/رقم العضوية وبتختار من النتايج.
        • بتوضّح العضو المربوط حالياً، ولو الرقم المسجّل مش عضو حقيقي بتقول كده
          صراحة — وده بالظبط الحالة اللي الفجوة بتبلّغ عنها.
        • الكنترولر بقى يرفض أي member_id مش رقم عضو موجود، في الإضافة والتعديل —
          عشان الفجوة دي ما تتكررش من أصلها.
      
      - tools/route_smoke.py: كان بيحط id=1 دايماً، والكنترولر بيعمل redirect لما
        الصف مش موجود، فالشاشة ما بتتفتحش والفحص بيعدّي وهو فاضي — وده اللي خلّى
        شاشات ناقصة view تعدّي الفحص وتقع عند المستخدم. بقى بيجيب ID حقيقي من
        قاعدة البيانات لكل نوع، وبيبلّغ عن أي شاشة ما اتفتحتش بدل ما يعتبرها نجاح.
      2a77ae3a
    • DevPilot's avatar
      fix(payments, cashier, treasury): void-type gaps, unpaid fines via queue,... · 366460fd
      DevPilot authored
      fix(payments, cashier, treasury): void-type gaps, unpaid fines via queue, invisible guest payments, missing permission checks
      
      Payments/Installments:
      - PaymentLifecycleService::onPaymentVoided() ما كانتش بترجع حالة العضو
        إلا لـ membership_fee/down_payment بس — رغم إن foreign_membership_fee
        وsports_membership_fee وseasonal_fee كلهم بينشّطوا العضو، إلغاء أي واحدة
        منهم كان بيسيب العضو "فعال" للأبد من غير أي غطاء دفع حقيقي.
        MembershipPaymentGuard::deactivateMember() نفسها كانت أصلًا عارفة تتعامل
        مع الخمس أنواع صح — المشكلة كانت في القايمة اللي بتقرر تنادي عليها.
      - غرامة بتترسل لطابور الدفع (fine) كانت بتتحصّل وتتطبع إيصال، لكن صف
        الغرامة في جدول fines فضل زي ما هو "غير مسدد" للأبد — مفيش حد كان بيسمع
        الحدث ده أصلًا. أي فحص رصيد أو أهلية كارنيه كان هيفضل يقول إن الغرامة
        لسه مستحقة حتى بعد سدادها فعليًا. اتضاف مستمع بيقفل الغرامة المحددة.
      - GET /api/v1/payments كان مكسور تمامًا — بيفلتر بعمود is_archived مش
        موجود أصلًا في جدول payments (الصح is_voided).
      - شاشة كل المدفوعات والتقرير اليومي كانوا بيستخدموا INNER JOIN مع
        members، فأي دفعة لعميل زائر (member_id فاضي — زي تسجيل رياضي لغير
        عضو) كانت تختفي تمامًا من الشاشتين دول رغم إنها محصّلة فعليًا
        (23 دفعة بحوالي 41,800 جنيه في البيانات الحية). اتحول لـ LEFT JOIN.
      - زرار إلغاء الدفعة كان بيتحكم بصلاحية payment.void_receipt بينما الـ
        route بتاعه محتاج payment.void — محدش في الأدوار الحية عنده الصلاحية
        التانية، فكان عمليًا محدش غير super_admin يقدر يلغي دفعة من الشاشة دي.
      - عضوية موسمية بتتفعّل من كاشير مباشرة من غير ما تعدّي على
        activateIncludedDependents — يعني زوجة/أبناء العضو الموسمي المرفقين في
        نفس الرسم ما كانوش بيتفعّلوا معاه.
      - حذف DefaultChecker.php الميتة تمامًا (صفر استدعاء) — نفس منطق فحص
        التعثر بالظبط موجود وشغّال فعليًا في cron/jobs/InstallmentDefaultJob.php.
      
      Treasury/Cashier:
      - شاشات موديول Treasury بالكامل كانت من غير أي فحص can() على الإطلاق —
        كل الأزرار بتظهر لأي حد شايف الشاشة بغض النظر عن صلاحياته الحقيقية.
        اتأكد إن ده مش نظري: أدوار حية زي auditor وtreasury_manager وmain_cashier
        عندهم جزء من صلاحيات الخزنة بس مش كلها، فكانوا بياخدوا 403 على أزرار
        شايفينها. كل زرار دلوقتي بيتحقق من نفس صلاحية الـ route بتاعه.
        نفس الحاجة اتصلحت في زرار فتح/قفل وردية كاشير، وزرار طباعة الإيصال اللي
        كان بيتحكم بصلاحية receipt.print بدل payment.view الحقيقية.
      - عداد "طلبات معلقة" في لوحة الخزنة الفرعية كان بيستخدم قايمة أنواع دفع
        أقدم من القايمة الحقيقية المستخدمة في الطابور نفسه — ناقصة رسوم عضوية
        أجانب/رياضية وبعض أنواع الأنشطة الرياضية.
      
      Plus extreme-detail reference docs for Payments/Installments, Cashier, and Treasury.
      366460fd
    • DevPilot's avatar
      fix(facility-grids, members): cross-grid delete auth gap, blank names, wrong... · d3f2e87e
      DevPilot authored
      fix(facility-grids, members): cross-grid delete auth gap, blank names, wrong permissions, dead crons, FY string mismatch
      
      FacilityGrids:
      - ZoneTraineeController::remove()/clearZone()/move() ما كانوش بيتأكدوا إن
        المتدرب/المنطقة فعلاً تابعين لنفس الشبكة في الـ URL — أي حد عنده
        facility_grid.manage على أي شبكة كان يقدر يمسح متدرب أو يفرغ منطقة تابعة
        لشبكة تانية تمامًا لو خمّن الـ ID. دلوقتي بيتأكدوا من الملكية الأول.
      - FacilityAttendanceService::sendPaymentReminders() والـ view بتاع شاشة
        الحضور كانوا لسه بيقروا name_ar بعد ما الكويري اتصلح لـ full_name_ar —
        فكل تذكير دفع كان بيتبعت باسم فاضي، وشاشة تسجيل الحضور كانت بتطلع كل
        أسماء المدربين/المتدربين فاضية وبتحفظ entity_name_cache فاضي في القاعدة.
      - MonthlyPlanController::index() كان بيتحقق من صلاحية مختلفة عن اللي مسجلة
        على الـ route بتاعه (manage بدل view) — يعني حتى لو عندك صلاحية العرض
        بس، تاخد 403 فور ما تدخل شاشة الخطط الشهرية.
      - زرار "إنشاء شبكة" و"خطة جديدة" كانوا بيتحكموا بصلاحية facility.manage
        (بتاعة موديول Facilities المختلف) بدل facility_grid.manage بتاع
        الموديول نفسه.
      - زرار "تصدير PDF" في لوحة المالية كان بيسمي نفسه غلط — الملف اللي بينزل
        فعليًا HTML مش PDF.
      
      Members:
      - AutoFreezeService::freezeTemporaryAtAgeLimit() كانت شرطه بيدور على أكواد
        فئات (sisters_under_25 إلخ) مش موجودة في القاعدة أصلًا — الفئات الحقيقية
        المخزنة هي sister/stepchild/orphan، فالكرون كان ميت فعليًا ومفيش شقيقة
        ولا ابن زوج ولا يتيم بيتقفل تلقائيًا بعد سن 25.
      - حالة permanently_dropped (اللي بيكتبها كرون إسقاط الأعضاء) ما كانتش في
        قائمة تسميات الحالات — فكانت بتظهر بالإنجليزي الخام في أي شاشة تعرض حالة
        العضو. اتضافت هي وterminated اللي كانت ناقصة في نفس القائمة.
      - MemberController::show() كان بيبني السنة المالية الحالية بصيغة فيها شرطة
        (2025-2026) بينما كل مكان تاني في الكود بيكتبها بشرطة مايلة (2025/2026)
        زي ما فعليًا متخزن في subscriptions — فكانت شاشة بروفايل أي عضو دايمًا
        بتقول إن مفيش اشتراك للسنة الحالية حتى لو مدفوع فعلًا.
      
      Plus extreme-detail reference docs for both modules.
      d3f2e87e
    • DevPilot's avatar
      docs: extreme-detail reference for PlayerAffairs · 00d84740
      DevPilot authored
      يوثق كل شاشات الموديول (تقييمات، لياقة، إصابات، الملفات) وأهم نقطة فيه:
      جدول players هنا منفصل تمامًا عن sa_players بتاع SportsActivity — مفيش
      مفتاح مشترك ولا مزامنة. بيوثق كمان الإصلاحات اللي اتعملت قبل كده في نفس
      الجلسة لصلاحيات الأزرار، وموضوع تضارب شاشتي اعتماد الشهادات الطبية.
      00d84740
    • DevPilot's avatar
      fix(player-affairs): buttons gated by a different module's permission, and one... · ea7b4757
      DevPilot authored
      fix(player-affairs): buttons gated by a different module's permission, and one by a permission marked deprecated
      
      كل شاشات الموديول تقريبًا كانت بتتحكم في ظهور أزرار الإضافة/التعديل بصلاحية
      sa.player.manage اللي أصلًا مسجّلة في SportsActivity مش هنا — فموظف عنده
      صلاحياته الصحيحة (player.register، player.edit، player.evaluate،
      player.manage_injuries، player.manage_fitness) ما كانش بيشوف زراره خالص.
      
      وشاشة اعتماد الشهادات الطبية كانت بتتحكم بصلاحية sa.medical.approve
      اللي متسجّلة في كود SportsActivity نفسه بعلامة '(ملغي) استخدم
      medical.board.approve' — يعني حتى المصلحة الأصلية بتقول إنها ملغاة،
      والصلاحية الحقيقية للـ route هي player.approve_medical. كل الأزرار
      بقت بتتحكم بنفس الصلاحية اللي الـ route فعليًا محتاجها.
      ea7b4757
    • DevPilot's avatar
      docs: extreme-detail reference for SA core structure · a77f34ec
      DevPilot authored
      Disciplines, academies/contracts, programs, groups (enroll vs force-enroll,
      the three disagreeing pause implementations, medical grace-period sweep),
      coaches, facilities/units, pricing resolution algorithm, attendance,
      player cards, and both SA reports. Documents the coach-cost fix's real
      impact (9 of 25 active coaches previously cost 0 in the finance report)
      and flags the card-renewal duration bug as a deliberate fix, not a
      quick patch, since it touches both fee calculation and payment payload.
      a77f34ec
    • DevPilot's avatar
      docs: extreme-detail reference for SA bookings, passes, scheduling, waitlist, transfers · 9c54a882
      DevPilot authored
      Classic hourly booking form, booking passes (confirmed orphaned gate-scan
      API), daily/weekly schedule, blackout dates, conflict detection scope,
      locker rentals, group schedule/waitlist/transfer (including the separate
      TrainingGroups parallel system), makeup sessions, and Service Desk. Flags
      a consistent cash-collected-outside-the-system pattern across three
      screens for a deliberate finance decision rather than three silent fixes.
      9c54a882
    • DevPilot's avatar
      fix(sports-activity): 4 real bugs found while documenting core screens · 28ea8431
      DevPilot authored
      - تقرير الماليات (SaFinanceReportService): الـ switch بتاع تكلفة المدربين
        كان بيقارن بقيم زي 'monthly'/'hourly' مش موجودة في enum الحقيقي
        (monthly_fixed/per_session/per_player/hybrid/salary) — يعني أي مدرب
        مش per_session كان بيتحسب تكلفته صفر، وصافي الربح المعروض في التقرير
        كان مبالغ فيه بشكل منهجي. اتصلحت لتغطي الخمس نماذج كلهم، بنفس صيغة
        الحساب المستخدمة فعلًا في Coaches\Services\CoachPaymentService
        (مدرب الأكاديمية 'salary' متعمّد يفضل صفر — أجره بيتحسب من خلال تسوية
        عقد الأكاديمية مش كبند تكلفة مباشر).
      - FacilityUnit::$fillable ما كانش فيه expected_capacity، فالحقل كان
        بيتقبل في الفورم ويتفلتر بصمت قبل ما يوصل لقاعدة البيانات.
      - EnrollmentService::forceEnroll() كان بيثبّت is_full=1 دايمًا بدل ما
        يحسبه — تجاوز واحد في مجموعة سعتها 20 كان بيقفل التسجيل العادي على
        الـ 19 مكان الفاضيين اللي بعده.
      - زرار سحب اللاعب من المجموعة كان بيتفعّل بصلاحية sa.group.enroll بينما
        الـ route نفسه محتاج sa.group.manage — تضارب بين الاتنين حسب الدور.
      28ea8431
    • DevPilot's avatar
      docs: extreme-detail reference for the rest of Accounting · 5d693197
      DevPilot authored
      Chart of accounts, cost centers/budgets, dimensions, revenue mapping
      manual screens, posting chains, accruals, gaps, opening entries/
      reclassification, bank reconciliation, loans/documentary credits/
      guarantees, settlements/billing, fiscal years/journal types/branch
      fees, and the exact calculation logic behind every report screen
      including all 5 financial statements — plus a status table for every
      defect found and fixed or flagged along the way.
      5d693197
    • DevPilot's avatar
      fix(accounting): guarantee_type dropdown offered values not in the live ENUM · e6e92996
      DevPilot authored
      نفس فئة الغلط بتاعة نوع القرض: القائمة كانت بتعرض initial/final/advance
      بينما الـ ENUM الحقيقي في القاعدة tender/performance/advance_payment/
      maintenance/customs/other — فأي اختيار غير maintenance كان يفشل الحفظ
      بـ Data truncated for column 'guarantee_type'.
      e6e92996
    • DevPilot's avatar
      fix(accounting): two more submit-blockers + rate columns too narrow for real values · 26a4ff88
      DevPilot authored
      بعد إصلاح أسماء الحقول، اختبرت الشاشات الثلاثة على السيرفر الحي ولقيت:
      
      - إنشاء اعتماد مستندي: تاريخ الانتهاء NOT NULL في القاعدة بس مش required
        في الفورم — الحفظ كان بيقع بـ 500. بقى required.
      - إنشاء خطاب ضمان: حقل رقم الخطاب كان required في الـ HTML بس الكنترولر
        بيتجاهله ويولّد رقمه لوحده — يعني المتصفح نفسه كان بيمنع الحفظ من غير
        ما توصل السيرفر أصلًا. بقى للعرض بس زي رقم القرض.
      - bank_loans.interest_rate و letters_of_guarantee.commission_rate كانوا
        DECIMAL(5,4) — أقصى قيمة 9.9999%. أي سعر فايدة حقيقي (15%، 22%...
        معدلات مصرية عادية) كان يفشل بـ Out of range. اتوسّعوا لـ DECIMAL(7,4).
      26a4ff88
    • DevPilot's avatar
      fix(accounting): 3 forms crash or silently discard data on submit · 3479e8e6
      DevPilot authored
      اكتشفت أثناء توثيق الشاشات المتبقية إن تلات فورمات معطلة فعليًا:
      
      - إنشاء اعتماد مستندي: أسماء الحقول (credit_number، supplier_id،
        bank_account_id، currency) ما تطابقش اللي الكنترولر بيقراه
        (issuing_bank_id، beneficiary_supplier_id، currency_code)، ومفيش حقل
        أصلًا لـ beneficiary_name رغم إنه NOT NULL في القاعدة — يعني أي محاولة
        حفظ كانت بتطلّع 500 من قاعدة البيانات.
      - خطاب ضمان جديد: نفس المشكلة بالظبط — beneficiary بدل beneficiary_name
        NOT NULL، فأي حفظ كان بيقع.
      - قرض بنكي جديد: خانة الضمان اسمها collateral بس الكنترولر بيقرا
        collateral_description، فأي نص كان بيتكتب فيها بيتفقد بصمت. وقائمة
        نوع القرض فيها قيم (term_loan، equipment) مش موجودة في enum القاعدة
        أصلًا (term، revolving، overdraft، mortgage) — اختيارها كان هيفشل
        الحفظ أو يبوّظ العمود. رقم القرض واسم البنك كانا حقول قابلة للتعديل
        بالمصادفة بس الكنترولر بيتجاهلهم دايمًا — بقوا معروضين للقراءة بس.
      3479e8e6
    • DevPilot's avatar
      docs: extreme-detail reference for all 5 system wizards · 05a6e6f8
      DevPilot authored
      Member retroactive entry, SA player registration, SA hourly booking,
      swimming pool reservations, and the accounting revenue-mapping wizard.
      Every field, validation rule, permission key, button, DB write, and
      error message, sourced from reading the actual controller/service code
      rather than the UI — plus a list of real defects found along the way
      (transaction leaks, dead endpoints, schema mismatches, silent failures)
      flagged for a deliberate fix pass rather than folded in unannounced.
      05a6e6f8
  2. 11 Sep, 2026 27 commits
    • DevPilot's avatar
      docs: الدليل النهائي — ٨٧ صفحة، ١٦ فصل، ١٤٥ خطوة · 9236b6a1
      DevPilot authored
      اتضافت خطوة البحث في دليل الحسابات (٧٣٣ حساب) اللي بتوضّح إن البحث
      بيشتغل من غير همزة وبالأرقام العربية ومع الأخطاء المطبعية.
      9236b6a1
    • DevPilot's avatar
      fix(ui): نواة البحث بتتحمّل في الـ head · 8f83d048
      DevPilot authored
      سكربتات الشاشات بتشتغل وقت تحميل الصفحة، يعني قبل سكربتات آخرها. فدليل
      الحسابات كان بيقع على الطريقة القديمة: «إيراد» بترجّع ١٢ و«ايراد» ٤،
      و«١٢٠٦» بالأرقام العربية صفر. النواة دلوقتي في الـ head قبل أي شاشة.
      8f83d048
    • DevPilot's avatar
      fix(ui): كل بحث في النظام بقى بيستخدم نواة التطبيع العربي · be4bf013
      DevPilot authored
      سبع شاشات كانت بتعمل بحث بـ toLowerCase().indexOf — يعني «احمد» ما
      بتلاقيش «أحمد» و«المطابقه» ما بتلاقيش «المطابقة». أهمهم دليل الحسابات
      (٧٣٣ حساب) والمدينون والدائنون والحسابات البنكية ومراكز التكلفة.
      
      دلوقتي كلهم على ArabicSearch: تطبيع الهمزات والتشكيل والأرقام العربية،
      ومطابقة تقريبية بتتحمّل الأخطاء المطبعية. ودليل الحسابات بيطبّع نص كل
      صف مرة واحدة بدل كل ضغطة زرار.
      be4bf013
    • DevPilot's avatar
      chore(accounting): تنضيف بقايا شاشات كشف الحساب القديمة + ضبط دقة البحث · d7e46851
      DevPilot authored
      - شاشتين وميثودين وداليتين قراءة بقوا ميتين بعد توحيد كشوف الحسابات.
        AccountStatementService فضلت لتسجيل الحركات (١٦ مكان بيستخدمها).
      - البحث بالحروف المتتابعة كان فضفاض: «recon» كانت بتلاقي «شئون العضوية»
        لأن حروفها متفرقة في النص. اتضاف شرط تقارب — الحروف لازم تكون في مدى
        ضعف طول اللي اتكتب.
      d7e46851
    • DevPilot's avatar
      docs: تحديث الدليل — ٨٧ صفحة، ١٦ فصل، ١٤٤ خطوة · 7783bd1e
      DevPilot authored
      الفصل الجديد بيغطي تنظيم قائمة المالية الجديد، كشوف الحسابات الموحّدة
      بالمطابقة، البحث والترتيب في الجداول، وفحص جاهزية الإقفال. وفصل البنوك
      اتشال منه لقطة القائمة القديمة بعد ما التنظيم اتغيّر.
      7783bd1e
    • DevPilot's avatar
      fix(accounting): use statement مكانش ينفع جوه if — واتضاف فحص صياغة شامل · b2c3414b
      DevPilot authored
      الـ view اتدفع وفيه خطأ صياغة لأن git commit ما بيلنتش. tools/php_lint.sh
      بيفحص الـ ٢٣٩٠ ملف PHP في ثواني، وده اللي كان لازم يتشغّل قبل الدفع.
      b2c3414b
    • DevPilot's avatar
      feat(accounting): فحص جاهزية قبل إقفال الشهر · d1cdb878
      DevPilot authored
      الإقفال كان بيتحقق من حاجة واحدة (القيود المسودة) وبعدين يقفل. أسوأ حاجة
      في الإقفال إن المحاسب يقفل ويطلّع القوائم وبعدين يكتشف ناقص ويضطر يفتح تاني.
      
      الشاشة دلوقتي بتعمل سبع فحوصات قبل الإقفال، كل واحد بلينك يوصّل للشاشة
      اللي تحلّه: القيود المسودة، توازن الميزان، الشهر السابق، العمليات غير
      المقيّدة، الفلوس الواقفة في حسابات وسيطة، الشيكات المستحقة، والمطابقة
      البنكية.
      
      المسودة وعدم التوازن موانع بتوقف الإقفال — الباقي تنبيهات والقرار للمحاسب.
      والفحص بيتعاد في الكنترولر مش بس في الشاشة عشان حد ما يعديه بـ POST مباشر.
      d1cdb878
    • DevPilot's avatar
    • DevPilot's avatar
      fix(accounting): المطابقة تقارن حساب الذمم مش إجمالي الدفتر · 9245247e
      DevPilot authored
      أول ما شغّلت الشاشة على عضو حقيقي طلّعت «فرق ٣٦٣ ألف» على عضو حسابه سليم.
      السبب: القيد بيتعلّم برقم العضو على الطرفين (مدين الصندوق / دائن الإيراد)،
      فجمع كل سطوره بيطلّع صفر دايمًا والفرق مالوش معنى.
      
      المطابقة الصح بتقارن رصيد العضو في حسابات الذمم (١٢٠٣/١٢٠٤ للعملاء،
      ٢٣٠٦ للموردين) بالحساب المساعد. والعضو اللي بيدفع نقدي وقت العملية
      مالوش حساب ذمم أصلًا، فبقت الشاشة بتقول كده صريح بدل تحذير غلط.
      9245247e
    • DevPilot's avatar
      feat(ui): بحث وترتيب تلقائي لأي جدول طويل · 7a6dba5d
      DevPilot authored
      ٣١٦ شاشة فيها جدول من غير أي خانة بحث، والمستخدم بيلف بعينه على الصفحة.
      بدل ما نضيف خانة في كل شاشة (شغل ما بيخلصش وبيتنسي في الجديد)، مكوّن
      واحد بيشتغل لوحده على أي جدول أكتر من ٨ صفوف:
      
      - خانة بحث بتفلتر وانت بتكتب، بنفس نواة التطبيع العربي بتاعة النظام.
      - ترتيب بالضغط على عنوان العمود، بيفهم الأرقام والفلوس والتواريخ فـ
        «1,200.00 ج.م» بتترتّب كرقم مش كنص.
      - عدّاد «كام من كام»، ورسالة واضحة لما مفيش نتيجة.
      - بيتخطى الشاشات اللي فيها بحث أصلًا، وبيتعطّل بـ data-no-filter.
      7a6dba5d
    • DevPilot's avatar
      feat(accounting): المحاسبة المالية — قائمة بثلاث مستويات وكشف حساب موحّد · 60962799
      DevPilot authored
      القائمة:
      - «المحاسبة والدفتر العام» بقت «المحاسبة المالية»، وتحتها مجموعات:
        المؤشرات المالية، حسابات البنوك، المراجعة الداخلية والإقفال،
        القوائم المالية، والإعدادات المالية.
      - مراكز التكلفة والموازنات خرجوا لقائمة «المحاسبة الإدارية» — دول أدوات
        قرار مش جزء من دورة القيد والترحيل.
      - الشريط الجانبي كان بيدعم مستويين بس، فاتعمل يدعم أي عمق: الترشيح
        بالصلاحيات، وحالة «مفتوح»، والبحث كلهم بيمشوا على الشجرة كلها.
      
      كشوف الحسابات:
      - «كشف حساب عضو» و«كشف حساب عميل» كانوا شاشتين على نفس العضو. اتوحّدوا
        مع كشف المورد في شاشة واحدة بتختار فيها نوع الطرف، وبتعرض دفتر الأستاذ
        والحساب المساعد جنب بعض وتقارن بينهم — لأن اختلافهم هو بالظبط اللي
        المحاسب محتاج يشوفه. الروابط القديمة بتحوّل عليها بنفس الطرف والفترة.
      
      البحث:
      - نواة واحدة (arabic-search.js) بتطبّع الهمزات والتشكيل والأرقام العربية
        وبتتحمّل الأخطاء المطبعية وبترتّب بالأقرب. كان في تلات تطبيقات مختلفة،
        واحدة منهم بتعمل indexOf عادي فـ«احمد» ما كانتش بتلاقي «أحمد».
      - أي قايمة فيها ١٢ خيار أو أكتر بتتحوّل لقايمة بحث تلقائيًا.
      
      tools/menu_link_check.py بيتأكد إن كل بند قائمة وكل لينك في الشاشات له
      route فعلي — ٢٨١ بند و٠ مكسور.
      60962799
    • DevPilot's avatar
      docs: rebuild the step-by-step tutorial with the bank group and cheque chapters · 2b4a3fba
      DevPilot authored
      ٨٠ صفحة، ١٥ فصل، ١٣٣ خطوة بلقطات حقيقية من النظام. الجديد فصلين:
      حسابات البنوك (التجميع في القائمة، البحث في دليل الحسابات، وإقفال أوراق
      القبض بالقيد اللي بيتولد منه) ودورة حياة الشيك بسجل حركته الكامل.
      2b4a3fba
    • DevPilot's avatar
      feat(accounting): bank group in the menu, searchable account lookup, notes receivable closing · 913bbb73
      DevPilot authored
      ١. قايمة المالية كانت ٤٥ بند في ليستة واحدة. اتقسمت لمجموعات:
         المحاسبة والدفتر العام / حسابات البنوك / الإيرادات والتحصيل /
         التقارير المحاسبية / القوائم المالية.
         «حسابات البنوك» جمّعت الحسابات البنكية والشيكات والأوراق التجارية
         وإقفال أوراق الدفع والقبض والودائع والمطابقة والقروض والاعتمادات.
      
      ٢. البحث جوه القوايم المنسدلة: مكوّن مشترك بيتفعّل لوحده على أي قائمة
         فيها أكتر من ١٢ خيار (زي دليل الحسابات في قيد اليومية)، وبيدوّر
         بالاسم أو بالرقم. البحث بيتجاهل الهمزات والتشكيل عشان يلاقي بالعربي.
      
      ٣. «إقفال أوراق القبض» — المقابل الناقص لإقفال أوراق الدفع: بيأكّد تحصيل
         الشيكات الواردة من كشف حساب البنك.
      
      ٤. سجل حركة الشيك بقى بيعمل القيد المحاسبي فعلًا. كان بيسجّل الحركة بس
         من غير ترحيل، وده كان بيخلي الشاشة الجديدة تخالف قاعدة إن أي حركة
         مالية لازم تنعكس على الدفتر. كل حركة دلوقتي متربوطة بقيدها.
      913bbb73
    • DevPilot's avatar
      fix: create 6 missing views and repair view-level crashes found by the smoke sweep · 31671ce5
      DevPilot authored
      - ٦ شاشات كانت بتقع بـ View not found: طلب عروض الأسعار، أنواع العمل الإضافي،
        سجل عهدة الأصل، تقرير حضور المرفق، مندوب المبيعات، وأسعار العملاء الخاصة.
      - مكوّن الترقيم المشترك بقى بيكمّل المفاتيح الناقصة لوحده، لأن ٢٠+ موديل
        بيبنوا مصفوفة الترقيم بإيدهم وبيبعتوا الأساسي بس.
      - شاشة الملاعب بترجّع 404 لما الملعب مش موجود بدل ما تقع على null.
      - تقرير الأبناء وسجل البوابة كانوا بيقروا مفاتيح مش موجودة.
      31671ce5
    • DevPilot's avatar
      fix(core): route params coerced to declared type, request errors no longer render as BOOT FAILURE · 49ccc60a
      DevPilot authored
      اتنين أعطال بنيوية طلعوا من فحص الـ 830 شاشة:
      
      1. الراوتر بيبعت بارامترات الـ URL كـ string، وفي ١٣ كنترولر معرّفينها int،
         ومع strict_types ده TypeError بيقع الشاشة. الراوتر دلوقتي بيقرا النوع
         المطلوب من الميثود نفسها ويحوّل ليه.
      
      2. public/index.php كان لافف الـ dispatch والـ boot في try واحدة، فأي
         استثناء جوه الطلب بيطلّع «BOOT FAILURE» بـ 500 حتى لو كان 404 أو 403.
         الطلب دلوقتي ليه try لوحده بيسلّم لـ ExceptionHandler اللي بيحترم الكود.
      49ccc60a
    • DevPilot's avatar
      fix(core): findOrFail returns 404 instead of 500, add route smoke tester · 2d5219ad
      DevPilot authored
      كل رابط لسجل محذوف أو مش موجود كان بيطلّع صفحة 500 لأن findOrFail
      بيرمي RuntimeException من غير code، والـ ExceptionHandler بيعتبر أي
      code غير 401/403/404 عطل سيرفر.
      
      tools/route_smoke.py بيفتح كل GET route في النظام (٨٣٠ شاشة) ويبلّغ
      عن أي 500 برسالة الخطأ.
      2d5219ad
    • DevPilot's avatar
      fix(sql): final pass — schema checker now clean across app/ · c9938287
      DevPilot authored
      كمّلت باقي الأعمدة الغلط: payments (مفيهاش status/receipt_number/payable_*)،
      academy_contracts، facility_zone_schedules (جدول متكرر بيوم الأسبوع مش بتاريخ)،
      carnets، sa_attendance (المجموعة على الحجز)، وsubscriptions للسنة المالية.
      
      tools/sql_schema_check.py بيعدّي دلوقتي على app/ كلها من غير أي ملاحظة.
      c9938287
    • DevPilot's avatar
      fix(sql): second pass on wrong columns and missing tables · b29e4e27
      DevPilot authored
      جداول ما كانتش موجودة أصلًا (sessions، group_players، training_group_players،
      facility_units، activities، membership_types) وكانت أي شاشة أو كرون بيلمسها بيقع.
      اتوجّهت للجداول الحقيقية (training_sessions، sa_group_players، sa_facility_units،
      sport_disciplines)، وتذكير تجديد العضوية اتعمل على السنة المالية لأن members
      مفيهاش تاريخ انتهاء أصلًا. وCustomerPricingService اتكتب من أول وجديد لأن
      الربط بالعضو على مستوى الصنف مش على مستوى القائمة.
      b29e4e27
    • DevPilot's avatar
      fix(sql): correct column names that crash screens at runtime, add a schema checker · 952aa695
      DevPilot authored
      كشف حساب العضو كان بيقع بـ Unknown column 'phone' — جدول members فيه
      phone_mobile/phone_home مش phone. ولما دورت على الغلط ده في باقي الكود
      لقيت نفس النوع في 20+ مكان تاني (coaches.name_ar، players.name_ar،
      members.member_number، hr_attendance.employee_id، وغيرهم).
      
      tools/sql_schema_check.py بيقارن كل SQL في app/ بالـ schema الحقيقي
      ويقع بـ exit 1 لو لقى عمود مش موجود، عشان النوع ده ما يوصلش للـ production تاني.
      952aa695
    • DevPilot's avatar
    • DevPilot's avatar
      fix(accounting): cheque register summary lost closed cheques and cards did not reconcile · 761ee72a
      DevPilot authored
      A cheque that was collected and then closed fell out of every bucket, so the
      five cards stopped summing to the total. 'closed' is archival and is reachable
      from collected/paid/endorsed/returned/replaced/cancelled, so resolve it back to
      the status it held before the close movement.
      
      Buckets are now defined once in bucketStatuses() and read by both the summary
      and the filter, and the cards link by bucket instead of a single status, so the
      number on a card equals what you get when you click it. 'open' is the remainder
      so the cards always reconcile. The summary ignores the selected status/bucket
      so you can still navigate between cards after clicking one.
      761ee72a
    • DevPilot's avatar
    • DevPilot's avatar
      feat(accounting): unified cheque register with full lifecycle history · 927edd62
      DevPilot authored
      The cheque data existed but the screen was a per-direction list and the
      status was overwritten in place — there was no record of who did what,
      when, or what the previous state was. Adds:
      
      - صادر ووارد شيكات بنكية: one screen for both directions, with the
        full filter set (date range on either the cheque date or the movement
        date, direction, number, party, bank, branch, status, amount range),
        a reset, and per-direction summary cards whose totals are clickable
        and drive the filters.
      - instrument_movements: every action is appended as an immutable row
        (action, from/to status, date, user, bank, reference, notes). Nothing
        is ever deleted, so each cheque carries a complete audit trail.
        Existing cheques get an opening "register" movement on migrate so the
        history starts from a known point.
      - The complete status sets for both directions — registered, ready,
        delivered, deposited, under collection, pending, collected, paid,
        bounced, endorsed, returned, replaced, cancelled, closed — with a
        direction-specific transition map that refuses illogical moves such
        as collecting a cancelled cheque.
      - Cheques with movements cannot be deleted; corrections are new
        actions, not edits.
      
      The older screens now funnel through the same service, and the bounce
      and resolve paths log movements too, so the trail stays complete no
      matter which screen the action came from.
      927edd62
    • DevPilot's avatar
      docs: add tax rules, scoped voucher lookup and gap drill-down to the tutorial · ec7ca512
      DevPilot authored
      Two new chapters bring the guide to 108 steps over 13 chapters:
      
      - قواعد الضرائب: entering and amending income-tax brackets from the new
        screen — including the simulator that proves the calculation before
        payroll runs, and the validation refusing a set with a gap between
        brackets — plus the revenue tax profiles screen.
      - The voucher account lookup now scoped per side, shown for both a
        صرف and a قبض voucher, and the "عرض التفاصيل" drill-down on the
        accounting gaps screen listing the real documents behind each count.
      ec7ca512
    • DevPilot's avatar
      fix(accounting): gap detail queries referenced columns that do not exist · fa94e5b8
      DevPilot authored
      The drill-down queries were written against assumed column names and
      every one of them failed, so "عرض التفاصيل" always answered "لا توجد
      تفاصيل متاحة". Corrected against the real schema: pool zone bookings
      have label/zone_row/zone_col and no zone table, player cards use
      valid_from/issued_at and card_number, pool bookings carry booker_name
      rather than a member join, and private match bookings store total_cost
      rather than total_amount.
      fa94e5b8
    • DevPilot's avatar
      feat(hr,accounting): tax bracket admin screen, scoped voucher account lookup, gap drill-down · b16d18aa
      DevPilot authored
      Three things that were asked for and were genuinely missing:
      
      1. ضريبة كسب العمل had no screen at all — the brackets lived only in
         hr_tax_brackets and could only be changed with SQL. Adds a proper
         admin screen: brackets are versioned as a set per effective_date,
         old sets are kept (never deleted) so past payroll stays explainable,
         and activating a set deactivates the others. The form validates that
         brackets are contiguous, that only the last one is open-ended, and
         auto-fills the next bracket's start. A built-in simulator shows the
         tax on any annual income so the accountant can verify the set before
         running payroll.
      
         Also hardens the live calculator: IncomeTaxService summed every row
         flagged active regardless of effective_date, so two overlapping
         active sets produced a silently wrong tax. It now uses the newest
         active set only.
      
      2. The voucher screen searched the entire chart of accounts for both
         sides, so you could pick a fixed-asset account as the cash side or a
         cash account as the expense side — the latter trips the "same as the
         cash account" guard and the voucher just refuses to save. The lookup
         is now scoped per side: cash/bank accounts for the counter side, and
         expense (صرف) or revenue (قبض) accounts for the line side depending
         on the voucher's direction.
      
      3. The accounting gaps screen showed only a count per gap. Each gap now
         has a "عرض التفاصيل" button that lists the actual documents behind
         that number — id, date, party, quantity, recorded amount and status
         — so the accountant can check the cases before choosing a rate.
      b16d18aa
    • DevPilot's avatar
      docs: replace the screen catalogue with a real step-by-step tutorial · 33dc1451
      DevPilot authored
      The previous PDF showed screens from the outside — long full-page dumps
      of lists, with no instruction. This one teaches the system by driving
      it: every step opens the screen, fills the form with real data, submits,
      shows the result, and then shows the journal entry that operation
      produced. Screenshots are top-of-screen crops only.
      
      93 steps across 11 chapters: manual journal entry; the full fixed-asset
      lifecycle (purchase, maintenance, monthly depreciation, disposal with
      gain/loss) with the auto-generated entry after each; the government
      procurement cycle from PR through tender, committees, technical
      evaluation, the 3-offer gate, re-tender, award by item and the
      resulting purchase orders; the auction cycle from lots and committees
      through bidding, award, collection and the disposal entry; payment and
      receipt vouchers with cheque tracking; asset custody; GL sync preview;
      and the five financial statements.
      
      The superseded catalogue PDF is removed — it remains in git history.
      33dc1451