completed migrating data scope from user to workspace
This commit is contained in:
@@ -36,6 +36,7 @@ class BatchScreenModel {
|
||||
int batchItemsTotalElements = 0;
|
||||
String batchItemsSearchQuery = '';
|
||||
String? batchItemsStatusFilter;
|
||||
String? batchItemsRecipientAccount;
|
||||
bool batchItemsIsLoadingMore = false;
|
||||
|
||||
// Provider products cache: providerId -> list of products
|
||||
@@ -196,7 +197,7 @@ class BatchController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
final raw = await http.get(
|
||||
'/public/group-batches?${buildQueryParameters(params)}',
|
||||
'/group-batches?${buildQueryParameters(params)}',
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
final pageableModel = PageableModel.fromJson(
|
||||
@@ -268,8 +269,7 @@ class BatchController extends ChangeNotifier {
|
||||
String batchId, {
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
String? search,
|
||||
String? status,
|
||||
Map<String, String>? filters,
|
||||
}) async {
|
||||
if (page == 0) {
|
||||
model.isLoading = true;
|
||||
@@ -287,16 +287,12 @@ class BatchController extends ChangeNotifier {
|
||||
'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;
|
||||
if (filters != null) {
|
||||
params.addAll(filters);
|
||||
}
|
||||
|
||||
final raw = await http.get(
|
||||
'/public/group-batches/items?${buildQueryParameters(params)}',
|
||||
'/group-batches/items?${buildQueryParameters(params)}',
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
final pageableModel = PageableModel.fromJson(
|
||||
@@ -337,10 +333,20 @@ class BatchController extends ChangeNotifier {
|
||||
String batchId, {
|
||||
String? search,
|
||||
String? status,
|
||||
String? account,
|
||||
}) async {
|
||||
model.batchItemsSearchQuery = search ?? '';
|
||||
model.batchItemsStatusFilter = status;
|
||||
await listBatchItems(batchId, page: 0, search: search, status: status);
|
||||
model.batchItemsRecipientAccount = account;
|
||||
await listBatchItems(
|
||||
batchId,
|
||||
page: 0,
|
||||
filters: _buildBatchItemFilters(
|
||||
search: search,
|
||||
status: status,
|
||||
account: account,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> loadNextBatchItemsPage(String batchId) async {
|
||||
@@ -351,22 +357,44 @@ class BatchController extends ChangeNotifier {
|
||||
await listBatchItems(
|
||||
batchId,
|
||||
page: model.batchItemsCurrentPage + 1,
|
||||
search: model.batchItemsSearchQuery.isNotEmpty
|
||||
? model.batchItemsSearchQuery
|
||||
: null,
|
||||
status: model.batchItemsStatusFilter,
|
||||
filters: _buildBatchItemFilters(
|
||||
search: model.batchItemsSearchQuery.isNotEmpty
|
||||
? model.batchItemsSearchQuery
|
||||
: null,
|
||||
status: model.batchItemsStatusFilter,
|
||||
account: model.batchItemsRecipientAccount,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Builds the filters map for batch items queries from individual params.
|
||||
Map<String, String>? _buildBatchItemFilters({
|
||||
String? search,
|
||||
String? status,
|
||||
String? account,
|
||||
}) {
|
||||
final filters = <String, String>{};
|
||||
if (search != null && search.isNotEmpty) {
|
||||
filters['recipientName'] = search;
|
||||
}
|
||||
if (status != null && status.isNotEmpty) {
|
||||
filters['status'] = status;
|
||||
}
|
||||
if (account != null && account.isNotEmpty) {
|
||||
filters['recipientAccount'] = account;
|
||||
}
|
||||
return filters.isEmpty ? null : filters;
|
||||
}
|
||||
|
||||
Future<ApiResponse<GroupBatch>> getBatch(
|
||||
String batchId,
|
||||
String userId,
|
||||
String workspaceId,
|
||||
) async {
|
||||
model.isActing = true;
|
||||
if (!_disposed) notifyListeners();
|
||||
try {
|
||||
final raw = await http.get(
|
||||
'/public/group-batches/$batchId?userId=$userId',
|
||||
'/group-batches/$batchId?workspaceId=$workspaceId',
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
final batch = GroupBatch.fromJson(response as Map<String, dynamic>);
|
||||
@@ -386,7 +414,7 @@ class BatchController extends ChangeNotifier {
|
||||
model.isActing = true;
|
||||
if (!_disposed) notifyListeners();
|
||||
try {
|
||||
final raw = await http.post('/public/group-batches', req.toJson());
|
||||
final raw = await http.post('/group-batches', req.toJson());
|
||||
final response = _unwrap(raw);
|
||||
final batch = GroupBatch.fromJson(response as Map<String, dynamic>);
|
||||
model.currentBatch = batch;
|
||||
@@ -409,7 +437,7 @@ class BatchController extends ChangeNotifier {
|
||||
if (!_disposed) notifyListeners();
|
||||
try {
|
||||
final raw = await http.put(
|
||||
'/public/group-batches/${request.id}',
|
||||
'/group-batches/${request.id}',
|
||||
request.toJson(),
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
@@ -440,7 +468,7 @@ class BatchController extends ChangeNotifier {
|
||||
if (!_disposed) notifyListeners();
|
||||
try {
|
||||
final raw = await http.put(
|
||||
'/public/group-batches/$batchId/items',
|
||||
'/group-batches/$batchId/items',
|
||||
updateList.toJson(),
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
@@ -463,16 +491,21 @@ class BatchController extends ChangeNotifier {
|
||||
|
||||
Future<ApiResponse<GroupBatch>> confirmBatch(
|
||||
String batchId,
|
||||
String userId,
|
||||
String workspaceId,
|
||||
String authType,
|
||||
) async {
|
||||
model.isActing = true;
|
||||
if (!_disposed) notifyListeners();
|
||||
try {
|
||||
final key =
|
||||
'batch-$batchId-confirm-${DateTime.now().millisecondsSinceEpoch}';
|
||||
final body = BatchActionRequest(userId: userId, idempotencyKey: key);
|
||||
final body = BatchActionRequest(
|
||||
workspaceId: workspaceId,
|
||||
authType: authType,
|
||||
idempotencyKey: key,
|
||||
);
|
||||
final raw = await http.post(
|
||||
'/public/group-batches/$batchId/confirm',
|
||||
'/group-batches/$batchId/confirm',
|
||||
body.toJson(),
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
@@ -491,16 +524,19 @@ class BatchController extends ChangeNotifier {
|
||||
|
||||
Future<ApiResponse<TransactionModel>> requestBatch(
|
||||
String batchId,
|
||||
String userId,
|
||||
String workspaceId,
|
||||
) async {
|
||||
model.isActing = true;
|
||||
if (!_disposed) notifyListeners();
|
||||
try {
|
||||
final key =
|
||||
'batch-$batchId-request-${DateTime.now().millisecondsSinceEpoch}';
|
||||
final body = BatchActionRequest(userId: userId, idempotencyKey: key);
|
||||
final body = BatchActionRequest(
|
||||
workspaceId: workspaceId,
|
||||
idempotencyKey: key,
|
||||
);
|
||||
final raw = await http.post(
|
||||
'/public/group-batches/$batchId/request',
|
||||
'/group-batches/$batchId/request',
|
||||
body.toJson(),
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
@@ -519,13 +555,13 @@ class BatchController extends ChangeNotifier {
|
||||
|
||||
Future<ApiResponse<Map<String, dynamic>>> downloadBatchReport(
|
||||
String batchId,
|
||||
String userId,
|
||||
String workspaceId,
|
||||
) async {
|
||||
model.isActing = true;
|
||||
if (!_disposed) notifyListeners();
|
||||
try {
|
||||
final raw = await http.get(
|
||||
'/public/group-batches/$batchId/report?userId=$userId',
|
||||
'/group-batches/$batchId/report?workspaceId=$workspaceId',
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
model.isActing = false;
|
||||
@@ -542,16 +578,19 @@ class BatchController extends ChangeNotifier {
|
||||
|
||||
Future<ApiResponse<TransactionModel>> pollBatch(
|
||||
String batchId,
|
||||
String userId,
|
||||
String workspaceId,
|
||||
) async {
|
||||
model.isActing = true;
|
||||
if (!_disposed) notifyListeners();
|
||||
try {
|
||||
final key =
|
||||
'batch-$batchId-poll-${DateTime.now().millisecondsSinceEpoch}';
|
||||
final body = BatchActionRequest(userId: userId, idempotencyKey: key);
|
||||
final body = BatchActionRequest(
|
||||
workspaceId: workspaceId,
|
||||
idempotencyKey: key,
|
||||
);
|
||||
final raw = await http.post(
|
||||
'/public/group-batches/$batchId/poll',
|
||||
'/group-batches/$batchId/poll',
|
||||
body.toJson(),
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
@@ -578,10 +617,10 @@ class BatchController extends ChangeNotifier {
|
||||
|
||||
Future<void> deleteBatch({
|
||||
required String batchId,
|
||||
required String userId,
|
||||
required String workspaceId,
|
||||
}) async {
|
||||
try {
|
||||
await http.delete('/public/group-batches/$batchId?userId=$userId');
|
||||
await http.delete('/group-batches/$batchId?workspaceId=$workspaceId');
|
||||
model.batches.removeWhere((b) => b.id == batchId);
|
||||
model.selectedBatchIds.remove(batchId);
|
||||
model.currentBatch = null;
|
||||
@@ -595,7 +634,7 @@ class BatchController extends ChangeNotifier {
|
||||
|
||||
Future<void> deleteSelectedBatches({
|
||||
required String groupId,
|
||||
required String userId,
|
||||
required String workspaceId,
|
||||
}) async {
|
||||
final idsToDelete = List<String>.from(model.selectedBatchIds);
|
||||
model.selectMode = false;
|
||||
@@ -603,7 +642,7 @@ class BatchController extends ChangeNotifier {
|
||||
if (!_disposed) notifyListeners();
|
||||
|
||||
for (final id in idsToDelete) {
|
||||
await deleteBatch(batchId: id, userId: userId);
|
||||
await deleteBatch(batchId: id, workspaceId: workspaceId);
|
||||
}
|
||||
|
||||
// Refresh after deletion
|
||||
|
||||
@@ -53,7 +53,7 @@ class GroupController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
Future<ApiResponse<PageableModel>> listGroups({
|
||||
required String userId,
|
||||
required String workspaceId,
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
String? name,
|
||||
@@ -69,7 +69,7 @@ class GroupController extends ChangeNotifier {
|
||||
|
||||
try {
|
||||
final params = <String, String>{
|
||||
'userId': userId,
|
||||
'workspaceId': workspaceId,
|
||||
'page': page.toString(),
|
||||
'size': size.toString(),
|
||||
};
|
||||
@@ -78,7 +78,7 @@ class GroupController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
final raw = await http.get(
|
||||
'/public/recipient-groups?${buildQueryParameters(params)}',
|
||||
'/recipient-groups?${buildQueryParameters(params)}',
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
final pageableModel = PageableModel.fromJson(
|
||||
@@ -115,17 +115,17 @@ class GroupController extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> searchGroups({required String userId, String? name}) async {
|
||||
Future<void> searchGroups({required String workspaceId, String? name}) async {
|
||||
model.searchQuery = name ?? '';
|
||||
await listGroups(userId: userId, page: 0, name: name);
|
||||
await listGroups(workspaceId: workspaceId, page: 0, name: name);
|
||||
}
|
||||
|
||||
Future<void> loadNextPage({required String userId}) async {
|
||||
Future<void> loadNextPage({required String workspaceId}) async {
|
||||
if (model.isLoadingMore || model.currentPage + 1 >= model.totalPages) {
|
||||
return;
|
||||
}
|
||||
await listGroups(
|
||||
userId: userId,
|
||||
workspaceId: workspaceId,
|
||||
page: model.currentPage + 1,
|
||||
name: model.searchQuery.isNotEmpty ? model.searchQuery : null,
|
||||
);
|
||||
@@ -138,7 +138,7 @@ class GroupController extends ChangeNotifier {
|
||||
if (!_disposed) notifyListeners();
|
||||
try {
|
||||
final raw = await http.get(
|
||||
'/public/recipient-groups/members?recipientGroupId=$groupId',
|
||||
'/recipient-groups/members?recipientGroupId=$groupId',
|
||||
);
|
||||
final response = _unwrap(raw);
|
||||
final List<dynamic> content;
|
||||
@@ -172,7 +172,7 @@ class GroupController extends ChangeNotifier {
|
||||
model.isLoading = true;
|
||||
if (!_disposed) notifyListeners();
|
||||
try {
|
||||
final raw = await http.post('/public/recipient-groups', req.toJson());
|
||||
final raw = await http.post('/recipient-groups', req.toJson());
|
||||
final response = _unwrap(raw);
|
||||
final group = RecipientGroup.fromJson(response as Map<String, dynamic>);
|
||||
model.groups = [group, ...model.groups];
|
||||
@@ -190,11 +190,11 @@ class GroupController extends ChangeNotifier {
|
||||
|
||||
Future<void> deleteGroup({
|
||||
required String groupId,
|
||||
required String userId,
|
||||
required String workspaceId,
|
||||
}) async {
|
||||
try {
|
||||
await http.delete(
|
||||
'/public/recipient-groups/delete/$groupId?userId=$userId',
|
||||
'/recipient-groups/delete/$groupId?workspaceId=$workspaceId',
|
||||
);
|
||||
model.groups.removeWhere((g) => g.id == groupId);
|
||||
model.selectedGroupIds.remove(groupId);
|
||||
@@ -226,11 +226,11 @@ class GroupController extends ChangeNotifier {
|
||||
Future<void> removeMember({
|
||||
required String groupId,
|
||||
required String recipientId,
|
||||
required String userId,
|
||||
required String workspaceId,
|
||||
}) async {
|
||||
try {
|
||||
await http.delete(
|
||||
'/public/recipient-groups/$groupId/members/$recipientId?userId=$userId',
|
||||
'/recipient-groups/$groupId/members/$recipientId?workspaceId=$workspaceId',
|
||||
);
|
||||
model.members.removeWhere((m) => m.id == recipientId);
|
||||
if (!_disposed) notifyListeners();
|
||||
@@ -241,19 +241,20 @@ class GroupController extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteSelectedGroups({required String userId}) async {
|
||||
Future<void> deleteSelectedGroups({required String workspaceId}) async {
|
||||
final idsToDelete = List<String>.from(model.selectedGroupIds);
|
||||
model.selectMode = false;
|
||||
model.selectedGroupIds.clear();
|
||||
if (!_disposed) notifyListeners();
|
||||
|
||||
for (final id in idsToDelete) {
|
||||
await deleteGroup(groupId: id, userId: userId);
|
||||
await deleteGroup(groupId: id, workspaceId: workspaceId);
|
||||
}
|
||||
}
|
||||
|
||||
Future<ApiResponse<CreateGroupFromFileResponse>> createGroupFromFile({
|
||||
required String groupName,
|
||||
required String workspaceId,
|
||||
required String userId,
|
||||
String? description,
|
||||
required String currency,
|
||||
@@ -266,6 +267,7 @@ class GroupController extends ChangeNotifier {
|
||||
try {
|
||||
final fields = <String, String>{
|
||||
'groupName': groupName,
|
||||
'workspaceId': workspaceId,
|
||||
'userId': userId,
|
||||
'currency': currency,
|
||||
};
|
||||
@@ -274,7 +276,7 @@ class GroupController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
final raw = await http.multipartPost(
|
||||
'/public/recipient-groups/create-from-file',
|
||||
'/recipient-groups/create-from-file',
|
||||
fields,
|
||||
'file',
|
||||
fileBytes,
|
||||
|
||||
@@ -35,9 +35,9 @@ class GroupBatchItemUpdateRequest {
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
class GroupBatchItemUpdateList {
|
||||
final List<GroupBatchItemUpdateRequest>? items;
|
||||
final String? userId;
|
||||
final String? workspaceId;
|
||||
|
||||
const GroupBatchItemUpdateList({this.items, this.userId});
|
||||
const GroupBatchItemUpdateList({this.items, this.workspaceId});
|
||||
|
||||
factory GroupBatchItemUpdateList.fromJson(Map<String, dynamic> json) =>
|
||||
_$GroupBatchItemUpdateListFromJson(json);
|
||||
|
||||
@@ -42,12 +42,12 @@ GroupBatchItemUpdateList _$GroupBatchItemUpdateListFromJson(
|
||||
(e) => GroupBatchItemUpdateRequest.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList(),
|
||||
userId: json['userId'] as String?,
|
||||
workspaceId: json['workspaceId'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GroupBatchItemUpdateListToJson(
|
||||
GroupBatchItemUpdateList instance,
|
||||
) => <String, dynamic>{
|
||||
'items': instance.items?.map((e) => e.toJson()).toList(),
|
||||
'userId': instance.userId,
|
||||
'workspaceId': instance.workspaceId,
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ class GroupBatch {
|
||||
final String? id;
|
||||
final String? recipientGroupId;
|
||||
final String? userId;
|
||||
final String? workspaceId;
|
||||
final String? currency;
|
||||
final String? description;
|
||||
final double? value;
|
||||
@@ -24,6 +25,7 @@ class GroupBatch {
|
||||
this.id,
|
||||
this.recipientGroupId,
|
||||
this.userId,
|
||||
this.workspaceId,
|
||||
this.currency,
|
||||
this.description,
|
||||
this.value,
|
||||
@@ -88,6 +90,7 @@ class GroupBatchItem {
|
||||
class CreateBatchRequest {
|
||||
final String recipientGroupId;
|
||||
final String userId;
|
||||
final String workspaceId;
|
||||
final String currency;
|
||||
final String description;
|
||||
final double amount;
|
||||
@@ -98,6 +101,7 @@ class CreateBatchRequest {
|
||||
const CreateBatchRequest({
|
||||
required this.recipientGroupId,
|
||||
required this.userId,
|
||||
required this.workspaceId,
|
||||
required this.currency,
|
||||
required this.description,
|
||||
required this.amount,
|
||||
@@ -114,12 +118,14 @@ class CreateBatchRequest {
|
||||
|
||||
@JsonSerializable()
|
||||
class BatchActionRequest {
|
||||
final String userId;
|
||||
final String workspaceId;
|
||||
final String idempotencyKey;
|
||||
final String? authType;
|
||||
|
||||
const BatchActionRequest({
|
||||
required this.userId,
|
||||
required this.workspaceId,
|
||||
required this.idempotencyKey,
|
||||
this.authType,
|
||||
});
|
||||
|
||||
factory BatchActionRequest.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -131,14 +137,14 @@ class BatchActionRequest {
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
class GroupBatchUpdateRequest {
|
||||
String? id;
|
||||
String? userId;
|
||||
String? workspaceId;
|
||||
String? paymentProcessorLabel;
|
||||
String? paymentProcessorName;
|
||||
String? paymentProcessorImage;
|
||||
|
||||
GroupBatchUpdateRequest({
|
||||
this.id,
|
||||
this.userId,
|
||||
this.workspaceId,
|
||||
this.paymentProcessorLabel,
|
||||
this.paymentProcessorName,
|
||||
this.paymentProcessorImage,
|
||||
|
||||
@@ -10,6 +10,7 @@ GroupBatch _$GroupBatchFromJson(Map<String, dynamic> json) => GroupBatch(
|
||||
id: json['id'] as String?,
|
||||
recipientGroupId: json['recipientGroupId'] as String?,
|
||||
userId: json['userId'] as String?,
|
||||
workspaceId: json['workspaceId'] as String?,
|
||||
currency: json['currency'] as String?,
|
||||
description: json['description'] as String?,
|
||||
value: (json['value'] as num?)?.toDouble(),
|
||||
@@ -29,6 +30,7 @@ Map<String, dynamic> _$GroupBatchToJson(GroupBatch instance) =>
|
||||
'id': instance.id,
|
||||
'recipientGroupId': instance.recipientGroupId,
|
||||
'userId': instance.userId,
|
||||
'workspaceId': instance.workspaceId,
|
||||
'currency': instance.currency,
|
||||
'description': instance.description,
|
||||
'value': instance.value,
|
||||
@@ -83,6 +85,7 @@ CreateBatchRequest _$CreateBatchRequestFromJson(Map<String, dynamic> json) =>
|
||||
CreateBatchRequest(
|
||||
recipientGroupId: json['recipientGroupId'] as String,
|
||||
userId: json['userId'] as String,
|
||||
workspaceId: json['workspaceId'] as String,
|
||||
currency: json['currency'] as String,
|
||||
description: json['description'] as String,
|
||||
amount: (json['amount'] as num).toDouble(),
|
||||
@@ -95,6 +98,7 @@ Map<String, dynamic> _$CreateBatchRequestToJson(CreateBatchRequest instance) =>
|
||||
<String, dynamic>{
|
||||
'recipientGroupId': instance.recipientGroupId,
|
||||
'userId': instance.userId,
|
||||
'workspaceId': instance.workspaceId,
|
||||
'currency': instance.currency,
|
||||
'description': instance.description,
|
||||
'amount': instance.amount,
|
||||
@@ -105,21 +109,23 @@ Map<String, dynamic> _$CreateBatchRequestToJson(CreateBatchRequest instance) =>
|
||||
|
||||
BatchActionRequest _$BatchActionRequestFromJson(Map<String, dynamic> json) =>
|
||||
BatchActionRequest(
|
||||
userId: json['userId'] as String,
|
||||
workspaceId: json['workspaceId'] as String,
|
||||
idempotencyKey: json['idempotencyKey'] as String,
|
||||
authType: json['authType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$BatchActionRequestToJson(BatchActionRequest instance) =>
|
||||
<String, dynamic>{
|
||||
'userId': instance.userId,
|
||||
'workspaceId': instance.workspaceId,
|
||||
'idempotencyKey': instance.idempotencyKey,
|
||||
'authType': instance.authType,
|
||||
};
|
||||
|
||||
GroupBatchUpdateRequest _$GroupBatchUpdateRequestFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => GroupBatchUpdateRequest(
|
||||
id: json['id'] as String?,
|
||||
userId: json['userId'] as String?,
|
||||
workspaceId: json['workspaceId'] as String?,
|
||||
paymentProcessorLabel: json['paymentProcessorLabel'] as String?,
|
||||
paymentProcessorName: json['paymentProcessorName'] as String?,
|
||||
paymentProcessorImage: json['paymentProcessorImage'] as String?,
|
||||
@@ -129,7 +135,7 @@ Map<String, dynamic> _$GroupBatchUpdateRequestToJson(
|
||||
GroupBatchUpdateRequest instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'userId': instance.userId,
|
||||
'workspaceId': instance.workspaceId,
|
||||
'paymentProcessorLabel': instance.paymentProcessorLabel,
|
||||
'paymentProcessorName': instance.paymentProcessorName,
|
||||
'paymentProcessorImage': instance.paymentProcessorImage,
|
||||
|
||||
@@ -9,6 +9,7 @@ class RecipientGroup {
|
||||
final String name;
|
||||
final String? description;
|
||||
final String? userId;
|
||||
final String? workspaceId;
|
||||
final String? createdAt;
|
||||
|
||||
const RecipientGroup({
|
||||
@@ -16,6 +17,7 @@ class RecipientGroup {
|
||||
required this.name,
|
||||
this.description,
|
||||
this.userId,
|
||||
this.workspaceId,
|
||||
this.createdAt,
|
||||
});
|
||||
|
||||
@@ -56,12 +58,14 @@ class CreateGroupRequest {
|
||||
final String name;
|
||||
final String? description;
|
||||
final String userId;
|
||||
final String workspaceId;
|
||||
final List<Recipient> recipients;
|
||||
|
||||
const CreateGroupRequest({
|
||||
required this.name,
|
||||
this.description,
|
||||
required this.userId,
|
||||
required this.workspaceId,
|
||||
required this.recipients,
|
||||
});
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ RecipientGroup _$RecipientGroupFromJson(Map<String, dynamic> json) =>
|
||||
name: json['name'] as String,
|
||||
description: json['description'] as String?,
|
||||
userId: json['userId'] as String?,
|
||||
workspaceId: json['workspaceId'] as String?,
|
||||
createdAt: json['createdAt'] as String?,
|
||||
);
|
||||
|
||||
@@ -21,6 +22,7 @@ Map<String, dynamic> _$RecipientGroupToJson(RecipientGroup instance) =>
|
||||
'name': instance.name,
|
||||
'description': instance.description,
|
||||
'userId': instance.userId,
|
||||
'workspaceId': instance.workspaceId,
|
||||
'createdAt': instance.createdAt,
|
||||
};
|
||||
|
||||
@@ -53,6 +55,7 @@ CreateGroupRequest _$CreateGroupRequestFromJson(Map<String, dynamic> json) =>
|
||||
name: json['name'] as String,
|
||||
description: json['description'] as String?,
|
||||
userId: json['userId'] as String,
|
||||
workspaceId: json['workspaceId'] as String,
|
||||
recipients: (json['recipients'] as List<dynamic>)
|
||||
.map((e) => Recipient.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
@@ -63,5 +66,6 @@ Map<String, dynamic> _$CreateGroupRequestToJson(CreateGroupRequest instance) =>
|
||||
'name': instance.name,
|
||||
'description': instance.description,
|
||||
'userId': instance.userId,
|
||||
'workspaceId': instance.workspaceId,
|
||||
'recipients': instance.recipients.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
@@ -90,10 +90,12 @@ class _BatchCreateScreenState extends State<BatchCreateScreen> {
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final userId = prefs.getString('userId') ?? '';
|
||||
final workspaceId = prefs.getString('workspaceId') ?? '';
|
||||
|
||||
final req = CreateBatchRequest(
|
||||
recipientGroupId: widget.group.id ?? '',
|
||||
userId: userId,
|
||||
workspaceId: workspaceId,
|
||||
currency: _currency,
|
||||
description: _descriptionController.text.trim(),
|
||||
amount: amount,
|
||||
@@ -115,7 +117,7 @@ class _BatchCreateScreenState extends State<BatchCreateScreen> {
|
||||
batch.id!,
|
||||
selectedProvider,
|
||||
selectedProduct,
|
||||
userId,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -130,7 +132,7 @@ class _BatchCreateScreenState extends State<BatchCreateScreen> {
|
||||
String batchId,
|
||||
BillProvider provider,
|
||||
BillProduct? product,
|
||||
String userId,
|
||||
String workspaceId,
|
||||
) async {
|
||||
// Fetch the batch items so we can update them
|
||||
final itemsResult = await batchController.listBatchItems(batchId);
|
||||
@@ -154,7 +156,7 @@ class _BatchCreateScreenState extends State<BatchCreateScreen> {
|
||||
|
||||
final updateList = GroupBatchItemUpdateList(
|
||||
items: updateItems,
|
||||
userId: userId,
|
||||
workspaceId: workspaceId,
|
||||
);
|
||||
|
||||
await batchController.updateBatchItems(batchId, updateList);
|
||||
|
||||
@@ -33,12 +33,13 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
ConfirmController? _confirmController;
|
||||
GroupBatchUpdateRequest? _cachedUpdateReq;
|
||||
bool _showPollButton = false;
|
||||
String _userId = '';
|
||||
String _workspaceId = '';
|
||||
bool _initialLoading = true;
|
||||
String? _loadError;
|
||||
PaymentProcessor? _selectedProcessor;
|
||||
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
final TextEditingController _accountController = TextEditingController();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
bool _editMode = false;
|
||||
@@ -85,9 +86,9 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
|
||||
Future<void> _init() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
_userId = prefs.getString('userId') ?? '';
|
||||
_workspaceId = prefs.getString('workspaceId') ?? '';
|
||||
|
||||
final result = await controller.getBatch(widget.batchId, _userId);
|
||||
final result = await controller.getBatch(widget.batchId, _workspaceId);
|
||||
if (!mounted) return;
|
||||
|
||||
if (result.isSuccess) {
|
||||
@@ -117,6 +118,7 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
_accountController.dispose();
|
||||
_scrollController.dispose();
|
||||
_entryController.dispose();
|
||||
_confirmController?.dispose();
|
||||
@@ -136,7 +138,7 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
// Persist the selected payment processor on the batch before confirming
|
||||
final updateReq = GroupBatchUpdateRequest(
|
||||
id: batch.id,
|
||||
userId: _userId,
|
||||
workspaceId: _workspaceId,
|
||||
paymentProcessorLabel: processor.label,
|
||||
paymentProcessorName: processor.name,
|
||||
paymentProcessorImage: processor.image,
|
||||
@@ -152,7 +154,11 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
_cachedUpdateReq = updateReq;
|
||||
|
||||
// Step 2: Confirm the batch
|
||||
final result = await controller.confirmBatch(batch.id!, _userId);
|
||||
final result = await controller.confirmBatch(
|
||||
batch.id!,
|
||||
_workspaceId,
|
||||
_selectedProcessor!.authType,
|
||||
);
|
||||
if (!mounted) return;
|
||||
if (!result.isSuccess) {
|
||||
_showError(result.error ?? 'Confirm failed.');
|
||||
@@ -431,7 +437,7 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
final batch = _batch;
|
||||
if (batch == null) return;
|
||||
|
||||
final result = await controller.requestBatch(batch.id!, _userId);
|
||||
final result = await controller.requestBatch(batch.id!, _workspaceId);
|
||||
if (!mounted) return;
|
||||
|
||||
if (!result.isSuccess) {
|
||||
@@ -484,7 +490,7 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final pollResult = await controller.pollBatch(batch.id!, _userId);
|
||||
final pollResult = await controller.pollBatch(batch.id!, _workspaceId);
|
||||
if (!mounted) return;
|
||||
if (pollResult.isSuccess) {
|
||||
setState(() => _showPollButton = false);
|
||||
@@ -672,7 +678,7 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
Future<void> _onPoll() async {
|
||||
final batch = _batch;
|
||||
if (batch == null) return;
|
||||
final result = await controller.pollBatch(batch.id!, _userId);
|
||||
final result = await controller.pollBatch(batch.id!, _workspaceId);
|
||||
if (!mounted) return;
|
||||
if (result.isSuccess) {
|
||||
setState(() => _showPollButton = false);
|
||||
@@ -685,7 +691,10 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
Future<void> _onDownloadReport() async {
|
||||
final batch = _batch;
|
||||
if (batch == null) return;
|
||||
final result = await controller.downloadBatchReport(batch.id!, _userId);
|
||||
final result = await controller.downloadBatchReport(
|
||||
batch.id!,
|
||||
_workspaceId,
|
||||
);
|
||||
if (!mounted) return;
|
||||
if (result.isSuccess) {
|
||||
final fileUrl = result.data?['fileUrl'] as String?;
|
||||
@@ -1435,7 +1444,10 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
|
||||
if (items.isEmpty) return false;
|
||||
|
||||
final updateList = GroupBatchItemUpdateList(items: items, userId: _userId);
|
||||
final updateList = GroupBatchItemUpdateList(
|
||||
items: items,
|
||||
workspaceId: _workspaceId,
|
||||
);
|
||||
|
||||
final result = await controller.updateBatchItems(batch.id!, updateList);
|
||||
if (!mounted) return false;
|
||||
@@ -1492,6 +1504,8 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
onPressed: () {
|
||||
setSheetState(() {
|
||||
controller.model.batchItemsStatusFilter = null;
|
||||
controller.model.batchItemsRecipientAccount = null;
|
||||
_accountController.clear();
|
||||
});
|
||||
},
|
||||
child: const Text('Clear All'),
|
||||
@@ -1517,14 +1531,18 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
items: const [
|
||||
DropdownMenuItem(value: null, child: Text('All')),
|
||||
DropdownMenuItem(
|
||||
value: 'PENDING',
|
||||
child: Text('Pending'),
|
||||
value: 'CONFIRMED',
|
||||
child: Text('CONFIRMED'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 'SUCCESS',
|
||||
child: Text('Success'),
|
||||
value: 'INTEGRATED',
|
||||
child: Text('INTEGRATED'),
|
||||
),
|
||||
DropdownMenuItem(value: 'FAILED', child: Text('FAILED')),
|
||||
DropdownMenuItem(
|
||||
value: 'SKIPPED',
|
||||
child: Text('SKIPPED'),
|
||||
),
|
||||
DropdownMenuItem(value: 'FAILED', child: Text('Failed')),
|
||||
],
|
||||
onChanged: (v) {
|
||||
setSheetState(
|
||||
@@ -1532,6 +1550,24 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: _accountController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Account Number',
|
||||
hintText: 'Filter by account number',
|
||||
filled: true,
|
||||
fillColor: Colors.grey.shade100,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
@@ -1546,6 +1582,9 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
? _searchController.text
|
||||
: null,
|
||||
status: controller.model.batchItemsStatusFilter,
|
||||
account: _accountController.text.isNotEmpty
|
||||
? _accountController.text
|
||||
: null,
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -2021,7 +2060,9 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
// Action Bar — search, filter, edit, refresh
|
||||
// ──────────────────────────────────────────────────────────────
|
||||
Widget _buildActionBar(ThemeData theme, bool isDark) {
|
||||
final hasFilters = controller.model.batchItemsStatusFilter != null;
|
||||
final hasFilters =
|
||||
controller.model.batchItemsStatusFilter != null ||
|
||||
controller.model.batchItemsRecipientAccount != null;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -2033,7 +2074,7 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
child: TextFormField(
|
||||
controller: _searchController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search by name or phone',
|
||||
hintText: 'Search by recipient name',
|
||||
prefixIcon: const Icon(Icons.search, size: 20),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? IconButton(
|
||||
@@ -2046,6 +2087,9 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
batchId,
|
||||
status:
|
||||
controller.model.batchItemsStatusFilter,
|
||||
account: controller
|
||||
.model
|
||||
.batchItemsRecipientAccount,
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -2075,6 +2119,7 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
batchId,
|
||||
search: value.isNotEmpty ? value : null,
|
||||
status: controller.model.batchItemsStatusFilter,
|
||||
account: controller.model.batchItemsRecipientAccount,
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -2147,6 +2192,26 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
||||
search: _searchController.text.isNotEmpty
|
||||
? _searchController.text
|
||||
: null,
|
||||
account: controller.model.batchItemsRecipientAccount,
|
||||
);
|
||||
}
|
||||
},
|
||||
theme,
|
||||
),
|
||||
if (controller.model.batchItemsRecipientAccount != null)
|
||||
_activeFilterChip(
|
||||
'Account: ${controller.model.batchItemsRecipientAccount}',
|
||||
() {
|
||||
controller.model.batchItemsRecipientAccount = null;
|
||||
_accountController.clear();
|
||||
final batchId = _batch?.id ?? '';
|
||||
if (batchId.isNotEmpty) {
|
||||
controller.searchBatchItems(
|
||||
batchId,
|
||||
search: _searchController.text.isNotEmpty
|
||||
? _searchController.text
|
||||
: null,
|
||||
status: controller.model.batchItemsStatusFilter,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -65,10 +65,12 @@ class _CreateGroupFromFileScreenState extends State<CreateGroupFromFileScreen> {
|
||||
}
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final workspaceId = prefs.getString('workspaceId') ?? '';
|
||||
final userId = prefs.getString('userId') ?? '';
|
||||
|
||||
final result = await controller.createGroupFromFile(
|
||||
groupName: _groupNameController.text.trim(),
|
||||
workspaceId: workspaceId,
|
||||
userId: userId,
|
||||
description: _descriptionController.text.trim().isEmpty
|
||||
? null
|
||||
|
||||
@@ -82,6 +82,7 @@ class _GroupCreateScreenState extends State<GroupCreateScreen> {
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final userId = prefs.getString('userId') ?? '';
|
||||
final workspaceId = prefs.getString('workspaceId') ?? '';
|
||||
|
||||
final req = CreateGroupRequest(
|
||||
name: _nameController.text.trim(),
|
||||
@@ -89,6 +90,7 @@ class _GroupCreateScreenState extends State<GroupCreateScreen> {
|
||||
? null
|
||||
: _descriptionController.text.trim(),
|
||||
userId: userId,
|
||||
workspaceId: workspaceId,
|
||||
recipients: recipients,
|
||||
);
|
||||
|
||||
|
||||
@@ -552,7 +552,7 @@ class _GroupDetailScreenState extends State<GroupDetailScreen>
|
||||
await groupController.removeMember(
|
||||
groupId: widget.group.id ?? '',
|
||||
recipientId: member.id ?? '',
|
||||
userId: widget.group.userId ?? '',
|
||||
workspaceId: widget.group.workspaceId ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -675,7 +675,7 @@ class _GroupDetailScreenState extends State<GroupDetailScreen>
|
||||
}
|
||||
|
||||
void _onDeleteSelected() async {
|
||||
final userId = widget.group.userId ?? '';
|
||||
final workspaceId = widget.group.workspaceId ?? '';
|
||||
final count = batchController.model.selectedBatchIds.length;
|
||||
|
||||
final confirmed = await showDialog<bool>(
|
||||
@@ -700,7 +700,7 @@ class _GroupDetailScreenState extends State<GroupDetailScreen>
|
||||
if (confirmed == true) {
|
||||
await batchController.deleteSelectedBatches(
|
||||
groupId: widget.group.id ?? '',
|
||||
userId: userId,
|
||||
workspaceId: workspaceId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,26 +30,28 @@ class _GroupsScreenState extends State<GroupsScreen> {
|
||||
void _onScroll() {
|
||||
if (_scrollController.position.pixels >=
|
||||
_scrollController.position.maxScrollExtent - 200) {
|
||||
controller.loadNextPage(userId: prefs.getString('userId') ?? '');
|
||||
controller.loadNextPage(
|
||||
workspaceId: prefs.getString('workspaceId') ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadData() async {
|
||||
prefs = await SharedPreferences.getInstance();
|
||||
final userId = prefs.getString('userId') ?? '';
|
||||
await controller.listGroups(userId: userId);
|
||||
final workspaceId = prefs.getString('workspaceId') ?? '';
|
||||
await controller.listGroups(workspaceId: workspaceId);
|
||||
}
|
||||
|
||||
void _onSearchChanged(String value) {
|
||||
final userId = prefs.getString('userId') ?? '';
|
||||
final workspaceId = prefs.getString('workspaceId') ?? '';
|
||||
controller.searchGroups(
|
||||
userId: userId,
|
||||
workspaceId: workspaceId,
|
||||
name: value.isNotEmpty ? value : null,
|
||||
);
|
||||
}
|
||||
|
||||
void _onDeleteSelected() async {
|
||||
final userId = prefs.getString('userId') ?? '';
|
||||
final workspaceId = prefs.getString('workspaceId') ?? '';
|
||||
final count = controller.model.selectedGroupIds.length;
|
||||
|
||||
final confirmed = await showDialog<bool>(
|
||||
@@ -72,7 +74,7 @@ class _GroupsScreenState extends State<GroupsScreen> {
|
||||
);
|
||||
|
||||
if (confirmed == true) {
|
||||
await controller.deleteSelectedGroups(userId: userId);
|
||||
await controller.deleteSelectedGroups(workspaceId: workspaceId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user