[HOLD][No QA] CTB net-30 implementation#96146
Conversation
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6acaf3eb1e
ℹ️ 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".
| const formattedSpend = convertToDisplayString(travelSpend, CONST.CURRENCY.USD); | ||
|
|
||
| // Pay-by-invoice customers settle by wire against an invoice, so the pay CTA and modal use invoice copy | ||
| const isPayByInvoice = getIsTravelBillingPayByInvoice(travelSettings); |
There was a problem hiding this comment.
Use invoice copy for pending settlements
When invoiceTo is set, confirming the “Send invoice now” modal still calls payTravelInvoicingSpend, which optimistically sets pendingSettlementAmount; this same component then hides the button and renders the existing currentTravelSpendPaymentQueued subtitle, so invoice customers see “Payment of X is queued and will be processed soon” after sending an invoice. Please apply the pay-by-invoice branch to the pending-settlement subtitle as well so the post-confirmation state does not describe an ACH payment.
Useful? React with 👍 / 👎.
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
@flaviadefaria @deetergp please ignore the ping, this is a WIP PR |
This comment has been minimized.
This comment has been minimized.
…lyTravelBilling # Conflicts: # src/pages/workspace/travel/WorkspaceTravelInvoicingSection.tsx
…ware visibility check The pay-by-invoice CTA text was silently dropped due to a nonexistent Button prop, always rendering "Pay balance" instead of "Send invoice now". The button's visibility check also ignored pendingInvoiceAmount, showing it even when there was no new spend beyond an invoice already sent and awaiting payment.
…-by-invoice workspaces Queuing an early bill via "Send invoice now" reused the same "Payment is queued" copy as ACH settlement, which is misleading since no automatic debit happens for pay-by-invoice customers.
Translation bot handles non-English locale files; only en.ts should be edited by hand.
🦜 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 85f68ad8b9c..30d9ad5b54b 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -6071,8 +6071,8 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
subsections: {
currentTravelSpendLabel: 'Aktuelle Reisekosten',
currentTravelSpendPaymentQueued: (amount: string) => `Die Zahlung über ${amount} ist in der Warteschlange und wird in Kürze bearbeitet.`,
- currentTravelSpendInvoiceQueued: 'Eine neue Rechnung für Ihre Reisekosten wird erstellt und Ihnen in Kürze zugesendet.',
- currentTravelSpendInvoicePending: (amount: string) => `Eine Rechnung über ${amount} wurde gesendet und wartet auf Zahlung.`,
+ currentTravelSpendInvoiceQueued: 'Eine neue Rechnung für Ihre Reisekosten wird erstellt und Ihnen in Kürze zugesandt.',
+ currentTravelSpendInvoicePending: (amount: string) => `Eine Rechnung über ${amount} wurde gesendet und wartet auf Bezahlung.`,
currentTravelSpendCta: 'Saldo bezahlen',
currentTravelLimitLabel: 'Aktuelles Reisekontingent',
settlementAccountLabel: 'Verrechnungskonto',
@@ -6110,9 +6110,9 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
enabledDescription: 'Alle Reisekosten in diesem Workspace werden nun in einer monatlichen Rechnung zentralisiert.',
sendInvoiceModal: {
title: (amount: string) => `Rechnung über ${amount} senden?`,
- body: 'Wir erstellen eine Rechnung für Ihre aktuellen Reisekosten. Ihr Reisebudget wird wieder freigegeben, sobald die Rechnung bezahlt ist.',
+ body: 'Wir erstellen eine Rechnung für Ihre aktuellen Reisekosten. Ihr Reiselimit wird wieder frei, sobald die Rechnung bezahlt ist.',
},
- depositOnly: 'Nur Einzahlung',
+ depositOnly: 'Nur Einzahlungen',
},
personalDetailsDescription: 'Um eine Reise zu buchen, gib bitte deinen amtlichen Namen genau so ein, wie er auf deinem amtlichen Ausweis steht.',
},
diff --git a/src/languages/es.ts b/src/languages/es.ts
index c3e4a8892a0..8061810bca0 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -5904,7 +5904,7 @@ ${amount} para ${merchant} - ${date}`,
subsections: {
currentTravelSpendLabel: 'Gasto actual en viajes',
currentTravelSpendPaymentQueued: (amount: string) => `El pago de ${amount} está en cola y se procesará pronto.`,
- currentTravelSpendInvoiceQueued: 'Se creará y enviará pronto una nueva factura por tu gasto de viaje.',
+ currentTravelSpendInvoiceQueued: 'Pronto se creará y enviará una nueva factura por tus gastos de viaje.',
currentTravelSpendInvoicePending: (amount: string) => `Se ha enviado una factura por ${amount} y está a la espera de pago.`,
currentTravelSpendCta: 'Pagar saldo',
sendInvoiceNowCta: 'Enviar factura ahora',
@@ -5937,7 +5937,7 @@ ${amount} para ${merchant} - ${date}`,
},
sendInvoiceModal: {
title: (amount: string) => `¿Enviar factura por ${amount}?`,
- body: 'Crearemos una factura por tus gastos de viaje actuales. Tu límite de viaje se liberará una vez que se pague la factura.',
+ body: 'Crearemos una factura por tus gastos de viaje actuales. Tu límite de viaje se libera una vez que se pague la factura.',
},
exportToPDF: 'Exportar a PDF',
exportToCSV: 'Exportar a CSV',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index c769f19964e..93a509905a7 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -6130,8 +6130,8 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
enabled: 'Facturation de voyage consolidée activée !',
enabledDescription: 'Toutes les dépenses de voyage sur cet espace de travail seront désormais centralisées dans une facture mensuelle.',
sendInvoiceModal: {
- title: (amount: string) => `Envoyer la facture pour ${amount} ?`,
- body: 'Nous créerons une facture pour vos dépenses de voyage actuelles. Votre plafond de voyage sera à nouveau disponible une fois la facture payée.',
+ title: (amount: string) => `Envoyer la facture de ${amount} ?`,
+ body: 'Nous créerons une facture pour vos dépenses de voyage actuelles. Votre plafond de voyage est à nouveau disponible une fois la facture payée.',
},
depositOnly: 'Dépôt uniquement',
},
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 9e21cf9c751..11d9e866c6f 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -6053,7 +6053,7 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
subsections: {
currentTravelSpendLabel: 'Spesa di viaggio attuale',
currentTravelSpendPaymentQueued: (amount: string) => `Il pagamento di ${amount} è in coda e verrà elaborato a breve.`,
- currentTravelSpendInvoiceQueued: 'Una nuova fattura per la tua spesa di viaggio verrà creata e inviata a breve.',
+ currentTravelSpendInvoiceQueued: 'Una nuova fattura per le tue spese di viaggio verrà creata e ti sarà inviata a breve.',
currentTravelSpendInvoicePending: (amount: string) => `Una fattura di ${amount} è stata inviata ed è in attesa di pagamento.`,
currentTravelSpendCta: 'Paga saldo',
currentTravelLimitLabel: 'Limite di viaggio attuale',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 92e886807eb..cdf13e0f50a 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -5982,7 +5982,7 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO
subsections: {
currentTravelSpendLabel: '現在の出張費支出',
currentTravelSpendPaymentQueued: (amount: string) => `${amount} の支払いはキューに登録されており、まもなく処理されます。`,
- currentTravelSpendInvoiceQueued: '出張費用の新しい請求書がまもなく作成され、送付されます。',
+ currentTravelSpendInvoiceQueued: '出張費用の新しい請求書が作成され、まもなくお客様に送信されます。',
currentTravelSpendInvoicePending: (amount: string) => `${amount} の請求書が送信され、支払い待ちです。`,
currentTravelSpendCta: '残高を支払う',
currentTravelLimitLabel: '現在の出張上限',
@@ -6015,10 +6015,10 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO
enabled: '一括出張請求が有効になりました!',
enabledDescription: 'このワークスペースでの出張費用は、今後すべて月次の請求書に集約されます。',
sendInvoiceModal: {
- title: (amount: string) => `${amount} の請求書を送信しますか?`,
+ title: (amount: string) => `${amount}の請求書を送信しますか?`,
body: '現在の出張費用について請求書を作成します。請求書が支払われると、出張の利用可能枠が再び使えるようになります。',
},
- depositOnly: '入金のみ',
+ depositOnly: '入金専用',
},
personalDetailsDescription: '旅行を予約するために、政府発行の身分証明書に記載されているとおりの正式な氏名を入力してください。',
},
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 4eec5156fa5..fbc3d335719 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -6042,7 +6042,7 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
subsections: {
currentTravelSpendLabel: 'Huidige reiskosten',
currentTravelSpendPaymentQueued: (amount: string) => `Betaling van ${amount} staat in de wachtrij en wordt binnenkort verwerkt.`,
- currentTravelSpendInvoiceQueued: 'Er wordt binnenkort een nieuwe factuur voor uw reisuitgaven aangemaakt en naar u verzonden.',
+ currentTravelSpendInvoiceQueued: 'Er wordt binnenkort een nieuwe factuur voor je reiskosten aangemaakt en naar je verzonden.',
currentTravelSpendInvoicePending: (amount: string) => `Er is een factuur voor ${amount} verzonden en deze wacht op betaling.`,
currentTravelSpendCta: 'Saldo betalen',
currentTravelLimitLabel: 'Huidige reisl imiet',
@@ -6055,7 +6055,7 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
reduceLimitWarning: 'Als u het limiet verlaagt, kunnen leden die dit bedrag al hebben overschreden geen nieuwe reisboekingen maken tot volgende maand.',
provisioningError:
'We konden voor sommige leden van je werkruimte geen toegang instellen tot Geconsolideerde Reisfacturering. Probeer het later opnieuw of neem contact op met Concierge voor ondersteuning.',
- sendInvoiceNowCta: 'Factuur nu verzenden',
+ sendInvoiceNowCta: 'Verzend factuur nu',
},
},
disableModal: {
@@ -6080,7 +6080,7 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
enabledDescription: 'Alle reiskosten in deze workspace worden nu gebundeld op één maandelijkse factuur.',
sendInvoiceModal: {
title: (amount: string) => `Factuur voor ${amount} versturen?`,
- body: 'We maken een factuur voor je huidige reiskosten. Je reistegoed komt weer vrij zodra de factuur is betaald.',
+ body: 'We maken een factuur aan voor je huidige reiskosten. Je reistegoed komt weer vrij zodra de factuur is betaald.',
},
depositOnly: 'Alleen storting',
},
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index ed764b99d87..5f85d843b68 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -6021,7 +6021,7 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
subsections: {
currentTravelSpendLabel: 'Aktualne wydatki na podróże',
currentTravelSpendPaymentQueued: (amount: string) => `Płatność w wysokości ${amount} jest w kolejce i wkrótce zostanie przetworzona.`,
- currentTravelSpendInvoiceQueued: 'Wkrótce zostanie utworzona i wysłana do Ciebie nowa faktura za wydatki podróżne.',
+ currentTravelSpendInvoiceQueued: 'Nowa faktura za twoje wydatki związane z podróżą zostanie wkrótce utworzona i wysłana do ciebie.',
currentTravelSpendInvoicePending: (amount: string) => `Faktura na kwotę ${amount} została wysłana i oczekuje na płatność.`,
currentTravelSpendCta: 'Spłać saldo',
currentTravelLimitLabel: 'Aktualny limit podróży',
@@ -6059,8 +6059,8 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
enabled: 'Włączono zbiorcze rozliczanie podróży!',
enabledDescription: 'Wszystkie wydatki podróżne w tym obszarze roboczym będą teraz scentralizowane na miesięcznym rachunku.',
sendInvoiceModal: {
- title: (amount: string) => `Wysłać fakturę na kwotę ${amount}?`,
- body: 'Utworzymy fakturę za twoje bieżące wydatki na podróż. Twój limit podróży zwolni się, gdy faktura zostanie opłacona.',
+ title: (amount: string) => `Wysłać fakturę na ${amount}?`,
+ body: 'Utworzymy fakturę za twoje bieżące wydatki na podróż. Twój limit podróży zostanie zwolniony, gdy faktura zostanie opłacona.',
},
depositOnly: 'Tylko wpłata',
},
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 431b5426f13..da8312a146f 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -6034,7 +6034,7 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS
subsections: {
currentTravelSpendLabel: 'Gasto atual com viagens',
currentTravelSpendPaymentQueued: (amount: string) => `O pagamento de ${amount} está na fila e será processado em breve.`,
- currentTravelSpendInvoiceQueued: 'Uma nova fatura para o seu gasto de viagem será criada e enviada a você em breve.',
+ currentTravelSpendInvoiceQueued: 'Uma nova fatura dos seus gastos de viagem será criada e enviada para você em breve.',
currentTravelSpendInvoicePending: (amount: string) => `Uma fatura de ${amount} foi enviada e está aguardando pagamento.`,
currentTravelSpendCta: 'Pagar saldo',
currentTravelLimitLabel: 'Limite de viagem atual',
@@ -6073,7 +6073,7 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS
enabledDescription: 'Todos os gastos de viagem neste workspace agora serão centralizados em uma fatura mensal.',
sendInvoiceModal: {
title: (amount: string) => `Enviar fatura de ${amount}?`,
- body: 'Vamos criar uma fatura para seus gastos atuais de viagem. Seu limite de viagem é liberado assim que a fatura é paga.',
+ body: 'Vamos criar uma fatura para seus gastos atuais de viagem. Seu limite de viagem será liberado assim que a fatura for paga.',
},
depositOnly: 'Apenas depósito',
},
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 20533d30b57..0fb5b49e812 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -5854,7 +5854,7 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
subsections: {
currentTravelSpendLabel: '当前差旅支出',
currentTravelSpendPaymentQueued: (amount: string) => `金额为 ${amount} 的付款已排队,稍后将被处理。`,
- currentTravelSpendInvoiceQueued: '您差旅支出的新发票即将生成并发送给您。',
+ currentTravelSpendInvoiceQueued: '您的差旅费用新发票将很快创建并发送给您。',
currentTravelSpendInvoicePending: (amount: string) => `金额为 ${amount} 的发票已发送,正在等待付款。`,
currentTravelSpendCta: '支付余额',
currentTravelLimitLabel: '当前出行限额',
@@ -5878,8 +5878,8 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
invalidDateRangeError: '开始日期必须早于结束日期',
enabled: '已启用合并差旅账单!',
enabledDescription: '此工作区的所有差旅支出现在将统一汇总到一份月度账单中。',
- sendInvoiceModal: {title: (amount: string) => `发送金额为 ${amount} 的发票?`, body: '我们会根据你当前的差旅支出创建一张发票。发票支付后,你的差旅额度将被释放。'},
- depositOnly: '仅限存款',
+ sendInvoiceModal: {title: (amount: string) => `要发送金额为 ${amount} 的发票吗?`, body: '我们会为您当前的差旅支出创建一张发票。发票付清后,您的差旅额度将被释放。'},
+ depositOnly: '仅存款',
},
personalDetailsDescription: '为预订行程,请输入您在政府签发的身份证件上显示的法定姓名。',
},
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
Explanation of Change
Part of the Consolidated Travel Billing pay-by-invoice project (plan in the linked issue). A small set of covenant customers can't be auto-debited for their monthly travel spend: they settle from a deposit-only bank account by paying an invoice by wire.
Two UI changes, both inert until the backend enables them (keyed off
invoiceToin the workspace's travel settings, the client-visible pay-by-invoice hint):hasOutstandingTravelBalancetrue so Consolidated Travel Billing can't be disabled with money still owed.Fixed Issues
https://github.com/Expensify/Expensify/issues/657008 (partial — App portion; intentionally not using
$so the issue doesn't auto-close)PROPOSAL:
Tests
Requires a dev backend with the Auth/Web pay-by-invoice changes.
invoiceToin the workspace's TRAVEL_US card settings (pay-by-invoice config).pendingInvoiceAmountset) and verify the section shows an "awaiting payment" row for that amount and the pay button is hidden.invoiceToand verify the button reverts to "Pay balance" with the original modal copy.Offline tests
QA Steps
None: this UI is gated behind server-side pay-by-invoice configuration (
invoiceTo), which doesn't exist on staging yet.PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, 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.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
– written by Claude on Ben's behalf