-- ============================================================ -- التأمين للسائق — بند 3.5 من دراسة الفرص -- -- ‏ما كان موجوداً قبل هذا: جدول `driver_health_assurance` فيه بوليان -- ‏`assured` واسم شركة نصّاً، وملف add.php وحيد بلا حارس مصادقة — -- ‏و get.php و update.php **فارغان تماماً** (صفر بايت). أي أن الفكرة -- ‏لم "تبدأ وتتوقف" كما تقول الدراسة، بل لم تبدأ. -- -- ‏قرار المالك: شريك مرخّص (ميتلايف وآخر في مصر)، قسط يومي أو شهري -- ‏يُخصم من أرباح السائق، وأهلية مشروطة بعدد رحلات وتقييم — التأمين -- ‏مكافأة استمرار لا خدمة تُمنح لكل من سجّل. -- -- ‏لا نلمس driver_health_assurance: يبقى كما هو لمن سُجّل فيه سابقاً، -- ‏والجداول هنا هي المسار الجديد. -- ============================================================ -- ── ١) شركات التأمين ──────────────────────────────────────── CREATE TABLE IF NOT EXISTS `insurance_providers` ( `id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR(120) NOT NULL COMMENT 'MetLife وغيرها', `name_ar` VARCHAR(120) DEFAULT NULL, `country` VARCHAR(60) NOT NULL DEFAULT 'Egypt', `contact` VARCHAR(255) DEFAULT NULL, `is_active` TINYINT(1) NOT NULL DEFAULT 1, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_country_active` (`country`, `is_active`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ── ٢) الخطط ──────────────────────────────────────────────── -- ‏شروط الأهلية تُخزَّن مع الخطة لا في الكود: الشريك سيغيّرها، والسوق -- ‏المصري يختلف عن الأردني، وتغيير رقم في صف أرخص من نشر إصدار. CREATE TABLE IF NOT EXISTS `insurance_plans` ( `id` INT NOT NULL AUTO_INCREMENT, `provider_id` INT NOT NULL, `code` VARCHAR(60) NOT NULL COMMENT 'معرّف ثابت يُستعمل في الكود', `name_ar` VARCHAR(160) NOT NULL, `description_ar` TEXT DEFAULT NULL, `billing_cycle` ENUM('daily','monthly') NOT NULL DEFAULT 'daily', `premium` DECIMAL(10,3) NOT NULL COMMENT 'قسط الدورة الواحدة', `currency` VARCHAR(10) NOT NULL DEFAULT 'EGP', `coverage_summary` TEXT DEFAULT NULL, -- ── شروط الأهلية ── -- ‏قرار المالك: لا تأمين قبل ~٢٠٠ رحلة بتقييم جيد. المنطق سليم — -- ‏التأمين كلفة متكررة على الشركة، ومنحه لمن قد يختفي بعد أسبوع -- ‏يحوّله من أداة ولاء إلى نزيف. `min_completed_rides` INT NOT NULL DEFAULT 200, `min_rating` DECIMAL(3,2) NOT NULL DEFAULT 4.50, `min_account_days` INT NOT NULL DEFAULT 30, `is_active` TINYINT(1) NOT NULL DEFAULT 1, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uq_code` (`code`), KEY `idx_provider_active` (`provider_id`, `is_active`), CONSTRAINT `fk_plan_provider` FOREIGN KEY (`provider_id`) REFERENCES `insurance_providers` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ── ٣) وثائق السائقين ─────────────────────────────────────── CREATE TABLE IF NOT EXISTS `driver_insurance_policies` ( `id` INT NOT NULL AUTO_INCREMENT, `driver_id` VARCHAR(100) NOT NULL, `plan_id` INT NOT NULL, `policy_number` VARCHAR(120) DEFAULT NULL COMMENT 'رقم الوثيقة من الشريك', `status` ENUM('active','suspended','cancelled','expired') NOT NULL DEFAULT 'active', `started_at` DATE NOT NULL, `ended_at` DATE DEFAULT NULL, -- ‏لقطة الأهلية لحظة الاشتراك. بدونها لا يمكن الإجابة لاحقاً على -- ‏«لماذا قُبل هذا السائق؟» حين تتغيّر شروط الخطة أو ينخفض تقييمه. `rides_at_signup` INT NOT NULL DEFAULT 0, `rating_at_signup` DECIMAL(3,2) NOT NULL DEFAULT 0, -- ‏آخر يوم حُسب عنه قسط. هذا العمود — لا التاريخ الحالي — هو ما -- ‏يمنع الاحتساب المزدوج لو عمل الكرون مرتين في اليوم نفسه. `last_charged_on` DATE DEFAULT NULL, `cancel_reason` VARCHAR(255) DEFAULT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), -- ‏وثيقة واحدة نشطة لكل سائق. المفتاح الفريد على (driver_id, status) -- ‏لا يصلح — يمنع وثيقتين ملغيتين. نمنعه في الكود ونكشفه بفهرس. KEY `idx_driver_status` (`driver_id`, `status`), KEY `idx_charge_sweep` (`status`, `last_charged_on`), CONSTRAINT `fk_policy_plan` FOREIGN KEY (`plan_id`) REFERENCES `insurance_plans` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ── ٤) دفتر الأقساط ───────────────────────────────────────── -- ‏دفتر لا خصم مباشر. رصيد السائق يعيش في payment_server لا هنا، -- ‏فالكرون يقيّد استحقاقاً والتسوية تقرأه. الفصل مقصود أيضاً لسبب -- ‏محاسبي: القيد سجلّ دائم قابل للمراجعة، بينما الخصم المباشر يترك -- ‏رقماً متغيّراً بلا أثر يشرح كيف صار كذلك. CREATE TABLE IF NOT EXISTS `insurance_premium_ledger` ( `id` INT NOT NULL AUTO_INCREMENT, `policy_id` INT NOT NULL, `driver_id` VARCHAR(100) NOT NULL, `charge_date` DATE NOT NULL COMMENT 'اليوم أو أول الشهر المحتسَب', `amount` DECIMAL(10,3) NOT NULL, `currency` VARCHAR(10) NOT NULL DEFAULT 'EGP', `status` ENUM('pending','settled','waived','failed') NOT NULL DEFAULT 'pending', `settled_at` DATETIME DEFAULT NULL, `note` VARCHAR(255) DEFAULT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), -- ‏الحارس الحقيقي ضد الاحتساب المزدوج: قسط واحد لكل وثيقة في اليوم -- ‏الواحد مهما تكرّر تشغيل الكرون أو تزامنت نسختان منه. UNIQUE KEY `uq_policy_date` (`policy_id`, `charge_date`), KEY `idx_driver_status` (`driver_id`, `status`), CONSTRAINT `fk_ledger_policy` FOREIGN KEY (`policy_id`) REFERENCES `driver_insurance_policies` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;