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());
}
}
}