From f3f464b5b445a9feba1387275cf5a2b695801197 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 13 Aug 2026 11:36:01 +0700 Subject: [PATCH 1/4] Spend - Canadian multiple tax export is not shown if an expense was previously deleted. --- src/hooks/useSearchBulkActions.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/hooks/useSearchBulkActions.ts b/src/hooks/useSearchBulkActions.ts index b773dec8561e..08419d587662 100644 --- a/src/hooks/useSearchBulkActions.ts +++ b/src/hooks/useSearchBulkActions.ts @@ -576,8 +576,9 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { // A bulk selection can span several workspaces, so the Canadian Multiple Tax Export template is only offered when every selected item belongs to a workspace that outputs in CAD. // Reports and transactions are both checked because a selection can mix whole reports with individual transactions from other reports, and the export request covers all of them. const doAllSelectedItemsBelongToCADPolicies = useMemo(() => { + const policyIDFilter = getFilterFromQuery(queryJSON, CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID); + const selectedItems = [...selectedReports, ...Object.values(selectedTransactions)]; if (areAllMatchingItemsSelected) { - const policyIDFilter = getFilterFromQuery(queryJSON, CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID); if (!policyIDFilter.value?.length || policyIDFilter.isNegated) { return false; } @@ -585,13 +586,15 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { return policyIDFilter.value.every((policyID) => policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.outputCurrency === CONST.CURRENCY.CAD); } - const selectedItems = [...selectedReports, ...Object.values(selectedTransactions)]; if (selectedItems.length === 0) { return false; } - return selectedItems.every((item) => !!item.policyID && policies?.[`${ONYXKEYS.COLLECTION.POLICY}${item.policyID}`]?.outputCurrency === CONST.CURRENCY.CAD); - }, [areAllMatchingItemsSelected, queryJSON, selectedReports, selectedTransactions, policies]); + return selectedItems.every((item) => { + const policyID = item.policyID ?? getReportFromSearchSnapshot(item.reportID, currentSearchResults?.data, allReports)?.policyID; + return !!policyID && policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.outputCurrency === CONST.CURRENCY.CAD; + }); + }, [areAllMatchingItemsSelected, currentSearchResults?.data, allReports, queryJSON, selectedReports, selectedTransactions, policies]); const selectedBulkCurrency = selectedReports.at(0)?.currency ?? Object.values(selectedTransactions).at(0)?.currency; const totalFormattedAmount = getTotalFormattedAmount(convertToDisplayString, selectedReports, selectedTransactions, selectedBulkCurrency); From d9a588e15f6aaaf72a4a30227f6fc003765c90d9 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 13 Aug 2026 11:44:28 +0700 Subject: [PATCH 2/4] fix test --- src/hooks/useSearchBulkActions.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hooks/useSearchBulkActions.ts b/src/hooks/useSearchBulkActions.ts index 08419d587662..46fc78b37bea 100644 --- a/src/hooks/useSearchBulkActions.ts +++ b/src/hooks/useSearchBulkActions.ts @@ -591,10 +591,12 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { } return selectedItems.every((item) => { - const policyID = item.policyID ?? getReportFromSearchSnapshot(item.reportID, currentSearchResults?.data, allReports)?.policyID; + // Expense rows never carry a policyID, so a selection built from them has none either. The report the row belongs + // to is what holds it, which is also how the neighbouring fields on those selection entries are derived. + const policyID = item.policyID ?? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`]?.policyID; return !!policyID && policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.outputCurrency === CONST.CURRENCY.CAD; }); - }, [areAllMatchingItemsSelected, currentSearchResults?.data, allReports, queryJSON, selectedReports, selectedTransactions, policies]); + }, [areAllMatchingItemsSelected, allReports, queryJSON, selectedReports, selectedTransactions, policies]); const selectedBulkCurrency = selectedReports.at(0)?.currency ?? Object.values(selectedTransactions).at(0)?.currency; const totalFormattedAmount = getTotalFormattedAmount(convertToDisplayString, selectedReports, selectedTransactions, selectedBulkCurrency); From 17c16423315025462c33e5b6d688dabfc9b3d953 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 13 Aug 2026 11:46:35 +0700 Subject: [PATCH 3/4] fix typo issue --- src/hooks/useSearchBulkActions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hooks/useSearchBulkActions.ts b/src/hooks/useSearchBulkActions.ts index 46fc78b37bea..1d88d0c1dc94 100644 --- a/src/hooks/useSearchBulkActions.ts +++ b/src/hooks/useSearchBulkActions.ts @@ -576,9 +576,8 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { // A bulk selection can span several workspaces, so the Canadian Multiple Tax Export template is only offered when every selected item belongs to a workspace that outputs in CAD. // Reports and transactions are both checked because a selection can mix whole reports with individual transactions from other reports, and the export request covers all of them. const doAllSelectedItemsBelongToCADPolicies = useMemo(() => { - const policyIDFilter = getFilterFromQuery(queryJSON, CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID); - const selectedItems = [...selectedReports, ...Object.values(selectedTransactions)]; if (areAllMatchingItemsSelected) { + const policyIDFilter = getFilterFromQuery(queryJSON, CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID); if (!policyIDFilter.value?.length || policyIDFilter.isNegated) { return false; } @@ -586,13 +585,14 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { return policyIDFilter.value.every((policyID) => policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.outputCurrency === CONST.CURRENCY.CAD); } + const selectedItems = [...selectedReports, ...Object.values(selectedTransactions)]; if (selectedItems.length === 0) { return false; } return selectedItems.every((item) => { // Expense rows never carry a policyID, so a selection built from them has none either. The report the row belongs - // to is what holds it, which is also how the neighbouring fields on those selection entries are derived. + // to is what holds it, which is also how the neighboring fields on those selection entries are derived. const policyID = item.policyID ?? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`]?.policyID; return !!policyID && policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.outputCurrency === CONST.CURRENCY.CAD; }); From f2230fcaabf7fa3aa8e950756571ccd06b581676 Mon Sep 17 00:00:00 2001 From: Nguyen Van Duc <129500732+dukenv0307@users.noreply.github.com> Date: Thu, 13 Aug 2026 18:12:38 +0700 Subject: [PATCH 4/4] Update src/hooks/useSearchBulkActions.ts Co-authored-by: Nikki Wines --- src/hooks/useSearchBulkActions.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hooks/useSearchBulkActions.ts b/src/hooks/useSearchBulkActions.ts index 1d88d0c1dc94..ccf1b7bba007 100644 --- a/src/hooks/useSearchBulkActions.ts +++ b/src/hooks/useSearchBulkActions.ts @@ -591,8 +591,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { } return selectedItems.every((item) => { - // Expense rows never carry a policyID, so a selection built from them has none either. The report the row belongs - // to is what holds it, which is also how the neighboring fields on those selection entries are derived. + // Expense rows don't have a policyID, as it's derived from the report they belong to, same as the other report-derived fields on this selection entry. const policyID = item.policyID ?? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`]?.policyID; return !!policyID && policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.outputCurrency === CONST.CURRENCY.CAD; });