From 3fa40f133a6b1f6d5c8fa2dbb42db29ea584a153 Mon Sep 17 00:00:00 2001 From: asif Date: Fri, 26 Dec 2025 15:47:47 +0530 Subject: [PATCH] stop cheque screen added --- lib/api/services/cheque_service.dart | 4 +- ...screen.dart => cheque_enquiry_screen.dart} | 219 ++++++------ .../screens/cheque_management_screen.dart | 70 ++-- .../cheque/screens/stop_cheque_screen.dart | 315 ++++++++++++++++++ 4 files changed, 472 insertions(+), 136 deletions(-) rename lib/features/cheque/screens/{cheque_status_screen.dart => cheque_enquiry_screen.dart} (55%) create mode 100644 lib/features/cheque/screens/stop_cheque_screen.dart diff --git a/lib/api/services/cheque_service.dart b/lib/api/services/cheque_service.dart index 6082b1c..4f991c7 100644 --- a/lib/api/services/cheque_service.dart +++ b/lib/api/services/cheque_service.dart @@ -9,7 +9,7 @@ class Cheque { final String? Chequescount; final String? ChequeNumber; final String? transactionCode; - final String? amount; + final int? amount; final String? status; final String? stopIssueDate; final String? StopExpiryDate; @@ -41,7 +41,7 @@ this.StopExpiryDate, Chequescount: json['Chequescount'] ?? '', ChequeNumber: json['ChequeNumber'] ?? '', transactionCode: json['transactionCode'] ?? '', - amount: json['amount'] ?? '', + amount: json['amount'], status: json['status'] ?? '', stopIssueDate: json['stopIssueDate'] ?? '', StopExpiryDate: json['StopExpiryDate'] ?? '', diff --git a/lib/features/cheque/screens/cheque_status_screen.dart b/lib/features/cheque/screens/cheque_enquiry_screen.dart similarity index 55% rename from lib/features/cheque/screens/cheque_status_screen.dart rename to lib/features/cheque/screens/cheque_enquiry_screen.dart index a9def87..f830083 100644 --- a/lib/features/cheque/screens/cheque_status_screen.dart +++ b/lib/features/cheque/screens/cheque_enquiry_screen.dart @@ -3,31 +3,32 @@ import 'package:kmobile/api/services/cheque_service.dart'; import 'package:kmobile/data/models/user.dart'; import 'package:kmobile/di/injection.dart'; -class ChequeStatusScreen extends StatefulWidget { +class ChequeEnquiryScreen extends StatefulWidget { final List users; final int selectedIndex; - const ChequeStatusScreen({ + const ChequeEnquiryScreen({ super.key, required this.users, required this.selectedIndex, }); @override - State createState() => _ChequeStatusScreenState(); + State createState() => _ChequeEnquiryScreenState(); } -class _ChequeStatusScreenState extends State { +class _ChequeEnquiryScreenState extends State { User? _selectedAccount; final TextEditingController _searchController = TextEditingController(); var service = getIt(); bool _isLoading = true; List _allCheques = []; Map> _groupedCheques = {}; + List _filteredUsers = []; @override void initState() { super.initState(); - final List _filteredUsers = widget.users + _filteredUsers = widget.users .where((user) => ['SA', 'SB', 'CA', 'CC'].contains(user.accountType)) .toList(); @@ -119,7 +120,6 @@ class _ChequeStatusScreenState extends State { filteredCheques = _allCheques.where((cheque) { final lowerQuery = query.toLowerCase(); return (cheque.ChequeNumber?.toLowerCase().contains(lowerQuery) ?? false) || - (cheque.amount?.toLowerCase().contains(lowerQuery) ?? false) || (cheque.status?.toLowerCase().contains(lowerQuery) ?? false) || (cheque.fromCheque?.toLowerCase().contains(lowerQuery) ?? false) || (cheque.toCheque?.toLowerCase().contains(lowerQuery) ?? false); @@ -142,80 +142,108 @@ class _ChequeStatusScreenState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Track Cheque Status'), + title: const Text('Cheque Enquiry'), centerTitle: false, ), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - Card( - elevation: 4, - margin: const EdgeInsets.symmetric(vertical: 8.0), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - "Account Number", - style: - TextStyle(fontWeight: FontWeight.bold, fontSize: 18), + body: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Card( + elevation: 4, + margin: const EdgeInsets.symmetric(vertical: 8.0), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + "Account Number", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 18), + ), + const SizedBox(width: 16), + if (_selectedAccount != null) + Expanded( + child: DropdownButton( + value: _selectedAccount, + onChanged: (User? newUser) { + if (newUser != null) { + setState(() { + _selectedAccount = newUser; + _loadCheques(); + }); + } + }, + items: _filteredUsers.map((user) { + return DropdownMenuItem( + value: user, + child: Text(user.accountNo.toString()), + ); + }).toList(), + ), + ) + else + const Text('No accounts found'), + ], ), - if (_selectedAccount != null) - DropdownButtonFormField( - value: _selectedAccount, - onChanged: (User? newUser) { - if (newUser != null) { - setState(() { - _selectedAccount = newUser; - _loadCheques(); - }); - } - }, - items: widget.users - .where((user) => - ['SA', 'SB', 'CA', 'CC'].contains(user.accountType)) - .map((user) { - return DropdownMenuItem( - value: user, - child: Text(user.accountNo.toString()), - ); - }).toList(), - ) - else - const Text('No accounts found'), - ], + ), + ), + const SizedBox(height: 20), + Card( + elevation: 4, + margin: const EdgeInsets.symmetric(vertical: 8.0), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: _searchController, + decoration: const InputDecoration( + labelText: 'Search...', + prefixIcon: Icon(Icons.search), + border: InputBorder + .none, // Remove border to make it look like it's inside the card + ), + ), + ), + ), + const SizedBox(height: 20), + Expanded( + child: _isLoading + ? const Center(child: CircularProgressIndicator()) + : _groupedCheques.isEmpty + ? const Center(child: Text('No cheque status found.')) + : ListView( + children: _groupedCheques.entries.map((entry) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...entry.value.map((cheque) => + ChequeStatusTile(cheque: cheque)), + ], + ); + }).toList(), + ), + ), + ], + ), + ), + IgnorePointer( + child: Center( + child: Opacity( + opacity: 0.07, // Reduced opacity + child: ClipOval( + child: Image.asset( + 'assets/images/logo.png', + width: 200, // Adjust size as needed + height: 200, // Adjust size as needed + ), ), ), ), - const SizedBox(height: 20), - TextField( - controller: _searchController, - decoration: const InputDecoration( - labelText: 'Search...', - prefixIcon: Icon(Icons.search), - ), - ), - const SizedBox(height: 20), - Expanded( - child: _isLoading - ? const Center(child: CircularProgressIndicator()) - : _groupedCheques.isEmpty - ? const Center(child: Text('No cheque status found.')) - : ListView( - children: _groupedCheques.entries.map((entry) { - return ExpansionTile( - title: Text(entry.key, style: TextStyle(fontWeight: FontWeight.bold)), - children: entry.value - .map((cheque) => ChequeStatusTile(cheque: cheque)) - .toList(), - ); - }).toList(), - ), - ), - ], - ), + ), + ], ), ); } @@ -245,18 +273,22 @@ class ChequeStatusTile extends StatelessWidget { Widget _buildCiTile(BuildContext context) { return Card( - margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16), + margin: const EdgeInsets.symmetric( + vertical: 8.0, + ), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Chequebook Issued (CI)', + style: Theme.of(context).textTheme.titleLarge), + const SizedBox(height: 8), _buildInfoRow('Branch Code:', cheque.branchCode), _buildInfoRow('From Cheque:', cheque.fromCheque), _buildInfoRow('To Cheque:', cheque.toCheque), _buildInfoRow('Date:', cheque.Date), _buildInfoRow('Cheques Count:', cheque.Chequescount), - _buildInfoRow('Instrument Type:', cheque.InstrType), ], ), ), @@ -265,19 +297,21 @@ class ChequeStatusTile extends StatelessWidget { Widget _buildPrTile(BuildContext context) { return Card( - margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16), + margin: const EdgeInsets.symmetric(vertical: 8.0), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Presented Cheque (PR)', + style: Theme.of(context).textTheme.titleLarge), + const SizedBox(height: 8), _buildInfoRow('Branch Code:', cheque.branchCode), _buildInfoRow('Cheque Number:', cheque.ChequeNumber), _buildInfoRow('Date:', cheque.Date), _buildInfoRow('Transaction Code:', cheque.transactionCode), - _buildInfoRow('Amount:', cheque.amount), - _buildInfoRow('Status:', cheque.status, statusColor: _getStatusColor(cheque.status ?? '')), - _buildInfoRow('Instrument Type:', cheque.InstrType), + _buildInfoRow('Amount:', '₹ ${cheque.amount.toString()}'), + _buildInfoRow('Status:', cheque.status), ], ), ), @@ -286,48 +320,37 @@ class ChequeStatusTile extends StatelessWidget { Widget _buildStTile(BuildContext context) { return Card( - margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16), + margin: const EdgeInsets.symmetric(vertical: 8.0), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Stop Cheque (ST)', + style: Theme.of(context).textTheme.titleLarge), + const SizedBox(height: 8), _buildInfoRow('Branch Code:', cheque.branchCode), _buildInfoRow('From Cheque:', cheque.fromCheque), _buildInfoRow('To Cheque:', cheque.toCheque), _buildInfoRow('Stop Issue Date:', cheque.stopIssueDate), _buildInfoRow('Stop Expiry Date:', cheque.StopExpiryDate), _buildInfoRow('Cheques Count:', cheque.Chequescount), - _buildInfoRow('Instrument Type:', cheque.InstrType), ], ), ), ); } - - Widget _buildInfoRow(String label, String? value, {Color? statusColor}) { + + Widget _buildInfoRow(String label, String? value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label, style: const TextStyle(fontWeight: FontWeight.bold)), - Text(value ?? '', style: TextStyle(color: statusColor)), + Text(value ?? ''), ], ), ); } - - Color _getStatusColor(String status) { - switch (status.toLowerCase()) { - case 'cashed': - return Colors.green; - case 'pending': - return Colors.orange; - case 'bounced': - return Colors.red; - default: - return Colors.grey; - } - } } diff --git a/lib/features/cheque/screens/cheque_management_screen.dart b/lib/features/cheque/screens/cheque_management_screen.dart index 239f1a1..a735fe7 100644 --- a/lib/features/cheque/screens/cheque_management_screen.dart +++ b/lib/features/cheque/screens/cheque_management_screen.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:kmobile/data/models/user.dart'; -import 'package:kmobile/features/cheque/screens/cheque_status_screen.dart'; +import 'package:kmobile/features/cheque/screens/cheque_enquiry_screen.dart'; +import 'package:kmobile/features/cheque/screens/stop_cheque_screen.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; import '../../../l10n/app_localizations.dart'; @@ -10,7 +11,8 @@ class ChequeManagementScreen extends StatefulWidget { const ChequeManagementScreen({ super.key, required this.users, - required this.selectedIndex,}); + required this.selectedIndex, + }); @override State createState() => _ChequeManagementScreen(); @@ -36,45 +38,41 @@ class _ChequeManagementScreen extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Expanded( - child: ChequeManagementCardTile( - icon: Symbols.add, - label: AppLocalizations.of(context).requestChequeBook, - subtitle: "Apply for a new cheque book", - onTap: () {}, - ), - ), - Expanded( - child: ChequeManagementCardTile( - icon: Symbols.data_alert, - label: "Cheque History", - subtitle: "View the status of your issued cheques", - onTap: () {}, - ), - ), - Expanded( - child: ChequeManagementCardTile( - icon: Symbols.front_hand, - label: AppLocalizations.of(context).stopCheque, - subtitle: "Stop payment for a cheque", - onTap: () {}, - ), - ), Expanded( child: ChequeManagementCardTile( icon: Symbols.payments, - label: "Track Cheque Status", - subtitle: "Check the current status of your cheque", + label: "Cheque Enquiry", + subtitle: + "You can view the status of your issued cheque book, presented cheques and details of stopped cheques including relevant dates, cheque numbers and other essential information", onTap: () { Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ChequeStatusScreen( - users: users, - selectedIndex: selectedAccountIndex, - ), - ), - ); + context, + MaterialPageRoute( + builder: (context) => ChequeEnquiryScreen( + users: users, + selectedIndex: selectedAccountIndex, + ), + ), + ); + }, + ), + ), + Expanded( + child: ChequeManagementCardTile( + icon: Symbols.block_sharp, + label: AppLocalizations.of(context).stopCheque, + subtitle: + "Initiate a stop payment request for a cheque that has already been presented for payment. This action helps prevent unauthorized transactions or rectifies errors on cheques that are in the process of being cleared", + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => StopChequeScreen( + users: users, + selectedIndex: selectedAccountIndex, + ), + ), + ); }, ), ), diff --git a/lib/features/cheque/screens/stop_cheque_screen.dart b/lib/features/cheque/screens/stop_cheque_screen.dart new file mode 100644 index 0000000..4a6df04 --- /dev/null +++ b/lib/features/cheque/screens/stop_cheque_screen.dart @@ -0,0 +1,315 @@ +import 'package:flutter/material.dart'; +import 'package:kmobile/api/services/cheque_service.dart'; +import 'package:kmobile/data/models/user.dart'; +import 'package:kmobile/di/injection.dart'; + +class StopChequeScreen extends StatefulWidget { + final List users; + final int selectedIndex; + const StopChequeScreen({ + super.key, + required this.users, + required this.selectedIndex, + }); + + @override + State createState() => _StopChequeScreenState(); +} + +class _StopChequeScreenState extends State { + User? _selectedAccount; + final TextEditingController _searchController = TextEditingController(); + var service = getIt(); + bool _isLoading = true; + List _allCheques = []; + Map> _groupedCheques = {}; + List _filteredUsers = []; + + @override + void initState() { + super.initState(); + _filteredUsers = widget.users + .where((user) => ['SA', 'SB', 'CA', 'CC'].contains(user.accountType)) + .toList(); + + if (widget.users.isNotEmpty && widget.selectedIndex < widget.users.length) { + if (_filteredUsers.isNotEmpty) { + if (_filteredUsers.contains(widget.users[widget.selectedIndex])) { + _selectedAccount = widget.users[widget.selectedIndex]; + } else { + _selectedAccount = _filteredUsers.first; + } + } else { + _selectedAccount = widget.users[widget.selectedIndex]; + } + } else { + if (_filteredUsers.isNotEmpty) { + _selectedAccount = _filteredUsers.first; + } + } + + _loadCheques(); + _searchController.addListener(() { + _filterCheques(_searchController.text); + }); + } + + Future _loadCheques() async { + if (_selectedAccount == null) { + setState(() { + _isLoading = false; + _groupedCheques = {}; + }); + return; + } + setState(() { + _isLoading = true; + }); + + String instrType; + switch (_selectedAccount!.accountType) { + case 'SA': + case 'SB': + instrType = '10'; + break; + case 'CA': + instrType = '11'; + break; + case 'CC': + instrType = '13'; + break; + default: + instrType = '10'; + } + + try { + final data = await service.ChequeEnquiry( + accountNumber: _selectedAccount!.accountNo!, instrType: instrType); + _allCheques = data.where((cheque) => cheque.type == 'PR').toList(); + _groupedCheques.clear(); + for (var cheque in _allCheques) { + if (cheque.type != null) { + if (!_groupedCheques.containsKey(cheque.type)) { + _groupedCheques[cheque.type!] = []; + } + _groupedCheques[cheque.type!]!.add(cheque); + } + } + setState(() { + _isLoading = false; + }); + } catch (e) { + setState(() { + _isLoading = false; + _groupedCheques = {}; + }); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Failed to fetch cheque status: ${e.toString()}'), + ), + ); + } + } + + void _filterCheques(String query) { + _groupedCheques.clear(); + List filteredCheques; + if (query.isEmpty) { + filteredCheques = _allCheques; + } else { + filteredCheques = _allCheques.where((cheque) { + final lowerQuery = query.toLowerCase(); + return (cheque.ChequeNumber?.toLowerCase().contains(lowerQuery) ?? false) || + (cheque.status?.toLowerCase().contains(lowerQuery) ?? false) || + (cheque.fromCheque?.toLowerCase().contains(lowerQuery) ?? false) || + (cheque.toCheque?.toLowerCase().contains(lowerQuery) ?? false); + }).toList(); + } + + for (var cheque in filteredCheques) { + if (cheque.type != null) { + if (!_groupedCheques.containsKey(cheque.type)) { + _groupedCheques[cheque.type!] = []; + } + _groupedCheques[cheque.type!]!.add(cheque); + } + } + + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Stop Cheque'), + centerTitle: false, + ), + body: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Card( + elevation: 4, + margin: const EdgeInsets.symmetric(vertical: 8.0), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + "Account Number", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 18), + ), + const SizedBox(width: 16), + if (_selectedAccount != null) + Expanded( + child: DropdownButton( + value: _selectedAccount, + onChanged: (User? newUser) { + if (newUser != null) { + setState(() { + _selectedAccount = newUser; + _loadCheques(); + }); + } + }, + items: _filteredUsers.map((user) { + return DropdownMenuItem( + value: user, + child: Text(user.accountNo.toString()), + ); + }).toList(), + ), + ) + else + const Text('No accounts found'), + ], + ), + ), + ), + const SizedBox(height: 20), + Card( + elevation: 4, + margin: const EdgeInsets.symmetric(vertical: 8.0), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: _searchController, + decoration: const InputDecoration( + labelText: 'Search', + prefixIcon: Icon(Icons.search), + border: InputBorder + .none, // Remove border to make it look like it's inside the card + ), + ), + ), + ), + const SizedBox(height: 20), + Expanded( + child: _isLoading + ? const Center(child: CircularProgressIndicator()) + : _groupedCheques.isEmpty + ? const Center(child: Text('No cheque status found.')) + : ListView( + children: _groupedCheques.entries.map((entry) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...entry.value.map((cheque) => + ChequeStatusTile(cheque: cheque)), + ], + ); + }).toList(), + ), + ), + ], + ), + ), + IgnorePointer( + child: Center( + child: Opacity( + opacity: 0.07, // Reduced opacity + child: ClipOval( + child: Image.asset( + 'assets/images/logo.png', + width: 200, // Adjust size as needed + height: 200, // Adjust size as needed + ), + ), + ), + ), + ), + ], + ), + ); + } +} + +class ChequeStatusTile extends StatelessWidget { + final Cheque cheque; + + const ChequeStatusTile({ + super.key, + required this.cheque, + }); + + @override + Widget build(BuildContext context) { + switch (cheque.type) { + case 'PR': + return _buildPrTile(context); + default: + return const SizedBox.shrink(); + } + } + + Widget _buildPrTile(BuildContext context) { + return Card( + margin: const EdgeInsets.symmetric(vertical: 8.0), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Presented Cheque(PR)', + style: Theme.of(context).textTheme.titleLarge), + const SizedBox(height: 8), + _buildInfoRow('Branch Code:', cheque.branchCode), + _buildInfoRow('Cheque Number:', cheque.ChequeNumber), + _buildInfoRow('Date:', cheque.Date), + _buildInfoRow('Transaction Code:', cheque.transactionCode), + _buildInfoRow('Amount:', '₹ ${cheque.amount.toString()}'), + _buildInfoRow('Status:', cheque.status), + const SizedBox(height: 16), + Center( + child: ElevatedButton.icon( + onPressed: () { + // TODO: Implement stop cheque functionality + }, + icon: const Icon(Icons.block_sharp), + label: const Text('Stop Cheque'), + ), + ), + ], + ), + ), + ); + } + + Widget _buildInfoRow(String label, String? value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(label, style: const TextStyle(fontWeight: FontWeight.bold)), + Text(value ?? ''), + ], + ), + ); + } +}