Skip to content
Open
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: 3 additions & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5632,6 +5632,9 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
travel: {
title: 'Reisen',
subtitle: 'Buchen, verwalten und abstimmen Sie all Ihre Geschäftsreisen.',
disableTravelTitle: 'Deaktivieren Sie zuerst die Reiseabrechnung',
disableTravelPrompt: 'Reiseabrechnung ist für diesen Workspace aktiviert. Schalten Sie sie aus, bevor Sie Reisen deaktivieren können.',
disableTravelButton: 'Zu den Reiseeinstellungen gehen',
getStarted: {
title: 'Los geht’s mit Expensify Travel',
subtitle: 'Wir brauchen nur noch ein paar weitere Informationen zu deinem Unternehmen, dann bist du startklar.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5653,6 +5653,9 @@ const translations = {
travel: {
title: 'Travel',
subtitle: 'Book, manage, and reconcile all your business travel.',
disableTravelTitle: 'Turn off Travel Invoicing first',
disableTravelPrompt: 'Travel Invoicing is enabled for this workspace. Turn it off before you can disable Travel.',
disableTravelButton: 'Go to Travel settings',
getStarted: {
title: 'Get started with Expensify Travel',
subtitle: "We just need a few more pieces of info about your business, then you'll be ready for takeoff.",
Expand Down
3 changes: 3 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5465,6 +5465,9 @@ ${amount} para ${merchant} - ${date}`,
travel: {
title: 'Viajes',
subtitle: 'Reserva, gestiona y concilia todos tus viajes de negocios.',
disableTravelTitle: 'Desactiva primero la facturación de viajes',
disableTravelPrompt: 'La facturación de viajes está habilitada para este espacio de trabajo. Desactívala antes de poder deshabilitar Viajes.',
disableTravelButton: 'Ve a la configuración de Viajes',
getStarted: {
title: 'Comienza con Expensify Travel',
subtitle: 'Solo necesitamos algunos datos más sobre tu empresa y estarás listo para despegar.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5644,6 +5644,9 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
travel: {
title: 'Déplacements',
subtitle: 'Réservez, gérez et rapprochez tous vos déplacements professionnels.',
disableTravelTitle: 'Désactivez d’abord la facturation des déplacements',
disableTravelPrompt: 'La facturation de voyage est activée pour cet espace de travail. Désactivez-la avant de pouvoir désactiver Travel.',
disableTravelButton: 'Accéder aux paramètres de voyage',
getStarted: {
title: 'Commencez avec Expensify Travel',
subtitle: 'Nous avons simplement besoin de quelques informations supplémentaires sur votre entreprise, puis vous serez prêt à décoller.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5613,6 +5613,9 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
travel: {
title: 'Viaggi',
subtitle: 'Prenota, gestisci e riconcilia tutti i viaggi di lavoro.',
disableTravelTitle: 'Disattiva prima la fatturazione viaggi',
disableTravelPrompt: 'La fatturazione Viaggi è abilitata per questo spazio di lavoro. Disattivala prima di poter disabilitare Viaggi.',
disableTravelButton: 'Vai alle impostazioni Viaggio',
getStarted: {
title: 'Inizia con Expensify Travel',
subtitle: 'Ci servono solo ancora alcune informazioni sulla tua attività, poi sarai pronto al decollo.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5564,6 +5564,9 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO
travel: {
title: '出張',
subtitle: '出張の予約、管理、精算をすべて一元管理。',
disableTravelTitle: '先に Travel Invoicing をオフにしてください',
disableTravelPrompt: 'このワークスペースでは旅行請求書作成が有効になっています。Travel を無効にする前にオフにしてください。',
disableTravelButton: '出張設定に移動します',
getStarted: {
title: 'Expensify Travel を使い始める',
subtitle: 'ビジネスについてあと少しだけ情報を教えてください。準備が整い次第、すぐに開始できます。',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5602,6 +5602,9 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
travel: {
title: 'Reizen',
subtitle: 'Boek, beheer en reconcilieer al je zakelijke reizen.',
disableTravelTitle: 'Schakel eerst Reisfacturatie uit',
disableTravelPrompt: 'Reisfacturatie is ingeschakeld voor deze workspace. Schakel dit uit voordat je Reis kunt uitschakelen.',
disableTravelButton: 'Ga naar Reisinstellingen',
getStarted: {
title: 'Aan de slag met Expensify Travel',
subtitle: 'We hebben nog maar een paar extra gegevens over je bedrijf nodig, dan ben je klaar voor vertrek.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5590,6 +5590,9 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
travel: {
title: 'Podróże',
subtitle: 'Rezerwuj, zarządzaj i uzgadniaj wszystkie wyjazdy służbowe.',
disableTravelTitle: 'Najpierw wyłącz fakturowanie podróży',
disableTravelPrompt: 'Fakturowanie podróży jest włączone dla tego obszaru roboczego. Wyłącz je, zanim będziesz mógł wyłączyć funkcję Podróże.',
disableTravelButton: 'Przejdź do ustawień Podróży',
getStarted: {
title: 'Pierwsze kroki z Expensify Travel',
subtitle: 'Potrzebujemy jeszcze tylko kilku informacji o Twojej firmie, a potem będziesz gotowy do startu.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5594,6 +5594,9 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS
travel: {
title: 'Viagens',
subtitle: 'Reserve, gerencie e reconcilie todas as viagens de negócios da sua empresa.',
disableTravelTitle: 'Desative primeiro o faturamento de viagens',
disableTravelPrompt: 'O faturamento de viagens está ativado para este workspace. Desative-o antes de desativar o Travel.',
disableTravelButton: 'Ir para as configurações de Viagem',
getStarted: {
title: 'Comece a usar o Expensify Travel',
subtitle: 'Só precisamos de mais algumas informações sobre sua empresa e você estará pronto para decolar.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5466,6 +5466,9 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
travel: {
title: '差旅',
subtitle: '预订、管理并对账您所有的商务差旅。',
disableTravelTitle: '请先关闭差旅开票',
disableTravelPrompt: '此工作区已启用差旅开票。要禁用差旅功能,请先将其关闭。',
disableTravelButton: '前往差旅设置',
getStarted: {
title: '开始使用 Expensify Travel',
subtitle: '我们只需要再获取一些关于您企业的信息,之后您就可以准备起飞了。',
Expand Down
19 changes: 19 additions & 0 deletions src/pages/workspace/WorkspaceMoreFeaturesPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
isTimeTrackingEnabled,
tryNavigateToSubmitWorkspaceUpgrade,
} from '@libs/PolicyUtils';
import {getIsTravelInvoicingEnabled, getTravelInvoicingCardSettingsKey} from '@libs/TravelInvoicingUtils';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading';
import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading';
Expand Down Expand Up @@ -120,10 +121,13 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
const [cardList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`);
const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`);

const [travelCardSettings] = useOnyx(getTravelInvoicingCardSettingsKey(workspaceAccountID));

const workspaceCards = getAllCardsForWorkspace(workspaceAccountID, cardList, cardFeeds);
const isSmartLimitEnabled = isSmartLimitEnabledUtil(workspaceCards);
const settings = getCardSettings(cardSettings);
const paymentBankAccountID = settings?.paymentBankAccountID;
const isTravelInvoicingEnabled = getIsTravelInvoicingEnabled(getCardSettings(travelCardSettings, CONST.TRAVEL.PROGRAM_TRAVEL_US));

const warnAccountingManagesOrganizeFeature = async () => {
if (!hasAccountingConnection || !policyID) {
Expand Down Expand Up @@ -202,6 +206,19 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
navigateToConciergeChat(conciergeReportID, introSelected, currentUserAccountID, isSelfTourViewed, betas, false);
};

const promptDisableTravelViaInvoicing = async () => {
const {action} = await showConfirmModal({
title: translate('workspace.moreFeatures.travel.disableTravelTitle'),
prompt: translate('workspace.moreFeatures.travel.disableTravelPrompt'),
confirmText: translate('workspace.moreFeatures.travel.disableTravelButton'),
cancelText: translate('common.cancel'),
});
if (action !== ModalActions.CONFIRM || !policyID) {
return;
}
Navigation.navigate(ROUTES.WORKSPACE_TRAVEL.getRoute(policyID));
};

const promptDisableCompanyCardsViaConcierge = async () => {
const {action} = await showConfirmModal({
title: translate('workspace.moreFeatures.companyCards.disableCardTitle'),
Expand Down Expand Up @@ -491,6 +508,8 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
subtitle={translate('workspace.moreFeatures.travel.subtitle')}
isActive={policy?.isTravelEnabled ?? false}
pendingAction={policy?.pendingFields?.isTravelEnabled}
disabled={isTravelInvoicingEnabled}
Comment thread
blimpich marked this conversation as resolved.
disabledAction={promptDisableTravelViaInvoicing}
onToggle={(isEnabled) => {
if (!policyID) {
return;
Expand Down
52 changes: 52 additions & 0 deletions tests/ui/WorkspaceMoreFeaturesPageTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import * as CardUtils from '@libs/CardUtils';
import Navigation from '@libs/Navigation/Navigation';
import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator';
import * as PolicyUtils from '@libs/PolicyUtils';
import {getTravelInvoicingCardSettingsKey} from '@libs/TravelInvoicingUtils';
import type {WorkspaceSplitNavigatorParamList} from '@navigation/types';
import WorkspaceMoreFeaturesPage from '@pages/workspace/WorkspaceMoreFeaturesPage';
import * as ReportActions from '@userActions/Report';
Expand Down Expand Up @@ -246,6 +247,57 @@ describe('WorkspaceMoreFeaturesPage', () => {
});
});

describe('Travel toggle (locked when Travel Invoicing is enabled)', () => {
const workspaceAccountID = LHNTestUtils.getFakePolicy().workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;

const enableTravelInvoicing = () => Onyx.merge(getTravelInvoicingCardSettingsKey(workspaceAccountID), {[CONST.TRAVEL.PROGRAM_TRAVEL_US]: {isEnabled: true}});

it('locks the Travel switch when Travel Invoicing is enabled', async () => {
await TestHelper.signInWithTestUser();
await act(async () => {
await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, buildPolicy({id: POLICY_ID, isTravelEnabled: true}));
await enableTravelInvoicing();
});

renderPage({policyID: POLICY_ID});
await waitForBatchedUpdatesWithAct();

await expect(findLockedSwitch('workspace.moreFeatures.travel.subtitle')).resolves.toBeOnTheScreen();
});

it('leaves the Travel switch interactive when Travel Invoicing is not enabled', async () => {
await TestHelper.signInWithTestUser();
await act(async () => {
await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, buildPolicy({id: POLICY_ID, isTravelEnabled: true}));
});

renderPage({policyID: POLICY_ID});
await waitForBatchedUpdatesWithAct();

await expect(findUnlockedSwitch('workspace.moreFeatures.travel.subtitle')).resolves.toBeOnTheScreen();
});

it('navigates to Travel settings when the user confirms the disable-Travel warning', async () => {
await TestHelper.signInWithTestUser();
await act(async () => {
await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, buildPolicy({id: POLICY_ID, isTravelEnabled: true}));
await enableTravelInvoicing();
});

renderPage({policyID: POLICY_ID});
await waitForBatchedUpdatesWithAct();
fireEvent.press(await findLockedSwitch('workspace.moreFeatures.travel.subtitle'));

await waitFor(() => {
expect(screen.getByText(TestHelper.translateLocal('workspace.moreFeatures.travel.disableTravelPrompt'))).toBeOnTheScreen();
});
fireEvent.press(await screen.findByLabelText(TestHelper.translateLocal('workspace.moreFeatures.travel.disableTravelButton')));
await waitForBatchedUpdatesWithAct();

expect(navigateSpy).toHaveBeenCalledWith(ROUTES.WORKSPACE_TRAVEL.getRoute(POLICY_ID));
});
});

describe('Accounting toggle (locked when an integration is connected)', () => {
it('locks the Accounting switch when the policy has an active connection', async () => {
await TestHelper.signInWithTestUser();
Expand Down
Loading