Files
Kifi/kifi-app/lib/features/sales/presentation/customer_ledger_screen.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,
),
),
),
),
],
);
},
),
);
}
}