feat: المجموعة J — الرصيد التشغيلي + تصحيح نموذج العمولة (B3/B6)
فلسفة العمولة الصحيحة (docs/18): السائق يقبض أجرة الراكب **كاملة**،
والعمولة تُخصم من رصيد تشغيلي مدفوع سلفاً. ما بُني في f3fdc1b كان نموذج
أوبر (اقتطاع من الأرباح) — عكس المطلوب.
- J1: driver_credits + credit_txns. خصم ذرّي كنمط I1 لكن **بلا شرط رصيد**
ولا قيد >= 0: الدين مسموح عمداً (قرار المالك: الرحلة لا تُقطع أبداً)
- J2: خصم العمولة عند completed لا paid — الرحلة تمّت فالعمولة استُحقّت.
قاعدة واحدة للكاش والمحفظة (§5.5): لا تفريع حسب وسيلة الدفع
- J3: price_for_driver = price_for_passenger. TariffEngine.split →
commission، وبلا سقف بالأجرة لأن الخصم على رصيد منفصل يجوز أن يسلب
- J4: الحجب عند تجاوز credit_floor فقط، عند setOnline و accept — لا أثناء
رحلة. الحجب يمنع الاتصال أصلاً وإلا عُرضت رحلات يُرفض قبولها
- J6: مكافأة التسجيل عند الاعتماد (3 JOD / 300 SYP جديدة / 300 EGP).
حارسان: فحص تطبيقي للحالة الشائعة + فهرس فريد جزئي للسباق
- J7: credit_floor لكل عملة
- J8: GET /credit + /credit/transactions
تصحيح B3: مشوار الوصول تعويضُ إلغاء بعد انقضاء الانتظار المجاني — لا بند
في كل أجرة. أُزيل مفتاح pickup_leg.charge.
هجرة: DriverCredit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
2edd8f6916
commit
d9b0faab2d
@@ -0,0 +1,59 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
/**
|
||||
* الرصيد التشغيلي للسائق (docs/18 — المجموعة J).
|
||||
* عمولة مدفوعة سلفاً، منفصلة تماماً عن محفظة أرباحه.
|
||||
*
|
||||
* **بلا قيد `balance >= 0`** — بخلاف `tripz_wallets`: الدين مسموح عمداً
|
||||
* (قرار المالك: الرحلة لا تُقطع أبداً ولو صار الرصيد سالباً).
|
||||
*/
|
||||
export class DriverCredit1721850000000 implements MigrationInterface {
|
||||
public async up(q: QueryRunner): Promise<void> {
|
||||
await q.query(`
|
||||
CREATE TABLE IF NOT EXISTS tripz_driver_credits (
|
||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
tenant_id uuid NOT NULL,
|
||||
driver_id uuid NOT NULL,
|
||||
balance numeric(12,3) NOT NULL DEFAULT 0,
|
||||
currency varchar NOT NULL DEFAULT 'JOD',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT now()
|
||||
)
|
||||
`);
|
||||
await q.query(`
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "IDX_tripz_driver_credits_tenant_driver"
|
||||
ON tripz_driver_credits (tenant_id, driver_id)
|
||||
`);
|
||||
|
||||
await q.query(`
|
||||
CREATE TABLE IF NOT EXISTS tripz_credit_txns (
|
||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
tenant_id uuid NOT NULL,
|
||||
driver_id uuid NOT NULL,
|
||||
amount numeric(12,3) NOT NULL,
|
||||
type varchar NOT NULL,
|
||||
balance_after numeric(12,3),
|
||||
trip_id uuid,
|
||||
ref varchar,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT now()
|
||||
)
|
||||
`);
|
||||
await q.query(`
|
||||
CREATE INDEX IF NOT EXISTS "IDX_tripz_credit_txns_driver_time"
|
||||
ON tripz_credit_txns (tenant_id, driver_id, created_at)
|
||||
`);
|
||||
|
||||
// مكافأة التسجيل مرة واحدة لكل سائق — تُفرض على القاعدة لا بفحص تطبيقي،
|
||||
// فتسجيلان متزامنان لا يمنحانها مرتين (docs/18 — J6).
|
||||
await q.query(`
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "IDX_tripz_credit_txns_signup_once"
|
||||
ON tripz_credit_txns (tenant_id, driver_id)
|
||||
WHERE type = 'signup_bonus'
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(q: QueryRunner): Promise<void> {
|
||||
await q.query(`DROP TABLE IF EXISTS tripz_credit_txns`);
|
||||
await q.query(`DROP TABLE IF EXISTS tripz_driver_credits`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user