Stop Cheque Screens Created #Single

This commit is contained in:
2025-12-29 18:02:23 +05:30
parent 3fa40f133a
commit 149d4dbc83
5 changed files with 388 additions and 107 deletions

View File

@@ -200,7 +200,7 @@ class _ChequeEnquiryScreenState extends State<ChequeEnquiryScreen> {
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

View File

@@ -62,7 +62,7 @@ class _ChequeManagementScreen extends State<ChequeManagementScreen> {
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,

View File

@@ -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<User> users;
@@ -18,11 +21,9 @@ class StopChequeScreen extends StatefulWidget {
class _StopChequeScreenState extends State<StopChequeScreen> {
User? _selectedAccount;
final TextEditingController _searchController = TextEditingController();
var service = getIt<ChequeService>();
bool _isLoading = true;
List<Cheque> _allCheques = [];
Map<String, List<Cheque>> _groupedCheques = {};
Cheque? _ciCheque;
List<User> _filteredUsers = [];
@override
@@ -49,16 +50,13 @@ class _StopChequeScreenState extends State<StopChequeScreen> {
}
_loadCheques();
_searchController.addListener(() {
_filterCheques(_searchController.text);
});
}
Future<void> _loadCheques() async {
if (_selectedAccount == null) {
setState(() {
_isLoading = false;
_groupedCheques = {};
_ciCheque = null;
});
return;
}
@@ -85,23 +83,15 @@ class _StopChequeScreenState extends State<StopChequeScreen> {
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<StopChequeScreen> {
}
}
void _filterCheques(String query) {
_groupedCheques.clear();
List<Cheque> 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<StopChequeScreen> {
),
),
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<StopChequeScreen> {
),
);
}
}
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),
],
),
),

View File

@@ -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<StopMultipleChequesScreen> createState() =>
_StopMultipleChequesScreenState();
}
class _StopMultipleChequesScreenState extends State<StopMultipleChequesScreen> {
final _formKey = GlobalKey<FormState>();
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'),
),
],
),
),
),
);
}
}

View File

@@ -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<StopSingleChequeScreen> createState() => _StopSingleChequeScreenState();
}
class _StopSingleChequeScreenState extends State<StopSingleChequeScreen> {
final _formKey = GlobalKey<FormState>();
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'),
),
],
),
),
),
);
}
}