Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -9378,6 +9378,7 @@ const CONST = {
INBOX_TAB_STALE_UNREAD_MONTHS: 3,
CHANGE_POLICY_TRAINING_MODAL: 'changePolicyModal',
AGENTS_RULES_BANNER: 'agentsRulesBanner',
WISE_GLOBAL_REIMBURSEMENTS_WALLET_BANNER: 'wiseGlobalReimbursementsWalletBanner',
SMART_BANNER_HEIGHT: 152,

NAVIGATION_TESTS: {
Expand Down Expand Up @@ -9620,6 +9621,10 @@ const CONST = {
CTA: 'AgentsRulesBanner-CTA',
DISMISS: 'AgentsRulesBanner-Dismiss',
},
WISE_GLOBAL_REIMBURSEMENTS_WALLET_BANNER: {
CTA: 'WiseGlobalReimbursementsWalletBanner-CTA',
DISMISS: 'WiseGlobalReimbursementsWalletBanner-Dismiss',
},
PRODUCT_MARKETING_WINDOW: {
CTA: 'ProductMarketingWindow-CTA',
DISMISS: 'ProductMarketingWindow-Dismiss',
Expand Down
8 changes: 7 additions & 1 deletion src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2834,7 +2834,13 @@ const translations: TranslationDeepObject<typeof en> = {
accountRequiresAttention: 'Dieses Konto erfordert Ihre Aufmerksamkeit',
unlock: 'Entsperren',
},
wiseGlobalReimbursementsIntro: {title: 'Globale Rückerstattungen einrichten?', subtitle: 'Zahlen Sie Mitglieder weltweit in ihrer lokalen Währung.', getStarted: "Los geht's"},
wiseGlobalReimbursementsIntro: {
title: 'Globale Rückerstattungen einrichten?',
subtitle: 'Zahlen Sie Mitglieder weltweit in ihrer lokalen Währung.',
getStarted: "Los geht's",
headerTitle: 'Globale Erstattungen',
},
wiseGlobalReimbursementsBanner: {title: 'Weltweit bezahlen', subtitle: 'Aktivieren Sie globale Erstattungen auf Ihrem USD-Konto.'},
},
addCardToDigitalWallet: {
title: ({walletName}: {walletName: string}) => `Karte zu ${walletName} hinzufügen`,
Expand Down
8 changes: 7 additions & 1 deletion src/languages/el.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2890,7 +2890,13 @@ const translations: TranslationDeepObject<typeof en> = {
accountRequiresAttention: 'Αυτός ο λογαριασμός χρειάζεται προσοχή',
unlock: 'Ξεκλείδωμα',
},
wiseGlobalReimbursementsIntro: {title: 'Να ρυθμιστούν οι παγκόσμιες αποζημιώσεις;', subtitle: 'Πληρώστε μέλη παγκοσμίως στο τοπικό τους νόμισμα.', getStarted: 'Ξεκινήστε'},
wiseGlobalReimbursementsIntro: {
title: 'Να ρυθμιστούν οι παγκόσμιες αποζημιώσεις;',
subtitle: 'Πληρώστε μέλη παγκοσμίως στο τοπικό τους νόμισμα.',
getStarted: 'Ξεκινήστε',
headerTitle: 'Παγκόσμιες αποζημιώσεις',
},
wiseGlobalReimbursementsBanner: {title: 'Πληρώστε ανθρώπους παγκοσμίως', subtitle: 'Ενεργοποιήστε τις παγκόσμιες αποζημιώσεις στον λογαριασμό USD σας.'},
},
addCardToDigitalWallet: {
title: ({walletName}: {walletName: string}) => `Προσθήκη κάρτας στο ${walletName}`,
Expand Down
5 changes: 5 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2903,10 +2903,15 @@ const translations = {
nickname: 'Nickname',
editNicknameInstruction: 'Give the bank account a nickname that sets it apart from others.',
wiseGlobalReimbursementsIntro: {
headerTitle: 'Global reimbursements',
title: 'Set up global reimbursements?',
subtitle: 'Pay members worldwide in their local currency.',
getStarted: 'Get started',
},
wiseGlobalReimbursementsBanner: {
title: 'Pay people worldwide',
subtitle: 'Enable global reimbursements on your USD account.',
},
deleteCard: 'Delete card',
deleteCardConfirmation:
'All unsubmitted card transactions, including those on open reports, will be removed. Are you sure you want to delete this card? You cannot undo this action.',
Expand Down
8 changes: 7 additions & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2744,7 +2744,13 @@ const translations: TranslationDeepObject<typeof en> = {
accountRequiresAttention: 'Esta cuenta requiere atención',
unlock: 'Desbloquear',
},
wiseGlobalReimbursementsIntro: {title: '¿Configurar reembolsos globales?', subtitle: 'Paga a miembros de todo el mundo en su moneda local.', getStarted: 'Empieza'},
wiseGlobalReimbursementsIntro: {
title: '¿Configurar reembolsos globales?',
subtitle: 'Paga a miembros de todo el mundo en su moneda local.',
getStarted: 'Empieza',
headerTitle: 'Reembolsos globales',
},
wiseGlobalReimbursementsBanner: {title: 'Paga a personas de todo el mundo', subtitle: 'Habilita los reembolsos globales en tu cuenta en USD.'},
},
addCardToDigitalWallet: {
title: ({walletName}: {walletName: string}) => `Añadir tarjeta a ${walletName}`,
Expand Down
2 changes: 2 additions & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2843,7 +2843,9 @@ const translations: TranslationDeepObject<typeof en> = {
title: 'Configurer les remboursements globaux ?',
subtitle: 'Payez des membres partout dans le monde dans leur devise locale.',
getStarted: 'Commencer',
headerTitle: 'Remboursements globaux',
},
wiseGlobalReimbursementsBanner: {title: 'Payer des personnes dans le monde entier', subtitle: 'Activez les remboursements globaux sur votre compte en USD.'},
},
addCardToDigitalWallet: {
title: ({walletName}: {walletName: string}) => `Ajouter la carte à ${walletName}`,
Expand Down
8 changes: 7 additions & 1 deletion src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2823,7 +2823,13 @@ const translations: TranslationDeepObject<typeof en> = {
accountRequiresAttention: 'Questo conto richiede attenzione',
unlock: 'Sblocca',
},
wiseGlobalReimbursementsIntro: {title: 'Configurare i rimborsi globali?', subtitle: 'Paga i membri in tutto il mondo nella loro valuta locale.', getStarted: 'Inizia'},
wiseGlobalReimbursementsIntro: {
title: 'Configurare i rimborsi globali?',
subtitle: 'Paga i membri in tutto il mondo nella loro valuta locale.',
getStarted: 'Inizia',
headerTitle: 'Rimborsi globali',
},
wiseGlobalReimbursementsBanner: {title: 'Paga persone in tutto il mondo', subtitle: 'Abilita i rimborsi globali sul tuo conto in USD.'},
},
addCardToDigitalWallet: {
title: ({walletName}: {walletName: string}) => `Aggiungi carta a ${walletName}`,
Expand Down
8 changes: 7 additions & 1 deletion src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2802,7 +2802,13 @@ const translations: TranslationDeepObject<typeof en> = {
accountRequiresAttention: 'このアカウントには対応が必要です',
unlock: 'ロック解除',
},
wiseGlobalReimbursementsIntro: {title: 'グローバル払い戻しを設定しますか?', subtitle: '世界中のメンバーに現地通貨で支払えます。', getStarted: '使い始める'},
wiseGlobalReimbursementsIntro: {
title: 'グローバル払い戻しを設定しますか?',
subtitle: '世界中のメンバーに現地通貨で支払えます。',
getStarted: '使い始める',
headerTitle: 'グローバル払い戻し',
},
wiseGlobalReimbursementsBanner: {title: '世界中の人に支払いましょう', subtitle: 'USD アカウントでグローバル払い戻しを有効にします。'},
},
addCardToDigitalWallet: {
title: ({walletName}: {walletName: string}) => `${walletName} にカードを追加`,
Expand Down
8 changes: 7 additions & 1 deletion src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2827,7 +2827,13 @@ const translations: TranslationDeepObject<typeof en> = {
accountRequiresAttention: 'Deze account vereist aandacht',
unlock: 'Ontgrendelen',
},
wiseGlobalReimbursementsIntro: {title: 'Wereldwijde terugbetalingen instellen?', subtitle: 'Betaal leden wereldwijd in hun eigen lokale valuta.', getStarted: 'Aan de slag'},
wiseGlobalReimbursementsIntro: {
title: 'Wereldwijde terugbetalingen instellen?',
subtitle: 'Betaal leden wereldwijd in hun eigen lokale valuta.',
getStarted: 'Aan de slag',
headerTitle: 'Wereldwijde terugbetalingen',
},
wiseGlobalReimbursementsBanner: {title: 'Betaal mensen wereldwijd', subtitle: 'Schakel wereldwijde terugbetalingen in op je USD-rekening.'},
},
addCardToDigitalWallet: {
title: ({walletName}: {walletName: string}) => `Kaart toevoegen aan ${walletName}`,
Expand Down
8 changes: 7 additions & 1 deletion src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2861,7 +2861,13 @@ const translations: TranslationDeepObject<typeof en> = {
accountRequiresAttention: 'To konto wymaga uwagi',
unlock: 'Odblokuj',
},
wiseGlobalReimbursementsIntro: {title: 'Skonfigurować globalne zwroty kosztów?', subtitle: 'Płać członkom na całym świecie w ich lokalnej walucie.', getStarted: 'Zacznij'},
wiseGlobalReimbursementsIntro: {
title: 'Skonfigurować globalne zwroty kosztów?',
subtitle: 'Płać członkom na całym świecie w ich lokalnej walucie.',
getStarted: 'Zacznij',
headerTitle: 'Globalne zwroty kosztów',
},
wiseGlobalReimbursementsBanner: {title: 'Płać ludziom na całym świecie', subtitle: 'Włącz globalne zwroty wydatków na swoim koncie w USD.'},
},
addCardToDigitalWallet: {
title: ({walletName}: {walletName: string}) => `Dodaj kartę do ${walletName}`,
Expand Down
8 changes: 7 additions & 1 deletion src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2818,7 +2818,13 @@ const translations: TranslationDeepObject<typeof en> = {
accountRequiresAttention: 'Esta conta precisa de atenção',
unlock: 'Desbloquear',
},
wiseGlobalReimbursementsIntro: {title: 'Configurar reembolsos globais?', subtitle: 'Pague membros no mundo todo na moeda local deles.', getStarted: 'Começar'},
wiseGlobalReimbursementsIntro: {
title: 'Configurar reembolsos globais?',
subtitle: 'Pague membros no mundo todo na moeda local deles.',
getStarted: 'Começar',
headerTitle: 'Reembolsos globais',
},
wiseGlobalReimbursementsBanner: {title: 'Pague pessoas no mundo todo', subtitle: 'Ative reembolsos globais na sua conta em USD.'},
},
addCardToDigitalWallet: {
title: ({walletName}: {walletName: string}) => `Adicionar cartão à ${walletName}`,
Expand Down
3 changes: 2 additions & 1 deletion src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2728,7 +2728,8 @@ const translations: TranslationDeepObject<typeof en> = {
accountRequiresAttention: '此账户需要处理',
unlock: '解锁',
},
wiseGlobalReimbursementsIntro: {title: '设置全球报销?', subtitle: '按当地货币向全球成员付款。', getStarted: '开始使用'},
wiseGlobalReimbursementsIntro: {title: '设置全球报销?', subtitle: '按当地货币向全球成员付款。', getStarted: '开始使用', headerTitle: '全球报销'},
wiseGlobalReimbursementsBanner: {title: '向全球付款', subtitle: '在您的美元账户中启用全球报销。'},
},
addCardToDigitalWallet: {
title: ({walletName}: {walletName: string}) => `将卡添加到 ${walletName}`,
Expand Down
36 changes: 34 additions & 2 deletions src/libs/BankAccountUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ function isFilterableBankAccount(bankAccount: OnyxEntry<OnyxTypes.BankAccount>):
/**
* A verified (OPEN) USD business bank account that is not already set up for global reimbursements can start the Wise flow.
*/
function isEligibleForWiseGlobalReimbursements(accountData: AccountData | undefined): boolean {
function isBankAccountEligibleForGlobalReimbursements(accountData: AccountData | undefined): boolean {
return (
accountData?.additionalData?.currency === CONST.CURRENCY.USD &&
accountData.type === CONST.BANK_ACCOUNT.TYPE.BUSINESS &&
Expand All @@ -148,6 +148,37 @@ function isEligibleForWiseGlobalReimbursements(accountData: AccountData | undefi
);
}

/**
* Picks the bank account the Wallet global reimbursements banner points to: the most recently added eligible account.
*/
function getWiseGlobalReimbursementsBannerBankAccountID(bankAccountList: OnyxEntry<OnyxTypes.BankAccountList>): number | undefined {
const newest = Object.values(bankAccountList ?? {}).reduce<AccountData | undefined>((latest, bankAccount) => {
// Accounts deleted offline stay in the list until the request syncs, so they should not be offered
if (bankAccount.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) {
return latest;
}

const accountData = bankAccount.accountData;
if (!accountData?.bankAccountID || !isBankAccountEligibleForGlobalReimbursements(accountData)) {
return latest;
}

if (!latest) {
return accountData;
}

// `created` is a DB timestamp, so plain string comparison orders it chronologically.
// Accounts created in the same second fall back to the ID, which grows with each new account
const created = accountData.created ?? '';
const latestCreated = latest.created ?? '';
const isNewer = created > latestCreated || (created === latestCreated && accountData.bankAccountID > (latest.bankAccountID ?? CONST.DEFAULT_NUMBER_ID));

return isNewer ? accountData : latest;
}, undefined);

return newest?.bankAccountID;
}

function doesPolicyHavePartiallySetupBankAccount(bankAccountList: OnyxEntry<OnyxTypes.BankAccountList>, policyID: string) {
if (!bankAccountList) {
return false;
Expand Down Expand Up @@ -312,7 +343,8 @@ function showUnlockAlreadyRequestedModal(showConfirmModal: ReturnType<typeof use
export {
getBankAccountSearchLabel,
isFilterableBankAccount,
isEligibleForWiseGlobalReimbursements,
isBankAccountEligibleForGlobalReimbursements,
getWiseGlobalReimbursementsBannerBankAccountID,
getDefaultCompanyWebsite,
getBankAccountState,
hasBankAccountAllowDebit,
Expand Down
18 changes: 12 additions & 6 deletions src/pages/settings/Wallet/WalletPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import usePaymentMethodState from '@hooks/usePaymentMethodState';
import type {FormattedSelectedPaymentMethod} from '@hooks/usePaymentMethodState/types';
import usePermissions from '@hooks/usePermissions';
import {usePersonalDetailsByIDs} from '@hooks/usePersonalDetails';
import useRefreshPendingDigitalWalletApproval from '@hooks/useRefreshPendingDigitalWalletApproval';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';

import {isPersonalBankAccountMissingInfo, showUnlockAlreadyRequestedModal} from '@libs/BankAccountUtils';
import {isBankAccountEligibleForGlobalReimbursements, isPersonalBankAccountMissingInfo, showUnlockAlreadyRequestedModal} from '@libs/BankAccountUtils';
import {hasDisplayableAssignedCards, isDirectFeed, maskCardNumber} from '@libs/CardUtils';
import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute';
import Navigation from '@libs/Navigation/Navigation';
Expand All @@ -48,6 +49,7 @@ import {buildCannedSearchQuery} from '@libs/SearchQueryUtils';

import PaymentMethodList from '@pages/settings/Wallet/PaymentMethodList';
import {getFirstPageName} from '@pages/settings/Wallet/UpdatePersonalBankAccountPage';
import WiseGlobalReimbursementsBanner from '@pages/settings/Wallet/WiseGlobalReimbursementsBanner';

import {deletePaymentBankAccount, openPersonalBankAccountSetupView, pressLockedBankAccount, resetPersonalBankAccountForUpdate} from '@userActions/BankAccounts';
import {deletePersonalCard} from '@userActions/Card';
Expand Down Expand Up @@ -112,6 +114,8 @@ function WalletPage() {
const delegateAccountID = useDelegateAccountID();
const isUserValidated = userAccount?.validated ?? false;
const {isAccountLocked} = useLockedAccountState();
const {isBetaEnabled} = usePermissions();
const isWiseGlobalReimbursementsEnabled = isBetaEnabled(CONST.BETAS.WISE_REIMBURSEMENTS);
const {showLockedAccountModal} = useLockedAccountActions();
const {login: currentUserLogin, email, accountID: currentUserAccountID} = useCurrentUserPersonalDetails();
const {translate, localeCompare} = useLocalize();
Expand Down Expand Up @@ -469,11 +473,7 @@ function WalletPage() {

const shouldShowEditNicknameButton = paymentMethod.selectedPaymentMethod?.state === CONST.BANK_ACCOUNT.STATE.OPEN;

const shouldShowEnableGlobalReimbursementsButton =
paymentMethod.selectedPaymentMethod?.additionalData?.currency === CONST.CURRENCY.USD &&
paymentMethod.selectedPaymentMethod.type === CONST.BANK_ACCOUNT.TYPE.BUSINESS &&
!paymentMethod.selectedPaymentMethod?.additionalData?.verifications?.corpay &&
paymentMethod.selectedPaymentMethod?.state === CONST.BANK_ACCOUNT.STATE.OPEN;
const shouldShowEnableGlobalReimbursementsButton = isBankAccountEligibleForGlobalReimbursements(paymentMethod.selectedPaymentMethod);

// Determines whether or not the modal popup is mounted from the bottom of the screen instead of the side mount on Web screen
const alertTextStyle = [styles.inlineSystemMessage, styles.flexShrink1];
Expand Down Expand Up @@ -606,6 +606,10 @@ function WalletPage() {
closeModal(() => showLockedAccountModal());
return;
}
if (isWiseGlobalReimbursementsEnabled) {
closeModal(() => Navigation.navigate(ROUTES.SETTINGS_WALLET_WISE_GLOBAL_REIMBURSEMENTS_INTRO.getRoute(paymentMethod.selectedPaymentMethod.bankAccountID)));
return;
}
closeModal(() =>
Navigation.navigate(
ROUTES.SETTINGS_WALLET_ENABLE_GLOBAL_REIMBURSEMENTS_BUSINESS.getRoute(
Expand Down Expand Up @@ -635,6 +639,7 @@ function WalletPage() {
hasEligibleShareRecipient,
shouldShowUnshareButton,
shouldShowEnableGlobalReimbursementsButton,
isWiseGlobalReimbursementsEnabled,
isAccountLocked,
makeDefaultPaymentMethod,
showLockedAccountModal,
Expand Down Expand Up @@ -765,6 +770,7 @@ function WalletPage() {
errors={userWallet?.errors}
errorRowStyles={styles.ph6}
>
<WiseGlobalReimbursementsBanner />
<Section
subtitle={translate('walletPage.addBankAccountToSendAndReceive')}
title={translate('common.bankAccounts')}
Expand Down
Loading
Loading