Skip to content

Commit b26837b

Browse files
authored
Merge branch 'Expensify:main' into issue/63704
2 parents ac4b1a1 + 46278af commit b26837b

14 files changed

Lines changed: 151 additions & 36 deletions

File tree

docs/articles/expensify-classic/settings/Locked-Account-Tool.md renamed to docs/articles/expensify-classic/settings/Report-Suspicious-Activity.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
title: Locked Account Tool
3-
description: Understand how the locked account tool works in Expensify Classic, what features it restricts, and how to request an unlock.
2+
title: Report Suspicious Activity
3+
description: Learn how to report suspicious activity and lock your account in Expensify Classic, what features it restricts, and how to request an unlock.
44
keywords: [locked account, Expensify Classic, domain admin, secondary login, reimbursements blocked, card locked, unlock account]
55
---
66
<div id="expensify-classic" markdown="1">
@@ -11,7 +11,7 @@ The locked account tool prevents high-risk actions from being taken on accounts
1111

1212
## When is it appropriate to click the button for your own account or for your employee’s account?
1313

14-
Use the **Lock Account** button if:
14+
Use the **Report Suspicious Activity** button if:
1515
- You suspect unauthorized activity on your own or an employee's account.
1616
- There are changes to bank accounts, logins, or suspicious payment activity.
1717
- You want to pause activity on an account until Concierge investigates.
@@ -22,6 +22,11 @@ Use the **Lock Account** button if:
2222

2323
The following actions are restricted when an account is locked in Expensify Classic:
2424

25+
## Admin Functionality
26+
27+
- If you are the owner of a reimbursement account in Expensify, all ability to process reimbursements for anyone with access to the bank account will be locked.
28+
- If you are a Domain Admin and have Expensify Cards, all Expensify Cards for the domain or workspace will be locked and all card activity will be suspended.
29+
2530
### Profile settings
2631
- Adding a secondary login
2732
- Adding or removing Two Factor Authentication (2FA)
@@ -60,9 +65,9 @@ The following actions are restricted when an account is locked in Expensify Clas
6065
## How to do it?
6166

6267

63-
- **Members:** The **Lock Account** button appears at the bottom of the screen in `Settings > Account > Profile`.
68+
- **Members:** The **Report Suspicious Activity** button appears at the bottom of the screen in `Settings > Account > Profile`.
6469

65-
- **Domain Admins:** You can lock a domain member's account using the **Lock Account** option located in `Settings > Domains > Domain Members > Edit Settings`.
70+
- **Domain Admins:** You can lock a domain member's account using the **Report Suspicious Activity** option located in `Settings > Domains > Domain Members > Edit Settings`.
6671

6772
---
6873

docs/articles/new-expensify/settings/Lock-Account-Tool.md renamed to docs/articles/new-expensify/settings/Report-Suspicious-Activity.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
---
2-
title: Lock Account Tool
3-
description: Learn how the locked account tool works in New Expensify, when to use it, what features are blocked, and how users can regain access.
4-
keywords: [locked account, New Expensify, lock features, wallet disabled, card suspended, 2FA blocked, unlock account]
2+
title: Report Suspicious Activity
3+
description: Learn how to report suspicious activity and lock your account in New Expensify, when to use it, what features are blocked, and how users can regain access.
4+
keywords: [locked account, New Expensify, lock features, wallet disabled, card suspended, 2FA blocked, unlock account, suspicious activity]
55
---
66
<div id="new-expensify" markdown="1">
77

88
The Lock Account tool blocks access to high-risk features when an account is suspected to be compromised. It protects sensitive data by preventing unauthorized actions and triggering internal review by the Expensify team.
99

1010
---
1111

12-
# When to Use the Lock Account Button
12+
# When to Use the Report Suspicious Activity Button
1313

14-
Click the **Lock Account** button if:
14+
Click the **Report Suspicious Activity** button if:
1515

1616
- You believe your own account has been compromised (e.g., suspicious logins, changes to contact info, or unfamiliar devices).
1717
- You’re a Workspace Admin and suspect an employee’s account is at risk.
@@ -25,6 +25,11 @@ Locking the account restricts access to sensitive features and alerts Concierge
2525

2626
The following actions will be blocked when an account is locked:
2727

28+
## Admin Functionality
29+
30+
- If you are the owner of a reimbursement account in Expensify, all ability to process reimbursements for anyone with access to the bank account will be locked.
31+
- If you are a Domain Admin and have Expensify Cards, all Expensify Cards for the domain or workspace will be locked and all card activity will be suspended.
32+
2833
## Contact & Security
2934
- Adding or removing a contact method
3035
- Changing the default contact method
@@ -61,15 +66,15 @@ The following actions will be blocked when an account is locked:
6166
To lock your own account or an employee’s account:
6267

6368
1. Go to **Account > Security**.
64-
2. Click **Lock Account**.
69+
2. Click **Report Suspicious Activity**.
6570

6671
⚠️ **Important:** This action is not reversible from within the product. Once an account is locked, only Expensify Support can unlock it.
6772

6873
---
6974

7075
# How to Unlock an Account
7176

72-
Unlocking a locked account requires assistance from Expensify Support. Once you click **Lock Account**, Concierge will begin reviewing the account and contact you with the next steps. You may be asked to:
77+
Unlocking a locked account requires assistance from Expensify Support. Once you click **Report Suspicious Activity**, Concierge will begin reviewing the account and contact you with the next steps. You may be asked to:
7378

7479
- Verify identity
7580
- Reset login credentials

docs/redirects.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,5 +866,7 @@ https://help.expensify.com/articles/new-expensify/reports-and-expenses/Duplicate
866866
https://help.expensify.com/articles/new-expensify/expenses-&-payments/Duplicate-detection.html,https://help.expensify.com/articles/new-expensify/reports-and-expenses/Duplicate-Detection
867867
https://help.expensify.com/articles/new-expensify/reports-and-expenses/Export-download-expenses,https://help.expensify.com/articles/new-expensify/reports-and-expenses/Search-and-Download-Expenses
868868
https://help.expensify.com/articles/new-expensify/expenses-and-payments/Send-an-invoice.html,https://help.expensify.com/articles/new-expensify/reports-and-expenses/Send-an-Invoice
869+
https://help.expensify.com/articles/expensify-classic/settings/Locked-Account-Tool,https://help.expensify.com/articles/expensify-classic/settings/Report-Suspicious-Activity
870+
https://help.expensify.com/articles/new-expensify/settings/Lock-Account-Tool,https://help.expensify.com/articles/new-expensify/settings/Report-Suspicious-Activity
869871
https://help.expensify.com/articles/new-expensify/reports-and-expenses/Split-Expenses,https://help.expensify.com/articles/new-expensify/reports-and-expenses/Split-Personal-Expenses
870872
https://help.expensify.com/articles/new-expensify/reports-and-expenses/Suggested-Search,https://help.expensify.com/articles/new-expensify/reports-and-expenses/Using-Search-on-the-Reports-Page

src/components/ImportSpreadsheet.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import useOnyx from '@hooks/useOnyx';
88
import useResponsiveLayout from '@hooks/useResponsiveLayout';
99
import useThemeStyles from '@hooks/useThemeStyles';
1010
import {setSpreadsheetData} from '@libs/actions/ImportSpreadsheet';
11+
import {setImportedSpreadsheetIsImportingMultiLevelTags} from '@libs/actions/Policy/Tag';
1112
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
1213
import {splitExtensionFromFileName} from '@libs/fileDownload/FileUtils';
1314
import Navigation from '@libs/Navigation/Navigation';
@@ -155,7 +156,9 @@ function ImportSpreadsheet({backTo, goTo}: ImportSpreadsheetProps) {
155156
{...panResponder.panHandlers}
156157
>
157158
<Text style={[styles.textFileUpload, styles.mb1]}>{spreadsheet?.isImportingMultiLevelTags ? translate('spreadsheet.import') : translate('spreadsheet.upload')}</Text>
158-
<RenderHTML html={getTextForImportModal()} />
159+
<Text style={[styles.subTextFileUpload, styles.textSupporting]}>
160+
<RenderHTML html={getTextForImportModal()} />
161+
</Text>
159162
</View>
160163
<FilePicker acceptableFileTypes={acceptableFileTypes}>
161164
{({openPicker}) => (
@@ -195,7 +198,12 @@ function ImportSpreadsheet({backTo, goTo}: ImportSpreadsheetProps) {
195198
<View style={[styles.flex1, safeAreaPaddingBottomStyle]}>
196199
<HeaderWithBackButton
197200
title={translate('spreadsheet.importSpreadsheet')}
198-
onBackButtonPress={() => Navigation.goBack(backTo)}
201+
onBackButtonPress={() => {
202+
if (spreadsheet?.isImportingMultiLevelTags) {
203+
setImportedSpreadsheetIsImportingMultiLevelTags(false);
204+
}
205+
Navigation.goBack(backTo);
206+
}}
199207
/>
200208

201209
<View style={[styles.flex1, styles.uploadFileView(isSmallScreenWidth)]}>

src/components/KYCWall/BaseKYCWall.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ function KYCWall({
109109
}
110110

111111
if (paymentMethod === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) {
112-
openPersonalBankAccountSetupView();
112+
openPersonalBankAccountSetupView({shouldSetUpUSBankAccount: isIOUReport(iouReport)});
113113
} else if (paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD) {
114114
Navigation.navigate(addDebitCardRoute ?? ROUTES.HOME);
115115
} else if (paymentMethod === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT || policy) {

src/libs/PaymentUtils.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,18 @@ type TriggerKYCFlow = (event: KYCFlowEvent, iouPaymentType: PaymentMethodType) =
2626
type AccountType = ValueOf<typeof CONST.PAYMENT_METHODS> | undefined;
2727

2828
/**
29-
* Check to see if user has either a debit card or personal bank account added that can be used with a wallet.
29+
* Check to see if user has either a debit card or personal US bank account added that can be used with a wallet.
3030
*/
3131
function hasExpensifyPaymentMethod(fundList: Record<string, Fund>, bankAccountList: Record<string, BankAccount>, shouldIncludeDebitCard = true): boolean {
3232
const validBankAccount = Object.values(bankAccountList).some((bankAccountJSON) => {
3333
const bankAccount = new BankAccountModel(bankAccountJSON);
3434

35-
return bankAccount.getPendingAction() !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && bankAccount.isOpen() && bankAccount.getType() === CONST.BANK_ACCOUNT.TYPE.PERSONAL;
35+
return (
36+
bankAccount.getPendingAction() !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE &&
37+
bankAccount.isOpen() &&
38+
bankAccount.getType() === CONST.BANK_ACCOUNT.TYPE.PERSONAL &&
39+
bankAccount?.getCountry() === CONST.COUNTRY.US
40+
);
3641
});
3742

3843
// Hide any billing cards that are not P2P debit cards for now because you cannot make them your default method, or delete them

src/libs/actions/BankAccounts.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,23 @@ export {openOnfidoFlow, answerQuestionsForWallet, verifyIdentity, acceptWalletTe
5151

5252
type AccountFormValues = typeof ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT_FORM | typeof ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM;
5353

54+
type OpenPersonalBankAccountSetupViewProps = {
55+
/** The reportID of the report to redirect to once the flow is finished */
56+
exitReportID?: string;
57+
58+
/** The policyID of the policy to set the bank account on */
59+
policyID?: string;
60+
61+
/** The source of the bank account */
62+
source?: string;
63+
64+
/** Whether to set up a US bank account */
65+
shouldSetUpUSBankAccount?: boolean;
66+
67+
/** Whether the user is validated */
68+
isUserValidated?: boolean;
69+
};
70+
5471
function clearPlaid(): Promise<void | void[]> {
5572
Onyx.set(ONYXKEYS.PLAID_LINK_TOKEN, '');
5673
Onyx.set(ONYXKEYS.PLAID_CURRENT_EVENT, null);
@@ -74,7 +91,7 @@ function setPlaidEvent(eventName: string | null) {
7491
/**
7592
* Open the personal bank account setup flow, with an optional exitReportID to redirect to once the flow is finished.
7693
*/
77-
function openPersonalBankAccountSetupView(exitReportID?: string, policyID?: string, source?: string, isUserValidated = true) {
94+
function openPersonalBankAccountSetupView({exitReportID, policyID, source, shouldSetUpUSBankAccount = false, isUserValidated = true}: OpenPersonalBankAccountSetupViewProps) {
7895
clearInternationalBankAccount().then(() => {
7996
if (exitReportID) {
8097
Onyx.merge(ONYXKEYS.PERSONAL_BANK_ACCOUNT, {exitReportID});
@@ -89,6 +106,10 @@ function openPersonalBankAccountSetupView(exitReportID?: string, policyID?: stri
89106
Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHOD_VERIFY_ACCOUNT.getRoute(Navigation.getActiveRoute(), ROUTES.SETTINGS_ADD_BANK_ACCOUNT.route));
90107
return;
91108
}
109+
if (shouldSetUpUSBankAccount) {
110+
Navigation.navigate(ROUTES.SETTINGS_ADD_US_BANK_ACCOUNT);
111+
return;
112+
}
92113
Navigation.navigate(ROUTES.SETTINGS_ADD_BANK_ACCOUNT.getRoute(Navigation.getActiveRoute()));
93114
});
94115
}

src/libs/actions/IOU.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11225,6 +11225,18 @@ function updateLastLocationPermissionPrompt() {
1122511225
Onyx.set(ONYXKEYS.NVP_LAST_LOCATION_PERMISSION_PROMPT, new Date().toISOString());
1122611226
}
1122711227

11228+
function setMultipleMoneyRequestParticipantsFromReport(transactionIDs: string[], reportValue: OnyxEntry<OnyxTypes.Report>) {
11229+
const participants = getMoneyRequestParticipantsFromReport(reportValue);
11230+
const updatedTransactions: Record<`${typeof ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${string}`, NullishDeep<OnyxTypes.Transaction>> = {};
11231+
transactionIDs.forEach((transactionID) => {
11232+
updatedTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`] = {
11233+
participants,
11234+
participantsAutoAssigned: true,
11235+
};
11236+
});
11237+
return Onyx.mergeCollection(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, updatedTransactions);
11238+
}
11239+
1122811240
/** Instead of merging the duplicates, it updates the transaction we want to keep and puts the others on hold without deleting them */
1122911241
function resolveDuplicates(params: MergeDuplicatesParams) {
1123011242
if (!params.transactionID) {
@@ -11685,7 +11697,6 @@ function saveSplitTransactions(draftTransaction: OnyxEntry<OnyxTypes.Transaction
1168511697

1168611698
const originalTransactionID = draftTransaction?.comment?.originalTransactionID ?? CONST.IOU.OPTIMISTIC_TRANSACTION_ID;
1168711699
const originalTransaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`];
11688-
const originalTransactionViolations = allTransactionViolations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${originalTransactionID}`] ?? [];
1168911700
const iouActions = getIOUActionForTransactions([originalTransactionID], expenseReport?.reportID);
1169011701

1169111702
// This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850
@@ -11827,10 +11838,7 @@ function saveSplitTransactions(draftTransaction: OnyxEntry<OnyxTypes.Transaction
1182711838
key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
1182811839
value: {
1182911840
data: {
11830-
[`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`]: {
11831-
...originalTransactionViolations,
11832-
reportID: CONST.REPORT.SPLIT_REPORT_ID,
11833-
},
11841+
[`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`]: null,
1183411842
},
1183511843
},
1183611844
});
@@ -11840,7 +11848,7 @@ function saveSplitTransactions(draftTransaction: OnyxEntry<OnyxTypes.Transaction
1184011848
key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
1184111849
value: {
1184211850
data: {
11843-
[`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`]: originalTransactionViolations,
11851+
[`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`]: originalTransaction,
1184411852
},
1184511853
},
1184611854
});
@@ -11929,6 +11937,7 @@ export {
1192911937
setMoneyRequestParticipantsFromReport,
1193011938
getMoneyRequestParticipantsFromReport,
1193111939
setMoneyRequestPendingFields,
11940+
setMultipleMoneyRequestParticipantsFromReport,
1193211941
setMoneyRequestReceipt,
1193311942
setMoneyRequestTag,
1193411943
setMoneyRequestTaxAmount,

src/pages/home/report/PureReportActionItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -969,7 +969,7 @@ function PureReportActionItem({
969969
success
970970
style={[styles.w100, styles.requestPreviewBox]}
971971
text={translate('bankAccount.addBankAccount')}
972-
onPress={() => openPersonalBankAccountSetupView(Navigation.getTopmostReportId() ?? targetReport?.reportID, undefined, undefined, isUserValidated)}
972+
onPress={() => openPersonalBankAccountSetupView({exitReportID: Navigation.getTopmostReportId() ?? targetReport?.reportID, isUserValidated})}
973973
pressOnEnter
974974
large
975975
/>

src/pages/iou/request/step/IOURequestStepScan/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import {
6161
setMoneyRequestParticipants,
6262
setMoneyRequestParticipantsFromReport,
6363
setMoneyRequestReceipt,
64+
setMultipleMoneyRequestParticipantsFromReport,
6465
startSplitBill,
6566
trackExpense,
6667
updateLastLocationPermissionPrompt,
@@ -499,9 +500,8 @@ function IOURequestStepScan({
499500
createTransaction(files, participant);
500501
return;
501502
}
502-
503-
const setParticipantsPromises = files.map((receiptFile) => setMoneyRequestParticipantsFromReport(receiptFile.transactionID, report));
504-
Promise.all(setParticipantsPromises).then(() => navigateToConfirmationPage());
503+
const transactionIDs = files.map((receiptFile) => receiptFile.transactionID);
504+
setMultipleMoneyRequestParticipantsFromReport(transactionIDs, report).then(() => navigateToConfirmationPage());
505505
return;
506506
}
507507

0 commit comments

Comments
 (0)