From 149d4dbc83e9c1f1a481d60dd1e38a1b17f1a61b Mon Sep 17 00:00:00 2001 From: asif Date: Mon, 29 Dec 2025 18:02:23 +0530 Subject: [PATCH] Stop Cheque Screens Created #Single --- .../cheque/screens/cheque_enquiry_screen.dart | 2 +- .../screens/cheque_management_screen.dart | 2 +- .../cheque/screens/stop_cheque_screen.dart | 239 ++++++++++-------- .../screens/stop_multiple_cheques_screen.dart | 73 ++++++ .../screens/stop_single_cheque_screen.dart | 179 +++++++++++++ 5 files changed, 388 insertions(+), 107 deletions(-) create mode 100644 lib/features/cheque/screens/stop_multiple_cheques_screen.dart create mode 100644 lib/features/cheque/screens/stop_single_cheque_screen.dart diff --git a/lib/features/cheque/screens/cheque_enquiry_screen.dart b/lib/features/cheque/screens/cheque_enquiry_screen.dart index f830083..b651d5d 100644 --- a/lib/features/cheque/screens/cheque_enquiry_screen.dart +++ b/lib/features/cheque/screens/cheque_enquiry_screen.dart @@ -200,7 +200,7 @@ class _ChequeEnquiryScreenState extends State { child: TextField( controller: _searchController, decoration: const InputDecoration( - labelText: 'Search...', + labelText: 'Search by Cheque Details', prefixIcon: Icon(Icons.search), border: InputBorder .none, // Remove border to make it look like it's inside the card diff --git a/lib/features/cheque/screens/cheque_management_screen.dart b/lib/features/cheque/screens/cheque_management_screen.dart index a735fe7..baf1d18 100644 --- a/lib/features/cheque/screens/cheque_management_screen.dart +++ b/lib/features/cheque/screens/cheque_management_screen.dart @@ -62,7 +62,7 @@ class _ChequeManagementScreen extends State { 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", + "Initiate stop for one or more cheques from your issued checkbook. This essential service helps prevent unauthorized transactions and protects against fraud.", onTap: () { Navigator.push( context, diff --git a/lib/features/cheque/screens/stop_cheque_screen.dart b/lib/features/cheque/screens/stop_cheque_screen.dart index 4a6df04..4632924 100644 --- a/lib/features/cheque/screens/stop_cheque_screen.dart +++ b/lib/features/cheque/screens/stop_cheque_screen.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:kmobile/features/cheque/screens/stop_multiple_cheques_screen.dart'; +import 'package:kmobile/features/cheque/screens/stop_single_cheque_screen.dart'; import 'package:kmobile/api/services/cheque_service.dart'; import 'package:kmobile/data/models/user.dart'; import 'package:kmobile/di/injection.dart'; +import 'package:kmobile/l10n/app_localizations.dart'; class StopChequeScreen extends StatefulWidget { final List users; @@ -18,11 +21,9 @@ class StopChequeScreen extends StatefulWidget { class _StopChequeScreenState extends State { User? _selectedAccount; - final TextEditingController _searchController = TextEditingController(); var service = getIt(); bool _isLoading = true; - List _allCheques = []; - Map> _groupedCheques = {}; + Cheque? _ciCheque; List _filteredUsers = []; @override @@ -49,16 +50,13 @@ class _StopChequeScreenState extends State { } _loadCheques(); - _searchController.addListener(() { - _filterCheques(_searchController.text); - }); } Future _loadCheques() async { if (_selectedAccount == null) { setState(() { _isLoading = false; - _groupedCheques = {}; + _ciCheque = null; }); return; } @@ -85,23 +83,15 @@ class _StopChequeScreenState extends State { 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); - } - } + final ciCheques = data.where((cheque) => cheque.type == 'CI').toList(); setState(() { + _ciCheque = ciCheques.isNotEmpty ? ciCheques.first : null; _isLoading = false; }); } catch (e) { setState(() { _isLoading = false; - _groupedCheques = {}; + _ciCheque = null; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -111,31 +101,19 @@ class _StopChequeScreenState extends State { } } - 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(); + String _getAccountTypeDisplayName(String accountType) { + switch (accountType.toLowerCase()) { + case 'sa': + return AppLocalizations.of(context).savingsAccount; + case 'sb': + return AppLocalizations.of(context).savingsAccount; + case 'ca': + return "Current Account"; + case 'cc': + return "Cash Credit Account"; + default: + return accountType; } - - for (var cheque in filteredCheques) { - if (cheque.type != null) { - if (!_groupedCheques.containsKey(cheque.type)) { - _groupedCheques[cheque.type!] = []; - } - _groupedCheques[cheque.type!]!.add(cheque); - } - } - - setState(() {}); } @override @@ -192,39 +170,112 @@ class _StopChequeScreenState extends State { ), ), 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 + Row( + children: [ + Expanded( + child: Card( + color: Theme.of(context).colorScheme.primaryContainer, + elevation: 4, + child: InkWell( + onTap: () { + if (_selectedAccount != null && _ciCheque != null) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + StopSingleChequeScreen( + selectedAccount: _selectedAccount!, + date: _ciCheque!.Date!, + instrType: _ciCheque!.InstrType!, + fromCheque: _ciCheque!.fromCheque!, + toCheque: _ciCheque!.toCheque!, + ), + ), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'No cheque book found to stop cheques from.'), + ), + ); + } + }, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: Text( + 'Stop Single Cheque', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Theme.of(context) + .colorScheme + .onPrimaryContainer, + ), + ), + ), + ), + ), ), ), - ), + const SizedBox(width: 10), + Expanded( + child: Card( + color: + Theme.of(context).colorScheme.primaryContainer, + elevation: 4, + child: InkWell( + onTap: () { + if (_selectedAccount != null) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + StopMultipleChequesScreen( + selectedAccount: _selectedAccount!, + ), + ), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Please select an account first.'), + ), + ); + } + }, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: Text( + 'Stop Multiple Cheques', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Theme.of(context) + .colorScheme + .onSecondaryContainer, + ), + ), + ), + ), + ), + ), + ), + ], ), 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(), - ), + : _ciCheque == null + ? const Center( + child: Text('No Cheque Issued status found.')) + : _buildCiTile(context, _ciCheque!), ), ], ), @@ -247,53 +298,31 @@ class _StopChequeScreenState extends State { ), ); } -} -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) { + Widget _buildCiTile(BuildContext context, Cheque cheque) { return Card( - margin: const EdgeInsets.symmetric(vertical: 8.0), + 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)', + Text('Chequebook Details', style: Theme.of(context).textTheme.titleLarge), const SizedBox(height: 8), + _buildInfoRow('Account Number:', _selectedAccount!.accountNo!), + _buildInfoRow('Customer Name:', _selectedAccount!.name!), + _buildInfoRow('CIF Number:', _selectedAccount!.cifNumber!), + _buildInfoRow('Account Type:', + _getAccountTypeDisplayName(_selectedAccount!.accountType!)), _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'), - ), - ), + _buildInfoRow('Starting Cheque Number:', cheque.fromCheque), + _buildInfoRow('Ending Cheque Number:', cheque.toCheque), + _buildInfoRow('Issue Date:', cheque.Date), + _buildInfoRow('Number of Cheques:', cheque.Chequescount), + _buildInfoRow('Instrument Type:', cheque.InstrType), ], ), ), diff --git a/lib/features/cheque/screens/stop_multiple_cheques_screen.dart b/lib/features/cheque/screens/stop_multiple_cheques_screen.dart new file mode 100644 index 0000000..f45c5d9 --- /dev/null +++ b/lib/features/cheque/screens/stop_multiple_cheques_screen.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:kmobile/data/models/user.dart'; + +class StopMultipleChequesScreen extends StatefulWidget { + final User selectedAccount; + + const StopMultipleChequesScreen({super.key, required this.selectedAccount}); + + @override + State createState() => + _StopMultipleChequesScreenState(); +} + +class _StopMultipleChequesScreenState extends State { + final _formKey = GlobalKey(); + final _fromChequeController = TextEditingController(); + final _toChequeController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Stop Multiple Cheques'), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: Column( + children: [ + TextFormField( + controller: _fromChequeController, + decoration: const InputDecoration( + labelText: 'From Cheque Number', + ), + keyboardType: TextInputType.number, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter the starting cheque number'; + } + return null; + }, + ), + const SizedBox(height: 20), + TextFormField( + controller: _toChequeController, + decoration: const InputDecoration( + labelText: 'To Cheque Number', + ), + keyboardType: TextInputType.number, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter the ending cheque number'; + } + return null; + }, + ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + // TODO: Implement stop multiple cheques logic + } + }, + child: const Text('Submit'), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/cheque/screens/stop_single_cheque_screen.dart b/lib/features/cheque/screens/stop_single_cheque_screen.dart new file mode 100644 index 0000000..c94658a --- /dev/null +++ b/lib/features/cheque/screens/stop_single_cheque_screen.dart @@ -0,0 +1,179 @@ +import 'package:flutter/material.dart'; +import 'package:kmobile/data/models/user.dart'; + +class StopSingleChequeScreen extends StatefulWidget { + final User selectedAccount; + final String date; + final String instrType; + final String fromCheque; + final String toCheque; + + const StopSingleChequeScreen( + {super.key, + required this.selectedAccount, + required this.date, + required this.instrType, + required this.fromCheque, + required this.toCheque}); + + @override + State createState() => _StopSingleChequeScreenState(); +} + +class _StopSingleChequeScreenState extends State { + final _formKey = GlobalKey(); + final _stopFromChequeNoController = TextEditingController(); + final _stopIssueDateController = TextEditingController(); + final _stopExpiryDateController = TextEditingController(); + final _stopAmountController = TextEditingController(); + final _stopCommentController = TextEditingController(); + + bool _validateChequeNumber() { + final value = _stopFromChequeNoController.text; + if (value.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Please enter a cheque number')), + ); + return false; + } + final chequeNumber = int.tryParse(value); + final fromCheque = int.tryParse(widget.fromCheque); + final toCheque = int.tryParse(widget.toCheque); + if (chequeNumber == null || fromCheque == null || toCheque == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Invalid cheque number format')), + ); + return false; + } + if (chequeNumber < fromCheque || chequeNumber > toCheque) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Cheque number must be between ${widget.fromCheque} and ${widget.toCheque}')), + ); + return false; + } + return true; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Stop Single Cheque'), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: ListView( + children: [ + Card( + elevation: 0, + margin: const EdgeInsets.symmetric(vertical: 8.0), + child: ListTile( + leading: Image.asset( + 'assets/images/logo.png', + width: 40, + height: 40, + ), + title: Text(widget.selectedAccount.accountNo!), + subtitle: const Text("Account Number"), + ), + ), + const SizedBox(height: 24), + TextFormField( + controller: _stopFromChequeNoController, + decoration: const InputDecoration( + labelText: 'Cheque Number', + border: OutlineInputBorder(), + ), + keyboardType: TextInputType.number, + validator: (value) { + // This validator will only return null or empty string to allow SnackBar to display + if (value == null || value.isEmpty) { + return ''; // Return empty string to trigger error state without message + } + final chequeNumber = int.tryParse(value); + final fromCheque = int.tryParse(widget.fromCheque); + final toCheque = int.tryParse(widget.toCheque); + if (chequeNumber == null || + fromCheque == null || + toCheque == null) { + return ''; + } + if (chequeNumber < fromCheque || chequeNumber > toCheque) { + return ''; + } + return null; + }, + ), + const SizedBox(height: 16), + TextFormField( + initialValue: widget.instrType, + readOnly: true, + decoration: const InputDecoration( + labelText: 'Instrument Type', + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 16), + TextFormField( + controller: _stopIssueDateController, + decoration: const InputDecoration( + labelText: 'Stop Issue Date', + border: OutlineInputBorder(), + ), + keyboardType: TextInputType.datetime, + ), + const SizedBox(height: 16), + TextFormField( + controller: _stopExpiryDateController, + decoration: const InputDecoration( + labelText: 'Stop Expiry Date', + border: OutlineInputBorder(), + ), + keyboardType: TextInputType.datetime, + ), + const SizedBox(height: 16), + TextFormField( + controller: _stopAmountController, + decoration: const InputDecoration( + labelText: 'Stop Amount', + border: OutlineInputBorder(), + ), + keyboardType: TextInputType.number, + ), + const SizedBox(height: 16), + TextFormField( + controller: _stopCommentController, + decoration: const InputDecoration( + labelText: 'Stop Comment', + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 16), + TextFormField( + initialValue: widget.date, + readOnly: true, + decoration: const InputDecoration( + labelText: 'Chequebook Issue Date', + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 32), + ElevatedButton( + onPressed: () { + if (_formKey.currentState!.validate() && _validateChequeNumber()) { + // TODO: Implement stop single cheque logic + } + }, + child: const Text('Stop Cheque'), + ), + ], + ), + ), + ), + ); + } +}