Update: 2026-08-08 12:58:16
This commit is contained in:
@@ -7,11 +7,19 @@
|
||||
* رصيد السائق يعيش في payment_server لا في هذه القاعدة، والخصم
|
||||
* المباشر من هنا كان سيعني كتابةً عابرة للحدود بلا معاملة موحّدة —
|
||||
* أي خطر رصيد ناقص بلا قيد يقابله، أو قيد بلا خصم. فنقيّد استحقاقاً
|
||||
* هنا، وتقرأه التسوية.
|
||||
* هنا، ويقرأه محرك التسوية `cron_obligation_settlement.php`.
|
||||
*
|
||||
* تحديث (٢٠٢٦-٠٨-٠٨): صار هذا الملف **مُصدِر استحقاق** في محرك
|
||||
* الالتزامات العام، فيكتب في `obligation_ledger` لا في
|
||||
* `insurance_premium_ledger`. سبب النقل أن الدفتر القديم لم يكن يقرأه
|
||||
* أحد إطلاقاً — «تقرأه التسوية» كان وعداً بلا منفّذ، وكل قسط قُيّد منذ
|
||||
* الإطلاق بقي معلّقاً بلا تحصيل. جداول التأمين تبقى كما هي شاهداً
|
||||
* تاريخياً، وقد رُحّلت بياناتها في
|
||||
* `migrations/2026_08_08_obligations_engine.sql`.
|
||||
*
|
||||
* الحماية من الاحتساب المزدوج طبقتان:
|
||||
* ١. `last_charged_on` في الوثيقة — نفلتر به قبل العمل
|
||||
* ٢. مفتاح فريد (policy_id, charge_date) في الدفتر — يحسم السباق
|
||||
* ١. `last_charged_on` في الالتزام — نفلتر به قبل العمل
|
||||
* ٢. مفتاح فريد (obligation_id, charge_date) في الدفتر — يحسم السباق
|
||||
* لو عملت نسختان من الكرون معاً
|
||||
*
|
||||
* الأولى وحدها لا تكفي: بين القراءة والكتابة نافذة، والمفتاح الفريد
|
||||
@@ -39,19 +47,24 @@ $charged = 0;
|
||||
$skipped = 0;
|
||||
|
||||
try {
|
||||
// المصدر الآن الالتزامات لا الوثائق. القيد على `kind = 'recurring'`
|
||||
// لا على المنتج التأميني تحديداً: أي منتج دوري يُضاف لاحقاً يُقيَّد
|
||||
// بالمنطق نفسه بلا سطر جديد هنا.
|
||||
$st = $con->query("
|
||||
SELECT p.id, p.driver_id, p.last_charged_on,
|
||||
pl.premium, pl.currency, pl.billing_cycle
|
||||
FROM driver_insurance_policies p
|
||||
JOIN insurance_plans pl ON pl.id = p.plan_id
|
||||
WHERE p.status = 'active'
|
||||
ORDER BY p.id ASC
|
||||
SELECT o.id, o.driver_id, o.last_charged_on, o.cycle_amount,
|
||||
pr.code AS product_code, pr.currency, pr.billing_cycle
|
||||
FROM driver_obligations o
|
||||
JOIN obligation_products pr ON pr.id = o.product_id
|
||||
WHERE o.status = 'active'
|
||||
AND pr.kind = 'recurring'
|
||||
AND pr.billing_cycle <> 'none'
|
||||
ORDER BY o.id ASC
|
||||
");
|
||||
|
||||
foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $policy) {
|
||||
$chargeDate = $policy['billing_cycle'] === 'monthly' ? $monthStart : $today;
|
||||
|
||||
// الفلترة الرخيصة أولاً — تجنّبنا محاولة إدراج لكل وثيقة كل يوم.
|
||||
// الفلترة الرخيصة أولاً — تجنّبنا محاولة إدراج لكل التزام كل يوم.
|
||||
if ($policy['last_charged_on'] === $chargeDate) {
|
||||
$skipped++;
|
||||
continue;
|
||||
@@ -61,16 +74,17 @@ try {
|
||||
$con->beginTransaction();
|
||||
|
||||
$con->prepare("
|
||||
INSERT INTO insurance_premium_ledger
|
||||
(policy_id, driver_id, charge_date, amount, currency, status)
|
||||
VALUES (?, ?, ?, ?, ?, 'pending')
|
||||
INSERT INTO obligation_ledger
|
||||
(obligation_id, driver_id, product_code, charge_date,
|
||||
amount, amount_collected, amount_remaining, currency, status)
|
||||
VALUES (?, ?, ?, ?, ?, 0, ?, ?, 'pending')
|
||||
")->execute([
|
||||
$policy['id'], $policy['driver_id'], $chargeDate,
|
||||
$policy['premium'], $policy['currency'],
|
||||
$policy['id'], $policy['driver_id'], $policy['product_code'], $chargeDate,
|
||||
$policy['cycle_amount'], $policy['cycle_amount'], $policy['currency'],
|
||||
]);
|
||||
|
||||
$con->prepare("
|
||||
UPDATE driver_insurance_policies SET last_charged_on = ? WHERE id = ?
|
||||
UPDATE driver_obligations SET last_charged_on = ? WHERE id = ?
|
||||
")->execute([$chargeDate, $policy['id']]);
|
||||
|
||||
$con->commit();
|
||||
@@ -82,11 +96,11 @@ try {
|
||||
// هذه ليست حالة خطأ — هي بالضبط ما صُمّم المفتاح لمنعه.
|
||||
if ($e->getCode() === '23000') {
|
||||
$con->prepare("
|
||||
UPDATE driver_insurance_policies SET last_charged_on = ? WHERE id = ?
|
||||
UPDATE driver_obligations SET last_charged_on = ? WHERE id = ?
|
||||
")->execute([$chargeDate, $policy['id']]);
|
||||
$skipped++;
|
||||
} else {
|
||||
error_log("[insurance] تعذّر قيد قسط الوثيقة #{$policy['id']}: " . $e->getMessage());
|
||||
error_log("[insurance] تعذّر قيد استحقاق الالتزام #{$policy['id']}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user