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
2 changes: 0 additions & 2 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6670,8 +6670,6 @@ Der Control-Tarif beginnt bei 9 $ pro aktivem Mitglied und Monat.`,
peopleAdmins: 'Personaladministratoren',
paymentsAdmins: 'Zahlungsadmins',
members: 'Mitglieder',
removeMemberPromptExpensifyCard: ({memberName}: {memberName: string}) =>
`Sie können ${memberName} nicht aus diesem Workspace entfernen, solange diese Person eine Expensify Karte hat. Bitte deaktivieren Sie die Karte unter Workspace > Expensify Karte und versuchen Sie es dann erneut.`,
},
card: {
getStartedIssuing: 'Beginne, indem du deine erste virtuelle oder physische Karte ausstellst.',
Expand Down
2 changes: 0 additions & 2 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6768,8 +6768,6 @@ const translations = {
`${memberName} has outstanding expense reports to approve. Please ask them to approve, or take control of their reports before removing them from the workspace.`,
removeMemberPromptReimburser: ({memberName}: {memberName: string}) =>
`You can't remove ${memberName} from this workspace. Please set a new reimburser in Workflows > Make or track payments, then try again.`,
removeMemberPromptExpensifyCard: ({memberName}: {memberName: string}) =>
`You can't remove ${memberName} from this workspace while they have an Expensify Card. Please deactivate their card in Workspace > Expensify Card, then try again.`,
removeMemberPromptExporter: ({memberName, workspaceOwner}: {memberName: string; workspaceOwner: string}) =>
`If you remove ${memberName} from this workspace, we'll replace them as the preferred exporter with ${workspaceOwner}, the workspace owner.`,
removeMemberPromptTechContact: ({memberName, workspaceOwner}: {memberName: string; workspaceOwner: string}) =>
Expand Down
2 changes: 0 additions & 2 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6512,8 +6512,6 @@ El plan Controlar empieza en 9 $ por miembro activo al mes.`,
peopleAdmins: 'Administradores de personas',
paymentsAdmins: 'Administradores de pagos',
members: 'Miembros',
removeMemberPromptExpensifyCard: ({memberName}: {memberName: string}) =>
`No puedes eliminar a ${memberName} de este espacio de trabajo mientras tenga una Tarjeta Expensify. Desactiva su tarjeta en Espacio de trabajo > Tarjeta Expensify y vuelve a intentarlo.`,
},
accounting: {
settings: 'configuración',
Expand Down
2 changes: 0 additions & 2 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6695,8 +6695,6 @@ Le forfait Control commence à 9 $ par Membre actif et par mois.`,
peopleAdmins: 'Administrateurs des personnes',
paymentsAdmins: 'Administrateurs des paiements',
members: 'Membres',
removeMemberPromptExpensifyCard: ({memberName}: {memberName: string}) =>
`Vous ne pouvez pas supprimer ${memberName} de cet espace de travail tant qu’iel possède une Carte Expensify. Veuillez désactiver sa carte dans Espace de travail > Carte Expensify, puis réessayer.`,
},
card: {
getStartedIssuing: 'Commencez par émettre votre première carte virtuelle ou physique.',
Expand Down
2 changes: 0 additions & 2 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6646,8 +6646,6 @@ Il piano Control parte da 9 $ al mese per ogni membro attivo.`,
peopleAdmins: 'Amministratori persone',
paymentsAdmins: 'Amministratori pagamenti',
members: 'Membri',
removeMemberPromptExpensifyCard: ({memberName}: {memberName: string}) =>
`Non puoi rimuovere ${memberName} da questo spazio di lavoro finché ha una Carta Expensify. Disattiva prima la sua carta in Spazio di lavoro > Carta Expensify, quindi riprova.`,
},
card: {
getStartedIssuing: 'Inizia emettendo la tua prima carta virtuale o fisica.',
Expand Down
2 changes: 0 additions & 2 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6566,8 +6566,6 @@ Control プランは、アクティブメンバー1人あたり月額 $9 から
peopleAdmins: 'People 管理者',
paymentsAdmins: '支払い管理者',
members: 'メンバー',
removeMemberPromptExpensifyCard: ({memberName}: {memberName: string}) =>
`${memberName}さんはExpensify カードをお持ちの間、このワークスペースから削除できません。ワークスペース > Expensify カードでカードを無効化してから、もう一度お試しください。`,
},
card: {
getStartedIssuing: 'まずは最初のバーチャルカードまたは物理カードを発行しましょう。',
Expand Down
2 changes: 0 additions & 2 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6632,8 +6632,6 @@ Het Control-abonnement begint bij $9 per actieve deelnemer per maand.`,
peopleAdmins: 'People-beheerders',
paymentsAdmins: 'Betalingsbeheerders',
members: 'Leden',
removeMemberPromptExpensifyCard: ({memberName}: {memberName: string}) =>
`Je kunt ${memberName} niet uit deze werkruimte verwijderen zolang diegene een Expensify Kaart heeft. Deactiveer eerst hun kaart in Werkruimte > Expensify Kaart en probeer het daarna opnieuw.`,
},
card: {
getStartedIssuing: 'Begin met het uitgeven van je eerste virtuele of fysieke kaart.',
Expand Down
2 changes: 0 additions & 2 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6613,8 +6613,6 @@ Plan Control zaczyna się od 9 USD za aktywnego członka miesięcznie.`,
peopleAdmins: 'Administratorzy osób',
paymentsAdmins: 'Administratorzy płatności',
members: 'Członkowie',
removeMemberPromptExpensifyCard: ({memberName}: {memberName: string}) =>
`Nie możesz usunąć użytkownika ${memberName} z tego workspace, dopóki ma Kartę Expensify. Dezaktywuj jego kartę w Workspace > Karta Expensify, a następnie spróbuj ponownie.`,
},
card: {
getStartedIssuing: 'Zacznij od wydania swojej pierwszej wirtualnej lub fizycznej karty.',
Expand Down
2 changes: 0 additions & 2 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6630,8 +6630,6 @@ O plano Control começa em US$ 9 por membro ativo por mês.`,
peopleAdmins: 'Administradores de pessoas',
paymentsAdmins: 'Administradores de pagamentos',
members: 'Membros',
removeMemberPromptExpensifyCard: ({memberName}: {memberName: string}) =>
`Você não pode remover ${memberName} deste workspace enquanto essa pessoa tiver um Cartão Expensify. Desative o cartão em Workspace > Cartão Expensify e tente novamente.`,
},
card: {
getStartedIssuing: 'Comece emitindo seu primeiro cartão virtual ou físico.',
Expand Down
2 changes: 0 additions & 2 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6417,8 +6417,6 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
peopleAdmins: '人员管理员',
paymentsAdmins: '付款管理员',
members: '成员',
removeMemberPromptExpensifyCard: ({memberName}: {memberName: string}) =>
`在 ${memberName} 仍持有 Expensify 卡时,您无法将其从此工作区中移除。请在“工作区 > Expensify 卡”中停用其卡片后重试。`,
},
card: {
getStartedIssuing: '从发放您的第一张虚拟卡或实体卡开始使用。',
Expand Down
8 changes: 0 additions & 8 deletions src/libs/CardUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1293,13 +1293,6 @@ function isSmartLimitEnabled(cardsList: CardList) {
return hasAssignedCardMatching(cardsList, (card) => card.nameValuePairs?.limitType === CONST.EXPENSIFY_CARD.LIMIT_TYPES.SMART);
}

function hasActiveExpensifyCardAssigned(workspaceCards: CardList | undefined, accountID: number): boolean {
return hasAssignedCardMatching(
workspaceCards,
(card) => card.accountID === accountID && card.bank === CONST.EXPENSIFY_CARD.BANK && !isTravelCard(card) && card.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
);
}

const CUSTOM_FEEDS = [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD, CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX, CONST.COMPANY_CARD.FEED_BANK_NAME.CSV];

function collectUsedCSVFeedSlotNumbersFromCompanyCards(companyCards: CompanyFeeds | undefined, csvPrefix: string): number[] {
Expand Down Expand Up @@ -2147,7 +2140,6 @@ export {
getDomainByFundID,
isPolicyIDInLinkedExpensifyCardPolicyList,
filterAllInactiveCards,
hasActiveExpensifyCardAssigned,
hasAssignedCardMatching,
forEachAssignedCard,
isActiveCard,
Expand Down
33 changes: 1 addition & 32 deletions src/pages/workspace/WorkspaceMembersPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ import Text from '@components/Text';
import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types';
import TextLink from '@components/TextLink';

import useCardFeeds from '@hooks/useCardFeeds';
import useConfirmModal from '@hooks/useConfirmModal';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useExpensifyCardFeeds from '@hooks/useExpensifyCardFeeds';
import useHRSyncResultsModal from '@hooks/useHRSyncResultsModal';
import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
Expand Down Expand Up @@ -46,7 +44,6 @@ import {
} from '@libs/actions/Policy/Member';
import {removeApprovalWorkflow as removeApprovalWorkflowAction, updateApprovalWorkflow} from '@libs/actions/Workflow';
import {isRuleBotEnforcingRules} from '@libs/AgentRulesUtils';
import {getAllCardsForWorkspace, hasActiveExpensifyCardAssigned} from '@libs/CardUtils';
import {getLatestErrorMessageField} from '@libs/ErrorUtils';
import {getConnectedHRProvider, showMergeHRManualSyncLimitModalIfReached} from '@libs/HRUtils';
import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute';
Expand Down Expand Up @@ -145,13 +142,6 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
const isFocused = useIsFocused();
const policyID = route.params.policyID;
const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policyID}`);
const [cardFeeds] = useCardFeeds(policyID);
const [cardList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`);
const expensifyCardSettings = useExpensifyCardFeeds(policyID);
const workspaceCards = useMemo(
() => getAllCardsForWorkspace(policy?.policyAccountID ?? CONST.DEFAULT_NUMBER_ID, cardList, cardFeeds, expensifyCardSettings),
[policy?.policyAccountID, cardList, cardFeeds, expensifyCardSettings],
);
const [invitedEmailsToAccountIDsDraft] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${policyID}`);
const illustrations = useMemoizedLazyIllustrations(['ReceiptWrangler', 'EmptyShelves']);

Expand All @@ -172,16 +162,6 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers

const canSelectMultiple = canWriteMembers && (shouldUseNarrowLayout ? isMobileSelectionModeEnabled : true);

const blockedCardholderName = useMemo(() => {
const cardholderEmail = selectedEmployees.find((selectedEmployee) => hasActiveExpensifyCardAssigned(workspaceCards, policyMemberEmailsToAccountIDs[selectedEmployee]));

if (!cardholderEmail) {
return;
}

return getDisplayNameForParticipant({accountID: policyMemberEmailsToAccountIDs[cardholderEmail], formatPhoneNumber, translate});
}, [selectedEmployees, workspaceCards, policyMemberEmailsToAccountIDs, formatPhoneNumber, translate]);

const confirmModalPrompt = useMemo(() => {
const approverEmail = selectedEmployees.find((selectedEmployee) => isPolicyApprover(policy, selectedEmployee));

Expand Down Expand Up @@ -295,17 +275,6 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
showRuleBotGuardModal('remove', policyID);
return;
}
if (blockedCardholderName) {
showConfirmModal({
shouldShowCancelButton: false,
success: true,
title: translate('workspace.people.removeMembersTitle', {count: selectedEmployees.length}),
prompt: translate('workspace.people.removeMemberPromptExpensifyCard', {memberName: blockedCardholderName}),
confirmText: translate('common.buttonConfirm'),
cancelText: translate('common.cancel'),
});
return;
}

showConfirmModal({
danger: true,
Expand All @@ -326,7 +295,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers

removeUsers();
});
}, [blockedCardholderName, confirmModalPrompt, removeUsers, selectedEmployees, policyMemberEmailsToAccountIDs, policy, policyID, showConfirmModal, showRuleBotGuardModal, translate]);
}, [confirmModalPrompt, removeUsers, selectedEmployees, policyMemberEmailsToAccountIDs, policy, policyID, showConfirmModal, showRuleBotGuardModal, translate]);

/** Opens the member details page */
const openMemberDetails = useCallback(
Expand Down
19 changes: 3 additions & 16 deletions src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import {setPolicyPreventSelfApproval} from '@libs/actions/Policy/Policy';
import {removeApprovalWorkflow as removeApprovalWorkflowAction, updateApprovalWorkflow} from '@libs/actions/Workflow';
import {isRuleBotEnforcingRules} from '@libs/AgentRulesUtils';
import {
getAllCardsForWorkspace,
getCardFeedIcon,
getCardFeedWithDomainID,
getPlaidInstitutionIconUrl,
hasActiveExpensifyCardAssigned,
lastFourNumbersFromCardName,
maskCardNumber,
} from '@libs/CardUtils';
import {getAllCardsForWorkspace, getCardFeedIcon, getCardFeedWithDomainID, getPlaidInstitutionIconUrl, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils';
import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import {getPersonalDetailByEmail, getPhoneNumber, temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
Expand Down Expand Up @@ -155,7 +147,6 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM
const phoneNumber = getPhoneNumber(details);
const reimburserEmail = getReimburserEmail(policy);
const isReimburser = !!reimburserEmail && reimburserEmail === memberLogin;
const hasActiveExpensifyCard = hasActiveExpensifyCardAssigned(workspaceCards, accountID);
const {isAccountLocked} = useLockedAccountState();
const {showLockedAccountModal} = useLockedAccountActions();

Expand Down Expand Up @@ -189,11 +180,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM

let confirmModalPrompt = translate('workspace.people.removeMembersWarningPrompt', displayName, policyOwnerDisplayName);

if (hasActiveExpensifyCard) {
confirmModalPrompt = translate('workspace.people.removeMemberPromptExpensifyCard', {
memberName: displayName,
});
} else if (isTechnicalContact) {
if (isTechnicalContact) {
confirmModalPrompt = translate('workspace.people.removeMemberPromptTechContact', {
memberName: displayName,
workspaceOwner: policyOwnerDisplayName,
Expand Down Expand Up @@ -282,7 +269,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM
showRuleBotGuardModal('remove', policyID);
return;
}
if (hasActiveExpensifyCard || isReimburser) {
if (isReimburser) {
showConfirmModal({
shouldShowCancelButton: false,
success: true,
Expand Down
51 changes: 0 additions & 51 deletions tests/unit/CardUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ import {
getSelectedFeed,
getTranslationKeyForCardStatus,
getYearFromExpirationDateString,
hasActiveExpensifyCardAssigned,
hasAssignedCardMatching,
hasIssuedExpensifyCard,
hasOnlyOneCardToAssign,
Expand Down Expand Up @@ -2235,56 +2234,6 @@ describe('CardUtils', () => {
});
});

describe('hasActiveExpensifyCardAssigned', () => {
const cardholderAccountID = 11;

it('returns true when the member holds an Expensify Card on the workspace', () => {
const cards = createMock<CardList>({
'1': {cardID: 1, accountID: cardholderAccountID, bank: CONST.EXPENSIFY_CARD.BANK},
});
expect(hasActiveExpensifyCardAssigned(cards, cardholderAccountID)).toBe(true);
});

it('returns false for an undefined card list', () => {
expect(hasActiveExpensifyCardAssigned(undefined, cardholderAccountID)).toBe(false);
});

it('returns false when the Expensify Card belongs to another member', () => {
const cards = createMock<CardList>({
'1': {cardID: 1, accountID: 22, bank: CONST.EXPENSIFY_CARD.BANK},
});
expect(hasActiveExpensifyCardAssigned(cards, cardholderAccountID)).toBe(false);
});

it('returns false when the member only holds a company card', () => {
const cards = createMock<CardList>({
'1': {cardID: 1, accountID: cardholderAccountID, bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA},
});
expect(hasActiveExpensifyCardAssigned(cards, cardholderAccountID)).toBe(false);
});

it('returns false when the member only holds a travel card', () => {
const cards = createMock<CardList>({
'1': {cardID: 1, accountID: cardholderAccountID, bank: CONST.EXPENSIFY_CARD.BANK, nameValuePairs: {feedCountry: CONST.TRAVEL.PROGRAM_TRAVEL_US}},
});
expect(hasActiveExpensifyCardAssigned(cards, cardholderAccountID)).toBe(false);
});

it('returns false when the Expensify Card is already pending removal', () => {
const cards = createMock<CardList>({
'1': {cardID: 1, accountID: cardholderAccountID, bank: CONST.EXPENSIFY_CARD.BANK, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE},
});
expect(hasActiveExpensifyCardAssigned(cards, cardholderAccountID)).toBe(false);
});

it('skips the cardList bucket of cards still available to assign', () => {
const cards = createMock<CardList>({
cardList: {'CREDIT CARD...1234': 'encrypted-value'} as Record<string, string>,
});
expect(hasActiveExpensifyCardAssigned(cards, cardholderAccountID)).toBe(false);
});
});

describe('forEachAssignedCard', () => {
const cardsList = createMock<WorkspaceCardsList>({
'1': {cardID: 1, state: CONST.EXPENSIFY_CARD.STATE.OPEN},
Expand Down
Loading