This commit is contained in:
Surendiran 2026-04-04 18:20:57 +05:30
commit f1a57d1d8c
5 changed files with 117 additions and 149 deletions

View File

@ -2192,13 +2192,26 @@ class ApiService {
return response;
}
Future<Map<String, dynamic>> getPayoutList() async {
Future<Map<String, dynamic>> getPayoutList({
String? fromDate,
String? toDate,
}) async {
// print(_token);
if (_token == null) {
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 = {
'Authorization': 'Bearer $_token' ?? '',

View File

@ -70,7 +70,12 @@ class _PayoutListState extends ConsumerState<PayoutList> {
});
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') {
print('PL 3 => getPayoutList - ${response['data']}');
@ -78,7 +83,8 @@ class _PayoutListState extends ConsumerState<PayoutList> {
print(' PL 4 => getPayoutList => ${response['data']}');
setState(() {
currentPage = 1;
if (data is List) {
// Already a list of maps
getPayoutData = List<Map<String, dynamic>>.from(data);
@ -145,7 +151,9 @@ class _PayoutListState extends ConsumerState<PayoutList> {
return sortedData.sublist(startIndex, endIndex);
}
void filterDateRange() {}
void filterDateRange() {
getPayoutList();
}
void refrshfilterDateRange() {
setState(() {
@ -166,22 +174,17 @@ class _PayoutListState extends ConsumerState<PayoutList> {
final q = query.toLowerCase();
setState(() {
filteredData = getPayoutData.where((item) {
// Search against the same display strings as the table (not raw API values).
final updatedAtSearch = _exportUpdatedAtForExcel(item['updated_at'])
.toLowerCase();
// Search matches visible table columns only (broker / referer / updated hidden).
final invoiceDateSearch = _formatDateSafe(item['invoice_date'])
.toLowerCase();
final statusSearch =
_exportPayoutStatusLabel(item['payout_status']).toLowerCase();
return updatedAtSearch.contains(q) ||
invoiceDateSearch.contains(q) ||
return invoiceDateSearch.contains(q) ||
(item['invoice_no'] ?? '-').toLowerCase().contains(q) ||
(item['invoice_amount_indian_format'] ?? '-')
.toLowerCase()
.contains(q) ||
(item['broker_name'] ?? '-').toLowerCase().contains(q) ||
(item['partner_names'] ?? '-').toLowerCase().contains(q) ||
(item['utr_numbers'] ?? '-').toLowerCase().contains(q) ||
(item['invoiced_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).
String _exportPayoutStatusLabel(dynamic v) {
if (v == null) return '-';
@ -233,19 +227,6 @@ class _PayoutListState extends ConsumerState<PayoutList> {
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) {
if (value == null) return '-';
final text = value.toString().trim();
@ -258,18 +239,6 @@ class _PayoutListState extends ConsumerState<PayoutList> {
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) {
final text = _displayText(value);
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) {
return Column(
children: [
if (ResponsiveLayout.isMobile(context))
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: _payoutInvoiceDateFilter(context),
),
Container(
// height: 40,
// color: Colors.pink,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
@ -702,8 +693,20 @@ class _PayoutListState extends ConsumerState<PayoutList> {
fontWeight: FontWeight.w400,
),
),
Spacer(),
if (!ResponsiveLayout.isMobile(context)) ...[
const SizedBox(width: 16),
Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: _payoutInvoiceDateFilter(context),
),
),
),
] else ...[
const Spacer(),
],
ThemedSearchField(
hintText: 'Search',
// backgroundColor: Color(0xFFF6F8F8),
@ -753,31 +756,24 @@ class _PayoutListState extends ConsumerState<PayoutList> {
"S.No",
"Invoice No",
"Invoice Date",
"Broker Name",
"Referer",
"UTR Details",
"UTR Number",
"Invoiced Amount",
"Payout Amount",
"Balance Amount",
"Status",
"Updated Date",
],
keys: [
"id",
"invoice_no",
"invoice_date",
"broker_name",
"partner_names",
"utr_numbers",
"invoiced_amount",
"payout_amount",
"balance_amount",
"payout_status",
"updated_at",
],
valueFormatters: {
'payout_status': (v, _) => _exportPayoutStatusLabel(v),
'updated_at': (v, _) => _exportUpdatedAtForExcel(v),
},
),
],
@ -801,29 +797,17 @@ class _PayoutListState extends ConsumerState<PayoutList> {
flex: 3,
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(
flex: 2,
child: Text('Broker Name', 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),
child: Text('Payout Amount', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Payout\nAmount', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Balance\nAmount', style: _headerStyle),
child: Text('Balance Amount', 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)),
],
),
@ -897,29 +881,6 @@ class _PayoutListState extends ConsumerState<PayoutList> {
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(
flex: 2,
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)
Expanded(
flex: 2,

View File

@ -61,6 +61,14 @@ class _PayoutReportScreenState extends ConsumerState<PayoutReportScreen> {
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() {
if (_isAgentRole()) {
final uid = ref.read(userIdProvider);
@ -793,6 +801,9 @@ class _PayoutReportScreenState extends ConsumerState<PayoutReportScreen> {
'utr_no',
'received_or_not',
],
valueFormatters: {
'received_or_not': (v, _) => _receivedLabel(v),
},
);
if (mounted) {
ToastHelper.showSuccessToast(context, 'Export finished');
@ -974,7 +985,7 @@ class _PayoutReportScreenState extends ConsumerState<PayoutReportScreen> {
_buildSearchAndActions(context),
],
);
}
}
Widget _tableHeader() {
return Container(
@ -1103,7 +1114,7 @@ class _PayoutReportScreenState extends ConsumerState<PayoutReportScreen> {
Expanded(
flex: 2,
child: Text(
_cell(item, ['received_or_not']),
_receivedLabel(item['received_or_not']),
style: rowStyle,
maxLines: 2,
overflow: TextOverflow.ellipsis,

View File

@ -54,6 +54,9 @@ class DateFilterRow extends ConsumerStatefulWidget {
final VoidCallback onRefresh;
final GlobalKey<FormState> formKey;
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 id;
@ -77,6 +80,7 @@ class DateFilterRow extends ConsumerStatefulWidget {
required this.role,
required this.id,
this.isMobile = false,
this.compactDateFiltersOnly = false,
this.dataFrom,
});
@ -116,6 +120,7 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
// final handlerId = ref.watch(handlerIdProvider);
role = ref.watch(userRoleProvider);
print("managerId - $managerId");
if (widget.compactDateFiltersOnly) return;
if (userID != null && role != null) {
print('hansles');
getStaffDetails(userID);
@ -235,7 +240,8 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
@override
Widget build(BuildContext context) {
if (widget.role == 'Accounts' &&
if (!widget.compactDateFiltersOnly &&
widget.role == 'Accounts' &&
widget.dataFrom == 'Policy' &&
filteredPartnerData.isEmpty &&
!_partnerFetchAttempted) {
@ -275,23 +281,24 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
buildEndDate(context),
SizedBox(width: spacing),
if (widget.dataFrom == null && widget.dataFrom != 'Policy') ...[
buildStatusSearch(context,widget.role),
SizedBox(width: spacing),
],
if (!widget.compactDateFiltersOnly) ...[
if (widget.dataFrom == null && widget.dataFrom != 'Policy') ...[
buildStatusSearch(context, widget.role),
SizedBox(width: spacing),
],
if (widget.role == 'manager' || widget.role == 'handler') ...[
buildSelectStaffMem(context),
SizedBox(width: spacing),
if (widget.role == 'manager' || widget.role == 'handler') ...[
buildSelectStaffMem(context),
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 = [

View File

@ -117,10 +117,10 @@ packages:
dependency: transitive
description:
name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev"
source: hosted
version: "1.4.0"
version: "1.4.1"
charcode:
dependency: transitive
description:
@ -780,26 +780,26 @@ packages:
dependency: transitive
description:
name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
version: "0.12.17"
version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
version: "0.11.1"
version: "0.13.0"
meta:
dependency: transitive
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev"
source: hosted
version: "1.16.0"
version: "1.17.0"
mime:
dependency: transitive
description:
@ -1297,10 +1297,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
url: "https://pub.dev"
source: hosted
version: "0.7.6"
version: "0.7.10"
toastification:
dependency: "direct main"
description:
@ -1502,5 +1502,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.8.1 <4.0.0"
dart: ">=3.9.0-0 <4.0.0"
flutter: ">=3.32.0"