diff --git a/lib/api/services/deposit_service.dart b/lib/api/services/deposit_service.dart new file mode 100644 index 0000000..eb33019 --- /dev/null +++ b/lib/api/services/deposit_service.dart @@ -0,0 +1,175 @@ +import 'dart:developer'; +import 'package:dio/dio.dart'; + +class DepositService{ + final Dio _dio; + + DepositService(this._dio); + + Future fetchaccountdetails({ + required String accountno, + }) async { + try { + final response = await _dio.post( + "/api/deposit/req/deposit", + data: { + 'accountNo': accountno, + }, + options: Options( + headers: { + "Content-Type": "application/json", + }, + ), + ); + + log("PMY Details Response: ${response.data}"); + + if (response.statusCode == 200) { + return response.data; + } else { + throw Exception("INTERNAL SERVER ERROR"); + } + } catch (e) { + log("Error fetching Account details: $e"); + return null; + } + } + + Future createFdTd({ + String? fromacctno, + String? cifNo, + String? idno, + String? customername, + String? nationality, + String? addressline1, + String? addressline2, + String? pincode, + String? product, + String? type, + String? customercategory, + String? termlocation, + String? currency, + String? acctsgmtcode, + String? interestpaymentmethod, + String? taxfilenumberindicator, + String? termlenght, + String? termbasis, + String? termvaluedeposited, + String? interestfrequency, + String? termdays, + String? termmonths, + String? termyears, + String? nominationRequired, + String? printNomineename, + required String tpin, + }) async { + final response = await _dio.post( + '/api/deposit/create/fd-td', + options: Options( + validateStatus: (int? status) => true, + receiveDataWhenStatusError: true, + ), + data: { + "fromacctno": fromacctno, + "cifNo": cifNo, + "idno": idno, + "customername": customername, + "nationality": nationality, + "addressline1": addressline1, + "addressline2": addressline2, + "pincode": pincode, + "product": product, + "type": type, + "customercategory": customercategory, + "termlocation": termlocation, + "currency": currency, + "acctsgmtcode": acctsgmtcode, + "interestpaymentmethod": interestpaymentmethod, + "taxfilenumberindicator": taxfilenumberindicator, + "termlenght": termlenght, + "termbasis": termbasis, + "termvaluedeposited": termvaluedeposited, + "interestfrequency": interestfrequency, + "termdays": termdays, + "termmonths": termmonths, + "termyears": termyears, + "nominationRequired": nominationRequired, + "printNomineename": printNomineename, + 'tpin': tpin, + }, + ); + return response.data; + } + + Future createRd({ + String? fromacctno, + String? cifNo, + String? idno, + String? customername, + String? nationality, + String? addressline1, + String? addressline2, + String? pincode, + String? product, + String? type, + String? customercategory, + String? termlocation, + String? currency, + String? acctsgmtcode, + String? interestpaymentmethod, + String? taxfilenumberindicator, + String? termlenght, + String? termbasis, + String? termvaluedeposited, + String? interestfrequency, + String? termdays, + String? termmonths, + String? termyears, + String? nominationRequired, + String? printNomineename, + String? rdinstallmentvalue, + String? monthlyRDInstallmentdueday, + String? rdInstlFreq, + required String tpin, + }) async { + final response = await _dio.post( + '/api/deposit/create/rd', + options: Options( + validateStatus: (int? status) => true, + receiveDataWhenStatusError: true, + ), + data: { + "fromacctno": fromacctno, + "cifNo": cifNo, + "idno": idno, + "customername": customername, + "nationality": nationality, + "addressline1": addressline1, + "addressline2": addressline2, + "pincode": pincode, + "product": product, + "type": type, + "customercategory": customercategory, + "termlocation": termlocation, + "currency": currency, + "acctsgmtcode": acctsgmtcode, + "interestpaymentmethod": interestpaymentmethod, + "taxfilenumberindicator": taxfilenumberindicator, + "termlenght": termlenght, + "termbasis": termbasis, + "termvaluedeposited": termvaluedeposited, + "interestfrequency": interestfrequency, + "termdays": termdays, + "termmonths": termmonths, + "termyears": termyears, + "nominationRequired": nominationRequired, + "printNomineename": printNomineename, + "rdinstallmentvalue": rdinstallmentvalue, + "monthlyRDInstallmentdueday": monthlyRDInstallmentdueday, + "rdInstlFreq": rdInstlFreq, + 'tpin': tpin, + }, + ); + return response.data; + } +} \ No newline at end of file diff --git a/lib/di/injection.dart b/lib/di/injection.dart index 9a78c06..59d62d8 100644 --- a/lib/di/injection.dart +++ b/lib/di/injection.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:kmobile/api/services/branch_service.dart'; import 'package:kmobile/api/services/cheque_service.dart'; +import 'package:kmobile/api/services/deposit_service.dart'; import 'package:kmobile/api/services/limit_service.dart'; import 'package:kmobile/api/services/rtgs_service.dart'; import 'package:kmobile/api/services/neft_service.dart'; @@ -60,6 +61,7 @@ Future setupDependencies() async { getIt.registerSingleton(BranchService(getIt())); getIt.registerSingleton(ChequeService(getIt())); getIt.registerSingleton(YojnaService(getIt())); + getIt.registerSingleton(DepositService(getIt())); getIt.registerLazySingleton( () => ChangePasswordService(getIt()), ); diff --git a/lib/features/account_opening/screens/account_opening_screen.dart b/lib/features/account_opening/screens/account_opening_screen.dart index b25b1c0..39bc06d 100644 --- a/lib/features/account_opening/screens/account_opening_screen.dart +++ b/lib/features/account_opening/screens/account_opening_screen.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:kmobile/api/services/deposit_service.dart'; import 'package:kmobile/data/models/user.dart'; +import 'package:kmobile/di/injection.dart'; import 'package:kmobile/l10n/app_localizations.dart'; import 'package:kmobile/features/account_opening/screens/create_deposit_screen.dart'; @@ -117,26 +119,50 @@ class _AccountOpeningScreenState extends State { : () async { if (_formKey.currentState!.validate() && _selectedAccount != null) { - // Simulating API response - final mockResponse = { - "addressline1": "DATA CENTRE KCCB CIVIL BAZAR D/SHALA DHA", - "addressline2": "RAMSHALA KANGRA", - "cifNo": "30022497139", - "customername": "Mr. RAJAT KUMAR MAHARANA", - "idno": "536652226333 61", - "nationality": "IN", - "pincode": "176047" - }; + setState(() { + _isLoading = true; + }); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => CreateDepositScreen( - selectedAccount: _selectedAccount!, - initialData: mockResponse, - ), - ), - ); + try { + final response = await getIt() + .fetchaccountdetails( + accountno: + _selectedAccount!.accountNo.toString()); + + if (response != null) { + if (mounted) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => CreateDepositScreen( + selectedAccount: _selectedAccount!, + initialData: response, + ), + ), + ); + } + } else { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + "Failed to fetch account details")), + ); + } + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Error: $e")), + ); + } + } finally { + if (mounted) { + setState(() { + _isLoading = false; + }); + } + } } }, style: ElevatedButton.styleFrom( diff --git a/lib/features/account_opening/screens/create_deposit_screen.dart b/lib/features/account_opening/screens/create_deposit_screen.dart index 3a55ad2..c63c560 100644 --- a/lib/features/account_opening/screens/create_deposit_screen.dart +++ b/lib/features/account_opening/screens/create_deposit_screen.dart @@ -1,5 +1,9 @@ +import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; +import 'package:kmobile/api/services/deposit_service.dart'; import 'package:kmobile/data/models/user.dart'; +import 'package:kmobile/di/injection.dart'; +import 'package:kmobile/features/fund_transfer/screens/transaction_pin_screen.dart'; import 'package:kmobile/l10n/app_localizations.dart'; class CreateDepositScreen extends StatefulWidget { @@ -17,6 +21,7 @@ class CreateDepositScreen extends StatefulWidget { class _CreateDepositScreenState extends State { final _formKey = GlobalKey(); + bool _isLoading = false; // Controllers late final _fromAcctNoController = @@ -171,13 +176,245 @@ class _CreateDepositScreenState extends State { void _handleCreate() { if (_formKey.currentState!.validate()) { - // Implementation logic goes here - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text("Processing Deposit Creation...")), + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => TransactionPinScreen( + onPinCompleted: (ctx, pin) async { + Navigator.pop(context); + _executeCreation(pin); + }, + ), + ), ); } } + void _executeCreation(String pin) async { + setState(() { + _isLoading = true; + }); + + try { + final product = _productController.text; + dynamic response; + + if (product == '20' || product == '25') { + response = await getIt().createFdTd( + fromacctno: _fromAcctNoController.text, + cifNo: _cifNoController.text, + idno: _idNoController.text, + customername: _customerNameController.text, + nationality: _nationalityController.text, + addressline1: _addressLine1Controller.text, + addressline2: _addressLine2Controller.text, + pincode: _pincodeController.text, + product: _productController.text, + type: _typeController.text, + customercategory: _customerCategoryController.text, + termlocation: _termLocationController.text, + currency: _currencyController.text, + acctsgmtcode: _acctSgmtCodeController.text, + interestpaymentmethod: _interestPaymentMethodController.text, + taxfilenumberindicator: _taxFileNumberIndicatorController.text, + termlenght: _termLengthController.text, + termbasis: _termBasisController.text, + termvaluedeposited: _termValueDepositedController.text, + interestfrequency: _interestFrequencyController.text, + termdays: _termDaysController.text, + termmonths: _termMonthsController.text, + termyears: _termYearsController.text, + nominationRequired: _nominationRequiredController.text, + printNomineename: _printNomineeNameController.text, + tpin: pin, + ); + } else if (product == '28') { + response = await getIt().createRd( + fromacctno: _fromAcctNoController.text, + cifNo: _cifNoController.text, + idno: _idNoController.text, + customername: _customerNameController.text, + nationality: _nationalityController.text, + addressline1: _addressLine1Controller.text, + addressline2: _addressLine2Controller.text, + pincode: _pincodeController.text, + product: _productController.text, + type: _typeController.text, + customercategory: _customerCategoryController.text, + termlocation: _termLocationController.text, + currency: _currencyController.text, + acctsgmtcode: _acctSgmtCodeController.text, + interestpaymentmethod: _interestPaymentMethodController.text, + taxfilenumberindicator: _taxFileNumberIndicatorController.text, + termlenght: _termLengthController.text, + termbasis: _termBasisController.text, + termvaluedeposited: _termValueDepositedController.text, + interestfrequency: _interestFrequencyController.text, + termdays: _termDaysController.text, + termmonths: _termMonthsController.text, + termyears: _termYearsController.text, + nominationRequired: _nominationRequiredController.text, + printNomineename: _printNomineeNameController.text, + rdinstallmentvalue: _rdInstallmentValueController.text, + monthlyRDInstallmentdueday: _monthlyRDInstallmentDueDayController.text, + rdInstlFreq: _rdInstlFreqController.text, + tpin: pin, + ); + } + + if (response != null && response is Map) { + if (mounted) { + if (response['error'] == 'INCORRECT_TPIN' || response['message'] == 'INCORRECT_TPIN') { + _showSimpleDialog('Invalid TPIN', 'The TPIN you entered is incorrect. Please try again.'); + } else if (response['status'] == 'FAILED') { + _showFailureDialog(response['message']?.toString()); + } else if (response.containsKey('accountno')) { + _showResponseDialog(response); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Unexpected response from server")), + ); + } + } + } else { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Failed to create deposit")), + ); + } + } + } on DioException catch (e) { + if (mounted) { + try { + final errorBody = e.response?.data; + if (errorBody is Map && (errorBody['error'] == 'INCORRECT_TPIN' || errorBody['message'] == 'INCORRECT_TPIN')) { + _showSimpleDialog('Invalid TPIN', 'The TPIN you entered is incorrect. Please try again.'); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Error: ${e.message}")), + ); + } + } catch (_) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Internal Server Error")), + ); + } + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Error: $e")), + ); + } + } finally { + if (mounted) { + setState(() { + _isLoading = false; + }); + } + } + } + + Future _showSimpleDialog(String title, String message) async { + return showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return AlertDialog( + title: Text(title), + content: SingleChildScrollView( + child: ListBody( + children: [ + Text(message), + ], + ), + ), + actions: [ + TextButton( + child: const Text("OK"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + + void _showFailureDialog(String? apiMessage) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Row( + children: [ + Icon(Icons.error_outline, color: Theme.of(context).colorScheme.error), + const SizedBox(width: 8), + const Text("Creation Failed"), + ], + ), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "Account creation could not be completed at this time.", + style: TextStyle(fontWeight: FontWeight.bold), + ), + const SizedBox(height: 12), + if (apiMessage != null) ...[ + Text("Reason: $apiMessage"), + const SizedBox(height: 12), + ], + const Text( + "Please ensure that the provided details (term length, basis, interest frequency, etc.) are correct and compatible with the selected product. If the issue persists, please contact the branch.", + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text("OK"), + ), + ], + ), + ); + } + + void _showResponseDialog(Map response) { + final accountNo = response['accountno']?.toString() ?? 'N/A'; + final accountTypeRaw = response['accounttype']?.toString() ?? ''; + final accountTypeText = _productOptions[accountTypeRaw] ?? accountTypeRaw; + final amount = response['amount']?.toString() ?? '0.0'; + + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => AlertDialog( + title: const Text("Deposit Created Successfully"), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Account No: $accountNo"), + const SizedBox(height: 8), + Text("Account Type: $accountTypeText"), + const SizedBox(height: 8), + Text("Amount: $amount"), + ], + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).popUntil((route) => route.isFirst); + }, + child: const Text("OK"), + ), + ], + ), + ); + } + @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); @@ -280,7 +517,8 @@ class _CreateDepositScreenState extends State { mandatory: true), _buildTextField(_termValueDepositedController, "Term Value Deposited", - keyboardType: TextInputType.number, mandatory: true), + keyboardType: TextInputType.number, + mandatory: _productController.text != '28'), _buildDropdownField(_interestFrequencyController, "Interest Frequency", _interestFrequencyOptions), _buildTextField(_termDaysController, "Term Days", @@ -327,7 +565,7 @@ class _CreateDepositScreenState extends State { const SizedBox(height: 24), ElevatedButton( - onPressed: _handleCreate, + onPressed: _isLoading ? null : _handleCreate, style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.primaryContainer, @@ -339,10 +577,18 @@ class _CreateDepositScreenState extends State { borderRadius: BorderRadius.circular(8), ), ), - child: const Text( - "Proceed", - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), + child: _isLoading + ? const SizedBox( + height: 20, + width: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + ), + ) + : const Text( + "Proceed", + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), ), const SizedBox(height: 32), ], @@ -364,7 +610,7 @@ class _CreateDepositScreenState extends State { padding: const EdgeInsets.only(bottom: 16.0), child: DropdownButtonFormField( value: currentValue, - onChanged: readOnly + onChanged: readOnly || _isLoading ? null : (newValue) { if (newValue != null) { @@ -407,8 +653,8 @@ class _CreateDepositScreenState extends State { padding: const EdgeInsets.only(bottom: 16.0), child: TextFormField( controller: controller, - readOnly: readOnly || onTap != null, - onTap: onTap, + readOnly: readOnly || onTap != null || _isLoading, + onTap: _isLoading ? null : onTap, decoration: InputDecoration( labelText: mandatory ? '$label *' : label, border: const OutlineInputBorder(), diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 78128ef..c4191b4 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -36,7 +36,7 @@ "accountStatement": "Account \n Statement", "handleCheque": "Handle \n Cheque", "manageBeneficiary": "Manage \n Beneficiary", - "contactUs": "Contact \n Us", + "contactUs": "Contact Us", "addBeneficiary": "Add Beneficiary", "confirmAccountNumber": "Confirm Account Number", "name": "Name", diff --git a/lib/l10n/app_hi.arb b/lib/l10n/app_hi.arb index 761fc1c..87c1198 100644 --- a/lib/l10n/app_hi.arb +++ b/lib/l10n/app_hi.arb @@ -36,7 +36,7 @@ "accountStatement": "खाता \n विवरण", "handleCheque": "चेक \n संभालें", "manageBeneficiary": "लाभार्थी \n प्रबंधन", - "contactUs": "संपर्क \n करें", + "contactUs": "संपर्क करें", "addBeneficiary": "लाभार्थी जोड़ें", "confirmAccountNumber": "खाता संख्या की पुष्टि करें", "name": "नाम",