Skip to content
Merged
1 change: 1 addition & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6566,6 +6566,7 @@ const CONST = {
STATEMENTS: 'statements',
UNAPPROVED_CASH: 'unapprovedCash',
UNAPPROVED_CARD: 'unapprovedCard',
RECONCILIATION: 'reconciliation',
},
GROUP_PREFIX: 'group_',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ function SearchFiltersBar({queryJSON, headerButtonsOptions, isMobileSelectionMod
const shouldDisplayGroupCurrencyFilter = shouldDisplayGroupByFilter && hasMultipleOutputCurrency;
const shouldDisplayFeedFilter = feedOptions.length > 1 && !!filterFormValues.feed;
const shouldDisplayPostedFilter = !!filterFormValues.feed && (!!filterFormValues.postedOn || !!filterFormValues.postedAfter || !!filterFormValues.postedBefore);
const shouldDisplayWithdrawalTypeFilter = groupBy?.value === CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID && !!filterFormValues.withdrawalType;
const shouldDisplayWithdrawalTypeFilter = !!filterFormValues.withdrawalType;
const shouldDisplayWithdrawnFilter = !!filterFormValues.withdrawnOn || !!filterFormValues.withdrawnAfter || !!filterFormValues.withdrawnBefore;

const filterList = [
Expand Down
9 changes: 7 additions & 2 deletions src/components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,12 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS
return false;
}

const eligibleSearchKeys: Partial<SearchKey[]> = [CONST.SEARCH.SEARCH_KEYS.STATEMENTS, CONST.SEARCH.SEARCH_KEYS.UNAPPROVED_CASH, CONST.SEARCH.SEARCH_KEYS.UNAPPROVED_CARD];
const eligibleSearchKeys: Partial<SearchKey[]> = [
CONST.SEARCH.SEARCH_KEYS.STATEMENTS,
CONST.SEARCH.SEARCH_KEYS.UNAPPROVED_CASH,
CONST.SEARCH.SEARCH_KEYS.UNAPPROVED_CARD,
CONST.SEARCH.SEARCH_KEYS.RECONCILIATION,
];
return eligibleSearchKeys.includes(searchKey);
}, [offset, searchKey]);

Expand Down Expand Up @@ -756,7 +761,7 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS
return (
<View style={[shouldUseNarrowLayout ? styles.searchListContentContainerStyles : styles.mt3, styles.flex1]}>
<EmptySearchView
hash={hash}
similarSearchHash={similarSearchHash}
type={type}
groupBy={groupBy}
hasResults={searchResults.search.hasResults}
Expand Down
2 changes: 2 additions & 0 deletions src/hooks/useSearchTypeMenuSections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const policySelector = (policy: OnyxEntry<Policy>): OnyxEntry<Policy> =>
employeeList: policy.employeeList,
reimbursementChoice: policy.reimbursementChoice,
areCompanyCardsEnabled: policy.areCompanyCardsEnabled,
areExpensifyCardsEnabled: policy.areExpensifyCardsEnabled,
achAccount: policy.achAccount,
};

/**
Expand Down
1 change: 1 addition & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6047,6 +6047,7 @@ const translations = {
statements: 'Erklärungen',
unapprovedCash: 'Nicht genehmigtes Bargeld',
unapprovedCard: 'Nicht genehmigte Karte',
reconciliation: 'Abgleich',
saveSearch: 'Suche speichern',
deleteSavedSearch: 'Gespeicherte Suche löschen',
deleteSavedSearchConfirm: 'Möchten Sie diese Suche wirklich löschen?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6021,6 +6021,7 @@ const translations = {
statements: 'Statements',
unapprovedCash: 'Unapproved cash',
unapprovedCard: 'Unapproved card',
reconciliation: 'Reconciliation',
saveSearch: 'Save search',
deleteSavedSearch: 'Delete saved search',
deleteSavedSearchConfirm: 'Are you sure you want to delete this search?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6042,6 +6042,7 @@ const translations = {
statements: 'Extractos',
unapprovedCash: 'Efectivo no aprobado',
unapprovedCard: 'Tarjeta no aprobada',
reconciliation: 'Conciliación',
saveSearch: 'Guardar búsqueda',
savedSearchesMenuItemTitle: 'Guardadas',
searchName: 'Nombre de la búsqueda',
Expand Down
3 changes: 2 additions & 1 deletion src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5241,7 +5241,7 @@ const translations = {
autoSync: 'Synchronisation automatique',
autoSyncDescription: 'Synchronisez NetSuite et Expensify automatiquement, chaque jour. Exportez le rapport finalisé en temps réel.',
reimbursedReports: 'Synchroniser les rapports remboursés',
cardReconciliation: 'Rapprochement de carte',
cardReconciliation: 'Rapprochement',
reconciliationAccount: 'Compte de réconciliation',
continuousReconciliation: 'Réconciliation Continue',
saveHoursOnReconciliation:
Expand Down Expand Up @@ -6060,6 +6060,7 @@ const translations = {
statements: 'Relevés',
unapprovedCash: 'Espèces non approuvées',
unapprovedCard: 'Carte non approuvée',
reconciliation: 'Reconciliation',
saveSearch: 'Enregistrer la recherche',
deleteSavedSearch: 'Supprimer la recherche enregistrée',
deleteSavedSearchConfirm: 'Êtes-vous sûr de vouloir supprimer cette recherche ?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6062,6 +6062,7 @@ const translations = {
statements: 'Dichiarazioni',
unapprovedCash: 'Contanti non approvati',
unapprovedCard: 'Carta non approvata',
reconciliation: 'Riconciliazione',
saveSearch: 'Salva ricerca',
deleteSavedSearch: 'Elimina ricerca salvata',
deleteSavedSearchConfirm: 'Sei sicuro di voler eliminare questa ricerca?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6019,6 +6019,7 @@ const translations = {
statements: 'ステートメント',
unapprovedCash: '未承認現金',
unapprovedCard: '未承認のカード',
reconciliation: '照合',
saveSearch: '検索を保存',
deleteSavedSearch: '保存された検索を削除',
deleteSavedSearchConfirm: 'この検索を削除してもよろしいですか?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6054,6 +6054,7 @@ const translations = {
statements: 'Verklaringen',
unapprovedCash: 'Niet goedgekeurd contant geld',
unapprovedCard: 'Niet-goedgekeurde kaart',
reconciliation: 'Afstemming',
saveSearch: 'Zoekopdracht opslaan',
deleteSavedSearch: 'Verwijder opgeslagen zoekopdracht',
deleteSavedSearchConfirm: 'Weet je zeker dat je deze zoekopdracht wilt verwijderen?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6040,6 +6040,7 @@ const translations = {
statements: 'Oświadczenia',
unapprovedCash: 'Niezatwierdzone środki pieniężne',
unapprovedCard: 'Niezatwierdzona karta',
reconciliation: 'Uzgodnienie',
saveSearch: 'Zapisz wyszukiwanie',
deleteSavedSearch: 'Usuń zapisaną wyszukiwarkę',
deleteSavedSearchConfirm: 'Czy na pewno chcesz usunąć to wyszukiwanie?',
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 @@ -6055,6 +6055,7 @@ const translations = {
statements: 'Declarações',
unapprovedCash: 'Dinheiro não aprovado',
unapprovedCard: 'Cartão não aprovado',
reconciliation: 'Conciliação',
saveSearch: 'Salvar pesquisa',
deleteSavedSearch: 'Excluir pesquisa salva',
deleteSavedSearchConfirm: 'Tem certeza de que deseja excluir esta pesquisa?',
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 @@ -5937,6 +5937,7 @@ const translations = {
statements: '发言',
unapprovedCash: '未经批准的现金',
unapprovedCard: '未批准的卡',
reconciliation: '对账',
saveSearch: '保存搜索',
deleteSavedSearch: '删除已保存的搜索',
deleteSavedSearchConfirm: '您确定要删除此搜索吗?',
Expand Down
6 changes: 3 additions & 3 deletions src/libs/SearchQueryUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,11 @@ function getQueryHashes(query: SearchQueryJSON): {primaryHash: number; recentSea
orderedQuery += ` ${CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS}:${Array.isArray(query.status) ? query.status.join(',') : query.status}`;
orderedQuery += ` ${CONST.SEARCH.SYNTAX_ROOT_KEYS.GROUP_BY}:${query.groupBy}`;

let similarSearchHashInput = orderedQuery;
const filterSet = new Set<string>(orderedQuery);

query.flatFilters
.map((filter) => {
similarSearchHashInput += filter.key;
filterSet.add(filter.key);
Comment on lines +303 to +307
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

NAB. Should we exclude dates so that their presence does not affect the similar hash value? Currently if you remove the withdrawn filter, the reconciliation LHN item is no longer highlighted (same for statements with posted filter)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure that we want to completely remove those, since those filters are still part of the suggested search. I think we leave it as is for now and maybe reconsider if we think this is disruptive to users.


const filters = cloneDeep(filter.filters);
filters.sort((a, b) => customCollator.compare(a.value.toString(), b.value.toString()));
Expand All @@ -313,7 +313,7 @@ function getQueryHashes(query: SearchQueryJSON): {primaryHash: number; recentSea
.sort()
.forEach((filterString) => (orderedQuery += ` ${filterString}`));

const similarSearchHash = hashText(similarSearchHashInput, 2 ** 32);
const similarSearchHash = hashText(Array.from(filterSet).join(''), 2 ** 32);
const recentSearchHash = hashText(orderedQuery, 2 ** 32);

orderedQuery += ` ${CONST.SEARCH.SYNTAX_ROOT_KEYS.SORT_BY}:${query.sortBy}`;
Expand Down
36 changes: 35 additions & 1 deletion src/libs/SearchUIUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,26 @@ function getSuggestedSearches(defaultFeedID: string | undefined, accountID: numb
return this.searchQueryJSON?.similarSearchHash ?? CONST.DEFAULT_NUMBER_ID;
},
},
[CONST.SEARCH.SEARCH_KEYS.RECONCILIATION]: {
key: CONST.SEARCH.SEARCH_KEYS.RECONCILIATION,
translationPath: 'search.reconciliation',
type: CONST.SEARCH.DATA_TYPES.EXPENSE,
icon: Expensicons.Bank,
searchQuery: buildQueryStringFromFilterFormValues({
type: CONST.SEARCH.DATA_TYPES.EXPENSE,
withdrawalType: CONST.SEARCH.WITHDRAWAL_TYPE.REIMBURSEMENT,
Comment thread
luacmartins marked this conversation as resolved.
withdrawnOn: CONST.SEARCH.DATE_PRESETS.LAST_MONTH,
}),
get searchQueryJSON() {
return buildSearchQueryJSON(this.searchQuery);
},
get hash() {
return this.searchQueryJSON?.hash ?? CONST.DEFAULT_NUMBER_ID;
},
get similarSearchHash() {
return this.searchQueryJSON?.similarSearchHash ?? CONST.DEFAULT_NUMBER_ID;
},
},
};
}

Expand Down Expand Up @@ -474,6 +494,9 @@ function getSuggestedSearchesVisibility(
const isApprover = policy.approver === currentUserEmail;
const isApprovalEnabled = policy.approvalMode ? policy.approvalMode !== CONST.POLICY.APPROVAL_MODE.OPTIONAL : false;
const isPaymentEnabled = arePaymentsEnabled(policy);
const hasVBBA = !!policy.achAccount?.bankAccountID && policy.achAccount.state === CONST.BANK_ACCOUNT.STATE.OPEN;
const hasReimburser = !!policy.achAccount?.reimburser;
const isECardEnabled = !!policy.areExpensifyCardsEnabled;
const isSubmittedTo = Object.values(policy.employeeList ?? {}).some((employee) => {
return employee.submitsTo === currentUserEmail || employee.forwardsTo === currentUserEmail;
});
Expand All @@ -485,7 +508,7 @@ function getSuggestedSearchesVisibility(
const isEligibleForStatementsSuggestion = isPaidPolicy && !!policy.areCompanyCardsEnabled && cardFeedsByPolicy[policy.id]?.length > 0;
const isEligibleForUnapprovedCashSuggestion = isPaidPolicy && isAdmin && isApprovalEnabled && isPaymentEnabled;
const isEligibleForUnapprovedCardSuggestion = isPaidPolicy && isAdmin && isApprovalEnabled && cardFeedsByPolicy[policy.id]?.length > 0;
const isEligibleForReconciliationSuggestion = isPaidPolicy && false; // s77rt TODO
const isEligibleForReconciliationSuggestion = isPaidPolicy && isAdmin && ((isPaymentEnabled && hasVBBA && hasReimburser) || isECardEnabled);
Comment thread
luacmartins marked this conversation as resolved.

shouldShowSubmitSuggestion ||= isEligibleForSubmitSuggestion;
shouldShowPaySuggestion ||= isEligibleForPaySuggestion;
Expand Down Expand Up @@ -520,6 +543,7 @@ function getSuggestedSearchesVisibility(
[CONST.SEARCH.SEARCH_KEYS.STATEMENTS]: shouldShowStatementsSuggestion,
[CONST.SEARCH.SEARCH_KEYS.UNAPPROVED_CASH]: shouldShowUnapprovedCashSuggestion,
[CONST.SEARCH.SEARCH_KEYS.UNAPPROVED_CARD]: shouldShowUnapprovedCardSuggestion,
[CONST.SEARCH.SEARCH_KEYS.RECONCILIATION]: shouldShowReconciliationSuggestion,
};
}

Expand Down Expand Up @@ -1895,6 +1919,16 @@ function createTypeMenuSections(
},
});
}
if (suggestedSearchesVisibility[CONST.SEARCH.SEARCH_KEYS.RECONCILIATION]) {
accountingSection.menuItems.push({
...suggestedSearches[CONST.SEARCH.SEARCH_KEYS.RECONCILIATION],
emptyState: {
headerMedia: DotLottieAnimations.Fireworks,
title: 'search.searchResults.emptyStatementsResults.title',
subtitle: 'search.searchResults.emptyStatementsResults.subtitle',
},
});
}

if (accountingSection.menuItems.length > 0) {
typeMenuSections.push(accountingSection);
Expand Down
8 changes: 4 additions & 4 deletions src/pages/Search/EmptySearchView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import type {Policy} from '@src/types/onyx';
import type {SearchDataTypes} from '@src/types/onyx/SearchResults';

type EmptySearchViewProps = {
hash: number;
similarSearchHash: number;
groupBy?: ValueOf<typeof CONST.SEARCH.GROUP_BY>;
type: SearchDataTypes;
hasResults: boolean;
Expand Down Expand Up @@ -74,7 +74,7 @@ const tripsFeatures: FeatureListItem[] = [
},
];

function EmptySearchView({hash, type, groupBy, hasResults}: EmptySearchViewProps) {
function EmptySearchView({similarSearchHash, type, groupBy, hasResults}: EmptySearchViewProps) {
const theme = useTheme();
const StyleUtils = useStyleUtils();
const {translate} = useLocalize();
Expand Down Expand Up @@ -183,7 +183,7 @@ function EmptySearchView({hash, type, groupBy, hasResults}: EmptySearchViewProps
// Begin by going through all of our To-do searches, and returning their empty state
// if it exists
for (const menuItem of typeMenuItems) {
if (menuItem.hash === hash && menuItem.emptyState) {
if (menuItem.similarSearchHash === similarSearchHash && menuItem.emptyState) {
return {
headerMedia: menuItem.emptyState.headerMedia,
title: translate(menuItem.emptyState.title),
Expand Down Expand Up @@ -353,7 +353,7 @@ function EmptySearchView({hash, type, groupBy, hasResults}: EmptySearchViewProps
groupBy,
type,
typeMenuItems,
hash,
similarSearchHash,
translate,
StyleUtils,
theme.todoBG,
Expand Down
12 changes: 6 additions & 6 deletions tests/ui/components/EmptySearchViewTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('EmptySearchView', () => {
render(
<Wrapper>
<EmptySearchView
hash={1}
similarSearchHash={1}
type={dataType}
hasResults={false}
/>
Expand All @@ -76,7 +76,7 @@ describe('EmptySearchView', () => {
render(
<Wrapper>
<EmptySearchView
hash={1}
similarSearchHash={1}
type={dataType}
hasResults={false}
/>
Expand Down Expand Up @@ -118,7 +118,7 @@ describe('EmptySearchView', () => {
render(
<Wrapper>
<EmptySearchView
hash={queryJSON?.hash ?? 1}
similarSearchHash={queryJSON?.similarSearchHash ?? 1}
type={dataType}
hasResults={false}
groupBy={CONST.SEARCH.GROUP_BY.REPORTS}
Expand Down Expand Up @@ -152,7 +152,7 @@ describe('EmptySearchView', () => {
render(
<Wrapper>
<EmptySearchView
hash={queryJSON?.hash ?? 1}
similarSearchHash={queryJSON?.similarSearchHash ?? 1}
type={dataType}
hasResults={false}
groupBy={CONST.SEARCH.GROUP_BY.REPORTS}
Expand Down Expand Up @@ -181,7 +181,7 @@ describe('EmptySearchView', () => {
render(
<Wrapper>
<EmptySearchView
hash={1}
similarSearchHash={1}
type={dataType}
hasResults={false}
/>
Expand All @@ -204,7 +204,7 @@ describe('EmptySearchView', () => {
render(
<Wrapper>
<EmptySearchView
hash={1}
similarSearchHash={1}
type={dataType}
hasResults={false}
/>
Expand Down
7 changes: 7 additions & 0 deletions tests/unit/Search/SearchQueryUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,13 @@ describe('SearchQueryUtils', () => {
expect(queryJSONa?.similarSearchHash).toEqual(queryJSONb?.similarSearchHash);
});

it('should return same similarSearchHash for queries with a date range', () => {
const queryJSONa = buildSearchQueryJSON('type:expense withdrawal-type:reimbursement withdrawn:last-month');
const queryJSONb = buildSearchQueryJSON('type:expense withdrawal-type:reimbursement withdrawn>2025-01-01 withdrawn<2025-01-03');

expect(queryJSONa?.similarSearchHash).toEqual(queryJSONb?.similarSearchHash);
});

it('should return different similarSearchHash for two queries that have different types', () => {
const queryJSONa = buildSearchQueryJSON('sortBy:date sortOrder:desc type:expense feed:"oauth.americanexpressfdx.com 1001"');
const queryJSONb = buildSearchQueryJSON('sortBy:date sortOrder:desc type:trip feed:"oauth.americanexpressfdx.com 1001"');
Expand Down
Loading
Loading