diff --git a/src/app/(dashboard)/finance/rm-pricing/groups/rm-groups-page-client.tsx b/src/app/(dashboard)/finance/rm-pricing/groups/rm-groups-page-client.tsx index 42c582b..ca57826 100644 --- a/src/app/(dashboard)/finance/rm-pricing/groups/rm-groups-page-client.tsx +++ b/src/app/(dashboard)/finance/rm-pricing/groups/rm-groups-page-client.tsx @@ -59,9 +59,14 @@ function RMGroupsPageContent() { const exportMutation = useExportRMGroups() const handleExport = () => { - // Export respects the active_filter only (search is UI-level; the backend - // Export RPC intentionally does not accept search params). - exportMutation.mutate({ activeFilter: filters.activeFilter }) + // Export honors active_filter + search from the list-page filters. + // Selected-mode (group_head_ids) is exposed via the BFF route for + // programmatic callers; the list page itself doesn't expose multi-select + // export (yet) — Filtered + All cover the bulk-migration workflow. + exportMutation.mutate({ + activeFilter: filters.activeFilter, + search: filters.search, + }) } // Use isFetching (not isLoading) so background refetches triggered on diff --git a/src/app/api/v1/finance/rm-groups/export/route.ts b/src/app/api/v1/finance/rm-groups/export/route.ts index 2a3f24f..2c3610a 100644 --- a/src/app/api/v1/finance/rm-groups/export/route.ts +++ b/src/app/api/v1/finance/rm-groups/export/route.ts @@ -9,9 +9,20 @@ export async function GET(request: NextRequest) { const metadata = createMetadataFromRequest(request) const client = getRmGroupClient() + // Comma-separated list of group_head_id UUIDs for the "Selected" mode. + const idsParam = searchParams.get("group_head_ids") || searchParams.get("groupHeadIds") || "" + const groupHeadIds = idsParam + ? idsParam + .split(",") + .map((s) => s.trim()) + .filter(Boolean) + : [] const response = await client.exportRMGroups( { - activeFilter: Number(searchParams.get("activeFilter") || searchParams.get("active_filter")) || 0, + activeFilter: + Number(searchParams.get("activeFilter") || searchParams.get("active_filter")) || 0, + search: searchParams.get("search") || "", + groupHeadIds, }, metadata ) diff --git a/src/types/finance/rm-group.ts b/src/types/finance/rm-group.ts index 96426b4..f2759a7 100644 --- a/src/types/finance/rm-group.ts +++ b/src/types/finance/rm-group.ts @@ -133,6 +133,12 @@ export interface ListUngroupedItemsParams { export interface ExportRMGroupsParams { activeFilter?: ActiveFilter + /** Free-text search across code/name/description (Filtered mode). */ + search?: string + /** Explicit group_head_id list (Selected mode). When non-empty overrides + * activeFilter and search at the backend. Sent as `group_head_ids` query + * param (comma-separated UUIDs). */ + groupHeadIds?: string[] } // Form data for group head create/edit diff --git a/src/types/generated/finance/v1/rm_group.ts b/src/types/generated/finance/v1/rm_group.ts index eb4eafd..5c17d1d 100644 --- a/src/types/generated/finance/v1/rm_group.ts +++ b/src/types/generated/finance/v1/rm_group.ts @@ -930,10 +930,24 @@ export interface GetRMGroupItemRatesResponse { data: RMGroupItemRates[]; } -/** ExportRMGroupsRequest filters the export. Active filter only (no pagination). */ +/** + * ExportRMGroupsRequest filters the export. Three mutually-exclusive modes: + * + * - All groups: leave both filters unset (default). + * - Filtered: set `active_filter` (and/or `search`) to narrow the set. + * - Selected: pass an explicit list of group_head_ids; all other filters + * are ignored. + */ export interface ExportRMGroupsRequest { /** Filter by active/inactive (UNSPECIFIED = all). */ activeFilter: ActiveFilter; + /** + * Optional explicit group selection. When non-empty, only these groups + * are exported (overrides `active_filter` and `search`). + */ + groupHeadIds: string[]; + /** Optional code/name search string. Empty = no search filter. */ + search: string; } /** ExportRMGroupsResponse returns a multi-sheet Excel (Groups + Items). */ @@ -6747,7 +6761,7 @@ export const GetRMGroupItemRatesResponse: MessageFns = { @@ -6755,6 +6769,12 @@ export const ExportRMGroupsRequest: MessageFns = { if (message.activeFilter !== 0) { writer.uint32(8).int32(message.activeFilter); } + for (const v of message.groupHeadIds) { + writer.uint32(18).string(v!); + } + if (message.search !== "") { + writer.uint32(26).string(message.search); + } return writer; }, @@ -6773,6 +6793,22 @@ export const ExportRMGroupsRequest: MessageFns = { message.activeFilter = reader.int32() as any; continue; } + case 2: { + if (tag !== 18) { + break; + } + + message.groupHeadIds.push(reader.string()); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.search = reader.string(); + continue; + } } if ((tag & 7) === 4 || tag === 0) { break; @@ -6789,6 +6825,12 @@ export const ExportRMGroupsRequest: MessageFns = { : isSet(object.active_filter) ? activeFilterFromJSON(object.active_filter) : 0, + groupHeadIds: globalThis.Array.isArray(object?.groupHeadIds) + ? object.groupHeadIds.map((e: any) => globalThis.String(e)) + : globalThis.Array.isArray(object?.group_head_ids) + ? object.group_head_ids.map((e: any) => globalThis.String(e)) + : [], + search: isSet(object.search) ? globalThis.String(object.search) : "", }; }, @@ -6797,6 +6839,12 @@ export const ExportRMGroupsRequest: MessageFns = { if (message.activeFilter !== 0) { obj.activeFilter = activeFilterToJSON(message.activeFilter); } + if (message.groupHeadIds?.length) { + obj.groupHeadIds = message.groupHeadIds; + } + if (message.search !== "") { + obj.search = message.search; + } return obj; }, @@ -6806,6 +6854,8 @@ export const ExportRMGroupsRequest: MessageFns = { fromPartial(object: DeepPartial): ExportRMGroupsRequest { const message = createBaseExportRMGroupsRequest(); message.activeFilter = object.activeFilter ?? 0; + message.groupHeadIds = object.groupHeadIds?.map((e) => e) || []; + message.search = object.search ?? ""; return message; }, };