Vaibhav_Feature-#768 #59
@ -39,4 +39,34 @@ class ExpenseController extends GetxController {
|
|||||||
isLoading.value = false;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,5 +56,5 @@ class ApiEndpoints {
|
|||||||
static const String getMasterPaymentModes = "/master/payment-modes";
|
static const String getMasterPaymentModes = "/master/payment-modes";
|
||||||
static const String getMasterExpenseStatus = "/master/expenses-status";
|
static const String getMasterExpenseStatus = "/master/expenses-status";
|
||||||
static const String getMasterExpenseTypes = "/master/expenses-types";
|
static const String getMasterExpenseTypes = "/master/expenses-types";
|
||||||
|
static const String updateExpenseStatus = "/expense/action";
|
||||||
}
|
}
|
||||||
|
@ -241,6 +241,51 @@ class ApiService {
|
|||||||
|
|
||||||
// === Expense APIs === //
|
// === 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 {
|
static Future<List<dynamic>?> getExpenseListApi() async {
|
||||||
const endpoint = ApiEndpoints.getExpenseList;
|
const endpoint = ApiEndpoints.getExpenseList;
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:marco/controller/project_controller.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_spacing.dart';
|
||||||
import 'package:marco/helpers/widgets/my_text.dart';
|
import 'package:marco/helpers/widgets/my_text.dart';
|
||||||
import 'package:marco/model/expense/expense_list_model.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 {
|
class ExpenseDetailScreen extends StatelessWidget {
|
||||||
const ExpenseDetailScreen({super.key});
|
const ExpenseDetailScreen({super.key});
|
||||||
@ -31,6 +32,9 @@ class ExpenseDetailScreen extends StatelessWidget {
|
|||||||
final ExpenseModel expense = Get.arguments['expense'] as ExpenseModel;
|
final ExpenseModel expense = Get.arguments['expense'] as ExpenseModel;
|
||||||
final statusColor = getStatusColor(expense.status.name);
|
final statusColor = getStatusColor(expense.status.name);
|
||||||
final projectController = Get.find<ProjectController>();
|
final projectController = Get.find<ProjectController>();
|
||||||
|
final expenseController = Get.find<ExpenseController>();
|
||||||
|
print(
|
||||||
|
"Next Status List: ${expense.nextStatus.map((e) => e.toJson()).toList()}");
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: const Color(0xFFF7F7F7),
|
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: "Payment Mode", value: expense.paymentMode.name),
|
||||||
_DetailRow(
|
_DetailRow(
|
||||||
title: "Paid By",
|
title: "Paid By",
|
||||||
value:
|
value: '${expense.paidBy.firstName} ${expense.paidBy.lastName}'),
|
||||||
'${expense.paidBy.firstName} ${expense.paidBy.lastName}'),
|
|
||||||
_DetailRow(
|
_DetailRow(
|
||||||
title: "Created By",
|
title: "Created By",
|
||||||
value:
|
value:
|
||||||
@ -243,8 +304,7 @@ class _ExpenseDetailsList extends StatelessWidget {
|
|||||||
title: "Next Status",
|
title: "Next Status",
|
||||||
value: expense.nextStatus.map((e) => e.name).join(", ")),
|
value: expense.nextStatus.map((e) => e.name).join(", ")),
|
||||||
_DetailRow(
|
_DetailRow(
|
||||||
title: "Pre-Approved",
|
title: "Pre-Approved", value: expense.preApproved ? "Yes" : "No"),
|
||||||
value: expense.preApproved ? "Yes" : "No"),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user