From db956bf91810e99dc591074deb6ea60d2ce85616 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Wed, 14 Jan 2026 19:11:40 +0200 Subject: [PATCH 01/44] Change payer for share --- src/languages/en.ts | 10 + .../workflows/WorkspaceWorkflowsPayerPage.tsx | 255 ++++++++++++++---- 2 files changed, 206 insertions(+), 59 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 946ab9b84316..7f5399d934f4 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2288,6 +2288,16 @@ const translations = { admins: 'Admins', payer: 'Payer', paymentAccount: 'Payment account', + shareBankAccount: { + shareTitle: 'Share bank account access?', + shareDescription: ({admin}: {admin: string}) => `You'll need to share bank account access with ${admin} to make them the payer.`, + validationTitle: 'Bank account awaiting validation', + validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => + `You need to validate this bank account. Once that’s done, you can share bank account access with ${admin} to make them the payer.`, + errorTitle: 'Can’t change payer', + errorDescription: ({admin, chatLink}: {admin: string; chatLink: string}) => + `${admin} doesn’t have access to this bank account, so you can’t make them the payer. Chat with ${admin} if the bank account should be shared.`, + }, }, reportFraudPage: { title: 'Report virtual card fraud', diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index ee8f1d57ec0e..03a70cbff59c 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -1,29 +1,38 @@ -import React, {useCallback, useMemo, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import type {SectionListData} from 'react-native'; +import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import Badge from '@components/Badge'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import ConfirmationPage from '@components/ConfirmationPage'; +import ConfirmModal from '@components/ConfirmModal'; +import ErrorMessageRow from '@components/ErrorMessageRow'; +import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import RenderHTML from '@components/RenderHTML'; import ScreenWrapper from '@components/ScreenWrapper'; +import ScrollView from '@components/ScrollView'; // eslint-disable-next-line no-restricted-imports import SelectionList from '@components/SelectionListWithSections'; import type {ListItem, Section} from '@components/SelectionListWithSections/types'; import UserListItem from '@components/SelectionListWithSections/UserListItem'; -import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; +import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; +import useThemeStyles from '@hooks/useThemeStyles'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getSearchValueForPhoneOrEmail} from '@libs/OptionsListUtils'; -import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; +import {getDisplayNameOrDefault, getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {getMemberAccountIDsForWorkspace, goBackFromInvalidPolicy, isExpensifyTeam, isPendingDeletePolicy} from '@libs/PolicyUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; +import {clearShareBankAccount, clearShareBankAccountErrors, shareBankAccount} from '@userActions/BankAccounts'; import {setWorkspacePayer} from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -32,7 +41,7 @@ import type {PersonalDetailsList, PolicyEmployee} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type WorkspaceWorkflowsPayerPageOnyxProps = { - /** All of the personal details for everyone */ + /** All the personal details for everyone */ personalDetails: OnyxEntry; }; @@ -45,22 +54,44 @@ type MembersSection = SectionListData>; function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingReportData = true}: WorkspaceWorkflowsPayerPageProps) { const {translate, formatPhoneNumber} = useLocalize(); const policyName = policy?.name ?? ''; + const bankAccountID = policy?.achAccount?.bankAccountID; const {isOffline} = useNetwork(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); const icons = useMemoizedLazyExpensifyIcons(['FallbackAvatar'] as const); const [searchTerm, setSearchTerm] = useState(''); + const [sharedBankAccountData] = useOnyx(ONYXKEYS.SHARE_BANK_ACCOUNT, {canBeMissing: true}); + const [selectedPayer, setSelectedPayer] = useState(undefined); + const shouldShowSuccess = sharedBankAccountData?.shouldShowSuccess ?? false; + const styles = useThemeStyles(); + const illustrations = useMemoizedLazyIllustrations(['ShareBank', 'Telescope'] as const); + const isLoading = sharedBankAccountData?.isLoading ?? false; + const [isAlertVisible, setIsAlertVisible] = useState(false); + const [showShareModal, setShowShareModal] = useState(false); + const [showValidationModal, setShowValidationModal] = useState(false); + const [showErrorModal, setShowErrorModal] = useState(false); + const policyMemberEmailsToAccountIDs = getMemberAccountIDsForWorkspace(policy?.employeeList); + const selectedPayerDetails = selectedPayer ? getPersonalDetailByEmail(selectedPayer) : undefined; - const isDeletedPolicyEmployee = useCallback( - (policyEmployee: PolicyEmployee) => !isOffline && policyEmployee.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isEmptyObject(policyEmployee.errors), - [isOffline], - ); + useEffect(() => { + setSelectedPayer(policy?.achAccount?.reimburser); + }, [policy?.achAccount?.reimburser]); + + useEffect(() => { + return () => { + if (!shouldShowSuccess) { + return; + } + clearShareBankAccount(); + }; + }, [shouldShowSuccess]); + + const isDeletedPolicyEmployee = (policyEmployee: PolicyEmployee) => + !isOffline && policyEmployee.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isEmptyObject(policyEmployee.errors); - const [formattedPolicyAdmins, formattedAuthorizedPayer] = useMemo(() => { + const getPayersAndAdmins = () => { const policyAdminDetails: MemberOption[] = []; const authorizedPayerDetails: MemberOption[] = []; - const policyMemberEmailsToAccountIDs = getMemberAccountIDsForWorkspace(policy?.employeeList); - for (const [email, policyEmployee] of Object.entries(policy?.employeeList ?? {})) { const accountID = policyMemberEmailsToAccountIDs?.[email] ?? ''; const details = personalDetails?.[accountID]; @@ -79,7 +110,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const roleBadge = ; - const isAuthorizedPayer = policy?.achAccount?.reimburser === details?.login; + const isAuthorizedPayer = selectedPayer === details?.login; const formattedMember = { keyForList: String(accountID), @@ -108,19 +139,11 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR } } return [policyAdminDetails, authorizedPayerDetails]; - }, [ - policy?.employeeList, - policy?.owner, - policy?.achAccount?.reimburser, - policy?.pendingFields?.reimburser, - personalDetails, - isDeletedPolicyEmployee, - translate, - formatPhoneNumber, - icons.FallbackAvatar, - ]); - - const sections: MembersSection[] = useMemo(() => { + }; + + const [formattedPolicyAdmins, formattedAuthorizedPayer] = getPayersAndAdmins(); + + const getSections = () => { const sectionsArray: MembersSection[] = []; if (searchTerm !== '') { @@ -147,40 +170,66 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR shouldShow: true, }); return sectionsArray; - }, [searchTerm, formattedAuthorizedPayer, translate, formattedPolicyAdmins, countryCode]); + }; - const headerMessage = useMemo( - () => (searchTerm && !sections.at(0)?.data.length ? translate('common.noResultsFound') : ''), + const sections: MembersSection[] = getSections(); - // eslint-disable-next-line react-hooks/exhaustive-deps - [translate, sections], - ); + const headerMessage = searchTerm && !sections.at(0)?.data.length ? translate('common.noResultsFound') : ''; - const setPolicyAuthorizedPayer = (member: MemberOption) => { - const authorizedPayerEmail = personalDetails?.[member.accountID]?.login ?? ''; + const handleShareBankAccount = () => { + // setShowShareModal(true); + // setShowValidationModal(true); + setShowErrorModal(true); + }; + + const handleConfirm = () => { + setShowShareModal(false); + if (!bankAccountID) { + return; + } + + if (!selectedPayer) { + setIsAlertVisible(true); + return; + } + + const accountID = policyMemberEmailsToAccountIDs?.[selectedPayer] ?? ''; + + const authorizedPayerEmail = personalDetails?.[accountID]?.login ?? ''; if (policy?.achAccount?.reimburser === authorizedPayerEmail || policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES) { Navigation.goBack(); return; } + shareBankAccount(Number(bankAccountID), [authorizedPayerEmail]); + }; + + const setPolicyAuthorizedPayer = (member: MemberOption) => { + const login = personalDetails?.[member.accountID]?.login; + setSelectedPayer(login); + }; + + const onButtonPress = () => { + if (!selectedPayer || !policy) { + Navigation.closeRHPFlow(); + return; + } + const accountID = policyMemberEmailsToAccountIDs?.[selectedPayer] ?? ''; + const authorizedPayerEmail = personalDetails?.[accountID]?.login ?? ''; + setWorkspacePayer(policy?.id, authorizedPayerEmail); - Navigation.goBack(); + Navigation.closeRHPFlow(); }; - // eslint-disable-next-line rulesdir/no-negated-variables - const shouldShowNotFoundPage = useMemo( - () => (isEmptyObject(policy) && !isLoadingReportData) || isPendingDeletePolicy(policy) || policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES, - [policy, isLoadingReportData], - ); + const shouldShowBlockingPage = + (isEmptyObject(policy) && !isLoadingReportData) || isPendingDeletePolicy(policy) || policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES; - const totalNumberOfEmployeesEitherOwnerOrAdmin = useMemo(() => { - return Object.entries(policy?.employeeList ?? {}).filter(([email, policyEmployee]) => { - const isOwner = policy?.owner === email; - const isAdmin = policyEmployee.role === CONST.POLICY.ROLE.ADMIN; - return !isDeletedPolicyEmployee(policyEmployee) && (isOwner || isAdmin); - }); - }, [isDeletedPolicyEmployee, policy?.employeeList, policy?.owner]); + const totalNumberOfEmployeesEitherOwnerOrAdmin = Object.entries(policy?.employeeList ?? {}).filter(([email, policyEmployee]) => { + const isOwner = policy?.owner === email; + const isAdmin = policyEmployee.role === CONST.POLICY.ROLE.ADMIN; + return !isDeletedPolicyEmployee(policyEmployee) && (isOwner || isAdmin); + }); const shouldShowSearchInput = totalNumberOfEmployeesEitherOwnerOrAdmin.length >= CONST.STANDARD_LIST_ITEM_LIMIT; const textInputLabel = shouldShowSearchInput ? translate('selectionList.findMember') : undefined; @@ -191,7 +240,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR policyID={route.params.policyID} > - + {shouldShowSuccess ? ( + + + + ) : ( + + } + containerStyles={[styles.flexReset, styles.flexGrow0, styles.flexShrink0, styles.flexBasisAuto]} + /> + } + /> + )} + setShowShareModal(false)} + success + prompt={ + + + + } + confirmText={translate('common.share')} + /> + setShowValidationModal(false)} + success + prompt={ + + + + } + shouldShowCancelButton={false} + confirmText={translate('common.buttonConfirm')} + /> + setShowErrorModal(false)} + success + prompt={ + + + + } + shouldShowCancelButton={false} + confirmText={translate('common.buttonConfirm')} + /> ); } From f8993490b5d5d1ddded6ebfb3a5e1af654e89f5d Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Fri, 16 Jan 2026 13:26:33 +0200 Subject: [PATCH 02/44] fix unshare error view and add latest updates for change payer --- src/languages/de.ts | 11 +++ src/languages/en.ts | 5 +- src/languages/es.ts | 11 +++ src/languages/fr.ts | 11 +++ src/languages/it.ts | 11 +++ src/languages/ja.ts | 11 +++ src/languages/nl.ts | 11 +++ src/languages/pl.ts | 11 +++ src/languages/pt-BR.ts | 11 +++ src/languages/zh-hans.ts | 11 +++ .../UnshareBankAccount/UnshareBankAccount.tsx | 18 ++++- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 71 +++++++++++++++---- src/types/onyx/Policy.ts | 3 + 13 files changed, 177 insertions(+), 19 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index 27bc9d97b453..c8eb11b65e29 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -2314,6 +2314,17 @@ ${amount} für ${merchant} – ${date}`, admins: 'Admins', payer: 'Zahler', paymentAccount: 'Zahlungskonto', + shareBankAccount: { + shareTitle: 'Bankkontozugriff teilen?', + shareDescription: ({admin}: {admin: string}) => `Sie müssen ${admin} den Bankkontozugriff gewähren, damit dieser als Zahler eingetragen werden kann.`, + validationTitle: 'Bankkonto wartet auf Validierung', + validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => + `Sie müssen dieses Bankkonto validieren. Anschließend können Sie den Zugriff auf das Bankkonto mit ${admin} teilen, um ihn/sie als Zahler festzulegen.`, + errorTitle: 'Zahler kann nicht geändert werden', + errorDescription: ({admin}: {admin: string}) => `${admin} hat keinen Zugriff auf dieses Bankkonto, daher können Sie ihn/sie nicht als Zahler festlegen.`, + errorDescriptionLink: ({admin}: {admin: string}) => `Kontaktieren Sie ${admin}`, + errorDescriptionLastPart: `wenn das Bankkonto geteilt werden soll.`, + }, }, reportFraudPage: { title: 'Virtuelle Kartenbetrugsfälle melden', diff --git a/src/languages/en.ts b/src/languages/en.ts index 7f5399d934f4..f57d123a3366 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2295,8 +2295,9 @@ const translations = { validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => `You need to validate this bank account. Once that’s done, you can share bank account access with ${admin} to make them the payer.`, errorTitle: 'Can’t change payer', - errorDescription: ({admin, chatLink}: {admin: string; chatLink: string}) => - `${admin} doesn’t have access to this bank account, so you can’t make them the payer. Chat with ${admin} if the bank account should be shared.`, + errorDescription: ({admin}: {admin: string}) => `${admin} doesn’t have access to this bank account, so you can’t make them the payer. `, + errorDescriptionLink: ({admin}: {admin: string}) => `Chat with ${admin}`, + errorDescriptionLastPart: ` if the bank account should be shared.`, }, }, reportFraudPage: { diff --git a/src/languages/es.ts b/src/languages/es.ts index 68506d5ced42..06e35aacc2a7 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2003,6 +2003,17 @@ ${amount} para ${merchant} - ${date}`, admins: 'Administradores', payer: 'Pagador', paymentAccount: 'Cuenta de pago', + shareBankAccount: { + shareTitle: '¿Compartir acceso a la cuenta bancaria?', + shareDescription: ({admin}: {admin: string}) => `Necesitarás compartir el acceso a la cuenta bancaria con ${admin} para que sea el pagador.`, + validationTitle: 'Cuenta bancaria en espera de validación', + validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => + `Necesitas validar esta cuenta bancaria. Una vez hecho esto, puedes compartir el acceso a la cuenta bancaria con ${admin} para que sea el pagador.`, + errorTitle: 'No se puede cambiar el pagador', + errorDescription: ({admin}: {admin: string}) => `${admin} no tiene acceso a esta cuenta bancaria, por lo que no puedes convertirlo en el pagador.`, + errorDescriptionLink: ({admin}: {admin: string}) => `Chatear con ${admin}`, + errorDescriptionLastPart: `si se debe compartir la cuenta bancaria.`, + }, }, reportFraudPage: { title: 'Reportar fraude con la tarjeta virtual', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index a331be4738f1..8bbe789c298b 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -2317,6 +2317,17 @@ ${amount} pour ${merchant} - ${date}`, admins: 'Administrateurs', payer: 'Payeur', paymentAccount: 'Compte de paiement', + shareBankAccount: { + shareTitle: "Partager l'accès au compte bancaire?", + shareDescription: ({admin}: {admin: string}) => `Vous devrez partager l'accès au compte bancaire avec ${admin} pour qu'il/elle soit le payeur.`, + validationTitle: 'Compte bancaire en attente de validation', + validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => + `Vous devez valider ce compte bancaire. Une fois cette opération effectuée, vous pourrez partager l'accès au compte bancaire avec ${admin} pour en faire le payeur.`, + errorTitle: 'Impossible de modifier le payeur', + errorDescription: ({admin}: {admin: string}) => `${admin} n'a pas accès à ce compte bancaire, vous ne pouvez donc pas le désigner comme payeur.`, + errorDescriptionLink: ({admin}: {admin: string}) => `Discutez avec ${admin}`, + errorDescriptionLastPart: ` si le compte bancaire doit être partagé.`, + }, }, reportFraudPage: { title: 'Signaler une fraude de carte virtuelle', diff --git a/src/languages/it.ts b/src/languages/it.ts index 579005161cef..1903ad0429ca 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -2308,6 +2308,17 @@ ${amount} per ${merchant} - ${date}`, admins: 'Amministratori', payer: 'Pagatore', paymentAccount: 'Conto di pagamento', + shareBankAccount: { + shareTitle: "Condividere l'accesso al conto bancario?", + shareDescription: ({admin}: {admin: string}) => `Dovrai condividere l'accesso al conto bancario con ${admin} per renderlo il pagatore.`, + validationTitle: 'Conto bancario in attesa di convalida', + validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => + `Devi convalidare questo conto bancario. Una volta fatto, puoi condividere l'accesso al conto bancario con ${admin} per renderlo il pagatore.`, + errorTitle: 'Impossibile cambiare il pagatore', + errorDescription: ({admin}: {admin: string}) => `${admin} non ha accesso a questo conto bancario, quindi non puoi renderlo il pagatore.`, + errorDescriptionLink: ({admin}: {admin: string}) => `Chatta con ${admin}`, + errorDescriptionLastPart: ` se il conto bancario deve essere condiviso.`, + }, }, reportFraudPage: { title: 'Segnala frode con carta virtuale', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index a633689ceb4c..6157e777192d 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -2299,6 +2299,17 @@ ${merchant} への ${amount}(${date})`, admins: '管理者', payer: '支払者', paymentAccount: '支払い口座', + shareBankAccount: { + shareTitle: '銀行口座へのアクセスを共有しますか?', + shareDescription: ({admin}: {admin: string}) => `${admin}を支払人にするには、銀行口座へのアクセスを${admin}と共有する必要があります。`, + validationTitle: '銀行口座の検証待ち', + validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => + `この銀行口座を検証する必要があります。検証が完了すると、銀行口座へのアクセス権を${admin}と共有して、支払人にすることができます。`, + errorTitle: '支払人を変更できません', + errorDescription: ({admin}: {admin: string}) => `${admin}はこの銀行口座へのアクセス権がないため、支払人にすることはできません。`, + errorDescriptionLink: ({admin}: {admin: string}) => `${admin}とチャットする`, + errorDescriptionLastPart: ` 銀行口座を共有する必要がある場合。`, + }, }, reportFraudPage: { title: 'バーチャルカードの不正利用を報告', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 5ef71c2f20b5..5eff1a7c19e7 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -2307,6 +2307,17 @@ ${amount} voor ${merchant} - ${date}`, admins: 'Beheerders', payer: 'Betaler', paymentAccount: 'Betaalrekening', + shareBankAccount: { + shareTitle: 'Bankrekeningtoegang delen?', + shareDescription: ({admin}: {admin: string}) => `U moet bankrekeningtoegang delen met ${admin} om hem/haar de betaler te maken.`, + validationTitle: 'Bankrekening wacht op validatie', + validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => + `U moet deze bankrekening valideren. Zodra dat is gedaan, kunt u de toegang tot de bankrekening delen met ${admin} om hem/haar de betaler te maken.`, + errorTitle: 'Betaler kan niet worden gewijzigd', + errorDescription: ({admin}: {admin: string}) => `${admin} heeft geen toegang tot deze bankrekening, dus u kunt hem/haar niet de betaler maken.`, + errorDescriptionLink: ({admin}: {admin: string}) => `Chat met ${admin}`, + errorDescriptionLastPart: `als de bankrekening moet worden gedeeld.`, + }, }, reportFraudPage: { title: 'Fraude met virtuele kaart melden', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index ab204c691bc9..21e77cb383a2 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -2300,6 +2300,17 @@ ${amount} dla ${merchant} - ${date}`, admins: 'Administratorzy', payer: 'Płatnik', paymentAccount: 'Konto płatnicze', + shareBankAccount: { + shareTitle: 'Udostępnić dostęp do konta bankowego?', + shareDescription: ({admin}: {admin: string}) => `Musisz udostępnić dostęp do konta bankowego użytkownikowi ${admin}, aby uczynić go płatnikiem.`, + validationTitle: 'Konto bankowe oczekuje na walidację', + validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => + `Musisz zweryfikować to konto bankowe. Po wykonaniu tej czynności możesz udostępnić dostęp do konta bankowego użytkownikowi ${admin}, aby uczynić go płatnikiem.`, + errorTitle: 'Nie można zmienić płatnika', + errorDescription: ({admin}: {admin: string}) => `${admin} nie ma dostępu do tego konta bankowego, więc nie możesz uczynić go płatnikiem. `, + errorDescriptionLink: ({admin}: {admin: string}) => `Porozmawiaj z ${admin}`, + errorDescriptionLastPart: `czy konto bankowe powinno być współdzielone.`, + }, }, reportFraudPage: { title: 'Zgłoś oszustwo dotyczące karty wirtualnej', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 7c627b644bca..438e5bb41d13 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -2300,6 +2300,17 @@ ${amount} para ${merchant} - ${date}`, admins: 'Administradores', payer: 'Pagador', paymentAccount: 'Conta de pagamento', + shareBankAccount: { + shareTitle: 'Compartilhar acesso à conta bancária?', + shareDescription: ({admin}: {admin: string}) => `Você precisará compartilhar o acesso à conta bancária com ${admin} para torná-lo o pagador.`, + validationTitle: 'Conta bancária aguardando validação', + validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => + `Você precisa validar esta conta bancária. Depois disso, você poderá compartilhar o acesso à conta bancária com ${admin} para torná-lo o pagador.`, + errorTitle: 'Não é possível alterar o pagador', + errorDescription: ({admin}: {admin: string}) => `${admin} não tem acesso a esta conta bancária, portanto, você não pode torná-lo o pagador.`, + errorDescriptionLink: ({admin}: {admin: string}) => `Converse com ${admin}`, + errorDescriptionLastPart: ` se a conta bancária deve ser compartilhada.`, + }, }, reportFraudPage: { title: 'Denunciar fraude em cartão virtual', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 1b4f353c16b3..c8da4538b944 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -2267,6 +2267,17 @@ ${amount},商户:${merchant} - ${date}`, admins: '管理员', payer: '付款方', paymentAccount: '付款账户', + shareBankAccount: { + shareTitle: '共享银行账户访问权限?', + shareDescription: ({admin}: {admin: string}) => `您需要与 ${admin} 共享银行账户访问权限,才能将其设置为付款人。`, + validationTitle: '银行账户等待验证', + validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => + `您需要验证此银行账户。验证完成后,您可以将银行账户访问权限分享给 ${admin},使其成为付款人。`, + errorTitle: '无法更改付款人', + errorDescription: ({admin}: {admin: string}) => `${admin} 没有此银行账户的访问权限,因此您无法将其设置为付款人。`, + errorDescriptionLink: ({admin}: {admin: string}) => `请联系 ${admin}`, + errorDescriptionLastPart: `如果需要分享银行账户,请联系 ${admin}。`, + }, }, reportFraudPage: { title: '报告虚拟卡欺诈', diff --git a/src/pages/settings/Wallet/UnshareBankAccount/UnshareBankAccount.tsx b/src/pages/settings/Wallet/UnshareBankAccount/UnshareBankAccount.tsx index acbbad521004..8e0224d5a7ca 100644 --- a/src/pages/settings/Wallet/UnshareBankAccount/UnshareBankAccount.tsx +++ b/src/pages/settings/Wallet/UnshareBankAccount/UnshareBankAccount.tsx @@ -15,6 +15,7 @@ import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; +import {getLatestErrorMessage} from '@libs/ErrorUtils'; import {formatMemberForList, getHeaderMessage, getSearchValueForPhoneOrEmail} from '@libs/OptionsListUtils'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; @@ -42,6 +43,8 @@ function UnshareBankAccount({route}: ShareBankAccountProps) { const {translate} = useLocalize(); const admins = bankAccountList?.[bankAccountID]?.accountData?.sharees; const totalAdmins = bankAccountList?.[bankAccountID]?.accountData?.sharees?.length; + const error = getLatestErrorMessage(bankAccountList?.[bankAccountID] ?? {}); + const isExpensifyCardError = error?.includes(CONST.EXPENSIFY_CARD.BANK); const isExpensifyCardSettlementAccount = bankAccountList?.[bankAccountID]?.isExpensifyCardSettlementAccount ?? false; const shouldShowTextInput = Number(totalAdmins) >= CONST.STANDARD_LIST_ITEM_LIMIT; const textInputLabel = shouldShowTextInput ? translate('common.search') : undefined; @@ -57,6 +60,14 @@ function UnshareBankAccount({route}: ShareBankAccountProps) { } }, [totalAdmins, shouldShowSuccess]); + useEffect(() => { + if (!isExpensifyCardError) { + return; + } + setUnshareUser(undefined); + setShowExpensifyCardErrorModal(true); + }, [isExpensifyCardError]); + const handleUnshare = () => { if (!bankAccountID || !unshareUser?.login) { return; @@ -101,7 +112,10 @@ function UnshareBankAccount({route}: ShareBankAccountProps) { return adminsToDisplay; }; - const hideUnshareErrorModal = () => setShowExpensifyCardErrorModal(false); + const hideUnshareErrorModal = () => { + clearUnshareBankAccountErrors(Number(bankAccountID)); + setShowExpensifyCardErrorModal(false); + }; const itemRightSideComponent = (item: ListItem) => { return ( @@ -148,7 +162,7 @@ function UnshareBankAccount({route}: ShareBankAccountProps) { rightHandSideComponent={itemRightSideComponent} footerContent={ clearUnshareBankAccountErrors(Number(bankAccountID))} /> diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 03a70cbff59c..b800d291dc54 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -16,6 +16,9 @@ import ScrollView from '@components/ScrollView'; import SelectionList from '@components/SelectionListWithSections'; import type {ListItem, Section} from '@components/SelectionListWithSections/types'; import UserListItem from '@components/SelectionListWithSections/UserListItem'; +import Text from '@components/Text'; +import TextLink from '@components/TextLink'; +import useEnvironment from '@hooks/useEnvironment'; import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -27,6 +30,7 @@ import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavig import {getSearchValueForPhoneOrEmail} from '@libs/OptionsListUtils'; import {getDisplayNameOrDefault, getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {getMemberAccountIDsForWorkspace, goBackFromInvalidPolicy, isExpensifyTeam, isPendingDeletePolicy} from '@libs/PolicyUtils'; +import {getRouteForCurrentStep as getReimbursementAccountRouteForCurrentStep} from '@libs/ReimbursementAccountUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; @@ -34,8 +38,10 @@ import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullsc import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import {clearShareBankAccount, clearShareBankAccountErrors, shareBankAccount} from '@userActions/BankAccounts'; import {setWorkspacePayer} from '@userActions/Policy/Policy'; +import {navigateToAndOpenReportWithAccountIDs} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetailsList, PolicyEmployee} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -53,6 +59,7 @@ type MembersSection = SectionListData>; function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingReportData = true}: WorkspaceWorkflowsPayerPageProps) { const {translate, formatPhoneNumber} = useLocalize(); + const {environmentURL} = useEnvironment(); const policyName = policy?.name ?? ''; const bankAccountID = policy?.achAccount?.bankAccountID; const {isOffline} = useNetwork(); @@ -176,12 +183,6 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const headerMessage = searchTerm && !sections.at(0)?.data.length ? translate('common.noResultsFound') : ''; - const handleShareBankAccount = () => { - // setShowShareModal(true); - // setShowValidationModal(true); - setShowErrorModal(true); - }; - const handleConfirm = () => { setShowShareModal(false); if (!bankAccountID) { @@ -205,6 +206,29 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR shareBankAccount(Number(bankAccountID), [authorizedPayerEmail]); }; + const handleShareBankAccount = () => { + if (!selectedPayer) { + return; + } + const isAccountAlreadyShared = policy?.achAccount?.sharees ? policy.achAccount.sharees.includes(selectedPayer) : false; + + if (isAccountAlreadyShared) { + handleConfirm(); + return; + } + + if (policy?.achAccount?.state === CONST.BANK_ACCOUNT.STATE.PENDING) { + setShowValidationModal(true); + return; + } + + if (policy?.achAccount?.state !== CONST.BANK_ACCOUNT.STATE.PENDING && policy?.achAccount?.state !== CONST.BANK_ACCOUNT.STATE.OPEN) { + setShowErrorModal(true); + return; + } + setShowShareModal(true); + }; + const setPolicyAuthorizedPayer = (member: MemberOption) => { const login = personalDetails?.[member.accountID]?.login; setSelectedPayer(login); @@ -323,14 +347,16 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR setShowValidationModal(false)} + onConfirm={() => { + setShowValidationModal(false); + }} success prompt={ @@ -341,17 +367,32 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR setShowErrorModal(false)} + onConfirm={() => { + setShowErrorModal(false); + }} success prompt={ - - + + {translate('workflowsPayerPage.shareBankAccount.errorDescription', { admin: selectedPayerDetails?.displayName ?? '', - chatLink: '', })} - /> - + + { + if (!selectedPayerDetails?.accountID || !policy?.ownerAccountID) { + return; + } + setShowErrorModal(false); + navigateToAndOpenReportWithAccountIDs([selectedPayerDetails.accountID], policy.ownerAccountID); + }} + > + {translate('workflowsPayerPage.shareBankAccount.errorDescriptionLink', { + admin: selectedPayerDetails?.displayName ?? '', + })} + + {translate('workflowsPayerPage.shareBankAccount.errorDescriptionLastPart')} + } shouldShowCancelButton={false} confirmText={translate('common.buttonConfirm')} diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index f005dc7d034d..2493f2d5500d 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -1512,6 +1512,9 @@ type ACHAccount = { /** Bank account state */ state?: string; + + /** All user emails that have access to this bank account */ + sharees?: string[]; }; /** Prohibited expense types */ From 46bdb5dc42df6f7b5e38b4a2167fc555d03f97fc Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Fri, 16 Jan 2026 13:52:58 +0200 Subject: [PATCH 03/44] change during testing --- src/languages/en.ts | 2 +- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 29 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index f57d123a3366..b5f655945753 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2296,7 +2296,7 @@ const translations = { `You need to validate this bank account. Once that’s done, you can share bank account access with ${admin} to make them the payer.`, errorTitle: 'Can’t change payer', errorDescription: ({admin}: {admin: string}) => `${admin} doesn’t have access to this bank account, so you can’t make them the payer. `, - errorDescriptionLink: ({admin}: {admin: string}) => `Chat with ${admin}`, + errorDescriptionLink: ({owner}: {owner: string}) => `Chat with ${owner}`, errorDescriptionLastPart: ` if the bank account should be shared.`, }, }, diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index b800d291dc54..77354c766d66 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -78,6 +78,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const [showErrorModal, setShowErrorModal] = useState(false); const policyMemberEmailsToAccountIDs = getMemberAccountIDsForWorkspace(policy?.employeeList); const selectedPayerDetails = selectedPayer ? getPersonalDetailByEmail(selectedPayer) : undefined; + const ownerDetails = policy?.owner ? getPersonalDetailByEmail(policy?.owner) : undefined; useEffect(() => { setSelectedPayer(policy?.achAccount?.reimburser); @@ -206,6 +207,18 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR shareBankAccount(Number(bankAccountID), [authorizedPayerEmail]); }; + const onButtonPress = () => { + if (!selectedPayer || !policy) { + Navigation.closeRHPFlow(); + return; + } + const accountID = policyMemberEmailsToAccountIDs?.[selectedPayer] ?? ''; + const authorizedPayerEmail = personalDetails?.[accountID]?.login ?? ''; + + setWorkspacePayer(policy?.id, authorizedPayerEmail); + Navigation.closeRHPFlow(); + }; + const handleShareBankAccount = () => { if (!selectedPayer) { return; @@ -213,7 +226,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const isAccountAlreadyShared = policy?.achAccount?.sharees ? policy.achAccount.sharees.includes(selectedPayer) : false; if (isAccountAlreadyShared) { - handleConfirm(); + onButtonPress(); return; } @@ -234,18 +247,6 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR setSelectedPayer(login); }; - const onButtonPress = () => { - if (!selectedPayer || !policy) { - Navigation.closeRHPFlow(); - return; - } - const accountID = policyMemberEmailsToAccountIDs?.[selectedPayer] ?? ''; - const authorizedPayerEmail = personalDetails?.[accountID]?.login ?? ''; - - setWorkspacePayer(policy?.id, authorizedPayerEmail); - Navigation.closeRHPFlow(); - }; - const shouldShowBlockingPage = (isEmptyObject(policy) && !isLoadingReportData) || isPendingDeletePolicy(policy) || policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES; @@ -388,7 +389,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR }} > {translate('workflowsPayerPage.shareBankAccount.errorDescriptionLink', { - admin: selectedPayerDetails?.displayName ?? '', + owner: ownerDetails?.displayName ?? '', })} {translate('workflowsPayerPage.shareBankAccount.errorDescriptionLastPart')} From bae22f37234c89b47b5f4aad4cffa998ceed4fb9 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Fri, 16 Jan 2026 19:16:49 +0200 Subject: [PATCH 04/44] latest updates --- src/languages/de.ts | 2 +- src/languages/es.ts | 2 +- src/languages/fr.ts | 2 +- src/languages/it.ts | 2 +- src/languages/ja.ts | 2 +- src/languages/nl.ts | 2 +- src/languages/pl.ts | 2 +- src/languages/pt-BR.ts | 2 +- src/languages/zh-hans.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index c8eb11b65e29..7ff50b59adba 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -2322,7 +2322,7 @@ ${amount} für ${merchant} – ${date}`, `Sie müssen dieses Bankkonto validieren. Anschließend können Sie den Zugriff auf das Bankkonto mit ${admin} teilen, um ihn/sie als Zahler festzulegen.`, errorTitle: 'Zahler kann nicht geändert werden', errorDescription: ({admin}: {admin: string}) => `${admin} hat keinen Zugriff auf dieses Bankkonto, daher können Sie ihn/sie nicht als Zahler festlegen.`, - errorDescriptionLink: ({admin}: {admin: string}) => `Kontaktieren Sie ${admin}`, + errorDescriptionLink: ({owner}: {owner: string}) => `Kontaktieren Sie ${owner}`, errorDescriptionLastPart: `wenn das Bankkonto geteilt werden soll.`, }, }, diff --git a/src/languages/es.ts b/src/languages/es.ts index 06e35aacc2a7..7e57c2cd9780 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2011,7 +2011,7 @@ ${amount} para ${merchant} - ${date}`, `Necesitas validar esta cuenta bancaria. Una vez hecho esto, puedes compartir el acceso a la cuenta bancaria con ${admin} para que sea el pagador.`, errorTitle: 'No se puede cambiar el pagador', errorDescription: ({admin}: {admin: string}) => `${admin} no tiene acceso a esta cuenta bancaria, por lo que no puedes convertirlo en el pagador.`, - errorDescriptionLink: ({admin}: {admin: string}) => `Chatear con ${admin}`, + errorDescriptionLink: ({owner}: {owner: string}) => `Chatear con ${owner}`, errorDescriptionLastPart: `si se debe compartir la cuenta bancaria.`, }, }, diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 8bbe789c298b..dec43bcd33ac 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -2325,7 +2325,7 @@ ${amount} pour ${merchant} - ${date}`, `Vous devez valider ce compte bancaire. Une fois cette opération effectuée, vous pourrez partager l'accès au compte bancaire avec ${admin} pour en faire le payeur.`, errorTitle: 'Impossible de modifier le payeur', errorDescription: ({admin}: {admin: string}) => `${admin} n'a pas accès à ce compte bancaire, vous ne pouvez donc pas le désigner comme payeur.`, - errorDescriptionLink: ({admin}: {admin: string}) => `Discutez avec ${admin}`, + errorDescriptionLink: ({owner}: {owner: string}) => `Discutez avec ${owner}`, errorDescriptionLastPart: ` si le compte bancaire doit être partagé.`, }, }, diff --git a/src/languages/it.ts b/src/languages/it.ts index 1903ad0429ca..af58cfb886a3 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -2316,7 +2316,7 @@ ${amount} per ${merchant} - ${date}`, `Devi convalidare questo conto bancario. Una volta fatto, puoi condividere l'accesso al conto bancario con ${admin} per renderlo il pagatore.`, errorTitle: 'Impossibile cambiare il pagatore', errorDescription: ({admin}: {admin: string}) => `${admin} non ha accesso a questo conto bancario, quindi non puoi renderlo il pagatore.`, - errorDescriptionLink: ({admin}: {admin: string}) => `Chatta con ${admin}`, + errorDescriptionLink: ({owner}: {owner: string}) => `Chatta con ${owner}`, errorDescriptionLastPart: ` se il conto bancario deve essere condiviso.`, }, }, diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 6157e777192d..df5f2b18c71f 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -2307,7 +2307,7 @@ ${merchant} への ${amount}(${date})`, `この銀行口座を検証する必要があります。検証が完了すると、銀行口座へのアクセス権を${admin}と共有して、支払人にすることができます。`, errorTitle: '支払人を変更できません', errorDescription: ({admin}: {admin: string}) => `${admin}はこの銀行口座へのアクセス権がないため、支払人にすることはできません。`, - errorDescriptionLink: ({admin}: {admin: string}) => `${admin}とチャットする`, + errorDescriptionLink: ({owner}: {owner: string}) => `${owner}とチャットする`, errorDescriptionLastPart: ` 銀行口座を共有する必要がある場合。`, }, }, diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 5eff1a7c19e7..f9edad39d89c 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -2315,7 +2315,7 @@ ${amount} voor ${merchant} - ${date}`, `U moet deze bankrekening valideren. Zodra dat is gedaan, kunt u de toegang tot de bankrekening delen met ${admin} om hem/haar de betaler te maken.`, errorTitle: 'Betaler kan niet worden gewijzigd', errorDescription: ({admin}: {admin: string}) => `${admin} heeft geen toegang tot deze bankrekening, dus u kunt hem/haar niet de betaler maken.`, - errorDescriptionLink: ({admin}: {admin: string}) => `Chat met ${admin}`, + errorDescriptionLink: ({owner}: {owner: string}) => `Chat met ${owner}`, errorDescriptionLastPart: `als de bankrekening moet worden gedeeld.`, }, }, diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 21e77cb383a2..85c099acba37 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -2308,7 +2308,7 @@ ${amount} dla ${merchant} - ${date}`, `Musisz zweryfikować to konto bankowe. Po wykonaniu tej czynności możesz udostępnić dostęp do konta bankowego użytkownikowi ${admin}, aby uczynić go płatnikiem.`, errorTitle: 'Nie można zmienić płatnika', errorDescription: ({admin}: {admin: string}) => `${admin} nie ma dostępu do tego konta bankowego, więc nie możesz uczynić go płatnikiem. `, - errorDescriptionLink: ({admin}: {admin: string}) => `Porozmawiaj z ${admin}`, + errorDescriptionLink: ({owner}: {owner: string}) => `Porozmawiaj z ${owner}`, errorDescriptionLastPart: `czy konto bankowe powinno być współdzielone.`, }, }, diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 438e5bb41d13..2e1a2ea7d1af 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -2308,7 +2308,7 @@ ${amount} para ${merchant} - ${date}`, `Você precisa validar esta conta bancária. Depois disso, você poderá compartilhar o acesso à conta bancária com ${admin} para torná-lo o pagador.`, errorTitle: 'Não é possível alterar o pagador', errorDescription: ({admin}: {admin: string}) => `${admin} não tem acesso a esta conta bancária, portanto, você não pode torná-lo o pagador.`, - errorDescriptionLink: ({admin}: {admin: string}) => `Converse com ${admin}`, + errorDescriptionLink: ({owner}: {owner: string}) => `Converse com ${owner}`, errorDescriptionLastPart: ` se a conta bancária deve ser compartilhada.`, }, }, diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index c8da4538b944..a81c28756ea3 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -2275,7 +2275,7 @@ ${amount},商户:${merchant} - ${date}`, `您需要验证此银行账户。验证完成后,您可以将银行账户访问权限分享给 ${admin},使其成为付款人。`, errorTitle: '无法更改付款人', errorDescription: ({admin}: {admin: string}) => `${admin} 没有此银行账户的访问权限,因此您无法将其设置为付款人。`, - errorDescriptionLink: ({admin}: {admin: string}) => `请联系 ${admin}`, + errorDescriptionLink: ({owner}: {owner: string}) => `请联系 ${owner}`, errorDescriptionLastPart: `如果需要分享银行账户,请联系 ${admin}。`, }, }, From b1fa5763386b1b389a1e0cd10d8b9f1ed68d21c2 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Fri, 16 Jan 2026 19:27:11 +0200 Subject: [PATCH 05/44] add missing item --- src/languages/zh-hans.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index a81c28756ea3..4dedd19d9e97 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -2276,7 +2276,7 @@ ${amount},商户:${merchant} - ${date}`, errorTitle: '无法更改付款人', errorDescription: ({admin}: {admin: string}) => `${admin} 没有此银行账户的访问权限,因此您无法将其设置为付款人。`, errorDescriptionLink: ({owner}: {owner: string}) => `请联系 ${owner}`, - errorDescriptionLastPart: `如果需要分享银行账户,请联系 ${admin}。`, + errorDescriptionLastPart: ` 如果银行账户应该共享。`, }, }, reportFraudPage: { From 81d5881a2d5416dcaf7c9bb9e0a73b6c83b65f8f Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Mon, 19 Jan 2026 13:33:21 +0200 Subject: [PATCH 06/44] more updates --- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 8f4dc71c620b..89080b1cbbd2 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -18,6 +18,7 @@ import type {ListItem, Section} from '@components/SelectionListWithSections/type import UserListItem from '@components/SelectionListWithSections/UserListItem'; import Text from '@components/Text'; import TextLink from '@components/TextLink'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useEnvironment from '@hooks/useEnvironment'; import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; @@ -63,11 +64,12 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const policyName = policy?.name ?? ''; const bankAccountID = policy?.achAccount?.bankAccountID; const {isOffline} = useNetwork(); + const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); const icons = useMemoizedLazyExpensifyIcons(['FallbackAvatar'] as const); const [searchTerm, setSearchTerm] = useState(''); const [sharedBankAccountData] = useOnyx(ONYXKEYS.SHARE_BANK_ACCOUNT, {canBeMissing: true}); - const [selectedPayer, setSelectedPayer] = useState(undefined); + const [selectedPayer, setSelectedPayer] = useState(policy?.achAccount?.reimburser); const shouldShowSuccess = sharedBankAccountData?.shouldShowSuccess ?? false; const styles = useThemeStyles(); const illustrations = useMemoizedLazyIllustrations(['ShareBank', 'Telescope'] as const); @@ -80,10 +82,6 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const selectedPayerDetails = selectedPayer ? getPersonalDetailByEmail(selectedPayer) : undefined; const ownerDetails = policy?.owner ? getPersonalDetailByEmail(policy?.owner) : undefined; - useEffect(() => { - setSelectedPayer(policy?.achAccount?.reimburser); - }, [policy?.achAccount?.reimburser]); - useEffect(() => { return () => { if (!shouldShowSuccess) { @@ -383,11 +381,11 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR { - if (!selectedPayerDetails?.accountID || !policy?.ownerAccountID) { + if (!currentUserPersonalDetails?.accountID || !policy?.ownerAccountID) { return; } setShowErrorModal(false); - navigateToAndOpenReportWithAccountIDs([selectedPayerDetails.accountID], policy.ownerAccountID); + navigateToAndOpenReportWithAccountIDs([currentUserPersonalDetails.accountID], policy.ownerAccountID); }} > {translate('workflowsPayerPage.shareBankAccount.errorDescriptionLink', { From 4fe2e16afe29e818b0ae85a77e9c62bce0600ebc Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Tue, 20 Jan 2026 14:50:12 +0200 Subject: [PATCH 07/44] fix bugs --- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 59 ++++++++----------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 89080b1cbbd2..d58a7e3b225d 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -25,13 +25,13 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; +import {isBankAccountPartiallySetup} from '@libs/BankAccountUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getSearchValueForPhoneOrEmail} from '@libs/OptionsListUtils'; import {getDisplayNameOrDefault, getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {getMemberAccountIDsForWorkspace, goBackFromInvalidPolicy, isExpensifyTeam, isPendingDeletePolicy} from '@libs/PolicyUtils'; -import {getRouteForCurrentStep as getReimbursementAccountRouteForCurrentStep} from '@libs/ReimbursementAccountUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; @@ -81,6 +81,15 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const policyMemberEmailsToAccountIDs = getMemberAccountIDsForWorkspace(policy?.employeeList); const selectedPayerDetails = selectedPayer ? getPersonalDetailByEmail(selectedPayer) : undefined; const ownerDetails = policy?.owner ? getPersonalDetailByEmail(policy?.owner) : undefined; + const accountID = selectedPayer ? policyMemberEmailsToAccountIDs?.[selectedPayer] : ''; + const authorizedPayerEmail = personalDetails?.[accountID]?.login ?? ''; + + useEffect(() => { + if (!shouldShowSuccess || !policy?.id || !accountID) { + return; + } + setWorkspacePayer(policy?.id, authorizedPayerEmail); + }, [accountID, authorizedPayerEmail, policy?.id, shouldShowSuccess]); useEffect(() => { return () => { @@ -97,30 +106,24 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const getPayersAndAdmins = () => { const policyAdminDetails: MemberOption[] = []; const authorizedPayerDetails: MemberOption[] = []; - for (const [email, policyEmployee] of Object.entries(policy?.employeeList ?? {})) { - const accountID = policyMemberEmailsToAccountIDs?.[email] ?? ''; - const details = personalDetails?.[accountID]; + const adminAccountID = policyMemberEmailsToAccountIDs?.[email] ?? ''; + const details = personalDetails?.[adminAccountID]; if (!details) { - Log.hmmm(`[WorkspaceMembersPage] no personal details found for policy member with accountID: ${accountID}`); + Log.hmmm(`[WorkspaceMembersPage] no personal details found for policy member with accountID: ${adminAccountID}`); continue; } - const isOwner = policy?.owner === details?.login; const isAdmin = policyEmployee.role === CONST.POLICY.ROLE.ADMIN; const shouldSkipMember = isDeletedPolicyEmployee(policyEmployee) || isExpensifyTeam(details?.login) || (!isOwner && !isAdmin); - if (shouldSkipMember) { continue; } - const roleBadge = ; - const isAuthorizedPayer = selectedPayer === details?.login; - const formattedMember = { - keyForList: String(accountID), - accountID, + keyForList: String(adminAccountID), + accountID: adminAccountID, isSelected: isAuthorizedPayer, isDisabled: policyEmployee.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || !isEmptyObject(policyEmployee.errors), text: formatPhoneNumber(getDisplayNameOrDefault(details)), @@ -131,13 +134,12 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR source: details.avatar ?? icons.FallbackAvatar, name: formatPhoneNumber(details?.login ?? ''), type: CONST.ICON_TYPE_AVATAR, - id: accountID, + id: adminAccountID, }, ], errors: policyEmployee.errors, pendingAction: (policyEmployee.pendingAction ?? isAuthorizedPayer) ? policy?.pendingFields?.reimburser : null, }; - if (isAuthorizedPayer) { authorizedPayerDetails.push(formattedMember); } else { @@ -151,11 +153,9 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const getSections = () => { const sectionsArray: MembersSection[] = []; - if (searchTerm !== '') { const searchValue = getSearchValueForPhoneOrEmail(searchTerm, countryCode); const filteredOptions = tokenizedSearch([...formattedPolicyAdmins, ...formattedAuthorizedPayer], searchValue, (option) => [option.text ?? '', option.login ?? '']); - return [ { title: undefined, @@ -164,12 +164,10 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR }, ]; } - sectionsArray.push({ data: formattedAuthorizedPayer, shouldShow: true, }); - sectionsArray.push({ title: translate('workflowsPayerPage.admins'), data: formattedPolicyAdmins, @@ -179,7 +177,6 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR }; const sections: MembersSection[] = getSections(); - const headerMessage = searchTerm && !sections.at(0)?.data.length ? translate('common.noResultsFound') : ''; const handleConfirm = () => { @@ -187,21 +184,14 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR if (!bankAccountID) { return; } - if (!selectedPayer) { setIsAlertVisible(true); return; } - - const accountID = policyMemberEmailsToAccountIDs?.[selectedPayer] ?? ''; - - const authorizedPayerEmail = personalDetails?.[accountID]?.login ?? ''; - if (policy?.achAccount?.reimburser === authorizedPayerEmail || policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES) { Navigation.goBack(); return; } - shareBankAccount(Number(bankAccountID), [authorizedPayerEmail]); }; @@ -210,9 +200,6 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR Navigation.closeRHPFlow(); return; } - const accountID = policyMemberEmailsToAccountIDs?.[selectedPayer] ?? ''; - const authorizedPayerEmail = personalDetails?.[accountID]?.login ?? ''; - setWorkspacePayer(policy?.id, authorizedPayerEmail); Navigation.closeRHPFlow(); }; @@ -221,19 +208,19 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR if (!selectedPayer) { return; } + const isOwner = policy?.owner === selectedPayer; const isAccountAlreadyShared = policy?.achAccount?.sharees ? policy.achAccount.sharees.includes(selectedPayer) : false; - - if (isAccountAlreadyShared) { + if (isAccountAlreadyShared || isOwner) { onButtonPress(); return; } - - if (policy?.achAccount?.state === CONST.BANK_ACCOUNT.STATE.PENDING) { + if (isBankAccountPartiallySetup(policy?.achAccount?.state)) { setShowValidationModal(true); return; } - - if (policy?.achAccount?.state !== CONST.BANK_ACCOUNT.STATE.PENDING && policy?.achAccount?.state !== CONST.BANK_ACCOUNT.STATE.OPEN) { + const isAccountAlreadySharedWithCurrentUser = + policy?.achAccount?.sharees && currentUserPersonalDetails?.login ? policy.achAccount.sharees.includes(currentUserPersonalDetails?.login) : false; + if (!isOwner && !isAccountAlreadyShared && !isAccountAlreadySharedWithCurrentUser) { setShowErrorModal(true); return; } @@ -357,7 +344,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR From b4dd281a35f4f5898d3fe61a6c150b812dbddb0b Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Wed, 21 Jan 2026 10:48:01 +0200 Subject: [PATCH 08/44] change owner detection --- .../workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index d58a7e3b225d..612909ac8265 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -208,9 +208,9 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR if (!selectedPayer) { return; } - const isOwner = policy?.owner === selectedPayer; + const isSelectedPayerOwner = policy?.owner === selectedPayer; const isAccountAlreadyShared = policy?.achAccount?.sharees ? policy.achAccount.sharees.includes(selectedPayer) : false; - if (isAccountAlreadyShared || isOwner) { + if (isAccountAlreadyShared || isSelectedPayerOwner) { onButtonPress(); return; } @@ -220,6 +220,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR } const isAccountAlreadySharedWithCurrentUser = policy?.achAccount?.sharees && currentUserPersonalDetails?.login ? policy.achAccount.sharees.includes(currentUserPersonalDetails?.login) : false; + const isOwner = policy?.owner === currentUserPersonalDetails?.login; if (!isOwner && !isAccountAlreadyShared && !isAccountAlreadySharedWithCurrentUser) { setShowErrorModal(true); return; From 43917185db870551d3fd78b9aaf540ca4abaac9a Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 22 Jan 2026 18:35:52 +0200 Subject: [PATCH 09/44] switch to use bank account data --- .../workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 612909ac8265..2c30e12f1857 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -37,7 +37,7 @@ import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; -import {clearShareBankAccount, clearShareBankAccountErrors, shareBankAccount} from '@userActions/BankAccounts'; +import {clearShareBankAccount, clearShareBankAccountErrors, getBankAccountFromID, shareBankAccount} from '@userActions/BankAccounts'; import {setWorkspacePayer} from '@userActions/Policy/Policy'; import {navigateToAndOpenReportWithAccountIDs} from '@userActions/Report'; import CONST from '@src/CONST'; @@ -63,6 +63,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const {environmentURL} = useEnvironment(); const policyName = policy?.name ?? ''; const bankAccountID = policy?.achAccount?.bankAccountID; + const bankAccountInfo = getBankAccountFromID(bankAccountID); const {isOffline} = useNetwork(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); @@ -209,7 +210,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR return; } const isSelectedPayerOwner = policy?.owner === selectedPayer; - const isAccountAlreadyShared = policy?.achAccount?.sharees ? policy.achAccount.sharees.includes(selectedPayer) : false; + const isAccountAlreadyShared = bankAccountInfo?.accountData?.sharees ? bankAccountInfo?.accountData.sharees.includes(selectedPayer) : false; if (isAccountAlreadyShared || isSelectedPayerOwner) { onButtonPress(); return; @@ -219,7 +220,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR return; } const isAccountAlreadySharedWithCurrentUser = - policy?.achAccount?.sharees && currentUserPersonalDetails?.login ? policy.achAccount.sharees.includes(currentUserPersonalDetails?.login) : false; + bankAccountInfo?.accountData?.sharees && currentUserPersonalDetails?.login ? bankAccountInfo?.accountData?.sharees.includes(currentUserPersonalDetails?.login) : false; const isOwner = policy?.owner === currentUserPersonalDetails?.login; if (!isOwner && !isAccountAlreadyShared && !isAccountAlreadySharedWithCurrentUser) { setShowErrorModal(true); From ea384be91a9b78f46385938734e9381122ec5c3f Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 22 Jan 2026 19:45:28 +0200 Subject: [PATCH 10/44] remove not need item from deps --- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 2c30e12f1857..cf422add304e 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -86,11 +86,11 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const authorizedPayerEmail = personalDetails?.[accountID]?.login ?? ''; useEffect(() => { - if (!shouldShowSuccess || !policy?.id || !accountID) { + if (!shouldShowSuccess || !policy?.id) { return; } setWorkspacePayer(policy?.id, authorizedPayerEmail); - }, [accountID, authorizedPayerEmail, policy?.id, shouldShowSuccess]); + }, [authorizedPayerEmail, policy?.id, shouldShowSuccess]); useEffect(() => { return () => { From 95ed551b228874f4f1454ca7294dc6aae8529914 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 22 Jan 2026 20:48:52 +0200 Subject: [PATCH 11/44] changes for success flow --- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 45 +++--------- .../WorkspaceWorkflowsPayerSuccessPage.tsx | 69 +++++++++++++++++++ 2 files changed, 78 insertions(+), 36 deletions(-) create mode 100644 src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index cf422add304e..573179ab3e5f 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -1,17 +1,15 @@ -import React, {useEffect, useState} from 'react'; +import React, {useState} from 'react'; import type {SectionListData} from 'react-native'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import Badge from '@components/Badge'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; -import ConfirmationPage from '@components/ConfirmationPage'; import ConfirmModal from '@components/ConfirmModal'; import ErrorMessageRow from '@components/ErrorMessageRow'; import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import RenderHTML from '@components/RenderHTML'; import ScreenWrapper from '@components/ScreenWrapper'; -import ScrollView from '@components/ScrollView'; // eslint-disable-next-line no-restricted-imports import SelectionList from '@components/SelectionListWithSections'; import type {ListItem, Section} from '@components/SelectionListWithSections/types'; @@ -20,7 +18,7 @@ import Text from '@components/Text'; import TextLink from '@components/TextLink'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useEnvironment from '@hooks/useEnvironment'; -import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; +import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -37,7 +35,7 @@ import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; -import {clearShareBankAccount, clearShareBankAccountErrors, getBankAccountFromID, shareBankAccount} from '@userActions/BankAccounts'; +import {clearShareBankAccountErrors, getBankAccountFromID, shareBankAccount} from '@userActions/BankAccounts'; import {setWorkspacePayer} from '@userActions/Policy/Policy'; import {navigateToAndOpenReportWithAccountIDs} from '@userActions/Report'; import CONST from '@src/CONST'; @@ -46,6 +44,7 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetailsList, PolicyEmployee} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import WorkspaceWorkflowsPayerSuccessPage from './WorkspaceWorkflowsPayerSuccessPage'; type WorkspaceWorkflowsPayerPageOnyxProps = { /** All the personal details for everyone */ @@ -73,7 +72,6 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const [selectedPayer, setSelectedPayer] = useState(policy?.achAccount?.reimburser); const shouldShowSuccess = sharedBankAccountData?.shouldShowSuccess ?? false; const styles = useThemeStyles(); - const illustrations = useMemoizedLazyIllustrations(['ShareBank', 'Telescope'] as const); const isLoading = sharedBankAccountData?.isLoading ?? false; const [isAlertVisible, setIsAlertVisible] = useState(false); const [showShareModal, setShowShareModal] = useState(false); @@ -85,22 +83,6 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const accountID = selectedPayer ? policyMemberEmailsToAccountIDs?.[selectedPayer] : ''; const authorizedPayerEmail = personalDetails?.[accountID]?.login ?? ''; - useEffect(() => { - if (!shouldShowSuccess || !policy?.id) { - return; - } - setWorkspacePayer(policy?.id, authorizedPayerEmail); - }, [authorizedPayerEmail, policy?.id, shouldShowSuccess]); - - useEffect(() => { - return () => { - if (!shouldShowSuccess) { - return; - } - clearShareBankAccount(); - }; - }, [shouldShowSuccess]); - const isDeletedPolicyEmployee = (policyEmployee: PolicyEmployee) => !isOffline && policyEmployee.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isEmptyObject(policyEmployee.errors); @@ -267,20 +249,11 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR subtitle={policyName} onBackButtonPress={Navigation.goBack} /> - {shouldShowSuccess ? ( - - - + {shouldShowSuccess && selectedPayer ? ( + ) : ( { + if (policyID) { + return; + } + setWorkspacePayer(policyID, selectedPayer); + }, [policyID, selectedPayer]); + + useEffect(() => { + return () => { + if (!shouldShowSuccess) { + return; + } + clearShareBankAccount(); + }; + }, [shouldShowSuccess]); + + const onButtonPress = () => { + if (!selectedPayer || !policyID) { + Navigation.closeRHPFlow(); + return; + } + setWorkspacePayer(policyID, selectedPayer); + Navigation.closeRHPFlow(); + }; + + return ( + + + + ); +} + +export default WorkspaceWorkflowsPayerSuccessPage; From 114f912b36f34635b6e1103facc7339667c60ecc Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 22 Jan 2026 20:50:19 +0200 Subject: [PATCH 12/44] add state of the bank --- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 573179ab3e5f..9a88eca6d853 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -197,7 +197,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR onButtonPress(); return; } - if (isBankAccountPartiallySetup(policy?.achAccount?.state)) { + if (isBankAccountPartiallySetup(bankAccountInfo?.accountData?.state)) { setShowValidationModal(true); return; } From 3e0998d3bf65993e2ed77739e1e9a09d0defad8e Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Fri, 23 Jan 2026 13:04:21 +0200 Subject: [PATCH 13/44] fix condition --- .../workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx index d877f6b569cc..157c50d0d5ec 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx @@ -25,7 +25,7 @@ function WorkspaceWorkflowsPayerSuccessPage({policyID, selectedPayer}: Workspace const illustrations = useMemoizedLazyIllustrations(['ShareBank', 'Telescope'] as const); useEffect(() => { - if (policyID) { + if (!policyID) { return; } setWorkspacePayer(policyID, selectedPayer); From 88945ce510cbf3ee45d276808c365f03954bb40a Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Mon, 26 Jan 2026 16:05:48 +0200 Subject: [PATCH 14/44] c+ review and bug fix --- src/languages/de.ts | 9 +++-- src/languages/en.ts | 9 +++-- src/languages/es.ts | 9 +++-- src/languages/fr.ts | 9 +++-- src/languages/it.ts | 9 +++-- src/languages/ja.ts | 9 +++-- src/languages/nl.ts | 9 +++-- src/languages/pl.ts | 9 +++-- src/languages/pt-BR.ts | 9 +++-- src/languages/zh-hans.ts | 8 ++--- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 33 +++++++------------ .../WorkspaceWorkflowsPayerSuccessPage.tsx | 2 +- 12 files changed, 52 insertions(+), 72 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index 86a64f84f9c3..f241012347aa 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -2313,12 +2313,11 @@ ${amount} für ${merchant} – ${date}`, shareTitle: 'Bankkontozugriff teilen?', shareDescription: ({admin}: {admin: string}) => `Sie müssen ${admin} den Bankkontozugriff gewähren, damit dieser als Zahler eingetragen werden kann.`, validationTitle: 'Bankkonto wartet auf Validierung', - validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => - `Sie müssen dieses Bankkonto validieren. Anschließend können Sie den Zugriff auf das Bankkonto mit ${admin} teilen, um ihn/sie als Zahler festzulegen.`, + validationDescription: ({admin}: {admin: string}) => + `Sie müssen dieses Bankkonto validieren. Anschließend können Sie den Zugriff auf das Bankkonto mit ${admin} teilen, um ihn/sie als Zahler festzulegen.`, errorTitle: 'Zahler kann nicht geändert werden', - errorDescription: ({admin}: {admin: string}) => `${admin} hat keinen Zugriff auf dieses Bankkonto, daher können Sie ihn/sie nicht als Zahler festlegen.`, - errorDescriptionLink: ({owner}: {owner: string}) => `Kontaktieren Sie ${owner}`, - errorDescriptionLastPart: `wenn das Bankkonto geteilt werden soll.`, + errorDescription: ({admin, owner}: {admin: string; owner: string}) => + `${admin} hat keinen Zugriff auf dieses Bankkonto, daher kann er nicht als Zahler festgelegt werden. Kontaktieren Sie ${owner}, falls das Bankkonto freigegeben werden soll.`, }, }, reportFraudPage: { diff --git a/src/languages/en.ts b/src/languages/en.ts index 03df33470710..3e744bd599fa 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2288,12 +2288,11 @@ const translations = { shareTitle: 'Share bank account access?', shareDescription: ({admin}: {admin: string}) => `You'll need to share bank account access with ${admin} to make them the payer.`, validationTitle: 'Bank account awaiting validation', - validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => - `You need to validate this bank account. Once that’s done, you can share bank account access with ${admin} to make them the payer.`, + validationDescription: ({admin}: {admin: string}) => + `You need to validate this bank account. Once that’s done, you can share bank account access with ${admin} to make them the payer.`, errorTitle: 'Can’t change payer', - errorDescription: ({admin}: {admin: string}) => `${admin} doesn’t have access to this bank account, so you can’t make them the payer. `, - errorDescriptionLink: ({owner}: {owner: string}) => `Chat with ${owner}`, - errorDescriptionLastPart: ` if the bank account should be shared.`, + errorDescription: ({admin, owner}: {admin: string; owner: string}) => + `${admin} doesn’t have access to this bank account, so you can’t make them the payer. Chat with ${owner} if the bank account should be shared.`, }, }, reportFraudPage: { diff --git a/src/languages/es.ts b/src/languages/es.ts index bb7d695e7c5c..df123af94b84 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2016,12 +2016,11 @@ ${amount} para ${merchant} - ${date}`, shareTitle: '¿Compartir acceso a la cuenta bancaria?', shareDescription: ({admin}: {admin: string}) => `Necesitarás compartir el acceso a la cuenta bancaria con ${admin} para que sea el pagador.`, validationTitle: 'Cuenta bancaria en espera de validación', - validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => - `Necesitas validar esta cuenta bancaria. Una vez hecho esto, puedes compartir el acceso a la cuenta bancaria con ${admin} para que sea el pagador.`, + validationDescription: ({admin}: {admin: string}) => + `Necesitas validar esta cuenta bancaria. Una vez hecho esto, puedes compartir el acceso a la cuenta bancaria con ${admin} para que sea el pagador.`, errorTitle: 'No se puede cambiar el pagador', - errorDescription: ({admin}: {admin: string}) => `${admin} no tiene acceso a esta cuenta bancaria, por lo que no puedes convertirlo en el pagador.`, - errorDescriptionLink: ({owner}: {owner: string}) => `Chatear con ${owner}`, - errorDescriptionLastPart: `si se debe compartir la cuenta bancaria.`, + errorDescription: ({admin, owner}: {admin: string; owner: string}) => + `${admin} no tiene acceso a esta cuenta bancaria, por lo que no puede asignarle el pago. Chatea con ${owner} si la cuenta bancaria debe compartirse.`, }, }, reportFraudPage: { diff --git a/src/languages/fr.ts b/src/languages/fr.ts index cdcfb55d06cd..1d57d9ba923c 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -2317,12 +2317,11 @@ ${amount} pour ${merchant} - ${date}`, shareTitle: "Partager l'accès au compte bancaire?", shareDescription: ({admin}: {admin: string}) => `Vous devrez partager l'accès au compte bancaire avec ${admin} pour qu'il/elle soit le payeur.`, validationTitle: 'Compte bancaire en attente de validation', - validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => - `Vous devez valider ce compte bancaire. Une fois cette opération effectuée, vous pourrez partager l'accès au compte bancaire avec ${admin} pour en faire le payeur.`, + validationDescription: ({admin}: {admin: string}) => + `Vous devez valider ce compte bancaire. Une fois cette opération effectuée, vous pourrez partager l'accès au compte bancaire avec ${admin} pour en faire le payeur.`, errorTitle: 'Impossible de modifier le payeur', - errorDescription: ({admin}: {admin: string}) => `${admin} n'a pas accès à ce compte bancaire, vous ne pouvez donc pas le désigner comme payeur.`, - errorDescriptionLink: ({owner}: {owner: string}) => `Discutez avec ${owner}`, - errorDescriptionLastPart: ` si le compte bancaire doit être partagé.`, + errorDescription: ({admin, owner}: {admin: string; owner: string}) => + `${admin} n'a pas accès à ce compte bancaire, vous ne pouvez donc pas le désigner comme payeur. Discutez avec ${owner} si le compte bancaire doit être partagé.`, }, }, reportFraudPage: { diff --git a/src/languages/it.ts b/src/languages/it.ts index c625deb75eab..aa55643a6c8b 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -2309,12 +2309,11 @@ ${amount} per ${merchant} - ${date}`, shareTitle: "Condividere l'accesso al conto bancario?", shareDescription: ({admin}: {admin: string}) => `Dovrai condividere l'accesso al conto bancario con ${admin} per renderlo il pagatore.`, validationTitle: 'Conto bancario in attesa di convalida', - validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => - `Devi convalidare questo conto bancario. Una volta fatto, puoi condividere l'accesso al conto bancario con ${admin} per renderlo il pagatore.`, + validationDescription: ({admin}: {admin: string}) => + `Devi convalidare questo conto bancario. Una volta fatto, puoi condividere l'accesso al conto bancario con ${admin} per renderlo il pagatore.`, errorTitle: 'Impossibile cambiare il pagatore', - errorDescription: ({admin}: {admin: string}) => `${admin} non ha accesso a questo conto bancario, quindi non puoi renderlo il pagatore.`, - errorDescriptionLink: ({owner}: {owner: string}) => `Chatta con ${owner}`, - errorDescriptionLastPart: ` se il conto bancario deve essere condiviso.`, + errorDescription: ({admin, owner}: {admin: string; owner: string}) => + `${admin} non ha accesso a questo conto bancario, quindi non puoi impostarlo come pagatore. Chatta con ${owner} se il conto bancario deve essere condiviso.`, }, }, reportFraudPage: { diff --git a/src/languages/ja.ts b/src/languages/ja.ts index f0e316c6ea25..afbaf0b6e793 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -2299,12 +2299,11 @@ ${merchant} への ${amount}(${date})`, shareTitle: '銀行口座へのアクセスを共有しますか?', shareDescription: ({admin}: {admin: string}) => `${admin}を支払人にするには、銀行口座へのアクセスを${admin}と共有する必要があります。`, validationTitle: '銀行口座の検証待ち', - validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => - `この銀行口座を検証する必要があります。検証が完了すると、銀行口座へのアクセス権を${admin}と共有して、支払人にすることができます。`, + validationDescription: ({admin}: {admin: string}) => + `この銀行口座を検証する必要があります。検証が完了すると、銀行口座へのアクセス権を${admin}と共有して、支払人にすることができます。`, errorTitle: '支払人を変更できません', - errorDescription: ({admin}: {admin: string}) => `${admin}はこの銀行口座へのアクセス権がないため、支払人にすることはできません。`, - errorDescriptionLink: ({owner}: {owner: string}) => `${owner}とチャットする`, - errorDescriptionLastPart: ` 銀行口座を共有する必要がある場合。`, + errorDescription: ({admin, owner}: {admin: string; owner: string}) => + `${admin} はこの銀行口座へのアクセス権がないため、支払人に指定できません。銀行口座を共有する必要がある場合は、${owner} とチャットしてください。`, }, }, reportFraudPage: { diff --git a/src/languages/nl.ts b/src/languages/nl.ts index c84f42bb29fd..f68288e3f661 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -2308,12 +2308,11 @@ ${amount} voor ${merchant} - ${date}`, shareTitle: 'Bankrekeningtoegang delen?', shareDescription: ({admin}: {admin: string}) => `U moet bankrekeningtoegang delen met ${admin} om hem/haar de betaler te maken.`, validationTitle: 'Bankrekening wacht op validatie', - validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => - `U moet deze bankrekening valideren. Zodra dat is gedaan, kunt u de toegang tot de bankrekening delen met ${admin} om hem/haar de betaler te maken.`, + validationDescription: ({admin}: {admin: string}) => + `U moet deze bankrekening valideren. Zodra dat is gedaan, kunt u de toegang tot de bankrekening delen met ${admin} om hem/haar de betaler te maken.`, errorTitle: 'Betaler kan niet worden gewijzigd', - errorDescription: ({admin}: {admin: string}) => `${admin} heeft geen toegang tot deze bankrekening, dus u kunt hem/haar niet de betaler maken.`, - errorDescriptionLink: ({owner}: {owner: string}) => `Chat met ${owner}`, - errorDescriptionLastPart: `als de bankrekening moet worden gedeeld.`, + errorDescription: ({admin, owner}: {admin: string; owner: string}) => + `${admin} heeft geen toegang tot deze bankrekening, dus je kunt hem/haar niet als betaler instellen. Chat met ${owner} als de bankrekening gedeeld moet worden.`, }, }, reportFraudPage: { diff --git a/src/languages/pl.ts b/src/languages/pl.ts index f520f6d783a1..72ca3d7dc5be 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -2302,12 +2302,11 @@ ${amount} dla ${merchant} - ${date}`, shareTitle: 'Udostępnić dostęp do konta bankowego?', shareDescription: ({admin}: {admin: string}) => `Musisz udostępnić dostęp do konta bankowego użytkownikowi ${admin}, aby uczynić go płatnikiem.`, validationTitle: 'Konto bankowe oczekuje na walidację', - validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => - `Musisz zweryfikować to konto bankowe. Po wykonaniu tej czynności możesz udostępnić dostęp do konta bankowego użytkownikowi ${admin}, aby uczynić go płatnikiem.`, + validationDescription: ({admin}: {admin: string}) => + `Musisz zweryfikować to konto bankowe. Po wykonaniu tej czynności możesz udostępnić dostęp do konta bankowego użytkownikowi ${admin}, aby uczynić go płatnikiem.`, errorTitle: 'Nie można zmienić płatnika', - errorDescription: ({admin}: {admin: string}) => `${admin} nie ma dostępu do tego konta bankowego, więc nie możesz uczynić go płatnikiem. `, - errorDescriptionLink: ({owner}: {owner: string}) => `Porozmawiaj z ${owner}`, - errorDescriptionLastPart: `czy konto bankowe powinno być współdzielone.`, + errorDescription: ({admin, owner}: {admin: string; owner: string}) => + `${admin} nie ma dostępu do tego konta bankowego, więc nie możesz uczynić go płatnikiem. Porozmawiaj z ${owner}em/właścicielem, jeśli konto bankowe powinno być współdzielone.`, }, }, reportFraudPage: { diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index e9f74f3da083..316d9a707292 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -2301,12 +2301,11 @@ ${amount} para ${merchant} - ${date}`, shareTitle: 'Compartilhar acesso à conta bancária?', shareDescription: ({admin}: {admin: string}) => `Você precisará compartilhar o acesso à conta bancária com ${admin} para torná-lo o pagador.`, validationTitle: 'Conta bancária aguardando validação', - validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => - `Você precisa validar esta conta bancária. Depois disso, você poderá compartilhar o acesso à conta bancária com ${admin} para torná-lo o pagador.`, + validationDescription: ({admin}: {admin: string}) => + `Você precisa validar esta conta bancária. Depois disso, você poderá compartilhar o acesso à conta bancária com ${admin} para torná-lo o pagador.`, errorTitle: 'Não é possível alterar o pagador', - errorDescription: ({admin}: {admin: string}) => `${admin} não tem acesso a esta conta bancária, portanto, você não pode torná-lo o pagador.`, - errorDescriptionLink: ({owner}: {owner: string}) => `Converse com ${owner}`, - errorDescriptionLastPart: ` se a conta bancária deve ser compartilhada.`, + errorDescription: ({admin, owner}: {admin: string; owner: string}) => + `${admin} não tem acesso a esta conta bancária, portanto, você não pode torná-lo o pagador. Converse com ${owner} se a conta bancária deve ser compartilhada.`, }, }, reportFraudPage: { diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 6c2d9d4199d9..4649bafdfc6f 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -2269,12 +2269,10 @@ ${amount},商户:${merchant} - ${date}`, shareTitle: '共享银行账户访问权限?', shareDescription: ({admin}: {admin: string}) => `您需要与 ${admin} 共享银行账户访问权限,才能将其设置为付款人。`, validationTitle: '银行账户等待验证', - validationDescription: ({admin, validationLink}: {admin: string; validationLink: string}) => - `您需要验证此银行账户。验证完成后,您可以将银行账户访问权限分享给 ${admin},使其成为付款人。`, + validationDescription: ({admin}: {admin: string}) => `您需要验证此银行账户。验证完成后,您可以将银行账户访问权限分享给 ${admin},使其成为付款人。`, errorTitle: '无法更改付款人', - errorDescription: ({admin}: {admin: string}) => `${admin} 没有此银行账户的访问权限,因此您无法将其设置为付款人。`, - errorDescriptionLink: ({owner}: {owner: string}) => `请联系 ${owner}`, - errorDescriptionLastPart: ` 如果银行账户应该共享。`, + errorDescription: ({admin, owner}: {admin: string; owner: string}) => + `${admin} 没有此银行账户的访问权限,因此您无法将其设置为付款人。请联系 ${owner} 了解是否需要共享此银行账户。`, }, }, reportFraudPage: { diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 9a88eca6d853..388b9640775d 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -14,16 +14,12 @@ import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionListWithSections'; import type {ListItem, Section} from '@components/SelectionListWithSections/types'; import UserListItem from '@components/SelectionListWithSections/UserListItem'; -import Text from '@components/Text'; -import TextLink from '@components/TextLink'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; -import useEnvironment from '@hooks/useEnvironment'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; -import {isBankAccountPartiallySetup} from '@libs/BankAccountUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -37,6 +33,7 @@ import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullsc import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import {clearShareBankAccountErrors, getBankAccountFromID, shareBankAccount} from '@userActions/BankAccounts'; import {setWorkspacePayer} from '@userActions/Policy/Policy'; +import {navigateToBankAccountRoute} from '@userActions/ReimbursementAccount'; import {navigateToAndOpenReportWithAccountIDs} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -59,14 +56,14 @@ type MembersSection = SectionListData>; function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingReportData = true}: WorkspaceWorkflowsPayerPageProps) { const {translate, formatPhoneNumber} = useLocalize(); - const {environmentURL} = useEnvironment(); const policyName = policy?.name ?? ''; + const policyID = policy?.id; const bankAccountID = policy?.achAccount?.bankAccountID; const bankAccountInfo = getBankAccountFromID(bankAccountID); const {isOffline} = useNetwork(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); - const icons = useMemoizedLazyExpensifyIcons(['FallbackAvatar'] as const); + const icons = useMemoizedLazyExpensifyIcons(['FallbackAvatar']); const [searchTerm, setSearchTerm] = useState(''); const [sharedBankAccountData] = useOnyx(ONYXKEYS.SHARE_BANK_ACCOUNT, {canBeMissing: true}); const [selectedPayer, setSelectedPayer] = useState(policy?.achAccount?.reimburser); @@ -197,7 +194,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR onButtonPress(); return; } - if (isBankAccountPartiallySetup(bankAccountInfo?.accountData?.state)) { + if (bankAccountInfo?.accountData?.state === CONST.BANK_ACCOUNT.STATE.VERIFYING) { setShowValidationModal(true); return; } @@ -317,9 +314,9 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR prompt={ navigateToBankAccountRoute(policyID, ROUTES.WORKSPACE_WORKFLOWS.getRoute(policyID))} html={translate('workflowsPayerPage.shareBankAccount.validationDescription', { admin: selectedPayerDetails?.displayName ?? '', - validationLink: `${environmentURL}/${ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(policy?.id, '', ROUTES.WORKSPACE_WORKFLOWS.getRoute(route.params.policyID))}`, })} /> @@ -335,27 +332,21 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR }} success prompt={ - - - {translate('workflowsPayerPage.shareBankAccount.errorDescription', { - admin: selectedPayerDetails?.displayName ?? '', - })} - - { + + { if (!currentUserPersonalDetails?.accountID || !policy?.ownerAccountID) { return; } setShowErrorModal(false); navigateToAndOpenReportWithAccountIDs([currentUserPersonalDetails.accountID], policy.ownerAccountID); }} - > - {translate('workflowsPayerPage.shareBankAccount.errorDescriptionLink', { + html={translate('workflowsPayerPage.shareBankAccount.errorDescription', { + admin: selectedPayerDetails?.displayName ?? '', owner: ownerDetails?.displayName ?? '', })} - - {translate('workflowsPayerPage.shareBankAccount.errorDescriptionLastPart')} - + /> + } shouldShowCancelButton={false} confirmText={translate('common.buttonConfirm')} diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx index 157c50d0d5ec..6a4653e1815a 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx @@ -22,7 +22,7 @@ function WorkspaceWorkflowsPayerSuccessPage({policyID, selectedPayer}: Workspace const [sharedBankAccountData] = useOnyx(ONYXKEYS.SHARE_BANK_ACCOUNT, {canBeMissing: true}); const shouldShowSuccess = sharedBankAccountData?.shouldShowSuccess ?? false; const styles = useThemeStyles(); - const illustrations = useMemoizedLazyIllustrations(['ShareBank', 'Telescope'] as const); + const illustrations = useMemoizedLazyIllustrations(['ShareBank', 'Telescope']); useEffect(() => { if (!policyID) { From 1695b3e1b191bee353b325259771ae2c3ad3c292 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Wed, 28 Jan 2026 22:01:44 +0200 Subject: [PATCH 15/44] switch condition --- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 388b9640775d..b7cf3ffb1bbb 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -194,7 +194,9 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR onButtonPress(); return; } - if (bankAccountInfo?.accountData?.state === CONST.BANK_ACCOUNT.STATE.VERIFYING) { + console.log('bankAccountInfo'); + console.log(bankAccountInfo); + if (bankAccountInfo?.accountData?.state === CONST.BANK_ACCOUNT.STATE.PENDING) { setShowValidationModal(true); return; } From 08d0b602fd3838b553061a5bba823e7d8e351da9 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 29 Jan 2026 00:05:44 +0200 Subject: [PATCH 16/44] add changes --- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index b7cf3ffb1bbb..9cc60d17bda2 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -31,7 +31,7 @@ import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; -import {clearShareBankAccountErrors, getBankAccountFromID, shareBankAccount} from '@userActions/BankAccounts'; +import {clearShareBankAccountErrors, shareBankAccount} from '@userActions/BankAccounts'; import {setWorkspacePayer} from '@userActions/Policy/Policy'; import {navigateToBankAccountRoute} from '@userActions/ReimbursementAccount'; import {navigateToAndOpenReportWithAccountIDs} from '@userActions/Report'; @@ -58,8 +58,9 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const {translate, formatPhoneNumber} = useLocalize(); const policyName = policy?.name ?? ''; const policyID = policy?.id; + const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST, {canBeMissing: true}); + const bankAccountConnectedToWorkspace = Object.values(bankAccountList ?? {}).find((account) => account?.accountData?.additionalData?.policyID === policyID); const bankAccountID = policy?.achAccount?.bankAccountID; - const bankAccountInfo = getBankAccountFromID(bankAccountID); const {isOffline} = useNetwork(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); @@ -189,19 +190,20 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR return; } const isSelectedPayerOwner = policy?.owner === selectedPayer; - const isAccountAlreadyShared = bankAccountInfo?.accountData?.sharees ? bankAccountInfo?.accountData.sharees.includes(selectedPayer) : false; - if (isAccountAlreadyShared || isSelectedPayerOwner) { + const isSelectedAlreadyAPayer = policy?.achAccount?.reimburser === selectedPayer; + const isAccountAlreadyShared = bankAccountConnectedToWorkspace?.accountData?.sharees ? bankAccountConnectedToWorkspace?.accountData.sharees.includes(selectedPayer) : false; + if (isAccountAlreadyShared || isSelectedPayerOwner || isSelectedAlreadyAPayer) { onButtonPress(); return; } - console.log('bankAccountInfo'); - console.log(bankAccountInfo); - if (bankAccountInfo?.accountData?.state === CONST.BANK_ACCOUNT.STATE.PENDING) { + if (bankAccountConnectedToWorkspace?.accountData?.state === CONST.BANK_ACCOUNT.STATE.PENDING) { setShowValidationModal(true); return; } const isAccountAlreadySharedWithCurrentUser = - bankAccountInfo?.accountData?.sharees && currentUserPersonalDetails?.login ? bankAccountInfo?.accountData?.sharees.includes(currentUserPersonalDetails?.login) : false; + bankAccountConnectedToWorkspace?.accountData?.sharees && currentUserPersonalDetails?.login + ? bankAccountConnectedToWorkspace?.accountData?.sharees.includes(currentUserPersonalDetails?.login) + : false; const isOwner = policy?.owner === currentUserPersonalDetails?.login; if (!isOwner && !isAccountAlreadyShared && !isAccountAlreadySharedWithCurrentUser) { setShowErrorModal(true); From 2e444d9631b37500c8bb6f629d8be4a3468ab3dd Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 29 Jan 2026 00:21:51 +0200 Subject: [PATCH 17/44] fix after merge --- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index a1cc31e44318..ecbb87443074 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -1,7 +1,5 @@ import React, {useState} from 'react'; -import type {SectionListData} from 'react-native'; import {View} from 'react-native'; -import React, {useCallback, useMemo, useState} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import Badge from '@components/Badge'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -11,15 +9,11 @@ import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import RenderHTML from '@components/RenderHTML'; import ScreenWrapper from '@components/ScreenWrapper'; -// eslint-disable-next-line no-restricted-imports -import SelectionList from '@components/SelectionListWithSections'; -import type {ListItem, Section} from '@components/SelectionListWithSections/types'; -import UserListItem from '@components/SelectionListWithSections/UserListItem'; -import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import UserListItem from '@components/SelectionList/ListItem/UserListItem'; import SelectionList from '@components/SelectionList/SelectionListWithSections'; import type {Section} from '@components/SelectionList/SelectionListWithSections/types'; import type {ListItem} from '@components/SelectionList/types'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -261,17 +255,6 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR /> ) : ( Date: Thu, 29 Jan 2026 10:27:23 +0200 Subject: [PATCH 18/44] add more loggers for debug --- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index ecbb87443074..a76e8c34f772 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -60,6 +60,19 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST, {canBeMissing: true}); const bankAccountConnectedToWorkspace = Object.values(bankAccountList ?? {}).find((account) => account?.accountData?.additionalData?.policyID === policyID); const bankAccountID = policy?.achAccount?.bankAccountID; + console.log('policyID'); + console.log(policyID); + console.log('policy?.achAccount'); + console.log(policy?.achAccount); + console.log('bankAccountID'); + console.log(bankAccountID); + console.log('bankAccountConnectedToWorkspace'); + console.log(bankAccountConnectedToWorkspace); + const bankAccountInfo = bankAccountID ? bankAccountList?.[bankAccountID] : bankAccountConnectedToWorkspace; + console.log('bankAccountList'); + console.log(bankAccountList); + console.log('bankAccountInfo'); + console.log(bankAccountInfo); const {isOffline} = useNetwork(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); @@ -190,19 +203,17 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR } const isSelectedPayerOwner = policy?.owner === selectedPayer; const isSelectedAlreadyAPayer = policy?.achAccount?.reimburser === selectedPayer; - const isAccountAlreadyShared = bankAccountConnectedToWorkspace?.accountData?.sharees ? bankAccountConnectedToWorkspace?.accountData.sharees.includes(selectedPayer) : false; + const isAccountAlreadyShared = bankAccountInfo?.accountData?.sharees ? bankAccountInfo?.accountData.sharees.includes(selectedPayer) : false; if (isAccountAlreadyShared || isSelectedPayerOwner || isSelectedAlreadyAPayer) { onButtonPress(); return; } - if (bankAccountConnectedToWorkspace?.accountData?.state === CONST.BANK_ACCOUNT.STATE.PENDING) { + if (bankAccountInfo?.accountData?.state === CONST.BANK_ACCOUNT.STATE.PENDING || bankAccountInfo?.accountData?.state === CONST.BANK_ACCOUNT.STATE.VERIFYING) { setShowValidationModal(true); return; } const isAccountAlreadySharedWithCurrentUser = - bankAccountConnectedToWorkspace?.accountData?.sharees && currentUserPersonalDetails?.login - ? bankAccountConnectedToWorkspace?.accountData?.sharees.includes(currentUserPersonalDetails?.login) - : false; + bankAccountInfo?.accountData?.sharees && currentUserPersonalDetails?.login ? bankAccountInfo?.accountData?.sharees.includes(currentUserPersonalDetails?.login) : false; const isOwner = policy?.owner === currentUserPersonalDetails?.login; if (!isOwner && !isAccountAlreadyShared && !isAccountAlreadySharedWithCurrentUser) { setShowErrorModal(true); From 00eed783d2902f1eae8160eba5b740ea7e353e51 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 29 Jan 2026 11:00:08 +0200 Subject: [PATCH 19/44] add check for states --- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index a76e8c34f772..caad51fd8ec6 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -19,6 +19,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; +import {isBankAccountPartiallySetup} from '@libs/BankAccountUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -208,7 +209,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR onButtonPress(); return; } - if (bankAccountInfo?.accountData?.state === CONST.BANK_ACCOUNT.STATE.PENDING || bankAccountInfo?.accountData?.state === CONST.BANK_ACCOUNT.STATE.VERIFYING) { + if (isBankAccountPartiallySetup(bankAccountInfo?.accountData?.state)) { setShowValidationModal(true); return; } From a567b88563ff3309bc7d7a5d7d3bd2ca6c9320d9 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 29 Jan 2026 18:37:27 +0200 Subject: [PATCH 20/44] update condition --- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index caad51fd8ec6..c11658fca554 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -69,7 +69,8 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR console.log(bankAccountID); console.log('bankAccountConnectedToWorkspace'); console.log(bankAccountConnectedToWorkspace); - const bankAccountInfo = bankAccountID ? bankAccountList?.[bankAccountID] : bankAccountConnectedToWorkspace; + const bankAccountFromList = bankAccountID ? bankAccountList?.[bankAccountID] : undefined; + const bankAccountInfo = bankAccountFromList ?? bankAccountConnectedToWorkspace; console.log('bankAccountList'); console.log(bankAccountList); console.log('bankAccountInfo'); From caf15744afab091d92b3c8df0c3f9503902a6bff Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Fri, 30 Jan 2026 10:58:11 +0200 Subject: [PATCH 21/44] remove debug info --- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index c11658fca554..dd1d16bbd547 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -61,20 +61,8 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST, {canBeMissing: true}); const bankAccountConnectedToWorkspace = Object.values(bankAccountList ?? {}).find((account) => account?.accountData?.additionalData?.policyID === policyID); const bankAccountID = policy?.achAccount?.bankAccountID; - console.log('policyID'); - console.log(policyID); - console.log('policy?.achAccount'); - console.log(policy?.achAccount); - console.log('bankAccountID'); - console.log(bankAccountID); - console.log('bankAccountConnectedToWorkspace'); - console.log(bankAccountConnectedToWorkspace); const bankAccountFromList = bankAccountID ? bankAccountList?.[bankAccountID] : undefined; const bankAccountInfo = bankAccountFromList ?? bankAccountConnectedToWorkspace; - console.log('bankAccountList'); - console.log(bankAccountList); - console.log('bankAccountInfo'); - console.log(bankAccountInfo); const {isOffline} = useNetwork(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); From 6c8dbfa65975d7bc45636f339f2790a42595ef83 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Fri, 30 Jan 2026 18:28:46 +0200 Subject: [PATCH 22/44] fix closing modals --- .../workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index dd1d16bbd547..dff5c63d7209 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -316,10 +316,14 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR setShowValidationModal(false); }} success + onCancel={() => setShowValidationModal(false)} prompt={ navigateToBankAccountRoute(policyID, ROUTES.WORKSPACE_WORKFLOWS.getRoute(policyID))} + onLinkPress={() => { + setShowValidationModal(false); + navigateToBankAccountRoute(policyID, ROUTES.WORKSPACE_WORKFLOWS.getRoute(policyID)); + }} html={translate('workflowsPayerPage.shareBankAccount.validationDescription', { admin: selectedPayerDetails?.displayName ?? '', })} @@ -332,6 +336,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR setShowErrorModal(false)} onConfirm={() => { setShowErrorModal(false); }} From 42c028f6b72213333766c62824466e7acbc1252c Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 12 Feb 2026 15:30:55 +0200 Subject: [PATCH 23/44] use policy shares to check for main bank account --- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index dff5c63d7209..9f621c477c29 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -194,7 +194,8 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const isSelectedPayerOwner = policy?.owner === selectedPayer; const isSelectedAlreadyAPayer = policy?.achAccount?.reimburser === selectedPayer; const isAccountAlreadyShared = bankAccountInfo?.accountData?.sharees ? bankAccountInfo?.accountData.sharees.includes(selectedPayer) : false; - if (isAccountAlreadyShared || isSelectedPayerOwner || isSelectedAlreadyAPayer) { + const isAccountAlreadySharedOnMainBankAccount = policy?.achAccount?.sharees ? policy?.achAccount.sharees.includes(selectedPayer) : false; + if (isAccountAlreadyShared || isSelectedPayerOwner || isSelectedAlreadyAPayer || isAccountAlreadySharedOnMainBankAccount) { onButtonPress(); return; } From ac089e07ab1f99bd4466ed4273ec37b9d0f7e795 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Tue, 17 Feb 2026 09:50:10 +0200 Subject: [PATCH 24/44] add ai comments --- .../workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 7 ++++--- .../workflows/WorkspaceWorkflowsPayerSuccessPage.tsx | 9 +-------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 012a3f8f3af9..97580e5edb83 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -60,9 +60,10 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const policyID = policy?.id; const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST, {canBeMissing: true}); const bankAccountConnectedToWorkspace = Object.values(bankAccountList ?? {}).find((account) => account?.accountData?.additionalData?.policyID === policyID); - const bankAccountID = policy?.achAccount?.bankAccountID; - const bankAccountFromList = bankAccountID ? bankAccountList?.[bankAccountID] : undefined; + const policyBankAccountID = policy?.achAccount?.bankAccountID; + const bankAccountFromList = policyBankAccountID ? bankAccountList?.[policyBankAccountID] : undefined; const bankAccountInfo = bankAccountFromList ?? bankAccountConnectedToWorkspace; + const bankAccountID = policyBankAccountID ?? bankAccountInfo?.accountData?.bankAccountID; const {isOffline} = useNetwork(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); @@ -323,7 +324,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR { setShowValidationModal(false); - navigateToBankAccountRoute(policyID, ROUTES.WORKSPACE_WORKFLOWS.getRoute(policyID)); + navigateToBankAccountRoute({policyID, backTo: ROUTES.WORKSPACE_WORKFLOWS.getRoute(policyID)}); }} html={translate('workflowsPayerPage.shareBankAccount.validationDescription', { admin: selectedPayerDetails?.displayName ?? '', diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx index 6a4653e1815a..8e07a1b8f9c3 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx @@ -40,14 +40,7 @@ function WorkspaceWorkflowsPayerSuccessPage({policyID, selectedPayer}: Workspace }; }, [shouldShowSuccess]); - const onButtonPress = () => { - if (!selectedPayer || !policyID) { - Navigation.closeRHPFlow(); - return; - } - setWorkspacePayer(policyID, selectedPayer); - Navigation.closeRHPFlow(); - }; + const onButtonPress = () => Navigation.closeRHPFlow(); return ( From 16d59b594f053b3e420cb219b0c3316cfab7a064 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Tue, 17 Feb 2026 10:39:32 +0200 Subject: [PATCH 25/44] add ai comments --- src/languages/pl.ts | 2 +- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 9a5b3a474161..4ea8feb74f7f 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -2366,7 +2366,7 @@ ${amount} dla ${merchant} - ${date}`, `Musisz zweryfikować to konto bankowe. Po wykonaniu tej czynności możesz udostępnić dostęp do konta bankowego użytkownikowi ${admin}, aby uczynić go płatnikiem.`, errorTitle: 'Nie można zmienić płatnika', errorDescription: ({admin, owner}: {admin: string; owner: string}) => - `${admin} nie ma dostępu do tego konta bankowego, więc nie możesz uczynić go płatnikiem. Porozmawiaj z ${owner}em/właścicielem, jeśli konto bankowe powinno być współdzielone.`, + `${admin} nie ma dostępu do tego konta bankowego, więc nie możesz uczynić go płatnikiem. Porozmawiaj z ${owner}, jeśli konto bankowe powinno być współdzielone.`, }, }, reportFraudPage: { diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 97580e5edb83..1495534342e6 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -109,7 +109,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR keyForList: String(adminAccountID), accountID: adminAccountID, isSelected: isAuthorizedPayer, - isDisabled: policyEmployee.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || !isEmptyObject(policyEmployee.errors), + isDisabled: policyEmployee.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || !isEmptyObject(policyEmployee.errors) || isLoading, text: formatPhoneNumber(getDisplayNameOrDefault(details)), alternateText: formatPhoneNumber(details?.login ?? ''), rightElement: roleBadge, From 39db18f3cad5e94b91f0ea50f0405612d5ba69a3 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Tue, 17 Feb 2026 18:04:56 +0200 Subject: [PATCH 26/44] fix AI --- .../workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 1495534342e6..bfb58dcb6522 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -165,7 +165,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const handleConfirm = () => { setShowShareModal(false); - if (!bankAccountID) { + if (!bankAccountID || !authorizedPayerEmail) { return; } if (!selectedPayer) { @@ -180,7 +180,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR }; const onButtonPress = () => { - if (!selectedPayer || !policy) { + if (!selectedPayer || !policy || !authorizedPayerEmail) { Navigation.closeRHPFlow(); return; } @@ -351,7 +351,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR return; } setShowErrorModal(false); - navigateToAndOpenReportWithAccountIDs([currentUserPersonalDetails.accountID], policy.ownerAccountID); + navigateToAndOpenReportWithAccountIDs([policy.ownerAccountID], currentUserPersonalDetails.accountID); }} html={translate('workflowsPayerPage.shareBankAccount.errorDescription', { admin: selectedPayerDetails?.displayName ?? '', From c8097200ce801f82a4a0fbb3b5411d0e5f0df97f Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Tue, 17 Feb 2026 19:32:04 +0200 Subject: [PATCH 27/44] add bottom padding --- .../workflows/WorkspaceWorkflowsPayerSuccessPage.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx index 8e07a1b8f9c3..be39881bcb0e 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx @@ -43,7 +43,10 @@ function WorkspaceWorkflowsPayerSuccessPage({policyID, selectedPayer}: Workspace const onButtonPress = () => Navigation.closeRHPFlow(); return ( - + Date: Wed, 18 Feb 2026 12:51:40 +0200 Subject: [PATCH 28/44] refactor useConfirmModal --- src/components/Modal/Global/ModalContext.tsx | 2 +- src/hooks/useConfirmModal.ts | 7 +- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 141 ++++++++---------- 3 files changed, 71 insertions(+), 79 deletions(-) diff --git a/src/components/Modal/Global/ModalContext.tsx b/src/components/Modal/Global/ModalContext.tsx index 91bbcf679969..7e84ae53fb6f 100644 --- a/src/components/Modal/Global/ModalContext.tsx +++ b/src/components/Modal/Global/ModalContext.tsx @@ -103,5 +103,5 @@ function ModalProvider({children}: {children: React.ReactNode}) { ); } -export type {ModalProps}; +export type {ModalProps, ModalStateChangePayload}; export {ModalProvider, useModal, ModalActions}; diff --git a/src/hooks/useConfirmModal.ts b/src/hooks/useConfirmModal.ts index 25b2c827c2de..8f7f6e26abba 100644 --- a/src/hooks/useConfirmModal.ts +++ b/src/hooks/useConfirmModal.ts @@ -1,5 +1,5 @@ import ConfirmModalWrapper from '@components/Modal/Global/ConfirmModalWrapper'; -import type {ModalProps} from '@components/Modal/Global/ModalContext'; +import type {ModalProps, ModalStateChangePayload} from '@components/Modal/Global/ModalContext'; import {useModal} from '@components/Modal/Global/ModalContext'; type ConfirmModalOptions = Omit, keyof ModalProps>; @@ -17,8 +17,13 @@ const useConfirmModal = () => { }); }; + const closeConfirmModal = (data?: ModalStateChangePayload) => { + context.closeModal(data); + }; + return { ...context, + closeModal: closeConfirmModal, showConfirmModal, }; }; diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index bfb58dcb6522..62c57503b5d6 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -3,7 +3,6 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import Badge from '@components/Badge'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; -import ConfirmModal from '@components/ConfirmModal'; import ErrorMessageRow from '@components/ErrorMessageRow'; import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -13,6 +12,7 @@ import UserListItem from '@components/SelectionList/ListItem/UserListItem'; import SelectionListWithSections from '@components/SelectionList/SelectionListWithSections'; import type {Section} from '@components/SelectionList/SelectionListWithSections/types'; import type {ListItem} from '@components/SelectionList/types'; +import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; @@ -27,6 +27,7 @@ import {getSearchValueForPhoneOrEmail} from '@libs/OptionsListUtils'; import {getDisplayNameOrDefault, getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {getMemberAccountIDsForWorkspace, goBackFromInvalidPolicy, isExpensifyTeam, isPendingDeletePolicy} from '@libs/PolicyUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; +import {ModalActions} from '@components/Modal/Global/ModalContext'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; @@ -75,9 +76,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const styles = useThemeStyles(); const isLoading = sharedBankAccountData?.isLoading ?? false; const [isAlertVisible, setIsAlertVisible] = useState(false); - const [showShareModal, setShowShareModal] = useState(false); - const [showValidationModal, setShowValidationModal] = useState(false); - const [showErrorModal, setShowErrorModal] = useState(false); + const {showConfirmModal, closeModal} = useConfirmModal(); const policyMemberEmailsToAccountIDs = getMemberAccountIDsForWorkspace(policy?.employeeList); const selectedPayerDetails = selectedPayer ? getPersonalDetailByEmail(selectedPayer) : undefined; const ownerDetails = policy?.owner ? getPersonalDetailByEmail(policy?.owner) : undefined; @@ -164,7 +163,6 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const headerMessage = searchTerm && !sections.at(0)?.data.length ? translate('common.noResultsFound') : ''; const handleConfirm = () => { - setShowShareModal(false); if (!bankAccountID || !authorizedPayerEmail) { return; } @@ -201,17 +199,75 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR return; } if (isBankAccountPartiallySetup(bankAccountInfo?.accountData?.state)) { - setShowValidationModal(true); + showConfirmModal({ + title: translate('workflowsPayerPage.shareBankAccount.validationTitle'), + success: true, + shouldShowCancelButton: false, + confirmText: translate('common.buttonConfirm'), + prompt: ( + + { + closeModal(); + navigateToBankAccountRoute({policyID, backTo: ROUTES.WORKSPACE_WORKFLOWS.getRoute(policyID)}); + }} + html={translate('workflowsPayerPage.shareBankAccount.validationDescription', { + admin: selectedPayerDetails?.displayName ?? '', + })} + /> + + ), + }); return; } const isAccountAlreadySharedWithCurrentUser = bankAccountInfo?.accountData?.sharees && currentUserPersonalDetails?.login ? bankAccountInfo?.accountData?.sharees.includes(currentUserPersonalDetails?.login) : false; const isOwner = policy?.owner === currentUserPersonalDetails?.login; if (!isOwner && !isAccountAlreadyShared && !isAccountAlreadySharedWithCurrentUser) { - setShowErrorModal(true); + showConfirmModal({ + title: translate('workflowsPayerPage.shareBankAccount.errorTitle'), + success: true, + shouldShowCancelButton: false, + confirmText: translate('common.buttonConfirm'), + prompt: ( + + { + if (!currentUserPersonalDetails?.accountID || !policy?.ownerAccountID) { + return; + } + closeModal(); + navigateToAndOpenReportWithAccountIDs([policy.ownerAccountID], currentUserPersonalDetails.accountID); + }} + html={translate('workflowsPayerPage.shareBankAccount.errorDescription', { + admin: selectedPayerDetails?.displayName ?? '', + owner: ownerDetails?.displayName ?? '', + })} + /> + + ), + }); return; } - setShowShareModal(true); + showConfirmModal({ + title: translate('workflowsPayerPage.shareBankAccount.shareTitle'), + success: true, + confirmText: translate('common.share'), + prompt: ( + + + + ), + }).then((result) => { + if (result.action !== ModalActions.CONFIRM) { + return; + } + handleConfirm(); + }); }; const setPolicyAuthorizedPayer = (member: MemberOption) => { @@ -294,75 +350,6 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR )} - setShowShareModal(false)} - success - prompt={ - - - - } - confirmText={translate('common.share')} - /> - { - setShowValidationModal(false); - }} - success - onCancel={() => setShowValidationModal(false)} - prompt={ - - { - setShowValidationModal(false); - navigateToBankAccountRoute({policyID, backTo: ROUTES.WORKSPACE_WORKFLOWS.getRoute(policyID)}); - }} - html={translate('workflowsPayerPage.shareBankAccount.validationDescription', { - admin: selectedPayerDetails?.displayName ?? '', - })} - /> - - } - shouldShowCancelButton={false} - confirmText={translate('common.buttonConfirm')} - /> - setShowErrorModal(false)} - onConfirm={() => { - setShowErrorModal(false); - }} - success - prompt={ - - { - if (!currentUserPersonalDetails?.accountID || !policy?.ownerAccountID) { - return; - } - setShowErrorModal(false); - navigateToAndOpenReportWithAccountIDs([policy.ownerAccountID], currentUserPersonalDetails.accountID); - }} - html={translate('workflowsPayerPage.shareBankAccount.errorDescription', { - admin: selectedPayerDetails?.displayName ?? '', - owner: ownerDetails?.displayName ?? '', - })} - /> - - } - shouldShowCancelButton={false} - confirmText={translate('common.buttonConfirm')} - /> ); } From a9b7c5d15a40cf7beee5103757bf394af41f2cc0 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 19 Feb 2026 10:19:59 +0200 Subject: [PATCH 29/44] rollback direct confirm modals --- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 101 ++++++++++-------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 62c57503b5d6..42db0c1f8f0a 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -42,6 +42,7 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetailsList, PolicyEmployee} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import ConfirmModal from '@components/ConfirmModal'; import WorkspaceWorkflowsPayerSuccessPage from './WorkspaceWorkflowsPayerSuccessPage'; type WorkspaceWorkflowsPayerPageOnyxProps = { @@ -74,9 +75,11 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const [selectedPayer, setSelectedPayer] = useState(policy?.achAccount?.reimburser); const shouldShowSuccess = sharedBankAccountData?.shouldShowSuccess ?? false; const styles = useThemeStyles(); + const {showConfirmModal} = useConfirmModal(); const isLoading = sharedBankAccountData?.isLoading ?? false; const [isAlertVisible, setIsAlertVisible] = useState(false); - const {showConfirmModal, closeModal} = useConfirmModal(); + const [showValidationModal, setShowValidationModal] = useState(false); + const [showErrorModal, setShowErrorModal] = useState(false); const policyMemberEmailsToAccountIDs = getMemberAccountIDsForWorkspace(policy?.employeeList); const selectedPayerDetails = selectedPayer ? getPersonalDetailByEmail(selectedPayer) : undefined; const ownerDetails = policy?.owner ? getPersonalDetailByEmail(policy?.owner) : undefined; @@ -199,54 +202,14 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR return; } if (isBankAccountPartiallySetup(bankAccountInfo?.accountData?.state)) { - showConfirmModal({ - title: translate('workflowsPayerPage.shareBankAccount.validationTitle'), - success: true, - shouldShowCancelButton: false, - confirmText: translate('common.buttonConfirm'), - prompt: ( - - { - closeModal(); - navigateToBankAccountRoute({policyID, backTo: ROUTES.WORKSPACE_WORKFLOWS.getRoute(policyID)}); - }} - html={translate('workflowsPayerPage.shareBankAccount.validationDescription', { - admin: selectedPayerDetails?.displayName ?? '', - })} - /> - - ), - }); + setShowValidationModal(true); return; } const isAccountAlreadySharedWithCurrentUser = bankAccountInfo?.accountData?.sharees && currentUserPersonalDetails?.login ? bankAccountInfo?.accountData?.sharees.includes(currentUserPersonalDetails?.login) : false; const isOwner = policy?.owner === currentUserPersonalDetails?.login; if (!isOwner && !isAccountAlreadyShared && !isAccountAlreadySharedWithCurrentUser) { - showConfirmModal({ - title: translate('workflowsPayerPage.shareBankAccount.errorTitle'), - success: true, - shouldShowCancelButton: false, - confirmText: translate('common.buttonConfirm'), - prompt: ( - - { - if (!currentUserPersonalDetails?.accountID || !policy?.ownerAccountID) { - return; - } - closeModal(); - navigateToAndOpenReportWithAccountIDs([policy.ownerAccountID], currentUserPersonalDetails.accountID); - }} - html={translate('workflowsPayerPage.shareBankAccount.errorDescription', { - admin: selectedPayerDetails?.displayName ?? '', - owner: ownerDetails?.displayName ?? '', - })} - /> - - ), - }); + setShowErrorModal(true); return; } showConfirmModal({ @@ -350,6 +313,58 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR )} + { + setShowValidationModal(false); + }} + success + onCancel={() => setShowValidationModal(false)} + prompt={ + + { + setShowValidationModal(false); + navigateToBankAccountRoute({policyID, backTo: ROUTES.WORKSPACE_WORKFLOWS.getRoute(policyID)}); + }} + html={translate('workflowsPayerPage.shareBankAccount.validationDescription', { + admin: selectedPayerDetails?.displayName ?? '', + })} + /> + + } + shouldShowCancelButton={false} + confirmText={translate('common.buttonConfirm')} + /> + setShowErrorModal(false)} + onConfirm={() => { + setShowErrorModal(false); + }} + success + prompt={ + + { + if (!currentUserPersonalDetails?.accountID || !policy?.ownerAccountID) { + return; + } + setShowErrorModal(false); + navigateToAndOpenReportWithAccountIDs([policy.ownerAccountID], currentUserPersonalDetails.accountID); + }} + html={translate('workflowsPayerPage.shareBankAccount.errorDescription', { + admin: selectedPayerDetails?.displayName ?? '', + owner: ownerDetails?.displayName ?? '', + })} + /> + + } + shouldShowCancelButton={false} + confirmText={translate('common.buttonConfirm')} + /> ); } From 68b539902bc8e4d7e6f92f14f51e3686ab5f7742 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 19 Feb 2026 10:22:05 +0200 Subject: [PATCH 30/44] rollback close modal --- src/components/Modal/Global/ModalContext.tsx | 2 +- src/hooks/useConfirmModal.ts | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/components/Modal/Global/ModalContext.tsx b/src/components/Modal/Global/ModalContext.tsx index 856e54a36f5c..28f3d10feb38 100644 --- a/src/components/Modal/Global/ModalContext.tsx +++ b/src/components/Modal/Global/ModalContext.tsx @@ -106,5 +106,5 @@ function ModalProvider({children}: {children: React.ReactNode}) { ); } -export type {ModalProps, ModalStateChangePayload}; +export type {ModalProps}; export {ModalProvider, useModal, ModalActions}; diff --git a/src/hooks/useConfirmModal.ts b/src/hooks/useConfirmModal.ts index 8f7f6e26abba..25b2c827c2de 100644 --- a/src/hooks/useConfirmModal.ts +++ b/src/hooks/useConfirmModal.ts @@ -1,5 +1,5 @@ import ConfirmModalWrapper from '@components/Modal/Global/ConfirmModalWrapper'; -import type {ModalProps, ModalStateChangePayload} from '@components/Modal/Global/ModalContext'; +import type {ModalProps} from '@components/Modal/Global/ModalContext'; import {useModal} from '@components/Modal/Global/ModalContext'; type ConfirmModalOptions = Omit, keyof ModalProps>; @@ -17,13 +17,8 @@ const useConfirmModal = () => { }); }; - const closeConfirmModal = (data?: ModalStateChangePayload) => { - context.closeModal(data); - }; - return { ...context, - closeModal: closeConfirmModal, showConfirmModal, }; }; From 6fbdadd2e345307e4f766ba9757de84691d5b9c7 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Fri, 20 Feb 2026 17:33:58 +0200 Subject: [PATCH 31/44] use new API to share bank and update payer --- .../ShareBankAccountAndSetPayerParams.ts | 7 +++ src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 2 + src/libs/actions/BankAccounts.ts | 47 +++++++++++++++++++ .../workflows/WorkspaceWorkflowsPayerPage.tsx | 11 ++--- .../WorkspaceWorkflowsPayerSuccessPage.tsx | 17 +------ 6 files changed, 62 insertions(+), 23 deletions(-) create mode 100644 src/libs/API/parameters/ShareBankAccountAndSetPayerParams.ts diff --git a/src/libs/API/parameters/ShareBankAccountAndSetPayerParams.ts b/src/libs/API/parameters/ShareBankAccountAndSetPayerParams.ts new file mode 100644 index 000000000000..15f5e1689473 --- /dev/null +++ b/src/libs/API/parameters/ShareBankAccountAndSetPayerParams.ts @@ -0,0 +1,7 @@ +type ShareBankAccountAndSetPayerParams = { + bankAccountID: number; + shareeAccountID: number; + policyID: string; +}; + +export default ShareBankAccountAndSetPayerParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index e3f8f62f3b24..646ad01ada11 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -151,6 +151,7 @@ export type {default as AcceptWalletTermsParams} from './AcceptWalletTermsParams export type {default as ChronosRemoveOOOEventParams} from './ChronosRemoveOOOEventParams'; export type {default as TransferWalletBalanceParams} from './TransferWalletBalanceParams'; export type {default as DeleteWorkspaceParams} from './DeleteWorkspaceParams'; +export type {default as ShareBankAccountAndSetPayerParams} from './ShareBankAccountAndSetPayerParams'; export type {default as CreateWorkspaceParams} from './CreateWorkspaceParams'; export type {default as UpdateWorkspaceGeneralSettingsParams} from './UpdateWorkspaceGeneralSettingsParams'; export type {default as DeleteWorkspaceAvatarParams} from './DeleteWorkspaceAvatarParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 97dfdee78c76..5a600e1cc80b 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -513,6 +513,7 @@ const WRITE_COMMANDS = { GET_CORPAY_BANK_ACCOUNT_FIELDS: 'GetCorpayBankAccountFields', BANK_ACCOUNT_CREATE_CORPAY: 'BankAccount_CreateCorpay', SHARE_BANK_ACCOUNT: 'ShareBankAccount', + SHARE_BANK_ACCOUNT_AND_UPDATE_POLICY_REIMBURSER: 'ShareBankAccountAndUpdatePolicyReimburser', UPDATE_WORKSPACE_CUSTOM_UNIT: 'UpdateWorkspaceCustomUnit', GET_ACCESSIBLE_POLICIES: 'GetAccessibleDomainPoliciesForOnyx', VALIDATE_USER_AND_GET_ACCESSIBLE_POLICIES: 'ValidateUserAndGetAccessiblePolicies', @@ -735,6 +736,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.RENAME_POLICY_TAG]: Parameters.RenamePolicyTagsParams; [WRITE_COMMANDS.UPDATE_POLICY_TAG_GL_CODE]: Parameters.UpdatePolicyTagGLCodeParams; [WRITE_COMMANDS.SHARE_BANK_ACCOUNT]: Parameters.ShareBankAccountParams; + [WRITE_COMMANDS.SHARE_BANK_ACCOUNT_AND_UPDATE_POLICY_REIMBURSER]: Parameters.ShareBankAccountAndSetPayerParams; [WRITE_COMMANDS.SET_POLICY_TAGS_ENABLED]: Parameters.SetPolicyTagsEnabled; [WRITE_COMMANDS.DELETE_POLICY_TAGS]: Parameters.DeletePolicyTagsParams; [WRITE_COMMANDS.CREATE_TASK]: Parameters.CreateTaskParams; diff --git a/src/libs/actions/BankAccounts.ts b/src/libs/actions/BankAccounts.ts index 459de1a27c5f..0ea6f0f70b41 100644 --- a/src/libs/actions/BankAccounts.ts +++ b/src/libs/actions/BankAccounts.ts @@ -14,6 +14,7 @@ import type { OpenReimbursementAccountPageParams, SaveCorpayOnboardingBeneficialOwnerParams, SendReminderForCorpaySignerInformationParams, + ShareBankAccountAndSetPayerParams, ShareBankAccountParams, UnshareBankAccountParams, ValidateBankAccountWithTransactionsParams, @@ -1413,6 +1414,51 @@ function shareBankAccount(bankAccountID: number, emailList: string[]) { API.write(WRITE_COMMANDS.SHARE_BANK_ACCOUNT, parameters, onyxData); } +function shareBankAccountAndSetPayer(bankAccountID: number, shareeAccountID: number, policyID: string) { + const parameters: ShareBankAccountAndSetPayerParams = { + bankAccountID, + shareeAccountID, + policyID, + }; + + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.SHARE_BANK_ACCOUNT, + value: { + isLoading: true, + errors: null, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.SHARE_BANK_ACCOUNT, + value: { + isLoading: false, + errors: null, + admins: null, + shouldShowSuccess: true, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.SHARE_BANK_ACCOUNT, + value: { + isLoading: false, + errors: getMicroSecondOnyxErrorWithTranslationKey('walletPage.shareBankAccountFailure'), + }, + }, + ], + }; + + API.write(WRITE_COMMANDS.SHARE_BANK_ACCOUNT_AND_UPDATE_POLICY_REIMBURSER, parameters, onyxData); +} + /** * Get bank account from bankAccountID */ @@ -1498,6 +1544,7 @@ export { clearReimbursementAccountSaveCorpayOnboardingDirectorInformation, clearCorpayBankAccountFields, finishCorpayBankAccountOnboarding, + shareBankAccountAndSetPayer, clearReimbursementAccountFinishCorpayBankAccountOnboarding, enableGlobalReimbursementsForUSDBankAccount, clearEnableGlobalReimbursementsForUSDBankAccount, diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 42db0c1f8f0a..4758f8ee342f 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -32,7 +32,7 @@ import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; -import {clearShareBankAccountErrors, shareBankAccount} from '@userActions/BankAccounts'; +import {clearShareBankAccountErrors, shareBankAccountAndSetPayer} from '@userActions/BankAccounts'; import {setWorkspacePayer} from '@userActions/Policy/Policy'; import {navigateToBankAccountRoute} from '@userActions/ReimbursementAccount'; import {navigateToAndOpenReportWithAccountIDs} from '@userActions/Report'; @@ -166,7 +166,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const headerMessage = searchTerm && !sections.at(0)?.data.length ? translate('common.noResultsFound') : ''; const handleConfirm = () => { - if (!bankAccountID || !authorizedPayerEmail) { + if (!bankAccountID || !authorizedPayerEmail || !accountID || !policyID) { return; } if (!selectedPayer) { @@ -177,7 +177,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR Navigation.goBack(); return; } - shareBankAccount(Number(bankAccountID), [authorizedPayerEmail]); + shareBankAccountAndSetPayer(Number(bankAccountID), accountID, policyID); }; const onButtonPress = () => { @@ -271,10 +271,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR onBackButtonPress={Navigation.goBack} /> {shouldShowSuccess && selectedPayer ? ( - + ) : ( { - if (!policyID) { - return; - } - setWorkspacePayer(policyID, selectedPayer); - }, [policyID, selectedPayer]); - useEffect(() => { return () => { if (!shouldShowSuccess) { From a329d08fddf1908084f7572f03f90bd90eadbd07 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Fri, 20 Feb 2026 17:47:50 +0200 Subject: [PATCH 32/44] add word to spell config --- cspell.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cspell.json b/cspell.json index 50255feec7a6..7f14943b0c19 100644 --- a/cspell.json +++ b/cspell.json @@ -101,7 +101,9 @@ "Bronn", "BROWSABLE", "buildscript", + "sharee", "shareeEmail", + "sharees", "Buildscript", "Bushwick", "BYOC", From c9fd510b58b40dd7f4578cf7afa95a96528cd383 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Tue, 24 Feb 2026 13:55:21 +0200 Subject: [PATCH 33/44] fix checks --- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 4 ++-- .../workflows/WorkspaceWorkflowsPayerSuccessPage.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index faa86f39517e..ddaae178e8f6 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -60,7 +60,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const {translate, formatPhoneNumber} = useLocalize(); const policyName = policy?.name ?? ''; const policyID = policy?.id; - const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST, {canBeMissing: true}); + const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const bankAccountConnectedToWorkspace = Object.values(bankAccountList ?? {}).find((account) => account?.accountData?.additionalData?.policyID === policyID); const policyBankAccountID = policy?.achAccount?.bankAccountID; const bankAccountFromList = policyBankAccountID ? bankAccountList?.[policyBankAccountID] : undefined; @@ -71,7 +71,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); const icons = useMemoizedLazyExpensifyIcons(['FallbackAvatar'] as const); const [searchTerm, setSearchTerm] = useState(''); - const [sharedBankAccountData] = useOnyx(ONYXKEYS.SHARE_BANK_ACCOUNT, {canBeMissing: true}); + const [sharedBankAccountData] = useOnyx(ONYXKEYS.SHARE_BANK_ACCOUNT); const [selectedPayer, setSelectedPayer] = useState(policy?.achAccount?.reimburser); const shouldShowSuccess = sharedBankAccountData?.shouldShowSuccess ?? false; const styles = useThemeStyles(); diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx index f6481ef4742a..341b33ad9db6 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerSuccessPage.tsx @@ -11,7 +11,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; function WorkspaceWorkflowsPayerSuccessPage() { const {translate} = useLocalize(); - const [sharedBankAccountData] = useOnyx(ONYXKEYS.SHARE_BANK_ACCOUNT, {canBeMissing: true}); + const [sharedBankAccountData] = useOnyx(ONYXKEYS.SHARE_BANK_ACCOUNT); const shouldShowSuccess = sharedBankAccountData?.shouldShowSuccess ?? false; const styles = useThemeStyles(); const illustrations = useMemoizedLazyIllustrations(['ShareBank', 'Telescope']); From 65c7478756b9d24c85f3d39b2e38e0b1d104c51b Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Fri, 27 Feb 2026 17:38:13 +0200 Subject: [PATCH 34/44] fix prettier --- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index ddaae178e8f6..683724e39ea5 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -3,9 +3,11 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import Badge from '@components/Badge'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import ConfirmModal from '@components/ConfirmModal'; import ErrorMessageRow from '@components/ErrorMessageRow'; import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {ModalActions} from '@components/Modal/Global/ModalContext'; import RenderHTML from '@components/RenderHTML'; import ScreenWrapper from '@components/ScreenWrapper'; import UserListItem from '@components/SelectionList/ListItem/UserListItem'; @@ -27,7 +29,6 @@ import {getSearchValueForPhoneOrEmail} from '@libs/OptionsListUtils'; import {getDisplayNameOrDefault, getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {getMemberAccountIDsForWorkspace, goBackFromInvalidPolicy, isExpensifyTeam, isPendingDeletePolicy} from '@libs/PolicyUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; -import {ModalActions} from '@components/Modal/Global/ModalContext'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; @@ -42,7 +43,6 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetailsList, PolicyEmployee} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import ConfirmModal from '@components/ConfirmModal'; import WorkspaceWorkflowsPayerSuccessPage from './WorkspaceWorkflowsPayerSuccessPage'; type WorkspaceWorkflowsPayerPageOnyxProps = { From 89e8ca062f957a783f09006e5c1e8663e2e3dae1 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 5 Mar 2026 15:35:36 +0200 Subject: [PATCH 35/44] change condition --- src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index e9ffc40bf3ea..404b8e1b293b 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -458,7 +458,7 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) { brickRoadIndicator={hasReimburserError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} /> )} - {shouldShowBankAccount && !isAccountInSetupState && ( + {shouldShowBankAccount && ( Date: Wed, 11 Mar 2026 14:02:43 +0200 Subject: [PATCH 36/44] revert condition --- src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index 404b8e1b293b..e9ffc40bf3ea 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -458,7 +458,7 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) { brickRoadIndicator={hasReimburserError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} /> )} - {shouldShowBankAccount && ( + {shouldShowBankAccount && !isAccountInSetupState && ( Date: Wed, 11 Mar 2026 15:37:54 +0200 Subject: [PATCH 37/44] revert condition fix overlay --- src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx | 2 +- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index e9ffc40bf3ea..404b8e1b293b 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -458,7 +458,7 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) { brickRoadIndicator={hasReimburserError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} /> )} - {shouldShowBankAccount && !isAccountInSetupState && ( + {shouldShowBankAccount && ( Date: Wed, 11 Mar 2026 16:32:26 +0200 Subject: [PATCH 38/44] fix ts --- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index ae5daea42c07..a7af53437bbf 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -66,6 +66,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const bankAccountFromList = policyBankAccountID ? bankAccountList?.[policyBankAccountID] : undefined; const bankAccountInfo = bankAccountFromList ?? bankAccountConnectedToWorkspace; const bankAccountID = policyBankAccountID ?? bankAccountInfo?.accountData?.bankAccountID; + const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const {isOffline} = useNetwork(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE); @@ -351,7 +352,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR return; } setShowErrorModal(false); - navigateToAndOpenReportWithAccountIDs([policy.ownerAccountID], currentUserPersonalDetails.accountID); + navigateToAndOpenReportWithAccountIDs([policy.ownerAccountID], currentUserPersonalDetails.accountID, introSelected); }} html={translate('workflowsPayerPage.shareBankAccount.errorDescription', { admin: selectedPayerDetails?.displayName ?? '', From f573ef9b889061a303ae7c279b0427037146a8f3 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 12 Mar 2026 10:03:01 +0200 Subject: [PATCH 39/44] use flag to get bank account in setup mode --- src/libs/API/parameters/OpenPolicyWorkflowsPageParams.ts | 1 + src/libs/actions/Policy/Policy.ts | 4 ++-- src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libs/API/parameters/OpenPolicyWorkflowsPageParams.ts b/src/libs/API/parameters/OpenPolicyWorkflowsPageParams.ts index eea0788b3927..20349d0336b8 100644 --- a/src/libs/API/parameters/OpenPolicyWorkflowsPageParams.ts +++ b/src/libs/API/parameters/OpenPolicyWorkflowsPageParams.ts @@ -1,5 +1,6 @@ type OpenPolicyWorkflowsPageParams = { policyID: string; + includeAllBankAccounts?: boolean; }; export default OpenPolicyWorkflowsPageParams; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index f1ea4d0a6d80..1616b03071d8 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -3398,7 +3398,7 @@ function duplicateWorkspace(policy: Policy, options: DuplicatePolicyDataOptions) return params; } -function openPolicyWorkflowsPage(policyID: string) { +function openPolicyWorkflowsPage(policyID: string, includeAllBankAccounts?: boolean) { if (!policyID) { Log.warn('openPolicyWorkflowsPage invalid params', {policyID}); return; @@ -3434,7 +3434,7 @@ function openPolicyWorkflowsPage(policyID: string) { ], }; - const params: OpenPolicyWorkflowsPageParams = {policyID}; + const params: OpenPolicyWorkflowsPageParams = {policyID, includeAllBankAccounts}; API.read(READ_COMMANDS.OPEN_POLICY_WORKFLOWS_PAGE, params, onyxData); } diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index 404b8e1b293b..b7838014b51a 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -119,7 +119,7 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) { const onPressAutoReportingFrequency = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_WORKFLOWS_AUTOREPORTING_FREQUENCY.getRoute(route.params.policyID)), [route.params.policyID]); const fetchData = useCallback(() => { - openPolicyWorkflowsPage(route.params.policyID); + openPolicyWorkflowsPage(route.params.policyID, true); getPaymentMethods(true); }, [route.params.policyID]); From c7a55bddbab2004b958f49e0077f94241e17e6d5 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Tue, 17 Mar 2026 16:10:42 +0200 Subject: [PATCH 40/44] add unit tests and small comments --- src/languages/en.ts | 6 +- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 10 +- .../ShareBankAccountAndWorkspacePayerTest.ts | 132 ++++++++++++++++++ 3 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 tests/actions/ShareBankAccountAndWorkspacePayerTest.ts diff --git a/src/languages/en.ts b/src/languages/en.ts index 284ceedbf147..4310a3367f91 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2505,10 +2505,10 @@ const translations = { shareDescription: ({admin}: {admin: string}) => `You'll need to share bank account access with ${admin} to make them the payer.`, validationTitle: 'Bank account awaiting validation', validationDescription: ({admin}: {admin: string}) => - `You need to validate this bank account. Once that’s done, you can share bank account access with ${admin} to make them the payer.`, - errorTitle: 'Can’t change payer', + `You need to validate this bank account. Once that's done, you can share bank account access with ${admin} to make them the payer.`, + errorTitle: "Can't change payer", errorDescription: ({admin, owner}: {admin: string; owner: string}) => - `${admin} doesn’t have access to this bank account, so you can’t make them the payer. Chat with ${owner} if the bank account should be shared.`, + `${admin} doesn't have access to this bank account, so you can't make them the payer. Chat with ${owner} if the bank account should be shared.`, }, }, reportFraudPage: { diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index a7af53437bbf..9fbee9aa6a07 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -191,6 +191,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR }; const handleShareBankAccount = () => { + // No payer selected — nothing to share with if (!selectedPayer) { return; } @@ -198,10 +199,12 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const isSelectedAlreadyAPayer = policy?.achAccount?.reimburser === selectedPayer; const isAccountAlreadyShared = bankAccountInfo?.accountData?.sharees ? bankAccountInfo?.accountData.sharees.includes(selectedPayer) : false; const isAccountAlreadySharedOnMainBankAccount = policy?.achAccount?.sharees ? policy?.achAccount.sharees.includes(selectedPayer) : false; + // Selected payer already has access (owner, reimburser, or sharee) — proceed without sharing if (isAccountAlreadyShared || isSelectedPayerOwner || isSelectedAlreadyAPayer || isAccountAlreadySharedOnMainBankAccount) { onButtonPress(); return; } + // Bank account setup incomplete — block and show validation if (isBankAccountPartiallySetup(bankAccountInfo?.accountData?.state)) { setShowValidationModal(true); return; @@ -209,6 +212,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const isAccountAlreadySharedWithCurrentUser = bankAccountInfo?.accountData?.sharees && currentUserPersonalDetails?.login ? bankAccountInfo?.accountData?.sharees.includes(currentUserPersonalDetails?.login) : false; const isOwner = policy?.owner === currentUserPersonalDetails?.login; + // Current user has no right to share (not owner and not a sharee) — show error if (!isOwner && !isAccountAlreadyShared && !isAccountAlreadySharedWithCurrentUser) { setShowErrorModal(true); return; @@ -227,6 +231,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR ), }).then((result) => { + // User dismissed or cancelled the confirm modal — do not share if (result.action !== ModalActions.CONFIRM) { return; } @@ -234,10 +239,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR }); }; - const setPolicyAuthorizedPayer = (member: MemberOption) => { - const login = personalDetails?.[member.accountID]?.login; - setSelectedPayer(login); - }; + const setPolicyAuthorizedPayer = (member: MemberOption) => setSelectedPayer(personalDetails?.[member.accountID]?.login); const shouldShowBlockingPage = (isEmptyObject(policy) && !isLoadingReportData) || isPendingDeletePolicy(policy) || policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES; diff --git a/tests/actions/ShareBankAccountAndWorkspacePayerTest.ts b/tests/actions/ShareBankAccountAndWorkspacePayerTest.ts new file mode 100644 index 000000000000..e9f5a939ab3e --- /dev/null +++ b/tests/actions/ShareBankAccountAndWorkspacePayerTest.ts @@ -0,0 +1,132 @@ +import Onyx from 'react-native-onyx'; +import * as BankAccounts from '@libs/actions/BankAccounts'; +import * as Policy from '@libs/actions/Policy/Policy'; +import * as API from '@libs/API'; +import {WRITE_COMMANDS} from '@libs/API/types'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; + +jest.mock('@libs/API'); +const mockAPI = API as jest.Mocked; + +describe('actions/ShareBankAccountAndWorkspacePayer', () => { + beforeAll(() => { + Onyx.init({ + keys: ONYXKEYS, + }); + }); + + beforeEach(() => { + jest.clearAllMocks(); + return Onyx.clear().then(waitForBatchedUpdates); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + describe('shareBankAccountAndSetPayer', () => { + it('should call API.write with ShareBankAccountAndUpdatePolicyReimburser command and correct parameters', async () => { + const bankAccountID = 123; + const shareeAccountID = 456; + const policyID = 'policy_789'; + + BankAccounts.shareBankAccountAndSetPayer(bankAccountID, shareeAccountID, policyID); + await waitForBatchedUpdates(); + + expect(mockAPI.write).toHaveBeenCalledWith( + WRITE_COMMANDS.SHARE_BANK_ACCOUNT_AND_UPDATE_POLICY_REIMBURSER, + { + bankAccountID, + shareeAccountID, + policyID, + }, + expect.objectContaining({ + optimisticData: expect.arrayContaining([ + expect.objectContaining({ + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.SHARE_BANK_ACCOUNT, + value: expect.objectContaining({ + isLoading: true, + errors: null, + }), + }), + ]), + successData: expect.arrayContaining([ + expect.objectContaining({ + key: ONYXKEYS.SHARE_BANK_ACCOUNT, + value: expect.objectContaining({ + isLoading: false, + errors: null, + admins: null, + shouldShowSuccess: true, + }), + }), + ]), + failureData: expect.arrayContaining([ + expect.objectContaining({ + key: ONYXKEYS.SHARE_BANK_ACCOUNT, + value: expect.objectContaining({ + isLoading: false, + }), + }), + ]), + }), + ); + }); + }); + + describe('setWorkspacePayer', () => { + it('should call API.write with SetWorkspacePayer command and correct parameters', async () => { + const policyID = 'policy_abc'; + const reimburserEmail = 'payer@example.com'; + + Policy.setWorkspacePayer(policyID, reimburserEmail); + await waitForBatchedUpdates(); + + expect(mockAPI.write).toHaveBeenCalledWith( + WRITE_COMMANDS.SET_WORKSPACE_PAYER, + { + policyID, + reimburserEmail, + }, + expect.objectContaining({ + optimisticData: expect.arrayContaining([ + expect.objectContaining({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: expect.objectContaining({ + reimburser: reimburserEmail, + achAccount: {reimburser: reimburserEmail}, + errorFields: {reimburser: null}, + pendingFields: {reimburser: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + }), + }), + ]), + successData: expect.arrayContaining([ + expect.objectContaining({ + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: expect.objectContaining({ + errorFields: {reimburser: null}, + pendingFields: {reimburser: null}, + }), + }), + ]), + failureData: expect.arrayContaining([ + expect.objectContaining({ + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: expect.objectContaining({ + // Error object shape from ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey + errorFields: expect.objectContaining({ + reimburser: expect.anything(), // eslint-disable-line @typescript-eslint/no-unsafe-assignment -- Jest matcher + }), + pendingFields: {reimburser: null}, + }), + }), + ]), + }), + ); + }); + }); +}); From 0388ceaea1011c19a2c2991f5aba410f0828c5c9 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Tue, 17 Mar 2026 16:27:00 +0200 Subject: [PATCH 41/44] fix eslint --- .../ShareBankAccountAndWorkspacePayerTest.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/actions/ShareBankAccountAndWorkspacePayerTest.ts b/tests/actions/ShareBankAccountAndWorkspacePayerTest.ts index e9f5a939ab3e..b1e72cd8c44c 100644 --- a/tests/actions/ShareBankAccountAndWorkspacePayerTest.ts +++ b/tests/actions/ShareBankAccountAndWorkspacePayerTest.ts @@ -1,14 +1,15 @@ import Onyx from 'react-native-onyx'; -import * as BankAccounts from '@libs/actions/BankAccounts'; -import * as Policy from '@libs/actions/Policy/Policy'; -import * as API from '@libs/API'; +import {shareBankAccountAndSetPayer} from '@libs/actions/BankAccounts'; +import {setWorkspacePayer} from '@libs/actions/Policy/Policy'; +import {write} from '@libs/API'; import {WRITE_COMMANDS} from '@libs/API/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; -jest.mock('@libs/API'); -const mockAPI = API as jest.Mocked; +jest.mock('@libs/API', () => ({ + write: jest.fn(), +})); describe('actions/ShareBankAccountAndWorkspacePayer', () => { beforeAll(() => { @@ -32,10 +33,10 @@ describe('actions/ShareBankAccountAndWorkspacePayer', () => { const shareeAccountID = 456; const policyID = 'policy_789'; - BankAccounts.shareBankAccountAndSetPayer(bankAccountID, shareeAccountID, policyID); + shareBankAccountAndSetPayer(bankAccountID, shareeAccountID, policyID); await waitForBatchedUpdates(); - expect(mockAPI.write).toHaveBeenCalledWith( + expect(write).toHaveBeenCalledWith( WRITE_COMMANDS.SHARE_BANK_ACCOUNT_AND_UPDATE_POLICY_REIMBURSER, { bankAccountID, @@ -82,10 +83,10 @@ describe('actions/ShareBankAccountAndWorkspacePayer', () => { const policyID = 'policy_abc'; const reimburserEmail = 'payer@example.com'; - Policy.setWorkspacePayer(policyID, reimburserEmail); + setWorkspacePayer(policyID, reimburserEmail); await waitForBatchedUpdates(); - expect(mockAPI.write).toHaveBeenCalledWith( + expect(write).toHaveBeenCalledWith( WRITE_COMMANDS.SET_WORKSPACE_PAYER, { policyID, From e35036c33923f1e8d6ac5ffb7717db9945a00741 Mon Sep 17 00:00:00 2001 From: narefyev91 Date: Tue, 17 Mar 2026 18:12:41 +0200 Subject: [PATCH 42/44] Update src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx Co-authored-by: Nikki Wines --- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 9fbee9aa6a07..913148beff63 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -199,6 +199,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const isSelectedAlreadyAPayer = policy?.achAccount?.reimburser === selectedPayer; const isAccountAlreadyShared = bankAccountInfo?.accountData?.sharees ? bankAccountInfo?.accountData.sharees.includes(selectedPayer) : false; const isAccountAlreadySharedOnMainBankAccount = policy?.achAccount?.sharees ? policy?.achAccount.sharees.includes(selectedPayer) : false; + // Selected payer already has access (owner, reimburser, or sharee) — proceed without sharing if (isAccountAlreadyShared || isSelectedPayerOwner || isSelectedAlreadyAPayer || isAccountAlreadySharedOnMainBankAccount) { onButtonPress(); From 6b0c1e9c5602a6c0c0da993e1f7321b385444e66 Mon Sep 17 00:00:00 2001 From: narefyev91 Date: Tue, 17 Mar 2026 18:12:54 +0200 Subject: [PATCH 43/44] Update src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx Co-authored-by: Nikki Wines --- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 913148beff63..eeccd2144938 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -205,6 +205,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR onButtonPress(); return; } + // Bank account setup incomplete — block and show validation if (isBankAccountPartiallySetup(bankAccountInfo?.accountData?.state)) { setShowValidationModal(true); From fc59fc889c4b50684b21c95a1c27b44194b13bc4 Mon Sep 17 00:00:00 2001 From: narefyev91 Date: Tue, 17 Mar 2026 18:13:03 +0200 Subject: [PATCH 44/44] Update src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx Co-authored-by: Nikki Wines --- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index eeccd2144938..4e73c62a9f47 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -214,6 +214,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR const isAccountAlreadySharedWithCurrentUser = bankAccountInfo?.accountData?.sharees && currentUserPersonalDetails?.login ? bankAccountInfo?.accountData?.sharees.includes(currentUserPersonalDetails?.login) : false; const isOwner = policy?.owner === currentUserPersonalDetails?.login; + // Current user has no right to share (not owner and not a sharee) — show error if (!isOwner && !isAccountAlreadyShared && !isAccountAlreadySharedWithCurrentUser) { setShowErrorModal(true);