From 2290319386cb7feec7c46e46f19ced28bd015c18 Mon Sep 17 00:00:00 2001 From: YujithIsura Date: Tue, 9 Jul 2024 11:44:40 +0530 Subject: [PATCH 1/4] lint --- .../asset_admin/lib/widgets/stock_replenishment.dart | 8 -------- campus/frontend/pubspec.yaml | 1 + 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_replenishment.dart b/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_replenishment.dart index d32f48ee..18d0a94c 100644 --- a/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_replenishment.dart +++ b/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_replenishment.dart @@ -167,14 +167,6 @@ class _StockReplenishmentState extends State { Person person = campusAppsPortalInstance.getUserPerson(); var formattedDate = formatDateTime(_selectedDate!); - for (var item in _fetchedStockList) { - int? avinya_type_id = item.avinya_type?.id; - item.avinya_type_id = avinya_type_id; - item.person_id = person.id; - item.consumable_id = item.consumable!.id; - item.updated = formattedDate; - item.organization_id = parentOrgId; - } _initializeControllers(); diff --git a/campus/frontend/pubspec.yaml b/campus/frontend/pubspec.yaml index 57b6265c..485aec2e 100644 --- a/campus/frontend/pubspec.yaml +++ b/campus/frontend/pubspec.yaml @@ -61,6 +61,7 @@ dependencies: fl_chart: 0.60.0 pdf: ^3.6.4 + month_year_picker: ^0.3.0+1 dev_dependencies: flutter_test: From ebe5a8d9c996a8703e538e5f780da89f884f24ab Mon Sep 17 00:00:00 2001 From: YujithIsura Date: Fri, 12 Jul 2024 10:35:13 +0530 Subject: [PATCH 2/4] stock depletion WIP --- .../lib/avinya/asset_admin/lib/app.dart | 66 +-- .../asset_admin/lib/data/stock_depletion.dart | 180 +++++++ .../lib/data/stock_repenishment.dart | 67 ++- .../asset_admin/lib/screens/scaffold.dart | 47 +- .../lib/screens/scaffold_body.dart | 12 +- .../lib/screens/stock_depletion.dart | 28 ++ .../lib/widgets/stock_depletion.dart | 452 ++++++++++++++++++ .../lib/widgets/stock_replenishment.dart | 24 +- .../lib/avinya/asset_admin/pubspec.yaml | 1 + .../frontend/lib/widgets/success_message.dart | 27 ++ campus/frontend/pubspec.yaml | 1 + 11 files changed, 863 insertions(+), 42 deletions(-) create mode 100644 campus/frontend/lib/avinya/asset_admin/lib/data/stock_depletion.dart create mode 100644 campus/frontend/lib/avinya/asset_admin/lib/screens/stock_depletion.dart create mode 100644 campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_depletion.dart create mode 100644 campus/frontend/lib/widgets/success_message.dart diff --git a/campus/frontend/lib/avinya/asset_admin/lib/app.dart b/campus/frontend/lib/avinya/asset_admin/lib/app.dart index b37c822f..6c250e5d 100644 --- a/campus/frontend/lib/avinya/asset_admin/lib/app.dart +++ b/campus/frontend/lib/avinya/asset_admin/lib/app.dart @@ -7,12 +7,12 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:gallery/auth.dart'; import 'package:month_year_picker/month_year_picker.dart'; import 'package:flutter_localizations/src/material_localizations.dart'; +import 'package:oktoast/oktoast.dart'; //import 'auth.dart'; import 'routing.dart'; import 'screens/navigator.dart'; - class AssetAdminSystem extends StatefulWidget { const AssetAdminSystem({super.key}); @@ -38,6 +38,7 @@ class _AssetAdminSystemState extends State { '/asset_dashboard', '/consumable_dashboard', '/stock_replenishment', + '/stock_depletion', '/consumable_monthly_report', '/consumable_weekly_report', // '/assets/new', @@ -74,31 +75,33 @@ class _AssetAdminSystemState extends State { notifier: _routeState, child: SMSAuthScope( notifier: _auth, - child: MaterialApp.router( - localizationsDelegates: [ - GlobalWidgetsLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - MonthYearPickerLocalizations.delegate, - ], - routerDelegate: _routerDelegate, - routeInformationParser: _routeParser, - // Revert back to pre-Flutter-2.5 transition behavior: - // https://github.com/flutter/flutter/issues/82053 - theme: ThemeData( - pageTransitionsTheme: const PageTransitionsTheme( - builders: { - TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(), - TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), - TargetPlatform.linux: FadeUpwardsPageTransitionsBuilder(), - TargetPlatform.macOS: CupertinoPageTransitionsBuilder(), - TargetPlatform.windows: FadeUpwardsPageTransitionsBuilder(), - }, - ), - drawerTheme: DrawerThemeData( - backgroundColor: Colors.yellow[700], - //width: 270.0, + child: OKToast( + child: MaterialApp.router( + localizationsDelegates: [ + GlobalWidgetsLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + MonthYearPickerLocalizations.delegate, + ], + routerDelegate: _routerDelegate, + routeInformationParser: _routeParser, + // Revert back to pre-Flutter-2.5 transition behavior: + // https://github.com/flutter/flutter/issues/82053 + theme: ThemeData( + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(), + TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), + TargetPlatform.linux: FadeUpwardsPageTransitionsBuilder(), + TargetPlatform.macOS: CupertinoPageTransitionsBuilder(), + TargetPlatform.windows: FadeUpwardsPageTransitionsBuilder(), + }, + ), + drawerTheme: DrawerThemeData( + backgroundColor: Colors.yellow[700], + //width: 270.0, + ), + appBarTheme: AppBarTheme(backgroundColor: Colors.yellow[700]), ), - appBarTheme: AppBarTheme(backgroundColor: Colors.yellow[700]), ), ), ), @@ -122,9 +125,12 @@ class _AssetAdminSystemState extends State { final stockReplenishmentRoute = ParsedRoute('/stock_replenishment', '/stock_replenishment', {}, {}); - final consumableMonthlyReportRoute = - ParsedRoute('/consumable_monthly_report', '/consumable_monthly_report', {}, {}); - + final stockDepletionRoute = + ParsedRoute('/stock_depletion', '/stock_depletion', {}, {}); + + final consumableMonthlyReportRoute = ParsedRoute( + '/consumable_monthly_report', '/consumable_monthly_report', {}, {}); + final consumableWeeklyReportRoute = ParsedRoute( '/consumable_weekly_report', '/consumable_weekly_report', {}, {}); @@ -141,7 +147,9 @@ class _AssetAdminSystemState extends State { return consumableDashboardReportRoute; } else if (signedIn && from == stockReplenishmentRoute) { return stockReplenishmentRoute; - } else if (signedIn && from == consumableMonthlyReportRoute){ + } else if (signedIn && from == stockDepletionRoute) { + return stockDepletionRoute; + } else if (signedIn && from == consumableMonthlyReportRoute) { return consumableMonthlyReportRoute; } else if (signedIn && from == consumableWeeklyReportRoute) { return consumableWeeklyReportRoute; diff --git a/campus/frontend/lib/avinya/asset_admin/lib/data/stock_depletion.dart b/campus/frontend/lib/avinya/asset_admin/lib/data/stock_depletion.dart new file mode 100644 index 00000000..6b755ccd --- /dev/null +++ b/campus/frontend/lib/avinya/asset_admin/lib/data/stock_depletion.dart @@ -0,0 +1,180 @@ +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:gallery/avinya/asset_admin/lib/data.dart'; +import 'package:gallery/widgets/success_message.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; + +import 'package:gallery/config/app_config.dart'; +import 'package:oktoast/oktoast.dart'; + +class StockDepletion { + int? id; + int? avinya_type_id; + int? consumable_id; + int? person_id; + int? organization_id; + String? updated; + String? created; + AvinyaType? avinya_type; + double? quantity; + double? quantity_in; + double? quantity_out; + ResourceProperty? resource_property; + Consumable? consumable; + + StockDepletion( + {this.id, + this.avinya_type_id, + this.consumable_id, + this.person_id, + this.organization_id, + this.updated, + this.created, + this.avinya_type, + this.quantity, + this.quantity_in, + this.quantity_out, + this.resource_property, + this.consumable}); + + factory StockDepletion.fromJson(Map json) => StockDepletion( + id: json["id"] == null ? null : json["id"], + avinya_type_id: + json["avinya_type_id"] == null ? null : json["avinya_type_id"], + consumable_id: + json["consumable_id"] == null ? null : json["consumable_id"], + person_id: json["person_id"] == null ? null : json["person_id"], + organization_id: + json["organization_id"] == null ? null : json["organization_id"], + updated: json["updated"] == null ? null : json["updated"], + created: json["created"] == null ? null : json["created"], + avinya_type: json['avinya_type'] == null + ? null + : AvinyaType.fromJson(json['avinya_type']), + quantity: json["quantity"] == null ? null : json["quantity"], + quantity_in: json["quantity_in"] == null ? null : json["quantity_in"], + quantity_out: + json["quantity_out"] == null ? null : json["quantity_out"], + resource_property: json['resource_property'] == null + ? null + : ResourceProperty.fromJson(json['resource_property']), + consumable: json['consumable'] == null + ? null + : Consumable.fromJson(json['consumable']), + ); + + Map toJson() => { + "id": id == null ? null : id, + "avinya_type_id": avinya_type_id == null ? null : avinya_type_id, + "consumable_id": consumable_id == null ? null : consumable_id, + "person_id": person_id == null ? null : person_id, + "organization_id": organization_id == null ? null : organization_id, + "updated": updated == null ? null : updated, + "avinya_type": avinya_type == null ? null : avinya_type, + "quantity": quantity == null ? null : quantity, + "quantity_in": quantity_in == null ? null : quantity_in, + "quantity_out": quantity_out == null ? null : quantity_out, + "resource_property": + resource_property == null ? null : resource_property, + "consumable": consumable == null ? null : consumable, + }; +} + +Future> addConsumableDepletion( + List stockList, + int? person_id, + int? organization_id, + String? to_date) async { + // Transform the original list to the new structure + List> transformedList = + stockList.map((stock) => stock.toJson()).toList().map((item) { + return { + "avinya_type_id": item["avinya_type_id"], + "consumable_id": item["consumable_id"], + "quantity": item["quantity"], + "quantity_in": item["quantity_in"] + }; + }).toList(); + final response = await http.post( + Uri.parse( + '${AppConfig.campusAssetsBffApiUrl}/consumable_replenishment/$person_id/$organization_id/$to_date'), + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + 'accept': 'application/json', + 'Authorization': 'Bearer ${AppConfig.campusBffApiKey}', + }, + body: jsonEncode(transformedList), + ); + if (response.statusCode > 199 && response.statusCode < 300) { + // return StockReplenishment.fromJson(jsonDecode(response.body)); + var resultsJson = json.decode(response.body)['data'] + ['consumable_replenishment'] as List; + // var resultsJson = json.decode(response.body).cast>(); + List stockList = await resultsJson + .map((json) => StockDepletion.fromJson(json)) + .toList(); + return stockList; + } else { + throw Exception('Failed to create Activity Participant Attendance.'); + } +} + +Future> updateConsumableDepletion( + List stockList, String? to_date) async { + // Transform the original list to the new structure + List> transformedList = + stockList.map((stock) => stock.toJson()).toList().map((item) { + return { + "id": item["id"], + "quantity": item["quantity"], + "quantity_in": item["quantity_in"], + "updated": to_date, + }; + }).toList(); + final response = await http.put( + Uri.parse('${AppConfig.campusAssetsBffApiUrl}/consumable_replenishment'), + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + 'accept': 'application/json', + 'Authorization': 'Bearer ${AppConfig.campusBffApiKey}', + }, + body: jsonEncode(transformedList), + ); + if (response.statusCode > 199 && response.statusCode < 300) { + // return StockReplenishment.fromJson(jsonDecode(response.body)); + var resultsJson = json.decode(response.body)['data'] + ['update_consumable_replenishment'] as List; + // var resultsJson = json.decode(response.body).cast>(); + List stockList = await resultsJson + .map((json) => StockDepletion.fromJson(json)) + .toList(); + showSuccessToast(); + return stockList; + } else { + throw Exception('Failed to create Activity Participant Attendance.'); + } +} + +Future> getStockListforDepletion( + int? organization_id, String to_date) async { + final response = await http.get( + Uri.parse( + '${AppConfig.campusAssetsBffApiUrl}/inventory_data_by_organization/$organization_id/$to_date'), + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + 'accept': 'application/json', + 'Authorization': 'Bearer ${AppConfig.campusBffApiKey}', + }, + ); + if (response.statusCode > 199 && response.statusCode < 300) { + var resultsJson = json.decode(response.body).cast>(); + List stockList = await resultsJson + .map((json) => StockDepletion.fromJson(json)) + .toList(); + return stockList; + } else { + throw Exception( + 'Failed to get Activity Participant Attendance report for organization ID $organization_id and activity'); + } +} diff --git a/campus/frontend/lib/avinya/asset_admin/lib/data/stock_repenishment.dart b/campus/frontend/lib/avinya/asset_admin/lib/data/stock_repenishment.dart index 333abb1c..60552326 100644 --- a/campus/frontend/lib/avinya/asset_admin/lib/data/stock_repenishment.dart +++ b/campus/frontend/lib/avinya/asset_admin/lib/data/stock_repenishment.dart @@ -1,9 +1,12 @@ import 'dart:ui'; +import 'package:flutter/material.dart'; import 'package:gallery/avinya/asset_admin/lib/data.dart'; +import 'package:gallery/widgets/success_message.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:gallery/config/app_config.dart'; +import 'package:oktoast/oktoast.dart'; class StockReplenishment { int? id; @@ -12,6 +15,7 @@ class StockReplenishment { int? person_id; int? organization_id; String? updated; + String? created; AvinyaType? avinya_type; double? quantity; double? quantity_in; @@ -26,6 +30,7 @@ class StockReplenishment { this.person_id, this.organization_id, this.updated, + this.created, this.avinya_type, this.quantity, this.quantity_in, @@ -44,6 +49,7 @@ class StockReplenishment { organization_id: json["organization_id"] == null ? null : json["organization_id"], updated: json["updated"] == null ? null : json["updated"], + created: json["created"] == null ? null : json["created"], avinya_type: json['avinya_type'] == null ? null : AvinyaType.fromJson(json['avinya_type']), @@ -77,15 +83,29 @@ class StockReplenishment { } Future> addConsumableReplenishment( - List stockList) async { + List stockList, + int? person_id, + int? organization_id, + String? to_date) async { + // Transform the original list to the new structure + List> transformedList = + stockList.map((stock) => stock.toJson()).toList().map((item) { + return { + "avinya_type_id": item["avinya_type_id"], + "consumable_id": item["consumable_id"], + "quantity": item["quantity"], + "quantity_in": item["quantity_in"] + }; + }).toList(); final response = await http.post( - Uri.parse('${AppConfig.campusAssetsBffApiUrl}/consumable_replenishment'), + Uri.parse( + '${AppConfig.campusAssetsBffApiUrl}/consumable_replenishment/$person_id/$organization_id/$to_date'), headers: { 'Content-Type': 'application/json; charset=UTF-8', 'accept': 'application/json', 'Authorization': 'Bearer ${AppConfig.campusBffApiKey}', }, - body: jsonEncode(stockList.map((stockList) => stockList.toJson()).toList()), + body: jsonEncode(transformedList), ); if (response.statusCode > 199 && response.statusCode < 300) { // return StockReplenishment.fromJson(jsonDecode(response.body)); @@ -101,6 +121,42 @@ Future> addConsumableReplenishment( } } +Future> updateConsumableReplenishment( + List stockList, String? to_date) async { + // Transform the original list to the new structure + List> transformedList = + stockList.map((stock) => stock.toJson()).toList().map((item) { + return { + "id": item["id"], + "quantity": item["quantity"], + "quantity_in": item["quantity_in"], + "updated": to_date, + }; + }).toList(); + final response = await http.put( + Uri.parse('${AppConfig.campusAssetsBffApiUrl}/consumable_replenishment'), + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + 'accept': 'application/json', + 'Authorization': 'Bearer ${AppConfig.campusBffApiKey}', + }, + body: jsonEncode(transformedList), + ); + if (response.statusCode > 199 && response.statusCode < 300) { + // return StockReplenishment.fromJson(jsonDecode(response.body)); + var resultsJson = json.decode(response.body)['data'] + ['update_consumable_replenishment'] as List; + // var resultsJson = json.decode(response.body).cast>(); + List stockList = await resultsJson + .map((json) => StockReplenishment.fromJson(json)) + .toList(); + showSuccessToast(); + return stockList; + } else { + throw Exception('Failed to create Activity Participant Attendance.'); + } +} + Future> getStockListforReplenishment( int? organization_id, String to_date) async { final response = await http.get( @@ -125,10 +181,7 @@ Future> getStockListforReplenishment( } Future> getConsumableMonthlyReport( - int organization_id, - int year, - int month - ) async { + int organization_id, int year, int month) async { final response = await http.get( Uri.parse( '${AppConfig.campusAssetsBffApiUrl}/consumable_monthly_report/$organization_id/$year/$month'), diff --git a/campus/frontend/lib/avinya/asset_admin/lib/screens/scaffold.dart b/campus/frontend/lib/avinya/asset_admin/lib/screens/scaffold.dart index 7ead5572..2c89e2e2 100644 --- a/campus/frontend/lib/avinya/asset_admin/lib/screens/scaffold.dart +++ b/campus/frontend/lib/avinya/asset_admin/lib/screens/scaffold.dart @@ -20,6 +20,7 @@ class _SMSScaffoldState extends State { bool isConsumableSectionHovered = false; bool isConsumableReportSectionHovered = false; bool isStockReplenishmentSectionHovered = false; + bool isStockDepletionSectionHovered = false; @override Widget build(BuildContext context) { @@ -32,8 +33,7 @@ class _SMSScaffoldState extends State { SideNavigationSectionTile( tileName: "Resource Allocation", route: "/resource_allocation_report", - icon: Icons.report - ), + icon: Icons.report), ]; consumableReportDestinations = [ @@ -44,7 +44,7 @@ class _SMSScaffoldState extends State { SideNavigationSectionTile( tileName: "Consumable Monthly Report", route: "/consumable_monthly_report", - icon: Icons.summarize_sharp), + icon: Icons.summarize_sharp), ]; return Scaffold( @@ -292,6 +292,47 @@ class _SMSScaffoldState extends State { ), ), ), + MouseRegion( + onEnter: (_) { + setState(() { + isStockDepletionSectionHovered = true; + }); + }, + onExit: (_) { + setState(() { + isStockDepletionSectionHovered = false; + }); + }, + child: Container( + decoration: BoxDecoration( + color: isStockDepletionSectionHovered + ? Colors.white.withOpacity(0.3) + : null, + borderRadius: BorderRadius.circular(15.0), + ), + margin: EdgeInsets.all(8.0), + child: ListTile( + leading: Icon(Icons.inventory, + color: Colors.white, size: 20.0), + title: Container( + margin: EdgeInsets.only(left: 12.0), + transform: + Matrix4.translationValues(-25, 0.0, 0.0), + child: Text( + "Stock Depletion", + style: TextStyle( + color: Colors.white, + fontSize: 12, + ), + ), + ), + onTap: () { + Navigator.pop(context); // Close the drawer + routeState.go('/stock_depletion'); + }, + ), + ), + ), SideNavigationSection( initialSectionHoveredValue: isConsumableReportSectionHovered, diff --git a/campus/frontend/lib/avinya/asset_admin/lib/screens/scaffold_body.dart b/campus/frontend/lib/avinya/asset_admin/lib/screens/scaffold_body.dart index 148d6a77..cee6026d 100644 --- a/campus/frontend/lib/avinya/asset_admin/lib/screens/scaffold_body.dart +++ b/campus/frontend/lib/avinya/asset_admin/lib/screens/scaffold_body.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:asset_admin/screens/empty_screen.dart'; import 'package:gallery/avinya/asset_admin/lib/screens/asset_dashboard_screen.dart'; import 'package:gallery/avinya/asset_admin/lib/screens/consumable_dashboard_screen.dart'; +import 'package:gallery/avinya/asset_admin/lib/screens/stock_depletion.dart'; import 'package:gallery/avinya/asset_admin/lib/screens/stock_replenishment.dart'; import 'package:gallery/avinya/asset_admin/lib/screens/consumable_monthly_report.dart'; import 'package:gallery/avinya/asset_admin/lib/screens/consumable_weekly_report.dart'; @@ -51,12 +52,19 @@ class SMSScaffoldBody extends StatelessWidget { key: ValueKey('stock_replenishment'), child: StockReplenishmentScreen(), ) - else if (currentRoute.pathTemplate.startsWith('/consumable_monthly_report')) + else if (currentRoute.pathTemplate.startsWith('/stock_depletion')) + const FadeTransitionPage( + key: ValueKey('stock_depletion'), + child: StockDepletionScreen(), + ) + else if (currentRoute.pathTemplate + .startsWith('/consumable_monthly_report')) const FadeTransitionPage( key: ValueKey('consumable_monthly_report'), child: ConsumableMonthlyReportScreen(), ) - else if (currentRoute.pathTemplate.startsWith('/consumable_weekly_report')) + else if (currentRoute.pathTemplate + .startsWith('/consumable_weekly_report')) const FadeTransitionPage( key: ValueKey('consumable_weekly_report'), child: ConsumableWeeklyReportScreen(), diff --git a/campus/frontend/lib/avinya/asset_admin/lib/screens/stock_depletion.dart b/campus/frontend/lib/avinya/asset_admin/lib/screens/stock_depletion.dart new file mode 100644 index 00000000..d7a8d20d --- /dev/null +++ b/campus/frontend/lib/avinya/asset_admin/lib/screens/stock_depletion.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:gallery/avinya/asset_admin/lib/widgets/stock_depletion.dart'; + +class StockDepletionScreen extends StatefulWidget { + const StockDepletionScreen({super.key}); + + @override + State createState() => _StockDepletionScreenState(); +} + +class _StockDepletionScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + title: Text("Stock Depletion"), + ), + body: SingleChildScrollView( + child: Container( + child: StockDepletionForm( + title: 'Stock Depletion', + ), + ), + ), + ); + } +} diff --git a/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_depletion.dart b/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_depletion.dart new file mode 100644 index 00000000..5114fc0a --- /dev/null +++ b/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_depletion.dart @@ -0,0 +1,452 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:gallery/avinya/asset_admin/lib/data/stock_depletion.dart'; +// import 'package:gallery/avinya/asset_admin/lib/data/stock_repenishment.dart'; +// import 'package:asset_admin/data/stock_repenishment.dart'; +import 'package:gallery/data/campus_apps_portal.dart'; +import 'package:gallery/data/person.dart'; +import 'package:intl/intl.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; + +class StockDepletionForm extends StatefulWidget { + const StockDepletionForm({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + State createState() => _StockDepletionState(); +} + +class _StockDepletionState extends State { + List _fetchedStockList = []; + List _fetchedStockListAfterSchool = []; + Organization? _fetchedOrganization; + bool _isFetching = true; + List _fetchedStudentList = []; + Person? _person; + String? formatted_date; + int? parent_org_id; + + bool _isSubmitting = false; + bool _isUpdate = false; + //calendar specific variables + DateTime? _selectedDay; + + List _controllers = []; + DateTime _selectedDate = DateTime.now(); + + late DataTableSource _data; + List columnNames = []; + List> attendanceList = []; + var _selectedValue; + var activityId = 0; + bool _isDisplayErrorMessage = false; + + var today = DateTime.now(); + + // DateTime? _selectedDate; + late TextEditingController _controller; + Future _pickDate(BuildContext context) async { + final DateTime? picked = await showDatePicker( + context: context, + initialDate: _selectedDate ?? DateTime.now(), + firstDate: DateTime(2000), + lastDate: DateTime(2101), + ); + if (picked != null && picked != _selectedDate) { + setState(() { + _selectedDate = picked; + }); + } + + int? parentOrgId = + campusAppsPortalInstance.getUserPerson().organization!.id; + if (parentOrgId != null) { + setState(() { + _isFetching = true; // Set _isFetching to true before starting the fetch + }); + try { + setState(() { + refreshState(DateFormat('yyyy-MM-dd').format(_selectedDate!)); + }); + } catch (error) { + // Handle any errors that occur during the fetch + setState(() { + _isFetching = false; // Set _isFetching to false in case of error + }); + // Perform error handling, e.g., show an error message + } + } + } + + @override + void initState() { + super.initState(); + // var today = DateTime.now(); + activityId = campusAppsPortalInstance.activityIds['homeroom']!; + _selectedDate = DateTime.now(); + _fetchInitialData(); + // selectWeek(today, activityId); + } + + void _initializeControllers() { + _controllers = _fetchedStockList.map((item) { + DateTime isToday = DateTime.now(); + return TextEditingController( + text: + _selectedDate == isToday ? '' : item.quantity_in?.toString() ?? '', + ); + }).toList(); + } + + @override + void dispose() { + // Perform any cleanup tasks here + for (var controller in _controllers) { + controller.dispose(); + } + super.dispose(); + } + + Future _fetchInitialData() async { + int? parentOrgId = + campusAppsPortalInstance.getUserPerson().organization?.id; + if (parentOrgId != null) { + setState(() { + _isFetching = true; // Show loading indicator + }); + try { + await refreshState(DateFormat('yyyy-MM-dd').format(_selectedDate!)); + } catch (error) { + // Handle any errors that occur during the fetch + // You can show an error message or take appropriate actions here + } finally { + setState(() { + _isFetching = false; // Hide loading indicator + }); + } + } + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + // Perform any initialization that depends on context + // _pickDate(context); + } + + void updateSelected(int index, bool value, List selected) { + setState(() { + selected[index] = value; + }); + } + + String formatDateTime(DateTime dateTime) { + return "${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')} " + "${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}:${dateTime.second.toString().padLeft(2, '0')}"; + } + + Future refreshState(String? newValue) async { + setState(() { + _isFetching = true; // Set _isFetching to true before starting the fetch + }); + int? parentOrgId = + campusAppsPortalInstance.getUserPerson().organization!.id; + _selectedValue = newValue ?? null; + + _fetchedStockList = await getStockListforDepletion(parentOrgId, newValue!); + Person person = campusAppsPortalInstance.getUserPerson(); + + var formattedDate = formatDateTime(_selectedDate!); + + _initializeControllers(); + _fetchedStockList.map((item) { + if (item.quantity_in != 0) { + _isUpdate = true; + } + }).toList(); + + if (mounted) { + setState(() { + _fetchedStockList; + _person = person; + _fetchedStockList.map((item) { + if (item.quantity_in != 0) { + _isUpdate = true; + } + }).toList(); + formatted_date = formattedDate; + parent_org_id = parentOrgId; + _isFetching = + false; // Ensure _isFetching is set to false after fetching + }); + } + } + + Future _handleSubmit() async { + setState(() { + _isSubmitting = true; + }); + + if (_isUpdate) { + var result = await updateConsumableDepletion( + _fetchedStockList, this.formatted_date); + } else { + var result = await addConsumableDepletion(_fetchedStockList, + this._person?.id, this.parent_org_id, this.formatted_date); + } + + // Add your form submission logic here + + setState(() { + _isSubmitting = false; + _isUpdate = true; + }); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: campusAppsPortalPersonMetaDataInstance + .getGroups() + .contains('Student') + ? Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: Text( + "Please go to 'Mark Attendance' Page", + style: TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold), + ), + ), + ) + : Padding( + padding: const EdgeInsets.all(30.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Align( + alignment: Alignment.centerLeft, + child: Container( + width: 200, + child: TextField( + readOnly: true, + onTap: () => _pickDate(context), + decoration: InputDecoration( + labelText: 'Select Date', + suffixIcon: Icon(Icons.calendar_today), + ), + controller: TextEditingController( + text: _selectedDate == null + ? '' + : '${_selectedDate!.toLocal()}'.split(' ')[0], + ), + ), + ), + ), + SizedBox(height: 16.0), + _isFetching + ? Center( + child: SpinKitCircle( + color: Colors.deepPurpleAccent, + size: 50, + ), + ) + : _fetchedStockList.isNotEmpty + ? LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: ConstrainedBox( + constraints: BoxConstraints( + minWidth: constraints.maxWidth, + ), + child: DataTable( + columns: const [ + DataColumn( + label: Text( + "Product Name", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + DataColumn( + label: Text( + "Quantity", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + DataColumn( + label: Text( + "On Hand", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + DataColumn( + label: Text( + "Total", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + DataColumn( + label: Text( + "Unit", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + ], + rows: _fetchedStockList + .asMap() + .entries + .map((entry) { + int index = entry.key; + StockDepletion item = entry.value; + double totalQuantityIn = + (item.quantity_in ?? 0.0) + + (item.quantity ?? 0.0); + DateTime isToday = DateTime.now(); + _controller = TextEditingController( + text: _selectedDate != null && + _selectedDate == isToday + ? '' + : item.quantity_in?.toString() ?? + '', + ); + // _controller.addListener(() { + // final text = _controller.text; + // final quantity_in = + // int.tryParse(text); + // if (quantity_in != null) { + // setState(() { + // item.quantity_in = quantity_in; + // totalQuantityIn = + // (item.quantity_in ?? 0) + + // (item.quantity ?? 0); + // }); + // } + // }); + return DataRow(cells: [ + DataCell( + Text(item.consumable!.name!)), + DataCell( + Padding( + padding: + const EdgeInsets.symmetric( + vertical: 6.0, + horizontal: 2.0), + child: TextField( + controller: _controllers[index], + keyboardType: TextInputType + .numberWithOptions( + decimal: true), + inputFormatters: [ + FilteringTextInputFormatter + .allow(RegExp( + r'^\d*\.?\d{0,2}')), + ], + onChanged: + (String value) async { + double? quantity_in = + double.tryParse(value); + if (quantity_in != null) { + print( + 'Quantity: $quantity_in'); + setState(() { + item.quantity_in = + quantity_in; + totalQuantityIn = + (item.quantity_in ?? + 0.0) + + (item.quantity ?? + 0.0); + }); + } + }, + decoration: InputDecoration( + hintText: item.quantity_in + ?.toString() ?? + 'Enter quantity', + border: OutlineInputBorder(), + ), + ), + ), + ), + DataCell( + Text(item.quantity!.toString())), + DataCell( + Text(totalQuantityIn.toString())), + DataCell(Text(item + .resource_property!.value! + .toString())), + ]); + }).toList(), + ), + ), + ); + }, + ) + : Center( + child: Text('No data found'), + ), + if (!_isFetching && _fetchedStockList.isNotEmpty) + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Align( + alignment: Alignment.centerRight, + child: SizedBox( + width: 120, // Set the width of the button + // child: ElevatedButton( + // onPressed: _isSubmitting ? null : _handleSubmit, + // style: ElevatedButton.styleFrom( + // primary: Colors.deepPurple, // Change button color + // elevation: 4, // Add elevation + // ), + // child: Padding( + // padding: const EdgeInsets.all(8.0), + // child: Text( + // _isUpdate ? 'Update' : 'Save', + // style: TextStyle( + // fontSize: 16, // Increase font size + // fontWeight: FontWeight.bold, + // color: Colors.white, // Change text color + // ), + // ), + // ), + // ), + child: ElevatedButton( + onPressed: _isSubmitting ? null : _handleSubmit, + style: ButtonStyle( + padding: MaterialStateProperty.all( + EdgeInsets.all(16.0)), + textStyle: MaterialStateProperty.all( + const TextStyle(fontSize: 16), + ), + elevation: MaterialStateProperty.all(20), + backgroundColor: + MaterialStateProperty.all(Colors.greenAccent), + foregroundColor: + MaterialStateProperty.all(Colors.black), + ), + child: Text( + _isUpdate ? 'Update' : 'Save', + ), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_replenishment.dart b/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_replenishment.dart index 18d0a94c..4777cc3c 100644 --- a/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_replenishment.dart +++ b/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_replenishment.dart @@ -34,6 +34,9 @@ class _StockReplenishmentState extends State { Organization? _fetchedOrganization; bool _isFetching = true; List _fetchedStudentList = []; + Person? _person; + String? formatted_date; + int? parent_org_id; bool _isSubmitting = false; bool _isUpdate = false; @@ -169,10 +172,23 @@ class _StockReplenishmentState extends State { var formattedDate = formatDateTime(_selectedDate!); _initializeControllers(); + _fetchedStockList.map((item) { + if (item.quantity_in != 0) { + _isUpdate = true; + } + }).toList(); if (mounted) { setState(() { _fetchedStockList; + _person = person; + _fetchedStockList.map((item) { + if (item.quantity_in != 0) { + _isUpdate = true; + } + }).toList(); + formatted_date = formattedDate; + parent_org_id = parentOrgId; _isFetching = false; // Ensure _isFetching is set to false after fetching }); @@ -184,7 +200,13 @@ class _StockReplenishmentState extends State { _isSubmitting = true; }); - var result = await addConsumableReplenishment(_fetchedStockList); + if (_isUpdate) { + var result = await updateConsumableReplenishment( + _fetchedStockList, this.formatted_date); + } else { + var result = await addConsumableReplenishment(_fetchedStockList, + this._person?.id, this.parent_org_id, this.formatted_date); + } // Add your form submission logic here diff --git a/campus/frontend/lib/avinya/asset_admin/pubspec.yaml b/campus/frontend/lib/avinya/asset_admin/pubspec.yaml index a6f35493..7ed9fa95 100644 --- a/campus/frontend/lib/avinya/asset_admin/pubspec.yaml +++ b/campus/frontend/lib/avinya/asset_admin/pubspec.yaml @@ -34,6 +34,7 @@ dependencies: url: https://github.com/google/flutter-desktop-embedding.git path: plugins/window_size month_year_picker: ^0.3.0+1 + oktoast: ^3.4.0 dev_dependencies: flutter_lints: ^2.0.1 diff --git a/campus/frontend/lib/widgets/success_message.dart b/campus/frontend/lib/widgets/success_message.dart new file mode 100644 index 00000000..1ed52499 --- /dev/null +++ b/campus/frontend/lib/widgets/success_message.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:oktoast/oktoast.dart'; + +void showSuccessToast() { + showToastWidget( + Container( + padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(25.0), + color: Colors.green[600], + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.check, color: Colors.white), + SizedBox(width: 12.0), + Text( + 'Stock Replenishment Successfully Updated!', + style: TextStyle(color: Colors.white), + ), + ], + ), + ), + duration: Duration(seconds: 3), + position: ToastPosition.bottom, + ); +} diff --git a/campus/frontend/pubspec.yaml b/campus/frontend/pubspec.yaml index 485aec2e..25847b55 100644 --- a/campus/frontend/pubspec.yaml +++ b/campus/frontend/pubspec.yaml @@ -62,6 +62,7 @@ dependencies: pdf: ^3.6.4 month_year_picker: ^0.3.0+1 + oktoast: ^3.4.0 dev_dependencies: flutter_test: From 484f52705d7d2aee4a78374a0f3edf5d68a221c7 Mon Sep 17 00:00:00 2001 From: YujithIsura Date: Tue, 23 Jul 2024 17:37:54 +0530 Subject: [PATCH 3/4] consumable module completed --- .../asset_admin/lib/data/stock_depletion.dart | 35 +- .../lib/data/stock_repenishment.dart | 11 +- .../lib/widgets/stock_depletion.dart | 495 +++++++++-------- .../lib/widgets/stock_replenishment.dart | 501 ++++++++++-------- .../frontend/lib/widgets/success_message.dart | 4 +- 5 files changed, 601 insertions(+), 445 deletions(-) diff --git a/campus/frontend/lib/avinya/asset_admin/lib/data/stock_depletion.dart b/campus/frontend/lib/avinya/asset_admin/lib/data/stock_depletion.dart index 6b755ccd..2f13e5cd 100644 --- a/campus/frontend/lib/avinya/asset_admin/lib/data/stock_depletion.dart +++ b/campus/frontend/lib/avinya/asset_admin/lib/data/stock_depletion.dart @@ -4,7 +4,6 @@ import 'package:gallery/avinya/asset_admin/lib/data.dart'; import 'package:gallery/widgets/success_message.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; - import 'package:gallery/config/app_config.dart'; import 'package:oktoast/oktoast.dart'; @@ -18,6 +17,8 @@ class StockDepletion { String? created; AvinyaType? avinya_type; double? quantity; + double? total_quantity; + double? prev_quantity; double? quantity_in; double? quantity_out; ResourceProperty? resource_property; @@ -33,6 +34,8 @@ class StockDepletion { this.created, this.avinya_type, this.quantity, + this.prev_quantity, + this.total_quantity, this.quantity_in, this.quantity_out, this.resource_property, @@ -53,6 +56,9 @@ class StockDepletion { ? null : AvinyaType.fromJson(json['avinya_type']), quantity: json["quantity"] == null ? null : json["quantity"], + prev_quantity: + json["prev_quantity"] == null ? null : json["prev_quantity"], + total_quantity: json["quantity"] == null ? null : json["quantity"], quantity_in: json["quantity_in"] == null ? null : json["quantity_in"], quantity_out: json["quantity_out"] == null ? null : json["quantity_out"], @@ -73,6 +79,8 @@ class StockDepletion { "updated": updated == null ? null : updated, "avinya_type": avinya_type == null ? null : avinya_type, "quantity": quantity == null ? null : quantity, + "prev_quantity": prev_quantity == null ? null : prev_quantity, + "total_quantity": quantity == null ? null : quantity, "quantity_in": quantity_in == null ? null : quantity_in, "quantity_out": quantity_out == null ? null : quantity_out, "resource_property": @@ -85,20 +93,22 @@ Future> addConsumableDepletion( List stockList, int? person_id, int? organization_id, - String? to_date) async { + String? to_date, + bool _isUpdate) async { // Transform the original list to the new structure List> transformedList = stockList.map((stock) => stock.toJson()).toList().map((item) { return { "avinya_type_id": item["avinya_type_id"], "consumable_id": item["consumable_id"], - "quantity": item["quantity"], - "quantity_in": item["quantity_in"] + "quantity": item["quantity"] - item["quantity_out"], + "quantity_out": item["quantity_out"], + "prev_quantity": _isUpdate ? item["prev_quantity"] : item["quantity"] }; }).toList(); final response = await http.post( Uri.parse( - '${AppConfig.campusAssetsBffApiUrl}/consumable_replenishment/$person_id/$organization_id/$to_date'), + '${AppConfig.campusAssetsBffApiUrl}/consumable_depletion/$person_id/$organization_id/$to_date'), headers: { 'Content-Type': 'application/json; charset=UTF-8', 'accept': 'application/json', @@ -108,12 +118,13 @@ Future> addConsumableDepletion( ); if (response.statusCode > 199 && response.statusCode < 300) { // return StockReplenishment.fromJson(jsonDecode(response.body)); - var resultsJson = json.decode(response.body)['data'] - ['consumable_replenishment'] as List; + var resultsJson = json.decode(response.body)['data']['consumable_depletion'] + as List; // var resultsJson = json.decode(response.body).cast>(); List stockList = await resultsJson .map((json) => StockDepletion.fromJson(json)) .toList(); + showSuccessToast("Stock Depletion Successfully Saved!"); return stockList; } else { throw Exception('Failed to create Activity Participant Attendance.'); @@ -127,13 +138,13 @@ Future> updateConsumableDepletion( stockList.map((stock) => stock.toJson()).toList().map((item) { return { "id": item["id"], - "quantity": item["quantity"], - "quantity_in": item["quantity_in"], + "quantity": item["quantity"] - item["quantity_out"], + "quantity_out": item["quantity_out"], "updated": to_date, }; }).toList(); final response = await http.put( - Uri.parse('${AppConfig.campusAssetsBffApiUrl}/consumable_replenishment'), + Uri.parse('${AppConfig.campusAssetsBffApiUrl}/consumable_depletion'), headers: { 'Content-Type': 'application/json; charset=UTF-8', 'accept': 'application/json', @@ -144,12 +155,12 @@ Future> updateConsumableDepletion( if (response.statusCode > 199 && response.statusCode < 300) { // return StockReplenishment.fromJson(jsonDecode(response.body)); var resultsJson = json.decode(response.body)['data'] - ['update_consumable_replenishment'] as List; + ['update_consumable_depletion'] as List; // var resultsJson = json.decode(response.body).cast>(); List stockList = await resultsJson .map((json) => StockDepletion.fromJson(json)) .toList(); - showSuccessToast(); + showSuccessToast("Stock Depletion Successfully Updated!"); return stockList; } else { throw Exception('Failed to create Activity Participant Attendance.'); diff --git a/campus/frontend/lib/avinya/asset_admin/lib/data/stock_repenishment.dart b/campus/frontend/lib/avinya/asset_admin/lib/data/stock_repenishment.dart index 74732136..a4866201 100644 --- a/campus/frontend/lib/avinya/asset_admin/lib/data/stock_repenishment.dart +++ b/campus/frontend/lib/avinya/asset_admin/lib/data/stock_repenishment.dart @@ -21,6 +21,7 @@ class StockReplenishment { double? prev_quantity; double? quantity_in; double? quantity_out; + double? total_quantity; ResourceProperty? resource_property; Consumable? consumable; @@ -37,6 +38,7 @@ class StockReplenishment { this.prev_quantity, this.quantity_in, this.quantity_out, + this.total_quantity, this.resource_property, this.consumable}); @@ -58,6 +60,7 @@ class StockReplenishment { quantity: json["quantity"] == null ? null : json["quantity"], prev_quantity: json["prev_quantity"] == null ? null : json["prev_quantity"], + total_quantity: json["quantity"] == null ? null : json["quantity"], quantity_in: json["quantity_in"] == null ? null : json["quantity_in"], quantity_out: json["quantity_out"] == null ? null : json["quantity_out"], @@ -79,6 +82,7 @@ class StockReplenishment { "avinya_type": avinya_type == null ? null : avinya_type, "quantity": quantity == null ? null : quantity, "prev_quantity": prev_quantity == null ? null : prev_quantity, + "total_quantity": quantity == null ? null : quantity, "quantity_in": quantity_in == null ? null : quantity_in, "quantity_out": quantity_out == null ? null : quantity_out, "resource_property": @@ -99,7 +103,7 @@ Future> addConsumableReplenishment( return { "avinya_type_id": item["avinya_type_id"], "consumable_id": item["consumable_id"], - "quantity": item["quantity"], + "quantity": item["quantity_in"] + item["quantity"], "quantity_in": item["quantity_in"], "prev_quantity": _isUpdate ? item["prev_quantity"] : item["quantity"] }; @@ -122,6 +126,7 @@ Future> addConsumableReplenishment( List stockList = await resultsJson .map((json) => StockReplenishment.fromJson(json)) .toList(); + showSuccessToast("Stock Replenishment Successfully Saved!"); return stockList; } else { throw Exception('Failed to create Activity Participant Attendance.'); @@ -135,7 +140,7 @@ Future> updateConsumableReplenishment( stockList.map((stock) => stock.toJson()).toList().map((item) { return { "id": item["id"], - "quantity": item["quantity"], + "quantity": item["quantity_in"] + item["prev_quantity"], "quantity_in": item["quantity_in"], "updated": to_date, }; @@ -157,7 +162,7 @@ Future> updateConsumableReplenishment( List stockList = await resultsJson .map((json) => StockReplenishment.fromJson(json)) .toList(); - showSuccessToast(); + showSuccessToast("Stock Replenishment Successfully Updated!"); return stockList; } else { throw Exception('Failed to create Activity Participant Attendance.'); diff --git a/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_depletion.dart b/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_depletion.dart index 5114fc0a..f5643d48 100644 --- a/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_depletion.dart +++ b/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_depletion.dart @@ -1,10 +1,7 @@ import 'dart:ui'; - +import 'package:gallery/avinya/asset_admin/lib/data/stock_depletion.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:gallery/avinya/asset_admin/lib/data/stock_depletion.dart'; -// import 'package:gallery/avinya/asset_admin/lib/data/stock_repenishment.dart'; -// import 'package:asset_admin/data/stock_repenishment.dart'; import 'package:gallery/data/campus_apps_portal.dart'; import 'package:gallery/data/person.dart'; import 'package:intl/intl.dart'; @@ -13,15 +10,6 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; class StockDepletionForm extends StatefulWidget { const StockDepletionForm({Key? key, required this.title}) : super(key: key); - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - final String title; @override @@ -31,8 +19,10 @@ class StockDepletionForm extends StatefulWidget { class _StockDepletionState extends State { List _fetchedStockList = []; List _fetchedStockListAfterSchool = []; + List result = []; Organization? _fetchedOrganization; bool _isFetching = true; + bool _isAfter = false; List _fetchedStudentList = []; Person? _person; String? formatted_date; @@ -40,11 +30,16 @@ class _StockDepletionState extends State { bool _isSubmitting = false; bool _isUpdate = false; + bool _showQtyIn = false; + bool _backDate = false; + double prevQtyIn = 0.00; + int prevIndex = 0; //calendar specific variables DateTime? _selectedDay; List _controllers = []; DateTime _selectedDate = DateTime.now(); + DateTime _fetcheddDate = DateTime.now(); late DataTableSource _data; List columnNames = []; @@ -65,20 +60,26 @@ class _StockDepletionState extends State { lastDate: DateTime(2101), ); if (picked != null && picked != _selectedDate) { + DateTime isToday = DateTime.now(); + _isAfter = picked.isAfter(isToday); setState(() { _selectedDate = picked; + _isAfter = _isAfter; + prevQtyIn = 0.00; }); } int? parentOrgId = campusAppsPortalInstance.getUserPerson().organization!.id; + if (parentOrgId != null) { setState(() { _isFetching = true; // Set _isFetching to true before starting the fetch }); try { setState(() { - refreshState(DateFormat('yyyy-MM-dd').format(_selectedDate!)); + refreshState( + DateFormat('yyyy-MM-dd HH:mm:ss').format(_selectedDate!)); }); } catch (error) { // Handle any errors that occur during the fetch @@ -104,8 +105,9 @@ class _StockDepletionState extends State { _controllers = _fetchedStockList.map((item) { DateTime isToday = DateTime.now(); return TextEditingController( - text: - _selectedDate == isToday ? '' : item.quantity_in?.toString() ?? '', + text: _showQtyIn ? item.quantity_out?.toString() : '0', + // text: + // _selectedDate == isToday ? '' : item.quantity_out?.toString() ?? '', ); }).toList(); } @@ -127,7 +129,8 @@ class _StockDepletionState extends State { _isFetching = true; // Show loading indicator }); try { - await refreshState(DateFormat('yyyy-MM-dd').format(_selectedDate!)); + await refreshState( + DateFormat('yyyy-MM-dd HH:mm:ss').format(_selectedDate!)); } catch (error) { // Handle any errors that occur during the fetch // You can show an error message or take appropriate actions here @@ -142,8 +145,6 @@ class _StockDepletionState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - // Perform any initialization that depends on context - // _pickDate(context); } void updateSelected(int index, bool value, List selected) { @@ -168,12 +169,68 @@ class _StockDepletionState extends State { _fetchedStockList = await getStockListforDepletion(parentOrgId, newValue!); Person person = campusAppsPortalInstance.getUserPerson(); - var formattedDate = formatDateTime(_selectedDate!); + var formattedDate = formatDateTime(_selectedDate); + + for (var item in _fetchedStockList) { + DateTime itemDate = DateTime.parse(item.updated!).toLocal(); + DateTime itemDateOnly = + DateTime(itemDate.year, itemDate.month, itemDate.day); + DateTime currentDate = DateTime.now().toLocal(); + if (_selectedDate.year == currentDate.year && + _selectedDate.month == currentDate.month && + _selectedDate.day == currentDate.day) { + if (_selectedDate.isAfter(itemDateOnly) && + item.quantity_out != 0 && + _selectedDate.day != itemDateOnly.day) { + _showQtyIn = false; + _backDate = false; + } else if (_selectedDate.isBefore(itemDateOnly) && + item.quantity_out != 0 && + _selectedDate.day != itemDateOnly.day) { + _showQtyIn = false; + _backDate = false; + } else if (item.quantity_out != 0) { + _isSubmitting = false; + _showQtyIn = true; + _backDate = false; + _isUpdate = true; + } else { + _isSubmitting = false; + _showQtyIn = false; + _backDate = false; + _isUpdate = false; + } + } else if (_selectedDate.isBefore(currentDate)) { + if (_selectedDate.isAfter(itemDateOnly) && item.quantity_out != 0) { + _isSubmitting = false; + _showQtyIn = false; + _backDate = false; + _isUpdate = true; + break; + } else if (_selectedDate.isBefore(itemDateOnly)) { + _isSubmitting = true; + _showQtyIn = true; + _backDate = true; + _isUpdate = true; + } else { + _isSubmitting = true; + _backDate = false; + _showQtyIn = false; + _isUpdate = false; + } + } else { + _isSubmitting = false; + _showQtyIn = false; + _backDate = false; + _isUpdate = false; + } + } - _initializeControllers(); _fetchedStockList.map((item) { - if (item.quantity_in != 0) { - _isUpdate = true; + if (item.quantity_out != 0) { + if (item.updated != null) { + _fetcheddDate = DateTime.parse(item.updated!); + } } }).toList(); @@ -181,17 +238,19 @@ class _StockDepletionState extends State { setState(() { _fetchedStockList; _person = person; - _fetchedStockList.map((item) { - if (item.quantity_in != 0) { - _isUpdate = true; - } - }).toList(); + _isSubmitting = _isSubmitting; + _backDate = _backDate; + _showQtyIn = _showQtyIn; + _fetcheddDate = _fetcheddDate; + _isUpdate = _isUpdate; formatted_date = formattedDate; parent_org_id = parentOrgId; _isFetching = false; // Ensure _isFetching is set to false after fetching }); } + + _initializeControllers(); } Future _handleSubmit() async { @@ -199,17 +258,16 @@ class _StockDepletionState extends State { _isSubmitting = true; }); - if (_isUpdate) { - var result = await updateConsumableDepletion( + if (_showQtyIn || _isUpdate) { + result = await updateConsumableDepletion( _fetchedStockList, this.formatted_date); } else { - var result = await addConsumableDepletion(_fetchedStockList, - this._person?.id, this.parent_org_id, this.formatted_date); + result = await addConsumableDepletion(_fetchedStockList, this._person?.id, + this.parent_org_id, this.formatted_date, _isUpdate); } - // Add your form submission logic here - setState(() { + refreshState(DateFormat('yyyy-MM-dd HH:mm:ss').format(_selectedDate)); _isSubmitting = false; _isUpdate = true; }); @@ -263,187 +321,208 @@ class _StockDepletionState extends State { size: 50, ), ) - : _fetchedStockList.isNotEmpty - ? LayoutBuilder( - builder: (context, constraints) { - return SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: ConstrainedBox( - constraints: BoxConstraints( - minWidth: constraints.maxWidth, - ), - child: DataTable( - columns: const [ - DataColumn( - label: Text( - "Product Name", - style: TextStyle( - fontWeight: FontWeight.bold), - )), - DataColumn( - label: Text( - "Quantity", - style: TextStyle( - fontWeight: FontWeight.bold), - )), - DataColumn( - label: Text( - "On Hand", - style: TextStyle( - fontWeight: FontWeight.bold), - )), - DataColumn( - label: Text( - "Total", - style: TextStyle( - fontWeight: FontWeight.bold), - )), - DataColumn( - label: Text( - "Unit", - style: TextStyle( - fontWeight: FontWeight.bold), - )), - ], - rows: _fetchedStockList - .asMap() - .entries - .map((entry) { - int index = entry.key; - StockDepletion item = entry.value; - double totalQuantityIn = - (item.quantity_in ?? 0.0) + - (item.quantity ?? 0.0); - DateTime isToday = DateTime.now(); - _controller = TextEditingController( - text: _selectedDate != null && - _selectedDate == isToday - ? '' - : item.quantity_in?.toString() ?? - '', - ); - // _controller.addListener(() { - // final text = _controller.text; - // final quantity_in = - // int.tryParse(text); - // if (quantity_in != null) { - // setState(() { - // item.quantity_in = quantity_in; - // totalQuantityIn = - // (item.quantity_in ?? 0) + - // (item.quantity ?? 0); - // }); - // } - // }); - return DataRow(cells: [ - DataCell( - Text(item.consumable!.name!)), - DataCell( - Padding( - padding: - const EdgeInsets.symmetric( + : _backDate != true + ? _fetchedStockList.isNotEmpty + ? LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: ConstrainedBox( + constraints: BoxConstraints( + minWidth: constraints.maxWidth, + ), + child: DataTable( + columns: const [ + DataColumn( + label: Text( + "Product Name", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + DataColumn( + label: Text( + "Quantity", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + DataColumn( + label: Text( + "On Hand", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + DataColumn( + label: Text( + "Total", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + DataColumn( + label: Text( + "Unit", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + ], + rows: _fetchedStockList + .asMap() + .entries + .map((entry) { + int index = entry.key; + StockDepletion item = entry.value; + + _controller = TextEditingController( + text: _isAfter + ? '0' + : item.quantity_out + ?.toString() ?? + '', + ); + return DataRow(cells: [ + DataCell( + Text(item.consumable!.name!)), + DataCell( + Padding( + padding: const EdgeInsets + .symmetric( vertical: 6.0, horizontal: 2.0), - child: TextField( - controller: _controllers[index], - keyboardType: TextInputType - .numberWithOptions( - decimal: true), - inputFormatters: [ - FilteringTextInputFormatter - .allow(RegExp( - r'^\d*\.?\d{0,2}')), - ], - onChanged: - (String value) async { - double? quantity_in = - double.tryParse(value); - if (quantity_in != null) { - print( - 'Quantity: $quantity_in'); - setState(() { - item.quantity_in = - quantity_in; - totalQuantityIn = - (item.quantity_in ?? - 0.0) + - (item.quantity ?? - 0.0); - }); - } - }, - decoration: InputDecoration( - hintText: item.quantity_in - ?.toString() ?? - 'Enter quantity', - border: OutlineInputBorder(), + child: TextField( + controller: + _controllers[index], + keyboardType: TextInputType + .numberWithOptions( + decimal: true), + inputFormatters: [ + FilteringTextInputFormatter + .allow(RegExp( + r'^\d*\.?\d{0,2}')), + ], + onChanged: + (String value) async { + double? quantity_out = + double.tryParse( + value); + if (quantity_out != + null) { + print( + 'Quantity: $quantity_out'); + setState(() { + item.quantity_out = + quantity_out; + prevQtyIn = (item + .quantity_out ?? + 0.0); + prevIndex = index; + + if (_showQtyIn) { + item.total_quantity = + (item.prev_quantity ?? + 0.0) - + (item.quantity_out ?? + 0.0); + } else { + item.total_quantity = + (item.quantity ?? + 0.0) - + double.tryParse( + value)!; + } + }); + } + }, + decoration: InputDecoration( + border: + OutlineInputBorder(), + ), + enabled: !_isSubmitting, + ), ), ), - ), - ), - DataCell( - Text(item.quantity!.toString())), - DataCell( - Text(totalQuantityIn.toString())), - DataCell(Text(item - .resource_property!.value! - .toString())), - ]); - }).toList(), - ), - ), - ); - }, - ) - : Center( - child: Text('No data found'), - ), + DataCell( + Text(_showQtyIn + ? item.prev_quantity! + .toString() + : item.quantity! + .toString()), + ), + DataCell(Text(item.total_quantity + .toString())), + DataCell(Text(item + .resource_property!.value! + .toString())), + ]); + }).toList(), + ), + ), + ); + }, + ) + : Center( + child: Text('No data found'), + ) + : Text("Please Select Valid Date"), if (!_isFetching && _fetchedStockList.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: Align( - alignment: Alignment.centerRight, - child: SizedBox( - width: 120, // Set the width of the button - // child: ElevatedButton( - // onPressed: _isSubmitting ? null : _handleSubmit, - // style: ElevatedButton.styleFrom( - // primary: Colors.deepPurple, // Change button color - // elevation: 4, // Add elevation - // ), - // child: Padding( - // padding: const EdgeInsets.all(8.0), - // child: Text( - // _isUpdate ? 'Update' : 'Save', - // style: TextStyle( - // fontSize: 16, // Increase font size - // fontWeight: FontWeight.bold, - // color: Colors.white, // Change text color - // ), - // ), - // ), - // ), - child: ElevatedButton( - onPressed: _isSubmitting ? null : _handleSubmit, - style: ButtonStyle( - padding: MaterialStateProperty.all( - EdgeInsets.all(16.0)), - textStyle: MaterialStateProperty.all( - const TextStyle(fontSize: 16), + _backDate != true + ? Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Align( + alignment: Alignment.centerRight, + child: SizedBox( + width: 120, + child: ElevatedButton( + onPressed: + _isSubmitting ? null : _handleSubmit, + style: ButtonStyle( + padding: MaterialStateProperty.all( + EdgeInsets.all(16.0)), + textStyle: + MaterialStateProperty.resolveWith( + (states) { + if (states + .contains(MaterialState.disabled)) { + return TextStyle( + fontSize: 16, color: Colors.grey); + } + return TextStyle( + fontSize: 16, color: Colors.black); + }), + elevation: + MaterialStateProperty.resolveWith( + (states) { + if (states + .contains(MaterialState.disabled)) { + return 0.0; + } + return 20.0; + }), + backgroundColor: + MaterialStateProperty.resolveWith( + (states) { + if (states + .contains(MaterialState.disabled)) { + return Colors.grey.shade400; + } + return Colors.greenAccent; + }), + foregroundColor: + MaterialStateProperty.resolveWith( + (states) { + if (states + .contains(MaterialState.disabled)) { + return Colors.grey.shade700; + } + return Colors.black; + }), + ), + child: Text( + _isUpdate ? 'Update' : 'Save', + ), + ), ), - elevation: MaterialStateProperty.all(20), - backgroundColor: - MaterialStateProperty.all(Colors.greenAccent), - foregroundColor: - MaterialStateProperty.all(Colors.black), ), - child: Text( - _isUpdate ? 'Update' : 'Save', - ), - ), - ), - ), - ), + ) + : Text(""), ], ), ), diff --git a/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_replenishment.dart b/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_replenishment.dart index 06eea79a..897ae33f 100644 --- a/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_replenishment.dart +++ b/campus/frontend/lib/avinya/asset_admin/lib/widgets/stock_replenishment.dart @@ -1,9 +1,7 @@ import 'dart:ui'; - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:gallery/avinya/asset_admin/lib/data/stock_repenishment.dart'; -// import 'package:asset_admin/data/stock_repenishment.dart'; import 'package:gallery/data/campus_apps_portal.dart'; import 'package:gallery/data/person.dart'; import 'package:intl/intl.dart'; @@ -13,15 +11,6 @@ class StockReplenishmentForm extends StatefulWidget { const StockReplenishmentForm({Key? key, required this.title}) : super(key: key); - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - final String title; @override @@ -31,6 +20,7 @@ class StockReplenishmentForm extends StatefulWidget { class _StockReplenishmentState extends State { List _fetchedStockList = []; List _fetchedStockListAfterSchool = []; + List result = []; Organization? _fetchedOrganization; bool _isFetching = true; bool _isAfter = false; @@ -41,11 +31,16 @@ class _StockReplenishmentState extends State { bool _isSubmitting = false; bool _isUpdate = false; + bool _showQtyIn = false; + bool _backDate = false; + double prevQtyIn = 0.00; + int prevIndex = 0; //calendar specific variables DateTime? _selectedDay; List _controllers = []; DateTime _selectedDate = DateTime.now(); + DateTime _fetcheddDate = DateTime.now(); late DataTableSource _data; List columnNames = []; @@ -71,6 +66,7 @@ class _StockReplenishmentState extends State { setState(() { _selectedDate = picked; _isAfter = _isAfter; + prevQtyIn = 0.00; }); } @@ -83,7 +79,8 @@ class _StockReplenishmentState extends State { }); try { setState(() { - refreshState(DateFormat('yyyy-MM-dd').format(_selectedDate!)); + refreshState( + DateFormat('yyyy-MM-dd HH:mm:ss').format(_selectedDate!)); }); } catch (error) { // Handle any errors that occur during the fetch @@ -109,7 +106,7 @@ class _StockReplenishmentState extends State { _controllers = _fetchedStockList.map((item) { DateTime isToday = DateTime.now(); return TextEditingController( - text: _isAfter ? '0' : item.quantity_in?.toString() ?? '', + text: _showQtyIn ? item.quantity_in?.toString() : '0', // text: // _selectedDate == isToday ? '' : item.quantity_in?.toString() ?? '', ); @@ -133,7 +130,8 @@ class _StockReplenishmentState extends State { _isFetching = true; // Show loading indicator }); try { - await refreshState(DateFormat('yyyy-MM-dd').format(_selectedDate!)); + await refreshState( + DateFormat('yyyy-MM-dd HH:mm:ss').format(_selectedDate!)); } catch (error) { // Handle any errors that occur during the fetch // You can show an error message or take appropriate actions here @@ -148,8 +146,6 @@ class _StockReplenishmentState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - // Perform any initialization that depends on context - // _pickDate(context); } void updateSelected(int index, bool value, List selected) { @@ -175,12 +171,74 @@ class _StockReplenishmentState extends State { await getStockListforReplenishment(parentOrgId, newValue!); Person person = campusAppsPortalInstance.getUserPerson(); - var formattedDate = formatDateTime(_selectedDate!); + var formattedDate = formatDateTime(_selectedDate); + + for (var item in _fetchedStockList) { + DateTime itemDate = DateTime.parse(item.updated!).toLocal(); + DateTime itemDateOnly = + DateTime(itemDate.year, itemDate.month, itemDate.day); + DateTime currentDate = DateTime.now().toLocal(); + if (item.quantity_out != 0) { + if (_selectedDate.day == currentDate.day) { + _isSubmitting = false; + _showQtyIn = false; + _backDate = false; + _isUpdate = false; + } else { + _isUpdate = true; + _backDate = false; + _isSubmitting = true; + _showQtyIn = false; + // this is a depletion + } + } else { + if (_selectedDate.year == currentDate.year && + _selectedDate.month == currentDate.month && + _selectedDate.day == currentDate.day) { + if (_selectedDate.isAfter(itemDateOnly) && + item.quantity_in != 0 && + _selectedDate.day != itemDateOnly.day) { + _showQtyIn = false; + _backDate = false; + _isUpdate = false; + } else { + _isSubmitting = false; + _showQtyIn = true; + _backDate = false; + _isUpdate = true; + } + } else if (_selectedDate.isBefore(currentDate)) { + if (_selectedDate.isAfter(itemDateOnly) && item.quantity_in != 0) { + _isSubmitting = false; + _showQtyIn = false; + _backDate = false; + _isUpdate = false; + break; + } else if (_selectedDate.isBefore(itemDateOnly)) { + _isSubmitting = true; + _showQtyIn = true; + _backDate = true; + _isUpdate = true; + } else { + _isSubmitting = true; + _backDate = false; + _showQtyIn = true; + _isUpdate = true; + } + } else { + _isSubmitting = false; + _showQtyIn = false; + _backDate = false; + _isUpdate = false; + } + } + } - _initializeControllers(); _fetchedStockList.map((item) { if (item.quantity_in != 0) { - _isUpdate = true; + if (item.updated != null) { + _fetcheddDate = DateTime.parse(item.updated!); + } } }).toList(); @@ -188,17 +246,19 @@ class _StockReplenishmentState extends State { setState(() { _fetchedStockList; _person = person; - _fetchedStockList.map((item) { - if (item.quantity_in != 0) { - _isUpdate = true; - } - }).toList(); + _isSubmitting = _isSubmitting; + _backDate = _backDate; + _showQtyIn = _showQtyIn; + _fetcheddDate = _fetcheddDate; + _isUpdate = _isUpdate; formatted_date = formattedDate; parent_org_id = parentOrgId; _isFetching = false; // Ensure _isFetching is set to false after fetching }); } + + _initializeControllers(); } Future _handleSubmit() async { @@ -206,17 +266,16 @@ class _StockReplenishmentState extends State { _isSubmitting = true; }); - if (_isUpdate) { - var result = await updateConsumableReplenishment( + if (_showQtyIn || _isUpdate) { + result = await updateConsumableReplenishment( _fetchedStockList, this.formatted_date); } else { - var result = await addConsumableReplenishment(_fetchedStockList, + result = await addConsumableReplenishment(_fetchedStockList, this._person?.id, this.parent_org_id, this.formatted_date, _isUpdate); } - // Add your form submission logic here - setState(() { + refreshState(DateFormat('yyyy-MM-dd HH:mm:ss').format(_selectedDate)); _isSubmitting = false; _isUpdate = true; }); @@ -270,205 +329,207 @@ class _StockReplenishmentState extends State { size: 50, ), ) - : _fetchedStockList.isNotEmpty - ? LayoutBuilder( - builder: (context, constraints) { - return SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: ConstrainedBox( - constraints: BoxConstraints( - minWidth: constraints.maxWidth, - ), - child: DataTable( - columns: const [ - DataColumn( - label: Text( - "Product Name", - style: TextStyle( - fontWeight: FontWeight.bold), - )), - DataColumn( - label: Text( - "Quantity", - style: TextStyle( - fontWeight: FontWeight.bold), - )), - DataColumn( - label: Text( - "On Hand", - style: TextStyle( - fontWeight: FontWeight.bold), - )), - DataColumn( - label: Text( - "Total", - style: TextStyle( - fontWeight: FontWeight.bold), - )), - DataColumn( - label: Text( - "Unit", - style: TextStyle( - fontWeight: FontWeight.bold), - )), - ], - rows: _fetchedStockList - .asMap() - .entries - .map((entry) { - int index = entry.key; - StockReplenishment item = entry.value; - double totalQuantityIn = 0.0; - if (_isUpdate) { - totalQuantityIn = - (item.quantity_in ?? 0.0) + - (item.prev_quantity ?? 0.0); - } else { - totalQuantityIn = - (item.quantity_in ?? 0.0) + - (item.quantity ?? 0.0); - } - - _controller = TextEditingController( - text: _isAfter - ? '0' - : item.quantity_in?.toString() ?? - '', - ); - // _controller.addListener(() { - // final text = _controller.text; - // final quantity_in = - // int.tryParse(text); - // if (quantity_in != null) { - // setState(() { - // item.quantity_in = quantity_in; - // totalQuantityIn = - // (item.quantity_in ?? 0) + - // (item.quantity ?? 0); - // }); - // } - // }); - return DataRow(cells: [ - DataCell( - Text(item.consumable!.name!)), - DataCell( - Padding( - padding: - const EdgeInsets.symmetric( + : _backDate != true + ? _fetchedStockList.isNotEmpty + ? LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: ConstrainedBox( + constraints: BoxConstraints( + minWidth: constraints.maxWidth, + ), + child: DataTable( + columns: const [ + DataColumn( + label: Text( + "Product Name", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + DataColumn( + label: Text( + "Quantity", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + DataColumn( + label: Text( + "On Hand", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + DataColumn( + label: Text( + "Total", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + DataColumn( + label: Text( + "Unit", + style: TextStyle( + fontWeight: FontWeight.bold), + )), + ], + rows: _fetchedStockList + .asMap() + .entries + .map((entry) { + int index = entry.key; + StockReplenishment item = + entry.value; + _controller = TextEditingController( + text: _isAfter + ? '0' + : item.quantity_in + ?.toString() ?? + '', + ); + return DataRow(cells: [ + DataCell( + Text(item.consumable!.name!)), + DataCell( + Padding( + padding: const EdgeInsets + .symmetric( vertical: 6.0, horizontal: 2.0), - child: TextField( - controller: _controllers[index], - keyboardType: TextInputType - .numberWithOptions( - decimal: true), - inputFormatters: [ - FilteringTextInputFormatter - .allow(RegExp( - r'^\d*\.?\d{0,2}')), - ], - onChanged: - (String value) async { - double? quantity_in = - double.tryParse(value); - if (quantity_in != null) { - print( - 'Quantity: $quantity_in'); - setState(() { - item.quantity_in = - quantity_in; - if (_isAfter) { - totalQuantityIn = - (item.quantity_in ?? - 0.0) + - (item.quantity ?? - 0.0); - } else { - totalQuantityIn = (item - .quantity_in ?? - 0.0) + - (item.prev_quantity ?? - 0.0); + child: TextField( + controller: + _controllers[index], + keyboardType: TextInputType + .numberWithOptions( + decimal: true), + inputFormatters: [ + FilteringTextInputFormatter + .allow(RegExp( + r'^\d*\.?\d{0,2}')), + ], + onChanged: + (String value) async { + double? quantity_in = + double.tryParse( + value); + if (quantity_in != null) { + print( + 'Quantity: $quantity_in'); + setState(() { + item.quantity_in = + quantity_in; + prevQtyIn = (item + .quantity_in ?? + 0.0); + prevIndex = index; + + if (_showQtyIn) { + item.total_quantity = + (item.quantity_in ?? + 0.0) + + (item.prev_quantity ?? + 0.0); + } else { + item.total_quantity = + double.tryParse( + value)! + + (item.quantity ?? + 0.0); + } + }); } - }); - } - }, - decoration: InputDecoration( - // hintText: item.quantity_in - // ?.toString() ?? - // 'Enter quantity', - border: OutlineInputBorder(), + }, + decoration: InputDecoration( + border: + OutlineInputBorder(), + ), + enabled: !_isSubmitting, + ), ), ), - ), - ), - DataCell( - Text(_isAfter - ? item.quantity!.toString() - : item.prev_quantity! - .toString()), - ), - DataCell( - Text(totalQuantityIn.toString())), - DataCell(Text(item - .resource_property!.value! - .toString())), - ]); - }).toList(), - ), - ), - ); - }, - ) - : Center( - child: Text('No data found'), - ), + DataCell( + Text(_showQtyIn + ? item.prev_quantity! + .toString() + : item.quantity! + .toString()), + ), + DataCell(Text(item.total_quantity + .toString())), + DataCell(Text(item + .resource_property!.value! + .toString())), + ]); + }).toList(), + ), + ), + ); + }, + ) + : Center( + child: Text('No data found'), + ) + : Text("Please Select Valid Date"), if (!_isFetching && _fetchedStockList.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: Align( - alignment: Alignment.centerRight, - child: SizedBox( - width: 120, // Set the width of the button - // child: ElevatedButton( - // onPressed: _isSubmitting ? null : _handleSubmit, - // style: ElevatedButton.styleFrom( - // primary: Colors.deepPurple, // Change button color - // elevation: 4, // Add elevation - // ), - // child: Padding( - // padding: const EdgeInsets.all(8.0), - // child: Text( - // _isUpdate ? 'Update' : 'Save', - // style: TextStyle( - // fontSize: 16, // Increase font size - // fontWeight: FontWeight.bold, - // color: Colors.white, // Change text color - // ), - // ), - // ), - // ), - child: ElevatedButton( - onPressed: _isSubmitting ? null : _handleSubmit, - style: ButtonStyle( - padding: MaterialStateProperty.all( - EdgeInsets.all(16.0)), - textStyle: MaterialStateProperty.all( - const TextStyle(fontSize: 16), + _backDate != true + ? Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Align( + alignment: Alignment.centerRight, + child: SizedBox( + width: 120, + child: ElevatedButton( + onPressed: + _isSubmitting ? null : _handleSubmit, + style: ButtonStyle( + padding: MaterialStateProperty.all( + EdgeInsets.all(16.0)), + textStyle: + MaterialStateProperty.resolveWith( + (states) { + if (states + .contains(MaterialState.disabled)) { + return TextStyle( + fontSize: 16, color: Colors.grey); + } + return TextStyle( + fontSize: 16, color: Colors.black); + }), + elevation: + MaterialStateProperty.resolveWith( + (states) { + if (states + .contains(MaterialState.disabled)) { + return 0.0; + } + return 20.0; + }), + backgroundColor: + MaterialStateProperty.resolveWith( + (states) { + if (states + .contains(MaterialState.disabled)) { + return Colors.grey.shade400; + } + return Colors.greenAccent; + }), + foregroundColor: + MaterialStateProperty.resolveWith( + (states) { + if (states + .contains(MaterialState.disabled)) { + return Colors.grey.shade700; + } + return Colors.black; + }), + ), + child: Text( + _isUpdate ? 'Update' : 'Save', + ), + ), ), - elevation: MaterialStateProperty.all(20), - backgroundColor: - MaterialStateProperty.all(Colors.greenAccent), - foregroundColor: - MaterialStateProperty.all(Colors.black), - ), - child: Text( - _isUpdate ? 'Update' : 'Save', ), - ), - ), - ), - ), + ) + : Text(""), ], ), ), diff --git a/campus/frontend/lib/widgets/success_message.dart b/campus/frontend/lib/widgets/success_message.dart index 1ed52499..2a507155 100644 --- a/campus/frontend/lib/widgets/success_message.dart +++ b/campus/frontend/lib/widgets/success_message.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:oktoast/oktoast.dart'; -void showSuccessToast() { +void showSuccessToast(item) { showToastWidget( Container( padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0), @@ -15,7 +15,7 @@ void showSuccessToast() { Icon(Icons.check, color: Colors.white), SizedBox(width: 12.0), Text( - 'Stock Replenishment Successfully Updated!', + item.toString(), style: TextStyle(color: Colors.white), ), ], From 184fb130a005f64904e848a4ec9f727841e101c8 Mon Sep 17 00:00:00 2001 From: YujithIsura Date: Tue, 23 Jul 2024 20:37:50 +0530 Subject: [PATCH 4/4] fixed issues in cons weekly report --- .../lib/widgets/consumable_weekly_report.dart | 90 ++++++++++--------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/campus/frontend/lib/avinya/asset_admin/lib/widgets/consumable_weekly_report.dart b/campus/frontend/lib/avinya/asset_admin/lib/widgets/consumable_weekly_report.dart index 95dbe946..a9238923 100644 --- a/campus/frontend/lib/avinya/asset_admin/lib/widgets/consumable_weekly_report.dart +++ b/campus/frontend/lib/avinya/asset_admin/lib/widgets/consumable_weekly_report.dart @@ -33,7 +33,7 @@ class _ConsumableWeeklyReportState extends State { DateTime? _rangeEnd; List<_GroupedItem> _displayData = []; - void selectWeek(DateTime today) async { + void selectWeek(DateTime today) async { // Calculate the start of the week (excluding weekends) based on the selected day DateTime startOfWeek = today.subtract(Duration(days: today.weekday - 1)); while (startOfWeek.weekday > DateTime.friday) { @@ -57,15 +57,18 @@ class _ConsumableWeeklyReportState extends State { }); try { - final fetchedConsumableWeeklySummaryData = await getConsumableWeeklyReport( - parentOrgId, - DateFormat('yyyy-MM-dd').format(startOfWeek), - DateFormat('yyyy-MM-dd').format(endOfWeek)); + final fetchedConsumableWeeklySummaryData = + await getConsumableWeeklyReport( + parentOrgId, + DateFormat('yyyy-MM-dd').format(startOfWeek), + DateFormat('yyyy-MM-dd').format(endOfWeek)); setState(() { - _fetchedConsumableWeeklySummaryData = fetchedConsumableWeeklySummaryData; - _isFetching = false; // Set _isFetching to false after the fetch completes - displayData(); + _fetchedConsumableWeeklySummaryData = + fetchedConsumableWeeklySummaryData; + _isFetching = + false; // Set _isFetching to false after the fetch completes + displayData(); }); } catch (error) { // Handle any errors that occur during the fetch @@ -87,16 +90,17 @@ class _ConsumableWeeklyReportState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - _data = MyData(_fetchedConsumableWeeklySummaryData, _displayData, updateSelected); + _data = MyData( + _fetchedConsumableWeeklySummaryData, _displayData, updateSelected); // DateRangePicker(updateDateRange, formattedStartDate); } - void displayData(){ - - Map> groupedData = {}; + void displayData() { + Map> groupedData = {}; for (StockReplenishment item in _fetchedConsumableWeeklySummaryData) { - final date = DateFormat('yyyy-MM-dd').format(DateTime.parse(item.updated!)); + final date = + DateFormat('yyyy-MM-dd').format(DateTime.parse(item.updated!)); if (!groupedData.containsKey(date)) { groupedData[date] = []; } @@ -110,11 +114,9 @@ class _ConsumableWeeklyReportState extends State { displayData.add(_GroupedItem(date, items[i], i == 0)); } }); - - this._displayData = displayData; - - } + this._displayData = displayData; + } void updateSelected(int index, bool value, List selected) { setState(() { @@ -131,12 +133,11 @@ class _ConsumableWeeklyReportState extends State { _isFetching = true; // Set _isFetching to true before starting the fetch }); try { - _fetchedConsumableWeeklySummaryData = - await getConsumableWeeklyReport( - parentOrgId, - DateFormat('yyyy-MM-dd').format(_rangeStart), - DateFormat('yyyy-MM-dd').format(_rangeEnd)); - + _fetchedConsumableWeeklySummaryData = await getConsumableWeeklyReport( + parentOrgId, + DateFormat('yyyy-MM-dd').format(_rangeStart), + DateFormat('yyyy-MM-dd').format(_rangeEnd)); + setState(() { final startDate = _rangeStart ?? _selectedDay; final endDate = _rangeEnd ?? _selectedDay; @@ -145,11 +146,12 @@ class _ConsumableWeeklyReportState extends State { final formattedEndDate = formatter.format(endDate!); this.formattedStartDate = formattedStartDate; this.formattedEndDate = formattedEndDate; - this._fetchedConsumableWeeklySummaryData = _fetchedConsumableWeeklySummaryData; + this._fetchedConsumableWeeklySummaryData = + _fetchedConsumableWeeklySummaryData; _isFetching = false; displayData(); - _data = MyData(_fetchedConsumableWeeklySummaryData,_displayData,updateSelected); - + _data = MyData(_fetchedConsumableWeeklySummaryData, _displayData, + updateSelected); }); } catch (error) { // Handle any errors that occur during the fetch @@ -171,11 +173,11 @@ class _ConsumableWeeklyReportState extends State { columnNames.add(DataColumn( label: Text('Product Name', style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)))); - + columnNames.add(DataColumn( label: Text('Opening Stock(QTY)', style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)))); - + columnNames.add(DataColumn( label: Text('Replenishment(QTY)', style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)))); @@ -199,7 +201,6 @@ class _ConsumableWeeklyReportState extends State { return columnNames; } - @override Widget build(BuildContext context) { return SingleChildScrollView( @@ -318,7 +319,8 @@ class _ConsumableWeeklyReportState extends State { } class MyData extends DataTableSource { - MyData(this._fetchedConsumableWeeklySummaryData,this.displayData, this.updateSelected); + MyData(this._fetchedConsumableWeeklySummaryData, this.displayData, + this.updateSelected); final List _fetchedConsumableWeeklySummaryData; List<_GroupedItem> displayData; @@ -326,27 +328,33 @@ class MyData extends DataTableSource { @override DataRow? getRow(int index) { - - final groupedItem = displayData[index]; + final groupedItem = displayData[index]; final consumableItem = groupedItem.stockReplenishment; List cells = List.filled(8, DataCell.empty); // Display date only once for the first item of the group - cells[0] = DataCell(Center(child: Text(groupedItem.showDate ? groupedItem.date.toString() : ''))); - cells[1] = DataCell(Center(child: Text(consumableItem.consumable!.name.toString()))); - cells[2] = DataCell(Center(child: Text(consumableItem.quantity.toString()))); - cells[3] = DataCell(Center(child: Text(consumableItem.quantity_in.toString()))); - - double? balance = (consumableItem.quantity!) + (consumableItem.quantity_in!); + cells[0] = DataCell(Center( + child: Text(groupedItem.showDate ? groupedItem.date.toString() : ''))); + cells[1] = DataCell( + Center(child: Text(consumableItem.consumable!.name.toString()))); + cells[2] = + DataCell(Center(child: Text(consumableItem.prev_quantity.toString()))); + cells[3] = + DataCell(Center(child: Text(consumableItem.quantity_in.toString()))); + + double? balance = + (consumableItem.prev_quantity!) + (consumableItem.quantity_in!); cells[4] = DataCell(Center(child: Text(balance.toStringAsFixed(2)))); - cells[5] = DataCell(Center(child: Text(consumableItem.quantity_out.toString()))); - + cells[5] = + DataCell(Center(child: Text(consumableItem.quantity_out.toString()))); + double? closingStock = (balance) - (consumableItem.quantity_out!); cells[6] = DataCell(Center(child: Text(closingStock.toStringAsFixed(2)))); - cells[7] = DataCell(Center(child: Text(consumableItem.resource_property!.value!))); + cells[7] = + DataCell(Center(child: Text(consumableItem.resource_property!.value!))); return DataRow(cells: cells); }