262 lines
8.8 KiB
Dart
262 lines
8.8 KiB
Dart
import 'dart:convert';
|
|
import 'package:get/get.dart';
|
|
import 'package:marco/helpers/services/api_service.dart';
|
|
import 'package:marco/helpers/services/app_logger.dart';
|
|
import 'package:marco/model/expense/expense_list_model.dart';
|
|
import 'package:marco/model/expense/payment_types_model.dart';
|
|
import 'package:marco/model/expense/expense_type_model.dart';
|
|
import 'package:marco/model/expense/expense_status_model.dart';
|
|
import 'package:marco/model/employee_model.dart';
|
|
|
|
class ExpenseController extends GetxController {
|
|
final RxList<ExpenseModel> expenses = <ExpenseModel>[].obs;
|
|
final RxBool isLoading = false.obs;
|
|
final RxString errorMessage = ''.obs;
|
|
|
|
// Master data
|
|
final RxList<ExpenseTypeModel> expenseTypes = <ExpenseTypeModel>[].obs;
|
|
final RxList<PaymentModeModel> paymentModes = <PaymentModeModel>[].obs;
|
|
final RxList<ExpenseStatusModel> expenseStatuses = <ExpenseStatusModel>[].obs;
|
|
final RxList<String> globalProjects = <String>[].obs;
|
|
final RxMap<String, String> projectsMap = <String, String>{}.obs;
|
|
RxList<EmployeeModel> allEmployees = <EmployeeModel>[].obs;
|
|
|
|
// Persistent Filter States
|
|
final RxString selectedProject = ''.obs;
|
|
final RxString selectedStatus = ''.obs;
|
|
final Rx<DateTime?> startDate = Rx<DateTime?>(null);
|
|
final Rx<DateTime?> endDate = Rx<DateTime?>(null);
|
|
final RxList<EmployeeModel> selectedPaidByEmployees = <EmployeeModel>[].obs;
|
|
final RxList<EmployeeModel> selectedCreatedByEmployees =
|
|
<EmployeeModel>[].obs;
|
|
final RxString selectedDateType = 'Transaction Date'.obs;
|
|
|
|
final List<String> dateTypes = [
|
|
'Transaction Date',
|
|
'Created At',
|
|
];
|
|
|
|
int _pageSize = 20;
|
|
int _pageNumber = 1;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
loadInitialMasterData();
|
|
fetchAllEmployees();
|
|
}
|
|
|
|
bool get isFilterApplied {
|
|
return selectedProject.value.isNotEmpty ||
|
|
selectedStatus.value.isNotEmpty ||
|
|
startDate.value != null ||
|
|
endDate.value != null ||
|
|
selectedPaidByEmployees.isNotEmpty ||
|
|
selectedCreatedByEmployees.isNotEmpty;
|
|
}
|
|
|
|
/// Load master data
|
|
Future<void> loadInitialMasterData() async {
|
|
await fetchGlobalProjects();
|
|
await fetchMasterData();
|
|
}
|
|
|
|
Future<void> deleteExpense(String expenseId) async {
|
|
try {
|
|
logSafe("Attempting to delete expense: $expenseId");
|
|
final success = await ApiService.deleteExpense(expenseId);
|
|
if (success) {
|
|
expenses.removeWhere((e) => e.id == expenseId);
|
|
logSafe("Expense deleted successfully.");
|
|
Get.snackbar("Deleted", "Expense has been deleted successfully.");
|
|
} else {
|
|
logSafe("Failed to delete expense: $expenseId", level: LogLevel.error);
|
|
Get.snackbar("Failed", "Failed to delete expense.");
|
|
}
|
|
} catch (e, stack) {
|
|
logSafe("Exception in deleteExpense: $e", level: LogLevel.error);
|
|
logSafe("StackTrace: $stack", level: LogLevel.debug);
|
|
Get.snackbar("Error", "Something went wrong while deleting.");
|
|
}
|
|
}
|
|
|
|
/// Fetch expenses using filters
|
|
Future<void> fetchExpenses({
|
|
List<String>? projectIds,
|
|
List<String>? statusIds,
|
|
List<String>? createdByIds,
|
|
List<String>? paidByIds,
|
|
DateTime? startDate,
|
|
DateTime? endDate,
|
|
int pageSize = 20,
|
|
int pageNumber = 1,
|
|
}) async {
|
|
isLoading.value = true;
|
|
errorMessage.value = '';
|
|
|
|
_pageSize = pageSize;
|
|
_pageNumber = pageNumber;
|
|
|
|
final Map<String, dynamic> filterMap = {
|
|
"projectIds": projectIds ??
|
|
(selectedProject.value.isEmpty
|
|
? []
|
|
: [projectsMap[selectedProject.value] ?? '']),
|
|
"statusIds": statusIds ??
|
|
(selectedStatus.value.isEmpty ? [] : [selectedStatus.value]),
|
|
"createdByIds":
|
|
createdByIds ?? selectedCreatedByEmployees.map((e) => e.id).toList(),
|
|
"paidByIds":
|
|
paidByIds ?? selectedPaidByEmployees.map((e) => e.id).toList(),
|
|
"startDate": (startDate ?? this.startDate.value)?.toIso8601String(),
|
|
"endDate": (endDate ?? this.endDate.value)?.toIso8601String(),
|
|
"isTransactionDate": selectedDateType.value == 'Transaction Date',
|
|
};
|
|
|
|
try {
|
|
logSafe("Fetching expenses with filter: ${jsonEncode(filterMap)}");
|
|
|
|
final result = await ApiService.getExpenseListApi(
|
|
filter: jsonEncode(filterMap),
|
|
pageSize: _pageSize,
|
|
pageNumber: _pageNumber,
|
|
);
|
|
|
|
if (result != null) {
|
|
try {
|
|
final expenseResponse = ExpenseResponse.fromJson(result);
|
|
expenses.assignAll(expenseResponse.data.data);
|
|
|
|
logSafe("Expenses loaded: ${expenses.length}");
|
|
logSafe(
|
|
"Pagination Info: Page ${expenseResponse.data.currentPage} of ${expenseResponse.data.totalPages} | Total: ${expenseResponse.data.totalEntites}");
|
|
} catch (e) {
|
|
errorMessage.value = 'Failed to parse expenses: $e';
|
|
logSafe("Parse error in fetchExpenses: $e", level: LogLevel.error);
|
|
}
|
|
} else {
|
|
errorMessage.value = 'Failed to fetch expenses from server.';
|
|
logSafe("fetchExpenses failed: null response", level: LogLevel.error);
|
|
}
|
|
} catch (e, stack) {
|
|
errorMessage.value = 'An unexpected error occurred.';
|
|
logSafe("Exception in fetchExpenses: $e", level: LogLevel.error);
|
|
logSafe("StackTrace: $stack", level: LogLevel.debug);
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
|
|
/// Clear all filters
|
|
void clearFilters() {
|
|
selectedProject.value = '';
|
|
selectedStatus.value = '';
|
|
startDate.value = null;
|
|
endDate.value = null;
|
|
selectedPaidByEmployees.clear();
|
|
selectedCreatedByEmployees.clear();
|
|
}
|
|
|
|
/// Fetch master data: expense types, payment modes, and expense status
|
|
Future<void> fetchMasterData() async {
|
|
try {
|
|
final expenseTypesData = await ApiService.getMasterExpenseTypes();
|
|
if (expenseTypesData is List) {
|
|
expenseTypes.value =
|
|
expenseTypesData.map((e) => ExpenseTypeModel.fromJson(e)).toList();
|
|
}
|
|
|
|
final paymentModesData = await ApiService.getMasterPaymentModes();
|
|
if (paymentModesData is List) {
|
|
paymentModes.value =
|
|
paymentModesData.map((e) => PaymentModeModel.fromJson(e)).toList();
|
|
}
|
|
|
|
final expenseStatusData = await ApiService.getMasterExpenseStatus();
|
|
if (expenseStatusData is List) {
|
|
expenseStatuses.value = expenseStatusData
|
|
.map((e) => ExpenseStatusModel.fromJson(e))
|
|
.toList();
|
|
}
|
|
} catch (e) {
|
|
Get.snackbar("Error", "Failed to fetch master data: $e");
|
|
}
|
|
}
|
|
|
|
/// Fetch global projects
|
|
Future<void> fetchGlobalProjects() async {
|
|
try {
|
|
final response = await ApiService.getGlobalProjects();
|
|
if (response != null) {
|
|
final names = <String>[];
|
|
for (var item in response) {
|
|
final name = item['name']?.toString().trim();
|
|
final id = item['id']?.toString().trim();
|
|
if (name != null && id != null && name.isNotEmpty) {
|
|
projectsMap[name] = id;
|
|
names.add(name);
|
|
}
|
|
}
|
|
globalProjects.assignAll(names);
|
|
logSafe("Fetched ${names.length} global projects");
|
|
}
|
|
} catch (e) {
|
|
logSafe("Failed to fetch global projects: $e", level: LogLevel.error);
|
|
}
|
|
}
|
|
|
|
/// Fetch all employees
|
|
Future<void> fetchAllEmployees() async {
|
|
isLoading.value = true;
|
|
try {
|
|
final response = await ApiService.getAllEmployees();
|
|
if (response != null && response.isNotEmpty) {
|
|
allEmployees
|
|
.assignAll(response.map((json) => EmployeeModel.fromJson(json)));
|
|
logSafe(
|
|
"All Employees fetched for Manage Bucket: ${allEmployees.length}",
|
|
level: LogLevel.info,
|
|
);
|
|
} else {
|
|
allEmployees.clear();
|
|
logSafe("No employees found for Manage Bucket.",
|
|
level: LogLevel.warning);
|
|
}
|
|
} catch (e) {
|
|
allEmployees.clear();
|
|
logSafe("Error fetching employees in Manage Bucket",
|
|
level: LogLevel.error, error: e);
|
|
}
|
|
isLoading.value = false;
|
|
update();
|
|
}
|
|
|
|
/// Update expense status
|
|
Future<bool> updateExpenseStatus(String expenseId, String statusId) async {
|
|
isLoading.value = true;
|
|
errorMessage.value = '';
|
|
try {
|
|
logSafe("Updating status for expense: $expenseId -> $statusId");
|
|
final success = await ApiService.updateExpenseStatusApi(
|
|
expenseId: expenseId,
|
|
statusId: statusId,
|
|
);
|
|
if (success) {
|
|
logSafe("Expense status updated successfully.");
|
|
await fetchExpenses();
|
|
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);
|
|
return false;
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
}
|