[NO QA] make Travel Invoicing use 1:1:1 commands and implement verifying UI state#83481
[NO QA] make Travel Invoicing use 1:1:1 commands and implement verifying UI state#83481
Conversation
Replace TOGGLE_TRAVEL_INVOICING with CONFIGURE_TRAVEL_INVOICING_FOR_POLICY and DEACTIVATE_TRAVEL_INVOICING. Create corresponding parameter types.
…ctions Replace toggleTravelInvoicing with two separate actions. The enable action combines settlement account and isEnabled optimistic data into a single API call. Rename clearToggleTravelInvoicingErrors to clearTravelInvoicingErrors.
Settlement account page now calls configureTravelInvoicingForPolicy when enabling (single API call instead of two). Section page uses configureTravelInvoicingForPolicy for enable and deactivateTravelInvoicing for disable.
Replace toggleTravelInvoicing test with separate tests for configureTravelInvoicingForPolicy and deactivateTravelInvoicing.
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
Collapse multi-line variable assignment to single line to satisfy prettier check.
🦜 Polyglot Parrot! 🦜Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues: View the translation diffdiff --git a/src/languages/de.ts b/src/languages/de.ts
index 6412f175..2bfa7f50 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -955,8 +955,8 @@ const translations: TranslationDeepObject<typeof en> = {
ctaFix: 'Beheben',
fixCompanyCardConnection: {
title: ({feedName}: {feedName: string}) => (feedName ? `${feedName}-Firmenkartenverbindung reparieren` : 'Firmenkarte reparieren Verbindung der Firmenkarte reparieren'),
- defaultSubtitle: 'Workspace > Unternehmenskarten',
- subtitle: ({policyName}: {policyName: string}) => `${policyName} > Unternehmenskarten`,
+ defaultSubtitle: 'Arbeitsbereich > Firmenkarten',
+ subtitle: ({policyName}: {policyName: string}) => `${policyName} > Firmenkarten`,
},
fixAccountingConnection: {
title: ({integrationName}: {integrationName: string}) => `${integrationName}-Verbindung reparieren`,
@@ -5172,6 +5172,8 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
body: 'Sie haben noch einen offenen Reisensaldo. Bitte begleichen Sie zuerst Ihren Saldo.',
confirm: 'Verstanden',
},
+ enabled: 'Zentrale Rechnungsstellung aktiviert!',
+ enabledDescription: 'Alle Reisekosten in diesem Workspace werden nun in einer monatlichen Rechnung zentralisiert.',
},
},
expensifyCard: {
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 12d92d6b..a66e007c 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -959,7 +959,7 @@ const translations: TranslationDeepObject<typeof en> = {
fixCompanyCardConnection: {
title: ({feedName}: {feedName: string}) => (feedName ? `Corriger la connexion de la carte d'entreprise ${feedName}` : 'Corriger la connexion de la carte entreprise'),
defaultSubtitle: 'Espace de travail > Cartes d’entreprise',
- subtitle: ({policyName}: {policyName: string}) => `${policyName} > Cartes d'entreprise`,
+ subtitle: ({policyName}: {policyName: string}) => `${policyName} > Cartes d’entreprise`,
},
fixAccountingConnection: {
title: ({integrationName}: {integrationName: string}) => `Corriger la connexion ${integrationName}`,
@@ -5190,6 +5190,8 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
body: 'Vous avez encore un solde de voyage impayé. Veuillez d’abord régler ce solde.',
confirm: 'Compris',
},
+ enabled: 'Facturation centralisée activée !',
+ enabledDescription: 'Toutes les dépenses de voyage sur cet espace de travail seront désormais centralisées dans une facture mensuelle.',
},
},
expensifyCard: {
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 71f57aba..95930d19 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -959,7 +959,7 @@ const translations: TranslationDeepObject<typeof en> = {
},
fixAccountingConnection: {
title: ({integrationName}: {integrationName: string}) => `Correggi connessione ${integrationName}`,
- defaultSubtitle: 'Spazio di lavoro > Contabilità',
+ defaultSubtitle: 'Area di lavoro > Contabilità',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Contabilità`,
},
fixPersonalCardConnection: {
@@ -5162,6 +5162,8 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
body: 'Hai ancora un saldo di viaggio in sospeso. Paga prima il tuo saldo.',
confirm: 'Capito',
},
+ enabled: 'Fatturazione centralizzata abilitata!',
+ enabledDescription: 'Tutte le spese di viaggio in questo spazio di lavoro saranno ora centralizzate in una fattura mensile.',
},
},
expensifyCard: {
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 922ebd79..4617f55f 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -949,7 +949,7 @@ const translations: TranslationDeepObject<typeof en> = {
ctaFix: '修正',
fixCompanyCardConnection: {
title: ({feedName}: {feedName: string}) => (feedName ? `${feedName} 会社カード接続を修正` : '法人クレジットカードの接続を修正'),
- defaultSubtitle: 'ワークスペース > 会社カード',
+ defaultSubtitle: 'ワークスペース > 会社カード',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > 会社カード`,
},
fixAccountingConnection: {
@@ -959,7 +959,7 @@ const translations: TranslationDeepObject<typeof en> = {
},
fixPersonalCardConnection: {
title: ({cardName}: {cardName?: string}) => (cardName ? `${cardName}個人カードの接続を修正` : '個人カードの連携を修正'),
- subtitle: 'ウォレット > 割り当てられたカード',
+ subtitle: 'ウォレット > 割り当てられたカード',
},
},
assignedCards: '割り当て済みカード',
@@ -5108,6 +5108,8 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO
confirm: 'オフにする',
},
outstandingBalanceModal: {title: 'トラベル請求書作成をオフにできません', body: '未清算の出張残高があります。先に残高を精算してください。', confirm: '了解しました'},
+ enabled: '集中請求が有効になりました!',
+ enabledDescription: 'このワークスペースのすべての出張費は、今後、月次請求書で一元管理されます。',
},
},
expensifyCard: {
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 913f1975..a0737c56 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -953,7 +953,7 @@ const translations: TranslationDeepObject<typeof en> = {
ctaFix: 'Repareren',
fixCompanyCardConnection: {
title: ({feedName}: {feedName: string}) => (feedName ? `Verbinding bedrijfskaart ${feedName} herstellen` : 'Verbinding van bedrijfskaart repareren'),
- defaultSubtitle: 'Werkruimte > Bedrijfspassen',
+ defaultSubtitle: 'Workspace > Bedrijfskaarten',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Bedrijfspassen`,
},
fixAccountingConnection: {
@@ -963,7 +963,7 @@ const translations: TranslationDeepObject<typeof en> = {
},
fixPersonalCardConnection: {
title: ({cardName}: {cardName?: string}) => (cardName ? `Verbinding van persoonlijke kaart ${cardName} herstellen` : 'Verbinding persoonlijke kaart herstellen'),
- subtitle: 'Wallet > Toegewezen kaarten',
+ subtitle: 'Portemonnee > Toegewezen kaarten',
},
},
assignedCards: 'Toegewezen kaarten',
@@ -5144,6 +5144,8 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
confirm: 'Uitschakelen',
},
outstandingBalanceModal: {title: 'Kan Reiskostenfacturatie niet uitschakelen', body: 'Je hebt nog een openstaand reissaldo. Betaal eerst je saldo.', confirm: 'Begrepen'},
+ enabled: 'Centraal factureren ingeschakeld!',
+ enabledDescription: 'Alle reiskosten in deze workspace worden nu gebundeld op één maandelijkse factuur.',
},
},
expensifyCard: {
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index c653a278..bb9c0569 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -955,7 +955,7 @@ const translations: TranslationDeepObject<typeof en> = {
fixCompanyCardConnection: {
title: ({feedName}: {feedName: string}) => (feedName ? `Napraw połączenie karty firmowej ${feedName}` : 'Napraw połączenie karty firmowej'),
defaultSubtitle: 'Workspace > Karty firmowe',
- subtitle: ({policyName}: {policyName: string}) => `${policyName} > Karty firmowe`,
+ subtitle: ({policyName}: {policyName: string}) => `${policyName} > Firmowe karty`,
},
fixAccountingConnection: {
title: ({integrationName}: {integrationName: string}) => `Napraw połączenie ${integrationName}`,
@@ -5138,6 +5138,8 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
body: 'Masz nadal zaległe saldo za podróż. Najpierw ureguluj swoje saldo.',
confirm: 'Rozumiem',
},
+ enabled: 'Centralne fakturowanie włączone!',
+ enabledDescription: 'Wszystkie wydatki podróżne w tym obszarze roboczym będą teraz rozliczane na scentralizowanej, miesięcznej fakturze.',
},
},
expensifyCard: {
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index dfa3650b..17312517 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -952,7 +952,7 @@ const translations: TranslationDeepObject<typeof en> = {
ctaFix: 'Corrigir',
fixCompanyCardConnection: {
title: ({feedName}: {feedName: string}) => (feedName ? `Corrigir conexão do cartão corporativo ${feedName}` : 'Corrigir conexão do cartão corporativo'),
- defaultSubtitle: 'Área de trabalho > Cartões corporativos',
+ defaultSubtitle: 'Espaço de trabalho > Cartões corporativos',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > Cartões corporativos`,
},
fixAccountingConnection: {
@@ -5144,6 +5144,8 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS
body: 'Você ainda tem um saldo de viagem pendente. Pague esse saldo primeiro.',
confirm: 'Entendi',
},
+ enabled: 'Faturamento centralizado ativado!',
+ enabledDescription: 'Todos os gastos de viagem neste workspace agora serão centralizados em uma fatura mensal.',
},
},
expensifyCard: {
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index dc6a168f..876fa7da 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -940,13 +940,13 @@ const translations: TranslationDeepObject<typeof en> = {
fixCompanyCardConnection: {
title: ({feedName}: {feedName: string}) => (feedName ? `修复 ${feedName} 公司卡连接` : '修复公司卡连接'),
defaultSubtitle: '工作区 > 公司卡片',
- subtitle: ({policyName}: {policyName: string}) => `${policyName} > 公司卡片`,
+ subtitle: ({policyName}: {policyName: string}) => `${policyName} > 公司卡`,
},
- fixPersonalCardConnection: {title: ({cardName}: {cardName?: string}) => (cardName ? `修复 ${cardName} 个人卡连接` : '修复个人银行卡连接'), subtitle: '钱包 > 已分配的卡片'},
+ fixPersonalCardConnection: {title: ({cardName}: {cardName?: string}) => (cardName ? `修复 ${cardName} 个人卡连接` : '修复个人银行卡连接'), subtitle: '钱包 > 已分配的卡片'},
fixAccountingConnection: {
title: ({integrationName}: {integrationName: string}) => `修复 ${integrationName} 连接`,
defaultSubtitle: '工作区 > 会计',
- subtitle: ({policyName}: {policyName: string}) => `${policyName} > 会计`,
+ subtitle: ({policyName}: {policyName: string}) => `${policyName} > 会计`,
},
},
assignedCards: '已分配的卡片',
@@ -5030,6 +5030,8 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
},
disableModal: {title: '关闭差旅开票?', body: '即将到来的酒店和汽车租赁预订可能需要使用不同的付款方式重新预订,以避免被取消。', confirm: '关闭'},
outstandingBalanceModal: {title: '无法关闭差旅开票', body: '你仍有未结清的差旅余额。请先支付该余额。', confirm: '明白了'},
+ enabled: '中央开票已启用!',
+ enabledDescription: '此工作区的所有差旅支出现在将集中在一张月度发票中。',
},
},
expensifyCard: {
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
# Conflicts: # src/languages/de.ts # src/languages/fr.ts # src/languages/it.ts # src/languages/ja.ts # src/languages/nl.ts # src/languages/pl.ts # src/languages/pt-BR.ts # src/languages/zh-hans.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This reverts commit 676ad4c.
|
spanish translation help here: https://expensify.slack.com/archives/C01GTK53T8Q/p1772484760754349 |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7ce290bfda
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
src/pages/workspace/travel/WorkspaceTravelInvoicingSettlementAccountPage.tsx
Outdated
Show resolved
Hide resolved
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
allgandalf
left a comment
There was a problem hiding this comment.
Looks good! Just a few minor suggestions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| // Has settlement account - enable Travel Invoicing directly | ||
| toggleTravelInvoicing(policyID, workspaceAccountID, true); | ||
| // Has settlement account - enable Travel Invoicing and navigate to settlement page to show verification state | ||
| const existingPaymentBankAccountID = cardSettings?.[CONST.TRAVEL.PROGRAM_TRAVEL_US]?.paymentBankAccountID ?? cardSettings?.paymentBankAccountID ?? settlementAccount?.bankAccountID; |
There was a problem hiding this comment.
Do we still need the fallback after the backend changes?
There was a problem hiding this comment.
We won't, but the backend changes for the expensifyCardSettings NVP aren't merged yet, so this would break if we did. I'm going to push up a change that will make it work for both versions of the backend.
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppAndroid: mWeb ChromeiOS: HybridAppiOS: mWeb SafariMacOS: Chrome / Safari |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ikevin127
left a comment
There was a problem hiding this comment.
🟢 LGTM
Definitely needed as discussed before during Travel Invoicing – Release 2.2: Toggle Behavior (Enable / Disable Travel Invoicing), thanks for opening this one! 🙌
|
🎯 @ikevin127, thanks for reviewing and testing this PR! 🎉 An E/App issue has been created to issue payment here: #84240. |
src/libs/TravelInvoicingUtils.ts
Outdated
| // Merge root settings with TRAVEL_US so partial optimistic updates (e.g. only isEnabled) still | ||
| // inherit other fields like monthlySettlementDate from the root. | ||
| return {...cardSettings, ...cardSettings.TRAVEL_US}; | ||
| return cardSettings?.TRAVEL_US; |
There was a problem hiding this comment.
Why we dont want to use the getCardSettings?
There was a problem hiding this comment.
Using it in TravelInvoicingUtils causes a very messy transitive dependency issue, so the options were to either revert it to this or get rid of getTravelSettings entirely and replace it with getCardSettings. There were other options like using a ton of mocks to make the transitive dependency not break our tests, but it was really messy.
There was a problem hiding this comment.
Open to changing it!
There was a problem hiding this comment.
Okay, latest commit redoes all of this. Way more of a pain to deal with than I thought it would be. I've tested it though and it works.
There was a problem hiding this comment.
Aaaaaaand there's merge conflicts
allgandalf
left a comment
There was a problem hiding this comment.
LGTM! Though @rlinoz 's comment ^ does make sense
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚧 @blimpich has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/blimpich in version: 9.3.32-0 🚀
|
|
🚀 Deployed to production by https://github.com/blimpich in version: 9.3.32-3 🚀
|
Explanation of Change
This PR finishes the core work of #81470, which makes our core Travel Invoicing flow 1:1:1. Instead of using the
ToggleTravelInvoicingcommand to turn the feature on and off, we now instead use two separate commands:ConfigureTravelInvoicingForPolicyandDeactivateTravelInvoicing. It also adds logic to the frontend to handle the "verifying" state of a bank account, where a bank account can be waiting to be verified by our internal tools and therefore we need to communicate to the user that their request is in transit and being looked at.Fixed Issues
#81470
PROPOSAL:
Tests
Note: here's how to create bank account locally that will work for this:
Verifying state
Note: you'll want the domain of the user to be fresh since Travel won't like it if you use a domain it's already seen before. So use a domain like
@testN.comwhere N is the iteration of your test.Success state
Do the exact same thing as the verifying state except add your user to the
allowSameExpensifyCardSettlementBBAbeta, and make sure that you mark the bank account as "validated" when running the bank account generator script.Offline tests
QA Steps
// TODO: These must be filled out, or the issue title must include "[No QA]."
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps./** comment above it */thisproperly so there are no scoping issues (i.e. foronClick={this.submit}the methodthis.submitshould be bound tothisin the constructor)thisare necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);ifthis.submitis never passed to a component event handler likeonClick)Screenshots/Videos
555674156-e6a66fd0-d7b4-4684-bccb-b5fcbcd20270.mov
Screen.Recording.2026-02-26.at.3.01.27.PM.mov