diff --git a/config/eslint/eslint.seatbelt.tsv b/config/eslint/eslint.seatbelt.tsv
index a6b9244c96f..39af0f899c8 100644
--- a/config/eslint/eslint.seatbelt.tsv
+++ b/config/eslint/eslint.seatbelt.tsv
@@ -733,7 +733,7 @@
"../../src/libs/ReportTitleUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 1
"../../src/libs/ReportUtils.ts" "@typescript-eslint/no-deprecated/getPolicy" 28
"../../src/libs/ReportUtils.ts" "@typescript-eslint/no-deprecated/translateLocal" 30
-"../../src/libs/ReportUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 33
+"../../src/libs/ReportUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 34
"../../src/libs/ReportUtils.ts" "rulesdir/no-onyx-connect" 16
"../../src/libs/SearchAutocompleteUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 4
"../../src/libs/SearchQueryUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 72
@@ -1790,7 +1790,7 @@
"../../tests/unit/ReportActionItemSingleTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1
"../../tests/unit/ReportActionsFollowupUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 5
"../../tests/unit/ReportActionsListPaddingViewTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 4
-"../../tests/unit/ReportActionsUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 35
+"../../tests/unit/ReportActionsUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 36
"../../tests/unit/ReportLayoutUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1
"../../tests/unit/ReportNameUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 23
"../../tests/unit/ReportPrimaryActionUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 35
@@ -1801,7 +1801,7 @@
"../../tests/unit/ReportTitleUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 3
"../../tests/unit/ReportTitleUtilsTest.ts" "no-restricted-imports" 1
"../../tests/unit/ReportUtilsGetIconsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1
-"../../tests/unit/ReportUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 18
+"../../tests/unit/ReportUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 19
"../../tests/unit/ReportUtilsTest.ts" "no-restricted-imports" 1
"../../tests/unit/ReportWelcomeTextTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1
"../../tests/unit/RequestConflictUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 11
diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index 52b1994f6fe..4aab3d6a4d9 100644
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -1728,6 +1728,23 @@ const CONST = {
CORPORATE_UPGRADE: 'POLICYCHANGELOG_CORPORATE_UPGRADE',
CORPORATE_FORCE_UPGRADE: 'POLICYCHANGELOG_CORPORATE_FORCE_UPGRADE',
TEAM_DOWNGRADE: 'POLICYCHANGELOG_TEAM_DOWNGRADE',
+ COPY_OVERVIEW: 'POLICYCHANGELOG_COPY_OVERVIEW',
+ COPY_EMPLOYEES: 'POLICYCHANGELOG_COPY_EMPLOYEES',
+ COPY_REPORT_FIELDS: 'POLICYCHANGELOG_COPY_REPORT_FIELDS',
+ COPY_ACCOUNTING: 'POLICYCHANGELOG_COPY_ACCOUNTING',
+ COPY_RECEIPT_PARTNERS: 'POLICYCHANGELOG_COPY_RECEIPT_PARTNERS',
+ COPY_HR: 'POLICYCHANGELOG_COPY_HR',
+ COPY_CATEGORIES: 'POLICYCHANGELOG_COPY_CATEGORIES',
+ COPY_TAGS: 'POLICYCHANGELOG_COPY_TAGS',
+ COPY_TAXES: 'POLICYCHANGELOG_COPY_TAXES',
+ COPY_TIME_TRACKING: 'POLICYCHANGELOG_COPY_TIME_TRACKING',
+ COPY_WORKFLOWS: 'POLICYCHANGELOG_COPY_WORKFLOWS',
+ COPY_RULES: 'POLICYCHANGELOG_COPY_RULES',
+ COPY_CODING_RULES: 'POLICYCHANGELOG_COPY_CODING_RULES',
+ COPY_DISTANCE: 'POLICYCHANGELOG_COPY_DISTANCE',
+ COPY_PER_DIEM: 'POLICYCHANGELOG_COPY_PER_DIEM',
+ COPY_INVOICES: 'POLICYCHANGELOG_COPY_INVOICES',
+ COPY_TRAVEL: 'POLICYCHANGELOG_COPY_TRAVEL',
},
RECEIPT_SCAN_FAILED: 'RECEIPTSCANFAILED',
RESOLVED_DUPLICATES: 'RESOLVEDDUPLICATES',
diff --git a/src/languages/de.ts b/src/languages/de.ts
index 24f94eeeae1..2b8ad4b1391 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -8318,6 +8318,46 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
customUnitRateDateRangeFrom: (date: string) => `ab dem ${date}`,
customUnitRateDateRangeUntilEnd: (date: string) => `bis ${date}`,
customUnitRateDateRangeAllDates: () => `für alle Daten`,
+ policyCopy: {
+ overview: (sourcePolicyName: string, sourcePolicyURL: string) => `Übersicht von ${sourcePolicyName} kopiert`,
+ employees: (sourcePolicyName: string, sourcePolicyURL: string) => `Mitglieder von ${sourcePolicyName} kopiert`,
+ reportFields: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 Berichtsfeld von ${sourcePolicyName} kopiert`,
+ other: (count: number) => `${count} Berichtsfelder von ${sourcePolicyName} kopiert`,
+ }),
+ accounting: (sourcePolicyName: string, sourcePolicyURL: string) => `Buchhaltungseinstellungen von ${sourcePolicyName} kopiert`,
+ receiptPartners: (sourcePolicyName: string, sourcePolicyURL: string) => `Einstellungen für Belegpartner von ${sourcePolicyName} kopiert`,
+ hr: (sourcePolicyName: string, sourcePolicyURL: string) => `HR-Einstellungen von ${sourcePolicyName} kopiert`,
+ categories: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 Kategorie von ${sourcePolicyName} kopiert`,
+ other: (count: number) => `${count} Kategorien von ${sourcePolicyName} kopiert`,
+ }),
+ tags: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 Tag von ${sourcePolicyName} kopiert`,
+ other: (count: number) => `${count} Tags von ${sourcePolicyName} kopiert`,
+ }),
+ taxes: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 Steuersatz aus ${sourcePolicyName} kopiert`,
+ other: (count: number) => `${count} Steuersätze von ${sourcePolicyName} kopiert`,
+ }),
+ timeTracking: (sourcePolicyName: string, sourcePolicyURL: string) => `Zeiterfassungseinstellungen von ${sourcePolicyName} kopiert`,
+ workflows: (sourcePolicyName: string, sourcePolicyURL: string) => `Workflows von ${sourcePolicyName} kopiert`,
+ rules: (sourcePolicyName: string, sourcePolicyURL: string) => `Kopierte Regeln von ${sourcePolicyName}`,
+ codingRules: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 Händlerregel von ${sourcePolicyName} kopiert`,
+ other: (count: number) => `${count} Händlerregeln von ${sourcePolicyName} kopiert`,
+ }),
+ distanceRates: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 Entfernungssatz aus ${sourcePolicyName} kopiert`,
+ other: (count: number) => `${count} Entfernungssätze aus ${sourcePolicyName} kopiert`,
+ }),
+ perDiem: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 Pauschale von ${sourcePolicyName} kopiert`,
+ other: (count: number) => `${count} Pauschalsätze pro Tag von ${sourcePolicyName} kopiert`,
+ }),
+ invoices: (sourcePolicyName: string, sourcePolicyURL: string) => `Rechnungseinstellungen von ${sourcePolicyName} kopiert`,
+ travel: (sourcePolicyName: string, sourcePolicyURL: string) => `Reiseeinstellungen von ${sourcePolicyName} kopiert`,
+ },
},
roomMembersPage: {
memberNotFound: 'Mitglied nicht gefunden.',
diff --git a/src/languages/en.ts b/src/languages/en.ts
index e1b8fb07ffa..2c9cc42055c 100644
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -8385,6 +8385,46 @@ const translations = {
setMaxExpenseAge: (newValue: string) => `set max expense age to "${newValue}" days`,
changedMaxExpenseAge: (oldValue: string, newValue: string) => `changed max expense age to "${newValue}" days (previously "${oldValue}")`,
removedMaxExpenseAge: (oldValue: string) => `removed max expense age (previously "${oldValue}" days)`,
+ policyCopy: {
+ overview: (sourcePolicyName: string, sourcePolicyURL: string) => `copied overview from ${sourcePolicyName}`,
+ employees: (sourcePolicyName: string, sourcePolicyURL: string) => `copied members from ${sourcePolicyName}`,
+ reportFields: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copied 1 report field from ${sourcePolicyName}`,
+ other: (count: number) => `copied ${count} report fields from ${sourcePolicyName}`,
+ }),
+ accounting: (sourcePolicyName: string, sourcePolicyURL: string) => `copied accounting settings from ${sourcePolicyName}`,
+ receiptPartners: (sourcePolicyName: string, sourcePolicyURL: string) => `copied receipt partner settings from ${sourcePolicyName}`,
+ hr: (sourcePolicyName: string, sourcePolicyURL: string) => `copied HR settings from ${sourcePolicyName}`,
+ categories: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copied 1 category from ${sourcePolicyName}`,
+ other: (count: number) => `copied ${count} categories from ${sourcePolicyName}`,
+ }),
+ tags: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copied 1 tag from ${sourcePolicyName}`,
+ other: (count: number) => `copied ${count} tags from ${sourcePolicyName}`,
+ }),
+ taxes: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copied 1 tax rate from ${sourcePolicyName}`,
+ other: (count: number) => `copied ${count} tax rates from ${sourcePolicyName}`,
+ }),
+ timeTracking: (sourcePolicyName: string, sourcePolicyURL: string) => `copied time tracking settings from ${sourcePolicyName}`,
+ workflows: (sourcePolicyName: string, sourcePolicyURL: string) => `copied workflows from ${sourcePolicyName}`,
+ rules: (sourcePolicyName: string, sourcePolicyURL: string) => `copied rules from ${sourcePolicyName}`,
+ codingRules: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copied 1 merchant rule from ${sourcePolicyName}`,
+ other: (count: number) => `copied ${count} merchant rules from ${sourcePolicyName}`,
+ }),
+ distanceRates: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copied 1 distance rate from ${sourcePolicyName}`,
+ other: (count: number) => `copied ${count} distance rates from ${sourcePolicyName}`,
+ }),
+ perDiem: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copied 1 per diem rate from ${sourcePolicyName}`,
+ other: (count: number) => `copied ${count} per diem rates from ${sourcePolicyName}`,
+ }),
+ invoices: (sourcePolicyName: string, sourcePolicyURL: string) => `copied invoice settings from ${sourcePolicyName}`,
+ travel: (sourcePolicyName: string, sourcePolicyURL: string) => `copied travel settings from ${sourcePolicyName}`,
+ },
},
roomMembersPage: {
memberNotFound: 'Member not found.',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index f8e09a6398d..4a8e9c44894 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -8108,6 +8108,46 @@ ${amount} para ${merchant} - ${date}`,
customUnitRateDateRangeFrom: (date: string) => `desde ${date}`,
customUnitRateDateRangeUntilEnd: (date: string) => `hasta ${date}`,
customUnitRateDateRangeAllDates: () => `para todas las fechas`,
+ policyCopy: {
+ overview: (sourcePolicyName: string, sourcePolicyURL: string) => `copió la descripción general de ${sourcePolicyName}`,
+ employees: (sourcePolicyName: string, sourcePolicyURL: string) => `copió miembros de ${sourcePolicyName}`,
+ reportFields: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `se copió 1 campo de informe desde ${sourcePolicyName}`,
+ other: (count: number) => `copió ${count} campos de informe de ${sourcePolicyName}`,
+ }),
+ accounting: (sourcePolicyName: string, sourcePolicyURL: string) => `copió la configuración de contabilidad de ${sourcePolicyName}`,
+ receiptPartners: (sourcePolicyName: string, sourcePolicyURL: string) => `copió la configuración de socios de recibos de ${sourcePolicyName}`,
+ hr: (sourcePolicyName: string, sourcePolicyURL: string) => `copió la configuración de RR. HH. de ${sourcePolicyName}`,
+ categories: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copió 1 categoría de ${sourcePolicyName}`,
+ other: (count: number) => `copió ${count} categorías de ${sourcePolicyName}`,
+ }),
+ tags: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `se copió 1 etiqueta de ${sourcePolicyName}`,
+ other: (count: number) => `copió ${count} etiquetas de ${sourcePolicyName}`,
+ }),
+ taxes: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `se copió 1 tasa de impuestos de ${sourcePolicyName}`,
+ other: (count: number) => `copió ${count} tasas de impuestos de ${sourcePolicyName}`,
+ }),
+ timeTracking: (sourcePolicyName: string, sourcePolicyURL: string) => `copió la configuración de registro de tiempo de ${sourcePolicyName}`,
+ workflows: (sourcePolicyName: string, sourcePolicyURL: string) => `copió los flujos de trabajo de ${sourcePolicyName}`,
+ rules: (sourcePolicyName: string, sourcePolicyURL: string) => `reglas copiadas de ${sourcePolicyName}`,
+ codingRules: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copió 1 regla de comercio de ${sourcePolicyName}`,
+ other: (count: number) => `copió ${count} reglas de comercio de ${sourcePolicyName}`,
+ }),
+ distanceRates: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `se copió 1 tarifa de distancia de ${sourcePolicyName}`,
+ other: (count: number) => `copió ${count} tarifas por distancia de ${sourcePolicyName}`,
+ }),
+ perDiem: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copió 1 Per diem Tasa de ${sourcePolicyName}`,
+ other: (count: number) => `copió ${count} tasas de per diem de ${sourcePolicyName}`,
+ }),
+ invoices: (sourcePolicyName: string, sourcePolicyURL: string) => `copió la configuración de facturas de ${sourcePolicyName}`,
+ travel: (sourcePolicyName: string, sourcePolicyURL: string) => `copió la configuración de viaje de ${sourcePolicyName}`,
+ },
},
roomMembersPage: {
memberNotFound: 'Miembro no encontrado.',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 88fa4ebcae0..c25d04124e0 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -8353,6 +8353,46 @@ Ajoutez davantage de règles de dépenses pour protéger la trésorerie de l’e
customUnitRateDateRangeFrom: (date: string) => `à partir du ${date}`,
customUnitRateDateRangeUntilEnd: (date: string) => `jusqu’au ${date}`,
customUnitRateDateRangeAllDates: () => `pour toutes les dates`,
+ policyCopy: {
+ overview: (sourcePolicyName: string, sourcePolicyURL: string) => `aperçu copié depuis ${sourcePolicyName}`,
+ employees: (sourcePolicyName: string, sourcePolicyURL: string) => `a copié les membres depuis ${sourcePolicyName}`,
+ reportFields: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 champ de note de frais copié depuis ${sourcePolicyName}`,
+ other: (count: number) => `${count} champs de note de frais copiés depuis ${sourcePolicyName}`,
+ }),
+ accounting: (sourcePolicyName: string, sourcePolicyURL: string) => `paramètres comptables copiés depuis ${sourcePolicyName}`,
+ receiptPartners: (sourcePolicyName: string, sourcePolicyURL: string) => `a copié les paramètres de reçu partenaire depuis ${sourcePolicyName}`,
+ hr: (sourcePolicyName: string, sourcePolicyURL: string) => `paramètres RH copiés depuis ${sourcePolicyName}`,
+ categories: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 catégorie copiée depuis ${sourcePolicyName}`,
+ other: (count: number) => `${count} catégories copiées depuis ${sourcePolicyName}`,
+ }),
+ tags: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 tag copié depuis ${sourcePolicyName}`,
+ other: (count: number) => `${count} tags copiés depuis ${sourcePolicyName}`,
+ }),
+ taxes: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 taux de taxe copié depuis ${sourcePolicyName}`,
+ other: (count: number) => `a copié ${count} taux de taxe depuis ${sourcePolicyName}`,
+ }),
+ timeTracking: (sourcePolicyName: string, sourcePolicyURL: string) => `paramètres de suivi du temps copiés depuis ${sourcePolicyName}`,
+ workflows: (sourcePolicyName: string, sourcePolicyURL: string) => `workflows copiés depuis ${sourcePolicyName}`,
+ rules: (sourcePolicyName: string, sourcePolicyURL: string) => `règles copiées depuis ${sourcePolicyName}`,
+ codingRules: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 règle de commerçant copiée depuis ${sourcePolicyName}`,
+ other: (count: number) => `a copié ${count} règles de commerçant depuis ${sourcePolicyName}`,
+ }),
+ distanceRates: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 taux de distance copié depuis ${sourcePolicyName}`,
+ other: (count: number) => `${count} taux de distance copiés depuis ${sourcePolicyName}`,
+ }),
+ perDiem: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `a copié 1 taux de per diem depuis ${sourcePolicyName}`,
+ other: (count: number) => `a copié ${count} taux journaliers de ${sourcePolicyName}`,
+ }),
+ invoices: (sourcePolicyName: string, sourcePolicyURL: string) => `paramètres de facturation copiés depuis ${sourcePolicyName}`,
+ travel: (sourcePolicyName: string, sourcePolicyURL: string) => `paramètres de déplacement copiés depuis ${sourcePolicyName}`,
+ },
},
roomMembersPage: {
memberNotFound: 'Membre introuvable.',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 22f1c24f722..78bdd7ffec4 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -8308,6 +8308,48 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`,
customUnitRateDateRangeFrom: (date: string) => `dal ${date}`,
customUnitRateDateRangeUntilEnd: (date: string) => `fino al ${date}`,
customUnitRateDateRangeAllDates: () => `per tutte le date`,
+ policyCopy: {
+ overview: (sourcePolicyName: string, sourcePolicyURL: string) => `panoramica copiata da ${sourcePolicyName}`,
+ employees: (sourcePolicyName: string, sourcePolicyURL: string) => `ha copiato i membri da ${sourcePolicyName}`,
+ reportFields: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiato 1 campo del report da ${sourcePolicyName}`,
+ other: (count: number) => `copiati ${count} campi del report da ${sourcePolicyName}`,
+ }),
+ accounting: (sourcePolicyName: string, sourcePolicyURL: string) => `impostazioni contabili copiate da ${sourcePolicyName}`,
+ receiptPartners: (sourcePolicyName: string, sourcePolicyURL: string) =>
+ `ha copiato le impostazioni del partner di ricevute da ${sourcePolicyName}`,
+ hr: (sourcePolicyName: string, sourcePolicyURL: string) => `impostazioni HR copiate da ${sourcePolicyName}`,
+ categories: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiata 1 categoria da ${sourcePolicyName}`,
+ other: (count: number) => `copiate ${count} categorie da ${sourcePolicyName}`,
+ }),
+ tags: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiato 1 tag da ${sourcePolicyName}`,
+ other: (count: number) => `copiati ${count} tag da ${sourcePolicyName}`,
+ }),
+ taxes: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiata 1 aliquota fiscale da ${sourcePolicyName}`,
+ other: (count: number) => `copiate ${count} aliquote fiscali da ${sourcePolicyName}`,
+ }),
+ timeTracking: (sourcePolicyName: string, sourcePolicyURL: string) =>
+ `ha copiato le impostazioni di rilevazione del tempo da ${sourcePolicyName}`,
+ workflows: (sourcePolicyName: string, sourcePolicyURL: string) => `ha copiato i flussi di lavoro da ${sourcePolicyName}`,
+ rules: (sourcePolicyName: string, sourcePolicyURL: string) => `regole copiate da ${sourcePolicyName}`,
+ codingRules: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiata 1 regola esercente da ${sourcePolicyName}`,
+ other: (count: number) => `copiate ${count} regole esercente da ${sourcePolicyName}`,
+ }),
+ distanceRates: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiata 1 tariffa chilometrica da ${sourcePolicyName}`,
+ other: (count: number) => `copiate ${count} tariffe chilometriche da ${sourcePolicyName}`,
+ }),
+ perDiem: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiata 1 indennità giornaliera da ${sourcePolicyName}`,
+ other: (count: number) => `copiati ${count} tassi di diaria da ${sourcePolicyName}`,
+ }),
+ invoices: (sourcePolicyName: string, sourcePolicyURL: string) => `impostazioni fattura copiate da ${sourcePolicyName}`,
+ travel: (sourcePolicyName: string, sourcePolicyURL: string) => `impostazioni di viaggio copiate da ${sourcePolicyName}`,
+ },
},
roomMembersPage: {
memberNotFound: 'Membro non trovato.',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index d9d7f87120e..cb2af1a3f83 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -8207,6 +8207,46 @@ ${reportName}`,
customUnitRateDateRangeFrom: (date: string) => `${date} から`,
customUnitRateDateRangeUntilEnd: (date: string) => `${date}まで`,
customUnitRateDateRangeAllDates: () => `すべての日付に対して`,
+ policyCopy: {
+ overview: (sourcePolicyName: string, sourcePolicyURL: string) => `${sourcePolicyName} から概要をコピーしました`,
+ employees: (sourcePolicyName: string, sourcePolicyURL: string) => `${sourcePolicyName} からメンバーをコピーしました`,
+ reportFields: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `${sourcePolicyName} からレポート項目を 1 件コピーしました`,
+ other: (count: number) => `${sourcePolicyName} からレポート項目を ${count} 件コピーしました`,
+ }),
+ accounting: (sourcePolicyName: string, sourcePolicyURL: string) => `${sourcePolicyName} から会計設定をコピーしました`,
+ receiptPartners: (sourcePolicyName: string, sourcePolicyURL: string) => `${sourcePolicyName} から領収書パートナー設定をコピーしました`,
+ hr: (sourcePolicyName: string, sourcePolicyURL: string) => `${sourcePolicyName} から人事設定をコピーしました`,
+ categories: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `${sourcePolicyName} からカテゴリを 1 件コピーしました`,
+ other: (count: number) => `${sourcePolicyName} から ${count} 件のカテゴリをコピーしました`,
+ }),
+ tags: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `${sourcePolicyName} からタグを 1 件コピーしました`,
+ other: (count: number) => `${sourcePolicyName} から ${count} 個のタグをコピーしました`,
+ }),
+ taxes: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `${sourcePolicyName} から税率を 1 件コピーしました`,
+ other: (count: number) => `${sourcePolicyName} から税率を ${count} 件コピーしました`,
+ }),
+ timeTracking: (sourcePolicyName: string, sourcePolicyURL: string) => `${sourcePolicyName} からタイムトラッキング設定をコピーしました`,
+ workflows: (sourcePolicyName: string, sourcePolicyURL: string) => `${sourcePolicyName} からワークフローをコピーしました`,
+ rules: (sourcePolicyName: string, sourcePolicyURL: string) => `${sourcePolicyName} からルールをコピーしました`,
+ codingRules: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `${sourcePolicyName} から取引先ルールを 1 件コピーしました`,
+ other: (count: number) => `${sourcePolicyName} から ${count} 件の取引先ルールをコピーしました`,
+ }),
+ distanceRates: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `${sourcePolicyName} から距離レートを1件コピーしました`,
+ other: (count: number) => `${sourcePolicyName} から距離レートを ${count} 件コピーしました`,
+ }),
+ perDiem: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `${sourcePolicyName} から日当レートを1件コピーしました`,
+ other: (count: number) => `${sourcePolicyName} から日当レートを ${count} 件コピーしました`,
+ }),
+ invoices: (sourcePolicyName: string, sourcePolicyURL: string) => `${sourcePolicyName} から請求書の設定をコピーしました`,
+ travel: (sourcePolicyName: string, sourcePolicyURL: string) => `${sourcePolicyName} から出張設定をコピーしました`,
+ },
},
roomMembersPage: {
memberNotFound: 'メンバーが見つかりません。',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 33a50fcd003..0209bb8dee7 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -8279,6 +8279,46 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`,
customUnitRateDateRangeFrom: (date: string) => `vanaf ${date}`,
customUnitRateDateRangeUntilEnd: (date: string) => `tot ${date}`,
customUnitRateDateRangeAllDates: () => `voor alle data`,
+ policyCopy: {
+ overview: (sourcePolicyName: string, sourcePolicyURL: string) => `overzicht gekopieerd van ${sourcePolicyName}`,
+ employees: (sourcePolicyName: string, sourcePolicyURL: string) => `leden gekopieerd van ${sourcePolicyName}`,
+ reportFields: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 rapportveld gekopieerd van ${sourcePolicyName}`,
+ other: (count: number) => `${count} rapportvelden gekopieerd van ${sourcePolicyName}`,
+ }),
+ accounting: (sourcePolicyName: string, sourcePolicyURL: string) => `boekhoudinstellingen gekopieerd van ${sourcePolicyName}`,
+ receiptPartners: (sourcePolicyName: string, sourcePolicyURL: string) => `instelling bonpartner gekopieerd van ${sourcePolicyName}`,
+ hr: (sourcePolicyName: string, sourcePolicyURL: string) => `HR-instellingen gekopieerd van ${sourcePolicyName}`,
+ categories: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 categorie gekopieerd van ${sourcePolicyName}`,
+ other: (count: number) => `${count} categorieën gekopieerd van ${sourcePolicyName}`,
+ }),
+ tags: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 label gekopieerd van ${sourcePolicyName}`,
+ other: (count: number) => `${count} tags gekopieerd van ${sourcePolicyName}`,
+ }),
+ taxes: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 belastingtarief gekopieerd van ${sourcePolicyName}`,
+ other: (count: number) => `heeft ${count} btw-tarieven gekopieerd van ${sourcePolicyName}`,
+ }),
+ timeTracking: (sourcePolicyName: string, sourcePolicyURL: string) => `tijdregistratie-instellingen gekopieerd van ${sourcePolicyName}`,
+ workflows: (sourcePolicyName: string, sourcePolicyURL: string) => `gekopieerde workflows van ${sourcePolicyName}`,
+ rules: (sourcePolicyName: string, sourcePolicyURL: string) => `regels gekopieerd van ${sourcePolicyName}`,
+ codingRules: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 leveranciersregel gekopieerd van ${sourcePolicyName}`,
+ other: (count: number) => `heeft ${count} leveranciersregels gekopieerd van ${sourcePolicyName}`,
+ }),
+ distanceRates: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 kilometervergoeding gekopieerd van ${sourcePolicyName}`,
+ other: (count: number) => `heeft ${count} kilometertarieven gekopieerd van ${sourcePolicyName}`,
+ }),
+ perDiem: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `1 dagvergoeding gekopieerd uit ${sourcePolicyName}`,
+ other: (count: number) => `${count} dagvergoedingen gekopieerd van ${sourcePolicyName}`,
+ }),
+ invoices: (sourcePolicyName: string, sourcePolicyURL: string) => `factuurinstellingen gekopieerd van ${sourcePolicyName}`,
+ travel: (sourcePolicyName: string, sourcePolicyURL: string) => `reiskosteninstellingen gekopieerd van ${sourcePolicyName}`,
+ },
},
roomMembersPage: {
memberNotFound: 'Lid niet gevonden.',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 5f6f2b1f6a8..48b5bb44d38 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -8268,6 +8268,46 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`,
customUnitRateDateRangeFrom: (date: string) => `od ${date}`,
customUnitRateDateRangeUntilEnd: (date: string) => `do ${date}`,
customUnitRateDateRangeAllDates: () => `dla wszystkich dat`,
+ policyCopy: {
+ overview: (sourcePolicyName: string, sourcePolicyURL: string) => `skopiowano podsumowanie z ${sourcePolicyName}`,
+ employees: (sourcePolicyName: string, sourcePolicyURL: string) => `skopiowano członków z ${sourcePolicyName}`,
+ reportFields: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `skopiowano 1 pole raportu z ${sourcePolicyName}`,
+ other: (count: number) => `skopiowano ${count} pola raportu z ${sourcePolicyName}`,
+ }),
+ accounting: (sourcePolicyName: string, sourcePolicyURL: string) => `skopiowano ustawienia księgowe z ${sourcePolicyName}`,
+ receiptPartners: (sourcePolicyName: string, sourcePolicyURL: string) => `skopiowano ustawienia partnera paragonów z ${sourcePolicyName}`,
+ hr: (sourcePolicyName: string, sourcePolicyURL: string) => `skopiowano ustawienia HR z ${sourcePolicyName}`,
+ categories: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `skopiowano 1 kategorię z ${sourcePolicyName}`,
+ other: (count: number) => `skopiowano ${count} kategorie z ${sourcePolicyName}`,
+ }),
+ tags: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `skopiowano 1 znacznik z ${sourcePolicyName}`,
+ other: (count: number) => `skopiowano ${count} tagów z ${sourcePolicyName}`,
+ }),
+ taxes: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `skopiowano 1 stawkę podatku z ${sourcePolicyName}`,
+ other: (count: number) => `skopiowano ${count} stawki podatkowe z ${sourcePolicyName}`,
+ }),
+ timeTracking: (sourcePolicyName: string, sourcePolicyURL: string) => `skopiowano ustawienia śledzenia czasu z ${sourcePolicyName}`,
+ workflows: (sourcePolicyName: string, sourcePolicyURL: string) => `skopiowano schematy pracy z ${sourcePolicyName}`,
+ rules: (sourcePolicyName: string, sourcePolicyURL: string) => `skopiowano zasady z ${sourcePolicyName}`,
+ codingRules: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `skopiowano 1 regułę sprzedawcy z ${sourcePolicyName}`,
+ other: (count: number) => `skopiowano ${count} reguł sprzedawcy z ${sourcePolicyName}`,
+ }),
+ distanceRates: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `skopiowano 1 stawkę za dystans z ${sourcePolicyName}`,
+ other: (count: number) => `skopiowano ${count} stawki za przejazd z ${sourcePolicyName}`,
+ }),
+ perDiem: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `skopiowano 1 stawkę ryczałtową z ${sourcePolicyName}`,
+ other: (count: number) => `skopiowano ${count} stawki diet z ${sourcePolicyName}`,
+ }),
+ invoices: (sourcePolicyName: string, sourcePolicyURL: string) => `skopiowano ustawienia faktur z ${sourcePolicyName}`,
+ travel: (sourcePolicyName: string, sourcePolicyURL: string) => `skopiowano ustawienia podróży z ${sourcePolicyName}`,
+ },
},
roomMembersPage: {
memberNotFound: 'Nie znaleziono członka.',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index f7c967cebe8..a94ffdffa85 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -8266,6 +8266,46 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`,
customUnitRateDateRangeFrom: (date: string) => `de ${date}`,
customUnitRateDateRangeUntilEnd: (date: string) => `até ${date}`,
customUnitRateDateRangeAllDates: () => `para todas as datas`,
+ policyCopy: {
+ overview: (sourcePolicyName: string, sourcePolicyURL: string) => `visão geral copiada de ${sourcePolicyName}`,
+ employees: (sourcePolicyName: string, sourcePolicyURL: string) => `copiou membros de ${sourcePolicyName}`,
+ reportFields: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiou 1 campo de relatório de ${sourcePolicyName}`,
+ other: (count: number) => `copiou ${count} campos de relatório de ${sourcePolicyName}`,
+ }),
+ accounting: (sourcePolicyName: string, sourcePolicyURL: string) => `configurações de contabilidade copiadas de ${sourcePolicyName}`,
+ receiptPartners: (sourcePolicyName: string, sourcePolicyURL: string) => `configurações de parceiro de recibos copiadas de ${sourcePolicyName}`,
+ hr: (sourcePolicyName: string, sourcePolicyURL: string) => `copiou as configurações de RH de ${sourcePolicyName}`,
+ categories: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiou 1 categoria de ${sourcePolicyName}`,
+ other: (count: number) => `copiou ${count} categorias de ${sourcePolicyName}`,
+ }),
+ tags: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiou 1 etiqueta de ${sourcePolicyName}`,
+ other: (count: number) => `copiou ${count} tags de ${sourcePolicyName}`,
+ }),
+ taxes: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiou 1 alíquota de imposto de ${sourcePolicyName}`,
+ other: (count: number) => `copiou ${count} taxas de imposto de ${sourcePolicyName}`,
+ }),
+ timeTracking: (sourcePolicyName: string, sourcePolicyURL: string) => `copiou as configurações de controle de horas de ${sourcePolicyName}`,
+ workflows: (sourcePolicyName: string, sourcePolicyURL: string) => `fluxos de trabalho copiados de ${sourcePolicyName}`,
+ rules: (sourcePolicyName: string, sourcePolicyURL: string) => `regras copiadas de ${sourcePolicyName}`,
+ codingRules: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiou 1 regra de comerciante de ${sourcePolicyName}`,
+ other: (count: number) => `copiou ${count} regras de comerciante de ${sourcePolicyName}`,
+ }),
+ distanceRates: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiou 1 taxa de distância de ${sourcePolicyName}`,
+ other: (count: number) => `copiou ${count} taxas de distância de ${sourcePolicyName}`,
+ }),
+ perDiem: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `copiou 1 tarifa de diária de ${sourcePolicyName}`,
+ other: (count: number) => `copiou ${count} taxas de diária de ${sourcePolicyName}`,
+ }),
+ invoices: (sourcePolicyName: string, sourcePolicyURL: string) => `configurações de fatura copiadas de ${sourcePolicyName}`,
+ travel: (sourcePolicyName: string, sourcePolicyURL: string) => `configurações de viagem copiadas de ${sourcePolicyName}`,
+ },
},
roomMembersPage: {
memberNotFound: 'Membro não encontrado.',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 4ad48b461da..95d3482422e 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -8045,6 +8045,46 @@ ${reportName}`,
customUnitRateDateRangeFrom: (date: string) => `自 ${date} 起`,
customUnitRateDateRangeUntilEnd: (date: string) => `直到 ${date}`,
customUnitRateDateRangeAllDates: () => `适用于所有日期`,
+ policyCopy: {
+ overview: (sourcePolicyName: string, sourcePolicyURL: string) => `已从 ${sourcePolicyName} 复制概览`,
+ employees: (sourcePolicyName: string, sourcePolicyURL: string) => `已从 ${sourcePolicyName} 复制成员`,
+ reportFields: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `已从 ${sourcePolicyName} 复制 1 个报表字段`,
+ other: (count: number) => `已从 ${sourcePolicyName} 复制了 ${count} 个报表字段`,
+ }),
+ accounting: (sourcePolicyName: string, sourcePolicyURL: string) => `已从 ${sourcePolicyName} 复制会计设置`,
+ receiptPartners: (sourcePolicyName: string, sourcePolicyURL: string) => `已从 ${sourcePolicyName} 复制收据合作方设置`,
+ hr: (sourcePolicyName: string, sourcePolicyURL: string) => `已从 ${sourcePolicyName} 复制人力资源设置`,
+ categories: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `已从 ${sourcePolicyName} 复制 1 个类别`,
+ other: (count: number) => `已从 ${sourcePolicyName} 复制了 ${count} 个类别`,
+ }),
+ tags: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `已从 ${sourcePolicyName} 复制 1 个标签`,
+ other: (count: number) => `已从 ${sourcePolicyName} 复制了 ${count} 个标签`,
+ }),
+ taxes: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `已从 ${sourcePolicyName} 复制 1 个税率`,
+ other: (count: number) => `已从 ${sourcePolicyName} 复制了 ${count} 个税率`,
+ }),
+ timeTracking: (sourcePolicyName: string, sourcePolicyURL: string) => `已从 ${sourcePolicyName} 复制时间跟踪设置`,
+ workflows: (sourcePolicyName: string, sourcePolicyURL: string) => `已从 ${sourcePolicyName} 复制工作流`,
+ rules: (sourcePolicyName: string, sourcePolicyURL: string) => `已从 ${sourcePolicyName} 复制规则`,
+ codingRules: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `已从 ${sourcePolicyName} 复制 1 条商家规则`,
+ other: (count: number) => `已从 ${sourcePolicyName} 复制 ${count} 条商户规则`,
+ }),
+ distanceRates: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `已从 ${sourcePolicyName} 复制 1 个里程费率`,
+ other: (count: number) => `已从 ${sourcePolicyName} 复制了 ${count} 个里程费率`,
+ }),
+ perDiem: ({sourcePolicyName, sourcePolicyURL}: {sourcePolicyName: string; sourcePolicyURL: string}) => ({
+ one: `已从 ${sourcePolicyName} 复制了 1 个每日补贴标准`,
+ other: (count: number) => `已从${sourcePolicyName}复制了${count}个每日津贴标准`,
+ }),
+ invoices: (sourcePolicyName: string, sourcePolicyURL: string) => `已从 ${sourcePolicyName} 复制发票设置`,
+ travel: (sourcePolicyName: string, sourcePolicyURL: string) => `已从 ${sourcePolicyName} 复制出差设置`,
+ },
},
roomMembersPage: {
memberNotFound: '未找到成员。',
diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts
index 672c3056a29..98fd202b071 100644
--- a/src/libs/OptionsListUtils/index.ts
+++ b/src/libs/OptionsListUtils/index.ts
@@ -105,6 +105,7 @@ import {
isMovedTransactionAction,
isOldDotReportAction,
isPendingRemove,
+ isPolicyCopyReportAction,
isReimbursementDeQueuedOrCanceledAction,
isReimbursementQueuedAction,
isRenamedAction,
@@ -130,6 +131,7 @@ import {
getMovedActionMessage,
getMovedTransactionMessage,
getParticipantsAccountIDsForDisplay,
+ getPolicyChangeLogCopyMessage,
getPolicyChangeMessage,
getPolicyName,
getReimbursementDeQueuedOrCanceledActionMessage,
@@ -1016,6 +1018,9 @@ function getLastMessageTextForReport({
if (isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_FEATURE_ENABLED)) {
lastMessageTextFromReport = getWorkspaceFeatureEnabledMessage(translate, lastReportAction);
}
+ if (isPolicyCopyReportAction(lastReportAction)) {
+ lastMessageTextFromReport = Parser.htmlToText(getPolicyChangeLogCopyMessage(translate, lastReportAction));
+ }
// we do not want to show report closed in LHN for non archived report so use getReportLastMessage as fallback instead of lastMessageText from report
if (reportID && !isReportArchived && report.lastActionType === CONST.REPORT.ACTIONS.TYPE.CLOSED) {
diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts
index 749b4dbb721..bc84e108ce8 100644
--- a/src/libs/ReportActionsUtils.ts
+++ b/src/libs/ReportActionsUtils.ts
@@ -41,7 +41,7 @@ import type {
import type {PolicyReportFieldType} from '@src/types/onyx/Policy';
import type Report from '@src/types/onyx/Report';
import type ReportAction from '@src/types/onyx/ReportAction';
-import type {Message, OldDotReportAction, OriginalMessage, ReportActions} from '@src/types/onyx/ReportAction';
+import type {Message, OldDotReportAction, OriginalMessage, PolicyChangeLogCopyReportActionNames, ReportActions} from '@src/types/onyx/ReportAction';
import type ReportActionName from '@src/types/onyx/ReportActionName';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import {getBankName, isCardPendingActivate} from './CardUtils';
@@ -207,7 +207,8 @@ function isDeletedAction(reportAction: OnyxInputOrEntry): boolean {
return !!originalMessage && typeof originalMessage === 'object' && 'reasoning' in originalMessage && !!originalMessage.reasoning;
}
+function isPolicyCopyReportAction(action: OnyxInputOrEntry): action is ReportAction {
+ return [
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_OVERVIEW,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_EMPLOYEES,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_REPORT_FIELDS,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_ACCOUNTING,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_RECEIPT_PARTNERS,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_HR,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CATEGORIES,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAGS,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAXES,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TIME_TRACKING,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_WORKFLOWS,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_RULES,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CODING_RULES,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_DISTANCE,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_PER_DIEM,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_INVOICES,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TRAVEL,
+ ].some((actionName) => actionName === action?.actionName);
+}
+
export {
doesReportHaveVisibleActions,
extractLinksFromMessageHtml,
@@ -4983,6 +5006,7 @@ export {
stripFollowupListFromHtml,
getDynamicExternalWorkflowSubmitFailedActionMessage,
getDynamicExternalWorkflowApproveFailedActionMessage,
+ isPolicyCopyReportAction,
};
export type {LastVisibleMessage};
diff --git a/src/libs/ReportNameUtils.ts b/src/libs/ReportNameUtils.ts
index 47292f11839..0a50232eef5 100644
--- a/src/libs/ReportNameUtils.ts
+++ b/src/libs/ReportNameUtils.ts
@@ -113,6 +113,7 @@ import {
isMovedAction,
isOldDotReportAction,
isOriginalReportDeleted,
+ isPolicyCopyReportAction,
isReimbursementDeQueuedOrCanceledAction,
isReimbursementQueuedAction,
isRejectedAction,
@@ -131,6 +132,7 @@ import {
getMovedActionMessage,
getMovedTransactionMessage,
getParentReport,
+ getPolicyChangeLogCopyMessage,
getPolicyChangeMessage,
getPolicyName,
getReimbursementDeQueuedOrCanceledActionMessage,
@@ -820,6 +822,9 @@ function computeReportNameBasedOnReportAction(
if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.REMOVE_EXPENSIFY_CARD_RULE)) {
return getRemoveExpensifyCardRuleMessage(translate, parentReportAction);
}
+ if (isPolicyCopyReportAction(parentReportAction)) {
+ return Parser.htmlToText(getPolicyChangeLogCopyMessage(translate, parentReportAction));
+ }
return undefined;
}
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 1ec30f23752..f524296708c 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -6900,6 +6900,72 @@ function getPolicyChangeMessage(translate: LocalizedTranslate, action: ReportAct
return message;
}
+function getPolicyChangeLogCopyMessage(translate: LocalizedTranslate, action: ReportAction) {
+ const PolicyChangeLogCopyOriginalMessage = getOriginalMessage(action as ReportAction) ?? {};
+ const {sourcePolicyID, quantity} = PolicyChangeLogCopyOriginalMessage;
+ // The name is interpolated into an tag and rendered as HTML, so it must be encoded to avoid markup in the name being parsed as HTML.
+ const sourcePolicyName = Str.htmlEncode(allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${sourcePolicyID}`]?.name ?? '');
+ const sourcePolicyURL = sourcePolicyID ? `${environmentURL}/${ROUTES.WORKSPACE_OVERVIEW.getRoute(sourcePolicyID)}` : '';
+ const count = quantity ?? 0;
+ let message = '';
+ switch (action.actionName) {
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_OVERVIEW:
+ message = translate('workspaceActions.policyCopy.overview', sourcePolicyName, sourcePolicyURL);
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_EMPLOYEES:
+ message = translate('workspaceActions.policyCopy.employees', sourcePolicyName, sourcePolicyURL);
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_REPORT_FIELDS:
+ message = translate('workspaceActions.policyCopy.reportFields', {count, sourcePolicyName, sourcePolicyURL});
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_ACCOUNTING:
+ message = translate('workspaceActions.policyCopy.accounting', sourcePolicyName, sourcePolicyURL);
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_RECEIPT_PARTNERS:
+ message = translate('workspaceActions.policyCopy.receiptPartners', sourcePolicyName, sourcePolicyURL);
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_HR:
+ message = translate('workspaceActions.policyCopy.hr', sourcePolicyName, sourcePolicyURL);
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CATEGORIES:
+ message = translate('workspaceActions.policyCopy.categories', {count, sourcePolicyName, sourcePolicyURL});
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAGS:
+ message = translate('workspaceActions.policyCopy.tags', {count, sourcePolicyName, sourcePolicyURL});
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAXES:
+ message = translate('workspaceActions.policyCopy.taxes', {count, sourcePolicyName, sourcePolicyURL});
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TIME_TRACKING:
+ message = translate('workspaceActions.policyCopy.timeTracking', sourcePolicyName, sourcePolicyURL);
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_WORKFLOWS:
+ message = translate('workspaceActions.policyCopy.workflows', sourcePolicyName, sourcePolicyURL);
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_RULES:
+ message = translate('workspaceActions.policyCopy.rules', sourcePolicyName, sourcePolicyURL);
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CODING_RULES:
+ message = translate('workspaceActions.policyCopy.codingRules', {count, sourcePolicyName, sourcePolicyURL});
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_DISTANCE:
+ message = translate('workspaceActions.policyCopy.distanceRates', {count, sourcePolicyName, sourcePolicyURL});
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_PER_DIEM:
+ message = translate('workspaceActions.policyCopy.perDiem', {count, sourcePolicyName, sourcePolicyURL});
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_INVOICES:
+ message = translate('workspaceActions.policyCopy.invoices', sourcePolicyName, sourcePolicyURL);
+ break;
+ case CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TRAVEL:
+ message = translate('workspaceActions.policyCopy.travel', sourcePolicyName, sourcePolicyURL);
+ break;
+ default:
+ break;
+ }
+ return message;
+}
+
/**
* @param iouReportID - the report ID of the IOU report the action belongs to
* @param type - IOUReportAction type. Can be oneOf(create, decline, cancel, pay, split)
@@ -13557,6 +13623,7 @@ export {
getReportSubtitlePrefix,
computeOptimisticReportName,
getPolicyChangeMessage,
+ getPolicyChangeLogCopyMessage,
getMovedTransactionMessage,
getUnreportedTransactionMessage,
navigateToLinkedReportAction,
diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts
index a3e90b203ca..e2ba488e6c2 100644
--- a/src/libs/SidebarUtils.ts
+++ b/src/libs/SidebarUtils.ts
@@ -139,6 +139,7 @@ import {
isInviteOrRemovedAction,
isLeavePolicyAction,
isOldDotReportAction,
+ isPolicyCopyReportAction,
isRenamedAction,
isTagModificationAction,
isTaskAction,
@@ -156,6 +157,7 @@ import {
getIcons,
getMovedTransactionMessage,
getParticipantsAccountIDsForDisplay,
+ getPolicyChangeLogCopyMessage,
getPolicyName,
getReceiptUploadErrorReason,
getReportDescription,
@@ -1248,6 +1250,8 @@ function getOptionData({
result.alternateText = getUpdatedIndividualBudgetNotificationMessage(translate, lastAction);
} else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.SHARED_BUDGET_NOTIFICATION) {
result.alternateText = getUpdatedSharedBudgetNotificationMessage(translate, lastAction);
+ } else if (isPolicyCopyReportAction(lastAction)) {
+ result.alternateText = Parser.htmlToText(getPolicyChangeLogCopyMessage(translate, lastAction));
} else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.RETRACTED) {
result.alternateText = translate('iou.retracted');
} else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.REOPENED) {
diff --git a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx
index 1481d70907b..88c512f0835 100644
--- a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx
+++ b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx
@@ -153,6 +153,7 @@ import {
isMovedAction,
isOldDotReportAction,
isOriginalReportDeleted,
+ isPolicyCopyReportAction,
isReimbursementDeQueuedOrCanceledAction,
isReimbursementQueuedAction,
isRejectedAction,
@@ -178,6 +179,7 @@ import {
getIOUReportActionDisplayMessage,
getMovedActionMessage,
getMovedTransactionMessage,
+ getPolicyChangeLogCopyMessage,
getPolicyChangeMessage,
getReimbursementDeQueuedOrCanceledActionMessage,
getReimbursementQueuedActionMessage,
@@ -1252,6 +1254,8 @@ const ContextMenuActions: ContextMenuAction[] = [
setClipboardMessage(getUpdatedIndividualBudgetNotificationMessage(translate, reportAction));
} else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.SHARED_BUDGET_NOTIFICATION)) {
setClipboardMessage(getUpdatedSharedBudgetNotificationMessage(translate, reportAction));
+ } else if (isPolicyCopyReportAction(reportAction)) {
+ setClipboardMessage(getPolicyChangeLogCopyMessage(translate, reportAction));
} else if (
isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.TAKE_CONTROL) ||
isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.REROUTE) ||
diff --git a/src/pages/inbox/report/actionContents/PolicyChangeLogContent.tsx b/src/pages/inbox/report/actionContents/PolicyChangeLogContent.tsx
index 164095ad605..e56090e81dc 100644
--- a/src/pages/inbox/report/actionContents/PolicyChangeLogContent.tsx
+++ b/src/pages/inbox/report/actionContents/PolicyChangeLogContent.tsx
@@ -84,7 +84,7 @@ import {
getWorkspaceTaxUpdateMessage,
getWorkspaceUpdateFieldMessage,
} from '@libs/ReportActionsUtils';
-import {getWorkspaceNameUpdatedMessage} from '@libs/ReportUtils';
+import {getPolicyChangeLogCopyMessage, getWorkspaceNameUpdatedMessage} from '@libs/ReportUtils';
import {getAddExpensifyCardRuleMessage, getRemoveExpensifyCardRuleMessage, getUpdateExpensifyCardRuleMessage} from '@libs/SpendRuleChangeLogUtils';
import ReportActionItemBasicMessage from '@pages/inbox/report/ReportActionItemBasicMessage';
import CONST from '@src/CONST';
@@ -99,6 +99,9 @@ type ResolverFn = (translate: LocaleContextProps['translate'], action: OnyxTypes
const categoryResolver: ResolverFn = (translate, action, policy) => getWorkspaceCategoryUpdateMessage(translate, action, policy);
const taxResolver: ResolverFn = (translate, action) => getWorkspaceTaxUpdateMessage(translate, action);
const tagModificationResolver: ResolverFn = (translate, action) => getCleanedTagName(getWorkspaceTagUpdateMessage(translate, action));
+const copySettingsResolver: ResolverFn = (translate, action) => ({
+ html: `${getPolicyChangeLogCopyMessage(translate, action)}`,
+});
const POLICY_CHANGE_LOG_RESOLVERS: Record = {
// Simple string translations
@@ -218,6 +221,25 @@ const POLICY_CHANGE_LOG_RESOLVERS: Record = {
[CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.DELETE_TAG]: tagModificationResolver,
[CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.DELETE_MULTIPLE_TAGS]: tagModificationResolver,
[CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_TAG]: tagModificationResolver,
+
+ // Policy copy settings
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_OVERVIEW]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_EMPLOYEES]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_REPORT_FIELDS]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_ACCOUNTING]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_RECEIPT_PARTNERS]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_HR]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CATEGORIES]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAGS]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAXES]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TIME_TRACKING]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_WORKFLOWS]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_RULES]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CODING_RULES]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_DISTANCE]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_PER_DIEM]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_INVOICES]: copySettingsResolver,
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TRAVEL]: copySettingsResolver,
};
/**
diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts
index 3fe91c50742..a361afad8ab 100644
--- a/src/types/onyx/OriginalMessage.ts
+++ b/src/types/onyx/OriginalMessage.ts
@@ -6,6 +6,7 @@ import type {PolicyRuleTaxRate} from './ExpenseRule';
import type {Attendee} from './IOU';
import type {OldDotOriginalMessageMap} from './OldDotAction';
import type {AllConnectionName} from './Policy';
+import type {PolicyChangeLogCopyReportActionNames} from './ReportAction';
import type ReportActionName from './ReportActionName';
import type {Reservation, TransactionCommentVendor} from './Transaction';
import type TransactionPending3DSReview from './TransactionPending3DSReview';
@@ -882,6 +883,15 @@ type OriginalMessageSpendRuleChangeLog = {
currency?: string;
};
+/** Model of a policy copy change log action */
+type OriginalMessagePolicyChangeCopyLog = {
+ /** The ID of the source policy from which the user copied settings */
+ sourcePolicyID?: string;
+
+ /** The quantity of the item copied from source policy */
+ quantity?: number;
+};
+
/** Model of `join policy` report action */
type OriginalMessageJoinPolicy = {
/** What was the invited user decision */
@@ -1721,7 +1731,8 @@ type OriginalMessageMap = {
[CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENT_DIRECTOR_INFORMATION_REQUIRED]: OriginalMessageReimbursementDirectorInformationRequired;
[CONST.REPORT.ACTIONS.TYPE.SETTLEMENT_ACCOUNT_LOCKED]: OriginalMessageSettlementAccountLocked;
} & OldDotOriginalMessageMap &
- Record, OriginalMessagePolicyChangeLog> & {
+ Record, OriginalMessagePolicyChangeLog> &
+ Record & {
[CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.ADD_EXPENSIFY_CARD_RULE]: OriginalMessageSpendRuleChangeLog;
[CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_EXPENSIFY_CARD_RULE]: OriginalMessageSpendRuleChangeLog;
[CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.REMOVE_EXPENSIFY_CARD_RULE]: OriginalMessageSpendRuleChangeLog;
diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts
index 95ab730457b..6c602ccfe8b 100644
--- a/src/types/onyx/ReportAction.ts
+++ b/src/types/onyx/ReportAction.ts
@@ -314,5 +314,25 @@ type ReportActions = Record;
/** Collection of mock report actions, indexed by reportActions_${reportID} */
type ReportActionsCollectionDataSet = CollectionDataSet;
+/** A union type of all report action names related to policy copy log */
+type PolicyChangeLogCopyReportActionNames =
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_OVERVIEW
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_EMPLOYEES
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_REPORT_FIELDS
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_ACCOUNTING
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_RECEIPT_PARTNERS
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_HR
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CATEGORIES
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAGS
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAXES
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TIME_TRACKING
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_WORKFLOWS
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_RULES
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CODING_RULES
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_DISTANCE
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_PER_DIEM
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_INVOICES
+ | typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TRAVEL;
+
export default ReportAction;
-export type {ReportActions, Message, LinkMetadata, OriginalMessage, ReportActionsCollectionDataSet, OldDotReportAction};
+export type {ReportActions, Message, LinkMetadata, OriginalMessage, ReportActionsCollectionDataSet, OldDotReportAction, PolicyChangeLogCopyReportActionNames};
diff --git a/tests/unit/PolicyChangeLogContentTest.tsx b/tests/unit/PolicyChangeLogContentTest.tsx
index e5ddf6cd72d..e771e8ba552 100644
--- a/tests/unit/PolicyChangeLogContentTest.tsx
+++ b/tests/unit/PolicyChangeLogContentTest.tsx
@@ -1,3 +1,4 @@
+import {NavigationContainer} from '@react-navigation/native';
import {act, render, screen} from '@testing-library/react-native';
import React from 'react';
import Onyx from 'react-native-onyx';
@@ -49,12 +50,14 @@ describe('PolicyChangeLogContent', () => {
const fakeAction = {actionName: type, originalMessage: {}} as ReportAction;
render(
-
-
- ,
+
+
+
+
+ ,
);
await waitForBatchedUpdatesWithAct();
diff --git a/tests/unit/ReportActionsUtilsTest.ts b/tests/unit/ReportActionsUtilsTest.ts
index e368b958de0..5940146e078 100644
--- a/tests/unit/ReportActionsUtilsTest.ts
+++ b/tests/unit/ReportActionsUtilsTest.ts
@@ -5952,4 +5952,49 @@ describe('ReportActionsUtils', () => {
expect(getModerationFlagState(action)).toEqual({latestDecision: undefined, hasBeenFlagged: false});
});
});
+
+ describe('isPolicyCopyReportAction', () => {
+ function buildAction(actionName: ReportAction['actionName']): ReportAction {
+ return {
+ actionName,
+ reportActionID: '1',
+ reportID: '123',
+ created: '2026-05-15 10:00:00.000',
+ message: [],
+ } as unknown as ReportAction;
+ }
+
+ it.each([
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_OVERVIEW,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_EMPLOYEES,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_REPORT_FIELDS,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_ACCOUNTING,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_RECEIPT_PARTNERS,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_HR,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CATEGORIES,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAGS,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAXES,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TIME_TRACKING,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_WORKFLOWS,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_RULES,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CODING_RULES,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_DISTANCE,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_PER_DIEM,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_INVOICES,
+ CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TRAVEL,
+ ])('returns true for the policy copy action %s', (actionName) => {
+ expect(ReportActionsUtils.isPolicyCopyReportAction(buildAction(actionName))).toBe(true);
+ });
+
+ it.each([CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, CONST.REPORT.ACTIONS.TYPE.CHANGE_POLICY, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_NAME, CONST.REPORT.ACTIONS.TYPE.CREATED])(
+ 'returns false for the non-copy action %s',
+ (actionName) => {
+ expect(ReportActionsUtils.isPolicyCopyReportAction(buildAction(actionName))).toBe(false);
+ },
+ );
+
+ it('returns false for an undefined action', () => {
+ expect(ReportActionsUtils.isPolicyCopyReportAction(undefined)).toBe(false);
+ });
+ });
});
diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts
index 925f1561408..84647fcdaa4 100644
--- a/tests/unit/ReportUtilsTest.ts
+++ b/tests/unit/ReportUtilsTest.ts
@@ -102,6 +102,7 @@ import {
getOutstandingChildRequest,
getParentNavigationSubtitle,
getParticipantsList,
+ getPolicyChangeLogCopyMessage,
getPolicyExpenseChat,
getPolicyIDsWithEmptyReportsForAccount,
getPolicyName,
@@ -7402,6 +7403,104 @@ describe('ReportUtils', () => {
});
});
+ describe('getPolicyChangeLogCopyMessage', () => {
+ const sourcePolicyID = 'sourcePolicy1';
+ const sourcePolicyName = 'Acme HQ Workspace';
+ let environmentURL: string;
+
+ // Builds the same source policy link the implementation wraps the policy name in.
+ const buildLink = (policyID: string | undefined, name: string) => {
+ const sourcePolicyURL = policyID ? `${environmentURL}/${ROUTES.WORKSPACE_OVERVIEW.getRoute(policyID)}` : '';
+ return `${name}`;
+ };
+
+ function buildCopyAction(actionName: ReportAction['actionName'], originalMessage?: {sourcePolicyID?: string; quantity?: number}): ReportAction {
+ return {
+ actionName,
+ reportActionID: '1',
+ reportID: '123',
+ created: '2026-05-15 10:00:00.000',
+ message: [],
+ originalMessage,
+ } as unknown as ReportAction;
+ }
+
+ beforeEach(async () => {
+ environmentURL = await getEnvironmentURL();
+ await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${sourcePolicyID}`, {id: sourcePolicyID, name: sourcePolicyName});
+ await waitForBatchedUpdates();
+ });
+
+ it.each([
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_OVERVIEW, 'copied overview from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_EMPLOYEES, 'copied members from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_ACCOUNTING, 'copied accounting settings from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_RECEIPT_PARTNERS, 'copied receipt partner settings from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_HR, 'copied HR settings from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TIME_TRACKING, 'copied time tracking settings from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_WORKFLOWS, 'copied workflows from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_RULES, 'copied rules from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_INVOICES, 'copied invoice settings from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TRAVEL, 'copied travel settings from'],
+ ])('returns the non-counted message for %s', (actionName, prefix) => {
+ const action = buildCopyAction(actionName, {sourcePolicyID});
+ expect(getPolicyChangeLogCopyMessage(translateLocal, action)).toBe(`${prefix} ${buildLink(sourcePolicyID, sourcePolicyName)}`);
+ });
+
+ it.each([
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_REPORT_FIELDS, 1, 'copied 1 report field from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_REPORT_FIELDS, 3, 'copied 3 report fields from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CATEGORIES, 1, 'copied 1 category from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CATEGORIES, 5, 'copied 5 categories from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAGS, 1, 'copied 1 tag from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAGS, 2, 'copied 2 tags from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAXES, 1, 'copied 1 tax rate from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAXES, 4, 'copied 4 tax rates from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CODING_RULES, 1, 'copied 1 merchant rule from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CODING_RULES, 6, 'copied 6 merchant rules from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_DISTANCE, 1, 'copied 1 distance rate from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_DISTANCE, 7, 'copied 7 distance rates from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_PER_DIEM, 1, 'copied 1 per diem rate from'],
+ [CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_PER_DIEM, 8, 'copied 8 per diem rates from'],
+ ])('returns the counted message for %s with quantity %d', (actionName, quantity, prefix) => {
+ const action = buildCopyAction(actionName, {sourcePolicyID, quantity});
+ expect(getPolicyChangeLogCopyMessage(translateLocal, action)).toBe(`${prefix} ${buildLink(sourcePolicyID, sourcePolicyName)}`);
+ });
+
+ it('falls back to a count of 0 (plural form) when quantity is missing on a counted action', () => {
+ const action = buildCopyAction(CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_CATEGORIES, {sourcePolicyID});
+ expect(getPolicyChangeLogCopyMessage(translateLocal, action)).toBe(`copied 0 categories from ${buildLink(sourcePolicyID, sourcePolicyName)}`);
+ });
+
+ it('uses an empty source policy name when the source policy is not in Onyx', () => {
+ const action = buildCopyAction(CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_OVERVIEW, {sourcePolicyID: 'unknownPolicy'});
+ expect(getPolicyChangeLogCopyMessage(translateLocal, action)).toBe(`copied overview from ${buildLink('unknownPolicy', '')}`);
+ });
+
+ it('uses an empty source policy name and URL when sourcePolicyID is missing', () => {
+ const action = buildCopyAction(CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_OVERVIEW, {});
+ expect(getPolicyChangeLogCopyMessage(translateLocal, action)).toBe(`copied overview from ${buildLink(undefined, '')}`);
+ });
+
+ it('handles a missing original message (no sourcePolicyID, count defaults to 0)', () => {
+ const action = buildCopyAction(CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_TAGS);
+ expect(getPolicyChangeLogCopyMessage(translateLocal, action)).toBe(`copied 0 tags from ${buildLink(undefined, '')}`);
+ });
+
+ it('HTML-encodes a source policy name that contains markup characters', async () => {
+ const htmlPolicyID = 'htmlPolicy';
+ await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${htmlPolicyID}`, {id: htmlPolicyID, name: 'A B'});
+ await waitForBatchedUpdates();
+ const action = buildCopyAction(CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.COPY_OVERVIEW, {sourcePolicyID: htmlPolicyID});
+ expect(getPolicyChangeLogCopyMessage(translateLocal, action)).toBe(`copied overview from ${buildLink(htmlPolicyID, 'A <b>B</b>')}`);
+ });
+
+ it('returns an empty string for an action that is not a policy copy action', () => {
+ const action = buildCopyAction(CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, {sourcePolicyID});
+ expect(getPolicyChangeLogCopyMessage(translateLocal, action)).toBe('');
+ });
+ });
+
describe('buildOptimisticIOUReportAction', () => {
it('should set the action reportID to the provided iouReportID when tracking a personal expense', () => {
const iouAction = buildOptimisticIOUReportAction({