167 lines
6.2 KiB
Dart
167 lines
6.2 KiB
Dart
import 'dart:io';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:pdf/widgets.dart' as pw;
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:share_plus/share_plus.dart';
|
|
|
|
class ExportService {
|
|
static final ExportService _instance = ExportService._internal();
|
|
factory ExportService() => _instance;
|
|
ExportService._internal();
|
|
|
|
Future<String?> exportToPdf(List<Map<String, dynamic>> transactions,
|
|
String storeName, DateTimeRange? dateRange) async {
|
|
try {
|
|
debugPrint('exportToPdf called with ${transactions.length} transactions');
|
|
final pdf = pw.Document();
|
|
|
|
pdf.addPage(
|
|
pw.Page(
|
|
build: (context) {
|
|
return pw.Column(
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
|
children: [
|
|
pw.Text(storeName, style: pw.TextStyle(fontSize: 24, fontWeight: pw.FontWeight.bold)),
|
|
pw.SizedBox(height: 8),
|
|
if (dateRange != null)
|
|
pw.Text('${_formatDate(dateRange.start)} - ${_formatDate(dateRange.end)}',
|
|
style: pw.TextStyle(fontSize: 12)),
|
|
pw.SizedBox(height: 16),
|
|
pw.Table(
|
|
border: pw.TableBorder.all(),
|
|
children: [
|
|
pw.TableRow(
|
|
children: [
|
|
pw.Padding(padding: pw.EdgeInsets.all(8), child: pw.Text('Tanggal', style: pw.TextStyle(fontWeight: pw.FontWeight.bold))),
|
|
pw.Padding(padding: pw.EdgeInsets.all(8), child: pw.Text('ID', style: pw.TextStyle(fontWeight: pw.FontWeight.bold))),
|
|
pw.Padding(padding: pw.EdgeInsets.all(8), child: pw.Text('Total', style: pw.TextStyle(fontWeight: pw.FontWeight.bold))),
|
|
pw.Padding(padding: pw.EdgeInsets.all(8), child: pw.Text('Bayar', style: pw.TextStyle(fontWeight: pw.FontWeight.bold))),
|
|
pw.Padding(padding: pw.EdgeInsets.all(8), child: pw.Text('Kembali', style: pw.TextStyle(fontWeight: pw.FontWeight.bold))),
|
|
],
|
|
),
|
|
for (final tx in transactions)
|
|
pw.TableRow(
|
|
children: [
|
|
pw.Padding(padding: const pw.EdgeInsets.all(8), child: pw.Text(_formatDate(tx['date']))),
|
|
pw.Padding(padding: const pw.EdgeInsets.all(8), child: pw.Text(tx['id'])),
|
|
pw.Padding(padding: const pw.EdgeInsets.all(8), child: pw.Text(tx['total'])),
|
|
pw.Padding(padding: const pw.EdgeInsets.all(8), child: pw.Text(tx['paid'])),
|
|
pw.Padding(padding: const pw.EdgeInsets.all(8), child: pw.Text(tx['change'])),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
|
|
Directory? directory;
|
|
try {
|
|
directory = await getTemporaryDirectory();
|
|
debugPrint('PDF using temporary directory: ${directory.path}');
|
|
} catch (e) {
|
|
debugPrint('getTemporaryDirectory failed for PDF: $e');
|
|
return null;
|
|
}
|
|
|
|
final file = File('${directory.path}/laporan_transaksi_${DateTime.now().millisecondsSinceEpoch}.pdf');
|
|
try {
|
|
await file.writeAsBytes(await pdf.save());
|
|
debugPrint('PDF file written: ${file.path}, exists: ${await file.exists()}');
|
|
} catch (e) {
|
|
debugPrint('Failed to write PDF file: $e');
|
|
return null;
|
|
}
|
|
|
|
return file.path;
|
|
} catch (e) {
|
|
debugPrint('exportToPdf error: $e');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<String?> exportToCsv(List<Map<String, dynamic>> transactions) async {
|
|
try {
|
|
debugPrint('=== CSV Export Started ===');
|
|
debugPrint('Transactions count: ${transactions.length}');
|
|
|
|
if (transactions.isEmpty) {
|
|
debugPrint('No transactions to export');
|
|
return null;
|
|
}
|
|
|
|
final buffer = StringBuffer();
|
|
buffer.writeln('Tanggal,ID Transaksi,Total,Bayar,Kembali,Item,Diskon');
|
|
|
|
for (final tx in transactions) {
|
|
final date = _formatDate(tx['date']);
|
|
final id = tx['id'];
|
|
final total = tx['total'];
|
|
final paid = tx['paid'];
|
|
final change = tx['change'];
|
|
final items = tx['items'] is List ? (tx['items'] as List).length : (tx['items'] as int? ?? 0);
|
|
final discount = tx['discount'] ?? 0;
|
|
|
|
buffer.writeln('$date,$id,$total,$paid,$change,$items,$discount');
|
|
}
|
|
|
|
debugPrint('CSV buffer created, length: ${buffer.toString().length}');
|
|
|
|
String? filePath;
|
|
try {
|
|
final tempDir = await getTemporaryDirectory();
|
|
debugPrint('Temp dir: ${tempDir.path}');
|
|
|
|
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
|
final fileName = 'laporan_transaksi_$timestamp.csv';
|
|
filePath = '${tempDir.path}/$fileName';
|
|
|
|
final file = File(filePath);
|
|
await file.writeAsString(buffer.toString());
|
|
debugPrint('CSV file written: $filePath');
|
|
debugPrint('File exists: ${await file.exists()}');
|
|
debugPrint('File size: ${await file.length()}');
|
|
} catch (e) {
|
|
debugPrint('Error creating CSV file: $e');
|
|
return null;
|
|
}
|
|
|
|
debugPrint('=== CSV Export Completed Successfully ===');
|
|
return filePath;
|
|
} catch (e) {
|
|
debugPrint('=== CSV Export Failed ===');
|
|
debugPrint('Error: $e');
|
|
debugPrint('Stack trace: ${StackTrace.current}');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<void> shareFile(String filePath) async {
|
|
try {
|
|
debugPrint('Sharing file: $filePath');
|
|
final file = File(filePath);
|
|
final exists = await file.exists();
|
|
debugPrint('File exists: $exists, size: ${exists ? await file.length() : 0}');
|
|
|
|
if (!exists) {
|
|
debugPrint('File does not exist, cannot share');
|
|
return;
|
|
}
|
|
|
|
await Share.shareXFiles([XFile(filePath)], text: 'Laporan Transaksi');
|
|
debugPrint('Share completed');
|
|
} catch (e) {
|
|
debugPrint('shareFile error: $e');
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
String _formatDate(dynamic date) {
|
|
if (date is DateTime) {
|
|
return '${date.day}/${date.month}/${date.year}';
|
|
}
|
|
return date.toString();
|
|
}
|
|
} |