# نظام إدارة بيانات عائلة آل بارجاء

Laravel 12 + Filament 3 — عربي RTL بالكامل، بتوقيت عدن.

---

## ⚠️ القاعدة الذهبية — اقرأ قبل أي شيء

قاعدة البيانات **موجودة مسبقاً** (55 جدولاً، 89 علاقة، أعمدة مولّدة، قيود CHECK) من ملف `barjaa_full.sql`.

- ❌ **ممنوع** إنشاء migrations لجداول النظام.
- ❌ **ممنوع** `php artisan migrate:fresh` — سيدمّر القاعدة.
- ✅ المسموح فقط: migrations جداول لارافل التشغيلية (users, sessions, cache, jobs) — وهي المرفقة في `database/migrations/` ومحمية بـ `hasTable`.
- ✅ `charset=utf8mb4` و `collation=utf8mb4_unicode_520_ci` إلزاميان (مضبوطان في `config/database.php`).

## التثبيت

```bash
# 1) القاعدة أولاً — قبل أي أمر لارافل
mysql -u root -p -e "CREATE DATABASE barjaa CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci"
mysql -u root -p barjaa < barjaa_full.sql

# 2) الحزم والبيئة
composer install
cp .env.example .env
php artisan key:generate
# عدّل بيانات الاتصال في .env

# 3) جداول لارافل التشغيلية فقط (آمنة — لن تمس جداول النظام)
php artisan migrate

# 4) مستخدم أول (اربطه بدور super_admin من جدول role_user)
php artisan tinker
>>> $u = App\Models\User::create(['name'=>'المدير','email'=>'admin@barjaa.ye','password'=>'كلمة-قوية']);
>>> $u->roles()->attach(App\Models\Role::where('code','super_admin')->value('id'));

# 5) شغّل
php artisan serve          # لوحة التحكم: /admin — النموذج العام: /form
php artisan queue:work     # للمهام (الاكتمال)
php artisan schedule:work  # للجدولة اليومية
```

## استيراد الاستمارات الـ85

```bash
# dry-run إلزامي أولاً — تقرير كامل بلا كتابة
php artisan barjaa:import storage/forms.xlsx --dry-run

# راجع التقرير (مطابقات، يحتاج مراجعة، قيم نصية شاذة) ثم:
php artisan barjaa:import storage/forms.xlsx
```

المعالجات المدمجة: حذف محارف الاتجاه `\u202a`، الأرقام الهندية، تفكيك الخانات متعددة القيم (مريضان ← صفان، 7 دورات ← 7 صفوف، سيؤن + صنعاء ← سكن أساسي + ثانوي)، فصل المدينة/الحي، الزوجات من خارج العائلة (بازغيفان/باحنان/باحميد/باكثير ← `is_family_member=0`)، الأرقام النصية («لا يوجد أبناء») ← تُهمل مع ملاحظة.

## الاختبارات

```bash
# فحص المطبِّع بلا قاعدة (30 حالة):
php tests/standalone_normalizer_check.php

# قاعدة الاختبار: نفس barjaa_full.sql في قاعدة مستقلة — لا migrate:fresh
mysql -u root -p -e "CREATE DATABASE barjaa_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci"
mysql -u root -p barjaa_test < barjaa_full.sql
vendor/bin/phpunit
```

العزل في `tests/TestCase.php` عبر معاملات يدوية (rollback بعد كل اختبار).

## خريطة الكود

| المكوّن | الملف | المرجع |
|---|---|---|
| المطبِّع العربي (اسم/هاتف/تاريخ مرن) | `app/Support/ArabicNormalizer.php` | §3 |
| ترجمة قيود UNIQUE لرسائل عربية | `app/Exceptions/DatabaseGuardHandler.php` | §4 |
| الأعمدة المولّدة (حماية 3105) | `app/Models/Concerns/HasGeneratedColumns.php` | §2 |
| النطاقات الاشتقاقية (أرامل/أيتام/طلاب...) | `app/Models/Person.php` | §5 |
| الأدوار وحجب البيانات الصحية + تسجيل الاطلاع | `app/Policies/` | §6 |
| النموذج العام من field_definitions | `app/Http/Controllers/PublicFormController.php` + `resources/views/form/` | §7 |
| **ميزة الرابط الجزئي** | `app/Services/FillRequestService.php` + `FillLinkController` + `views/fill/` | §8 |
| المطابقة (4 طبقات، عتبات 85/60) | `app/Services/PersonMatchService.php` | §9 |
| الدمج مع snapshot وتراجع | `app/Services/PersonMergeService.php` | §9 |
| الاكتمال والحقول الناقصة | `app/Services/CompletenessService.php` + `RecalculateCompletenessJob` | §10 |
| الاستيراد | `app/Console/Commands/ImportSubmissionsCommand.php` | §11 |
| لوحة Filament (RTL، Cairo، أخضر) | `app/Filament/` + `AdminPanelProvider` | §6-§8 |

## المبادئ المضمّنة في الكود

1. **الخام لا يُمس**: `form_submissions` تُحفظ أولاً ولا تُحذف أبداً (`canDelete=false`).
2. **لا كتابة مباشرة من الروابط**: كل ما يصل من `/f/...` يمر عبر `data_update_requests` والمراجعة (إلا `auto_approve`).
3. **token خام يُعرض مرة واحدة**: يُخزَّن `sha256` فقط، والمقارنة بـ `hash_equals`.
4. **تقليل النطاق**: صفحة الرابط تعرض الاسم للتأكيد + الحقول المطلوبة فقط.
5. **لا حذف فعلي**: `SoftDeletes` + الدمج بـ snapshot قابل للتراجع.
6. **التطبيع في Observer**: يعمل من كل المداخل (نموذج، استيراد، لوحة).
7. **الاشتقاق لا الأعلام**: أرملة/يتيم/طالب تُحسب من العلاقات وقت الطلب.

## خيارات الاستمارة الأصلية (`field_definitions`)

كل حقل من نوع `select/radio/multiselect/checkbox` صار له خيارات — مصدرها أحد اثنين:

1. **`lookup_table`** (١٥ حقلاً: الجنسية، المدينة، المهنة، التخصص، الحالات الصحية…) — تُقرأ حيّة من الجدول
   عبر `FieldDefinition::resolvedOptions()`، فالمفتاح هو `id` وأي صف جديد في الجدول يظهر فوراً في الاستمارة.
2. **`options`** (١٥ حقلاً enum أو منطقي) — تُزرع بـ `php artisan db:seed --class=FormFieldOptionsSeeder`.

> **قاعدة إلزامية**: مفتاح الخيار هو ما يُكتب في القاعدة، والقيمة هي ما يراه المستجيب.
> فحقل مرتبط بعمود enum مفاتيحه من قيم الـ enum حرفياً (`male` لا «ذكر»)، والحقل المنطقي مفاتيحه `1`/`0`.
> يحرس هذا `FormFieldOptionsTest` الذي يقارن كل مفتاح بقيم عمود القاعدة الفعلية.

أسئلة «هل…» تأخذ نعم/لا، وتُضاف «ربما» حين تكون واقعية (`jobs.willingness` و`sponsor.needs_support`).
التحرير من اللوحة: **الإعدادات ← تعريفات الحقول** — فيها اختيار جدول المصدر أو تحرير الخيارات الثابتة.

## قسم المشاريع — بطاقة تعريف المشروع

شاشة إضافة المشروع (`/admin/projects/create`) تغطي بنود بطاقة التعريف الـ21 موزّعة على أربعة تبويبات:
التعريف (١–٦) • المستفيدون والتنفيذ (٧–٩ و١٥) • التمويل والميزانية (١٠–١٤) • الفريق والمتابعة (١٦–٢١).

لإضافة مشروع: **المشاريع ← المشاريع ← زر «إضافة مشروع»** أعلى يسار الجدول (أو `/admin/projects/create` مباشرة).

**الخيارات تُدار من اللوحة لا من الكود.** كل قائمة منسدلة مصدرها جدول خيارات مستقل، وكلها
مجموعة في مدخل واحد داخل قسم المشاريع: **المشاريع ← خيارات المشاريع** (`/admin/projects/options`):

| البند | الجدول | البند | الجدول |
|---|---|---|---|
| ٢ الجهة المشرفة | `supervising_bodies` | ١٣ العملة | `currencies` |
| ٣ نوع المشروع | `project_types` | ١٤ نوع الشريك | `partner_types` |
| ٣ التصنيف القطاعي | `project_categories` | ١٥ آلية التسجيل | `registration_methods` |
| ٦ مدة المشروع | `project_durations` | ١٧ دور عضو الفريق | `team_roles` |
| ٧ فئات المستفيدين | `beneficiary_categories` | ١٨ مؤشرات النجاح | `success_indicator_types` |
| ١٠ جهة التمويل | `funding_sources` | ١٩ دورية التقارير | `reporting_periods` |
| ١١ مدة التمويل | `funding_periods` | ٢٠ حالة المشروع | `project_statuses` |

- القيم الأولية تُزرع بـ `php artisan db:seed --class=ProjectCardOptionsSeeder` (idempotent — يُطابق على `code` فلا يكرّر ولا يمسح ترتيبك).
- **الإخفاء لا الحذف**: أوقف `is_active` ليختفي الخيار من القوائم الجديدة دون المساس بالمشاريع التي استخدمته.
- `project_statuses.legacy_status` يربط حالات البطاقة الست بعمود `projects.status` القديم (enum)، ويُزامَن آلياً عند كل حفظ — فالتقارير القديمة تبقى صحيحة.
- الشركاء وفريق العمل ومؤشرات النجاح تُخزَّن JSON في `projects` (repeater في اللوحة)، والفئات المستهدفة في جدول وسيط `project_beneficiary_category`.

> الأعمدة الجديدة أُضيفت بـ migration **إضافية فقط** (`2026_07_31_000100_extend_projects_for_project_card`)
> محميّة بـ `hasTable`/`hasColumn`: لا حذف ولا تعديل لأي عمود قائم — القاعدة الذهبية سليمة.

## ملاحظة مواءمة المخطط

الكود كُتب وفق أسماء الجداول والأعمدة الواردة في المواصفات. ملف `barjaa_full.sql` الفعلي لم يكن مرفقاً أثناء البناء، فبعض الأعمدة المفترضة قد تحتاج مطابقة سريعة مع المخطط الحقيقي، أبرزها:

- `field_definitions`: `model_attribute`, `target_table`, `visible_when`, `label_ar`, `weight`
- `family_housing`: `city`, `neighborhood`, `residence_type`
- `person_health_conditions`: `condition_name` — `person_training_interests`: `course_name`
- `fill_requests`: أسماء أعمدة الحالة والعدادات

أي فرق يُحل بتعديل اسم العمود في الموديل/الخدمة المعنية فقط — البنية والمنطق لا يتغيران.
