Limit Added to SMS enabled device
This commit is contained in:
@@ -2,7 +2,9 @@ import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:kmobile/api/services/imps_service.dart';
|
||||
import 'package:kmobile/api/services/limit_service.dart';
|
||||
import 'package:kmobile/api/services/neft_service.dart';
|
||||
import 'package:kmobile/api/services/rtgs_service.dart';
|
||||
import 'package:kmobile/data/models/imps_transaction.dart';
|
||||
@@ -28,7 +30,10 @@ class QuickPayOutsideBankScreen extends StatefulWidget {
|
||||
|
||||
class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
final _limitService = getIt<LimitService>();
|
||||
Limit? _limit;
|
||||
bool _isLoadingLimit = true;
|
||||
final _formatCurrency = NumberFormat.currency(locale: 'en_IN', symbol: '₹');
|
||||
// Controllers
|
||||
final accountNumberController = TextEditingController();
|
||||
final confirmAccountNumberController = TextEditingController();
|
||||
@@ -41,6 +46,7 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
|
||||
final remarksController = TextEditingController();
|
||||
final _ifscFocusNode = FocusNode();
|
||||
final service = getIt<BeneficiaryService>();
|
||||
bool _isAmountOverLimit = false;
|
||||
|
||||
late String accountType;
|
||||
bool _isValidating = false;
|
||||
@@ -50,6 +56,7 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadLimit();
|
||||
_ifscFocusNode.addListener(() {
|
||||
if (!_ifscFocusNode.hasFocus && ifscController.text.trim().length == 11) {
|
||||
_validateIFSC();
|
||||
@@ -60,8 +67,51 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
|
||||
accountType = 'Savings';
|
||||
});
|
||||
});
|
||||
amountController.addListener(_checkAmountLimit);
|
||||
}
|
||||
|
||||
Future<void> _loadLimit() async {
|
||||
setState(() {
|
||||
_isLoadingLimit = true;
|
||||
});
|
||||
try {
|
||||
final limitData = await _limitService.getLimit();
|
||||
setState(() {
|
||||
_limit = limitData;
|
||||
_isLoadingLimit = false;
|
||||
});
|
||||
} catch (e) {
|
||||
// Handle error if needed
|
||||
setState(() {
|
||||
_isLoadingLimit = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add this method to check the amount against the limit
|
||||
void _checkAmountLimit() {
|
||||
if (_limit == null) return;
|
||||
|
||||
final amount = double.tryParse(amountController.text) ?? 0;
|
||||
final remainingLimit = _limit!.dailyLimit - _limit!.usedLimit;
|
||||
final bool isOverLimit = amount > remainingLimit;
|
||||
|
||||
if (isOverLimit) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Amount exceeds remaining daily limit of ${_formatCurrency.format(remainingLimit)}'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (_isAmountOverLimit != isOverLimit) {
|
||||
setState(() {
|
||||
_isAmountOverLimit = isOverLimit;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _validateIFSC() async {
|
||||
final ifsc = ifscController.text.trim().toUpperCase();
|
||||
if (ifsc.isEmpty) return;
|
||||
@@ -718,6 +768,9 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
@@ -783,6 +836,22 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (_isLoadingLimit)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 8.0),
|
||||
child: Text('Fetching daily limit...'),
|
||||
),
|
||||
if (!_isLoadingLimit && _limit != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: Text(
|
||||
'Remaining Daily Limit: ${_formatCurrency.format(_limit!.dailyLimit - _limit!.usedLimit)}',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Row(
|
||||
children: [
|
||||
@@ -795,24 +864,31 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 45),
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: SwipeButton.expand(
|
||||
thumb: Icon(Icons.arrow_forward,
|
||||
color: Theme.of(context).dialogBackgroundColor),
|
||||
activeThumbColor: Theme.of(context).colorScheme.primary,
|
||||
activeTrackColor:
|
||||
Theme.of(context).colorScheme.secondary.withAlpha(100),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
height: 56,
|
||||
onSwipe: _onProceedToPay,
|
||||
child: Text(
|
||||
AppLocalizations.of(context).swipeToPay,
|
||||
style: const TextStyle(
|
||||
fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: SwipeButton.expand(
|
||||
thumb: Icon(Icons.arrow_forward,
|
||||
color: _isAmountOverLimit ? Colors.grey : Theme.of(context).dialogBackgroundColor),
|
||||
activeThumbColor: _isAmountOverLimit ? Colors.grey.shade700 :
|
||||
Theme.of(context).colorScheme.primary,
|
||||
activeTrackColor: _isAmountOverLimit
|
||||
? Colors.grey.shade300
|
||||
: Theme.of(context).colorScheme.secondary.withAlpha(100),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
height: 56,
|
||||
onSwipe: () {
|
||||
if (_isAmountOverLimit) {
|
||||
return; // Do nothing if amount is over the limit
|
||||
}
|
||||
_onProceedToPay();
|
||||
},
|
||||
child: Text(
|
||||
AppLocalizations.of(context).swipeToPay,
|
||||
style: const TextStyle(
|
||||
fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_swipe_button/flutter_swipe_button.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:kmobile/api/services/beneficiary_service.dart';
|
||||
import 'package:kmobile/api/services/limit_service.dart';
|
||||
import 'package:kmobile/api/services/payment_service.dart';
|
||||
import 'package:kmobile/data/models/transfer.dart';
|
||||
import 'package:kmobile/di/injection.dart';
|
||||
@@ -19,14 +21,17 @@ class QuickPayWithinBankScreen extends StatefulWidget {
|
||||
|
||||
class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
final _limitService = getIt<LimitService>();
|
||||
Limit? _limit;
|
||||
bool _isLoadingLimit = true;
|
||||
final _formatCurrency = NumberFormat.currency(locale: 'en_IN', symbol: '₹');
|
||||
final TextEditingController accountNumberController = TextEditingController();
|
||||
final TextEditingController confirmAccountNumberController =
|
||||
TextEditingController();
|
||||
final TextEditingController amountController = TextEditingController();
|
||||
final TextEditingController remarksController = TextEditingController();
|
||||
String? _selectedAccountType;
|
||||
|
||||
bool _isAmountOverLimit = false;
|
||||
String? _beneficiaryName;
|
||||
bool _isValidating = false;
|
||||
bool _isBeneficiaryValidated = false;
|
||||
@@ -35,10 +40,54 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadLimit();
|
||||
accountNumberController.addListener(_resetBeneficiaryValidation);
|
||||
confirmAccountNumberController.addListener(_resetBeneficiaryValidation);
|
||||
amountController.addListener(_checkAmountLimit);
|
||||
}
|
||||
|
||||
Future<void> _loadLimit() async {
|
||||
setState(() {
|
||||
_isLoadingLimit = true;
|
||||
});
|
||||
try {
|
||||
final limitData = await _limitService.getLimit();
|
||||
setState(() {
|
||||
_limit = limitData;
|
||||
_isLoadingLimit = false;
|
||||
});
|
||||
} catch (e) {
|
||||
// Handle error if needed
|
||||
setState(() {
|
||||
_isLoadingLimit = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _checkAmountLimit() {
|
||||
if (_limit == null) return;
|
||||
|
||||
final amount = double.tryParse(amountController.text) ?? 0;
|
||||
final remainingLimit = _limit!.dailyLimit - _limit!.usedLimit;
|
||||
final bool isOverLimit = amount > remainingLimit;
|
||||
|
||||
if (isOverLimit) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Amount exceeds remaining daily limit of ${_formatCurrency.format(remainingLimit)}'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Update state only if it changes to avoid unnecessary rebuilds
|
||||
if (_isAmountOverLimit != isOverLimit) {
|
||||
setState(() {
|
||||
_isAmountOverLimit = isOverLimit;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _resetBeneficiaryValidation() {
|
||||
if (_isBeneficiaryValidated ||
|
||||
_beneficiaryName != null ||
|
||||
@@ -53,6 +102,7 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
amountController.removeListener(_checkAmountLimit);
|
||||
accountNumberController.removeListener(_resetBeneficiaryValidation);
|
||||
confirmAccountNumberController.removeListener(_resetBeneficiaryValidation);
|
||||
accountNumberController.dispose();
|
||||
@@ -102,7 +152,8 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
TextFormField(
|
||||
@@ -297,6 +348,7 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).amount,
|
||||
@@ -327,66 +379,81 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (_isLoadingLimit)
|
||||
const Text('Fetching daily limit...'),
|
||||
if (!_isLoadingLimit && _limit != null)
|
||||
Text(
|
||||
'Remaining Daily Limit: ${_formatCurrency.format(_limit!.dailyLimit - _limit!.usedLimit)}',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 45),
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: SwipeButton.expand(
|
||||
thumb: Icon(Icons.arrow_forward,
|
||||
color: Theme.of(context).dialogBackgroundColor),
|
||||
activeThumbColor: Theme.of(context).colorScheme.primary,
|
||||
activeTrackColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.secondary.withAlpha(100),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
height: 56,
|
||||
child: Text(
|
||||
AppLocalizations.of(context).swipeToPay,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
onSwipe: () {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
if (!_isBeneficiaryValidated) {
|
||||
setState(() {
|
||||
_validationError = AppLocalizations.of(context)
|
||||
.validateBeneficiaryproceeding;
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Perform payment logic
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => TransactionPinScreen(
|
||||
onPinCompleted: (pinScreenContext, tpin) async {
|
||||
final transfer = Transfer(
|
||||
fromAccount: widget.debitAccount,
|
||||
toAccount: accountNumberController.text,
|
||||
toAccountType: _selectedAccountType!,
|
||||
amount: amountController.text,
|
||||
tpin: tpin,
|
||||
remarks: remarksController.text,
|
||||
);
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: SwipeButton.expand(
|
||||
thumb: Icon(Icons.arrow_forward,
|
||||
color: _isAmountOverLimit ? Colors.grey : Theme.of(context).dialogBackgroundColor),
|
||||
activeThumbColor: _isAmountOverLimit ? Colors.grey.shade700 :
|
||||
Theme.of(context).colorScheme.primary,
|
||||
activeTrackColor: _isAmountOverLimit
|
||||
? Colors.grey.shade300
|
||||
: Theme.of(
|
||||
context,
|
||||
).colorScheme.secondary.withAlpha(100),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
height: 56,
|
||||
child: Text(
|
||||
AppLocalizations.of(context).swipeToPay,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
onSwipe: () {
|
||||
if (_isAmountOverLimit) {
|
||||
return; // Do nothing if amount is over limit
|
||||
}
|
||||
if (_formKey.currentState!.validate()) {
|
||||
if (!_isBeneficiaryValidated) {
|
||||
setState(() {
|
||||
_validationError = AppLocalizations.of(context)
|
||||
.validateBeneficiaryproceeding;
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Perform payment logic
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => TransactionPinScreen(
|
||||
onPinCompleted: (pinScreenContext, tpin) async {
|
||||
final transfer = Transfer(
|
||||
fromAccount: widget.debitAccount,
|
||||
toAccount: accountNumberController.text,
|
||||
toAccountType: _selectedAccountType!,
|
||||
amount: amountController.text,
|
||||
tpin: tpin,
|
||||
remarks: remarksController.text,
|
||||
);
|
||||
|
||||
final paymentService = getIt<PaymentService>();
|
||||
final paymentResponseFuture = paymentService
|
||||
.processQuickPayWithinBank(transfer);
|
||||
final paymentService = getIt<PaymentService>();
|
||||
final paymentResponseFuture = paymentService
|
||||
.processQuickPayWithinBank(transfer);
|
||||
|
||||
Navigator.of(pinScreenContext).pushReplacement(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => PaymentAnimationScreen(
|
||||
paymentResponse: paymentResponseFuture),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
Navigator.of(pinScreenContext).pushReplacement(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => PaymentAnimationScreen(
|
||||
paymentResponse: paymentResponseFuture),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user