145 lines
3.8 KiB
Dart
145 lines
3.8 KiB
Dart
import 'dart:async';
|
|
import 'package:get/get.dart';
|
|
import 'package:marco/model/finance/advance_payment_model.dart';
|
|
import 'package:marco/model/finance/get_employee_model.dart';
|
|
import 'package:marco/helpers/services/app_logger.dart';
|
|
import 'package:marco/helpers/services/api_service.dart';
|
|
|
|
class AdvancePaymentController extends GetxController {
|
|
/// Advance payments list
|
|
var payments = <AdvancePayment>[].obs;
|
|
var isLoading = false.obs;
|
|
|
|
/// Employees for dropdown search
|
|
var employees = <Employee>[].obs;
|
|
var allEmployees = <Employee>[]; // cache of last API response
|
|
var employeesLoading = false.obs;
|
|
var searchQuery = ''.obs;
|
|
var selectedEmployee = Rxn<Employee>();
|
|
|
|
/// Prevents unwanted API calls while programmatically updating search
|
|
var _suppressSearch = false.obs;
|
|
|
|
Timer? _debounceTimer;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
|
|
ever<String>(searchQuery, (q) {
|
|
if (_suppressSearch.value) return; // Skip while selecting employee
|
|
|
|
// 🔹 When user types new text, clear previous employee + payments instantly
|
|
if (selectedEmployee.value != null) {
|
|
selectedEmployee.value = null;
|
|
payments.clear();
|
|
}
|
|
|
|
// 🔹 Show fresh dropdown results for new query
|
|
_debounceTimer?.cancel();
|
|
_debounceTimer = Timer(const Duration(milliseconds: 400), () {
|
|
if (q.isNotEmpty) {
|
|
fetchEmployees(q); // repopulate dropdown
|
|
} else {
|
|
employees.clear(); // hide dropdown when search cleared
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
_debounceTimer?.cancel();
|
|
super.onClose();
|
|
}
|
|
|
|
/// Fetch employees by query
|
|
Future<void> fetchEmployees(String q) async {
|
|
if (q.isEmpty) {
|
|
employees.clear();
|
|
return;
|
|
}
|
|
|
|
if (employeesLoading.value) return;
|
|
|
|
try {
|
|
employeesLoading.value = true;
|
|
|
|
final list = await ApiService.getEmployees(query: q);
|
|
final parsed = Employee.listFromJson(list);
|
|
logSafe("✅ Employees fetched from API: ${parsed.map((e) => e.name).toList()}");
|
|
|
|
|
|
// Save full result and filter locally
|
|
allEmployees = parsed;
|
|
_filterEmployees(q);
|
|
} catch (e, s) {
|
|
logSafe("❌ fetchEmployees error: $e\n$s", level: LogLevel.error);
|
|
employees.clear();
|
|
} finally {
|
|
employeesLoading.value = false;
|
|
}
|
|
}
|
|
|
|
/// Local filter to update list based on search text
|
|
void _filterEmployees(String query) {
|
|
final q = query.toLowerCase();
|
|
employees
|
|
..clear()
|
|
..addAll(allEmployees.where((e) {
|
|
return e.name.toLowerCase().contains(q) ||
|
|
e.email.toLowerCase().contains(q);
|
|
}));
|
|
}
|
|
|
|
/// When user selects employee
|
|
void selectEmployee(Employee emp) {
|
|
_suppressSearch.value = true;
|
|
|
|
selectedEmployee.value = emp;
|
|
employees.clear(); // hide dropdown
|
|
searchQuery.value = emp.name;
|
|
|
|
fetchAdvancePayments(emp.id);
|
|
|
|
// Re-enable search after a short delay
|
|
Future.delayed(const Duration(milliseconds: 300), () {
|
|
_suppressSearch.value = false;
|
|
});
|
|
}
|
|
|
|
/// Fetch advance payments for the selected employee
|
|
Future<void> fetchAdvancePayments(String employeeId) async {
|
|
if (employeeId.isEmpty) {
|
|
payments.clear();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
isLoading.value = true;
|
|
final list = await ApiService.getAdvancePayments(employeeId);
|
|
payments.assignAll(list);
|
|
} catch (e, s) {
|
|
logSafe("❌ fetchAdvancePayments error: $e\n$s", level: LogLevel.error);
|
|
payments.clear();
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
|
|
/// Clear employee selection
|
|
void clearSelection() {
|
|
selectedEmployee.value = null;
|
|
payments.clear();
|
|
employees.clear();
|
|
searchQuery.value = '';
|
|
}
|
|
|
|
void resetSelectionOnNewSearch() {
|
|
if (selectedEmployee.value != null) {
|
|
selectedEmployee.value = null;
|
|
payments.clear();
|
|
}
|
|
}
|
|
}
|