feat(expense): implement update expense status functionality and UI integration
This commit is contained in:
parent
debc12bc1b
commit
e0ed35a671
@ -39,4 +39,34 @@ class ExpenseController extends GetxController {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Update expense status and refresh the list
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,5 +59,5 @@ class ApiEndpoints {
|
||||
static const String getMasterPaymentModes = "/master/payment-modes";
|
||||
static const String getMasterExpenseStatus = "/master/expenses-status";
|
||||
static const String getMasterExpenseTypes = "/master/expenses-types";
|
||||
|
||||
static const String updateExpenseStatus = "/expense/action";
|
||||
}
|
||||
|
@ -241,6 +241,51 @@ class ApiService {
|
||||
|
||||
// === Expense APIs === //
|
||||
|
||||
/// Update Expense Status API
|
||||
static Future<bool> updateExpenseStatusApi({
|
||||
required String expenseId,
|
||||
required String statusId,
|
||||
}) async {
|
||||
final payload = {
|
||||
"expenseId": expenseId,
|
||||
"statusId": statusId,
|
||||
};
|
||||
|
||||
const endpoint = ApiEndpoints.updateExpenseStatus;
|
||||
logSafe("Updating expense status with payload: $payload");
|
||||
|
||||
try {
|
||||
final response =
|
||||
await _postRequest(endpoint, payload, customTimeout: extendedTimeout);
|
||||
|
||||
if (response == null) {
|
||||
logSafe("Update expense status failed: null response",
|
||||
level: LogLevel.error);
|
||||
return false;
|
||||
}
|
||||
|
||||
logSafe("Update expense status response status: ${response.statusCode}");
|
||||
logSafe("Update expense status response body: ${response.body}");
|
||||
|
||||
final json = jsonDecode(response.body);
|
||||
if (json['success'] == true) {
|
||||
logSafe("Expense status updated successfully: ${json['data']}");
|
||||
return true;
|
||||
} else {
|
||||
logSafe(
|
||||
"Failed to update expense status: ${json['message'] ?? 'Unknown error'}",
|
||||
level: LogLevel.warning,
|
||||
);
|
||||
}
|
||||
} catch (e, stack) {
|
||||
logSafe("Exception during updateExpenseStatus API: $e",
|
||||
level: LogLevel.error);
|
||||
logSafe("StackTrace: $stack", level: LogLevel.debug);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static Future<List<dynamic>?> getExpenseListApi() async {
|
||||
const endpoint = ApiEndpoints.getExpenseList;
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
import 'package:marco/controller/expense/expense_screen_controller.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/model/expense/expense_list_model.dart';
|
||||
import 'package:marco/helpers/utils/date_time_utils.dart'; // Import DateTimeUtils
|
||||
import 'package:marco/helpers/utils/date_time_utils.dart';
|
||||
|
||||
class ExpenseDetailScreen extends StatelessWidget {
|
||||
const ExpenseDetailScreen({super.key});
|
||||
@ -31,6 +32,9 @@ class ExpenseDetailScreen extends StatelessWidget {
|
||||
final ExpenseModel expense = Get.arguments['expense'] as ExpenseModel;
|
||||
final statusColor = getStatusColor(expense.status.name);
|
||||
final projectController = Get.find<ProjectController>();
|
||||
final expenseController = Get.find<ExpenseController>();
|
||||
print(
|
||||
"Next Status List: ${expense.nextStatus.map((e) => e.toJson()).toList()}");
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF7F7F7),
|
||||
@ -110,6 +114,64 @@ class ExpenseDetailScreen extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: expense.nextStatus.isNotEmpty
|
||||
? SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
alignment: WrapAlignment.center,
|
||||
children: expense.nextStatus.map((next) {
|
||||
return ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size(100, 40),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8, horizontal: 12),
|
||||
backgroundColor: Colors.red,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
final success =
|
||||
await expenseController.updateExpenseStatus(
|
||||
expense.id,
|
||||
next.id,
|
||||
);
|
||||
|
||||
if (success) {
|
||||
Get.snackbar(
|
||||
'Success',
|
||||
'Expense moved to ${next.name}',
|
||||
backgroundColor: Colors.green.withOpacity(0.8),
|
||||
colorText: Colors.white,
|
||||
);
|
||||
Get.back(result: true);
|
||||
} else {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Failed to update status.',
|
||||
backgroundColor: Colors.red.withOpacity(0.8),
|
||||
colorText: Colors.white,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
next.name,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -228,8 +290,7 @@ class _ExpenseDetailsList extends StatelessWidget {
|
||||
_DetailRow(title: "Payment Mode", value: expense.paymentMode.name),
|
||||
_DetailRow(
|
||||
title: "Paid By",
|
||||
value:
|
||||
'${expense.paidBy.firstName} ${expense.paidBy.lastName}'),
|
||||
value: '${expense.paidBy.firstName} ${expense.paidBy.lastName}'),
|
||||
_DetailRow(
|
||||
title: "Created By",
|
||||
value:
|
||||
@ -243,8 +304,7 @@ class _ExpenseDetailsList extends StatelessWidget {
|
||||
title: "Next Status",
|
||||
value: expense.nextStatus.map((e) => e.name).join(", ")),
|
||||
_DetailRow(
|
||||
title: "Pre-Approved",
|
||||
value: expense.preApproved ? "Yes" : "No"),
|
||||
title: "Pre-Approved", value: expense.preApproved ? "Yes" : "No"),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user