Vaibhav_Feature-#768 #59
@ -33,18 +33,21 @@ class ExpenseDetailController extends GetxController {
|
|||||||
try {
|
try {
|
||||||
logSafe("Fetching expense details for ID: $_expenseId");
|
logSafe("Fetching expense details for ID: $_expenseId");
|
||||||
|
|
||||||
final result = await ApiService.getExpenseDetailsApi(expenseId: _expenseId);
|
final result =
|
||||||
|
await ApiService.getExpenseDetailsApi(expenseId: _expenseId);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
try {
|
try {
|
||||||
expense.value = ExpenseDetailModel.fromJson(result);
|
expense.value = ExpenseDetailModel.fromJson(result);
|
||||||
logSafe("Expense details loaded successfully: ${expense.value?.id}");
|
logSafe("Expense details loaded successfully: ${expense.value?.id}");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errorMessage.value = 'Failed to parse expense details: $e';
|
errorMessage.value = 'Failed to parse expense details: $e';
|
||||||
logSafe("Parse error in fetchExpenseDetails: $e", level: LogLevel.error);
|
logSafe("Parse error in fetchExpenseDetails: $e",
|
||||||
|
level: LogLevel.error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
errorMessage.value = 'Failed to fetch expense details from server.';
|
errorMessage.value = 'Failed to fetch expense details from server.';
|
||||||
logSafe("fetchExpenseDetails failed: null response", level: LogLevel.error);
|
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.';
|
||||||
@ -55,6 +58,27 @@ class ExpenseDetailController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In ExpenseDetailController
|
||||||
|
List<String> 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)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
if (permissionData is String) {
|
||||||
|
final clean = permissionData.replaceAll(RegExp(r'[\[\]]'), '');
|
||||||
|
return clean
|
||||||
|
.split(',')
|
||||||
|
.map((e) => e.trim())
|
||||||
|
.where((e) => e.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch all employees
|
/// Fetch all employees
|
||||||
Future<void> fetchAllEmployees() async {
|
Future<void> fetchAllEmployees() async {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
@ -64,7 +88,8 @@ class ExpenseDetailController extends GetxController {
|
|||||||
final response = await ApiService.getAllEmployees();
|
final response = await ApiService.getAllEmployees();
|
||||||
if (response != null && response.isNotEmpty) {
|
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}", level: LogLevel.info);
|
logSafe("All Employees fetched: ${allEmployees.length}",
|
||||||
|
level: LogLevel.info);
|
||||||
} else {
|
} else {
|
||||||
allEmployees.clear();
|
allEmployees.clear();
|
||||||
logSafe("No employees found.", level: LogLevel.warning);
|
logSafe("No employees found.", level: LogLevel.warning);
|
||||||
@ -103,7 +128,7 @@ class ExpenseDetailController extends GetxController {
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
logSafe("Reimbursement submitted successfully.");
|
logSafe("Reimbursement submitted successfully.");
|
||||||
await fetchExpenseDetails(); // refresh latest
|
await fetchExpenseDetails();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
errorMessage.value = "Failed to submit reimbursement.";
|
errorMessage.value = "Failed to submit reimbursement.";
|
||||||
@ -111,7 +136,8 @@ class ExpenseDetailController extends GetxController {
|
|||||||
}
|
}
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
errorMessage.value = 'An unexpected error occurred.';
|
errorMessage.value = 'An unexpected error occurred.';
|
||||||
logSafe("Exception in updateExpenseStatusWithReimbursement: $e", level: LogLevel.error);
|
logSafe("Exception in updateExpenseStatusWithReimbursement: $e",
|
||||||
|
level: LogLevel.error);
|
||||||
logSafe("StackTrace: $stack", level: LogLevel.debug);
|
logSafe("StackTrace: $stack", level: LogLevel.debug);
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
@ -134,7 +160,7 @@ class ExpenseDetailController extends GetxController {
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
logSafe("Expense status updated successfully.");
|
logSafe("Expense status updated successfully.");
|
||||||
await fetchExpenseDetails(); // refresh
|
await fetchExpenseDetails();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
errorMessage.value = "Failed to update expense status.";
|
errorMessage.value = "Failed to update expense status.";
|
||||||
|
@ -117,6 +117,23 @@ class PermissionController extends GetxController {
|
|||||||
return assigned;
|
return assigned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> get allowedPermissionIds {
|
||||||
|
final ids = permissions.map((p) => p.id).toList();
|
||||||
|
logSafe("[PermissionController] Allowed Permission IDs: $ids",
|
||||||
|
level: LogLevel.debug);
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasAnyPermission(List<String> ids) {
|
||||||
|
logSafe("[PermissionController] Checking if any of these are allowed: $ids",
|
||||||
|
level: LogLevel.debug);
|
||||||
|
final allowed = allowedPermissionIds;
|
||||||
|
final result = ids.any((id) => allowed.contains(id));
|
||||||
|
logSafe("[PermissionController] Permission match result: $result",
|
||||||
|
level: LogLevel.debug);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
_refreshTimer?.cancel();
|
_refreshTimer?.cancel();
|
||||||
|
@ -5,8 +5,6 @@ class DateTimeUtils {
|
|||||||
/// Converts a UTC datetime string to local time and formats it.
|
/// Converts a UTC datetime string to local time and formats it.
|
||||||
static String convertUtcToLocal(String utcTimeString, {String format = 'dd-MM-yyyy'}) {
|
static String convertUtcToLocal(String utcTimeString, {String format = 'dd-MM-yyyy'}) {
|
||||||
try {
|
try {
|
||||||
logSafe('convertUtcToLocal: input="$utcTimeString", format="$format"');
|
|
||||||
|
|
||||||
final parsed = DateTime.parse(utcTimeString);
|
final parsed = DateTime.parse(utcTimeString);
|
||||||
final utcDateTime = DateTime.utc(
|
final utcDateTime = DateTime.utc(
|
||||||
parsed.year,
|
parsed.year,
|
||||||
@ -18,13 +16,10 @@ class DateTimeUtils {
|
|||||||
parsed.millisecond,
|
parsed.millisecond,
|
||||||
parsed.microsecond,
|
parsed.microsecond,
|
||||||
);
|
);
|
||||||
logSafe('Parsed (assumed UTC): $utcDateTime');
|
|
||||||
|
|
||||||
final localDateTime = utcDateTime.toLocal();
|
final localDateTime = utcDateTime.toLocal();
|
||||||
logSafe('Converted to Local: $localDateTime');
|
|
||||||
|
|
||||||
final formatted = _formatDateTime(localDateTime, format: format);
|
final formatted = _formatDateTime(localDateTime, format: format);
|
||||||
logSafe('Formatted Local Time: $formatted');
|
|
||||||
|
|
||||||
return formatted;
|
return formatted;
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
|
@ -3,6 +3,8 @@ import 'package:get/get.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:marco/controller/expense/expense_detail_controller.dart';
|
import 'package:marco/controller/expense/expense_detail_controller.dart';
|
||||||
import 'package:marco/controller/project_controller.dart';
|
import 'package:marco/controller/project_controller.dart';
|
||||||
|
import 'package:marco/controller/permission_controller.dart';
|
||||||
|
|
||||||
import 'package:marco/helpers/utils/date_time_utils.dart';
|
import 'package:marco/helpers/utils/date_time_utils.dart';
|
||||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||||
import 'package:marco/helpers/widgets/my_text.dart';
|
import 'package:marco/helpers/widgets/my_text.dart';
|
||||||
@ -40,6 +42,7 @@ class ExpenseDetailScreen extends StatelessWidget {
|
|||||||
final controller = Get.put(ExpenseDetailController());
|
final controller = Get.put(ExpenseDetailController());
|
||||||
final projectController = Get.find<ProjectController>();
|
final projectController = Get.find<ProjectController>();
|
||||||
controller.init(expenseId);
|
controller.init(expenseId);
|
||||||
|
final permissionController = Get.find<PermissionController>();
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: const Color(0xFFF7F7F7),
|
backgroundColor: const Color(0xFFF7F7F7),
|
||||||
@ -169,7 +172,11 @@ class ExpenseDetailScreen extends StatelessWidget {
|
|||||||
alignment: WrapAlignment.center,
|
alignment: WrapAlignment.center,
|
||||||
spacing: 10,
|
spacing: 10,
|
||||||
runSpacing: 10,
|
runSpacing: 10,
|
||||||
children: expense.nextStatus.map((next) {
|
children: expense.nextStatus.where((next) {
|
||||||
|
return permissionController.hasAnyPermission(
|
||||||
|
controller.parsePermissionIds(next.permissionIds),
|
||||||
|
);
|
||||||
|
}).map((next) {
|
||||||
Color buttonColor = Colors.red;
|
Color buttonColor = Colors.red;
|
||||||
if (next.color.isNotEmpty) {
|
if (next.color.isNotEmpty) {
|
||||||
try {
|
try {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user