213 lines
6.6 KiB
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;
|
|
}
|