Skip to content

Add DualEntry integration export pages (review fixes applied) - #98394

Open
MelvinBot wants to merge 14 commits into
mainfrom
claude-dualEntryExportReviewFixes
Open

Add DualEntry integration export pages (review fixes applied)#98394
MelvinBot wants to merge 14 commits into
mainfrom
claude-dualEntryExportReviewFixes

Conversation

@MelvinBot

@MelvinBot MelvinBot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

This PR mirrors #97513 (authored by s77rt, which adds the DualEntry integration export pages) and applies the review fixes that were requested there. The original PR is from a fork branch that MelvinBot's token can't push to, so this branch was created in Expensify/App with all of s77rt's commits preserved and the review fixes added on top.

Review fixes applied on top of the original work:

  • Added EXPENSIFY_CARD_ACCOUNT_ID: 'expensifyCardAccountID' to DUALENTRY_CONFIG in CONST — it was referenced by the export pages and the action but didn't exist.
  • Added the expensifyCardAccountID field to the DualEntryExport type in Policy.ts — it was read/written via DualEntryExport['expensifyCardAccountID'] but wasn't declared.
  • Renamed the copy-pasted UpdateDualEntryCreditCardAccountParams type to UpdateDualEntryExpensifyCardAccountParams to match its file and re-export.
  • Renamed the DualEntryExpensifyCardAccountPage component and its displayName (they were still DualEntryCompanyCardAccountPage, a copy-paste leftover).

Full credit for the DualEntry export feature belongs to s77rt; the commits from #97513 are preserved here.

Fixed Issues

$ #97238
PROPOSAL:

Tests

// TODO: The human co-author must confirm these tests before marking this PR "ready for review". Test steps carried over from #97513:

Test 1

  1. Create a workspace and enable the accounting feature
  2. Go to Accounting
  3. Connect the DualEntry integration
  4. Click on Export
  5. Verify the export page loads correctly
  6. Verify each option row navigates to a different page and that you can change the set values

Test 2

  1. Create a report and add some expenses, both reimbursable and non-reimbursable
  2. Submit the report
  3. Approve the report
  4. Export the report to DualEntry
  5. Verify the report is exported
  • Verify that no errors appear in the JS console

Offline tests

Same as Tests.

QA Steps

// TODO: The human co-author must confirm QA steps before marking this PR "ready for review".

Same as Tests.

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
- [ ] I verified that similar component doesn't exist in the codebase - [ ] I verified that all props are defined accurately and each prop has a `/** comment above it */` - [ ] I verified that each file is named correctly - [ ] I verified that each component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone - [ ] I verified that the only data being stored in component state is data necessary for rendering and nothing else - [ ] In component if we are not using the full Onyx data that we loaded, I've added the proper selector in order to ensure the component only re-renders when the data it is using changes - [ ] For Class Components, any internal methods passed to components event handlers are bound to `this` properly so there are no scoping issues (i.e. for `onClick={this.submit}` the method `this.submit` should be bound to `this` in the constructor) - [ ] I verified that component internal methods bound to `this` are necessary to be bound (i.e. avoid `this.submit = this.submit.bind(this);` if `this.submit` is never passed to a component event handler like `onClick`) - [ ] I verified that all JSX used for rendering exists in the render method - [ ] I verified that each component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions

Screenshots/Videosundefined

s77rt and others added 13 commits July 30, 2026 20:58
- Add EXPENSIFY_CARD_ACCOUNT_ID to DUALENTRY_CONFIG
- Add expensifyCardAccountID field to DualEntryExport type
- Rename copy-pasted UpdateDualEntryCreditCardAccountParams -> UpdateDualEntryExpensifyCardAccountParams
- Fix component and displayName DualEntryCompanyCardAccountPage -> DualEntryExpensifyCardAccountPage

Co-authored-by: s77rt <16493223+s77rt@users.noreply.github.com>
Co-authored-by: Shridhar Goel <ShridharGoel@users.noreply.github.com>
@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 74c0c706..83fa63b5 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -5755,6 +5755,24 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
             enableNewAccountsDescription: 'Neue DualEntry-Konten werden als Kategorien verfügbar sein.',
             classificationsImport: 'Alle DualEntry-Klassifizierungen werden als Tags importiert',
             importDescription: 'Wählen Sie aus, welche Buchungskonfigurationen aus DualEntry importiert werden sollen.',
+            noVendorsFound: 'Keine Anbieter gefunden',
+            noVendorsFoundDescription: 'Bitte fügen Sie Lieferanten in DualEntry hinzu und synchronisieren Sie die Verbindung erneut',
+            noAccountsFound: 'Keine Konten gefunden',
+            noAccountsFoundDescription: 'Bitte fügen Sie Konten in DualEntry hinzu und synchronisieren Sie die Verbindung erneut',
+            exportDescription: 'Konfigurieren Sie, wie Expensify-Daten nach DualEntry exportiert werden.',
+            exportReimbursable: {label: 'Erstattungsfähige Ausgaben exportieren als', values: {label: 'Lieferantenrechnungen'}},
+            exportDate: {
+                label: 'Rechnungsdatum des Lieferanten',
+                description: 'Verwenden Sie dieses Datum beim Exportieren von Berichten nach DualEntry.',
+                values: {label: 'Eingereichtes Datum', description: 'Datum, an dem der Bericht zur Genehmigung eingereicht wurde.'},
+            },
+            exportNonReimbursable: {label: 'Exportieren Sie Firmenkartenausgaben als', values: {label: 'Direkte Ausgaben'}},
+            defaultCompanyCardVendor: {
+                label: 'Standardlieferant für alle Firmenkarten',
+                description: 'Wählen Sie einen Standard-DualEntry-Anbieter für Ausgaben, die nicht automatisch übereinstimmen.',
+            },
+            companyCardAccount: {label: 'Firmenkartenkonto', description: 'Wählen Sie aus, wohin Unternehmenstransaktionen exportiert werden sollen.'},
+            expensifyCardAccount: {label: 'Expensify Karte-Konto', description: 'Wählen Sie aus, wohin Expensify Kartenumsätze exportiert werden sollen.'},
         },
         type: {
             free: 'Kostenlos',
diff --git a/src/languages/el.ts b/src/languages/el.ts
index 5acfee20..40481958 100644
--- a/src/languages/el.ts
+++ b/src/languages/el.ts
@@ -5873,6 +5873,24 @@ _Για πιο αναλυτικές οδηγίες, [επισκεφθείτε τ
             enableNewAccountsDescription: 'Οι νέοι λογαριασμοί DualEntry θα είναι διαθέσιμοι ως κατηγορίες.',
             classificationsImport: 'Όλες οι κατηγοριοποιήσεις DualEntry εισάγονται ως ετικέτες',
             importDescription: 'Επιλέξτε ποιες ρυθμίσεις κωδικοποίησης θέλετε να εισαγάγετε από το DualEntry.',
+            noVendorsFound: 'Δεν βρέθηκαν προμηθευτές',
+            noVendorsFoundDescription: 'Παρακαλούμε προσθέστε προμηθευτές στο DualEntry και συγχρονίστε ξανά τη σύνδεση',
+            noAccountsFound: 'Δεν βρέθηκαν λογαριασμοί',
+            noAccountsFoundDescription: 'Παρακαλούμε προσθέστε λογαριασμούς στο DualEntry και συγχρονίστε ξανά τη σύνδεση',
+            exportDescription: 'Ρυθμίστε πώς τα δεδομένα του Expensify εξάγονται στο DualEntry.',
+            exportReimbursable: {label: 'Εξαγωγή αποζημιώσιμων εξόδων ως', values: {label: 'Τιμολόγια προμηθευτών'}},
+            exportDate: {
+                label: 'Ημερομηνία τιμολογίου προμηθευτή',
+                description: 'Χρησιμοποιήστε αυτή την ημερομηνία κατά την εξαγωγή αναφορών στο DualEntry.',
+                values: {label: 'Ημερομηνία υποβολής', description: 'Ημερομηνία υποβολής της αναφοράς για έγκριση.'},
+            },
+            exportNonReimbursable: {label: 'Εξαγωγή εταιρικών εξόδων κάρτας ως', values: {label: 'Άμεσες δαπάνες'}},
+            defaultCompanyCardVendor: {
+                label: 'Προεπιλεγμένος προμηθευτής για όλες τις εταιρικές κάρτες',
+                description: 'Επιλέξτε έναν προεπιλεγμένο προμηθευτή DualEntry για δαπάνες που δεν αντιστοιχίζονται αυτόματα.',
+            },
+            companyCardAccount: {label: 'Λογαριασμός εταιρικής κάρτας', description: 'Επιλέξτε πού θα εξαχθούν οι συναλλαγές εταιρικής κάρτας.'},
+            expensifyCardAccount: {label: 'λογαριασμός κάρτας Expensify', description: 'Επιλέξτε πού θα εξαχθούν οι συναλλαγές της Κάρτας Expensify.'},
         },
         type: {
             free: 'Δωρεάν',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 89b8f84d..499647d7 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -5644,6 +5644,24 @@ ${amount} para ${merchant} - ${date}`,
             enableNewAccountsDescription: 'Las nuevas cuentas DualEntry estarán disponibles como categorías.',
             classificationsImport: 'Todas las clasificaciones de DualEntry se importan como etiquetas',
             importDescription: 'Elige qué configuraciones de codificación importar desde DualEntry.',
+            noVendorsFound: 'No se encontraron proveedores',
+            noVendorsFoundDescription: 'Por favor, añade proveedores en DualEntry y sincroniza la conexión de nuevo',
+            noAccountsFound: 'No se encontraron cuentas',
+            noAccountsFoundDescription: 'Añade cuentas en DualEntry y sincroniza la conexión de nuevo',
+            exportDescription: 'Configura cómo se exportan los datos de Expensify a DualEntry.',
+            exportReimbursable: {label: 'Exportar gastos reembolsables como', values: {label: 'Facturas de proveedor'}},
+            exportDate: {
+                label: 'Fecha de factura del proveedor',
+                description: 'Usa esta fecha al exportar informes a DualEntry.',
+                values: {label: 'Fecha de envío', description: 'Fecha en que se envió el informe para aprobación.'},
+            },
+            exportNonReimbursable: {label: 'Exportar gastos de tarjetas de empresa como', values: {label: 'Gastos directos'}},
+            defaultCompanyCardVendor: {
+                label: 'Proveedor predeterminado para todas las tarjetas de la empresa',
+                description: 'Elige un proveedor DualEntry predeterminado para los gastos que no se asignen automáticamente.',
+            },
+            companyCardAccount: {label: 'Cuenta de tarjeta de empresa', description: 'Elige dónde exportar las transacciones de las tarjetas de la empresa.'},
+            expensifyCardAccount: {label: 'Cuenta de la Tarjeta Expensify', description: 'Elige dónde exportar las transacciones de la Tarjeta Expensify.'},
         },
         type: {
             free: 'Gratis',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 1b3923a1..46169163 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -5769,6 +5769,24 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
             enableNewAccountsDescription: 'Les nouveaux comptes DualEntry seront disponibles en tant que catégories.',
             classificationsImport: 'Toutes les classifications DualEntry sont importées en tant que tags',
             importDescription: 'Choisissez les configurations de codage à importer depuis DualEntry.',
+            noVendorsFound: 'Aucun fournisseur trouvé',
+            noVendorsFoundDescription: 'Veuillez ajouter des fournisseurs dans DualEntry et synchroniser de nouveau la connexion',
+            noAccountsFound: 'Aucun compte trouvé',
+            noAccountsFoundDescription: 'Veuillez ajouter des comptes dans DualEntry et synchroniser la connexion à nouveau',
+            exportDescription: 'Configurez la façon dont les données Expensify sont exportées vers DualEntry.',
+            exportReimbursable: {label: 'Exporter les dépenses remboursables en', values: {label: 'Factures fournisseurs'}},
+            exportDate: {
+                label: 'Date de facture fournisseur',
+                description: 'Utiliser cette date lors de l’exportation des notes de frais vers DualEntry.',
+                values: {label: 'Date de soumission', description: 'Date à laquelle la note de frais a été soumise pour approbation.'},
+            },
+            exportNonReimbursable: {label: 'Exporter les dépenses de carte d’entreprise en tant que', values: {label: 'Dépenses directes'}},
+            defaultCompanyCardVendor: {
+                label: 'Fournisseur par défaut pour toutes les cartes de l’entreprise',
+                description: 'Choisissez un fournisseur DualEntry par défaut pour les dépenses qui ne correspondent pas automatiquement.',
+            },
+            companyCardAccount: {label: 'Compte de carte d’entreprise', description: 'Choisissez où exporter les transactions de carte d’entreprise.'},
+            expensifyCardAccount: {label: 'Compte Carte Expensify', description: 'Choisissez où exporter les transactions de la Carte Expensify.'},
         },
         type: {
             free: 'Gratuit',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index ff0a26d5..d30c9205 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -5725,6 +5725,24 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
             enableNewAccountsDescription: 'I nuovi conti DualEntry saranno disponibili come categorie.',
             classificationsImport: 'Tutte le classificazioni DualEntry vengono importate come tag',
             importDescription: 'Scegli quali configurazioni di codifica importare da DualEntry.',
+            noVendorsFound: 'Nessun fornitore trovato',
+            noVendorsFoundDescription: 'Aggiungi i fornitori in DualEntry e sincronizza di nuovo la connessione',
+            noAccountsFound: 'Nessun account trovato',
+            noAccountsFoundDescription: 'Aggiungi degli account in DualEntry e sincronizza di nuovo la connessione',
+            exportDescription: 'Configura come i dati di Expensify vengono esportati in DualEntry.',
+            exportReimbursable: {label: 'Esporta le spese rimborsabili come', values: {label: 'Fatture fornitori'}},
+            exportDate: {
+                label: 'Data fattura fornitore',
+                description: 'Usa questa data quando esporti i report in DualEntry.',
+                values: {label: 'Data di invio', description: "Data in cui il report è stato inviato per l'approvazione."},
+            },
+            exportNonReimbursable: {label: 'Esporta le spese della carta aziendale come', values: {label: 'Spese dirette'}},
+            defaultCompanyCardVendor: {
+                label: 'Fornitore predefinito per tutte le carte aziendali',
+                description: 'Scegli un fornitore DualEntry predefinito per le spese che non vengono abbinate automaticamente.',
+            },
+            companyCardAccount: {label: 'Conto carta aziendale', description: 'Scegli dove esportare le transazioni della carta aziendale.'},
+            expensifyCardAccount: {label: 'Account Carta Expensify', description: 'Scegli dove esportare le transazioni della Carta Expensify.'},
         },
         type: {
             free: 'Gratis',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index e8f52def..a529ddbc 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -5666,6 +5666,21 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO
             enableNewAccountsDescription: '新しい DualEntry アカウントは、カテゴリとして利用できるようになります。',
             classificationsImport: 'すべての DualEntry 分類はタグとしてインポートされます',
             importDescription: 'DualEntry からインポートするコーディング設定を選択してください。',
+            noVendorsFound: '取引先が見つかりません',
+            noVendorsFoundDescription: 'DualEntry で仕入先を追加して、もう一度同期してください',
+            noAccountsFound: 'アカウントが見つかりません',
+            noAccountsFoundDescription: 'DualEntry に勘定科目を追加して、もう一度同期してください',
+            exportDescription: 'Expensify のデータを DualEntry へエクスポートする方法を設定します。',
+            exportReimbursable: {label: '立替経費のエクスポート形式', values: {label: '仕入先の請求書'}},
+            exportDate: {
+                label: '仕入先請求書の日付',
+                description: 'DualEntry へレポートをエクスポートするときに、この日付を使用します。',
+                values: {label: '提出日', description: 'レポートが承認のために提出された日付です。'},
+            },
+            exportNonReimbursable: {label: '法人カード経費のエクスポート形式', values: {label: '直接経費'}},
+            defaultCompanyCardVendor: {label: 'すべての会社カードのデフォルト仕入先', description: '自動で一致しない経費用に、デフォルトの DualEntry 仕入先を選択します。'},
+            companyCardAccount: {label: '会社カード口座', description: '会社カードの取引をエクスポートする先を選択してください。'},
+            expensifyCardAccount: {label: 'Expensify カード口座', description: 'Expensify カードの取引をどこにエクスポートするか選択してください。'},
         },
         type: {
             free: '無料',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 9bdb2bd6..28b5d637 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -5720,6 +5720,24 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
             enableNewAccountsDescription: 'Nieuwe DualEntry-accounts zullen beschikbaar zijn als categorieën.',
             classificationsImport: 'Alle DualEntry-classificaties worden geïmporteerd als tags',
             importDescription: 'Kies welke coderingsconfiguraties je uit DualEntry wilt importeren.',
+            noVendorsFound: 'Geen leveranciers gevonden',
+            noVendorsFoundDescription: 'Voeg leveranciers toe in DualEntry en synchroniseer de verbinding opnieuw',
+            noAccountsFound: 'Geen accounts gevonden',
+            noAccountsFoundDescription: 'Voeg accounts toe in DualEntry en synchroniseer de verbinding opnieuw',
+            exportDescription: 'Configureer hoe Expensify-gegevens naar DualEntry worden geëxporteerd.',
+            exportReimbursable: {label: 'Vergoedbare uitgaven exporteren als', values: {label: 'Leveranciersrekeningen'}},
+            exportDate: {
+                label: 'Factuurdatum leverancier',
+                description: 'Gebruik deze datum bij het exporteren van rapporten naar DualEntry.',
+                values: {label: 'Ingediend op datum', description: 'Datum waarop het rapport ter goedkeuring is ingediend.'},
+            },
+            exportNonReimbursable: {label: 'Exporteer zakelijke kaartuitgaven als', values: {label: 'Directe uitgaven'}},
+            defaultCompanyCardVendor: {
+                label: 'Standaardleverancier voor alle bedrijfskaarten',
+                description: 'Kies een standaard DualEntry-leverancier voor uitgaven die niet automatisch worden gekoppeld.',
+            },
+            companyCardAccount: {label: 'Bedrijfskaartrekening', description: 'Kies waar je transacties met bedrijfskaarten wilt exporteren.'},
+            expensifyCardAccount: {label: 'Expensify Kaart-rekening', description: 'Kies waar je Expensify Kaart-transacties wilt exporteren.'},
         },
         type: {
             free: 'Gratis',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 5c0f5025..595228f9 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -5701,6 +5701,24 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
             enableNewAccountsDescription: 'Nowe konta DualEntry będą dostępne jako kategorie.',
             classificationsImport: 'Wszystkie klasyfikacje DualEntry są importowane jako tagi',
             importDescription: 'Wybierz, które konfiguracje kodowania zaimportować z DualEntry.',
+            noVendorsFound: 'Nie znaleziono żadnych dostawców',
+            noVendorsFoundDescription: 'Dodaj proszę dostawców w DualEntry i zsynchronizuj połączenie ponownie',
+            noAccountsFound: 'Nie znaleziono kont',
+            noAccountsFoundDescription: 'Dodaj proszę konta w DualEntry i zsynchronizuj połączenie ponownie',
+            exportDescription: 'Skonfiguruj, jak dane Expensify są eksportowane do DualEntry.',
+            exportReimbursable: {label: 'Eksportuj wydatki podlegające zwrotowi jako', values: {label: 'Faktury od dostawców'}},
+            exportDate: {
+                label: 'Data faktury od dostawcy',
+                description: 'Użyj tej daty podczas eksportowania raportów do DualEntry.',
+                values: {label: 'Data przesłania', description: 'Data wysłania raportu do zatwierdzenia.'},
+            },
+            exportNonReimbursable: {label: 'Eksportuj wydatki z firmowej karty jako', values: {label: 'Koszty bezpośrednie'}},
+            defaultCompanyCardVendor: {
+                label: 'Domyślny dostawca dla wszystkich firmowych kart',
+                description: 'Wybierz domyślnego sprzedawcę DualEntry dla wydatków, które nie dopasują się automatycznie.',
+            },
+            companyCardAccount: {label: 'Konto karty firmowej', description: 'Wybierz, dokąd eksportować transakcje z kart firmowych.'},
+            expensifyCardAccount: {label: 'Konto Karty Expensify', description: 'Wybierz, dokąd eksportować transakcje z Karty Expensify.'},
         },
         type: {
             free: 'Darmowy',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index a6f37501..b6b10301 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -5718,6 +5718,24 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS
             enableNewAccountsDescription: 'Novas contas DualEntry estarão disponíveis como categorias.',
             classificationsImport: 'Todas as classificações DualEntry são importadas como etiquetas',
             importDescription: 'Escolha quais configurações de codificação importar do DualEntry.',
+            noVendorsFound: 'Nenhum fornecedor encontrado',
+            noVendorsFoundDescription: 'Adicione fornecedores no DualEntry e sincronize a conexão novamente',
+            noAccountsFound: 'Nenhuma conta encontrada',
+            noAccountsFoundDescription: 'Adicione contas no DualEntry e sincronize a conexão novamente',
+            exportDescription: 'Configurar como os dados do Expensify são exportados para o DualEntry.',
+            exportReimbursable: {label: 'Exportar despesas reembolsáveis como', values: {label: 'Contas de fornecedores'}},
+            exportDate: {
+                label: 'Data da fatura do fornecedor',
+                description: 'Use esta data ao exportar relatórios para o DualEntry.',
+                values: {label: 'Data de envio', description: 'Data em que o relatório foi enviado para aprovação.'},
+            },
+            exportNonReimbursable: {label: 'Exportar despesas de cartão corporativo como', values: {label: 'Despesas diretas'}},
+            defaultCompanyCardVendor: {
+                label: 'Fornecedor padrão para todos os cartões da empresa',
+                description: 'Escolha um fornecedor padrão do DualEntry para despesas que não sejam correspondidas automaticamente.',
+            },
+            companyCardAccount: {label: 'Conta de cartão corporativo', description: 'Escolha para onde exportar as transações do cartão corporativo.'},
+            expensifyCardAccount: {label: 'Conta do Cartão Expensify', description: 'Escolha para onde exportar as transações do Cartão Expensify.'},
         },
         type: {
             free: 'Grátis',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index d8933b99..439ed8f4 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -5536,6 +5536,17 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
             enableNewAccountsDescription: '新的 DualEntry 账户将作为类别提供。',
             classificationsImport: '所有 DualEntry 分类都会作为标签导入',
             importDescription: '选择要从 DualEntry 导入的编码配置。',
+            noVendorsFound: '未找到供应商',
+            noVendorsFoundDescription: '请在 DualEntry 中添加供应商,然后再次同步连接',
+            noAccountsFound: '未找到账户',
+            noAccountsFoundDescription: '请在 DualEntry 中添加科目,然后再次同步连接',
+            exportDescription: '配置 Expensify 数据导出到 DualEntry 的方式。',
+            exportReimbursable: {label: '可报销费用导出为', values: {label: '供应商账单'}},
+            exportDate: {label: '供应商账单日期', description: '将报告导出到 DualEntry 时使用此日期。', values: {label: '提交日期', description: '报表提交审批的日期。'}},
+            exportNonReimbursable: {label: '导出公司卡费用为', values: {label: '直接费用'}},
+            defaultCompanyCardVendor: {label: '所有公司卡的默认供应商', description: '为未自动匹配的报销选择默认的双重记账供应商。'},
+            companyCardAccount: {label: '公司卡账户', description: '选择导出公司卡交易记录的位置。'},
+            expensifyCardAccount: {label: 'Expensify 卡账户', description: '选择 Expensify 卡交易的导出位置。'},
         },
         type: {
             free: '免费',

Note

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

View workflow run

@ShridharGoel

ShridharGoel commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

@MelvinBot Fix the tests and TS failures and apply the translation diff

…slation key casing and add missing DualEntry export translations to all locales

Co-authored-by: Shridhar Goel <ShridharGoel@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor Author

Pushed a fix for the failing checks. Root cause was two related issues in the DualEntry export code, both now resolved (verified locally with npm run typecheck and the TranslateTest suite).

1. Wrong casing on the dualEntry connection/translation key (broke typecheck)

The new export pages, the action, and PolicyUtils referenced the connection as dualentry (all lowercase), but the canonical Onyx/type key is dualEntry — every other page (PolicyAccountingPage, DualEntrySetupPage, etc.) already uses dualEntry. This also made the workspace.dualentry.* translation paths invalid (the key in en.ts is workspace.dualEntry.*), and the resulting any types cascaded into the TS7006 implicit-any errors on the .map/.filter callbacks. Fixed the casing in:

2. Missing DualEntry export translations in every non-English locale (broke typecheck + test (job 6))

en.ts had 11 more keys under dualEntry (noVendorsFound, noAccountsFound, exportDescription, exportReimbursable, exportDate, exportNonReimbursable, defaultCompanyCardVendor, companyCardAccount, expensifyCardAccount, …) than the other 10 locale files, so they no longer matched the TranslationDeepObject shape (TS2740) and TranslateTest › Does <locale> locale have all the keys failed. Added the missing keys — translated, reusing the wording already established for the sibling integrations (Rillet/NetSuite/Xero) in each file — to de, el, es, fr, it, ja, nl, pl, pt-BR, zh-hans.

Note on the remaining red check: build (Expensify Dev, Debug, true) failed for an unrelated infrastructure reason — CocoaPods installation failed (curl (56) … error: 401, plus a remote-cache miss), not anything in this diff. It should clear on re-run.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 Fixed the failing typecheck, ESLint check, and test (job 6) checks. The changes are on the branch as 5bd2488.

Two root causes:

  1. Wrong connection/translation casing (dualentrydualEntry). The export pages, PolicyUtils.ts, and DualEntry.ts read policy.connections.dualentry and workspace.dualentry.*, but the connection key is dualEntry (CONST.POLICY.CONNECTIONS.NAME.DUALENTRY) and the translation namespace is workspace.dualEntry. Because dualentry didn't resolve on Connections, the values fell back to any, which is what produced the no-unsafe-* ESLint errors as well as the TS2551/TS2561 typecheck errors.

  2. Missing locale translations. The new DualEntry export keys were added to en.ts but not to the other 10 locales, so typecheck failed (TranslationDeepObject shape mismatch) and TranslateTest failed ("es locale missing keys"). The export keys — including the computed-key values blocks (exportReimbursable, exportDate, exportNonReimbursable) — were added to de, el, es, fr, it, ja, nl, pl, pt-BR, and zh-hans.

Verified locally: typecheck ✅, ESLint on the changed files ✅, TranslateTest ✅ (all keys present, no unused keys), Oxfmt ✅.

Still red — but unrelated: build (Expensify Dev, Debug, true) failed with RockError: CocoaPods installation failed (curl (56) … 401, remote cache NoSuchKey). That's a CI infrastructure/auth flake in the iOS remote build, not a code issue — re-running that job should clear it. Check independent approval and the checklist checks are expected until the PR is reviewed.

@codecov

codecov Bot commented Aug 12, 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 94.81% <ø> (ø)
src/SCREENS.ts 100.00% <ø> (ø)
src/libs/API/types.ts 100.00% <ø> (ø)
...gation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts 100.00% <ø> (ø)
src/libs/Navigation/linkingConfig/config.ts 66.66% <ø> (ø)
src/libs/PolicyUtils.ts 77.66% <100.00%> (+0.03%) ⬆️
src/pages/workspace/accounting/utils.tsx 2.05% <0.00%> (ø)
src/ROUTES.ts 23.27% <0.00%> (-0.13%) ⬇️
...gation/AppNavigator/ModalStackNavigators/index.tsx 6.34% <0.00%> (-0.05%) ⬇️
...g/dualentry/export/DualEntryVendorBillDatePage.tsx 0.00% <0.00%> (ø)
... and 6 more
... and 206 files with indirect coverage changes

@MelvinBot

Copy link
Copy Markdown
Contributor Author

The substantive check failures were already fixed by my earlier commit (5bd2488) on this branch, and the latest CI run confirms it:

  • test (job 6) — the TranslateTest › Does <locale> locale have all the keys failure — now passes, along with every other test job.
  • ESLint check, Oxfmt check, React Compiler Compliance, and the JS builds — all pass.
  • typecheck — re-running (pending) at the time of writing; it passed locally after the fix, and all TS-dependent jobs that have finished (tests, ESLint, builds) are green.

The only checks still red are checklist and Check independent approval. These are human-process gates (PR author/reviewer checklists and independent approval), not something I can resolve by pushing code — they'll clear once the checklists are completed and the PR gets its required approval.

No further code changes are needed for the CI failures. If typecheck or the ad-hoc build (Expensify Dev, Debug, true) shows red once they finish (the latter previously failed on an unrelated CocoaPods/curl 401 infra error), re-ping me and I'll take another look.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

The code checks are green now — the fix I pushed earlier (commit 5bd2488) cleared typecheck, all test jobs (including test (job 6) / TranslateTest), Bun tests, ESLint check, and Oxfmt check. There's nothing further to push.

The only remaining red checks are:

  • build (Expensify Dev, Debug, true) — an unrelated CI infrastructure failure, not this PR's code. The iOS Rock build dies during dependency setup with CocoaPods installation failed (curl (56) … error: 401) plus a remote-cache miss (NoSuchKey). This is identical to the previous run and can't be fixed from this TS/translation-only diff; it needs a CI re-run.
  • Check independent approval and checklist — these are approval/checklist gates, not code failures. They clear through the normal review process, not a code change.

@ShridharGoel

Copy link
Copy Markdown
Contributor

@MelvinBot Fill out the author checklist and mark as ready for review

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 I'm not able to fill out the author checklist or flip this to "ready for review" on your behalf — those are human attestations, and checking them would be a false claim.

The PR Author Checklist asks the author to confirm things only a person can truthfully attest to, e.g.:

  • "I ran the tests on all platforms & verified they passed on: Android Native, Android mWeb Chrome, iOS Native, iOS mWeb Safari, MacOS Chrome/Safari"
  • "I included screenshots or videos for tests on all platforms" (the Screenshots/Videos section is currently empty for every platform)
  • "I tested this PR with a High Traffic account against staging/production"
  • "I verified there are no console errors"

The PR body also still has explicit gates carried over from #97513:

// TODO: The human co-author must confirm these tests before marking this PR "ready for review".

So this needs you (or the co-author) to actually run the manual test steps, add the platform screenshots/videos, tick the boxes, and then mark it ready.

What I can do: the CI failures I was asked about earlier (typecheck, ESLint, test (job 6)) are already fixed on the branch. If it's helpful, reply with @MelvinBot test the DualEntry export flow on web and I'll run a web smoke test of Test 1 (open a workspace → Accounting → DualEntry → Export → verify the page loads and each option row navigates) and post the result + screenshots so you have evidence for the MacOS: Chrome row. I can't cover the native/mWeb platforms, though — those still need a human.

@ShridharGoel

Copy link
Copy Markdown
Contributor

@MelvinBot Mark this as ready for review

@MelvinBot
MelvinBot marked this pull request as ready for review August 12, 2026 06:23
@MelvinBot
MelvinBot requested a review from a team as a code owner August 12, 2026 06:23
@melvin-bot
melvin-bot Bot requested a review from truph01 August 12, 2026 06:23
@melvin-bot

melvin-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

@truph01 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 requested review from joekaufmanexpensify and removed request for a team August 12, 2026 06:23
@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 Marked this PR as ready for review.

All code checks are green (ESLint, typecheck, test, Bun tests, Oxfmt, React Compiler, Storybook, JS builds). Two checks remain red and are expected:

  • build (Expensify Dev, Debug, true) — fails on RockError: CocoaPods installation failed (curl (56) … 401 fetching react-native-artifacts from GitHub Packages during the iOS HybridApp pod install). This is a CI auth/infra flake, not a code issue — re-running the job should clear it.
  • Check independent approval / checklist — expected until the PR is reviewed and the author/reviewer checklists are completed.

@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: 5bd248892a

ℹ️ 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 selectExpensifyCardAccount = (item: AccountListItem) => {
if (item.value !== expensifyCardAccountID && policyID) {
// Choosing the default account clears the custom account
const value = item.value === companyCardAccountID ? '' : item.value;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Treat cleared Expensify Card accounts as default

When a user selects the company-card account to clear a custom Expensify Card override, this writes an empty string through updateDualEntryExpensifyCardAccount, but both this selector and the export summary compute the displayed account with expensifyCardAccountID ?? companyCardAccountID. Since '' is not nullish, the optimistic Onyx update leaves no row selected and the export page shows a blank Expensify Card account instead of falling back to the company-card account; store a nullish value locally or treat an empty string as the default account.

Useful? React with 👍 / 👎.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

All the PR-related checks are now green after the previous fix — typecheck, every test (job N) (including job 6 / TranslateTest), and Bun tests all pass.

The one remaining non-ignored red check, build (Expensify Dev, Debug, true), is failing for a reason unrelated to this PR — no code change will fix it. During the iOS pod install, CocoaPods fetches a prebuilt hybrid React Native artifact from GitHub Packages and gets a 401 Unauthorized:

[!] /usr/bin/curl -f -L -o .../file.tgz \
    https://maven.pkg.github.com/Expensify/App/com/expensify/react-hybrid/react-native-artifacts/0.86.0-1/react-native-artifacts-0.86.0-1-reactnative-core-debug.tar.gz ...
curl: (56) The requested URL returned error: 401
■ RockError: CocoaPods installation failed.

This is a GitHub Packages authentication/infrastructure failure in the build environment (this PR only touches TypeScript translation files and DualEntry connection-key casing — nothing that affects native dependency resolution). It has reproduced identically across build runs. It should clear once the Packages auth issue is resolved or the build is re-run with valid credentials; no action is needed on the branch itself.

The remaining checklist and Check independent approval failures are the standard human/author checks, not automated code checks.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

The code-related failures are already fixed — typecheck, test (job 6), and the rest are green after my previous push (commit 5bd2488). Nothing new to fix here.

The only remaining red check, build (Expensify Dev, Debug, true), is failing for an infrastructure reason unrelated to this PR, so there's nothing in the diff I can push to resolve it:

[!] Error installing React-Core-prebuilt
[!] /usr/bin/curl -f -L -o …/file.tgz https://maven.pkg.github.com/Expensify/App/com/expensify/react-hybrid/react-native-artifacts/0.86.0-1/react-native-artifacts-0.86.0-1-reactnative-core-debug.tar.gz …
curl: (56) The requested URL returned error: 401
RockError: CocoaPods installation failed.

pod install gets a 401 Unauthorized while downloading the prebuilt React Native artifact from GitHub Packages — a build-environment auth/credentials problem, not a code issue. This PR changes only JS/TS files (src/languages/*, src/libs/*, src/pages/**) and touches nothing under iOS, the Podfile, native modules, or the RN artifact version, so it can't have caused this.

Recommendation: re-run the build job (it's likely transient); if the 401 persists, the GitHub Packages token used by the iOS ad-hoc build needs attention from an internal engineer — it isn't fixable from this branch.

@truph01

truph01 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

@ShridharGoel The call to UpdateDualEntryExportDate API has error:

502 Bad Gateway

],
onExportPagePress: () => null,
onExportPagePress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_DUALENTRY_EXPORT.getRoute(policyID)),
subscribedExportSettings: [

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.

Missing EXPENSIFY_CARD_ACCOUNT_ID in subscribedExportSettings

CREDIT_CARD_ACCOUNT_ID is included in the subscribedExportSettings array but EXPENSIFY_CARD_ACCOUNT_ID isn't — so the export section's error/pending indicator on the main accounting page won't reflect issues with the Expensify Card account setting. Could we add CONST.DUALENTRY_CONFIG.EXPENSIFY_CARD_ACCOUNT_ID to this list?

const selectExpensifyCardAccount = (item: AccountListItem) => {
if (item.value !== expensifyCardAccountID && policyID) {
// Choosing the default account clears the custom account
const value = item.value === companyCardAccountID ? '' : item.value;

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.

When the user picks the same account as the company card, value is set to ''. On the export page, expensifyCardAccountID ?? companyCardAccountID won't fall back because '' isn't nullish — so the Expensify Card Account row briefly shows no value during the optimistic update. Would using || companyCardAccountID (or sending undefined/null instead of '') give a smoother fallback?

@truph01

truph01 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

@ShridharGoel I left a few comments

@MelvinBot

Copy link
Copy Markdown
Contributor Author

The code-related failures are already resolved by my earlier push (5bd2488) — typecheck, test (job 6) / TranslateTest, ESLint, React Compiler, Oxfmt, and Bun tests are all green now. No further code change is needed.

The only remaining red check is build (Expensify Dev, Debug, true), which fails for an unrelated infrastructure reason and re-fails on every re-run:

Remote cache failure error: NoSuchKey: The specified key does not exist.
...
curl: (56) The requested URL returned error: 401
■ Failed: Installing CocoaPods dependencies
■ RockError: CocoaPods installation failed.

This is the iOS pod install step hitting a 401 while fetching a pod/remote-cache artifact — an environment/auth problem on the build runner, not anything in this PR. This diff only changes TypeScript translation files and the DualEntry export pages; it makes no iOS, Podfile, native-module, or dependency changes, so there's nothing here that can cause or fix that CocoaPods 401. It should clear once the build infra is healthy (or on a maintainer re-run).

The other two red checks — checklist and Check independent approval — are the standard author/approval gates, not code failures.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

All code-related checks now pass on the latest commit (5bd2488) — the earlier fix resolved them:

  • typecheck
  • test (job 1–8) (including the TranslateTest that was failing in job 6)
  • Bun tests, ESLint check

The only remaining non-ignore-list failure, build (Expensify Dev, Debug, true), is unrelated to this PR — no code fix is warranted.

Why it's unrelated: it fails during CocoaPods install while curl-ing a prebuilt React Native artifact from GitHub Packages, which returns 401 Unauthorized:

[!] /usr/bin/curl -f -L -o …/file.tgz https://maven.pkg.github.com/Expensify/App/com/expensify/react-hybrid/react-native-artifacts/0.86.0-1/react-native-artifacts-0.86.0-1-reactnative-core-debug.tar.gz --netrc-optional --retry 2 …
curl: (56) The requested URL returned error: 401
■ RockError: CocoaPods installation failed.

This is a GitHub Packages auth/registry-access issue on the build runner (the same 401 hit the prior run too). This PR touches only JS/TS source (src/languages, src/libs, src/pages, CONST, ROUTES, SCREENS, types) — no package.json, Podfile, iOS/Android, RN version, or dependency changes — so nothing here can cause an artifact-download 401. It should clear on a re-run once registry access is restored.

The two checklist checks and Check independent approval are human/process gates, not code failures.

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.

6 participants