Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
33cf846
Reapply "Reapply "Feat: bulk edit multiple""
pasyukevich Mar 10, 2026
72bba39
Fix conditions for unreported expenses and fix common dependent tag l…
pasyukevich Mar 10, 2026
e132c76
Merge branch 'main' into feauture/bulk-edit-expenses-3
pasyukevich Mar 11, 2026
6fd9a0b
Update SearchEditMultiplePage to include isInvoiceReport check in tra…
pasyukevich Mar 11, 2026
4413a6e
Add isAnyInvoice check to determine IOU type in SearchEditMultipleAmo…
pasyukevich Mar 11, 2026
e0418e5
Enhance tax handling in updateMultipleMoneyRequests by adding tax amo…
pasyukevich Mar 11, 2026
c1aab27
Add support for negative amounts in self-DM track expenses in SearchE…
pasyukevich Mar 11, 2026
fdc4e3c
Fix selected tax rate appears as a separate rate
pasyukevich Mar 11, 2026
19cce59
Fix cross policy bulk edit category shows rate not valid, refactor u…
pasyukevich Mar 11, 2026
109395e
Refactor tax tracking logic in SearchEditMultiplePage to consider all…
pasyukevich Mar 11, 2026
3b636bf
Enhance updateMultipleMoneyRequests to skip optimistic violation upda…
pasyukevich Mar 11, 2026
3e5d947
Fix tag field and tag page header show different name
pasyukevich Mar 11, 2026
ab429bd
Merge branch 'main' into feauture/bulk-edit-expenses-3
pasyukevich Mar 12, 2026
46d1bf5
Update tax rate title handling
pasyukevich Mar 13, 2026
9499888
Add reportID to optimistic report action in updateMultipleMoneyRequests
pasyukevich Mar 13, 2026
041dc2a
Merge branch 'main' into feauture/bulk-edit-expenses-3
pasyukevich Mar 13, 2026
2c0a4a2
Fix failing tests
pasyukevich Mar 13, 2026
4b0f5b8
Merge branch 'main' into feauture/bulk-edit-expenses-3
pasyukevich Mar 13, 2026
5b46373
Enhance transaction validation by adding check for non-IOU reports be…
pasyukevich Mar 13, 2026
9d447c1
Add tests for transaction category validation in IOU actions
pasyukevich Mar 16, 2026
346d8dc
Merge branch 'main' into feauture/bulk-edit-expenses-3
pasyukevich Mar 16, 2026
09c894a
fix tests
pasyukevich Mar 16, 2026
71047b4
Add BULK_EDIT beta flag
pasyukevich Mar 16, 2026
4c91de7
Merge branch 'main' into feauture/bulk-edit-expenses-3
pasyukevich Mar 16, 2026
71c0018
fix lint
pasyukevich Mar 16, 2026
d74cab0
fix tests
pasyukevich Mar 16, 2026
c56a5ce
Refactor transaction policy handling in updateMultipleMoneyRequests f…
pasyukevich Mar 17, 2026
b8f0ffc
Merge branch 'main' into feauture/bulk-edit-expenses-3
pasyukevich Mar 17, 2026
91955b5
fix typecheck
pasyukevich Mar 17, 2026
f7b7b52
Remove Onyx connection that can be skipped
pasyukevich Mar 18, 2026
f2feaa3
Fix tax amount calculation to use effective transaction amount in upd…
pasyukevich Mar 18, 2026
7c91567
Refactor updateMultipleMoneyRequests to improve optimistic violation …
pasyukevich Mar 18, 2026
15755d5
Enhance updateMultipleMoneyRequests to handle unreported expenses by …
pasyukevich Mar 19, 2026
bcedd5a
Fix system message dismisses after dismissing the error
pasyukevich Mar 19, 2026
3d02e0f
Merge branch 'main' into feauture/bulk-edit-expenses-3
pasyukevich Mar 19, 2026
b6a42e4
fix infinite loading and missing MoneyRequestView with buildOptimisti…
LukasMod Mar 20, 2026
148a503
Merge pull request #42 from callstack-internal/feauture/bulk-edit-exp…
LukasMod Mar 20, 2026
171e289
Merge branch 'main' of github.com:callstack-internal/Expensify-App in…
LukasMod Mar 23, 2026
2a29f58
Use allPolicyTags from onyx hook instead Onyx.connect
LukasMod Mar 23, 2026
e5e37c5
pass allPolicyCategories
LukasMod Mar 23, 2026
4e667a4
add validation unit test for unreported expenses
LukasMod Mar 23, 2026
a84855d
fix issue with bulk edit split and unreported expenses
LukasMod Mar 23, 2026
7c1d8e5
Merge snapshot data to fix missing data after hard refresh
LukasMod Mar 23, 2026
a8d0fa7
fix test 11 and 16 after hard refresh scenario
LukasMod Mar 23, 2026
23f617b
Merge branch 'main' of github.com:callstack-internal/Expensify-App in…
LukasMod Mar 24, 2026
0c6aadc
Merge branch 'main' of github.com:callstack-internal/Expensify-App in…
LukasMod Mar 24, 2026
aa0b93a
fix import
LukasMod Mar 24, 2026
b31a98c
Merge branch 'main' of github.com:callstack-internal/Expensify-App in…
LukasMod Mar 25, 2026
cc436ba
resolve ts issues after merging main
LukasMod Mar 25, 2026
b861e46
fix ts issue
LukasMod Mar 25, 2026
cd8e631
Merge branch 'main' of github.com:callstack-internal/Expensify-App in…
LukasMod Mar 26, 2026
4680b7a
Merge branch 'main' of github.com:callstack-internal/Expensify-App in…
LukasMod Mar 27, 2026
668686c
Use transactionPolicy instead of shared bulk-edit policy
LukasMod Mar 27, 2026
5f8e120
fix: use per-transaction policy instead of shared bulk-edit policy in…
LukasMod Mar 27, 2026
bfa7971
fix: add backfilledCreatedActionID to resolve issue with multiple CRE…
LukasMod Mar 27, 2026
be005b2
fix: use snapshot data context with mergedReports
LukasMod Mar 27, 2026
734d78d
fix: use snapshot-merged reports in bulk-edit pickers and extract use…
LukasMod Mar 27, 2026
b1ebe1b
fix useOnyx import
LukasMod Mar 27, 2026
8645978
Merge branch 'main' of github.com:callstack-internal/Expensify-App in…
LukasMod Mar 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,7 @@ const CONST = {
PERSONAL_CARD_IMPORT: 'personalCardImport',
SUGGESTED_FOLLOWUPS: 'suggestedFollowups',
FREEZE_CARD: 'freezeCard',
BULK_EDIT: 'bulkEdit',
NEW_MANUAL_EXPENSE_FLOW: 'newManualExpenseFlow',
},
BUTTON_STATES: {
Expand Down Expand Up @@ -3248,6 +3249,8 @@ const CONST = {
QUANTITY_MAX_LENGTH: 12,
// This is the transactionID used when going through the create expense flow so that it mimics a real transaction (like the edit flow)
OPTIMISTIC_TRANSACTION_ID: '1',
// This is the transactionID used when bulk editing multiple expenses
OPTIMISTIC_BULK_EDIT_TRANSACTION_ID: 'optimisticBulkEditTransactionID',
// This is the transactionID used when going through the distance split expense flow so that it mimics a draft transaction
OPTIMISTIC_DISTANCE_SPLIT_TRANSACTION_ID: '2',
// Note: These payment types are used when building IOU reportAction message values in the server and should
Expand Down Expand Up @@ -4556,6 +4559,7 @@ const CONST = {
TAX_RATE: 'taxRate',
TAX_AMOUNT: 'taxAmount',
REIMBURSABLE: 'reimbursable',
BILLABLE: 'billable',
REPORT: 'report',
},
FOOTER: {
Expand Down Expand Up @@ -7357,6 +7361,7 @@ const CONST = {
TAG: 'tag',
},
BULK_ACTION_TYPES: {
EDIT: 'edit',
EXPORT: 'export',
APPROVE: 'approve',
PAY: 'pay',
Expand Down
9 changes: 9 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,12 @@ const ONYXKEYS = {
WORKSPACE_PER_DIEM_FORM_DRAFT: 'workspacePerDiemFormDraft',
ENABLE_GLOBAL_REIMBURSEMENTS: 'enableGlobalReimbursementsForm',
ENABLE_GLOBAL_REIMBURSEMENTS_DRAFT: 'enableGlobalReimbursementsFormDraft',
SEARCH_EDIT_MULTIPLE_DESCRIPTION_FORM: 'searchEditMultipleDescriptionForm',
SEARCH_EDIT_MULTIPLE_DESCRIPTION_FORM_DRAFT: 'searchEditMultipleDescriptionFormDraft',
SEARCH_EDIT_MULTIPLE_MERCHANT_FORM: 'searchEditMultipleMerchantForm',
SEARCH_EDIT_MULTIPLE_MERCHANT_FORM_DRAFT: 'searchEditMultipleMerchantFormDraft',
SEARCH_EDIT_MULTIPLE_DATE_FORM: 'searchEditMultipleDateForm',
SEARCH_EDIT_MULTIPLE_DATE_FORM_DRAFT: 'searchEditMultipleDateFormDraft',
CREATE_DOMAIN_FORM: 'createDomainForm',
CREATE_DOMAIN_FORM_DRAFT: 'createDomainFormDraft',
SPLIT_EXPENSE_EDIT_DATES: 'splitExpenseEditDates',
Expand Down Expand Up @@ -1187,6 +1193,9 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.INTERNATIONAL_BANK_ACCOUNT_FORM]: FormTypes.InternationalBankAccountForm;
[ONYXKEYS.FORMS.WORKSPACE_PER_DIEM_FORM]: FormTypes.WorkspacePerDiemForm;
[ONYXKEYS.FORMS.ENABLE_GLOBAL_REIMBURSEMENTS]: FormTypes.EnableGlobalReimbursementsForm;
[ONYXKEYS.FORMS.SEARCH_EDIT_MULTIPLE_DESCRIPTION_FORM]: FormTypes.SearchEditMultipleDescriptionForm;
[ONYXKEYS.FORMS.SEARCH_EDIT_MULTIPLE_MERCHANT_FORM]: FormTypes.SearchEditMultipleMerchantForm;
[ONYXKEYS.FORMS.SEARCH_EDIT_MULTIPLE_DATE_FORM]: FormTypes.SearchEditMultipleDateForm;
[ONYXKEYS.FORMS.CREATE_DOMAIN_FORM]: FormTypes.CreateDomainForm;
[ONYXKEYS.FORMS.SPLIT_EXPENSE_EDIT_DATES]: FormTypes.SplitExpenseEditDateForm;
[ONYXKEYS.FORMS.EXPENSE_RULE_FORM]: FormTypes.ExpenseRuleForm;
Expand Down
13 changes: 13 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,19 @@ const ROUTES = {
},
},
SEARCH_REJECT_REASON_RHP: 'search/reject',
SEARCH_EDIT_MULTIPLE_TRANSACTIONS_RHP: 'search/edit-multiple-transactions',
SEARCH_EDIT_MULTIPLE_AMOUNT_RHP: 'search/edit-multiple/amount',
SEARCH_EDIT_MULTIPLE_DESCRIPTION_RHP: 'search/edit-multiple/description',
SEARCH_EDIT_MULTIPLE_MERCHANT_RHP: 'search/edit-multiple/merchant',
SEARCH_EDIT_MULTIPLE_DATE_RHP: 'search/edit-multiple/date',
SEARCH_EDIT_MULTIPLE_CATEGORY_RHP: 'search/edit-multiple/category',
SEARCH_EDIT_MULTIPLE_TAG_RHP: {
route: 'search/edit-multiple/tag/:tagListIndex',
getRoute: (tagListIndex = 0) => `search/edit-multiple/tag/${tagListIndex}` as const,
},
SEARCH_EDIT_MULTIPLE_BILLABLE_RHP: 'search/edit-multiple/billable',
SEARCH_EDIT_MULTIPLE_REIMBURSABLE_RHP: 'search/edit-multiple/reimbursable',
SEARCH_EDIT_MULTIPLE_TAX_RHP: 'search/edit-multiple/tax',
MOVE_TRANSACTIONS_SEARCH_RHP: {
route: 'search/move-transactions/search/:backTo?',
getRoute: (backTo?: string) => {
Expand Down
10 changes: 10 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ const SCREENS = {
TRANSACTION_HOLD_REASON_RHP: 'Search_Transaction_Hold_Reason_RHP',
TRANSACTION_HOLD_REASON_SEARCH: 'Search_Transaction_Hold_Reason_Search',
SEARCH_REJECT_REASON_RHP: 'Search_Reject_Reason_RHP',
EDIT_MULTIPLE_TRANSACTIONS_RHP: 'Search_Edit_Multiple_Transactions_RHP',
EDIT_MULTIPLE_AMOUNT_RHP: 'Search_Edit_Multiple_Amount_RHP',
EDIT_MULTIPLE_DESCRIPTION_RHP: 'Search_Edit_Multiple_Description_RHP',
EDIT_MULTIPLE_MERCHANT_RHP: 'Search_Edit_Multiple_Merchant_RHP',
EDIT_MULTIPLE_DATE_RHP: 'Search_Edit_Multiple_Date_RHP',
EDIT_MULTIPLE_CATEGORY_RHP: 'Search_Edit_Multiple_Category_RHP',
EDIT_MULTIPLE_TAG_RHP: 'Search_Edit_Multiple_Tag_RHP',
EDIT_MULTIPLE_BILLABLE_RHP: 'Search_Edit_Multiple_Billable_RHP',
EDIT_MULTIPLE_REIMBURSABLE_RHP: 'Search_Edit_Multiple_Reimbursable_RHP',
EDIT_MULTIPLE_TAX_RHP: 'Search_Edit_Multiple_Tax_RHP',
TRANSACTIONS_CHANGE_REPORT_SEARCH_RHP: 'Search_Transactions_Change_Report_Search',
},
SETTINGS: {
Expand Down
28 changes: 2 additions & 26 deletions src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ import {
isTrackExpenseReportNew,
shouldEnableNegative,
} from '@libs/ReportUtils';
import {hasEnabledTags} from '@libs/TagsOptionsListUtils';
import {hasEnabledTags, shouldShowDependentTagList} from '@libs/TagsOptionsListUtils';
import {
getBillable,
getCurrency,
Expand All @@ -87,7 +87,6 @@ import {
getOriginalAmountForDisplay,
getOriginalTransactionWithSplitInfo,
getReimbursable,
getTagArrayFromName,
getTagForDisplay,
getTaxName,
hasMissingSmartscanFields,
Expand Down Expand Up @@ -738,30 +737,7 @@ function MoneyRequestView({
const tagForDisplay = getTagForDisplay(updatedTransaction ?? transaction, index);
let shouldShow = false;
if (hasDependentTags) {
if (index === 0) {
shouldShow = true;
} else {
const prevTagValue = getTagForDisplay(transaction, index - 1);
if (!prevTagValue) {
shouldShow = false;
} else {
const parentTag = getTagArrayFromName(transactionTag ?? '')
.slice(0, index)
.join(':');

const availableTags = Object.values(tags).filter((policyTag) => {
const filterRegex = policyTag.rules?.parentTagsFilter;
if (!filterRegex) {
return true;
}

const regex = new RegExp(filterRegex);
return regex.test(parentTag ?? '');
});

shouldShow = availableTags.some((tag) => tag.enabled);
}
}
shouldShow = shouldShowDependentTagList(index, transactionTag, tags);
} else {
shouldShow = !!tagForDisplay || (canEdit && hasEnabledOptions(tags));
}
Expand Down
21 changes: 19 additions & 2 deletions src/components/TaxPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,25 @@ type TaxPickerProps = {
* If enabled, the content will have a bottom padding equal to account for the safe bottom area inset.
*/
addBottomSafeAreaPadding?: boolean;

/**
* If enabled, allows deselecting the currently selected tax rate by tapping it again.
* When disabled (default), tapping the selected tax rate will dismiss the picker without calling onSubmit.
*/
allowDeselect?: boolean;
};

function TaxPicker({selectedTaxRate = '', policyID, transactionID, onSubmit, action, iouType, onDismiss = Navigation.goBack, addBottomSafeAreaPadding}: TaxPickerProps) {
function TaxPicker({
selectedTaxRate = '',
policyID,
transactionID,
onSubmit,
action,
iouType,
onDismiss = Navigation.goBack,
addBottomSafeAreaPadding,
allowDeselect = false,
}: TaxPickerProps) {
const {translate, localeCompare} = useLocalize();
const [searchValue, setSearchValue] = useState('');
const [splitDraftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`);
Expand Down Expand Up @@ -119,7 +135,8 @@ function TaxPicker({selectedTaxRate = '', policyID, transactionID, onSubmit, act
const currentTaxRateValue = taxCode ? taxRates?.taxes?.[taxCode]?.value : undefined;
const hasMatchingTaxValue = taxValue === undefined || currentTaxRateValue === taxValue;

if (isSameTaxCode && hasMatchingTaxValue) {
// If deselection is not allowed and the same option is selected, just dismiss
if (!allowDeselect && isSameTaxCode && hasMatchingTaxValue) {
onDismiss();
return;
}
Expand Down
39 changes: 37 additions & 2 deletions src/hooks/useSearchBulkActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import Navigation from '@libs/Navigation/Navigation';
import {getConnectedIntegration} from '@libs/PolicyUtils';
import {getSecondaryExportReportActions, isMergeActionForSelectedTransactions} from '@libs/ReportSecondaryActionUtils';
import {
canEditMultipleTransactions,
getIntegrationIcon,
getReportOrDraftReport,
isBusinessInvoiceRoom,
Expand All @@ -49,7 +50,7 @@ import {navigateToSearchRHP, shouldShowDeleteOption} from '@libs/SearchUIUtils';
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
import {hasTransactionBeenRejected, isDeletedTransaction} from '@libs/TransactionUtils';
import variables from '@styles/variables';
import {canIOUBePaid, dismissRejectUseExplanation} from '@userActions/IOU';
import {canIOUBePaid, dismissRejectUseExplanation, initBulkEditDraftTransaction} from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
Expand All @@ -63,6 +64,7 @@ import {useMemoizedLazyExpensifyIcons} from './useLazyAsset';
import useLocalize from './useLocalize';
import useNetwork from './useNetwork';
import useOnyx from './useOnyx';
import usePermissions from './usePermissions';
import usePersonalPolicy from './usePersonalPolicy';
import useSelfDMReport from './useSelfDMReport';
import useTheme from './useTheme';
Expand Down Expand Up @@ -98,6 +100,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) {
const {accountID} = currentUserPersonalDetails;
const allTransactions = useAllTransactions();
const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
const [allReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ PERF-11 (docs)

useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS) subscribes to the entire report actions collection — one of the largest and most frequently updated collections in Onyx (every new chat message, reaction, or action update triggers a re-render). This hook is active on the search screen and will cause excessive re-renders.

The data is only needed to pass into canEditMultipleTransactions() for the selected transactions' reports. Consider either:

  1. Moving the canEditMultipleTransactions check into the onSelected handler where data can be read imperatively via Onyx.getValue() rather than maintaining a live subscription.
  2. Subscribing only to the specific REPORT_ACTIONS keys needed based on the selected transactions' report IDs.

Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

const [allReportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS);
const selfDMReport = useSelfDMReport();
const [lastPaymentMethods] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD);
Expand All @@ -110,6 +113,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) {
const [allTransactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
const personalPolicy = usePersonalPolicy();
const [userBillingGraceEndPeriods] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END);
const {isBetaEnabled} = usePermissions();
const [ownerBillingGraceEndPeriod] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END);
const undeleteTransactions = useUndeleteTransactions();

Expand Down Expand Up @@ -671,7 +675,9 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) {
);

const onBulkPaySelectedRef = useRef(onBulkPaySelected);
onBulkPaySelectedRef.current = onBulkPaySelected;
useEffect(() => {
onBulkPaySelectedRef.current = onBulkPaySelected;
});
const stableOnBulkPaySelected = useCallback((paymentMethod?: PaymentMethodType, additionalData?: BulkPaySelectionData) => {
onBulkPaySelectedRef.current?.(paymentMethod, additionalData);
}, []);
Expand Down Expand Up @@ -886,6 +892,30 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) {
return [exportButtonOption];
}

const isExpenseReportSearch = typeExpenseReport || searchResults?.search.type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT;
const selectedTransactionsList = Object.values(selectedTransactions)
.map((transaction) => transaction.transaction)
.filter((transaction): transaction is Transaction => !!transaction);
const canEditMultiple =
canEditMultipleTransactions(selectedTransactionsList, allReportActions, allReports, policies, isExpenseReportSearch, searchResults?.data) && isBetaEnabled(CONST.BETAS.BULK_EDIT);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NAB, probably could check for the beta first since that will be much faster but doubt it matters.


if (canEditMultiple) {
options.push({
icon: expensifyIcons.Pencil,
text: translate('search.bulkActions.editMultiple'),
value: CONST.SEARCH.BULK_ACTION_TYPES.EDIT,
shouldCloseModalOnSelect: true,
onSelected: () => {
const selectedTransactionIDs = Object.keys(selectedTransactions).filter((transactionID) => {
const selectedTransaction = selectedTransactions[transactionID];
return !!selectedTransaction?.transaction?.transactionID || !!allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`];
});
initBulkEditDraftTransaction(selectedTransactionIDs);
Navigation.navigate(ROUTES.SEARCH_EDIT_MULTIPLE_TRANSACTIONS_RHP);
},
});
}

const areSelectedTransactionsIncludedInReports = selectedTransactionsKeys.every((id) =>
selectedTransactions[id].reportID ? selectedReportIDs.includes(selectedTransactions[id].reportID) : true,
);
Expand Down Expand Up @@ -1190,6 +1220,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) {
expensifyIcons.ArrowCollapse,
expensifyIcons.DocumentMerge,
expensifyIcons.ArrowSplit,
expensifyIcons.Pencil,
expensifyIcons.Trashcan,
expensifyIcons.RotateLeft,
expensifyIcons.Exclamation,
Expand All @@ -1205,6 +1236,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) {
selectedTransactionReportIDs,
selectedPolicyIDs,
policies,
allReportActions,
integrationsExportTemplates,
csvExportLayouts,
allReports,
Expand Down Expand Up @@ -1242,6 +1274,9 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) {
userBillingGraceEndPeriods,
ownerBillingGraceEndPeriod,
currentSearchKey,
allTransactions,
isBetaEnabled,
shouldShowBusinessBankAccountOptions,
]);

const handleOfflineModalClose = useCallback(() => {
Expand Down
29 changes: 29 additions & 0 deletions src/hooks/useSearchBulkEditPolicyID.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {useSearchStateContext} from '@components/Search/SearchContext';
import {getSearchBulkEditPolicyID} from '@libs/SearchUIUtils';
import {withSnapshotReports, withSnapshotTransactions} from '@pages/Search/SearchEditMultiple/SearchEditMultipleUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import useOnyx from './useOnyx';

/**
* Resolves the bulk-edit policyID from the selected transactions, using
* snapshot-merged collections so that expenses only present in the search
* snapshot (e.g. after a hard refresh) are still resolved correctly.
*/
function useSearchBulkEditPolicyID(): string | undefined {
const {currentSearchResults} = useSearchStateContext();
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const [draftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_BULK_EDIT_TRANSACTION_ID}`);
const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION);
const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);

const snapshotData = currentSearchResults?.data;
const mergedTransactions = withSnapshotTransactions(allTransactions, snapshotData);
const mergedReports = withSnapshotReports(allReports, snapshotData);

const selectedTransactionIDs = draftTransaction?.selectedTransactionIDs ?? [];

return getSearchBulkEditPolicyID(selectedTransactionIDs, activePolicyID, mergedTransactions, mergedReports);
}

export default useSearchBulkEditPolicyID;
Loading
Loading