FIX_grid Details - Segment Changes
This commit is contained in:
parent
780159f380
commit
c2521ed76b
File diff suppressed because one or more lines are too long
@ -938,6 +938,7 @@ class ApiService {
|
|||||||
final searchTrimmed = search?.trim();
|
final searchTrimmed = search?.trim();
|
||||||
final loggedIdTrimmed = loggedId?.trim();
|
final loggedIdTrimmed = loggedId?.trim();
|
||||||
final endpoint = Uri.parse('${Env.apiUrl}grid');
|
final endpoint = Uri.parse('${Env.apiUrl}grid');
|
||||||
|
// final endpoint = Uri.parse('http://localhost/nhance_partner_be/grid');
|
||||||
final roleLower = roleTrimmed?.toLowerCase();
|
final roleLower = roleTrimmed?.toLowerCase();
|
||||||
|
|
||||||
final queryParameters = <String, String>{};
|
final queryParameters = <String, String>{};
|
||||||
@ -1086,7 +1087,6 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final url = Uri.parse('${Env.apiUrl}agent/exportRetentionRateExcel');
|
final url = Uri.parse('${Env.apiUrl}agent/exportRetentionRateExcel');
|
||||||
// final url = Uri.parse('http://localhost/nhance_partner_be/agent/exportRetentionRateExcel');
|
|
||||||
final headers = {
|
final headers = {
|
||||||
'Authorization': 'Bearer ${_token ?? ''}',
|
'Authorization': 'Bearer ${_token ?? ''}',
|
||||||
'app-signature': Env.App_Signature,
|
'app-signature': Env.App_Signature,
|
||||||
@ -1122,7 +1122,6 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final url = Uri.parse('${Env.apiUrl}agent/importRetentionRateExcel');
|
final url = Uri.parse('${Env.apiUrl}agent/importRetentionRateExcel');
|
||||||
// final url = Uri.parse('http://localhost/nhance_partner_be/agent/importRetentionRateExcel');
|
|
||||||
try {
|
try {
|
||||||
final request = http.MultipartRequest('POST', url);
|
final request = http.MultipartRequest('POST', url);
|
||||||
request.headers.addAll({
|
request.headers.addAll({
|
||||||
@ -1141,6 +1140,15 @@ class ApiService {
|
|||||||
filename: file.name,
|
filename: file.name,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
// Backward/forward compatibility:
|
||||||
|
// some backend versions expect `retention_rate_excel`.
|
||||||
|
request.files.add(
|
||||||
|
http.MultipartFile.fromBytes(
|
||||||
|
'retention_rate_excel',
|
||||||
|
file.bytes!,
|
||||||
|
filename: file.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
final streamedResponse = await request.send();
|
final streamedResponse = await request.send();
|
||||||
final responseBody = await streamedResponse.stream.bytesToString();
|
final responseBody = await streamedResponse.stream.bytesToString();
|
||||||
@ -1202,6 +1210,7 @@ class ApiService {
|
|||||||
Future<Map<String, dynamic>> updateAgentVehicleRetention({
|
Future<Map<String, dynamic>> updateAgentVehicleRetention({
|
||||||
required String agentId,
|
required String agentId,
|
||||||
required String vehicleTypeId,
|
required String vehicleTypeId,
|
||||||
|
required String segmentId,
|
||||||
required String retentionRate,
|
required String retentionRate,
|
||||||
required String updatedBy,
|
required String updatedBy,
|
||||||
}) async {
|
}) async {
|
||||||
@ -1217,6 +1226,7 @@ class ApiService {
|
|||||||
final body = {
|
final body = {
|
||||||
'agent_id': agentId,
|
'agent_id': agentId,
|
||||||
'vehicle_type_id': vehicleTypeId,
|
'vehicle_type_id': vehicleTypeId,
|
||||||
|
'segment_id': segmentId,
|
||||||
'retention_rate': retentionRate,
|
'retention_rate': retentionRate,
|
||||||
'updated_by': updatedBy,
|
'updated_by': updatedBy,
|
||||||
};
|
};
|
||||||
@ -1547,7 +1557,6 @@ class ApiService {
|
|||||||
if (_token == null) await _initializeToken();
|
if (_token == null) await _initializeToken();
|
||||||
|
|
||||||
final url = Uri.parse('${Env.apiUrl}partner/$id/details');
|
final url = Uri.parse('${Env.apiUrl}partner/$id/details');
|
||||||
// final url = Uri.parse('http://localhost/nhance_partner_be/partner/$id/details');
|
|
||||||
|
|
||||||
final headers = {
|
final headers = {
|
||||||
'Authorization': 'Bearer $_token',
|
'Authorization': 'Bearer $_token',
|
||||||
@ -1563,7 +1572,6 @@ class ApiService {
|
|||||||
if (_token == null) await _initializeToken();
|
if (_token == null) await _initializeToken();
|
||||||
|
|
||||||
final url = Uri.parse('${Env.apiUrl}partner/$id/policies');
|
final url = Uri.parse('${Env.apiUrl}partner/$id/policies');
|
||||||
// final url = Uri.parse('http://localhost/nhance_partner_be/partner/$id/details/policies');
|
|
||||||
|
|
||||||
final headers = {
|
final headers = {
|
||||||
'Authorization': 'Bearer $_token',
|
'Authorization': 'Bearer $_token',
|
||||||
@ -1583,7 +1591,7 @@ class ApiService {
|
|||||||
if (_token == null) await _initializeToken();
|
if (_token == null) await _initializeToken();
|
||||||
|
|
||||||
final url = Uri.parse('${Env.apiUrl}partner/$id/renewals?days=$days');
|
final url = Uri.parse('${Env.apiUrl}partner/$id/renewals?days=$days');
|
||||||
// final url = Uri.parse('http://localhost/nhance_partner_be/partner/$id/renewals?days=$days');
|
|
||||||
final headers = {
|
final headers = {
|
||||||
'Authorization': 'Bearer $_token',
|
'Authorization': 'Bearer $_token',
|
||||||
'app-signature': Env.App_Signature,
|
'app-signature': Env.App_Signature,
|
||||||
@ -1604,7 +1612,7 @@ class ApiService {
|
|||||||
? '?financial_year=${Uri.encodeQueryComponent(financialYear)}'
|
? '?financial_year=${Uri.encodeQueryComponent(financialYear)}'
|
||||||
: '';
|
: '';
|
||||||
final url = Uri.parse('${Env.apiUrl}partner/$id/earnings$q');
|
final url = Uri.parse('${Env.apiUrl}partner/$id/earnings$q');
|
||||||
// final url = Uri.parse('http://localhost/nhance_partner_be/partner/$id/earnings$q');
|
|
||||||
final headers = {
|
final headers = {
|
||||||
'Authorization': 'Bearer $_token',
|
'Authorization': 'Bearer $_token',
|
||||||
'app-signature': Env.App_Signature,
|
'app-signature': Env.App_Signature,
|
||||||
@ -2145,9 +2153,6 @@ class ApiService {
|
|||||||
'${Env.apiUrl}invoice/getAgentUnusedCommissionList?manager_id=$id',
|
'${Env.apiUrl}invoice/getAgentUnusedCommissionList?manager_id=$id',
|
||||||
);
|
);
|
||||||
|
|
||||||
// url = Uri.parse(
|
|
||||||
// 'http://localhost/nhance_partner_be/invoice/getAgentUnusedCommissionList?manager_id=$id',
|
|
||||||
// );
|
|
||||||
|
|
||||||
print('fetchAGENTNameDropDown 2');
|
print('fetchAGENTNameDropDown 2');
|
||||||
final headers = {
|
final headers = {
|
||||||
@ -2288,7 +2293,7 @@ class ApiService {
|
|||||||
if (managerId != null) 'manager_id': managerId.toString(),
|
if (managerId != null) 'manager_id': managerId.toString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
final url = Uri.parse('${Env.apiUrl}invoice/view').replace(
|
final url = Uri.parse('${Env.apiUrl}invoice/view').replace(
|
||||||
queryParameters: queryParameters,
|
queryParameters: queryParameters,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2302,7 +2307,6 @@ class ApiService {
|
|||||||
|
|
||||||
Future<Map<String, dynamic>> getCreateOrUpdate(data) async {
|
Future<Map<String, dynamic>> getCreateOrUpdate(data) async {
|
||||||
final url = Uri.parse('${Env.apiUrl}invoice/create-or-update');
|
final url = Uri.parse('${Env.apiUrl}invoice/create-or-update');
|
||||||
// final url = Uri.parse('http://localhost/nhance_partner_be/invoice/create-or-update');
|
|
||||||
|
|
||||||
// final token = await getToken(); // Fetch token
|
// final token = await getToken(); // Fetch token
|
||||||
|
|
||||||
|
|||||||
@ -188,14 +188,14 @@ class AgentListState extends ConsumerState<AgentList> {
|
|||||||
if (mounted) {
|
if (mounted) {
|
||||||
ToastHelper.showSuccessToast(
|
ToastHelper.showSuccessToast(
|
||||||
context,
|
context,
|
||||||
'Rentation rate Excel downloaded',
|
'Retention rate Excel downloaded',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ToastHelper.showErrorToast(
|
ToastHelper.showErrorToast(
|
||||||
context,
|
context,
|
||||||
'Failed to export rentation rate Excel',
|
'Failed to export retention rate Excel',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,26 +234,30 @@ class AgentListState extends ConsumerState<AgentList> {
|
|||||||
final report = (response['report'] is Map<String, dynamic>)
|
final report = (response['report'] is Map<String, dynamic>)
|
||||||
? response['report'] as Map<String, dynamic>
|
? response['report'] as Map<String, dynamic>
|
||||||
: <String, dynamic>{};
|
: <String, dynamic>{};
|
||||||
|
final inserted = (data['inserted_cells'] ?? data['inserted_rows'] ?? 0);
|
||||||
final inserted = (data['inserted_cells'] ?? 0).toString();
|
final updated = (data['updated_cells'] ?? data['updated_rows'] ?? 0);
|
||||||
final updated = (data['updated_cells'] ?? 0).toString();
|
final message = (response['message'] ?? '').toString().trim();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ToastHelper.showSuccessToast(
|
ToastHelper.showSuccessToast(
|
||||||
context,
|
context,
|
||||||
'Import done',
|
message.isNotEmpty
|
||||||
|
? message
|
||||||
|
: 'Import done (Inserted: $inserted, Updated: $updated)',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// doN'T Delete This function And Related Codes
|
if (report.isNotEmpty && mounted) {
|
||||||
// await _showRetentionImportReportDialog(data, report);
|
await _showRetentionImportReportDialog(data, report);
|
||||||
|
}
|
||||||
refresh();
|
refresh();
|
||||||
} else {
|
} else {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ToastHelper.showErrorToast(context, 'Import failed');
|
final message = (response['message'] ?? 'Import failed').toString();
|
||||||
|
ToastHelper.showErrorToast(context, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ToastHelper.showErrorToast(context, 'Failed to import rentation rate');
|
ToastHelper.showErrorToast(context, 'Failed to import retention rate');
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@ -285,7 +289,14 @@ class AgentListState extends ConsumerState<AgentList> {
|
|||||||
.where((e) => e.trim().isNotEmpty)
|
.where((e) => e.trim().isNotEmpty)
|
||||||
.toList() ??
|
.toList() ??
|
||||||
<String>[];
|
<String>[];
|
||||||
final unknownVehicleTypes = (report['unknown_vehicle_types'] as List?)
|
final unknownVehicleTypes = ((report['unknown_vehicle_types'] ??
|
||||||
|
report['unknown_vehicle_type_names']) as List?)
|
||||||
|
?.map((e) => e.toString())
|
||||||
|
.where((e) => e.trim().isNotEmpty)
|
||||||
|
.toList() ??
|
||||||
|
<String>[];
|
||||||
|
final unknownSegments = ((report['unknown_segments'] ??
|
||||||
|
report['unknown_segment_names']) as List?)
|
||||||
?.map((e) => e.toString())
|
?.map((e) => e.toString())
|
||||||
.where((e) => e.trim().isNotEmpty)
|
.where((e) => e.trim().isNotEmpty)
|
||||||
.toList() ??
|
.toList() ??
|
||||||
@ -322,13 +333,22 @@ class AgentListState extends ConsumerState<AgentList> {
|
|||||||
|
|
||||||
if (unknownVehicleTypes.isNotEmpty) {
|
if (unknownVehicleTypes.isNotEmpty) {
|
||||||
lines.add('');
|
lines.add('');
|
||||||
lines.add('Unknown Vehicle Types (header):');
|
lines.add('Unknown Vehicle Types:');
|
||||||
lines.addAll(unknownVehicleTypes.take(10).map((e) => '- $e'));
|
lines.addAll(unknownVehicleTypes.take(10).map((e) => '- $e'));
|
||||||
if (unknownVehicleTypes.length > 10) {
|
if (unknownVehicleTypes.length > 10) {
|
||||||
lines.add('- ...and ${unknownVehicleTypes.length - 10} more');
|
lines.add('- ...and ${unknownVehicleTypes.length - 10} more');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unknownSegments.isNotEmpty) {
|
||||||
|
lines.add('');
|
||||||
|
lines.add('Unknown Segments:');
|
||||||
|
lines.addAll(unknownSegments.take(10).map((e) => '- $e'));
|
||||||
|
if (unknownSegments.length > 10) {
|
||||||
|
lines.add('- ...and ${unknownSegments.length - 10} more');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (invalidCells.isNotEmpty) {
|
if (invalidCells.isNotEmpty) {
|
||||||
lines.add('');
|
lines.add('');
|
||||||
lines.add('Invalid Cell Samples:');
|
lines.add('Invalid Cell Samples:');
|
||||||
@ -366,7 +386,7 @@ class AgentListState extends ConsumerState<AgentList> {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('Rentation Rate Import Report'),
|
title: const Text('Retention Rate Import Report'),
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
width: 520,
|
width: 520,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
|
|||||||
@ -75,7 +75,7 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
|||||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||||
|
|
||||||
List<Map<String, dynamic>> _vehicleTypes = [];
|
List<Map<String, dynamic>> _vehicleTypes = [];
|
||||||
final Map<int, TextEditingController> _rateControllers = {};
|
final Map<String, TextEditingController> _rateControllers = {};
|
||||||
|
|
||||||
List<Map<String, dynamic>> _salesExecutives = [];
|
List<Map<String, dynamic>> _salesExecutives = [];
|
||||||
dynamic _selectedSalesExecutiveId;
|
dynamic _selectedSalesExecutiveId;
|
||||||
@ -157,35 +157,46 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initRateControllersFromTypes({Map<int, String>? initial}) {
|
String _retentionKey(int vehicleTypeId, int segmentId) => '${vehicleTypeId}_$segmentId';
|
||||||
|
|
||||||
|
int _readInt(dynamic value) => int.tryParse(value?.toString() ?? '') ?? 0;
|
||||||
|
|
||||||
|
String _displayVehicleSegment(Map<String, dynamic> vt) {
|
||||||
|
final name = (vt['vehicle_type'] ?? '-').toString();
|
||||||
|
final segment = (vt['segment'] ?? '').toString().trim();
|
||||||
|
if (segment.isEmpty) return name;
|
||||||
|
return '$name - $segment';
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initRateControllersFromTypes({Map<String, String>? initial}) {
|
||||||
for (final c in _rateControllers.values) {
|
for (final c in _rateControllers.values) {
|
||||||
c.dispose();
|
c.dispose();
|
||||||
}
|
}
|
||||||
_rateControllers.clear();
|
_rateControllers.clear();
|
||||||
for (final vt in _vehicleTypes) {
|
for (final vt in _vehicleTypes) {
|
||||||
final id = int.tryParse(vt['id'].toString()) ?? 0;
|
final vehicleTypeId = _readInt(vt['vehicle_type_id'] ?? vt['id']);
|
||||||
if (id == 0) continue;
|
final segmentId = _readInt(vt['segment_id']);
|
||||||
final text = initial?[id] ?? '';
|
if (vehicleTypeId == 0 || segmentId == 0) continue;
|
||||||
_rateControllers[id] = TextEditingController(text: text);
|
final key = _retentionKey(vehicleTypeId, segmentId);
|
||||||
|
final text = initial?[key] ?? '';
|
||||||
|
_rateControllers[key] = TextEditingController(text: text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds vehicle_type_id → retention % text from [findAgent] (or compatible) payload.
|
/// Builds (vehicle_type_id + segment_id) → retention % text from [findAgent].
|
||||||
Map<int, String> _retentionMapFromAgentData(Map<String, dynamic> data) {
|
Map<String, String> _retentionMapFromAgentData(Map<String, dynamic> data) {
|
||||||
final Map<int, String> initial = {};
|
final Map<String, String> initial = {};
|
||||||
final raw = data['retention_by_vehicle'] ?? data['retentionByVehicle'];
|
final raw = data['retention_by_vehicle'] ?? data['retentionByVehicle'];
|
||||||
if (raw is! List) return initial;
|
if (raw is! List) return initial;
|
||||||
for (final row in raw) {
|
for (final row in raw) {
|
||||||
if (row is! Map) continue;
|
if (row is! Map) continue;
|
||||||
final m = Map<String, dynamic>.from(row);
|
final m = Map<String, dynamic>.from(row);
|
||||||
final vid = int.tryParse(
|
final vid = _readInt(m['vehicle_type_id'] ?? m['vehicleTypeId']);
|
||||||
(m['vehicle_type_id'] ?? m['vehicleTypeId'] ?? '').toString(),
|
final sid = _readInt(m['segment_id'] ?? m['segmentId']);
|
||||||
) ??
|
if (vid == 0 || sid == 0) continue;
|
||||||
0;
|
|
||||||
if (vid == 0) continue;
|
|
||||||
final rate = m['retention_rate'] ?? m['retentionRate'];
|
final rate = m['retention_rate'] ?? m['retentionRate'];
|
||||||
if (rate == null) continue;
|
if (rate == null) continue;
|
||||||
initial[vid] = rate.toString();
|
initial[_retentionKey(vid, sid)] = rate.toString();
|
||||||
}
|
}
|
||||||
return initial;
|
return initial;
|
||||||
}
|
}
|
||||||
@ -238,9 +249,13 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
|||||||
List<Map<String, dynamic>> get _filteredVehicleTypes {
|
List<Map<String, dynamic>> get _filteredVehicleTypes {
|
||||||
final q = _retentionSearchCtrl.text.trim().toLowerCase();
|
final q = _retentionSearchCtrl.text.trim().toLowerCase();
|
||||||
if (q.isEmpty) return _vehicleTypes;
|
if (q.isEmpty) return _vehicleTypes;
|
||||||
return _vehicleTypes
|
final terms = q.split(RegExp(r'\s+')).where((t) => t.isNotEmpty).toList();
|
||||||
.where((vt) => (vt['vehicle_type'] ?? '').toString().toLowerCase().contains(q))
|
return _vehicleTypes.where((vt) {
|
||||||
.toList();
|
final vehicle = (vt['vehicle_type'] ?? '').toString().toLowerCase();
|
||||||
|
final segment = (vt['segment'] ?? '').toString().toLowerCase();
|
||||||
|
final searchable = '$vehicle $segment';
|
||||||
|
return terms.every(searchable.contains);
|
||||||
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -261,16 +276,19 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
|||||||
List<Map<String, dynamic>> _collectBulkRetentionPayload() {
|
List<Map<String, dynamic>> _collectBulkRetentionPayload() {
|
||||||
final out = <Map<String, dynamic>>[];
|
final out = <Map<String, dynamic>>[];
|
||||||
for (final vt in _vehicleTypes) {
|
for (final vt in _vehicleTypes) {
|
||||||
final id = int.tryParse(vt['id'].toString()) ?? 0;
|
final vehicleTypeId = _readInt(vt['vehicle_type_id'] ?? vt['id']);
|
||||||
if (id == 0) continue;
|
final segmentId = _readInt(vt['segment_id']);
|
||||||
final ctrl = _rateControllers[id];
|
if (vehicleTypeId == 0 || segmentId == 0) continue;
|
||||||
|
final key = _retentionKey(vehicleTypeId, segmentId);
|
||||||
|
final ctrl = _rateControllers[key];
|
||||||
if (ctrl == null) continue;
|
if (ctrl == null) continue;
|
||||||
final t = ctrl.text.trim();
|
final t = ctrl.text.trim();
|
||||||
if (t.isEmpty) continue;
|
if (t.isEmpty) continue;
|
||||||
final v = double.tryParse(t);
|
final v = double.tryParse(t);
|
||||||
if (v == null || v < 0 || v > 100) continue;
|
if (v == null || v < 0 || v > 100) continue;
|
||||||
out.add({
|
out.add({
|
||||||
'vehicle_type_id': id,
|
'vehicle_type_id': vehicleTypeId,
|
||||||
|
'segment_id': segmentId,
|
||||||
'retention_rate': v,
|
'retention_rate': v,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -360,7 +378,7 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _saveRowRetention(int vehicleTypeId) async {
|
Future<void> _saveRowRetention(Map<String, dynamic> vt) async {
|
||||||
if (!_retentionTableEnabled) {
|
if (!_retentionTableEnabled) {
|
||||||
ToastHelper.showWarningToast(
|
ToastHelper.showWarningToast(
|
||||||
context,
|
context,
|
||||||
@ -369,7 +387,14 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_savingAllRetention) return;
|
if (_savingAllRetention) return;
|
||||||
final ctrl = _rateControllers[vehicleTypeId];
|
final vehicleTypeId = _readInt(vt['vehicle_type_id'] ?? vt['id']);
|
||||||
|
final segmentId = _readInt(vt['segment_id']);
|
||||||
|
if (vehicleTypeId == 0 || segmentId == 0) {
|
||||||
|
ToastHelper.showErrorToast(context, 'Vehicle type or segment missing');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final key = _retentionKey(vehicleTypeId, segmentId);
|
||||||
|
final ctrl = _rateControllers[key];
|
||||||
if (ctrl == null) return;
|
if (ctrl == null) return;
|
||||||
final t = ctrl.text.trim();
|
final t = ctrl.text.trim();
|
||||||
if (t.isEmpty) {
|
if (t.isEmpty) {
|
||||||
@ -391,6 +416,7 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
|||||||
final res = await _api.updateAgentVehicleRetention(
|
final res = await _api.updateAgentVehicleRetention(
|
||||||
agentId: _loadedAgentPk!,
|
agentId: _loadedAgentPk!,
|
||||||
vehicleTypeId: '$vehicleTypeId',
|
vehicleTypeId: '$vehicleTypeId',
|
||||||
|
segmentId: '$segmentId',
|
||||||
retentionRate: t,
|
retentionRate: t,
|
||||||
updatedBy: userId,
|
updatedBy: userId,
|
||||||
);
|
);
|
||||||
@ -1126,7 +1152,7 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: 220,
|
width: 220,
|
||||||
child: ThemedSearchField(
|
child: ThemedSearchField(
|
||||||
hintText: 'Search vehicle type',
|
hintText: 'Search vehicle type and segment',
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
txtHeight: 34,
|
txtHeight: 34,
|
||||||
controller: _retentionSearchCtrl,
|
controller: _retentionSearchCtrl,
|
||||||
@ -1267,9 +1293,11 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _retentionMobileCard(Map<String, dynamic> vt) {
|
Widget _retentionMobileCard(Map<String, dynamic> vt) {
|
||||||
final id = int.tryParse(vt['id'].toString()) ?? 0;
|
final vehicleTypeId = _readInt(vt['vehicle_type_id'] ?? vt['id']);
|
||||||
final name = (vt['vehicle_type'] ?? '-').toString();
|
final segmentId = _readInt(vt['segment_id']);
|
||||||
final ctrl = _rateControllers[id];
|
final key = _retentionKey(vehicleTypeId, segmentId);
|
||||||
|
final name = _displayVehicleSegment(vt);
|
||||||
|
final ctrl = _rateControllers[key];
|
||||||
if (ctrl == null) return const SizedBox.shrink();
|
if (ctrl == null) return const SizedBox.shrink();
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
@ -1305,7 +1333,7 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
|||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: (_retentionTableEnabled && !_retentionActionsBusy)
|
onPressed: (_retentionTableEnabled && !_retentionActionsBusy)
|
||||||
? () => _saveRowRetention(id)
|
? () => _saveRowRetention(vt)
|
||||||
: null,
|
: null,
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: const Color(0xFF2E7D6E),
|
foregroundColor: const Color(0xFF2E7D6E),
|
||||||
@ -1337,7 +1365,7 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 5,
|
flex: 5,
|
||||||
child: Text(
|
child: Text(
|
||||||
'Vehicle type',
|
'Vehicle type / Segment',
|
||||||
style: _headerStyle,
|
style: _headerStyle,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@ -1365,9 +1393,11 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _retentionDataRow(Map<String, dynamic> vt) {
|
Widget _retentionDataRow(Map<String, dynamic> vt) {
|
||||||
final id = int.tryParse(vt['id'].toString()) ?? 0;
|
final vehicleTypeId = _readInt(vt['vehicle_type_id'] ?? vt['id']);
|
||||||
final name = (vt['vehicle_type'] ?? '-').toString();
|
final segmentId = _readInt(vt['segment_id']);
|
||||||
final ctrl = _rateControllers[id];
|
final key = _retentionKey(vehicleTypeId, segmentId);
|
||||||
|
final name = _displayVehicleSegment(vt);
|
||||||
|
final ctrl = _rateControllers[key];
|
||||||
if (ctrl == null) return const SizedBox.shrink();
|
if (ctrl == null) return const SizedBox.shrink();
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
@ -1409,7 +1439,7 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
|||||||
child: Center(
|
child: Center(
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: (_retentionTableEnabled && !_retentionActionsBusy)
|
onPressed: (_retentionTableEnabled && !_retentionActionsBusy)
|
||||||
? () => _saveRowRetention(id)
|
? () => _saveRowRetention(vt)
|
||||||
: null,
|
: null,
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: const Color(0xFF2E7D6E),
|
foregroundColor: const Color(0xFF2E7D6E),
|
||||||
|
|||||||
@ -444,6 +444,7 @@ class _RenewalAlertsCard extends StatelessWidget {
|
|||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text('Renewal Alerts', style: _chartHeader),
|
Text('Renewal Alerts', style: _chartHeader),
|
||||||
Text(
|
Text(
|
||||||
@ -512,6 +513,7 @@ class _RenewalAlertsCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
SizedBox(width: 40, child: Text('S.No', style: _tableHeadStyle)),
|
SizedBox(width: 40, child: Text('S.No', style: _tableHeadStyle)),
|
||||||
Expanded(flex: 2, child: Text('Policy No.', style: _tableHeadStyle)),
|
Expanded(flex: 2, child: Text('Policy No.', style: _tableHeadStyle)),
|
||||||
|
Expanded(flex: 2, child: Text('Vehicle Type', style: _tableHeadStyle)),
|
||||||
Expanded(flex: 2, child: Text('Policy Holder Name', style: _tableHeadStyle)),
|
Expanded(flex: 2, child: Text('Policy Holder Name', style: _tableHeadStyle)),
|
||||||
Expanded(flex: 2, child: Text('Expiry Date', style: _tableHeadStyle)),
|
Expanded(flex: 2, child: Text('Expiry Date', style: _tableHeadStyle)),
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -587,6 +589,14 @@ class _RenewalTableRow extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Text(
|
||||||
|
renewal['vehicle_type']?.toString() ?? '--',
|
||||||
|
style: _tableTeal,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|||||||
@ -182,6 +182,9 @@ class _InvoiceListState extends ConsumerState<InvoiceList> {
|
|||||||
(item['invoice_no'] ?? '-').toLowerCase().contains(
|
(item['invoice_no'] ?? '-').toLowerCase().contains(
|
||||||
query.toLowerCase(),
|
query.toLowerCase(),
|
||||||
) ||
|
) ||
|
||||||
|
(item['vehicle_types'] ?? '-').toLowerCase().contains(
|
||||||
|
query.toLowerCase(),
|
||||||
|
) ||
|
||||||
(item['invoice_amount_indian_format'] ?? '-')
|
(item['invoice_amount_indian_format'] ?? '-')
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.contains(query.toLowerCase()) ||
|
.contains(query.toLowerCase()) ||
|
||||||
@ -742,6 +745,7 @@ class _InvoiceListState extends ConsumerState<InvoiceList> {
|
|||||||
displayHeaders: [
|
displayHeaders: [
|
||||||
"S.No",
|
"S.No",
|
||||||
"Invoice No",
|
"Invoice No",
|
||||||
|
"Vehicle Type",
|
||||||
"Invoice Date",
|
"Invoice Date",
|
||||||
"Broker Name",
|
"Broker Name",
|
||||||
"POS Name",
|
"POS Name",
|
||||||
@ -756,6 +760,7 @@ class _InvoiceListState extends ConsumerState<InvoiceList> {
|
|||||||
keys: [
|
keys: [
|
||||||
"id",
|
"id",
|
||||||
"invoice_no",
|
"invoice_no",
|
||||||
|
"vehicle_types",
|
||||||
"invoice_date",
|
"invoice_date",
|
||||||
"broker_name",
|
"broker_name",
|
||||||
"pos_name",
|
"pos_name",
|
||||||
@ -789,6 +794,10 @@ class _InvoiceListState extends ConsumerState<InvoiceList> {
|
|||||||
flex: 3,
|
flex: 3,
|
||||||
child: Text('Invoice No', style: _headerStyle),
|
child: Text('Invoice No', style: _headerStyle),
|
||||||
),
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Text('Vehicle Type', style: _headerStyle),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: Text('Broker Name', style: _headerStyle),
|
child: Text('Broker Name', style: _headerStyle),
|
||||||
@ -885,6 +894,15 @@ class _InvoiceListState extends ConsumerState<InvoiceList> {
|
|||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Text(
|
||||||
|
_displayText(item['vehicle_types']),
|
||||||
|
style: _dataBold,
|
||||||
|
softWrap: true,
|
||||||
|
maxLines: 3,
|
||||||
|
),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|||||||
@ -137,6 +137,7 @@ class _PayOutDetailsViewState extends ConsumerState<PayOutDetailsView> {
|
|||||||
}
|
}
|
||||||
filteredPolicies = masterPolicies.where((item) {
|
filteredPolicies = masterPolicies.where((item) {
|
||||||
return (item['policy_no'] ?? '').toString().contains(q) ||
|
return (item['policy_no'] ?? '').toString().contains(q) ||
|
||||||
|
(item['vehicle_type'] ?? '').toString().contains(q) ||
|
||||||
(item['customer_name'] ?? '').toString().contains(q) ||
|
(item['customer_name'] ?? '').toString().contains(q) ||
|
||||||
(item['agent_name'] ?? '').toString().contains(q) ||
|
(item['agent_name'] ?? '').toString().contains(q) ||
|
||||||
(item['insurer_name'] ?? '').toString().contains(q) ||
|
(item['insurer_name'] ?? '').toString().contains(q) ||
|
||||||
@ -186,10 +187,11 @@ class _PayOutDetailsViewState extends ConsumerState<PayOutDetailsView> {
|
|||||||
List<double> _columnWidths(double maxWidth) {
|
List<double> _columnWidths(double maxWidth) {
|
||||||
const sidePadding = 32.0; // 16 left + 16 right
|
const sidePadding = 32.0; // 16 left + 16 right
|
||||||
final usable = (maxWidth - sidePadding).clamp(720.0, 2000.0);
|
final usable = (maxWidth - sidePadding).clamp(720.0, 2000.0);
|
||||||
const totalFlex = 2 + 14 + 10 + 20 + 16 + 11 + 11;
|
const totalFlex = 2 + 14 + 12 + 10 + 20 + 16 + 11 + 11;
|
||||||
return [
|
return [
|
||||||
usable * (2 / totalFlex), // S.NO
|
usable * (2 / totalFlex), // S.NO
|
||||||
usable * (14 / totalFlex), // POLICY NO
|
usable * (14 / totalFlex), // POLICY NO
|
||||||
|
usable * (12 / totalFlex), // VEHICLE TYPE
|
||||||
usable * (10 / totalFlex), // POLICY DATE
|
usable * (10 / totalFlex), // POLICY DATE
|
||||||
usable * (20 / totalFlex), // PARTNER
|
usable * (20 / totalFlex), // PARTNER
|
||||||
usable * (16 / totalFlex), // CUSTOMER
|
usable * (16 / totalFlex), // CUSTOMER
|
||||||
@ -252,11 +254,12 @@ class _PayOutDetailsViewState extends ConsumerState<PayOutDetailsView> {
|
|||||||
children: [
|
children: [
|
||||||
_headerText('S.NO', w[0]),
|
_headerText('S.NO', w[0]),
|
||||||
_headerText('POLICY NO', w[1]),
|
_headerText('POLICY NO', w[1]),
|
||||||
_headerText('POLICY DATE', w[2]),
|
_headerText('VEHICLE TYPE', w[2]),
|
||||||
_headerText('PARTNER', w[3]),
|
_headerText('POLICY DATE', w[3]),
|
||||||
_headerText('CUSTOMER', w[4]),
|
_headerText('PARTNER', w[4]),
|
||||||
_headerText('PREMIUM (₹)', w[5], align: TextAlign.right),
|
_headerText('CUSTOMER', w[5]),
|
||||||
_headerText('PAYOUT (₹)', w[6], align: TextAlign.right),
|
_headerText('PREMIUM (₹)', w[6], align: TextAlign.right),
|
||||||
|
_headerText('PAYOUT (₹)', w[7], align: TextAlign.right),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -274,22 +277,23 @@ class _PayOutDetailsViewState extends ConsumerState<PayOutDetailsView> {
|
|||||||
children: [
|
children: [
|
||||||
_cell('${index + 1}', w[0]),
|
_cell('${index + 1}', w[0]),
|
||||||
_cell(p['policy_no']?.toString(), w[1]),
|
_cell(p['policy_no']?.toString(), w[1]),
|
||||||
_cell(_formatDate(p['issued_date']?.toString()), w[2]),
|
_cell(p['vehicle_type']?.toString(), w[2]),
|
||||||
|
_cell(_formatDate(p['issued_date']?.toString()), w[3]),
|
||||||
_cell(
|
_cell(
|
||||||
(p['agent_code'] != null && p['agent_name'] != null)
|
(p['agent_code'] != null && p['agent_name'] != null)
|
||||||
? '${p['agent_code']} - ${p['agent_name']}'
|
? '${p['agent_code']} - ${p['agent_name']}'
|
||||||
: (p['agent_name'] ?? '-').toString(),
|
: (p['agent_name'] ?? '-').toString(),
|
||||||
w[3],
|
w[4],
|
||||||
),
|
),
|
||||||
_cell(p['customer_name']?.toString(), w[4]),
|
_cell(p['customer_name']?.toString(), w[5]),
|
||||||
_cell(
|
_cell(
|
||||||
_amountText(p['premium_amount']),
|
_amountText(p['premium_amount']),
|
||||||
w[5],
|
w[6],
|
||||||
align: TextAlign.right,
|
align: TextAlign.right,
|
||||||
),
|
),
|
||||||
_cell(
|
_cell(
|
||||||
_amountText(p['commission_amount']),
|
_amountText(p['commission_amount']),
|
||||||
w[6],
|
w[7],
|
||||||
align: TextAlign.right,
|
align: TextAlign.right,
|
||||||
color: const Color(0xFF009B77),
|
color: const Color(0xFF009B77),
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@ -321,6 +325,7 @@ class _PayOutDetailsViewState extends ConsumerState<PayOutDetailsView> {
|
|||||||
return {
|
return {
|
||||||
'sno': 0,
|
'sno': 0,
|
||||||
'policy_no': p['policy_no'],
|
'policy_no': p['policy_no'],
|
||||||
|
'vehicle_type': p['vehicle_type'],
|
||||||
'policy_date': _formatDate(p['issued_date']?.toString()),
|
'policy_date': _formatDate(p['issued_date']?.toString()),
|
||||||
'partner': (p['agent_code'] != null && p['agent_name'] != null)
|
'partner': (p['agent_code'] != null && p['agent_name'] != null)
|
||||||
? '${p['agent_code']} - ${p['agent_name']}'
|
? '${p['agent_code']} - ${p['agent_name']}'
|
||||||
@ -431,6 +436,7 @@ class _PayOutDetailsViewState extends ConsumerState<PayOutDetailsView> {
|
|||||||
displayHeaders: const [
|
displayHeaders: const [
|
||||||
'S.NO',
|
'S.NO',
|
||||||
'POLICY NO',
|
'POLICY NO',
|
||||||
|
'VEHICLE TYPE',
|
||||||
'POLICY DATE',
|
'POLICY DATE',
|
||||||
'PARTNER',
|
'PARTNER',
|
||||||
'CUSTOMER',
|
'CUSTOMER',
|
||||||
@ -440,6 +446,7 @@ class _PayOutDetailsViewState extends ConsumerState<PayOutDetailsView> {
|
|||||||
keys: const [
|
keys: const [
|
||||||
'sno',
|
'sno',
|
||||||
'policy_no',
|
'policy_no',
|
||||||
|
'vehicle_type',
|
||||||
'policy_date',
|
'policy_date',
|
||||||
'partner',
|
'partner',
|
||||||
'customer',
|
'customer',
|
||||||
|
|||||||
22
pubspec.lock
22
pubspec.lock
@ -117,10 +117,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: characters
|
name: characters
|
||||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.1"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -780,26 +780,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.17"
|
version: "0.12.19"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.1"
|
version: "0.13.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.0"
|
version: "1.17.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1297,10 +1297,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
|
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.6"
|
version: "0.7.10"
|
||||||
toastification:
|
toastification:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1502,5 +1502,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.8.1 <4.0.0"
|
dart: ">=3.9.0-0 <4.0.0"
|
||||||
flutter: ">=3.32.0"
|
flutter: ">=3.32.0"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user