Update: 2026-08-08 12:58:16
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/**
|
||||
* fuel/functions.php — منطق محفظة الوقود المشترك
|
||||
* ─────────────────────────────────────────────────────────────
|
||||
* محفظة الوقود منتج `drawdown` في محرك الالتزامات: لا دورة فوترة له،
|
||||
* والاستحقاق يُقيَّد لحظة الصرف من المحطة لا على جدول زمني.
|
||||
*
|
||||
* كل ما يخصّ المال هنا يقتصر على **قيد الدَّين**. الخصم من رصيد السائق
|
||||
* يبقى حكراً على محرك التسوية، كما في التأمين.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../obligations/functions.php';
|
||||
|
||||
const FUEL_PRODUCT_CODE = 'fuel_wallet';
|
||||
const FUEL_VOUCHER_TTL_MIN = 45;
|
||||
|
||||
/**
|
||||
* التزام الوقود النشط للسائق، أو null.
|
||||
*/
|
||||
function fuelObligation(PDO $con, string $driverId): ?array
|
||||
{
|
||||
$st = $con->prepare("
|
||||
SELECT o.*, p.currency, p.daily_cap_percent
|
||||
FROM driver_obligations o
|
||||
JOIN obligation_products p ON p.id = o.product_id
|
||||
WHERE o.driver_id = ? AND p.code = ? AND o.status = 'active'
|
||||
LIMIT 1
|
||||
");
|
||||
$st->execute([$driverId, FUEL_PRODUCT_CODE]);
|
||||
|
||||
return $st->fetch(PDO::FETCH_ASSOC) ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* يُنهي صلاحية القسائم التي مضى وقتها.
|
||||
*
|
||||
* يُستدعى قبل أي حساب سقف لا في كرون: القسيمة المنتهية تحجز من سقف
|
||||
* السائق، وتأخير تحريرها ساعةً كاملةً حتى يمرّ الكرون يعني منعه من
|
||||
* التزوّد بسبب قسيمة ماتت. التنظيف عند القراءة يجعل الرقم صادقاً دائماً.
|
||||
*/
|
||||
function fuelExpireStaleVouchers(PDO $con, string $driverId): void
|
||||
{
|
||||
$con->prepare("
|
||||
UPDATE fuel_vouchers
|
||||
SET status = 'expired'
|
||||
WHERE driver_id = ? AND status = 'issued' AND expires_at < NOW()
|
||||
")->execute([$driverId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* حالة سقف السائق: المستخدَم، المحجوز، والمتاح.
|
||||
*
|
||||
* المحجوز يُطرح كالمستخدَم تماماً: قسيمة أُصدرت ولم تُصرف بعد قد تُصرف
|
||||
* بعد لحظة، والسماح بإصدار قسائم بمجموع يتجاوز السقف يعني سائقاً
|
||||
* يتزوّد بضعف ما يستحق ثم لا نجد ما نخصم منه.
|
||||
*
|
||||
* @return array{limit: float, outstanding: float, held: float, available: float}
|
||||
*/
|
||||
function fuelCreditState(PDO $con, array $obligation): array
|
||||
{
|
||||
$obligationId = (int) $obligation['id'];
|
||||
|
||||
$st = $con->prepare("
|
||||
SELECT COALESCE(SUM(amount_remaining), 0)
|
||||
FROM obligation_ledger
|
||||
WHERE obligation_id = ? AND status IN ('pending', 'partial')
|
||||
");
|
||||
$st->execute([$obligationId]);
|
||||
$outstanding = (float) $st->fetchColumn();
|
||||
|
||||
$st = $con->prepare("
|
||||
SELECT COALESCE(SUM(amount_authorized), 0)
|
||||
FROM fuel_vouchers
|
||||
WHERE obligation_id = ? AND status = 'issued' AND expires_at >= NOW()
|
||||
");
|
||||
$st->execute([$obligationId]);
|
||||
$held = (float) $st->fetchColumn();
|
||||
|
||||
$limit = (float) $obligation['credit_limit'];
|
||||
$available = round($limit - $outstanding - $held, 3);
|
||||
|
||||
return [
|
||||
'limit' => $limit,
|
||||
'outstanding' => round($outstanding, 3),
|
||||
'held' => round($held, 3),
|
||||
'available' => max($available, 0.0),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* يولّد رمز قسيمة غير متوقَّع.
|
||||
*
|
||||
* `random_int` لا `rand`: الرمز مفتاحُ صرفٍ فعلي، ومولّد عشوائية قابل
|
||||
* للتنبّؤ يعني أن من يعرف رمزاً واحداً قد يخمّن التالي ويتزوّد على
|
||||
* حساب سائق آخر. عشرة أرقام تُبقي المجال واسعاً والرقم قابلاً للإملاء
|
||||
* على عامل المحطة هاتفياً.
|
||||
*/
|
||||
function fuelGenerateCode(PDO $con): string
|
||||
{
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$code = (string) random_int(1000000000, 9999999999);
|
||||
|
||||
$st = $con->prepare("SELECT 1 FROM fuel_vouchers WHERE code = ? LIMIT 1");
|
||||
$st->execute([$code]);
|
||||
if (!$st->fetchColumn()) {
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
|
||||
// عشر محاولات فاشلة على مجال من تسعة مليارات ليست صدفة — هي عطل
|
||||
// (قاعدة ممتلئة أو مولّد معطوب). الاستمرار هنا يعني رمزاً مكرّراً.
|
||||
throw new RuntimeException('تعذّر توليد رمز قسيمة فريد');
|
||||
}
|
||||
Reference in New Issue
Block a user