import 'dart:io'; import 'package:flutter/services.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; import '../../../common/utils/pdf_service.dart'; import '../../../domain/analytic/analytic.dart'; import '../../../domain/outlet/outlet.dart'; class InventoryReport { static final primaryColor = PdfColor.fromHex("36175e"); static Future previewPdf({ required String searchDateFormatted, required InventoryAnalytic inventory, required Outlet outlet, }) async { final pdf = pw.Document(); final ByteData dataImage = await rootBundle.load('assets/images/logo.png'); final Uint8List bytes = dataImage.buffer.asUint8List(); final image = pw.MemoryImage(bytes); pdf.addPage( pw.MultiPage( pageFormat: PdfPageFormat.a4, margin: pw.EdgeInsets.zero, build: (pw.Context context) { return [ pw.Container( padding: pw.EdgeInsets.all(20), child: pw.Row( mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ // Bagian kiri - Logo dan Info Perusahaan pw.Row( crossAxisAlignment: pw.CrossAxisAlignment.center, children: [ // Icon/Logo placeholder (bisa diganti dengan gambar logo) pw.Container( width: 40, height: 40, child: pw.Image(image), ), pw.SizedBox(width: 15), pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ pw.Text( 'Apskel', style: pw.TextStyle( fontSize: 28, fontWeight: pw.FontWeight.bold, color: primaryColor, ), ), pw.SizedBox(height: 4), pw.Text( outlet.name, style: pw.TextStyle( fontSize: 16, color: PdfColors.grey700, ), ), pw.SizedBox(height: 2), pw.Text( outlet.address, style: pw.TextStyle( fontSize: 12, color: PdfColors.grey600, ), ), ], ), ], ), // Bagian kanan - Info Laporan pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.end, children: [ pw.Text( 'Laporan Transaksi', style: pw.TextStyle( fontSize: 24, fontWeight: pw.FontWeight.bold, color: PdfColors.grey800, ), ), pw.SizedBox(height: 8), pw.Text( searchDateFormatted, style: pw.TextStyle( fontSize: 14, color: PdfColors.grey600, ), ), pw.SizedBox(height: 4), pw.Text( 'Laporan', style: pw.TextStyle( fontSize: 12, color: PdfColors.grey500, ), ), ], ), ], ), ), pw.Container( width: double.infinity, height: 3, color: primaryColor, ), // Summary pw.Container( padding: pw.EdgeInsets.all(20), child: pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ _buildSectionWidget('1. Ringkasan'), pw.SizedBox(height: 30), pw.Row( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ pw.Expanded( flex: 1, child: pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ _buildSummaryItem( 'Total Item', (inventory.summary.totalProducts).toString(), ), _buildSummaryItem( 'Total Item Masuk', (inventory.products.fold( 0, (sum, item) => sum + (item.totalIn), )).toString(), ), _buildSummaryItem( 'Total Item Keluar', (inventory.products.fold( 0, (sum, item) => sum + (item.totalOut), )).toString(), ), ], ), ), pw.SizedBox(width: 20), pw.Expanded( flex: 1, child: pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ _buildSummaryItem( 'Total Ingredient', (inventory.summary.totalIngredients).toString(), ), _buildSummaryItem( 'Total Ingredient Masuk', (inventory.ingredients.fold( 0, (sum, item) => sum + (item.totalIn), )).toString(), ), _buildSummaryItem( 'Total Ingredient Keluar', (inventory.ingredients.fold( 0, (sum, item) => sum + (item.totalOut), )).toString(), ), ], ), ), ], ), ], ), ), // Summary Item pw.Container( padding: pw.EdgeInsets.all(20), child: pw.Column( children: [ pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ _buildSectionWidget('2. Item'), pw.SizedBox(height: 30), pw.Container( decoration: pw.BoxDecoration( color: primaryColor, // Purple color borderRadius: pw.BorderRadius.only( topLeft: pw.Radius.circular(8), topRight: pw.Radius.circular(8), ), ), child: pw.Table( columnWidths: const { 0: pw.FlexColumnWidth(2.5), // Produk 1: pw.FlexColumnWidth(2), // Kategori 2: pw.FlexColumnWidth(1), // Stock 3: pw.FlexColumnWidth(2), // Masuk 4: pw.FlexColumnWidth(2), // Keluar }, children: [ pw.TableRow( children: [ _buildHeaderCell('Nama'), _buildHeaderCell('Kategori'), _buildHeaderCell('Stock'), _buildHeaderCell('Masuk'), _buildHeaderCell('Keluar'), ], ), ], ), ), pw.Container( decoration: pw.BoxDecoration(color: PdfColors.white), child: pw.Table( columnWidths: { 0: pw.FlexColumnWidth(2.5), // Produk 1: pw.FlexColumnWidth(2), // Kategori 2: pw.FlexColumnWidth(1), // Stock 3: pw.FlexColumnWidth(2), // Masuk 4: pw.FlexColumnWidth(2), // Keluar }, children: inventory.products .map( (item) => _buildProductDataRow( item, inventory.products.indexOf(item) % 2 == 0, ), ) .toList(), ), ), pw.Container( decoration: pw.BoxDecoration( color: primaryColor, // Purple color borderRadius: pw.BorderRadius.only( bottomLeft: pw.Radius.circular(8), bottomRight: pw.Radius.circular(8), ), ), child: pw.Table( columnWidths: const { 0: pw.FlexColumnWidth(2.5), // Produk 1: pw.FlexColumnWidth(2), // Kategori 2: pw.FlexColumnWidth(1), // Stock 3: pw.FlexColumnWidth(2), // Masuk 4: pw.FlexColumnWidth(2), // Keluar }, children: [ pw.TableRow( children: [ _buildTotalCell('TOTAL'), _buildTotalCell(''), _buildTotalCell( (inventory.products.fold( 0, (sum, item) => sum + (item.quantity), )).toString(), ), _buildTotalCell( (inventory.products.fold( 0, (sum, item) => sum + (item.totalIn), )).toString(), ), _buildTotalCell( (inventory.products.fold( 0, (sum, item) => sum + (item.totalOut), )).toString(), ), ], ), ], ), ), ], ), ], ), ), // Summary Ingredient pw.Container( padding: pw.EdgeInsets.all(20), child: pw.Column( children: [ pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ _buildSectionWidget('3. Ingredient'), pw.SizedBox(height: 30), pw.Container( decoration: pw.BoxDecoration( color: primaryColor, // Purple color borderRadius: pw.BorderRadius.only( topLeft: pw.Radius.circular(8), topRight: pw.Radius.circular(8), ), ), child: pw.Table( columnWidths: const { 0: pw.FlexColumnWidth(2.5), // Name 1: pw.FlexColumnWidth(1), // Stock 2: pw.FlexColumnWidth(2), // Masuk 3: pw.FlexColumnWidth(2), // Keluar }, children: [ pw.TableRow( children: [ _buildHeaderCell('Nama'), _buildHeaderCell('Stock'), _buildHeaderCell('Masuk'), _buildHeaderCell('Keluar'), ], ), ], ), ), pw.Container( decoration: pw.BoxDecoration(color: PdfColors.white), child: pw.Table( columnWidths: { 0: pw.FlexColumnWidth(2.5), // Name 1: pw.FlexColumnWidth(1), // Stock 2: pw.FlexColumnWidth(2), // Masuk 3: pw.FlexColumnWidth(2), // Keluar }, children: inventory.ingredients .map( (item) => _buildIngredientsDataRow( item, inventory.ingredients.indexOf(item) % 2 == 0, ), ) .toList(), ), ), pw.Container( decoration: pw.BoxDecoration( color: primaryColor, // Purple color borderRadius: pw.BorderRadius.only( bottomLeft: pw.Radius.circular(8), bottomRight: pw.Radius.circular(8), ), ), child: pw.Table( columnWidths: const { 0: pw.FlexColumnWidth(2.5), // Name 1: pw.FlexColumnWidth(1), // Stock 2: pw.FlexColumnWidth(2), // Masuk 3: pw.FlexColumnWidth(2), // Keluar }, children: [ pw.TableRow( children: [ _buildTotalCell('TOTAL'), _buildTotalCell( (inventory.ingredients.fold( 0, (sum, item) => sum + (item.quantity), )).toString(), ), _buildTotalCell( (inventory.ingredients.fold( 0, (sum, item) => sum + (item.totalIn), )).toString(), ), _buildTotalCell( (inventory.ingredients.fold( 0, (sum, item) => sum + (item.totalOut), )).toString(), ), ], ), ], ), ), ], ), ], ), ), ]; }, ), ); return HelperPdfService.saveDocument( name: 'Apskel POS | Inventory Report | ${DateTime.now().millisecondsSinceEpoch}.pdf', pdf: pdf, ); } static pw.Widget _buildSectionWidget(String title) { return pw.Text( title, style: pw.TextStyle( fontSize: 20, fontWeight: pw.FontWeight.bold, color: primaryColor, ), ); } static pw.Widget _buildSummaryItem( String label, String value, { pw.TextStyle? valueStyle, pw.TextStyle? labelStyle, }) { return pw.Container( padding: pw.EdgeInsets.only(bottom: 8), margin: pw.EdgeInsets.only(bottom: 16), decoration: pw.BoxDecoration( border: pw.Border(bottom: pw.BorderSide(color: PdfColors.grey300)), ), child: pw.Row( mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, children: [ pw.Text(label, style: labelStyle), pw.Text( value, style: valueStyle ?? pw.TextStyle(fontWeight: pw.FontWeight.bold), ), ], ), ); } static pw.Widget _buildHeaderCell(String text) { return pw.Container( padding: pw.EdgeInsets.symmetric(horizontal: 12, vertical: 16), child: pw.Text( text, style: pw.TextStyle( color: PdfColors.white, fontWeight: pw.FontWeight.bold, fontSize: 12, ), textAlign: pw.TextAlign.center, ), ); } static pw.Widget _buildDataCell( String text, { pw.Alignment alignment = pw.Alignment.center, PdfColor? textColor, }) { return pw.Container( padding: pw.EdgeInsets.symmetric(horizontal: 12, vertical: 16), alignment: alignment, child: pw.Text( text, style: pw.TextStyle( fontSize: 12, color: textColor ?? PdfColors.black, fontWeight: pw.FontWeight.normal, ), textAlign: alignment == pw.Alignment.centerLeft ? pw.TextAlign.left : pw.TextAlign.center, ), ); } static pw.Widget _buildTotalCell(String text) { return pw.Container( padding: pw.EdgeInsets.symmetric(horizontal: 12, vertical: 16), child: pw.Text( text, style: pw.TextStyle( color: PdfColors.white, fontWeight: pw.FontWeight.bold, fontSize: 12, ), textAlign: pw.TextAlign.center, ), ); } static pw.TableRow _buildProductDataRow( InventoryProduct product, bool isEven, ) { return pw.TableRow( decoration: pw.BoxDecoration( color: product.isZeroStock ? PdfColors.red100 : product.isLowStock ? PdfColors.yellow100 : isEven ? PdfColors.grey50 : PdfColors.white, ), children: [ _buildDataCell(product.productName, alignment: pw.Alignment.centerLeft), _buildDataCell( product.categoryName, alignment: pw.Alignment.centerLeft, ), _buildDataCell(product.quantity.toString()), _buildDataCell(product.totalIn.toString()), _buildDataCell(product.totalOut.toString()), ], ); } static pw.TableRow _buildIngredientsDataRow( InventoryIngredient item, bool isEven, ) { return pw.TableRow( decoration: pw.BoxDecoration( color: item.isZeroStock ? PdfColors.red100 : item.isLowStock ? PdfColors.yellow100 : isEven ? PdfColors.grey50 : PdfColors.white, ), children: [ _buildDataCell(item.ingredientName, alignment: pw.Alignment.centerLeft), _buildDataCell(item.quantity.toString()), _buildDataCell(item.totalIn.toString()), _buildDataCell(item.totalOut.toString()), ], ); } }