import 'dart:async'; import 'dart:developer'; import 'dart:io'; import 'dart:math'; import 'package:collection/collection.dart'; import 'package:excel/excel.dart' as exc; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:rrispat_app/src/features/store/domain/models/store_user_model.dart'; import 'package:rrispat_app/src/shared/providers/providers.dart'; import '../../../../../l10n/app_localizations.dart'; import '../../../../shared/widgets/loader_widget.dart'; import '../../../../shared/widgets/rr_filled_button.dart'; import '../../../auth/controllers/auth_controller.dart'; import '../../../auth/domains/models/auth_model.dart'; import '../../../auth/domains/providers/auth_model_provider.dart'; import '../../controller/store_item_controller.dart'; import '../../controller/store_user_controller.dart'; import '../../domain/models/store_item_model.dart'; import '../../domain/store_enums.dart'; class StoreAddStock extends ConsumerStatefulWidget { const StoreAddStock({Key? key}) : super(key: key); @override ConsumerState<StoreAddStock> createState() => _StoreAddStockState(); } class _StoreAddStockState extends ConsumerState<StoreAddStock> { final GlobalKey<FormState> _pieces = GlobalKey<FormState>(); bool _showLoading = true; final TextEditingController _itemNameController = TextEditingController(); final TextEditingController _poNumberController = TextEditingController(); final TextEditingController pieces = TextEditingController(); StoreUserModel? storeUser; List<StoreItemModel> storeItemListfromBe = []; List<AuthModel> authModels = []; double screenWidth = 0; PaginationParams _paginationParams = PaginationParams( page: 1, pageSize: 10, totalPages: 0, itemName: '', poNumber: '', ); int _totalItems = 0; @override void initState() { super.initState(); Timer(const Duration(seconds: 90), () { if (mounted) { setState(() { _showLoading = false; }); } }); } Future<void> uploadExcelDataFromWebStoreStockAdd(BuildContext context) async { FilePickerResult? file = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['xlsx'], allowMultiple: false, ); if (file != null) { List<StoreItemModel> tempList = []; Uint8List fileBytes = file.files.first.bytes!; var excel = exc.Excel.decodeBytes(fileBytes); for (var table in excel.tables.keys) { for (var row in excel.tables[table]!.rows) { if (row[0]?.value.toString() == "Company Name") continue; try { StoreItemModel storeItemModel = StoreItemModel( id: '', itemName: (row[4]?.value).toString(), vendorName: (row[1]?.value).toString(), pieces: double.parse((row[6]?.value).toString()), issuedPcs: double.parse(0.toString()), createdAt: DateTime.now(), createdBy: ref.read(authModelProvider)!.uid, itemCode: (row[3]?.value).toString(), poNumber: (row[8]?.value).toString(), unit: (row[5]?.value).toString(), issuedCompleted: double.parse(0.toString()), sapItemCode: '', plantName: (row[10]?.value).toString(), poDate: (row[11]?.value).toString(), status: StoreItemStatus.approved, type: StoreItemCreatedType.normal, remark: '', ); tempList.add(storeItemModel); } catch (e) { print("$e store item error getFile"); } } } for (var element in tempList) { ref .read(storeItemControllerProvider) .createStoreItemStock(ref, element); } } } Widget buildFilters(BuildContext context) { final double screenWidth = MediaQuery.of(context).size.width; if (screenWidth < 600) { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ TextFormField( controller: _itemNameController, decoration: InputDecoration( labelText: "Filter by Item Name", border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)), ), onChanged: (value) { setState(() { _paginationParams = PaginationParams( page: 1, pageSize: _paginationParams.pageSize, totalPages: _paginationParams.totalPages, itemName: value, poNumber: _poNumberController.text, ); }); }, ), const SizedBox(height: 10), TextFormField( controller: _poNumberController, decoration: InputDecoration( labelText: "Filter by PO Number", border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)), ), onChanged: (value) { setState(() { _paginationParams = PaginationParams( page: 1, pageSize: _paginationParams.pageSize, totalPages: _paginationParams.totalPages, itemName: _itemNameController.text, poNumber: value, ); }); }, ), const SizedBox(height: 10), ], ); } else { return Row( children: [ Expanded( child: TextFormField( controller: _itemNameController, decoration: InputDecoration( labelText: "Filter by Item Name", border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)), ), onChanged: (value) { setState(() { _paginationParams = PaginationParams( page: 1, pageSize: _paginationParams.pageSize, totalPages: _paginationParams.totalPages, itemName: value, poNumber: _poNumberController.text, ); }); }, ), ), const SizedBox(width: 10), Expanded( child: TextFormField( controller: _poNumberController, decoration: InputDecoration( labelText: "Filter by PO Number", border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)), ), onChanged: (value) { setState(() { _paginationParams = PaginationParams( page: 1, pageSize: _paginationParams.pageSize, totalPages: _paginationParams.totalPages, itemName: _itemNameController.text, poNumber: value, ); }); }, ), ), ], ); } } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; ref .watch(authUserListStreamProvider) .whenData((value) => authModels = value); ref.watch(storeUserStreamProvider).whenData((value) => storeUser = value); return Scaffold( appBar: AppBar( title: Text('PO - Stock Add'), centerTitle: true, ), body: Padding( padding: const EdgeInsets.all(10), child: Column( children: [ if (storeUser != null && storeUser!.userType.contains(StoreUserType.poListUpload)) Center( child: RrFilledButton( onPressed: () { uploadExcelDataFromWebStoreStockAdd(context); }, label: ('Store Item Excel File Upload'), ), ), const SizedBox(height: 20), buildFilters(context), const SizedBox(height: 10), Expanded( child: ref .watch(storeItemStockListWithPaginationStreamProvider( _paginationParams)) .when( data: (paginatedData) { _totalItems = paginatedData.totalItems; _paginationParams = PaginationParams( page: _paginationParams.page, pageSize: _paginationParams.pageSize, totalPages: paginatedData.totalPages, itemName: _itemNameController.text, poNumber: _poNumberController.text, ); if (paginatedData.storeItemStocks.isEmpty) { return _showLoading ? const Padding( padding: EdgeInsets.all(16.0), child: Center(child: CircularProgressIndicator()), ) : const Padding( padding: EdgeInsets.all(16.0), child: Center(child: Text("No Stock Item Found")), ); } else { return ListView.builder( itemCount: paginatedData.storeItemStocks.length, itemBuilder: (context, index) { return addStockCard( context, paginatedData.storeItemStocks[index]); }, ); } }, error: (error, stack) => Center(child: Text(error.toString())), loading: () => const Center(child: CircularProgressIndicator()), ), ), _buildPageNumbers( computedTotalPages: _paginationParams.totalPages ?? 1), ], ), ), ); } Widget _buildPageNumbers({required int computedTotalPages}) { int totalVisiblePages = 5; List<Widget> pageButtons = []; int startPage = max(1, _paginationParams.page - totalVisiblePages ~/ 2); int endPage = min(computedTotalPages, startPage + totalVisiblePages - 1); if (endPage - startPage < totalVisiblePages - 1) { startPage = max(1, endPage - totalVisiblePages + 1); } if (startPage > 1) { pageButtons.add(_pageButton(1, 'First')); } for (int i = startPage; i <= endPage; i++) { pageButtons.add(_pageButton(i, '$i')); } if (endPage < computedTotalPages) { pageButtons.add(_pageButton(computedTotalPages, 'Last')); } return Wrap( alignment: WrapAlignment.center, children: pageButtons, ); } Widget _pageButton(int pageNumber, String text) { return Padding( padding: const EdgeInsets.all(2.0), child: InkWell( onTap: () => _changePage(pageNumber), child: Padding( padding: const EdgeInsets.all(10.0), child: Text( text, style: TextStyle( color: pageNumber == _paginationParams.page ? Theme.of(context).colorScheme.primary : Colors.grey, ), ), ), ), ); } void _changePage(int page) { if (page != _paginationParams.page) { setState(() { _paginationParams = PaginationParams( page: page, pageSize: _paginationParams.pageSize, totalPages: _paginationParams.totalPages, itemName: _itemNameController.text, poNumber: _poNumberController.text, ); }); } } Widget addStockCard(BuildContext context, StoreItemModel storeItemModel) { bool isClosed = storeItemModel.issuedPcs != 0; AuthModel closedByPerson = authModels.firstWhere( (element) => element.uid == storeItemModel.createdBy, ); return Padding( padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5), child: Card( color: Theme.of(context).colorScheme.background, child: ListTile( leading: Container( width: 40, height: 40, decoration: BoxDecoration( color: Theme.of(context).colorScheme.tertiary, shape: BoxShape.circle, ), alignment: Alignment.center, child: Text( storeItemModel.pieces.toStringAsFixed(2), style: Theme.of(context) .textTheme .titleMedium ?.copyWith(color: Colors.white, fontSize: 9), textAlign: TextAlign.center, ), ), title: Text( storeItemModel.itemName.toUpperCase(), style: Theme.of(context).textTheme.titleLarge, ), subtitle: Wrap( spacing: 20, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ Text( "Pieces: ", style: TextStyle( color: Theme.of(context).colorScheme.primary, ), ), Text(storeItemModel.pieces.toStringAsFixed(2)), Text(" ${storeItemModel.unit.toString()}"), ], ), Row( mainAxisSize: MainAxisSize.min, children: [ Text( "${AppLocalizations.of(context).translate('Vendor Name')}: ", style: TextStyle( color: Theme.of(context).colorScheme.primary, ), ), Text(storeItemModel.vendorName), ], ), Row( mainAxisSize: MainAxisSize.min, children: [ Text( "SAP Item Code: ", style: TextStyle( color: Theme.of(context).colorScheme.primary, ), ), Text(storeItemModel.itemCode), ], ), Row( mainAxisSize: MainAxisSize.min, children: [ Text( "Plant Name: ", style: TextStyle( color: Theme.of(context).colorScheme.primary, ), ), Text(storeItemModel.plantName), ], ), Row( mainAxisSize: MainAxisSize.min, children: [ Text( "PO Created Date: ", style: TextStyle( color: Theme.of(context).colorScheme.primary, ), ), Text(storeItemModel.poDate), ], ), Row( mainAxisSize: MainAxisSize.min, children: [ Text( "PO Number: ", style: TextStyle( color: Theme.of(context).colorScheme.primary, ), ), Text(storeItemModel.poNumber), ], ), Row( mainAxisSize: MainAxisSize.min, children: [ Text( "${AppLocalizations.of(context).translate('Created At')}: ", style: TextStyle( color: Theme.of(context).colorScheme.primary, ), ), Text( ref .read(dateFormatterProvider) .format(storeItemModel.createdAt), ), ], ), screenWidth <= 500 ? (isClosed) ? Row( mainAxisSize: MainAxisSize.min, children: [ Text( "Closed By : ${closedByPerson.fullName}", style: TextStyle( color: Theme.of(context).colorScheme.tertiary, ), ), ], ) : Row( mainAxisAlignment: MainAxisAlignment.center, children: [ buildIconButton( Icons.done_all_rounded, AppLocalizations.of(context).translate('Approve'), () { addStockModel(storeItemModel); }, ), const SizedBox(width: 20), buildIconButton( Icons.close_rounded, 'Close', () { partiallyCloseStockModel(storeItemModel); }, ), ], ) : const SizedBox.shrink(), ], ), trailing: screenWidth > 500 ? (isClosed) ? Wrap( children: [ Text( "Closed By : ${closedByPerson.fullName}", style: TextStyle( color: Theme.of(context).colorScheme.tertiary, ), ), ], ) : Wrap( children: [ buildIconButton( Icons.done_all_rounded, AppLocalizations.of(context).translate('Approve'), () { addStockModel(storeItemModel); }, ), const SizedBox(width: 20), buildIconButton( Icons.close_rounded, 'Close', () { partiallyCloseStockModel(storeItemModel); }, ), ], ) : null, ), ), ); } Widget buildIconButton( IconData icon, String tooltip, void Function() onPressed) { return Tooltip( message: tooltip, child: IconButton( onPressed: onPressed, icon: Icon( icon, color: Theme.of(context).colorScheme.tertiary, ), ), ); } void partiallyCloseStockModel(StoreItemModel storeItemModel) { try { showDialog( barrierDismissible: false, context: context, builder: (context) { return AlertDialog( title: Center( child: Text( "Verify", style: Theme.of(context).textTheme.bodyLarge, ), ), content: const SizedBox( height: 80, child: Center( child: Column( children: [ Text('Are you sure you want to close this stock item?'), SizedBox(height: 5), ], ), ), ), actions: <Widget>[ TextButton( child: const Text('Cancel'), onPressed: () { context.pop(true); }, ), ElevatedButton( child: const Text('Close'), onPressed: () async { String closedBy = ref.read(authModelProvider)!.uid; ref.read(storeItemControllerProvider).updateStoreItemStock( ref, storeItemModel, storeItemModel.pieces, closedBy); context.pop(true); ref.invalidate( storeItemStockListWithPaginationStreamProvider); }, ) ], ); }, ); } catch (e) { print(e.toString()); } } void addStockModel(StoreItemModel storeItemModel) { try { showDialog( barrierDismissible: false, context: context, builder: (context) { return AlertDialog( title: Center( child: Text( "Verify", style: Theme.of(context).textTheme.bodyLarge, ), ), content: SizedBox( height: 180, child: Center( child: Column( children: [ Text('Item Name : ${storeItemModel.itemName}'), const SizedBox(height: 5), Text( 'Total pcs. in Po list : ${storeItemModel.pieces} ${storeItemModel.unit}'), const SizedBox(height: 10), Form( key: _pieces, child: TextFormField( validator: (text) { if (text == null || text.isEmpty || double.parse(text) < 1) { return 'Please enter Pieces to add'; } if (double.parse(text) > (storeItemModel.pieces + (storeItemModel.pieces * 0.1))) { return 'Pcs should not be greater than available stock'; } return null; }, controller: pieces, keyboardType: TextInputType.number, textCapitalization: TextCapitalization.words, decoration: InputDecoration( labelText: "Pieces", hintText: "Enter Pieces", border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), ), ), ), ), const SizedBox(height: 5), ], ), ), ), actions: <Widget>[ TextButton( child: const Text('Cancel'), onPressed: () { context.pop(true); }, ), ElevatedButton( child: const Text('OK'), onPressed: () async { if (_pieces.currentState!.validate()) { double pcs = double.parse(pieces.text); String updateBy = ref.read(authModelProvider)!.uid; ref .read(storeItemControllerProvider) .updateStoreItem(ref, storeItemModel, pcs, updateBy); context.pop(true); pieces.clear(); _itemNameController.clear(); _poNumberController.clear(); ref.invalidate( storeItemStockListWithPaginationStreamProvider); _pieces.currentState?.reset(); } }, ) ], ); }, ); } catch (e) { print(e.toString()); } } }