Skip to content

[HOLD] Show and action Bill Pay reports in New Expensify - #102113

Open
cead22 wants to merge 15 commits into
mainfrom
carlos-billpay
Open

cead22 wants to merge 15 commits into
mainfrom
carlos-billpay

Conversation

@cead22

@cead22 cead22 commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

@cead22
cead22 requested review from a team as code owners September 24, 2026 01:17
@melvin-bot
melvin-bot Bot requested review from dylanexpensify and rlinoz and removed request for a team September 24, 2026 01:17
@melvin-bot

melvin-bot Bot commented Sep 24, 2026

Copy link
Copy Markdown

@rlinoz Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@melvin-bot
melvin-bot Bot removed the request for review from a team September 24, 2026 01:17
@cead22
cead22 removed the request for review from dylanexpensify September 24, 2026 01:18
@OSBotify

This comment has been minimized.

@cead22
cead22 removed the request for review from rlinoz September 24, 2026 01:18
@codecov

codecov Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.

Files with missing lines Coverage Δ
src/CONST/index.ts 95.90% <ø> (ø)
src/ONYXKEYS.ts 100.00% <ø> (ø)
src/ROUTES.ts 32.57% <ø> (ø)
src/SCREENS.ts 100.00% <ø> (ø)
...components/Search/FilterDropdowns/DisplayPopup.tsx 52.63% <100.00%> (+48.63%) ⬆️
...h/SearchList/ListItem/ActionCell/PayActionCell.tsx 79.66% <100.00%> (-14.99%) ⬇️
...ponents/Search/SearchList/ListItem/GroupHeader.tsx 83.45% <100.00%> (ø)
...h/SearchList/ListItem/TransactionGroupListItem.tsx 74.40% <100.00%> (ø)
src/components/Search/hooks/useSearchSnapshot.ts 94.50% <100.00%> (ø)
src/components/Search/index.tsx 70.82% <100.00%> (ø)
... and 37 more
... and 70 files with indirect coverage changes

Comment thread src/types/onyx/Report.ts Outdated
/** Invoice room receiver data */
invoiceReceiver?: InvoiceReceiver;

billID?: string;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ CONSISTENCY-10 (docs)

These six new Report properties are added with no /** ... */ documentation, yet they sit directly among documented siblings (invoiceReceiver, transactionCount, managerID) and each carries a non-obvious fact the reader cannot infer from the name and type alone. isHiddenForBillReceiver gates whether the linked invoice is hidden from the LHN, search sections, and report previews; isBillPayReport reclassifies an invoice report as a bill-pay report; billSenderAccountID/billReceiverAccountID override ownerAccountID/managerID for the Bills From/To display; and billID/invoiceID link the two paired reports together. Document each with a block comment stating its role, e.g.:

/** ID of the bill report this invoice is linked to */
billID?: string;

/** ID of the invoice report paired with this bill */
invoiceID?: string;

/** Account ID shown in the Bills "From" column, overriding ownerAccountID */
billSenderAccountID?: number;

/** Account ID shown in the Bills "To" column, overriding managerID */
billReceiverAccountID?: number;

/** Whether this invoice report should be treated as a bill-pay report */
isBillPayReport?: boolean;

/** Whether this linked invoice is hidden from the receiver's LHN, search, and previews */
isHiddenForBillReceiver?: boolean;

Reviewed at: 4873eb4 | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4873eb48dc

ℹ️ 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".

Comment on lines +22 to +23
if (canApproveBill(report, accountID)) {
billsApprove++;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Wire bill approval eligibility into action flows

For a submitted bill with no transaction records, canApproveBill marks it as approvable and this counter exposes the Bills approval view, but the actual header, preview, and Search action predicates still use canApproveIOU/isApproveAction, which require a nonempty transaction list. Consequently, an approver can reach the bill but receives no Approve action, so it cannot advance to the payment stage. Use the bill-specific approval predicate in those action paths as well, rather than only counting it here.

Useful? React with 👍 / 👎.

@cead22 cead22 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Inline context for the Bill Pay changes, requested to help review. Each note explains the code and links its issue or Slack requirement. Notes also identify places where later requirements still need follow-up.

@@ -0,0 +1,112 @@
/** Pays bills and received invoices without depending on a parent room. */

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Adds a shared ACH/Mark as paid menu for bills and invoices. It uses the report directly, so payment does not depend on a parent room.

Context: Approval and payment requirements, No parent room requirement.

const shouldDisplayWorkspaceFilter = workspaces.some((section) => section.data.length > 1);

let currentType = type ?? CONST.SEARCH.DATA_TYPES.EXPENSE;
let currentType = type === CONST.SEARCH.DATA_TYPES.BILL ? CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT : (type ?? CONST.SEARCH.DATA_TYPES.EXPENSE);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Reuses expense-report filters so Bills has an advanced-filter menu. The issue specifically asks for invoice-filter parity, so this mapping needs review against that requirement.

Context: Columns and filters requirements.

@cead22 cead22 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Inline context for the Bill Pay changes, requested to help review. Each note explains the code and links its issue or Slack requirement. Notes also identify places where later requirements still need follow-up.

Comment thread src/libs/ReportUtils.ts
function isExpenseReport(reportOrID: OnyxInputOrEntry<Report> | string): boolean {
const report = typeof reportOrID === 'string' ? (getReport(reportOrID, deprecatedAllReports) ?? null) : reportOrID;
return report?.type === CONST.REPORT.TYPE.EXPENSE;
return report?.type === CONST.REPORT.TYPE.EXPENSE || report?.type === CONST.REPORT.TYPE.BILL;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Lets bills reuse expense-report behavior, including approval and export UI. This is a broad helper change, so callers need to retain Classic parity and use the dedicated bill path where payment differs.

Context: Approval and payment requirements.

Comment thread src/libs/SearchUIUtils.ts
case CONST.SEARCH.DATA_TYPES.EXPENSE:
return CONST.SEARCH.TYPE_DEFAULT_COLUMNS.EXPENSE;
case CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT:
case CONST.SEARCH.DATA_TYPES.BILL:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Supplies the report-column defaults for Bills. Review these defaults against the issue’s seven required bill columns; this should not introduce extra product fields.

Context: Columns and filters requirements.

Comment thread src/libs/SearchUIUtils.ts
}
}

if (hasBills) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Adds all three searches when hasBills is true. This implements the initial content gate, but not the later agreement to seed each search separately by stable approval/payment eligibility.

Context: Bills search requirements, Stable search eligibility.

Comment thread src/libs/actions/IOU/ReportWorkflow.ts Outdated
const iouSettled = isSettled(iouReport);

if (isEmptyObject(iouReport)) {
if (isEmptyObject(iouReport) || iouReport.isHiddenForBillReceiver) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Routes bills through their eligibility helper and rejects hidden invoices. This early return currently skips the later disabled-payments check, so it needs alignment with the Slack rule that disabled payments show no Pay button.

Context: Payment permissions clarification.

};
break;
case CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT:
case CONST.SEARCH.DATA_TYPES.BILL:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Uses report-style empty-state handling for Bills. The issue calls for bill-specific approval/payment copy; adding this case alone does not add that copy.

Context: Bills search requirements.

singleReportIDs[CONST.SEARCH.SEARCH_KEYS.EXPORT],
),
},
{

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Adds Pay N bills and opens Ready to pay with this user’s payer filter. The issue also asks for Approve N bills; that separate Home entry is not added here.

Context: Home and Inbox requirements.

@cead22 cead22 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Inline context for the Bill Pay changes, requested to help review. Each note explains the code and links its issue or Slack requirement. Notes also identify places where later requirements still need follow-up.

}
payBill(report, paymentMethod, bankAccountID, searchHash, currentSearchKey);
};
const bankOptions = Object.values(bankAccounts ?? {})

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Offers accessible, open USD debit accounts and excludes card-settlement accounts. Slack clarifies that payment admins may use any bank account they can access, not only the workspace account.

Context: Bank access clarification.

Comment thread src/libs/BillPayUtils.ts
return report?.type === CONST.REPORT.TYPE.BILL;
}

function isBillPayReport(report: OnyxEntry<Report>, accountID: number): boolean {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Includes bills and received standalone invoices, but rejects invoice counterparts hidden for this viewer. Auth’s flag handles workspace receivers that a simple manager-ID check cannot identify.

Context: Search clarification.

Comment thread src/libs/BillPayUtils.ts
return isBillReport(report) && report?.managerID === accountID && report.stateNum === CONST.REPORT.STATE_NUM.SUBMITTED && report.statusNum === CONST.REPORT.STATUS_NUM.SUBMITTED;
}

function canPayBill(report: OnyxEntry<Report>, policy: OnyxEntry<Policy> | null, accountID: number, email: string): boolean {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Checks amount, state, bank setup, workspace state, and payer. Follow-up: this function serves both buttons and todos, but Slack now gives workspace/payment admins broader payment rights and disables payment when workspace payments are off.

Context: Payment permissions clarification, Bank access clarification.

return this.searchQueryJSON?.recentSearchHash ?? CONST.DEFAULT_NUMBER_ID;
},
},
[CONST.SEARCH.SEARCH_KEYS.BILLS_PAY]: {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Adds action:pay and the current payer to Ready to pay. This is todo assignment, which Slack distinguishes from the broader permission to use a Pay button.

Context: Payment permissions clarification.

Comment thread tests/unit/BillPayTest.ts
expect(canPayBill({...approved, isWaitingOnBankAccount: true}, policy, accountID, email)).toBe(false);
});

it('only offers payment to the authorized payer, even when another user is an admin', () => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI: Records the current payer-only behavior. This expectation needs updating for the later Slack agreement that workspace/payment admins may pay while todos remain assigned to the designated payer.

Context: Bank access clarification, Payment permissions clarification.

# Conflicts:
#	src/components/ParentNavigationSubtitle.tsx
#	src/languages/en.ts
#	src/libs/ReportActionsUtils.ts
#	src/libs/ReportUtils.ts
#	src/libs/actions/IOU/ReportWorkflow.ts
#	src/libs/actions/OnyxDerived/configs/visibleReportActions.ts
#	src/pages/inbox/report/actionContents/ReimbursementQueuedContent.tsx
#	src/types/onyx/ReportNameValuePairs.ts
#	tests/ui/ParentNavigationSubtitleTest.tsx
@OSBotify

Copy link
Copy Markdown
Contributor

🦜 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 diff
diff --git a/src/languages/de.ts b/src/languages/de.ts
index 4d78f85a..ff9a0c5e 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -36,19 +36,19 @@ type States = Record<keyof typeof COMMON_CONST.STATES, StateValue>;
 type AllCountries = Record<Country, string>;
 const translations: TranslationDeepObject<typeof en> = {
     billPay: {
-        bills: 'Eingangsrechnungen',
-        createBill: 'Eingangsrechnung erstellen',
-        vendorEmail: 'E-Mail des Lieferanten',
-        domain: 'Empfangende Domain',
+        bills: 'Rechnungen',
+        createBill: 'Rechnung erstellen',
+        vendorEmail: 'E-Mail des Anbieters',
+        domain: 'Empfangsdomäne',
         invoiceDetails: 'Rechnungsdetails',
         attachPDF: 'PDF anhängen',
         payByACH: 'Per ACH bezahlen',
         markAsPaid: 'Als bezahlt markieren',
-        paymentFailed: 'Diese Rechnung konnte nicht bezahlt werden. Bitte versuche es erneut.',
-        createFailed: 'Diese Rechnung konnte nicht erstellt werden. Bitte versuche es erneut.',
-        payBills: ({count}) => ({one: '1 Rechnung bezahlen', other: `${count} Rechnungen bezahlen`}),
-        primaryContactHint: (domain) =>
-            `Der Hauptkontakt ist die zentrale Kontaktperson für ${domain} und erhält Rechnungen, die an die Rechnungsadresse dieser Domain ${domain}@expensify.cash gesendet werden.`,
+        paymentFailed: 'Wir konnten diese Rechnung nicht bezahlen. Bitte versuchen Sie es erneut.',
+        createFailed: 'Wir konnten diese Rechnung nicht erstellen. Bitte versuchen Sie es erneut.',
+        payBills: ({count}: {count: number}) => ({one: '1 Rechnung bezahlen', other: `${count} Rechnungen bezahlen`}),
+        primaryContactHint: (domain: string) =>
+            `Die Hauptansprechperson ist der primäre Kontakt für ${domain} und erhält Rechnungen, die an die Rechnungszahlungsadresse dieser Domain ${domain}@expensify.cash gesendet werden.`,
     },
     common: {
         durationDays: ({count}: {count: number}) => ({
diff --git a/src/languages/el.ts b/src/languages/el.ts
index f17175e8..b689ba53 100644
--- a/src/languages/el.ts
+++ b/src/languages/el.ts
@@ -36,16 +36,16 @@ const translations: TranslationDeepObject<typeof en> = {
         bills: 'Λογαριασμοί',
         createBill: 'Δημιουργία λογαριασμού',
         vendorEmail: 'Email προμηθευτή',
-        domain: 'Τομέας παραλήπτη',
-        invoiceDetails: 'Στοιχεία τιμολογίου',
+        domain: 'Τομέας παραλαβής',
+        invoiceDetails: 'Λεπτομέρειες τιμολογίου',
         attachPDF: 'Επισύναψη PDF',
-        payByACH: 'Πληρωμή μέσω ACH',
+        payByACH: 'Πληρωμή με ACH',
         markAsPaid: 'Σήμανση ως πληρωμένο',
-        paymentFailed: 'Δεν μπορέσαμε να πληρώσουμε αυτόν τον λογαριασμό. Δοκιμάστε ξανά.',
-        createFailed: 'Δεν μπορέσαμε να δημιουργήσουμε αυτόν τον λογαριασμό. Δοκιμάστε ξανά.',
-        payBills: ({count}) => ({one: 'Πληρωμή 1 λογαριασμού', other: `Πληρωμή ${count} λογαριασμών`}),
-        primaryContactHint: (domain) =>
-            `Η κύρια επαφή είναι το βασικό σημείο επικοινωνίας για το ${domain} και θα λαμβάνει λογαριασμούς που αποστέλλονται στη διεύθυνση πληρωμών του τομέα ${domain}@expensify.cash.`,
+        paymentFailed: 'Δεν μπορέσαμε να πληρώσουμε αυτόν τον λογαριασμό. Παρακαλούμε δοκιμάστε ξανά.',
+        createFailed: 'Δεν μπορέσαμε να δημιουργήσουμε αυτόν τον λογαριασμό. Προσπαθήστε ξανά.',
+        payBills: ({count}: {count: number}) => ({one: 'Πληρωμή 1 λογαριασμού', other: `Πληρώστε ${count} λογαριασμούς`}),
+        primaryContactHint: (domain: string) =>
+            `Η κύρια επαφή είναι το βασικό σημείο επικοινωνίας για το ${domain} και θα λαμβάνει τους λογαριασμούς που αποστέλλονται στη διεύθυνση πληρωμής λογαριασμών αυτού του domain ${domain}@expensify.cash`,
     },
     common: {
         durationDays: ({count}: {count: number}) => ({
diff --git a/src/languages/es.ts b/src/languages/es.ts
index bf0704cf..abf52b4a 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -22,19 +22,19 @@ import type {AllConnectionName, PolicyConnectionSyncStage} from './TranslationTy
 import type {TranslationDeepObject} from './types';
 const translations: TranslationDeepObject<typeof en> = {
     billPay: {
-        bills: 'Facturas por pagar',
-        createBill: 'Crear factura por pagar',
-        vendorEmail: 'Correo del proveedor',
+        bills: 'Facturas',
+        createBill: 'Crear factura',
+        vendorEmail: 'Correo electrónico del proveedor',
         domain: 'Dominio receptor',
         invoiceDetails: 'Detalles de la factura',
         attachPDF: 'Adjuntar PDF',
         payByACH: 'Pagar por ACH',
-        markAsPaid: 'Marcar como pagada',
-        paymentFailed: 'No pudimos pagar esta factura. Inténtalo de nuevo.',
-        createFailed: 'No pudimos crear esta factura. Inténtalo de nuevo.',
-        payBills: ({count}) => ({one: 'Pagar 1 factura', other: `Pagar ${count} facturas`}),
-        primaryContactHint: (domain) =>
-            `El contacto principal es el punto de contacto de ${domain} y recibirá las facturas enviadas a la dirección de pago de facturas del dominio ${domain}@expensify.cash.`,
+        markAsPaid: 'Marcar como pagado',
+        paymentFailed: 'No pudimos pagar esta factura. Por favor, inténtalo de nuevo.',
+        createFailed: 'No hemos podido crear esta factura. Por favor, inténtalo de nuevo.',
+        payBills: ({count}: {count: number}) => ({one: 'Pagar 1 factura', other: `Pagar ${count} facturas`}),
+        primaryContactHint: (domain: string) =>
+            `El contacto principal es la persona de referencia para ${domain} y recibirá las facturas enviadas a la dirección de pago de facturas de este dominio ${domain}@expensify.cash`,
     },
     common: {
         durationDays: ({count}: {count: number}) => ({
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index ae1bfc2f..71a6425b 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -36,19 +36,19 @@ type States = Record<keyof typeof COMMON_CONST.STATES, StateValue>;
 type AllCountries = Record<Country, string>;
 const translations: TranslationDeepObject<typeof en> = {
     billPay: {
-        bills: 'Factures à payer',
-        createBill: 'Créer une facture à payer',
+        bills: 'Factures',
+        createBill: 'Créer une facture',
         vendorEmail: 'E-mail du fournisseur',
-        domain: 'Domaine destinataire',
+        domain: 'Domaine de réception',
         invoiceDetails: 'Détails de la facture',
         attachPDF: 'Joindre un PDF',
-        payByACH: 'Payer par ACH',
-        markAsPaid: 'Marquer comme payée',
+        payByACH: 'Payer par virement ACH',
+        markAsPaid: 'Marquer comme payé',
         paymentFailed: 'Nous n’avons pas pu payer cette facture. Veuillez réessayer.',
         createFailed: 'Nous n’avons pas pu créer cette facture. Veuillez réessayer.',
-        payBills: ({count}) => ({one: 'Payer 1 facture', other: `Payer ${count} factures`}),
-        primaryContactHint: (domain) =>
-            `Le contact principal est l’interlocuteur de ${domain} et recevra les factures envoyées à l’adresse de paiement des factures du domaine ${domain}@expensify.cash.`,
+        payBills: ({count}: {count: number}) => ({one: 'Payer 1 facture', other: `Payer ${count} factures`}),
+        primaryContactHint: (domain: string) =>
+            `Le contact principal est l’interlocuteur principal pour ${domain} et recevra les factures envoyées à l’adresse de paiement des factures de ce domaine ${domain}@expensify.cash`,
     },
     common: {
         durationDays: ({count}: {count: number}) => ({
@@ -1582,7 +1582,7 @@ const translations: TranslationDeepObject<typeof en> = {
         forwarded: (memo?: string) => `approuvé${memo ? `, indiquant « ${memo} »` : ''}`,
         rejectedThisReport: 'rejeté',
         waitingOnBankAccount: (submitterDisplayName: string) => `a commencé le paiement, mais attend que ${submitterDisplayName} ajoute un compte bancaire personnel.`,
-        waitingOnVendorBankAccount: (vendorDisplayName: string) => `a commencé le paiement, mais attend que ${vendorDisplayName} ajoute un compte bancaire.`,
+        waitingOnVendorBankAccount: (vendorDisplayName: string) => `a lancé le paiement, mais attend que ${vendorDisplayName} ajoute un compte bancaire.`,
         adminCanceledRequest: 'a annulé le paiement',
         canceledRequest: (amount: string, submitterDisplayName: string) =>
             `a annulé le paiement de ${amount}, car ${submitterDisplayName} n’a pas activé son Portefeuille Expensify dans un délai de 30 jours`,
diff --git a/src/languages/it.ts b/src/languages/it.ts
index f92f3409..b7a89483 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -36,19 +36,19 @@ type States = Record<keyof typeof COMMON_CONST.STATES, StateValue>;
 type AllCountries = Record<Country, string>;
 const translations: TranslationDeepObject<typeof en> = {
     billPay: {
-        bills: 'Fatture da pagare',
-        createBill: 'Crea fattura da pagare',
-        vendorEmail: 'Email del fornitore',
-        domain: 'Dominio destinatario',
-        invoiceDetails: 'Dettagli della fattura',
+        bills: 'Fatture',
+        createBill: 'Crea fattura',
+        vendorEmail: 'Email fornitore',
+        domain: 'Dominio di ricezione',
+        invoiceDetails: 'Dettagli fattura',
         attachPDF: 'Allega PDF',
-        payByACH: 'Paga tramite ACH',
-        markAsPaid: 'Segna come pagata',
-        paymentFailed: 'Non è stato possibile pagare questa fattura. Riprova.',
-        createFailed: 'Non è stato possibile creare questa fattura. Riprova.',
-        payBills: ({count}) => ({one: 'Paga 1 fattura', other: `Paga ${count} fatture`}),
-        primaryContactHint: (domain) =>
-            `Il contatto principale è il referente di ${domain} e riceverà le fatture inviate all’indirizzo per il pagamento delle fatture del dominio ${domain}@expensify.cash.`,
+        payByACH: 'Paga con ACH',
+        markAsPaid: 'Segna come pagato',
+        paymentFailed: 'Non siamo riusciti a pagare questa fattura. Riprova.',
+        createFailed: 'Non siamo riusciti a creare questa fattura. Riprova.',
+        payBills: ({count}: {count: number}) => ({one: 'Paga 1 fattura', other: `Paga ${count} fatture`}),
+        primaryContactHint: (domain: string) =>
+            `Il contatto principale è il riferimento principale per ${domain} e riceverà le fatture inviate all’indirizzo di pagamento di questo dominio ${domain}@expensify.cash`,
     },
     common: {
         durationDays: ({count}: {count: number}) => ({
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 40712b36..70a5a038 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -36,18 +36,19 @@ type States = Record<keyof typeof COMMON_CONST.STATES, StateValue>;
 type AllCountries = Record<Country, string>;
 const translations: TranslationDeepObject<typeof en> = {
     billPay: {
-        bills: '支払請求書',
-        createBill: '支払請求書を作成',
-        vendorEmail: '取引先のメールアドレス',
+        bills: '請求書',
+        createBill: '請求書を作成',
+        vendorEmail: 'ベンダーのメールアドレス',
         domain: '受信ドメイン',
         invoiceDetails: '請求書の詳細',
-        attachPDF: 'PDFを添付',
-        payByACH: 'ACHで支払う',
-        markAsPaid: '支払済みにする',
+        attachPDF: 'PDF を添付',
+        payByACH: 'ACH で支払う',
+        markAsPaid: '支払い済みにする',
         paymentFailed: 'この請求書を支払えませんでした。もう一度お試しください。',
         createFailed: 'この請求書を作成できませんでした。もう一度お試しください。',
-        payBills: ({count}) => ({one: '1件の請求書を支払う', other: `${count}件の請求書を支払う`}),
-        primaryContactHint: (domain) => `主な連絡先は${domain}の窓口となり、このドメインの請求書支払い用アドレス${domain}@expensify.cashに送信された請求書を受け取ります。`,
+        payBills: ({count}: {count: number}) => ({one: '請求書 1 件を支払う', other: `${count} 件の請求書を支払う`}),
+        primaryContactHint: (domain: string) =>
+            `プライマリ連絡先は、${domain} の主な窓口であり、このドメインの請求支払い用アドレス ${domain}@expensify.cash に送信された請求書を受け取ります`,
     },
     common: {
         durationDays: ({count}: {count: number}) => ({
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 5be3634c..d2ebcae4 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -36,19 +36,19 @@ type States = Record<keyof typeof COMMON_CONST.STATES, StateValue>;
 type AllCountries = Record<Country, string>;
 const translations: TranslationDeepObject<typeof en> = {
     billPay: {
-        bills: 'Te betalen facturen',
-        createBill: 'Te betalen factuur maken',
+        bills: 'Rekeningen',
+        createBill: 'Rekening aanmaken',
         vendorEmail: 'E-mailadres leverancier',
-        domain: 'Ontvangend domein',
-        invoiceDetails: 'Factuurgegevens',
-        attachPDF: 'PDF bijvoegen',
+        domain: 'Ontvangstdomein',
+        invoiceDetails: 'Factuurdetails',
+        attachPDF: 'PDF toevoegen',
         payByACH: 'Betalen via ACH',
         markAsPaid: 'Markeren als betaald',
-        paymentFailed: 'We konden deze factuur niet betalen. Probeer het opnieuw.',
-        createFailed: 'We konden deze factuur niet maken. Probeer het opnieuw.',
-        payBills: ({count}) => ({one: '1 factuur betalen', other: `${count} facturen betalen`}),
-        primaryContactHint: (domain) =>
-            `De primaire contactpersoon is het aanspreekpunt voor ${domain} en ontvangt facturen die naar het factuuradres van dit domein ${domain}@expensify.cash worden gestuurd.`,
+        paymentFailed: 'We konden deze rekening niet betalen. Probeer het opnieuw.',
+        createFailed: 'We konden deze rekening niet aanmaken. Probeer het opnieuw.',
+        payBills: ({count}: {count: number}) => ({one: 'Betaal 1 rekening', other: `Betaal ${count} rekeningen`}),
+        primaryContactHint: (domain: string) =>
+            `De primaire contactpersoon is het belangrijkste aanspreekpunt voor ${domain} en ontvangt rekeningen die worden verzonden naar het betaaladres van dit domein: ${domain}@expensify.cash`,
     },
     common: {
         durationDays: ({count}: {count: number}) => ({
@@ -1574,7 +1574,7 @@ const translations: TranslationDeepObject<typeof en> = {
         forwarded: (memo?: string) => `goedgekeurd${memo ? `, met de omschrijving ${memo}` : ''}`,
         rejectedThisReport: 'afgekeurd',
         waitingOnBankAccount: (submitterDisplayName: string) => `is met de betaling begonnen, maar wacht tot ${submitterDisplayName} een persoonlijke bankrekening toevoegt.`,
-        waitingOnVendorBankAccount: (vendorDisplayName: string) => `is met de betaling begonnen, maar wacht tot ${vendorDisplayName} een bankrekening toevoegt.`,
+        waitingOnVendorBankAccount: (vendorDisplayName: string) => `is begonnen met betalen, maar wacht tot ${vendorDisplayName} een bankrekening toevoegt.`,
         adminCanceledRequest: 'heeft de betaling geannuleerd',
         canceledRequest: (amount: string, submitterDisplayName: string) =>
             `heeft de betaling van ${amount} geannuleerd, omdat ${submitterDisplayName} hun Expensify Wallet niet binnen 30 dagen heeft geactiveerd`,
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 0dac647d..c9933e78 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -38,16 +38,17 @@ const translations: TranslationDeepObject<typeof en> = {
     billPay: {
         bills: 'Rachunki',
         createBill: 'Utwórz rachunek',
-        vendorEmail: 'E-mail dostawcy',
-        domain: 'Domena odbiorcy',
+        vendorEmail: 'E-mail sprzedawcy',
+        domain: 'Domena odbiorcza',
         invoiceDetails: 'Szczegóły faktury',
-        attachPDF: 'Załącz PDF',
-        payByACH: 'Zapłać przez ACH',
-        markAsPaid: 'Oznacz jako opłacone',
+        attachPDF: 'Dołącz PDF',
+        payByACH: 'Zapłać przelewem ACH',
+        markAsPaid: 'Oznacz jako zapłacone',
         paymentFailed: 'Nie udało się opłacić tego rachunku. Spróbuj ponownie.',
         createFailed: 'Nie udało się utworzyć tego rachunku. Spróbuj ponownie.',
-        payBills: ({count}) => ({one: 'Opłać 1 rachunek', few: `Opłać ${count} rachunki`, many: `Opłać ${count} rachunków`, other: `Opłać ${count} rachunku`}),
-        primaryContactHint: (domain) => `Główna osoba kontaktowa odpowiada za kontakt w domenie ${domain} i otrzyma rachunki wysłane na adres płatności tej domeny ${domain}@expensify.cash.`,
+        payBills: ({count}: {count: number}) => ({one: 'Zapłać 1 rachunek', other: `Opłać ${count} rachunki`}),
+        primaryContactHint: (domain: string) =>
+            `Główny kontakt jest osobą odpowiedzialną za ${domain} i będzie otrzymywać rachunki wysyłane na adres do opłacania rachunków tej domeny: ${domain}@expensify.cash`,
     },
     common: {
         durationDays: ({count}: {count: number}) => ({
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 897744a9..0bbc458f 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -36,19 +36,19 @@ type States = Record<keyof typeof COMMON_CONST.STATES, StateValue>;
 type AllCountries = Record<Country, string>;
 const translations: TranslationDeepObject<typeof en> = {
     billPay: {
-        bills: 'Contas a pagar',
-        createBill: 'Criar conta a pagar',
+        bills: 'Contas',
+        createBill: 'Criar conta',
         vendorEmail: 'E-mail do fornecedor',
-        domain: 'Domínio destinatário',
+        domain: 'Domínio de recebimento',
         invoiceDetails: 'Detalhes da fatura',
         attachPDF: 'Anexar PDF',
         payByACH: 'Pagar por ACH',
-        markAsPaid: 'Marcar como paga',
+        markAsPaid: 'Marcar como pago',
         paymentFailed: 'Não foi possível pagar esta conta. Tente novamente.',
-        createFailed: 'Não foi possível criar esta conta. Tente novamente.',
-        payBills: ({count}) => ({one: 'Pagar 1 conta', other: `Pagar ${count} contas`}),
-        primaryContactHint: (domain) =>
-            `O contato principal é o ponto de contato de ${domain} e receberá as contas enviadas ao endereço de pagamento de contas deste domínio ${domain}@expensify.cash.`,
+        createFailed: 'Não foi possível criar esta fatura. Tente novamente.',
+        payBills: ({count}: {count: number}) => ({one: 'Pagar 1 conta', other: `Pagar ${count} contas`}),
+        primaryContactHint: (domain: string) =>
+            `O contato principal é o ponto de contato responsável por ${domain} e receberá as faturas enviadas para o endereço de pagamento deste domínio: ${domain}@expensify.cash`,
     },
     common: {
         durationDays: ({count}: {count: number}) => ({
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 972c078e..6ab4a283 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -39,15 +39,15 @@ const translations: TranslationDeepObject<typeof en> = {
         bills: '账单',
         createBill: '创建账单',
         vendorEmail: '供应商邮箱',
-        domain: '接收域名',
+        domain: '接收域',
         invoiceDetails: '发票详情',
         attachPDF: '附加 PDF',
         payByACH: '通过 ACH 支付',
         markAsPaid: '标记为已支付',
-        paymentFailed: '无法支付此账单。请重试。',
-        createFailed: '无法创建此账单。请重试。',
-        payBills: ({count}) => ({one: '支付 1 笔账单', other: `支付 ${count} 笔账单`}),
-        primaryContactHint: (domain) => `主要联系人是 ${domain} 的主要联络人,并将收到发送至此域名账单支付地址 ${domain}@expensify.cash 的账单。`,
+        paymentFailed: '我们无法支付这笔账单。请重试。',
+        createFailed: '我们无法创建此账单。请重试。',
+        payBills: ({count}: {count: number}) => ({one: '支付 1 笔账单', other: `支付 ${count} 笔账单`}),
+        primaryContactHint: (domain: string) => `主要联系人是 ${domain} 的首要联络人,会接收发送到该域名账单支付地址 ${domain}@expensify.cash 的账单`,
     },
     common: {
         durationDays: ({count}: {count: number}) => ({

Note

You can apply these changes to your branch by copying the patch to your clipboard, then running pbpaste | git apply 😉

View workflow run

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants