import 'dart:io'; import 'dart:developer'; import 'package:enaklo_pos/core/extensions/date_time_ext.dart'; import 'package:enaklo_pos/core/extensions/int_ext.dart'; import 'package:enaklo_pos/data/models/response/summary_response_model.dart'; import 'package:flutter/services.dart'; import 'package:enaklo_pos/core/utils/helper_pdf_service.dart'; import 'package:pdf/widgets.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; import 'package:flutter/foundation.dart'; class RevenueInvoice { static late Font ttf; static Future generate( SummaryModel summaryModel, String searchDateFormatted, ) async { try { log("Starting PDF generation for summary report"); log("Summary model: ${summaryModel.toMap()}"); log("Search date formatted: $searchDateFormatted"); final pdf = Document(); log("PDF document created"); // Load logo image log("Loading logo image..."); final ByteData dataImage = await rootBundle.load('assets/images/logo.png'); final Uint8List bytes = dataImage.buffer.asUint8List(); final image = pw.MemoryImage(bytes); log("Logo image loaded successfully, size: ${bytes.length} bytes"); log("Adding page to PDF..."); pdf.addPage( MultiPage( build: (context) => [ buildHeader(summaryModel, image, searchDateFormatted), SizedBox(height: 1 * PdfPageFormat.cm), buildTotal(summaryModel), ], footer: (context) => buildFooter(summaryModel), ), ); log("PDF page added successfully"); log("Saving PDF document..."); return HelperPdfService.saveDocument( name: 'Apskel POS | Summary Sales Report | ${DateTime.now().millisecondsSinceEpoch}.pdf', pdf: pdf, ); } catch (e) { log("Error generating PDF: $e"); log("Error stack trace: ${StackTrace.current}"); return Future.error("Failed to generate PDF: $e"); } } static Widget buildHeader( SummaryModel invoice, MemoryImage image, String searchDateFormatted, ) => Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 1 * PdfPageFormat.cm), Text('Apskel POS | Summary Sales Report', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, )), SizedBox(height: 0.2 * PdfPageFormat.cm), Text( "Data: $searchDateFormatted", ), Text( 'Created At: ${DateTime.now().toFormattedDate3()}', ), ], ), Image( image, width: 80.0, height: 80.0, fit: BoxFit.fill, ), ]); static Widget buildTotal(SummaryModel summaryModel) { log("Building total section with summary model: ${summaryModel.toMap()}"); // Helper function to safely parse string to int int safeParseInt(String? value) { if (value == null || value.isEmpty) return 0; try { return int.parse(value.replaceAll('.00', '')); } catch (e) { log("Error parsing value '$value' to int: $e"); return 0; } } return Container( width: double.infinity, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildText( title: 'Revenue', value: safeParseInt(summaryModel.totalRevenue).currencyFormatRp, unite: true, ), Divider(), buildText( title: 'Sub Total', titleStyle: TextStyle(fontWeight: FontWeight.normal), value: safeParseInt(summaryModel.totalSubtotal).currencyFormatRp, unite: true, ), buildText( title: 'Discount', titleStyle: TextStyle(fontWeight: FontWeight.normal), value: "- ${safeParseInt(summaryModel.totalDiscount).currencyFormatRp}", unite: true, textStyle: TextStyle( color: PdfColor.fromHex('#FF0000'), fontWeight: FontWeight.bold, ), ), buildText( title: 'Tax', titleStyle: TextStyle(fontWeight: FontWeight.normal), value: "- ${safeParseInt(summaryModel.totalTax).currencyFormatRp}", textStyle: TextStyle( color: PdfColor.fromHex('#FF0000'), fontWeight: FontWeight.bold, ), unite: true, ), buildText( title: 'Service Charge', titleStyle: TextStyle( fontWeight: FontWeight.normal, ), value: safeParseInt(summaryModel.totalServiceCharge).currencyFormatRp, unite: true, ), Divider(), buildText( title: 'Total ', titleStyle: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, ), value: (summaryModel.total ?? 0).currencyFormatRp, unite: true, ), SizedBox(height: 2 * PdfPageFormat.mm), Container(height: 1, color: PdfColors.grey400), SizedBox(height: 0.5 * PdfPageFormat.mm), Container(height: 1, color: PdfColors.grey400), ], ), ); } static Widget buildFooter(SummaryModel summaryModel) => Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Divider(), SizedBox(height: 2 * PdfPageFormat.mm), buildSimpleText( title: 'Address', value: 'Jalan Melati No. 12, Mranggen, Demak, Central Java, 89568'), SizedBox(height: 1 * PdfPageFormat.mm), ], ); static buildSimpleText({ required String title, required String value, }) { final style = TextStyle(fontWeight: FontWeight.bold); return Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: pw.CrossAxisAlignment.end, children: [ Text(title, style: style), SizedBox(width: 2 * PdfPageFormat.mm), Text(value), ], ); } static buildText({ required String title, required String value, double width = double.infinity, TextStyle? titleStyle, TextStyle? textStyle, bool unite = false, }) { final style = titleStyle ?? TextStyle(fontWeight: FontWeight.bold); final style2 = textStyle ?? TextStyle(fontWeight: FontWeight.bold); return Container( width: width, child: Row( children: [ Expanded(child: Text(title, style: style)), Text(value, style: style2), ], ), ); } }