Files
Kifi/kifi-app/lib/features/sales/presentation/customer_ledger_screen.dart

277 lines
9.9 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,
),
),
),
),
],
);
},
),
);
}
}