diff --git a/lib/controller/expense/expense_detail_controller.dart b/lib/controller/expense/expense_detail_controller.dart index 88b71cd..23d3d4e 100644 --- a/lib/controller/expense/expense_detail_controller.dart +++ b/lib/controller/expense/expense_detail_controller.dart @@ -11,9 +11,8 @@ class ExpenseDetailController extends GetxController { final Rx selectedReimbursedBy = Rx(null); final RxList allEmployees = [].obs; - bool _isInitialized = false; late String _expenseId; - + bool _isInitialized = false; /// Call this once from the screen (NOT inside build) to initialize void init(String expenseId) { if (_isInitialized) return; @@ -21,48 +20,61 @@ class ExpenseDetailController extends GetxController { _isInitialized = true; _expenseId = expenseId; - fetchExpenseDetails(); - fetchAllEmployees(); + // Use Future.wait to fetch details and employees concurrently + Future.wait([ + fetchExpenseDetails(), + fetchAllEmployees(), + ]); } - /// Fetch expense details by stored ID - Future fetchExpenseDetails() async { + /// Generic method to handle API calls with loading and error states + Future _apiCallWrapper( + Future Function() apiCall, String operationName) async { isLoading.value = true; - errorMessage.value = ''; + errorMessage.value = ''; // Clear previous errors try { - logSafe("Fetching expense details for ID: $_expenseId"); - - final result = - await ApiService.getExpenseDetailsApi(expenseId: _expenseId); - if (result != null) { - try { - expense.value = ExpenseDetailModel.fromJson(result); - logSafe("Expense details loaded successfully: ${expense.value?.id}"); - } catch (e) { - errorMessage.value = 'Failed to parse expense details: $e'; - logSafe("Parse error in fetchExpenseDetails: $e", - level: LogLevel.error); - } - } else { - errorMessage.value = 'Failed to fetch expense details from server.'; - logSafe("fetchExpenseDetails failed: null response", - level: LogLevel.error); - } + logSafe("Initiating $operationName..."); + final result = await apiCall(); + logSafe("$operationName completed successfully."); + return result; } catch (e, stack) { - errorMessage.value = 'An unexpected error occurred.'; - logSafe("Exception in fetchExpenseDetails: $e", level: LogLevel.error); + errorMessage.value = 'An unexpected error occurred during $operationName.'; + logSafe("Exception in $operationName: $e", level: LogLevel.error); logSafe("StackTrace: $stack", level: LogLevel.debug); + return null; } finally { isLoading.value = false; } } -// In ExpenseDetailController + /// Fetch expense details by stored ID + Future fetchExpenseDetails() async { + final result = await _apiCallWrapper( + () => ApiService.getExpenseDetailsApi(expenseId: _expenseId), + "fetch expense details"); + + if (result != null) { + try { + expense.value = ExpenseDetailModel.fromJson(result); + logSafe("Expense details loaded successfully: ${expense.value?.id}"); + } catch (e) { + errorMessage.value = 'Failed to parse expense details: $e'; + logSafe("Parse error in fetchExpenseDetails: $e", + level: LogLevel.error); + } + } else { + errorMessage.value = 'Failed to fetch expense details from server.'; + logSafe("fetchExpenseDetails failed: null response", + level: LogLevel.error); + } + } + + // This method seems like a utility and might be better placed in a helper or utility class + // if it's used across multiple controllers. Keeping it here for now as per original code. List parsePermissionIds(dynamic permissionData) { if (permissionData == null) return []; if (permissionData is List) { - // If it's already a List, return a list of Strings. return permissionData .map((e) => e.toString().trim()) .where((e) => e.isNotEmpty) @@ -81,26 +93,24 @@ class ExpenseDetailController extends GetxController { /// Fetch all employees Future fetchAllEmployees() async { - isLoading.value = true; - errorMessage.value = ''; + final response = await _apiCallWrapper( + () => ApiService.getAllEmployees(), "fetch all employees"); - try { - final response = await ApiService.getAllEmployees(); - if (response != null && response.isNotEmpty) { + if (response != null && response.isNotEmpty) { + try { allEmployees.assignAll(response.map((e) => EmployeeModel.fromJson(e))); logSafe("All Employees fetched: ${allEmployees.length}", level: LogLevel.info); - } else { - allEmployees.clear(); - logSafe("No employees found.", level: LogLevel.warning); + } catch (e) { + errorMessage.value = 'Failed to parse employee data: $e'; + logSafe("Parse error in fetchAllEmployees: $e", level: LogLevel.error); } - } catch (e) { + } else { allEmployees.clear(); - logSafe("Error fetching employees", level: LogLevel.error, error: e); - } finally { - isLoading.value = false; - update(); + logSafe("No employees found.", level: LogLevel.warning); } + // `update()` is typically not needed for RxList directly unless you have specific GetBuilder/Obx usage that requires it + // If you are using Obx widgets, `allEmployees.assignAll` will automatically trigger a rebuild. } /// Update expense with reimbursement info and status @@ -111,68 +121,43 @@ class ExpenseDetailController extends GetxController { required String reimburseById, required String statusId, }) async { - isLoading.value = true; - errorMessage.value = ''; - - try { - logSafe("Submitting reimbursement for expense: $_expenseId"); - - final success = await ApiService.updateExpenseStatusApi( + final success = await _apiCallWrapper( + () => ApiService.updateExpenseStatusApi( expenseId: _expenseId, statusId: statusId, comment: comment, reimburseTransactionId: reimburseTransactionId, reimburseDate: reimburseDate, reimbursedById: reimburseById, - ); + ), + "submit reimbursement", + ); - if (success) { - logSafe("Reimbursement submitted successfully."); - await fetchExpenseDetails(); - return true; - } else { - errorMessage.value = "Failed to submit reimbursement."; - return false; - } - } catch (e, stack) { - errorMessage.value = 'An unexpected error occurred.'; - logSafe("Exception in updateExpenseStatusWithReimbursement: $e", - level: LogLevel.error); - logSafe("StackTrace: $stack", level: LogLevel.debug); + if (success == true) { // Explicitly check for true as _apiCallWrapper returns T? + await fetchExpenseDetails(); // Refresh details after successful update + return true; + } else { + errorMessage.value = "Failed to submit reimbursement."; return false; - } finally { - isLoading.value = false; } } /// Update status for this specific expense Future updateExpenseStatus(String statusId) async { - isLoading.value = true; - errorMessage.value = ''; - - try { - logSafe("Updating status for expense: $_expenseId -> $statusId"); - - final success = await ApiService.updateExpenseStatusApi( + final success = await _apiCallWrapper( + () => ApiService.updateExpenseStatusApi( expenseId: _expenseId, statusId: statusId, - ); + ), + "update expense status", + ); - if (success) { - logSafe("Expense status updated successfully."); - await fetchExpenseDetails(); - return true; - } else { - errorMessage.value = "Failed to update expense status."; - return false; - } - } catch (e, stack) { - errorMessage.value = 'An unexpected error occurred.'; - logSafe("Exception in updateExpenseStatus: $e", level: LogLevel.error); - logSafe("StackTrace: $stack", level: LogLevel.debug); + if (success == true) { // Explicitly check for true as _apiCallWrapper returns T? + await fetchExpenseDetails(); // Refresh details after successful update + return true; + } else { + errorMessage.value = "Failed to update expense status."; return false; - } finally { - isLoading.value = false; } } -} +} \ No newline at end of file