import 'dart:io'; import 'package:csv/csv.dart'; import 'package:excel/excel.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:generp/Models/financeModels/paymentRequisitionPaymentsDetailsResponse.dart'; import 'package:generp/Models/financeModels/paymentRequisitionPaymentsListResponse.dart'; import 'package:generp/Notifiers/HomeScreenNotifier.dart'; import 'package:generp/services/api_calling.dart'; import 'package:path_provider/path_provider.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; import 'package:permission_handler/permission_handler.dart'; import 'package:printing/printing.dart'; import 'package:provider/provider.dart'; import '../../Utils/commonServices.dart'; class Paymentrequisitionpaymentslistprovider extends ChangeNotifier{ bool _showMoreDetails = false; List _paymentsList = []; PaymentDetails _paymentDetails = PaymentDetails(); List _headings = []; List _subHeadings = []; List get paymentsList => _paymentsList; PaymentDetails get paymentDetails => _paymentDetails; bool _isLoading = true; List get Headings => _headings; List get subHeadings => _subHeadings; bool get isLoading => _isLoading; bool get showMoreDetails => _showMoreDetails; set showMoreDetails(bool value){ _showMoreDetails = value; notifyListeners(); } bool _isLoadingMore = false; bool get isLoadingMore => _isLoadingMore; bool _hasMoreData = true; bool get hasMoreData => _hasMoreData; int _currentPage = 1; String? _errorMessage; String? get errorMessage => _errorMessage; /// Reset pagination before new filter or refresh void resetPagination() { _currentPage = 1; _hasMoreData = true; _paymentsList.clear(); notifyListeners(); } Future paymentsListAPI( BuildContext context, String from, String to, { bool append = false, }) async { try { var prov = Provider.of(context, listen: false); if (!append) { _isLoading = true; _errorMessage = null; notifyListeners(); } else { _isLoadingMore = true; notifyListeners(); } final data = await ApiCalling.paymentRequisitionPaymentListAPI( prov.empId, prov.session, from, to, _currentPage.toString(), // pass page number ); debugPrint('empId: ${prov.empId}, session: ${prov.session}, pageNumber: $_currentPage'); if (data != null && data.error == "0") { if (append) { _paymentsList.addAll(data.paymentsList ?? []); } else { _paymentsList = data.paymentsList ?? []; } if (data.paymentsList == null || data.paymentsList!.length < 10) { _hasMoreData = false; // no more pages } } else { if (!append) _errorMessage = "No data found!"; _hasMoreData = false; } } catch (e) { _errorMessage = "Error: $e"; } _isLoading = false; _isLoadingMore = false; notifyListeners(); } /// Load next page Future loadMore(BuildContext context, String from, String to) async { if (_isLoadingMore || !_hasMoreData) return; _currentPage++; await paymentsListAPI(context, from, to, append: true); } Future paymentsListDetailsAPI(context,paymentId) async { try{ var prov = Provider.of(context,listen: false); final data = await ApiCalling.paymentRequisitionPaymentDetailsAPI(prov.empId, prov.session,paymentId); if(data!=null){ if(data.error=="0"){ _paymentDetails = data.paymentDetails??PaymentDetails( accountId: "-", accountName: "-", amount: "-", attachmentDirFilePath: "-", attachmentViewFileName: "-", createdDatetime: "-", description: "-", id: "-", updatedDatetime: "-", refType: "-",refId: "-",paymentRemarks: "-",paymentReferenceNumber: "-",paymentModeId: "-",paymentEmployeeName: "-",paymentDate: "-",paymentAccountName: "-",paymentAccountId: "-",mode: "-",isExists: "-",createdEmployeeId: "-",bankUpiId: "-",bankName: "-",bankIfscCode:"-",bankBranchName: "-",bankAccountNumber: "-",bankAccountHolderName:"-" , ); _headings = [ "From Account", "Payment Mode", "Created Employee", "Attachment", "Payment Date", "Note", "Bank Name", "Bank Branch Name", "Bank IFSC Code", "Bank Holder Name", "Bank Account Number", "Bank UPI ID", "Payment Reference Number", "Created Date Time", "Updated Date Time" ]; _subHeadings = [ _paymentDetails.paymentAccountName??"-", _paymentDetails.mode??"-", _paymentDetails.paymentEmployeeName??"-", _paymentDetails.attachmentDirFilePath??"-", _paymentDetails.paymentDate??"-", _paymentDetails.paymentRemarks??"-", _paymentDetails.bankName??"-", _paymentDetails.bankBranchName??"-", _paymentDetails.bankIfscCode??"-", _paymentDetails.bankAccountHolderName??"-", _paymentDetails.bankAccountNumber??"-", _paymentDetails.bankUpiId??"-", _paymentDetails.paymentReferenceNumber??"-", _paymentDetails.createdDatetime??"-", _paymentDetails.updatedDatetime??"-" ]; notifyListeners(); } } }catch(e,s){ } } List> prepareExportData() { final headers = [ 'ID', 'Payment Account', 'Amount', 'Branch', 'Account Number', 'Description', 'Mode', 'Payment Reference Number', 'Created Employee', 'Date', ]; final rows = paymentsList .map( (item) => [ item.id ?? '', item.payAccount ?? '', item.amount ?? '', item.bankName??"", item.bankBranchName ?? '', item.bankAccountNumber ?? '', item.description ?? '', item.requestMode ?? '', item.paymentReferenceNumber ?? '', item.createdEmployee ?? '', item.createdDatetime ?? '', ], ) .toList(); return [headers, ...rows]; } void copyToClipboard(BuildContext context) async { try { if (paymentsList.isEmpty) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text("No data to copy"))); return; } final data = prepareExportData(); String raw = data.map((row) => row.join('\t')).join('\n'); print('Clipboard data: $raw'); await Clipboard.setData(ClipboardData(text: raw)); toast(context, "Copied to Clipboard"); } catch (e) { print('Error copying to clipboard: $e'); } } Future getSaveDirectory() async { // Try Downloads directory first try { if (Platform.isAndroid) { // Request storage permission for Android if (await Permission.storage .request() .isGranted || await Permission.manageExternalStorage .request() .isGranted) { final dir = await getApplicationDocumentsDirectory(); if (dir != null) { print('Using Downloads directory: ${dir.path}'); return dir.path; } } } } catch (e) { print('Error accessing Downloads directory: $e'); } // Fallback to shared Documents directory try { final dir = await getDownloadsDirectory(); if (dir != null) { final customDir = Directory('${dir.path}/RequisitionData'); if (!await customDir.exists()) { await customDir.create(recursive: true); } print('Using custom Documents directory: ${customDir.path}'); return customDir.path; } } catch (e) { print('Error accessing Documents directory: $e'); } // Final fallback to app's Documents directory final dir = await getApplicationDocumentsDirectory(); print('Using app Documents directory: ${dir.path}'); return dir.path; } Future downloadCSV(BuildContext context) async { try { if (paymentsList.isEmpty) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text("No data to export"))); return; } final data = prepareExportData(); final csvData = const ListToCsvConverter().convert(data); final dirPath = await getSaveDirectory(); final file = File('$dirPath/requisition_data.csv'); await file.writeAsString(csvData); print('CSV saved at: ${file.path}'); bool exists = await file.exists(); print('File exists: $exists'); // await OpenFile.open(file.path); // Open the file // await Share.share(file.path); // Share the file toast(context, "CSV Downloaded"); } catch (e) { print('Error downloading CSV: $e'); } } Future downloadXLS(BuildContext context) async { try { if (paymentsList.isEmpty) { toast(context, "No Data to export"); return; } final data = prepareExportData(); var excel = Excel.createExcel(); Sheet sheet = excel['Sheet1']; for (var row in data) { sheet.appendRow(row.map((cell) => TextCellValue(cell)).toList()); } final dirPath = await getSaveDirectory(); final file = File('$dirPath/requisition_data.xlsx'); final bytes = excel.encode(); if (bytes == null) throw Exception("Excel encoding failed"); await file.writeAsBytes(bytes); print('XLSX saved at: ${file.path}'); bool exists = await file.exists(); print('File exists: $exists'); // await OpenFile.open(file.path); // Open the file // await Share.share([file.path], text: 'Requisition Data XLSX'); // Share the file toast(context, ("XLSX Downloaded and opened")); } catch (e) { print('Error downloading XLSX: $e'); } } Future downloadPDF(BuildContext context) async { try { if (paymentsList.isEmpty) { toast(context, "No Data to export"); return; } final data = prepareExportData(); final pdf = pw.Document(); pdf.addPage( pw.Page(build: (context) => pw.Table.fromTextArray(data: data)), ); final dirPath = await getSaveDirectory(); final file = File('$dirPath/requisition_data.pdf'); await file.writeAsBytes(await pdf.save()); print('PDF saved at: ${file.path}'); bool exists = await file.exists(); print('File exists: $exists'); // await OpenFile.open(file.path); // Open the file // await Share.shareXFiles([file.path], text: 'Requisition Data PDF'); // Share the file toast(context, "PDF Downloaded "); } catch (e) { print('Error downloading PDF: $e'); } } Future printData(BuildContext context) async { try { if (paymentsList.isEmpty) { toast(context, "No Data to Print"); return; } final data = prepareExportData(); final pdf = pw.Document(); pdf.addPage( pw.Page(build: (context) => pw.Table.fromTextArray(data: data)), ); await Printing.layoutPdf( onLayout: (PdfPageFormat format) async => pdf.save(), ); } catch (e) { print('Error printing data: $e'); } } void resetForm() { _paymentsList = []; _paymentDetails = PaymentDetails(); _headings = []; _subHeadings = []; } }