refactor: streamline API call handling in ExpenseDetailController and enhance error logging

This commit is contained in:
Vaibhav Surve 2025-07-31 16:57:15 +05:30
parent 31966f4bc5
commit 3427c5bd26

View File

@ -11,9 +11,8 @@ class ExpenseDetailController extends GetxController {
final Rx<EmployeeModel?> selectedReimbursedBy = Rx<EmployeeModel?>(null); final Rx<EmployeeModel?> selectedReimbursedBy = Rx<EmployeeModel?>(null);
final RxList<EmployeeModel> allEmployees = <EmployeeModel>[].obs; final RxList<EmployeeModel> allEmployees = <EmployeeModel>[].obs;
bool _isInitialized = false;
late String _expenseId; late String _expenseId;
bool _isInitialized = false;
/// Call this once from the screen (NOT inside build) to initialize /// Call this once from the screen (NOT inside build) to initialize
void init(String expenseId) { void init(String expenseId) {
if (_isInitialized) return; if (_isInitialized) return;
@ -21,48 +20,61 @@ class ExpenseDetailController extends GetxController {
_isInitialized = true; _isInitialized = true;
_expenseId = expenseId; _expenseId = expenseId;
fetchExpenseDetails(); // Use Future.wait to fetch details and employees concurrently
fetchAllEmployees(); Future.wait([
fetchExpenseDetails(),
fetchAllEmployees(),
]);
} }
/// Fetch expense details by stored ID /// Generic method to handle API calls with loading and error states
Future<void> fetchExpenseDetails() async { Future<T?> _apiCallWrapper<T>(
Future<T?> Function() apiCall, String operationName) async {
isLoading.value = true; isLoading.value = true;
errorMessage.value = ''; errorMessage.value = ''; // Clear previous errors
try { try {
logSafe("Fetching expense details for ID: $_expenseId"); logSafe("Initiating $operationName...");
final result = await apiCall();
final result = logSafe("$operationName completed successfully.");
await ApiService.getExpenseDetailsApi(expenseId: _expenseId); return result;
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);
}
} catch (e, stack) { } catch (e, stack) {
errorMessage.value = 'An unexpected error occurred.'; errorMessage.value = 'An unexpected error occurred during $operationName.';
logSafe("Exception in fetchExpenseDetails: $e", level: LogLevel.error); logSafe("Exception in $operationName: $e", level: LogLevel.error);
logSafe("StackTrace: $stack", level: LogLevel.debug); logSafe("StackTrace: $stack", level: LogLevel.debug);
return null;
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }
} }
// In ExpenseDetailController /// Fetch expense details by stored ID
Future<void> 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<String> parsePermissionIds(dynamic permissionData) { List<String> parsePermissionIds(dynamic permissionData) {
if (permissionData == null) return []; if (permissionData == null) return [];
if (permissionData is List) { if (permissionData is List) {
// If it's already a List, return a list of Strings.
return permissionData return permissionData
.map((e) => e.toString().trim()) .map((e) => e.toString().trim())
.where((e) => e.isNotEmpty) .where((e) => e.isNotEmpty)
@ -81,26 +93,24 @@ class ExpenseDetailController extends GetxController {
/// Fetch all employees /// Fetch all employees
Future<void> fetchAllEmployees() async { Future<void> fetchAllEmployees() async {
isLoading.value = true; final response = await _apiCallWrapper(
errorMessage.value = ''; () => ApiService.getAllEmployees(), "fetch all employees");
try { if (response != null && response.isNotEmpty) {
final response = await ApiService.getAllEmployees(); try {
if (response != null && response.isNotEmpty) {
allEmployees.assignAll(response.map((e) => EmployeeModel.fromJson(e))); allEmployees.assignAll(response.map((e) => EmployeeModel.fromJson(e)));
logSafe("All Employees fetched: ${allEmployees.length}", logSafe("All Employees fetched: ${allEmployees.length}",
level: LogLevel.info); level: LogLevel.info);
} else { } catch (e) {
allEmployees.clear(); errorMessage.value = 'Failed to parse employee data: $e';
logSafe("No employees found.", level: LogLevel.warning); logSafe("Parse error in fetchAllEmployees: $e", level: LogLevel.error);
} }
} catch (e) { } else {
allEmployees.clear(); allEmployees.clear();
logSafe("Error fetching employees", level: LogLevel.error, error: e); logSafe("No employees found.", level: LogLevel.warning);
} finally {
isLoading.value = false;
update();
} }
// `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 /// Update expense with reimbursement info and status
@ -111,68 +121,43 @@ class ExpenseDetailController extends GetxController {
required String reimburseById, required String reimburseById,
required String statusId, required String statusId,
}) async { }) async {
isLoading.value = true; final success = await _apiCallWrapper(
errorMessage.value = ''; () => ApiService.updateExpenseStatusApi(
try {
logSafe("Submitting reimbursement for expense: $_expenseId");
final success = await ApiService.updateExpenseStatusApi(
expenseId: _expenseId, expenseId: _expenseId,
statusId: statusId, statusId: statusId,
comment: comment, comment: comment,
reimburseTransactionId: reimburseTransactionId, reimburseTransactionId: reimburseTransactionId,
reimburseDate: reimburseDate, reimburseDate: reimburseDate,
reimbursedById: reimburseById, reimbursedById: reimburseById,
); ),
"submit reimbursement",
);
if (success) { if (success == true) { // Explicitly check for true as _apiCallWrapper returns T?
logSafe("Reimbursement submitted successfully."); await fetchExpenseDetails(); // Refresh details after successful update
await fetchExpenseDetails(); return true;
return true; } else {
} else { errorMessage.value = "Failed to submit reimbursement.";
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);
return false; return false;
} finally {
isLoading.value = false;
} }
} }
/// Update status for this specific expense /// Update status for this specific expense
Future<bool> updateExpenseStatus(String statusId) async { Future<bool> updateExpenseStatus(String statusId) async {
isLoading.value = true; final success = await _apiCallWrapper(
errorMessage.value = ''; () => ApiService.updateExpenseStatusApi(
try {
logSafe("Updating status for expense: $_expenseId -> $statusId");
final success = await ApiService.updateExpenseStatusApi(
expenseId: _expenseId, expenseId: _expenseId,
statusId: statusId, statusId: statusId,
); ),
"update expense status",
);
if (success) { if (success == true) { // Explicitly check for true as _apiCallWrapper returns T?
logSafe("Expense status updated successfully."); await fetchExpenseDetails(); // Refresh details after successful update
await fetchExpenseDetails(); return true;
return true; } else {
} else { errorMessage.value = "Failed to update expense status.";
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);
return false; return false;
} finally {
isLoading.value = false;
} }
} }
} }