This commit is contained in:
sanjeev.p 2026-04-04 18:09:28 +05:30
parent 6c91f141d5
commit aebb7271d0
4 changed files with 105 additions and 137 deletions

View File

@ -2192,13 +2192,26 @@ class ApiService {
return response; return response;
} }
Future<Map<String, dynamic>> getPayoutList() async { Future<Map<String, dynamic>> getPayoutList({
String? fromDate,
String? toDate,
}) async {
// print(_token); // print(_token);
if (_token == null) { if (_token == null) {
await _initializeToken(); await _initializeToken();
} }
final url = Uri.parse('${Env.apiUrl}invoice/list'); final Uri url;
if (fromDate != null &&
fromDate.isNotEmpty &&
toDate != null &&
toDate.isNotEmpty) {
url = Uri.parse('${Env.apiUrl}invoice/list').replace(
queryParameters: {'from_date': fromDate, 'to_date': toDate},
);
} else {
url = Uri.parse('${Env.apiUrl}invoice/list');
}
final headers = { final headers = {
'Authorization': 'Bearer $_token' ?? '', 'Authorization': 'Bearer $_token' ?? '',

View File

@ -70,7 +70,12 @@ class _PayoutListState extends ConsumerState<PayoutList> {
}); });
try { try {
final response = await apiService.getPayoutList(); final from = controllers['startDate']?.text.trim() ?? '';
final to = controllers['endDate']?.text.trim() ?? '';
final response = await apiService.getPayoutList(
fromDate: from.isNotEmpty ? from : null,
toDate: to.isNotEmpty ? to : null,
);
if (response['status'] == 'success') { if (response['status'] == 'success') {
print('PL 3 => getPayoutList - ${response['data']}'); print('PL 3 => getPayoutList - ${response['data']}');
@ -78,7 +83,8 @@ class _PayoutListState extends ConsumerState<PayoutList> {
print(' PL 4 => getPayoutList => ${response['data']}'); print(' PL 4 => getPayoutList => ${response['data']}');
setState(() { setState(() {
currentPage = 1;
if (data is List) { if (data is List) {
// Already a list of maps // Already a list of maps
getPayoutData = List<Map<String, dynamic>>.from(data); getPayoutData = List<Map<String, dynamic>>.from(data);
@ -145,7 +151,9 @@ class _PayoutListState extends ConsumerState<PayoutList> {
return sortedData.sublist(startIndex, endIndex); return sortedData.sublist(startIndex, endIndex);
} }
void filterDateRange() {} void filterDateRange() {
getPayoutList();
}
void refrshfilterDateRange() { void refrshfilterDateRange() {
setState(() { setState(() {
@ -166,22 +174,17 @@ class _PayoutListState extends ConsumerState<PayoutList> {
final q = query.toLowerCase(); final q = query.toLowerCase();
setState(() { setState(() {
filteredData = getPayoutData.where((item) { filteredData = getPayoutData.where((item) {
// Search against the same display strings as the table (not raw API values). // Search matches visible table columns only (broker / referer / updated hidden).
final updatedAtSearch = _exportUpdatedAtForExcel(item['updated_at'])
.toLowerCase();
final invoiceDateSearch = _formatDateSafe(item['invoice_date']) final invoiceDateSearch = _formatDateSafe(item['invoice_date'])
.toLowerCase(); .toLowerCase();
final statusSearch = final statusSearch =
_exportPayoutStatusLabel(item['payout_status']).toLowerCase(); _exportPayoutStatusLabel(item['payout_status']).toLowerCase();
return updatedAtSearch.contains(q) || return invoiceDateSearch.contains(q) ||
invoiceDateSearch.contains(q) ||
(item['invoice_no'] ?? '-').toLowerCase().contains(q) || (item['invoice_no'] ?? '-').toLowerCase().contains(q) ||
(item['invoice_amount_indian_format'] ?? '-') (item['invoice_amount_indian_format'] ?? '-')
.toLowerCase() .toLowerCase()
.contains(q) || .contains(q) ||
(item['broker_name'] ?? '-').toLowerCase().contains(q) ||
(item['partner_names'] ?? '-').toLowerCase().contains(q) ||
(item['utr_numbers'] ?? '-').toLowerCase().contains(q) || (item['utr_numbers'] ?? '-').toLowerCase().contains(q) ||
(item['invoiced_amount'] ?? '0').toLowerCase().contains(q) || (item['invoiced_amount'] ?? '0').toLowerCase().contains(q) ||
(item['payout_amount'] ?? '0').toLowerCase().contains(q) || (item['payout_amount'] ?? '0').toLowerCase().contains(q) ||
@ -214,15 +217,6 @@ class _PayoutListState extends ConsumerState<PayoutList> {
} }
} }
String _formatTime(String rawDate) {
try {
final dateTime = DateTime.parse(rawDate);
return DateFormat('HH:mm').format(dateTime);
} catch (e) {
return '-';
}
}
/// Excel export: match table (1 = Pending, 2 = Completed). /// Excel export: match table (1 = Pending, 2 = Completed).
String _exportPayoutStatusLabel(dynamic v) { String _exportPayoutStatusLabel(dynamic v) {
if (v == null) return '-'; if (v == null) return '-';
@ -233,19 +227,6 @@ class _PayoutListState extends ConsumerState<PayoutList> {
return 'Completed'; return 'Completed';
} }
/// Excel export: same as table dd-MM-yyyy + 24h time in one cell.
String _exportUpdatedAtForExcel(dynamic v) {
if (v == null) return '-';
final raw = v.toString().trim();
if (raw.isEmpty || raw.toLowerCase() == 'null') return '-';
try {
final dateTime = DateTime.parse(raw);
return '${DateFormat('dd-MM-yyyy').format(dateTime)} ${DateFormat('HH:mm').format(dateTime)}';
} catch (_) {
return raw;
}
}
String _displayText(dynamic value) { String _displayText(dynamic value) {
if (value == null) return '-'; if (value == null) return '-';
final text = value.toString().trim(); final text = value.toString().trim();
@ -258,18 +239,6 @@ class _PayoutListState extends ConsumerState<PayoutList> {
return '${value.substring(0, maxChars)}...'; return '${value.substring(0, maxChars)}...';
} }
String _partnerTooltipText(dynamic value) {
final text = _displayText(value);
if (text == '-') return text;
final items = text
.split(',')
.map((e) => e.trim())
.where((e) => e.isNotEmpty)
.toList();
if (items.isEmpty) return text;
return items.join('\n');
}
String _utrTooltipText(dynamic value) { String _utrTooltipText(dynamic value) {
final text = _displayText(value); final text = _displayText(value);
if (text == '-') return text; if (text == '-') return text;
@ -685,14 +654,36 @@ class _PayoutListState extends ConsumerState<PayoutList> {
); );
} }
Widget _payoutInvoiceDateFilter(BuildContext context) {
return DateFilterRow(
compactDateFiltersOnly: true,
dataFrom: 'Payout',
role: roleId,
id: userId,
selectedStaffId: null,
startController: controllers['startDate']!,
endController: controllers['endDate']!,
onStatusChanged: (_) {},
formKey: _formKey,
isMobile: ResponsiveLayout.isMobile(context),
onFilter: filterDateRange,
onRefresh: refrshfilterDateRange,
);
}
Widget _buildContent(BuildContext context) { Widget _buildContent(BuildContext context) {
return Column( return Column(
children: [ children: [
if (ResponsiveLayout.isMobile(context))
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: _payoutInvoiceDateFilter(context),
),
Container( Container(
// height: 40, // height: 40,
// color: Colors.pink, // color: Colors.pink,
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
Text( Text(
@ -702,8 +693,20 @@ class _PayoutListState extends ConsumerState<PayoutList> {
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
), ),
), ),
if (!ResponsiveLayout.isMobile(context)) ...[
Spacer(), const SizedBox(width: 16),
Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: _payoutInvoiceDateFilter(context),
),
),
),
] else ...[
const Spacer(),
],
ThemedSearchField( ThemedSearchField(
hintText: 'Search', hintText: 'Search',
// backgroundColor: Color(0xFFF6F8F8), // backgroundColor: Color(0xFFF6F8F8),
@ -753,31 +756,24 @@ class _PayoutListState extends ConsumerState<PayoutList> {
"S.No", "S.No",
"Invoice No", "Invoice No",
"Invoice Date", "Invoice Date",
"Broker Name", "UTR Number",
"Referer",
"UTR Details",
"Invoiced Amount", "Invoiced Amount",
"Payout Amount", "Payout Amount",
"Balance Amount", "Balance Amount",
"Status", "Status",
"Updated Date",
], ],
keys: [ keys: [
"id", "id",
"invoice_no", "invoice_no",
"invoice_date", "invoice_date",
"broker_name",
"partner_names",
"utr_numbers", "utr_numbers",
"invoiced_amount", "invoiced_amount",
"payout_amount", "payout_amount",
"balance_amount", "balance_amount",
"payout_status", "payout_status",
"updated_at",
], ],
valueFormatters: { valueFormatters: {
'payout_status': (v, _) => _exportPayoutStatusLabel(v), 'payout_status': (v, _) => _exportPayoutStatusLabel(v),
'updated_at': (v, _) => _exportUpdatedAtForExcel(v),
}, },
), ),
], ],
@ -801,29 +797,17 @@ class _PayoutListState extends ConsumerState<PayoutList> {
flex: 3, flex: 3,
child: Text('Invoice No', style: _headerStyle), child: Text('Invoice No', style: _headerStyle),
), ),
Expanded(flex: 2, child: Text('UTR Number', style: _headerStyle)),
Expanded(flex: 2, child: Text('Invoiced Amount', style: _headerStyle),),
Expanded( Expanded(
flex: 2, flex: 2,
child: Text('Broker Name', style: _headerStyle), child: Text('Payout Amount', style: _headerStyle),
),
Expanded(flex: 3, child: Text('Referer', style: _headerStyle)),
Expanded(flex: 2, child: Text('UTR Details', style: _headerStyle)),
Expanded(
flex: 2,
child: Text('Invoiced\nAmount', style: _headerStyle),
), ),
Expanded( Expanded(
flex: 2, flex: 2,
child: Text('Payout\nAmount', style: _headerStyle), child: Text('Balance Amount', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Balance\nAmount', style: _headerStyle),
), ),
Expanded(flex: 2, child: Text('Status', style: _headerStyle)), Expanded(flex: 2, child: Text('Status', style: _headerStyle)),
Expanded(
flex: 2,
child: Text('Updated Date', style: _headerStyle),
),
Expanded(flex: 2, child: Text('Action', style: _headerStyle)), Expanded(flex: 2, child: Text('Action', style: _headerStyle)),
], ],
), ),
@ -897,29 +881,6 @@ class _PayoutListState extends ConsumerState<PayoutList> {
maxLines: 3, maxLines: 3,
), ),
), ),
Expanded(
flex: 2,
child: Text(
_displayText(item['broker_name']),
style: _dataBold,
softWrap: true,
maxLines: 3,
),
),
Expanded(
flex: 3,
child: Tooltip(
message: _partnerTooltipText(item['partner_names']),
waitDuration: const Duration(milliseconds: 250),
child: Text(
_truncateText(_displayText(item['partner_names']), maxChars: 25),
style: _dataBold,
softWrap: false,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
),
Expanded( Expanded(
flex: 2, flex: 2,
child: Tooltip( child: Tooltip(
@ -976,30 +937,6 @@ class _PayoutListState extends ConsumerState<PayoutList> {
), ),
), ),
Expanded(
flex: 2,
child: item['updated_at'] != null
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
_formatDate(item['updated_at']),
style: _dataBold,
softWrap: false,
maxLines: 1,
),
Text(
_formatTime(item['updated_at']),
style: _dataBold,
softWrap: false,
maxLines: 1,
),
],
)
: Text('-', style: _dataBold),
),
// ACTION ICONS (Edit + Delete) // ACTION ICONS (Edit + Delete)
Expanded( Expanded(
flex: 2, flex: 2,

View File

@ -61,6 +61,14 @@ class _PayoutReportScreenState extends ConsumerState<PayoutReportScreen> {
return v == 'no'; return v == 'no';
} }
/// UI / export: capitalize first letter, lowercase the rest (e.g. `yes` `Yes`).
String _receivedLabel(dynamic v) {
final s = v?.toString().trim() ?? '';
if (s.isEmpty) return '-';
return s[0].toUpperCase() +
(s.length > 1 ? s.substring(1).toLowerCase() : '');
}
String? _agentIdForApi() { String? _agentIdForApi() {
if (_isAgentRole()) { if (_isAgentRole()) {
final uid = ref.read(userIdProvider); final uid = ref.read(userIdProvider);
@ -746,6 +754,9 @@ class _PayoutReportScreenState extends ConsumerState<PayoutReportScreen> {
'utr_no', 'utr_no',
'received_or_not', 'received_or_not',
], ],
valueFormatters: {
'received_or_not': (v, _) => _receivedLabel(v),
},
); );
if (mounted) { if (mounted) {
ToastHelper.showSuccessToast(context, 'Export finished'); ToastHelper.showSuccessToast(context, 'Export finished');
@ -1052,7 +1063,7 @@ class _PayoutReportScreenState extends ConsumerState<PayoutReportScreen> {
Expanded( Expanded(
flex: 2, flex: 2,
child: Text( child: Text(
_cell(item, ['received_or_not']), _receivedLabel(item['received_or_not']),
style: rowStyle, style: rowStyle,
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,

View File

@ -54,6 +54,9 @@ class DateFilterRow extends ConsumerStatefulWidget {
final VoidCallback onRefresh; final VoidCallback onRefresh;
final GlobalKey<FormState> formKey; final GlobalKey<FormState> formKey;
final bool isMobile; final bool isMobile;
/// When true, only start/end date fields and filter/refresh actions are shown
/// (no status, staff, insurer, or partner controls).
final bool compactDateFiltersOnly;
final role; final role;
final id; final id;
@ -77,6 +80,7 @@ class DateFilterRow extends ConsumerStatefulWidget {
required this.role, required this.role,
required this.id, required this.id,
this.isMobile = false, this.isMobile = false,
this.compactDateFiltersOnly = false,
this.dataFrom, this.dataFrom,
}); });
@ -116,6 +120,7 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
// final handlerId = ref.watch(handlerIdProvider); // final handlerId = ref.watch(handlerIdProvider);
role = ref.watch(userRoleProvider); role = ref.watch(userRoleProvider);
print("managerId - $managerId"); print("managerId - $managerId");
if (widget.compactDateFiltersOnly) return;
if (userID != null && role != null) { if (userID != null && role != null) {
print('hansles'); print('hansles');
getStaffDetails(userID); getStaffDetails(userID);
@ -235,7 +240,8 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (widget.role == 'Accounts' && if (!widget.compactDateFiltersOnly &&
widget.role == 'Accounts' &&
widget.dataFrom == 'Policy' && widget.dataFrom == 'Policy' &&
filteredPartnerData.isEmpty && filteredPartnerData.isEmpty &&
!_partnerFetchAttempted) { !_partnerFetchAttempted) {
@ -275,23 +281,24 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
buildEndDate(context), buildEndDate(context),
SizedBox(width: spacing), SizedBox(width: spacing),
if (widget.dataFrom == null && widget.dataFrom != 'Policy') ...[ if (!widget.compactDateFiltersOnly) ...[
buildStatusSearch(context,widget.role), if (widget.dataFrom == null && widget.dataFrom != 'Policy') ...[
SizedBox(width: spacing), buildStatusSearch(context, widget.role),
], SizedBox(width: spacing),
],
if (widget.role == 'manager' || widget.role == 'handler') ...[ if (widget.role == 'manager' || widget.role == 'handler') ...[
buildSelectStaffMem(context), buildSelectStaffMem(context),
SizedBox(width: spacing), SizedBox(width: spacing),
],
if (widget.role == 'Accounts' && widget.dataFrom == 'Policy') ...[
buildSelectPartnerAccountsPolicy(context),
SizedBox(width: spacing),
buildPolicyReportFlagSearch(context),
SizedBox(width: spacing),
buildSelectInsurer(context),
],
], ],
if (widget.role == 'Accounts' && widget.dataFrom == 'Policy') ...[
buildSelectPartnerAccountsPolicy(context),
SizedBox(width: spacing),
buildPolicyReportFlagSearch(context),
SizedBox(width: spacing),
buildSelectInsurer(context),
],
]; ];
final buttons = [ final buttons = [