Added market place specific feature
This commit is contained in:
@@ -18,9 +18,16 @@ import '../domain/invoice.dart';
|
||||
import '../providers/invoices_provider.dart';
|
||||
import '../providers/customers_provider.dart';
|
||||
import '../../business/providers/business_provider.dart';
|
||||
import '../../business/providers/indian_states_provider.dart';
|
||||
import '../../transactions/presentation/widgets/attachment_gallery_screen.dart';
|
||||
import '../../../core/network/dio_client.dart';
|
||||
import 'widgets/receive_payment_sheet.dart';
|
||||
import 'widgets/create_credit_note_sheet.dart';
|
||||
import 'widgets/settle_marketplace_order_sheet.dart';
|
||||
import 'credit_notes_list_screen.dart';
|
||||
import 'marketplace_settlements_screen.dart';
|
||||
import '../providers/credit_notes_provider.dart';
|
||||
import '../providers/marketplace_settlements_provider.dart';
|
||||
import 'invoice_builder_screen.dart';
|
||||
|
||||
bool _isCommodityProduct({
|
||||
@@ -533,19 +540,239 @@ class _InvoiceDetailsScreenState extends ConsumerState<InvoiceDetailsScreen> {
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
|
||||
// Bottom Action: Receive Payment if balance due
|
||||
if (remaining > 0)
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _showReceivePaymentSheet(context, ref, latestInvoice),
|
||||
icon: const Icon(LucideIcons.plusCircle, size: 18),
|
||||
label: const Text('Record Payment', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
backgroundColor: Colors.green,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
// Credit Notes / Returns associated with this Invoice
|
||||
if (latestInvoice.id != null) ...[
|
||||
ref.watch(invoiceCreditNotesProvider(latestInvoice.id!)).when(
|
||||
data: (cns) {
|
||||
if (cns.isEmpty) return const SizedBox.shrink();
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Returns & Credit Notes Issued', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 8),
|
||||
...cns.map((cn) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.withValues(alpha: isDark ? 0.1 : 0.05),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.red.withValues(alpha: 0.25)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(LucideIcons.undo2, color: Colors.red, size: 16),
|
||||
const SizedBox(width: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Credit Note #${cn.creditNoteNumber}',
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13),
|
||||
),
|
||||
Text(
|
||||
'${cn.reason.replaceAll("_", " ")} • ${formatDate.format(cn.returnDate)}',
|
||||
style: TextStyle(fontSize: 11, color: Colors.grey.shade600),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
formatCurrency.format(cn.totalAmount),
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: Colors.red),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
},
|
||||
loading: () => const SizedBox.shrink(),
|
||||
error: (_, __) => const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
|
||||
// Marketplace Settlement Breakdown Card (if settled)
|
||||
if (latestInvoice.id != null) ...[
|
||||
ref.watch(invoiceSettlementProvider(latestInvoice.id!)).when(
|
||||
data: (settlement) {
|
||||
if (settlement == null) return const SizedBox.shrink();
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text('Marketplace Settlement & Payout', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF10B981).withValues(alpha: 0.12),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(LucideIcons.checkCheck, size: 12, color: Color(0xFF10B981)),
|
||||
SizedBox(width: 4),
|
||||
Text('Reconciled', style: TextStyle(fontSize: 11, fontWeight: FontWeight.bold, color: Color(0xFF10B981))),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(14),
|
||||
decoration: BoxDecoration(
|
||||
color: isDark ? const Color(0xFF1E293B) : Colors.white,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
border: Border.all(color: isDark ? Colors.white10 : Colors.grey.shade200),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('${settlement.channelName ?? "Marketplace"} Remittance', style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13)),
|
||||
Text(formatDate.format(settlement.settlementDate), style: TextStyle(fontSize: 12, color: Colors.grey.shade600)),
|
||||
],
|
||||
),
|
||||
if (settlement.settlementRef != null) ...[
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(LucideIcons.hash, size: 12, color: Colors.grey),
|
||||
const SizedBox(width: 4),
|
||||
Text('Bank UTR: ${settlement.settlementRef}', style: TextStyle(fontSize: 11, color: Colors.grey.shade600)),
|
||||
],
|
||||
),
|
||||
],
|
||||
const Divider(height: 16),
|
||||
_buildSummaryRow('Gross Sale Value', formatCurrency.format(settlement.grossAmount)),
|
||||
if (settlement.commissionFee > 0)
|
||||
_buildSummaryRow('Referral / Commission Fee', '- ${formatCurrency.format(settlement.commissionFee)}', color: Colors.red),
|
||||
if (settlement.shippingFee > 0)
|
||||
_buildSummaryRow('Logistics / Shipping Fee', '- ${formatCurrency.format(settlement.shippingFee)}', color: Colors.red),
|
||||
if (settlement.otherFees > 0)
|
||||
_buildSummaryRow('Closing / Fixed Fees', '- ${formatCurrency.format(settlement.otherFees)}', color: Colors.red),
|
||||
if (settlement.feeGst > 0)
|
||||
_buildSummaryRow('GST on Marketplace Fees (18%)', '- ${formatCurrency.format(settlement.feeGst)}', color: Colors.red),
|
||||
if (settlement.tcsGstAmount > 0)
|
||||
_buildSummaryRow('GST TCS Withheld (1%)', '- ${formatCurrency.format(settlement.tcsGstAmount)}', color: Colors.red),
|
||||
if (settlement.tdsAmount > 0)
|
||||
_buildSummaryRow('TDS u/s 194O Withheld (0.1%)', '- ${formatCurrency.format(settlement.tdsAmount)}', color: Colors.red),
|
||||
const Divider(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text('Net Bank Remittance Payout:', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 13)),
|
||||
Text(
|
||||
formatCurrency.format(settlement.netPayoutAmount),
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: Color(0xFF10B981)),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
},
|
||||
loading: () => const SizedBox.shrink(),
|
||||
error: (_, __) => const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
|
||||
// Action Buttons Row: Settle Marketplace Payout / Record Payment & Process Return
|
||||
Row(
|
||||
children: [
|
||||
if (latestInvoice.status != 'RETURNED') ...[
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
final result = await showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
||||
),
|
||||
builder: (_) => CreateCreditNoteSheet(invoice: latestInvoice),
|
||||
);
|
||||
if (result != null) {
|
||||
ref.refresh(invoicesProvider);
|
||||
if (latestInvoice.id != null) {
|
||||
ref.refresh(invoiceCreditNotesProvider(latestInvoice.id!));
|
||||
}
|
||||
}
|
||||
},
|
||||
icon: const Icon(LucideIcons.undo2, size: 16, color: Colors.red),
|
||||
label: const Text('Return / Credit Note', style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold, fontSize: 13)),
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
side: BorderSide(color: Colors.red.shade300),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
if (remaining > 0) ...[
|
||||
if (latestInvoice.salesChannel != null &&
|
||||
latestInvoice.salesChannel!.toUpperCase() != 'DIRECT_POS' &&
|
||||
latestInvoice.salesChannel!.toUpperCase() != 'STORE') ...[
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () async {
|
||||
final result = await showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
||||
),
|
||||
builder: (_) => SettleMarketplaceOrderSheet(invoice: latestInvoice),
|
||||
);
|
||||
if (result != null) {
|
||||
ref.refresh(invoicesProvider);
|
||||
if (latestInvoice.id != null) {
|
||||
ref.refresh(invoiceSettlementProvider(latestInvoice.id!));
|
||||
}
|
||||
}
|
||||
},
|
||||
icon: const Icon(LucideIcons.landmark, size: 16),
|
||||
label: const Text('Settle Payout', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 13)),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: const Color(0xFF10B981),
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
),
|
||||
),
|
||||
] else ...[
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () => _showReceivePaymentSheet(context, ref, latestInvoice),
|
||||
icon: const Icon(LucideIcons.plusCircle, size: 16),
|
||||
label: const Text('Record Payment', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 13)),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: Colors.green,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
],
|
||||
),
|
||||
@@ -555,6 +782,23 @@ class _InvoiceDetailsScreenState extends ConsumerState<InvoiceDetailsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Color _getDispatchColor(String? status) {
|
||||
switch (status?.toUpperCase()) {
|
||||
case 'DELIVERED':
|
||||
return Colors.green;
|
||||
case 'SHIPPED':
|
||||
case 'IN_TRANSIT':
|
||||
return Colors.indigo;
|
||||
case 'PACKED':
|
||||
return Colors.blue;
|
||||
case 'RTO':
|
||||
return Colors.red;
|
||||
case 'PENDING':
|
||||
default:
|
||||
return Colors.grey;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildDetailBadge(String text, Color color) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
@@ -656,7 +900,9 @@ class _InvoiceDetailsScreenState extends ConsumerState<InvoiceDetailsScreen> {
|
||||
|
||||
await Share.shareXFiles([XFile(imagePath.path)], text: 'Invoice $invoiceNumber');
|
||||
} catch (e) {
|
||||
if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error sharing image: $e')));
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error sharing image: $e')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,31 +914,28 @@ class _InvoiceDetailsScreenState extends ConsumerState<InvoiceDetailsScreen> {
|
||||
orElse: () => widget.invoice,
|
||||
) ?? widget.invoice;
|
||||
|
||||
final pdf = pw.Document();
|
||||
final business = ref.read(businessProfileProvider).value;
|
||||
final customers = ref.read(customersProvider).value ?? [];
|
||||
final customer = customers.where((c) => c.id == latestInvoice.customerId).firstOrNull;
|
||||
|
||||
final business = ref.read(businessProfileProvider).value;
|
||||
final products = ref.read(productsProvider).value ?? [];
|
||||
final categories = ref.read(productCategoriesProvider).value ?? [];
|
||||
final uoms = ref.read(uomsProvider).value ?? [];
|
||||
final indianStates = ref.read(indianStatesProvider).value ?? [];
|
||||
|
||||
final businessState = indianStates.where((s) => s.id == business?.stateId).firstOrNull;
|
||||
final customerState = indianStates.where((s) => s.id == (latestInvoice.placeOfSupplyStateId ?? customer?.stateId)).firstOrNull;
|
||||
|
||||
final isSameState = businessState != null && customerState != null && businessState.id == customerState.id;
|
||||
|
||||
final formatCurrency = NumberFormat.currency(symbol: 'Rs. ', decimalDigits: 2);
|
||||
final formatDate = DateFormat('dd MMM yyyy');
|
||||
final formatCurrency = NumberFormat.currency(symbol: 'Rs. ', decimalDigits: 2);
|
||||
|
||||
final font = await PdfGoogleFonts.robotoRegular();
|
||||
final boldFont = await PdfGoogleFonts.robotoBold();
|
||||
|
||||
final businessStateId = business?.stateId;
|
||||
final customerStateId = customer?.stateId;
|
||||
final isSameState = businessStateId != null && customerStateId != null
|
||||
? (businessStateId == customerStateId)
|
||||
: true;
|
||||
|
||||
// Group items by product ID if available, or itemize
|
||||
// Group items by productId
|
||||
final Map<int, List<InvoiceItem>> groupedItems = {};
|
||||
final List<InvoiceItem> ungroupedItems = [];
|
||||
|
||||
for (final item in latestInvoice.items) {
|
||||
for (var item in latestInvoice.items) {
|
||||
if (item.productId != null) {
|
||||
groupedItems.putIfAbsent(item.productId!, () => []).add(item);
|
||||
} else {
|
||||
@@ -700,96 +943,64 @@ class _InvoiceDetailsScreenState extends ConsumerState<InvoiceDetailsScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
final pdf = pw.Document();
|
||||
|
||||
pdf.addPage(
|
||||
pw.MultiPage(
|
||||
pageFormat: PdfPageFormat.a4,
|
||||
theme: pw.ThemeData.withFont(
|
||||
base: font,
|
||||
bold: boldFont,
|
||||
),
|
||||
margin: const pw.EdgeInsets.all(28),
|
||||
build: (pw.Context context) {
|
||||
return [
|
||||
// 1. Header with Business Details & TAX INVOICE title
|
||||
pw.Row(
|
||||
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||
children: [
|
||||
pw.Expanded(
|
||||
child: pw.Column(
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||
children: [
|
||||
pw.Text(
|
||||
business?.businessName ?? 'KIFI JEWELLERS',
|
||||
style: pw.TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
color: PdfColors.blue900,
|
||||
),
|
||||
),
|
||||
pw.SizedBox(height: 4),
|
||||
if (business?.address != null && business!.address!.isNotEmpty)
|
||||
margin: const pw.EdgeInsets.all(32),
|
||||
build: (context) => [
|
||||
pw.Column(
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 1. Enterprise Invoice Header
|
||||
pw.Row(
|
||||
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||
children: [
|
||||
pw.Expanded(
|
||||
child: pw.Column(
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||
children: [
|
||||
pw.Text(
|
||||
business.address!,
|
||||
style: const pw.TextStyle(fontSize: 9, color: PdfColors.grey700),
|
||||
),
|
||||
if (business?.contactNumber != null && business!.contactNumber!.isNotEmpty)
|
||||
pw.Text(
|
||||
'Phone: ${business.contactNumber}',
|
||||
style: const pw.TextStyle(fontSize: 9, color: PdfColors.grey700),
|
||||
),
|
||||
if (business?.gstin != null && business!.gstin!.isNotEmpty)
|
||||
pw.Text(
|
||||
'GSTIN: ${business.gstin}',
|
||||
business?.businessName ?? 'KIFI ENTERPRISE',
|
||||
style: pw.TextStyle(
|
||||
fontSize: 9,
|
||||
fontSize: 18,
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
color: PdfColors.blue800,
|
||||
color: PdfColors.blue900,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (business?.address != null && business!.address!.isNotEmpty)
|
||||
pw.Text(
|
||||
business.address!,
|
||||
style: const pw.TextStyle(fontSize: 9, color: PdfColors.grey700),
|
||||
),
|
||||
if (business?.contactNumber != null && business!.contactNumber!.isNotEmpty)
|
||||
pw.Text(
|
||||
'Phone: ${business.contactNumber}',
|
||||
style: const pw.TextStyle(fontSize: 9, color: PdfColors.grey700),
|
||||
),
|
||||
if (business?.gstin != null && business!.gstin!.isNotEmpty)
|
||||
pw.Text(
|
||||
'GSTIN: ${business.gstin}',
|
||||
style: pw.TextStyle(
|
||||
fontSize: 9,
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
color: PdfColors.blue800,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
pw.Column(
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.end,
|
||||
children: [
|
||||
pw.Text(
|
||||
'TAX INVOICE',
|
||||
style: pw.TextStyle(
|
||||
fontSize: 22,
|
||||
fontSize: 18,
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
color: PdfColors.blue900,
|
||||
),
|
||||
),
|
||||
pw.SizedBox(height: 6),
|
||||
pw.Container(
|
||||
padding: const pw.EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
decoration: pw.BoxDecoration(
|
||||
color: _getEffectiveStatus(latestInvoice) == 'PAID'
|
||||
? PdfColors.green100
|
||||
: (_getEffectiveStatus(latestInvoice) == 'PARTIAL' ? PdfColors.orange100 : PdfColors.grey200),
|
||||
borderRadius: const pw.BorderRadius.all(pw.Radius.circular(4)),
|
||||
border: pw.Border.all(
|
||||
color: _getEffectiveStatus(latestInvoice) == 'PAID'
|
||||
? PdfColors.green700
|
||||
: (_getEffectiveStatus(latestInvoice) == 'PARTIAL' ? PdfColors.orange700 : PdfColors.grey500),
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
child: pw.Text(
|
||||
_getEffectiveStatus(latestInvoice),
|
||||
style: pw.TextStyle(
|
||||
fontSize: 9,
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
color: _getEffectiveStatus(latestInvoice) == 'PAID'
|
||||
? PdfColors.green900
|
||||
: (_getEffectiveStatus(latestInvoice) == 'PARTIAL' ? PdfColors.orange900 : PdfColors.grey800),
|
||||
),
|
||||
),
|
||||
),
|
||||
pw.SizedBox(height: 6),
|
||||
pw.Text(
|
||||
'Invoice #: ${latestInvoice.invoiceNumber}',
|
||||
style: pw.TextStyle(fontSize: 11, fontWeight: pw.FontWeight.bold),
|
||||
@@ -798,6 +1009,21 @@ class _InvoiceDetailsScreenState extends ConsumerState<InvoiceDetailsScreen> {
|
||||
'Date: ${formatDate.format(latestInvoice.issueDate)}',
|
||||
style: const pw.TextStyle(fontSize: 9, color: PdfColors.grey700),
|
||||
),
|
||||
if (latestInvoice.salesChannel != null && latestInvoice.salesChannel!.isNotEmpty)
|
||||
pw.Text(
|
||||
'Channel: ${latestInvoice.salesChannel!}',
|
||||
style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold, color: PdfColors.blue900),
|
||||
),
|
||||
if (latestInvoice.marketplaceOrderId != null && latestInvoice.marketplaceOrderId!.isNotEmpty)
|
||||
pw.Text(
|
||||
'Order Ref: #${latestInvoice.marketplaceOrderId!}',
|
||||
style: const pw.TextStyle(fontSize: 8.5, color: PdfColors.grey700),
|
||||
),
|
||||
if (latestInvoice.courierPartner != null || latestInvoice.trackingNumber != null)
|
||||
pw.Text(
|
||||
'Courier: ${latestInvoice.courierPartner ?? ""}${latestInvoice.trackingNumber != null ? " (AWB: ${latestInvoice.trackingNumber})" : ""}',
|
||||
style: const pw.TextStyle(fontSize: 8.5, color: PdfColors.grey700),
|
||||
),
|
||||
if (latestInvoice.dueDate != null)
|
||||
pw.Text(
|
||||
'Due Date: ${formatDate.format(latestInvoice.dueDate!)}',
|
||||
@@ -809,7 +1035,7 @@ class _InvoiceDetailsScreenState extends ConsumerState<InvoiceDetailsScreen> {
|
||||
),
|
||||
pw.SizedBox(height: 18),
|
||||
|
||||
// 2. Bill To Box (Customer Details)
|
||||
// 2. Bill To & Ship To Box (Customer & Delivery Details)
|
||||
pw.Container(
|
||||
padding: const pw.EdgeInsets.all(10),
|
||||
decoration: pw.BoxDecoration(
|
||||
@@ -836,7 +1062,7 @@ class _InvoiceDetailsScreenState extends ConsumerState<InvoiceDetailsScreen> {
|
||||
pw.Text(
|
||||
customer?.name ?? 'Walk-in Customer',
|
||||
style: pw.TextStyle(
|
||||
fontSize: 12,
|
||||
fontSize: 11,
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
),
|
||||
),
|
||||
@@ -852,6 +1078,38 @@ class _InvoiceDetailsScreenState extends ConsumerState<InvoiceDetailsScreen> {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (latestInvoice.shippingAddress != null && latestInvoice.shippingAddress!.isNotEmpty) ...[
|
||||
pw.SizedBox(width: 14),
|
||||
pw.Expanded(
|
||||
child: pw.Column(
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||
children: [
|
||||
pw.Text(
|
||||
'SHIP TO (DELIVERY):',
|
||||
style: pw.TextStyle(
|
||||
fontSize: 8.5,
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
color: PdfColors.grey700,
|
||||
),
|
||||
),
|
||||
pw.SizedBox(height: 2),
|
||||
pw.Text(
|
||||
customer?.name ?? 'Recipient',
|
||||
style: pw.TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
),
|
||||
),
|
||||
pw.Text(
|
||||
latestInvoice.shippingAddress! + (latestInvoice.shippingPincode != null ? ' - ${latestInvoice.shippingPincode}' : ''),
|
||||
style: const pw.TextStyle(fontSize: 9),
|
||||
),
|
||||
if (latestInvoice.courierPartner != null)
|
||||
pw.Text('Via: ${latestInvoice.courierPartner}', style: const pw.TextStyle(fontSize: 8.5, color: PdfColors.grey700)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -1421,10 +1679,11 @@ class _InvoiceDetailsScreenState extends ConsumerState<InvoiceDetailsScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
},
|
||||
),
|
||||
);
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final pdfBytes = await pdf.save();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user