Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/libs/actions/Policy/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@
betas?: Beta[];
};

const deprecatedAllPolicies: OnyxCollection<Policy> = {};

Check warning on line 254 in src/libs/actions/Policy/Policy.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

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

let deprecatedAllReportActions: OnyxCollection<ReportActions>;

Check warning on line 270 in src/libs/actions/Policy/Policy.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

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

let deprecatedSessionEmail = '';
let deprecatedSessionAccountID = 0;

Check warning on line 280 in src/libs/actions/Policy/Policy.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

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

let deprecatedAllPersonalDetails: OnyxEntry<PersonalDetailsList>;

Check warning on line 289 in src/libs/actions/Policy/Policy.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
Onyx.connect({
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (val) => (deprecatedAllPersonalDetails = val),
Expand Down Expand Up @@ -384,6 +384,7 @@
localeCompare: LocaleContextProps['localeCompare'];
hasDeleteWorkspaceExpensifyCardsError?: boolean;
currentUserAccountID: number;
accountIDToLogin: Record<number, string>;
};

/**
Expand All @@ -405,6 +406,7 @@
personalPolicyID,
hasDeleteWorkspaceExpensifyCardsError,
currentUserAccountID,
accountIDToLogin,
} = params;

const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];
Expand Down Expand Up @@ -547,7 +549,7 @@
// Announce & admin chats have FAKE owners, but expense chats w/ users do have owners.
let emailClosingReport: string = CONST.POLICY.OWNER_EMAIL_FAKE;
if (!!ownerAccountID && ownerAccountID !== CONST.POLICY.OWNER_ACCOUNT_ID_FAKE) {
emailClosingReport = deprecatedAllPersonalDetails?.[ownerAccountID]?.login ?? '';
emailClosingReport = accountIDToLogin[ownerAccountID] ?? '';
}
const optimisticClosedReportAction = ReportUtils.buildOptimisticClosedReportAction(emailClosingReport, policyName, currentUserAccountID, CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED);
optimisticData.push({
Expand Down
3 changes: 3 additions & 0 deletions src/pages/workspace/WorkspaceOverviewPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import {accountIDToLoginSelector} from '@src/selectors/PersonalDetails';
import {ownerPoliciesSelector} from '@src/selectors/Policy';
import {reimbursementAccountErrorSelector} from '@src/selectors/ReimbursementAccount';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
Expand Down Expand Up @@ -182,6 +183,7 @@ function WorkspaceOverviewPage({policyDraft, policy: policyProp, route}: Workspa
const [isLeaveModalOpen, setIsLeaveModalOpen] = useState(false);
const [session] = useOnyx(ONYXKEYS.SESSION);
const personalDetails = usePersonalDetails();
const [accountIDToLogin] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: accountIDToLoginSelector(reportsToArchive)});
const [isCannotLeaveWorkspaceModalOpen, setIsCannotLeaveWorkspaceModalOpen] = useState(false);
const privateSubscription = usePrivateSubscription();
const accountID = currentUserPersonalDetails?.accountID;
Expand Down Expand Up @@ -268,6 +270,7 @@ function WorkspaceOverviewPage({policyDraft, policy: policyProp, route}: Workspa
personalPolicyID,
hasDeleteWorkspaceExpensifyCardsError,
currentUserAccountID: accountID,
accountIDToLogin: accountIDToLogin ?? {},
});
if (isOffline) {
setIsDeleteModalOpen(false);
Expand Down
3 changes: 3 additions & 0 deletions src/pages/workspace/WorkspacesListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import {accountIDToLoginSelector} from '@src/selectors/PersonalDetails';
import {ownerPoliciesSelector} from '@src/selectors/Policy';
import {reimbursementAccountErrorSelector} from '@src/selectors/ReimbursementAccount';
import type {Policy as PolicyType} from '@src/types/onyx';
Expand Down Expand Up @@ -202,6 +203,7 @@ function WorkspacesListPage() {
policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyIDToDelete}`]?.workspaceAccountID);
const hasExpensifyCard = !!policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyIDToDelete}`]?.areExpensifyCardsEnabled && !isEmptyObject(cardsList);
const personalDetails = usePersonalDetails();
const [accountIDToLogin] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: accountIDToLoginSelector(reportsToArchive)});
const [isLeaveModalOpen, setIsLeaveModalOpen] = useState(false);
const [isCannotLeaveWorkspaceModalOpen, setIsCannotLeaveWorkspaceModalOpen] = useState(false);
const [policyIDToLeave, setPolicyIDToLeave] = useState<string>();
Expand Down Expand Up @@ -242,6 +244,7 @@ function WorkspacesListPage() {
personalPolicyID,
hasDeleteWorkspaceExpensifyCardsError,
currentUserAccountID: currentUserPersonalDetails.accountID,
accountIDToLogin: accountIDToLogin ?? {},
});
if (isOffline) {
setIsDeleteModalOpen(false);
Expand Down
16 changes: 14 additions & 2 deletions src/selectors/PersonalDetails.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import type {OnyxEntry} from 'react-native-onyx';
import type {PersonalDetailsList} from '@src/types/onyx';
import CONST from '@src/CONST';
import type {PersonalDetailsList, Report} from '@src/types/onyx';

const personalDetailsSelector = (accountID: number) => (personalDetailsList: OnyxEntry<PersonalDetailsList>) => personalDetailsList?.[accountID];

const personalDetailsLoginSelector = (accountID: number) => (personalDetailsList: OnyxEntry<PersonalDetailsList>) => personalDetailsList?.[accountID]?.login;

export {personalDetailsSelector, personalDetailsLoginSelector};
const accountIDToLoginSelector = (reportsToArchive: Report[]) => (personalDetailsList: OnyxEntry<PersonalDetailsList>) => {
const map: Record<number, string> = {};
for (const report of reportsToArchive) {
const {ownerAccountID} = report;
if (ownerAccountID && ownerAccountID !== CONST.POLICY.OWNER_ACCOUNT_ID_FAKE && personalDetailsList?.[ownerAccountID]?.login) {
map[ownerAccountID] = personalDetailsList[ownerAccountID].login;
}
}
return map;
};

export {personalDetailsSelector, personalDetailsLoginSelector, accountIDToLoginSelector};
1 change: 1 addition & 0 deletions tests/actions/IOUTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10132,6 +10132,7 @@ describe('actions/IOU', () => {
lastUsedPaymentMethods: undefined,
localeCompare,
currentUserAccountID: CARLOS_ACCOUNT_ID,
accountIDToLogin: {},
});
}
return waitForBatchedUpdates();
Expand Down
128 changes: 128 additions & 0 deletions tests/actions/PolicyTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2756,6 +2756,7 @@ describe('actions/Policy', () => {
lastUsedPaymentMethods: undefined,
localeCompare: TestHelper.localeCompare,
currentUserAccountID: ESH_ACCOUNT_ID,
accountIDToLogin: {},
});

await waitForBatchedUpdates();
Expand Down Expand Up @@ -2855,6 +2856,7 @@ describe('actions/Policy', () => {
lastUsedPaymentMethods: undefined,
localeCompare: TestHelper.localeCompare,
currentUserAccountID: ESH_ACCOUNT_ID,
accountIDToLogin: {},
});

await waitForBatchedUpdates();
Expand Down Expand Up @@ -2912,6 +2914,7 @@ describe('actions/Policy', () => {
lastUsedPaymentMethods: undefined,
localeCompare: TestHelper.localeCompare,
currentUserAccountID: ESH_ACCOUNT_ID,
accountIDToLogin: {},
});
await waitForBatchedUpdates();

Expand Down Expand Up @@ -2950,6 +2953,7 @@ describe('actions/Policy', () => {
lastUsedPaymentMethods: undefined,
localeCompare: TestHelper.localeCompare,
currentUserAccountID: ESH_ACCOUNT_ID,
accountIDToLogin: {},
});
await waitForBatchedUpdates();

Expand Down Expand Up @@ -2990,6 +2994,7 @@ describe('actions/Policy', () => {
lastUsedPaymentMethods: undefined,
localeCompare: TestHelper.localeCompare,
currentUserAccountID: ESH_ACCOUNT_ID,
accountIDToLogin: {},
});
await waitForBatchedUpdates();

Expand All @@ -3005,6 +3010,129 @@ describe('actions/Policy', () => {

expect(lastAccessedWorkspacePolicyIDAfterDelete).toBe(lastAccessedWorkspacePolicyID);
});

it('should use accountIDToLogin to resolve owner email in closed report action', async () => {
const ownerAccountID = 42;
const ownerLogin = 'owner@example.com';
const fakePolicy = createRandomPolicy(0);
const fakeReport = {
...createRandomReport(0, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT),
ownerAccountID,
stateNum: CONST.REPORT.STATE_NUM.OPEN,
statusNum: CONST.REPORT.STATUS_NUM.OPEN,
policyName: fakePolicy.name,
};

await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy);
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${fakeReport.reportID}`, fakeReport);

Policy.deleteWorkspace({
policies: {[`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`]: fakePolicy},
policyID: fakePolicy.id,
personalPolicyID: undefined,
activePolicyID: undefined,
policyName: fakePolicy.name,
lastAccessedWorkspacePolicyID: undefined,
policyCardFeeds: undefined,
reportsToArchive: [fakeReport],
transactionViolations: undefined,
reimbursementAccountError: undefined,
lastUsedPaymentMethods: undefined,
localeCompare: TestHelper.localeCompare,
currentUserAccountID: ESH_ACCOUNT_ID,
accountIDToLogin: {[ownerAccountID]: ownerLogin},
});

await waitForBatchedUpdates();

// Then the closed report action should contain the owner's login from accountIDToLogin
const reportActions = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${fakeReport.reportID}` as const);
const closedAction = Object.values(reportActions ?? {}).find((action) => action && 'actionName' in action && action.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED);
expect(closedAction).toBeDefined();
const message = closedAction && 'message' in closedAction && Array.isArray(closedAction.message) ? closedAction.message.at(0) : undefined;
expect(message?.text).toBe(ownerLogin);
});

it('should use fake owner email when ownerAccountID is fake', async () => {
const fakePolicy = createRandomPolicy(0);
const fakeReport = {
...createRandomReport(0, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT),
ownerAccountID: CONST.POLICY.OWNER_ACCOUNT_ID_FAKE,
stateNum: CONST.REPORT.STATE_NUM.OPEN,
statusNum: CONST.REPORT.STATUS_NUM.OPEN,
policyName: fakePolicy.name,
};

await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy);
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${fakeReport.reportID}`, fakeReport);

Policy.deleteWorkspace({
policies: {[`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`]: fakePolicy},
policyID: fakePolicy.id,
personalPolicyID: undefined,
activePolicyID: undefined,
policyName: fakePolicy.name,
lastAccessedWorkspacePolicyID: undefined,
policyCardFeeds: undefined,
reportsToArchive: [fakeReport],
transactionViolations: undefined,
reimbursementAccountError: undefined,
lastUsedPaymentMethods: undefined,
localeCompare: TestHelper.localeCompare,
currentUserAccountID: ESH_ACCOUNT_ID,
accountIDToLogin: {},
});

await waitForBatchedUpdates();

// Then the closed report action should use the fake owner email
const reportActions = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${fakeReport.reportID}` as const);
const closedAction = Object.values(reportActions ?? {}).find((action) => action && 'actionName' in action && action.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED);
expect(closedAction).toBeDefined();
const message = closedAction && 'message' in closedAction && Array.isArray(closedAction.message) ? closedAction.message.at(0) : undefined;
expect(message?.text).toBe(CONST.POLICY.OWNER_EMAIL_FAKE);
});

it('should fall back to empty string when accountIDToLogin has no entry for ownerAccountID', async () => {
const ownerAccountID = 99;
const fakePolicy = createRandomPolicy(0);
const fakeReport = {
...createRandomReport(0, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT),
ownerAccountID,
stateNum: CONST.REPORT.STATE_NUM.OPEN,
statusNum: CONST.REPORT.STATUS_NUM.OPEN,
policyName: fakePolicy.name,
};

await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy);
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${fakeReport.reportID}`, fakeReport);

Policy.deleteWorkspace({
policies: {[`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`]: fakePolicy},
policyID: fakePolicy.id,
personalPolicyID: undefined,
activePolicyID: undefined,
policyName: fakePolicy.name,
lastAccessedWorkspacePolicyID: undefined,
policyCardFeeds: undefined,
reportsToArchive: [fakeReport],
transactionViolations: undefined,
reimbursementAccountError: undefined,
lastUsedPaymentMethods: undefined,
localeCompare: TestHelper.localeCompare,
currentUserAccountID: ESH_ACCOUNT_ID,
accountIDToLogin: {},
});

await waitForBatchedUpdates();

// Then the closed report action should have an empty string for the email
const reportActions = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${fakeReport.reportID}` as const);
const closedAction = Object.values(reportActions ?? {}).find((action) => action && 'actionName' in action && action.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED);
expect(closedAction).toBeDefined();
const message = closedAction && 'message' in closedAction && Array.isArray(closedAction.message) ? closedAction.message.at(0) : undefined;
expect(message?.text).toBe('');
});
});

describe('leaveWorkspace', () => {
Expand Down
Loading