- 12 Sep, 2026 7 commits
-
-
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.
-
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 — تضارب بين الاتنين حسب الدور.
-
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.
-
DevPilot authored
نفس فئة الغلط بتاعة نوع القرض: القائمة كانت بتعرض initial/final/advance بينما الـ ENUM الحقيقي في القاعدة tender/performance/advance_payment/ maintenance/customs/other — فأي اختيار غير maintenance كان يفشل الحفظ بـ Data truncated for column 'guarantee_type'.
-
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).
-
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) — اختيارها كان هيفشل الحفظ أو يبوّظ العمود. رقم القرض واسم البنك كانا حقول قابلة للتعديل بالمصادفة بس الكنترولر بيتجاهلهم دايمًا — بقوا معروضين للقراءة بس.
-
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.
-
- 11 Sep, 2026 33 commits
-
-
DevPilot authored
اتضافت خطوة البحث في دليل الحسابات (٧٣٣ حساب) اللي بتوضّح إن البحث بيشتغل من غير همزة وبالأرقام العربية ومع الأخطاء المطبعية.
-
DevPilot authored
سكربتات الشاشات بتشتغل وقت تحميل الصفحة، يعني قبل سكربتات آخرها. فدليل الحسابات كان بيقع على الطريقة القديمة: «إيراد» بترجّع ١٢ و«ايراد» ٤، و«١٢٠٦» بالأرقام العربية صفر. النواة دلوقتي في الـ head قبل أي شاشة.
-
DevPilot authored
سبع شاشات كانت بتعمل بحث بـ toLowerCase().indexOf — يعني «احمد» ما بتلاقيش «أحمد» و«المطابقه» ما بتلاقيش «المطابقة». أهمهم دليل الحسابات (٧٣٣ حساب) والمدينون والدائنون والحسابات البنكية ومراكز التكلفة. دلوقتي كلهم على ArabicSearch: تطبيع الهمزات والتشكيل والأرقام العربية، ومطابقة تقريبية بتتحمّل الأخطاء المطبعية. ودليل الحسابات بيطبّع نص كل صف مرة واحدة بدل كل ضغطة زرار.
-
DevPilot authored
- شاشتين وميثودين وداليتين قراءة بقوا ميتين بعد توحيد كشوف الحسابات. AccountStatementService فضلت لتسجيل الحركات (١٦ مكان بيستخدمها). - البحث بالحروف المتتابعة كان فضفاض: «recon» كانت بتلاقي «شئون العضوية» لأن حروفها متفرقة في النص. اتضاف شرط تقارب — الحروف لازم تكون في مدى ضعف طول اللي اتكتب.
-
DevPilot authored
الفصل الجديد بيغطي تنظيم قائمة المالية الجديد، كشوف الحسابات الموحّدة بالمطابقة، البحث والترتيب في الجداول، وفحص جاهزية الإقفال. وفصل البنوك اتشال منه لقطة القائمة القديمة بعد ما التنظيم اتغيّر.
-
DevPilot authored
الـ view اتدفع وفيه خطأ صياغة لأن git commit ما بيلنتش. tools/php_lint.sh بيفحص الـ ٢٣٩٠ ملف PHP في ثواني، وده اللي كان لازم يتشغّل قبل الدفع.
-
DevPilot authored
الإقفال كان بيتحقق من حاجة واحدة (القيود المسودة) وبعدين يقفل. أسوأ حاجة في الإقفال إن المحاسب يقفل ويطلّع القوائم وبعدين يكتشف ناقص ويضطر يفتح تاني. الشاشة دلوقتي بتعمل سبع فحوصات قبل الإقفال، كل واحد بلينك يوصّل للشاشة اللي تحلّه: القيود المسودة، توازن الميزان، الشهر السابق، العمليات غير المقيّدة، الفلوس الواقفة في حسابات وسيطة، الشيكات المستحقة، والمطابقة البنكية. المسودة وعدم التوازن موانع بتوقف الإقفال — الباقي تنبيهات والقرار للمحاسب. والفحص بيتعاد في الكنترولر مش بس في الشاشة عشان حد ما يعديه بـ POST مباشر.
-
DevPilot authored
-
DevPilot authored
أول ما شغّلت الشاشة على عضو حقيقي طلّعت «فرق ٣٦٣ ألف» على عضو حسابه سليم. السبب: القيد بيتعلّم برقم العضو على الطرفين (مدين الصندوق / دائن الإيراد)، فجمع كل سطوره بيطلّع صفر دايمًا والفرق مالوش معنى. المطابقة الصح بتقارن رصيد العضو في حسابات الذمم (١٢٠٣/١٢٠٤ للعملاء، ٢٣٠٦ للموردين) بالحساب المساعد. والعضو اللي بيدفع نقدي وقت العملية مالوش حساب ذمم أصلًا، فبقت الشاشة بتقول كده صريح بدل تحذير غلط.
-
DevPilot authored
٣١٦ شاشة فيها جدول من غير أي خانة بحث، والمستخدم بيلف بعينه على الصفحة. بدل ما نضيف خانة في كل شاشة (شغل ما بيخلصش وبيتنسي في الجديد)، مكوّن واحد بيشتغل لوحده على أي جدول أكتر من ٨ صفوف: - خانة بحث بتفلتر وانت بتكتب، بنفس نواة التطبيع العربي بتاعة النظام. - ترتيب بالضغط على عنوان العمود، بيفهم الأرقام والفلوس والتواريخ فـ «1,200.00 ج.م» بتترتّب كرقم مش كنص. - عدّاد «كام من كام»، ورسالة واضحة لما مفيش نتيجة. - بيتخطى الشاشات اللي فيها بحث أصلًا، وبيتعطّل بـ data-no-filter.
-
DevPilot authored
القائمة: - «المحاسبة والدفتر العام» بقت «المحاسبة المالية»، وتحتها مجموعات: المؤشرات المالية، حسابات البنوك، المراجعة الداخلية والإقفال، القوائم المالية، والإعدادات المالية. - مراكز التكلفة والموازنات خرجوا لقائمة «المحاسبة الإدارية» — دول أدوات قرار مش جزء من دورة القيد والترحيل. - الشريط الجانبي كان بيدعم مستويين بس، فاتعمل يدعم أي عمق: الترشيح بالصلاحيات، وحالة «مفتوح»، والبحث كلهم بيمشوا على الشجرة كلها. كشوف الحسابات: - «كشف حساب عضو» و«كشف حساب عميل» كانوا شاشتين على نفس العضو. اتوحّدوا مع كشف المورد في شاشة واحدة بتختار فيها نوع الطرف، وبتعرض دفتر الأستاذ والحساب المساعد جنب بعض وتقارن بينهم — لأن اختلافهم هو بالظبط اللي المحاسب محتاج يشوفه. الروابط القديمة بتحوّل عليها بنفس الطرف والفترة. البحث: - نواة واحدة (arabic-search.js) بتطبّع الهمزات والتشكيل والأرقام العربية وبتتحمّل الأخطاء المطبعية وبترتّب بالأقرب. كان في تلات تطبيقات مختلفة، واحدة منهم بتعمل indexOf عادي فـ«احمد» ما كانتش بتلاقي «أحمد». - أي قايمة فيها ١٢ خيار أو أكتر بتتحوّل لقايمة بحث تلقائيًا. tools/menu_link_check.py بيتأكد إن كل بند قائمة وكل لينك في الشاشات له route فعلي — ٢٨١ بند و٠ مكسور.
-
DevPilot authored
٨٠ صفحة، ١٥ فصل، ١٣٣ خطوة بلقطات حقيقية من النظام. الجديد فصلين: حسابات البنوك (التجميع في القائمة، البحث في دليل الحسابات، وإقفال أوراق القبض بالقيد اللي بيتولد منه) ودورة حياة الشيك بسجل حركته الكامل.
-
DevPilot authored
١. قايمة المالية كانت ٤٥ بند في ليستة واحدة. اتقسمت لمجموعات: المحاسبة والدفتر العام / حسابات البنوك / الإيرادات والتحصيل / التقارير المحاسبية / القوائم المالية. «حسابات البنوك» جمّعت الحسابات البنكية والشيكات والأوراق التجارية وإقفال أوراق الدفع والقبض والودائع والمطابقة والقروض والاعتمادات. ٢. البحث جوه القوايم المنسدلة: مكوّن مشترك بيتفعّل لوحده على أي قائمة فيها أكتر من ١٢ خيار (زي دليل الحسابات في قيد اليومية)، وبيدوّر بالاسم أو بالرقم. البحث بيتجاهل الهمزات والتشكيل عشان يلاقي بالعربي. ٣. «إقفال أوراق القبض» — المقابل الناقص لإقفال أوراق الدفع: بيأكّد تحصيل الشيكات الواردة من كشف حساب البنك. ٤. سجل حركة الشيك بقى بيعمل القيد المحاسبي فعلًا. كان بيسجّل الحركة بس من غير ترحيل، وده كان بيخلي الشاشة الجديدة تخالف قاعدة إن أي حركة مالية لازم تنعكس على الدفتر. كل حركة دلوقتي متربوطة بقيدها.
-
DevPilot authored
- ٦ شاشات كانت بتقع بـ View not found: طلب عروض الأسعار، أنواع العمل الإضافي، سجل عهدة الأصل، تقرير حضور المرفق، مندوب المبيعات، وأسعار العملاء الخاصة. - مكوّن الترقيم المشترك بقى بيكمّل المفاتيح الناقصة لوحده، لأن ٢٠+ موديل بيبنوا مصفوفة الترقيم بإيدهم وبيبعتوا الأساسي بس. - شاشة الملاعب بترجّع 404 لما الملعب مش موجود بدل ما تقع على null. - تقرير الأبناء وسجل البوابة كانوا بيقروا مفاتيح مش موجودة.
-
DevPilot authored
اتنين أعطال بنيوية طلعوا من فحص الـ 830 شاشة: 1. الراوتر بيبعت بارامترات الـ URL كـ string، وفي ١٣ كنترولر معرّفينها int، ومع strict_types ده TypeError بيقع الشاشة. الراوتر دلوقتي بيقرا النوع المطلوب من الميثود نفسها ويحوّل ليه. 2. public/index.php كان لافف الـ dispatch والـ boot في try واحدة، فأي استثناء جوه الطلب بيطلّع «BOOT FAILURE» بـ 500 حتى لو كان 404 أو 403. الطلب دلوقتي ليه try لوحده بيسلّم لـ ExceptionHandler اللي بيحترم الكود.
-
DevPilot authored
كل رابط لسجل محذوف أو مش موجود كان بيطلّع صفحة 500 لأن findOrFail بيرمي RuntimeException من غير code، والـ ExceptionHandler بيعتبر أي code غير 401/403/404 عطل سيرفر. tools/route_smoke.py بيفتح كل GET route في النظام (٨٣٠ شاشة) ويبلّغ عن أي 500 برسالة الخطأ.
-
DevPilot authored
كمّلت باقي الأعمدة الغلط: payments (مفيهاش status/receipt_number/payable_*)، academy_contracts، facility_zone_schedules (جدول متكرر بيوم الأسبوع مش بتاريخ)، carnets، sa_attendance (المجموعة على الحجز)، وsubscriptions للسنة المالية. tools/sql_schema_check.py بيعدّي دلوقتي على app/ كلها من غير أي ملاحظة.
-
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 اتكتب من أول وجديد لأن الربط بالعضو على مستوى الصنف مش على مستوى القائمة.
-
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 تاني.
-
DevPilot authored
-
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.
-
DevPilot authored
-
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.
-
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.
-
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.
-
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.
-
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.
-
DevPilot authored
The transfer form posts new_employee_id / new_location, but storeTransfer() read to_employee_id / to_location. Both came back empty, so every transfer wrote NULL over custodian_employee_id and blanked the location — the asset ended up with no custodian at all, and asset_custody_history recorded the move with an empty recipient. Reads the names the form actually sends, keeping the old ones as a fallback.
-
DevPilot authored
When fewer than three offers pass technical evaluation the tender is flagged "تحتاج إعادة طرح" — but that status also hid the invite-vendors form, so there was no way to actually invite more vendors and the process dead-ended with no route forward. Adds a "إعادة طرح المناقصة" action next to the rejection reason that returns the tender to published and clears the reason, so additional vendors can be invited and their offers recorded.
-
DevPilot authored
MySQL strict mode rejects '' for a DATE/DATETIME column, so any form with an untouched optional date aborted the whole insert with "Incorrect date value: ''". This was not one bug — quote responses, vouchers, bounced cheques, bank loans, documentary credits, letters of guarantee, settlements, tenders, auctions, committees, payments, overtime, permission requests and more all passed the raw post value straight through. Adds Request::postDate(), which returns NULL for an empty date field, and routes all 33 optional-date call sites through it. QuoteService also guards expiry_date directly since it is called from more than one path.
-
DevPilot authored
Four issues raised from the demo instance: 1. GL sync preview kept listing rows that could never sync. The preview selected every non-voided payment while syncPayments() skips amount <= 0 in PHP, so zero-amount payments stayed "pending" forever — most visible right after a sync. Both queries now filter amount > 0, matching the dashboard count. 2. The five financial statements (income statement, balance sheet, consolidated balance sheet, cash flow, changes in equity) moved out of the flat Accounting menu into their own "القوائم المالية" group inside the المالية section. 3. Technical and financial committees — in both tenders and auctions — now carry تاريخ التشكيل / تاريخ الانعقاد / تاريخ البت on the formation screen, plus multi-file attachments per committee (new committee_attachments table + CommitteeAttachmentService, modelled on the existing Support attachment service). Dates and attachment links render on the tender and auction pages, with a permission-checked download route for each scope. 4. Asset custody screens inner-joined inventory_items, so every asset without a stock item — buildings, courts, machines registered directly, which is most of the register — was invisible there. Now LEFT JOIN with the same name fallback the asset card already uses.
-
DevPilot authored
QuoteController@recordResponse rendered Procurement.Views.quotes.response_form, which was never created — so /procurement/quotes/{id}/response returned a 500 and vendor prices could not be recorded at all. Without it the whole tender cycle is blocked: no quote totals, so no technical gate, no financial comparison, and no award. Fields match what storeResponse() reads: quote_item_ids[], quantities[], unit_prices[], delivery_days[], plus delivery/payment terms and expiry date. -
DevPilot authored
The cheque fields stay in the voucher form for every payment method, so check_date posts as '' when paying cash or by transfer. That went straight into a DATE column and MySQL rejected the row with "Incorrect date value: ''", so no non-cheque voucher could be saved at all. Send NULL instead; VoucherService already accepted null. Found by driving the voucher screen end-to-end while building the step-by-step accounting tutorial.
-