feat: report transaction
This commit is contained in:
parent
0c375de6e4
commit
f37814fec8
912
lib/core/utils/transaction_report.dart
Normal file
912
lib/core/utils/transaction_report.dart
Normal file
@ -0,0 +1,912 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:enaklo_pos/core/extensions/string_ext.dart';
|
||||
import 'package:enaklo_pos/core/utils/helper_pdf_service.dart';
|
||||
import 'package:enaklo_pos/data/models/response/category_analytic_response_model.dart';
|
||||
import 'package:enaklo_pos/data/models/response/payment_method_analytic_response_model.dart';
|
||||
import 'package:enaklo_pos/data/models/response/product_analytic_response_model.dart';
|
||||
import 'package:enaklo_pos/data/models/response/profit_loss_response_model.dart';
|
||||
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:pdf/pdf.dart';
|
||||
import 'package:pdf/widgets.dart' as pw;
|
||||
|
||||
class TransactionReport {
|
||||
static final primaryColor = PdfColor.fromHex("36175e");
|
||||
|
||||
static Future<File> previewPdf({
|
||||
required Outlet outlet,
|
||||
required String searchDateFormatted,
|
||||
required CategoryAnalyticData? categoryAnalyticData,
|
||||
required ProfitLossData? profitLossData,
|
||||
required PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
required ProductAnalyticData? productAnalyticData,
|
||||
}) async {
|
||||
final pdf = pw.Document();
|
||||
final ByteData dataImage = await rootBundle.load('assets/logo/logo.png');
|
||||
final Uint8List bytes = dataImage.buffer.asUint8List();
|
||||
|
||||
final profitLossProductSummary = {
|
||||
'totalRevenue': profitLossData?.productData
|
||||
.fold<num>(0, (sum, item) => sum + (item.revenue)) ??
|
||||
0,
|
||||
'totalCost': profitLossData?.productData
|
||||
.fold<num>(0, (sum, item) => sum + (item.cost)) ??
|
||||
0,
|
||||
'totalGrossProfit': profitLossData?.productData
|
||||
.fold<num>(0, (sum, item) => sum + (item.grossProfit)) ??
|
||||
0,
|
||||
'totalQuantity': profitLossData?.productData
|
||||
.fold<num>(0, (sum, item) => sum + (item.quantitySold)) ??
|
||||
0,
|
||||
};
|
||||
|
||||
final categorySummary = {
|
||||
'totalRevenue': categoryAnalyticData?.data
|
||||
.fold<num>(0, (sum, item) => sum + (item.totalRevenue)) ??
|
||||
0,
|
||||
'orderCount': categoryAnalyticData?.data
|
||||
.fold<num>(0, (sum, item) => sum + (item.orderCount)) ??
|
||||
0,
|
||||
'productCount': categoryAnalyticData?.data
|
||||
.fold<num>(0, (sum, item) => sum + (item.productCount)) ??
|
||||
0,
|
||||
'totalQuantity': categoryAnalyticData?.data
|
||||
.fold<num>(0, (sum, item) => sum + (item.totalQuantity)) ??
|
||||
0,
|
||||
};
|
||||
|
||||
final productItemSummary = {
|
||||
'totalRevenue': productAnalyticData?.data
|
||||
.fold<num>(0, (sum, item) => sum + (item.revenue)) ??
|
||||
0,
|
||||
'orderCount': productAnalyticData?.data
|
||||
.fold<num>(0, (sum, item) => sum + (item.orderCount)) ??
|
||||
0,
|
||||
'totalQuantitySold': productAnalyticData?.data
|
||||
.fold<num>(0, (sum, item) => sum + (item.quantitySold)) ??
|
||||
0,
|
||||
};
|
||||
|
||||
// Membuat objek Image dari gambar
|
||||
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 Penjualan (termasuk rasik)',
|
||||
(profitLossData?.summary.totalRevenue ?? 0)
|
||||
.toString()
|
||||
.currencyFormatRpV2,
|
||||
),
|
||||
_buildSummaryItem(
|
||||
'Total Total Terjual',
|
||||
(profitLossData?.summary.totalOrders ?? 0)
|
||||
.toString(),
|
||||
),
|
||||
_buildSummaryItem(
|
||||
'HPP',
|
||||
'${(profitLossData?.summary.totalCost ?? 0).toString().currencyFormatRpV2} | ${(((profitLossData?.summary.totalCost ?? 0) / (profitLossData?.summary.totalRevenue ?? 1)) * 100).round()}%',
|
||||
),
|
||||
_buildSummaryItem(
|
||||
'Laba Kotor',
|
||||
'${(profitLossData?.summary.grossProfit ?? 0).toString().currencyFormatRpV2} | ${(profitLossData?.summary.grossProfitMargin ?? 0).round()}%',
|
||||
valueStyle: pw.TextStyle(
|
||||
color: PdfColors.green800,
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
labelStyle: pw.TextStyle(
|
||||
color: PdfColors.green800,
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
pw.SizedBox(width: 20),
|
||||
pw.Expanded(
|
||||
flex: 1,
|
||||
child: pw.Column(
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSummaryItem(
|
||||
'Biaya Lain lain',
|
||||
'${(profitLossData?.summary.totalTax ?? 0).toString().currencyFormatRpV2} | ${(((profitLossData?.summary.totalTax ?? 0) / (profitLossData?.summary.totalRevenue ?? 1)) * 100).round()}%',
|
||||
),
|
||||
_buildSummaryItem(
|
||||
'Laba/Rugi',
|
||||
'${(profitLossData?.summary.netProfit ?? 0).toString().currencyFormatRpV2} | ${(profitLossData?.summary.netProfitMargin ?? 0).round()}%',
|
||||
valueStyle: pw.TextStyle(
|
||||
color: PdfColors.blue800,
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
labelStyle: pw.TextStyle(
|
||||
color: PdfColors.blue800,
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
pw.SizedBox(height: 16),
|
||||
pw.Text(
|
||||
"Laba Rugi Perproduk",
|
||||
style: pw.TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
pw.SizedBox(height: 20),
|
||||
pw.Column(
|
||||
children: [
|
||||
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(1), // Qty
|
||||
2: pw.FlexColumnWidth(2.5), // Pendapatan
|
||||
3: pw.FlexColumnWidth(2), // HPP
|
||||
4: pw.FlexColumnWidth(2), // Laba Kotor
|
||||
5: pw.FlexColumnWidth(2), // Margin (%)
|
||||
},
|
||||
children: [
|
||||
pw.TableRow(
|
||||
children: [
|
||||
_buildHeaderCell('Produk'),
|
||||
_buildHeaderCell('Qty'),
|
||||
_buildHeaderCell('Pendapatan'),
|
||||
_buildHeaderCell('HPP'),
|
||||
_buildHeaderCell('Laba Kotor'),
|
||||
_buildHeaderCell('Margin (%)'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
pw.Container(
|
||||
decoration: pw.BoxDecoration(
|
||||
color: PdfColors.white,
|
||||
),
|
||||
child: pw.Table(
|
||||
columnWidths: {
|
||||
0: pw.FlexColumnWidth(2.5), // Produk
|
||||
1: pw.FlexColumnWidth(1), // Qty
|
||||
2: pw.FlexColumnWidth(2.5), // Pendapatan
|
||||
3: pw.FlexColumnWidth(2), // HPP
|
||||
4: pw.FlexColumnWidth(2), // Laba Kotor
|
||||
5: pw.FlexColumnWidth(2), // Margin (%)
|
||||
},
|
||||
children: profitLossData?.productData
|
||||
.map(
|
||||
(profitLoss) => _buildPerProductDataRow(
|
||||
product: profitLoss.productName,
|
||||
qty: profitLoss.quantitySold.toString(),
|
||||
pendapatan: profitLoss.revenue
|
||||
.toString()
|
||||
.currencyFormatRpV2,
|
||||
hpp: profitLoss.cost
|
||||
.toString()
|
||||
.currencyFormatRpV2,
|
||||
labaKotor: profitLoss.grossProfit
|
||||
.toString()
|
||||
.currencyFormatRpV2,
|
||||
margin:
|
||||
'${profitLoss.grossProfitMargin.round()}%',
|
||||
isEven: profitLossData.productData
|
||||
.indexOf(profitLoss) %
|
||||
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(1), // Qty
|
||||
2: pw.FlexColumnWidth(2.5), // Pendapatan
|
||||
3: pw.FlexColumnWidth(2), // HPP
|
||||
4: pw.FlexColumnWidth(2), // Laba Kotor
|
||||
5: pw.FlexColumnWidth(2), // Margin (%)
|
||||
},
|
||||
children: [
|
||||
pw.TableRow(
|
||||
children: [
|
||||
_buildTotalCell('TOTAL'),
|
||||
_buildTotalCell(
|
||||
profitLossProductSummary['totalQuantity']
|
||||
.toString()),
|
||||
_buildTotalCell(
|
||||
profitLossProductSummary['totalRevenue']
|
||||
.toString()
|
||||
.currencyFormatRpV2),
|
||||
_buildTotalCell(
|
||||
profitLossProductSummary['totalCost']
|
||||
.toString()
|
||||
.currencyFormatRpV2),
|
||||
_buildTotalCell(
|
||||
profitLossProductSummary['totalGrossProfit']
|
||||
.toString()
|
||||
.currencyFormatRpV2),
|
||||
_buildTotalCell(''),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Summary Payment Method
|
||||
pw.Container(
|
||||
padding: pw.EdgeInsets.all(20),
|
||||
child: pw.Column(
|
||||
children: [
|
||||
pw.Column(
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionWidget('2. Ringkasan Metode Pembayaran'),
|
||||
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), // Nama
|
||||
1: pw.FlexColumnWidth(1), // Tipe
|
||||
2: pw.FlexColumnWidth(2.5), // Jumlah Order
|
||||
3: pw.FlexColumnWidth(2), // Total Amount
|
||||
4: pw.FlexColumnWidth(2), // Presentase
|
||||
},
|
||||
children: [
|
||||
pw.TableRow(
|
||||
children: [
|
||||
_buildHeaderCell('Nama'),
|
||||
_buildHeaderCell('Tipe'),
|
||||
_buildHeaderCell('Jumlah Order'),
|
||||
_buildHeaderCell('Total Amount'),
|
||||
_buildHeaderCell('Presentase'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
pw.Container(
|
||||
decoration: pw.BoxDecoration(
|
||||
color: PdfColors.white,
|
||||
),
|
||||
child: pw.Table(
|
||||
columnWidths: {
|
||||
0: pw.FlexColumnWidth(2.5), // Nama
|
||||
1: pw.FlexColumnWidth(1), // Tipe
|
||||
2: pw.FlexColumnWidth(2.5), // Jumlah Order
|
||||
3: pw.FlexColumnWidth(2), // Total Amount
|
||||
4: pw.FlexColumnWidth(2), // Presentase
|
||||
},
|
||||
children: paymentMethodAnalyticData?.data
|
||||
.map(
|
||||
(payment) => _buildPaymentMethodDataRow(
|
||||
name: payment.paymentMethodName,
|
||||
tipe: payment.paymentMethodType
|
||||
.toTitleCase(),
|
||||
jumlahOrder:
|
||||
payment.orderCount.toString(),
|
||||
totalAmount: payment.totalAmount
|
||||
.toString()
|
||||
.currencyFormatRpV2,
|
||||
presentase:
|
||||
'${payment.percentage.round()}%',
|
||||
isEven: paymentMethodAnalyticData.data
|
||||
.indexOf(payment) %
|
||||
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(1), // Qty
|
||||
2: pw.FlexColumnWidth(2.5), // Pendapatan
|
||||
3: pw.FlexColumnWidth(2), // HPP
|
||||
4: pw.FlexColumnWidth(2), // Laba Kotor
|
||||
5: pw.FlexColumnWidth(2), // Margin (%)
|
||||
},
|
||||
children: [
|
||||
pw.TableRow(
|
||||
children: [
|
||||
_buildTotalCell('TOTAL'),
|
||||
_buildTotalCell(''),
|
||||
_buildTotalCell((paymentMethodAnalyticData
|
||||
?.summary.totalOrders ??
|
||||
0)
|
||||
.toString()),
|
||||
_buildTotalCell((paymentMethodAnalyticData
|
||||
?.summary.totalAmount ??
|
||||
0)
|
||||
.toString()
|
||||
.currencyFormatRpV2),
|
||||
_buildTotalCell(''),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Summary Category
|
||||
pw.Container(
|
||||
padding: pw.EdgeInsets.all(20),
|
||||
child: pw.Column(
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionWidget('3. Ringkasan Kategori'),
|
||||
pw.SizedBox(height: 30),
|
||||
pw.Column(
|
||||
children: [
|
||||
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), // Nama
|
||||
1: pw.FlexColumnWidth(2), // Total Product
|
||||
2: pw.FlexColumnWidth(1), // qty
|
||||
3: pw.FlexColumnWidth(2), // Jumlah Order
|
||||
4: pw.FlexColumnWidth(2.5), // Presentase
|
||||
},
|
||||
children: [
|
||||
pw.TableRow(
|
||||
children: [
|
||||
_buildHeaderCell('Nama'),
|
||||
_buildHeaderCell('Total Produk'),
|
||||
_buildHeaderCell('Qty'),
|
||||
_buildHeaderCell('Jumlah Order'),
|
||||
_buildHeaderCell('Pendapatan'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
pw.Container(
|
||||
decoration: pw.BoxDecoration(
|
||||
color: PdfColors.white,
|
||||
),
|
||||
child: pw.Table(
|
||||
columnWidths: {
|
||||
0: pw.FlexColumnWidth(2.5), // Nama
|
||||
1: pw.FlexColumnWidth(2), // Total Product
|
||||
2: pw.FlexColumnWidth(1), // qty
|
||||
3: pw.FlexColumnWidth(2), // Jumlah Order
|
||||
4: pw.FlexColumnWidth(2.5), // Presentase
|
||||
},
|
||||
children: categoryAnalyticData?.data
|
||||
.map((category) => _buildCategoryDataRow(
|
||||
name: category.categoryName,
|
||||
totalProduct:
|
||||
category.productCount.toString(),
|
||||
qty: category.totalQuantity.toString(),
|
||||
jumlahOrder:
|
||||
category.orderCount.toString(),
|
||||
pendapatan: category.totalRevenue
|
||||
.toString()
|
||||
.currencyFormatRpV2,
|
||||
isEven: categoryAnalyticData.data
|
||||
.indexOf(category) %
|
||||
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), // Nama
|
||||
1: pw.FlexColumnWidth(2), // Total Product
|
||||
2: pw.FlexColumnWidth(1), // qty
|
||||
3: pw.FlexColumnWidth(2), // Jumlah Order
|
||||
4: pw.FlexColumnWidth(2.5), // Presentase
|
||||
},
|
||||
children: [
|
||||
pw.TableRow(
|
||||
children: [
|
||||
_buildTotalCell('TOTAL'),
|
||||
_buildTotalCell(
|
||||
categorySummary['productCount'].toString()),
|
||||
_buildTotalCell(categorySummary['totalQuantity']
|
||||
.toString()),
|
||||
_buildTotalCell(
|
||||
categorySummary['orderCount'].toString()),
|
||||
_buildTotalCell(categorySummary['totalRevenue']
|
||||
.toString()
|
||||
.currencyFormatRpV2),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Summary Item
|
||||
pw.Container(
|
||||
padding: pw.EdgeInsets.all(20),
|
||||
child: pw.Column(
|
||||
children: [
|
||||
pw.Column(
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionWidget('4. Ringkasan 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), // qty
|
||||
3: pw.FlexColumnWidth(2), // Order
|
||||
4: pw.FlexColumnWidth(2), // Pendapatan
|
||||
5: pw.FlexColumnWidth(2), // Average
|
||||
},
|
||||
children: [
|
||||
pw.TableRow(
|
||||
children: [
|
||||
_buildHeaderCell('Produk'),
|
||||
_buildHeaderCell('Kategori'),
|
||||
_buildHeaderCell('Qty'),
|
||||
_buildHeaderCell('Order'),
|
||||
_buildHeaderCell('Pendapatan'),
|
||||
_buildHeaderCell('Rata Rata'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
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), // qty
|
||||
3: pw.FlexColumnWidth(2), // Order
|
||||
4: pw.FlexColumnWidth(2), // Pendapatan
|
||||
5: pw.FlexColumnWidth(2), // Average
|
||||
},
|
||||
children: productAnalyticData?.data
|
||||
.map((item) => _buildItemDataRow(
|
||||
product: item.productName,
|
||||
category: item.categoryName,
|
||||
qty: item.quantitySold.toString(),
|
||||
order: item.orderCount.toString(),
|
||||
pendapatan: item.revenue
|
||||
.toString()
|
||||
.currencyFormatRpV2,
|
||||
average: item.averagePrice
|
||||
.round()
|
||||
.toString()
|
||||
.currencyFormatRpV2,
|
||||
isEven: productAnalyticData.data
|
||||
.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), // qty
|
||||
3: pw.FlexColumnWidth(2), // Order
|
||||
4: pw.FlexColumnWidth(2), // Pendapatan
|
||||
5: pw.FlexColumnWidth(2), // Average
|
||||
},
|
||||
children: [
|
||||
pw.TableRow(
|
||||
children: [
|
||||
_buildTotalCell('TOTAL'),
|
||||
_buildTotalCell(''),
|
||||
_buildTotalCell(
|
||||
productItemSummary['totalQuantitySold']
|
||||
.toString()),
|
||||
_buildTotalCell(productItemSummary['orderCount']
|
||||
.toString()),
|
||||
_buildTotalCell(
|
||||
productItemSummary['totalRevenue']
|
||||
.toString()
|
||||
.currencyFormatRpV2),
|
||||
_buildTotalCell(''),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return HelperPdfService.saveDocument(
|
||||
name: 'Laporan Transaksi | $searchDateFormatted.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 _buildPerProductDataRow({
|
||||
required String product,
|
||||
required String qty,
|
||||
required String pendapatan,
|
||||
required String hpp,
|
||||
required String labaKotor,
|
||||
required String margin,
|
||||
required bool isEven,
|
||||
}) {
|
||||
return pw.TableRow(
|
||||
decoration: pw.BoxDecoration(
|
||||
color: isEven ? PdfColors.grey50 : PdfColors.white,
|
||||
),
|
||||
children: [
|
||||
_buildDataCell(product, alignment: pw.Alignment.centerLeft),
|
||||
_buildDataCell(qty),
|
||||
_buildDataCell(pendapatan),
|
||||
_buildDataCell(hpp, textColor: PdfColors.red600),
|
||||
_buildDataCell(labaKotor, textColor: PdfColors.green600),
|
||||
_buildDataCell(margin),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static pw.TableRow _buildPaymentMethodDataRow({
|
||||
required String name,
|
||||
required String tipe,
|
||||
required String jumlahOrder,
|
||||
required String totalAmount,
|
||||
required String presentase,
|
||||
required bool isEven,
|
||||
}) {
|
||||
return pw.TableRow(
|
||||
decoration: pw.BoxDecoration(
|
||||
color: isEven ? PdfColors.grey50 : PdfColors.white,
|
||||
),
|
||||
children: [
|
||||
_buildDataCell(name, alignment: pw.Alignment.centerLeft),
|
||||
_buildDataCell(tipe),
|
||||
_buildDataCell(jumlahOrder),
|
||||
_buildDataCell(totalAmount),
|
||||
_buildDataCell(presentase),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static pw.TableRow _buildCategoryDataRow({
|
||||
required String name,
|
||||
required String totalProduct,
|
||||
required String qty,
|
||||
required String jumlahOrder,
|
||||
required String pendapatan,
|
||||
required bool isEven,
|
||||
}) {
|
||||
return pw.TableRow(
|
||||
decoration: pw.BoxDecoration(
|
||||
color: isEven ? PdfColors.grey50 : PdfColors.white,
|
||||
),
|
||||
children: [
|
||||
_buildDataCell(name, alignment: pw.Alignment.centerLeft),
|
||||
_buildDataCell(totalProduct),
|
||||
_buildDataCell(qty),
|
||||
_buildDataCell(jumlahOrder),
|
||||
_buildDataCell(pendapatan),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static pw.TableRow _buildItemDataRow({
|
||||
required String product,
|
||||
required String category,
|
||||
required String qty,
|
||||
required String order,
|
||||
required String pendapatan,
|
||||
required String average,
|
||||
required bool isEven,
|
||||
}) {
|
||||
return pw.TableRow(
|
||||
decoration: pw.BoxDecoration(
|
||||
color: isEven ? PdfColors.grey50 : PdfColors.white,
|
||||
),
|
||||
children: [
|
||||
_buildDataCell(product, alignment: pw.Alignment.centerLeft),
|
||||
_buildDataCell(category, alignment: pw.Alignment.centerLeft),
|
||||
_buildDataCell(qty),
|
||||
_buildDataCell(order),
|
||||
_buildDataCell(pendapatan),
|
||||
_buildDataCell(average),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ import 'package:dio/dio.dart';
|
||||
import 'package:enaklo_pos/core/constants/variables.dart';
|
||||
import 'package:enaklo_pos/core/network/dio_client.dart';
|
||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||
import 'package:enaklo_pos/data/models/response/category_analytic_response_model.dart';
|
||||
import 'package:enaklo_pos/data/models/response/dashboard_analytic_response_model.dart';
|
||||
import 'package:enaklo_pos/data/models/response/payment_method_analytic_response_model.dart';
|
||||
import 'package:enaklo_pos/data/models/response/product_analytic_response_model.dart';
|
||||
@ -184,4 +185,38 @@ class AnalyticRemoteDatasource {
|
||||
return left('Unexpected error occurred');
|
||||
}
|
||||
}
|
||||
|
||||
Future<Either<String, CategoryAnalyticResponseModel>> getCategory({
|
||||
required DateTime dateFrom,
|
||||
required DateTime dateTo,
|
||||
}) async {
|
||||
final authData = await AuthLocalDataSource().getAuthData();
|
||||
final headers = {
|
||||
'Authorization': 'Bearer ${authData.token}',
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
|
||||
try {
|
||||
final response = await dio.get(
|
||||
'${Variables.baseUrl}/api/v1/analytics/categories',
|
||||
queryParameters: {
|
||||
'date_from': DateFormat('dd-MM-yyyy').format(dateFrom),
|
||||
'date_to': DateFormat('dd-MM-yyyy').format(dateTo),
|
||||
},
|
||||
options: Options(headers: headers),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return right(CategoryAnalyticResponseModel.fromMap(response.data));
|
||||
} else {
|
||||
return left('Terjadi Kesalahan, Coba lagi nanti.');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
log('Dio error: ${e.message}');
|
||||
return left(e.response?.data.toString() ?? e.message ?? 'Unknown error');
|
||||
} catch (e) {
|
||||
log('Unexpected error: $e');
|
||||
return left('Unexpected error occurred');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
129
lib/data/models/response/category_analytic_response_model.dart
Normal file
129
lib/data/models/response/category_analytic_response_model.dart
Normal file
@ -0,0 +1,129 @@
|
||||
class CategoryAnalyticResponseModel {
|
||||
final bool success;
|
||||
final CategoryAnalyticData? data;
|
||||
final dynamic errors;
|
||||
|
||||
CategoryAnalyticResponseModel({
|
||||
required this.success,
|
||||
required this.data,
|
||||
this.errors,
|
||||
});
|
||||
|
||||
// Dari JSON String ke Model
|
||||
factory CategoryAnalyticResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
return CategoryAnalyticResponseModel(
|
||||
success: json['success'],
|
||||
data: json['data'] == null
|
||||
? null
|
||||
: CategoryAnalyticData.fromMap(json['data']),
|
||||
errors: json['errors'],
|
||||
);
|
||||
}
|
||||
|
||||
// Dari Model ke JSON String
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'success': success,
|
||||
'data': data?.toMap(),
|
||||
'errors': errors,
|
||||
};
|
||||
}
|
||||
|
||||
// Dari Map ke Model
|
||||
factory CategoryAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
|
||||
return CategoryAnalyticResponseModel(
|
||||
success: map['success'],
|
||||
data: CategoryAnalyticData.fromMap(map['data']),
|
||||
errors: map['errors'],
|
||||
);
|
||||
}
|
||||
|
||||
// Dari Model ke Map
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'success': success,
|
||||
'data': data?.toMap(),
|
||||
'errors': errors,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class CategoryAnalyticData {
|
||||
final String organizationId;
|
||||
final String outletId;
|
||||
final DateTime dateFrom;
|
||||
final DateTime dateTo;
|
||||
final List<CategoryAnalyticItem> data;
|
||||
|
||||
CategoryAnalyticData({
|
||||
required this.organizationId,
|
||||
required this.outletId,
|
||||
required this.dateFrom,
|
||||
required this.dateTo,
|
||||
required this.data,
|
||||
});
|
||||
|
||||
factory CategoryAnalyticData.fromMap(Map<String, dynamic> map) {
|
||||
return CategoryAnalyticData(
|
||||
organizationId: map['organization_id'],
|
||||
outletId: map['outlet_id'],
|
||||
dateFrom: DateTime.parse(map['date_from']),
|
||||
dateTo: DateTime.parse(map['date_to']),
|
||||
data: map['data'] == null
|
||||
? []
|
||||
: List<CategoryAnalyticItem>.from(
|
||||
map['data']?.map((x) => CategoryAnalyticItem.fromMap(x)) ?? [],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'organization_id': organizationId,
|
||||
'outlet_id': outletId,
|
||||
'date_from': dateFrom.toIso8601String(),
|
||||
'date_to': dateTo.toIso8601String(),
|
||||
'data': data.map((x) => x.toMap()).toList(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class CategoryAnalyticItem {
|
||||
final String categoryId;
|
||||
final String categoryName;
|
||||
final int totalRevenue;
|
||||
final int totalQuantity;
|
||||
final int productCount;
|
||||
final int orderCount;
|
||||
|
||||
CategoryAnalyticItem({
|
||||
required this.categoryId,
|
||||
required this.categoryName,
|
||||
required this.totalRevenue,
|
||||
required this.totalQuantity,
|
||||
required this.productCount,
|
||||
required this.orderCount,
|
||||
});
|
||||
|
||||
factory CategoryAnalyticItem.fromMap(Map<String, dynamic> map) {
|
||||
return CategoryAnalyticItem(
|
||||
categoryId: map['category_id'],
|
||||
categoryName: map['category_name'],
|
||||
totalRevenue: map['total_revenue'] ?? 0,
|
||||
totalQuantity: map['total_quantity'] ?? 0,
|
||||
productCount: map['product_count'] ?? 0,
|
||||
orderCount: map['order_count'] ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'category_id': categoryId,
|
||||
'category_name': categoryName,
|
||||
'total_revenue': totalRevenue,
|
||||
'total_quantity': totalQuantity,
|
||||
'product_count': productCount,
|
||||
'order_count': orderCount,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
class PaymentMethodAnalyticResponseModel {
|
||||
final bool success;
|
||||
final PaymentMethodAnalyticData data;
|
||||
final PaymentMethodAnalyticData? data;
|
||||
final dynamic errors;
|
||||
|
||||
PaymentMethodAnalyticResponseModel({
|
||||
@ -18,7 +18,9 @@ class PaymentMethodAnalyticResponseModel {
|
||||
factory PaymentMethodAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
|
||||
return PaymentMethodAnalyticResponseModel(
|
||||
success: map['success'],
|
||||
data: PaymentMethodAnalyticData.fromMap(map['data']),
|
||||
data: map['data'] == null
|
||||
? null
|
||||
: PaymentMethodAnalyticData.fromMap(map['data']),
|
||||
errors: map['errors'],
|
||||
);
|
||||
}
|
||||
@ -26,7 +28,7 @@ class PaymentMethodAnalyticResponseModel {
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'success': success,
|
||||
'data': data.toMap(),
|
||||
'data': data?.toMap(),
|
||||
'errors': errors,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
class ProductAnalyticResponseModel {
|
||||
final bool success;
|
||||
final ProductAnalyticData data;
|
||||
final ProductAnalyticData? data;
|
||||
final dynamic errors;
|
||||
|
||||
ProductAnalyticResponseModel({
|
||||
@ -17,7 +17,8 @@ class ProductAnalyticResponseModel {
|
||||
factory ProductAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
|
||||
return ProductAnalyticResponseModel(
|
||||
success: map['success'] ?? false,
|
||||
data: ProductAnalyticData.fromMap(map['data']),
|
||||
data:
|
||||
map['data'] == null ? null : ProductAnalyticData.fromMap(map['data']),
|
||||
errors: map['errors'],
|
||||
);
|
||||
}
|
||||
@ -25,7 +26,7 @@ class ProductAnalyticResponseModel {
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'success': success,
|
||||
'data': data.toMap(),
|
||||
'data': data?.toMap(),
|
||||
'errors': errors,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
class ProfitLossResponseModel {
|
||||
final bool success;
|
||||
final ProfitLossData data;
|
||||
final ProfitLossData? data;
|
||||
final dynamic errors;
|
||||
|
||||
ProfitLossResponseModel({
|
||||
@ -13,7 +13,7 @@ class ProfitLossResponseModel {
|
||||
factory ProfitLossResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
return ProfitLossResponseModel(
|
||||
success: json['success'],
|
||||
data: ProfitLossData.fromMap(json['data']),
|
||||
data: json['data'] == null ? null : ProfitLossData.fromMap(json['data']),
|
||||
errors: json['errors'],
|
||||
);
|
||||
}
|
||||
@ -22,7 +22,7 @@ class ProfitLossResponseModel {
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'success': success,
|
||||
'data': data.toMap(),
|
||||
'data': data?.toMap(),
|
||||
'errors': errors,
|
||||
};
|
||||
}
|
||||
@ -40,7 +40,7 @@ class ProfitLossResponseModel {
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'success': success,
|
||||
'data': data.toMap(),
|
||||
'data': data?.toMap(),
|
||||
'errors': errors,
|
||||
};
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_
|
||||
import 'package:enaklo_pos/presentation/home/bloc/user_update_outlet/user_update_outlet_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/refund/bloc/refund_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/report/blocs/profit_loss/profit_loss_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/report/blocs/report/report_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/sales/blocs/order_loader/order_loader_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/sales/blocs/payment_form/payment_form_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_bloc.dart';
|
||||
@ -288,6 +289,9 @@ class _MyAppState extends State<MyApp> {
|
||||
BlocProvider(
|
||||
create: (context) => TransferTableBloc(TableRemoteDataSource()),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => ReportBloc(AnalyticRemoteDatasource()),
|
||||
),
|
||||
],
|
||||
child: MaterialApp(
|
||||
navigatorKey: AuthInterceptor.navigatorKey,
|
||||
|
||||
@ -22,7 +22,7 @@ class PaymentMethodReportBloc
|
||||
(l) => emit(_Error(l)),
|
||||
(r) => emit(
|
||||
_Loaded(
|
||||
r.data,
|
||||
r.data!,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@ -16,7 +16,7 @@ class ProfitLossBloc extends Bloc<ProfitLossEvent, ProfitLossState> {
|
||||
dateFrom: event.startDate,
|
||||
dateTo: event.endDate,
|
||||
);
|
||||
result.fold((l) => emit(_Error(l)), (r) => emit(_Success(r.data)));
|
||||
result.fold((l) => emit(_Error(l)), (r) => emit(_Success(r.data!)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
63
lib/presentation/report/blocs/report/report_bloc.dart
Normal file
63
lib/presentation/report/blocs/report/report_bloc.dart
Normal file
@ -0,0 +1,63 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:enaklo_pos/data/datasources/analytic_remote_datasource.dart';
|
||||
import 'package:enaklo_pos/data/datasources/outlet_local_datasource.dart';
|
||||
import 'package:enaklo_pos/data/models/response/category_analytic_response_model.dart';
|
||||
import 'package:enaklo_pos/data/models/response/payment_method_analytic_response_model.dart';
|
||||
import 'package:enaklo_pos/data/models/response/product_analytic_response_model.dart';
|
||||
import 'package:enaklo_pos/data/models/response/profit_loss_response_model.dart';
|
||||
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'report_event.dart';
|
||||
part 'report_state.dart';
|
||||
part 'report_bloc.freezed.dart';
|
||||
|
||||
class ReportBloc extends Bloc<ReportEvent, ReportState> {
|
||||
final AnalyticRemoteDatasource _datasource;
|
||||
ReportBloc(this._datasource) : super(ReportState.initial()) {
|
||||
on<_Get>((event, emit) async {
|
||||
emit(_Loading());
|
||||
|
||||
final outlet = await OutletLocalDatasource().get();
|
||||
|
||||
final category = await _datasource.getCategory(
|
||||
dateFrom: event.startDate, dateTo: event.endDate);
|
||||
|
||||
final product = await _datasource.getProduct(
|
||||
dateFrom: event.startDate, dateTo: event.endDate);
|
||||
|
||||
final paymentMethod = await _datasource.getPaymentMethod(
|
||||
dateFrom: event.startDate, dateTo: event.endDate);
|
||||
|
||||
final profitLoss = await _datasource.getProfitLoss(
|
||||
dateFrom: event.startDate, dateTo: event.endDate);
|
||||
|
||||
if (category.isLeft() ||
|
||||
product.isLeft() ||
|
||||
paymentMethod.isLeft() ||
|
||||
profitLoss.isLeft()) {
|
||||
emit(_Error());
|
||||
}
|
||||
|
||||
emit(_Loaded(
|
||||
outlet,
|
||||
category
|
||||
.getOrElse(
|
||||
() => CategoryAnalyticResponseModel(success: false, data: null))
|
||||
.data!,
|
||||
profitLoss
|
||||
.getOrElse(
|
||||
() => ProfitLossResponseModel(success: false, data: null))
|
||||
.data,
|
||||
paymentMethod
|
||||
.getOrElse(() =>
|
||||
PaymentMethodAnalyticResponseModel(success: false, data: null))
|
||||
.data,
|
||||
product
|
||||
.getOrElse(
|
||||
() => ProductAnalyticResponseModel(success: false, data: null))
|
||||
.data,
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
975
lib/presentation/report/blocs/report/report_bloc.freezed.dart
Normal file
975
lib/presentation/report/blocs/report/report_bloc.freezed.dart
Normal file
@ -0,0 +1,975 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'report_bloc.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ReportEvent {
|
||||
DateTime get startDate => throw _privateConstructorUsedError;
|
||||
DateTime get endDate => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(DateTime startDate, DateTime endDate) get,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(DateTime startDate, DateTime endDate)? get,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(DateTime startDate, DateTime endDate)? get,
|
||||
required TResult orElse(),
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Get value) get,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Get value)? get,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Get value)? get,
|
||||
required TResult orElse(),
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ReportEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ReportEventCopyWith<ReportEvent> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ReportEventCopyWith<$Res> {
|
||||
factory $ReportEventCopyWith(
|
||||
ReportEvent value, $Res Function(ReportEvent) then) =
|
||||
_$ReportEventCopyWithImpl<$Res, ReportEvent>;
|
||||
@useResult
|
||||
$Res call({DateTime startDate, DateTime endDate});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ReportEventCopyWithImpl<$Res, $Val extends ReportEvent>
|
||||
implements $ReportEventCopyWith<$Res> {
|
||||
_$ReportEventCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ReportEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? startDate = null,
|
||||
Object? endDate = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
startDate: null == startDate
|
||||
? _value.startDate
|
||||
: startDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,
|
||||
endDate: null == endDate
|
||||
? _value.endDate
|
||||
: endDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$GetImplCopyWith<$Res> implements $ReportEventCopyWith<$Res> {
|
||||
factory _$$GetImplCopyWith(_$GetImpl value, $Res Function(_$GetImpl) then) =
|
||||
__$$GetImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({DateTime startDate, DateTime endDate});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$GetImplCopyWithImpl<$Res>
|
||||
extends _$ReportEventCopyWithImpl<$Res, _$GetImpl>
|
||||
implements _$$GetImplCopyWith<$Res> {
|
||||
__$$GetImplCopyWithImpl(_$GetImpl _value, $Res Function(_$GetImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ReportEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? startDate = null,
|
||||
Object? endDate = null,
|
||||
}) {
|
||||
return _then(_$GetImpl(
|
||||
startDate: null == startDate
|
||||
? _value.startDate
|
||||
: startDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,
|
||||
endDate: null == endDate
|
||||
? _value.endDate
|
||||
: endDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$GetImpl implements _Get {
|
||||
const _$GetImpl({required this.startDate, required this.endDate});
|
||||
|
||||
@override
|
||||
final DateTime startDate;
|
||||
@override
|
||||
final DateTime endDate;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ReportEvent.get(startDate: $startDate, endDate: $endDate)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$GetImpl &&
|
||||
(identical(other.startDate, startDate) ||
|
||||
other.startDate == startDate) &&
|
||||
(identical(other.endDate, endDate) || other.endDate == endDate));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, startDate, endDate);
|
||||
|
||||
/// Create a copy of ReportEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$GetImplCopyWith<_$GetImpl> get copyWith =>
|
||||
__$$GetImplCopyWithImpl<_$GetImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(DateTime startDate, DateTime endDate) get,
|
||||
}) {
|
||||
return get(startDate, endDate);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(DateTime startDate, DateTime endDate)? get,
|
||||
}) {
|
||||
return get?.call(startDate, endDate);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(DateTime startDate, DateTime endDate)? get,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (get != null) {
|
||||
return get(startDate, endDate);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Get value) get,
|
||||
}) {
|
||||
return get(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Get value)? get,
|
||||
}) {
|
||||
return get?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Get value)? get,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (get != null) {
|
||||
return get(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Get implements ReportEvent {
|
||||
const factory _Get(
|
||||
{required final DateTime startDate,
|
||||
required final DateTime endDate}) = _$GetImpl;
|
||||
|
||||
@override
|
||||
DateTime get startDate;
|
||||
@override
|
||||
DateTime get endDate;
|
||||
|
||||
/// Create a copy of ReportEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$GetImplCopyWith<_$GetImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ReportState {
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() initial,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)
|
||||
loaded,
|
||||
required TResult Function() error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? initial,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)?
|
||||
loaded,
|
||||
TResult? Function()? error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? initial,
|
||||
TResult Function()? loading,
|
||||
TResult Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)?
|
||||
loaded,
|
||||
TResult Function()? error,
|
||||
required TResult orElse(),
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Initial value) initial,
|
||||
required TResult Function(_Loading value) loading,
|
||||
required TResult Function(_Loaded value) loaded,
|
||||
required TResult Function(_Error value) error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Initial value)? initial,
|
||||
TResult? Function(_Loading value)? loading,
|
||||
TResult? Function(_Loaded value)? loaded,
|
||||
TResult? Function(_Error value)? error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Initial value)? initial,
|
||||
TResult Function(_Loading value)? loading,
|
||||
TResult Function(_Loaded value)? loaded,
|
||||
TResult Function(_Error value)? error,
|
||||
required TResult orElse(),
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ReportStateCopyWith<$Res> {
|
||||
factory $ReportStateCopyWith(
|
||||
ReportState value, $Res Function(ReportState) then) =
|
||||
_$ReportStateCopyWithImpl<$Res, ReportState>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ReportStateCopyWithImpl<$Res, $Val extends ReportState>
|
||||
implements $ReportStateCopyWith<$Res> {
|
||||
_$ReportStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ReportState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$InitialImplCopyWith<$Res> {
|
||||
factory _$$InitialImplCopyWith(
|
||||
_$InitialImpl value, $Res Function(_$InitialImpl) then) =
|
||||
__$$InitialImplCopyWithImpl<$Res>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$InitialImplCopyWithImpl<$Res>
|
||||
extends _$ReportStateCopyWithImpl<$Res, _$InitialImpl>
|
||||
implements _$$InitialImplCopyWith<$Res> {
|
||||
__$$InitialImplCopyWithImpl(
|
||||
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ReportState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$InitialImpl implements _Initial {
|
||||
const _$InitialImpl();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ReportState.initial()';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType && other is _$InitialImpl);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => runtimeType.hashCode;
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() initial,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)
|
||||
loaded,
|
||||
required TResult Function() error,
|
||||
}) {
|
||||
return initial();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? initial,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)?
|
||||
loaded,
|
||||
TResult? Function()? error,
|
||||
}) {
|
||||
return initial?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? initial,
|
||||
TResult Function()? loading,
|
||||
TResult Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)?
|
||||
loaded,
|
||||
TResult Function()? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (initial != null) {
|
||||
return initial();
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Initial value) initial,
|
||||
required TResult Function(_Loading value) loading,
|
||||
required TResult Function(_Loaded value) loaded,
|
||||
required TResult Function(_Error value) error,
|
||||
}) {
|
||||
return initial(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Initial value)? initial,
|
||||
TResult? Function(_Loading value)? loading,
|
||||
TResult? Function(_Loaded value)? loaded,
|
||||
TResult? Function(_Error value)? error,
|
||||
}) {
|
||||
return initial?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Initial value)? initial,
|
||||
TResult Function(_Loading value)? loading,
|
||||
TResult Function(_Loaded value)? loaded,
|
||||
TResult Function(_Error value)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (initial != null) {
|
||||
return initial(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Initial implements ReportState {
|
||||
const factory _Initial() = _$InitialImpl;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$LoadingImplCopyWith<$Res> {
|
||||
factory _$$LoadingImplCopyWith(
|
||||
_$LoadingImpl value, $Res Function(_$LoadingImpl) then) =
|
||||
__$$LoadingImplCopyWithImpl<$Res>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$LoadingImplCopyWithImpl<$Res>
|
||||
extends _$ReportStateCopyWithImpl<$Res, _$LoadingImpl>
|
||||
implements _$$LoadingImplCopyWith<$Res> {
|
||||
__$$LoadingImplCopyWithImpl(
|
||||
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ReportState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$LoadingImpl implements _Loading {
|
||||
const _$LoadingImpl();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ReportState.loading()';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType && other is _$LoadingImpl);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => runtimeType.hashCode;
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() initial,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)
|
||||
loaded,
|
||||
required TResult Function() error,
|
||||
}) {
|
||||
return loading();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? initial,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)?
|
||||
loaded,
|
||||
TResult? Function()? error,
|
||||
}) {
|
||||
return loading?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? initial,
|
||||
TResult Function()? loading,
|
||||
TResult Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)?
|
||||
loaded,
|
||||
TResult Function()? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (loading != null) {
|
||||
return loading();
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Initial value) initial,
|
||||
required TResult Function(_Loading value) loading,
|
||||
required TResult Function(_Loaded value) loaded,
|
||||
required TResult Function(_Error value) error,
|
||||
}) {
|
||||
return loading(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Initial value)? initial,
|
||||
TResult? Function(_Loading value)? loading,
|
||||
TResult? Function(_Loaded value)? loaded,
|
||||
TResult? Function(_Error value)? error,
|
||||
}) {
|
||||
return loading?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Initial value)? initial,
|
||||
TResult Function(_Loading value)? loading,
|
||||
TResult Function(_Loaded value)? loaded,
|
||||
TResult Function(_Error value)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (loading != null) {
|
||||
return loading(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Loading implements ReportState {
|
||||
const factory _Loading() = _$LoadingImpl;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$LoadedImplCopyWith<$Res> {
|
||||
factory _$$LoadedImplCopyWith(
|
||||
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
|
||||
__$$LoadedImplCopyWithImpl<$Res>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$LoadedImplCopyWithImpl<$Res>
|
||||
extends _$ReportStateCopyWithImpl<$Res, _$LoadedImpl>
|
||||
implements _$$LoadedImplCopyWith<$Res> {
|
||||
__$$LoadedImplCopyWithImpl(
|
||||
_$LoadedImpl _value, $Res Function(_$LoadedImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ReportState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? outlet = null,
|
||||
Object? categoryAnalyticData = freezed,
|
||||
Object? profitLossData = freezed,
|
||||
Object? paymentMethodAnalyticData = freezed,
|
||||
Object? productAnalyticData = freezed,
|
||||
}) {
|
||||
return _then(_$LoadedImpl(
|
||||
null == outlet
|
||||
? _value.outlet
|
||||
: outlet // ignore: cast_nullable_to_non_nullable
|
||||
as Outlet,
|
||||
freezed == categoryAnalyticData
|
||||
? _value.categoryAnalyticData
|
||||
: categoryAnalyticData // ignore: cast_nullable_to_non_nullable
|
||||
as CategoryAnalyticData?,
|
||||
freezed == profitLossData
|
||||
? _value.profitLossData
|
||||
: profitLossData // ignore: cast_nullable_to_non_nullable
|
||||
as ProfitLossData?,
|
||||
freezed == paymentMethodAnalyticData
|
||||
? _value.paymentMethodAnalyticData
|
||||
: paymentMethodAnalyticData // ignore: cast_nullable_to_non_nullable
|
||||
as PaymentMethodAnalyticData?,
|
||||
freezed == productAnalyticData
|
||||
? _value.productAnalyticData
|
||||
: productAnalyticData // ignore: cast_nullable_to_non_nullable
|
||||
as ProductAnalyticData?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$LoadedImpl implements _Loaded {
|
||||
const _$LoadedImpl(
|
||||
this.outlet,
|
||||
this.categoryAnalyticData,
|
||||
this.profitLossData,
|
||||
this.paymentMethodAnalyticData,
|
||||
this.productAnalyticData);
|
||||
|
||||
@override
|
||||
final Outlet outlet;
|
||||
@override
|
||||
final CategoryAnalyticData? categoryAnalyticData;
|
||||
@override
|
||||
final ProfitLossData? profitLossData;
|
||||
@override
|
||||
final PaymentMethodAnalyticData? paymentMethodAnalyticData;
|
||||
@override
|
||||
final ProductAnalyticData? productAnalyticData;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ReportState.loaded(outlet: $outlet, categoryAnalyticData: $categoryAnalyticData, profitLossData: $profitLossData, paymentMethodAnalyticData: $paymentMethodAnalyticData, productAnalyticData: $productAnalyticData)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$LoadedImpl &&
|
||||
(identical(other.outlet, outlet) || other.outlet == outlet) &&
|
||||
(identical(other.categoryAnalyticData, categoryAnalyticData) ||
|
||||
other.categoryAnalyticData == categoryAnalyticData) &&
|
||||
(identical(other.profitLossData, profitLossData) ||
|
||||
other.profitLossData == profitLossData) &&
|
||||
(identical(other.paymentMethodAnalyticData,
|
||||
paymentMethodAnalyticData) ||
|
||||
other.paymentMethodAnalyticData == paymentMethodAnalyticData) &&
|
||||
(identical(other.productAnalyticData, productAnalyticData) ||
|
||||
other.productAnalyticData == productAnalyticData));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, outlet, categoryAnalyticData,
|
||||
profitLossData, paymentMethodAnalyticData, productAnalyticData);
|
||||
|
||||
/// Create a copy of ReportState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$LoadedImplCopyWith<_$LoadedImpl> get copyWith =>
|
||||
__$$LoadedImplCopyWithImpl<_$LoadedImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() initial,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)
|
||||
loaded,
|
||||
required TResult Function() error,
|
||||
}) {
|
||||
return loaded(outlet, categoryAnalyticData, profitLossData,
|
||||
paymentMethodAnalyticData, productAnalyticData);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? initial,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)?
|
||||
loaded,
|
||||
TResult? Function()? error,
|
||||
}) {
|
||||
return loaded?.call(outlet, categoryAnalyticData, profitLossData,
|
||||
paymentMethodAnalyticData, productAnalyticData);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? initial,
|
||||
TResult Function()? loading,
|
||||
TResult Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)?
|
||||
loaded,
|
||||
TResult Function()? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (loaded != null) {
|
||||
return loaded(outlet, categoryAnalyticData, profitLossData,
|
||||
paymentMethodAnalyticData, productAnalyticData);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Initial value) initial,
|
||||
required TResult Function(_Loading value) loading,
|
||||
required TResult Function(_Loaded value) loaded,
|
||||
required TResult Function(_Error value) error,
|
||||
}) {
|
||||
return loaded(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Initial value)? initial,
|
||||
TResult? Function(_Loading value)? loading,
|
||||
TResult? Function(_Loaded value)? loaded,
|
||||
TResult? Function(_Error value)? error,
|
||||
}) {
|
||||
return loaded?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Initial value)? initial,
|
||||
TResult Function(_Loading value)? loading,
|
||||
TResult Function(_Loaded value)? loaded,
|
||||
TResult Function(_Error value)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (loaded != null) {
|
||||
return loaded(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Loaded implements ReportState {
|
||||
const factory _Loaded(
|
||||
final Outlet outlet,
|
||||
final CategoryAnalyticData? categoryAnalyticData,
|
||||
final ProfitLossData? profitLossData,
|
||||
final PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
final ProductAnalyticData? productAnalyticData) = _$LoadedImpl;
|
||||
|
||||
Outlet get outlet;
|
||||
CategoryAnalyticData? get categoryAnalyticData;
|
||||
ProfitLossData? get profitLossData;
|
||||
PaymentMethodAnalyticData? get paymentMethodAnalyticData;
|
||||
ProductAnalyticData? get productAnalyticData;
|
||||
|
||||
/// Create a copy of ReportState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$LoadedImplCopyWith<_$LoadedImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ErrorImplCopyWith<$Res> {
|
||||
factory _$$ErrorImplCopyWith(
|
||||
_$ErrorImpl value, $Res Function(_$ErrorImpl) then) =
|
||||
__$$ErrorImplCopyWithImpl<$Res>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ErrorImplCopyWithImpl<$Res>
|
||||
extends _$ReportStateCopyWithImpl<$Res, _$ErrorImpl>
|
||||
implements _$$ErrorImplCopyWith<$Res> {
|
||||
__$$ErrorImplCopyWithImpl(
|
||||
_$ErrorImpl _value, $Res Function(_$ErrorImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ReportState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$ErrorImpl implements _Error {
|
||||
const _$ErrorImpl();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ReportState.error()';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType && other is _$ErrorImpl);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => runtimeType.hashCode;
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() initial,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)
|
||||
loaded,
|
||||
required TResult Function() error,
|
||||
}) {
|
||||
return error();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? initial,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)?
|
||||
loaded,
|
||||
TResult? Function()? error,
|
||||
}) {
|
||||
return error?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? initial,
|
||||
TResult Function()? loading,
|
||||
TResult Function(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData)?
|
||||
loaded,
|
||||
TResult Function()? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (error != null) {
|
||||
return error();
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Initial value) initial,
|
||||
required TResult Function(_Loading value) loading,
|
||||
required TResult Function(_Loaded value) loaded,
|
||||
required TResult Function(_Error value) error,
|
||||
}) {
|
||||
return error(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Initial value)? initial,
|
||||
TResult? Function(_Loading value)? loading,
|
||||
TResult? Function(_Loaded value)? loaded,
|
||||
TResult? Function(_Error value)? error,
|
||||
}) {
|
||||
return error?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Initial value)? initial,
|
||||
TResult Function(_Loading value)? loading,
|
||||
TResult Function(_Loaded value)? loaded,
|
||||
TResult Function(_Error value)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (error != null) {
|
||||
return error(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Error implements ReportState {
|
||||
const factory _Error() = _$ErrorImpl;
|
||||
}
|
||||
9
lib/presentation/report/blocs/report/report_event.dart
Normal file
9
lib/presentation/report/blocs/report/report_event.dart
Normal file
@ -0,0 +1,9 @@
|
||||
part of 'report_bloc.dart';
|
||||
|
||||
@freezed
|
||||
class ReportEvent with _$ReportEvent {
|
||||
const factory ReportEvent.get({
|
||||
required DateTime startDate,
|
||||
required DateTime endDate,
|
||||
}) = _Get;
|
||||
}
|
||||
15
lib/presentation/report/blocs/report/report_state.dart
Normal file
15
lib/presentation/report/blocs/report/report_state.dart
Normal file
@ -0,0 +1,15 @@
|
||||
part of 'report_bloc.dart';
|
||||
|
||||
@freezed
|
||||
class ReportState with _$ReportState {
|
||||
const factory ReportState.initial() = _Initial;
|
||||
const factory ReportState.loading() = _Loading;
|
||||
const factory ReportState.loaded(
|
||||
Outlet outlet,
|
||||
CategoryAnalyticData? categoryAnalyticData,
|
||||
ProfitLossData? profitLossData,
|
||||
PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||
ProductAnalyticData? productAnalyticData,
|
||||
) = _Loaded;
|
||||
const factory ReportState.error() = _Error;
|
||||
}
|
||||
@ -1,7 +1,11 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
||||
import 'package:enaklo_pos/core/utils/helper_pdf_service.dart';
|
||||
import 'package:enaklo_pos/core/utils/permession_handler.dart';
|
||||
import 'package:enaklo_pos/core/utils/transaction_report.dart';
|
||||
import 'package:enaklo_pos/presentation/report/blocs/profit_loss/profit_loss_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/report/blocs/report/report_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/report/widgets/dashboard_analytic_widget.dart';
|
||||
import 'package:enaklo_pos/presentation/report/widgets/profit_loss_widget.dart';
|
||||
import 'package:enaklo_pos/presentation/sales/pages/sales_page.dart';
|
||||
@ -44,6 +48,9 @@ class _ReportPageState extends State<ReportPage> {
|
||||
context.read<SummaryBloc>().add(
|
||||
SummaryEvent.getSummary(fromDate, toDate),
|
||||
);
|
||||
context.read<ReportBloc>().add(
|
||||
ReportEvent.get(startDate: fromDate, endDate: toDate),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -64,12 +71,14 @@ class _ReportPageState extends State<ReportPage> {
|
||||
initialDate: fromDate,
|
||||
onDateSelected: (selectedDate) {
|
||||
fromDate = selectedDate;
|
||||
|
||||
context.read<ReportBloc>().add(
|
||||
ReportEvent.get(startDate: fromDate, endDate: toDate),
|
||||
);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SpaceWidth(24.0),
|
||||
const SpaceWidth(12.0),
|
||||
SizedBox(
|
||||
width: 300,
|
||||
child: CustomDatePicker(
|
||||
@ -77,6 +86,9 @@ class _ReportPageState extends State<ReportPage> {
|
||||
initialDate: toDate,
|
||||
onDateSelected: (selectedDate) {
|
||||
toDate = selectedDate;
|
||||
context.read<ReportBloc>().add(
|
||||
ReportEvent.get(startDate: fromDate, endDate: toDate),
|
||||
);
|
||||
setState(() {});
|
||||
// context.read<TransactionReportBloc>().add(
|
||||
// TransactionReportEvent.getReport(
|
||||
@ -97,6 +109,72 @@ class _ReportPageState extends State<ReportPage> {
|
||||
},
|
||||
),
|
||||
),
|
||||
const SpaceWidth(12.0),
|
||||
BlocBuilder<ReportBloc, ReportState>(
|
||||
builder: (context, state) {
|
||||
return state.maybeWhen(
|
||||
orElse: () => SizedBox.shrink(),
|
||||
loading: () => SizedBox(
|
||||
height: 24,
|
||||
width: 24,
|
||||
child: const CircularProgressIndicator(),
|
||||
),
|
||||
loaded: (outlet, categoryAnalyticData, profitLossData,
|
||||
paymentMethodAnalyticData, productAnalyticData) =>
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
try {
|
||||
final status =
|
||||
await PermessionHelper().checkPermission();
|
||||
if (status) {
|
||||
final pdfFile = await TransactionReport.previewPdf(
|
||||
outlet: outlet,
|
||||
searchDateFormatted: searchDateFormatted,
|
||||
categoryAnalyticData: categoryAnalyticData,
|
||||
profitLossData: profitLossData,
|
||||
paymentMethodAnalyticData:
|
||||
paymentMethodAnalyticData,
|
||||
productAnalyticData: productAnalyticData,
|
||||
);
|
||||
log("pdfFile: $pdfFile");
|
||||
await HelperPdfService.openFile(pdfFile);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Storage permission is required to save PDF'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
log("Error generating PDF: $e");
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Failed to generate PDF: $e'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12.0, vertical: 8.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6.0),
|
||||
border: Border.all(
|
||||
color: AppColors.stroke,
|
||||
)),
|
||||
child: Icon(
|
||||
Icons.download,
|
||||
color: AppColors.primary,
|
||||
size: 28,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
|
||||
@ -51,6 +51,8 @@ class ReportTitle extends StatelessWidget {
|
||||
),
|
||||
if (actionWidget != null)
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: actionWidget!,
|
||||
),
|
||||
],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user