Update: 2026-08-03 11:32:12
This commit is contained in:
@@ -10,6 +10,11 @@ class FinancialV2Controller extends GetxController {
|
||||
Map<String, dynamic> stats = {};
|
||||
List<dynamic> settlements = [];
|
||||
|
||||
/// ملخّص محفظة سيرو القادم من خادم المدفوعات.
|
||||
/// المفاتيح: month_to_date / previous_month / all_time / growth_percent
|
||||
/// / by_payment_method — ولكل نافذة: credits و debits و net و tx_count.
|
||||
Map<String, dynamic> walletSummary = {};
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
@@ -23,12 +28,28 @@ class FinancialV2Controller extends GetxController {
|
||||
await Future.wait([
|
||||
fetchStats(),
|
||||
fetchSettlements(),
|
||||
fetchWalletSummary(),
|
||||
]);
|
||||
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
|
||||
Future<void> fetchWalletSummary() async {
|
||||
try {
|
||||
var res =
|
||||
await CRUD().getWallet(link: AppLink.siroWalletSummary, payload: {});
|
||||
if (res != 'failure' && res != null) {
|
||||
var d = res is String ? jsonDecode(res) : res;
|
||||
if (d['status'] == 'success' && d['data'] is Map) {
|
||||
walletSummary = Map<String, dynamic>.from(d['data']);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print('Error fetching Siro wallet summary: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchStats() async {
|
||||
try {
|
||||
var res =
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import 'dart:convert';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:siro_admin/constant/links.dart';
|
||||
import 'package:siro_admin/controller/functions/crud.dart';
|
||||
import 'package:siro_admin/views/widgets/snackbar.dart';
|
||||
import '../../print.dart';
|
||||
|
||||
/// إدارة المطاعم (وحدة الطعام).
|
||||
/// الباك إند كان جاهزاً بالكامل في backend/food/admin/ منذ البداية
|
||||
/// (merchants / merchant_approve / payouts) دون أي واجهة في اللوحة.
|
||||
class FoodMerchantsController extends GetxController {
|
||||
bool isLoading = true;
|
||||
List<dynamic> merchants = [];
|
||||
String filter = 'all';
|
||||
|
||||
/// المطعم قيد المعالجة — لتعطيل أزراره أثناء الطلب
|
||||
int? busyId;
|
||||
|
||||
/// الحالات كما تقبلها backend/food/admin/merchants.php حرفياً
|
||||
static const statuses = <String, String>{
|
||||
'all': 'الكل',
|
||||
'pending_approval': 'بانتظار الاعتماد',
|
||||
'active': 'نشط',
|
||||
'paused': 'موقوف مؤقتاً',
|
||||
'suspended': 'معلّق',
|
||||
'rejected': 'مرفوض',
|
||||
};
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
fetchMerchants();
|
||||
}
|
||||
|
||||
Future<void> changeFilter(String next) async {
|
||||
if (filter == next) return;
|
||||
filter = next;
|
||||
await fetchMerchants();
|
||||
}
|
||||
|
||||
Future<void> fetchMerchants() async {
|
||||
isLoading = true;
|
||||
update();
|
||||
|
||||
try {
|
||||
var res = await CRUD()
|
||||
.post(link: AppLink.foodMerchants, payload: {'status': filter});
|
||||
if (res != 'failure' && res != null) {
|
||||
var d = res is String ? jsonDecode(res) : res;
|
||||
if (d['status'] == 'success') {
|
||||
// jsonSuccess يغلّف الحمولة في message لا data
|
||||
final payload = d['message'] ?? d['data'];
|
||||
if (payload is Map && payload['merchants'] is List) {
|
||||
merchants = payload['merchants'];
|
||||
} else {
|
||||
merchants = [];
|
||||
}
|
||||
} else {
|
||||
mySnackbarError('${d['message'] ?? 'تعذّر جلب المطاعم'}');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print('Error fetching merchants: $e');
|
||||
mySnackbarError('$e');
|
||||
}
|
||||
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
|
||||
/// approve أو reject — الخادم يقبلهما فقط لمطعم بحالة pending_approval.
|
||||
Future<void> decide(int merchantId, String action) async {
|
||||
busyId = merchantId;
|
||||
update();
|
||||
|
||||
try {
|
||||
var res = await CRUD().post(
|
||||
link: AppLink.foodMerchantApprove,
|
||||
payload: {
|
||||
'merchant_id': merchantId.toString(),
|
||||
'action': action,
|
||||
},
|
||||
);
|
||||
if (res != 'failure' && res != null) {
|
||||
var d = res is String ? jsonDecode(res) : res;
|
||||
if (d['status'] == 'success') {
|
||||
mySnackbarSuccess(
|
||||
action == 'approve' ? 'تم اعتماد المطعم' : 'تم رفض المطعم');
|
||||
busyId = null;
|
||||
await fetchMerchants();
|
||||
return;
|
||||
}
|
||||
mySnackbarError('${d['message'] ?? 'تعذّر تنفيذ الإجراء'}');
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print('Error deciding merchant: $e');
|
||||
mySnackbarError('$e');
|
||||
}
|
||||
|
||||
busyId = null;
|
||||
update();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import 'dart:convert';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:siro_admin/constant/links.dart';
|
||||
import 'package:siro_admin/controller/functions/crud.dart';
|
||||
import 'package:siro_admin/views/widgets/snackbar.dart';
|
||||
import '../../print.dart';
|
||||
|
||||
/// طلبات سحب أرصدة السائقين (جدول driver_withdrawal_requests على خادم
|
||||
/// المدفوعات). لم تكن لها واجهة إطلاقاً قبل هذا: السائق يرسل الطلب فيُخزَّن
|
||||
/// وتُرسل رسالة واتساب لحظية، ثم لا يقرأ الجدول أحد.
|
||||
class WithdrawalController extends GetxController {
|
||||
bool isLoading = true;
|
||||
List<dynamic> requests = [];
|
||||
Map<String, dynamic> totals = {};
|
||||
String filter = 'pending';
|
||||
|
||||
/// المعرّف قيد المعالجة — لتعطيل أزرار الصف أثناء الطلب ومنع النقر المزدوج
|
||||
int? busyId;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
fetchRequests();
|
||||
}
|
||||
|
||||
Future<void> changeFilter(String next) async {
|
||||
if (filter == next) return;
|
||||
filter = next;
|
||||
await fetchRequests();
|
||||
}
|
||||
|
||||
Future<void> fetchRequests() async {
|
||||
isLoading = true;
|
||||
update();
|
||||
|
||||
try {
|
||||
var res = await CRUD().getWallet(
|
||||
link: AppLink.withdrawalRequests,
|
||||
payload: {'action': 'list', 'status': filter},
|
||||
);
|
||||
if (res != 'failure' && res != null) {
|
||||
var d = res is String ? jsonDecode(res) : res;
|
||||
if (d['status'] == 'success' && d['data'] is Map) {
|
||||
requests = d['data']['requests'] ?? [];
|
||||
totals = Map<String, dynamic>.from(d['data']['totals'] ?? {});
|
||||
} else {
|
||||
mySnackbarError('${d['message'] ?? 'تعذّر جلب طلبات السحب'}');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print('Error fetching withdrawal requests: $e');
|
||||
mySnackbarError('$e');
|
||||
}
|
||||
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
|
||||
/// تغيير الحالة يتطلّب super_admin على الخادم؛ الخادم يرفض أيضاً أي انتقال
|
||||
/// غير مسموح (مثل إعادة فتح طلب مرفوض) بـ 409، فنعرض رسالته كما هي.
|
||||
Future<void> updateStatus(int id, String status) async {
|
||||
busyId = id;
|
||||
update();
|
||||
|
||||
try {
|
||||
var res = await CRUD().getWallet(
|
||||
link: AppLink.withdrawalRequests,
|
||||
payload: {
|
||||
'action': 'update_status',
|
||||
'id': id.toString(),
|
||||
'status': status,
|
||||
},
|
||||
);
|
||||
if (res != 'failure' && res != null) {
|
||||
var d = res is String ? jsonDecode(res) : res;
|
||||
if (d['status'] == 'success') {
|
||||
mySnackbarSuccess('تم تحديث حالة الطلب');
|
||||
busyId = null;
|
||||
await fetchRequests();
|
||||
return;
|
||||
}
|
||||
mySnackbarError('${d['message'] ?? 'تعذّر تحديث الحالة'}');
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print('Error updating withdrawal status: $e');
|
||||
mySnackbarError('$e');
|
||||
}
|
||||
|
||||
busyId = null;
|
||||
update();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user