TKK_E32231105/lib/services/excel_export_service.dart

213 lines
6.6 KiB
Dart

import 'package:excel/excel.dart';
import 'package:intl/intl.dart';
import '../models/panen_model.dart';
import 'excel_save_helper.dart';
class ExcelExportService {
static Future<String> exportWeeklyReport({
required List<Panen> panens,
required DateTime startDate,
required DateTime endDate,
required String kandangFilter,
}) async {
try {
print('[ExcelExportService] Starting export...');
final bytes = _generateExcelContent(
panens,
startDate,
endDate,
kandangFilter,
);
final now = DateTime.now();
final fileName = 'Laporan_Telur_${_formatDate(now)}.xlsx';
final savedLocation = await saveReportFile(
fileName: fileName,
bytes: bytes,
mimeType:
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
);
print('[ExcelExportService] Export completed successfully');
return savedLocation;
} catch (e, stackTrace) {
print('[ExcelExportService] ERROR: $e');
print('[ExcelExportService] Stack trace: $stackTrace');
throw Exception('Error creating report: $e');
}
}
static String _formatDate(DateTime date) {
final day = date.day.toString().padLeft(2, '0');
final month = date.month.toString().padLeft(2, '0');
final year = date.year;
return '$day-$month-$year';
}
static List<int> _generateExcelContent(
List<Panen> panens,
DateTime startDate,
DateTime endDate,
String kandangFilter,
) {
final excel = Excel.createExcel();
const sheetName = 'Laporan';
final defaultSheet = excel.getDefaultSheet();
if (defaultSheet != null && defaultSheet != sheetName) {
excel.rename(defaultSheet, sheetName);
}
final sheet = excel[sheetName];
final boldStyle = CellStyle(bold: true);
CellIndex idx(int row, int col) =>
CellIndex.indexByColumnRow(columnIndex: col, rowIndex: row);
void writeText(int row, int col, String value, {bool bold = false}) {
sheet.updateCell(
idx(row, col),
TextCellValue(value),
cellStyle: bold ? boldStyle : null,
);
}
void writeInt(int row, int col, int value, {bool bold = false}) {
sheet.updateCell(
idx(row, col),
IntCellValue(value),
cellStyle: bold ? boldStyle : null,
);
}
int totalKandang1Pagi = 0;
int totalKandang1Sore = 0;
int totalKandang2Pagi = 0;
int totalKandang2Sore = 0;
int totalOverall = 0;
final perHari = <DateTime, _DailyReport>{};
for (final panen in panens) {
final dateKey = DateTime(
panen.tanggalPanen.year,
panen.tanggalPanen.month,
panen.tanggalPanen.day,
);
final daily = perHari.putIfAbsent(dateKey, () => _DailyReport());
final jumlah = panen.jumlahTelur;
final normalized = _normalizeKandang(panen.kandangId, panen.kandangNama);
final jenis = _normalizeJenisPanen(panen);
if (normalized.contains('kandang1')) {
if (jenis == 'pagi') {
totalKandang1Pagi += jumlah;
daily.kandang1Pagi += jumlah;
} else if (jenis == 'sore') {
totalKandang1Sore += jumlah;
daily.kandang1Sore += jumlah;
}
} else if (normalized.contains('kandang2')) {
if (jenis == 'pagi') {
totalKandang2Pagi += jumlah;
daily.kandang2Pagi += jumlah;
} else if (jenis == 'sore') {
totalKandang2Sore += jumlah;
daily.kandang2Sore += jumlah;
}
}
totalOverall += jumlah;
daily.total += jumlah;
}
final periodText =
'${DateFormat('dd/MM/yyyy').format(startDate)} - ${DateFormat('dd/MM/yyyy').format(endDate)}';
final filterText =
kandangFilter == 'semua' ? 'Semua Kandang' : kandangFilter;
writeText(0, 0, 'LAPORAN PRODUKSI TELUR', bold: true);
writeText(1, 0, 'Periode', bold: true);
writeText(1, 1, periodText);
writeText(2, 0, 'Filter Kandang', bold: true);
writeText(2, 1, filterText);
final totalKandang1 = totalKandang1Pagi + totalKandang1Sore;
final totalKandang2 = totalKandang2Pagi + totalKandang2Sore;
writeText(4, 0, 'Jumlah total telur kandang 1 (pagi)', bold: true);
writeInt(4, 1, totalKandang1Pagi, bold: true);
writeText(5, 0, 'Jumlah total telur kandang 1 (sore)', bold: true);
writeInt(5, 1, totalKandang1Sore, bold: true);
writeText(6, 0, 'Jumlah total telur kandang 1', bold: true);
writeInt(6, 1, totalKandang1, bold: true);
writeText(8, 0, 'Jumlah total telur kandang 2 (pagi)', bold: true);
writeInt(8, 1, totalKandang2Pagi, bold: true);
writeText(9, 0, 'Jumlah total telur kandang 2 (sore)', bold: true);
writeInt(9, 1, totalKandang2Sore, bold: true);
writeText(10, 0, 'Jumlah total telur kandang 2', bold: true);
writeInt(10, 1, totalKandang2, bold: true);
writeText(12, 0, 'Total keseluruhan', bold: true);
writeInt(12, 1, totalOverall, bold: true);
writeText(14, 0, 'Tanggal', bold: true);
writeText(14, 1, 'Kandang 1 Pagi', bold: true);
writeText(14, 2, 'Kandang 1 Sore', bold: true);
writeText(14, 3, 'Kandang 2 Pagi', bold: true);
writeText(14, 4, 'Kandang 2 Sore', bold: true);
writeText(14, 5, 'Total Telur Per Hari', bold: true);
final sortedDates = perHari.keys.toList()..sort((a, b) => a.compareTo(b));
var row = 15;
for (final date in sortedDates) {
final daily = perHari[date]!;
writeText(row, 0, DateFormat('dd/MM/yyyy').format(date));
writeInt(row, 1, daily.kandang1Pagi);
writeInt(row, 2, daily.kandang1Sore);
writeInt(row, 3, daily.kandang2Pagi);
writeInt(row, 4, daily.kandang2Sore);
writeInt(row, 5, daily.total);
row++;
}
final encoded = excel.encode();
if (encoded == null) {
throw Exception('Gagal membuat file Excel');
}
return encoded;
}
static String _normalizeKandang(String kandangId, String kandangNama) {
return '${kandangId.toLowerCase()} ${kandangNama.toLowerCase()}'
.replaceAll('_', '')
.replaceAll(' ', '');
}
static String _normalizeJenisPanen(Panen panen) {
final explicit = (panen.jenisPanen ?? '').trim().toLowerCase();
if (explicit == 'pagi' || explicit == 'sore') {
return explicit;
}
final match = RegExp(r'^(\d{1,2})[:.]').firstMatch(panen.jam.trim());
final hour = match != null ? int.tryParse(match.group(1)!) : null;
if (hour != null) {
return hour < 12 ? 'pagi' : 'sore';
}
return 'sore';
}
}
class _DailyReport {
int kandang1Pagi = 0;
int kandang1Sore = 0;
int kandang2Pagi = 0;
int kandang2Sore = 0;
int total = 0;
}