fixed add remove doscument
This commit is contained in:
parent
a88d085001
commit
502bb1e2d9
@ -42,6 +42,7 @@ class AddPaymentRequestController extends GetxController {
|
|||||||
final dueDateController = TextEditingController();
|
final dueDateController = TextEditingController();
|
||||||
final amountController = TextEditingController();
|
final amountController = TextEditingController();
|
||||||
final descriptionController = TextEditingController();
|
final descriptionController = TextEditingController();
|
||||||
|
final removedAttachments = <Map<String, dynamic>>[].obs;
|
||||||
|
|
||||||
// Attachments
|
// Attachments
|
||||||
final attachments = <File>[].obs;
|
final attachments = <File>[].obs;
|
||||||
@ -187,20 +188,47 @@ class AddPaymentRequestController extends GetxController {
|
|||||||
void selectCurrency(Currency currency) => selectedCurrency.value = currency;
|
void selectCurrency(Currency currency) => selectedCurrency.value = currency;
|
||||||
|
|
||||||
void addAttachment(File file) => attachments.add(file);
|
void addAttachment(File file) => attachments.add(file);
|
||||||
void removeAttachment(File file) => attachments.remove(file);
|
void removeAttachment(File file) {
|
||||||
|
if (attachments.contains(file)) {
|
||||||
|
attachments.remove(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeExistingAttachment(Map<String, dynamic> existingAttachment) {
|
||||||
|
final index = existingAttachments.indexWhere(
|
||||||
|
(e) => e['id'] == existingAttachment['id']); // match by normalized id
|
||||||
|
|
||||||
|
if (index != -1) {
|
||||||
|
// Mark as inactive
|
||||||
|
existingAttachments[index]['isActive'] = false;
|
||||||
|
existingAttachments.refresh();
|
||||||
|
|
||||||
|
// Add to removedAttachments to inform API
|
||||||
|
removedAttachments.add({
|
||||||
|
"documentId": existingAttachment['id'], // ensure API receives id
|
||||||
|
"isActive": false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show snackbar feedback
|
||||||
|
showAppSnackbar(
|
||||||
|
title: 'Removed',
|
||||||
|
message: 'Attachment has been removed.',
|
||||||
|
type: SnackbarType.success,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Build attachment payload
|
/// Build attachment payload
|
||||||
Future<List<Map<String, dynamic>>> buildAttachmentPayload() async {
|
Future<List<Map<String, dynamic>>> buildAttachmentPayload() async {
|
||||||
final existingPayload = existingAttachments
|
final existingPayload = existingAttachments
|
||||||
.map((e) => {
|
.map((e) => {
|
||||||
"documentId": e['documentId'],
|
"documentId": e['id'], // use the normalized id
|
||||||
"fileName": e['fileName'],
|
"fileName": e['fileName'],
|
||||||
"contentType": e['contentType'] ?? 'application/octet-stream',
|
"contentType": e['contentType'] ?? 'application/octet-stream',
|
||||||
"fileSize": e['fileSize'] ?? 0,
|
"fileSize": e['fileSize'] ?? 0,
|
||||||
"description": "",
|
"description": "",
|
||||||
"url": e['url'],
|
"url": e['url'],
|
||||||
"isActive": e['isActive'] ?? true,
|
"isActive": e['isActive'] ?? true,
|
||||||
"base64Data": "",
|
|
||||||
})
|
})
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
@ -215,7 +243,78 @@ class AddPaymentRequestController extends GetxController {
|
|||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return [...existingPayload, ...newPayload];
|
// Combine active + removed attachments
|
||||||
|
return [...existingPayload, ...newPayload, ...removedAttachments];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Submit edited payment request
|
||||||
|
Future<bool> submitEditedPaymentRequest({required String requestId}) async {
|
||||||
|
if (isSubmitting.value) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
isSubmitting.value = true;
|
||||||
|
|
||||||
|
// Validate form
|
||||||
|
if (!_validateForm()) return false;
|
||||||
|
|
||||||
|
// Build attachment payload
|
||||||
|
final billAttachments = await buildAttachmentPayload();
|
||||||
|
|
||||||
|
final payload = {
|
||||||
|
"id": requestId,
|
||||||
|
"title": titleController.text.trim(),
|
||||||
|
"projectId": selectedProject.value?['id'] ?? '',
|
||||||
|
"expenseCategoryId": selectedCategory.value?.id ?? '',
|
||||||
|
"amount": double.tryParse(amountController.text.trim()) ?? 0,
|
||||||
|
"currencyId": selectedCurrency.value?.id ?? '',
|
||||||
|
"description": descriptionController.text.trim(),
|
||||||
|
"payee": selectedPayee.value,
|
||||||
|
"dueDate": selectedDueDate.value?.toIso8601String(),
|
||||||
|
"isAdvancePayment": isAdvancePayment.value,
|
||||||
|
"billAttachments": billAttachments.map((a) {
|
||||||
|
return {
|
||||||
|
"documentId": a['documentId'],
|
||||||
|
"fileName": a['fileName'],
|
||||||
|
"base64Data": a['base64Data'] ?? "",
|
||||||
|
"contentType": a['contentType'],
|
||||||
|
"fileSize": a['fileSize'],
|
||||||
|
"description": a['description'] ?? "",
|
||||||
|
"isActive": a['isActive'] ?? true,
|
||||||
|
};
|
||||||
|
}).toList(),
|
||||||
|
};
|
||||||
|
|
||||||
|
logSafe("💡 Submitting Edited Payment Request: ${jsonEncode(payload)}");
|
||||||
|
|
||||||
|
final success = await ApiService.editExpensePaymentRequestApi(
|
||||||
|
id: payload['id'],
|
||||||
|
title: payload['title'],
|
||||||
|
projectId: payload['projectId'],
|
||||||
|
expenseCategoryId: payload['expenseCategoryId'],
|
||||||
|
amount: payload['amount'],
|
||||||
|
currencyId: payload['currencyId'],
|
||||||
|
description: payload['description'],
|
||||||
|
payee: payload['payee'],
|
||||||
|
dueDate: payload['dueDate'] ?? '',
|
||||||
|
isAdvancePayment: payload['isAdvancePayment'],
|
||||||
|
billAttachments: payload['billAttachments'],
|
||||||
|
);
|
||||||
|
|
||||||
|
logSafe("💡 Edit Payment Request API Response: $success");
|
||||||
|
|
||||||
|
if (success == true) {
|
||||||
|
logSafe("✅ Payment request edited successfully.");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return _errorSnackbar("Failed to edit payment request.");
|
||||||
|
}
|
||||||
|
} catch (e, st) {
|
||||||
|
logSafe("💥 Submit Edited Payment Request Error: $e\n$st",
|
||||||
|
level: LogLevel.error);
|
||||||
|
return _errorSnackbar("Something went wrong. Please try again later.");
|
||||||
|
} finally {
|
||||||
|
isSubmitting.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Submit payment request (Project API style)
|
/// Submit payment request (Project API style)
|
||||||
@ -314,5 +413,6 @@ class AddPaymentRequestController extends GetxController {
|
|||||||
isAdvancePayment.value = false;
|
isAdvancePayment.value = false;
|
||||||
attachments.clear();
|
attachments.clear();
|
||||||
existingAttachments.clear();
|
existingAttachments.clear();
|
||||||
|
removedAttachments.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,9 +21,9 @@ class ApiEndpoints {
|
|||||||
"/Expense/payment-request/filter";
|
"/Expense/payment-request/filter";
|
||||||
static const String updateExpensePaymentRequestStatus =
|
static const String updateExpensePaymentRequestStatus =
|
||||||
"/Expense/payment-request/action";
|
"/Expense/payment-request/action";
|
||||||
static const String createExpenseforPR =
|
static const String createExpenseforPR = "/expense/payment-request/action";
|
||||||
"/expense/payment-request/action";
|
static const String getExpensePaymentRequestEdit =
|
||||||
|
"/expense/payment-request/edit";
|
||||||
|
|
||||||
static const String getDashboardProjectProgress = "/dashboard/progression";
|
static const String getDashboardProjectProgress = "/dashboard/progression";
|
||||||
static const String getDashboardTasks = "/dashboard/tasks";
|
static const String getDashboardTasks = "/dashboard/tasks";
|
||||||
|
|||||||
@ -302,6 +302,69 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Edit Expense Payment Request
|
||||||
|
static Future<bool> editExpensePaymentRequestApi({
|
||||||
|
required String id,
|
||||||
|
required String title,
|
||||||
|
required String description,
|
||||||
|
required String payee,
|
||||||
|
required String currencyId,
|
||||||
|
required double amount,
|
||||||
|
required String dueDate,
|
||||||
|
required String projectId,
|
||||||
|
required String expenseCategoryId,
|
||||||
|
required bool isAdvancePayment,
|
||||||
|
List<Map<String, dynamic>> billAttachments = const [],
|
||||||
|
}) async {
|
||||||
|
final endpoint = "${ApiEndpoints.getExpensePaymentRequestEdit}/$id";
|
||||||
|
|
||||||
|
final body = {
|
||||||
|
"id": id,
|
||||||
|
"title": title,
|
||||||
|
"description": description,
|
||||||
|
"payee": payee,
|
||||||
|
"currencyId": currencyId,
|
||||||
|
"amount": amount,
|
||||||
|
"dueDate": dueDate,
|
||||||
|
"projectId": projectId,
|
||||||
|
"expenseCategoryId": expenseCategoryId,
|
||||||
|
"isAdvancePayment": isAdvancePayment,
|
||||||
|
"billAttachments": billAttachments,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await _putRequest(endpoint, body);
|
||||||
|
|
||||||
|
if (response == null) {
|
||||||
|
logSafe("Edit Expense Payment Request failed: null response",
|
||||||
|
level: LogLevel.error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
logSafe(
|
||||||
|
"Edit Expense Payment Request response status: ${response.statusCode}");
|
||||||
|
logSafe("Edit Expense Payment Request response body: ${response.body}");
|
||||||
|
|
||||||
|
final json = jsonDecode(response.body);
|
||||||
|
if (json['success'] == true) {
|
||||||
|
logSafe(
|
||||||
|
"Expense Payment Request edited successfully: ${json['data'] ?? 'No data'}");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logSafe(
|
||||||
|
"Failed to edit Expense Payment Request: ${json['message'] ?? 'Unknown error'}",
|
||||||
|
level: LogLevel.warning,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (e, stack) {
|
||||||
|
logSafe("Exception during editExpensePaymentRequestApi: $e",
|
||||||
|
level: LogLevel.error);
|
||||||
|
logSafe("StackTrace: $stack", level: LogLevel.debug);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create Expense for Payment Request
|
/// Create Expense for Payment Request
|
||||||
static Future<bool> createExpenseForPRApi({
|
static Future<bool> createExpenseForPRApi({
|
||||||
required String paymentModeId,
|
required String paymentModeId,
|
||||||
|
|||||||
@ -73,17 +73,14 @@ class _PaymentRequestBottomSheetState extends State<_PaymentRequestBottomSheet>
|
|||||||
controller.isAdvancePayment.value = data["isAdvancePayment"] ?? false;
|
controller.isAdvancePayment.value = data["isAdvancePayment"] ?? false;
|
||||||
|
|
||||||
// 🕒 Wait until categories & currencies are loaded before setting them
|
// 🕒 Wait until categories & currencies are loaded before setting them
|
||||||
everAll([
|
everAll([controller.categories, controller.currencies], (_) {
|
||||||
controller.categories,
|
|
||||||
controller.currencies,
|
|
||||||
], (_) {
|
|
||||||
controller.selectedCategory.value = controller.categories
|
controller.selectedCategory.value = controller.categories
|
||||||
.firstWhereOrNull((c) => c.id == data["expenseCategoryId"]);
|
.firstWhereOrNull((c) => c.id == data["expenseCategoryId"]);
|
||||||
controller.selectedCurrency.value = controller.currencies
|
controller.selectedCurrency.value = controller.currencies
|
||||||
.firstWhereOrNull((c) => c.id == data["currencyId"]);
|
.firstWhereOrNull((c) => c.id == data["currencyId"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 🖇 Attachments - Safe parsing (avoids null or wrong type)
|
// 🖇 Attachments
|
||||||
final attachmentsData = data["attachments"];
|
final attachmentsData = data["attachments"];
|
||||||
if (attachmentsData != null &&
|
if (attachmentsData != null &&
|
||||||
attachmentsData is List &&
|
attachmentsData is List &&
|
||||||
@ -91,12 +88,13 @@ class _PaymentRequestBottomSheetState extends State<_PaymentRequestBottomSheet>
|
|||||||
final attachments = attachmentsData
|
final attachments = attachmentsData
|
||||||
.whereType<Map<String, dynamic>>()
|
.whereType<Map<String, dynamic>>()
|
||||||
.map((a) => {
|
.map((a) => {
|
||||||
"id": a["id"],
|
"id": a["documentId"] ?? a["id"], // map documentId to id
|
||||||
"fileName": a["fileName"],
|
"fileName": a["fileName"],
|
||||||
"url": a["url"],
|
"url": a["url"],
|
||||||
"thumbUrl": a["thumbUrl"],
|
"thumbUrl": a["thumbUrl"],
|
||||||
"fileSize": a["fileSize"] ?? 0,
|
"fileSize": a["fileSize"] ?? 0,
|
||||||
"contentType": a["contentType"] ?? "",
|
"contentType": a["contentType"] ?? "",
|
||||||
|
"isActive": true, // ensure active by default
|
||||||
})
|
})
|
||||||
.toList();
|
.toList();
|
||||||
controller.existingAttachments.assignAll(attachments);
|
controller.existingAttachments.assignAll(attachments);
|
||||||
@ -106,7 +104,6 @@ class _PaymentRequestBottomSheetState extends State<_PaymentRequestBottomSheet>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Obx(() => Form(
|
return Obx(() => Form(
|
||||||
@ -120,7 +117,21 @@ class _PaymentRequestBottomSheetState extends State<_PaymentRequestBottomSheet>
|
|||||||
submitText: "Save as Draft",
|
submitText: "Save as Draft",
|
||||||
onSubmit: () async {
|
onSubmit: () async {
|
||||||
if (_formKey.currentState!.validate() && _validateSelections()) {
|
if (_formKey.currentState!.validate() && _validateSelections()) {
|
||||||
final success = await controller.submitPaymentRequest();
|
bool success = false;
|
||||||
|
if (widget.isEdit && widget.existingData != null) {
|
||||||
|
final requestId =
|
||||||
|
widget.existingData!['id']?.toString() ?? '';
|
||||||
|
if (requestId.isNotEmpty) {
|
||||||
|
success = await controller.submitEditedPaymentRequest(
|
||||||
|
requestId: requestId);
|
||||||
|
} else {
|
||||||
|
_showError("Invalid Payment Request ID");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
success = await controller.submitPaymentRequest();
|
||||||
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
Get.back();
|
Get.back();
|
||||||
if (widget.onUpdated != null) widget.onUpdated!();
|
if (widget.onUpdated != null) widget.onUpdated!();
|
||||||
|
|||||||
@ -72,6 +72,7 @@ class _PaymentRequestDetailScreenState extends State<PaymentRequestDetailScreen>
|
|||||||
showPaymentRequestBottomSheet(
|
showPaymentRequestBottomSheet(
|
||||||
isEdit: true,
|
isEdit: true,
|
||||||
existingData: {
|
existingData: {
|
||||||
|
"id": request.id,
|
||||||
"paymentRequestId": request.paymentRequestUID,
|
"paymentRequestId": request.paymentRequestUID,
|
||||||
"title": request.title,
|
"title": request.title,
|
||||||
"projectId": request.project.id,
|
"projectId": request.project.id,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user