Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
898daaf
add reject to bulk action types
lakchote Oct 17, 2025
dfde406
add reject reason route and navigation config
lakchote Oct 17, 2025
6622395
add reject translations for all languages
lakchote Oct 17, 2025
6845dee
add per-transaction loading helpers for bulk reject
lakchote Oct 17, 2025
a2d377b
implement client-side bulk reject loop
lakchote Oct 17, 2025
a2256be
create search reject reason page
lakchote Oct 17, 2025
60e4f4b
wire reject action to search page bulk menu
lakchote Oct 17, 2025
7f5a7f9
fixes
lakchote Oct 20, 2025
a9a4e6a
fix translations
lakchote Oct 20, 2025
1c7e6c1
make translation multi-line
lakchote Oct 21, 2025
48d9243
add comment
lakchote Oct 21, 2025
c32f729
remove unneeded check
lakchote Oct 21, 2025
b29db8b
put back the default value for `backTo`
lakchote Oct 21, 2025
65728a4
Merge branch 'main' into lucien/reject-bulk-action
lakchote Oct 22, 2025
c86f623
change `shouldShowRejectOption` condition
lakchote Oct 24, 2025
bf36580
`sharedRejectedToReportID` logic
lakchote Oct 24, 2025
6b99310
pass down the `sharedRejectedToReportID` when rejecting in bulk
lakchote Oct 24, 2025
e104d44
fix style
lakchote Oct 24, 2025
82e6486
Merge branch 'main' into lucien/reject-bulk-action
lakchote Oct 24, 2025
9a4e122
fix lint
lakchote Oct 24, 2025
1f3238e
Merge branch 'main' into lucien/reject-bulk-action
lakchote Oct 29, 2025
e8552ea
remove redundant condition
lakchote Oct 29, 2025
de40181
remove `backTo`
lakchote Oct 29, 2025
c4d16a0
fix lint
lakchote Oct 29, 2025
2aaf9e3
fix bug
lakchote Oct 29, 2025
e79e1ef
fix lint
lakchote Oct 29, 2025
66218a2
fix style
lakchote Oct 29, 2025
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
1 change: 1 addition & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6518,6 +6518,7 @@ const CONST = {
HOLD: 'hold',
UNHOLD: 'unhold',
DELETE: 'delete',
REJECT: 'reject',
CHANGE_REPORT: 'changeReport',
},
TRANSACTION_TYPE: {
Expand Down
1 change: 1 addition & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const ROUTES = {
},
},
TRANSACTION_HOLD_REASON_RHP: 'search/hold',
SEARCH_REJECT_REASON_RHP: 'search/reject',
MOVE_TRANSACTIONS_SEARCH_RHP: 'search/move-transactions',

// This is a utility route used to go to the user's concierge chat, or the sign-in page if the user's not authenticated
Expand Down
2 changes: 2 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const SCREENS = {
ROOT: 'Search_Root',
MONEY_REQUEST_REPORT: 'Search_Money_Request_Report',
MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS: 'Search_Money_Request_Report_Hold_Transactions',
MONEY_REQUEST_REPORT_REJECT_TRANSACTIONS: 'Search_Money_Request_Report_Reject_Transactions',
REPORT_RHP: 'Search_Report_RHP',
ADVANCED_FILTERS_RHP: 'Search_Advanced_Filters_RHP',
ADVANCED_FILTERS_TYPE_RHP: 'Search_Advanced_Filters_Type_RHP',
Expand Down Expand Up @@ -86,6 +87,7 @@ const SCREENS = {
SAVED_SEARCH_RENAME_RHP: 'Search_Saved_Search_Rename_RHP',
ADVANCED_FILTERS_IN_RHP: 'Search_Advanced_Filters_In_RHP',
TRANSACTION_HOLD_REASON_RHP: 'Search_Transaction_Hold_Reason_RHP',
SEARCH_REJECT_REASON_RHP: 'Search_Reject_Reason_RHP',
TRANSACTIONS_CHANGE_REPORT_SEARCH_RHP: 'Search_Transactions_Change_Report_RHP',
},
SETTINGS: {
Expand Down
1 change: 1 addition & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6250,6 +6250,7 @@ ${amount} für ${merchant} - ${date}`,
delete: 'Löschen',
hold: 'Halten',
unhold: 'Halten entfernen',
reject: 'Ablehnen',
noOptionsAvailable: 'Keine Optionen verfügbar für die ausgewählte Gruppe von Ausgaben.',
},
filtersHeader: 'Filter',
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6212,6 +6212,7 @@ const translations = {
delete: 'Delete',
hold: 'Hold',
unhold: 'Remove hold',
reject: 'Reject',
noOptionsAvailable: 'No options available for the selected group of expenses.',
},
filtersHeader: 'Filters',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6235,6 +6235,7 @@ ${amount} para ${merchant} - ${date}`,
delete: 'Eliminar',
hold: 'Retener',
unhold: 'Desbloquear',
reject: 'Rechazar',
noOptionsAvailable: 'No hay opciones disponibles para el grupo de gastos seleccionado.',
},
filtersHeader: 'Filtros',
Expand Down
1 change: 1 addition & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6256,6 +6256,7 @@ ${amount} pour ${merchant} - ${date}`,
delete: 'Supprimer',
hold: 'Attente',
unhold: 'Supprimer la suspension',
reject: 'Refuser',
noOptionsAvailable: 'Aucune option disponible pour le groupe de dépenses sélectionné.',
},
filtersHeader: 'Filtres',
Expand Down
1 change: 1 addition & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6262,6 +6262,7 @@ ${amount} per ${merchant} - ${date}`,
delete: 'Elimina',
hold: 'Attendere',
unhold: 'Rimuovi blocco',
reject: 'Rifiuta',
noOptionsAvailable: 'Nessuna opzione disponibile per il gruppo di spese selezionato.',
},
filtersHeader: 'Filtri',
Expand Down
1 change: 1 addition & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6197,6 +6197,7 @@ ${date} - ${merchant}に${amount}`,
delete: '削除',
hold: '保留',
unhold: '保留を解除',
reject: '却下',
noOptionsAvailable: '選択した経費グループには利用可能なオプションがありません。',
},
filtersHeader: 'フィルター',
Expand Down
1 change: 1 addition & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6246,6 +6246,7 @@ ${amount} voor ${merchant} - ${date}`,
delete: 'Verwijderen',
hold: 'Vasthouden',
unhold: 'Verwijder blokkering',
reject: 'Afwijzen',
noOptionsAvailable: 'Geen opties beschikbaar voor de geselecteerde groep uitgaven.',
},
filtersHeader: 'Filters',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6233,6 +6233,7 @@ ${amount} dla ${merchant} - ${date}`,
delete: 'Usuń',
hold: 'Trzymaj',
unhold: 'Usuń blokadę',
reject: 'Odrzuć',
noOptionsAvailable: 'Brak dostępnych opcji dla wybranej grupy wydatków.',
},
filtersHeader: 'Filtry',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6242,6 +6242,7 @@ ${amount} para ${merchant} - ${date}`,
delete: 'Excluir',
hold: 'Manter',
unhold: 'Remover retenção',
reject: 'Rejeitar',
noOptionsAvailable: 'Nenhuma opção disponível para o grupo de despesas selecionado.',
},
filtersHeader: 'Filtros',
Expand Down
1 change: 1 addition & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6118,6 +6118,7 @@ ${merchant}的${amount} - ${date}`,
delete: '删除',
hold: '保持',
unhold: '移除保留',
reject: '拒绝',
noOptionsAvailable: '所选费用组没有可用选项。',
},
filtersHeader: '筛选器',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,15 @@ const OPTIONS_PER_SCREEN: Partial<Record<Screen, PlatformStackNavigationOptions>
[SCREENS.SEARCH.MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS]: {
animation: Animations.NONE,
},
[SCREENS.SEARCH.MONEY_REQUEST_REPORT_REJECT_TRANSACTIONS]: {
animation: Animations.NONE,
},
[SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP]: {
animation: Animations.NONE,
},
[SCREENS.SEARCH.SEARCH_REJECT_REASON_RHP]: {
animation: Animations.NONE,
},
[SCREENS.SEARCH.TRANSACTIONS_CHANGE_REPORT_SEARCH_RHP]: {
animation: Animations.NONE,
},
Expand Down Expand Up @@ -831,7 +837,9 @@ const MergeTransactionStackNavigator = createModalStackNavigator<MergeTransactio
const SearchReportModalStackNavigator = createModalStackNavigator<SearchReportParamList>({
[SCREENS.SEARCH.REPORT_RHP]: () => require<ReactComponentModule>('../../../../pages/home/ReportScreen').default,
[SCREENS.SEARCH.MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS]: () => require<ReactComponentModule>('../../../../pages/Search/SearchHoldReasonPage').default,
[SCREENS.SEARCH.MONEY_REQUEST_REPORT_REJECT_TRANSACTIONS]: () => require<ReactComponentModule>('../../../../pages/Search/SearchRejectReasonPage').default,
[SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP]: () => require<ReactComponentModule>('../../../../pages/Search/SearchHoldReasonPage').default,
[SCREENS.SEARCH.SEARCH_REJECT_REASON_RHP]: () => require<ReactComponentModule>('../../../../pages/Search/SearchRejectReasonPage').default,
[SCREENS.SEARCH.TRANSACTIONS_CHANGE_REPORT_SEARCH_RHP]: () => require<ReactComponentModule>('../../../../pages/Search/SearchTransactionsChangeReport').default,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const SEARCH_TO_RHP: Partial<Record<keyof SearchFullscreenNavigatorParamList, st
SCREENS.SEARCH.ADVANCED_FILTERS_STATUS_RHP,
SCREENS.SEARCH.REPORT_RHP,
SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP,
SCREENS.SEARCH.SEARCH_REJECT_REASON_RHP,
SCREENS.SEARCH.TRANSACTIONS_CHANGE_REPORT_SEARCH_RHP,
SCREENS.SEARCH.ADVANCED_FILTERS_RHP,
SCREENS.SEARCH.ADVANCED_FILTERS_CURRENCY_RHP,
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1544,6 +1544,7 @@ const config: LinkingOptions<RootNavigatorParamList>['config'] = {
[SCREENS.SEARCH.REPORT_RHP]: ROUTES.SEARCH_REPORT.route,
[SCREENS.SEARCH.MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS]: ROUTES.SEARCH_MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS.route,
[SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP]: ROUTES.TRANSACTION_HOLD_REASON_RHP,
[SCREENS.SEARCH.SEARCH_REJECT_REASON_RHP]: ROUTES.SEARCH_REJECT_REASON_RHP,
[SCREENS.SEARCH.TRANSACTIONS_CHANGE_REPORT_SEARCH_RHP]: ROUTES.MOVE_TRANSACTIONS_SEARCH_RHP,
},
},
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2558,6 +2558,11 @@ type SearchReportParamList = {
/** Selected transactions' report ID */
reportID: string;
};
[SCREENS.SEARCH.SEARCH_REJECT_REASON_RHP]: Record<string, never>;
[SCREENS.SEARCH.MONEY_REQUEST_REPORT_REJECT_TRANSACTIONS]: {
/** Selected transactions' report ID */
reportID: string;
};
};

type SearchFullscreenNavigatorParamList = {
Expand Down
55 changes: 51 additions & 4 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@
};

let allPersonalDetails: OnyxTypes.PersonalDetailsList = {};
Onyx.connect({

Check warning on line 699 in src/libs/actions/IOU.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => {
allPersonalDetails = value ?? {};
Expand Down Expand Up @@ -773,13 +773,13 @@
};

let allBetas: OnyxEntry<OnyxTypes.Beta[]>;
Onyx.connect({

Check warning on line 776 in src/libs/actions/IOU.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.BETAS,
callback: (value) => (allBetas = value),
});

let allTransactions: NonNullable<OnyxCollection<OnyxTypes.Transaction>> = {};
Onyx.connect({

Check warning on line 782 in src/libs/actions/IOU.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -793,7 +793,7 @@
});

let allTransactionDrafts: NonNullable<OnyxCollection<OnyxTypes.Transaction>> = {};
Onyx.connect({

Check warning on line 796 in src/libs/actions/IOU.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION_DRAFT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -802,7 +802,7 @@
});

let allTransactionViolations: NonNullable<OnyxCollection<OnyxTypes.TransactionViolations>> = {};
Onyx.connect({

Check warning on line 805 in src/libs/actions/IOU.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -816,7 +816,7 @@
});

let allNextSteps: NonNullable<OnyxCollection<OnyxTypes.ReportNextStep>> = {};
Onyx.connect({

Check warning on line 819 in src/libs/actions/IOU.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.NEXT_STEP,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -825,14 +825,14 @@
});

let allPolicyCategories: OnyxCollection<OnyxTypes.PolicyCategories> = {};
Onyx.connect({

Check warning on line 828 in src/libs/actions/IOU.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY_CATEGORIES,
waitForCollectionCallback: true,
callback: (val) => (allPolicyCategories = val),
});

const allPolicies: OnyxCollection<OnyxTypes.Policy> = {};
Onyx.connect({

Check warning on line 835 in src/libs/actions/IOU.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY,
callback: (val, key) => {
if (!key) {
Expand Down Expand Up @@ -868,14 +868,14 @@
// `allRecentlyUsedTags` was moved here temporarily from `src/libs/actions/Policy/Tag.ts` during the `Deprecate Onyx.connect` refactor.
// All uses of this variable should be replaced with `useOnyx`.
let allRecentlyUsedTags: OnyxCollection<RecentlyUsedTags> = {};
Onyx.connect({

Check warning on line 871 in src/libs/actions/IOU.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS,
waitForCollectionCallback: true,
callback: (val) => (allRecentlyUsedTags = val),
});

let allReports: OnyxCollection<OnyxTypes.Report>;
Onyx.connect({

Check warning on line 878 in src/libs/actions/IOU.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand Down Expand Up @@ -12735,9 +12735,11 @@
* @param transactionID - The ID of the transaction to reject
* @param reportID - The ID of the expense report to reject
* @param comment - The comment to add to the reject action
* @returns The route to navigate back to
* @param options
* - sharedRejectedToReportID: When rejecting multiple expenses sequentially, pass a single shared destination reportID so all rejections land in the same new report.
* @returns The route to navigate back to*
*/
function rejectMoneyRequest(transactionID: string, reportID: string, comment: string): Route | undefined {
function rejectMoneyRequest(transactionID: string, reportID: string, comment: string, options?: {sharedRejectedToReportID?: string}): Route | undefined {
const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`];
const transactionAmount = getAmount(transaction);
const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`];
Expand All @@ -12757,7 +12759,7 @@
const childReportID = reportAction?.childReportID;

let movedToReport;
let rejectedToReportID;
let rejectedToReportID = options?.sharedRejectedToReportID;
let urlToNavigateBack;

const hasMultipleExpenses = getReportTransactions(reportID).length > 1;
Expand Down Expand Up @@ -12964,7 +12966,10 @@
},
});
} else {
rejectedToReportID = generateReportID();
// When no existing open report is found, use the sharedRejectedToReportID
// so multiple sequential rejections land in the same destination report
// Fallback to generating a fresh ID if not provided
rejectedToReportID = rejectedToReportID ?? generateReportID();
}
optimisticData.push(
{
Expand Down Expand Up @@ -13181,6 +13186,48 @@
});
Comment thread
luacmartins marked this conversation as resolved.
});

// Add snapshot updates if called from the Reports page
const currentSearchQueryJSON = getCurrentSearchQueryJSON();
if (currentSearchQueryJSON?.hash) {
optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}`,
value: {
data: {
[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]: {
isActionLoading: true,
errors: null,
},
},
},
});

successData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}`,
value: {
data: {
[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]: {
isActionLoading: false,
},
},
},
});

failureData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}`,
value: {
data: {
[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]: {
isActionLoading: false,
errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
},
},
});
}

// Build API parameters
const parameters: RejectMoneyRequestParams = {
transactionID,
Expand Down
26 changes: 26 additions & 0 deletions src/libs/actions/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type {OptimisticExportIntegrationAction} from '@libs/ReportUtils';
import {
buildOptimisticExportIntegrationAction,
buildOptimisticIOUReportAction,
generateReportID,
getReportTransactions,
hasHeldExpenses,
isExpenseReport,
Expand All @@ -47,6 +48,7 @@ import type {SearchPolicy, SearchReport, SearchTransaction} from '@src/types/ony
import type Nullable from '@src/types/utils/Nullable';
import SafeString from '@src/utils/SafeString';
import {setPersonalBankAccountContinueKYCOnSuccess} from './BankAccounts';
import {rejectMoneyRequest} from './IOU';
import {setOptimisticTransactionThread} from './Report';
import {saveLastSearchParams} from './ReportNavigation';

Expand Down Expand Up @@ -580,6 +582,29 @@ function unholdMoneyRequestOnSearch(hash: number, transactionIDList: string[]) {
API.write(WRITE_COMMANDS.UNHOLD_MONEY_REQUEST_ON_SEARCH, {hash, transactionIDList}, {optimisticData, finallyData});
}

function rejectMoneyRequestsOnSearch(hash: number, selectedTransactions: SelectedTransactions, comment: string) {
Comment thread
luacmartins marked this conversation as resolved.
const transactionIDs = Object.keys(selectedTransactions);

const transactionsByReport: Record<string, string[]> = {};
Comment thread
luacmartins marked this conversation as resolved.
transactionIDs.forEach((transactionID) => {
Comment thread
luacmartins marked this conversation as resolved.
const reportID = selectedTransactions[transactionID].reportID;
if (!transactionsByReport[reportID]) {
transactionsByReport[reportID] = [];
}
transactionsByReport[reportID].push(transactionID);
});

Object.entries(transactionsByReport).forEach(([reportID, reportTransactionIDs]) => {
Comment thread
luacmartins marked this conversation as resolved.
Comment thread
luacmartins marked this conversation as resolved.
// Share a single destination ID across all rejections from the same source report

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.

❌ JSDoc Comments (docs)

Missing JSDoc documentation for the rejectMoneyRequestsOnSearch function. All functions should have proper JSDoc comments explaining their purpose, parameters, and return values.

/**
 * Reject multiple money requests from search results in bulk
 * @param hash - The search query hash
 * @param selectedTransactions - Map of selected transactions with their metadata
 * @param comment - The rejection reason comment
 */
function rejectMoneyRequestsOnSearch(hash: number, selectedTransactions: SelectedTransactions, comment: string) {

const sharedRejectedToReportID = generateReportID();
reportTransactionIDs.forEach((transactionID) => {
rejectMoneyRequest(transactionID, reportID, comment, {sharedRejectedToReportID});
});
Comment thread
luacmartins marked this conversation as resolved.
});
Comment thread
lakchote marked this conversation as resolved.

playSound(SOUNDS.SUCCESS);
}

function deleteMoneyRequestOnSearch(hash: number, transactionIDList: string[]) {
const {optimisticData: loadingOptimisticData, finallyData} = getOnyxLoadingData(hash);
const optimisticData: OnyxUpdate[] = [
Expand Down Expand Up @@ -956,6 +981,7 @@ export {
deleteMoneyRequestOnSearch,
holdMoneyRequestOnSearch,
unholdMoneyRequestOnSearch,
rejectMoneyRequestsOnSearch,
exportSearchItemsToCSV,
queueExportSearchItemsToCSV,
queueExportSearchWithTemplate,
Expand Down
13 changes: 6 additions & 7 deletions src/pages/Search/SearchHoldReasonPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import ONYXKEYS from '@src/ONYXKEYS';
import SCREENS from '@src/SCREENS';
import INPUT_IDS from '@src/types/form/MoneyRequestHoldReasonForm';

function SearchHoldReasonPage({route}: PlatformStackScreenProps<Omit<SearchReportParamList, typeof SCREENS.SEARCH.REPORT_RHP>>) {
type SearchHoldReasonPageProps =
| PlatformStackScreenProps<SearchReportParamList, typeof SCREENS.SEARCH.MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS>
| PlatformStackScreenProps<SearchReportParamList, typeof SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP>;

function SearchHoldReasonPage({route}: SearchHoldReasonPageProps) {
const {translate} = useLocalize();
const {backTo = '', reportID} = route.params ?? {};
const context = useSearchContext();
Expand All @@ -40,11 +44,6 @@ function SearchHoldReasonPage({route}: PlatformStackScreenProps<Omit<SearchRepor
const validate = useCallback(
(values: FormOnyxValues<typeof ONYXKEYS.FORMS.MONEY_REQUEST_HOLD_FORM>) => {
const errors: FormInputErrors<typeof ONYXKEYS.FORMS.MONEY_REQUEST_HOLD_FORM> = getFieldRequiredErrors(values, [INPUT_IDS.COMMENT]);

if (!values.comment) {
errors.comment = translate('common.error.fieldRequired');
}

return errors;
},
[translate],
Comment thread
luacmartins marked this conversation as resolved.
Comment thread
luacmartins marked this conversation as resolved.
Comment thread
luacmartins marked this conversation as resolved.
Comment thread
luacmartins marked this conversation as resolved.
Expand All @@ -59,7 +58,7 @@ function SearchHoldReasonPage({route}: PlatformStackScreenProps<Omit<SearchRepor
<HoldReasonFormView
onSubmit={onSubmit}
validate={validate}
backTo={backTo}
backTo={backTo ?? ''}
/>
);
}
Expand Down
40 changes: 39 additions & 1 deletion src/pages/Search/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,14 @@ import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {SearchFullscreenNavigatorParamList} from '@libs/Navigation/types';
import {getActiveAdminWorkspaces, hasDynamicExternalWorkflow, hasVBBA, isPaidGroupPolicy} from '@libs/PolicyUtils';
import {generateReportID, getPolicyExpenseChat, getReportOrDraftReport, isExpenseReport as isExpenseReportUtil, isIOUReport as isIOUReportUtil} from '@libs/ReportUtils';
import {
canRejectReportAction,
generateReportID,
getPolicyExpenseChat,
getReportOrDraftReport,
isExpenseReport as isExpenseReportUtil,
isIOUReport as isIOUReportUtil,
} from '@libs/ReportUtils';
import {buildCannedSearchQuery, buildSearchQueryJSON} from '@libs/SearchQueryUtils';
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
import type {ReceiptFile} from '@pages/iou/request/step/IOURequestStepScan/types';
Expand Down Expand Up @@ -255,6 +262,8 @@ function SearchPage({route}: SearchPageProps) {
) as PaymentData[];

payMoneyRequestOnSearch(hash, paymentData, transactionIDList);

// eslint-disable-next-line @typescript-eslint/no-deprecated
InteractionManager.runAfterInteractions(() => {
clearSelectedTransactions();
});
Expand Down Expand Up @@ -397,6 +406,35 @@ function SearchPage({route}: SearchPageProps) {
},
});
}

const areSelectedTransactionsRejectable =
selectedTransactionReportIDs.length > 0 &&
selectedTransactionReportIDs.every((id) => {
const report = getReportOrDraftReport(id);
if (!report) {
return false;
Comment thread
luacmartins marked this conversation as resolved.
}
const policyForReport = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`] ?? undefined;
return canRejectReportAction(currentUserPersonalDetails?.login ?? '', report, policyForReport);
});
const shouldShowRejectOption = !isOffline && !isAnyTransactionOnHold && areSelectedTransactionsRejectable;
if (shouldShowRejectOption) {
options.push({
icon: Expensicons.ThumbsDown,
text: translate('search.bulkActions.reject'),
value: CONST.SEARCH.BULK_ACTION_TYPES.REJECT,
shouldCloseModalOnSelect: true,
onSelected: () => {
if (isOffline) {
setIsOfflineModalVisible(true);
return;
}

Navigation.navigate(ROUTES.SEARCH_REJECT_REASON_RHP);
},
});
}

const shouldEnableExpenseBulk = selectedReports.length
? selectedReports.every(
(report) => report.allActions.includes(CONST.SEARCH.ACTION_TYPES.PAY) && report.policyID && getLastPolicyPaymentMethod(report.policyID, lastPaymentMethods),
Expand Down
Loading
Loading