// accountsListProvider.dart import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:generp/Notifiers/HomeScreenNotifier.dart'; import 'package:generp/Utils/commonServices.dart'; import 'package:generp/screens/commom/accountsListDetails.dart'; import 'package:generp/screens/finance/financeDashboard.dart'; import 'package:generp/services/api_calling.dart'; import 'package:provider/provider.dart'; import '../../Models/commonModels/DistrictsResponse.dart'; import '../../Models/commonModels/SubLocationsResponse.dart'; import '../../Models/commonModels/commonAccountlistResponse.dart'; import '../../Models/commonModels/commonAddAccountsViewResponse.dart'; import '../../Models/financeModels/ValidateBankAccountDetailsResponse.dart'; import '../../Models/financeModels/ValidateGstNumResponse.dart'; class Accountslistprovider extends ChangeNotifier { TextEditingController nameController = TextEditingController(); TextEditingController mobileController = TextEditingController(); TextEditingController addressController = TextEditingController(); TextEditingController bankNameController = TextEditingController(); TextEditingController branchNameController = TextEditingController(); TextEditingController bankIfscCotroller = TextEditingController(); TextEditingController bankHolderNameController = TextEditingController(); TextEditingController gstNumberController = TextEditingController(); TextEditingController bankAcNumberController = TextEditingController(); TextEditingController bankUpiController = TextEditingController(); TextEditingController contactPersonController = TextEditingController(); TextEditingController contectPersonDesignationController = TextEditingController(); TextEditingController contectPersonAltMobController = TextEditingController(); TextEditingController contectPersonTeleController = TextEditingController(); TextEditingController contectPersonMailController = TextEditingController(); TextEditingController mobileNumberController = TextEditingController(); TextEditingController companyNameController = TextEditingController(); TextEditingController stateSearchController = TextEditingController(); TextEditingController districtSearchController = TextEditingController(); TextEditingController subLocSearchController = TextEditingController(); String? accountError; String? nameError; String? mobileError; String? stateError; String? districtError; String? localityError; String? addressError; String? gstNumberError; String? banknameError; String? bankBranchError; String? bankIFSCError; String? bankHolderNameError; String? bankAcNumberError; String? upiError; String? contactPersonError; String? desigantionError; String? altMobError; String? teleError; String? mailError; String? addMoreDetailsError; List _states = []; List _districts = []; List _subLocations = []; List _accountTypes = []; List _accountsList = []; bool _isLoading = true; bool _hasMoreData = true; bool get isLoading => _isLoading; bool get hasMoreData => _hasMoreData; int _pageNum = 1; int get pageNum => _pageNum; set pageNum(value) { _pageNum = value; notifyListeners(); } bool _isVisible = false; List get states => _states; List get districts => _districts; List get subLocations => _subLocations; List get accountTypes => _accountTypes; List get accountsList => _accountsList; bool get isVisible => _isVisible; set isVisible(bool value) { _isVisible = value; if (value == true) { addMoreDetailsError = null; } notifyListeners(); } ValidateGstNumResponse? gstResponse; ValidateBankAccountDetailsResponse? bankResponse; String? errorMessage; States? _selectedState; Districts? _selectedDistricts; SubLocations? _selectedSubLocations; String? _selectedAccountType; String? _selectedDistrictID; String? _selectedDistrictValue; String? _selectedStateID; String? _selectedStateValue; String? _selectedSubLocID; String? _selectedSubLocValue; States? get selectedState => _selectedState; String? get selectedStateID => _selectedStateID; String? get selectedStateValue => _selectedStateValue; Districts? get selectedDistricts => _selectedDistricts; String? get selectedDistrictId => _selectedDistrictID; String? get selectedDistrictValue => _selectedDistrictValue; SubLocations? get selectedSubLocations => _selectedSubLocations; String? get selectedSubLocID => _selectedSubLocID; String? get selectedSubLocValue => _selectedSubLocValue; String? get selectedAccountType => _selectedAccountType; // --- NEW: setter for isLoading so callers can do `isLoading = true;` --- set isLoading(bool value) { _isLoading = value; notifyListeners(); } set selectedAccountType(String? value) { _selectedAccountType = value; accountError = null; notifyListeners(); } set selectedState(States? value) { _selectedState = value; _selectedStateID = value?.id; _selectedStateValue = value?.name; stateError = null; notifyListeners(); } set selectedDistricts(Districts? value) { _selectedDistricts = value; _selectedDistrictID = value?.id; _selectedDistrictValue = value?.district; districtError = null; notifyListeners(); } set selectedSubLocations(SubLocations? value) { _selectedSubLocations = value; _selectedSubLocID = value?.id; _selectedSubLocValue = value?.subLocality; localityError = null; notifyListeners(); } set selectedStateID(value) { _selectedStateID = value; notifyListeners(); } set selectedStateValue(value) { _selectedStateID = value; notifyListeners(); } set selectedDistrictId(value) { _selectedDistrictID = value; notifyListeners(); } set selectedDistrictValue(value) { _selectedDistrictValue = value; notifyListeners(); } set selectedSubLocID(value) { _selectedSubLocID = value; notifyListeners(); } set selectedSubLocValue(value) { _selectedSubLocValue = value; notifyListeners(); } // ---------- API / data loading ---------- Future addCommonAccountViewAPI(context) async { try { var homeProv = Provider.of(context, listen: false); final data = await ApiCalling.commonAddAccountViewAPI( homeProv.empId, homeProv.session, ); _accountTypes.clear(); _states.clear(); notifyListeners(); if (data != null) { if (data.error == "0") { ChechkDropdownValues(); _accountTypes = data.accountTypes ?? []; _states = data.states ?? []; notifyListeners(); } } } catch (e) { // keep quiet but log in debug if (kDebugMode) debugPrint('addCommonAccountViewAPI error: $e'); } } Future getDistrictAPI(context, stateID) async { try { var homeProv = Provider.of(context, listen: false); _districts.clear(); notifyListeners(); final data = await ApiCalling.commonAddAccountViewDistrictAPI( homeProv.empId, homeProv.session, stateID, ); if (data != null) { if (data.error == "0") { _districts = data.districts ?? []; notifyListeners(); } } } catch (e) { if (kDebugMode) debugPrint('getDistrictAPI error: $e'); } } Future getSubLocationAPI(context, districtID) async { try { var homeProv = Provider.of(context, listen: false); _subLocations.clear(); notifyListeners(); final data = await ApiCalling.commonAddAccountViewSubLocationAPI( homeProv.empId, homeProv.session, districtID, ); if (data != null) { if (data.error == "0") { _subLocations = data.subLocations ?? []; notifyListeners(); } } } catch (e) { if (kDebugMode) debugPrint('getSubLocationAPI error: $e'); } } Future checkInputsAPI(context, type, value) async { try { var homeProv = Provider.of(context, listen: false); final data = await ApiCalling.commonAddAccountCheckInputsAPI( homeProv.empId, homeProv.session, type, value, ); if (data != null) { if (data.error == "0") { nameError = null; mobileError = null; notifyListeners(); } else if (data.error == "1") { if (data.message?.contains("name already exists") ?? false) { nameError = data.message ?? ""; } else { mobileError = data.message ?? ""; } notifyListeners(); } } } catch (e) { if (kDebugMode) debugPrint('checkInputsAPI error: $e'); } } /// Validate GST Number /// Returns true if validated successfully (and sets gstResponse). Caller can await. Future validateGstNumber( String empId, String sessionId, String gstNumber) async { // if empty — don't call API; clear any previous errors/responses if (gstNumber.trim().isEmpty) { gstResponse = null; gstNumberError = null; notifyListeners(); return false; } try { isLoading = true; errorMessage = null; // call API final response = await ApiCalling.validateGstNumberApi( empId, sessionId, gstNumber, ); // assign gstResponse = response; if (response == null) { gstNumberError = "Failed to validate GST number"; notifyListeners(); return false; } // Response object expected to contain `error` and `address` and `legal_name_of_business` if (response.error == "0") { gstNumberError = null; // Optionally update address or other fields in UI (UI will read gstResponse) notifyListeners(); return true; } else { // API returned invalid GST gstNumberError = response.message ?? "Invalid GST number"; notifyListeners(); return false; } } catch (e) { gstNumberError = "Error validating GST: ${e.toString()}"; if (kDebugMode) debugPrint('validateGstNumber error: $e'); notifyListeners(); return false; } finally { isLoading = false; notifyListeners(); } } /// Validate Bank Account Details /// Returns true if validated successfully (and sets bankResponse). Caller can await. Future validateBankDetails( String empId, String sessionId, String accountNumber) async { // require accountNumber and IFSC to be present final ifsc = bankIfscCotroller.text.trim(); if (accountNumber.trim().isEmpty || ifsc.isEmpty) { // clear previous bankResponse if user cleared input bankResponse = null; bankAcNumberError = null; bankIFSCError = null; notifyListeners(); return false; } // basic IFSC format check (caller/UI also checks) — we trust UI but double-check final reg = RegExp(r'^[A-Za-z]{4}0[A-Za-z0-9]{6}$'); if (!reg.hasMatch(ifsc)) { bankIFSCError = "Invalid IFSC format"; notifyListeners(); return false; } try { isLoading = true; errorMessage = null; // call API - pass account number and IFSC from controller (some APIs require both) final response = await ApiCalling.validateBankAccountDetailsApi( empId, sessionId, accountNumber, ifsc, ); bankResponse = response; if (response == null) { bankAcNumberError = "Failed to validate bank account"; notifyListeners(); return false; } if (response.error == "0") { // success — populate fields in UI from the provider.bankResponse (UI will take the values) bankAcNumberError = null; bankIFSCError = null; banknameError = null; bankBranchError = null; bankHolderNameError = null; notifyListeners(); return true; } else { // error returned bankAcNumberError = response.message ?? "Invalid account details"; notifyListeners(); return false; } } catch (e) { bankAcNumberError = "Error validating account: ${e.toString()}"; if (kDebugMode) debugPrint('validateBankDetails error: $e'); notifyListeners(); return false; } finally { isLoading = false; notifyListeners(); } } bool hasFilledAdditionalDetails = false; bool _submitClicked = false; bool get submitClickced => _submitClicked; set submitClickced(bool value) { _submitClicked = value; notifyListeners(); } Future submitCommonAccountsAPI(context, from) async { try { if (!validatereceiptForm(context)) { return; } _submitClicked = true; notifyListeners(); var homeProv = Provider.of(context, listen: false); final data = await ApiCalling.commonAddAccountSubmitAPI( homeProv.empId, homeProv.session, _selectedAccountType, nameController.text, mobileController.text, contectPersonAltMobController.text, contectPersonTeleController.text, contectPersonMailController.text, contectPersonDesignationController.text, addressController.text, _selectedStateID, _selectedDistrictID, _selectedSubLocID, bankNameController.text, branchNameController.text, bankIfscCotroller.text, bankHolderNameController.text, bankAcNumberController.text, bankUpiController.text, ); if (data != null) { if (data.error == "0") { _submitClicked = false; notifyListeners(); var res = data.id!; if (from == "Requesition") { Navigator.pop(context, res); } else if (from == "Dashboard") { Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (context) => Accountslistdetails(accountID: data.id), ), (Route route) { return route.settings.arguments == 'Financedashboard'; }, ); } resetValues(); toast(context, data.message); } else if (data.error == "1") { _submitClicked = false; notifyListeners(); toast(context, data.message); } else { _submitClicked = false; notifyListeners(); } } else { _submitClicked = false; notifyListeners(); } } catch (e) { _submitClicked = false; notifyListeners(); } } bool validateStep1() { accountError = null; nameError = null; mobileError = null; contactPersonError = null; bool isValid = true; if (_selectedAccountType == null || _selectedAccountType!.isEmpty) { accountError = "Please select an Account"; isValid = false; } if (nameController.text.trim().isEmpty) { nameError = "Please Enter a Name"; isValid = false; } if (mobileController.text.trim().isNotEmpty && mobileController.text.length < 10) { mobileError = "Mobile Number should be 10 digits"; isValid = false; } if (mobileController.text.trim().isEmpty) { mobileError = "Please Enter Mobile Number"; isValid = false; } if (contactPersonController.text.trim().isEmpty) { contactPersonError = "Please Enter Contact Person Name"; isValid = false; } _submitClicked = false; notifyListeners(); return isValid; } bool validateStep2() { stateError = null; districtError = null; localityError = null; addressError = null; bool isValid = true; // optional step — you commented it optional. Keep commented validations deactivated. _submitClicked = false; notifyListeners(); return isValid; } bool validateStep3() { banknameError = null; bankBranchError = null; bankIFSCError = null; bankHolderNameError = null; bankAcNumberError = null; upiError = null; gstNumberError = null; bool isValid = true; _submitClicked = false; notifyListeners(); return isValid; } bool validateStep4() { bool isValid = true; _submitClicked = false; notifyListeners(); return isValid; } bool validatereceiptForm(BuildContext context) { accountError = null; nameError = null; mobileError = null; stateError = null; districtError = null; localityError = null; addressError = null; banknameError = null; gstNumberError = null; bankBranchError = null; bankIFSCError = null; bankHolderNameError = null; bankAcNumberError = null; upiError = null; contactPersonError = null; desigantionError = null; altMobError = null; teleError = null; mailError = null; addMoreDetailsError = null; bool isValid = true; if (_selectedAccountType == null || _selectedAccountType!.isEmpty) { accountError = "Please select an Account"; isValid = false; } if (nameController.text.trim().isEmpty) { nameError = "Please Enter a Name"; isValid = false; } if (mobileController.text.trim().isEmpty) { mobileError = "Please Enter Mobile Number"; isValid = false; } if (contactPersonController.text.trim().isEmpty) { contactPersonError = "Please Enter Contact Person Name"; isValid = false; } _submitClicked = false; notifyListeners(); return isValid; } void updateName(String value) { nameError = null; notifyListeners(); } void updateMobile(String value) { mobileError = null; notifyListeners(); } void updateAddress(String value) { addressError = null; notifyListeners(); } void updateBankName(String value) { banknameError = null; notifyListeners(); } /// simplified update - kept for compatibility if UI uses it Future updateGSTNumber(BuildContext context, String value) async { // We won't call the GST API here — UI flow calls validateGstNumber explicitly. // This function simply clears previous error on typing. gstNumberError = null; notifyListeners(); } void updateBankBranch(String value) { bankBranchError = null; notifyListeners(); } void updateIFSC(String value) { bankIFSCError = null; notifyListeners(); } void updateHolder(String value) { bankHolderNameError = null; notifyListeners(); } void updateNumber(String value) { bankAcNumberError = null; notifyListeners(); } void updateUPI(String value) { upiError = null; notifyListeners(); } void updateContactPerson(String value) { contactPersonError = null; notifyListeners(); } void updateDesignation(String value) { desigantionError = null; notifyListeners(); } void updateAltMobile(String value) { altMobError = null; notifyListeners(); } void updateTeleMobile(String value) { teleError = null; notifyListeners(); } void updateMail(String value) { mailError = null; notifyListeners(); } void onChangedMobileNum(value) { notifyListeners(); } void onChangedCompanyName(value) { notifyListeners(); } // Common Account list provider Future commonAccountListAPIFunction( BuildContext context, { bool append = false, }) async { try { var prov = Provider.of(context, listen: false); if (!append) { // Fresh load / Refresh _accountsList.clear(); _pageNum = 1; _hasMoreData = true; _isLoading = true; } else { _isLoading = true; } notifyListeners(); final data = await ApiCalling.commonAccountListAPI( prov.empId, prov.session, _pageNum, companyNameController.text, mobileNumberController.text, ); debugPrint('empId: ${prov.empId}, session: ${prov.session}, pageNumber: $_pageNum'); if (data != null && data.error == "0") { if (data.accountList != null) { if (append) { final existingIds = _accountsList.map((e) => e.id).toSet(); final newItems = data.accountList!.where((item) => !existingIds.contains(item.id)).toList(); _accountsList.addAll(newItems); } else { _accountsList = data.accountList!; } // Check if we still have more records _hasMoreData = data.accountList!.length >= 10; if (_hasMoreData) _pageNum++; } } } catch (e) { debugPrint("Error in commonAccountListAPIFunction: $e"); } finally { _isLoading = false; notifyListeners(); } } resetValues() { if (kDebugMode) debugPrint("rv"); _selectedAccountType = null; _selectedState = null; _selectedDistricts = null; _selectedSubLocations = null; _selectedStateID = null; _selectedStateValue = null; _selectedDistrictID = null; _selectedDistrictValue = null; _selectedSubLocID = null; _selectedSubLocValue = null; gstResponse = null; bankResponse = null; errorMessage = null; _isVisible = false; stateSearchController.clear(); districtSearchController.clear(); subLocSearchController.clear(); nameController.clear(); mobileController.clear(); addressController.clear(); bankNameController.clear(); branchNameController.clear(); bankIfscCotroller.clear(); bankHolderNameController.clear(); bankAcNumberController.clear(); bankUpiController.clear(); contactPersonController.clear(); contectPersonDesignationController.clear(); contectPersonAltMobController.clear(); contectPersonTeleController.clear(); contectPersonMailController.clear(); mobileNumberController.clear(); companyNameController.clear(); accountError = null; nameError = null; mobileError = null; stateError = null; districtError = null; localityError = null; addressError = null; banknameError = null; gstNumberError = null; bankBranchError = null; bankIFSCError = null; bankHolderNameError = null; bankAcNumberError = null; upiError = null; contactPersonError = null; desigantionError = null; altMobError = null; teleError = null; mailError = null; addMoreDetailsError = null; ChechkDropdownValues(); notifyListeners(); } void ChechkDropdownValues() { if (!_accountTypes.contains(_selectedAccountType) && _selectedAccountType != null) { _selectedAccountType = null; } if (!_states.contains(_selectedState) && _selectedState != null) { _selectedStateID = null; _selectedStateValue = null; } if (!_districts.contains(_selectedDistricts) && _selectedDistricts != null) { _selectedDistrictID = null; _selectedDistrictValue = null; } if (!_subLocations.contains(_selectedSubLocations) && _selectedSubLocations != null) { _selectedSubLocID = null; _selectedSubLocValue = null; } notifyListeners(); } }