From 5963ffc5dc375a0414c0a7b0c1696ec8e087610a Mon Sep 17 00:00:00 2001 From: VH Date: Tue, 3 Feb 2026 16:51:28 +0700 Subject: [PATCH 01/14] fix: show reportID when report is deleted in unapproved transactions message When a report is deleted, reportName becomes empty causing an empty anchor link. This change displays the plain reportID (e.g., #123456) instead when reportName is not available, ensuring the message remains meaningful across all languages. --- src/languages/de.ts | 4 ++-- src/languages/en.ts | 4 ++-- src/languages/es.ts | 4 ++-- src/languages/fr.ts | 4 ++-- src/languages/it.ts | 4 ++-- src/languages/ja.ts | 4 ++-- src/languages/nl.ts | 4 ++-- src/languages/params.ts | 1 + src/languages/pl.ts | 4 ++-- src/languages/pt-BR.ts | 4 ++-- src/languages/zh-hans.ts | 4 ++-- src/libs/ReportActionsUtils.ts | 2 +- 12 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index 95ce823e52e8..641aadbce890 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -925,8 +925,8 @@ const translations: TranslationDeepObject = { asCopilot: 'als Copilot für', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `hat diesen Bericht erstellt, um alle Ausgaben aus ${reportName} aufzunehmen, die nicht mit der von dir gewählten Häufigkeit eingereicht werden konnten`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName}: CreatedReportForUnapprovedTransactionsParams) => - `erstellte diesen Bericht für alle zurückgehaltenen Ausgaben aus ${reportName}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => + `erstellte diesen Bericht für alle zurückgehaltenen Ausgaben aus ${reportName ? `${reportName}` : `#${reportID}`}`, }, mentionSuggestions: { hereAlternateText: 'Alle in dieser Unterhaltung benachrichtigen', diff --git a/src/languages/en.ts b/src/languages/en.ts index 058ae9ea21bb..1ed7bd923b70 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -939,8 +939,8 @@ const translations = { asCopilot: 'as copilot for', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `created this report to hold all expenses from ${reportName} that couldn't be submitted on your chosen frequency`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName}: CreatedReportForUnapprovedTransactionsParams) => - `created this report for any held expenses from ${reportName}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => + `created this report for any held expenses from ${reportName ? `${reportName}` : `#${reportID}`}`, }, mentionSuggestions: { hereAlternateText: 'Notify everyone in this conversation', diff --git a/src/languages/es.ts b/src/languages/es.ts index aa9a9f802e57..e3051b199834 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -687,8 +687,8 @@ const translations: TranslationDeepObject = { asCopilot: 'como copiloto de', harvestCreatedExpenseReport: (reportUrl, reportName) => `creó este informe para contener todos los gastos de ${reportName} que no se pudieron enviar con la frecuencia que elegiste`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName}: CreatedReportForUnapprovedTransactionsParams) => - `creó este informe para cualquier gasto retenido de ${reportName}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => + `creó este informe para cualquier gasto retenido de ${reportName ? `${reportName}` : `#${reportID}`}`, }, mentionSuggestions: { hereAlternateText: 'Notificar a todos en esta conversación', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index ae288dfd8238..abf22a3190d0 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -929,8 +929,8 @@ const translations: TranslationDeepObject = { asCopilot: 'en tant que copilote pour', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `a créé cette note de frais pour regrouper toutes les dépenses de ${reportName} qui n'ont pas pu être soumises à la fréquence que vous avez choisie`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName}: CreatedReportForUnapprovedTransactionsParams) => - `a créé cette note de frais pour toutes les dépenses en attente de ${reportName}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => + `a créé cette note de frais pour toutes les dépenses en attente de ${reportName ? `${reportName}` : `#${reportID}`}`, }, mentionSuggestions: { hereAlternateText: 'Notifier tout le monde dans cette conversation', diff --git a/src/languages/it.ts b/src/languages/it.ts index ae040c048fa3..3a4e1e1d89c5 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -925,8 +925,8 @@ const translations: TranslationDeepObject = { asCopilot: 'come copilota per', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `ha creato questo report per raccogliere tutte le spese da ${reportName} che non potevano essere inviate con la frequenza scelta`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName}: CreatedReportForUnapprovedTransactionsParams) => - `ha creato questo report per tutte le spese in sospeso da ${reportName}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => + `ha creato questo report per tutte le spese in sospeso da ${reportName ? `${reportName}` : `#${reportID}`}`, }, mentionSuggestions: { hereAlternateText: 'Notifica tutti in questa conversazione', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index f169d12a662b..3596afbab30c 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -921,8 +921,8 @@ const translations: TranslationDeepObject = { asCopilot: '共同操縦者として', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `選択した頻度で提出できなかった、${reportName} のすべての経費を保持するためにこのレポートを作成しました`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName}: CreatedReportForUnapprovedTransactionsParams) => - `${reportName} から保留中の経費を対象としてこのレポートを作成しました`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => + `${reportName ? `${reportName}` : `#${reportID}`} から保留中の経費を対象としてこのレポートを作成しました`, }, mentionSuggestions: { hereAlternateText: 'この会話の全員に通知', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index f1b4424ee451..24ff1eac7d72 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -924,8 +924,8 @@ const translations: TranslationDeepObject = { asCopilot: 'als copiloot voor', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `heeft dit rapport gemaakt om alle uitgaven van ${reportName} te bewaren die niet konden worden ingediend op de door jou gekozen frequentie`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName}: CreatedReportForUnapprovedTransactionsParams) => - `heeft dit rapport gemaakt voor uitgestelde uitgaven van ${reportName}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => + `heeft dit rapport gemaakt voor uitgestelde uitgaven van ${reportName ? `${reportName}` : `#${reportID}`}`, }, mentionSuggestions: { hereAlternateText: 'Laat iedereen in dit gesprek een melding krijgen', diff --git a/src/languages/params.ts b/src/languages/params.ts index 82296d24a987..5dc98638d69f 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -84,6 +84,7 @@ type ReportArchiveReasonsInvoiceReceiverPolicyDeletedParams = { type CreatedReportForUnapprovedTransactionsParams = { reportUrl: string; reportName: string; + reportID: string | undefined; }; type MovedTransactionParams = { diff --git a/src/languages/pl.ts b/src/languages/pl.ts index d3dbb5696f5b..41dbcb502641 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -925,8 +925,8 @@ const translations: TranslationDeepObject = { asCopilot: 'jako drugi pilot dla', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `utworzył(-a) ten raport, aby zawierał wszystkie wydatki z ${reportName}, których nie można było złożyć z wybraną przez Ciebie częstotliwością`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName}: CreatedReportForUnapprovedTransactionsParams) => - `utworzył/-a ten raport dla wszystkich wstrzymanych wydatków z ${reportName}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => + `utworzył/-a ten raport dla wszystkich wstrzymanych wydatków z ${reportName ? `${reportName}` : `#${reportID}`}`, }, mentionSuggestions: { hereAlternateText: 'Powiadom wszystkich w tej konwersacji', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index ad95bf3a24f0..8545b76ea925 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -924,8 +924,8 @@ const translations: TranslationDeepObject = { asCopilot: 'como copiloto de', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `criou este relatório para manter todas as despesas de ${reportName} que não puderam ser enviadas na frequência escolhida por você`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName}: CreatedReportForUnapprovedTransactionsParams) => - `criou este relatório para quaisquer despesas em espera de ${reportName}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => + `criou este relatório para quaisquer despesas em espera de ${reportName ? `${reportName}` : `#${reportID}`}`, }, mentionSuggestions: { hereAlternateText: 'Notificar todas as pessoas nesta conversa', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index e8d83be9ce05..cce2b9fdd196 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -913,8 +913,8 @@ const translations: TranslationDeepObject = { reportAction: { asCopilot: '作为副驾驶,用于', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `创建了此报销单,用于保存所有来自 ${reportName} 且无法按照你选择的频率提交的费用`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName}: CreatedReportForUnapprovedTransactionsParams) => - `从${reportName}创建了此报表,用于查看任何被暂扣的报销费用`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => + `从${reportName ? `${reportName}` : `#${reportID}`}创建了此报表,用于查看任何被暂扣的报销费用`, }, mentionSuggestions: { hereAlternateText: '通知此会话中的所有人', diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 33c10db00101..00e547663baf 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -3562,7 +3562,7 @@ function getHarvestCreatedExpenseReportMessage(reportID: string | undefined, rep function getCreatedReportForUnapprovedTransactionsMessage(reportID: string | undefined, reportName: string, translate: LocalizedTranslate): string { const reportUrl = getReportURLForCurrentContext(reportID); - return translate('reportAction.createdReportForUnapprovedTransactions', {reportUrl, reportName}); + return translate('reportAction.createdReportForUnapprovedTransactions', {reportUrl, reportName, reportID}); } function getDynamicExternalWorkflowRoutedMessage( From 286774e342d9e73f32560dfa500dfc71f08bb282 Mon Sep 17 00:00:00 2001 From: VH Date: Tue, 3 Feb 2026 16:57:33 +0700 Subject: [PATCH 02/14] test: update getCreatedReportForUnapprovedTransactionsMessage tests --- tests/unit/ReportActionsUtilsTest.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/unit/ReportActionsUtilsTest.ts b/tests/unit/ReportActionsUtilsTest.ts index 24a06868b876..1df8ea24053e 100644 --- a/tests/unit/ReportActionsUtilsTest.ts +++ b/tests/unit/ReportActionsUtilsTest.ts @@ -1664,12 +1664,19 @@ describe('ReportActionsUtils', () => { const expectedMessage = translateLocal('reportAction.createdReportForUnapprovedTransactions', { reportUrl, reportName, + reportID, }); const result = getCreatedReportForUnapprovedTransactionsMessage(reportID, reportName, translateLocal); expect(result).toBe(expectedMessage); }); + + it('should return a message with plain reportID when reportName is empty', () => { + const result = getCreatedReportForUnapprovedTransactionsMessage('123456', '', translateLocal); + + expect(result).toContain('#123456'); + }); }); describe('isDynamicExternalWorkflowSubmitFailedAction', () => { From c2b7589513c6518c9282bdd115fa3fffad347810 Mon Sep 17 00:00:00 2001 From: VH Date: Tue, 3 Feb 2026 17:11:05 +0700 Subject: [PATCH 03/14] run prettier --- tests/unit/ReportActionsUtilsTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/ReportActionsUtilsTest.ts b/tests/unit/ReportActionsUtilsTest.ts index 1df8ea24053e..6c2c96f2a586 100644 --- a/tests/unit/ReportActionsUtilsTest.ts +++ b/tests/unit/ReportActionsUtilsTest.ts @@ -1674,7 +1674,7 @@ describe('ReportActionsUtils', () => { it('should return a message with plain reportID when reportName is empty', () => { const result = getCreatedReportForUnapprovedTransactionsMessage('123456', '', translateLocal); - + expect(result).toContain('#123456'); }); }); From c96e8137cf614acb1d3546f0be7f10f4072e418b Mon Sep 17 00:00:00 2001 From: VH Date: Tue, 3 Feb 2026 18:09:00 +0700 Subject: [PATCH 04/14] Update translations for createdReportForUnapprovedTransactions - Add "deleted report" text when reportName is empty across all language files - Update test to verify "deleted report" text is included --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- tests/unit/ReportActionsUtilsTest.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 1ed7bd923b70..75655cebb291 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -940,7 +940,7 @@ const translations = { harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `created this report to hold all expenses from ${reportName} that couldn't be submitted on your chosen frequency`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - `created this report for any held expenses from ${reportName ? `${reportName}` : `#${reportID}`}`, + `created this report for any held expenses from ${reportName ? `${reportName}` : `deleted report #${reportID}`}`, }, mentionSuggestions: { hereAlternateText: 'Notify everyone in this conversation', diff --git a/src/languages/es.ts b/src/languages/es.ts index e3051b199834..28d1fccddca7 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -688,7 +688,7 @@ const translations: TranslationDeepObject = { harvestCreatedExpenseReport: (reportUrl, reportName) => `creó este informe para contener todos los gastos de ${reportName} que no se pudieron enviar con la frecuencia que elegiste`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - `creó este informe para cualquier gasto retenido de ${reportName ? `${reportName}` : `#${reportID}`}`, + `creó este informe para cualquier gasto retenido de ${reportName ? `${reportName}` : `informe eliminado #${reportID}`}`, }, mentionSuggestions: { hereAlternateText: 'Notificar a todos en esta conversación', diff --git a/tests/unit/ReportActionsUtilsTest.ts b/tests/unit/ReportActionsUtilsTest.ts index 6c2c96f2a586..c0e2c8caee53 100644 --- a/tests/unit/ReportActionsUtilsTest.ts +++ b/tests/unit/ReportActionsUtilsTest.ts @@ -1675,7 +1675,7 @@ describe('ReportActionsUtils', () => { it('should return a message with plain reportID when reportName is empty', () => { const result = getCreatedReportForUnapprovedTransactionsMessage('123456', '', translateLocal); - expect(result).toContain('#123456'); + expect(result).toContain('deleted report #123456'); }); }); From 6f89f0dd0799f8d58a6355d04fa0555c27c68a08 Mon Sep 17 00:00:00 2001 From: VH Date: Wed, 4 Feb 2026 05:43:55 +0700 Subject: [PATCH 05/14] refactor: use complete sentences in translation for deleted reports Changed from embedded ternary to complete separate sentences for better translation quality. Translators now see full context for both cases: - When report exists: link with report name - When report deleted: text with reportID This improves translation accuracy across all 10 languages. --- src/languages/de.ts | 4 +++- src/languages/en.ts | 4 +++- src/languages/es.ts | 4 +++- src/languages/fr.ts | 4 +++- src/languages/it.ts | 4 +++- src/languages/ja.ts | 4 +++- src/languages/nl.ts | 4 +++- src/languages/pl.ts | 4 +++- src/languages/pt-BR.ts | 4 +++- src/languages/zh-hans.ts | 2 +- 10 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index 641aadbce890..7d18df80b4e7 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -926,7 +926,9 @@ const translations: TranslationDeepObject = { harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `hat diesen Bericht erstellt, um alle Ausgaben aus ${reportName} aufzunehmen, die nicht mit der von dir gewählten Häufigkeit eingereicht werden konnten`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - `erstellte diesen Bericht für alle zurückgehaltenen Ausgaben aus ${reportName ? `${reportName}` : `#${reportID}`}`, + reportName + ? `erstellte diesen Bericht für alle zurückgehaltenen Ausgaben aus ${reportName}` + : `erstellte diesen Bericht für alle zurückgehaltenen Ausgaben aus gelöschtem Bericht #${reportID}`, }, mentionSuggestions: { hereAlternateText: 'Alle in dieser Unterhaltung benachrichtigen', diff --git a/src/languages/en.ts b/src/languages/en.ts index 75655cebb291..6aa5164ebdde 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -940,7 +940,9 @@ const translations = { harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `created this report to hold all expenses from ${reportName} that couldn't be submitted on your chosen frequency`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - `created this report for any held expenses from ${reportName ? `${reportName}` : `deleted report #${reportID}`}`, + reportName + ? `created this report for any held expenses from ${reportName}` + : `created this report for any held expenses from deleted report #${reportID}`, }, mentionSuggestions: { hereAlternateText: 'Notify everyone in this conversation', diff --git a/src/languages/es.ts b/src/languages/es.ts index 28d1fccddca7..4a02fe5034d3 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -688,7 +688,9 @@ const translations: TranslationDeepObject = { harvestCreatedExpenseReport: (reportUrl, reportName) => `creó este informe para contener todos los gastos de ${reportName} que no se pudieron enviar con la frecuencia que elegiste`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - `creó este informe para cualquier gasto retenido de ${reportName ? `${reportName}` : `informe eliminado #${reportID}`}`, + reportName + ? `creó este informe para cualquier gasto retenido de ${reportName}` + : `creó este informe para cualquier gasto retenido del informe eliminado #${reportID}`, }, mentionSuggestions: { hereAlternateText: 'Notificar a todos en esta conversación', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index abf22a3190d0..3453f27aa1b8 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -930,7 +930,9 @@ const translations: TranslationDeepObject = { harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `a créé cette note de frais pour regrouper toutes les dépenses de ${reportName} qui n'ont pas pu être soumises à la fréquence que vous avez choisie`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - `a créé cette note de frais pour toutes les dépenses en attente de ${reportName ? `${reportName}` : `#${reportID}`}`, + reportName + ? `a créé cette note de frais pour toutes les dépenses en attente de ${reportName}` + : `a créé cette note de frais pour toutes les dépenses en attente de rapport supprimé #${reportID}`, }, mentionSuggestions: { hereAlternateText: 'Notifier tout le monde dans cette conversation', diff --git a/src/languages/it.ts b/src/languages/it.ts index 3a4e1e1d89c5..000e70cb937c 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -926,7 +926,9 @@ const translations: TranslationDeepObject = { harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `ha creato questo report per raccogliere tutte le spese da ${reportName} che non potevano essere inviate con la frequenza scelta`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - `ha creato questo report per tutte le spese in sospeso da ${reportName ? `${reportName}` : `#${reportID}`}`, + reportName + ? `ha creato questo report per tutte le spese in sospeso da ${reportName}` + : `ha creato questo report per tutte le spese in sospeso da report eliminato #${reportID}`, }, mentionSuggestions: { hereAlternateText: 'Notifica tutti in questa conversazione', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 3596afbab30c..a5e4059114b4 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -922,7 +922,9 @@ const translations: TranslationDeepObject = { harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `選択した頻度で提出できなかった、${reportName} のすべての経費を保持するためにこのレポートを作成しました`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - `${reportName ? `${reportName}` : `#${reportID}`} から保留中の経費を対象としてこのレポートを作成しました`, + reportName + ? `${reportName} から保留中の経費を対象としてこのレポートを作成しました` + : `削除されたレポート #${reportID} から保留中の経費を対象としてこのレポートを作成しました`, }, mentionSuggestions: { hereAlternateText: 'この会話の全員に通知', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 24ff1eac7d72..a675f7da1eed 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -925,7 +925,9 @@ const translations: TranslationDeepObject = { harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `heeft dit rapport gemaakt om alle uitgaven van ${reportName} te bewaren die niet konden worden ingediend op de door jou gekozen frequentie`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - `heeft dit rapport gemaakt voor uitgestelde uitgaven van ${reportName ? `${reportName}` : `#${reportID}`}`, + reportName + ? `heeft dit rapport gemaakt voor uitgestelde uitgaven van ${reportName}` + : `heeft dit rapport gemaakt voor uitgestelde uitgaven van verwijderd rapport #${reportID}`, }, mentionSuggestions: { hereAlternateText: 'Laat iedereen in dit gesprek een melding krijgen', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 41dbcb502641..8809349acb21 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -926,7 +926,9 @@ const translations: TranslationDeepObject = { harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `utworzył(-a) ten raport, aby zawierał wszystkie wydatki z ${reportName}, których nie można było złożyć z wybraną przez Ciebie częstotliwością`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - `utworzył/-a ten raport dla wszystkich wstrzymanych wydatków z ${reportName ? `${reportName}` : `#${reportID}`}`, + reportName + ? `utworzył/-a ten raport dla wszystkich wstrzymanych wydatków z ${reportName}` + : `utworzył/-a ten raport dla wszystkich wstrzymanych wydatków z usuniętego raportu #${reportID}`, }, mentionSuggestions: { hereAlternateText: 'Powiadom wszystkich w tej konwersacji', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 8545b76ea925..fa3a801567a9 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -925,7 +925,9 @@ const translations: TranslationDeepObject = { harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `criou este relatório para manter todas as despesas de ${reportName} que não puderam ser enviadas na frequência escolhida por você`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - `criou este relatório para quaisquer despesas em espera de ${reportName ? `${reportName}` : `#${reportID}`}`, + reportName + ? `criou este relatório para quaisquer despesas em espera de ${reportName}` + : `criou este relatório para quaisquer despesas em espera de relatório excluído #${reportID}`, }, mentionSuggestions: { hereAlternateText: 'Notificar todas as pessoas nesta conversa', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index cce2b9fdd196..065b30dfc29a 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -914,7 +914,7 @@ const translations: TranslationDeepObject = { asCopilot: '作为副驾驶,用于', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `创建了此报销单,用于保存所有来自 ${reportName} 且无法按照你选择的频率提交的费用`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - `从${reportName ? `${reportName}` : `#${reportID}`}创建了此报表,用于查看任何被暂扣的报销费用`, + reportName ? `从${reportName}创建了此报表,用于查看任何被暂扣的报销费用` : `从已删除的报告 #${reportID}创建了此报表,用于查看任何被暂扣的报销费用`, }, mentionSuggestions: { hereAlternateText: '通知此会话中的所有人', From 794867cf4a2eb54647348d4cacd91b004425dd50 Mon Sep 17 00:00:00 2001 From: VH Date: Wed, 4 Feb 2026 05:44:02 +0700 Subject: [PATCH 06/14] Update test --- tests/unit/ReportActionsUtilsTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/ReportActionsUtilsTest.ts b/tests/unit/ReportActionsUtilsTest.ts index c0e2c8caee53..1a323854b4ca 100644 --- a/tests/unit/ReportActionsUtilsTest.ts +++ b/tests/unit/ReportActionsUtilsTest.ts @@ -1675,7 +1675,7 @@ describe('ReportActionsUtils', () => { it('should return a message with plain reportID when reportName is empty', () => { const result = getCreatedReportForUnapprovedTransactionsMessage('123456', '', translateLocal); - expect(result).toContain('deleted report #123456'); + expect(result).toBe('created this report for any held expenses from deleted report #123456'); }); }); From 96fd208b837603d19a2980a31c35902d3496eb88 Mon Sep 17 00:00:00 2001 From: VH Date: Wed, 4 Feb 2026 08:58:54 +0700 Subject: [PATCH 07/14] use isReportDeleted to determine a report is deleted more reliable --- src/languages/de.ts | 8 ++--- src/languages/en.ts | 8 ++--- src/languages/es.ts | 8 ++--- src/languages/fr.ts | 8 ++--- src/languages/it.ts | 8 ++--- src/languages/ja.ts | 8 ++--- src/languages/nl.ts | 8 ++--- src/languages/params.ts | 1 + src/languages/pl.ts | 8 ++--- src/languages/pt-BR.ts | 8 ++--- src/languages/zh-hans.ts | 6 ++-- src/libs/ReportActionsUtils.ts | 4 +-- src/libs/ReportUtils.ts | 14 +++++++-- .../report/ContextMenu/ContextMenuActions.tsx | 6 ++-- .../inbox/report/PureReportActionItem.tsx | 8 +++-- tests/unit/ReportActionsUtilsTest.ts | 11 ++++--- tests/unit/ReportUtilsTest.ts | 31 +++++++++++++++++++ 17 files changed, 102 insertions(+), 51 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index 7d18df80b4e7..422b537750ab 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -925,10 +925,10 @@ const translations: TranslationDeepObject = { asCopilot: 'als Copilot für', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `hat diesen Bericht erstellt, um alle Ausgaben aus ${reportName} aufzunehmen, die nicht mit der von dir gewählten Häufigkeit eingereicht werden konnten`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - reportName - ? `erstellte diesen Bericht für alle zurückgehaltenen Ausgaben aus ${reportName}` - : `erstellte diesen Bericht für alle zurückgehaltenen Ausgaben aus gelöschtem Bericht #${reportID}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => + isReportDeleted + ? `erstellte diesen Bericht für alle zurückgehaltenen Ausgaben aus gelöschtem Bericht #${reportID}` + : `erstellte diesen Bericht für alle zurückgehaltenen Ausgaben aus ${reportName}`, }, mentionSuggestions: { hereAlternateText: 'Alle in dieser Unterhaltung benachrichtigen', diff --git a/src/languages/en.ts b/src/languages/en.ts index 6aa5164ebdde..0c5a5fc92530 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -939,10 +939,10 @@ const translations = { asCopilot: 'as copilot for', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `created this report to hold all expenses from ${reportName} that couldn't be submitted on your chosen frequency`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - reportName - ? `created this report for any held expenses from ${reportName}` - : `created this report for any held expenses from deleted report #${reportID}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => + isReportDeleted + ? `created this report for any held expenses from deleted report #${reportID}` + : `created this report for any held expenses from ${reportName}`, }, mentionSuggestions: { hereAlternateText: 'Notify everyone in this conversation', diff --git a/src/languages/es.ts b/src/languages/es.ts index 4a02fe5034d3..993ade4d0a2b 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -687,10 +687,10 @@ const translations: TranslationDeepObject = { asCopilot: 'como copiloto de', harvestCreatedExpenseReport: (reportUrl, reportName) => `creó este informe para contener todos los gastos de ${reportName} que no se pudieron enviar con la frecuencia que elegiste`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - reportName - ? `creó este informe para cualquier gasto retenido de ${reportName}` - : `creó este informe para cualquier gasto retenido del informe eliminado #${reportID}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => + isReportDeleted + ? `creó este informe para cualquier gasto retenido del informe eliminado #${reportID}` + : `creó este informe para cualquier gasto retenido de ${reportName}`, }, mentionSuggestions: { hereAlternateText: 'Notificar a todos en esta conversación', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 3453f27aa1b8..094e0cb8b767 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -929,10 +929,10 @@ const translations: TranslationDeepObject = { asCopilot: 'en tant que copilote pour', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `a créé cette note de frais pour regrouper toutes les dépenses de ${reportName} qui n'ont pas pu être soumises à la fréquence que vous avez choisie`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - reportName - ? `a créé cette note de frais pour toutes les dépenses en attente de ${reportName}` - : `a créé cette note de frais pour toutes les dépenses en attente de rapport supprimé #${reportID}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => + isReportDeleted + ? `a créé cette note de frais pour toutes les dépenses en attente de rapport supprimé #${reportID}` + : `a créé cette note de frais pour toutes les dépenses en attente de ${reportName}`, }, mentionSuggestions: { hereAlternateText: 'Notifier tout le monde dans cette conversation', diff --git a/src/languages/it.ts b/src/languages/it.ts index 000e70cb937c..d9d84c7abcb4 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -925,10 +925,10 @@ const translations: TranslationDeepObject = { asCopilot: 'come copilota per', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `ha creato questo report per raccogliere tutte le spese da ${reportName} che non potevano essere inviate con la frequenza scelta`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - reportName - ? `ha creato questo report per tutte le spese in sospeso da ${reportName}` - : `ha creato questo report per tutte le spese in sospeso da report eliminato #${reportID}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => + isReportDeleted + ? `ha creato questo report per tutte le spese in sospeso da report eliminato #${reportID}` + : `ha creato questo report per tutte le spese in sospeso da ${reportName}`, }, mentionSuggestions: { hereAlternateText: 'Notifica tutti in questa conversazione', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index a5e4059114b4..09dcf0b3383c 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -921,10 +921,10 @@ const translations: TranslationDeepObject = { asCopilot: '共同操縦者として', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `選択した頻度で提出できなかった、${reportName} のすべての経費を保持するためにこのレポートを作成しました`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - reportName - ? `${reportName} から保留中の経費を対象としてこのレポートを作成しました` - : `削除されたレポート #${reportID} から保留中の経費を対象としてこのレポートを作成しました`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => + isReportDeleted + ? `削除されたレポート #${reportID} から保留中の経費を対象としてこのレポートを作成しました` + : `${reportName} から保留中の経費を対象としてこのレポートを作成しました`, }, mentionSuggestions: { hereAlternateText: 'この会話の全員に通知', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index a675f7da1eed..1ba072ef3af6 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -924,10 +924,10 @@ const translations: TranslationDeepObject = { asCopilot: 'als copiloot voor', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `heeft dit rapport gemaakt om alle uitgaven van ${reportName} te bewaren die niet konden worden ingediend op de door jou gekozen frequentie`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - reportName - ? `heeft dit rapport gemaakt voor uitgestelde uitgaven van ${reportName}` - : `heeft dit rapport gemaakt voor uitgestelde uitgaven van verwijderd rapport #${reportID}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => + isReportDeleted + ? `heeft dit rapport gemaakt voor uitgestelde uitgaven van verwijderd rapport #${reportID}` + : `heeft dit rapport gemaakt voor uitgestelde uitgaven van ${reportName}`, }, mentionSuggestions: { hereAlternateText: 'Laat iedereen in dit gesprek een melding krijgen', diff --git a/src/languages/params.ts b/src/languages/params.ts index 5dc98638d69f..12bcd0a3a71f 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -85,6 +85,7 @@ type CreatedReportForUnapprovedTransactionsParams = { reportUrl: string; reportName: string; reportID: string | undefined; + isReportDeleted: boolean; }; type MovedTransactionParams = { diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 8809349acb21..f4d85a7fce3b 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -925,10 +925,10 @@ const translations: TranslationDeepObject = { asCopilot: 'jako drugi pilot dla', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `utworzył(-a) ten raport, aby zawierał wszystkie wydatki z ${reportName}, których nie można było złożyć z wybraną przez Ciebie częstotliwością`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - reportName - ? `utworzył/-a ten raport dla wszystkich wstrzymanych wydatków z ${reportName}` - : `utworzył/-a ten raport dla wszystkich wstrzymanych wydatków z usuniętego raportu #${reportID}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => + isReportDeleted + ? `utworzył/-a ten raport dla wszystkich wstrzymanych wydatków z usuniętego raportu #${reportID}` + : `utworzył/-a ten raport dla wszystkich wstrzymanych wydatków z ${reportName}`, }, mentionSuggestions: { hereAlternateText: 'Powiadom wszystkich w tej konwersacji', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index fa3a801567a9..e0133cde4ed2 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -924,10 +924,10 @@ const translations: TranslationDeepObject = { asCopilot: 'como copiloto de', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `criou este relatório para manter todas as despesas de ${reportName} que não puderam ser enviadas na frequência escolhida por você`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - reportName - ? `criou este relatório para quaisquer despesas em espera de ${reportName}` - : `criou este relatório para quaisquer despesas em espera de relatório excluído #${reportID}`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => + isReportDeleted + ? `criou este relatório para quaisquer despesas em espera de relatório excluído #${reportID}` + : `criou este relatório para quaisquer despesas em espera de ${reportName}`, }, mentionSuggestions: { hereAlternateText: 'Notificar todas as pessoas nesta conversa', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 065b30dfc29a..de32afcded9e 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -913,8 +913,10 @@ const translations: TranslationDeepObject = { reportAction: { asCopilot: '作为副驾驶,用于', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `创建了此报销单,用于保存所有来自 ${reportName} 且无法按照你选择的频率提交的费用`, - createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID}: CreatedReportForUnapprovedTransactionsParams) => - reportName ? `从${reportName}创建了此报表,用于查看任何被暂扣的报销费用` : `从已删除的报告 #${reportID}创建了此报表,用于查看任何被暂扣的报销费用`, + createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => + isReportDeleted + ? `从已删除的报告 #${reportID}创建了此报表,用于查看任何被暂扣的报销费用` + : `从${reportName}创建了此报表,用于查看任何被暂扣的报销费用`, }, mentionSuggestions: { hereAlternateText: '通知此会话中的所有人', diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 00e547663baf..abca43dab0ef 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -3560,9 +3560,9 @@ function getHarvestCreatedExpenseReportMessage(reportID: string | undefined, rep return translate('reportAction.harvestCreatedExpenseReport', reportUrl, reportName); } -function getCreatedReportForUnapprovedTransactionsMessage(reportID: string | undefined, reportName: string, translate: LocalizedTranslate): string { +function getCreatedReportForUnapprovedTransactionsMessage(reportID: string | undefined, reportName: string, isReportDeleted: boolean, translate: LocalizedTranslate): string { const reportUrl = getReportURLForCurrentContext(reportID); - return translate('reportAction.createdReportForUnapprovedTransactions', {reportUrl, reportName, reportID}); + return translate('reportAction.createdReportForUnapprovedTransactions', {reportUrl, reportName, reportID, isReportDeleted}); } function getDynamicExternalWorkflowRoutedMessage( diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0127b0521923..fb31afb7ba71 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5972,11 +5972,11 @@ function getReportName( } if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.CREATED_REPORT_FOR_UNAPPROVED_TRANSACTIONS)) { const {originalID} = getOriginalMessage(parentReportAction) ?? {}; - const originalReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${originalID}`]; + const originalReportOfUnapprovedTransactions = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${originalID}`]; // eslint-disable-next-line @typescript-eslint/no-deprecated -- temporarily disabling rule for deprecated functions out of issue scope - const reportName = getReportName(originalReport); + const reportName = getReportName(originalReportOfUnapprovedTransactions); // eslint-disable-next-line @typescript-eslint/no-deprecated -- temporarily disabling rule for deprecated functions out of issue scope - return getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, translateLocal); + return getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, isReportDeleted(originalReportOfUnapprovedTransactions), translateLocal); } if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.SETTLEMENT_ACCOUNT_LOCKED)) { @@ -10644,6 +10644,13 @@ function isReportParticipant(accountID: number | undefined, report: OnyxEntry): boolean { + return !report || !report.reportID || report.pendingFields?.preview === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; +} + /** * Check to see if the current user has access to view the report. */ @@ -13435,6 +13442,7 @@ export { shouldHideSingleReportField, getReportForHeader, isReportOpenOrUnsubmitted, + isReportDeleted, }; export type { diff --git a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx index 6bfcee0aaaeb..2176436be3f0 100644 --- a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx @@ -144,6 +144,7 @@ import { getUnreportedTransactionMessage, getWorkspaceNameUpdatedMessage, isExpenseReport, + isReportDeleted, shouldDisableThread, shouldDisplayThreadReplies as shouldDisplayThreadRepliesReportUtils, } from '@libs/ReportUtils'; @@ -971,8 +972,9 @@ const ContextMenuActions: ContextMenuAction[] = [ setClipboardMessage(displayMessage); } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.CREATED_REPORT_FOR_UNAPPROVED_TRANSACTIONS)) { const {originalID} = getOriginalMessage(reportAction) ?? {}; - const reportName = getReportName(getReportOrDraftReport(originalID)); - const displayMessage = getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, translate); + const originalReportOfUnapprovedTransactions = getReportOrDraftReport(originalID); + const reportName = getReportName(originalReportOfUnapprovedTransactions); + const displayMessage = getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, isReportDeleted(originalReportOfUnapprovedTransactions), translate); setClipboardMessage(displayMessage); } else if (content) { setClipboardMessage( diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index 72a9bad07520..63d14399aeea 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -190,6 +190,7 @@ import { isCompletedTaskReport, isExpenseReport, isHarvestCreatedExpenseReport as isHarvestCreatedExpenseReportUtils, + isReportDeleted, isTaskReport, shouldDisplayThreadReplies as shouldDisplayThreadRepliesUtils, } from '@libs/ReportUtils'; @@ -1649,10 +1650,13 @@ function PureReportActionItem({ ); } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.CREATED_REPORT_FOR_UNAPPROVED_TRANSACTIONS)) { const {originalID} = getOriginalMessage(action) ?? {}; - const reportName = getReportName(allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${originalID}`]); + const originalReportOfUnapprovedTransactions = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${originalID}`]; + const reportName = getReportName(originalReportOfUnapprovedTransactions); children = ( - ${getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, translate)}`} /> + ${getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, isReportDeleted(originalReportOfUnapprovedTransactions), translate)}`} + /> ); } else if (isActionableMentionWhisper(action)) { diff --git a/tests/unit/ReportActionsUtilsTest.ts b/tests/unit/ReportActionsUtilsTest.ts index 1a323854b4ca..960ceab3abe1 100644 --- a/tests/unit/ReportActionsUtilsTest.ts +++ b/tests/unit/ReportActionsUtilsTest.ts @@ -1657,23 +1657,26 @@ describe('ReportActionsUtils', () => { }); describe('getCreatedReportForUnapprovedTransactionsMessage', () => { - it('should return the correct message with a valid report ID and report name', () => { + it('should return the correct message with a valid report ID and report name when report is not deleted', () => { const reportID = '67890'; const reportName = 'Original Report'; + const isReportDeleted = false; const reportUrl = getReportURLForCurrentContext(reportID); const expectedMessage = translateLocal('reportAction.createdReportForUnapprovedTransactions', { reportUrl, reportName, reportID, + isReportDeleted, }); - const result = getCreatedReportForUnapprovedTransactionsMessage(reportID, reportName, translateLocal); + const result = getCreatedReportForUnapprovedTransactionsMessage(reportID, reportName, isReportDeleted, translateLocal); expect(result).toBe(expectedMessage); }); - it('should return a message with plain reportID when reportName is empty', () => { - const result = getCreatedReportForUnapprovedTransactionsMessage('123456', '', translateLocal); + it('should return a message with plain reportID when report is deleted', () => { + const isReportDeleted = true; + const result = getCreatedReportForUnapprovedTransactionsMessage('123456', 'Some Name', isReportDeleted, translateLocal); expect(result).toBe('created this report for any held expenses from deleted report #123456'); }); diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 59b81f0512fa..aa34893a3ca3 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -97,6 +97,7 @@ import { isHarvestCreatedExpenseReport, isMoneyRequestReportEligibleForMerge, isPayer, + isReportDeleted, isReportOutstanding, isRootGroupChat, isSelfDMOrSelfDMThread, @@ -11575,4 +11576,34 @@ describe('ReportUtils', () => { }); }); }); + + describe('isReportDeleted', () => { + it('should return true when report is undefined', () => { + expect(isReportDeleted(undefined)).toBe(true); + }); + + it('should return true when report has no reportID', () => { + const report = { + ...createRandomReport(1, undefined), + reportID: '', + }; + expect(isReportDeleted(report)).toBe(true); + }); + + it('should return true when report has pendingAction DELETE', () => { + const report = { + ...createRandomReport(1, undefined), + pendingFields: {preview: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}, + }; + expect(isReportDeleted(report)).toBe(true); + }); + + it('should return false when report has valid reportID and no DELETE pendingAction', () => { + const report = { + ...createRandomReport(1, undefined), + pendingFields: {preview: null}, + }; + expect(isReportDeleted(report)).toBe(false); + }); + }); }); From 0bfc367be8f97cd31b4f16a2a98a386a12aa9f3d Mon Sep 17 00:00:00 2001 From: VH Date: Wed, 4 Feb 2026 17:28:04 +0700 Subject: [PATCH 08/14] update translations for createdReportForUnapprovedTransactions message --- src/languages/de.ts | 4 ++-- src/languages/fr.ts | 4 ++-- src/languages/it.ts | 2 +- src/languages/ja.ts | 4 ++-- src/languages/nl.ts | 4 ++-- src/languages/pl.ts | 4 ++-- src/languages/pt-BR.ts | 4 ++-- src/languages/zh-hans.ts | 4 +--- 8 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index 422b537750ab..503a5bf3c2a1 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -927,8 +927,8 @@ const translations: TranslationDeepObject = { `hat diesen Bericht erstellt, um alle Ausgaben aus ${reportName} aufzunehmen, die nicht mit der von dir gewählten Häufigkeit eingereicht werden konnten`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => isReportDeleted - ? `erstellte diesen Bericht für alle zurückgehaltenen Ausgaben aus gelöschtem Bericht #${reportID}` - : `erstellte diesen Bericht für alle zurückgehaltenen Ausgaben aus ${reportName}`, + ? `hat diesen Bericht für alle zurückgehaltenen Ausgaben aus dem gelöschten Bericht #${reportID} erstellt` + : `hat diesen Bericht für alle zurückgehaltenen Ausgaben aus ${reportName} erstellt`, }, mentionSuggestions: { hereAlternateText: 'Alle in dieser Unterhaltung benachrichtigen', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 094e0cb8b767..2fb2b0d2662e 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -931,8 +931,8 @@ const translations: TranslationDeepObject = { `a créé cette note de frais pour regrouper toutes les dépenses de ${reportName} qui n'ont pas pu être soumises à la fréquence que vous avez choisie`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => isReportDeleted - ? `a créé cette note de frais pour toutes les dépenses en attente de rapport supprimé #${reportID}` - : `a créé cette note de frais pour toutes les dépenses en attente de ${reportName}`, + ? `a créé cette note de frais pour toutes les dépenses retenues de la note de frais supprimée n°${reportID}` + : `a créé cette note de frais pour toutes les dépenses retenues provenant de ${reportName}`, }, mentionSuggestions: { hereAlternateText: 'Notifier tout le monde dans cette conversation', diff --git a/src/languages/it.ts b/src/languages/it.ts index d9d84c7abcb4..7f05698dd531 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -927,7 +927,7 @@ const translations: TranslationDeepObject = { `ha creato questo report per raccogliere tutte le spese da ${reportName} che non potevano essere inviate con la frequenza scelta`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => isReportDeleted - ? `ha creato questo report per tutte le spese in sospeso da report eliminato #${reportID}` + ? `ha creato questo rendiconto per tutte le spese in sospeso dal rendiconto eliminato n. ${reportID}` : `ha creato questo report per tutte le spese in sospeso da ${reportName}`, }, mentionSuggestions: { diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 09dcf0b3383c..94854bd1771f 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -923,8 +923,8 @@ const translations: TranslationDeepObject = { `選択した頻度で提出できなかった、${reportName} のすべての経費を保持するためにこのレポートを作成しました`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => isReportDeleted - ? `削除されたレポート #${reportID} から保留中の経費を対象としてこのレポートを作成しました` - : `${reportName} から保留中の経費を対象としてこのレポートを作成しました`, + ? `削除されたレポート #${reportID} に保留されていた経費のためにこのレポートを作成しました` + : `${reportName} の保留中経費すべてに対してこのレポートを作成しました`, }, mentionSuggestions: { hereAlternateText: 'この会話の全員に通知', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 1ba072ef3af6..466d374bea20 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -926,8 +926,8 @@ const translations: TranslationDeepObject = { `heeft dit rapport gemaakt om alle uitgaven van ${reportName} te bewaren die niet konden worden ingediend op de door jou gekozen frequentie`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => isReportDeleted - ? `heeft dit rapport gemaakt voor uitgestelde uitgaven van verwijderd rapport #${reportID}` - : `heeft dit rapport gemaakt voor uitgestelde uitgaven van ${reportName}`, + ? `heeft dit rapport gemaakt voor alle vastgehouden uitgaven uit verwijderd rapport nr. ${reportID}` + : `heeft dit rapport gemaakt voor alle vastgehouden uitgaven van ${reportName}`, }, mentionSuggestions: { hereAlternateText: 'Laat iedereen in dit gesprek een melding krijgen', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index f4d85a7fce3b..257b492e249e 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -927,8 +927,8 @@ const translations: TranslationDeepObject = { `utworzył(-a) ten raport, aby zawierał wszystkie wydatki z ${reportName}, których nie można było złożyć z wybraną przez Ciebie częstotliwością`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => isReportDeleted - ? `utworzył/-a ten raport dla wszystkich wstrzymanych wydatków z usuniętego raportu #${reportID}` - : `utworzył/-a ten raport dla wszystkich wstrzymanych wydatków z ${reportName}`, + ? `utworzono ten raport dla wszystkich wstrzymanych wydatków z usuniętego raportu nr ${reportID}` + : `utworzył(-a) ten raport dla wszystkich wstrzymanych wydatków z ${reportName}`, }, mentionSuggestions: { hereAlternateText: 'Powiadom wszystkich w tej konwersacji', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index e0133cde4ed2..5066195bdcc4 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -926,8 +926,8 @@ const translations: TranslationDeepObject = { `criou este relatório para manter todas as despesas de ${reportName} que não puderam ser enviadas na frequência escolhida por você`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => isReportDeleted - ? `criou este relatório para quaisquer despesas em espera de relatório excluído #${reportID}` - : `criou este relatório para quaisquer despesas em espera de ${reportName}`, + ? `criou este relatório para quaisquer despesas retidas do relatório excluído nº ${reportID}` + : `criou este relatório para quaisquer despesas retidas de ${reportName}`, }, mentionSuggestions: { hereAlternateText: 'Notificar todas as pessoas nesta conversa', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index de32afcded9e..0488cc58461b 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -914,9 +914,7 @@ const translations: TranslationDeepObject = { asCopilot: '作为副驾驶,用于', harvestCreatedExpenseReport: (reportUrl: string, reportName: string) => `创建了此报销单,用于保存所有来自 ${reportName} 且无法按照你选择的频率提交的费用`, createdReportForUnapprovedTransactions: ({reportUrl, reportName, reportID, isReportDeleted}: CreatedReportForUnapprovedTransactionsParams) => - isReportDeleted - ? `从已删除的报告 #${reportID}创建了此报表,用于查看任何被暂扣的报销费用` - : `从${reportName}创建了此报表,用于查看任何被暂扣的报销费用`, + isReportDeleted ? `为已删除报销单 #${reportID} 中的所有暂挂报销创建了此报销单` : `为从${reportName}中被暂挂的任何报销创建了此报表`, }, mentionSuggestions: { hereAlternateText: '通知此会话中的所有人', From b87dd439667c3162eab744e253ecf90692af524c Mon Sep 17 00:00:00 2001 From: VH Date: Thu, 5 Feb 2026 06:18:10 +0700 Subject: [PATCH 09/14] Address review feedback --- src/languages/params.ts | 2 +- src/libs/ReportActionsUtils.ts | 3 +++ tests/unit/ReportActionsUtilsTest.ts | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/languages/params.ts b/src/languages/params.ts index 12bcd0a3a71f..d56bc15972c7 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -84,7 +84,7 @@ type ReportArchiveReasonsInvoiceReceiverPolicyDeletedParams = { type CreatedReportForUnapprovedTransactionsParams = { reportUrl: string; reportName: string; - reportID: string | undefined; + reportID: string; isReportDeleted: boolean; }; diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index abca43dab0ef..40fa1eb0650a 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -3561,6 +3561,9 @@ function getHarvestCreatedExpenseReportMessage(reportID: string | undefined, rep } function getCreatedReportForUnapprovedTransactionsMessage(reportID: string | undefined, reportName: string, isReportDeleted: boolean, translate: LocalizedTranslate): string { + if (reportID === undefined) { + return ''; + } const reportUrl = getReportURLForCurrentContext(reportID); return translate('reportAction.createdReportForUnapprovedTransactions', {reportUrl, reportName, reportID, isReportDeleted}); } diff --git a/tests/unit/ReportActionsUtilsTest.ts b/tests/unit/ReportActionsUtilsTest.ts index 960ceab3abe1..5df4ce454dbf 100644 --- a/tests/unit/ReportActionsUtilsTest.ts +++ b/tests/unit/ReportActionsUtilsTest.ts @@ -1680,6 +1680,13 @@ describe('ReportActionsUtils', () => { expect(result).toBe('created this report for any held expenses from deleted report #123456'); }); + + it('should handle undefined reportID and report is deleted', () => { + const isReportDeleted = true; + const result = getCreatedReportForUnapprovedTransactionsMessage(undefined, 'Some Name', isReportDeleted, translateLocal); + + expect(result).toBe(''); + }); }); describe('isDynamicExternalWorkflowSubmitFailedAction', () => { From 51c372c1a816d924456c6870e60eba6a0ba18ef0 Mon Sep 17 00:00:00 2001 From: VH Date: Mon, 2 Mar 2026 17:49:35 +0700 Subject: [PATCH 10/14] Replace report deletion helper with action flag and remove obsolete tests --- ...dReportForUnapprovedTransactionsAction.tsx | 3 +- src/libs/ReportUtils.ts | 14 ++------- .../report/ContextMenu/ContextMenuActions.tsx | 6 ++-- src/types/onyx/ReportAction.ts | 3 ++ tests/unit/ReportUtilsTest.ts | 31 ------------------- 5 files changed, 9 insertions(+), 48 deletions(-) diff --git a/src/components/ReportActionItem/CreatedReportForUnapprovedTransactionsAction.tsx b/src/components/ReportActionItem/CreatedReportForUnapprovedTransactionsAction.tsx index 79bc3cd94814..4d035ec09ef6 100644 --- a/src/components/ReportActionItem/CreatedReportForUnapprovedTransactionsAction.tsx +++ b/src/components/ReportActionItem/CreatedReportForUnapprovedTransactionsAction.tsx @@ -4,7 +4,6 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import {getCreatedReportForUnapprovedTransactionsMessage, getOriginalMessage} from '@libs/ReportActionsUtils'; import {getReportName} from '@libs/ReportNameUtils'; -import {isReportDeleted} from '@libs/ReportUtils'; import ReportActionItemBasicMessage from '@pages/inbox/report/ReportActionItemBasicMessage'; import type CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -20,7 +19,7 @@ function CreatedReportForUnapprovedTransactionsAction({action}: CreatedReportFor const {translate} = useLocalize(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${originalID}`); const reportName = getReportName(report); - const htmlContent = `${getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, isReportDeleted(report), translate)}`; + const htmlContent = `${getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, !!action.isOriginalReportDeleted, translate)}`; return ( diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 1477752ae9e3..96d9cc5a0838 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5744,11 +5744,11 @@ function getReportName(reportNameInformation: GetReportNameParams): string { if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.CREATED_REPORT_FOR_UNAPPROVED_TRANSACTIONS)) { const {originalID} = getOriginalMessage(parentReportAction) ?? {}; - const originalReportOfUnapprovedTransactions = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${originalID}`]; + const originalReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${originalID}`]; // eslint-disable-next-line @typescript-eslint/no-deprecated -- temporarily disabling rule for deprecated functions out of issue scope - const reportName = getReportName({report: originalReportOfUnapprovedTransactions}); + const reportName = getReportName({report: originalReport}); // eslint-disable-next-line @typescript-eslint/no-deprecated -- temporarily disabling rule for deprecated functions out of issue scope - return getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, isReportDeleted(originalReportOfUnapprovedTransactions), translateLocal); + return getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, !!parentReportAction.isOriginalReportDeleted, translateLocal); } if (isTaskReport(report)) { @@ -10499,13 +10499,6 @@ function isReportParticipant(accountID: number | undefined, report: OnyxEntry): boolean { - return !report || !report.reportID || report.pendingFields?.preview === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; -} - /** * Check to see if the current user has access to view the report. */ @@ -13312,7 +13305,6 @@ export { getBillableAndTaxTotal, getReportForHeader, isReportOpenOrUnsubmitted, - isReportDeleted, getIconsForExpenseReport, }; diff --git a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx index 2ae8ee57aa2d..788e04d55825 100644 --- a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx @@ -163,7 +163,6 @@ import { getUnreportedTransactionMessage, getWorkspaceNameUpdatedMessage, isExpenseReport, - isReportDeleted, shouldDisableThread, shouldDisplayThreadReplies as shouldDisplayThreadRepliesReportUtils, } from '@libs/ReportUtils'; @@ -1062,9 +1061,8 @@ const ContextMenuActions: ContextMenuAction[] = [ setClipboardMessage(displayMessage); } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.CREATED_REPORT_FOR_UNAPPROVED_TRANSACTIONS)) { const {originalID} = getOriginalMessage(reportAction) ?? {}; - const originalReportOfUnapprovedTransactions = getReportOrDraftReport(originalID); - const reportName = getReportName(originalReportOfUnapprovedTransactions); - const displayMessage = getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, isReportDeleted(originalReportOfUnapprovedTransactions), translate); + const reportName = getReportName(getReportOrDraftReport(originalID)); + const displayMessage = getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, !!reportAction.isOriginalReportDeleted, translate); setClipboardMessage(displayMessage); } else if (content) { setClipboardMessage( diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 3c9061bc666c..95ab730457b7 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -265,6 +265,9 @@ type ReportActionBase = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Flag for checking if data is from optimistic data */ isOptimisticAction?: boolean; + /** Whether the original report related to this action was deleted */ + isOriginalReportDeleted?: boolean; + /** The admins's ID */ adminAccountID?: number; diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 4541e1236809..e29971f97c93 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -104,7 +104,6 @@ import { isHarvestCreatedExpenseReport, isMoneyRequestReportEligibleForMerge, isPayer, - isReportDeleted, isReportIneligibleForMoveExpenses, isReportOutstanding, isRootGroupChat, @@ -11879,36 +11878,6 @@ describe('ReportUtils', () => { }); }); - describe('isReportDeleted', () => { - it('should return true when report is undefined', () => { - expect(isReportDeleted(undefined)).toBe(true); - }); - - it('should return true when report has no reportID', () => { - const report = { - ...createRandomReport(1, undefined), - reportID: '', - }; - expect(isReportDeleted(report)).toBe(true); - }); - - it('should return true when report has pendingAction DELETE', () => { - const report = { - ...createRandomReport(1, undefined), - pendingFields: {preview: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}, - }; - expect(isReportDeleted(report)).toBe(true); - }); - - it('should return false when report has valid reportID and no DELETE pendingAction', () => { - const report = { - ...createRandomReport(1, undefined), - pendingFields: {preview: null}, - }; - expect(isReportDeleted(report)).toBe(false); - }); - }); - describe('getReportNotificationPreference', () => { it('should return hidden if notification preference is empty', () => { const report: Report = { From 070fe33f3a0e583eaf729579bf2baceceb224eb2 Mon Sep 17 00:00:00 2001 From: VH Date: Mon, 2 Mar 2026 18:06:25 +0700 Subject: [PATCH 11/14] Fix isOriginalReportDeleted field propagation through action mapper The lightweight action mapper in ReportActionsListItemRenderer was filtering out the isOriginalReportDeleted field, preventing it from reaching components that need it. Added the field to both the action construction object and useMemo dependency array. Also updated ReportNameUtils getReportName to pass the isOriginalReportDeleted flag to getCreatedReportForUnapprovedTransactionsMessage. --- src/libs/ReportNameUtils.ts | 2 +- src/pages/inbox/report/ReportActionsListItemRenderer.tsx | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportNameUtils.ts b/src/libs/ReportNameUtils.ts index 5e1db82b1a06..8e13aa502ff4 100644 --- a/src/libs/ReportNameUtils.ts +++ b/src/libs/ReportNameUtils.ts @@ -895,7 +895,7 @@ function getReportName( const originalReportName = originalReport ? getReportName(originalReport, reportAttributesDerivedValue) : ''; const currentLocale = IntlStore.getCurrentLocale(); const translateInCurrentLocale: LocalizedTranslate = (path, ...parameters) => translateWithLocale(currentLocale, path, ...parameters); - return getCreatedReportForUnapprovedTransactionsMessage(originalID, originalReportName, translateInCurrentLocale); + return getCreatedReportForUnapprovedTransactionsMessage(originalID, originalReportName, !!parentReportAction.isOriginalReportDeleted, translateInCurrentLocale); } if (isInvoiceRoom(report)) { diff --git a/src/pages/inbox/report/ReportActionsListItemRenderer.tsx b/src/pages/inbox/report/ReportActionsListItemRenderer.tsx index bb24d0c5d504..0cd87191d5ab 100644 --- a/src/pages/inbox/report/ReportActionsListItemRenderer.tsx +++ b/src/pages/inbox/report/ReportActionsListItemRenderer.tsx @@ -153,6 +153,7 @@ function ReportActionsListItemRenderer({ delegateAccountID: reportAction.delegateAccountID, previousMessage: reportAction.previousMessage, isAttachmentWithText: reportAction.isAttachmentWithText, + isOriginalReportDeleted: reportAction.isOriginalReportDeleted, childStateNum: reportAction.childStateNum, childStatusNum: reportAction.childStatusNum, childReportName: reportAction.childReportName, @@ -182,6 +183,7 @@ function ReportActionsListItemRenderer({ reportAction.delegateAccountID, reportAction.previousMessage, reportAction.isAttachmentWithText, + reportAction.isOriginalReportDeleted, reportAction.childStateNum, reportAction.childStatusNum, reportAction.childReportName, From f3624150846392b847ba1e1ca0dca3e3d23dd538 Mon Sep 17 00:00:00 2001 From: VH Date: Mon, 2 Mar 2026 18:19:55 +0700 Subject: [PATCH 12/14] Add isOriginalReportDeleted to DebugUtils validation schemas Updated both ReportAction validation schemas in DebugUtils to include the isOriginalReportDeleted field, fixing TypeScript compilation errors. --- src/libs/DebugUtils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index 4f33ff60b8b8..d4617974c40c 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -769,6 +769,7 @@ function validateReportActionDraftProperty(key: keyof ReportAction, value: strin childReportNotificationPreference: CONST.RED_BRICK_ROAD_PENDING_ACTION, isNewestReportAction: CONST.RED_BRICK_ROAD_PENDING_ACTION, isOptimisticAction: CONST.RED_BRICK_ROAD_PENDING_ACTION, + isOriginalReportDeleted: CONST.RED_BRICK_ROAD_PENDING_ACTION, adminAccountID: CONST.RED_BRICK_ROAD_PENDING_ACTION, whisperedToAccountIDs: CONST.RED_BRICK_ROAD_PENDING_ACTION, reportActionTimestamp: CONST.RED_BRICK_ROAD_PENDING_ACTION, @@ -1279,6 +1280,7 @@ function validateTransactionDraftProperty(key: keyof Transaction, value: string) childReportNotificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE, isNewestReportAction: 'boolean', isOptimisticAction: 'boolean', + isOriginalReportDeleted: 'boolean', adminAccountID: 'number', whisperedToAccountIDs: 'array', reportActionTimestamp: 'string', From 275da4a972ebd496c1275355e261f6bf5fe912cf Mon Sep 17 00:00:00 2001 From: VH Date: Mon, 2 Mar 2026 18:35:08 +0700 Subject: [PATCH 13/14] Add isOriginalReportDeleted case to switch exhaustiveness check Added the missing isOriginalReportDeleted case in validateReportActionDraftProperty switch statement to fix ESLint exhaustiveness check error. --- src/libs/DebugUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index d4617974c40c..770f148035f6 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -718,6 +718,7 @@ function validateReportActionDraftProperty(key: keyof ReportAction, value: strin case 'isAttachmentWithText': case 'isNewestReportAction': case 'isOptimisticAction': + case 'isOriginalReportDeleted': return validateBoolean(value); case 'created': case 'lastModified': From 48840271a36cf3502441e34f729e05d51b523e65 Mon Sep 17 00:00:00 2001 From: VH Date: Mon, 2 Mar 2026 20:58:00 +0700 Subject: [PATCH 14/14] Add isOriginalReportDeleted utility to handle both online and offline deletion states - Created isOriginalReportDeleted() utility function in ReportActionsUtils - Function checks both backend flag (action.isOriginalReportDeleted) and optimistic offline state (originalReport.pendingFields.preview === DELETE) - Updated 3 usage sites to use the new utility: * CreatedReportForUnapprovedTransactionsAction component * ReportNameUtils.getReportName() * ContextMenuActions copy message handler - Added comprehensive unit tests covering all edge cases --- ...dReportForUnapprovedTransactionsAction.tsx | 8 +-- src/libs/ReportActionsUtils.ts | 9 ++++ src/libs/ReportNameUtils.ts | 3 +- .../report/ContextMenu/ContextMenuActions.tsx | 11 ++++- tests/unit/ReportActionsUtilsTest.ts | 49 +++++++++++++++++++ 5 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/CreatedReportForUnapprovedTransactionsAction.tsx b/src/components/ReportActionItem/CreatedReportForUnapprovedTransactionsAction.tsx index 4d035ec09ef6..b4fc71ebea7c 100644 --- a/src/components/ReportActionItem/CreatedReportForUnapprovedTransactionsAction.tsx +++ b/src/components/ReportActionItem/CreatedReportForUnapprovedTransactionsAction.tsx @@ -2,7 +2,7 @@ import React from 'react'; import RenderHTML from '@components/RenderHTML'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import {getCreatedReportForUnapprovedTransactionsMessage, getOriginalMessage} from '@libs/ReportActionsUtils'; +import {getCreatedReportForUnapprovedTransactionsMessage, getOriginalMessage, isOriginalReportDeleted} from '@libs/ReportActionsUtils'; import {getReportName} from '@libs/ReportNameUtils'; import ReportActionItemBasicMessage from '@pages/inbox/report/ReportActionItemBasicMessage'; import type CONST from '@src/CONST'; @@ -17,9 +17,9 @@ type CreatedReportForUnapprovedTransactionsActionProps = { function CreatedReportForUnapprovedTransactionsAction({action}: CreatedReportForUnapprovedTransactionsActionProps) { const {originalID} = getOriginalMessage(action) ?? {}; const {translate} = useLocalize(); - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${originalID}`); - const reportName = getReportName(report); - const htmlContent = `${getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, !!action.isOriginalReportDeleted, translate)}`; + const [originalReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${originalID}`); + const reportName = getReportName(originalReport); + const htmlContent = `${getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, isOriginalReportDeleted(action, originalReport), translate)}`; return ( diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 901aa47aed9a..d33df150b296 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -4091,6 +4091,14 @@ function getHarvestCreatedExpenseReportMessage(reportID: string | undefined, rep return translate('reportAction.harvestCreatedExpenseReport', reportUrl, reportName); } +/** + * Check if the original report referenced by a report action is deleted. + */ +function isOriginalReportDeleted(action: OnyxEntry, originalReport: OnyxEntry): boolean { + // isOriginalReportDeleted is set to true when the original report is deleted from the backend. We can fall back to check if the report is optimistically deleted on the frontend side. + return !!action?.isOriginalReportDeleted || originalReport?.pendingFields?.preview === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; +} + function getCreatedReportForUnapprovedTransactionsMessage(reportID: string | undefined, reportName: string, isReportDeleted: boolean, translate: LocalizedTranslate): string { if (reportID === undefined) { return ''; @@ -4590,6 +4598,7 @@ export { getWorkspaceCategoriesUpdatedMessage, getHarvestCreatedExpenseReportMessage, getCreatedReportForUnapprovedTransactionsMessage, + isOriginalReportDeleted, isSystemUserMentioned, withDEWRoutedActionsArray, withDEWRoutedActionsObject, diff --git a/src/libs/ReportNameUtils.ts b/src/libs/ReportNameUtils.ts index 8e13aa502ff4..b2adbf1e8b07 100644 --- a/src/libs/ReportNameUtils.ts +++ b/src/libs/ReportNameUtils.ts @@ -92,6 +92,7 @@ import { isMoneyRequestAction, isMovedAction, isOldDotReportAction, + isOriginalReportDeleted, isRenamedAction, isReportActionAttachment, isTagModificationAction, @@ -895,7 +896,7 @@ function getReportName( const originalReportName = originalReport ? getReportName(originalReport, reportAttributesDerivedValue) : ''; const currentLocale = IntlStore.getCurrentLocale(); const translateInCurrentLocale: LocalizedTranslate = (path, ...parameters) => translateWithLocale(currentLocale, path, ...parameters); - return getCreatedReportForUnapprovedTransactionsMessage(originalID, originalReportName, !!parentReportAction.isOriginalReportDeleted, translateInCurrentLocale); + return getCreatedReportForUnapprovedTransactionsMessage(originalID, originalReportName, isOriginalReportDeleted(parentReportAction, originalReport), translateInCurrentLocale); } if (isInvoiceRoom(report)) { diff --git a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx index 788e04d55825..97351a4cc479 100644 --- a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx @@ -131,6 +131,7 @@ import { isMoneyRequestAction, isMovedAction, isOldDotReportAction, + isOriginalReportDeleted, isReimbursementDeQueuedOrCanceledAction, isReimbursementQueuedAction, isRenamedAction, @@ -1061,8 +1062,14 @@ const ContextMenuActions: ContextMenuAction[] = [ setClipboardMessage(displayMessage); } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.CREATED_REPORT_FOR_UNAPPROVED_TRANSACTIONS)) { const {originalID} = getOriginalMessage(reportAction) ?? {}; - const reportName = getReportName(getReportOrDraftReport(originalID)); - const displayMessage = getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, !!reportAction.isOriginalReportDeleted, translate); + const originalReportOfUnapprovedTransaction = getReportOrDraftReport(originalID); + const reportName = getReportName(originalReportOfUnapprovedTransaction); + const displayMessage = getCreatedReportForUnapprovedTransactionsMessage( + originalID, + reportName, + isOriginalReportDeleted(reportAction, originalReportOfUnapprovedTransaction), + translate, + ); setClipboardMessage(displayMessage); } else if (content) { setClipboardMessage( diff --git a/tests/unit/ReportActionsUtilsTest.ts b/tests/unit/ReportActionsUtilsTest.ts index 730dc75e195c..5e6e1ca4c72f 100644 --- a/tests/unit/ReportActionsUtilsTest.ts +++ b/tests/unit/ReportActionsUtilsTest.ts @@ -3726,4 +3726,53 @@ describe('ReportActionsUtils', () => { expect(withoutWrite.lastVisibleAction?.reportActionID).toBe(normalAction.reportActionID); }); }); + + describe('isOriginalReportDeleted', () => { + it('should return true when action.isOriginalReportDeleted is true', () => { + const action = { + ...createRandomReportAction(1), + isOriginalReportDeleted: true, + }; + const originalReport = createRandomReport(1, undefined); + + expect(ReportActionsUtils.isOriginalReportDeleted(action, originalReport)).toBe(true); + }); + + it('should return true when originalReport.pendingFields.preview is DELETE', () => { + const action = createRandomReportAction(1); + const originalReport = { + ...createRandomReport(1, undefined), + pendingFields: { + preview: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + }, + }; + + expect(ReportActionsUtils.isOriginalReportDeleted(action, originalReport)).toBe(true); + }); + + it('should return false when both conditions are not met', () => { + const action = { + ...createRandomReportAction(1), + isOriginalReportDeleted: false, + }; + const originalReport = createRandomReport(1, undefined); + + expect(ReportActionsUtils.isOriginalReportDeleted(action, originalReport)).toBe(false); + }); + + it('should return false when originalReport.pendingFields.preview is not DELETE', () => { + const action = { + ...createRandomReportAction(1), + isOriginalReportDeleted: false, + }; + const originalReport = { + ...createRandomReport(1, undefined), + pendingFields: { + preview: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }; + + expect(ReportActionsUtils.isOriginalReportDeleted(action, originalReport)).toBe(false); + }); + }); });