Fix_partner added in endorsement

This commit is contained in:
sanjeev.p 2026-03-16 18:53:22 +05:30
parent ae1abb3243
commit 001953fbd7
4 changed files with 388 additions and 36 deletions

View File

@ -2088,8 +2088,6 @@ class ApiService {
final url = Uri.parse(
'${Env.apiUrl}reports/endorsement-excel?manager_id=$managerId',
);
print('getPdfDownload endorsement-excel - $url');
await _initializeToken();

View File

@ -91,6 +91,7 @@ class _CreateEndorsementDialogState
bool isStartDateFocused = false;
bool isEndDateFocused = false;
bool isPaymentModeFocused = false;
bool isPartnerFocused = false;
String policyFrom = "Internal";
String financialOrNonFinancial = "Yes";
@ -131,6 +132,7 @@ class _CreateEndorsementDialogState
String? selectedBroker;
String? selectedEndorsementNo;
// String? selectedInsuredName;
String? selectedPartner;
String financialType = "Yes";
// 🔹 LISTS
@ -139,6 +141,10 @@ class _CreateEndorsementDialogState
List<Map<String, dynamic>> endorsementNoList = [];
List<Map<String, dynamic>> insuredNameList = [];
// Partner / Agent lists
List<Map<String, dynamic>> getAgentData = [];
List<Map<String, dynamic>> filteredAgentData = [];
PlatformFile? docUploadedEndorsFile;
List<Map<String, dynamic>> getEndrosmentType = [];
@ -180,6 +186,11 @@ class _CreateEndorsementDialogState
"verification_status": verificationStatus,
};
// Add agent_id only when a Partner has been selected
if (selectedPartner != null && selectedPartner!.toString().isNotEmpty) {
data["agent_id"] = selectedPartner;
}
if (isInternal) {
data.addAll({
"endorsement_no": endorsementNoController.text,
@ -239,28 +250,43 @@ class _CreateEndorsementDialogState
'endDate': TextEditingController(),
};
// FIX move it inside Future.microtask, after providers are read
Future.microtask(() async {
managerId = ref.read(managerIdProvider);
roleId = ref.read(userRoleProvider);
userId = ref.read(userIdProvider);
// FIX: fallback to widget.managerId if provider returns null
final effectiveManagerId = managerId ?? widget.managerId;
print("MID CED - $managerId | widget.managerId - ${widget.managerId} | effective - $effectiveManagerId");
// Set Internal/External flags AFTER state is confirmed
if (mounted) {
setState(() {
isInternal = selectedEndrosementPolicyFrom == 'Internal';
isExternal = selectedEndrosementPolicyFrom == 'External';
});
}
if (userId != null) {
await getStaffList(managerId, userId, roleId);
await getStaffList(effectiveManagerId, userId, roleId);
}
// FIX: always call getAgentList using effectiveManagerId, no null check skip
if (effectiveManagerId != null) {
await getAgentList(effectiveManagerId);
} else {
print("❌ getAgentList NOT called — both managerId and widget.managerId are null");
}
});
if(selectedEndrosementPolicyFrom == 'Internal'){
isInternal = true;
}
_initializeToken();
getEnroementType();
getBroker();
getPaymentMode();
getInsurerDetails();
// getAgent(managerId);
}
@override
@ -406,11 +432,55 @@ class _CreateEndorsementDialogState
print('Error: $e');
} finally {
setState(() {
isLoading = true;
isLoading = false;
});
}
}
Future<void> getAgentList(id) async {
print('getAgentList in create_endorsement_dialog.dart called with manager ID: $id');
if (!mounted) return;
setState(() {
isLoading = true;
});
try {
final response = await apiService.fetchAgentNameDropDown(id);
print('getAgentList response: $response');
if (mounted && response['status'] == 'success' && response['data'] != null) {
setState(() {
getAgentData = List<Map<String, dynamic>>.from(response['data']);
print('getAgentList successful. Data received for dropdown: $getAgentData');
filteredAgentData = List.from(getAgentData);
});
} else {
print('getAgentList failed or returned no data. Status: ${response['status']}');
if (mounted) {
setState(() {
getAgentData = [];
filteredAgentData = [];
});
}
}
} catch (e) {
print('Exception occurred in getAgentList: $e');
if (mounted) {
setState(() {
getAgentData = [];
filteredAgentData = [];
});
}
} finally {
if (mounted) {
setState(() {
isLoading = false;
});
}
}
}
Future<void> getPolicyListSearchData(String val) async {
setState(() {
isLoading = true;
@ -523,7 +593,8 @@ class _CreateEndorsementDialogState
isLoading = true;
});
final id = int.parse(managerId);
// DELETE THIS LINE ENTIRELY
// final id = int.parse(managerId);
// FORCE BOTH VALUES TO STRING
final String managerIdStr = managerId?.toString() ?? '';
final String roleStr = role?.toString() ?? '';
@ -998,7 +1069,8 @@ class _CreateEndorsementDialogState
children: [
Expanded(child: buildContactPersonField(context)),
const SizedBox(width: 20),
Spacer(),
Expanded(child: buildPartnerDropdown(context)),
// Spacer(),
// Expanded(child: buildPendingDaysField(context)),
],
),
@ -1831,6 +1903,18 @@ class _CreateEndorsementDialogState
);
}
Widget buildPartnerField(context) {
return buildCommonTextField(
label: "Partner",
controller: contactPersonController,
isRequired: false,
isFocused: isContactPersonFocused,
onFocusChanged: (val) {
setState(() => isContactPersonFocused = val);
},
);
}
// Widget buildPendingDaysField(context) {
// return buildCommonTextField(
// label: "Pending Days",
@ -2327,7 +2411,7 @@ class _CreateEndorsementDialogState
selectedItem: selectedItem,
items: (f, i) => items,
itemAsString: itemAsString,
compareFn: (item, s) => item['id'].toString() == s['id'].toString(),
compareFn: (item, s) => item['id']?.toString() == s['id']?.toString(),
onChanged: onChanged,
validator: (value) {
@ -2536,6 +2620,7 @@ class _CreateEndorsementDialogState
}
Widget buildBrokerDropdown(context) {
print("******");
Map<String, dynamic>? selectedItem =
brokerList
.where(
@ -2607,6 +2692,113 @@ class _CreateEndorsementDialogState
);
}
Widget buildPartnerDropdown(BuildContext context) {
// FIX: find selected item by id match
Map<String, dynamic>? selectedItem = filteredAgentData.isEmpty
? null
: filteredAgentData
.where(
(item) => item['id']?.toString() == selectedPartner?.toString(),
)
.isNotEmpty
? filteredAgentData.firstWhere(
(item) =>
item['id']?.toString() == selectedPartner?.toString(),
)
: null;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Partner",
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
color: Color(0xFF374151),
),
),
const SizedBox(height: 8),
Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(14)),
child: DropdownSearch<Map<String, dynamic>>(
selectedItem: selectedItem,
items: (f, i) => filteredAgentData,
// FIX: compare by id string safe null guard
compareFn: (item, s) =>
item['id']?.toString() == s['id']?.toString(),
// FIX: use agent_name, fallback to name
itemAsString: (val) {
final code = val['agent_code']?.toString() ?? '';
final name = val['agent_name']?.toString() ??
val['name']?.toString() ??
'';
return code.isNotEmpty ? '$code - $name' : name;
},
onChanged: (val) {
if (val != null) {
setState(() {
selectedPartner = val['id']?.toString();
});
print('Partner selected: id=${val['id']}, name=${val['agent_name'] ?? val['name']}');
}
},
onBeforePopupOpening: (val) async {
setState(() => isPartnerFocused = true);
return true;
},
popupProps: PopupProps.menu(
onDismissed: () => setState(() => isPartnerFocused = false),
showSearchBox: true,
),
decoratorProps: DropDownDecoratorProps(
decoration: InputDecoration(
filled: true,
fillColor: const Color(0xFFF9FAFB),
isDense: true,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 13,
),
helperText: " ",
helperStyle: const TextStyle(height: 0.8),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(
color: Color(0xFFE5E7EB),
width: 1.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(
color: Color(0xFF0F766E),
width: 1.5,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(color: Colors.red, width: 1.5),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(color: Colors.red, width: 1.5),
),
errorStyle: const TextStyle(fontSize: 11, height: 1.3),
),
),
),
),
],
);
}
// ------------------- STyle ---------------------------------
static final TextStyle _textStyle = TextStyle(

View File

@ -100,10 +100,13 @@ class _endorsomentValidationState extends ConsumerState<endorsomentValidation> {
bool isStartDateFocused = false;
bool isEndDateFocused = false;
bool isPaymentModeFocused = false;
bool isPartnerFocused = false;
String policyFrom = "";
String financialOrNonFinancial = "";
String? selectedPartner;
bool isPolicyNumberReadOnly = true;
PlatformFile? uploadedFile;
@ -151,6 +154,10 @@ class _endorsomentValidationState extends ConsumerState<endorsomentValidation> {
List<Map<String, dynamic>> endorsementNoList = [];
List<Map<String, dynamic>> insuredNameList = [];
// Partner / Agent lists
List<Map<String, dynamic>> getAgentData = [];
List<Map<String, dynamic>> filteredAgentData = [];
PlatformFile? docUploadedFile;
PlatformFile? docUploadedEndorsFile;
@ -195,6 +202,11 @@ class _endorsomentValidationState extends ConsumerState<endorsomentValidation> {
"status": lifecycleStatus,
};
// Add agent_id only when a Partner has been selected
if (selectedPartner != null && selectedPartner!.toString().isNotEmpty) {
data["agent_id"] = selectedPartner;
}
if (isInternal) {
data.addAll({
"endorsement_no": endorsementNoController.text,
@ -268,8 +280,14 @@ class _endorsomentValidationState extends ConsumerState<endorsomentValidation> {
roleId = ref.read(userRoleProvider);
userId = ref.read(userIdProvider);
final effectiveManagerId = managerId ?? widget.managerId;
if (userId != null) {
await getStaffList(managerId, userId, roleId);
await getStaffList(effectiveManagerId, userId, roleId);
}
if (effectiveManagerId != null) {
await getAgentList(effectiveManagerId); // wait for list to load
}
});
@ -329,6 +347,7 @@ class _endorsomentValidationState extends ConsumerState<endorsomentValidation> {
selectedEndorsement = item['endorsement_type']?.toString();
selectedEndrosementPolicyFrom = item['policy_from'];
lifecycleStatus = item['status'];
selectedPartner = item['agent_id']?.toString();
financialType = item['financia_or_non_financial'] ?? "Yes";
@ -369,32 +388,24 @@ class _endorsomentValidationState extends ConsumerState<endorsomentValidation> {
}
Future<void> getPaymentMode() async {
print('getPaymentMode called');
try {
final response = await apiService.fetchMasterDropDown('PaymentMode');
if (response['status'] == 200) {
print('getPaymentModeData - ${response['data']}');
setState(() {
getPaymentModeData = List<Map<String, dynamic>>.from(
response['data'],
);
print('API Data - $getPaymentModeData');
getPaymentModeData = List<Map<String, dynamic>>.from(response['data']);
filteredPaymentModeData = List.from(getPaymentModeData);
print('originalData - $filteredPaymentModeData');
// Re-apply selectedPaymentModeId AFTER list is loaded
final savedId = widget.item?['payment_mode_id']?.toString();
if (savedId != null) {
selectedPaymentModeId = savedId;
}
});
} else {
getPaymentModeData = [];
filteredPaymentModeData = [];
}
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
setState(() => isLoading = false);
}
}
@ -455,6 +466,49 @@ class _endorsomentValidationState extends ConsumerState<endorsomentValidation> {
}
}
Future<void> getAgentList(id) async {
print('getAgentList in endorsomentUpdateValidation.dart called with manager ID: $id');
if (!mounted) return;
setState(() {
isLoading = true;
});
try {
final response = await apiService.fetchAgentNameDropDown(id);
print('getAgentList response: $response');
if (mounted && response['status'] == 'success' && response['data'] != null) {
setState(() {
getAgentData = List<Map<String, dynamic>>.from(response['data']);
print('getAgentList successful. Data received for dropdown: $getAgentData');
filteredAgentData = List.from(getAgentData);
});
} else {
print('getAgentList failed or returned no data. Status: ${response['status']}');
if (mounted) {
setState(() {
getAgentData = [];
filteredAgentData = [];
});
}
}
} catch (e) {
print('Exception occurred in getAgentList: $e');
if (mounted) {
setState(() {
getAgentData = [];
filteredAgentData = [];
});
}
} finally {
if (mounted) {
setState(() {
isLoading = false;
});
}
}
}
Future<void> getPolicyListSearchData(String val) async {
try {
final response = await apiService.fetchPolicySearch(val);
@ -530,7 +584,7 @@ class _endorsomentValidationState extends ConsumerState<endorsomentValidation> {
isLoading = true;
});
final id = int.parse(managerId);
// final id = int.parse(managerId);
// FORCE BOTH VALUES TO STRING
final String managerIdStr = managerId?.toString() ?? '';
final String roleStr = role?.toString() ?? '';
@ -1225,7 +1279,8 @@ class _endorsomentValidationState extends ConsumerState<endorsomentValidation> {
),
),
const SizedBox(width: 20),
Spacer(),
Expanded(child: buildPartnerDropdown(context)),
// Spacer(),
// Expanded(child: buildPendingDaysField(context)),
],
),
@ -3209,6 +3264,113 @@ class _endorsomentValidationState extends ConsumerState<endorsomentValidation> {
},
);
}
Widget buildPartnerDropdown(BuildContext context) {
// FIX: find selected item by id match
Map<String, dynamic>? selectedItem = filteredAgentData.isEmpty
? null
: filteredAgentData
.where(
(item) => item['id']?.toString() == selectedPartner?.toString(),
)
.isNotEmpty
? filteredAgentData.firstWhere(
(item) =>
item['id']?.toString() == selectedPartner?.toString(),
)
: null;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Partner",
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
color: Color(0xFF374151),
),
),
const SizedBox(height: 8),
Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(14)),
child: DropdownSearch<Map<String, dynamic>>(
selectedItem: selectedItem,
items: (f, i) => filteredAgentData,
// FIX: compare by id string safe null guard
compareFn: (item, s) =>
item['id']?.toString() == s['id']?.toString(),
// FIX: use agent_name, fallback to name
itemAsString: (val) {
final code = val['agent_code']?.toString() ?? '';
final name = val['name']?.toString() ??
val['name']?.toString() ??
'';
return code.isNotEmpty ? '$code - $name' : name;
},
onChanged: (val) {
if (val != null) {
setState(() {
selectedPartner = val['id']?.toString();
});
print('Partner selected: id=${val['id']}, name=${val['agent_name'] ?? val['name']}');
}
},
onBeforePopupOpening: (val) async {
setState(() => isPartnerFocused = true);
return true;
},
popupProps: PopupProps.menu(
onDismissed: () => setState(() => isPartnerFocused = false),
showSearchBox: true,
),
decoratorProps: DropDownDecoratorProps(
decoration: InputDecoration(
filled: true,
fillColor: const Color(0xFFF9FAFB),
isDense: true,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 13,
),
helperText: " ",
helperStyle: const TextStyle(height: 0.8),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(
color: Color(0xFFE5E7EB),
width: 1.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(
color: Color(0xFF0F766E),
width: 1.5,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(color: Colors.red, width: 1.5),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(color: Colors.red, width: 1.5),
),
errorStyle: const TextStyle(fontSize: 11, height: 1.3),
),
),
),
),
],
);
}
// ------------------- STyle ---------------------------------
static final TextStyle _textStyle = TextStyle(

View File

@ -2210,7 +2210,7 @@ class endosementState extends ConsumerState<Endorsement> {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text('Upload Revised Endorsement Document (PDF)', style: _dataBold),
Text('Upload Endorsement Document (PDF)', style: _dataBold),
const SizedBox(height: 10),
_buildEndorsementUploadField(item),
],
@ -2233,12 +2233,12 @@ class endosementState extends ConsumerState<Endorsement> {
mainAxisSize: MainAxisSize.min,
children: [
// UPLOAD LABEL
Text('Upload Revised Document', style: GoogleFonts.poppins(fontSize: 11, fontWeight: FontWeight.w500)),
Text('Upload Document', style: GoogleFonts.poppins(fontSize: 11, fontWeight: FontWeight.w500)),
const SizedBox(height: 6),
// UPLOAD FIELD
ThemedUploadField(
hintText: selectedFileNames ?? "Upload Revised Document",
hintText: selectedFileNames ?? "Upload Document",
padHorizontal: 4,
padVertical: 5,
fontSZ: 11,