Files
kulakpos_app/lib/Screens/Purchase/bulk purchase/bulk_purchase.dart

333 lines
12 KiB
Dart
Raw Normal View History

2026-02-07 15:57:09 +07:00
import 'dart:io';
import 'package:excel/excel.dart' as e;
import 'package:file_selector/file_selector.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mobile_pos/Provider/product_provider.dart';
import 'package:mobile_pos/Screens/Products/add%20product/add_product.dart';
import 'package:mobile_pos/constant.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../../GlobalComponents/glonal_popup.dart';
import '../../../Provider/add_to_cart_purchase.dart';
import '../../../http_client/custome_http_client.dart';
import '../../../service/check_user_role_permission_provider.dart';
import '../../Products/add product/modle/create_product_model.dart';
import '../Repo/purchase_repo.dart';
class BulkPurchaseUploader extends ConsumerStatefulWidget {
const BulkPurchaseUploader({super.key});
@override
ConsumerState<BulkPurchaseUploader> createState() => _BulkPurchaseUploaderState();
}
class _BulkPurchaseUploaderState extends ConsumerState<BulkPurchaseUploader> {
String? filePat;
File? file;
String getFileExtension(String fileName) {
return fileName.split('/').last;
}
Future<void> createExcelFile() async {
if (!await Permission.storage.request().isDenied) {
EasyLoading.showError('Storage permission is required to create Excel file!');
return;
}
EasyLoading.show();
final List<e.CellValue> excelData = [
e.TextCellValue('SL'),
e.TextCellValue('Product Code*'),
e.TextCellValue('Purchase Quantity*'),
e.TextCellValue('Purchase Price'),
e.TextCellValue('Profit Percent %'),
e.TextCellValue('Sale Price'),
e.TextCellValue('Wholesale Price'),
e.TextCellValue('Dealer Price'),
e.TextCellValue('Batch No'),
e.TextCellValue('Mfg Date'),
e.TextCellValue('Expire Date'),
];
e.CellStyle cellStyle = e.CellStyle(
bold: true,
textWrapping: e.TextWrapping.WrapText,
rotation: 0,
);
var excel = e.Excel.createExcel();
var sheet = excel['Sheet1'];
sheet.appendRow(excelData);
for (int i = 0; i < excelData.length; i++) {
var cell = sheet.cell(e.CellIndex.indexByColumnRow(columnIndex: i, rowIndex: 0));
cell.cellStyle = cellStyle;
}
const downloadsFolderPath = '/storage/emulated/0/Download/';
Directory dir = Directory(downloadsFolderPath);
final file = File('${dir.path}/${appsName}_bulk_purchase_upload.xlsx');
if (await file.exists()) {
EasyLoading.showSuccess('The Excel file has already been downloaded');
} else {
await file.writeAsBytes(excel.encode()!);
EasyLoading.showSuccess('Downloaded successfully in download folder');
}
}
@override
Widget build(BuildContext context) {
final permissionService = PermissionService(ref);
return GlobalPopup(
child: Scaffold(
backgroundColor: kWhite,
appBar: AppBar(
title: const Text('Excel Uploader'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Visibility(
visible: file != null,
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Card(
child: ListTile(
leading: Container(
height: 40,
width: 40,
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: const BorderRadius.all(Radius.circular(10)),
),
child: const Image(image: AssetImage('images/excel.png'))),
title: Text(
getFileExtension(file?.path ?? ''),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
trailing: GestureDetector(
onTap: () {
setState(() {
file = null;
});
},
child: const Text('Remove')))),
),
),
Visibility(
visible: file == null,
child: const Padding(
padding: EdgeInsets.only(bottom: 20),
child: Image(
height: 100,
width: 100,
image: AssetImage('images/file-upload.png'),
)),
),
ElevatedButton(
style: const ButtonStyle(backgroundColor: WidgetStatePropertyAll(kMainColor)),
onPressed: () async {
if (!permissionService.hasPermission(Permit.bulkUploadsCreate.value)) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.red,
content: Text('You do not have permission to upload bulk.'),
),
);
return;
}
if (file == null) {
await pickAndUploadFile(ref: ref);
} else {
EasyLoading.show(status: 'Uploading...');
await uploadProducts(ref: ref, file: file!, context: context);
EasyLoading.dismiss();
}
},
child: Text(file == null ? 'Pick and Upload File' : 'Upload', style: const TextStyle(color: Colors.white)),
),
TextButton(
onPressed: () async {
if (!permissionService.hasPermission(Permit.bulkUploadsRead.value)) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.red,
content: Text('You do not have permission to download file.'),
),
);
return;
}
await createExcelFile();
},
child: const Text('Download Excel Format'),
),
],
),
),
),
),
);
}
///
Future<void> pickAndUploadFile({required WidgetRef ref}) async {
const XTypeGroup typeGroup = XTypeGroup(
label: 'Excel Files',
extensions: ['xlsx'],
);
final XFile? fileResult = await openFile(acceptedTypeGroups: [typeGroup]);
if (fileResult != null) {
final File files = File(fileResult.path);
setState(() {
file = files;
});
} else {
print("No file selected");
}
}
Future<void> uploadProducts({
required File file,
required WidgetRef ref,
required BuildContext context,
}) async {
try {
final purchaseCart = ref.watch(cartNotifierPurchaseNew);
e.Excel excel = e.Excel.decodeBytes(file.readAsBytesSync());
var sheet = excel.sheets.keys.first;
var table = excel.tables[sheet]!;
for (var row in table.rows) {
CartProductModelPurchase? data = await createProductModelFromExcelData(row: row, ref: ref);
if (data != null) purchaseCart.addToCartRiverPod(cartItem: data, isVariation: data.productType == ProductType.variant.name);
}
Future.delayed(const Duration(seconds: 1), () {
EasyLoading.showSuccess('Upload Done');
int count = 0;
Navigator.popUntil(context, (route) {
return count++ == 1;
});
});
} catch (e) {
EasyLoading.showError(e.toString());
return;
}
}
Future<CartProductModelPurchase?> createProductModelFromExcelData({required List<e.Data?> row, required WidgetRef ref}) async {
Future<CartProductModelPurchase?> getProductFromDatabase({required WidgetRef ref, required String givenProductCode}) async {
final products = ref.watch(productProvider);
CartProductModelPurchase? cartProductModel;
// Wait for the category data to load
await products.when(
data: (product) async {
for (var element in product) {
if (element.productCode?.toLowerCase().trim() == givenProductCode.toLowerCase().trim()) {
cartProductModel = CartProductModelPurchase(
productId: element.id ?? 0,
vatRate: element.vat?.rate ?? 0,
productName: element.productName ?? '',
vatAmount: element.vatAmount ?? 0,
vatType: element.vatType ?? '',
productWholeSalePrice: 0,
productDealerPrice: 0,
productPurchasePrice: 0,
productSalePrice: 0,
productType: element.productType ?? 'single',
quantities: 0,
stock: 0,
brandName: '',
profitPercent: 0,
mfgDate: '',
expireDate: '',
batchNumber: '',
);
return cartProductModel;
}
}
},
error: (error, stackTrace) {},
loading: () {},
);
return cartProductModel;
}
CartProductModelPurchase? productModel;
// Loop through the row data
for (var element in row) {
if (element?.rowIndex == 0) {
// Skip header row
return null;
}
switch (element?.columnIndex) {
case 1: // Product code
if (element?.value == null) return null;
productModel = await getProductFromDatabase(ref: ref, givenProductCode: element?.value.toString() ?? '');
break;
case 2: // Product quantity
if (element?.value == null) return null;
productModel?.quantities = num.tryParse(element?.value.toString() ?? '0');
break;
case 3: // purchase price
productModel?.productPurchasePrice = num.tryParse(element?.value.toString() ?? '') ?? 0;
break;
case 4: // profit percent
productModel?.profitPercent = num.tryParse(element?.value.toString() ?? '') ?? 0;
break;
case 5: // sales price
productModel?.productSalePrice = num.tryParse(element?.value.toString() ?? '') ?? 0;
break;
case 6: // wholesale price
productModel?.productWholeSalePrice = num.tryParse(element?.value.toString() ?? '') ?? 0;
break;
case 7: //dealer price
if (element?.value != null) {
productModel?.productDealerPrice = num.tryParse(element?.value.toString() ?? '') ?? 0;
}
break;
case 8: // Batch (optional)
if (element?.value != null) {
productModel?.batchNumber = element?.value.toString() ?? '';
}
break;
case 9: // mgf date (optional)
if (element?.value != null) {
productModel?.mfgDate = element?.value.toString() ?? '';
}
break;
case 10: // expire date (optional)
if (element?.value != null) {
productModel?.expireDate = element?.value.toString() ?? '';
}
break;
}
}
// Return null if any of the required fields are missing
if (productModel?.productName == null || productModel?.quantities == null) {
return null;
}
return productModel;
}
}