completed batch feature
This commit is contained in:
@@ -7,6 +7,8 @@ import 'package:qpay/screens/home/home_controller.dart';
|
||||
import 'package:qpay/screens/pay/pay_controller.dart';
|
||||
import 'package:qpay/screens/transactions/transaction_model.dart';
|
||||
|
||||
import '../models/batch_item_update_model.dart';
|
||||
|
||||
class BatchScreenModel {
|
||||
List<GroupBatch> batches = [];
|
||||
List<GroupBatchItem> batchItems = [];
|
||||
@@ -27,6 +29,18 @@ class BatchScreenModel {
|
||||
String? currencyFilter;
|
||||
bool selectMode = false;
|
||||
Set<String> selectedBatchIds = {};
|
||||
|
||||
// Batch items pagination state
|
||||
int batchItemsCurrentPage = 0;
|
||||
int batchItemsTotalPages = 0;
|
||||
int batchItemsTotalElements = 0;
|
||||
String batchItemsSearchQuery = '';
|
||||
String? batchItemsStatusFilter;
|
||||
bool batchItemsIsLoadingMore = false;
|
||||
|
||||
// Provider products cache: providerId -> list of products
|
||||
Map<String, List<BillProduct>> providerProducts = {};
|
||||
Set<String> loadingProviderProducts = {};
|
||||
}
|
||||
|
||||
class BatchController extends ChangeNotifier {
|
||||
@@ -111,6 +125,44 @@ class BatchController extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<ApiResponse<List<BillProduct>>> loadProviderProducts(
|
||||
String providerId,
|
||||
) async {
|
||||
if (model.loadingProviderProducts.contains(providerId)) {
|
||||
// Already loading, return cached data if available
|
||||
final cached = model.providerProducts[providerId];
|
||||
if (cached != null) {
|
||||
return ApiResponse.success(cached);
|
||||
}
|
||||
return ApiResponse.failure('Loading in progress');
|
||||
}
|
||||
|
||||
model.loadingProviderProducts.add(providerId);
|
||||
if (!_disposed) notifyListeners();
|
||||
|
||||
try {
|
||||
final raw = await http.get(
|
||||
'/public/providers/$providerId/products',
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
final List<dynamic> list = response is List
|
||||
? response
|
||||
: (response['content'] ?? []);
|
||||
final products = list
|
||||
.map((e) => BillProduct.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
model.providerProducts[providerId] = products;
|
||||
model.loadingProviderProducts.remove(providerId);
|
||||
if (!_disposed) notifyListeners();
|
||||
return ApiResponse.success(products);
|
||||
} catch (e) {
|
||||
logger.e(e);
|
||||
model.loadingProviderProducts.remove(providerId);
|
||||
if (!_disposed) notifyListeners();
|
||||
return ApiResponse.failure('Failed to load products');
|
||||
}
|
||||
}
|
||||
|
||||
Future<ApiResponse<List<GroupBatch>>> listBatches(
|
||||
String groupId, {
|
||||
int page = 0,
|
||||
@@ -215,41 +267,99 @@ class BatchController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
Future<ApiResponse<List<GroupBatchItem>>> listBatchItems(
|
||||
String batchId,
|
||||
) async {
|
||||
model.isLoading = true;
|
||||
String batchId, {
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
String? search,
|
||||
String? status,
|
||||
}) async {
|
||||
if (page == 0) {
|
||||
model.isLoading = true;
|
||||
model.batchItems = [];
|
||||
model.batchItemsCurrentPage = 0;
|
||||
} else {
|
||||
model.batchItemsIsLoadingMore = true;
|
||||
}
|
||||
if (!_disposed) notifyListeners();
|
||||
|
||||
try {
|
||||
final params = <String, String>{
|
||||
'groupBatchId': batchId,
|
||||
'page': page.toString(),
|
||||
'size': size.toString(),
|
||||
'sort': 'createdAt,desc',
|
||||
};
|
||||
if (search != null && search.isNotEmpty) {
|
||||
params['recipientName'] = search;
|
||||
params['recipientPhone'] = search;
|
||||
}
|
||||
if (status != null && status.isNotEmpty) {
|
||||
params['status'] = status;
|
||||
}
|
||||
|
||||
final raw = await http.get(
|
||||
'/public/group-batches/items?groupBatchId=$batchId&sort=createdAt,desc',
|
||||
'/public/group-batches/items?${buildQueryParameters(params)}',
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
final List<dynamic> content;
|
||||
if (response is Map && response.containsKey('content')) {
|
||||
content = PageableModel.fromJson(
|
||||
response as Map<String, dynamic>,
|
||||
).content;
|
||||
} else if (response is List) {
|
||||
content = response;
|
||||
} else {
|
||||
content = [];
|
||||
}
|
||||
model.batchItems = content
|
||||
final pageableModel = PageableModel.fromJson(
|
||||
response as Map<String, dynamic>,
|
||||
);
|
||||
|
||||
final newItems = pageableModel.content
|
||||
.map((e) => GroupBatchItem.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
|
||||
if (page == 0) {
|
||||
model.batchItems = newItems;
|
||||
} else {
|
||||
model.batchItems.addAll(newItems);
|
||||
}
|
||||
model.batchItemsCurrentPage = pageableModel.number;
|
||||
model.batchItemsTotalPages = pageableModel.totalPages;
|
||||
model.batchItemsTotalElements = pageableModel.totalElements;
|
||||
model.isLoading = false;
|
||||
model.batchItemsIsLoadingMore = false;
|
||||
|
||||
if (!_disposed) notifyListeners();
|
||||
return ApiResponse.success(model.batchItems);
|
||||
} catch (e) {
|
||||
logger.e(e);
|
||||
model.errorMessage = e.toString();
|
||||
model.batchItems = [];
|
||||
if (page == 0) {
|
||||
model.batchItems = [];
|
||||
}
|
||||
model.isLoading = false;
|
||||
model.batchItemsIsLoadingMore = false;
|
||||
if (!_disposed) notifyListeners();
|
||||
return ApiResponse.failure('Failed to load batch items');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> searchBatchItems(
|
||||
String batchId, {
|
||||
String? search,
|
||||
String? status,
|
||||
}) async {
|
||||
model.batchItemsSearchQuery = search ?? '';
|
||||
model.batchItemsStatusFilter = status;
|
||||
await listBatchItems(batchId, page: 0, search: search, status: status);
|
||||
}
|
||||
|
||||
Future<void> loadNextBatchItemsPage(String batchId) async {
|
||||
if (model.batchItemsIsLoadingMore ||
|
||||
model.batchItemsCurrentPage + 1 >= model.batchItemsTotalPages) {
|
||||
return;
|
||||
}
|
||||
await listBatchItems(
|
||||
batchId,
|
||||
page: model.batchItemsCurrentPage + 1,
|
||||
search: model.batchItemsSearchQuery.isNotEmpty
|
||||
? model.batchItemsSearchQuery
|
||||
: null,
|
||||
status: model.batchItemsStatusFilter,
|
||||
);
|
||||
}
|
||||
|
||||
Future<ApiResponse<GroupBatch>> getBatch(
|
||||
String batchId,
|
||||
String userId,
|
||||
@@ -324,6 +434,35 @@ class BatchController extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<ApiResponse<List<GroupBatchItem>>> updateBatchItems(
|
||||
String batchId,
|
||||
GroupBatchItemUpdateList updateList,
|
||||
) async {
|
||||
model.isActing = true;
|
||||
if (!_disposed) notifyListeners();
|
||||
try {
|
||||
final raw = await http.put(
|
||||
'/public/group-batches/$batchId/items',
|
||||
updateList.toJson(),
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
final items = (response as List<dynamic>)
|
||||
.map((e) => GroupBatchItem.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
// Update items in the local batch items list if the current batch matches
|
||||
model.batchItems = items;
|
||||
model.isActing = false;
|
||||
if (!_disposed) notifyListeners();
|
||||
return ApiResponse.success(items);
|
||||
} catch (e, s) {
|
||||
logger.e(e);
|
||||
logger.e(s);
|
||||
model.isActing = false;
|
||||
if (!_disposed) notifyListeners();
|
||||
return ApiResponse.failure('Failed to update batch items');
|
||||
}
|
||||
}
|
||||
|
||||
Future<ApiResponse<GroupBatch>> confirmBatch(
|
||||
String batchId,
|
||||
String userId,
|
||||
|
||||
Reference in New Issue
Block a user