227 lines
7.7 KiB
Dart
227 lines
7.7 KiB
Dart
// earnings_models.dart — نماذج شاشة الأرباح
|
|
//
|
|
// كل الأرقام تصل من نقطة واحدة (`ride/earnings/summary.php`) بختم زمني
|
|
// واحد، فلا مجال لأن يتناقض رقمان في الشاشة نفسها.
|
|
|
|
class PeriodEarnings {
|
|
final int trips;
|
|
|
|
/// صافي السائق — حصته المثبَّتة لحظة عرض السعر، أي ما وُعد به قبل القبول.
|
|
final double net;
|
|
|
|
/// إجمالي ما دفعه الركاب.
|
|
final double gross;
|
|
|
|
/// عمولة المنصة. تُعرض صراحةً لا تُطرح بصمت: السائق الذي يرى تركيبة
|
|
/// المبلغ يفهم، والذي يرى الصافي وحده يخمّن ويشكّ.
|
|
final double commission;
|
|
|
|
final double distance;
|
|
final int minutes;
|
|
|
|
PeriodEarnings({
|
|
required this.trips,
|
|
required this.net,
|
|
required this.gross,
|
|
required this.commission,
|
|
required this.distance,
|
|
required this.minutes,
|
|
});
|
|
|
|
double get hours => minutes / 60.0;
|
|
|
|
factory PeriodEarnings.fromJson(Map<String, dynamic> j) => PeriodEarnings(
|
|
trips: int.tryParse(j['trips']?.toString() ?? '0') ?? 0,
|
|
net: double.tryParse(j['net']?.toString() ?? '0') ?? 0,
|
|
gross: double.tryParse(j['gross']?.toString() ?? '0') ?? 0,
|
|
commission: double.tryParse(j['commission']?.toString() ?? '0') ?? 0,
|
|
distance: double.tryParse(j['distance']?.toString() ?? '0') ?? 0,
|
|
minutes: int.tryParse(j['minutes']?.toString() ?? '0') ?? 0,
|
|
);
|
|
|
|
static PeriodEarnings empty() => PeriodEarnings(
|
|
trips: 0, net: 0, gross: 0, commission: 0, distance: 0, minutes: 0);
|
|
}
|
|
|
|
class DayEarnings {
|
|
final String day;
|
|
final int trips;
|
|
final double net;
|
|
final int minutes;
|
|
|
|
DayEarnings(
|
|
{required this.day,
|
|
required this.trips,
|
|
required this.net,
|
|
required this.minutes});
|
|
|
|
factory DayEarnings.fromJson(Map<String, dynamic> j) => DayEarnings(
|
|
day: j['day']?.toString() ?? '',
|
|
trips: int.tryParse(j['trips']?.toString() ?? '0') ?? 0,
|
|
net: double.tryParse(j['net']?.toString() ?? '0') ?? 0,
|
|
minutes: int.tryParse(j['minutes']?.toString() ?? '0') ?? 0,
|
|
);
|
|
}
|
|
|
|
class DeductionItem {
|
|
final String product;
|
|
final String name;
|
|
final double outstanding;
|
|
|
|
DeductionItem(
|
|
{required this.product, required this.name, required this.outstanding});
|
|
|
|
factory DeductionItem.fromJson(Map<String, dynamic> j) => DeductionItem(
|
|
product: j['product']?.toString() ?? '',
|
|
name: j['name']?.toString() ?? '',
|
|
outstanding: double.tryParse(j['outstanding']?.toString() ?? '0') ?? 0,
|
|
);
|
|
}
|
|
|
|
/// الاقتطاعات (تأمين، وقود) تُعرض بجانب الأرباح التي اقتُطعت منها.
|
|
/// اقتطاعٌ لا يظهر في مكانه يُقرأ سرقةً مهما كان مشروعاً.
|
|
class Deductions {
|
|
final double today;
|
|
final double week;
|
|
final double month;
|
|
final double outstanding;
|
|
final List<DeductionItem> items;
|
|
|
|
Deductions({
|
|
required this.today,
|
|
required this.week,
|
|
required this.month,
|
|
required this.outstanding,
|
|
required this.items,
|
|
});
|
|
|
|
bool get hasAny => today > 0 || outstanding > 0;
|
|
|
|
factory Deductions.fromJson(Map<String, dynamic> j) => Deductions(
|
|
today: double.tryParse(j['today']?.toString() ?? '0') ?? 0,
|
|
week: double.tryParse(j['week']?.toString() ?? '0') ?? 0,
|
|
month: double.tryParse(j['month']?.toString() ?? '0') ?? 0,
|
|
outstanding: double.tryParse(j['outstanding']?.toString() ?? '0') ?? 0,
|
|
items: (j['items'] is List)
|
|
? (j['items'] as List)
|
|
.map((i) => DeductionItem.fromJson(Map<String, dynamic>.from(i)))
|
|
.toList()
|
|
: <DeductionItem>[],
|
|
);
|
|
}
|
|
|
|
class EarningsSummary {
|
|
final PeriodEarnings today;
|
|
final PeriodEarnings week;
|
|
final PeriodEarnings month;
|
|
final PeriodEarnings prevWeek;
|
|
|
|
/// نسبة التغيّر عن الأسبوع الماضي، أو null إن لم يكن هناك ما يُقارَن به.
|
|
/// المقارنة بالذات لا بالسائقين الآخرين: ترتيبٌ يُحبط أكثر من يحتاج
|
|
/// التحفيز — فهو من سيقع في أسفله.
|
|
final double? trendPercent;
|
|
|
|
final List<DayEarnings> daily;
|
|
final DayEarnings? bestDay;
|
|
final Deductions deductions;
|
|
final String asOf;
|
|
|
|
EarningsSummary({
|
|
required this.today,
|
|
required this.week,
|
|
required this.month,
|
|
required this.prevWeek,
|
|
required this.daily,
|
|
required this.deductions,
|
|
required this.asOf,
|
|
this.trendPercent,
|
|
this.bestDay,
|
|
});
|
|
|
|
factory EarningsSummary.fromJson(Map<String, dynamic> j) => EarningsSummary(
|
|
today: PeriodEarnings.fromJson(Map<String, dynamic>.from(j['today'] ?? {})),
|
|
week: PeriodEarnings.fromJson(Map<String, dynamic>.from(j['week'] ?? {})),
|
|
month: PeriodEarnings.fromJson(Map<String, dynamic>.from(j['month'] ?? {})),
|
|
prevWeek:
|
|
PeriodEarnings.fromJson(Map<String, dynamic>.from(j['prev_week'] ?? {})),
|
|
trendPercent: j['trend_percent'] == null
|
|
? null
|
|
: double.tryParse(j['trend_percent'].toString()),
|
|
daily: (j['daily'] is List)
|
|
? (j['daily'] as List)
|
|
.map((d) => DayEarnings.fromJson(Map<String, dynamic>.from(d)))
|
|
.toList()
|
|
: <DayEarnings>[],
|
|
bestDay: j['best_day'] is Map
|
|
? DayEarnings.fromJson(Map<String, dynamic>.from(j['best_day']))
|
|
: null,
|
|
deductions:
|
|
Deductions.fromJson(Map<String, dynamic>.from(j['deductions'] ?? {})),
|
|
asOf: j['as_of']?.toString() ?? '',
|
|
);
|
|
}
|
|
|
|
class TripBreakdown {
|
|
final double gross;
|
|
final double commission;
|
|
final double bonus;
|
|
final double net;
|
|
|
|
/// إعفاء العمولة (سلسلة ٠٪) — هدية تدفع المنصة ثمنها ولم تكن تظهر
|
|
/// للسائق في أي شاشة. نميّزها لتُعرض مكسباً لا سطراً فارغاً.
|
|
final bool zeroCommission;
|
|
|
|
TripBreakdown({
|
|
required this.gross,
|
|
required this.commission,
|
|
required this.bonus,
|
|
required this.net,
|
|
required this.zeroCommission,
|
|
});
|
|
|
|
factory TripBreakdown.fromJson(Map<String, dynamic> j) => TripBreakdown(
|
|
gross: double.tryParse(j['gross']?.toString() ?? '0') ?? 0,
|
|
commission: double.tryParse(j['commission']?.toString() ?? '0') ?? 0,
|
|
bonus: double.tryParse(j['bonus']?.toString() ?? '0') ?? 0,
|
|
net: double.tryParse(j['net']?.toString() ?? '0') ?? 0,
|
|
zeroCommission: j['zero_commission'] == true,
|
|
);
|
|
}
|
|
|
|
class TripEarning {
|
|
final int rideId;
|
|
final String at;
|
|
final String from;
|
|
final String to;
|
|
final String carType;
|
|
final String paymentMethod;
|
|
final double distanceKm;
|
|
final int minutes;
|
|
final TripBreakdown breakdown;
|
|
|
|
TripEarning({
|
|
required this.rideId,
|
|
required this.at,
|
|
required this.from,
|
|
required this.to,
|
|
required this.carType,
|
|
required this.paymentMethod,
|
|
required this.distanceKm,
|
|
required this.minutes,
|
|
required this.breakdown,
|
|
});
|
|
|
|
factory TripEarning.fromJson(Map<String, dynamic> j) => TripEarning(
|
|
rideId: int.tryParse(j['ride_id']?.toString() ?? '0') ?? 0,
|
|
at: j['at']?.toString() ?? '',
|
|
from: j['from']?.toString() ?? '',
|
|
to: j['to']?.toString() ?? '',
|
|
carType: j['car_type']?.toString() ?? '',
|
|
paymentMethod: j['payment_method']?.toString() ?? '',
|
|
distanceKm: double.tryParse(j['distance_km']?.toString() ?? '0') ?? 0,
|
|
minutes: int.tryParse(j['minutes']?.toString() ?? '0') ?? 0,
|
|
breakdown:
|
|
TripBreakdown.fromJson(Map<String, dynamic>.from(j['breakdown'] ?? {})),
|
|
);
|
|
}
|