Commit 7ba7402c authored by Sai Srinivas's avatar Sai Srinivas Committed by Sai Srinivas
Browse files

Edit form and some fixes added

parent d2939607
...@@ -428,7 +428,8 @@ class MyApp extends StatelessWidget { ...@@ -428,7 +428,8 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => CasualLeaveHistoryProvider()), ChangeNotifierProvider(create: (_) => CasualLeaveHistoryProvider()),
ChangeNotifierProvider(create: (_) => ContactProvider()), ChangeNotifierProvider(create: (_) => ContactProvider()),
ChangeNotifierProvider(create: (_) => QrProvider()), ChangeNotifierProvider(create: (_) => QrProvider()),
ChangeNotifierProvider(create: (_) => CrmNearByGeneratorsProvider()),
ChangeNotifierProvider(create: (_) => EditCommonAccountProvider()),
], ],
child: Builder( child: Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
......
...@@ -117,8 +117,18 @@ class _AccountslistState extends State<Accountslist> { ...@@ -117,8 +117,18 @@ class _AccountslistState extends State<Accountslist> {
return (connection == "Online") return (connection == "Online")
? Platform.isAndroid ? Platform.isAndroid
? WillPopScope( ? WillPopScope(
onWillPop: () => onBackPressed(context), onWillPop: () async {
child: SafeArea( onBackPressed(context);
_refreshList(context);
final provider = Provider.of<Accountslistprovider>(context, listen: false);
provider.resetValues();
provider.commonAccountListAPIFunction(context);
// Return true or false depending on whether you want to allow the pop
return true; // allow the back navigation
// return false; // prevent back navigation
},
child: SafeArea(
top: false, top: false,
bottom: true, bottom: true,
child: _scaffold(context), child: _scaffold(context),
......
...@@ -8,11 +8,13 @@ import 'package:generp/Notifiers/commonProvider/accountDetailsProvider.dart'; ...@@ -8,11 +8,13 @@ import 'package:generp/Notifiers/commonProvider/accountDetailsProvider.dart';
import 'package:generp/Utils/app_colors.dart'; import 'package:generp/Utils/app_colors.dart';
import 'package:generp/Utils/commonServices.dart'; import 'package:generp/Utils/commonServices.dart';
import 'package:generp/Utils/commonWidgets.dart'; import 'package:generp/Utils/commonWidgets.dart';
import 'package:generp/screens/commom/editCommonAccount.dart';
import 'package:generp/screens/commom/transactionDetails.dart'; import 'package:generp/screens/commom/transactionDetails.dart';
import 'package:generp/screens/finance/submitPaymentRequestionListsByMode.dart'; import 'package:generp/screens/finance/submitPaymentRequestionListsByMode.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../Models/commonModels/commonAccountdetailsResponse.dart'; import '../../Models/commonModels/commonAccountdetailsResponse.dart';
import 'addCommonPayment.dart';
class Accountslistdetails extends StatefulWidget { class Accountslistdetails extends StatefulWidget {
final accountID; final accountID;
...@@ -131,733 +133,762 @@ class _AccountslistdetailsState extends State<Accountslistdetails> { ...@@ -131,733 +133,762 @@ class _AccountslistdetailsState extends State<Accountslistdetails> {
), ),
resizeToAvoidBottomInset: true, resizeToAvoidBottomInset: true,
backgroundColor: AppColors.scaffold_bg_color, backgroundColor: AppColors.scaffold_bg_color,
body: SizedBox( body: Builder(builder: (context){
child: SingleChildScrollView( if (provider.isLoading) {
child: Column( return const Center(
crossAxisAlignment: CrossAxisAlignment.start, child: CircularProgressIndicator(color: Colors.blue),
children: [ );
Card( }
shape: RoundedRectangleBorder( return SizedBox(
borderRadius: BorderRadius.only( child: SingleChildScrollView(
bottomLeft: Radius.circular(30), child: Column(
bottomRight: Radius.circular(30), crossAxisAlignment: CrossAxisAlignment.start,
children: [
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30),
),
), ),
), elevation: 2,
elevation: 2,
child: Column( child: Column(
children: [ children: [
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.vertical( borderRadius: BorderRadius.vertical(
bottom: Radius.circular(30), bottom: Radius.circular(30),
),
),
// margin: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
padding: EdgeInsets.symmetric(
vertical: 10,
horizontal: 10,
),
child: Column(
children: [
Row(
children: [
Expanded(
flex: 1,
child: SizedBox(
height: 50,
width: 35,
child: SvgPicture.asset(
"assets/svg/crm/lead_details_ic.svg",
),
),
),
SizedBox(width: 10),
Expanded(
flex: 5,
child: SizedBox(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
provider.accountDetails.name ?? "-",
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
color: AppColors.semi_black,
),
),
Text(
provider.accountDetails.type ?? "-",
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
color: AppColors.grey_semi,
),
),
],
),
),
),
SizedBox(width: 10),
],
), ),
Visibility( ),
visible: // margin: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
provider.showMoreDetails ? true : false, padding: EdgeInsets.symmetric(
child: Column( vertical: 10,
horizontal: 10,
),
child: Column(
children: [
Row(
children: [ children: [
Container( Expanded(
padding: EdgeInsets.symmetric( flex: 1,
vertical: 4, child: SizedBox(
height: 50,
width: 35,
child: SvgPicture.asset(
"assets/svg/crm/lead_details_ic.svg",
),
), ),
child: Row( ),
children: [ SizedBox(width: 10),
Expanded( Expanded(
flex: 3, flex: 5,
child: Text( child: SizedBox(
"Bank Details", child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
provider.accountDetails.name ?? "-",
style: TextStyle( style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14, fontSize: 14,
fontFamily: "JakartaSemiBold", color: AppColors.semi_black,
), ),
), ),
), Text(
Expanded( provider.accountDetails.type ?? "-",
flex: 6, style: TextStyle(
child: DottedLine( fontFamily: "JakartaMedium",
dashGapLength: 4, fontSize: 14,
dashGapColor: Colors.white, color: AppColors.grey_semi,
dashColor: AppColors.grey_semi, ),
dashLength: 2,
lineThickness: 0.5,
), ),
],
),
),
),
SizedBox(width: 2),
Expanded(
flex: 1,
child: InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => EditCommonAccountScreen(accountID: widget.accountID,))
).then((_) async {
var prov = Provider.of<Accountdetailsprovider>(context, listen: false);
await prov.accountdetailsAPIFunction(context, widget.accountID);
});
},
child: SizedBox(
height: 25,
width: 25,
child: SvgPicture.asset(
"assets/svg/crm_contact_edit.svg",
), ),
], ),
), ),
), ),
...List.generate(subHeadings1.length, (j) { ],
return Container( ),
Visibility(
visible:
provider.showMoreDetails ? true : false,
child: Column(
children: [
Container(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
vertical: 7, vertical: 4,
), ),
child: Row( child: Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [ children: [
Expanded( Expanded(
flex: 3,
child: Text( child: Text(
headings1[j], "Bank Details",
style: TextStyle( style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 14, fontSize: 14,
color: AppColors.semi_black, fontFamily: "JakartaSemiBold",
), ),
), ),
), ),
Expanded( Expanded(
child: Text( flex: 6,
subHeadings1[j] == "" child: DottedLine(
? "-" dashGapLength: 4,
: subHeadings1[j], dashGapColor: Colors.white,
style: TextStyle( dashColor: AppColors.grey_semi,
fontSize: 14, dashLength: 2,
color: Color(0xFF818181), lineThickness: 0.5,
),
), ),
), ),
], ],
), ),
);
}),
Container(
padding: EdgeInsets.symmetric(
vertical: 4,
), ),
child: Row( ...List.generate(subHeadings1.length, (j) {
children: [ return Container(
Expanded( padding: EdgeInsets.symmetric(
flex: 3, vertical: 7,
child: Text(
"Address Details",
style: TextStyle(
fontSize: 14,
fontFamily: "JakartaSemiBold",
),
),
), ),
Expanded( child: Row(
flex: 6, crossAxisAlignment:
child: DottedLine( CrossAxisAlignment.start,
dashGapLength: 4, children: [
dashGapColor: Colors.white, Expanded(
dashColor: AppColors.grey_semi, child: Text(
dashLength: 2, headings1[j],
lineThickness: 0.5, style: TextStyle(
), fontFamily: "JakartaRegular",
fontSize: 14,
color: AppColors.semi_black,
),
),
),
Expanded(
child: Text(
subHeadings1[j] == ""
? "-"
: subHeadings1[j],
style: TextStyle(
fontSize: 14,
color: Color(0xFF818181),
),
),
),
],
), ),
], );
), }),
), Container(
...List.generate(subHeadings2.length, (j) {
return Container(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
vertical: 7, vertical: 4,
), ),
child: Row( child: Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [ children: [
Expanded( Expanded(
flex: 3,
child: Text( child: Text(
headings2[j], "Address Details",
style: TextStyle( style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 14, fontSize: 14,
color: AppColors.semi_black, fontFamily: "JakartaSemiBold",
), ),
), ),
), ),
Expanded( Expanded(
child: Text( flex: 6,
subHeadings2[j] == "" child: DottedLine(
? "-" dashGapLength: 4,
: subHeadings2[j], dashGapColor: Colors.white,
style: TextStyle( dashColor: AppColors.grey_semi,
fontSize: 14, dashLength: 2,
color: Color(0xFF818181), lineThickness: 0.5,
),
), ),
), ),
], ],
), ),
);
}),
Container(
padding: EdgeInsets.symmetric(
vertical: 4,
), ),
child: Row( ...List.generate(subHeadings2.length, (j) {
children: [ return Container(
Expanded( padding: EdgeInsets.symmetric(
flex: 3, vertical: 7,
child: Text(
"More Details",
style: TextStyle(
fontSize: 14,
fontFamily: "JakartaSemiBold",
),
),
), ),
Expanded( child: Row(
flex: 6, crossAxisAlignment:
child: DottedLine( CrossAxisAlignment.start,
dashGapLength: 4, children: [
dashGapColor: Colors.white, Expanded(
dashColor: AppColors.grey_semi, child: Text(
dashLength: 2, headings2[j],
lineThickness: 0.5, style: TextStyle(
), fontFamily: "JakartaRegular",
fontSize: 14,
color: AppColors.semi_black,
),
),
),
Expanded(
child: Text(
subHeadings2[j] == ""
? "-"
: subHeadings2[j],
style: TextStyle(
fontSize: 14,
color: Color(0xFF818181),
),
),
),
],
), ),
], );
), }),
), Container(
...List.generate(subHeadings3.length, (j) {
return Container(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
vertical: 7, vertical: 4,
), ),
child: Row( child: Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [ children: [
Expanded( Expanded(
flex: 3,
child: Text( child: Text(
headings3[j], "More Details",
style: TextStyle( style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 14, fontSize: 14,
color: AppColors.semi_black, fontFamily: "JakartaSemiBold",
), ),
), ),
), ),
Expanded( Expanded(
child: Text( flex: 6,
subHeadings3[j] == "" child: DottedLine(
? "-" dashGapLength: 4,
: subHeadings3[j], dashGapColor: Colors.white,
style: TextStyle( dashColor: AppColors.grey_semi,
fontSize: 14, dashLength: 2,
color: Color(0xFF818181), lineThickness: 0.5,
),
), ),
), ),
], ],
), ),
); ),
}), ...List.generate(subHeadings3.length, (j) {
], return Container(
padding: EdgeInsets.symmetric(
vertical: 7,
),
child: Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
headings3[j],
style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 14,
color: AppColors.semi_black,
),
),
),
Expanded(
child: Text(
subHeadings3[j] == ""
? "-"
: subHeadings3[j],
style: TextStyle(
fontSize: 14,
color: Color(0xFF818181),
),
),
),
],
),
);
}),
],
),
), ),
),
InkResponse( InkResponse(
onTap: () async { onTap: () async {
provider.showMoreDetails = provider.showMoreDetails =
!provider.showMoreDetails; !provider.showMoreDetails;
}, },
child: Container( child: Container(
padding: EdgeInsets.symmetric(vertical: 5), padding: EdgeInsets.symmetric(vertical: 5),
child: Row( child: Row(
crossAxisAlignment: crossAxisAlignment:
CrossAxisAlignment.center, CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( Text(
provider.showMoreDetails provider.showMoreDetails
? "Hide Details" ? "Hide Details"
: "View Details", : "View Details",
style: TextStyle( style: TextStyle(
fontFamily: "JakartaMedium", fontFamily: "JakartaMedium",
fontSize: 14, fontSize: 14,
color: AppColors.app_blue, color: AppColors.app_blue,
),
), ),
), Transform.flip(
Transform.flip( flipY:
flipY: provider.showMoreDetails
provider.showMoreDetails ? true
? true : false,
: false, child: SvgPicture.asset(
child: SvgPicture.asset( "assets/svg/arrow_dropdown.svg",
"assets/svg/arrow_dropdown.svg", height: 25,
height: 25, width: 20,
width: 20, color: AppColors.app_blue,
color: AppColors.app_blue, ),
), ),
), ],
], ),
), ),
), ),
), ],
], ),
), ),
), ],
], ),
), ),
),
Container( Container(
margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10), margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
child: Column( child: Column(
children: [ children: [
Container( Container(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: 10, horizontal: 10,
vertical: 15, vertical: 15,
), ),
margin: EdgeInsets.symmetric( margin: EdgeInsets.symmetric(
horizontal: 10, horizontal: 10,
vertical: 15, vertical: 15,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Color(0xFFEDF7FF), color: Color(0xFFEDF7FF),
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( Text(
provider.balanceDetails.balance ?? "-", provider.balanceDetails.balance ?? "-",
style: TextStyle(
fontSize: 20,
fontFamily: "JakartaMedium",
color: AppColors.app_blue,
),
),
],
),
Container(
padding: EdgeInsets.only(top: 5),
child: Text(
"Total Balance",
maxLines: 1,
style: TextStyle( style: TextStyle(
fontSize: 20, fontSize: 14,
fontFamily: "JakartaMedium", fontFamily: "JakartaRegular",
color: AppColors.app_blue, color: AppColors.semi_black,
), ),
), ),
],
),
Container(
padding: EdgeInsets.only(top: 5),
child: Text(
"Total Balance",
maxLines: 1,
style: TextStyle(
fontSize: 14,
fontFamily: "JakartaRegular",
color: AppColors.semi_black,
),
), ),
), ],
], ),
), ),
),
Row( Row(
children: [ children: [
...List.generate(2, (jj) { ...List.generate(2, (jj) {
final texts = ["Credited", "Debited"]; final texts = ["Credited", "Debited"];
final heads = [ final heads = [
provider.balanceDetails.totalCredit ?? "-", provider.balanceDetails.totalCredit ?? "-",
provider.balanceDetails.totalDebit ?? "-", provider.balanceDetails.totalDebit ?? "-",
]; ];
final svgs = [ final svgs = [
"assets/svg/finance/cred_debit_ic.svg", "assets/svg/finance/cred_debit_ic.svg",
"assets/svg/finance/deb_credit_ic.svg", "assets/svg/finance/deb_credit_ic.svg",
]; ];
return Expanded( return Expanded(
child: Container( child: Container(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: 10, horizontal: 10,
vertical: 15, vertical: 15,
), ),
margin: EdgeInsets.symmetric( margin: EdgeInsets.symmetric(
horizontal: 10, horizontal: 10,
vertical: 15, vertical: 15,
),
decoration: BoxDecoration(
color:
texts[jj] == "Credited"
? Color(0xFFFFEFEF)
: Color(0xFFE7FFE5),
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
heads[jj] ?? "-",
maxLines: 2,
style: TextStyle(
fontSize: 20,
color:
texts[jj] == "Credited"
? Color(0xFFEF3739)
: Color(0xFF0D9C00),
fontFamily: "JakartaMedium",
),
),
Container(
padding: EdgeInsets.only(top: 5),
child: Row(
children: [
Expanded(
flex: 3,
child: Text(
texts[jj] ?? "-",
maxLines: 1,
style: TextStyle(
fontSize: 14,
fontFamily: "JakartaRegular",
color: AppColors.semi_black,
),
),
),
Expanded(
flex: 1,
child: SvgPicture.asset(svgs[jj]),
),
],
),
),
],
),
), ),
decoration: BoxDecoration( );
color: }),
texts[jj] == "Credited" ],
? Color(0xFFFFEFEF) ),
: Color(0xFFE7FFE5), ],
borderRadius: BorderRadius.circular(16), ),
),
ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: provider.ledgerList.length,
itemBuilder: (context, index) {
double runningBalance = 0;
int currentIndex = provider.ledgerList.indexOf(
provider.ledgerList[index],
);
for (var i = 0; i <= currentIndex; i++) {
var ledgerItem = provider.ledgerList[i];
double credit =
double.tryParse(
ledgerItem.creditAmount.toString(),
) ??
0;
double debit =
double.tryParse(
ledgerItem.debitAmount.toString(),
) ??
0;
runningBalance += (debit - credit);
}
return InkResponse(
onTap: () async {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => Transactiondetails(
paymentID: provider.ledgerList[index].refId,
type: provider.ledgerList[index].type,
description:
provider.ledgerList[index].description,
),
),
);
},
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
margin: EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 1,
child: SizedBox(
child:
provider.ledgerList[index].type ==
"Credit"
? SvgPicture.asset(
"assets/svg/finance/debited_filled_ic.svg",
height: 45,
width: 45,
fit: BoxFit.contain,
)
: SvgPicture.asset(
"assets/svg/finance/credited_filled_ic.svg",
height: 45,
width: 45,
fit: BoxFit.contain,
),
), ),
child: Column( ),
crossAxisAlignment: SizedBox(width: 10),
CrossAxisAlignment.start, Expanded(
children: [ flex: 8,
Text( child: SizedBox(
heads[jj] ?? "-", child: Column(
maxLines: 2, children: [
style: TextStyle( Row(
fontSize: 20, crossAxisAlignment:
color: CrossAxisAlignment.start,
texts[jj] == "Credited"
? Color(0xFFEF3739)
: Color(0xFF0D9C00),
fontFamily: "JakartaMedium",
),
),
Container(
padding: EdgeInsets.only(top: 5),
child: Row(
children: [ children: [
Expanded(
flex: 5,
child: SizedBox(
child: Text(
provider
.ledgerList[index]
.description ??
"-",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 14,
),
),
),
),
Spacer(),
Expanded( Expanded(
flex: 3, flex: 3,
child: Text( child: SizedBox(
texts[jj] ?? "-", child: RichText(
maxLines: 1, maxLines: 1,
style: TextStyle( textAlign: TextAlign.right,
fontSize: 14, overflow: TextOverflow.ellipsis,
fontFamily: "JakartaRegular", text: TextSpan(
color: AppColors.semi_black, children: [
TextSpan(
text:
provider
.ledgerList[index]
.type ==
"Credit"
? "-"
: "+",
style: TextStyle(
color:
provider
.ledgerList[index]
.type ==
"Credit"
? Color(
0xFFEF3739,
)
: Color(
0xFF0D9C00,
),
fontSize: 14,
fontFamily:
"JakartaRegular",
),
),
TextSpan(
text:
"₹${provider.ledgerList[index].type == "Credit" ? "${provider.ledgerList[index].creditAmount}" : "${provider.ledgerList[index].debitAmount}"}",
style: TextStyle(
color:
provider
.ledgerList[index]
.type ==
"Credit"
? Color(
0xFFEF3739,
)
: Color(
0xFF0D9C00,
),
fontSize: 14,
fontFamily:
"JakartaRegular",
),
),
],
),
), ),
), ),
), ),
],
),
SizedBox(height: 7),
Row(
children: [
Expanded( Expanded(
flex: 1, flex: 5,
child: SvgPicture.asset(svgs[jj]), child: SizedBox(
child: Text(
provider
.ledgerList[index]
.date ??
"-",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontFamily: "JakartaRegular",
color: AppColors.grey_semi,
fontSize: 12,
),
),
),
), ),
Spacer(),
// Expanded(
// flex: 3,
// child: SizedBox(
// child: RichText(
// maxLines: 1,
// textAlign: TextAlign.right,
// overflow: TextOverflow.ellipsis,
// text: TextSpan(
// children: [
// TextSpan(
// text: "Bal: ",
// style: TextStyle(
// color:
// AppColors
// .semi_black,
// fontSize: 12,
// fontFamily:
// "JakartaRegular",
// ),
// ),
// TextSpan(
// text: "$runningBalance",
// style: TextStyle(
// color:
// AppColors.grey_semi,
// fontSize: 12,
// fontFamily:
// "JakartaMedium",
// ),
// ),
// ],
// ),
// ),
// ),
// ),
], ],
), ),
), ],
], ),
), ),
), ),
); ],
}), ),
], ),
), );
], },
), ),
), if ([
"Vendor",
ListView.builder( "Customer",
physics: NeverScrollableScrollPhysics(), ].contains(provider.accountDetails.type)) ...[
shrinkWrap: true, InkWell(
itemCount: provider.ledgerList.length, onTap: () {
itemBuilder: (context, index) {
double runningBalance = 0;
int currentIndex = provider.ledgerList.indexOf(
provider.ledgerList[index],
);
for (var i = 0; i <= currentIndex; i++) {
var ledgerItem = provider.ledgerList[i];
double credit =
double.tryParse(
ledgerItem.creditAmount.toString(),
) ??
0;
double debit =
double.tryParse(
ledgerItem.debitAmount.toString(),
) ??
0;
runningBalance += (debit - credit);
}
return InkResponse(
onTap: () async {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: builder:
(context) => Transactiondetails( (context) => Submitpaymentrequestionlistsbymode(
paymentID: provider.ledgerList[index].refId, mode: "other",
type: provider.ledgerList[index].type, pageTitleName: "Add Payment Request (Other)",
description: accountId: provider.accountDetails.id!,
provider.ledgerList[index].description, ),
),
), ),
); );
}, },
child: Container( child: Container(
padding: EdgeInsets.symmetric( alignment: Alignment.center,
horizontal: 10, height: 45,
vertical: 10, margin: EdgeInsets.only(
left: 5.0,
right: 5.0,
top: 5.0,
bottom: 5.0,
), ),
margin: EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: AppColors.app_blue, //1487C9
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(15.0),
), ),
child: Row( child: Center(
mainAxisAlignment: MainAxisAlignment.start, child: Text(
crossAxisAlignment: CrossAxisAlignment.start, "Add Payment Request (Other)",
children: [ textAlign: TextAlign.center,
Expanded( style: TextStyle(
flex: 1, color: Colors.white,
child: SizedBox( fontFamily: "JakartaMedium",
child: fontSize: 15,
provider.ledgerList[index].type ==
"Credit"
? SvgPicture.asset(
"assets/svg/finance/debited_filled_ic.svg",
height: 45,
width: 45,
fit: BoxFit.contain,
)
: SvgPicture.asset(
"assets/svg/finance/credited_filled_ic.svg",
height: 45,
width: 45,
fit: BoxFit.contain,
),
),
),
SizedBox(width: 10),
Expanded(
flex: 8,
child: SizedBox(
child: Column(
children: [
Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Expanded(
flex: 5,
child: SizedBox(
child: Text(
provider
.ledgerList[index]
.description ??
"-",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 14,
),
),
),
),
Spacer(),
Expanded(
flex: 3,
child: SizedBox(
child: RichText(
maxLines: 1,
textAlign: TextAlign.right,
overflow: TextOverflow.ellipsis,
text: TextSpan(
children: [
TextSpan(
text:
provider
.ledgerList[index]
.type ==
"Credit"
? "-"
: "+",
style: TextStyle(
color:
provider
.ledgerList[index]
.type ==
"Credit"
? Color(
0xFFEF3739,
)
: Color(
0xFF0D9C00,
),
fontSize: 14,
fontFamily:
"JakartaRegular",
),
),
TextSpan(
text:
"₹${provider.ledgerList[index].type == "Credit" ? "${provider.ledgerList[index].creditAmount}" : "${provider.ledgerList[index].debitAmount}"}",
style: TextStyle(
color:
provider
.ledgerList[index]
.type ==
"Credit"
? Color(
0xFFEF3739,
)
: Color(
0xFF0D9C00,
),
fontSize: 14,
fontFamily:
"JakartaRegular",
),
),
],
),
),
),
),
],
),
SizedBox(height: 7),
Row(
children: [
Expanded(
flex: 5,
child: SizedBox(
child: Text(
provider
.ledgerList[index]
.date ??
"-",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontFamily: "JakartaRegular",
color: AppColors.grey_semi,
fontSize: 12,
),
),
),
),
Spacer(),
Expanded(
flex: 3,
child: SizedBox(
child: RichText(
maxLines: 1,
textAlign: TextAlign.right,
overflow: TextOverflow.ellipsis,
text: TextSpan(
children: [
TextSpan(
text: "Bal: ",
style: TextStyle(
color:
AppColors
.semi_black,
fontSize: 12,
fontFamily:
"JakartaRegular",
),
),
TextSpan(
text: "$runningBalance",
style: TextStyle(
color:
AppColors.grey_semi,
fontSize: 12,
fontFamily:
"JakartaMedium",
),
),
],
),
),
),
),
],
),
],
),
),
), ),
],
),
),
);
},
),
if ([
"Vendor",
"Customer",
].contains(provider.accountDetails.type)) ...[
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => Submitpaymentrequestionlistsbymode(
mode: "other",
pageTitleName: "Add Payment Request (Other)",
accountId: provider.accountDetails.id!,
),
),
);
},
child: Container(
alignment: Alignment.center,
height: 45,
margin: EdgeInsets.only(
left: 5.0,
right: 5.0,
top: 5.0,
bottom: 5.0,
),
decoration: BoxDecoration(
color: AppColors.app_blue, //1487C9
borderRadius: BorderRadius.circular(15.0),
),
child: Center(
child: Text(
"Add Payment Request (Other)",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontFamily: "JakartaMedium",
fontSize: 15,
), ),
), ),
), ),
), ),
), ],
], ],
], ),
), ),
), );
), }
)
); );
}, },
); );
......
...@@ -31,7 +31,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> { ...@@ -31,7 +31,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
Dropdowntheme ddtheme = Dropdowntheme(); Dropdowntheme ddtheme = Dropdowntheme();
int _currentStep = 0; int _currentStep = 0;
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
List<FocusNode> focusNodes = List.generate(20, (index) => FocusNode()); final List<FocusNode> focusNodes = List.generate(30, (index) => FocusNode());
Map _source = {ConnectivityResult.mobile: true}; Map _source = {ConnectivityResult.mobile: true};
final MyConnectivity _connectivity = MyConnectivity.instance; final MyConnectivity _connectivity = MyConnectivity.instance;
...@@ -274,7 +274,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> { ...@@ -274,7 +274,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
// custom continue logic: // custom continue logic:
if (_currentStep == 0) { if (_currentStep == 0) {
// validate step1 via provider // validate step1 via provider
if (provider.validateStep1()) { if (provider.validateStep1(context)) {
setState(() => _currentStep = 1); setState(() => _currentStep = 1);
} else { } else {
// show error (provider sets errors and notifies) // show error (provider sets errors and notifies)
...@@ -337,7 +337,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> { ...@@ -337,7 +337,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
setState(() => _currentStep = 0); setState(() => _currentStep = 0);
} else if (value == 1) { } else if (value == 1) {
// user wants to jump to step 1 - ensure step 0 valid // user wants to jump to step 1 - ensure step 0 valid
if (provider.validateStep1()) { if (provider.validateStep1(context)) {
setState(() => _currentStep = 1); setState(() => _currentStep = 1);
} else { } else {
provider.notifyListeners(); provider.notifyListeners();
...@@ -345,7 +345,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> { ...@@ -345,7 +345,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
} }
} else if (value == 2) { } else if (value == 2) {
// allow jump if step0 valid; step1 optional // allow jump if step0 valid; step1 optional
if (provider.validateStep1()) { if (provider.validateStep1(context)) {
setState(() => _currentStep = 2); setState(() => _currentStep = 2);
} else { } else {
provider.notifyListeners(); provider.notifyListeners();
...@@ -353,7 +353,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> { ...@@ -353,7 +353,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
} }
} else if (value == 3) { } else if (value == 3) {
// final - require step0 valid // final - require step0 valid
if (!provider.validateStep1()) { if (!provider.validateStep1(context)) {
provider.notifyListeners(); provider.notifyListeners();
setState(() => _currentStep = 0); setState(() => _currentStep = 0);
return; return;
...@@ -440,9 +440,12 @@ class _AddcommonpaymentState extends State<Addcommonpayment> { ...@@ -440,9 +440,12 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
"Company Name", "Company Name",
"Enter Company Name", "Enter Company Name",
(p0) { (p0) {
provider.updateName(p0); provider.updateName(p0);
provider.checkInputsAPI(context, "name", provider.nameController.text); provider.checkInputsAPI(context, "name", provider.nameController.text);
}, // Recompare with GST response if exists
provider.recheckNameWithGst();
},
TextInputType.text, TextInputType.text,
false, false,
null, null,
...@@ -708,52 +711,120 @@ class _AddcommonpaymentState extends State<Addcommonpayment> { ...@@ -708,52 +711,120 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// GST field - if provided, validate and autofill address; compare company name // GST field (unique focus node 9). Only one GST widget here.
textControllerWidget( Text("GST Number"),
context, const SizedBox(height: 6),
provider.gstNumberController, TextFormField(
"GST Number", controller: provider.gstNumberController,
"Enter GST Number", focusNode: focusNodes[9],
(val) async { textInputAction: TextInputAction.done,
// when user types, just update; validation on field submit keyboardType: TextInputType.text,
provider.gstNumberController.text = val; style: TextStyle(
provider.notifyListeners(); height: 1.2,
color: Colors.black87,
backgroundColor: AppColors.text_field_color,
),
decoration: InputDecoration(
hintText: "Enter GST Number",
filled: true,
hintStyle: TextStyle(
height: 1.2,
backgroundColor: AppColors.text_field_color,
color: AppColors.grey_semi,
),
fillColor: AppColors.text_field_color,
contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide.none,
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide.none,
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide.none,
),
),
onChanged: (value) {
// only clear GST error when user edits
provider.updateGSTNumber(value);
final trimmed = value.trim();
if (trimmed.isEmpty) {
// user cleared GST -> clear response + errors
provider.gstResponse = null;
provider.gstNumberError = null;
provider.nameError = null; // also clear mismatch error, if any
provider.notifyListeners();
return;
}
// OPTIONAL: auto-validate once full 15-char GST typed
if (trimmed.length == 15) {
provider.checkAndApplyGst(context, trimmed);
}
},
onFieldSubmitted: (value) async {
final trimmed = value.trim();
if (trimmed.isNotEmpty) {
await provider.checkAndApplyGst(context, trimmed);
}
// move focus to Bank Account Number
FocusScope.of(context).requestFocus(focusNodes[10]);
}, },
TextInputType.text,
false,
null,
focusNodes[9],
focusNodes[10],
TextInputAction.next,
null,
// onEditingComplete / onFieldSubmitted, we will validate
), ),
// Validate GST when user leaves the field (on editing complete)
// To do that we use FocusNode
const SizedBox(height: 4), const SizedBox(height: 4),
errorWidget(context, provider.gstNumberError), errorWidget(context, provider.gstNumberError),
// Bank Account Number
const SizedBox(height: 4),
// errorWidget(context, provider.gstNumberError),
// Bank Account Number (use same style as other fields)
textControllerWidget( textControllerWidget(
context, context,
provider.bankAcNumberController, provider.bankAcNumberController,
"Bank Account Number", "Bank Account Number",
"Enter Bank Account Number", "Enter Bank Account Number",
(p0) { (p0) {
// update provider value and trigger bank validation if IFSC already present
provider.updateNumber(p0); provider.updateNumber(p0);
// attempt validation only when IFSC present // If account entered but IFSC empty -> provider will set IFSC required error via checkAndApplyBank
if (provider.bankIfscCotroller.text.trim().isNotEmpty) { if (provider.bankIfscCotroller.text.trim().isNotEmpty) {
_validateBankIfNeeded(provider); // validate once IFSC is present
provider.checkAndApplyBank(context, p0.trim());
} else {
// If account present but IFSC empty, show IFSC required error
if (p0.trim().isNotEmpty) {
provider.bankIFSCError = "IFSC is required when account number is entered";
provider.notifyListeners();
} else {
provider.bankIFSCError = null;
provider.notifyListeners();
}
} }
}, },
TextInputType.number, TextInputType.number,
false, false,
FilteringTextInputFormatter.digitsOnly, FilteringTextInputFormatter.digitsOnly,
focusNodes[10], focusNodes[10], // bank account unique
focusNodes[11], focusNodes[8], // move to IFSC next
TextInputAction.next, TextInputAction.next,
), ),
const SizedBox(height: 4),
errorWidget(context, provider.bankAcNumberError), errorWidget(context, provider.bankAcNumberError),
// IFSC
// Bank IFSC
textControllerWidget( textControllerWidget(
context, context,
provider.bankIfscCotroller, provider.bankIfscCotroller,
...@@ -761,28 +832,46 @@ class _AddcommonpaymentState extends State<Addcommonpayment> { ...@@ -761,28 +832,46 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
"Enter Bank IFSC", "Enter Bank IFSC",
(p0) { (p0) {
provider.updateIFSC(p0); provider.updateIFSC(p0);
// validate IFSC format locally; if good and account present, trigger server validation // local IFSC format check
if (_isIfscValidFormat(p0) && provider.bankAcNumberController.text.trim().isNotEmpty) { final reg = RegExp(r'^[A-Za-z]{4}0[A-Za-z0-9]{6}$');
_validateBankIfNeeded(provider); if (p0.trim().isEmpty) {
} else { // if account exists and IFSC empty -> show required error
if (p0.trim().isNotEmpty && !_isIfscValidFormat(p0)) { if (provider.bankAcNumberController.text.trim().isNotEmpty) {
provider.bankIFSCError = "Invalid IFSC format"; provider.bankIFSCError = "IFSC is required when account number is entered";
provider.notifyListeners(); provider.notifyListeners();
} else { } else {
provider.bankIFSCError = null; provider.bankIFSCError = null;
provider.notifyListeners(); provider.notifyListeners();
} }
return;
}
if (!reg.hasMatch(p0.trim())) {
provider.bankIFSCError = "Invalid IFSC format";
provider.notifyListeners();
return;
} else {
provider.bankIFSCError = null;
provider.notifyListeners();
}
// if acc and IFSC both present and valid -> call bank API
if (provider.bankAcNumberController.text.trim().isNotEmpty) {
provider.checkAndApplyBank(context, provider.bankAcNumberController.text.trim());
} }
}, },
TextInputType.text, TextInputType.text,
false, false,
null, null,
focusNodes[8], focusNodes[8], // unique IFSC focus
focusNodes[9], focusNodes[11], // next focus after IFSC (holder / or UPI)
TextInputAction.next, TextInputAction.next,
), ),
const SizedBox(height: 4),
errorWidget(context, provider.bankIFSCError), errorWidget(context, provider.bankIFSCError),
// Bank Name (autofill) const SizedBox(height: 8),
// Bank Name (autofill by API) (unique focus node 6)
textControllerWidget( textControllerWidget(
context, context,
provider.bankNameController, provider.bankNameController,
...@@ -797,7 +886,10 @@ class _AddcommonpaymentState extends State<Addcommonpayment> { ...@@ -797,7 +886,10 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
TextInputAction.next, TextInputAction.next,
), ),
errorWidget(context, provider.banknameError), errorWidget(context, provider.banknameError),
// Branch
const SizedBox(height: 8),
// Branch (unique focus node 7)
textControllerWidget( textControllerWidget(
context, context,
provider.branchNameController, provider.branchNameController,
...@@ -808,11 +900,14 @@ class _AddcommonpaymentState extends State<Addcommonpayment> { ...@@ -808,11 +900,14 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
false, false,
null, null,
focusNodes[7], focusNodes[7],
focusNodes[8], focusNodes[11],
TextInputAction.next, TextInputAction.next,
), ),
errorWidget(context, provider.bankBranchError), errorWidget(context, provider.bankBranchError),
// Holder
const SizedBox(height: 8),
// Bank Holder Name (unique focus node 11) — previously incorrectly used 9
textControllerWidget( textControllerWidget(
context, context,
provider.bankHolderNameController, provider.bankHolderNameController,
...@@ -822,26 +917,30 @@ class _AddcommonpaymentState extends State<Addcommonpayment> { ...@@ -822,26 +917,30 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
TextInputType.text, TextInputType.text,
false, false,
null, null,
focusNodes[9], focusNodes[11], // <-- corrected unique index
focusNodes[10],
TextInputAction.next,
),
errorWidget(context, provider.bankHolderNameError),
// UPI
textControllerWidget(
context,
provider.bankUpiController,
"Bank UPI ID",
"Enter Bank UPI ID",
provider.updateUPI,
TextInputType.text,
false,
null,
focusNodes[11],
focusNodes[12], focusNodes[12],
TextInputAction.next, TextInputAction.next,
), ),
errorWidget(context, provider.upiError), errorWidget(context, provider.bankHolderNameError),
const SizedBox(height: 8),
// Bank UPI (unique focus node 12)
// Bank UPI (use different focus nodes than Contact Person)
textControllerWidget(
context,
provider.bankUpiController,
"Bank UPI ID",
"Enter Bank UPI ID",
provider.updateUPI,
TextInputType.text,
false,
null,
focusNodes[17], //
focusNodes[18], // next focus
TextInputAction.next,
),
errorWidget(context, provider.upiError),
], ],
), ),
), ),
...@@ -973,6 +1072,23 @@ class _AddcommonpaymentState extends State<Addcommonpayment> { ...@@ -973,6 +1072,23 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
return; return;
} }
if (provider.validateStep1(context)) {
// move to first error step
final errStep = _firstErrorStep(provider);
if (errStep != null) {
setState(() {
_currentStep = errStep;
});
}else{
provider.submitClickced = true;
provider.notifyListeners();
await provider.submitCommonAccountsAPI(context, widget.from);
}
// ensure UI updated
provider.notifyListeners();
return;
}
// All good => submit // All good => submit
provider.submitClickced = true; provider.submitClickced = true;
provider.notifyListeners(); provider.notifyListeners();
...@@ -994,7 +1110,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> { ...@@ -994,7 +1110,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
onTap: () { onTap: () {
setState(() { setState(() {
if (_currentStep == 0) { if (_currentStep == 0) {
if (provider.validateStep1()) { if (provider.validateStep1(context)) {
_currentStep = 1; _currentStep = 1;
} else { } else {
provider.notifyListeners(); provider.notifyListeners();
......
// edit_common_account_screen.dart
import 'dart:async';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:generp/Notifiers/HomeScreenNotifier.dart';
import 'package:generp/Notifiers/commonProvider/editCommonAccountProvider.dart';
import 'package:generp/Utils/app_colors.dart';
import 'package:generp/Utils/commonWidgets.dart';
import 'package:generp/Utils/dropdownTheme.dart';
import '../../Models/commonModels/DistrictsResponse.dart';
import '../../Models/commonModels/SubLocationsResponse.dart';
import '../../Models/commonModels/commonAddAccountsViewResponse.dart';
class EditCommonAccountScreen extends StatefulWidget {
final String accountID;
const EditCommonAccountScreen({super.key, required this.accountID});
@override
State<EditCommonAccountScreen> createState() => _EditCommonAccountScreenState();
}
class _EditCommonAccountScreenState extends State<EditCommonAccountScreen> {
Timer? _nameDebounce;
Timer? _gstDebounce;
final _formKey = GlobalKey<FormState>();
// Focus nodes
late List<FocusNode> focusNodes;
static const int FN_ACCOUNT = 0;
static const int FN_NAME = 1;
static const int FN_NAME_NEXT = 2;
static const int FN_MOBILE = 3;
static const int FN_CONTACT = 4;
static const int FN_GST = 5;
static const int FN_STATE = 6;
static const int FN_DISTRICT = 7;
static const int FN_SUBLOC = 8;
static const int FN_ADDRESS = 9;
static const int FN_BANK_ACC = 10;
static const int FN_IFSC = 11;
static const int FN_BANK_NAME = 12;
static const int FN_BANK_BRANCH = 13;
static const int FN_BANK_HOLDER = 14;
static const int FN_UPI = 15;
static const int FN_CONTACT_DESIG = 16;
static const int FN_CONTACT_ALTMOB = 17;
static const int FN_CONTACT_TELE = 18;
static const int FN_CONTACT_MAIL = 19;
int _currentStep = 0;
final Dropdowntheme ddtheme = Dropdowntheme();
@override
void initState() {
super.initState();
// create enough focus nodes (0..19)
focusNodes = List.generate(20, (_) => FocusNode());
WidgetsBinding.instance.addPostFrameCallback((_) {
final prov = Provider.of<EditCommonAccountProvider>(context, listen: false);
// ensure only once: reset then load
prov.resetValues();
prov.loadAccountDetailsForEdit(context, widget.accountID);
});
}
@override
void dispose() {
for (final fn in focusNodes) {
try {
fn.dispose();
} catch (_) {}
}
_nameDebounce?.cancel();
_gstDebounce?.cancel();
// reset provider values when leaving screen to avoid stale state
final prov = Provider.of<EditCommonAccountProvider>(context, listen: false);
prov.resetValues();
super.dispose();
}
// small helper to check IFSC simple pattern
bool _isIfscValidFormat(String ifsc) {
final reg = RegExp(r'^[A-Za-z]{4}0[A-Za-z0-9]{6}$');
return reg.hasMatch(ifsc.trim());
}
bool _isGstValidFormat(String gst) {
gst = gst.trim().toUpperCase();
final reg = RegExp(r'^[0-3][0-9][A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$');
return reg.hasMatch(gst);
}
Future<void> _validateGstIfNeeded(EditCommonAccountProvider provider) async {
final gst = provider.gstNumberController.text.trim();
if (!_isGstValidFormat(gst)) {
provider.gstNumberError = "Invalid GST format";
provider.notifyListeners();
return;
}
if (gst.isEmpty) return;
final homeProv = Provider.of<HomescreenNotifier>(context, listen: false);
provider.isLoading = true;
await provider.validateGstNumber(homeProv.empId, homeProv.session, gst);
provider.isLoading = false;
}
Future<void> _validateBankIfNeeded(EditCommonAccountProvider provider) async {
final acc = provider.bankAcNumberController.text.trim();
final ifsc = provider.bankIfscCotroller.text.trim();
final accList = provider.detailsListResponse?.accountList!.first;
if (acc.isEmpty && ifsc.isEmpty) return;
if (acc.isNotEmpty && ifsc.isEmpty) {
provider.bankIFSCError = "IFSC is required when account number is entered";
provider.notifyListeners();
return;
}
if (acc.isEmpty && ifsc.isEmpty) {
provider.bankNameController.text = accList?.bankName ?? "";
provider.branchNameController.text = accList?.bankBranchName ?? "";
provider.bankHolderNameController.text = accList?.bankAccountHolderName ?? "";
provider.notifyListeners();
return;
}
if (!_isIfscValidFormat(ifsc)) {
provider.bankIFSCError = "Invalid IFSC format";
provider.notifyListeners();
return;
}
final homeProv = Provider.of<HomescreenNotifier>(context, listen: false);
provider.isLoading = true;
await provider.validateBankDetails(homeProv.empId, homeProv.session, acc);
provider.isLoading = false;
// If validateBankDetails succeeded provider.bankResponse will be set and UI autofills
if (provider.bankResponse != null && provider.bankResponse!.error == "0") {
provider.bankNameController.text = provider.bankResponse!.bankName ?? provider.bankNameController.text;
provider.branchNameController.text = provider.bankResponse!.branch ?? provider.branchNameController.text;
provider.bankHolderNameController.text = provider.bankResponse!.nameAtBank ?? provider.bankHolderNameController.text;
provider.notifyListeners();
}
}
int? _firstErrorStep(EditCommonAccountProvider p) {
if ((p.accountError?.isNotEmpty ?? false) ||
(p.nameError?.isNotEmpty ?? false) ||
(p.mobileError?.isNotEmpty ?? false) ||
(p.contactPersonError?.isNotEmpty ?? false)) {
return 0;
}
if ((p.stateError?.isNotEmpty ?? false) ||
(p.districtError?.isNotEmpty ?? false) ||
(p.localityError?.isNotEmpty ?? false) ||
(p.addressError?.isNotEmpty ?? false)) {
return 1;
}
if ((p.gstNumberError?.isNotEmpty ?? false) ||
(p.bankAcNumberError?.isNotEmpty ?? false) ||
(p.bankIFSCError?.isNotEmpty ?? false) ||
(p.banknameError?.isNotEmpty ?? false) ||
(p.bankBranchError?.isNotEmpty ?? false) ||
(p.bankHolderNameError?.isNotEmpty ?? false) ||
(p.upiError?.isNotEmpty ?? false)) {
return 2;
}
if ((p.desigantionError?.isNotEmpty ?? false) ||
(p.altMobError?.isNotEmpty ?? false) ||
(p.teleError?.isNotEmpty ?? false) ||
(p.mailError?.isNotEmpty ?? false)) {
return 3;
}
return null;
}
@override
Widget build(BuildContext context) {
return Consumer<EditCommonAccountProvider>(builder: (context, provider, child) {
return Scaffold(
appBar: appbar2New(context, "Edit Account", provider.resetValues, const SizedBox.shrink(), 0xFFFFFFFF),
backgroundColor: AppColors.scaffold_bg_color,
body: Form(
key: _formKey,
child: Stepper(
type: StepperType.horizontal,
currentStep: _currentStep,
onStepContinue: () async {
// custom continue logic same as your flow
if (_currentStep == 0) {
provider.validateStep();
if (provider.gstNumberController.text.trim().isNotEmpty) {
await _validateGstIfNeeded(provider);
}
if (provider.validateStep1(context)) {
setState(() => _currentStep = 1);
} else {
provider.notifyListeners();
setState(() => _currentStep = 0);
}
} else if (_currentStep == 1) {
// optional step -> only validate if something filled
if (provider.stateSearchController.text.trim().isNotEmpty ||
provider.districtSearchController.text.trim().isNotEmpty ||
provider.addressController.text.trim().isNotEmpty) {
if (provider.validateStep1(context)) {
setState(() => _currentStep = 2);
} else {
provider.notifyListeners();
}
} else {
setState(() => _currentStep = 2);
}
} else if (_currentStep == 2) {
// bank/gst step: if fields present, validate
if (provider.gstNumberController.text.trim().isNotEmpty) {
await _validateGstIfNeeded(provider);
}
provider.validateStep3();
if (provider.bankAcNumberController.text.trim().isNotEmpty || provider.bankIfscCotroller.text.trim().isNotEmpty) {
await _validateBankIfNeeded(provider);
}
// if any errors remain, stay
final hasBankErrors = (provider.gstNumberError?.isNotEmpty ?? false) ||
(provider.bankAcNumberError?.isNotEmpty ?? false) ||
(provider.bankIFSCError?.isNotEmpty ?? false);
if (hasBankErrors) {
provider.notifyListeners();
setState(() => _currentStep = 2);
} else {
setState(() => _currentStep = 3);
}
} else {
// last step - do nothing (submit handled by controlsBuilder)
}
},
onStepCancel: () {
if (_currentStep > 0) setState(() => _currentStep -= 1);
},
onStepTapped: (i) async {
// allow step tapping but guard jump if step0 invalid
if (i == 0) {
setState(() => _currentStep = 0);
} else if (i == 1) {
if (provider.validateStep1(context)) {
setState(() => _currentStep = 1);
} else {
provider.notifyListeners();
setState(() => _currentStep = 0);
}
} else if (i == 2) {
if (provider.validateStep1(context)) {
setState(() => _currentStep = 2);
} else {
provider.notifyListeners();
setState(() => _currentStep = 0);
}
} else if (i == 3) {
if (!provider.validateStep1(context)) {
provider.notifyListeners();
setState(() => _currentStep = 0);
return;
}
setState(() => _currentStep = 3);
}
},
connectorColor: WidgetStatePropertyAll(AppColors.app_blue),
stepIconBuilder: (stepIndex, stepState) {
return CircleAvatar(
radius: 12,
backgroundColor: stepIndex <= _currentStep ? Colors.transparent : Colors.grey[300],
);
},
steps: [
// ---------------- Step 1 ----------------
Step(
title: const Text(''),
label: const Text("Step 1", style: TextStyle(fontSize: 12)),
content: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Account", style: TextStyle(color: AppColors.app_blue, fontSize: 16)),
const SizedBox(height: 8),
// Account dropdown (keeps DropdownButton2 style)
errorWidget(context, provider.accountError),
const SizedBox(height: 10),
// Company Name (debounced uniqueness check)
textControllerWidget(
context,
provider.nameController,
"Company Name",
"Enter Company Name",
(p0) {
// local update + recheck with GST
provider.updateName(p0);
provider.recheckNameWithGst();
// debounce uniqueness API call
_nameDebounce?.cancel();
_nameDebounce = Timer(const Duration(milliseconds: 600), () {
final val = provider.nameController.text.trim();
if (val.isNotEmpty) provider.checkInputsAPI(context, "name", val);
});
},
TextInputType.text,
false,
null,
focusNodes[FN_NAME],
focusNodes[FN_NAME_NEXT],
TextInputAction.next,
),
errorWidget(context, provider.nameError),
const SizedBox(height: 8),
// Mobile (debounced uniqueness check)
textControllerWidget(
context,
provider.mobileController,
"Mobile Number",
"Enter Mobile",
(p0) {
provider.updateMobile(p0);
// debounce mobile check
_nameDebounce?.cancel();
_nameDebounce = Timer(const Duration(milliseconds: 600), () {
final val = provider.mobileController.text.trim();
if (val.isNotEmpty) provider.checkInputsAPI(context, "mob1", val);
});
},
TextInputType.phone,
false,
FilteringTextInputFormatter.digitsOnly,
focusNodes[FN_MOBILE],
focusNodes[FN_CONTACT],
TextInputAction.next,
10,
),
errorWidget(context, provider.mobileError),
const SizedBox(height: 8),
// Contact Person - explicit TextFormField to avoid helper-side reassignment
Text("Contact Person Name"),
const SizedBox(height: 6),
TextFormField(
controller: provider.contactPersonController,
focusNode: focusNodes[FN_CONTACT],
textInputAction: TextInputAction.next,
keyboardType: TextInputType.text,
onChanged: (v) {
// only update provider state (provider may call notifyListeners inside)
provider.updateContactPerson(v);
// do not call heavy APIs here
},
decoration: InputDecoration(
hintText: "Enter Contact Person Name",
hintStyle: TextStyle(
height: 1.2,
backgroundColor: AppColors.text_field_color,
color: AppColors.grey_semi,
),
filled: true,
fillColor: AppColors.text_field_color,
contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
),
),
errorWidget(context, provider.contactPersonError),
const SizedBox(height: 12),
// GST (debounced validation + autofill). Typing + deletion will work now.
Text("GST Number"),
const SizedBox(height: 6),
TextFormField(
controller: provider.gstNumberController,
focusNode: focusNodes[FN_GST],
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
onChanged: (value) {
provider.updateGSTNumber(value);
// if cleared -> reset responses immediately (keeps UI clean)
if (value.trim().isEmpty) {
provider.gstResponse = null;
provider.gstNumberError = null;
provider.nameError = null;
provider.notifyListeners();
return;
}
// immediate apply when full GST length typed (fast feedback)
if (value.trim().length == 15) {
_gstDebounce?.cancel();
provider.checkAndApplyGst(context, value.trim());
return;
}
// otherwise debounce (user typing)
_gstDebounce?.cancel();
_gstDebounce = Timer(const Duration(milliseconds: 700), () {
final trimmed = provider.gstNumberController.text.trim();
if (trimmed.isNotEmpty && trimmed.length == 15) {
provider.checkAndApplyGst(context, trimmed);
}
});
},
onFieldSubmitted: (value) async {
final trimmed = value.trim();
if (trimmed.isNotEmpty) await provider.checkAndApplyGst(context, trimmed);
FocusScope.of(context).requestFocus(focusNodes[FN_BANK_ACC]);
},
decoration: InputDecoration(
hintText: "Enter GST Number",
hintStyle: TextStyle(
height: 1.2,
backgroundColor: AppColors.text_field_color,
color: AppColors.grey_semi,
),
filled: true,
fillColor: AppColors.text_field_color,
contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
),
),
const SizedBox(height: 4),
errorWidget(context, provider.gstNumberError),
],
),
),
),
// ---------------- Step 2 ----------------
Step(
title: const Text(''),
label: const Text("Step 2", style: TextStyle(fontSize: 12)),
isActive: _currentStep >= 1,
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.only(bottom: 10),
child: Text(
"Address Details",
style: TextStyle(color: AppColors.app_blue, fontSize: 16, fontFamily: "JakartaMedium"),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// State
Text("State"),
const SizedBox(height: 6),
DropdownButtonHideUnderline(
child: Row(
children: [
Expanded(
child: DropdownButton2<States>(
focusNode: focusNodes[FN_STATE],
isExpanded: true,
hint: const Text('Select State', style: TextStyle(fontSize: 14)),
items: provider.states
.map((states) => DropdownMenuItem<States>(
value: states,
child: Text(states.name ?? '', style: const TextStyle(fontSize: 14), overflow: TextOverflow.ellipsis),
))
.toList(),
value: provider.states.contains(provider.selectedState) ? provider.selectedState : null,
onChanged: (States? value) {
if (value != null) {
provider.selectedState = value;
// Clear dependent district/sub-location
provider.districts.clear();
provider.subLocations.clear();
provider.getDistrictAPI(context, provider.selectedStateID);
}
},
dropdownSearchData: DropdownSearchData(
searchInnerWidgetHeight: 50,
searchController: provider.stateSearchController,
searchInnerWidget: Padding(
padding: const EdgeInsets.all(8),
child: TextFormField(
controller: provider.stateSearchController,
decoration: InputDecoration(isDense: true, contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), hintText: 'Search States...', border: OutlineInputBorder(borderRadius: BorderRadius.circular(8))),
),
),
searchMatchFn: (item, searchValue) {
return item.value?.name?.toLowerCase().contains(searchValue.toLowerCase()) ?? false;
},
),
onMenuStateChange: (isOpen) {
if (!isOpen) provider.stateSearchController.clear();
},
buttonStyleData: ddtheme.buttonStyleData,
iconStyleData: ddtheme.iconStyleData,
menuItemStyleData: ddtheme.menuItemStyleData,
dropdownStyleData: ddtheme.dropdownStyleData,
),
),
],
),
),
errorWidget(context, provider.stateError),
const SizedBox(height: 12),
// District
Text("District"),
const SizedBox(height: 6),
DropdownButtonHideUnderline(
child: Row(
children: [
Expanded(
child: DropdownButton2<Districts>(
focusNode: focusNodes[FN_DISTRICT],
isExpanded: true,
hint: const Text('Select District', style: TextStyle(fontSize: 14)),
items: provider.districts
.map((dist) => DropdownMenuItem<Districts>(
value: dist,
child: Text(dist.district ?? '', style: const TextStyle(fontSize: 14), overflow: TextOverflow.ellipsis),
))
.toList(),
value: provider.districts.contains(provider.selectedDistricts) ? provider.selectedDistricts : null,
onChanged: (Districts? value) {
if (value != null) {
provider.selectedDistricts = value;
provider.getSubLocationAPI(context, provider.selectedDistrictId);
}
},
dropdownSearchData: DropdownSearchData(
searchInnerWidgetHeight: 50,
searchController: provider.districtSearchController,
searchInnerWidget: Padding(
padding: const EdgeInsets.all(8),
child: TextFormField(
controller: provider.districtSearchController,
decoration: InputDecoration(isDense: true, contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), hintText: 'Search Districts...', border: OutlineInputBorder(borderRadius: BorderRadius.circular(8))),
),
),
searchMatchFn: (item, searchValue) {
return item.value?.district?.toLowerCase().contains(searchValue.toLowerCase()) ?? false;
},
),
onMenuStateChange: (isOpen) {
if (!isOpen) provider.districtSearchController.clear();
},
buttonStyleData: ddtheme.buttonStyleData,
iconStyleData: ddtheme.iconStyleData,
menuItemStyleData: ddtheme.menuItemStyleData,
dropdownStyleData: ddtheme.dropdownStyleData,
),
),
],
),
),
errorWidget(context, provider.districtError),
const SizedBox(height: 12),
// Sub Locality
Text("Sub Locality"),
const SizedBox(height: 6),
DropdownButtonHideUnderline(
child: Row(
children: [
Expanded(
child: DropdownButton2<SubLocations>(
focusNode: focusNodes[FN_SUBLOC],
isExpanded: true,
hint: const Text('Select Sub Locality', style: TextStyle(fontSize: 14)),
items: provider.subLocations
.map((subloc) => DropdownMenuItem<SubLocations>(
value: subloc,
child: Text(subloc.subLocality ?? '', style: const TextStyle(fontSize: 14), overflow: TextOverflow.ellipsis),
))
.toList(),
value: provider.subLocations.contains(provider.selectedSubLocations) ? provider.selectedSubLocations : null,
onChanged: (SubLocations? value) {
if (value != null) {
provider.selectedSubLocations = value;
}
},
dropdownSearchData: DropdownSearchData(
searchInnerWidgetHeight: 50,
searchController: provider.subLocSearchController,
searchInnerWidget: Padding(
padding: const EdgeInsets.all(8),
child: TextFormField(
controller: provider.subLocSearchController,
decoration: InputDecoration(isDense: true, contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), hintText: 'Search Sub Locality...', border: OutlineInputBorder(borderRadius: BorderRadius.circular(8))),
),
),
searchMatchFn: (item, searchValue) {
return item.value?.subLocality?.toLowerCase().contains(searchValue.toLowerCase()) ?? false;
},
),
onMenuStateChange: (isOpen) {
if (!isOpen) provider.subLocSearchController.clear();
},
buttonStyleData: ddtheme.buttonStyleData,
iconStyleData: ddtheme.iconStyleData,
menuItemStyleData: ddtheme.menuItemStyleData,
dropdownStyleData: ddtheme.dropdownStyleData,
),
),
],
),
),
errorWidget(context, provider.localityError),
const SizedBox(height: 12),
// Address text field (same UI as your other address field)
textControllerWidget(
context,
provider.addressController,
"Address",
"Enter Address",
provider.updateAddress,
TextInputType.text,
false,
null,
focusNodes[FN_ADDRESS],
focusNodes[FN_BANK_ACC],
TextInputAction.next,
),
errorWidget(context, provider.addressError),
],
),
),
],
),
),
// ---------------- Step 3 ----------------
Step(
title: const Text(''),
label: const Text("Step 3", style: TextStyle(fontSize: 12)),
content: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Bank Details", style: TextStyle(color: AppColors.app_blue, fontSize: 16)),
const SizedBox(height: 8),
const SizedBox(height: 12),
// Bank Account Number
textControllerWidget(
context,
provider.bankAcNumberController,
"Bank Account Number",
"Enter Bank Account Number",
(p0) {
provider.updateNumber(p0);
// IFSC required if account entered
if (p0.trim().isNotEmpty && provider.bankIfscCotroller.text.trim().isEmpty) {
provider.bankIFSCError = "IFSC is required when account number is entered";
provider.notifyListeners();
} else if (p0.trim().isNotEmpty && provider.bankIfscCotroller.text.trim().isNotEmpty) {
// both present -> validate bank
provider.checkAndApplyBank(context, p0.trim());
} else {
// cleared -> reset bank response/errors
if (p0.trim().isEmpty) {
provider.bankResponse = null;
provider.bankAcNumberError = null;
provider.bankIFSCError = null;
provider.notifyListeners();
}
}
},
TextInputType.number,
false,
FilteringTextInputFormatter.digitsOnly,
focusNodes[FN_BANK_ACC],
focusNodes[FN_IFSC],
TextInputAction.next,
),
errorWidget(context, provider.bankAcNumberError),
// IFSC
textControllerWidget(
context,
provider.bankIfscCotroller,
"Bank IFSC",
"Enter Bank IFSC",
(p0) async {
provider.updateIFSC(p0);
if (p0.trim().isEmpty) {
if (provider.bankAcNumberController.text.trim().isNotEmpty) {
provider.bankIFSCError = "IFSC is required when account number is entered";
provider.notifyListeners();
} else {
provider.bankIFSCError = null;
provider.notifyListeners();
}
return;
}
if (!_isIfscValidFormat(p0)) {
provider.bankIFSCError = "Invalid IFSC format";
provider.notifyListeners();
return;
} else {
provider.bankIFSCError = null;
provider.notifyListeners();
}
// if both present -> validate
if (provider.bankAcNumberController.text.trim().isNotEmpty) {
await provider.checkAndApplyBank(context, provider.bankAcNumberController.text.trim());
}
},
TextInputType.text,
false,
null,
focusNodes[FN_IFSC],
focusNodes[FN_BANK_NAME],
TextInputAction.next,
),
errorWidget(context, provider.bankIFSCError),
const SizedBox(height: 8),
// Bank Name (autofilled, readonly)
Text("Bank Name"),
const SizedBox(height: 6),
TextFormField(
controller: provider.bankNameController,
focusNode: focusNodes[FN_BANK_NAME],
readOnly: true,
decoration: InputDecoration(
hintText: "Bank Name",
hintStyle: TextStyle(
height: 1.2,
backgroundColor: AppColors.text_field_color,
color: AppColors.grey_semi,
),
filled: true,
fillColor: AppColors.text_field_color,
contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
),
),
errorWidget(context, provider.banknameError),
const SizedBox(height: 8),
// Branch (read-only)
Text("Bank Branch"),
const SizedBox(height: 6),
TextFormField(
controller: provider.branchNameController,
focusNode: focusNodes[FN_BANK_BRANCH],
readOnly: true,
decoration: InputDecoration(
hintText: "Branch ",
hintStyle: TextStyle(
height: 1.2,
backgroundColor: AppColors.text_field_color,
color: AppColors.grey_semi,
),
filled: true,
fillColor: AppColors.text_field_color,
contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
),
),
errorWidget(context, provider.bankBranchError),
const SizedBox(height: 8),
// Bank Holder (read-only)
Text("Bank Holder Name"),
const SizedBox(height: 6),
TextFormField(
controller: provider.bankHolderNameController,
focusNode: focusNodes[FN_BANK_HOLDER],
readOnly: true,
decoration: InputDecoration(
hintText: "Holder ",
hintStyle: TextStyle(
height: 1.2,
backgroundColor: AppColors.text_field_color,
color: AppColors.grey_semi,
),
filled: true,
fillColor: AppColors.text_field_color,
contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none),
),
),
errorWidget(context, provider.bankHolderNameError),
const SizedBox(height: 8),
// UPI (editable)
textControllerWidget(
context,
provider.bankUpiController,
"Bank UPI ID",
"Enter Bank UPI ID",
provider.updateUPI,
TextInputType.text,
false,
null,
focusNodes[FN_UPI],
focusNodes[FN_CONTACT_MAIL],
TextInputAction.next,
),
errorWidget(context, provider.upiError),
],
),
),
),
// ---------------- Step 4 ----------------
Step(
title: const Text(''),
label: const Text("Step 4", style: TextStyle(fontSize: 12)),
content: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Contact Details", style: TextStyle(color: AppColors.app_blue, fontSize: 16)),
const SizedBox(height: 8),
textControllerWidget(
context,
provider.contectPersonMailController,
"Mail ID",
"Enter Customer Mail ID",
provider.updateMail,
TextInputType.emailAddress,
false,
null,
focusNodes[FN_CONTACT_MAIL],
FocusNode(),
TextInputAction.done,
),
errorWidget(context, provider.mailError),
],
),
),
),
],
controlsBuilder: (context, details) {
return Column(
children: [
if (_currentStep == 3) ...[
InkResponse(
onTap: provider.isSubmitting
? null
: () async {
// trigger validations for gst/bank if present
if (provider.gstNumberController.text.trim().isNotEmpty) {
await _validateGstIfNeeded(provider);
}
if (provider.bankAcNumberController.text.trim().isNotEmpty && provider.bankIfscCotroller.text.trim().isNotEmpty) {
await _validateBankIfNeeded(provider);
}
final ok = provider.validatereceiptForm(context);
if (!ok) {
final err = _firstErrorStep(provider);
if (err != null) setState(() => _currentStep = err);
provider.notifyListeners();
return;
}
// call update API
provider.isLoading = true;
final success = await provider.updateAccountDetailsAPIFunction(
context,
accId: widget.accountID,
name: provider.nameController.text.trim(),
mob1: provider.mobileController.text.trim(),
email: provider.contectPersonMailController.text.trim(),
address: provider.addressController.text.trim(),
state: provider.selectedStateID,
district: provider.selectedDistrictId,
subLocality: provider.selectedSubLocID,
bankName: provider.bankNameController.text.trim(),
branchName: provider.branchNameController.text.trim(),
bankIfscCode: provider.bankIfscCotroller.text.trim(),
accHolderName: provider.bankHolderNameController.text.trim(),
bankAccNumber: provider.bankAcNumberController.text.trim(),
bankUpiId: provider.bankUpiController.text.trim(),
);
provider.isLoading = false;
if (success) {
Navigator.pop(context, true); // or pass updated id as you wish
} else {
if (kDebugMode) debugPrint("update failed: ${provider.updateErrorMessage}");
}
},
child: Container(
height: 45,
alignment: Alignment.center,
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(color: AppColors.app_blue, borderRadius: BorderRadius.circular(15)),
child: provider.submitClickced
? CircularProgressIndicator.adaptive(valueColor: AlwaysStoppedAnimation(AppColors.white))
: Text("Submit", style: TextStyle(fontSize: 15, fontFamily: "JakartaMedium", color: Colors.white)),
),
),
] else ...[
InkResponse(
onTap: () {
final error = provider.gstNumberError ?? "";
// proceed to next step (uses Stepper.onStepContinue logic)
if (error.isEmpty) {
details.onStepContinue!();
}
},
child: Container(
height: 45,
alignment: Alignment.center,
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(color: AppColors.app_blue, borderRadius: BorderRadius.circular(15)),
child: Text("Proceed to Next Step", textAlign: TextAlign.start, style: TextStyle(fontSize: 15, fontFamily: "JakartaMedium", color: Colors.white)),
),
),
],
if (_currentStep > 0) TextButton(onPressed: details.onStepCancel, child: Text('Back', style: TextStyle(color: AppColors.app_blue)))
],
);
},
),
),
);
});
}
}
import 'dart:io';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:generp/Utils/app_colors.dart';
import 'package:generp/Utils/commonWidgets.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:interactive_slider/interactive_slider.dart';
import 'package:provider/provider.dart';
import '../../Notifiers/crmProvider/CrmNearByGeneratorsProvider.dart';
import '../../Utils/dropdownTheme.dart';
class CrmNearbyGenerators extends StatefulWidget {
const CrmNearbyGenerators({super.key});
@override
State<CrmNearbyGenerators> createState() => _CrmNearbyGeneratorsState();
}
class _CrmNearbyGeneratorsState extends State<CrmNearbyGenerators> {
Dropdowntheme ddtheme = Dropdowntheme();
@override
void initState() {
// TODO: implement initState
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
var provider = Provider.of<CrmNearByGeneratorsProvider>(
context,
listen: false,
);
provider.getLocationPermission(context);
});
}
@override
Widget build(BuildContext context) {
debugPrint("Nearbygenerators widget rebuilt");
return Consumer<CrmNearByGeneratorsProvider>(
builder: (context, provider, child) {
var sendWidget = GestureDetector(
onTap: () {
_showFilterBottomSheet(context);
},
child: SvgPicture.asset("assets/svg/filter_ic.svg", height: 25),
);
return WillPopScope(
onWillPop: () => onBackPressed(context),
child: SafeArea(
top: false,
bottom: Platform.isIOS ? false : true,
child: Scaffold(
resizeToAvoidBottomInset: true,
appBar: appbar2(
context,
"Nearby Generators",
provider.resetAll,
sendWidget,
),
backgroundColor: AppColors.scaffold_bg_color,
body: Container(
child: SingleChildScrollView(
child: Column(
children: [
ClipRRect(
// Apply border radius using ClipRRect
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30.0),
topRight: Radius.circular(30.0),
),
// padding: EdgeInsets.fromLTRB(10, 20, 10, 20),
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
GoogleMap(
scrollGesturesEnabled: true,
rotateGesturesEnabled: true,
myLocationEnabled: true,
zoomGesturesEnabled: true,
zoomControlsEnabled: true,
gestureRecognizers: {
Factory<OneSequenceGestureRecognizer>(
() => EagerGestureRecognizer(),
),
Factory<PanGestureRecognizer>(
() => PanGestureRecognizer(),
),
Factory<ScaleGestureRecognizer>(
() => ScaleGestureRecognizer(),
), // Prioritize pinch-to-zoom
},
initialCameraPosition: CameraPosition(
target: provider.startLocation,
zoom: 14.0,
),
markers: provider.markers.toSet(),
mapType: MapType.normal,
onMapCreated: (controller) {
setState(() {
provider.mapController = controller;
});
},
onCameraMove: (position) {
provider.onCameraMove(context, position);
},
),
],
),
),
),
],
),
),
),
),
),
);
},
);
}
Future<void> _showFilterBottomSheet(BuildContext context) {
return showModalBottomSheet(
useSafeArea: true,
isDismissible: true,
isScrollControlled: true,
showDragHandle: true,
enableDrag: true,
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return SafeArea(
child: Consumer<CrmNearByGeneratorsProvider>(
builder: (context, provider, child) {
return Container(
margin: EdgeInsets.only(
bottom: 15,
left: 15,
right: 15,
top: 15,
),
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Filter",
style: TextStyle(
color: AppColors.app_blue,
fontFamily: "JakartaSemiBold",
fontSize: 16,
),
),
SizedBox(height: 15),
Align(
alignment: Alignment.centerLeft,
child: Text(
'Status',
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w500,
),
),
),
DropdownButtonHideUnderline(
child: Row(
children: [
Expanded(
child: DropdownButton2<String>(
isExpanded: true,
hint: const Row(
children: [
Expanded(
child: Text(
'Select Complaint Status',
style: TextStyle(fontSize: 14),
overflow: TextOverflow.ellipsis,
),
),
],
),
items:
<String>[
'Active',
'Inactive',
'Suspense',
]
.map(
(
value,
) => DropdownMenuItem<String>(
value: value,
child: Text(
value ?? '',
style: const TextStyle(
fontSize: 14,
),
overflow:
TextOverflow.ellipsis,
),
),
)
.toList(),
value: provider.selectedItem,
onChanged: (String? newValue) {
setState(() {
provider.selectedItem = newValue!;
});
},
buttonStyleData:
ddtheme.buttonStyleData,
iconStyleData: ddtheme.iconStyleData,
menuItemStyleData:
ddtheme.menuItemStyleData,
dropdownStyleData:
ddtheme.dropdownStyleData,
),
),
],
),
),
SizedBox(height: 10),
Row(
children: [
Text(
"Radius",
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w500,
),
),
Spacer(),
Text(
'${provider.currentValue.toStringAsFixed(2)} KM',
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w500,
),
),
],
),
InteractiveSlider(
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 0),
min: 1.0,
max: 5.0, // MAX 5 KM
enabled: true,
foregroundColor: AppColors.app_blue,
segmentDividerColor: Color(0xFFF6F6F8),
onChanged: (value) {
// just to be extra safe, clamp in provider too
provider.currentValue = value;
},
),
// Slider(
// value: provider.currentValue,
// max: 100,
// divisions: 100,
//
// label: provider.currentValue.toStringAsFixed(2),
// inactiveColor: Color(0xFFD7D7D7),
// activeColor: AppColors.cyan_blue,
// onChanged: (value) {
// provider.currentValue = value;
// provider.debounce(() {
// provider.LoadNearbyGeneratorsAPI(context);
// }, Duration(milliseconds: 200));
// },
// ),
SizedBox(height: 30.0),
Container(
child: InkWell(
onTap: () {
provider.debounce(() {
provider.LoadNearbyGeneratorsAPI(
context,
provider.currentValue,
);
Navigator.pop(context);
}, Duration(milliseconds: 500));
},
child: Container(
alignment: Alignment.center,
height: 45,
margin: EdgeInsets.only(
left: 15.0,
right: 15.0,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14.0),
color: AppColors.app_blue,
),
child: Text(
"Search",
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'JakartaMedium',
color: Colors.white,
),
),
),
),
),
],
),
],
),
),
);
},
),
);
},
);
},
);
}
}
...@@ -357,8 +357,21 @@ class _LeadlistbymodeState extends State<Leadlistbymode> { ...@@ -357,8 +357,21 @@ class _LeadlistbymodeState extends State<Leadlistbymode> {
), ),
child: Consumer<Leadlistprovider>( child: Consumer<Leadlistprovider>(
builder: (context, provider, child) { builder: (context, provider, child) {
// 🔹 Initialize filter selections from widget.filter only once
if (provider.selectedLeadStatus == null &&
widget.filter?.status != null &&
widget.filter!.status!.toString().isNotEmpty) {
provider.selectedLeadStatus = widget.filter!.status;
}
if (provider.selectedOpenStatus == null &&
widget.filter?.openStatus != null &&
widget.filter!.openStatus!.toString().isNotEmpty) {
provider.selectedOpenStatus = widget.filter!.openStatus;
}
int selectedIndex = isSelected.indexWhere( int selectedIndex = isSelected.indexWhere(
(element) => element == true, (element) => element == true,
); );
final headings = [ final headings = [
"Lead Status", "Lead Status",
...@@ -373,6 +386,22 @@ class _LeadlistbymodeState extends State<Leadlistbymode> { ...@@ -373,6 +386,22 @@ class _LeadlistbymodeState extends State<Leadlistbymode> {
if (widget.mode != "executive") { if (widget.mode != "executive") {
headings.add("Employee"); headings.add("Employee");
} }
// int selectedIndex = isSelected.indexWhere(
// (element) => element == true,
// );
// final headings = [
// "Lead Status",
// "Open/Close Status",
// "Mobile Number",
// "Company Name",
// "Source",
// "Reference",
// "Team",
// "Segment",
// ];
if (widget.mode != "executive") {
headings.add("Employee");
}
return SizedBox( return SizedBox(
height: MediaQuery.of(context).size.height * 0.7, height: MediaQuery.of(context).size.height * 0.7,
child: Column( child: Column(
...@@ -512,17 +541,12 @@ class _LeadlistbymodeState extends State<Leadlistbymode> { ...@@ -512,17 +541,12 @@ class _LeadlistbymodeState extends State<Leadlistbymode> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
if (selectedIndex == 0) ...[ if (selectedIndex == 0) ...[
...provider.leadStatusList.map(( ...provider.leadStatusList.map((status,) {// here i want to do that i want to show selected from widget.filter!.status,
status,
) {
return SizedBox( return SizedBox(
height: 35, height: 35,
child: CheckboxListTile( child: CheckboxListTile(
activeColor: activeColor: AppColors.app_blue,
AppColors.app_blue, controlAffinity: ListTileControlAffinity.leading,
controlAffinity:
ListTileControlAffinity
.leading,
checkboxShape: CircleBorder( checkboxShape: CircleBorder(
side: BorderSide( side: BorderSide(
width: 0.5, width: 0.5,
...@@ -568,7 +592,7 @@ class _LeadlistbymodeState extends State<Leadlistbymode> { ...@@ -568,7 +592,7 @@ class _LeadlistbymodeState extends State<Leadlistbymode> {
width: 0.5, width: 0.5,
), ),
), ),
title: Text( title: Text(// same here widget.filter!.openStatus,
status ?? 'Unknown Status', status ?? 'Unknown Status',
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
...@@ -963,6 +987,7 @@ class _LeadlistbymodeState extends State<Leadlistbymode> { ...@@ -963,6 +987,7 @@ class _LeadlistbymodeState extends State<Leadlistbymode> {
}, },
); );
} }
void _showContactOptions(BuildContext context, String? phoneNumber) { void _showContactOptions(BuildContext context, String? phoneNumber) {
if (phoneNumber == null || phoneNumber.isEmpty) { if (phoneNumber == null || phoneNumber.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
......
...@@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; ...@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:generp/Notifiers/crmProvider/addNewLeadsandProspectsProvider.dart'; import 'package:generp/Notifiers/crmProvider/addNewLeadsandProspectsProvider.dart';
import 'package:generp/screens/crm/CrmNearbyGenerators.dart';
import 'package:generp/screens/crm/addLeadsProspectsScreen.dart'; import 'package:generp/screens/crm/addLeadsProspectsScreen.dart';
import 'package:generp/screens/crm/appointmentCalendar.dart'; import 'package:generp/screens/crm/appointmentCalendar.dart';
import 'package:generp/screens/crm/followUpListonType.dart'; import 'package:generp/screens/crm/followUpListonType.dart';
...@@ -741,6 +742,69 @@ class _CrmdashboardScreenState extends State<CrmdashboardScreen> { ...@@ -741,6 +742,69 @@ class _CrmdashboardScreenState extends State<CrmdashboardScreen> {
], ],
), ),
), ),
SizedBox(height: 10,),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: EdgeInsets.only(left: 15, top: 10, right: 15),
child: Text(
"Nearby Generators",
style: TextStyle(
fontSize: 16,
color: AppColors.grey_semi,
),
),
),
),
SizedBox(height: 12,),
InkResponse(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CrmNearbyGenerators(),
),
);
},
child: Container(
height: 60,
margin: EdgeInsets.symmetric(horizontal: 10, vertical: 0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Row(
children: [
Expanded(
flex: 1,
child: Container(
height: 35,
width: 35,
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Color(0xFFEDF8FF),
shape: BoxShape.circle,
),
child: SvgPicture.asset(
"assets/svg/find_generator.svg",
),
),
),
Expanded(
flex: 4,
child: Text(
"Find Nearby Generators",
style: TextStyle(
fontSize: 14,
color: AppColors.app_blue,
),
),
),
],
),
),
),
if (provider.nearByLeads.isNotEmpty) ...[ if (provider.nearByLeads.isNotEmpty) ...[
Align( Align(
......
...@@ -89,6 +89,12 @@ class _AllpaymentrequesitionlistsbymodesState ...@@ -89,6 +89,12 @@ class _AllpaymentrequesitionlistsbymodesState
); );
} }
}); });
final providerclear = Provider.of<Requesitionlidtdetailsprovider>(
context,
listen: false,
);
providerclear.resetAll();
}); });
} }
...@@ -859,6 +865,10 @@ class _AllpaymentrequesitionlistsbymodesState ...@@ -859,6 +865,10 @@ class _AllpaymentrequesitionlistsbymodesState
], ],
), ),
), ),
errorWidget(
context,
provider.selectpaymentAccountError,
),
InkWell( InkWell(
onTap: provider.isLoading onTap: provider.isLoading
? null // Disable tap while loading ? null // Disable tap while loading
...@@ -871,23 +881,30 @@ class _AllpaymentrequesitionlistsbymodesState ...@@ -871,23 +881,30 @@ class _AllpaymentrequesitionlistsbymodesState
// Small delay to ensure rebuild and show spinner before API starts // Small delay to ensure rebuild and show spinner before API starts
await Future.delayed(const Duration(milliseconds: 300)); await Future.delayed(const Duration(milliseconds: 300));
// 🔄 Call API // Call API
await provider.paymentrequisitionApproveSubmitAPIFunction( if (remarks.text.isNotEmpty) {
await provider.paymentrequisitionApproveSubmitAPIFunction(
context, context,
widget.mode, widget.mode,
paymentID, paymentID,
provider.approvedAmount.text, safeNormalizeAmount(provider.approvedAmount.text),
remarks.text, remarks.text,
provider.selectedID, provider.selectedID,
); );
await Provider.of<Requesitionlidtdetailsprovider>(
context,
listen: false,
);
} else {
provider.remarksError = "Please Enter Remarks.";
provider.selectpaymentAccountError = "Please Select Payment Account.";
}
// 🔓 Stop loading only if context is still active // Stop loading only if context is still active
if (context.mounted) { if (context.mounted) {
provider.isLoading = false; provider.isLoading = false;
} }
}, },
child: AnimatedContainer( child: AnimatedContainer(
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
...@@ -926,9 +943,19 @@ class _AllpaymentrequesitionlistsbymodesState ...@@ -926,9 +943,19 @@ class _AllpaymentrequesitionlistsbymodesState
}, },
); );
}, },
).then((value) { ).then((value) async {
final provider = Provider.of<Requesitionlidtdetailsprovider>(
context,
listen: false,
);
await provider.resetRequired();
var providerpage = Provider.of<Requestionlistprovider>(
context,
listen: false,
);
providerpage.paymentRequestionListsAPIFunction(context, widget.mode, "", "");
// This runs only when the bottom sheet is explicitly popped // This runs only when the bottom sheet is explicitly popped
print("closing Sheet");
// Use the original context with mounted check // Use the original context with mounted check
if (context.mounted) { if (context.mounted) {
...@@ -962,6 +989,23 @@ class _AllpaymentrequesitionlistsbymodesState ...@@ -962,6 +989,23 @@ class _AllpaymentrequesitionlistsbymodesState
} }
}); });
} }
String safeNormalizeAmount(String? input) {
if (input == null || input.trim().isEmpty) return "0";
// Remove currency symbols, commas and spaces - keep digits and dots
String cleaned = input.replaceAll(RegExp(r'[^0-9.]'), '');
// Handle multiple decimal points: keep only first dot
int firstDot = cleaned.indexOf('.');
if (firstDot != -1) {
String before = cleaned.substring(0, firstDot + 1);
String after = cleaned.substring(firstDot + 1).replaceAll('.', '');
cleaned = before + after;
}
return cleaned.isEmpty ? "0" : cleaned;
}
Future<void> _showLevelRejectionSheet(BuildContext context, paymentID) { Future<void> _showLevelRejectionSheet(BuildContext context, paymentID) {
return showModalBottomSheet( return showModalBottomSheet(
...@@ -1130,7 +1174,7 @@ class _AllpaymentrequesitionlistsbymodesState ...@@ -1130,7 +1174,7 @@ class _AllpaymentrequesitionlistsbymodesState
context, context,
listen: false, listen: false,
); );
detailsprov.resetAll(); detailsprov.resetRequired();
provider.paymentRequestionListsAPIFunction( provider.paymentRequestionListsAPIFunction(
context, context,
......
...@@ -82,6 +82,7 @@ class _PaymentrequestionlistdetailsState ...@@ -82,6 +82,7 @@ class _PaymentrequestionlistdetailsState
provider.showMoreDetails = false; provider.showMoreDetails = false;
provider.checkDropDownReset(); provider.checkDropDownReset();
print(widget.paymentRequestId); print(widget.paymentRequestId);
provider.resetRequired();
provider.paymentRequesitionDetails(context, widget.paymentRequestId); provider.paymentRequesitionDetails(context, widget.paymentRequestId);
}); });
} }
...@@ -891,9 +892,9 @@ class _PaymentrequestionlistdetailsState ...@@ -891,9 +892,9 @@ class _PaymentrequestionlistdetailsState
), ),
), ),
Text( Text(
reqDet.amount == "" reqDet.amount == ""
? "-" ? "-"
: "${reqDet.amount}", : "${reqDet.amount }",
style: TextStyle( style: TextStyle(
fontFamily: "JakartaRegular", fontFamily: "JakartaRegular",
fontSize: 14, fontSize: 14,
...@@ -936,8 +937,7 @@ class _PaymentrequestionlistdetailsState ...@@ -936,8 +937,7 @@ class _PaymentrequestionlistdetailsState
final section = sections[sectionIndex]; final section = sections[sectionIndex];
final title = section["title"] as String; final title = section["title"] as String;
final headings = section["headings"] as List<String>; final headings = section["headings"] as List<String>;
final subHeadings = final subHeadings = section["subHeadings"] as List<String>;
section["subHeadings"] as List<String>;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
...@@ -1304,6 +1304,10 @@ class _PaymentrequestionlistdetailsState ...@@ -1304,6 +1304,10 @@ class _PaymentrequestionlistdetailsState
} }
Future<void> _showLevelApprovalSheet(BuildContext context, paymentID) { Future<void> _showLevelApprovalSheet(BuildContext context, paymentID) {
final provider = Provider.of<Requesitionlidtdetailsprovider>(
context,
listen: false,
);
return showModalBottomSheet( return showModalBottomSheet(
useSafeArea: true, useSafeArea: true,
isDismissible: true, isDismissible: true,
...@@ -1516,9 +1520,9 @@ class _PaymentrequestionlistdetailsState ...@@ -1516,9 +1520,9 @@ class _PaymentrequestionlistdetailsState
context, context,
widget.mode, widget.mode,
paymentID, paymentID,
provider.approvedAmount.text, safeNormalizeAmount(provider.approvedAmount.text),
remarks.text, remarks.text,
provider.selectedID, provider.selectedID ?? "",
); );
}, },
child: Container( child: Container(
...@@ -1580,9 +1584,30 @@ class _PaymentrequestionlistdetailsState ...@@ -1580,9 +1584,30 @@ class _PaymentrequestionlistdetailsState
}, },
); );
}, },
); ).then((_) async {
await provider.resetRequired();
});
} }
String safeNormalizeAmount(String? input) {
if (input == null || input.trim().isEmpty) return "0";
// Remove currency symbols, commas and spaces - keep digits and dots
String cleaned = input.replaceAll(RegExp(r'[^0-9.]'), '');
// Handle multiple decimal points: keep only first dot
int firstDot = cleaned.indexOf('.');
if (firstDot != -1) {
String before = cleaned.substring(0, firstDot + 1);
String after = cleaned.substring(firstDot + 1).replaceAll('.', '');
cleaned = before + after;
}
return cleaned.isEmpty ? "0" : cleaned;
}
Future<void> _showLevelRejectionSheet(BuildContext context, paymentID) { Future<void> _showLevelRejectionSheet(BuildContext context, paymentID) {
return showModalBottomSheet( return showModalBottomSheet(
useSafeArea: true, useSafeArea: true,
...@@ -2268,6 +2293,7 @@ class _PaymentrequestionlistdetailsState ...@@ -2268,6 +2293,7 @@ class _PaymentrequestionlistdetailsState
"Enter Proposed Payment Account", "Enter Proposed Payment Account",
(p0) {}, (p0) {},
), ),
TextWidget( TextWidget(
context, context,
"Payment Account", "Payment Account",
...@@ -2323,9 +2349,7 @@ class _PaymentrequestionlistdetailsState ...@@ -2323,9 +2349,7 @@ class _PaymentrequestionlistdetailsState
value, value,
) { ) {
if (value != null && if (value != null &&
provider provider.paymentsAccounts.isNotEmpty) {
.paymentsAccounts
.isNotEmpty) {
setState(() { setState(() {
provider.selectedPaymentAccounts = provider.selectedPaymentAccounts =
value; value;
...@@ -2415,6 +2439,8 @@ class _PaymentrequestionlistdetailsState ...@@ -2415,6 +2439,8 @@ class _PaymentrequestionlistdetailsState
], ],
), ),
), ),
errorWidget(context, provider.selectpaymentAccountError),
textControllerReadonlyWidget( textControllerReadonlyWidget(
context, context,
provider.approvedAmountReadonly, provider.approvedAmountReadonly,
...@@ -2652,8 +2678,9 @@ class _PaymentrequestionlistdetailsState ...@@ -2652,8 +2678,9 @@ class _PaymentrequestionlistdetailsState
), ),
child: InkWell( child: InkWell(
onTap: () { onTap: () {
provider HapticFeedback.mediumImpact();
.paymentrequisitionProcessSubmitAPIFunction( if (provider.selectedPaymentAccounts != null) {
provider.paymentrequisitionProcessSubmitAPIFunction(
context, context,
widget.mode, widget.mode,
paymentReferenceNumber.text, paymentReferenceNumber.text,
...@@ -2663,6 +2690,9 @@ class _PaymentrequestionlistdetailsState ...@@ -2663,6 +2690,9 @@ class _PaymentrequestionlistdetailsState
remarks.text, remarks.text,
provider.imagePath, provider.imagePath,
); );
} else{
provider.selectpaymentAccountError = "Please Select account";
}
}, },
child: Container( child: Container(
height: 45, height: 45,
......
...@@ -359,16 +359,19 @@ class _AttendanceRequestDetailScreenState ...@@ -359,16 +359,19 @@ class _AttendanceRequestDetailScreenState
"Other Details", "Other Details",
scaleFactor, scaleFactor,
), ),
if (details.type == "Check In" || details.type == "Check In/Out")
_buildDetailTile( _buildDetailTile(
"Check In Type", "Check In Type",
details.checkInType, details.checkInType,
scaleFactor, scaleFactor,
), ),
if (details.type == "Check Out" || details.type == "Check In/Out")
_buildDetailTile( _buildDetailTile(
"Check Out Type", "Check Out Type",
details.chechOutType, details.chechOutType,
scaleFactor, scaleFactor,
), ),
if (details.type == "Check Out" || details.type == "Check In/Out")
_buildDetailTile( _buildDetailTile(
"Check Out Time", "Check Out Time",
details.checkOutTime, details.checkOutTime,
......
...@@ -72,12 +72,12 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -72,12 +72,12 @@ class _ContactListScreenState extends State<ContactListScreen> {
// Clean phone number // Clean phone number
final cleanPhoneNumber = _cleanPhoneNumber(phoneNumber); final cleanPhoneNumber = _cleanPhoneNumber(phoneNumber);
// Instead of inserting contact, open system form // Instead of inserting contact, open system form
final newContact = Contact() final newContact = Contact()
..name = Name(first: name) ..name = Name(first: name)
..phones = [Phone(cleanPhoneNumber)]; ..phones = [Phone(cleanPhoneNumber)];
// 🟢 This opens the phone’s native “Add contact” screen (prefilled) // This opens the phone’s native “Add contact” screen (prefilled)
await FlutterContacts.openExternalInsert(newContact); await FlutterContacts.openExternalInsert(newContact);
// Hide loading // Hide loading
......
...@@ -25,6 +25,7 @@ export 'package:generp/Notifiers/commonProvider/accountDetailsProvider.dart'; ...@@ -25,6 +25,7 @@ export 'package:generp/Notifiers/commonProvider/accountDetailsProvider.dart';
export 'package:generp/Notifiers/commonProvider/accountsListProvider.dart'; export 'package:generp/Notifiers/commonProvider/accountsListProvider.dart';
export 'package:generp/Notifiers/commonProvider/commonPagesProvider.dart'; export 'package:generp/Notifiers/commonProvider/commonPagesProvider.dart';
export 'package:generp/Notifiers/commonProvider/accountLedgerProvider.dart'; export 'package:generp/Notifiers/commonProvider/accountLedgerProvider.dart';
export 'package:generp/Notifiers/commonProvider/editCommonAccountProvider.dart';
export 'package:generp/Notifiers/financeProvider/DashboardProvider.dart'; export 'package:generp/Notifiers/financeProvider/DashboardProvider.dart';
export 'package:generp/Notifiers/financeProvider/RequestionListProvider.dart'; export 'package:generp/Notifiers/financeProvider/RequestionListProvider.dart';
...@@ -56,6 +57,7 @@ export 'package:generp/Notifiers/crmProvider/addProspectLeadsProvider.dart'; ...@@ -56,6 +57,7 @@ export 'package:generp/Notifiers/crmProvider/addProspectLeadsProvider.dart';
export 'package:generp/Notifiers/crmProvider/followUpUpdateProvider.dart'; export 'package:generp/Notifiers/crmProvider/followUpUpdateProvider.dart';
export 'package:generp/Notifiers/crmProvider/appointmentCalendarProvider.dart'; export 'package:generp/Notifiers/crmProvider/appointmentCalendarProvider.dart';
export 'package:generp/Notifiers/crmProvider/addNewLeadsandProspectsProvider.dart'; export 'package:generp/Notifiers/crmProvider/addNewLeadsandProspectsProvider.dart';
export 'package:generp/Notifiers/crmProvider/CrmNearByGeneratorsProvider.dart';
export 'package:generp/Notifiers/hrmProvider/hrmAccessiblePagesProvider.dart'; export 'package:generp/Notifiers/hrmProvider/hrmAccessiblePagesProvider.dart';
export 'package:generp/Notifiers/hrmProvider/attendanceListProvider.dart'; export 'package:generp/Notifiers/hrmProvider/attendanceListProvider.dart';
......
...@@ -362,7 +362,7 @@ class _PaymentdetailsState extends State<Paymentdetails> { ...@@ -362,7 +362,7 @@ class _PaymentdetailsState extends State<Paymentdetails> {
), ),
errorWidget(context, provider.selectAmountError), errorWidget(context, provider.selectAmountError),
SizedBox(height: 10), SizedBox(height: 10),
if (provider.selectPaymentMode?.name?.toLowerCase() != 'upi') ...[ if (provider.selectPaymentMode?.name?.toLowerCase() != 'qr') ...[
Padding( Padding(
padding: const EdgeInsets.only(bottom: 5.0), padding: const EdgeInsets.only(bottom: 5.0),
child: Text("Reference Number"), child: Text("Reference Number"),
...@@ -481,7 +481,7 @@ class _PaymentdetailsState extends State<Paymentdetails> { ...@@ -481,7 +481,7 @@ class _PaymentdetailsState extends State<Paymentdetails> {
: () { : () {
HapticFeedback.selectionClick(); HapticFeedback.selectionClick();
// 🧩 Common validation // Common validation
if (provider.selectContact == null) { if (provider.selectContact == null) {
provider.selectContactError = "Please select phone number"; provider.selectContactError = "Please select phone number";
provider.notifyListeners(); provider.notifyListeners();
...@@ -494,15 +494,15 @@ class _PaymentdetailsState extends State<Paymentdetails> { ...@@ -494,15 +494,15 @@ class _PaymentdetailsState extends State<Paymentdetails> {
return; return;
} }
// 🧩 For UPI payment // For QR payment
if (provider.selectPaymentMode?.name?.toLowerCase() == 'upi') { if (provider.selectPaymentMode?.name?.toLowerCase() == 'qr') {
if (provider.imagePicked == 0 || provider.imagePath == null) { if (provider.imagePicked == 0 || provider.imagePath == null) {
provider.imageError = "Please upload reference document"; provider.imageError = "Please upload reference document";
provider.notifyListeners(); provider.notifyListeners();
return; return;
} }
// All validations passed // All validations passed
var homeProvider = Provider.of<HomescreenNotifier>( var homeProvider = Provider.of<HomescreenNotifier>(
context, context,
listen: false, listen: false,
...@@ -521,7 +521,7 @@ class _PaymentdetailsState extends State<Paymentdetails> { ...@@ -521,7 +521,7 @@ class _PaymentdetailsState extends State<Paymentdetails> {
), ),
); );
} else { } else {
// 🧩 For non-UPI payments // For non-QR payments
if (provider.Referencecontroller.text.isEmpty) { if (provider.Referencecontroller.text.isEmpty) {
provider.ReferenceError = "Please enter reference number"; provider.ReferenceError = "Please enter reference number";
provider.notifyListeners(); provider.notifyListeners();
...@@ -561,7 +561,7 @@ class _PaymentdetailsState extends State<Paymentdetails> { ...@@ -561,7 +561,7 @@ class _PaymentdetailsState extends State<Paymentdetails> {
), ),
) )
: Text( : Text(
provider.selectPaymentMode?.name?.toLowerCase() == 'upi' provider.selectPaymentMode?.name?.toLowerCase() == 'qr'
? "Show QR" ? "Show QR"
: "Send OTP", : "Send OTP",
textAlign: TextAlign.center, textAlign: TextAlign.center,
......
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
...@@ -31,6 +32,8 @@ class RazorpayQrScreen extends StatefulWidget { ...@@ -31,6 +32,8 @@ class RazorpayQrScreen extends StatefulWidget {
class _RazorpayQrScreenState extends State<RazorpayQrScreen> { class _RazorpayQrScreenState extends State<RazorpayQrScreen> {
Timer? _statusTimer; Timer? _statusTimer;
bool _isCancelling = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
...@@ -54,7 +57,6 @@ class _RazorpayQrScreenState extends State<RazorpayQrScreen> { ...@@ -54,7 +57,6 @@ class _RazorpayQrScreenState extends State<RazorpayQrScreen> {
} }
}); });
} }
void _startStatusPolling(QrProvider provider) { void _startStatusPolling(QrProvider provider) {
_statusTimer = Timer.periodic(const Duration(seconds: 5), (timer) async { _statusTimer = Timer.periodic(const Duration(seconds: 5), (timer) async {
if (!mounted) return; if (!mounted) return;
...@@ -69,21 +71,15 @@ class _RazorpayQrScreenState extends State<RazorpayQrScreen> { ...@@ -69,21 +71,15 @@ class _RazorpayQrScreenState extends State<RazorpayQrScreen> {
razorpayOrderId: razorpayOrderId, razorpayOrderId: razorpayOrderId,
); );
if (response != null && response.error == "0") {// if (response != null && response.error == "0") {
// success -> stop timer, notify user and pop
CustomSnackBar.showSuccess( CustomSnackBar.showSuccess(
context: context, context: context,
message:"Payment received successfully ", message: "Payment received successfully",
); );
// Fluttertoast.showToast(
// msg:
// backgroundColor: Colors.green,
// textColor: Colors.white,
// );
// Stop timer
timer.cancel(); timer.cancel();
// Go back two screens
if (mounted) { if (mounted) {
Navigator.pop(context); // close QR screen Navigator.pop(context); // close QR screen
Navigator.pop(context); // close previous screen Navigator.pop(context); // close previous screen
...@@ -92,6 +88,67 @@ class _RazorpayQrScreenState extends State<RazorpayQrScreen> { ...@@ -92,6 +88,67 @@ class _RazorpayQrScreenState extends State<RazorpayQrScreen> {
}); });
} }
/// Called when user explicitly cancels / presses close.
/// This stops polling and checks the final status once — shows failure snack only if payment not completed.
Future<void> _handleCancel() async {
// Prevent re-entry
if (_isCancelling) return;
_isCancelling = true;
try {
// Stop polling immediately
_statusTimer?.cancel();
_statusTimer = null;
final provider = context.read<QrProvider>();
final razorpayOrderId = provider.qrResponse?.qrId;
if (razorpayOrderId != null) {
// Check status once more before showing failure
final response = await provider.fetchRazorpayUpiQrStatus(
context: context,
sessionId: widget.sessionId,
empId: widget.empId,
razorpayOrderId: razorpayOrderId,
);
// If response == null or not successful -> show warning now (single time)
if (response == null || response.error != "0") {
if (mounted) {
CustomSnackBar.showWarning(
context: context,
message: "⚠️ Payment not yet completed or failed",
);
}
} else {
if (mounted) {
CustomSnackBar.showSuccess(
context: context,
message: "Payment received successfully",
);
}
}
} else {
// No order id — just show failure once
if (mounted) {
CustomSnackBar.showWarning(
context: context,
message: "⚠️ Payment not yet completed or failed",
);
}
}
} catch (e) {
// ignore or log
debugPrint('_handleCancel error: $e');
} finally {
// Finally pop the screen (single pop)
if (mounted) Navigator.pop(context);
// small delay to reset guard if this screen somehow remains mounted longer
// but normally screen is popped so _isCancelling reset isn't necessary
}
}
@override @override
void dispose() { void dispose() {
_statusTimer?.cancel(); _statusTimer?.cancel();
...@@ -100,33 +157,75 @@ class _RazorpayQrScreenState extends State<RazorpayQrScreen> { ...@@ -100,33 +157,75 @@ class _RazorpayQrScreenState extends State<RazorpayQrScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return WillPopScope(
appBar: appbarNew(context, "Scan QR to Pay", 0xFFFFFFFF), onWillPop: () async {
backgroundColor: Colors.black, await _handleCancel();
body: SafeArea( return false;
child: Consumer<QrProvider>( },
builder: (context, provider, _) { child: Scaffold(
if (provider.isLoading) { appBar: AppBar(
return _buildLoadingState(); backgroundColor: Color(0xFFFFFFFF),
} automaticallyImplyLeading: false,
title: SizedBox(
if (provider.errorMessage != null) { child: Row(
return _buildErrorState(provider.errorMessage!); mainAxisAlignment: MainAxisAlignment.start,
} crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (provider.qrResponse == null || provider.qrResponse?.qrCode == null) { InkResponse(
return _buildNoQrState(); onTap: () => _handleCancel(),
} child: SvgPicture.asset(
"assets/svg/appbar_back_button.svg",
if (provider.secondsLeft == 0) { height: 25,
Future.delayed(Duration.zero, () { ),
if (mounted) Navigator.pop(context); ),
}); SizedBox(width: 10),
return _buildExpiredState(); InkResponse(
} onTap: () => _handleCancel(),
child: Text(
return _buildQrScreen(provider); "Scan QR to Pay",
}, style: TextStyle(
fontSize: 16,
height: 1.1,
fontFamily: "JakartaSemiBold",
color: AppColors.semi_black,
),
),
),
],
),
),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.vertical(
// bottom: Radius.circular(30), // Adjust the radius as needed
// ),
// ),
),
backgroundColor: Colors.black,
body: SafeArea(
child: Consumer<QrProvider>(
builder: (context, provider, _) {
if (provider.isLoading) {
return _buildLoadingState();
}
if (provider.errorMessage != null) {
return _buildErrorState(provider.errorMessage!);
}
if (provider.qrResponse == null || provider.qrResponse?.qrCode == null) {
return _buildNoQrState();
}
if (provider.secondsLeft == 0) {
Future.delayed(Duration.zero, () {
if (mounted) Navigator.pop(context);
});
return _buildExpiredState();
}
return _buildQrScreen(provider);
},
),
), ),
), ),
); );
...@@ -356,7 +455,7 @@ class _RazorpayQrScreenState extends State<RazorpayQrScreen> { ...@@ -356,7 +455,7 @@ class _RazorpayQrScreenState extends State<RazorpayQrScreen> {
child: IconButton( child: IconButton(
icon: const Icon(Icons.close, size: 18), icon: const Icon(Icons.close, size: 18),
color: Colors.transparent, color: Colors.transparent,
onPressed: () => Navigator.pop(context), onPressed: () => _handleCancel(),
), ),
), ),
), ),
...@@ -432,7 +531,7 @@ class _RazorpayQrScreenState extends State<RazorpayQrScreen> { ...@@ -432,7 +531,7 @@ class _RazorpayQrScreenState extends State<RazorpayQrScreen> {
// Cancel Button // Cancel Button
Expanded( Expanded(
child: ElevatedButton.icon( child: ElevatedButton.icon(
onPressed: () => Navigator.pop(context), onPressed: () => _handleCancel(),
icon: const Icon(Icons.close, size: 18), icon: const Icon(Icons.close, size: 18),
label: const Text("Cancel"), label: const Text("Cancel"),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
......
...@@ -7,9 +7,11 @@ import 'package:flutter_svg/svg.dart'; ...@@ -7,9 +7,11 @@ import 'package:flutter_svg/svg.dart';
import 'package:generp/Notifiers/VisitDetailsProvider.dart'; import 'package:generp/Notifiers/VisitDetailsProvider.dart';
import 'package:generp/Utils/app_colors.dart'; import 'package:generp/Utils/app_colors.dart';
import 'package:generp/Utils/commonWidgets.dart'; import 'package:generp/Utils/commonWidgets.dart';
import 'package:generp/screens/serviceEngineer/RazorpayQrScreen.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../../Notifiers/HomeScreenNotifier.dart';
import '../../Utils/commonServices.dart'; import '../../Utils/commonServices.dart';
import '../finance/FileViewer.dart'; import '../finance/FileViewer.dart';
import 'Followupdetails.dart'; import 'Followupdetails.dart';
...@@ -46,6 +48,7 @@ class _VisitdetailsState extends State<Visitdetails> { ...@@ -46,6 +48,7 @@ class _VisitdetailsState extends State<Visitdetails> {
visitdetails.showMoreDetails = false; visitdetails.showMoreDetails = false;
visitdetails.LoadVisitDetailsAPI(context, widget.complaintID); visitdetails.LoadVisitDetailsAPI(context, widget.complaintID);
visitdetails.LoadFollowupListAPI(context, widget.complaintID); visitdetails.LoadFollowupListAPI(context, widget.complaintID);
visitdetails.serviceComplaintBillList(context, widget.complaintID);
}); });
} }
...@@ -90,6 +93,7 @@ class _VisitdetailsState extends State<Visitdetails> { ...@@ -90,6 +93,7 @@ class _VisitdetailsState extends State<Visitdetails> {
var generatorDetails = provider.generatorDetails; var generatorDetails = provider.generatorDetails;
var complaintDetails = provider.complaintDetailsNew; var complaintDetails = provider.complaintDetailsNew;
var followups = provider.followUpList; var followups = provider.followUpList;
var complaintBillList = provider.complaintList;
return WillPopScope( return WillPopScope(
onWillPop: () => onBackPressed(context), onWillPop: () => onBackPressed(context),
child: SafeArea( child: SafeArea(
...@@ -994,6 +998,129 @@ class _VisitdetailsState extends State<Visitdetails> { ...@@ -994,6 +998,129 @@ class _VisitdetailsState extends State<Visitdetails> {
), ),
), ),
], ],
if (complaintBillList.isNotEmpty) ...[
Container(
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
child: Text(
"Complaint Bill List",
style: TextStyle(
color: Color(0xFF818181),
fontFamily: "JakartaMedium",
),
),
),
SizedBox(
width: double.infinity,
height: 350,
child: ListView.builder(
padding: EdgeInsets.all(12),
itemCount: complaintBillList!.length,
itemBuilder: (context, index) {
final item = complaintBillList![index];
return Card(
margin: EdgeInsets.only(bottom: 12),
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// _detailRow("Bill ID", item.billId),
// _detailRow("Total Amount", item.totalAmount),
// _detailRow("Raw Amount", item.rawAmount),
// _detailRow("Narration", item.narration),
// _detailRow("Bill Date", item.billDate),
// _detailRow("Due Date", item.dueDate),
// _detailRow("Bill Paid", item.billPaid == "1" ? "Yes" : "No"),
Row(
children: [
// 👤 Avatar
Container(
height: 40,
width: 40,
decoration: const BoxDecoration(
color: Color(0xFFE6F6FF),
shape: BoxShape.circle,
),
clipBehavior: Clip.antiAlias,
child: SvgPicture.asset(
"assets/svg/compliant_list_ic.svg",
),
),
const SizedBox(width: 12),
// 📝 Info
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.narration ?? "-",
style: const TextStyle(
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w400,
fontSize: 14,
color: Colors.black87,
),
),
const SizedBox(height: 3),
Text(
item.dueDate ?? "-",
style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.grey.shade600,
height: 1.4,
),
),
],
),
),
// Call
if (item.billPaid == "0")
InkResponse(
onTap: (){
var homeProvider = Provider.of<HomescreenNotifier>(
context,
listen: false,
);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => RazorpayQrScreen(
sessionId: homeProvider.session,
empId: homeProvider.empId,
amount: item.rawAmount.toString(),
refType: "Bill",
refId: item.billId.toString()
))
);
},
child: Text("Pay Now", style: TextStyle(
fontSize: 14,
color: AppColors.app_blue,
),),
)
],
),
],
),
),
);
},
)
),
],
SizedBox(height: 75), SizedBox(height: 75),
], ],
), ),
...@@ -1215,6 +1342,28 @@ class _VisitdetailsState extends State<Visitdetails> { ...@@ -1215,6 +1342,28 @@ class _VisitdetailsState extends State<Visitdetails> {
); );
} }
Widget _detailRow(String title, String? value) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"$title: ",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
),
Expanded(
child: Text(
value ?? "--",
style: TextStyle(fontSize: 14),
),
),
],
),
);
}
Widget _scaffold(BuildContext context) { Widget _scaffold(BuildContext context) {
return Consumer<Visitdetailsprovider>( return Consumer<Visitdetailsprovider>(
builder: (context, provider, child) { builder: (context, provider, child) {
......
...@@ -64,6 +64,7 @@ import '../Models/PaymentCollectionResponse.dart'; ...@@ -64,6 +64,7 @@ import '../Models/PaymentCollectionResponse.dart';
import '../Models/PaymentCollectionValidateOTPResponse.dart'; import '../Models/PaymentCollectionValidateOTPResponse.dart';
import '../Models/PaymentCollectionWalletResponse.dart'; import '../Models/PaymentCollectionWalletResponse.dart';
import '../Models/ProfileResponse.dart'; import '../Models/ProfileResponse.dart';
import '../Models/ServiceComplaintBillListResponse.dart';
import '../Models/SessionResponse.dart'; import '../Models/SessionResponse.dart';
import '../Models/StatusResponse.dart'; import '../Models/StatusResponse.dart';
import '../Models/SubmitComplaintResponse.dart'; import '../Models/SubmitComplaintResponse.dart';
...@@ -78,6 +79,7 @@ import '../Models/UpdateComplaintResponse.dart'; ...@@ -78,6 +79,7 @@ import '../Models/UpdateComplaintResponse.dart';
import '../Models/UpdatePasswordResponse.dart'; import '../Models/UpdatePasswordResponse.dart';
import '../Models/VersionsResponse.dart'; import '../Models/VersionsResponse.dart';
import '../Models/ViewVisitDetailsResponse.dart'; import '../Models/ViewVisitDetailsResponse.dart';
import '../Models/commonModels/EditCommonAccFormDetailsResponse.dart';
import '../Models/commonModels/commonAddAccountsSubmitResponse.dart'; import '../Models/commonModels/commonAddAccountsSubmitResponse.dart';
import '../Models/crmModels/appointmentCalendarResponse.dart'; import '../Models/crmModels/appointmentCalendarResponse.dart';
import '../Models/crmModels/crmAddFollowUpResponse.dart'; import '../Models/crmModels/crmAddFollowUpResponse.dart';
...@@ -928,7 +930,7 @@ class ApiCalling { ...@@ -928,7 +930,7 @@ class ApiCalling {
}; };
final res = await post(data, technicianNearbyGeneratorsUrl, {}); final res = await post(data, technicianNearbyGeneratorsUrl, {});
if (res != null) { if (res != null) {
// print(data); print(" input data: $data");
// debugPrint(res.body); // debugPrint(res.body);
return NearbyGeneratorsResponse.fromJson(jsonDecode(res.body)); return NearbyGeneratorsResponse.fromJson(jsonDecode(res.body));
} else { } else {
...@@ -941,6 +943,36 @@ class ApiCalling { ...@@ -941,6 +943,36 @@ class ApiCalling {
} }
} }
static Future<NearbyGeneratorsResponse?> loadCrmNearbyGeneratorsAPI(
empId,
session,
techLoc,
radius,
status,
) async {
try {
Map<String, String> data = {
'emp_id': (empId).toString(),
'session_id': (session).toString(),
'emp_loc': (techLoc).toString(),
'radius': (radius).toString(),
};
final res = await post(data, crmNearbyGeneratorsUrl, {});
if (res != null) {
print(" input data: $data");
debugPrint("Map api response ${res.body}");
return NearbyGeneratorsResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
static Future<AccountSuggestionResonse?> AccountSuggestionAPI( static Future<AccountSuggestionResonse?> AccountSuggestionAPI(
empId, empId,
session, session,
...@@ -965,6 +997,34 @@ class ApiCalling { ...@@ -965,6 +997,34 @@ class ApiCalling {
return null; return null;
} }
} }
/// service Complaint Bill List Api
static Future<ServiceComplaintBillListResponse?> serviceComplaintBillListAPI(
empId,
session,
complaintId,
) async {
try {
Map<String, String> data = {
'emp_id': (empId).toString(),
'session_id': (session).toString(),
'complaint_id': (complaintId).toString(),
};
debugPrint("@@@@@@@@@@@@@@@@@@@@@@ input data: $data");
final res = await post(data, serviceComplaintBillListUrl, {});
if (res != null) {
debugPrint(res.body);
return ServiceComplaintBillListResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('###########Exception $e ');
return null;
}
}
static Future<TechnicianPendingComplaintsResponse?> static Future<TechnicianPendingComplaintsResponse?>
LoadTechnicianComplaintsAPI(empId, session) async { LoadTechnicianComplaintsAPI(empId, session) async {
...@@ -1218,15 +1278,17 @@ class ApiCalling { ...@@ -1218,15 +1278,17 @@ class ApiCalling {
session, session,
compId, compId,
) async { ) async {
try { try {
Map<String, String> data = { Map<String, String> data = {
'emp_id': (empId).toString(), 'emp_id': (empId).toString(),
'session_id': (session).toString(), 'session_id': (session).toString(),
'comp_id': (compId).toString(), 'comp_id': (compId).toString(),
}; };
debugPrint("&&&&&&&&&&&&&&&&&&&&&&& input: $data");
final res = await post(data, technicianComplaintFollowUpUrl, {}); final res = await post(data, technicianComplaintFollowUpUrl, {});
if (res != null) { if (res != null) {
debugPrint(res.body); debugPrint("followup response ${res.body}");
return FollowupListResponse.fromJson(jsonDecode(res.body)); return FollowupListResponse.fromJson(jsonDecode(res.body));
} else { } else {
debugPrint("Null Response"); debugPrint("Null Response");
...@@ -1750,6 +1812,68 @@ class ApiCalling { ...@@ -1750,6 +1812,68 @@ class ApiCalling {
} }
} }
static Future<CommonResponse?> commonUpdateAccountDetailsAPI(
empId,
sessionId,
accId,
name,
mob1,
mob2,
tel,
email,
designation,
address,
state,
district,
subLocality,
bankName,
branchName,
bankIfscCode,
accHolderName,
bankAccNumber,
bankUpiId,
) async {
try {
Map<String, String> data = {
'emp_id': empId.toString(),
'session_id': sessionId.toString(),
'acc_id': accId.toString(),
'name': name.toString(),
'mob1': mob1.toString(),
'mob2': mob2.toString(),
'tel': tel.toString(),
'email': email.toString(),
'designation': designation.toString(),
'address': address.toString(),
'state': state.toString(),
'district': district.toString(),
'sub_locality': subLocality.toString(),
'bank_name': bankName.toString(),
'bank_branch_name': branchName.toString(),
'bank_ifsc_code': bankIfscCode.toString(),
'bank_account_holder_name': accHolderName.toString(),
'bank_account_number': bankAccNumber.toString(),
'bank_upi_id': bankUpiId.toString(),
};
var res = await post(data, commonUpdateAccountDetailsUrl, {});
if (res != null) {
print("Input Date: $data");
debugPrint(res.body);
return CommonResponse.fromJson(jsonDecode(res.body),
);
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
static Future<paymentRequestionBankDetailsResponse?> static Future<paymentRequestionBankDetailsResponse?>
paymentRequestionBankDetailsAPI(empId, session, accountId) async { paymentRequestionBankDetailsAPI(empId, session, accountId) async {
try { try {
...@@ -2300,6 +2424,31 @@ class ApiCalling { ...@@ -2300,6 +2424,31 @@ class ApiCalling {
} }
} }
static Future<EditCommonAccFormDetailsResponse?> commonAccountDetailsListAPI(
empId,
session,
accountId,
) async {
try {
Map<String, String> data = {
'emp_id': (empId).toString(),
'session_id': (session).toString(),
'acc_id': accountId.toString(),
};
final res = await post(data, commonAccountDetailsUrl, {});
if (res != null) {
debugPrint(res.body);
return EditCommonAccFormDetailsResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
static Future<commonAccountLedgerFilterResponse?> static Future<commonAccountLedgerFilterResponse?>
commonAccountLedgerDropDownAPI(empId, session) async { commonAccountLedgerDropDownAPI(empId, session) async {
try { try {
...@@ -5701,7 +5850,7 @@ class ApiCalling { ...@@ -5701,7 +5850,7 @@ class ApiCalling {
empId, empId,
pageNumber pageNumber
) async { ) async {
debugPrint("🔥🔥🔥🔥🔥🔥🔥Response"); debugPrint("Response");
try { try {
Map<String, String> data = { Map<String, String> data = {
'session_id': (session).toString(), 'session_id': (session).toString(),
...@@ -5710,7 +5859,7 @@ class ApiCalling { ...@@ -5710,7 +5859,7 @@ class ApiCalling {
final res = await post(data, EmployeeContactListUrl, {}); final res = await post(data, EmployeeContactListUrl, {});
if (res != null) { if (res != null) {
print("Request Data: $data"); print("Request Data: $data");
debugPrint("🔥🔥🔥🔥🔥🔥🔥 Response: ${res.body}"); debugPrint(" Response: ${res.body}");
return ContactListResponse.fromJson(jsonDecode(res.body)); return ContactListResponse.fromJson(jsonDecode(res.body));
} else { } else {
debugPrint("Null Response"); debugPrint("Null Response");
...@@ -5739,6 +5888,7 @@ class ApiCalling { ...@@ -5739,6 +5888,7 @@ class ApiCalling {
'ref_id': (refId), 'ref_id': (refId),
}; };
debugPrint("Input to QR : $data");
final res = await post(data, createRazorpayUpiQrUrl, {}); final res = await post(data, createRazorpayUpiQrUrl, {});
if (res != null) { if (res != null) {
print(data); print(data);
...@@ -5821,4 +5971,29 @@ class ApiCalling { ...@@ -5821,4 +5971,29 @@ class ApiCalling {
// return null; // return null;
// } // }
// } // }
static Future<CommonResponse?> trackLiveLocationEmpolyeeAPI(empId, session,location) async {
try {
Map<String, String> data = {
'emp_id': (empId).toString(),
'session_id': (session).toString(),
'location': (location),
};
// print(data);
final res = await post(data, liveLocationStatusUrl, {});
if (res != null) {
print("BACKGROUND LOCATION DATA : ${data}");
debugPrint("BACKGROUND LOCATION: ${res.body}");
// print("check_session: ${res.body}");
return CommonResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
} }
const baseUrl = "https://erp.gengroup.in/ci/app/"; const baseUrl = "https://erp.gengroup.in/ci/app/";
const baseUrl_test = "https://erp.gengroup.in/ci/app/Api_home/"; const baseUrl_test = "https://erp.gengroup.in/ci/app/Api_home/";
const trackingUrl = "https://erp.gengroup.in/ci/app/Home/";
// var WEB_SOCKET_URL = "wss://ws.erp.gengroup.in/?type=user&route=employe_live_location_update&session_id=${Sessionid}"; // var WEB_SOCKET_URL = "wss://ws.erp.gengroup.in/?type=user&route=employe_live_location_update&session_id=${Sessionid}";
const getAppVersionUrl = "https://erp.gengroup.in/ci/assets/appversion.json"; const getAppVersionUrl = "https://erp.gengroup.in/ci/assets/appversion.json";
...@@ -31,6 +32,7 @@ const genTrackerRegisterComplaint = "${baseUrl}home/gen_tracker_register_complai ...@@ -31,6 +32,7 @@ const genTrackerRegisterComplaint = "${baseUrl}home/gen_tracker_register_complai
///complaint ///complaint
const complaintsSelectionUrl = "${baseUrl}home/compliants_select_data"; const complaintsSelectionUrl = "${baseUrl}home/compliants_select_data";
const serviceComplaintBillListUrl = "${baseUrl_test}service_complaint_bill_list";
///inventory ///inventory
const inventoryPartDetailsUrl = "${baseUrl}home/inventory_part_details"; const inventoryPartDetailsUrl = "${baseUrl}home/inventory_part_details";
...@@ -51,7 +53,7 @@ const technicianPaymentCollectionOtpUrl= "${baseUrl}home/technician_payment_coll ...@@ -51,7 +53,7 @@ const technicianPaymentCollectionOtpUrl= "${baseUrl}home/technician_payment_coll
const technicianPaymentCollectionUrl= "${baseUrl}home/technician_payment_collection_list"; const technicianPaymentCollectionUrl= "${baseUrl}home/technician_payment_collection_list";
const technicianWalletCollectionUrl= "${baseUrl}home/technician_payment_collection_wallet"; const technicianWalletCollectionUrl= "${baseUrl}home/technician_payment_collection_wallet";
const technicianComplaintDetailsUrl= "${baseUrl}home/technician_complaint_details"; const technicianComplaintDetailsUrl= "${baseUrl}home/technician_complaint_details";
const technicianComplaintFollowUpUrl= "${baseUrl}home/technician_complaint_followup_list"; const technicianComplaintFollowUpUrl= "${baseUrl}Home/technician_complaint_followup_list";
const technicianAddContactUrl= "${baseUrl}home/technician_add_contact"; const technicianAddContactUrl= "${baseUrl}home/technician_add_contact";
const technicianUpdateVisitUrl= "${baseUrl}home/technician_update_visit"; const technicianUpdateVisitUrl= "${baseUrl}home/technician_update_visit";
...@@ -76,6 +78,8 @@ const paymentRequesitionPaymentsReceiptsDetailsUrl = "${baseUrl_test}payment_rec ...@@ -76,6 +78,8 @@ const paymentRequesitionPaymentsReceiptsDetailsUrl = "${baseUrl_test}payment_rec
const paymentRequesitionEditProcessedPaymentUrl = "${baseUrl_test}edit_processes_payment"; const paymentRequesitionEditProcessedPaymentUrl = "${baseUrl_test}edit_processes_payment";
const validateGstNumberUrl = "${baseUrl_test}validate_gst_number"; const validateGstNumberUrl = "${baseUrl_test}validate_gst_number";
const validateBankAccountDetailsUrl = "${baseUrl_test}validate_bank_account_details"; const validateBankAccountDetailsUrl = "${baseUrl_test}validate_bank_account_details";
const commonUpdateAccountDetailsUrl = "${baseUrl_test}common_update_account_details";
///common Module ///common Module
const commonAccessiblePagesUrl = "${baseUrl_test}common_accessible_pages"; const commonAccessiblePagesUrl = "${baseUrl_test}common_accessible_pages";
...@@ -88,6 +92,7 @@ const commonAccountListUrl = "${baseUrl_test}common_account_list_v2"; ...@@ -88,6 +92,7 @@ const commonAccountListUrl = "${baseUrl_test}common_account_list_v2";
const commonAccountLedgerDropDownUrl = "${baseUrl_test}common_account_ledger_list_view"; const commonAccountLedgerDropDownUrl = "${baseUrl_test}common_account_ledger_list_view";
const commonAccountLedgerListWithFilterUrl = "${baseUrl_test}common_account_ledger_list_submit_filter_v2"; const commonAccountLedgerListWithFilterUrl = "${baseUrl_test}common_account_ledger_list_submit_filter_v2";
const commonAccountLedgerAccountDetails = "${baseUrl_test}common_account_details"; const commonAccountLedgerAccountDetails = "${baseUrl_test}common_account_details";
const commonAccountDetailsUrl = "${baseUrl_test}common_account_list_v2";
///order Module ///order Module
const ordersAccessiblePagesUrl = "${baseUrl_test}crm_order_accessible_pages"; const ordersAccessiblePagesUrl = "${baseUrl_test}crm_order_accessible_pages";
...@@ -183,6 +188,7 @@ const crmDashboardQuotationsUrl = "${baseUrl_test}crm_dashboard_quotations_list" ...@@ -183,6 +188,7 @@ const crmDashboardQuotationsUrl = "${baseUrl_test}crm_dashboard_quotations_list"
const ogcharturl = "${baseUrl_test}organisation_structures"; const ogcharturl = "${baseUrl_test}organisation_structures";
const JobDesciptionUrl ="${baseUrl_test}job_description"; const JobDesciptionUrl ="${baseUrl_test}job_description";
const crmNearbyGeneratorsUrl= "${baseUrl_test}crm_nearby_generators";
///HRM ///HRM
//Attendance //Attendance
...@@ -211,4 +217,5 @@ const AdvanceListUrl ="${baseUrl_test}advance_list"; ...@@ -211,4 +217,5 @@ const AdvanceListUrl ="${baseUrl_test}advance_list";
const createRazorpayUpiQrUrl ="${baseUrl_test}create_razorpay_upi_qr"; const createRazorpayUpiQrUrl ="${baseUrl_test}create_razorpay_upi_qr";
const fetchRazorpayUpiQrStatusUrl ="${baseUrl_test}fetch_razorpay_upi_qr_status"; const fetchRazorpayUpiQrStatusUrl ="${baseUrl_test}fetch_razorpay_upi_qr_status";
\ No newline at end of file const liveLocationStatusUrl ="${trackingUrl}sattendance_live_location_update";
\ No newline at end of file
...@@ -445,10 +445,10 @@ packages: ...@@ -445,10 +445,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.2" version: "1.3.3"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
...@@ -598,6 +598,14 @@ packages: ...@@ -598,6 +598,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.12.0" version: "1.12.0"
flutter_foreground_task:
dependency: "direct main"
description:
name: flutter_foreground_task
sha256: "9f1b25a81db95d7119d2c5cffc654048cbdd49d4056183e1beadc1a6a38f3e29"
url: "https://pub.dev"
source: hosted
version: "9.1.0"
flutter_html: flutter_html:
dependency: "direct main" dependency: "direct main"
description: description:
...@@ -1188,26 +1196,26 @@ packages: ...@@ -1188,26 +1196,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.8" version: "11.0.2"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.9" version: "3.0.10"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_testing name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.0.2"
lints: lints:
dependency: transitive dependency: transitive
description: description:
...@@ -1889,10 +1897,10 @@ packages: ...@@ -1889,10 +1897,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.4" version: "0.7.6"
timezone: timezone:
dependency: transitive dependency: transitive
description: description:
...@@ -2033,10 +2041,10 @@ packages: ...@@ -2033,10 +2041,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.2.0"
vm_service: vm_service:
dependency: transitive dependency: transitive
description: description:
...@@ -2134,5 +2142,5 @@ packages: ...@@ -2134,5 +2142,5 @@ packages:
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
sdks: sdks:
dart: ">=3.7.2 <3.10.0-z" dart: ">=3.8.0-0 <3.10.0-z"
flutter: ">=3.27.0" flutter: ">=3.27.0"
...@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev ...@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 1.0.107+115 version: 1.0.108+116
environment: environment:
sdk: ^3.7.2 sdk: ^3.7.2
...@@ -95,6 +95,7 @@ dependencies: ...@@ -95,6 +95,7 @@ dependencies:
photo_view: ^0.14.0 photo_view: ^0.14.0
flutter_contacts: ^1.1.9+2 flutter_contacts: ^1.1.9+2
open_filex: ^4.7.0 open_filex: ^4.7.0
flutter_foreground_task: ^9.1.0
dev_dependencies: dev_dependencies:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment