Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecated Policy Role in favor of employeeList #40178

Closed
Closed
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
3 changes: 2 additions & 1 deletion src/components/MoneyRequestHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as HeaderUtils from '@libs/HeaderUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
Expand Down Expand Up @@ -65,7 +66,7 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction,

// Only the requestor can take delete the expense, admins can only edit it.
const isActionOwner = typeof parentReportAction?.actorAccountID === 'number' && typeof session?.accountID === 'number' && parentReportAction.actorAccountID === session?.accountID;
const isPolicyAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN;
const isPolicyAdmin = PolicyUtils.isPolicyAdmin(policy);
const isApprover = ReportUtils.isMoneyRequestReport(moneyRequestReport) && (session?.accountID ?? null) === moneyRequestReport?.managerID;

const deleteTransaction = useCallback(() => {
Expand Down
18 changes: 17 additions & 1 deletion src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Str from 'expensify-common/lib/str';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import type {PolicySelector} from '@pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
Expand All @@ -15,6 +17,14 @@ import {getPersonalDetailByEmail} from './PersonalDetailsUtils';

type MemberEmailsToAccountIDs = Record<string, number>;

let sessionEmail = '';
Onyx.connect({
key: ONYXKEYS.SESSION,
callback: (val) => {
sessionEmail = val?.email ?? '';
},
});

/**
* Filter out the active policies, which will exclude policies with pending deletion
* These are policies that we can use to create reports with in NewDot.
Expand Down Expand Up @@ -117,10 +127,15 @@ function isExpensifyTeam(email: string | undefined): boolean {
return emailDomain === CONST.EXPENSIFY_PARTNER_NAME || emailDomain === CONST.EMAIL.GUIDES_DOMAIN;
}

function getPolicyRole(policy: OnyxEntry<Policy> | OnyxEntry<PolicySelector> | EmptyObject): ValueOf<typeof CONST.POLICY.ROLE> | undefined {
const role = policy?.employeeList?.[sessionEmail]?.role;
return role;
}

/**
* Checks if the current user is an admin of the policy.
*/
const isPolicyAdmin = (policy: OnyxEntry<Policy> | EmptyObject): boolean => policy?.role === CONST.POLICY.ROLE.ADMIN;
const isPolicyAdmin = (policy: OnyxEntry<Policy> | EmptyObject): boolean => getPolicyRole(policy) === CONST.POLICY.ROLE.ADMIN;

/**
* Checks if the policy is a free group policy.
Expand Down Expand Up @@ -357,6 +372,7 @@ export {
getTaxByID,
hasPolicyCategoriesError,
getPolicyIDFromNavigationState,
getPolicyRole,
};

export type {MemberEmailsToAccountIDs};
23 changes: 11 additions & 12 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1201,7 +1201,7 @@ function isAllowedToComment(report: OnyxEntry<Report>): boolean {
// If we've made it here, commenting on this report is restricted.
// If the user is an admin, allow them to post.
const policy = allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`];
return policy?.role === CONST.POLICY.ROLE.ADMIN;
return PolicyUtils.isPolicyAdmin(policy);
}

/**
Expand All @@ -1212,18 +1212,17 @@ function isPolicyExpenseChatAdmin(report: OnyxEntry<Report>, policies: OnyxColle
return false;
}

const policyRole = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]?.role;
const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`] ?? null;

return policyRole === CONST.POLICY.ROLE.ADMIN;
return PolicyUtils.isPolicyAdmin(policy);
}

/**
* Checks if the current user is the admin of the policy.
*/
function isPolicyAdmin(policyID: string, policies: OnyxCollection<Policy>): boolean {
const policyRole = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.role;

return policyRole === CONST.POLICY.ROLE.ADMIN;
const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? null;
return PolicyUtils.isPolicyAdmin(policy);
}

/**
Expand Down Expand Up @@ -1366,7 +1365,7 @@ function isPayer(session: OnyxEntry<Session>, iouReport: OnyxEntry<Report>) {
const isApproved = isReportApproved(iouReport);
const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${iouReport?.policyID}`] ?? null;
const policyType = policy?.type;
const isAdmin = policyType !== CONST.POLICY.TYPE.PERSONAL && policy?.role === CONST.POLICY.ROLE.ADMIN;
const isAdmin = policyType !== CONST.POLICY.TYPE.PERSONAL && PolicyUtils.isPolicyAdmin(policy);
const isManager = iouReport?.managerID === session?.accountID;
if (isPaidGroupPolicy(iouReport)) {
if (policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES) {
Expand Down Expand Up @@ -1467,7 +1466,7 @@ function canDeleteReportAction(reportAction: OnyxEntry<ReportAction>, reportID:
return false;
}

const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN && !isEmptyObject(report) && !isDM(report);
const isAdmin = PolicyUtils.isPolicyAdmin(policy) && !isEmptyObject(report) && !isDM(report);

return isActionOwner || isAdmin;
}
Expand Down Expand Up @@ -2233,10 +2232,10 @@ function getPolicyExpenseChatName(report: OnyxEntry<Report>, policy: OnyxEntry<P
return getPolicyName(report, false, policy);
}

let policyExpenseChatRole = 'user';
let policyExpenseChatRole: ValueOf<typeof CONST.POLICY.ROLE> = CONST.POLICY.ROLE.USER;
const policyItem = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`];
if (policyItem) {
policyExpenseChatRole = policyItem.role || 'user';
policyExpenseChatRole = PolicyUtils.getPolicyRole(policyItem) ?? CONST.POLICY.ROLE.USER;
}

// If this user is not admin and this policy expense chat has been archived because of account merging, this must be an old workspace chat
Expand Down Expand Up @@ -2474,7 +2473,7 @@ function canEditMoneyRequest(reportAction: OnyxEntry<ReportAction>): boolean {
}

const policy = getPolicy(moneyRequestReport?.policyID ?? '');
const isAdmin = policy.role === CONST.POLICY.ROLE.ADMIN;
const isAdmin = PolicyUtils.isPolicyAdmin(policy);
const isManager = currentUserAccountID === moneyRequestReport?.managerID;

// Admin & managers can always edit coding fields such as tag, category, billable, etc. As long as the report has a state higher than OPEN.
Expand Down Expand Up @@ -2524,7 +2523,7 @@ function canEditFieldOfMoneyRequest(reportAction: OnyxEntry<ReportAction>, field

if (TransactionUtils.isDistanceRequest(transaction)) {
const policy = getPolicy(moneyRequestReport?.reportID ?? '');
const isAdmin = isExpenseReport(moneyRequestReport) && policy.role === CONST.POLICY.ROLE.ADMIN;
const isAdmin = isExpenseReport(moneyRequestReport) && PolicyUtils.isPolicyAdmin(policy);
const isManager = isExpenseReport(moneyRequestReport) && currentUserAccountID === moneyRequestReport?.managerID;

return isAdmin || isManager;
Expand Down
2 changes: 1 addition & 1 deletion src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5496,7 +5496,7 @@ function submitReport(expenseReport: OnyxTypes.Report) {
const policy = getPolicy(expenseReport.policyID);
const isCurrentUserManager = currentUserPersonalDetails.accountID === expenseReport.managerID;
const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy);
const adminAccountID = policy.role === CONST.POLICY.ROLE.ADMIN ? currentUserPersonalDetails.accountID : undefined;
const adminAccountID = PolicyUtils.isPolicyAdmin(policy) ? currentUserPersonalDetails.accountID : undefined;
const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction(expenseReport?.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID, adminAccountID);
const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, isSubmitAndClosePolicy ? CONST.REPORT.STATUS_NUM.CLOSED : CONST.REPORT.STATUS_NUM.SUBMITTED);

Expand Down
15 changes: 8 additions & 7 deletions src/libs/actions/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,8 +285,11 @@ function hasActiveChatEnabledPolicies(policies: Array<OnyxEntry<PolicySelector>>
const adminChatEnabledPolicies = Object.values(policies ?? {}).filter(
(policy) =>
policy &&
((policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN) ||
(!includeOnlyFreePolicies && policy.type !== CONST.POLICY.TYPE.PERSONAL && policy.role === CONST.POLICY.ROLE.ADMIN && policy.isPolicyExpenseChatEnabled)),
((policy.type === CONST.POLICY.TYPE.FREE && PolicyUtils.getPolicyRole(policy) === CONST.POLICY.ROLE.ADMIN) ||
shubham1206agra marked this conversation as resolved.
Show resolved Hide resolved
(!includeOnlyFreePolicies &&
policy.type !== CONST.POLICY.TYPE.PERSONAL &&
PolicyUtils.getPolicyRole(policy) === CONST.POLICY.ROLE.ADMIN &&
shubham1206agra marked this conversation as resolved.
Show resolved Hide resolved
policy.isPolicyExpenseChatEnabled)),
);

if (adminChatEnabledPolicies.length === 0) {
Expand Down Expand Up @@ -430,7 +433,7 @@ function deleteWorkspace(policyID: string, policyName: string) {
* Is the user an admin of a free policy (aka workspace)?
*/
function isAdminOfFreePolicy(policies?: PoliciesRecord): boolean {
return Object.values(policies ?? {}).some((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN);
return Object.values(policies ?? {}).some((policy) => policy && PolicyUtils.isFreeGroupPolicy(policy) && PolicyUtils.isPolicyAdmin(policy));
}

/**
Expand Down Expand Up @@ -1078,7 +1081,7 @@ function updateWorkspaceMembersRole(policyID: string, accountIDs: number[], newR
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
employeeList: {
...memberRoles.reduce((member: Record<string, {role: string; pendingAction: PendingAction}>, current) => {
...memberRoles.reduce((member: Record<string, {role: ValueOf<typeof CONST.POLICY.ROLE>; pendingAction: PendingAction}>, current) => {
// eslint-disable-next-line no-param-reassign
member[current.email] = {role: current?.role, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE};
return member;
Expand All @@ -1095,7 +1098,7 @@ function updateWorkspaceMembersRole(policyID: string, accountIDs: number[], newR
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
employeeList: {
...memberRoles.reduce((member: Record<string, {role: string; pendingAction: PendingAction}>, current) => {
...memberRoles.reduce((member: Record<string, {role: ValueOf<typeof CONST.POLICY.ROLE>; pendingAction: PendingAction}>, current) => {
// eslint-disable-next-line no-param-reassign
member[current.email] = {role: current?.role, pendingAction: null};
return member;
Expand Down Expand Up @@ -2045,7 +2048,6 @@ function createDraftInitialWorkspace(policyOwnerEmail = '', policyName = '', pol
id: policyID,
type: CONST.POLICY.TYPE.TEAM,
name: workspaceName,
role: CONST.POLICY.ROLE.ADMIN,
owner: sessionEmail,
ownerAccountID: sessionAccountID,
isPolicyExpenseChatEnabled: true,
Expand Down Expand Up @@ -2107,7 +2109,6 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName
id: policyID,
type: CONST.POLICY.TYPE.TEAM,
name: workspaceName,
role: CONST.POLICY.ROLE.ADMIN,
owner: sessionEmail,
ownerAccountID: sessionAccountID,
isPolicyExpenseChatEnabled: true,
Expand Down
1 change: 0 additions & 1 deletion src/libs/actions/TeachersUnite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ function addSchoolPrincipal(firstName: string, partnerUserID: string, lastName:
isPolicyExpenseChatEnabled: true,
type: CONST.POLICY.TYPE.CORPORATE,
name: policyName,
role: CONST.POLICY.ROLE.USER,
owner: sessionEmail,
outputCurrency: allPersonalDetails?.[sessionAccountID]?.localCurrencyCode ?? CONST.CURRENCY.USD,
employeeList: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const useIsFocused = () => {
return isFocused || (topmostCentralPane?.name === SCREENS.SEARCH.CENTRAL_PANE && isSmallScreenWidth);
};

type PolicySelector = Pick<OnyxTypes.Policy, 'type' | 'role' | 'isPolicyExpenseChatEnabled' | 'pendingAction' | 'avatar' | 'name'>;
type PolicySelector = Pick<OnyxTypes.Policy, 'type' | 'employeeList' | 'isPolicyExpenseChatEnabled' | 'pendingAction' | 'avatar' | 'name'>;

type FloatingActionButtonAndPopoverOnyxProps = {
/** The list of policies the user has access to. */
Expand Down Expand Up @@ -73,7 +73,7 @@ type FloatingActionButtonAndPopoverRef = {
const policySelector = (policy: OnyxEntry<OnyxTypes.Policy>): PolicySelector =>
(policy && {
type: policy.type,
role: policy.role,
employeeList: policy.employeeList,
isPolicyExpenseChatEnabled: policy.isPolicyExpenseChatEnabled,
pendingAction: policy.pendingAction,
avatar: policy.avatar,
Expand Down
5 changes: 3 additions & 2 deletions src/pages/workspace/WorkspacesListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import WorkspacesListRow from './WorkspacesListRow';
type WorkspaceItem = Required<Pick<MenuItemProps, 'title' | 'disabled'>> &
Pick<MenuItemProps, 'brickRoadIndicator' | 'iconFill' | 'fallbackIcon'> &
Pick<OfflineWithFeedbackProps, 'errors' | 'pendingAction'> &
Pick<PolicyType, 'role' | 'type' | 'ownerAccountID'> & {
Pick<PolicyType, 'type' | 'ownerAccountID'> & {
icon: AvatarSource;
action: () => void;
dismissError: () => void;
Expand All @@ -56,6 +56,7 @@ type WorkspaceItem = Required<Pick<MenuItemProps, 'title' | 'disabled'>> &
adminRoom?: string | null;
announceRoom?: string | null;
isJoinRequestPending?: boolean;
role?: ValueOf<typeof CONST.POLICY.ROLE>;
};

// eslint-disable-next-line react/no-unused-prop-types
Expand Down Expand Up @@ -350,7 +351,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}:
adminRoom: policyRooms?.[policy.id]?.adminRoom ?? policy.chatReportIDAdmins?.toString(),
announceRoom: policyRooms?.[policy.id]?.announceRoom ?? policy.chatReportIDAnnounce?.toString(),
ownerAccountID: policy.ownerAccountID,
role: policy.role,
role: PolicyUtils.getPolicyRole(policy),
type: policy.type,
};
})
Expand Down
3 changes: 0 additions & 3 deletions src/types/onyx/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,6 @@ type Policy = OnyxCommon.OnyxValueWithOfflineFeedback<
/** The name of the policy */
name: string;

/** The current user's role in the policy */
role: ValueOf<typeof CONST.POLICY.ROLE>;

/** The policy type */
type: ValueOf<typeof CONST.POLICY.TYPE>;

Expand Down
4 changes: 3 additions & 1 deletion src/types/onyx/PolicyEmployee.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';
import type * as OnyxCommon from './OnyxCommon';

type PolicyEmployee = OnyxCommon.OnyxValueWithOfflineFeedback<{
/** Role of the user in the policy */
role?: string;
role?: ValueOf<typeof CONST.POLICY.ROLE>;

/** Email of the user */
email?: string;
Expand Down
13 changes: 10 additions & 3 deletions tests/actions/IOUTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1678,10 +1678,14 @@ describe('actions/IOU', () => {
{amount: 20000, comment: 'Double the amount!'},
{
id: '123',
role: 'user',
type: 'free',
name: '',
owner: '',
employeeList: {
[RORY_EMAIL]: {
role: 'user',
},
},
outputCurrency: '',
isPolicyExpenseChatEnabled: false,
},
Expand Down Expand Up @@ -1833,11 +1837,15 @@ describe('actions/IOU', () => {
{amount: 20000, comment: 'Double the amount!'},
{
id: '123',
role: 'user',
type: 'free',
name: '',
owner: '',
outputCurrency: '',
employeeList: {
[RORY_EMAIL]: {
role: 'user',
},
},
isPolicyExpenseChatEnabled: false,
},
{},
Expand Down Expand Up @@ -2568,7 +2576,6 @@ describe('actions/IOU', () => {
{amount: 20000, comment: 'Double the amount!'},
{
id: '123',
role: 'user',
type: 'free',
name: '',
owner: '',
Expand Down
1 change: 0 additions & 1 deletion tests/actions/PolicyTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ describe('actions/Policy', () => {
expect(policy?.id).toBe(policyID);
expect(policy?.name).toBe(WORKSPACE_NAME);
expect(policy?.type).toBe(CONST.POLICY.TYPE.TEAM);
expect(policy?.role).toBe(CONST.POLICY.ROLE.ADMIN);
expect(policy?.owner).toBe(ESH_EMAIL);
expect(policy?.isPolicyExpenseChatEnabled).toBe(true);
expect(policy?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD);
Expand Down
6 changes: 5 additions & 1 deletion tests/unit/NextStepUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ describe('libs/NextStepUtils', () => {
},
// Required props
name: 'Policy',
role: 'admin',
type: 'team',
employeeList: {
[currentUserEmail]: {
role: 'admin',
},
},
outputCurrency: CONST.CURRENCY.USD,
isPolicyExpenseChatEnabled: true,
};
Expand Down
6 changes: 5 additions & 1 deletion tests/unit/OptionsListUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,14 @@ describe('OptionsListUtils', () => {
const POLICY: Policy = {
id: policyID,
name: 'Hero Policy',
role: 'user',
type: 'free',
owner: '',
outputCurrency: '',
employeeList: {
'[email protected]': {
role: 'user',
},
},
isPolicyExpenseChatEnabled: false,
};

Expand Down
Loading
Loading