chore: initial commit Dutra Store POS app
This commit is contained in:
167
lib/services/export_service.dart
Normal file
167
lib/services/export_service.dart
Normal file
@@ -0,0 +1,167 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user