150 lines
6.8 KiB
Dart
150 lines
6.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:intl/intl.dart';
|
|
import '../domain/customer.dart';
|
|
import '../providers/invoices_provider.dart';
|
|
import '../../transactions/providers/providers.dart';
|
|
|
|
class CustomerLedgerScreen extends ConsumerStatefulWidget {
|
|
final Customer customer;
|
|
|
|
const CustomerLedgerScreen({super.key, required this.customer});
|
|
|
|
@override
|
|
ConsumerState<CustomerLedgerScreen> createState() => _CustomerLedgerScreenState();
|
|
}
|
|
|
|
class _CustomerLedgerScreenState extends ConsumerState<CustomerLedgerScreen> {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final invoicesState = ref.watch(invoicesProvider);
|
|
final formatCurrency = NumberFormat.currency(symbol: '₹');
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text('${widget.customer.name} Ledger'),
|
|
backgroundColor: Colors.white,
|
|
foregroundColor: Colors.black,
|
|
elevation: 0,
|
|
),
|
|
body: invoicesState.when(
|
|
loading: () => const Center(child: CircularProgressIndicator()),
|
|
error: (err, stack) => Center(child: Text('Error: $err')),
|
|
data: (invoices) {
|
|
final customerInvoices = invoices.where((i) => i.customerId == widget.customer.id).toList();
|
|
|
|
if (customerInvoices.isEmpty) {
|
|
return const Center(child: Text('No invoices found for this customer.'));
|
|
}
|
|
|
|
// Sort chronological
|
|
customerInvoices.sort((a, b) => a.issueDate.compareTo(b.issueDate));
|
|
|
|
double runningBalance = 0.0;
|
|
List<DataRow> rows = [];
|
|
final wallets = ref.read(walletProvider).value ?? [];
|
|
|
|
int index = 1;
|
|
for (var inv in customerInvoices) {
|
|
// Add row for Invoice (Debit)
|
|
runningBalance += inv.totalAmount;
|
|
rows.add(DataRow(
|
|
cells: [
|
|
DataCell(Text('${index++}')),
|
|
DataCell(Text(DateFormat('dd MMM yyyy').format(inv.issueDate))),
|
|
DataCell(Text(inv.invoiceNumber, style: const TextStyle(fontWeight: FontWeight.w600))),
|
|
const DataCell(Text('Invoice')),
|
|
const DataCell(Text('-')),
|
|
const DataCell(Text('-')),
|
|
DataCell(Text(formatCurrency.format(inv.totalAmount), style: TextStyle(color: Colors.red.shade700, fontWeight: FontWeight.bold))),
|
|
DataCell(Text(formatCurrency.format(runningBalance), style: const TextStyle(fontWeight: FontWeight.bold))),
|
|
],
|
|
));
|
|
|
|
// Add row for Payment (Credit)
|
|
if (inv.payments != null && inv.payments!.isNotEmpty) {
|
|
for (var p in inv.payments!) {
|
|
runningBalance -= p.amount;
|
|
final walletName = wallets.firstWhere((w) => w.id == p.walletId, orElse: () => wallets.first).name;
|
|
rows.add(DataRow(
|
|
cells: [
|
|
DataCell(Text('${index++}')),
|
|
DataCell(Text(p.paymentDate != null ? DateFormat('dd MMM yyyy').format(p.paymentDate!) : DateFormat('dd MMM yyyy').format(inv.issueDate))),
|
|
DataCell(Text(inv.invoiceNumber, style: const TextStyle(fontWeight: FontWeight.w600))),
|
|
const DataCell(Text('Payment')),
|
|
DataCell(Text(p.paymentMethod)),
|
|
DataCell(Text(p.walletId != null ? walletName : '-')),
|
|
DataCell(Text(formatCurrency.format(p.amount), style: TextStyle(color: Colors.green.shade700, fontWeight: FontWeight.bold))),
|
|
DataCell(Text(formatCurrency.format(runningBalance), style: const TextStyle(fontWeight: FontWeight.bold))),
|
|
],
|
|
));
|
|
}
|
|
} else if (inv.amountPaid > 0) {
|
|
runningBalance -= inv.amountPaid;
|
|
rows.add(DataRow(
|
|
cells: [
|
|
DataCell(Text('${index++}')),
|
|
DataCell(Text(DateFormat('dd MMM yyyy').format(inv.issueDate))),
|
|
DataCell(Text(inv.invoiceNumber, style: const TextStyle(fontWeight: FontWeight.w600))),
|
|
const DataCell(Text('Payment')),
|
|
const DataCell(Text('-')),
|
|
const DataCell(Text('-')),
|
|
DataCell(Text(formatCurrency.format(inv.amountPaid), style: TextStyle(color: Colors.green.shade700, fontWeight: FontWeight.bold))),
|
|
DataCell(Text(formatCurrency.format(runningBalance), style: const TextStyle(fontWeight: FontWeight.bold))),
|
|
],
|
|
));
|
|
}
|
|
}
|
|
|
|
return Column(
|
|
children: [
|
|
Container(
|
|
width: double.infinity,
|
|
color: Colors.blue.withOpacity(0.05),
|
|
padding: const EdgeInsets.all(24),
|
|
child: Column(
|
|
children: [
|
|
const Text('Outstanding Balance', style: TextStyle(color: Colors.grey, fontSize: 14)),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
formatCurrency.format(runningBalance),
|
|
style: TextStyle(
|
|
fontSize: 28,
|
|
fontWeight: FontWeight.bold,
|
|
color: runningBalance > 0 ? Colors.red.shade700 : Colors.green.shade700,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.vertical,
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
child: DataTable(
|
|
headingRowColor: WidgetStateProperty.resolveWith((states) => Colors.grey.shade100),
|
|
columnSpacing: 24,
|
|
columns: const [
|
|
DataColumn(label: Text('S.No', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
DataColumn(label: Text('Date', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
DataColumn(label: Text('Invoice #', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
DataColumn(label: Text('Type', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
DataColumn(label: Text('Mode', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
DataColumn(label: Text('Wallet', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
DataColumn(label: Text('Amount', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
DataColumn(label: Text('Balance', style: TextStyle(fontWeight: FontWeight.bold))),
|
|
],
|
|
rows: rows,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|