فلسفة العمولة الصحيحة (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>
43 lines
1.5 KiB
TypeScript
43 lines
1.5 KiB
TypeScript
import { Controller, ForbiddenException, Get, UseGuards } from '@nestjs/common';
|
|
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
|
import { DriverCreditService, CREDIT_FLOOR } from './driver-credit.service';
|
|
import { DriversService } from '../drivers/drivers.service';
|
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
|
import { CurrentUser, AuthUser } from '../auth/decorators/current-user.decorator';
|
|
|
|
@ApiTags('credit')
|
|
@ApiBearerAuth()
|
|
@UseGuards(JwtAuthGuard)
|
|
@Controller('credit')
|
|
export class CreditController {
|
|
constructor(
|
|
private readonly credit: DriverCreditService,
|
|
private readonly drivers: DriversService,
|
|
) {}
|
|
|
|
/** رصيدي التشغيلي — يعرضه تطبيق السائق (docs/18 — J8). */
|
|
@Get()
|
|
async mine(@CurrentUser() user: AuthUser) {
|
|
const driver = await this.drivers.findByUser(user.tenantId, user.userId);
|
|
if (!driver) throw new ForbiddenException('Not a driver');
|
|
|
|
const c = await this.credit.get(user.tenantId, driver.id);
|
|
const floor = CREDIT_FLOOR[c.currency] ?? 0;
|
|
const balance = Number(c.balance);
|
|
return {
|
|
balance,
|
|
currency: c.currency,
|
|
floor,
|
|
inDebt: balance < 0,
|
|
blocked: balance < floor,
|
|
};
|
|
}
|
|
|
|
@Get('transactions')
|
|
async history(@CurrentUser() user: AuthUser) {
|
|
const driver = await this.drivers.findByUser(user.tenantId, user.userId);
|
|
if (!driver) throw new ForbiddenException('Not a driver');
|
|
return this.credit.history(user.tenantId, driver.id);
|
|
}
|
|
}
|