# M-INV — مخطط التنصيب

تجربة على مجلد داخلي ضمن نطاق قائم، ثم الإنتاج على خادم مستقل.

> **حدّ هذه البيئة**: المجلد الداخلي للتجربة وبيئة **المحاكاة** فقط.
> لا تضع فيه شهادة إنتاج ولا مفاتيح عملاء حقيقيين — الاستضافة المشتركة
> لا تعطيك تحكماً كافياً في البيئة، وعهدة مفاتيح الغير تستلزم خادماً مستقلاً.

---

## 1. فحص البيئة قبل أي رفع

| المتطلب | الحد الأدنى | الفحص |
| --- | --- | --- |
| PHP | 8.2 | `php -v` |
| bcmath | مطلوب | `php -m \| grep bcmath` |
| openssl | مطلوب | `php -r "var_dump(in_array('secp256k1', openssl_get_curve_names()));"` |
| mbstring · dom · curl · fileinfo · pdo_mysql · zip | مطلوبة | `php -m` |
| MySQL / MariaDB | 8.0 / 10.6 | من لوحة التحكم |
| Composer | 2.x | `composer -V` |

**إن رجع فحص المنحنى `false` فتوقف هنا.** بلا `secp256k1` لا توقيع ولا شهادة،
وبقية الخطوات بلا معنى. راجع مزوّد الاستضافة أو فعّل نسخة PHP أخرى.

في cPanel يكون المفسّر غالباً `/usr/local/bin/ea-php82` لا `php` المجرّد،
و Composer عند `/opt/cpanel/composer/bin/composer`. ثبّت المسار الصحيح الآن
لأنك ستكرّره في كل أمر ومهمة مجدولة.

---

## 2. البنية على الخادم — أهم قرار في التنصيب

**التطبيق خارج المجلد المنشور، والمنشور هو `public` وحده.**

```
/home/USER/
├── minv-app/                 ← التطبيق كاملاً (خارج النشر)
│   ├── app/  bootstrap/  config/  database/  routes/
│   ├── storage/  vendor/  .env
│   └── public/               ← لا يُخدَم من هنا
└── public_html/
    └── minv/                 ← المنشور: محتوى public فقط
        ├── index.php         (مُعدَّل المسارات)
        ├── .htaccess
        └── storage → رابط رمزي
```

السبب مباشر: `.env` يحمل مفتاح التشفير الرئيسي وبيانات قاعدة البيانات.
وضع التطبيق كاملاً داخل `public_html` يجعل هذه الملفات قابلة للطلب من
المتصفح عند أول خطأ في إعداد الخادم. **لا تختصر هذه الخطوة.**

---

## 3. التركيب

```bash
cd /home/USER
composer create-project laravel/laravel minv-app
cd minv-app
composer require filament/filament:"^3.2"
composer require pestphp/pest --dev --with-all-dependencies
php artisan pest:install
```

ثم فك حزمة M-INV فوق جذر `minv-app`، وسجّل المزوّدين في
`bootstrap/providers.php`:

```php
return [
    App\Providers\AppServiceProvider::class,
    App\Providers\TenancyServiceProvider::class,
    App\Providers\Filament\AdminPanelProvider::class,
    App\Providers\Filament\AppPanelProvider::class,
];
```

**إن لم يتوفر Composer على الخادم**: نفّذ الخطوات على جهازك، وارفع المشروع
كاملاً مع مجلد `vendor` عبر FTP أو مدير الملفات.

---

## 4. نقل المجلد المنشور

```bash
mkdir -p /home/USER/public_html/minv
cp -r /home/USER/minv-app/public/. /home/USER/public_html/minv/
```

ثم **عدّل ثلاثة مسارات** في `public_html/minv/index.php`:

```php
// قبل
require __DIR__.'/../vendor/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {

// بعد
require __DIR__.'/../../minv-app/vendor/autoload.php';
$app = require_once __DIR__.'/../../minv-app/bootstrap/app.php';
if (file_exists($maintenance = __DIR__.'/../../minv-app/storage/framework/maintenance.php')) {
```

عدّل سطر الصيانة أولاً لأنه يظهر في أعلى الملف.

---

## 5. الإعداد

في `/home/USER/minv-app/.env`:

```ini
APP_NAME=M-INV
APP_ENV=production
APP_DEBUG=false
APP_URL=https://example.com/minv

# ضروري للمجلد الداخلي — بدونه تُطلب أصول Filament من جذر النطاق فتسقط
ASSET_URL=https://example.com/minv

APP_TIMEZONE=Asia/Riyadh
APP_LOCALE=ar

DB_CONNECTION=mysql
DB_DATABASE=...
DB_USERNAME=...
DB_PASSWORD=...

# مفتاح تشفير أسرار المنشآت — 48 محرفاً عشوائياً على الأقل
MINV_MASTER_KEY=

SESSION_DRIVER=database
SESSION_SECURE_COOKIE=true
QUEUE_CONNECTION=database

ZATCA_ENVIRONMENT=sandbox
```

ولّد المفتاحين:

```bash
php artisan key:generate
php -r "echo bin2hex(random_bytes(32)), PHP_EOL;"   # ضع الناتج في MINV_MASTER_KEY
```

**احتفظ بنسخة من `MINV_MASTER_KEY` في مكان آمن خارج الخادم.** فقدانه يعني
فقد كل شهادة ومفتاح مخزّن — لا استعادة ولا التفاف.

---

## 6. التهيئة

```bash
cd /home/USER/minv-app
php artisan migrate
php artisan db:seed --class=Database\\Seeders\\TenancySeeder
php artisan storage:link
php artisan config:cache
php artisan route:cache
php artisan filament:assets
```

`storage:link` سيُنشئ الرابط داخل `minv-app/public`. انقله يدوياً:

```bash
ln -s /home/USER/minv-app/storage/app/public /home/USER/public_html/minv/storage
```

### الصلاحيات

```bash
chmod -R 775 storage bootstrap/cache
```

---

## 7. إعادة الكتابة للمجلد الداخلي

في `public_html/minv/.htaccess` أضف داخل `<IfModule mod_rewrite.c>` بعد
`RewriteEngine On`:

```apache
RewriteBase /minv/
```

إغفالها يُنتج دورات إعادة توجيه أو `404` على كل مسار عدا الصفحة الأولى.

---

## 8. المهام المجدولة

في cPanel ← Cron Jobs:

```cron
* * * * * cd /home/USER/minv-app && /usr/local/bin/ea-php82 artisan schedule:run >/dev/null 2>&1
* * * * * cd /home/USER/minv-app && /usr/local/bin/ea-php82 artisan queue:work --stop-when-empty --max-time=55 >/dev/null 2>&1
```

السطر الثاني بديل عامل الطابور الدائم الذي لا تسمح به الاستضافة المشتركة:
يعمل دقيقة ثم ينتهي. **كافٍ للتجربة، غير كافٍ للإنتاج** — نافذة الإبلاغ
24 ساعة تحتمل التأخير، لكن اعتماد الفاتورة القياسية لا يحتمل انتظار دقيقة
كاملة أمام المستخدم.

---

## 9. أول دخول

| الرابط | لمن |
| --- | --- |
| `https://example.com/minv/admin` | لوحة التشغيل |
| `https://example.com/minv/app` | لوحة المنشأة |

غيّر كلمات مرور البذور فوراً — `change-this-now` قيمة تطوير.

---

## 10. فحص ما بعد التنصيب

```bash
php artisan test                    # 48 اختباراً
php artisan about                   # الإصدارات والاتصال
```

ومن المتصفح تحقق يدوياً:

- [ ] لوحة المنشأة تفتح وتُحمَّل أصولها (لا صفحة بلا تنسيق)
- [ ] `https://example.com/minv/../minv-app/.env` لا يُرجع شيئاً
- [ ] إنشاء عميل بعنوان ناقص **يُرفض** برسالة عربية
- [ ] إنشاء فاتورة ببند حر بلا صنف مسجّل ينجح
- [ ] الإجماليات في الشاشة تطابق الحساب اليدوي
- [ ] مستخدم منشأة لا يرى بيانات منشأة أخرى

البند الأخير هو معيار قبول المرحلة الأولى — اختبره يدوياً ولو مرة، لا
بالاختبارات وحدها.

---

## 11. الأخطاء المتوقعة في مجلد داخلي

| العَرَض | السبب | الحل |
| --- | --- | --- |
| صفحة بلا تنسيق | `ASSET_URL` غير مضبوط | أضفه وأعد `config:cache` |
| `404` على كل مسار داخلي | `RewriteBase` ناقص | أضفه في `.htaccess` |
| `500` بلا رسالة | صلاحيات `storage` | `chmod -R 775 storage` |
| خطأ في Livewire عند الحفظ | مسار التحديث لا يعرف المجلد | ثبّته في مزوّد الخدمة: `Livewire::setUpdateRoute(...)` |
| `Class not found` بعد الرفع | `vendor` ناقص أو مسارات `index.php` | راجع الخطوة 4 |
| فشل التشفير | `MINV_MASTER_KEY` فارغ | أضفه وأعد `config:cache` |

بعد أي تعديل على `.env`:

```bash
php artisan config:clear && php artisan config:cache
```

---

## 12. ما يختلف في الإنتاج

| البند | التجربة | الإنتاج |
| --- | --- | --- |
| المكان | مجلد داخلي | خادم مستقل داخل المملكة |
| الطابور | مهمة مجدولة كل دقيقة | عامل دائم |
| البيئة | محاكاة | إنتاج الهيئة |
| المفاتيح | تجريبية | شهادات عملاء فعليين |
| النسخ | يدوية | يومية مشفّرة باستعادة مُجرَّبة |
| النطاق | مسار فرعي | نطاق مستقل |

الانتقال ليس نسخ ملفات فقط: شهادة الإنتاج تُستخرج من جديد، ولا تُنقل من
بيئة المحاكاة.
