diff --git a/lib/controller/product_stock_controller.dart b/lib/controller/product_stock_controller.dart new file mode 100644 index 0000000..dd86d77 --- /dev/null +++ b/lib/controller/product_stock_controller.dart @@ -0,0 +1,49 @@ +import 'package:cheminova/controller/product_mannual_service.dart'; +import 'package:cheminova/controller/product_stock_service.dart'; +import 'package:cheminova/models/product_stock_model.dart'; +import 'package:get/get.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../models/product_mannual_model.dart'; // Your model import +// Your service import + +class ProductStockController extends GetxController { + + var productStockList = [].obs; + + // Service to fetch data + final ProductStockService productStockService = ProductStockService(); + + // Loading state + var isLoading = false.obs; + + // Method to fetch product manuals from the service + FuturefetchProductStock() async { + try { + // Set loading to true + isLoading.value = true; + SharedPreferences prefs = await SharedPreferences.getInstance(); + String? token = prefs.getString('token'); + var manuals = await productStockService.getProductStock(token!); + + // If data is returned, update the list + if (manuals != null) { + productStockList.value = manuals; + } else { + productStockList.value = []; // If no data, set an empty list + } + } catch (e) { + // Handle error here, for example logging or showing an error message + print("Error fetching product stock: $e"); + } finally { + // Set loading to false + isLoading.value = false; + } + } + + @override + void onInit() { + // Fetch product manuals when the controller is initialized + fetchProductStock(); + super.onInit(); + } +} diff --git a/lib/controller/product_stock_service.dart b/lib/controller/product_stock_service.dart new file mode 100644 index 0000000..9827744 --- /dev/null +++ b/lib/controller/product_stock_service.dart @@ -0,0 +1,40 @@ + + +import 'package:cheminova/utils/api_urls.dart'; + +import '../models/product_mannual_model.dart'; +import '../models/product_stock_model.dart'; +import '../utils/common_api_service.dart'; // Replace with your actual common API service import + +class ProductStockService { + // Method to fetch product manuals using an authorization token + Future?> getProductStock(String token) async { + try { + String url = ApiUrls.ProductStockUrl; // Base URL to fetch product manuals + + final response = await commonApiService>( + method: "GET", + url: url, + additionalHeaders: { // Pass the token here + 'Authorization': 'Bearer $token', + }, + fromJson: (json) { + if (json['stocks'] != null) { + // If the productManuals key is present, map the response to a list of ProductManualModel objects + final List productstock = (json['stocks'] as List) + .map((manualJson) => ProductStockModel.fromJson(manualJson as Map)) + .toList(); + return productstock; // Return the list of product manuals + } else { + return []; + } + }, + ); + return response; + } catch (e) { + + print(e.toString()); + return null; + } + } +} diff --git a/lib/controller/rd_processing_order_controller.dart b/lib/controller/rd_processing_order_controller.dart index 33b125b..fa4c04e 100644 --- a/lib/controller/rd_processing_order_controller.dart +++ b/lib/controller/rd_processing_order_controller.dart @@ -48,7 +48,8 @@ class RDOrderPlacedController extends GetxController { // Call the service to place the order await _rdOrderPlacedService.placRDeOrder(orderDetails, token!); } catch (e) { - print("Error placing order: $e"); + // print("Error placing order: $e"); + showSnackbar("stock not available"); } finally { isLoading.value = false; } diff --git a/lib/controller/update_stock_controller.dart b/lib/controller/update_stock_controller.dart new file mode 100644 index 0000000..02d3558 --- /dev/null +++ b/lib/controller/update_stock_controller.dart @@ -0,0 +1,32 @@ +import 'package:cheminova/controller/product_stock_service.dart'; +import 'package:cheminova/controller/update_stock_service.dart'; +import 'package:cheminova/models/product_stock_model.dart'; +import 'package:get/get.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class UpdateStockController extends GetxController{ + + + Future updateProductStock(List reason) async { + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String? token = prefs.getString('token'); // Get the token + + if (token == null || token.isEmpty) { + throw Exception("Token is missing. Please login again."); + } + + // Show loading indicator + + // Call the service function and pass the token, orderId, and reason + await UpdateStockService().UpdateStockProduct(token, reason); + + // Optionally refresh the data or show success message + print("UpdateStockService process complete."); + } catch (e) { + print("Error: $e"); + } finally { + + } + } +} \ No newline at end of file diff --git a/lib/controller/update_stock_service.dart b/lib/controller/update_stock_service.dart new file mode 100644 index 0000000..c9896c5 --- /dev/null +++ b/lib/controller/update_stock_service.dart @@ -0,0 +1,46 @@ +import 'dart:convert'; + +import 'package:cheminova/models/product_stock_model.dart'; +import 'package:dio/dio.dart'; + +import '../utils/api_urls.dart'; +import '../utils/common_api_service.dart'; +import '../utils/show_snackbar.dart'; + +class UpdateStockService{ + + // Function to handle password change functionality + Future UpdateStockProduct(String token, List stock) async { + try { + // Correct API URL with orderId passed in the URL + final String url = ApiUrls.ProductUpdateStockUrl; + + // Make the PUT request + final response = await Dio().put( + url, // Use the correct URL here + data: { + "products": stock, // Send the cancellation reason as JSON + }, + options: Options( + headers: { + 'Authorization': 'Bearer $token', // Pass the token in the Authorization header + 'Content-Type': 'application/json', // Set content-type to application/json + }, + ), + ); + + // Check the response status + if (response.statusCode == 200) { + print(response.data); + print("Stock Update successfully"); + } else { + throw Exception('Failed to Stock Update'); + } + } catch (e) { + // Handle error + print('Error Stock Update: $e'); + } + } + +} + diff --git a/lib/models/product_stock_model.dart b/lib/models/product_stock_model.dart new file mode 100644 index 0000000..670848f --- /dev/null +++ b/lib/models/product_stock_model.dart @@ -0,0 +1,41 @@ +class ProductStockModel { + final String productid; + final String name; + final String sku; + final int stock; + final int openingInventory; + + ProductStockModel({ + required this.productid, + required this.name, + required this.sku, + required this.stock, + required this.openingInventory, + }); + + factory ProductStockModel.fromJson(Map json) { + return ProductStockModel( + productid: json['productid']??"2323", + name: json['name']??"rert", + sku: json['SKU']??"", + stock: json['Stock']??"", + openingInventory: json['openingInventory']??"" + ); + } + + // Convert a ProductStock instance to a JSON map + Map toJson() { + return { + 'productid': productid, + 'name': name, + 'SKU': sku, + 'Stock': stock, + 'openingInventory':openingInventory + }; + } + + @override + String toString() { + return 'ProductStock(productid: $productid, name: $name, sku: $sku, stock: $stock,openingInventory:$openingInventory)'; + } +} diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 237f098..7966164 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -19,6 +19,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'kyc/kyc_retailer_info_screen.dart'; +import 'opening Inventory/inventory_management_screen.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @@ -206,7 +207,7 @@ class _HomeScreenState extends State { HomeCard( title: 'Opening Inventory', onTap: () => Get.to( - () => InventoryManagementScreen(), + () => OpeningInventoryManagementScreen(), ), ), // HomeCard( diff --git a/lib/screens/opening Inventory/inventory_management_screen.dart b/lib/screens/opening Inventory/inventory_management_screen.dart new file mode 100644 index 0000000..1042bed --- /dev/null +++ b/lib/screens/opening Inventory/inventory_management_screen.dart @@ -0,0 +1,290 @@ +import 'dart:async'; + +import 'package:cheminova/controller/product_stock_controller.dart'; +import 'package:cheminova/models/place_order_list_model.dart'; +import 'package:cheminova/models/product_stock_model.dart'; +import 'package:cheminova/screens/opening%20Inventory/update_stock_screen.dart'; +import 'package:cheminova/screens/order_management/order_management_detail_screen.dart'; +import 'package:cheminova/widgets/input_field.dart'; +import 'package:cheminova/widgets/my_drawer.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:get/get.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import '../../controller/cart_controller.dart'; +import '../../controller/get_order_placed_controller.dart'; +import '../../models/product_model1.dart'; + + +class OpeningInventoryManagementScreen extends StatefulWidget { + final ProductStockModel? productModel; +// PlacedOrderList? placeOrder; + OpeningInventoryManagementScreen({super.key, this.productModel,}); + + @override + State createState() => _OpeningInventoryManagementScreenState(); +} + +class _OpeningInventoryManagementScreenState extends State { + final _searchController = TextEditingController(); + //final List _filterList = ["Order Status", "Date Range"]; + + final ProductStockController _getProductStockController = Get.put(ProductStockController()); + final CartController _cartController = Get.put(CartController()); + final GlobalKey _refreshIndicatorKey = + GlobalKey(); + + @override + void initState() { + super.initState(); + getOrder1(); // Fetch orders when the screen initializes + } + + Future _onRefresh() async { + await getOrder1(); + await Future.delayed(Duration(seconds: 1)); + } + // Fetches orders from the API + Future getOrder1() async { + await _getProductStockController.fetchProductStock(); + print("Opening order Inventory fetched successfully"); + } + // Capitalizes the first letter of the string + String capitalizeFirstLetter(String text) { + if (text.isEmpty) return text; + return text[0].toUpperCase() + text.substring(1).toLowerCase(); + } + // Formats the date received from the API + String formatDate(String apiDate) { + DateTime parsedDate = DateTime.parse(apiDate); + String formattedDate = DateFormat('dd-MMM-yyyy').format(parsedDate); + return formattedDate; + } + + + @override + Widget build(BuildContext context) { + return Scaffold( + extendBodyBehindAppBar: true, + appBar: AppBar( + centerTitle: true, + backgroundColor: Colors.transparent, + elevation: 0, + leading: Builder( + builder: (context) { + return GestureDetector( + onTap: () => Scaffold.of(context).openDrawer(), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: SvgPicture.asset('assets/svg/menu.svg'), + ), + ); + }, + ), + actions: [ + GestureDetector( + onTap: () => Get.back(), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: SvgPicture.asset('assets/svg/back_arrow.svg'), + ), + ), + ], + title: const Text("Opening Inventory "), + ), + drawer: MyDrawer(), + body: Stack( + fit: StackFit.expand, + children: [ + Image.asset('assets/images/image_1.png', fit: BoxFit.cover), + SafeArea( + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: RefreshIndicator( + key: _refreshIndicatorKey, + onRefresh: _onRefresh, + color: Colors.black, + backgroundColor: Colors.white, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + InputField( + hintText: "Search Order", + labelText: "Search Order", + + controller: _searchController, + onChanged: (value) { + //searchOrder(value);// Call search function with input value + }, + ), + SizedBox(height: Get.height * 0.035), + Card( + margin: const EdgeInsets.symmetric(horizontal: 18), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(19), + side: const BorderSide(color: Color(0xFFFDFDFD)), + ), + color: const Color(0xFFB4D1E5).withOpacity(0.9), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // SizedBox( + // height: Get.height * 0.05, + // child: ListView.builder( + // shrinkWrap: true, + // scrollDirection: Axis.horizontal, + // itemCount: _filterList.length, + // itemBuilder: (context, index) => Padding( + // padding: const EdgeInsets.symmetric(horizontal: 4), + // child: Chip( + // label: Text( + // _filterList[index], + // style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.w500), + // ), + // ), + // ), + // ), + // ), + SizedBox( + height: Get.height * 0.6, + child: Obx(() { + // Use a set to keep track of unique order IDs + final Set uniqueOrderIds = {}; + final List uniqueOrders = []; + // Loop through the fetched orders to filter unique orders + for (var order in _getProductStockController.productStockList) { + if (uniqueOrderIds.add(order.productid)) { + uniqueOrders.add(order); + } + } + // Displaying unique orders in a ListView + return ListView.builder( + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: uniqueOrders.length, + itemBuilder: (context, index) { + final order = uniqueOrders[index]; + + // Combine product names into a single string + // final productNames = order.name + // .map((item) => capitalizeFirstLetter(item.name)) + // .join(', '); + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Card( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 8, 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text("Product ID: ", style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.bold)), + Text("${order.productid}") + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 8, 0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, // Aligns the Column to the top of the Text + children: [ + Text( + "Product Names: ", + style: GoogleFonts.roboto( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, // Aligns text to the right within the Column + children: [ + + Text( + '${order.name}', // Adds index and trims whitespace + textAlign: TextAlign.left, // Aligns text to the right + style: GoogleFonts.roboto( + fontSize: 14, + ), + ), + ], + ), + ), + ], + ), + ), + + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 8, 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text("SKU: ", style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.bold)), + Text("${order.sku}") + ], + ), + ), + + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 8, 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text("Opening Inventory: ", style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.bold)), + Text("${order.openingInventory}") + ], + ), + ), + + SizedBox( + width: Get.width * 0.5, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + onPressed: (){ + Get.to(InventoryUpdateStockScreen(products: _getProductStockController.productStockList,selectedProductId: _getProductStockController.productStockList[index].productid,)); + }, + + // Navigate to detail screen + style: ElevatedButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: const Color(0xFF004791), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10)), + ), + child: Text("Update Inventory", style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.w400)), + ), + ), + ) + ], + ), + ), + ); + }, + ); + }), + ) + ], + ), + ), + ), + ], + ), + ), + ), + ), + ), + ], + ), + ); + } +} + diff --git a/lib/screens/opening Inventory/update_stock_screen.dart b/lib/screens/opening Inventory/update_stock_screen.dart new file mode 100644 index 0000000..dca5347 --- /dev/null +++ b/lib/screens/opening Inventory/update_stock_screen.dart @@ -0,0 +1,350 @@ +import 'package:cheminova/controller/product_stock_service.dart'; +import 'package:cheminova/controller/update_stock_controller.dart'; +import 'package:cheminova/controller/update_stock_service.dart'; +import 'package:cheminova/models/product_model.dart'; +import 'package:cheminova/models/product_stock_model.dart'; +import 'package:cheminova/screens/opening%20Inventory/inventory_management_screen.dart'; +import 'package:cheminova/widgets/input_field.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:get/get.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import '../../utils/show_snackbar.dart'; + +// class InventoryUpdateStockScreen extends StatefulWidget { +// final ProductStockModel product; +// +// const InventoryUpdateStockScreen({ +// super.key, +// required this.product, +// }); +// +// @override +// State createState() => _InventoryUpdateStockScreenState(); +// } +// +// class _InventoryUpdateStockScreenState extends State { +// final _textController = TextEditingController(); +// +// final UpdateStockController _updateStockController = Get.put(UpdateStockController()); +// +// void _onUpdateStock() async { +// try { +// // Parse the new stock quantity from the text controller +// int? newStockQuantity = int.tryParse(_textController.text.trim()); +// +// // Check if the parsed quantity is valid +// if (newStockQuantity == null || newStockQuantity < 0) { +// showSnackbar("Please enter a valid stock quantity."); +// return; +// } +// +// // Create a list of ProductStockModel instances with updated stock +// List productStockList = [ +// ProductStockModel( +// productid: widget.product.productid, +// name: widget.product.name, +// sku: widget.product.sku, +// stock: newStockQuantity, // Update stock with new quantity +// ), +// ]; +// +// // Call the updateStock function +// final update = await _updateStockController.updateProductStock(productStockList); +// +// // Optionally, show a confirmation message or navigate to another screen +// showSnackbar("Stock updated successfully"); +// } catch (e) { +// print("Error updating stock: $e"); +// showSnackbar("Error updating stock. Please try again."); +// } +// } +// +// +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// extendBodyBehindAppBar: true, +// appBar: AppBar( +// centerTitle: true, +// backgroundColor: Colors.transparent, +// elevation: 0, +// leading: GestureDetector( +// onTap: () {}, +// child: Padding( +// padding: const EdgeInsets.all(16.0), +// child: SvgPicture.asset( +// 'assets/svg/menu.svg', +// ), +// ), +// ), +// actions: [ +// GestureDetector( +// onTap: () => Get.back(), +// child: Padding( +// padding: const EdgeInsets.all(8.0), +// child: SvgPicture.asset( +// 'assets/svg/back_arrow.svg', +// ), +// ), +// ), +// ], +// title: const Text( +// "Update Inventory", +// ), +// ), +// body: Stack( +// fit: StackFit.expand, +// children: [ +// Image.asset( +// 'assets/images/image_1.png', +// fit: BoxFit.cover, +// ), +// SafeArea( +// child: Column( +// mainAxisAlignment: MainAxisAlignment.start, +// children: [ +// SizedBox( +// height: Get.height * 0.02, +// ), +// Card( +// margin: const EdgeInsets.symmetric(horizontal: 16), +// shape: RoundedRectangleBorder( +// borderRadius: BorderRadius.circular(19), +// side: const BorderSide(color: Color(0xFFFDFDFD)), +// ), +// color: const Color(0xFFB4D1E5).withOpacity(0.9), +// child: Padding( +// padding: const EdgeInsets.all(12.0), +// child: Column( +// mainAxisSize: MainAxisSize.min, +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// +// Card( +// child: SizedBox( +// width: Get.width, +// child: Padding( +// padding: const EdgeInsets.all(12), +// child: Row( +// +// children: [ +// Text( +// "Product Name:", +// style: GoogleFonts.roboto( +// fontSize: Get.width * 0.04, +// fontWeight: FontWeight.w700, +// ), +// ), +// Text("${widget.product.name}"), +// ], +// ), +// ), +// ), +// ), +// +// InputField( +// hintText: "Enter New Stock Quantity:", +// labelText: "Enter New Stock Quantity:", +// controller: _textController, +// ), +// // InputField( +// // hintText: "Reason for Update: Restock", +// // labelText: "Reason for Update: Restock", +// // controller: _textController, +// // ) +// ], +// ), +// ), +// ), +// SizedBox(height: Get.height * 0.04), +// SizedBox( +// width: Get.width * 0.9, +// height: Get.height * 0.06, +// child: ElevatedButton( +// onPressed: () { +// +// _onUpdateStock(); +// }, +// style: ElevatedButton.styleFrom( +// foregroundColor: Colors.white, +// backgroundColor: const Color(0xFF00784C), +// shape: RoundedRectangleBorder( +// borderRadius: BorderRadius.circular(10), +// ), +// ), +// child: Text( +// "Update Stock Quantity", +// style: GoogleFonts.roboto( +// fontSize: Get.width * 0.05, +// fontWeight: FontWeight.w600, +// ), +// ), +// ), +// ), +// ], +// ), +// ), +// ], +// ), +// ); +// } +// } +import 'package:flutter_svg/svg.dart'; +import 'package:get/get.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import '../../controller/update_stock_controller.dart'; +import '../../models/product_stock_model.dart'; +import 'package:flutter/material.dart'; + +import '../../utils/show_snackbar.dart'; +import '../../widgets/input_field.dart'; +import 'inventory_management_screen.dart'; + +class InventoryUpdateStockScreen extends StatefulWidget { + final List products; + final String selectedProductId; + + const InventoryUpdateStockScreen({ + super.key, + required this.products, + required this.selectedProductId, + }); + + @override + State createState() => _InventoryUpdateStockScreenState(); +} + +class _InventoryUpdateStockScreenState extends State { + late TextEditingController _textController; + final UpdateStockController _updateStockController = Get.put(UpdateStockController()); + + @override + void initState() { + super.initState(); + // Initialize text controller with the stock of the selected product + final selectedProduct = widget.products.firstWhere((product) => product.productid == widget.selectedProductId); + _textController = TextEditingController(text: selectedProduct.stock.toString()); + } + + void _onUpdateStock() async { + try { + int? newStockQuantity = int.tryParse(_textController.text.trim()); + + if (newStockQuantity == null || newStockQuantity < 0) { + showSnackbar("Please enter a valid stock quantity."); + return; + } + + // Update only the stock for the selected product while keeping others the same + List updatedProductStockList = widget.products.map((product) { + if (product.productid == widget.selectedProductId) { + return ProductStockModel( + productid: product.productid, + name: product.name, + sku: product.sku, + stock: newStockQuantity, + openingInventory: newStockQuantity, + ); + } else { + return product; + } + }).toList(); + + // Call the updateStock function with the updated list + final update = await _updateStockController.updateProductStock(updatedProductStockList); + + showSnackbar("Opening Inventory for ${widget.selectedProductId} updated successfully"); + Get.to(OpeningInventoryManagementScreen()); + } catch (e) { + print("Error updating stock: $e"); + showSnackbar("Error updating stock. Please try again."); + } + } + + @override + Widget build(BuildContext context) { + final selectedProduct = widget.products.firstWhere((product) => product.productid == widget.selectedProductId); + + return Scaffold( + extendBodyBehindAppBar: true, + appBar: AppBar( + centerTitle: true, + backgroundColor: Colors.transparent, + elevation: 0, + leading: GestureDetector( + onTap: () {}, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: SvgPicture.asset( + 'assets/svg/menu.svg', + ), + ), + ), + actions: [ + GestureDetector( + onTap: () => Get.back(), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: SvgPicture.asset( + 'assets/svg/back_arrow.svg', + ), + ), + ), + ], + title: const Text( + "Update Inventory", + ), + ), + body: Stack( + fit: StackFit.expand, + children: [ + Image.asset( + 'assets/images/image_1.png', + fit: BoxFit.cover, + ), + SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox(height: Get.height * 0.02), + Text( + "Product Name: ${selectedProduct.name}", + style: GoogleFonts.roboto( + fontSize: Get.width * 0.04, + fontWeight: FontWeight.w700, + ), + ), + InputField( + hintText: "Enter New Opening Inventory Quantity:", + labelText: "Enter New Opening Inventory Quantity:", + controller: _textController, + ), + SizedBox(height: 8), + ElevatedButton( + onPressed: _onUpdateStock, + style: ElevatedButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: const Color(0xFF00784C), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: Text( + "Update Inventory Quantity", + style: GoogleFonts.roboto( + fontSize: Get.width * 0.05, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/order/checkout_screen.dart b/lib/screens/order/checkout_screen.dart index b69a5b2..e5ff327 100644 --- a/lib/screens/order/checkout_screen.dart +++ b/lib/screens/order/checkout_screen.dart @@ -14,9 +14,11 @@ import '../../controller/place_order_controller.dart'; import '../../controller/place_order_service.dart'; import '../../controller/product_controller.dart'; import '../../controller/product_service.dart'; +import '../../controller/shiptobilltoController.dart'; import '../../models/brand_model.dart'; import '../../models/oder_place_model.dart'; import '../../models/product_model1.dart'; +import '../../models/shiping_billing_address_model.dart'; import '../../widgets/my_drawer.dart'; import '../../widgets/product_card.dart'; import 'order_confermation_screen.dart'; @@ -39,6 +41,7 @@ class _CheckoutScreenState extends State { final OrderPlacedController _orderPlacedController = Get.put(OrderPlacedController()); final GetPlacedOrderController _getPlacedOrderController = Get.put(GetPlacedOrderController()); + final AddressController _addressController = Get.put(AddressController()); // Initialize AddressController int currentPage = 1; String _groupValue = "cheque"; @@ -52,17 +55,25 @@ class _CheckoutScreenState extends State { String? _selectedShippingAddress; String? _selectedBillingAddress; + + String capitalizeFirstLetter(String text) { + if (text.isEmpty) return text; + return text[0].toUpperCase() + text.substring(1).toLowerCase(); + } @override void initState() { super.initState(); // _getOrder(); + _addressController.fetchAddresses(); _loadSelectedAddress(); _loadSelectedPaymentMode(); - - if (_addressList.isNotEmpty) { - _selectedShippingAddress = _addressList.first; - _selectedBillingAddress = _addressList.first; - } + // _loadSelectedAddress(); + // _loadSelectedPaymentMode(); + // + // if (_addressList.isNotEmpty) { + // _selectedShippingAddress = _addressList.first; + // _selectedBillingAddress = _addressList.first; + // } } void _saveSelectedAddress() async { @@ -140,11 +151,35 @@ class _CheckoutScreenState extends State { ); }).toList(); + + // Get the full address for shipping and billing + String? shippingAddress = _addressController.addressList.firstWhere( + (address) => address.id == _addressController.selectedShippingAddressId.value, + orElse: () => UserShippingAddress( + id: '', + street: '', + city: '', + state: '', + postalCode: '', + country: '', tradeName: '', + )).toStringFullAddress(); + + String? billingAddress = _addressController.addressList.firstWhere( + (address) => address.id == _addressController.selectedBillingAddressId.value, + orElse: () => UserShippingAddress( + id: '', + street: '', + city: '', + state: '', + postalCode: '', + country: '', tradeName: '', + )).toStringFullAddress(); + // Update the placedOrder1 value _orderPlacedController.placedOrder1.value= PlacedOrderModel( paymentMode: _groupValue, - shipTo: _selectedShippingAddress!, - billTo: _selectedBillingAddress!, + shipTo: shippingAddress, // Full shipping address + billTo: billingAddress, // Full billing address orderItems: orderItems, gstTotal: _cartController.gstTotal.value, grandTotal: _cartController.grandTotal.value, @@ -157,11 +192,16 @@ class _CheckoutScreenState extends State { showSnackbar("Order Placed Successfully"); Get.to(() => OrderConfermationScreen( placedOrder: _orderPlacedController.placedOrder1.value, + )); } + + } catch (e) { print("PlaceOrderScreen error: $e"); } + + } @@ -219,15 +259,15 @@ class _CheckoutScreenState extends State { side: const BorderSide(color: Color(0xFFFDFDFD)), ), color: const Color(0xFFB4D1E5).withOpacity(0.9), - child: Padding( + child: + Padding( padding: EdgeInsets.all(Get.width * 0.04), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: EdgeInsets.symmetric( - horizontal: Get.width * 0.04), + padding: EdgeInsets.symmetric(horizontal: Get.width * 0.04), child: Text( 'Shipping Information', style: GoogleFonts.roboto( @@ -237,38 +277,54 @@ class _CheckoutScreenState extends State { ), ), ), - SizedBox(height: 5,), - DropdownButtonFormField( - decoration: InputDecoration( - labelText: 'Shipping Address:', - hintText: 'Select Shipping Address', - border: OutlineInputBorder(), + SizedBox(height: 5), + Obx(() => Container( + + child: DropdownButtonFormField( + decoration: InputDecoration( + labelText: 'Shipping Address:', + hintText: 'Select Shipping Address', + border: OutlineInputBorder(), + ), + value: _addressController.selectedShippingAddressId.value.isEmpty + ? null // Show null if there's no selection yet + : _addressController.selectedShippingAddressId.value, // Set the selected ID + items: _addressController.addressList.map((UserShippingAddress address) { + return DropdownMenuItem( + value: address.id, // Set the value as the address ID + child: Text("${address.street} ${address.city} ${address.state} \n ${address.postalCode} , ${address.country}"), // Display full address + ); + }).toList(), + onChanged: (value) { + _addressController.onShippingAddressChanged(value); // Update the selected address + }, ), - value: _selectedShippingAddress, - items: _addressList.map((String address) { - return DropdownMenuItem( - value: address, - child: Text(address), - ); - }).toList(), - onChanged: _onShippingAddressChanged, - ), + )), SizedBox(height: Get.height * 0.02), - DropdownButtonFormField( - decoration: InputDecoration( - labelText: 'Billing Address:', - hintText: 'Select Billing Address', - border: OutlineInputBorder(), + +// Billing Address Dropdown + Obx(() => Container( + + child: DropdownButtonFormField( + decoration: InputDecoration( + labelText: 'Billing Address:', + hintText: 'Select Billing Address', + border: OutlineInputBorder(), + ), + value: _addressController.selectedBillingAddressId.value.isEmpty + ? null // Show null if there's no selection yet + : _addressController.selectedBillingAddressId.value, // Set the selected ID + items: _addressController.addressList.map((UserShippingAddress address) { + return DropdownMenuItem( + value: address.id, // Set the value as the address ID + child: Text("${address.street} ${address.city} ${address.state} \n ${address.postalCode} ${address.country}"), // Display full address + ); + }).toList(), + onChanged: (value) { + _addressController.onBillingAddressChanged(value); // Update the selected address + }, ), - value: _selectedBillingAddress, - items: _addressList.map((String address) { - return DropdownMenuItem( - value: address, - child: Text(address), - ); - }).toList(), - onChanged: _onBillingAddressChanged, - ), + )), Padding( padding: EdgeInsets.symmetric( horizontal: Get.width * 0.04), diff --git a/lib/screens/order/order_confermation_screen.dart b/lib/screens/order/order_confermation_screen.dart index 92fa44b..0bf53df 100644 --- a/lib/screens/order/order_confermation_screen.dart +++ b/lib/screens/order/order_confermation_screen.dart @@ -1,4 +1,5 @@ import 'package:cheminova/controller/get_order_placed_controller.dart'; +import 'package:cheminova/controller/shiptobilltoController.dart'; import 'package:cheminova/models/product_model.dart'; import 'package:cheminova/utils/show_snackbar.dart'; import 'package:cheminova/widgets/my_drawer.dart'; @@ -12,14 +13,18 @@ import '../../controller/cart_controller.dart'; import '../../controller/place_order_controller.dart'; import '../../models/oder_place_model.dart'; import '../../models/product_model1.dart'; +import '../../models/shiping_billing_address_model.dart'; class OrderConfermationScreen extends StatefulWidget { Product? productModel; // The selected product model PlacedOrderModel? placedOrder; // The order details after placement List? selectedProducts; // List of selected products for the order + // final UserShippingAddress? shippingAddress; + // final UserShippingAddress? billingAddress; // Constructor to initialize the class with optional parameters - OrderConfermationScreen({super.key,this.productModel,this.placedOrder,this.selectedProducts}); + OrderConfermationScreen({super.key,this.productModel,this.placedOrder,this.selectedProducts,}); + @override State createState() => @@ -30,9 +35,11 @@ class _OrderConfermationScreenState extends State { final CartController _cartController = Get.put(CartController()); // Instance of CartController // Instance of OrderPlacedController final OrderPlacedController _placedController = Get.put(OrderPlacedController()); + final AddressController _addressController = Get.put(AddressController()); // Instance of GetPlacedOrderController final GetPlacedOrderController _getPlacedOrderController = Get.put(GetPlacedOrderController()); + @override Widget build(BuildContext context) { final orderItems = _placedController.placedOrder1; // Fetching the placed order details @@ -78,164 +85,172 @@ class _OrderConfermationScreenState extends State { fit: BoxFit.cover, ), SafeArea( - child: Column( - children: [ - SizedBox( - height: Get.height * 0.02, // Spacing from the top - ), - Card( - margin: const EdgeInsets.symmetric(horizontal: 18), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(19), - side: const BorderSide(color: Color(0xFFFDFDFD)), + child: SingleChildScrollView( + child: Column( + children: [ + SizedBox( + height: Get.height * 0.02, // Spacing from the top ), - color: const Color(0xFFB4D1E5).withOpacity(0.9), - child: Padding( - padding: EdgeInsets.all(Get.width * 0.04), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Card( - child: SizedBox( - width: Get.width, - height: Get.height * 0.05, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - "Order Number:1234", - style: GoogleFonts.roboto( - fontSize: Get.width * 0.04, - fontWeight: FontWeight.w400, - ), - ), - ), - ), - ), - Padding( - padding: EdgeInsets.all(Get.width * 0.02), - child: Text( - 'Order Summary', - style: GoogleFonts.roboto( - fontSize: Get.width * 0.04, - fontWeight: FontWeight.bold, - color: Colors.black, - ), - ), - ), - Card( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - height: Get.height * 0.22, - child: Padding( - padding: EdgeInsets.all(Get.width * 0.02), - child: ListView.builder( - padding: EdgeInsets.zero, - itemCount: _cartController.selectedProducts.length, - itemBuilder: (context, index) => - ProductCard( - productModel:_cartController.selectedProducts[index], - isCheckout: true, - ), + Card( + margin: const EdgeInsets.symmetric(horizontal: 18), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(19), + side: const BorderSide(color: Color(0xFFFDFDFD)), + ), + color: const Color(0xFFB4D1E5).withOpacity(0.9), + child: Padding( + padding: EdgeInsets.all(Get.width * 0.04), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Card( + child: SizedBox( + width: Get.width, + height: Get.height * 0.05, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "Order Number:1234", + style: GoogleFonts.roboto( + fontSize: Get.width * 0.04, + fontWeight: FontWeight.w400, ), ), ), - Padding( - padding: EdgeInsets.all(Get.width * 0.02), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('Subtotal',style: TextStyle(fontWeight: FontWeight.bold)), - Text('₹${_cartController.subtotal.value.toStringAsFixed(2)}'), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('GST',style: TextStyle(fontWeight: FontWeight.bold)), - Text('₹${_cartController.gstTotal.value.toStringAsFixed(2)}'), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('Total Amount',style: TextStyle(fontWeight: FontWeight.bold)), - Text('₹${_cartController.grandTotal.value.toStringAsFixed(2)}'), - ], - ), - ], - ), - ), - - ], - ), - ), - Padding( - padding: EdgeInsets.all(Get.width * 0.02), - child: Text( - 'Shipping Information', - style: GoogleFonts.roboto( - fontSize: Get.width * 0.04, - fontWeight: FontWeight.w500, - color: Colors.black, ), ), - ), - Card( - child: SizedBox( - width: Get.width, - height: Get.height * 0.1, + Padding( + padding: EdgeInsets.all(Get.width * 0.02), + child: Text( + 'Order Summary', + style: GoogleFonts.roboto( + fontSize: Get.width * 0.04, + fontWeight: FontWeight.bold, + color: Colors.black, + ), + ), + ), + Card( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: TextField( - controller: TextEditingController( - text: widget.placedOrder!.shipTo, // Pre-filling the address - ), - decoration: InputDecoration( - hintText: "Address : ${widget.placedOrder!.shipTo}", - hintStyle: GoogleFonts.roboto( - fontSize: Get.width * 0.04, - fontWeight: FontWeight.w400, + SizedBox( + height: Get.height * 0.22, + child: Padding( + padding: EdgeInsets.all(Get.width * 0.02), + child: ListView.builder( + padding: EdgeInsets.zero, + itemCount: _cartController.selectedProducts.length, + itemBuilder: (context, index) => + ProductCard( + productModel:_cartController.selectedProducts[index], + isCheckout: true, ), - border: OutlineInputBorder(), ), ), ), - - ]), + Padding( + padding: EdgeInsets.all(Get.width * 0.02), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Subtotal',style: TextStyle(fontWeight: FontWeight.bold)), + Text('₹${_cartController.subtotal.value.toStringAsFixed(2)}'), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('GST',style: TextStyle(fontWeight: FontWeight.bold)), + Text('₹${_cartController.gstTotal.value.toStringAsFixed(2)}'), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Total Amount',style: TextStyle(fontWeight: FontWeight.bold)), + Text('₹${_cartController.grandTotal.value.toStringAsFixed(2)}'), + ], + ), + ], + ), + ), + + ], + ), ), - ), - Card( - child: SizedBox( - width: Get.width, - height: Get.height * 0.05, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - "Estimated Delivery Date: ${widget.placedOrder!.orderItems[0].createdAt}", - style: GoogleFonts.roboto( - fontSize: Get.width * 0.04, - fontWeight: FontWeight.w400, + Padding( + padding: EdgeInsets.all(Get.width * 0.02), + child: Text( + 'Shipping Information', + style: GoogleFonts.roboto( + fontSize: Get.width * 0.04, + fontWeight: FontWeight.w500, + color: Colors.black, + ), + ), + ), + Card( + child: SizedBox( + width: Get.width, + height: Get.height * 0.1, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: TextEditingController( + text:widget.placedOrder!.shipTo + // _addressController.selectedShippingAddressId != null + // ? '${widget.placedOrder?.shipTo} + // '${_addressController.addressList[0].city}, ' + // '${_addressController.addressList[0].state}, ' + // '${_addressController.addressList[0].postalCode}' + // : 'No address selected', // Pre-filling with full address if available // Pre-filling the address + ), + decoration: InputDecoration( + hintText: "Address : ${widget.placedOrder!.shipTo}", + hintStyle: GoogleFonts.roboto( + fontSize: Get.width * 0.04, + fontWeight: FontWeight.w400, + ), + border: OutlineInputBorder(), + ), + ), + ), + + ]), + ), + ), + Card( + child: SizedBox( + width: Get.width, + height: Get.height * 0.05, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "Estimated Delivery Date: ${widget.placedOrder!.orderItems[0].createdAt}", + style: GoogleFonts.roboto( + fontSize: Get.width * 0.04, + fontWeight: FontWeight.w400, + ), ), ), ), ), - ), - - ], + + ], + ), ), ), - ), - - ], + + ], + ), ), ), ], diff --git a/lib/screens/order_management/order_management_detail_screen.dart b/lib/screens/order_management/order_management_detail_screen.dart index aa691dd..5f9f23f 100644 --- a/lib/screens/order_management/order_management_detail_screen.dart +++ b/lib/screens/order_management/order_management_detail_screen.dart @@ -1,5 +1,6 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:cheminova/controller/get_order_placed_controller.dart'; +import 'package:cheminova/controller/shiptobilltoController.dart'; import 'package:cheminova/models/oder_place_model.dart'; import 'package:cheminova/models/order_item_model.dart'; import 'package:cheminova/models/place_order_list_model.dart'; @@ -32,6 +33,7 @@ class _OrderManagementDetailScreenState extends State { // Controllers for managing cart and placed orders final CartController _cartController = Get.put(CartController()); + final AddressController _addressController = Get.put(AddressController()); final GetPlacedOrderController _getPlacedOrderController = Get.put(GetPlacedOrderController()); // Function to format date from the API to a more readable format String formatDate(String apiDate) { @@ -289,11 +291,12 @@ Future adduni()async { ), SizedBox( width: Get.width, - height: Get.height*0.06, + height: Get.height * 0.10, child: Padding( - padding: - const EdgeInsets.fromLTRB(8, 8, 8, 0), - child: Row( + padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), + child: Wrap( // Use Wrap to allow wrapping + crossAxisAlignment: WrapCrossAlignment.start, + direction: Axis.horizontal, children: [ Text( "Address: ", @@ -302,13 +305,23 @@ Future adduni()async { fontWeight: FontWeight.bold, ), ), - AutoSizeText("${widget.placedOrderList!.shipTo}",maxLines: 4, - overflow:TextOverflow.ellipsis,) + Text( + "${widget.placedOrderList!.shipTo.toString()}", + // Text( + // "${widget.placedOrderList!.shipTo}", + maxLines: 4, + overflow: TextOverflow.ellipsis, + style: GoogleFonts.roboto( + fontSize: Get.width * 0.04, + + ), + ), ], ), ), ), + ], ), ), diff --git a/lib/screens/order_management/order_management_screen.dart b/lib/screens/order_management/order_management_screen.dart index e4778b3..52915bc 100644 --- a/lib/screens/order_management/order_management_screen.dart +++ b/lib/screens/order_management/order_management_screen.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:cheminova/models/place_order_list_model.dart'; import 'package:cheminova/screens/order_management/order_management_detail_screen.dart'; import 'package:cheminova/widgets/input_field.dart'; @@ -57,6 +59,11 @@ class _OrderManagementScreenState extends State { return formattedDate; } + Future searchOrder(String query) async { + { + _getPlacedOrderController.searchOrder(); + } + } @override Widget build(BuildContext context) { return Scaffold( @@ -108,7 +115,11 @@ class _OrderManagementScreenState extends State { InputField( hintText: "Search Order", labelText: "Search Order", + controller: _searchController, + onChanged: (value) { + searchOrder(value);// Call search function with input value + }, ), SizedBox(height: Get.height * 0.035), Card( diff --git a/lib/widgets/input_field.dart b/lib/widgets/input_field.dart index ae7c3d5..d5e93a6 100644 --- a/lib/widgets/input_field.dart +++ b/lib/widgets/input_field.dart @@ -7,6 +7,7 @@ class InputField extends StatefulWidget { final TextEditingController controller; final bool obscureText; final TextInputType? keyboardType; + final void Function(String)? onChanged; final String? Function(String?)? validator;// Add this line for validation InputField({ @@ -15,6 +16,7 @@ class InputField extends StatefulWidget { required this.labelText, required this.controller, this.obscureText = false, + this.onChanged, this.keyboardType = TextInputType.text, this.validator, // Add this });